From cee88473a20b686c104d57d056cdd9b95b41610c Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Thu, 25 Sep 2025 18:59:53 +0200 Subject: [PATCH] Remove deprecated sensors and update remaning for Alexa Devices (#151230) --- .../components/alexa_devices/binary_sensor.py | 74 +++++++++---------- .../components/alexa_devices/config_flow.py | 4 +- .../components/alexa_devices/coordinator.py | 2 +- .../components/alexa_devices/diagnostics.py | 4 +- .../components/alexa_devices/icons.json | 40 ---------- .../components/alexa_devices/manifest.json | 2 +- .../components/alexa_devices/sensor.py | 13 ++++ .../components/alexa_devices/strings.json | 20 ----- .../components/alexa_devices/switch.py | 34 +++++++-- .../components/alexa_devices/utils.py | 25 ++++++- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/alexa_devices/const.py | 17 ++--- .../snapshots/test_binary_sensor.ambr | 48 ------------ .../snapshots/test_diagnostics.ambr | 26 +++++-- .../snapshots/test_services.ambr | 24 ++++-- .../alexa_devices/snapshots/test_switch.ambr | 2 +- tests/components/alexa_devices/test_sensor.py | 30 +++++++- tests/components/alexa_devices/test_switch.py | 50 ++++++++----- tests/components/alexa_devices/test_utils.py | 40 ++++++++++ 20 files changed, 250 insertions(+), 209 deletions(-) diff --git a/homeassistant/components/alexa_devices/binary_sensor.py b/homeassistant/components/alexa_devices/binary_sensor.py index 410ea4555e2..296f4c417f0 100644 --- a/homeassistant/components/alexa_devices/binary_sensor.py +++ b/homeassistant/components/alexa_devices/binary_sensor.py @@ -10,6 +10,7 @@ from aioamazondevices.api import AmazonDevice from aioamazondevices.const import SENSOR_STATE_OFF from homeassistant.components.binary_sensor import ( + DOMAIN as BINARY_SENSOR_DOMAIN, BinarySensorDeviceClass, BinarySensorEntity, BinarySensorEntityDescription, @@ -20,6 +21,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .coordinator import AmazonConfigEntry from .entity import AmazonEntity +from .utils import async_update_unique_id # Coordinator is used to centralize the data updates PARALLEL_UPDATES = 0 @@ -31,6 +33,7 @@ class AmazonBinarySensorEntityDescription(BinarySensorEntityDescription): is_on_fn: Callable[[AmazonDevice, str], bool] is_supported: Callable[[AmazonDevice, str], bool] = lambda device, key: True + is_available_fn: Callable[[AmazonDevice, str], bool] = lambda device, key: True BINARY_SENSORS: Final = ( @@ -41,46 +44,15 @@ BINARY_SENSORS: Final = ( is_on_fn=lambda device, _: device.online, ), AmazonBinarySensorEntityDescription( - key="bluetooth", - entity_category=EntityCategory.DIAGNOSTIC, - translation_key="bluetooth", - is_on_fn=lambda device, _: device.bluetooth_state, - ), - AmazonBinarySensorEntityDescription( - key="babyCryDetectionState", - translation_key="baby_cry_detection", - is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF), - is_supported=lambda device, key: device.sensors.get(key) is not None, - ), - AmazonBinarySensorEntityDescription( - key="beepingApplianceDetectionState", - translation_key="beeping_appliance_detection", - is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF), - is_supported=lambda device, key: device.sensors.get(key) is not None, - ), - AmazonBinarySensorEntityDescription( - key="coughDetectionState", - translation_key="cough_detection", - is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF), - is_supported=lambda device, key: device.sensors.get(key) is not None, - ), - AmazonBinarySensorEntityDescription( - key="dogBarkDetectionState", - translation_key="dog_bark_detection", - is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF), - is_supported=lambda device, key: device.sensors.get(key) is not None, - ), - AmazonBinarySensorEntityDescription( - key="humanPresenceDetectionState", + key="detectionState", device_class=BinarySensorDeviceClass.MOTION, - is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF), - is_supported=lambda device, key: device.sensors.get(key) is not None, - ), - AmazonBinarySensorEntityDescription( - key="waterSoundsDetectionState", - translation_key="water_sounds_detection", - is_on_fn=lambda device, key: (device.sensors[key].value != SENSOR_STATE_OFF), + is_on_fn=lambda device, key: bool( + device.sensors[key].value != SENSOR_STATE_OFF + ), is_supported=lambda device, key: device.sensors.get(key) is not None, + is_available_fn=lambda device, key: ( + device.online and device.sensors[key].error is False + ), ), ) @@ -94,6 +66,22 @@ async def async_setup_entry( coordinator = entry.runtime_data + # Replace unique id for "detectionState" binary sensor + await async_update_unique_id( + hass, + coordinator, + BINARY_SENSOR_DOMAIN, + "humanPresenceDetectionState", + "detectionState", + ) + + async_add_entities( + AmazonBinarySensorEntity(coordinator, serial_num, sensor_desc) + for sensor_desc in BINARY_SENSORS + for serial_num in coordinator.data + if sensor_desc.is_supported(coordinator.data[serial_num], sensor_desc.key) + ) + known_devices: set[str] = set() def _check_device() -> None: @@ -125,3 +113,13 @@ class AmazonBinarySensorEntity(AmazonEntity, BinarySensorEntity): return self.entity_description.is_on_fn( self.device, self.entity_description.key ) + + @property + def available(self) -> bool: + """Return if entity is available.""" + return ( + self.entity_description.is_available_fn( + self.device, self.entity_description.key + ) + and super().available + ) diff --git a/homeassistant/components/alexa_devices/config_flow.py b/homeassistant/components/alexa_devices/config_flow.py index a3bcce1965b..e863f137f70 100644 --- a/homeassistant/components/alexa_devices/config_flow.py +++ b/homeassistant/components/alexa_devices/config_flow.py @@ -64,7 +64,7 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN): data = await validate_input(self.hass, user_input) except CannotConnect: errors["base"] = "cannot_connect" - except (CannotAuthenticate, TypeError): + except CannotAuthenticate: errors["base"] = "invalid_auth" except CannotRetrieveData: errors["base"] = "cannot_retrieve_data" @@ -112,7 +112,7 @@ class AmazonDevicesConfigFlow(ConfigFlow, domain=DOMAIN): ) except CannotConnect: errors["base"] = "cannot_connect" - except (CannotAuthenticate, TypeError): + except CannotAuthenticate: errors["base"] = "invalid_auth" except CannotRetrieveData: errors["base"] = "cannot_retrieve_data" diff --git a/homeassistant/components/alexa_devices/coordinator.py b/homeassistant/components/alexa_devices/coordinator.py index 3b14324fdb6..6ce21aa2216 100644 --- a/homeassistant/components/alexa_devices/coordinator.py +++ b/homeassistant/components/alexa_devices/coordinator.py @@ -68,7 +68,7 @@ class AmazonDevicesCoordinator(DataUpdateCoordinator[dict[str, AmazonDevice]]): translation_key="cannot_retrieve_data_with_error", translation_placeholders={"error": repr(err)}, ) from err - except (CannotAuthenticate, TypeError) as err: + except CannotAuthenticate as err: raise ConfigEntryAuthFailed( translation_domain=DOMAIN, translation_key="invalid_auth", diff --git a/homeassistant/components/alexa_devices/diagnostics.py b/homeassistant/components/alexa_devices/diagnostics.py index 0c4cb794416..938a20fb218 100644 --- a/homeassistant/components/alexa_devices/diagnostics.py +++ b/homeassistant/components/alexa_devices/diagnostics.py @@ -60,7 +60,5 @@ def build_device_data(device: AmazonDevice) -> dict[str, Any]: "online": device.online, "serial number": device.serial_number, "software version": device.software_version, - "do not disturb": device.do_not_disturb, - "response style": device.response_style, - "bluetooth state": device.bluetooth_state, + "sensors": device.sensors, } diff --git a/homeassistant/components/alexa_devices/icons.json b/homeassistant/components/alexa_devices/icons.json index bedd4af1734..f9e8de057d0 100644 --- a/homeassistant/components/alexa_devices/icons.json +++ b/homeassistant/components/alexa_devices/icons.json @@ -1,44 +1,4 @@ { - "entity": { - "binary_sensor": { - "bluetooth": { - "default": "mdi:bluetooth-off", - "state": { - "on": "mdi:bluetooth" - } - }, - "baby_cry_detection": { - "default": "mdi:account-voice-off", - "state": { - "on": "mdi:account-voice" - } - }, - "beeping_appliance_detection": { - "default": "mdi:bell-off", - "state": { - "on": "mdi:bell-ring" - } - }, - "cough_detection": { - "default": "mdi:blur-off", - "state": { - "on": "mdi:blur" - } - }, - "dog_bark_detection": { - "default": "mdi:dog-side-off", - "state": { - "on": "mdi:dog-side" - } - }, - "water_sounds_detection": { - "default": "mdi:water-pump-off", - "state": { - "on": "mdi:water-pump" - } - } - } - }, "services": { "send_sound": { "service": "mdi:cast-audio" diff --git a/homeassistant/components/alexa_devices/manifest.json b/homeassistant/components/alexa_devices/manifest.json index 437c11e0a4c..14b2ddf90d9 100644 --- a/homeassistant/components/alexa_devices/manifest.json +++ b/homeassistant/components/alexa_devices/manifest.json @@ -8,5 +8,5 @@ "iot_class": "cloud_polling", "loggers": ["aioamazondevices"], "quality_scale": "platinum", - "requirements": ["aioamazondevices==6.0.0"] + "requirements": ["aioamazondevices==6.2.6"] } diff --git a/homeassistant/components/alexa_devices/sensor.py b/homeassistant/components/alexa_devices/sensor.py index 1a863e87c1a..e6dbc251b95 100644 --- a/homeassistant/components/alexa_devices/sensor.py +++ b/homeassistant/components/alexa_devices/sensor.py @@ -31,6 +31,9 @@ class AmazonSensorEntityDescription(SensorEntityDescription): """Amazon Devices sensor entity description.""" native_unit_of_measurement_fn: Callable[[AmazonDevice, str], str] | None = None + is_available_fn: Callable[[AmazonDevice, str], bool] = lambda device, key: ( + device.online and device.sensors[key].error is False + ) SENSORS: Final = ( @@ -99,3 +102,13 @@ class AmazonSensorEntity(AmazonEntity, SensorEntity): def native_value(self) -> StateType: """Return the state of the sensor.""" return self.device.sensors[self.entity_description.key].value + + @property + def available(self) -> bool: + """Return if entity is available.""" + return ( + self.entity_description.is_available_fn( + self.device, self.entity_description.key + ) + and super().available + ) diff --git a/homeassistant/components/alexa_devices/strings.json b/homeassistant/components/alexa_devices/strings.json index 8e56a7a51b6..f6b850f0920 100644 --- a/homeassistant/components/alexa_devices/strings.json +++ b/homeassistant/components/alexa_devices/strings.json @@ -58,26 +58,6 @@ } }, "entity": { - "binary_sensor": { - "bluetooth": { - "name": "Bluetooth" - }, - "baby_cry_detection": { - "name": "Baby crying" - }, - "beeping_appliance_detection": { - "name": "Beeping appliance" - }, - "cough_detection": { - "name": "Coughing" - }, - "dog_bark_detection": { - "name": "Dog barking" - }, - "water_sounds_detection": { - "name": "Water sounds" - } - }, "notify": { "speak": { "name": "Speak" diff --git a/homeassistant/components/alexa_devices/switch.py b/homeassistant/components/alexa_devices/switch.py index 138013666c6..2994ab77751 100644 --- a/homeassistant/components/alexa_devices/switch.py +++ b/homeassistant/components/alexa_devices/switch.py @@ -8,13 +8,17 @@ from typing import TYPE_CHECKING, Any, Final from aioamazondevices.api import AmazonDevice -from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription +from homeassistant.components.switch import ( + DOMAIN as SWITCH_DOMAIN, + SwitchEntity, + SwitchEntityDescription, +) from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .coordinator import AmazonConfigEntry from .entity import AmazonEntity -from .utils import alexa_api_call +from .utils import alexa_api_call, async_update_unique_id PARALLEL_UPDATES = 1 @@ -24,16 +28,17 @@ class AmazonSwitchEntityDescription(SwitchEntityDescription): """Alexa Devices switch entity description.""" is_on_fn: Callable[[AmazonDevice], bool] - subkey: str + is_available_fn: Callable[[AmazonDevice, str], bool] = lambda device, key: ( + device.online and device.sensors[key].error is False + ) method: str SWITCHES: Final = ( AmazonSwitchEntityDescription( - key="do_not_disturb", - subkey="AUDIO_PLAYER", + key="dnd", translation_key="do_not_disturb", - is_on_fn=lambda _device: _device.do_not_disturb, + is_on_fn=lambda device: bool(device.sensors["dnd"].value), method="set_do_not_disturb", ), ) @@ -48,6 +53,11 @@ async def async_setup_entry( coordinator = entry.runtime_data + # Replace unique id for "DND" switch and remove from Speaker Group + await async_update_unique_id( + hass, coordinator, SWITCH_DOMAIN, "do_not_disturb", "dnd" + ) + known_devices: set[str] = set() def _check_device() -> None: @@ -59,7 +69,7 @@ async def async_setup_entry( AmazonSwitchEntity(coordinator, serial_num, switch_desc) for switch_desc in SWITCHES for serial_num in new_devices - if switch_desc.subkey in coordinator.data[serial_num].capabilities + if switch_desc.key in coordinator.data[serial_num].sensors ) _check_device() @@ -94,3 +104,13 @@ class AmazonSwitchEntity(AmazonEntity, SwitchEntity): def is_on(self) -> bool: """Return True if switch is on.""" return self.entity_description.is_on_fn(self.device) + + @property + def available(self) -> bool: + """Return if entity is available.""" + return ( + self.entity_description.is_available_fn( + self.device, self.entity_description.key + ) + and super().available + ) diff --git a/homeassistant/components/alexa_devices/utils.py b/homeassistant/components/alexa_devices/utils.py index 437b681413b..f8898aa5fe4 100644 --- a/homeassistant/components/alexa_devices/utils.py +++ b/homeassistant/components/alexa_devices/utils.py @@ -6,9 +6,12 @@ from typing import Any, Concatenate from aioamazondevices.exceptions import CannotConnect, CannotRetrieveData +from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError +import homeassistant.helpers.entity_registry as er -from .const import DOMAIN +from .const import _LOGGER, DOMAIN +from .coordinator import AmazonDevicesCoordinator from .entity import AmazonEntity @@ -38,3 +41,23 @@ def alexa_api_call[_T: AmazonEntity, **_P]( ) from err return cmd_wrapper + + +async def async_update_unique_id( + hass: HomeAssistant, + coordinator: AmazonDevicesCoordinator, + domain: str, + old_key: str, + new_key: str, +) -> None: + """Update unique id for entities created with old format.""" + entity_registry = er.async_get(hass) + + for serial_num in coordinator.data: + unique_id = f"{serial_num}-{old_key}" + if entity_id := entity_registry.async_get_entity_id(domain, DOMAIN, unique_id): + _LOGGER.debug("Updating unique_id for %s", entity_id) + new_unique_id = unique_id.replace(old_key, new_key) + + # Update the registry with the new unique_id + entity_registry.async_update_entity(entity_id, new_unique_id=new_unique_id) diff --git a/requirements_all.txt b/requirements_all.txt index 9b5031fa8c0..c22b7072ad9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -185,7 +185,7 @@ aioairzone-cloud==0.7.2 aioairzone==1.0.1 # homeassistant.components.alexa_devices -aioamazondevices==6.0.0 +aioamazondevices==6.2.6 # homeassistant.components.ambient_network # homeassistant.components.ambient_station diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6fc33e991bc..0f75a9d8bff 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -173,7 +173,7 @@ aioairzone-cloud==0.7.2 aioairzone==1.0.1 # homeassistant.components.alexa_devices -aioamazondevices==6.0.0 +aioamazondevices==6.2.6 # homeassistant.components.ambient_network # homeassistant.components.ambient_station diff --git a/tests/components/alexa_devices/const.py b/tests/components/alexa_devices/const.py index d078e92199e..05a6ff58719 100644 --- a/tests/components/alexa_devices/const.py +++ b/tests/components/alexa_devices/const.py @@ -18,15 +18,13 @@ TEST_DEVICE_1 = AmazonDevice( online=True, serial_number=TEST_DEVICE_1_SN, software_version="echo_test_software_version", - do_not_disturb=False, - response_style=None, - bluetooth_state=True, entity_id="11111111-2222-3333-4444-555555555555", - appliance_id="G1234567890123456789012345678A", + endpoint_id="G1234567890123456789012345678A", sensors={ + "dnd": AmazonDeviceSensor(name="dnd", value=False, error=False, scale=None), "temperature": AmazonDeviceSensor( - name="temperature", value="22.5", scale="CELSIUS" - ) + name="temperature", value="22.5", error=False, scale="CELSIUS" + ), }, ) @@ -42,14 +40,11 @@ TEST_DEVICE_2 = AmazonDevice( online=True, serial_number=TEST_DEVICE_2_SN, software_version="echo_test_2_software_version", - do_not_disturb=False, - response_style=None, - bluetooth_state=True, entity_id="11111111-2222-3333-4444-555555555555", - appliance_id="G1234567890123456789012345678A", + endpoint_id="G1234567890123456789012345678A", sensors={ "temperature": AmazonDeviceSensor( - name="temperature", value="22.5", scale="CELSIUS" + name="temperature", value="22.5", error=False, scale="CELSIUS" ) }, ) diff --git a/tests/components/alexa_devices/snapshots/test_binary_sensor.ambr b/tests/components/alexa_devices/snapshots/test_binary_sensor.ambr index 16f9eeaedae..c6b9a2afa08 100644 --- a/tests/components/alexa_devices/snapshots/test_binary_sensor.ambr +++ b/tests/components/alexa_devices/snapshots/test_binary_sensor.ambr @@ -1,52 +1,4 @@ # serializer version: 1 -# name: test_all_entities[binary_sensor.echo_test_bluetooth-entry] - EntityRegistryEntrySnapshot({ - 'aliases': set({ - }), - '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.echo_test_bluetooth', - 'has_entity_name': True, - 'hidden_by': None, - 'icon': None, - 'id': , - 'labels': set({ - }), - 'name': None, - 'options': dict({ - }), - 'original_device_class': None, - 'original_icon': None, - 'original_name': 'Bluetooth', - 'platform': 'alexa_devices', - 'previous_unique_id': None, - 'suggested_object_id': None, - 'supported_features': 0, - 'translation_key': 'bluetooth', - 'unique_id': 'echo_test_serial_number-bluetooth', - 'unit_of_measurement': None, - }) -# --- -# name: test_all_entities[binary_sensor.echo_test_bluetooth-state] - StateSnapshot({ - 'attributes': ReadOnlyDict({ - 'friendly_name': 'Echo Test Bluetooth', - }), - 'context': , - 'entity_id': 'binary_sensor.echo_test_bluetooth', - 'last_changed': , - 'last_reported': , - 'last_updated': , - 'state': 'on', - }) -# --- # name: test_all_entities[binary_sensor.echo_test_connectivity-entry] EntityRegistryEntrySnapshot({ 'aliases': set({ diff --git a/tests/components/alexa_devices/snapshots/test_diagnostics.ambr b/tests/components/alexa_devices/snapshots/test_diagnostics.ambr index 9ae5832ce33..2450d9e7d7b 100644 --- a/tests/components/alexa_devices/snapshots/test_diagnostics.ambr +++ b/tests/components/alexa_devices/snapshots/test_diagnostics.ambr @@ -2,7 +2,6 @@ # name: test_device_diagnostics dict({ 'account name': 'Echo Test', - 'bluetooth state': True, 'capabilities': list([ 'AUDIO_PLAYER', 'MICROPHONE', @@ -12,9 +11,17 @@ ]), 'device family': 'mine', 'device type': 'echo', - 'do not disturb': False, 'online': True, - 'response style': None, + 'sensors': dict({ + 'dnd': dict({ + '__type': "", + 'repr': "AmazonDeviceSensor(name='dnd', value=False, error=False, scale=None)", + }), + 'temperature': dict({ + '__type': "", + 'repr': "AmazonDeviceSensor(name='temperature', value='22.5', error=False, scale='CELSIUS')", + }), + }), 'serial number': 'echo_test_serial_number', 'software version': 'echo_test_software_version', }) @@ -25,7 +32,6 @@ 'devices': list([ dict({ 'account name': 'Echo Test', - 'bluetooth state': True, 'capabilities': list([ 'AUDIO_PLAYER', 'MICROPHONE', @@ -35,9 +41,17 @@ ]), 'device family': 'mine', 'device type': 'echo', - 'do not disturb': False, 'online': True, - 'response style': None, + 'sensors': dict({ + 'dnd': dict({ + '__type': "", + 'repr': "AmazonDeviceSensor(name='dnd', value=False, error=False, scale=None)", + }), + 'temperature': dict({ + '__type': "", + 'repr': "AmazonDeviceSensor(name='temperature', value='22.5', error=False, scale='CELSIUS')", + }), + }), 'serial number': 'echo_test_serial_number', 'software version': 'echo_test_software_version', }), diff --git a/tests/components/alexa_devices/snapshots/test_services.ambr b/tests/components/alexa_devices/snapshots/test_services.ambr index 12eab4a683b..dc15796c32c 100644 --- a/tests/components/alexa_devices/snapshots/test_services.ambr +++ b/tests/components/alexa_devices/snapshots/test_services.ambr @@ -4,8 +4,6 @@ tuple( dict({ 'account_name': 'Echo Test', - 'appliance_id': 'G1234567890123456789012345678A', - 'bluetooth_state': True, 'capabilities': list([ 'AUDIO_PLAYER', 'MICROPHONE', @@ -16,12 +14,18 @@ 'device_family': 'mine', 'device_owner_customer_id': 'amazon_ower_id', 'device_type': 'echo', - 'do_not_disturb': False, + 'endpoint_id': 'G1234567890123456789012345678A', 'entity_id': '11111111-2222-3333-4444-555555555555', 'online': True, - 'response_style': None, 'sensors': dict({ + 'dnd': dict({ + 'error': False, + 'name': 'dnd', + 'scale': None, + 'value': False, + }), 'temperature': dict({ + 'error': False, 'name': 'temperature', 'scale': 'CELSIUS', 'value': '22.5', @@ -41,8 +45,6 @@ tuple( dict({ 'account_name': 'Echo Test', - 'appliance_id': 'G1234567890123456789012345678A', - 'bluetooth_state': True, 'capabilities': list([ 'AUDIO_PLAYER', 'MICROPHONE', @@ -53,12 +55,18 @@ 'device_family': 'mine', 'device_owner_customer_id': 'amazon_ower_id', 'device_type': 'echo', - 'do_not_disturb': False, + 'endpoint_id': 'G1234567890123456789012345678A', 'entity_id': '11111111-2222-3333-4444-555555555555', 'online': True, - 'response_style': None, 'sensors': dict({ + 'dnd': dict({ + 'error': False, + 'name': 'dnd', + 'scale': None, + 'value': False, + }), 'temperature': dict({ + 'error': False, 'name': 'temperature', 'scale': 'CELSIUS', 'value': '22.5', diff --git a/tests/components/alexa_devices/snapshots/test_switch.ambr b/tests/components/alexa_devices/snapshots/test_switch.ambr index c622cc67ea7..3ce484cf95b 100644 --- a/tests/components/alexa_devices/snapshots/test_switch.ambr +++ b/tests/components/alexa_devices/snapshots/test_switch.ambr @@ -30,7 +30,7 @@ 'suggested_object_id': None, 'supported_features': 0, 'translation_key': 'do_not_disturb', - 'unique_id': 'echo_test_serial_number-do_not_disturb', + 'unique_id': 'echo_test_serial_number-dnd', 'unit_of_measurement': None, }) # --- diff --git a/tests/components/alexa_devices/test_sensor.py b/tests/components/alexa_devices/test_sensor.py index 560a7e10b90..3bb1b3f0a0d 100644 --- a/tests/components/alexa_devices/test_sensor.py +++ b/tests/components/alexa_devices/test_sensor.py @@ -134,10 +134,38 @@ async def test_unit_of_measurement( mock_amazon_devices_client.get_devices_data.return_value[ TEST_DEVICE_1_SN - ].sensors = {sensor: AmazonDeviceSensor(name=sensor, value=api_value, scale=scale)} + ].sensors = { + sensor: AmazonDeviceSensor( + name=sensor, value=api_value, error=False, scale=scale + ) + } await setup_integration(hass, mock_config_entry) assert (state := hass.states.get(entity_id)) assert state.state == state_value assert state.attributes["unit_of_measurement"] == unit + + +async def test_sensor_unavailable( + hass: HomeAssistant, + freezer: FrozenDateTimeFactory, + mock_amazon_devices_client: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test sensor is unavailable.""" + + entity_id = "sensor.echo_test_illuminance" + + mock_amazon_devices_client.get_devices_data.return_value[ + TEST_DEVICE_1_SN + ].sensors = { + "illuminance": AmazonDeviceSensor( + name="illuminance", value="800", error=True, scale=None + ) + } + + await setup_integration(hass, mock_config_entry) + + assert (state := hass.states.get(entity_id)) + assert state.state == STATE_UNAVAILABLE diff --git a/tests/components/alexa_devices/test_switch.py b/tests/components/alexa_devices/test_switch.py index c5039d68da2..6bbc1f68d02 100644 --- a/tests/components/alexa_devices/test_switch.py +++ b/tests/components/alexa_devices/test_switch.py @@ -1,7 +1,9 @@ """Tests for the Alexa Devices switch platform.""" +from copy import deepcopy from unittest.mock import AsyncMock, patch +from aioamazondevices.api import AmazonDeviceSensor from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion @@ -23,10 +25,12 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from . import setup_integration -from .conftest import TEST_DEVICE_1_SN +from .conftest import TEST_DEVICE_1, TEST_DEVICE_1_SN from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform +ENTITY_ID = "switch.echo_test_do_not_disturb" + @pytest.mark.usefixtures("entity_registry_enabled_by_default") async def test_all_entities( @@ -52,48 +56,59 @@ async def test_switch_dnd( """Test switching DND.""" await setup_integration(hass, mock_config_entry) - entity_id = "switch.echo_test_do_not_disturb" - - assert (state := hass.states.get(entity_id)) + assert (state := hass.states.get(ENTITY_ID)) assert state.state == STATE_OFF await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_ON, - {ATTR_ENTITY_ID: entity_id}, + {ATTR_ENTITY_ID: ENTITY_ID}, blocking=True, ) assert mock_amazon_devices_client.set_do_not_disturb.call_count == 1 - mock_amazon_devices_client.get_devices_data.return_value[ - TEST_DEVICE_1_SN - ].do_not_disturb = True + device_data = deepcopy(TEST_DEVICE_1) + device_data.sensors = { + "dnd": AmazonDeviceSensor(name="dnd", value=True, error=False, scale=None), + "temperature": AmazonDeviceSensor( + name="temperature", value="22.5", error=False, scale="CELSIUS" + ), + } + mock_amazon_devices_client.get_devices_data.return_value = { + TEST_DEVICE_1_SN: device_data + } freezer.tick(SCAN_INTERVAL) async_fire_time_changed(hass) await hass.async_block_till_done() - assert (state := hass.states.get(entity_id)) + assert (state := hass.states.get(ENTITY_ID)) assert state.state == STATE_ON await hass.services.async_call( SWITCH_DOMAIN, SERVICE_TURN_OFF, - {ATTR_ENTITY_ID: entity_id}, + {ATTR_ENTITY_ID: ENTITY_ID}, blocking=True, ) - mock_amazon_devices_client.get_devices_data.return_value[ - TEST_DEVICE_1_SN - ].do_not_disturb = False + device_data.sensors = { + "dnd": AmazonDeviceSensor(name="dnd", value=False, error=False, scale=None), + "temperature": AmazonDeviceSensor( + name="temperature", value="22.5", error=False, scale="CELSIUS" + ), + } + mock_amazon_devices_client.get_devices_data.return_value = { + TEST_DEVICE_1_SN: device_data + } freezer.tick(SCAN_INTERVAL) async_fire_time_changed(hass) await hass.async_block_till_done() assert mock_amazon_devices_client.set_do_not_disturb.call_count == 2 - assert (state := hass.states.get(entity_id)) + assert (state := hass.states.get(ENTITY_ID)) assert state.state == STATE_OFF @@ -104,16 +119,13 @@ async def test_offline_device( mock_config_entry: MockConfigEntry, ) -> None: """Test offline device handling.""" - - entity_id = "switch.echo_test_do_not_disturb" - mock_amazon_devices_client.get_devices_data.return_value[ TEST_DEVICE_1_SN ].online = False await setup_integration(hass, mock_config_entry) - assert (state := hass.states.get(entity_id)) + assert (state := hass.states.get(ENTITY_ID)) assert state.state == STATE_UNAVAILABLE mock_amazon_devices_client.get_devices_data.return_value[ @@ -124,5 +136,5 @@ async def test_offline_device( async_fire_time_changed(hass) await hass.async_block_till_done() - assert (state := hass.states.get(entity_id)) + assert (state := hass.states.get(ENTITY_ID)) assert state.state != STATE_UNAVAILABLE diff --git a/tests/components/alexa_devices/test_utils.py b/tests/components/alexa_devices/test_utils.py index 1cf190bd297..020971d8f76 100644 --- a/tests/components/alexa_devices/test_utils.py +++ b/tests/components/alexa_devices/test_utils.py @@ -10,8 +10,10 @@ from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SERVICE_TUR from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import device_registry as dr, entity_registry as er from . import setup_integration +from .const import TEST_DEVICE_1_SN from tests.common import MockConfigEntry @@ -54,3 +56,41 @@ async def test_alexa_api_call_exceptions( assert exc_info.value.translation_domain == DOMAIN assert exc_info.value.translation_key == key assert exc_info.value.translation_placeholders == {"error": error} + + +async def test_alexa_unique_id_migration( + hass: HomeAssistant, + mock_amazon_devices_client: AsyncMock, + mock_config_entry: MockConfigEntry, + device_registry: dr.DeviceRegistry, + entity_registry: er.EntityRegistry, +) -> None: + """Test unique_id migration.""" + + mock_config_entry.add_to_hass(hass) + + device = device_registry.async_get_or_create( + config_entry_id=mock_config_entry.entry_id, + identifiers={(DOMAIN, mock_config_entry.entry_id)}, + name=mock_config_entry.title, + manufacturer="Amazon", + model="Echo Dot", + entry_type=dr.DeviceEntryType.SERVICE, + ) + + entity = entity_registry.async_get_or_create( + SWITCH_DOMAIN, + DOMAIN, + unique_id=f"{TEST_DEVICE_1_SN}-do_not_disturb", + device_id=device.id, + config_entry=mock_config_entry, + has_entity_name=True, + ) + + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + migrated_entity = entity_registry.async_get(entity.entity_id) + assert migrated_entity is not None + assert migrated_entity.config_entry_id == mock_config_entry.entry_id + assert migrated_entity.unique_id == f"{TEST_DEVICE_1_SN}-dnd"