From abf67c3153e971a76ee6f4752d9f524a8fc5e822 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Tue, 28 Jun 2022 22:45:25 +0200 Subject: [PATCH] Normalize deCONZ binary sensor unique IDs (#73657) --- .../components/deconz/binary_sensor.py | 30 +++++++++++-- tests/components/deconz/test_binary_sensor.py | 43 +++++++++++++------ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/deconz/binary_sensor.py b/homeassistant/components/deconz/binary_sensor.py index d109fb8b34d..0d090751edd 100644 --- a/homeassistant/components/deconz/binary_sensor.py +++ b/homeassistant/components/deconz/binary_sensor.py @@ -27,8 +27,9 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity_platform import AddEntitiesCallback +import homeassistant.helpers.entity_registry as er -from .const import ATTR_DARK, ATTR_ON +from .const import ATTR_DARK, ATTR_ON, DOMAIN as DECONZ_DOMAIN from .deconz_device import DeconzDevice from .gateway import DeconzGateway, get_gateway_from_config_entry @@ -179,6 +180,27 @@ BINARY_SENSOR_DESCRIPTIONS = [ ] +@callback +def async_update_unique_id( + hass: HomeAssistant, unique_id: str, description: DeconzBinarySensorDescription +) -> None: + """Update unique ID to always have a suffix. + + Introduced with release 2022.7. + """ + ent_reg = er.async_get(hass) + + new_unique_id = f"{unique_id}-{description.key}" + if ent_reg.async_get_entity_id(DOMAIN, DECONZ_DOMAIN, new_unique_id): + return + + if description.suffix: + unique_id = f'{unique_id.split("-", 1)[0]}-{description.suffix.lower()}' + + if entity_id := ent_reg.async_get_entity_id(DOMAIN, DECONZ_DOMAIN, unique_id): + ent_reg.async_update_entity(entity_id, new_unique_id=new_unique_id) + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, @@ -205,6 +227,8 @@ async def async_setup_entry( ): continue + async_update_unique_id(hass, sensor.unique_id, description) + async_add_entities([DeconzBinarySensor(sensor, gateway, description)]) gateway.register_platform_add_device_callback( @@ -255,9 +279,7 @@ class DeconzBinarySensor(DeconzDevice, BinarySensorEntity): @property def unique_id(self) -> str: """Return a unique identifier for this device.""" - if self.entity_description.suffix: - return f"{self.serial}-{self.entity_description.suffix.lower()}" - return super().unique_id + return f"{super().unique_id}-{self.entity_description.key}" @callback def async_update_callback(self) -> None: diff --git a/tests/components/deconz/test_binary_sensor.py b/tests/components/deconz/test_binary_sensor.py index b6a21fb9fa6..ae62205c636 100644 --- a/tests/components/deconz/test_binary_sensor.py +++ b/tests/components/deconz/test_binary_sensor.py @@ -4,7 +4,7 @@ from unittest.mock import patch import pytest -from homeassistant.components.binary_sensor import BinarySensorDeviceClass +from homeassistant.components.binary_sensor import DOMAIN, BinarySensorDeviceClass from homeassistant.components.deconz.const import ( CONF_ALLOW_CLIP_SENSOR, CONF_ALLOW_NEW_DEVICES, @@ -63,7 +63,8 @@ TEST_DATA = [ "entity_count": 3, "device_count": 3, "entity_id": "binary_sensor.alarm_10", - "unique_id": "00:15:8d:00:02:b5:d1:80-01-0500", + "unique_id": "00:15:8d:00:02:b5:d1:80-01-0500-alarm", + "old_unique_id": "00:15:8d:00:02:b5:d1:80-01-0500", "state": STATE_OFF, "entity_category": None, "device_class": BinarySensorDeviceClass.SAFETY, @@ -104,7 +105,8 @@ TEST_DATA = [ "entity_count": 4, "device_count": 3, "entity_id": "binary_sensor.cave_co", - "unique_id": "00:15:8d:00:02:a5:21:24-01-0101", + "unique_id": "00:15:8d:00:02:a5:21:24-01-0101-carbon_monoxide", + "old_unique_id": "00:15:8d:00:02:a5:21:24-01-0101", "state": STATE_OFF, "entity_category": None, "device_class": BinarySensorDeviceClass.CO, @@ -139,7 +141,8 @@ TEST_DATA = [ "entity_count": 2, "device_count": 3, "entity_id": "binary_sensor.sensor_kitchen_smoke", - "unique_id": "00:15:8d:00:01:d9:3e:7c-01-0500", + "unique_id": "00:15:8d:00:01:d9:3e:7c-01-0500-fire", + "old_unique_id": "00:15:8d:00:01:d9:3e:7c-01-0500", "state": STATE_OFF, "entity_category": None, "device_class": BinarySensorDeviceClass.SMOKE, @@ -175,7 +178,8 @@ TEST_DATA = [ "entity_count": 2, "device_count": 3, "entity_id": "binary_sensor.sensor_kitchen_smoke_test_mode", - "unique_id": "00:15:8d:00:01:d9:3e:7c-test mode", + "unique_id": "00:15:8d:00:01:d9:3e:7c-01-0500-in_test_mode", + "old_unique_id": "00:15:8d:00:01:d9:3e:7c-test mode", "state": STATE_OFF, "entity_category": EntityCategory.DIAGNOSTIC, "device_class": BinarySensorDeviceClass.SMOKE, @@ -207,7 +211,8 @@ TEST_DATA = [ "entity_count": 1, "device_count": 2, "entity_id": "binary_sensor.kitchen_switch", - "unique_id": "kitchen-switch", + "unique_id": "kitchen-switch-flag", + "old_unique_id": "kitchen-switch", "state": STATE_ON, "entity_category": None, "device_class": None, @@ -244,7 +249,8 @@ TEST_DATA = [ "entity_count": 3, "device_count": 3, "entity_id": "binary_sensor.back_door", - "unique_id": "00:15:8d:00:02:2b:96:b4-01-0006", + "unique_id": "00:15:8d:00:02:2b:96:b4-01-0006-open", + "old_unique_id": "00:15:8d:00:02:2b:96:b4-01-0006", "state": STATE_OFF, "entity_category": None, "device_class": BinarySensorDeviceClass.OPENING, @@ -290,7 +296,8 @@ TEST_DATA = [ "entity_count": 3, "device_count": 3, "entity_id": "binary_sensor.motion_sensor_4", - "unique_id": "00:17:88:01:03:28:8c:9b-02-0406", + "unique_id": "00:17:88:01:03:28:8c:9b-02-0406-presence", + "old_unique_id": "00:17:88:01:03:28:8c:9b-02-0406", "state": STATE_OFF, "entity_category": None, "device_class": BinarySensorDeviceClass.MOTION, @@ -331,7 +338,8 @@ TEST_DATA = [ "entity_count": 5, "device_count": 3, "entity_id": "binary_sensor.water2", - "unique_id": "00:15:8d:00:02:2f:07:db-01-0500", + "unique_id": "00:15:8d:00:02:2f:07:db-01-0500-water", + "old_unique_id": "00:15:8d:00:02:2f:07:db-01-0500", "state": STATE_OFF, "entity_category": None, "device_class": BinarySensorDeviceClass.MOISTURE, @@ -376,7 +384,8 @@ TEST_DATA = [ "entity_count": 3, "device_count": 3, "entity_id": "binary_sensor.vibration_1", - "unique_id": "00:15:8d:00:02:a5:21:24-01-0101", + "unique_id": "00:15:8d:00:02:a5:21:24-01-0101-vibration", + "old_unique_id": "00:15:8d:00:02:a5:21:24-01-0101", "state": STATE_ON, "entity_category": None, "device_class": BinarySensorDeviceClass.VIBRATION, @@ -414,7 +423,8 @@ TEST_DATA = [ "entity_count": 4, "device_count": 3, "entity_id": "binary_sensor.presence_sensor_tampered", - "unique_id": "00:00:00:00:00:00:00:00-tampered", + "unique_id": "00:00:00:00:00:00:00:00-00-tampered", + "old_unique_id": "00:00:00:00:00:00:00:00-tampered", "state": STATE_OFF, "entity_category": EntityCategory.DIAGNOSTIC, "device_class": BinarySensorDeviceClass.TAMPER, @@ -447,7 +457,8 @@ TEST_DATA = [ "entity_count": 4, "device_count": 3, "entity_id": "binary_sensor.presence_sensor_low_battery", - "unique_id": "00:00:00:00:00:00:00:00-low battery", + "unique_id": "00:00:00:00:00:00:00:00-00-low_battery", + "old_unique_id": "00:00:00:00:00:00:00:00-low battery", "state": STATE_OFF, "entity_category": EntityCategory.DIAGNOSTIC, "device_class": BinarySensorDeviceClass.BATTERY, @@ -470,6 +481,14 @@ async def test_binary_sensors( ent_reg = er.async_get(hass) dev_reg = dr.async_get(hass) + # Create entity entry to migrate to new unique ID + ent_reg.async_get_or_create( + DOMAIN, + DECONZ_DOMAIN, + expected["old_unique_id"], + suggested_object_id=expected["entity_id"].replace(DOMAIN, ""), + ) + with patch.dict(DECONZ_WEB_REQUEST, {"sensors": {"1": sensor_data}}): config_entry = await setup_deconz_integration( hass, aioclient_mock, options={CONF_ALLOW_CLIP_SENSOR: True}