diff --git a/homeassistant/components/homekit_controller/__init__.py b/homeassistant/components/homekit_controller/__init__.py index 4a8730b2e9e..beddd915fc0 100644 --- a/homeassistant/components/homekit_controller/__init__.py +++ b/homeassistant/components/homekit_controller/__init__.py @@ -13,7 +13,6 @@ from aiohomekit.model.services import Service, ServicesTypes from homeassistant.components import zeroconf from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers import device_registry as dr from homeassistant.helpers.entity import Entity from .config_flow import normalize_hkid @@ -190,21 +189,6 @@ async def async_setup_entry(hass, entry): del hass.data[KNOWN_DEVICES][conn.unique_id] raise ConfigEntryNotReady - conn_info = conn.connection_info - - device_registry = await dr.async_get_registry(hass) - device_registry.async_get_or_create( - config_entry_id=entry.entry_id, - identifiers={ - (DOMAIN, "serial-number", conn_info["serial-number"]), - (DOMAIN, "accessory-id", conn.unique_id), - }, - name=conn.name, - manufacturer=conn_info.get("manufacturer"), - model=conn_info.get("model"), - sw_version=conn_info.get("firmware.revision"), - ) - return True diff --git a/homeassistant/components/homekit_controller/connection.py b/homeassistant/components/homekit_controller/connection.py index 9d8eb00b547..a5deb8aa9bc 100644 --- a/homeassistant/components/homekit_controller/connection.py +++ b/homeassistant/components/homekit_controller/connection.py @@ -89,6 +89,10 @@ class HKDevice: # mapped to a HA entity. self.entities = [] + # A map of aid -> device_id + # Useful when routing events to triggers + self.devices = {} + self.available = True self.signal_state_updated = "_".join((DOMAIN, self.unique_id, "state_updated")) @@ -162,6 +166,59 @@ class HKDevice: return True + async def async_create_devices(self): + """ + Build device registry entries for all accessories paired with the bridge. + + This is done as well as by the entities for 2 reasons. First, the bridge + might not have any entities attached to it. Secondly there are stateless + entities like doorbells and remote controls. + """ + device_registry = await self.hass.helpers.device_registry.async_get_registry() + + devices = {} + + for accessory in self.entity_map.accessories: + info = accessory.services.first( + service_type=ServicesTypes.ACCESSORY_INFORMATION, + ) + + device_info = { + "identifiers": { + ( + DOMAIN, + "serial-number", + info.value(CharacteristicsTypes.SERIAL_NUMBER), + ) + }, + "name": info.value(CharacteristicsTypes.NAME), + "manufacturer": info.value(CharacteristicsTypes.MANUFACTURER, ""), + "model": info.value(CharacteristicsTypes.MODEL, ""), + "sw_version": info.value(CharacteristicsTypes.FIRMWARE_REVISION, ""), + } + + if accessory.aid == 1: + # Accessory 1 is the root device (sometimes the only device, sometimes a bridge) + # Link the root device to the pairing id for the connection. + device_info["identifiers"].add((DOMAIN, "accessory-id", self.unique_id)) + else: + # Every pairing has an accessory 1 + # It *doesn't* have a via_device, as it is the device we are connecting to + # Every other accessory should use it as its via device. + device_info["via_device"] = ( + DOMAIN, + "serial-number", + self.connection_info["serial-number"], + ) + + device = device_registry.async_get_or_create( + config_entry_id=self.config_entry.entry_id, **device_info, + ) + + devices[accessory.aid] = device.id + + self.devices = devices + async def async_process_entity_map(self): """ Process the entity map and load any platforms or entities that need adding. @@ -177,6 +234,8 @@ class HKDevice: await self.async_load_platforms() + await self.async_create_devices() + self.add_entities() if self.watchable_characteristics: