diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index ccabb2ac37bb1..aa40cb07274e5 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -16,6 +16,10 @@ env: # Base image version from https://github.com/home-assistant/docker BASE_IMAGE_VERSION: "2026.07.0" ARCHITECTURES: '["amd64", "aarch64"]' + MACHINES: >- + ["generic-x86-64", "khadas-vim3", "odroid-c2", "odroid-c4", "odroid-m1", + "odroid-n2", "qemuarm-64", "qemux86-64", "raspberrypi3-64", + "raspberrypi4-64", "raspberrypi5-64", "yellow", "green"] permissions: {} @@ -35,6 +39,7 @@ jobs: channel: ${{ steps.version.outputs.channel }} publish: ${{ steps.version.outputs.publish }} architectures: ${{ env.ARCHITECTURES }} + machines: ${{ env.MACHINES }} base_image_version: ${{ env.BASE_IMAGE_VERSION }} steps: - name: Checkout the repository @@ -218,20 +223,7 @@ jobs: id-token: write # For cosign signing strategy: matrix: - machine: - - generic-x86-64 - - khadas-vim3 - - odroid-c2 - - odroid-c4 - - odroid-m1 - - odroid-n2 - - qemuarm-64 - - qemux86-64 - - raspberrypi3-64 - - raspberrypi4-64 - - raspberrypi5-64 - - yellow - - green + machine: ${{ fromJson(needs.init.outputs.machines) }} include: # Default: aarch64 on native ARM runner - arch: aarch64 @@ -282,11 +274,29 @@ jobs: push: true version: ${{ needs.init.outputs.version }} + run_e2e_tests_base: + name: Run E2E tests on base images + if: github.repository_owner == 'home-assistant' + needs: ["init", "build_base"] + uses: ./.github/workflows/e2e-tests.yml + with: + version: ${{ needs.init.outputs.version }} + image_prefixes: ${{ needs.init.outputs.architectures }} + + run_e2e_tests_machine: + name: Run E2E tests on machine images + if: github.repository_owner == 'home-assistant' + needs: ["init", "build_machine"] + uses: ./.github/workflows/e2e-tests.yml + with: + version: ${{ needs.init.outputs.version }} + image_prefixes: ${{ needs.init.outputs.machines }} + publish_ha: name: Publish version files environment: ${{ needs.init.outputs.channel }} if: github.repository_owner == 'home-assistant' - needs: ["init", "build_machine"] + needs: ["init", "build_machine", "run_e2e_tests_machine"] runs-on: ubuntu-latest permissions: contents: read @@ -326,7 +336,7 @@ jobs: name: Publish to ${{ matrix.registry }} environment: ${{ needs.init.outputs.channel }} if: github.repository_owner == 'home-assistant' - needs: ["init", "build_base"] + needs: ["init", "build_base", "run_e2e_tests_base"] runs-on: ubuntu-latest permissions: contents: read # To check out the repository @@ -461,7 +471,7 @@ jobs: build_python: name: Build PyPi package environment: ${{ needs.init.outputs.channel }} - needs: ["init", "build_base"] + needs: ["init", "build_base", "run_e2e_tests_base"] runs-on: ubuntu-latest permissions: contents: read # To check out the repository diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 64b0d8c847671..f58f1af98d77b 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -8,32 +8,48 @@ on: description: "Image tag or digest to test (e.g. dev, 2026.7.1, 2026.8.0b0, sha256:0a1b2c3d…)" default: "dev" required: true + image_prefixes: + description: "JSON array of image prefixes to test. Will be prefixed to `-homeassistant`" + default: >- + ["generic-x86-64", "khadas-vim3", "odroid-c2", "odroid-c4", + "odroid-m1", "odroid-n2", "qemuarm-64", "qemux86-64", + "raspberrypi3-64", "raspberrypi4-64", "raspberrypi5-64", + "yellow", "green"] + required: true + workflow_call: + inputs: + version: + description: "Image tag or digest to test (e.g. dev, 2026.7.1, 2026.8.0b0, sha256:0a1b2c3d…)" + required: true + type: string + image_prefixes: + description: "JSON array of image prefixes to test. Will be prefixed to `-homeassistant`" + required: true + type: string permissions: {} concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.version }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.version }}-${{ inputs.image_prefixes }} cancel-in-progress: true jobs: boot_check: - name: Boot check ${{ matrix.arch }} core image + name: Boot check ${{ matrix.image_prefix }} image if: github.repository_owner == 'home-assistant' - runs-on: ${{ matrix.runs-on }} + # amd64 images need an x86 runner; all others are aarch64 + runs-on: ${{ contains(fromJson('["amd64", "generic-x86-64", "qemux86-64"]'), matrix.image_prefix) && 'ubuntu-24.04' || 'ubuntu-24.04-arm' }} timeout-minutes: 20 strategy: fail-fast: false matrix: - include: - - arch: amd64 - runs-on: ubuntu-24.04 - - arch: aarch64 - runs-on: ubuntu-24.04-arm + image_prefix: ${{ fromJson(inputs.image_prefixes) }} env: BASE_URL: http://localhost:8123 services: homeassistant: - image: ghcr.io/home-assistant/home-assistant${{ startsWith(inputs.version, 'sha256:') && '@' || ':' }}${{ inputs.version }} # zizmor: ignore[unpinned-images] + # Digests are referenced with '@', tags with ':' + image: ghcr.io/home-assistant/${{ matrix.image_prefix }}-homeassistant${{ startsWith(inputs.version, 'sha256:') && '@' || ':' }}${{ inputs.version }} # zizmor: ignore[unpinned-images] ports: - 8123:8123 # Gate steps until Home Assistant answers (60 x 5s ≈ 300s startup budget) @@ -76,7 +92,7 @@ jobs: if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: playwright-report-${{ matrix.arch }} + name: playwright-report-${{ matrix.image_prefix }} path: | tests/e2e/playwright-report/ tests/e2e/test-results/ @@ -91,5 +107,5 @@ jobs: if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: container-logs-${{ matrix.arch }} + name: container-logs-${{ matrix.image_prefix }} path: homeassistant.log diff --git a/homeassistant/components/blebox/button.py b/homeassistant/components/blebox/button.py index 16ab7b4493d71..e82216c8f0f43 100644 --- a/homeassistant/components/blebox/button.py +++ b/homeassistant/components/blebox/button.py @@ -21,6 +21,9 @@ "fav": ButtonEntityDescription(key="fav", translation_key="fav"), "open": ButtonEntityDescription(key="open", translation_key="open"), "close": ButtonEntityDescription(key="close", translation_key="close"), + "second_output": ButtonEntityDescription( + key="second_output", translation_key="second_output" + ), } _DEFAULT_BUTTON = ButtonEntityDescription(key="button") diff --git a/homeassistant/components/blebox/strings.json b/homeassistant/components/blebox/strings.json index 82f9cb4944f6a..70c49a6f98b20 100644 --- a/homeassistant/components/blebox/strings.json +++ b/homeassistant/components/blebox/strings.json @@ -78,6 +78,7 @@ "down": { "name": "Down" }, "fav": { "name": "Favorite" }, "open": { "name": "Open" }, + "second_output": { "name": "Second output" }, "up": { "name": "Up" } }, "light": { "channel": { "name": "Channel {index}" } }, diff --git a/homeassistant/components/portainer/button.py b/homeassistant/components/portainer/button.py index 72a193de6bf92..d3ab57f56103d 100644 --- a/homeassistant/components/portainer/button.py +++ b/homeassistant/components/portainer/button.py @@ -6,7 +6,7 @@ from datetime import timedelta from typing import Any, override -from pyportainer import Portainer +from pyportainer import DockerContainerState, Portainer from pyportainer.exceptions import ( PortainerAuthenticationError, PortainerConnectionError, @@ -54,6 +54,7 @@ class PortainerContainerButtonDescription(ButtonEntityDescription): [Portainer, int, str], Coroutine[Any, Any, DockerContainer | None], ] + available_fn: Callable[[PortainerContainerData], bool] ENDPOINT_BUTTONS: tuple[PortainerEndpointButtonDescription, ...] = ( @@ -89,6 +90,9 @@ class PortainerContainerButtonDescription(ButtonEntityDescription): endpoint_id, container_id ) ), + available_fn=lambda container: ( + container.container.state != DockerContainerState.PAUSED + ), ), PortainerContainerButtonDescription( key="pause", @@ -99,6 +103,9 @@ class PortainerContainerButtonDescription(ButtonEntityDescription): endpoint_id, container_id ) ), + available_fn=lambda container: ( + container.container.state == DockerContainerState.RUNNING + ), ), PortainerContainerButtonDescription( key="resume", @@ -109,6 +116,9 @@ class PortainerContainerButtonDescription(ButtonEntityDescription): endpoint_id, container_id ) ), + available_fn=lambda container: ( + container.container.state == DockerContainerState.PAUSED + ), ), PortainerContainerButtonDescription( key="recreate", @@ -122,6 +132,10 @@ class PortainerContainerButtonDescription(ButtonEntityDescription): pull_image=True, ) ), + available_fn=lambda container: ( + container.container.state + not in (DockerContainerState.REMOVING, DockerContainerState.DEAD) + ), ), PortainerContainerButtonDescription( key="kill", @@ -132,6 +146,10 @@ class PortainerContainerButtonDescription(ButtonEntityDescription): endpoint_id, container_id ) ), + available_fn=lambda container: ( + container.container.state + in (DockerContainerState.RUNNING, DockerContainerState.PAUSED) + ), ), ) @@ -244,6 +262,14 @@ class PortainerContainerButton(PortainerContainerEntity, PortainerBaseButton): entity_description: PortainerContainerButtonDescription + @property + @override + def available(self) -> bool: + """Return if the button is available.""" + return super().available and self.entity_description.available_fn( + self.container_data + ) + @override async def _async_press_call(self) -> None: """Call the container button press action.""" diff --git a/homeassistant/components/subaru/binary_sensor.py b/homeassistant/components/subaru/binary_sensor.py new file mode 100644 index 0000000000000..62507e3bb94e7 --- /dev/null +++ b/homeassistant/components/subaru/binary_sensor.py @@ -0,0 +1,295 @@ +"""Support for Subaru binary sensors.""" + +from collections.abc import Callable +from dataclasses import dataclass +from functools import partial +from typing import Any, override + +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, + BinarySensorEntityDescription, +) +from homeassistant.const import EntityCategory +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback + +from .const import ( + GEN_2_AND_NEWER, + VEHICLE_API_GEN, + VEHICLE_FEATURES, + VEHICLE_HAS_EV, + VEHICLE_HEALTH, + VEHICLE_STATUS, + VEHICLE_VIN, +) +from .coordinator import SubaruConfigEntry, SubaruDataUpdateCoordinator +from .entity import SubaruCoordinatorEntity + +# Keys returned by subarulink controller.get_data() inside vehicle_status. +DOOR_POSITION_KEYS: dict[str, str] = { + "DOOR_FRONT_LEFT_POSITION": "door_front_left", + "DOOR_FRONT_RIGHT_POSITION": "door_front_right", + "DOOR_REAR_LEFT_POSITION": "door_rear_left", + "DOOR_REAR_RIGHT_POSITION": "door_rear_right", + "DOOR_BOOT_POSITION": "door_boot", + "DOOR_ENGINE_HOOD_POSITION": "door_engine_hood", +} +WINDOW_STATUS_KEYS: dict[str, str] = { + "WINDOW_FRONT_LEFT_STATUS": "window_front_left", + "WINDOW_FRONT_RIGHT_STATUS": "window_front_right", + "WINDOW_REAR_LEFT_STATUS": "window_rear_left", + "WINDOW_REAR_RIGHT_STATUS": "window_rear_right", + "WINDOW_SUNROOF_STATUS": "window_sunroof", +} +LOCK_STATUS_KEYS: dict[str, str] = { + "LOCK_FRONT_LEFT_STATUS": "lock_status_front_left", + "LOCK_FRONT_RIGHT_STATUS": "lock_status_front_right", + "LOCK_REAR_LEFT_STATUS": "lock_status_rear_left", + "LOCK_REAR_RIGHT_STATUS": "lock_status_rear_right", + "LOCK_BOOT_STATUS": "lock_status_boot", +} + +# EV_IS_PLUGGED_IN values meaning connected; other known values mean not +# connected. +EV_PLUGGED_IN_STATES = frozenset({"CHARGING", "LOCKED_CONNECTED", "UNLOCKED_CONNECTED"}) +API_KEY_EV_IS_PLUGGED_IN = "EV_IS_PLUGGED_IN" +API_KEY_EV_CHARGER_STATE_TYPE = "EV_CHARGER_STATE_TYPE" +EV_CHARGING_STATE = "CHARGING" + +# vehicle_health response shape (see integration debug diagnostics). +HEALTH_ISTROUBLE = "ISTROUBLE" +HEALTH_FEATURES = "FEATURES" + +# Subaru MIL (Malfunction Indicator Lamp) feature codes -> translation keys. +# ATF_MIL is a transmission temperature warning, not fluid level. +MIL_TRANSLATION_KEYS: dict[str, str] = { + "SRS_MIL": "mil_srs", + "AWD_MIL": "mil_awd", + "ABS_MIL": "mil_abs", + "ATF_MIL": "mil_atf", + "BSDRCT_MIL": "mil_bsdrct", + "CEL_MIL": "mil_cel", + "EBD_MIL": "mil_ebd", + "EPB_MIL": "mil_epb", + "EOL_MIL": "mil_eol", + "ESS_MIL": "mil_ess", + "ISS_MIL": "mil_iss", + "OPL_MIL": "mil_opl", + "EPAS_MIL": "mil_epas", + "RAB_MIL": "mil_rab", + "TEL_MIL": "mil_tel", + "TPMS_MIL": "mil_tpms", + "VDC_MIL": "mil_vdc", + "WASH_MIL": "mil_wash", + "SRH_MIL": "mil_srh", +} + +# "CLOSED" (doors) or "CLOSE" (windows) means closed. +OPENING_CLOSED_VALUES = frozenset({"CLOSED", "CLOSE"}) +# Sentinel values meaning "no data"; compared case-insensitively. +UNKNOWN_STATUSES = frozenset({"UNKNOWN", "UNAVAILABLE", "NOT_EQUIPPED"}) + + +@dataclass(frozen=True, kw_only=True) +class SubaruBinarySensorEntityDescription(BinarySensorEntityDescription): + """Describes a Subaru binary sensor entity.""" + + is_on_fn: Callable[[dict[str, Any]], bool | None] + + +def _vehicle_status_value(vehicle_data: dict[str, Any], api_key: str) -> str | None: + """Return the normalized vehicle_status value for api_key, or None if missing/unknown.""" + status = (vehicle_data.get(VEHICLE_STATUS) or {}).get(api_key) + if status is None: + return None + status = status.upper() + return None if status in UNKNOWN_STATUSES else status + + +def _opening_is_on(vehicle_data: dict[str, Any], api_key: str) -> bool | None: + """Whether a door/window field is open.""" + value = _vehicle_status_value(vehicle_data, api_key) + return None if value is None else value not in OPENING_CLOSED_VALUES + + +def _lock_is_on(vehicle_data: dict[str, Any], api_key: str) -> bool | None: + """Whether a lock field is unlocked.""" + value = _vehicle_status_value(vehicle_data, api_key) + return None if value is None else value != "LOCKED" + + +def _mil_trouble(vehicle_data: dict[str, Any], feature: str) -> bool | None: + """Return vehicle_health.FEATURES[feature].ISTROUBLE, or None if not reported.""" + features = (vehicle_data.get(VEHICLE_HEALTH) or {}).get(HEALTH_FEATURES) or {} + feature_health = features.get(feature) + if not feature_health or HEALTH_ISTROUBLE not in feature_health: + return None + return bool(feature_health[HEALTH_ISTROUBLE]) + + +# Static descriptions for entities that are created for every Gen2+ vehicle. +# MIL diagnostics are built dynamically below based on vehicle_features. +BINARY_SENSORS: tuple[SubaruBinarySensorEntityDescription, ...] = ( + *( + SubaruBinarySensorEntityDescription( + key=api_key, + translation_key=trans_key, + device_class=BinarySensorDeviceClass.DOOR, + is_on_fn=partial(_opening_is_on, api_key=api_key), + ) + for api_key, trans_key in DOOR_POSITION_KEYS.items() + ), + *( + SubaruBinarySensorEntityDescription( + key=api_key, + translation_key=trans_key, + device_class=BinarySensorDeviceClass.WINDOW, + is_on_fn=partial(_opening_is_on, api_key=api_key), + ) + for api_key, trans_key in WINDOW_STATUS_KEYS.items() + ), + *( + SubaruBinarySensorEntityDescription( + key=api_key, + translation_key=trans_key, + device_class=BinarySensorDeviceClass.LOCK, + is_on_fn=partial(_lock_is_on, api_key=api_key), + ) + for api_key, trans_key in LOCK_STATUS_KEYS.items() + ), +) + +OVERALL_HEALTH_BINARY_SENSOR = SubaruBinarySensorEntityDescription( + key="health_istrouble", + translation_key="health_istrouble", + device_class=BinarySensorDeviceClass.PROBLEM, + entity_category=EntityCategory.DIAGNOSTIC, + is_on_fn=lambda d: ( + None + if not (health := d.get(VEHICLE_HEALTH)) or HEALTH_ISTROUBLE not in health + else bool(health[HEALTH_ISTROUBLE]) + ), +) + +EV_PLUG_BINARY_SENSOR = SubaruBinarySensorEntityDescription( + key=API_KEY_EV_IS_PLUGGED_IN, + translation_key="ev_is_plugged_in", + device_class=BinarySensorDeviceClass.PLUG, + is_on_fn=lambda d: ( + None + if (v := _vehicle_status_value(d, API_KEY_EV_IS_PLUGGED_IN)) is None + else v in EV_PLUGGED_IN_STATES + ), +) + +EV_CHARGING_BINARY_SENSOR = SubaruBinarySensorEntityDescription( + key=API_KEY_EV_CHARGER_STATE_TYPE, + translation_key="is_charging", + device_class=BinarySensorDeviceClass.BATTERY_CHARGING, + entity_registry_enabled_default=False, + is_on_fn=lambda d: ( + None + if (v := _vehicle_status_value(d, API_KEY_EV_CHARGER_STATE_TYPE)) is None + else v == EV_CHARGING_STATE + ), +) + + +def _build_mil_descriptions( + features: list[str], +) -> list[SubaruBinarySensorEntityDescription]: + """Return MIL descriptions for MIL feature codes that the vehicle reports. + + Built once at setup; a MIL code that starts appearing later (partial + first poll, or a code only reported once triggered) needs a reload. + """ + return [ + SubaruBinarySensorEntityDescription( + key=feature, + translation_key=MIL_TRANSLATION_KEYS[feature], + device_class=BinarySensorDeviceClass.PROBLEM, + entity_category=EntityCategory.DIAGNOSTIC, + # Disabled by default to avoid ~19 mostly-off entries per + # vehicle; the overall health rollup stays enabled. + entity_registry_enabled_default=False, + is_on_fn=partial(_mil_trouble, feature=feature), + ) + for feature in features + if feature in MIL_TRANSLATION_KEYS + ] + + +def _has_data( + description: SubaruBinarySensorEntityDescription, vehicle_status: dict[str, Any] +) -> bool: + """Whether a door/window/lock description should be created. + + Doors report even on an empty vehicle_status (a failed fetch), so + they're excluded only when explicitly NOT_EQUIPPED. Windows/locks/EV + fields are omitted entirely when unsupported, so presence decides. + """ + if description.key in DOOR_POSITION_KEYS: + return vehicle_status.get(description.key, "").upper() != "NOT_EQUIPPED" + return description.key in vehicle_status + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: SubaruConfigEntry, + async_add_entities: AddConfigEntryEntitiesCallback, +) -> None: + """Set up the Subaru binary sensors by config_entry.""" + coordinator = config_entry.runtime_data.coordinator + vehicle_info = config_entry.runtime_data.vehicles + + entities: list[SubaruBinarySensor] = [] + for info in vehicle_info.values(): + # Doors/windows/locks/health are only reported on Gen2+ vehicles. + if info[VEHICLE_API_GEN] not in GEN_2_AND_NEWER: + continue + vehicle_data = (coordinator.data or {}).get(info[VEHICLE_VIN]) or {} + vehicle_status = vehicle_data.get(VEHICLE_STATUS) or {} + + descriptions: list[SubaruBinarySensorEntityDescription] = [ + description + for description in BINARY_SENSORS + if _has_data(description, vehicle_status) + ] + descriptions.append(OVERALL_HEALTH_BINARY_SENSOR) + if info[VEHICLE_HAS_EV]: + if EV_PLUG_BINARY_SENSOR.key in vehicle_status: + descriptions.append(EV_PLUG_BINARY_SENSOR) + if EV_CHARGING_BINARY_SENSOR.key in vehicle_status: + descriptions.append(EV_CHARGING_BINARY_SENSOR) + + features = vehicle_data.get(VEHICLE_FEATURES) or [] + descriptions.extend(_build_mil_descriptions(features)) + + entities.extend( + SubaruBinarySensor(info, coordinator, description) + for description in descriptions + ) + async_add_entities(entities) + + +class SubaruBinarySensor(SubaruCoordinatorEntity, BinarySensorEntity): + """Representation of a Subaru binary sensor.""" + + entity_description: SubaruBinarySensorEntityDescription + + def __init__( + self, + vehicle_info: dict[str, Any], + coordinator: SubaruDataUpdateCoordinator, + description: SubaruBinarySensorEntityDescription, + ) -> None: + """Initialize the binary sensor.""" + super().__init__(vehicle_info, coordinator, description.key) + self.entity_description = description + + @property + @override + def is_on(self) -> bool | None: + """Return True if the sensor is on (open / unlocked / has trouble).""" + return self.entity_description.is_on_fn(self.coordinator.data[self.vin]) diff --git a/homeassistant/components/subaru/const.py b/homeassistant/components/subaru/const.py index d1a1662d64747..6f937ff6b3c63 100644 --- a/homeassistant/components/subaru/const.py +++ b/homeassistant/components/subaru/const.py @@ -25,6 +25,7 @@ VEHICLE_LAST_UPDATE = "last_update" VEHICLE_STATUS = "vehicle_status" VEHICLE_HEALTH = "vehicle_health" +VEHICLE_FEATURES = "vehicle_features" # Synthetic keys for sensors that don't read a single field directly; used # as both unique_id suffix and translation_key, so they must stay stable @@ -38,9 +39,13 @@ API_GEN_2 = "g2" API_GEN_3 = "g3" API_GEN_4 = "g4" +# Generations that report vehicle_status/vehicle_health data, used to gate +# binary_sensor entity creation. +GEN_2_AND_NEWER = (API_GEN_2, API_GEN_3, API_GEN_4) MANUFACTURER = "Subaru" PLATFORMS = [ + Platform.BINARY_SENSOR, Platform.BUTTON, Platform.DEVICE_TRACKER, Platform.LOCK, diff --git a/homeassistant/components/subaru/strings.json b/homeassistant/components/subaru/strings.json index fd8209f150ac6..fdf4ff4dc54c0 100644 --- a/homeassistant/components/subaru/strings.json +++ b/homeassistant/components/subaru/strings.json @@ -47,6 +47,122 @@ } }, "entity": { + "binary_sensor": { + "door_boot": { + "name": "Tailgate" + }, + "door_engine_hood": { + "name": "Hood" + }, + "door_front_left": { + "name": "Door front left" + }, + "door_front_right": { + "name": "Door front right" + }, + "door_rear_left": { + "name": "Door rear left" + }, + "door_rear_right": { + "name": "Door rear right" + }, + "ev_is_plugged_in": { + "name": "EV plug" + }, + "health_istrouble": { + "name": "Vehicle health" + }, + "is_charging": { + "name": "Charging" + }, + "lock_status_boot": { + "name": "Lock status tailgate" + }, + "lock_status_front_left": { + "name": "Lock status front left" + }, + "lock_status_front_right": { + "name": "Lock status front right" + }, + "lock_status_rear_left": { + "name": "Lock status rear left" + }, + "lock_status_rear_right": { + "name": "Lock status rear right" + }, + "mil_abs": { + "name": "ABS warning" + }, + "mil_atf": { + "name": "Transmission temperature warning" + }, + "mil_awd": { + "name": "AWD warning" + }, + "mil_bsdrct": { + "name": "Blind spot and rear cross traffic warning" + }, + "mil_cel": { + "name": "Check engine" + }, + "mil_ebd": { + "name": "Electronic brake force distribution warning" + }, + "mil_eol": { + "name": "Engine oil level warning" + }, + "mil_epas": { + "name": "Electric power steering warning" + }, + "mil_epb": { + "name": "Electric parking brake warning" + }, + "mil_ess": { + "name": "EyeSight warning" + }, + "mil_iss": { + "name": "Idle stop & start warning" + }, + "mil_opl": { + "name": "Oil pressure warning" + }, + "mil_rab": { + "name": "Reverse automatic braking warning" + }, + "mil_srh": { + "name": "Steering responsive headlights warning" + }, + "mil_srs": { + "name": "Airbag warning" + }, + "mil_tel": { + "name": "Telematics warning" + }, + "mil_tpms": { + "name": "Tire pressure warning" + }, + "mil_vdc": { + "name": "Vehicle dynamics control warning" + }, + "mil_wash": { + "name": "Washer fluid warning" + }, + "window_front_left": { + "name": "Window front left" + }, + "window_front_right": { + "name": "Window front right" + }, + "window_rear_left": { + "name": "Window rear left" + }, + "window_rear_right": { + "name": "Window rear right" + }, + "window_sunroof": { + "name": "Sunroof" + } + }, "button": { "remote_start": { "name": "Remote start" diff --git a/homeassistant/components/vicare/__init__.py b/homeassistant/components/vicare/__init__.py index 2df6790ae1c8d..59e657da06ea2 100644 --- a/homeassistant/components/vicare/__init__.py +++ b/homeassistant/components/vicare/__init__.py @@ -173,11 +173,43 @@ async def async_setup_entry(hass: HomeAssistant, entry: ViCareConfigEntry) -> bo # Migration can be removed in 2025.4.0 await async_migrate_devices_and_entities(hass, entry, device) + await _async_register_zigbee_gateway_devices(hass, entry) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True +async def _async_register_zigbee_gateway_devices( + hass: HomeAssistant, entry: ViCareConfigEntry +) -> None: + """Register zigbee gateway devices before their sub-devices are added. + + Zigbee sub-devices link to their gateway through via_device_id, which + requires the gateway device to already exist when the sub-device is added, + regardless of platform setup order. The gateway's remaining attributes are + filled in by its own entity. Sub-devices whose gateway is absent stay + unlinked, matching the previous via_device behavior. + """ + device_registry = dr.async_get(hass) + for device in entry.runtime_data.devices: + device_serial = device.serial + # A gateway (main) device has a two-part zigbee serial; its channels add + # a third part and link back to it. + if ( + device_serial is None + or not device_serial.startswith("zigbee-") + or len(device_serial.split("-", 2)) != 2 + ): + continue + gateway_serial = device.config.getConfig().serial + identifier = f"{gateway_serial}_{device_serial.replace('-', '_')}" + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, identifier)}, + ) + + def _remove_token_file(token_path: str) -> None: """Remove legacy token file if it exists.""" with suppress(FileNotFoundError): @@ -215,11 +247,14 @@ def _setup_vicare_api( str(device.isOnline()), ) - devices = [ - ViCareDevice(config=device_config, api=device_config.asAutoDetectDevice()) - for device_config in device_config_list - if bool(device_config.isOnline()) - ] + devices = [] + for device_config in device_config_list: + if not bool(device_config.isOnline()): + continue + api = device_config.asAutoDetectDevice() + devices.append( + ViCareDevice(config=device_config, api=api, serial=get_device_serial(api)) + ) return ViCareData(client=client, devices=devices) @@ -237,9 +272,7 @@ async def async_migrate_devices_and_entities( gateway_serial: str = device.config.getConfig().serial device_id = device.config.getId() - device_serial: str | None = await hass.async_add_executor_job( - get_device_serial, device.api - ) + device_serial: str | None = device.serial device_model = device.config.getModel() old_identifier = gateway_serial diff --git a/homeassistant/components/vicare/binary_sensor.py b/homeassistant/components/vicare/binary_sensor.py index 836a725196dea..cad5883e9d3e6 100644 --- a/homeassistant/components/vicare/binary_sensor.py +++ b/homeassistant/components/vicare/binary_sensor.py @@ -24,13 +24,7 @@ from .entity import ViCareEntity from .types import ViCareConfigEntry, ViCareDevice, ViCareRequiredKeysMixin -from .utils import ( - get_burners, - get_circuits, - get_compressors, - get_device_serial, - is_supported, -) +from .utils import get_burners, get_circuits, get_compressors, is_supported _LOGGER = logging.getLogger(__name__) @@ -157,7 +151,7 @@ def _build_entities( entities.extend( ViCareBinarySensor( description, - get_device_serial(device.api), + device.serial, device.config, device.api, ) @@ -173,7 +167,7 @@ def _build_entities( entities.extend( ViCareBinarySensor( description, - get_device_serial(device.api), + device.serial, device.config, device.api, component, diff --git a/homeassistant/components/vicare/button.py b/homeassistant/components/vicare/button.py index 39ef66395afde..00e8291a510bf 100644 --- a/homeassistant/components/vicare/button.py +++ b/homeassistant/components/vicare/button.py @@ -16,7 +16,7 @@ from .entity import ViCareEntity from .types import ViCareConfigEntry, ViCareDevice, ViCareRequiredKeysMixinWithSet -from .utils import get_device_serial, is_supported +from .utils import is_supported _LOGGER = logging.getLogger(__name__) @@ -54,7 +54,7 @@ def _build_entities( return [ ViCareButton( description, - get_device_serial(device.api), + device.serial, device.config, device.api, ) diff --git a/homeassistant/components/vicare/climate.py b/homeassistant/components/vicare/climate.py index 99ab2235bf96b..8cb0efbecd4a2 100644 --- a/homeassistant/components/vicare/climate.py +++ b/homeassistant/components/vicare/climate.py @@ -33,7 +33,7 @@ from .const import DOMAIN from .entity import ViCareEntity from .types import HeatingProgram, ViCareConfigEntry, ViCareDevice -from .utils import get_burners, get_circuits, get_compressors, get_device_serial +from .utils import get_burners, get_circuits, get_compressors _LOGGER = logging.getLogger(__name__) @@ -82,7 +82,7 @@ def _build_entities( """Create ViCare climate entities for a device.""" return [ ViCareClimate( - get_device_serial(device.api), + device.serial, device.config, device.api, circuit, diff --git a/homeassistant/components/vicare/entity.py b/homeassistant/components/vicare/entity.py index 4502e12ff864e..ef1e02c9c1ba1 100644 --- a/homeassistant/components/vicare/entity.py +++ b/homeassistant/components/vicare/entity.py @@ -3,6 +3,7 @@ from collections.abc import Generator from contextlib import contextmanager import logging +from typing import override from PyViCare.PyViCareDevice import Device as PyViCareDevice from PyViCare.PyViCareDeviceConfig import PyViCareDeviceConfig @@ -17,6 +18,7 @@ ) from requests.exceptions import ConnectionError as RequestConnectionError +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity import Entity @@ -72,28 +74,47 @@ def __init__( if component: self._attr_unique_id += f"-{component.id}" - self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, identifier)}, - name=model, + self._gateway_serial = gateway_serial + self._device_serial = device_serial + self._device_identifier = identifier + self._model = model + + @property + @override + def device_info(self) -> DeviceInfo: + """Return device info, resolving the zigbee gateway link at add time.""" + device_info = DeviceInfo( + identifiers={(DOMAIN, self._device_identifier)}, + name=self._model, manufacturer="Viessmann", - model=model, + model=self._model, configuration_url=VIESSMANN_DEVELOPER_PORTAL, ) + device_serial = self._device_serial if device_serial and device_serial.startswith("zigbee-"): parts = device_serial.split("-", 2) if len(parts) == 3: _, zigbee_ieee, _ = parts - self._attr_device_info["via_device"] = ( - DOMAIN, - f"{gateway_serial}_zigbee_{zigbee_ieee}", + config_entry = self.platform.config_entry + assert config_entry is not None + # Link best effort: the gateway may be absent (its main device + # was omitted or its serial could not be retrieved), in which + # case the channel stays unlinked rather than aborting setup. + gateway_device = dr.async_get(self.hass).async_get_device_by_identifier( + (DOMAIN, f"{self._gateway_serial}_zigbee_{zigbee_ieee}"), + config_entry.entry_id, ) + if gateway_device is not None: + device_info["via_device_id"] = gateway_device.id elif ( len(parts) == 2 and len(zigbee_ieee := device_serial.removeprefix("zigbee-")) == 16 ): - self._attr_device_info["serial_number"] = "-".join( + device_info["serial_number"] = "-".join( zigbee_ieee.upper()[i : i + 2] for i in range(0, 16, 2) ) else: - self._attr_device_info["serial_number"] = device_serial + device_info["serial_number"] = device_serial + + return device_info diff --git a/homeassistant/components/vicare/fan.py b/homeassistant/components/vicare/fan.py index fa926537dfaef..ae1da16457fb5 100644 --- a/homeassistant/components/vicare/fan.py +++ b/homeassistant/components/vicare/fan.py @@ -19,7 +19,7 @@ from .entity import ViCareEntity from .types import ViCareConfigEntry, ViCareDevice -from .utils import filter_state, get_device_serial, is_supported +from .utils import filter_state, is_supported _LOGGER = logging.getLogger(__name__) @@ -95,7 +95,7 @@ def _build_entities( ) -> list[ViCareFan]: """Create ViCare climate entities for a device.""" return [ - ViCareFan(get_device_serial(device.api), device.config, device.api) + ViCareFan(device.serial, device.config, device.api) for device in device_list if device.api.isVentilationDevice() ] diff --git a/homeassistant/components/vicare/number.py b/homeassistant/components/vicare/number.py index 0c3f45459ca61..a6d78ff83614d 100644 --- a/homeassistant/components/vicare/number.py +++ b/homeassistant/components/vicare/number.py @@ -30,7 +30,7 @@ ViCareDevice, ViCareRequiredKeysMixin, ) -from .utils import get_circuits, get_device_serial, is_supported +from .utils import get_circuits, is_supported _LOGGER = logging.getLogger(__name__) @@ -359,7 +359,7 @@ def _build_entities( entities.extend( ViCareNumber( description, - get_device_serial(device.api), + device.serial, device.config, device.api, ) @@ -370,7 +370,7 @@ def _build_entities( entities.extend( ViCareNumber( description, - get_device_serial(device.api), + device.serial, device.config, device.api, circuit, diff --git a/homeassistant/components/vicare/select.py b/homeassistant/components/vicare/select.py index b3f27e7338f89..65d9116f36a4c 100644 --- a/homeassistant/components/vicare/select.py +++ b/homeassistant/components/vicare/select.py @@ -20,7 +20,7 @@ from .entity import ViCareEntity from .types import ViCareConfigEntry, ViCareDevice -from .utils import get_device_serial, is_supported +from .utils import is_supported _LOGGER = logging.getLogger(__name__) @@ -39,7 +39,7 @@ def _build_entities( """Create ViCare select entities for a device.""" return [ ViCareDHWOperatingModeSelect( - get_device_serial(device.api), + device.serial, device.config, device.api, ) diff --git a/homeassistant/components/vicare/sensor.py b/homeassistant/components/vicare/sensor.py index 1706ce0a0e04b..4da5b1ce7cd33 100644 --- a/homeassistant/components/vicare/sensor.py +++ b/homeassistant/components/vicare/sensor.py @@ -57,7 +57,6 @@ get_circuits, get_compressors, get_condensers, - get_device_serial, get_evaporators, get_inverters, is_supported, @@ -1545,7 +1544,7 @@ def _build_entities( entities.extend( ViCareSensor( description, - get_device_serial(device.api), + device.serial, device.config, device.api, ) @@ -1557,7 +1556,7 @@ def _build_entities( entities.extend( ViCareSensor( description, - get_device_serial(device.api), + device.serial, device.config, device.api, ) @@ -1576,7 +1575,7 @@ def _build_entities( entities.extend( ViCareSensor( description, - get_device_serial(device.api), + device.serial, device.config, device.api, component, diff --git a/homeassistant/components/vicare/types.py b/homeassistant/components/vicare/types.py index 65ae2a53c3e6c..b5daee7d35fb8 100644 --- a/homeassistant/components/vicare/types.py +++ b/homeassistant/components/vicare/types.py @@ -80,6 +80,7 @@ class ViCareDevice: config: PyViCareDeviceConfig api: PyViCareDevice + serial: str | None @dataclass(frozen=True) diff --git a/homeassistant/components/vicare/water_heater.py b/homeassistant/components/vicare/water_heater.py index 2bfd6b2382ad7..ac64e97cf8ab7 100644 --- a/homeassistant/components/vicare/water_heater.py +++ b/homeassistant/components/vicare/water_heater.py @@ -19,7 +19,7 @@ from .entity import ViCareEntity from .types import ViCareConfigEntry, ViCareDevice -from .utils import get_circuits, get_device_serial +from .utils import get_circuits _LOGGER = logging.getLogger(__name__) @@ -60,7 +60,7 @@ def _build_entities( return [ ViCareWater( - get_device_serial(device.api), + device.serial, device.config, device.api, circuit, diff --git a/homeassistant/components/webostv/manifest.json b/homeassistant/components/webostv/manifest.json index 56be93a8e443b..45c5b3375756d 100644 --- a/homeassistant/components/webostv/manifest.json +++ b/homeassistant/components/webostv/manifest.json @@ -8,7 +8,7 @@ "iot_class": "local_push", "loggers": ["aiowebostv"], "quality_scale": "platinum", - "requirements": ["aiowebostv==0.9.0"], + "requirements": ["aiowebostv==0.9.1"], "ssdp": [ { "st": "urn:lge-com:service:webos-second-screen:1" diff --git a/homeassistant/components/zhong_hong/manifest.json b/homeassistant/components/zhong_hong/manifest.json index 3569466fb0a71..8247db910e5ec 100644 --- a/homeassistant/components/zhong_hong/manifest.json +++ b/homeassistant/components/zhong_hong/manifest.json @@ -6,5 +6,5 @@ "iot_class": "local_push", "loggers": ["zhong_hong_hvac"], "quality_scale": "legacy", - "requirements": ["zhong-hong-hvac==1.0.13"] + "requirements": ["zhong-hong-hvac==1.0.16"] } diff --git a/requirements_all.txt b/requirements_all.txt index d36e3eb34fa6f..9020c77cd6d7a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -474,7 +474,7 @@ aiowatttime==0.1.1 aiowebdav2==0.6.2 # homeassistant.components.webostv -aiowebostv==0.9.0 +aiowebostv==0.9.1 # homeassistant.components.withings aiowithings==3.1.6 @@ -3479,7 +3479,7 @@ zha-quirks==2.2.0 zha==2.1.0 # homeassistant.components.zhong_hong -zhong-hong-hvac==1.0.13 +zhong-hong-hvac==1.0.16 # homeassistant.components.ziggo_mediabox_xl ziggo-mediabox-xl==1.1.0 diff --git a/tests/components/alexa_devices/test_services.py b/tests/components/alexa_devices/test_services.py index 84829cc87b518..cf69e6eb073a1 100644 --- a/tests/components/alexa_devices/test_services.py +++ b/tests/components/alexa_devices/test_services.py @@ -333,10 +333,7 @@ async def test_missing_config_entry( other_entry = MockConfigEntry(domain="other_domain", data={}) other_entry.add_to_hass(hass) device_registry.async_update_device( - device_entry.id, add_config_entry_id=other_entry.entry_id - ) - device_registry.async_update_device( - device_entry.id, remove_config_entry_id=mock_config_entry.entry_id + device_entry.id, new_config_entry_id=other_entry.entry_id ) # Call Service diff --git a/tests/components/blebox/test_button.py b/tests/components/blebox/test_button.py index 1ec63623141bc..1a18de458b167 100644 --- a/tests/components/blebox/test_button.py +++ b/tests/components/blebox/test_button.py @@ -1,7 +1,7 @@ """Blebox button entities tests.""" import logging -from unittest.mock import PropertyMock +from unittest.mock import Mock, PropertyMock import blebox_uniapi import pytest @@ -56,6 +56,44 @@ async def test_tvliftbox_init( assert state.name == "My tvLiftBox" +@pytest.fixture(name="gatebox_second_output") +def gatebox_second_output_fixture(caplog: pytest.LogCaptureFixture): + """Return a gateBox second output button entity mock.""" + caplog.set_level(logging.ERROR) + + feature = mock_feature( + "buttons", + blebox_uniapi.button.Button, + unique_id="BleBox-gateBox-1afe34d27e4f-second_output", + full_name="gateBox-second_output", + query_string="second_output", + ) + + product = feature.product + type(product).name = PropertyMock(return_value="My gateBox") + type(product).type = PropertyMock(return_value="gateBox") + type(product).model = PropertyMock(return_value="gateBox") + + return (feature, "button.my_gatebox_second_output") + + +async def test_gatebox_second_output_init( + hass: HomeAssistant, + gatebox_second_output: tuple[Mock, str], + caplog: pytest.LogCaptureFixture, +) -> None: + """Test gateBox second output button initialisation.""" + caplog.set_level(logging.ERROR) + + _, entity_id = gatebox_second_output + entry = await async_setup_entity(hass, entity_id) + state = hass.states.get(entity_id) + + assert entry.unique_id == "BleBox-gateBox-1afe34d27e4f-second_output" + assert entry.translation_key == "second_output" + assert state.name == "My gateBox Second output" + + @pytest.mark.parametrize( ("query_string", "expected_translation_key", "expected_entity_id", "expected_name"), query_translation_key_matching, diff --git a/tests/components/derivative/test_init.py b/tests/components/derivative/test_init.py index 0208c1e9dce16..b852340f48c49 100644 --- a/tests/components/derivative/test_init.py +++ b/tests/components/derivative/test_init.py @@ -105,15 +105,12 @@ async def test_async_handle_source_entity_changes_source_entity_removed( events = track_entity_registry_actions(hass, derivative_entity_entry.entity_id) - # Remove the source sensor's config entry from the device, this removes the - # source sensor + # Remove the source device, this removes the source sensor with patch( "homeassistant.components.derivative.async_unload_entry", wraps=derivative.async_unload_entry, ) as mock_unload_entry: - device_registry.async_update_device( - sensor_device.id, remove_config_entry_id=sensor_config_entry.entry_id - ) + device_registry.async_remove_device(sensor_device.id) await hass.async_block_till_done() await hass.async_block_till_done() mock_unload_entry.assert_not_called() diff --git a/tests/components/freebox/test_camera.py b/tests/components/freebox/test_camera.py index f81845712592d..fc72b8ce5db94 100644 --- a/tests/components/freebox/test_camera.py +++ b/tests/components/freebox/test_camera.py @@ -42,10 +42,12 @@ async def test_label_change_propagates( router: Mock, ) -> None: """Test camera label changes from the API update the device registry.""" - await setup_platform(hass, CAMERA_DOMAIN) + mock_entry = await setup_platform(hass, CAMERA_DOMAIN) camera_node_id = 15 # Caméra I from fixture - device = device_registry.async_get_device(identifiers={(DOMAIN, camera_node_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, camera_node_id), mock_entry.entry_id + ) assert device is not None assert device.name == "Caméra I" @@ -60,7 +62,9 @@ async def test_label_change_propagates( async_fire_time_changed(hass) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, camera_node_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, camera_node_id), mock_entry.entry_id + ) assert device is not None assert device.name == "Caméra entrée" diff --git a/tests/components/freebox/test_init.py b/tests/components/freebox/test_init.py index 60d5cf1f1b439..05bf5da901645 100644 --- a/tests/components/freebox/test_init.py +++ b/tests/components/freebox/test_init.py @@ -197,10 +197,12 @@ async def test_home_device_label_sync( router: Mock, ) -> None: """Test home device label changes propagate to the device registry.""" - await setup_platform(hass, BINARY_SENSOR_DOMAIN) + entry = await setup_platform(hass, BINARY_SENSOR_DOMAIN) pir_node_id = 26 # Détecteur from fixture - device = device_registry.async_get_device(identifiers={(DOMAIN, pir_node_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, pir_node_id), entry.entry_id + ) assert device is not None assert device.name == "Détecteur" @@ -216,6 +218,8 @@ async def test_home_device_label_sync( async_fire_time_changed(hass) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, pir_node_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, pir_node_id), entry.entry_id + ) assert device is not None assert device.name == "Détecteur cuisine" diff --git a/tests/components/freedompro/test_binary_sensor.py b/tests/components/freedompro/test_binary_sensor.py index f3bfeb68c6cb3..125f9ada899aa 100644 --- a/tests/components/freedompro/test_binary_sensor.py +++ b/tests/components/freedompro/test_binary_sensor.py @@ -56,7 +56,9 @@ async def test_binary_sensor_get_state( ) -> None: """Test states of the binary_sensor.""" - device = device_registry.async_get_device(identifiers={("freedompro", uid)}) + device = device_registry.async_get_device_by_identifier( + ("freedompro", uid), init_integration.entry_id + ) assert device is not None assert device.identifiers == {("freedompro", uid)} assert device.manufacturer == "Freedompro" diff --git a/tests/components/freedompro/test_climate.py b/tests/components/freedompro/test_climate.py index 39aa5a49f9fb1..616423194d03a 100644 --- a/tests/components/freedompro/test_climate.py +++ b/tests/components/freedompro/test_climate.py @@ -40,7 +40,9 @@ async def test_climate_get_state( init_integration: MockConfigEntry, ) -> None: """Test states of the climate.""" - device = device_registry.async_get_device(identifiers={("freedompro", uid)}) + device = device_registry.async_get_device_by_identifier( + ("freedompro", uid), init_integration.entry_id + ) assert device is not None assert device.identifiers == {("freedompro", uid)} assert device.manufacturer == "Freedompro" diff --git a/tests/components/freedompro/test_cover.py b/tests/components/freedompro/test_cover.py index bcba1e0b9171f..01655060ec126 100644 --- a/tests/components/freedompro/test_cover.py +++ b/tests/components/freedompro/test_cover.py @@ -49,7 +49,9 @@ async def test_cover_get_state( ) -> None: """Test states of the cover.""" - device = device_registry.async_get_device(identifiers={("freedompro", uid)}) + device = device_registry.async_get_device_by_identifier( + ("freedompro", uid), init_integration.entry_id + ) assert device is not None assert device.identifiers == {("freedompro", uid)} assert device.manufacturer == "Freedompro" diff --git a/tests/components/freedompro/test_fan.py b/tests/components/freedompro/test_fan.py index 6593cf3e52d58..6020363e4c8c0 100644 --- a/tests/components/freedompro/test_fan.py +++ b/tests/components/freedompro/test_fan.py @@ -33,7 +33,9 @@ async def test_fan_get_state( ) -> None: """Test states of the fan.""" - device = device_registry.async_get_device(identifiers={("freedompro", uid)}) + device = device_registry.async_get_device_by_identifier( + ("freedompro", uid), init_integration.entry_id + ) assert device is not None assert device.identifiers == {("freedompro", uid)} assert device.manufacturer == "Freedompro" diff --git a/tests/components/freedompro/test_lock.py b/tests/components/freedompro/test_lock.py index e0252f8f9ff1a..3f306c632b86a 100644 --- a/tests/components/freedompro/test_lock.py +++ b/tests/components/freedompro/test_lock.py @@ -33,7 +33,9 @@ async def test_lock_get_state( ) -> None: """Test states of the lock.""" - device = device_registry.async_get_device(identifiers={("freedompro", uid)}) + device = device_registry.async_get_device_by_identifier( + ("freedompro", uid), init_integration.entry_id + ) assert device is not None assert device.identifiers == {("freedompro", uid)} assert device.manufacturer == "Freedompro" diff --git a/tests/components/freshr/test_init.py b/tests/components/freshr/test_init.py index 629abdc4e5552..9c1a8dfa31ba9 100644 --- a/tests/components/freshr/test_init.py +++ b/tests/components/freshr/test_init.py @@ -84,14 +84,21 @@ async def test_stale_device_removed( freezer: FrozenDateTimeFactory, ) -> None: """Test that a device absent from a successful poll is removed from the registry.""" - assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, DEVICE_ID), mock_config_entry.entry_id + ) mock_freshr_client.fetch_devices.return_value = [] freezer.tick(DEVICES_SCAN_INTERVAL) async_fire_time_changed(hass) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) is None + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, DEVICE_ID), mock_config_entry.entry_id + ) + is None + ) call_count = mock_freshr_client.fetch_device_current.call_count freezer.tick(READINGS_SCAN_INTERVAL) @@ -109,14 +116,18 @@ async def test_stale_device_not_removed_on_poll_error( freezer: FrozenDateTimeFactory, ) -> None: """Test that a device is not removed when the devices poll fails.""" - assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, DEVICE_ID), hass.config_entries.async_entries(DOMAIN)[0].entry_id + ) mock_freshr_client.fetch_devices.side_effect = ApiResponseError("cloud error") freezer.tick(DEVICES_SCAN_INTERVAL) async_fire_time_changed(hass) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, DEVICE_ID), hass.config_entries.async_entries(DOMAIN)[0].entry_id + ) @pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration") diff --git a/tests/components/freshr/test_sensor.py b/tests/components/freshr/test_sensor.py index 6e33c90753a77..9f78b442afebd 100644 --- a/tests/components/freshr/test_sensor.py +++ b/tests/components/freshr/test_sensor.py @@ -33,7 +33,9 @@ async def test_entities( """Test the sensor entities.""" await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, DEVICE_ID), mock_config_entry.entry_id + ) assert device_entry entity_entries = er.async_entries_for_config_entry( entity_registry, mock_config_entry.entry_id @@ -100,7 +102,9 @@ async def test_device_reappears_after_removal( freezer: FrozenDateTimeFactory, ) -> None: """Test that entities are re-created when a previously removed device reappears.""" - assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, DEVICE_ID), mock_config_entry.entry_id + ) # Device disappears from the account mock_freshr_client.fetch_devices.return_value = [] @@ -108,7 +112,12 @@ async def test_device_reappears_after_removal( async_fire_time_changed(hass) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) is None + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, DEVICE_ID), mock_config_entry.entry_id + ) + is None + ) # Device reappears mock_freshr_client.fetch_devices.return_value = [DeviceSummary(id=DEVICE_ID)] @@ -117,7 +126,9 @@ async def test_device_reappears_after_removal( async_fire_time_changed(hass) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, DEVICE_ID), mock_config_entry.entry_id + ) t1_entity_id = entity_registry.async_get_entity_id( "sensor", DOMAIN, f"{DEVICE_ID}_t1" ) @@ -135,7 +146,12 @@ async def test_dynamic_device_added( freezer: FrozenDateTimeFactory, ) -> None: """Test that sensors are created for a device that appears after initial setup.""" - assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID_2)}) is None + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, DEVICE_ID_2), mock_config_entry.entry_id + ) + is None + ) mock_freshr_client.fetch_devices.return_value = [ DeviceSummary(id=DEVICE_ID), @@ -146,7 +162,9 @@ async def test_dynamic_device_added( async_fire_time_changed(hass) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, DEVICE_ID_2)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, DEVICE_ID_2), mock_config_entry.entry_id + ) t1_entity_id = entity_registry.async_get_entity_id( "sensor", DOMAIN, f"{DEVICE_ID_2}_t1" ) diff --git a/tests/components/fritz/test_button.py b/tests/components/fritz/test_button.py index 370ede3a082e9..d140571fe3eb2 100644 --- a/tests/components/fritz/test_button.py +++ b/tests/components/fritz/test_button.py @@ -226,8 +226,8 @@ async def test_cleanup_button( assert entry.state is ConfigEntryState.LOADED # check if tracked device is registered properly - device = device_registry.async_get_device( - connections={("mac", "aa:bb:cc:00:11:22")} + device = device_registry.async_get_device_by_connection( + ("mac", "aa:bb:cc:00:11:22"), entry.entry_id ) assert device @@ -254,8 +254,8 @@ async def test_cleanup_button( await hass.async_block_till_done(wait_background_tasks=True) # check if orphan tracked device is removed - device = device_registry.async_get_device( - connections={("mac", "aa:bb:cc:00:11:22")} + device = device_registry.async_get_device_by_connection( + ("mac", "aa:bb:cc:00:11:22"), entry.entry_id ) assert not device diff --git a/tests/components/fritz/test_coordinator.py b/tests/components/fritz/test_coordinator.py index 5f362b0a5d892..9a12c4f1f21ed 100644 --- a/tests/components/fritz/test_coordinator.py +++ b/tests/components/fritz/test_coordinator.py @@ -205,8 +205,8 @@ async def test_no_software_version( assert entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + device = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert device assert device.sw_version == "string_version_not_number" @@ -616,8 +616,8 @@ async def test_async_trigger_cleanup( assert entry.state is ConfigEntryState.LOADED # Verify the printer is registered as tracked device - assert device_registry.async_get_device( - connections={(dr.CONNECTION_NETWORK_MAC, "aa:bb:cc:00:11:22")} + assert device_registry.async_get_device_by_connection( + (dr.CONNECTION_NETWORK_MAC, "aa:bb:cc:00:11:22"), entry.entry_id ) assert entity_registry.async_get("device_tracker.printer") assert entity_registry.async_get("switch.printer_internet_access") @@ -631,8 +631,8 @@ async def test_async_trigger_cleanup( # Verify the printer was removed from tracked devices assert ( - device_registry.async_get_device( - connections={(dr.CONNECTION_NETWORK_MAC, "aa:bb:cc:00:11:22")} + device_registry.async_get_device_by_connection( + (dr.CONNECTION_NETWORK_MAC, "aa:bb:cc:00:11:22"), entry.entry_id ) is None ) @@ -650,8 +650,8 @@ async def test_async_trigger_cleanup( await hass.async_block_till_done(wait_background_tasks=True) # Verify the printer is registered again as tracked device - assert device_registry.async_get_device( - connections={(dr.CONNECTION_NETWORK_MAC, "aa:bb:cc:00:11:22")} + assert device_registry.async_get_device_by_connection( + (dr.CONNECTION_NETWORK_MAC, "aa:bb:cc:00:11:22"), entry.entry_id ) assert entity_registry.async_get("device_tracker.printer") assert entity_registry.async_get("switch.printer_internet_access") @@ -674,8 +674,8 @@ async def test_async_trigger_cleanup_preserves_fritz_device( wrapper: AvmWrapper = entry.runtime_data # Verify the fritz box device was registered - fritz_device = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + fritz_device = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert fritz_device is not None @@ -691,8 +691,8 @@ async def test_async_trigger_cleanup_preserves_fritz_device( await wrapper.async_trigger_cleanup() # The fritz box device must still be present in the registry - fritz_device_after = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + fritz_device_after = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert fritz_device_after is not None assert fritz_device_after.id == fritz_device.id diff --git a/tests/components/fritz/test_services.py b/tests/components/fritz/test_services.py index 9e9250ad8cebe..3c6a6b5d8ec95 100644 --- a/tests/components/fritz/test_services.py +++ b/tests/components/fritz/test_services.py @@ -49,8 +49,8 @@ async def test_service_set_guest_wifi_password( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + device = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert device with patch( @@ -77,8 +77,8 @@ async def test_service_set_guest_wifi_password_unknown_parameter( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + device = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert device @@ -108,8 +108,8 @@ async def test_service_set_guest_wifi_password_service_not_supported( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + device = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert device @@ -161,8 +161,8 @@ async def test_service_dial( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + device = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert device with patch( @@ -193,8 +193,8 @@ async def test_service_dial_unknown_parameter( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + device = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert device @@ -226,8 +226,8 @@ async def test_service_dial_wrong_parameter( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + device = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert device @@ -276,8 +276,8 @@ async def test_service_dial_service_not_supported( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + device = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert device @@ -309,8 +309,8 @@ async def test_service_dial_failed( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device( - identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)} + device = device_registry.async_get_device_by_identifier( + (DOMAIN, MOCK_SERIAL_NUMBER), entry.entry_id ) assert device diff --git a/tests/components/fronius/test_init.py b/tests/components/fronius/test_init.py index b4255aef6acee..b31669959ad70 100644 --- a/tests/components/fronius/test_init.py +++ b/tests/components/fronius/test_init.py @@ -84,14 +84,18 @@ async def test_inverter_night_rescan( await hass.async_block_till_done() # We expect our inverter to be present now - inverter_1 = device_registry.async_get_device(identifiers={(DOMAIN, "203200")}) + inverter_1 = device_registry.async_get_device_by_identifier( + (DOMAIN, "203200"), config_entry.entry_id + ) assert inverter_1.manufacturer == "Fronius" # After another re-scan we still only expect this inverter freezer.tick(timedelta(minutes=SOLAR_NET_RESCAN_TIMER)) async_fire_time_changed(hass) await hass.async_block_till_done() - inverter_1 = device_registry.async_get_device(identifiers={(DOMAIN, "203200")}) + inverter_1 = device_registry.async_get_device_by_identifier( + (DOMAIN, "203200"), config_entry.entry_id + ) assert inverter_1.manufacturer == "Fronius" @@ -156,9 +160,13 @@ async def test_device_remove_devices( hass, is_logger=False, unique_id="12345678" ) - inverter_1 = device_registry.async_get_device(identifiers={(DOMAIN, "12345678")}) + inverter_1 = device_registry.async_get_device_by_identifier( + (DOMAIN, "12345678"), config_entry.entry_id + ) client = await hass_ws_client(hass) response = await client.remove_device(inverter_1.id, config_entry.entry_id) assert response["success"] - assert not device_registry.async_get_device(identifiers={(DOMAIN, "12345678")}) + assert not device_registry.async_get_device_by_identifier( + (DOMAIN, "12345678"), config_entry.entry_id + ) diff --git a/tests/components/fronius/test_sensor.py b/tests/components/fronius/test_sensor.py index 0d226de76c97e..1e97b45309e11 100644 --- a/tests/components/fronius/test_sensor.py +++ b/tests/components/fronius/test_sensor.py @@ -322,31 +322,37 @@ def assert_state(entity_id, expected_state): await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id) # Devices - solar_net = device_registry.async_get_device( - identifiers={(DOMAIN, "solar_net_12345678")} + solar_net = device_registry.async_get_device_by_identifier( + (DOMAIN, "solar_net_12345678"), config_entry.entry_id ) assert solar_net.configuration_url == "http://fronius" assert solar_net.manufacturer == "Fronius" assert solar_net.name == "SolarNet" - inverter_1 = device_registry.async_get_device(identifiers={(DOMAIN, "12345678")}) + inverter_1 = device_registry.async_get_device_by_identifier( + (DOMAIN, "12345678"), config_entry.entry_id + ) assert inverter_1.manufacturer == "Fronius" assert inverter_1.model == "Gen24" assert inverter_1.name == "Gen24 Storage" - meter = device_registry.async_get_device(identifiers={(DOMAIN, "1234567890")}) + meter = device_registry.async_get_device_by_identifier( + (DOMAIN, "1234567890"), config_entry.entry_id + ) assert meter.manufacturer == "Fronius" assert meter.model == "Smart Meter TS 65A-3" assert meter.name == "Smart Meter TS 65A-3" - ohmpilot = device_registry.async_get_device(identifiers={(DOMAIN, "23456789")}) + ohmpilot = device_registry.async_get_device_by_identifier( + (DOMAIN, "23456789"), config_entry.entry_id + ) assert ohmpilot.manufacturer == "Fronius" assert ohmpilot.model == "Ohmpilot 6" assert ohmpilot.name == "Ohmpilot" assert ohmpilot.sw_version == "1.0.25-3" - storage = device_registry.async_get_device( - identifiers={(DOMAIN, "P030T020Z2001234567 ")} + storage = device_registry.async_get_device_by_identifier( + (DOMAIN, "P030T020Z2001234567 "), config_entry.entry_id ) assert storage.manufacturer == "BYD" assert storage.model == "BYD Battery-Box Premium HV" @@ -376,8 +382,8 @@ def assert_state(entity_id, expected_state): await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id) # Devices - solar_net = device_registry.async_get_device( - identifiers={(DOMAIN, "solar_net_123.4567890")} + solar_net = device_registry.async_get_device_by_identifier( + (DOMAIN, "solar_net_123.4567890"), config_entry.entry_id ) assert solar_net.configuration_url == "http://fronius" assert solar_net.manufacturer == "Fronius" @@ -385,18 +391,22 @@ def assert_state(entity_id, expected_state): assert solar_net.name == "SolarNet" assert solar_net.sw_version == "3.18.7-1" - inverter_1 = device_registry.async_get_device(identifiers={(DOMAIN, "123456")}) + inverter_1 = device_registry.async_get_device_by_identifier( + (DOMAIN, "123456"), config_entry.entry_id + ) assert inverter_1.manufacturer == "Fronius" assert inverter_1.model == "Primo 5.0-1" assert inverter_1.name == "Primo 5.0-1" - inverter_2 = device_registry.async_get_device(identifiers={(DOMAIN, "234567")}) + inverter_2 = device_registry.async_get_device_by_identifier( + (DOMAIN, "234567"), config_entry.entry_id + ) assert inverter_2.manufacturer == "Fronius" assert inverter_2.model == "Primo 3.0-1" assert inverter_2.name == "Primo 3.0-1" - meter = device_registry.async_get_device( - identifiers={(DOMAIN, "solar_net_123.4567890:S0 Meter at inverter 1")} + meter = device_registry.async_get_device_by_identifier( + (DOMAIN, "solar_net_123.4567890:S0 Meter at inverter 1"), config_entry.entry_id ) assert meter.manufacturer == "Fronius" assert meter.model == "S0 Meter at inverter 1" diff --git a/tests/components/fully_kiosk/test_diagnostics.py b/tests/components/fully_kiosk/test_diagnostics.py index 31050dad1e030..5d81b0b4990a4 100644 --- a/tests/components/fully_kiosk/test_diagnostics.py +++ b/tests/components/fully_kiosk/test_diagnostics.py @@ -24,7 +24,9 @@ async def test_diagnostics( init_integration: MockConfigEntry, ) -> None: """Test Fully Kiosk diagnostics.""" - device = device_registry.async_get_device(identifiers={(DOMAIN, "abcdef-123456")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, "abcdef-123456"), init_integration.entry_id + ) diagnostics = await get_diagnostics_for_device( hass, hass_client, init_integration, device diff --git a/tests/components/fully_kiosk/test_services.py b/tests/components/fully_kiosk/test_services.py index 6bce012aad388..73a0a6178b6ff 100644 --- a/tests/components/fully_kiosk/test_services.py +++ b/tests/components/fully_kiosk/test_services.py @@ -29,8 +29,8 @@ async def test_services( init_integration: MockConfigEntry, ) -> None: """Test the Fully Kiosk Browser services.""" - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, "abcdef-123456")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "abcdef-123456"), init_integration.entry_id ) assert device_entry @@ -127,8 +127,8 @@ async def test_service_unloaded_entry( """Test service not called when config entry unloaded.""" await hass.config_entries.async_unload(init_integration.entry_id) - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, "abcdef-123456")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "abcdef-123456"), init_integration.entry_id ) assert device_entry diff --git a/tests/components/gardena_bluetooth/test_init.py b/tests/components/gardena_bluetooth/test_init.py index 265e7b53fc57d..9dfbc9303f575 100644 --- a/tests/components/gardena_bluetooth/test_init.py +++ b/tests/components/gardena_bluetooth/test_init.py @@ -90,8 +90,8 @@ async def test_setup( mock_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(mock_entry.entry_id) is True - device = device_registry.async_get_device( - identifiers={(DOMAIN, service_info.address)} + device = device_registry.async_get_device_by_identifier( + (DOMAIN, service_info.address), mock_entry.entry_id ) assert device == snapshot diff --git a/tests/components/generic_hygrostat/test_init.py b/tests/components/generic_hygrostat/test_init.py index d89232e9365fe..1562f3be6c1a5 100644 --- a/tests/components/generic_hygrostat/test_init.py +++ b/tests/components/generic_hygrostat/test_init.py @@ -183,15 +183,12 @@ async def test_async_handle_source_entity_changes_source_entity_removed( hass, generic_hygrostat_entity_entry.entity_id ) - # Remove the source entity's config entry from the device, this removes the - # source entity + # Remove the source device, this removes the source entity with patch( "homeassistant.components.generic_hygrostat.async_unload_entry", wraps=generic_hygrostat.async_unload_entry, ) as mock_unload_entry: - device_registry.async_update_device( - source_device.id, remove_config_entry_id=source_entity_entry.config_entry_id - ) + device_registry.async_remove_device(source_device.id) await hass.async_block_till_done() await hass.async_block_till_done() mock_unload_entry.assert_not_called() diff --git a/tests/components/generic_thermostat/test_init.py b/tests/components/generic_thermostat/test_init.py index 5ed1c5a1d5242..240a82199094d 100644 --- a/tests/components/generic_thermostat/test_init.py +++ b/tests/components/generic_thermostat/test_init.py @@ -187,15 +187,12 @@ async def test_async_handle_source_entity_changes_source_entity_removed( hass, generic_thermostat_entity_entry.entity_id ) - # Remove the source entity's config entry from the device, this removes the - # source entity + # Remove the source device, this removes the source entity with patch( "homeassistant.components.generic_thermostat.async_unload_entry", wraps=generic_thermostat.async_unload_entry, ) as mock_unload_entry: - device_registry.async_update_device( - source_device.id, remove_config_entry_id=source_entity_entry.config_entry_id - ) + device_registry.async_remove_device(source_device.id) await hass.async_block_till_done() await hass.async_block_till_done() mock_unload_entry.assert_not_called() diff --git a/tests/components/gentex_homelink/test_init.py b/tests/components/gentex_homelink/test_init.py index d4003c3d0a1af..1a2b8a10141d6 100644 --- a/tests/components/gentex_homelink/test_init.py +++ b/tests/components/gentex_homelink/test_init.py @@ -31,8 +31,8 @@ async def test_device( """Test device is registered correctly.""" await setup_integration(hass, mock_config_entry) - device = device_registry.async_get_device( - identifiers={(DOMAIN, "TestDevice")}, + device = device_registry.async_get_device_by_identifier( + (DOMAIN, "TestDevice"), mock_config_entry.entry_id ) assert device assert device == snapshot diff --git a/tests/components/goalzero/test_init.py b/tests/components/goalzero/test_init.py index 95f468a93fe87..a7aa4f13d9c78 100644 --- a/tests/components/goalzero/test_init.py +++ b/tests/components/goalzero/test_init.py @@ -91,7 +91,9 @@ async def test_device_info( """Test device info.""" entry = await async_init_integration(hass, aioclient_mock) - device = device_registry.async_get_device(identifiers={(DOMAIN, entry.entry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, entry.entry_id), entry.entry_id + ) assert device.connections == {("mac", "12:34:56:78:90:12")} assert device.identifiers == {(DOMAIN, entry.entry_id)} diff --git a/tests/components/gogogate2/test_cover.py b/tests/components/gogogate2/test_cover.py index 5163307be8076..298e5426e1723 100644 --- a/tests/components/gogogate2/test_cover.py +++ b/tests/components/gogogate2/test_cover.py @@ -342,7 +342,9 @@ async def test_device_info_ismartgate( assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, "xyz")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, "xyz"), config_entry.entry_id + ) assert device assert device.manufacturer == MANUFACTURER assert device.name == "mycontroller" @@ -378,7 +380,9 @@ async def test_device_info_gogogate2( assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, "xyz")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, "xyz"), config_entry.entry_id + ) assert device assert device.manufacturer == MANUFACTURER assert device.name == "mycontroller" diff --git a/tests/components/google_air_quality/test_services.py b/tests/components/google_air_quality/test_services.py index d8010f5580809..f19c285b5b0bc 100644 --- a/tests/components/google_air_quality/test_services.py +++ b/tests/components/google_air_quality/test_services.py @@ -29,8 +29,9 @@ async def test_get_forecast_service( snapshot: SnapshotAssertion, ) -> None: """Test fetching a forecast for a subentry.""" - device = dr.async_get(hass).async_get_device( # pylint: disable=home-assistant-tests-registry-fixtures - identifiers={(DOMAIN, f"{mock_config_entry.entry_id}_home-subentry-id")} + device = dr.async_get(hass).async_get_device_by_identifier( # pylint: disable=home-assistant-tests-registry-fixtures + (DOMAIN, f"{mock_config_entry.entry_id}_home-subentry-id"), + mock_config_entry.entry_id, ) assert device is not None diff --git a/tests/components/google_mail/test_init.py b/tests/components/google_mail/test_init.py index ead9868699696..e00ef9aa63a9d 100644 --- a/tests/components/google_mail/test_init.py +++ b/tests/components/google_mail/test_init.py @@ -210,7 +210,9 @@ async def test_device_info( await setup_integration() entry = hass.config_entries.async_entries(DOMAIN)[0] - device = device_registry.async_get_device(identifiers={(DOMAIN, entry.entry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, entry.entry_id), entry.entry_id + ) assert device.entry_type is dr.DeviceEntryType.SERVICE assert device.identifiers == {(DOMAIN, entry.entry_id)} diff --git a/tests/components/growatt_server/test_init.py b/tests/components/growatt_server/test_init.py index f2499de6fd286..2fa5f7781c25e 100644 --- a/tests/components/growatt_server/test_init.py +++ b/tests/components/growatt_server/test_init.py @@ -56,9 +56,12 @@ async def test_load_unload_config_entry( async def test_device_info( snapshot: SnapshotAssertion, device_registry: dr.DeviceRegistry, + mock_config_entry: MockConfigEntry, ) -> None: """Test device registry integration.""" - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None assert device_entry == snapshot @@ -303,7 +306,9 @@ async def test_classic_api_setup( mock_growatt_classic_api.login.assert_called() # Verify device was created - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "TLX123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "TLX123456"), mock_config_entry_classic.entry_id + ) assert device_entry is not None assert device_entry == snapshot @@ -834,10 +839,17 @@ async def test_dynamic_device_added( """Test that new devices are dynamically added when discovered during a scan.""" # Initially only MIN123456 device exists assert ( - device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) is not None ) - assert device_registry.async_get_device(identifiers={(DOMAIN, "NEW456789")}) is None + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, "NEW456789"), mock_config_entry.entry_id + ) + is None + ) # Mock a new device appearing in the device list mock_growatt_v1_api.device_list.return_value = { @@ -858,7 +870,9 @@ async def test_dynamic_device_added( # New device should now be in the device registry assert ( - device_registry.async_get_device(identifiers={(DOMAIN, "NEW456789")}) + device_registry.async_get_device_by_identifier( + (DOMAIN, "NEW456789"), mock_config_entry.entry_id + ) is not None ) # New device should be in runtime_data @@ -868,8 +882,8 @@ async def test_dynamic_device_added( assert hass.states.get("switch.new456789_charge_from_grid") is not None # Additional check: verify entities exist in the entity registry entity_registry = er.async_get(hass) # pylint: disable=home-assistant-tests-registry-fixtures - new_device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, "NEW456789")} + new_device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "NEW456789"), mock_config_entry.entry_id ) new_device_entities = er.async_entries_for_device( entity_registry, new_device_entry.id, include_disabled_entities=True @@ -889,7 +903,9 @@ async def test_stale_device_removed( """Test that stale devices are removed from the device registry during a scan.""" # Initially MIN123456 device exists with entities in the state machine assert ( - device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) is not None ) assert hass.states.get("switch.min123456_charge_from_grid") is not None @@ -903,7 +919,12 @@ async def test_stale_device_removed( await hass.async_block_till_done(wait_background_tasks=True) # The device should be removed from HA - assert device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) is None + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) + is None + ) # The coordinator should be removed from runtime_data assert "MIN123456" not in mock_config_entry.runtime_data.devices # Orphaned entities must also be gone from the entity registry and state machine @@ -937,7 +958,9 @@ async def test_device_scan_error_is_silent( assert mock_config_entry.state is ConfigEntryState.LOADED # Existing device should still be present assert ( - device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) is not None ) @@ -975,11 +998,18 @@ def min_detail_side_effect(device_sn: str) -> dict: await hass.async_block_till_done(wait_background_tasks=True) # New device should NOT be added — its refresh failed - assert device_registry.async_get_device(identifiers={(DOMAIN, "NEW456789")}) is None + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, "NEW456789"), mock_config_entry.entry_id + ) + is None + ) assert "NEW456789" not in mock_config_entry.runtime_data.devices # Existing device must be unaffected assert ( - device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) is not None ) @@ -1018,7 +1048,9 @@ async def test_classic_api_device_scan( # New device should be added via the classic scan path assert ( - device_registry.async_get_device(identifiers={(DOMAIN, "TLX999999")}) + device_registry.async_get_device_by_identifier( + (DOMAIN, "TLX999999"), mock_config_entry_classic.entry_id + ) is not None ) assert "TLX999999" in mock_config_entry_classic.runtime_data.devices @@ -1045,7 +1077,9 @@ async def test_classic_api_stale_device_removed( # Verify device exists after setup assert ( - device_registry.async_get_device(identifiers={(DOMAIN, "TLX123456")}) + device_registry.async_get_device_by_identifier( + (DOMAIN, "TLX123456"), mock_config_entry_classic.entry_id + ) is not None ) assert "TLX123456" in mock_config_entry_classic.runtime_data.devices @@ -1059,6 +1093,11 @@ async def test_classic_api_stale_device_removed( await hass.async_block_till_done(wait_background_tasks=True) # The device should be removed from HA - assert device_registry.async_get_device(identifiers={(DOMAIN, "TLX123456")}) is None + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, "TLX123456"), mock_config_entry_classic.entry_id + ) + is None + ) # The coordinator should be removed from runtime_data assert "TLX123456" not in mock_config_entry_classic.runtime_data.devices diff --git a/tests/components/growatt_server/test_services.py b/tests/components/growatt_server/test_services.py index d2dd4aa68ec4a..9df97ab9b0646 100644 --- a/tests/components/growatt_server/test_services.py +++ b/tests/components/growatt_server/test_services.py @@ -28,7 +28,9 @@ async def test_read_time_segments_single_device( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Test service call @@ -55,7 +57,9 @@ async def test_update_time_segment_charge_mode( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Test successful update @@ -89,7 +93,9 @@ async def test_update_time_segment_discharge_mode( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None await hass.services.async_call( @@ -121,7 +127,9 @@ async def test_update_time_segment_standby_mode( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None await hass.services.async_call( @@ -153,7 +161,9 @@ async def test_update_time_segment_disabled( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None await hass.services.async_call( @@ -185,7 +195,9 @@ async def test_update_time_segment_with_seconds( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Test with HH:MM:SS format (what the UI time selector sends) @@ -218,7 +230,9 @@ async def test_update_time_segment_api_error( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Mock API error - the library raises an exception instead of returning error dict @@ -270,7 +284,9 @@ async def test_no_min_devices_skips_service_registration( assert hass.services.has_service(DOMAIN, "read_time_segments") # Get the TLX device (non-MIN) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "TLX123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "TLX123456"), mock_config_entry_classic.entry_id + ) assert device_entry is not None # But calling them with a non-MIN device should fail with appropriate error @@ -312,7 +328,9 @@ async def test_multiple_devices_with_valid_device_id_works( await hass.async_block_till_done() # Get the device registry ID for the first MIN device - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Test update service with specific device_id (device registry ID) @@ -357,7 +375,9 @@ async def test_update_time_segment_invalid_time_format( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Test with invalid time format @@ -391,7 +411,9 @@ async def test_update_time_segment_invalid_segment_id( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Test segment_id too low @@ -445,7 +467,9 @@ async def test_update_time_segment_invalid_batt_mode( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Test invalid batt_mode @@ -481,7 +505,9 @@ async def test_read_time_segments_api_error( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Mock API error by making coordinator.read_time_segments raise an exception @@ -633,7 +659,9 @@ async def test_update_time_segment_invalid_end_time_format( await hass.async_block_till_done() # Get the device registry ID - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "MIN123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "MIN123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Test with invalid end_time format @@ -672,7 +700,9 @@ async def test_service_with_unloaded_config_entry( await hass.async_block_till_done() # Get the device - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "TLX123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "TLX123456"), mock_config_entry_classic.entry_id + ) assert device_entry is not None # Unload the config entry @@ -726,7 +756,9 @@ async def test_read_ac_charge_times( """Test reading AC charge times from SPH device.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None response = await hass.services.async_call( @@ -751,7 +783,9 @@ async def test_read_ac_discharge_times( """Test reading AC discharge times from SPH device.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None response = await hass.services.async_call( @@ -774,7 +808,9 @@ async def test_write_ac_charge_times( """Test writing AC charge times to SPH device.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None await hass.services.async_call( @@ -804,7 +840,9 @@ async def test_write_ac_charge_times_with_seconds_format( """Test writing AC charge times with HH:MM:SS format from UI time selector.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None await hass.services.async_call( @@ -834,7 +872,9 @@ async def test_write_ac_discharge_times( """Test writing AC discharge times to SPH device.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None await hass.services.async_call( @@ -863,7 +903,9 @@ async def test_write_ac_charge_times_api_error( """Test handling API error when writing AC charge times.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None mock_growatt_v1_api.sph_write_ac_charge_times.side_effect = ( @@ -893,7 +935,9 @@ async def test_write_ac_discharge_times_api_error( """Test handling API error when writing AC discharge times.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None mock_growatt_v1_api.sph_write_ac_discharge_times.side_effect = ( @@ -922,7 +966,9 @@ async def test_write_ac_charge_times_invalid_charge_power( """Test validation of charge_power range.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None with pytest.raises(ServiceValidationError) as excinfo: @@ -951,7 +997,9 @@ async def test_write_ac_charge_times_invalid_charge_stop_soc( """Test validation of charge_stop_soc range.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None with pytest.raises(ServiceValidationError) as excinfo: @@ -980,7 +1028,9 @@ async def test_write_ac_discharge_times_invalid_discharge_power( """Test validation of discharge_power range.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None with pytest.raises(ServiceValidationError) as excinfo: @@ -1008,7 +1058,9 @@ async def test_write_ac_discharge_times_invalid_discharge_stop_soc( """Test validation of discharge_stop_soc range.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None with pytest.raises(ServiceValidationError) as excinfo: @@ -1036,7 +1088,9 @@ async def test_write_ac_charge_times_invalid_period_time( """Test validation of invalid period time format.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None with pytest.raises(ServiceValidationError) as excinfo: @@ -1076,7 +1130,9 @@ async def test_no_sph_devices_fails_gracefully( assert hass.services.has_service(DOMAIN, "write_ac_charge_times") assert hass.services.has_service(DOMAIN, "read_ac_charge_times") - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "TLX123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "TLX123456"), mock_config_entry_classic.entry_id + ) assert device_entry is not None with pytest.raises(ServiceValidationError) as excinfo: @@ -1140,7 +1196,9 @@ async def test_write_ac_charge_times_uses_cached_periods_for_unspecified( """Test that unspecified periods are filled from cached settings.""" await _setup_sph_integration(hass, mock_config_entry, mock_growatt_v1_api) - device_entry = device_registry.async_get_device(identifiers={(DOMAIN, "SPH123456")}) + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, "SPH123456"), mock_config_entry.entry_id + ) assert device_entry is not None # Only override period 1; periods 2 and 3 should come from cache (all 00:00) diff --git a/tests/components/habitica/test_init.py b/tests/components/habitica/test_init.py index 38c2840bf9ad5..bea808935949a 100644 --- a/tests/components/habitica/test_init.py +++ b/tests/components/habitica/test_init.py @@ -148,8 +148,8 @@ async def test_remove_party_and_reload( assert config_entry.state is ConfigEntryState.LOADED assert ( - device_registry.async_get_device( - {(DOMAIN, f"{config_entry.unique_id}_{group_id}")} + device_registry.async_get_device_by_identifier( + (DOMAIN, f"{config_entry.unique_id}_{group_id}"), config_entry.entry_id ) is not None ) @@ -168,8 +168,8 @@ async def test_remove_party_and_reload( await hass.async_block_till_done() assert ( - device_registry.async_get_device( - {(DOMAIN, f"{config_entry.unique_id}_{group_id}")} + device_registry.async_get_device_by_identifier( + (DOMAIN, f"{config_entry.unique_id}_{group_id}"), config_entry.entry_id ) is None ) diff --git a/tests/components/harbor/test_init.py b/tests/components/harbor/test_init.py index ef0d69148f2aa..23996035cd0c8 100644 --- a/tests/components/harbor/test_init.py +++ b/tests/components/harbor/test_init.py @@ -140,5 +140,7 @@ async def _start() -> None: await setup_integration(hass, mock_config_entry) - device = device_registry.async_get_device(identifiers={(DOMAIN, SERIAL)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, SERIAL), mock_config_entry.entry_id + ) assert device == snapshot diff --git a/tests/components/hassio/test_init.py b/tests/components/hassio/test_init.py index 3db7ddb29f28c..062f6b2d0710a 100644 --- a/tests/components/hassio/test_init.py +++ b/tests/components/hassio/test_init.py @@ -1652,7 +1652,9 @@ async def mount_reload_test_setup( assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, "mount_NAS")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, "mount_NAS"), config_entry.entry_id + ) assert device is not None return device diff --git a/tests/components/heos/test_diagnostics.py b/tests/components/heos/test_diagnostics.py index 942449fe4fd4c..97eadff962a9c 100644 --- a/tests/components/heos/test_diagnostics.py +++ b/tests/components/heos/test_diagnostics.py @@ -72,7 +72,9 @@ async def test_device_diagnostics( config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) device_registry = dr.async_get(hass) # pylint: disable=home-assistant-tests-registry-fixtures - device = device_registry.async_get_device({(DOMAIN, "1")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, "1"), config_entry.entry_id + ) assert device is not None diagnostics = await get_diagnostics_for_device( hass, hass_client, config_entry, device diff --git a/tests/components/heos/test_init.py b/tests/components/heos/test_init.py index 80671abd0ba17..52396a7cd3477 100644 --- a/tests/components/heos/test_init.py +++ b/tests/components/heos/test_init.py @@ -183,14 +183,18 @@ async def test_device_info( """Test device information populates correctly.""" config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) - device = device_registry.async_get_device({(DOMAIN, "1")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, "1"), config_entry.entry_id + ) assert device is not None assert device.manufacturer == "HEOS" assert device.model == "Drive HS2" assert device.name == "Test Player" assert device.serial_number == "123456" assert device.sw_version == "1.0.0" - device = device_registry.async_get_device({(DOMAIN, "2")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, "2"), config_entry.entry_id + ) assert device is not None assert device.manufacturer == "HEOS" assert device.model == "Speaker" @@ -214,10 +218,32 @@ async def test_device_id_migration( ) assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done(wait_background_tasks=True) - assert device_registry.async_get_device({("Other", 1)}) is not None # type: ignore[arg-type] - assert device_registry.async_get_device({(DOMAIN, 1)}) is None # type: ignore[arg-type] - assert device_registry.async_get_device({(DOMAIN, "1")}) is not None - assert device_registry.async_get_device({("Other", "1")}) is not None + assert ( + device_registry.async_get_device_by_identifier( + ("Other", 1), # type: ignore[arg-type] + config_entry.entry_id, + ) + is not None + ) + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, 1), # type: ignore[arg-type] + config_entry.entry_id, + ) + is None + ) + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, "1"), config_entry.entry_id + ) + is not None + ) + assert ( + device_registry.async_get_device_by_identifier( + ("Other", "1"), config_entry.entry_id + ) + is not None + ) async def test_device_id_migration_both_present( @@ -237,8 +263,19 @@ async def test_device_id_migration_both_present( ) assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done(wait_background_tasks=True) - assert device_registry.async_get_device({(DOMAIN, 1)}) is None # type: ignore[arg-type] - assert device_registry.async_get_device({(DOMAIN, "1")}) is not None + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, 1), # type: ignore[arg-type] + config_entry.entry_id, + ) + is None + ) + assert ( + device_registry.async_get_device_by_identifier( + (DOMAIN, "1"), config_entry.entry_id + ) + is not None + ) @pytest.mark.parametrize( diff --git a/tests/components/heos/test_media_player.py b/tests/components/heos/test_media_player.py index 30d17f4a8caa5..2cf8492646cbd 100644 --- a/tests/components/heos/test_media_player.py +++ b/tests/components/heos/test_media_player.py @@ -256,7 +256,9 @@ async def test_updates_from_players_changed_new_ids( assert await hass.config_entries.async_setup(config_entry.entry_id) # Assert device registry matches current id - assert device_registry.async_get_device(identifiers={(DOMAIN, "1")}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, "1"), config_entry.entry_id + ) # Assert entity registry matches current id assert ( entity_registry.async_get_entity_id(MEDIA_PLAYER_DOMAIN, DOMAIN, "1") @@ -272,7 +274,9 @@ async def test_updates_from_players_changed_new_ids( # Assert device registry identifiers were updated assert len(device_registry.devices) == 2 - assert device_registry.async_get_device(identifiers={(DOMAIN, "101")}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, "101"), config_entry.entry_id + ) # Assert entity registry unique id was updated assert len(entity_registry.entities) == 2 assert ( diff --git a/tests/components/hikvision/test_binary_sensor.py b/tests/components/hikvision/test_binary_sensor.py index d0ba96fe03540..7ed77ca9462ad 100644 --- a/tests/components/hikvision/test_binary_sensor.py +++ b/tests/components/hikvision/test_binary_sensor.py @@ -91,8 +91,8 @@ async def test_binary_sensor_device_info( """Test binary sensors are linked to device.""" await setup_integration(hass, mock_config_entry) - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, TEST_DEVICE_ID)} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, TEST_DEVICE_ID), mock_config_entry.entry_id ) assert device_entry is not None assert device_entry.name == TEST_DEVICE_NAME @@ -153,14 +153,14 @@ async def test_binary_sensor_nvr_device( ) assert nvr_device is not None - channel_1_device = device_registry.async_get_device( - identifiers={(DOMAIN, f"{TEST_DEVICE_ID}_1")} + channel_1_device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{TEST_DEVICE_ID}_1"), mock_config_entry.entry_id ) assert channel_1_device is not None assert channel_1_device.via_device_id == nvr_device.id - channel_2_device = device_registry.async_get_device( - identifiers={(DOMAIN, f"{TEST_DEVICE_ID}_2")} + channel_2_device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{TEST_DEVICE_ID}_2"), mock_config_entry.entry_id ) assert channel_2_device is not None assert channel_2_device.via_device_id == nvr_device.id diff --git a/tests/components/hikvision/test_camera.py b/tests/components/hikvision/test_camera.py index 222e236461d33..6e5076f6474d7 100644 --- a/tests/components/hikvision/test_camera.py +++ b/tests/components/hikvision/test_camera.py @@ -72,14 +72,14 @@ async def test_nvr_entities_with_channel_names( await setup_integration(hass, mock_config_entry) # Verify device names use channel names instead of "Channel N" - device_1 = device_registry.async_get_device( - identifiers={(DOMAIN, f"{TEST_DEVICE_ID}_1")} + device_1 = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{TEST_DEVICE_ID}_1"), mock_config_entry.entry_id ) assert device_1 is not None assert device_1.name == "Front Camera channel 1" - device_2 = device_registry.async_get_device( - identifiers={(DOMAIN, f"{TEST_DEVICE_ID}_2")} + device_2 = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{TEST_DEVICE_ID}_2"), mock_config_entry.entry_id ) assert device_2 is not None assert device_2.name == "Front Camera channel 2" @@ -94,8 +94,8 @@ async def test_camera_device_info( """Test camera is linked to device.""" await setup_integration(hass, mock_config_entry) - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, TEST_DEVICE_ID)} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, TEST_DEVICE_ID), mock_config_entry.entry_id ) assert device_entry is not None assert device_entry.name == TEST_DEVICE_NAME diff --git a/tests/components/history_stats/test_init.py b/tests/components/history_stats/test_init.py index f0736fad5ae98..49550428d9242 100644 --- a/tests/components/history_stats/test_init.py +++ b/tests/components/history_stats/test_init.py @@ -135,15 +135,12 @@ async def test_async_handle_source_entity_changes_source_entity_removed( events = track_entity_registry_actions(hass, history_stats_entity_entry.entity_id) - # Remove the source sensor's config entry from the device, this removes the - # source sensor + # Remove the source device, this removes the source sensor with patch( "homeassistant.components.history_stats.async_unload_entry", wraps=history_stats.async_unload_entry, ) as mock_unload_entry: - device_registry.async_update_device( - sensor_device.id, remove_config_entry_id=sensor_config_entry.entry_id - ) + device_registry.async_remove_device(sensor_device.id) await hass.async_block_till_done() await hass.async_block_till_done() mock_unload_entry.assert_called_once() @@ -161,7 +158,7 @@ async def test_async_handle_source_entity_changes_source_entity_removed( # Check we got the expected events: the helper entity's device link is # cleared when the source device is removed (the helper entity belongs to - # the history_stats config entry, not the removed source config entry), + # the history_stats config entry, not the removed source device's config entry), # then the helper entity is removed when the history_stats config entry is # removed. Both registry actions are observed in fire order. assert events == ["update", "remove"] diff --git a/tests/components/hive/test_init.py b/tests/components/hive/test_init.py index 6467c661858f8..e33a554f25b82 100644 --- a/tests/components/hive/test_init.py +++ b/tests/components/hive/test_init.py @@ -102,7 +102,9 @@ async def test_hub_device_registers_mac_connection( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, "hive-hub-id")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, "hive-hub-id"), entry.entry_id + ) assert device is not None assert (dr.CONNECTION_NETWORK_MAC, "00:1c:2b:1c:2e:68") in device.connections @@ -124,7 +126,9 @@ async def test_hub_device_no_mac_connection_when_absent( await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, "hive-hub-id")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, "hive-hub-id"), entry.entry_id + ) assert device is not None assert not any( conn_type == dr.CONNECTION_NETWORK_MAC for conn_type, _ in device.connections diff --git a/tests/components/home_connect/test_binary_sensor.py b/tests/components/home_connect/test_binary_sensor.py index 74786f095808c..b2dc03c2bfdf9 100644 --- a/tests/components/home_connect/test_binary_sensor.py +++ b/tests/components/home_connect/test_binary_sensor.py @@ -54,7 +54,9 @@ async def test_paired_depaired_devices_flow( assert await integration_setup(client) assert config_entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) assert entity_entries @@ -70,7 +72,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert not device for entity_entry in entity_entries: assert not entity_registry.async_get(entity_entry.entity_id) @@ -87,7 +91,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) for entity_entry in entity_entries: assert entity_registry.async_get(entity_entry.entity_id) @@ -131,7 +137,9 @@ def get_status_side_effect(ha_id: str): assert config_entry.state is ConfigEntryState.LOADED client.get_status = get_status_original_mock - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device assert entity_registry.async_get_entity_id( Platform.BINARY_SENSOR, diff --git a/tests/components/home_connect/test_button.py b/tests/components/home_connect/test_button.py index 61cdcda0aecc9..81d1df0e57298 100644 --- a/tests/components/home_connect/test_button.py +++ b/tests/components/home_connect/test_button.py @@ -48,7 +48,9 @@ async def test_paired_depaired_devices_flow( assert await integration_setup(client) assert config_entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) assert entity_entries @@ -64,7 +66,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert not device for entity_entry in entity_entries: assert not entity_registry.async_get(entity_entry.entity_id) @@ -81,7 +85,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) for entity_entry in entity_entries: assert entity_registry.async_get(entity_entry.entity_id) @@ -137,7 +143,9 @@ async def get_all_programs_side_effect(ha_id: str): client.get_available_commands = get_available_commands_original_mock client.get_all_programs = get_all_programs_mock - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device assert entity_registry.async_get_entity_id( Platform.BUTTON, diff --git a/tests/components/home_connect/test_climate.py b/tests/components/home_connect/test_climate.py index 225d27a9b4aa5..b26ec2941f4d5 100644 --- a/tests/components/home_connect/test_climate.py +++ b/tests/components/home_connect/test_climate.py @@ -101,7 +101,9 @@ async def test_paired_depaired_devices_flow( assert await integration_setup(client) assert config_entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) assert entity_entries @@ -117,7 +119,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert not device for entity_entry in entity_entries: assert not entity_registry.async_get(entity_entry.entity_id) @@ -134,7 +138,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) for entity_entry in entity_entries: assert entity_registry.async_get(entity_entry.entity_id) @@ -178,7 +184,9 @@ async def get_all_programs_side_effect(ha_id: str): client.get_settings = get_settings_original_mock client.get_all_programs = get_all_programs_mock - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device assert not entity_registry.async_get_entity_id( Platform.CLIMATE, diff --git a/tests/components/home_connect/test_config_flow.py b/tests/components/home_connect/test_config_flow.py index b57d2855db12b..29afa27bea06c 100644 --- a/tests/components/home_connect/test_config_flow.py +++ b/tests/components/home_connect/test_config_flow.py @@ -498,7 +498,9 @@ async def test_dhcp_flow_complete_device_information( assert await integration_setup(client) assert config_entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device assert device.connections == set() @@ -510,7 +512,9 @@ async def test_dhcp_flow_complete_device_information( assert result["type"] is FlowResultType.ABORT assert result["reason"] == "already_configured" - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device assert device.connections == { (dr.CONNECTION_NETWORK_MAC, dr.format_mac(dhcp_discovery.macaddress)) diff --git a/tests/components/home_connect/test_coordinator.py b/tests/components/home_connect/test_coordinator.py index b053c5c870ba9..e57ab22392906 100644 --- a/tests/components/home_connect/test_coordinator.py +++ b/tests/components/home_connect/test_coordinator.py @@ -531,8 +531,12 @@ async def test_devices_updated_on_refresh( assert config_entry.state is ConfigEntryState.LOADED for appliance in appliances[:2]: - assert device_registry.async_get_device({(DOMAIN, appliance.ha_id)}) - assert not device_registry.async_get_device({(DOMAIN, appliances[2].ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) + assert not device_registry.async_get_device_by_identifier( + (DOMAIN, appliances[2].ha_id), config_entry.entry_id + ) client.get_home_appliances = AsyncMock( return_value=ArrayOfHomeAppliances(appliances[1:3]), @@ -547,9 +551,13 @@ async def test_devices_updated_on_refresh( await client.add_events([HomeConnectApiError("error.key", "error description")]) await hass.async_block_till_done() - assert not device_registry.async_get_device({(DOMAIN, appliances[0].ha_id)}) + assert not device_registry.async_get_device_by_identifier( + (DOMAIN, appliances[0].ha_id), config_entry.entry_id + ) for appliance in appliances[2:3]: - assert device_registry.async_get_device({(DOMAIN, appliance.ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) @pytest.mark.parametrize("appliance", ["Washer"], indirect=True) diff --git a/tests/components/home_connect/test_fan.py b/tests/components/home_connect/test_fan.py index 5c0a11ec3c864..f36f53707e810 100644 --- a/tests/components/home_connect/test_fan.py +++ b/tests/components/home_connect/test_fan.py @@ -96,7 +96,9 @@ async def test_paired_depaired_devices_flow( assert await integration_setup(client) assert config_entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) assert entity_entries @@ -112,7 +114,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert not device for entity_entry in entity_entries: assert not entity_registry.async_get(entity_entry.entity_id) @@ -129,7 +133,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) for entity_entry in entity_entries: assert entity_registry.async_get(entity_entry.entity_id) @@ -174,7 +180,9 @@ async def get_all_programs_side_effect(ha_id: str): client.get_settings = get_settings_original_mock client.get_all_programs = get_all_programs_mock - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device assert not entity_registry.async_get_entity_id( Platform.FAN, diff --git a/tests/components/home_connect/test_light.py b/tests/components/home_connect/test_light.py index 0bc60e33d73b4..bd8192e5607b5 100644 --- a/tests/components/home_connect/test_light.py +++ b/tests/components/home_connect/test_light.py @@ -67,7 +67,9 @@ async def test_paired_depaired_devices_flow( assert await integration_setup(client) assert config_entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) assert entity_entries @@ -83,7 +85,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert not device for entity_entry in entity_entries: assert not entity_registry.async_get(entity_entry.entity_id) @@ -100,7 +104,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) for entity_entry in entity_entries: assert entity_registry.async_get(entity_entry.entity_id) @@ -144,7 +150,9 @@ async def get_settings_side_effect(ha_id: str): assert config_entry.state is ConfigEntryState.LOADED client.get_settings = get_settings_original_mock - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device for key in keys_to_check: assert not entity_registry.async_get_entity_id( diff --git a/tests/components/home_connect/test_number.py b/tests/components/home_connect/test_number.py index f369ba629f6fa..e2976595ac759 100644 --- a/tests/components/home_connect/test_number.py +++ b/tests/components/home_connect/test_number.py @@ -88,7 +88,9 @@ async def test_paired_depaired_devices_flow( assert await integration_setup(client) assert config_entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) assert entity_entries @@ -104,7 +106,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert not device for entity_entry in entity_entries: assert not entity_registry.async_get(entity_entry.entity_id) @@ -121,7 +125,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) for entity_entry in entity_entries: assert entity_registry.async_get(entity_entry.entity_id) @@ -167,7 +173,9 @@ def get_settings_side_effect(ha_id: str): assert config_entry.state is ConfigEntryState.LOADED client.get_settings = get_settings_original_mock - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device for key in keys_to_check: assert not entity_registry.async_get_entity_id( diff --git a/tests/components/home_connect/test_select.py b/tests/components/home_connect/test_select.py index 4f889c483ae20..1966415036245 100644 --- a/tests/components/home_connect/test_select.py +++ b/tests/components/home_connect/test_select.py @@ -90,7 +90,9 @@ async def test_paired_depaired_devices_flow( assert await integration_setup(client) assert config_entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) assert entity_entries @@ -106,7 +108,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert not device for entity_entry in entity_entries: assert not entity_registry.async_get(entity_entry.entity_id) @@ -123,7 +127,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) for entity_entry in entity_entries: assert entity_registry.async_get(entity_entry.entity_id) @@ -181,7 +187,9 @@ async def get_all_programs_side_effect(ha_id: str): client.get_settings = get_settings_original_mock client.get_all_programs = get_all_programs_mock - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device for key in keys_to_check: assert not entity_registry.async_get_entity_id( diff --git a/tests/components/home_connect/test_sensor.py b/tests/components/home_connect/test_sensor.py index ab286a0e95001..92244fac3082d 100644 --- a/tests/components/home_connect/test_sensor.py +++ b/tests/components/home_connect/test_sensor.py @@ -129,7 +129,9 @@ async def test_paired_depaired_devices_flow( assert await integration_setup(client) assert config_entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) assert entity_entries @@ -145,7 +147,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert not device for entity_entry in entity_entries: assert not entity_registry.async_get(entity_entry.entity_id) @@ -162,7 +166,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) for entity_entry in entity_entries: assert entity_registry.async_get(entity_entry.entity_id) @@ -206,7 +212,9 @@ def get_status_side_effect(ha_id: str): assert config_entry.state is ConfigEntryState.LOADED client.get_status = get_status_original_mock - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device for key in keys_to_check: assert not entity_registry.async_get_entity_id( diff --git a/tests/components/home_connect/test_switch.py b/tests/components/home_connect/test_switch.py index b6c3adbc20851..a313cebab2ddc 100644 --- a/tests/components/home_connect/test_switch.py +++ b/tests/components/home_connect/test_switch.py @@ -73,7 +73,9 @@ async def test_paired_depaired_devices_flow( assert await integration_setup(client) assert config_entry.state is ConfigEntryState.LOADED - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) assert entity_entries @@ -89,7 +91,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert not device for entity_entry in entity_entries: assert not entity_registry.async_get(entity_entry.entity_id) @@ -106,7 +110,9 @@ async def test_paired_depaired_devices_flow( ) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + assert device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) for entity_entry in entity_entries: assert entity_registry.async_get(entity_entry.entity_id) @@ -153,7 +159,9 @@ async def get_settings_side_effect(ha_id: str): assert config_entry.state is ConfigEntryState.LOADED client.get_settings = get_settings_original_mock - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, appliance.ha_id), config_entry.entry_id + ) assert device for key in keys_to_check: assert not entity_registry.async_get_entity_id( diff --git a/tests/components/homee/test_diagnostics.py b/tests/components/homee/test_diagnostics.py index aedc3a78e192f..13bd12a8fbd15 100644 --- a/tests/components/homee/test_diagnostics.py +++ b/tests/components/homee/test_diagnostics.py @@ -58,8 +58,8 @@ async def test_diagnostics_device( """Test diagnostics for a device.""" await setup_mock_homee(hass, mock_homee, mock_config_entry) - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, f"{HOMEE_ID}-1")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}-1"), mock_config_entry.entry_id ) assert device_entry is not None result = await get_diagnostics_for_device( @@ -83,8 +83,8 @@ async def test_diagnostics_homee_device( mock_homee.get_node_by_id.return_value = mock_homee.nodes[0] await setup_integration(hass, mock_config_entry) - device_entry = device_registry.async_get_device( - identifiers={(DOMAIN, f"{HOMEE_ID}")} + device_entry = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}"), mock_config_entry.entry_id ) assert device_entry is not None result = await get_diagnostics_for_device( diff --git a/tests/components/homee/test_init.py b/tests/components/homee/test_init.py index b70491d3bbc1a..746408d263b8d 100644 --- a/tests/components/homee/test_init.py +++ b/tests/components/homee/test_init.py @@ -92,8 +92,12 @@ async def test_general_data( await setup_integration(hass, mock_config_entry) # Verify hub and device created correctly using snapshots. - hub = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}")}) - device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-3")}) + hub = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}"), mock_config_entry.entry_id + ) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}-3"), mock_config_entry.entry_id + ) assert hub == snapshot assert device == snapshot @@ -137,7 +141,9 @@ async def test_software_version( mock_homee.nodes = [build_mock_node("cover_without_position.json")] await setup_integration(hass, mock_config_entry) - device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-2")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}-2"), mock_config_entry.entry_id + ) assert device.sw_version == "1.45" @@ -153,7 +159,9 @@ async def test_invalid_profile( mock_homee.nodes[0].profile = 77 await setup_integration(hass, mock_config_entry) - device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-2")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}-2"), mock_config_entry.entry_id + ) assert device.model is None @@ -214,7 +222,9 @@ async def test_remove_stale_device_on_startup( mock_homee.get_node_by_id = lambda node_id: mock_homee.nodes[node_id - 1] await setup_integration(hass, mock_config_entry) - device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-3")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}-3"), mock_config_entry.entry_id + ) assert device is not None mock_homee.nodes.pop() # Remove node with id 3 @@ -223,7 +233,9 @@ async def test_remove_stale_device_on_startup( await hass.async_block_till_done() # Stale device should be removed - device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-3")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}-3"), mock_config_entry.entry_id + ) assert device is None @@ -242,7 +254,9 @@ async def test_remove_node_callback( mock_homee.get_node_by_id = lambda node_id: mock_homee.nodes[node_id - 1] await setup_integration(hass, mock_config_entry) - device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-3")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}-3"), mock_config_entry.entry_id + ) assert device is not None # Test device not removed when callback called with add=True @@ -251,7 +265,9 @@ async def test_remove_node_callback( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-3")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}-3"), mock_config_entry.entry_id + ) assert device is not None # Simulate removal of node with id 3 in homee @@ -261,5 +277,7 @@ async def test_remove_node_callback( await hass.async_block_till_done() # Device should be removed - device = device_registry.async_get_device(identifiers={(DOMAIN, f"{HOMEE_ID}-3")}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"{HOMEE_ID}-3"), mock_config_entry.entry_id + ) assert device is None diff --git a/tests/components/integration/test_init.py b/tests/components/integration/test_init.py index 5084022e5e86d..d422ac541860a 100644 --- a/tests/components/integration/test_init.py +++ b/tests/components/integration/test_init.py @@ -234,15 +234,12 @@ async def test_async_handle_source_entity_changes_source_entity_removed( events = track_entity_registry_actions(hass, integration_entity_entry.entity_id) - # Remove the source sensor's config entry from the device, this removes the - # source sensor + # Remove the source device, this removes the source sensor with patch( "homeassistant.components.integration.async_unload_entry", wraps=integration.async_unload_entry, ) as mock_unload_entry: - device_registry.async_update_device( - sensor_device.id, remove_config_entry_id=sensor_config_entry.entry_id - ) + device_registry.async_remove_device(sensor_device.id) await hass.async_block_till_done() await hass.async_block_till_done() mock_unload_entry.assert_not_called() diff --git a/tests/components/mold_indicator/test_init.py b/tests/components/mold_indicator/test_init.py index c5cb4abb6660c..e87fc42145b3f 100644 --- a/tests/components/mold_indicator/test_init.py +++ b/tests/components/mold_indicator/test_init.py @@ -228,15 +228,12 @@ async def test_async_handle_source_entity_changes_source_entity_removed( events = track_entity_registry_actions(hass, mold_indicator_entity_entry.entity_id) - # Remove the source entity's config entry from the device, this removes the - # source entity + # Remove the source device, this removes the source entity with patch( "homeassistant.components.mold_indicator.async_unload_entry", wraps=mold_indicator.async_unload_entry, ) as mock_unload_entry: - device_registry.async_update_device( - source_device.id, remove_config_entry_id=source_entity_entry.config_entry_id - ) + device_registry.async_remove_device(source_device.id) await hass.async_block_till_done() await hass.async_block_till_done() mock_unload_entry.assert_not_called() diff --git a/tests/components/mqtt/common.py b/tests/components/mqtt/common.py index 4eefc05b59f48..068926c073fb5 100644 --- a/tests/components/mqtt/common.py +++ b/tests/components/mqtt/common.py @@ -1905,7 +1905,9 @@ async def help_test_entity_device_info_with_identifier( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None assert device.identifiers == {("mqtt", "helloworld")} assert device.manufacturer == "Whatever" @@ -1941,8 +1943,9 @@ async def help_test_entity_device_info_with_connection( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device( - connections={(dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12")} + device = device_registry.async_get_device_by_connection( + (dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert device is not None assert device.connections == {(dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12")} @@ -1976,15 +1979,22 @@ async def help_test_entity_device_info_remove( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = dev_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = dev_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None assert ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique") async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", "") await hass.async_block_till_done() - device = dev_registry.async_get_device(identifiers={("mqtt", "helloworld")}) - assert device is None + assert ( + dev_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) + is None + ) assert not ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique") @@ -2010,7 +2020,9 @@ async def help_test_entity_device_info_update( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None assert device.name == "Beer" @@ -2019,7 +2031,9 @@ async def help_test_entity_device_info_update( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None assert device.name == "Milk" @@ -2054,7 +2068,9 @@ async def help_test_entity_name( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = registry.async_get_device({("mqtt", "helloworld")}) + device = registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None entity_id = f"{domain}.default_area_beer_{expected_entity_name}" @@ -2188,7 +2204,9 @@ async def help_test_entity_debug_info( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None debug_info_data = debug_info.info_for_device(hass, device.id) @@ -2228,7 +2246,9 @@ async def help_test_entity_debug_info_max_messages( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None debug_info_data = debug_info.info_for_device(hass, device.id) @@ -2307,7 +2327,9 @@ async def help_test_entity_debug_info_message( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None debug_info_data = debug_info.info_for_device(hass, device.id) @@ -2395,7 +2417,9 @@ async def help_test_entity_debug_info_remove( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None debug_info_data = debug_info.info_for_device(hass, device.id) @@ -2448,7 +2472,9 @@ async def help_test_entity_debug_info_update_entity_id( async_fire_mqtt_message(hass, f"homeassistant/{domain}/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None debug_info_data = debug_info.info_for_device(hass, device.id) @@ -2515,7 +2541,9 @@ async def help_test_entity_disabled_by_default( await hass.async_block_till_done() entity_id = ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique1") assert entity_id is not None and hass.states.get(entity_id) is None - assert dev_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + assert dev_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # Discover an enabled entity, tied to the same device config["enabled_by_default"] = True @@ -2531,7 +2559,10 @@ async def help_test_entity_disabled_by_default( await hass.async_block_till_done() assert not ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique1") assert not ent_registry.async_get_entity_id(domain, DOMAIN, "veryunique2") - assert not dev_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + assert not dev_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) async def help_test_entity_category( diff --git a/tests/components/mqtt/test_config_flow.py b/tests/components/mqtt/test_config_flow.py index 2936322fde4d2..5096957770eb4 100644 --- a/tests/components/mqtt/test_config_flow.py +++ b/tests/components/mqtt/test_config_flow.py @@ -4163,7 +4163,9 @@ async def test_subentry_reconfigure_remove_entity( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device is not None # assert we have an entity for all subentry components @@ -4288,7 +4290,9 @@ async def test_subentry_reconfigure_edit_entity_multi_entitites( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device is not None # assert we have an entity for all subentry components @@ -4729,7 +4733,9 @@ async def test_subentry_reconfigure_edit_entity_single_entity( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device is not None # assert we have an entity for the subentry component @@ -4868,7 +4874,9 @@ async def test_subentry_reconfigure_edit_entity_reset_fields( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device is not None # assert we have an entity for the subentry component @@ -5003,7 +5011,9 @@ async def test_subentry_reconfigure_add_entity( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device is not None # assert we have an entity for the subentry component @@ -5109,7 +5119,9 @@ async def test_subentry_reconfigure_update_device_properties( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device is not None # assert we have an entity for all subentry components @@ -5390,7 +5402,9 @@ async def test_subentry_reconfigure_export_settings( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device is not None # assert we entity for all subentry components diff --git a/tests/components/mqtt/test_device_tracker.py b/tests/components/mqtt/test_device_tracker.py index 7768c2250c210..87444a0600a4c 100644 --- a/tests/components/mqtt/test_device_tracker.py +++ b/tests/components/mqtt/test_device_tracker.py @@ -265,8 +265,12 @@ async def test_cleanup_device_tracker( ) await hass.async_block_till_done() + mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] + # Verify device and registry entries are created - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), mqtt_config_entry.entry_id + ) assert device_entry is not None entity_entry = entity_registry.async_get("device_tracker.mqtt_unique") assert entity_entry is not None @@ -275,7 +279,6 @@ async def test_cleanup_device_tracker( assert state is not None # Remove MQTT from the device - mqtt_config_entry = hass.config_entries.async_entries(DOMAIN)[0] response = await ws_client.remove_device( device_entry.id, mqtt_config_entry.entry_id ) @@ -284,7 +287,9 @@ async def test_cleanup_device_tracker( await hass.async_block_till_done() # Verify device and registry entries are cleared - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), mqtt_config_entry.entry_id + ) assert device_entry is None entity_entry = entity_registry.async_get("device_tracker.mqtt_unique") assert entity_entry is None diff --git a/tests/components/mqtt/test_device_trigger.py b/tests/components/mqtt/test_device_trigger.py index 3ca3bdf764bfd..2f090ceda6890 100644 --- a/tests/components/mqtt/test_device_trigger.py +++ b/tests/components/mqtt/test_device_trigger.py @@ -60,7 +60,9 @@ async def test_get_triggers( async_fire_mqtt_message(hass, discovery_topic, data) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) expected_triggers: list[dict[str, Any]] = [ { "platform": "device", @@ -93,7 +95,9 @@ async def test_get_unknown_triggers( async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data1) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert await async_setup_component( hass, @@ -140,7 +144,9 @@ async def test_get_non_existing_triggers( async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data1) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) triggers = await async_get_device_automations( hass, DeviceAutomationType.TRIGGER, device_entry.id ) @@ -166,7 +172,12 @@ async def test_discover_bad_triggers( ) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla/config", data0) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) is None + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) + is None + ) # Test sending correct data data1 = ( @@ -180,7 +191,9 @@ async def test_discover_bad_triggers( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla/config", data1) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) expected_triggers: list[dict[str, Any]] = [ { "platform": "device", @@ -240,7 +253,9 @@ async def test_update_remove_triggers( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla/config", data1) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry.name == "milk" expected_triggers1: list[dict[str, Any]] = [ { @@ -268,7 +283,9 @@ async def test_update_remove_triggers( hass, DeviceAutomationType.TRIGGER, device_entry.id ) assert triggers == unordered(expected_triggers1) - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry.name == "beer" # Update trigger type / subtype @@ -283,8 +300,12 @@ async def test_update_remove_triggers( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla/config", "") await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) - assert device_entry is None + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) + is None + ) async def test_if_fires_on_mqtt_message( @@ -314,7 +335,9 @@ async def test_if_fires_on_mqtt_message( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla2/config", data2) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert await async_setup_component( hass, @@ -402,7 +425,9 @@ async def test_if_discovery_id_is_prefered( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla2/config", data2) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert await async_setup_component( hass, @@ -481,12 +506,16 @@ async def test_non_unique_triggers( ) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry.name == "milk" async_fire_mqtt_message(hass, "homeassistant/device_automation/bla2/config", data2) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # The device entry was updated, but the trigger was not unique # and therefore it was not set up. assert device_entry.name == "beer" @@ -587,7 +616,9 @@ async def test_if_fires_on_mqtt_message_template( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla2/config", data2) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert await async_setup_component( hass, @@ -670,7 +701,9 @@ async def test_if_fires_on_mqtt_message_late_discover( ) async_fire_mqtt_message(hass, "homeassistant/sensor/bla0/config", data0) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert await async_setup_component( hass, @@ -759,7 +792,9 @@ async def test_if_fires_on_mqtt_message_after_update( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla2/config", data2) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert await async_setup_component( hass, @@ -840,7 +875,9 @@ async def test_no_resubscribe_same_topic( ) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert await async_setup_component( hass, @@ -888,7 +925,9 @@ async def test_not_fires_on_mqtt_message_after_remove_by_mqtt( ) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert await async_setup_component( hass, @@ -960,7 +999,9 @@ async def test_not_fires_on_mqtt_message_after_remove_from_registry( ) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert await async_setup_component( hass, @@ -1020,7 +1061,9 @@ async def test_attach_remove( ) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) callback_calls: list[dict[str, Any]] = [] @@ -1083,7 +1126,9 @@ async def test_attach_remove_late( ) async_fire_mqtt_message(hass, "homeassistant/sensor/bla0/config", data0) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) callback_calls: list[dict[str, Any]] = [] @@ -1149,7 +1194,9 @@ async def test_attach_remove_late2( ) async_fire_mqtt_message(hass, "homeassistant/sensor/bla0/config", data0) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) callback_calls: list[dict[str, Any]] = [] @@ -1219,8 +1266,9 @@ async def test_entity_device_info_with_connection( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device( - connections={(dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12")} + device = device_registry.async_get_device_by_connection( + (dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert device is not None assert device.connections == {(dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12")} @@ -1260,7 +1308,9 @@ async def test_entity_device_info_with_identifier( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None assert device.identifiers == {("mqtt", "helloworld")} assert device.manufacturer == "Whatever" @@ -1331,7 +1381,9 @@ async def test_entity_device_info_update( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None assert device.name == "Beer" @@ -1340,7 +1392,9 @@ async def test_entity_device_info_update( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None assert device.name == "Milk" @@ -1369,8 +1423,8 @@ async def test_cleanup_trigger( await hass.async_block_till_done() # Verify device registry entry is created - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -1389,10 +1443,13 @@ async def test_cleanup_trigger( await hass.async_block_till_done() # Verify device registry entry is cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) + is None ) - assert device_entry is None # Verify retained discovery topic has been cleared mqtt_mock.async_publish.assert_called_once_with( @@ -1424,8 +1481,8 @@ async def test_cleanup_device( await hass.async_block_till_done() # Verify device registry entry is created - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -1438,10 +1495,13 @@ async def test_cleanup_device( await hass.async_block_till_done() # Verify device registry entry is cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) + is None ) - assert device_entry is None async def test_cleanup_device_several_triggers( @@ -1475,8 +1535,8 @@ async def test_cleanup_device_several_triggers( await hass.async_block_till_done() # Verify device registry entry is created - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -1491,8 +1551,8 @@ async def test_cleanup_device_several_triggers( await hass.async_block_till_done() # Verify device registry entry is not cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -1506,10 +1566,13 @@ async def test_cleanup_device_several_triggers( await hass.async_block_till_done() # Verify device registry entry is cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) + is None ) - assert device_entry is None async def test_cleanup_device_with_entity1( @@ -1545,8 +1608,8 @@ async def test_cleanup_device_with_entity1( await hass.async_block_till_done() # Verify device registry entry is created - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -1559,8 +1622,8 @@ async def test_cleanup_device_with_entity1( await hass.async_block_till_done() # Verify device registry entry is not cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -1573,10 +1636,13 @@ async def test_cleanup_device_with_entity1( await hass.async_block_till_done() # Verify device registry entry is cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) + is None ) - assert device_entry is None async def test_cleanup_device_with_entity2( @@ -1612,8 +1678,8 @@ async def test_cleanup_device_with_entity2( await hass.async_block_till_done() # Verify device registry entry is created - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -1626,8 +1692,8 @@ async def test_cleanup_device_with_entity2( await hass.async_block_till_done() # Verify device registry entry is not cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -1640,10 +1706,13 @@ async def test_cleanup_device_with_entity2( await hass.async_block_till_done() # Verify device registry entry is cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) + is None ) - assert device_entry is None async def test_trigger_debug_info( @@ -1687,8 +1756,9 @@ async def test_trigger_debug_info( async_fire_mqtt_message(hass, "homeassistant/device_automation/bla2/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device( - connections={(dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12")} + device = device_registry.async_get_device_by_connection( + (dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert device is not None @@ -1746,7 +1816,9 @@ async def test_unload_entry( ) async_fire_mqtt_message(hass, "homeassistant/device_automation/bla1/config", data1) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert await async_setup_component( hass, diff --git a/tests/components/mqtt/test_diagnostics.py b/tests/components/mqtt/test_diagnostics.py index 03d18aa8b8dae..bb6319b27a63c 100644 --- a/tests/components/mqtt/test_diagnostics.py +++ b/tests/components/mqtt/test_diagnostics.py @@ -65,7 +65,9 @@ async def test_entry_diagnostics( ) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), config_entry.entry_id + ) expected_debug_info = { "entities": [ @@ -190,7 +192,9 @@ async def test_redact_diagnostics( async_fire_mqtt_message(hass, "attributes-topic", location_data) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), config_entry.entry_id + ) expected_debug_info = { "entities": [ diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index 2e6c1d3a48202..f5ea13afda181 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -154,7 +154,9 @@ async def help_check_discovered_items( """Help checking discovered test items are still available.""" # Check the device_trigger was discovered - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None triggers = await async_get_device_automations( hass, DeviceAutomationType.TRIGGER, device_entry.id @@ -449,7 +451,9 @@ async def test_correct_config_discovery_component( assert state.name == "test_device1 Beer" assert discovery_hash in hass.data["mqtt"].discovery_already_discovered - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None assert device_entry.name == "test_device1" @@ -474,7 +478,9 @@ async def test_correct_config_discovery_component( assert state.name == "test_device2 Milk" assert discovery_hash in hass.data["mqtt"].discovery_already_discovered - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None assert device_entry.name == "test_device2" @@ -490,7 +496,9 @@ async def test_correct_config_discovery_component( assert state is None - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is None @@ -539,7 +547,9 @@ async def test_correct_config_discovery_device( assert state.name == "test_device1 Beer" assert discovery_hash in hass.data["mqtt"].discovery_already_discovered - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None assert device_entry.name == "test_device1" @@ -569,7 +579,9 @@ async def test_correct_config_discovery_device( assert state.name == "test_device2 Milk" assert discovery_hash in hass.data["mqtt"].discovery_already_discovered - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None assert device_entry.name == "test_device2" @@ -585,7 +597,9 @@ async def test_correct_config_discovery_device( assert state is None - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is None @@ -758,7 +772,9 @@ async def test_discovery_migration_to_device_base( await hass.async_block_till_done() # Assert we still have our device entry - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None # Check our trigger was unloaden triggers = await async_get_device_automations( @@ -876,7 +892,9 @@ async def test_discovery_migration_to_device_base( await hass.async_block_till_done() await hass.async_block_till_done() # Check the device was removed as all device components were removed - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is None await hass.async_block_till_done(wait_background_tasks=True) @@ -1003,7 +1021,9 @@ async def test_discovery_rollback_to_single_base( ) # Assert we still have our device entry - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None # Check our trigger was unloaded triggers = await async_get_device_automations( @@ -1071,7 +1091,9 @@ async def test_discovery_rollback_to_single_base( await hass.async_block_till_done() await hass.async_block_till_done() # Check the device was removed as all device components were removed - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is None @@ -1763,7 +1785,9 @@ async def test_cleanup_device_manual( await mock_debouncer.wait() # Verify device and registry entries are created - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None for entity_id in entity_ids: @@ -1784,7 +1808,9 @@ async def test_cleanup_device_manual( await hass.async_block_till_done() # Verify device and registry entries are cleared - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is None entity_entry = entity_registry.async_get("sensor.mqtt_sensor") assert entity_entry is None @@ -1859,8 +1885,8 @@ async def test_cleanup_device_mqtt( await hass.async_block_till_done() # Verify the base entity has been created and it has a state - base_device_entry = device_registry.async_get_device( - identifiers={("mqtt", "0AFFD3")} + base_device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD3"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert base_device_entry is not None entity_entry = entity_registry.async_get(base_entity_id) @@ -1872,7 +1898,9 @@ async def test_cleanup_device_mqtt( await hass.async_block_till_done() # Verify device and registry entries are created - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None for entity_id in entity_ids: entity_entry = entity_registry.async_get(entity_id) @@ -1886,7 +1914,9 @@ async def test_cleanup_device_mqtt( await hass.async_block_till_done() # Verify device and registry entries are cleared - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is None for entity_id in entity_ids: @@ -1902,8 +1932,8 @@ async def test_cleanup_device_mqtt( mqtt_mock.async_publish.assert_not_called() # Verify the base entity still exists and it has a state - base_device_entry = device_registry.async_get_device( - identifiers={("mqtt", "0AFFD3")} + base_device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD3"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert base_device_entry is not None entity_entry = entity_registry.async_get(base_entity_id) @@ -1943,7 +1973,9 @@ async def test_cleanup_device_mqtt_device_discovery( await hass.async_block_till_done() # Verify device and registry entries are created - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None for entity_id in entity_ids: entity_entry = entity_registry.async_get(entity_id) @@ -1992,7 +2024,9 @@ async def test_cleanup_device_mqtt_device_discovery( ) async_fire_mqtt_message(hass, discovery_topic, discovery_payload_update2) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # Verify the device entry was removed with the last sensor assert device_entry is None for entity_id in entity_ids: @@ -2102,8 +2136,8 @@ async def test_cleanup_device_multiple_config_entries( await hass.async_block_till_done() # Verify device is still there but entity is cleared - device_entry = device_registry.async_get_device( - connections={("mac", "12:34:56:AB:CD:EF")} + device_entry = device_registry.async_get_device_by_connection( + ("mac", "12:34:56:AB:CD:EF"), config_entry.entry_id ) assert device_entry is not None entity_entry = entity_registry.async_get("sensor.mqtt_sensor") @@ -2227,8 +2261,8 @@ async def test_cleanup_device_multiple_config_entries_mqtt( await hass.async_block_till_done() # Verify device is still there but entity is cleared - device_entry = device_registry.async_get_device( - connections={("mac", "12:34:56:AB:CD:EF")} + device_entry = device_registry.async_get_device_by_connection( + ("mac", "12:34:56:AB:CD:EF"), config_entry.entry_id ) assert device_entry is not None entity_entry = entity_registry.async_get("sensor.mqtt_sensor") @@ -2923,8 +2957,9 @@ async def test_clear_config_topic_disabled_entity( assert hass.states.get("sensor.abc123_sbfspot_12345_2") is None # not unique # Verify device is created - device_entry = device_registry.async_get_device( - connections={("mac", "12:34:56:AB:CD:EF")} + device_entry = device_registry.async_get_device_by_connection( + ("mac", "12:34:56:AB:CD:EF"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert device_entry is not None @@ -2999,8 +3034,9 @@ async def test_clean_up_registry_monitoring( assert len(hooks) == 1 # Verify device is created - device_entry = device_registry.async_get_device( - connections={("mac", "12:34:56:AB:CD:EF")} + device_entry = device_registry.async_get_device_by_connection( + ("mac", "12:34:56:AB:CD:EF"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert device_entry is not None @@ -3183,7 +3219,9 @@ async def test_shared_state_topic( await hass.async_block_till_done() # Verify device and registry entries are created - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None for entity_id in entity_ids: entity_entry = entity_registry.async_get(entity_id) @@ -3229,8 +3267,9 @@ async def test_discovery_with_late_via_device_discovery( await hass.async_block_till_done() await hass.async_block_till_done() - via_device_entry = device_registry.async_get_device( - {("mqtt", "id_via_very_unique")} + via_device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "id_via_very_unique"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert via_device_entry is None # Discovery single config schema @@ -3242,8 +3281,9 @@ async def test_discovery_with_late_via_device_discovery( discovery_topic, payload, ) - via_device_entry = device_registry.async_get_device( - {("mqtt", "id_via_very_unique")} + via_device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "id_via_very_unique"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert via_device_entry is not None assert via_device_entry.name is None @@ -3272,8 +3312,9 @@ async def test_discovery_with_late_via_device_discovery( ) await hass.async_block_till_done() await hass.async_block_till_done() - via_device_entry = device_registry.async_get_device( - {("mqtt", "id_via_very_unique")} + via_device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "id_via_very_unique"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert via_device_entry is not None assert via_device_entry.name == "My Switch" @@ -3301,8 +3342,9 @@ async def test_discovery_with_late_via_device_update( await hass.async_block_till_done() await hass.async_block_till_done() - via_device_entry = device_registry.async_get_device( - {("mqtt", "id_via_very_unique")} + via_device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "id_via_very_unique"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert via_device_entry is None # Discovery single config schema without via device @@ -3313,8 +3355,9 @@ async def test_discovery_with_late_via_device_update( discovery_topic, payload, ) - via_device_entry = device_registry.async_get_device( - {("mqtt", "id_via_very_unique")} + via_device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "id_via_very_unique"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) await hass.async_block_till_done() await hass.async_block_till_done() @@ -3329,8 +3372,9 @@ async def test_discovery_with_late_via_device_update( discovery_topic, payload, ) - via_device_entry = device_registry.async_get_device( - {("mqtt", "id_via_very_unique")} + via_device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "id_via_very_unique"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert via_device_entry is not None assert via_device_entry.name is None @@ -3360,8 +3404,9 @@ async def test_discovery_with_late_via_device_update( ) await hass.async_block_till_done() await hass.async_block_till_done() - via_device_entry = device_registry.async_get_device( - {("mqtt", "id_via_very_unique")} + via_device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "id_via_very_unique"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert via_device_entry is not None assert via_device_entry.name == "My Switch" @@ -3588,7 +3633,9 @@ async def async_discovery_handler(discovery_payload: MQTTDiscoveryPayload) -> No handle() # Verify device and registry entries are created - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None # Check if the MQTT items are all available @@ -3615,7 +3662,9 @@ async def test_shared_qos_with_device_discovery( ) await hass.async_block_till_done() # Verify device and registry entries are created - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device_entry is not None # Check the subscriptions for tag and sensor were done with shared QoS diff --git a/tests/components/mqtt/test_event.py b/tests/components/mqtt/test_event.py index dad15506e8f66..ddd5a0eaffc76 100644 --- a/tests/components/mqtt/test_event.py +++ b/tests/components/mqtt/test_event.py @@ -568,7 +568,9 @@ async def test_entity_device_info_with_hub( async_fire_mqtt_message(hass, "homeassistant/event/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), mqtt_config_entry.entry_id + ) assert device is not None assert device.via_device_id == hub.id diff --git a/tests/components/mqtt/test_init.py b/tests/components/mqtt/test_init.py index 37e058cdc76c8..418c9297b636a 100644 --- a/tests/components/mqtt/test_init.py +++ b/tests/components/mqtt/test_init.py @@ -949,7 +949,9 @@ async def test_default_entry_setting_are_applied( async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), entry.entry_id + ) assert device_entry is not None @@ -1135,7 +1137,9 @@ async def test_mqtt_ws_remove_discovered_device( await hass.async_block_till_done() # Verify device entry is created - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries(DOMAIN)[0].entry_id + ) assert device_entry is not None client = await hass_ws_client(hass) @@ -1144,8 +1148,12 @@ async def test_mqtt_ws_remove_discovered_device( assert response["success"] # Verify device entry is cleared - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) - assert device_entry is None + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), mqtt_config_entry.entry_id + ) + is None + ) async def test_mqtt_ws_get_device_debug_info( @@ -1178,7 +1186,9 @@ async def test_mqtt_ws_get_device_debug_info( await hass.async_block_till_done() # Verify device entry is created - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries(DOMAIN)[0].entry_id + ) assert device_entry is not None client = await hass_ws_client(hass) @@ -1231,7 +1241,9 @@ async def test_mqtt_ws_get_device_debug_info_binary( await hass.async_block_till_done() # Verify device entry is created - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries(DOMAIN)[0].entry_id + ) assert device_entry is not None small_png = ( @@ -1340,7 +1352,9 @@ async def test_debug_info_multiple_devices( for dev in devices: domain = dev["domain"] device_id = dev["config"]["device"]["identifiers"][0] - device = device_registry.async_get_device(identifiers={("mqtt", device_id)}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", device_id), hass.config_entries.async_entries(DOMAIN)[0].entry_id + ) assert device is not None debug_info_data = debug_info.info_for_device(hass, device.id) @@ -1423,7 +1437,9 @@ async def test_debug_info_multiple_entities_triggers( await hass.async_block_till_done() device_id = config[0]["config"]["device"]["identifiers"][0] - device = device_registry.async_get_device(identifiers={("mqtt", device_id)}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", device_id), hass.config_entries.async_entries(DOMAIN)[0].entry_id + ) assert device is not None debug_info_data = debug_info.info_for_device(hass, device.id) assert len(debug_info_data["entities"]) == 2 @@ -1504,7 +1520,9 @@ async def test_debug_info_wildcard( async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries(DOMAIN)[0].entry_id + ) assert device is not None debug_info_data = debug_info.info_for_device(hass, device.id) @@ -1553,7 +1571,9 @@ async def test_debug_info_same_topic( async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries(DOMAIN)[0].entry_id + ) assert device is not None debug_info_data = debug_info.info_for_device(hass, device.id) @@ -1605,7 +1625,9 @@ async def test_debug_info_qos_retain( async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries(DOMAIN)[0].entry_id + ) assert device is not None debug_info_data = debug_info.info_for_device(hass, device.id) @@ -2420,8 +2442,9 @@ async def test_multi_platform_discovery( assert state is not None for platform in non_entity_configs: assert ( - device_registry.async_get_device( - identifiers={("mqtt", f"{platform}_0AFFD2")} + device_registry.async_get_device_by_identifier( + ("mqtt", f"{platform}_0AFFD2"), + hass.config_entries.async_entries(DOMAIN)[0].entry_id, ) is not None ) diff --git a/tests/components/mqtt/test_mixins.py b/tests/components/mqtt/test_mixins.py index bc8a0040328ed..93bb1017f7859 100644 --- a/tests/components/mqtt/test_mixins.py +++ b/tests/components/mqtt/test_mixins.py @@ -339,7 +339,9 @@ async def test_default_entity_and_device_name( hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED) await hass.async_block_till_done() - device = device_registry.async_get_device({("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), entry.entry_id + ) assert device is not None assert device.name == device_name @@ -847,7 +849,9 @@ async def test_loading_subentries( entry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id = next(iter(entry.subentries)) # Each subentry has one device - device = device_registry.async_get_device({("mqtt", subentry_id)}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", subentry_id), entry.entry_id + ) assert device is not None for object_id, component in mqtt_config_subentries_data[0]["data"][ "components" @@ -899,7 +903,9 @@ async def test_loading_subentry_with_bad_component_schema( entry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id = next(iter(entry.subentries)) # Each subentry has one device - device = device_registry.async_get_device({("mqtt", subentry_id)}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", subentry_id), entry.entry_id + ) assert device is None assert ( "Schema violation occurred when trying to set up entity from subentry" @@ -929,7 +935,9 @@ async def test_qos_on_mqtt_device_from_subentry( entry = hass.config_entries.async_entries(DOMAIN)[0] subentry_id = next(iter(entry.subentries)) # Each subentry has one device - device = device_registry.async_get_device({("mqtt", subentry_id)}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", subentry_id), entry.entry_id + ) assert device is not None assert hass.states.get("notify.milk_notifier_milkman_alert") is not None await hass.services.async_call( diff --git a/tests/components/mqtt/test_repairs.py b/tests/components/mqtt/test_repairs.py index ca3f8be814347..c994398dcbeee 100644 --- a/tests/components/mqtt/test_repairs.py +++ b/tests/components/mqtt/test_repairs.py @@ -85,7 +85,9 @@ async def test_subentry_reconfigure_export_settings( assert result["step_id"] == "summary_menu" # assert we have a device for the subentry - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device.config_entries_subentries[config_entry.entry_id] == {subentry_id} assert device is not None @@ -131,7 +133,9 @@ async def test_subentry_reconfigure_export_settings( await setup_helper(hass, suggested_values_from_schema) # Assert the subentry device was not effected by the exported configs - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device.config_entries_subentries[config_entry.entry_id] == {subentry_id} assert device is not None @@ -156,7 +160,9 @@ async def test_subentry_reconfigure_export_settings( assert data["type"] == "create_entry" # Assert the subentry is removed and no other entity has linked the device - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device is None await hass.async_block_till_done(wait_background_tasks=True) @@ -169,6 +175,8 @@ async def test_subentry_reconfigure_export_settings( # The MQTT device was now set up from the new source await hass.async_block_till_done(wait_background_tasks=True) - device = device_registry.async_get_device(identifiers={(DOMAIN, subentry_id)}) + device = device_registry.async_get_device_by_identifier( + (DOMAIN, subentry_id), config_entry.entry_id + ) assert device.config_entries_subentries[config_entry.entry_id] == {None} assert device is not None diff --git a/tests/components/mqtt/test_sensor.py b/tests/components/mqtt/test_sensor.py index 4f9dd0ba971e0..747576eaa22af 100644 --- a/tests/components/mqtt/test_sensor.py +++ b/tests/components/mqtt/test_sensor.py @@ -1604,7 +1604,9 @@ async def test_entity_device_info_with_hub( async_fire_mqtt_message(hass, "homeassistant/sensor/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), mqtt_config_entry.entry_id + ) assert device is not None assert device.via_device_id == hub.id diff --git a/tests/components/mqtt/test_tag.py b/tests/components/mqtt/test_tag.py index bd317d1c8c2d2..0d6d0a4f888b6 100644 --- a/tests/components/mqtt/test_tag.py +++ b/tests/components/mqtt/test_tag.py @@ -75,13 +75,20 @@ async def test_discover_bad_tag( data0 = '{ "device":{"identifiers":["0AFFD2"]}, "topics": "foobar/tag_scanned" }' async_fire_mqtt_message(hass, "homeassistant/tag/bla/config", data0) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) is None + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) + is None + ) # Test sending correct data async_fire_mqtt_message(hass, "homeassistant/tag/bla/config", json.dumps(config1)) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # Fake tag scan. async_fire_mqtt_message(hass, "foobar/tag_scanned", DEFAULT_TAG_SCAN) await hass.async_block_till_done() @@ -100,7 +107,9 @@ async def test_if_fires_on_mqtt_message_with_device( async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # Fake tag scan. async_fire_mqtt_message(hass, "foobar/tag_scanned", DEFAULT_TAG_SCAN) @@ -136,7 +145,9 @@ async def test_if_fires_on_mqtt_message_with_template( async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # Fake tag scan. async_fire_mqtt_message(hass, "foobar/tag_scanned", DEFAULT_TAG_SCAN_JSON) @@ -176,7 +187,9 @@ async def test_if_fires_on_mqtt_message_after_update_with_device( async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config1)) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # Fake tag scan. async_fire_mqtt_message(hass, "foobar/tag_scanned", DEFAULT_TAG_SCAN) @@ -272,7 +285,9 @@ async def test_if_fires_on_mqtt_message_after_update_with_template( async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config1)) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # Fake tag scan. async_fire_mqtt_message(hass, "foobar/tag_scanned", DEFAULT_TAG_SCAN_JSON) @@ -317,7 +332,9 @@ async def test_no_resubscribe_same_topic( async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) await hass.async_block_till_done() - assert device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + assert device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) call_count = mqtt_mock.async_subscribe.call_count async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) @@ -337,7 +354,9 @@ async def test_not_fires_on_mqtt_message_after_remove_by_mqtt_with_device( async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # Fake tag scan. async_fire_mqtt_message(hass, "foobar/tag_scanned", DEFAULT_TAG_SCAN) @@ -412,7 +431,9 @@ async def test_not_fires_on_mqtt_message_after_remove_from_registry( async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # Fake tag scan. async_fire_mqtt_message(hass, "foobar/tag_scanned", DEFAULT_TAG_SCAN) @@ -457,8 +478,9 @@ async def test_entity_device_info_with_connection( async_fire_mqtt_message(hass, "homeassistant/tag/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device( - connections={(dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12")} + device = device_registry.async_get_device_by_connection( + (dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12"), + hass.config_entries.async_entries("mqtt")[0].entry_id, ) assert device is not None assert device.connections == {(dr.CONNECTION_NETWORK_MAC, "02:5b:26:a8:dc:12")} @@ -495,7 +517,9 @@ async def test_entity_device_info_with_identifier( async_fire_mqtt_message(hass, "homeassistant/tag/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None assert device.identifiers == {("mqtt", "helloworld")} assert device.manufacturer == "Whatever" @@ -560,7 +584,9 @@ async def test_entity_device_info_update( async_fire_mqtt_message(hass, "homeassistant/tag/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None assert device.name == "Beer" @@ -569,7 +595,9 @@ async def test_entity_device_info_update( async_fire_mqtt_message(hass, "homeassistant/tag/bla/config", data) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={("mqtt", "helloworld")}) + device = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) assert device is not None assert device.name == "Milk" @@ -629,14 +657,14 @@ async def test_cleanup_tag( ) assert mqtt_device_entry1 is not None assert mqtt_device_entry1.config_entries == {mqtt_entry.entry_id} - device_entry2 = device_registry.async_get_device(identifiers={("mqtt", "hejhopp")}) + device_entry2 = device_registry.async_get_device_by_identifier( + ("mqtt", "hejhopp"), mqtt_entry.entry_id + ) assert device_entry2 is not None - # Removing the test config entry deletes its device; the MQTT device is untouched + # Removing the test config entry's device leaves the MQTT device untouched # and MQTT does not clear its discovery topic - device_registry.async_update_device( - device_entry1.id, remove_config_entry_id=config_entry.entry_id - ) + device_registry.async_remove_device(device_entry1.id) assert ( _get_device_for_config_entry( device_registry, @@ -652,7 +680,9 @@ async def test_cleanup_tag( ) assert mqtt_device_entry1 is not None assert mqtt_device_entry1.config_entries == {mqtt_entry.entry_id} - device_entry2 = device_registry.async_get_device(identifiers={("mqtt", "hejhopp")}) + device_entry2 = device_registry.async_get_device_by_identifier( + ("mqtt", "hejhopp"), mqtt_entry.entry_id + ) assert device_entry2 is not None mqtt_mock.async_publish.assert_not_called() @@ -666,11 +696,13 @@ async def test_cleanup_tag( await hass.async_block_till_done() # Verify device registry entry is cleared - device_entry1 = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry1 = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), mqtt_entry.entry_id ) assert device_entry1 is None - device_entry2 = device_registry.async_get_device(identifiers={("mqtt", "hejhopp")}) + device_entry2 = device_registry.async_get_device_by_identifier( + ("mqtt", "hejhopp"), mqtt_entry.entry_id + ) assert device_entry2 is not None # Verify retained discovery topic has been cleared @@ -696,8 +728,8 @@ async def test_cleanup_device( await hass.async_block_till_done() # Verify device registry entry is created - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -705,10 +737,13 @@ async def test_cleanup_device( await hass.async_block_till_done() # Verify device registry entry is cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) + is None ) - assert device_entry is None async def test_cleanup_device_several_tags( @@ -735,8 +770,8 @@ async def test_cleanup_device_several_tags( await hass.async_block_till_done() # Verify device registry entry is created - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -744,8 +779,8 @@ async def test_cleanup_device_several_tags( await hass.async_block_till_done() # Verify device registry entry is not cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -759,10 +794,13 @@ async def test_cleanup_device_several_tags( await hass.async_block_till_done() # Verify device registry entry is cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) + is None ) - assert device_entry is None async def test_cleanup_device_with_entity_and_trigger_1( @@ -806,8 +844,8 @@ async def test_cleanup_device_with_entity_and_trigger_1( await hass.async_block_till_done() # Verify device registry entry is created - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -820,8 +858,8 @@ async def test_cleanup_device_with_entity_and_trigger_1( await hass.async_block_till_done() # Verify device registry entry is not cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -832,10 +870,13 @@ async def test_cleanup_device_with_entity_and_trigger_1( await hass.async_block_till_done() # Verify device registry entry is cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) + is None ) - assert device_entry is None async def test_cleanup_device_with_entity2( @@ -879,8 +920,8 @@ async def test_cleanup_device_with_entity2( await hass.async_block_till_done() # Verify device registry entry is created - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -896,8 +937,8 @@ async def test_cleanup_device_with_entity2( await hass.async_block_till_done() # Verify device registry entry is not cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), hass.config_entries.async_entries("mqtt")[0].entry_id ) assert device_entry is not None @@ -905,10 +946,13 @@ async def test_cleanup_device_with_entity2( await hass.async_block_till_done() # Verify device registry entry is cleared - device_entry = device_registry.async_get_device( - identifiers={("mqtt", "helloworld")} + assert ( + device_registry.async_get_device_by_identifier( + ("mqtt", "helloworld"), + hass.config_entries.async_entries("mqtt")[0].entry_id, + ) + is None ) - assert device_entry is None async def test_update_with_bad_config_not_breaks_discovery( @@ -966,7 +1010,9 @@ async def test_unload_entry( async_fire_mqtt_message(hass, "homeassistant/tag/bla1/config", json.dumps(config)) await hass.async_block_till_done() - device_entry = device_registry.async_get_device(identifiers={("mqtt", "0AFFD2")}) + device_entry = device_registry.async_get_device_by_identifier( + ("mqtt", "0AFFD2"), hass.config_entries.async_entries("mqtt")[0].entry_id + ) # Fake tag scan, should be processed async_fire_mqtt_message(hass, "foobar/tag_scanned", DEFAULT_TAG_SCAN) diff --git a/tests/components/portainer/snapshots/test_button.ambr b/tests/components/portainer/snapshots/test_button.ambr index f701ef4211632..6fb9e0e40b230 100644 --- a/tests/components/portainer/snapshots/test_button.ambr +++ b/tests/components/portainer/snapshots/test_button.ambr @@ -247,7 +247,7 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': 'unavailable', }) # --- # name: test_all_button_entities_snapshot[button.focused_einstein_kill_container-entry] @@ -498,7 +498,7 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': 'unavailable', }) # --- # name: test_all_button_entities_snapshot[button.funny_chatelet_kill_container-entry] @@ -749,7 +749,7 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': 'unavailable', }) # --- # name: test_all_button_entities_snapshot[button.my_environment_prune_unused_images-entry] @@ -1101,7 +1101,7 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': 'unavailable', }) # --- # name: test_all_button_entities_snapshot[button.serene_banach_kill_container-entry] @@ -1352,7 +1352,7 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': 'unavailable', }) # --- # name: test_all_button_entities_snapshot[button.stoic_turing_kill_container-entry] @@ -1603,6 +1603,6 @@ 'last_changed': , 'last_reported': , 'last_updated': , - 'state': 'unknown', + 'state': 'unavailable', }) # --- diff --git a/tests/components/shelly/test_services.py b/tests/components/shelly/test_services.py index cda4479f3bf68..4e7c4ad0d362c 100644 --- a/tests/components/shelly/test_services.py +++ b/tests/components/shelly/test_services.py @@ -244,14 +244,9 @@ async def test_service_get_kvs_value_wrong_domain( ) other_entry.add_to_hass(hass) - # Add the other domain's config entry to the device + # Move the device to the other domain's config entry device_registry.async_update_device( - device.id, add_config_entry_id=other_entry.entry_id - ) - - # Remove the original Shelly config entry - device_registry.async_update_device( - device.id, remove_config_entry_id=entry.entry_id + device.id, new_config_entry_id=other_entry.entry_id ) with pytest.raises(ServiceValidationError) as exc_info: diff --git a/tests/components/statistics/test_init.py b/tests/components/statistics/test_init.py index 3901f464d219e..ca2c375eb565b 100644 --- a/tests/components/statistics/test_init.py +++ b/tests/components/statistics/test_init.py @@ -123,15 +123,12 @@ async def test_async_handle_source_entity_changes_source_entity_removed( events = track_entity_registry_actions(hass, statistics_entity_entry.entity_id) - # Remove the source sensor's config entry from the device, this removes the - # source sensor + # Remove the source device, this removes the source sensor with patch( "homeassistant.components.statistics.async_unload_entry", wraps=statistics.async_unload_entry, ) as mock_unload_entry: - device_registry.async_update_device( - sensor_device.id, remove_config_entry_id=sensor_config_entry.entry_id - ) + device_registry.async_remove_device(sensor_device.id) await hass.async_block_till_done() await hass.async_block_till_done() mock_unload_entry.assert_called_once() @@ -147,7 +144,7 @@ async def test_async_handle_source_entity_changes_source_entity_removed( # Check we got the expected events: the helper entity's device link is # cleared when the source device is removed (the helper entity belongs to - # the statistics config entry, not the removed source config entry), then + # the statistics config entry, not the removed source device's config entry), then # the helper entity is removed when the statistics config entry is removed. # Both registry actions are observed in fire order. assert events == ["update", "remove"] diff --git a/tests/components/subaru/api_responses.py b/tests/components/subaru/api_responses.py index c897ec3288771..d0117def474f2 100644 --- a/tests/components/subaru/api_responses.py +++ b/tests/components/subaru/api_responses.py @@ -8,6 +8,7 @@ API_GEN_3, API_GEN_4, VEHICLE_API_GEN, + VEHICLE_FEATURES, VEHICLE_HAS_EV, VEHICLE_HAS_REMOTE_SERVICE, VEHICLE_HAS_REMOTE_START, @@ -92,6 +93,11 @@ "EV_STATE_OF_CHARGE_MODE": "EV_MODE", "EV_STATE_OF_CHARGE_PERCENT": 20, "EV_TIME_TO_FULLY_CHARGED_UTC": MOCK_DATETIME, + "LOCK_BOOT_STATUS": "LOCKED", + "LOCK_FRONT_LEFT_STATUS": "LOCKED", + "LOCK_FRONT_RIGHT_STATUS": "LOCKED", + "LOCK_REAR_LEFT_STATUS": "LOCKED", + "LOCK_REAR_RIGHT_STATUS": "LOCKED", "ODOMETER": 1234, "TIMESTAMP": 1595560000.0, "TRANSMISSION_MODE": "UNKNOWN", @@ -111,7 +117,15 @@ }, VEHICLE_HEALTH: { "RECOMMENDED_TIRE_PRESSURE": {"FRONT_TIRES": 35, "REAR_TIRES": 33}, + # subarulink ORs MIL ISTROUBLE into the top-level one, so it can't + # be False here while a feature below is True. + "ISTROUBLE": True, + "FEATURES": { + "TPMS_MIL": {"ISTROUBLE": False, "ONDATE": None}, + "CEL_MIL": {"ISTROUBLE": True, "ONDATE": None}, + }, }, + VEHICLE_FEATURES: ["TPMS_MIL", "CEL_MIL"], } diff --git a/tests/components/subaru/snapshots/test_binary_sensor.ambr b/tests/components/subaru/snapshots/test_binary_sensor.ambr new file mode 100644 index 0000000000000..ef5cf7a2ce138 --- /dev/null +++ b/tests/components/subaru/snapshots/test_binary_sensor.ambr @@ -0,0 +1,1072 @@ +# serializer version: 1 +# name: test_all_entities[binary_sensor.test_vehicle_2_charging-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_charging', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Charging', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Charging', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'is_charging', + 'unique_id': 'JF2ABCDE6L0000002_EV_CHARGER_STATE_TYPE', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_charging-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'battery_charging', + : 'test_vehicle_2 Charging', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_charging', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_check_engine-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_vehicle_2_check_engine', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Check engine', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Check engine', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'mil_cel', + 'unique_id': 'JF2ABCDE6L0000002_CEL_MIL', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_check_engine-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'problem', + : 'test_vehicle_2 Check engine', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_check_engine', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_door_front_left-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_door_front_left', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Door front left', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Door front left', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'door_front_left', + 'unique_id': 'JF2ABCDE6L0000002_DOOR_FRONT_LEFT_POSITION', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_door_front_left-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'door', + : 'test_vehicle_2 Door front left', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_door_front_left', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_door_front_right-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_door_front_right', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Door front right', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Door front right', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'door_front_right', + 'unique_id': 'JF2ABCDE6L0000002_DOOR_FRONT_RIGHT_POSITION', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_door_front_right-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'door', + : 'test_vehicle_2 Door front right', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_door_front_right', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_door_rear_left-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_door_rear_left', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Door rear left', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Door rear left', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'door_rear_left', + 'unique_id': 'JF2ABCDE6L0000002_DOOR_REAR_LEFT_POSITION', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_door_rear_left-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'door', + : 'test_vehicle_2 Door rear left', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_door_rear_left', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_door_rear_right-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_door_rear_right', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Door rear right', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Door rear right', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'door_rear_right', + 'unique_id': 'JF2ABCDE6L0000002_DOOR_REAR_RIGHT_POSITION', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_door_rear_right-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'door', + : 'test_vehicle_2 Door rear right', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_door_rear_right', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_ev_plug-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_ev_plug', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'EV plug', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'EV plug', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'ev_is_plugged_in', + 'unique_id': 'JF2ABCDE6L0000002_EV_IS_PLUGGED_IN', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_ev_plug-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'plug', + : 'test_vehicle_2 EV plug', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_ev_plug', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_hood-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_hood', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Hood', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Hood', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'door_engine_hood', + 'unique_id': 'JF2ABCDE6L0000002_DOOR_ENGINE_HOOD_POSITION', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_hood-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'door', + : 'test_vehicle_2 Hood', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_hood', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_lock_status_front_left-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_lock_status_front_left', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Lock status front left', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Lock status front left', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lock_status_front_left', + 'unique_id': 'JF2ABCDE6L0000002_LOCK_FRONT_LEFT_STATUS', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_lock_status_front_left-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'lock', + : 'test_vehicle_2 Lock status front left', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_lock_status_front_left', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_lock_status_front_right-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_lock_status_front_right', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Lock status front right', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Lock status front right', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lock_status_front_right', + 'unique_id': 'JF2ABCDE6L0000002_LOCK_FRONT_RIGHT_STATUS', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_lock_status_front_right-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'lock', + : 'test_vehicle_2 Lock status front right', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_lock_status_front_right', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_lock_status_rear_left-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_lock_status_rear_left', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Lock status rear left', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Lock status rear left', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lock_status_rear_left', + 'unique_id': 'JF2ABCDE6L0000002_LOCK_REAR_LEFT_STATUS', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_lock_status_rear_left-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'lock', + : 'test_vehicle_2 Lock status rear left', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_lock_status_rear_left', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_lock_status_rear_right-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_lock_status_rear_right', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Lock status rear right', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Lock status rear right', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lock_status_rear_right', + 'unique_id': 'JF2ABCDE6L0000002_LOCK_REAR_RIGHT_STATUS', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_lock_status_rear_right-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'lock', + : 'test_vehicle_2 Lock status rear right', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_lock_status_rear_right', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_lock_status_tailgate-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_lock_status_tailgate', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Lock status tailgate', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Lock status tailgate', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'lock_status_boot', + 'unique_id': 'JF2ABCDE6L0000002_LOCK_BOOT_STATUS', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_lock_status_tailgate-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'lock', + : 'test_vehicle_2 Lock status tailgate', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_lock_status_tailgate', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_sunroof-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_sunroof', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Sunroof', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Sunroof', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'window_sunroof', + 'unique_id': 'JF2ABCDE6L0000002_WINDOW_SUNROOF_STATUS', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_sunroof-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'window', + : 'test_vehicle_2 Sunroof', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_sunroof', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_tailgate-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_tailgate', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Tailgate', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Tailgate', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'door_boot', + 'unique_id': 'JF2ABCDE6L0000002_DOOR_BOOT_POSITION', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_tailgate-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'door', + : 'test_vehicle_2 Tailgate', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_tailgate', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_tire_pressure_warning-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_vehicle_2_tire_pressure_warning', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Tire pressure warning', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Tire pressure warning', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'mil_tpms', + 'unique_id': 'JF2ABCDE6L0000002_TPMS_MIL', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_tire_pressure_warning-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'problem', + : 'test_vehicle_2 Tire pressure warning', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_tire_pressure_warning', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'off', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_vehicle_health-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': , + 'entity_id': 'binary_sensor.test_vehicle_2_vehicle_health', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Vehicle health', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Vehicle health', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'health_istrouble', + 'unique_id': 'JF2ABCDE6L0000002_health_istrouble', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_vehicle_health-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'problem', + : 'test_vehicle_2 Vehicle health', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_vehicle_health', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_window_front_left-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_window_front_left', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Window front left', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Window front left', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'window_front_left', + 'unique_id': 'JF2ABCDE6L0000002_WINDOW_FRONT_LEFT_STATUS', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_window_front_left-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'window', + : 'test_vehicle_2 Window front left', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_window_front_left', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_window_front_right-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_window_front_right', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Window front right', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Window front right', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'window_front_right', + 'unique_id': 'JF2ABCDE6L0000002_WINDOW_FRONT_RIGHT_STATUS', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_window_front_right-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'window', + : 'test_vehicle_2 Window front right', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_window_front_right', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_window_rear_left-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_window_rear_left', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Window rear left', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Window rear left', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'window_rear_left', + 'unique_id': 'JF2ABCDE6L0000002_WINDOW_REAR_LEFT_STATUS', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_window_rear_left-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'window', + : 'test_vehicle_2 Window rear left', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_window_rear_left', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_window_rear_right-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_vehicle_2_window_rear_right', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Window rear right', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Window rear right', + 'platform': 'subaru', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'window_rear_right', + 'unique_id': 'JF2ABCDE6L0000002_WINDOW_REAR_RIGHT_STATUS', + 'unit_of_measurement': None, + }) +# --- +# name: test_all_entities[binary_sensor.test_vehicle_2_window_rear_right-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'window', + : 'test_vehicle_2 Window rear right', + }), + 'context': , + 'entity_id': 'binary_sensor.test_vehicle_2_window_rear_right', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'unknown', + }) +# --- diff --git a/tests/components/subaru/snapshots/test_diagnostics.ambr b/tests/components/subaru/snapshots/test_diagnostics.ambr index b6cf9336d2f17..f1547e4b8533c 100644 --- a/tests/components/subaru/snapshots/test_diagnostics.ambr +++ b/tests/components/subaru/snapshots/test_diagnostics.ambr @@ -10,7 +10,22 @@ }), 'data': list([ dict({ + 'vehicle_features': list([ + 'TPMS_MIL', + 'CEL_MIL', + ]), 'vehicle_health': dict({ + 'FEATURES': dict({ + 'CEL_MIL': dict({ + 'ISTROUBLE': True, + 'ONDATE': None, + }), + 'TPMS_MIL': dict({ + 'ISTROUBLE': False, + 'ONDATE': None, + }), + }), + 'ISTROUBLE': True, 'RECOMMENDED_TIRE_PRESSURE': dict({ 'FRONT_TIRES': 35, 'REAR_TIRES': 33, @@ -34,6 +49,11 @@ 'EV_STATE_OF_CHARGE_PERCENT': 20, 'EV_TIME_TO_FULLY_CHARGED_UTC': '2020-07-24T03:06:40+00:00', 'LATITUDE': '**REDACTED**', + 'LOCK_BOOT_STATUS': 'LOCKED', + 'LOCK_FRONT_LEFT_STATUS': 'LOCKED', + 'LOCK_FRONT_RIGHT_STATUS': 'LOCKED', + 'LOCK_REAR_LEFT_STATUS': 'LOCKED', + 'LOCK_REAR_RIGHT_STATUS': 'LOCKED', 'LONGITUDE': '**REDACTED**', 'ODOMETER': '**REDACTED**', 'TIMESTAMP': 1595560000.0, @@ -67,7 +87,22 @@ 'username': '**REDACTED**', }), 'data': dict({ + 'vehicle_features': list([ + 'TPMS_MIL', + 'CEL_MIL', + ]), 'vehicle_health': dict({ + 'FEATURES': dict({ + 'CEL_MIL': dict({ + 'ISTROUBLE': True, + 'ONDATE': None, + }), + 'TPMS_MIL': dict({ + 'ISTROUBLE': False, + 'ONDATE': None, + }), + }), + 'ISTROUBLE': True, 'RECOMMENDED_TIRE_PRESSURE': dict({ 'FRONT_TIRES': 35, 'REAR_TIRES': 33, @@ -91,6 +126,11 @@ 'EV_STATE_OF_CHARGE_PERCENT': 20, 'EV_TIME_TO_FULLY_CHARGED_UTC': '2020-07-24T03:06:40+00:00', 'LATITUDE': '**REDACTED**', + 'LOCK_BOOT_STATUS': 'LOCKED', + 'LOCK_FRONT_LEFT_STATUS': 'LOCKED', + 'LOCK_FRONT_RIGHT_STATUS': 'LOCKED', + 'LOCK_REAR_LEFT_STATUS': 'LOCKED', + 'LOCK_REAR_RIGHT_STATUS': 'LOCKED', 'LONGITUDE': '**REDACTED**', 'ODOMETER': '**REDACTED**', 'TIMESTAMP': 1595560000.0, diff --git a/tests/components/subaru/test_binary_sensor.py b/tests/components/subaru/test_binary_sensor.py new file mode 100644 index 0000000000000..00eee35705022 --- /dev/null +++ b/tests/components/subaru/test_binary_sensor.py @@ -0,0 +1,464 @@ +"""Test Subaru binary sensors.""" + +import copy +from unittest.mock import patch + +import pytest +from syrupy.assertion import SnapshotAssertion + +from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN +from homeassistant.components.subaru.binary_sensor import ( + BINARY_SENSORS, + EV_CHARGING_BINARY_SENSOR, + EV_PLUG_BINARY_SENSOR, + LOCK_STATUS_KEYS, + MIL_TRANSLATION_KEYS, + OVERALL_HEALTH_BINARY_SENSOR, +) +from homeassistant.components.subaru.const import DOMAIN, VEHICLE_STATUS +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import ( + STATE_OFF, + STATE_ON, + STATE_UNAVAILABLE, + STATE_UNKNOWN, + Platform, +) +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from .api_responses import ( + TEST_VIN_1_G1, + TEST_VIN_2_EV, + TEST_VIN_3_G3, + TEST_VIN_4_G4, + VEHICLE_DATA, + VEHICLE_STATUS_EV, + VEHICLE_STATUS_G3, +) +from .conftest import setup_subaru_config_entry + +from tests.common import MockConfigEntry, snapshot_platform + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_all_entities( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + snapshot: SnapshotAssertion, + subaru_config_entry: MockConfigEntry, +) -> None: + """Snapshot all binary sensors created for an EV vehicle.""" + with patch( + "homeassistant.components.subaru.PLATFORMS", + [Platform.BINARY_SENSOR], + ): + await setup_subaru_config_entry(hass, subaru_config_entry) + await snapshot_platform( + hass, entity_registry, snapshot, subaru_config_entry.entry_id + ) + + +@pytest.mark.parametrize("feature", ["TPMS_MIL", "CEL_MIL"]) +@pytest.mark.usefixtures("ev_entry") +async def test_mil_entities_disabled_by_default( + entity_registry: er.EntityRegistry, + feature: str, +) -> None: + """MIL entities are created for reported MIL features and disabled by default.""" + entity_id = entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_2_EV}_{feature}" + ) + assert entity_id is not None + entry = entity_registry.async_get(entity_id) + assert entry is not None + assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION + assert entry.translation_key == MIL_TRANSLATION_KEYS[feature] + + +@pytest.mark.parametrize( + "key", + [desc.key for desc in BINARY_SENSORS] + + [OVERALL_HEALTH_BINARY_SENSOR.key, EV_PLUG_BINARY_SENSOR.key], +) +async def test_no_binary_sensors_for_g1( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, + key: str, +) -> None: + """Gen1 vehicles do not get any binary sensors (no door/lock/health data).""" + await setup_subaru_config_entry( + hass, + subaru_config_entry, + vehicle_list=[TEST_VIN_1_G1], + vehicle_data=VEHICLE_DATA[TEST_VIN_1_G1], + ) + assert ( + entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_1_G1}_{key}" + ) + is None + ) + + +async def test_no_ev_plug_binary_sensor_for_g3( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, +) -> None: + """Non-EV vehicles do not get the EV plug binary sensor.""" + await setup_subaru_config_entry( + hass, + subaru_config_entry, + vehicle_list=[TEST_VIN_3_G3], + vehicle_data=VEHICLE_DATA[TEST_VIN_3_G3], + vehicle_status=VEHICLE_STATUS_G3, + ) + assert ( + entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, + DOMAIN, + f"{TEST_VIN_3_G3}_{EV_PLUG_BINARY_SENSOR.key}", + ) + is None + ) + + +@pytest.mark.parametrize("key", list(LOCK_STATUS_KEYS)) +async def test_no_lock_sensors_when_unsupported( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, + key: str, +) -> None: + """Lock sensors aren't created for a vehicle whose status omits them. + + VEHICLE_STATUS_G3 has no LOCK_* keys, matching a real vehicle without + lock-status support -- subarulink omits these fields entirely rather + than reporting them as unknown. + """ + await setup_subaru_config_entry( + hass, + subaru_config_entry, + vehicle_list=[TEST_VIN_3_G3], + vehicle_data=VEHICLE_DATA[TEST_VIN_3_G3], + vehicle_status=VEHICLE_STATUS_G3, + ) + assert ( + entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_3_G3}_{key}" + ) + is None + ) + + +@pytest.mark.parametrize("not_equipped", ["NOT_EQUIPPED", "not_equipped"]) +async def test_door_not_equipped_gets_no_entity( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, + not_equipped: str, +) -> None: + """A door reported as NOT_EQUIPPED gets no entity, unlike a failed fetch. + + Unlike windows/locks, doors are always present in vehicle_status, so + a per-door NOT_EQUIPPED value (not the key's absence) is what signals + the trim genuinely lacks that sensor. Checked case-insensitively. + """ + vehicle_status = copy.deepcopy(VEHICLE_STATUS_EV) + vehicle_status[VEHICLE_STATUS]["DOOR_ENGINE_HOOD_POSITION"] = not_equipped + await setup_subaru_config_entry( + hass, subaru_config_entry, vehicle_status=vehicle_status + ) + assert ( + entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, + DOMAIN, + f"{TEST_VIN_2_EV}_DOOR_ENGINE_HOOD_POSITION", + ) + is None + ) + assert ( + entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, + DOMAIN, + f"{TEST_VIN_2_EV}_DOOR_FRONT_LEFT_POSITION", + ) + is not None + ) + + +async def test_no_ev_charging_sensor_when_unsupported( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, +) -> None: + """EV charging isn't created for an EV that doesn't report it, unlike EV plug.""" + vehicle_status = copy.deepcopy(VEHICLE_STATUS_EV) + del vehicle_status[VEHICLE_STATUS]["EV_CHARGER_STATE_TYPE"] + await setup_subaru_config_entry( + hass, subaru_config_entry, vehicle_status=vehicle_status + ) + assert ( + entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, + DOMAIN, + f"{TEST_VIN_2_EV}_{EV_CHARGING_BINARY_SENSOR.key}", + ) + is None + ) + assert ( + entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, + DOMAIN, + f"{TEST_VIN_2_EV}_{EV_PLUG_BINARY_SENSOR.key}", + ) + is not None + ) + + +async def test_overall_health_unknown_without_vehicle_health( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, +) -> None: + """Overall vehicle health is `unknown` when the API has not yet returned health data.""" + await setup_subaru_config_entry( + hass, + subaru_config_entry, + vehicle_list=[TEST_VIN_3_G3], + vehicle_data=VEHICLE_DATA[TEST_VIN_3_G3], + vehicle_status=VEHICLE_STATUS_G3, + ) + overall = entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_3_G3}_health_istrouble" + ) + assert overall is not None + state = hass.states.get(overall) + assert state is not None + assert state.state == STATE_UNKNOWN + + +async def test_binary_sensors_created_for_g4( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, +) -> None: + """Gen4 vehicles get binary sensors, same as Gen2/Gen3.""" + await setup_subaru_config_entry( + hass, + subaru_config_entry, + vehicle_list=[TEST_VIN_4_G4], + vehicle_data=VEHICLE_DATA[TEST_VIN_4_G4], + vehicle_status=VEHICLE_STATUS_G3, + ) + assert ( + entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_4_G4}_health_istrouble" + ) + is not None + ) + + +@pytest.mark.usefixtures("ev_entry") +async def test_ev_charging_disabled_by_default( + entity_registry: er.EntityRegistry, +) -> None: + """The EV charging sensor is disabled by default, unlike the EV plug sensor.""" + entity_id = entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, + DOMAIN, + f"{TEST_VIN_2_EV}_{EV_CHARGING_BINARY_SENSOR.key}", + ) + assert entity_id is not None + entry = entity_registry.async_get(entity_id) + assert entry is not None + assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION + + +@pytest.mark.parametrize("key", ["health_istrouble", "DOOR_FRONT_LEFT_POSITION"]) +async def test_entities_unavailable_when_vehicle_data_fetch_fails( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, + key: str, +) -> None: + """Setup survives a vehicle whose data fetch fails; its entities go unavailable. + + _refresh_subaru_data only adds coordinator.data[vin] when the per-vehicle + API call succeeds, so a failure must not crash setup for descriptions + that are created unconditionally (e.g. the door/health sensors). + """ + await setup_subaru_config_entry(hass, subaru_config_entry, vehicle_status={}) + assert subaru_config_entry.state is ConfigEntryState.LOADED + + entity_id = entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_2_EV}_{key}" + ) + assert entity_id is not None + state = hass.states.get(entity_id) + assert state is not None + assert state.state == STATE_UNAVAILABLE + + +async def test_no_window_or_lock_entities_when_vehicle_data_fetch_fails( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, +) -> None: + """Unlike doors, windows/locks aren't created when the initial fetch fails.""" + await setup_subaru_config_entry(hass, subaru_config_entry, vehicle_status={}) + assert ( + entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_2_EV}_WINDOW_FRONT_LEFT_STATUS" + ) + is None + ) + + +@pytest.mark.parametrize( + ("status", "expected_state"), + [ + ("CLOSED", STATE_OFF), + ("CLOSE", STATE_OFF), + ("closed", STATE_OFF), + ("OPEN", STATE_ON), + ("VENTED", STATE_ON), + ("UNKNOWN", STATE_UNKNOWN), + ("UNAVAILABLE", STATE_UNKNOWN), + ], +) +async def test_door_state( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, + status: str, + expected_state: str, +) -> None: + """Door state reflects the raw status, case-insensitively; sentinels are unknown.""" + vehicle_status = copy.deepcopy(VEHICLE_STATUS_EV) + vehicle_status[VEHICLE_STATUS]["DOOR_FRONT_LEFT_POSITION"] = status + await setup_subaru_config_entry( + hass, subaru_config_entry, vehicle_status=vehicle_status + ) + entity_id = entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_2_EV}_DOOR_FRONT_LEFT_POSITION" + ) + assert entity_id is not None + state = hass.states.get(entity_id) + assert state is not None + assert state.state == expected_state + + +@pytest.mark.parametrize( + ("status", "expected_state"), + [ + ("LOCKED", STATE_OFF), + ("UNLOCKED", STATE_ON), + ], +) +async def test_lock_sensor_state( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, + status: str, + expected_state: str, +) -> None: + """Lock-status sensor is on when unlocked.""" + vehicle_status = copy.deepcopy(VEHICLE_STATUS_EV) + vehicle_status[VEHICLE_STATUS]["LOCK_FRONT_LEFT_STATUS"] = status + await setup_subaru_config_entry( + hass, subaru_config_entry, vehicle_status=vehicle_status + ) + entity_id = entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_2_EV}_LOCK_FRONT_LEFT_STATUS" + ) + assert entity_id is not None + state = hass.states.get(entity_id) + assert state is not None + assert state.state == expected_state + + +@pytest.mark.parametrize( + ("status", "expected_state"), + [ + ("CHARGING", STATE_ON), + ("LOCKED_CONNECTED", STATE_ON), + ("UNLOCKED_CONNECTED", STATE_ON), + ("UNPLUGGED", STATE_OFF), + ], +) +async def test_ev_plug_state( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, + status: str, + expected_state: str, +) -> None: + """EV plug sensor is on for any documented connected state.""" + vehicle_status = copy.deepcopy(VEHICLE_STATUS_EV) + vehicle_status[VEHICLE_STATUS]["EV_IS_PLUGGED_IN"] = status + await setup_subaru_config_entry( + hass, subaru_config_entry, vehicle_status=vehicle_status + ) + entity_id = entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_2_EV}_EV_IS_PLUGGED_IN" + ) + assert entity_id is not None + state = hass.states.get(entity_id) + assert state is not None + assert state.state == expected_state + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +@pytest.mark.parametrize( + ("status", "expected_state"), + [ + ("CHARGING", STATE_ON), + ("NOT_CHARGING", STATE_OFF), + ("UNPLUGGED", STATE_OFF), + ], +) +async def test_ev_charging_state( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + subaru_config_entry: MockConfigEntry, + status: str, + expected_state: str, +) -> None: + """EV charging sensor is on only while actively CHARGING.""" + vehicle_status = copy.deepcopy(VEHICLE_STATUS_EV) + vehicle_status[VEHICLE_STATUS]["EV_CHARGER_STATE_TYPE"] = status + await setup_subaru_config_entry( + hass, subaru_config_entry, vehicle_status=vehicle_status + ) + entity_id = entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_2_EV}_EV_CHARGER_STATE_TYPE" + ) + assert entity_id is not None + state = hass.states.get(entity_id) + assert state is not None + assert state.state == expected_state + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default", "ev_entry") +async def test_mil_sensor_state( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, +) -> None: + """MIL sensor reflects the per-feature ISTROUBLE flag.""" + on_entity = entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_2_EV}_CEL_MIL" + ) + off_entity = entity_registry.async_get_entity_id( + BINARY_SENSOR_DOMAIN, DOMAIN, f"{TEST_VIN_2_EV}_TPMS_MIL" + ) + assert on_entity is not None + assert off_entity is not None + on_state = hass.states.get(on_entity) + off_state = hass.states.get(off_entity) + assert on_state is not None + assert off_state is not None + assert on_state.state == STATE_ON + assert off_state.state == STATE_OFF diff --git a/tests/components/tasmota/test_discovery.py b/tests/components/tasmota/test_discovery.py index 77a231826a268..fdfa04eadc8c7 100644 --- a/tests/components/tasmota/test_discovery.py +++ b/tests/components/tasmota/test_discovery.py @@ -422,15 +422,13 @@ async def test_device_remove_multiple_config_entries_2( assert device_entry.config_entries == {tasmota_entry.entry_id} assert other_device_entry.id != device_entry.id - # Remove the config entry from the other (non-Tasmota) device sharing the connection + # Remove the other (non-Tasmota) device sharing the connection mock_device_entry = _get_device_for_config_entry( device_registry, mock_entry.entry_id, connections={(dr.CONNECTION_NETWORK_MAC, mac)}, ) - device_registry.async_update_device( - mock_device_entry.id, remove_config_entry_id=mock_entry.entry_id - ) + device_registry.async_remove_device(mock_device_entry.id) await hass.async_block_till_done() # Verify the Tasmota device entry is not removed @@ -441,11 +439,9 @@ async def test_device_remove_multiple_config_entries_2( assert device_entry.config_entries == {tasmota_entry.entry_id} mqtt_mock.async_publish.assert_not_called() - # Remove other config entry from the other device + # Remove the other (non-Tasmota) device # Tasmota should not do any cleanup - device_registry.async_update_device( - other_device_entry.id, remove_config_entry_id=mock_entry.entry_id - ) + device_registry.async_remove_device(other_device_entry.id) await hass.async_block_till_done() mqtt_mock.async_publish.assert_not_called() diff --git a/tests/components/threshold/test_init.py b/tests/components/threshold/test_init.py index a10e2922eb7d3..0f8c1539880f2 100644 --- a/tests/components/threshold/test_init.py +++ b/tests/components/threshold/test_init.py @@ -233,15 +233,12 @@ async def test_async_handle_source_entity_changes_source_entity_removed( events = track_entity_registry_actions(hass, threshold_entity_entry.entity_id) - # Remove the source sensor's config entry from the device, this removes the - # source sensor + # Remove the source device, this removes the source sensor with patch( "homeassistant.components.threshold.async_unload_entry", wraps=threshold.async_unload_entry, ) as mock_unload_entry: - device_registry.async_update_device( - sensor_device.id, remove_config_entry_id=sensor_config_entry.entry_id - ) + device_registry.async_remove_device(sensor_device.id) await hass.async_block_till_done() await hass.async_block_till_done() mock_unload_entry.assert_not_called() diff --git a/tests/components/trend/test_init.py b/tests/components/trend/test_init.py index c6f9a783ef975..50533b8f1d646 100644 --- a/tests/components/trend/test_init.py +++ b/tests/components/trend/test_init.py @@ -155,15 +155,12 @@ async def test_async_handle_source_entity_changes_source_entity_removed( events = track_entity_registry_actions(hass, trend_entity_entry.entity_id) - # Remove the source sensor's config entry from the device, this removes the - # source sensor + # Remove the source device, this removes the source sensor with patch( "homeassistant.components.trend.async_unload_entry", wraps=trend.async_unload_entry, ) as mock_unload_entry: - device_registry.async_update_device( - sensor_device.id, remove_config_entry_id=sensor_config_entry.entry_id - ) + device_registry.async_remove_device(sensor_device.id) await hass.async_block_till_done() await hass.async_block_till_done() mock_unload_entry.assert_called_once() @@ -179,7 +176,7 @@ async def test_async_handle_source_entity_changes_source_entity_removed( # Check we got the expected events: the helper entity's device link is # cleared when the source device is removed (the helper entity belongs to - # the trend config entry, not the removed source config entry), then the + # the trend config entry, not the removed source device's config entry), then the # helper entity is removed when the trend config entry is removed. Both # registry actions are observed in fire order. assert events == ["update", "remove"] diff --git a/tests/components/utility_meter/test_init.py b/tests/components/utility_meter/test_init.py index 800f64359d703..31e3b80c493c9 100644 --- a/tests/components/utility_meter/test_init.py +++ b/tests/components/utility_meter/test_init.py @@ -600,15 +600,12 @@ async def test_async_handle_source_entity_changes_source_entity_removed( sensor_device = device_registry.async_get(sensor_device.id) assert utility_meter_config_entry.entry_id not in sensor_device.config_entries - # Remove the source sensor's config entry from the device, this removes the - # source sensor + # Remove the source device, this removes the source sensor with patch( "homeassistant.components.utility_meter.async_unload_entry", wraps=utility_meter.async_unload_entry, ) as mock_unload_entry: - device_registry.async_update_device( - sensor_device.id, remove_config_entry_id=sensor_config_entry.entry_id - ) + device_registry.async_remove_device(sensor_device.id) await hass.async_block_till_done() await hass.async_block_till_done() mock_unload_entry.assert_not_called() diff --git a/tests/components/velbus/test_init.py b/tests/components/velbus/test_init.py index 2e825b4b5167e..9af5ec773be05 100644 --- a/tests/components/velbus/test_init.py +++ b/tests/components/velbus/test_init.py @@ -250,9 +250,7 @@ async def test_remove_config_entry_device( ) result = await async_remove_config_entry_device(hass, config_entry, stale_device) assert result is True - device_registry.async_update_device( - stale_device.id, remove_config_entry_id=config_entry.entry_id - ) + device_registry.async_remove_device(stale_device.id) stale_device_after = device_registry.async_get(stale_device.id) assert ( diff --git a/tests/components/vicare/conftest.py b/tests/components/vicare/conftest.py index 4cb265422f3a7..e726a1b66eb08 100644 --- a/tests/components/vicare/conftest.py +++ b/tests/components/vicare/conftest.py @@ -15,6 +15,7 @@ ) from homeassistant.components.vicare.const import DOMAIN from homeassistant.components.vicare.types import ViCareData, ViCareDevice +from homeassistant.components.vicare.utils import get_device_serial from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component @@ -29,6 +30,8 @@ class Fixture: roles: set[str] data_file: str + # Opt-in shared gateway serial; defaults to a per-fixture gateway when unset. + gateway_id: str | None = None class MockPyViCare: @@ -39,7 +42,10 @@ def __init__(self, fixtures: list[Fixture]) -> None: self.devices = [] for idx, fixture in enumerate(fixtures): service = MockViCareService( - f"installation{idx}", f"gateway{idx}", f"deviceId{idx}", fixture + f"installation{idx}", + fixture.gateway_id or f"gateway{idx}", + f"deviceId{idx}", + fixture, ) self.devices.append( PyViCareDeviceConfig( @@ -74,13 +80,13 @@ def __init__(self, fixtures: list[Fixture]) -> None: def as_vicare_data(self) -> ViCareData: """Convert to ViCareData as returned by _setup_vicare_api.""" - return ViCareData( - client=self, - devices=[ - ViCareDevice(config=device, api=device.asAutoDetectDevice()) - for device in self.devices - ], - ) + devices = [] + for device in self.devices: + api = device.asAutoDetectDevice() + devices.append( + ViCareDevice(config=device, api=api, serial=get_device_serial(api)) + ) + return ViCareData(client=self, devices=devices) class MockViCareService: diff --git a/tests/components/vicare/test_init.py b/tests/components/vicare/test_init.py index 0e0a0343f3a3d..d74926ccb3312 100644 --- a/tests/components/vicare/test_init.py +++ b/tests/components/vicare/test_init.py @@ -23,11 +23,14 @@ issue_registry as ir, ) -from . import MODULE +from . import MODULE, setup_integration from .conftest import Fixture, MockPyViCare from tests.common import MockConfigEntry +# 16-character zigbee IEEE address shared by the FHT fixtures. +ZIGBEE_IEEE = "#" * 16 + @pytest.mark.usefixtures("mock_setup_entry") async def test_migrate_entry_v1_1_to_v2_1(hass: HomeAssistant) -> None: @@ -436,3 +439,36 @@ async def test_device_and_entity_migration( == "gateway1_deviceId1-heating-0" ) assert entity_registry.async_get(entry3.entity_id).unique_id == "gateway2-0" + + +async def test_device_via_device_links( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + device_registry: dr.DeviceRegistry, +) -> None: + """Test that a zigbee sub-device links to its gateway via via_device_id.""" + fixtures: list[Fixture] = [ + Fixture({"type:fhtMain"}, "vicare/FHTMain.json", gateway_id="gateway0"), + Fixture({"type:fhtChannel"}, "vicare/FHTChannel.json", gateway_id="gateway0"), + ] + with ( + patch( + "homeassistant.helpers.config_entry_oauth2_flow.OAuth2Session.async_ensure_token_valid", + ), + patch( + f"{MODULE}._setup_vicare_api", + return_value=MockPyViCare(fixtures).as_vicare_data(), + ), + ): + await setup_integration(hass, mock_config_entry) + + gateway_device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"gateway0_zigbee_{ZIGBEE_IEEE}"), mock_config_entry.entry_id + ) + assert gateway_device is not None + + channel_device = device_registry.async_get_device_by_identifier( + (DOMAIN, f"gateway0_zigbee_{ZIGBEE_IEEE}_2"), mock_config_entry.entry_id + ) + assert channel_device is not None + assert channel_device.via_device_id == gateway_device.id