From 6ee7011f7e91c0902108853cf237510b945a8d34 Mon Sep 17 00:00:00 2001 From: Xavi Esteve Date: Fri, 13 Feb 2026 19:48:28 +0100 Subject: [PATCH 1/5] Fix: group multi-function sub-devices under a single HA device VALLHORN and MYGGSPRAY expose multiple sub-devices (motion + illuminance) with different IDs but the same relation_id. Using the raw device ID as the HA device identifier caused each sub-device to appear separately. Use relation_id as the HA device identifier so sibling sub-devices are grouped under one device. Falls back to id for standalone devices. Also resolves sub-device naming: when a sub-device has no custom_name, it inherits the name from a sibling that does, avoiding garbled IDs. Fixes #7 Co-Authored-By: Claude Opus 4.6 --- .../dirigera_platform/base_classes.py | 21 +++++++++++++++++-- .../dirigera_platform/device_trigger.py | 7 ++----- .../dirigera_platform/hub_event_listener.py | 9 ++++---- custom_components/dirigera_platform/light.py | 2 +- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/custom_components/dirigera_platform/base_classes.py b/custom_components/dirigera_platform/base_classes.py index 9a408b5..80e108a 100644 --- a/custom_components/dirigera_platform/base_classes.py +++ b/custom_components/dirigera_platform/base_classes.py @@ -92,11 +92,19 @@ def available(self): def should_register_with_listener(self): return True + @property + def device_identifier(self) -> str: + """Return the identifier used for HA device grouping. + Uses relation_id to group sub-devices of the same physical hardware.""" + if self._json_data.relation_id: + return self._json_data.relation_id + return self._json_data.id + @property def device_info(self) -> DeviceInfo: - + return DeviceInfo( - identifiers={("dirigera_platform", self._json_data.id)}, + identifiers={("dirigera_platform", self.device_identifier)}, name=self.name, manufacturer=self._json_data.attributes.manufacturer, model=self._json_data.attributes.model, @@ -107,6 +115,15 @@ def device_info(self) -> DeviceInfo: @property def name(self): if self._json_data.attributes.custom_name is None or len(self._json_data.attributes.custom_name) == 0: + # For sub-devices with relation_id, try to get name from a sibling + if self._json_data.relation_id: + for reg_entry in hub_event_listener.device_registry.values(): + sibling = reg_entry.entity + if (hasattr(sibling, '_json_data') + and sibling._json_data.relation_id == self._json_data.relation_id + and sibling._json_data.id != self._json_data.id + and sibling._json_data.attributes.custom_name): + return sibling._json_data.attributes.custom_name return self.unique_id return self._json_data.attributes.custom_name diff --git a/custom_components/dirigera_platform/device_trigger.py b/custom_components/dirigera_platform/device_trigger.py index 404aaa1..c03dd2c 100644 --- a/custom_components/dirigera_platform/device_trigger.py +++ b/custom_components/dirigera_platform/device_trigger.py @@ -42,11 +42,8 @@ async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict[s registry_entity = registry_entry.entity - if "identifiers" not in registry_entity.device_info or len(list(registry_entity.device_info["identifiers"])[0]) < 2: - logger.warning(f"entity_id: {entity_id} corresponding in dirigera_platform entity doesnt have identifiers or isnt 2 entries long, device_info : {registry_entity.device_info}") - logger.info(registry_entity.device_info["identifiers"]) - registry_entity_id = list(registry_entity.device_info["identifiers"])[0][1] - + registry_entity_id = registry_entity.unique_id + if registry_entity_id != entity_id: logger.error(f"Found controller with entity id : {registry_entity_id} but doesnt match requested entity id: {entity_id}") continue diff --git a/custom_components/dirigera_platform/hub_event_listener.py b/custom_components/dirigera_platform/hub_event_listener.py index 7b5b94f..e0fa7fa 100644 --- a/custom_components/dirigera_platform/hub_event_listener.py +++ b/custom_components/dirigera_platform/hub_event_listener.py @@ -165,7 +165,8 @@ async def sync_all_device_areas(self): entity = registry_entry.entity if hasattr(entity, '_json_data') and entity._json_data.room is not None: room_name = entity._json_data.room.name - await self._update_device_area(device_id, room_name) + identifier = entity._json_data.relation_id or entity._json_data.id + await self._update_device_area(identifier, room_name) synced_count += 1 except Exception as ex: logger.error(f"Failed to sync area for device {device_id}: {ex}") @@ -536,7 +537,7 @@ def on_message(self, ws:Any, ws_msg:str): # but not yet in the HA device registry try: self._hass.loop.call_soon_threadsafe( - lambda room=new_room.name, device_id=id: self._hass.async_create_task( + lambda room=new_room.name, device_id=(entity._json_data.relation_id or id): self._hass.async_create_task( self._update_device_area(device_id, room) ) ) @@ -549,7 +550,7 @@ def on_message(self, ws:Any, ws_msg:str): room_changed = True try: self._hass.loop.call_soon_threadsafe( - lambda device_id=id: self._hass.async_create_task( + lambda device_id=(entity._json_data.relation_id or id): self._hass.async_create_task( self._update_device_area(device_id, "") ) ) @@ -621,7 +622,7 @@ def on_message(self, ws:Any, ws_msg:str): if name_changed and new_name is not None: try: self._hass.loop.call_soon_threadsafe( - lambda name=new_name, device_id=id: self._hass.async_create_task( + lambda name=new_name, device_id=(entity._json_data.relation_id or id): self._hass.async_create_task( self._update_device_name(device_id, name) ) ) diff --git a/custom_components/dirigera_platform/light.py b/custom_components/dirigera_platform/light.py index 1848870..dd0b60f 100644 --- a/custom_components/dirigera_platform/light.py +++ b/custom_components/dirigera_platform/light.py @@ -193,7 +193,7 @@ def available(self): def device_info(self) -> DeviceInfo: return DeviceInfo( - identifiers={("dirigera_platform", self._json_data.id)}, + identifiers={("dirigera_platform", self._json_data.relation_id or self._json_data.id)}, name=self.name, manufacturer=self._json_data.attributes.manufacturer, model=self._json_data.attributes.model, From a5da96d152501629d37f0a49263cfb3ffa6d8e1e Mon Sep 17 00:00:00 2001 From: nrbrt Date: Tue, 17 Feb 2026 10:58:47 +0100 Subject: [PATCH 2/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d95e68b..005b83e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## IKEA Dirigera Hub Integration -This is an actively maintained fork of [sanjoyg/dirigera_platform](https://github.com/sanjoyg/dirigera_platform), which appears to be abandoned (last activity March 2025). This integration connects Home Assistant with the IKEA Dirigera hub, built on the [dirigera](https://github.com/Leggin/dirigera) Python library by Nicolas Hilberg. +This is an actively maintained fork of [sanjoyg/dirigera_platform](https://github.com/sanjoyg/dirigera_platform), which was stagnant for a while, but the upstream maintainer resumed working on it, so I recommend to switch back to the upstream repository by SanjoyG. I will keep this respository up and may or may not continue working on it separately from the upstream repo.. This integration connects Home Assistant with the IKEA Dirigera hub, built on the [dirigera](https://github.com/Leggin/dirigera) Python library by Nicolas Hilberg. This fork addresses most of the outstanding issues from the upstream repository. Contributions are welcome — feel free to open [issues](https://github.com/nrbrt/dirigera_platform/issues) or submit pull requests. From 8fe625563bb6aef76f5561b3433849c46b6ce371 Mon Sep 17 00:00:00 2001 From: nrbrt Date: Tue, 17 Feb 2026 10:59:57 +0100 Subject: [PATCH 3/5] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 005b83e..de13f04 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ ## IKEA Dirigera Hub Integration -This is an actively maintained fork of [sanjoyg/dirigera_platform](https://github.com/sanjoyg/dirigera_platform), which was stagnant for a while, but the upstream maintainer resumed working on it, so I recommend to switch back to the upstream repository by SanjoyG. I will keep this respository up and may or may not continue working on it separately from the upstream repo.. This integration connects Home Assistant with the IKEA Dirigera hub, built on the [dirigera](https://github.com/Leggin/dirigera) Python library by Nicolas Hilberg. +This is a fork of [sanjoyg/dirigera_platform](https://github.com/sanjoyg/dirigera_platform), which was stagnant for a while, but the upstream maintainer resumed working on it, so I recommend to switch back to the upstream repository by SanjoyG. I will keep this respository up and may or may not continue working on it separately from the upstream repo. + +This integration connects Home Assistant with the IKEA Dirigera hub, built on the [dirigera](https://github.com/Leggin/dirigera) Python library by Nicolas Hilberg. This fork addresses most of the outstanding issues from the upstream repository. Contributions are welcome — feel free to open [issues](https://github.com/nrbrt/dirigera_platform/issues) or submit pull requests. From 7bc09c372547b850014bd7c8bbd1f38ca1f79095 Mon Sep 17 00:00:00 2001 From: nrbrt Date: Tue, 17 Feb 2026 11:02:00 +0100 Subject: [PATCH 4/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de13f04..2ab2f4c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## IKEA Dirigera Hub Integration -This is a fork of [sanjoyg/dirigera_platform](https://github.com/sanjoyg/dirigera_platform), which was stagnant for a while, but the upstream maintainer resumed working on it, so I recommend to switch back to the upstream repository by SanjoyG. I will keep this respository up and may or may not continue working on it separately from the upstream repo. +This is a fork of [sanjoyg/dirigera_platform](https://github.com/sanjoyg/dirigera_platform), which was stagnant for a while, but the upstream maintainer resumed working on it, so I recommend to switch back to the upstream repository by SanjoyG. I will keep this respository up and may or may not continue working on it separately from the upstream repo and will post PR requests upstream when I add or improve things. This integration connects Home Assistant with the IKEA Dirigera hub, built on the [dirigera](https://github.com/Leggin/dirigera) Python library by Nicolas Hilberg. From 80d52e60e9f6782aa05e57eb86808f3511660a02 Mon Sep 17 00:00:00 2001 From: Pablo Toledo Date: Tue, 24 Mar 2026 18:31:28 +0100 Subject: [PATCH 5/5] fix: register ikea_bulb_device_set with registry_entry and handle connection errors with ConfigEntryNotReady --- custom_components/dirigera_platform/__init__.py | 8 +++++++- custom_components/dirigera_platform/light.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/custom_components/dirigera_platform/__init__.py b/custom_components/dirigera_platform/__init__.py index 8a89a5e..9950caa 100644 --- a/custom_components/dirigera_platform/__init__.py +++ b/custom_components/dirigera_platform/__init__.py @@ -12,6 +12,7 @@ import voluptuous as vol from homeassistant import config_entries, core +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.components.light import PLATFORM_SCHEMA from homeassistant.const import CONF_IP_ADDRESS, CONF_TOKEN, Platform @@ -195,7 +196,12 @@ async def async_setup_entry( platform = ikea_gateway() hass.data[DOMAIN][PLATFORM] = platform logger.debug("Starting make_devices...") - await platform.make_devices(hass,hass_data[CONF_IP_ADDRESS], hass_data[CONF_TOKEN]) + try: + await platform.make_devices(hass,hass_data[CONF_IP_ADDRESS], hass_data[CONF_TOKEN]) + except (ConnectionError, OSError) as err: + raise ConfigEntryNotReady( + f"Cannot connect to IKEA Dirigera hub at {hass_data[CONF_IP_ADDRESS]}: {err}" + ) from err #await hass.async_add_executor_job(platform.make_devices,hass, hass_data[CONF_IP_ADDRESS], hass_data[CONF_TOKEN]) diff --git a/custom_components/dirigera_platform/light.py b/custom_components/dirigera_platform/light.py index dd0b60f..046700f 100644 --- a/custom_components/dirigera_platform/light.py +++ b/custom_components/dirigera_platform/light.py @@ -394,7 +394,7 @@ def available(self): def device_info(self) -> DeviceInfo: # Register the device for updates - hub_event_listener.register(self.unique_id, self) + hub_event_listener.register(self.unique_id, registry_entry(self)) return DeviceInfo( identifiers={("dirigera_platform", self._device_set.id)},