From 54c5c94f97e24980392163340a71cd22c1fb6789 Mon Sep 17 00:00:00 2001 From: wilburCforce <109390391+wilburCforce@users.noreply.github.com> Date: Mon, 19 Feb 2024 02:30:58 -0600 Subject: [PATCH] Fix uuid issue in Lutron (#110524) --- homeassistant/components/lutron/__init__.py | 110 +++++++++++++++++++- homeassistant/components/lutron/entity.py | 8 +- 2 files changed, 112 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/lutron/__init__.py b/homeassistant/components/lutron/__init__.py index ad1cbfe5ca6..ad69a14c0f5 100644 --- a/homeassistant/components/lutron/__init__.py +++ b/homeassistant/components/lutron/__init__.py @@ -16,8 +16,8 @@ from homeassistant.const import ( ) from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from homeassistant.helpers import device_registry as dr, entity_registry as er import homeassistant.helpers.config_validation as cv -import homeassistant.helpers.device_registry as dr from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue from homeassistant.helpers.typing import ConfigType from homeassistant.util import slugify @@ -186,6 +186,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b lutron_client.connect() _LOGGER.info("Connected to main repeater at %s", host) + entity_registry = er.async_get(hass) + device_registry = dr.async_get(hass) + entry_data = LutronData( client=lutron_client, binary_sensors=[], @@ -201,17 +204,39 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b for area in lutron_client.areas: _LOGGER.debug("Working on area %s", area.name) for output in area.outputs: + platform = None _LOGGER.debug("Working on output %s", output.type) if output.type == "SYSTEM_SHADE": entry_data.covers.append((area.name, output)) + platform = Platform.COVER elif output.type == "CEILING_FAN_TYPE": entry_data.fans.append((area.name, output)) + platform = Platform.FAN # Deprecated, should be removed in 2024.8 entry_data.lights.append((area.name, output)) elif output.is_dimmable: entry_data.lights.append((area.name, output)) + platform = Platform.LIGHT else: entry_data.switches.append((area.name, output)) + platform = Platform.SWITCH + + _async_check_entity_unique_id( + hass, + entity_registry, + platform, + output.uuid, + output.legacy_uuid, + entry_data.client.guid, + ) + _async_check_device_identifiers( + hass, + device_registry, + output.uuid, + output.legacy_uuid, + entry_data.client.guid, + ) + for keypad in area.keypads: for button in keypad.buttons: # If the button has a function assigned to it, add it as a scene @@ -228,11 +253,46 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b ) entry_data.scenes.append((area.name, keypad, button, led)) + platform = Platform.SCENE + _async_check_entity_unique_id( + hass, + entity_registry, + platform, + button.uuid, + button.legacy_uuid, + entry_data.client.guid, + ) + if led is not None: + platform = Platform.SWITCH + _async_check_entity_unique_id( + hass, + entity_registry, + platform, + led.uuid, + led.legacy_uuid, + entry_data.client.guid, + ) + entry_data.buttons.append(LutronButton(hass, area.name, keypad, button)) if area.occupancy_group is not None: entry_data.binary_sensors.append((area.name, area.occupancy_group)) + platform = Platform.BINARY_SENSOR + _async_check_entity_unique_id( + hass, + entity_registry, + platform, + area.occupancy_group.uuid, + area.occupancy_group.legacy_uuid, + entry_data.client.guid, + ) + _async_check_device_identifiers( + hass, + device_registry, + area.occupancy_group.uuid, + area.occupancy_group.legacy_uuid, + entry_data.client.guid, + ) - device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=config_entry.entry_id, identifiers={(DOMAIN, lutron_client.guid)}, @@ -247,6 +307,52 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b return True +def _async_check_entity_unique_id( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + platform: str, + uuid: str, + legacy_uuid: str, + controller_guid: str, +) -> None: + """If uuid becomes available update to use it.""" + + if not uuid: + return + + unique_id = f"{controller_guid}_{legacy_uuid}" + entity_id = entity_registry.async_get_entity_id( + domain=platform, platform=DOMAIN, unique_id=unique_id + ) + + if entity_id: + new_unique_id = f"{controller_guid}_{uuid}" + _LOGGER.debug("Updating entity id from %s to %s", unique_id, new_unique_id) + entity_registry.async_update_entity(entity_id, new_unique_id=new_unique_id) + + +def _async_check_device_identifiers( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + uuid: str, + legacy_uuid: str, + controller_guid: str, +) -> None: + """If uuid becomes available update to use it.""" + + if not uuid: + return + + unique_id = f"{controller_guid}_{legacy_uuid}" + device = device_registry.async_get_device(identifiers={(DOMAIN, unique_id)}) + if device: + new_unique_id = f"{controller_guid}_{uuid}" + _LOGGER.debug("Updating device id from %s to %s", unique_id, new_unique_id) + device_registry.async_update_device( + device.id, new_identifiers={(DOMAIN, new_unique_id)} + ) + + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Clean up resources and entities associated with the integration.""" return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/lutron/entity.py b/homeassistant/components/lutron/entity.py index 461e5acb56d..3910ecfa0ba 100644 --- a/homeassistant/components/lutron/entity.py +++ b/homeassistant/components/lutron/entity.py @@ -41,11 +41,11 @@ class LutronBaseEntity(Entity): self.schedule_update_ha_state() @property - def unique_id(self) -> str | None: + def unique_id(self) -> str: """Return a unique ID.""" - # Temporary fix for https://github.com/thecynic/pylutron/issues/70 + if self._lutron_device.uuid is None: - return None + return f"{self._controller.guid}_{self._lutron_device.legacy_uuid}" return f"{self._controller.guid}_{self._lutron_device.uuid}" def update(self) -> None: @@ -63,7 +63,7 @@ class LutronDevice(LutronBaseEntity): """Initialize the device.""" super().__init__(area_name, lutron_device, controller) self._attr_device_info = DeviceInfo( - identifiers={(DOMAIN, lutron_device.uuid)}, + identifiers={(DOMAIN, self.unique_id)}, manufacturer="Lutron", name=lutron_device.name, suggested_area=area_name,