De-duplicate generation of DeviceInfo data in homekit_controller (#64751)

This commit is contained in:
Jc2k 2022-01-23 21:57:16 +00:00 committed by GitHub
parent fbe2b81cd4
commit f71e053947
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 85 deletions

View File

@ -15,7 +15,7 @@ from aiohomekit.model.services import Service, ServicesTypes
from homeassistant.components import zeroconf from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_VIA_DEVICE, EVENT_HOMEASSISTANT_STOP from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity import DeviceInfo, Entity
@ -23,15 +23,7 @@ from homeassistant.helpers.typing import ConfigType
from .config_flow import normalize_hkid from .config_flow import normalize_hkid
from .connection import HKDevice, valid_serial_number from .connection import HKDevice, valid_serial_number
from .const import ( from .const import CONTROLLER, ENTITY_MAP, KNOWN_DEVICES, TRIGGERS
CONTROLLER,
DOMAIN,
ENTITY_MAP,
IDENTIFIER_ACCESSORY_ID,
IDENTIFIER_SERIAL_NUMBER,
KNOWN_DEVICES,
TRIGGERS,
)
from .storage import EntityMapStorage from .storage import EntityMapStorage
@ -45,7 +37,7 @@ class HomeKitEntity(Entity):
_attr_should_poll = False _attr_should_poll = False
def __init__(self, accessory, devinfo): def __init__(self, accessory: HKDevice, devinfo):
"""Initialise a generic HomeKit device.""" """Initialise a generic HomeKit device."""
self._accessory = accessory self._accessory = accessory
self._aid = devinfo["aid"] self._aid = devinfo["aid"]
@ -162,38 +154,7 @@ class HomeKitEntity(Entity):
@property @property
def device_info(self) -> DeviceInfo: def device_info(self) -> DeviceInfo:
"""Return the device info.""" """Return the device info."""
info = self.accessory_info return self._accessory.device_info_for_accessory(self.accessory)
accessory_serial = info.value(CharacteristicsTypes.SERIAL_NUMBER)
if valid_serial_number(accessory_serial):
# Some accessories do not have a serial number
identifier = (DOMAIN, IDENTIFIER_SERIAL_NUMBER, accessory_serial)
else:
identifier = (
DOMAIN,
IDENTIFIER_ACCESSORY_ID,
f"{self._accessory.unique_id}_{self._aid}",
)
device_info = DeviceInfo(
identifiers={identifier},
manufacturer=info.value(CharacteristicsTypes.MANUFACTURER, ""),
model=info.value(CharacteristicsTypes.MODEL, ""),
name=info.value(CharacteristicsTypes.NAME),
sw_version=info.value(CharacteristicsTypes.FIRMWARE_REVISION, ""),
hw_version=info.value(CharacteristicsTypes.HARDWARE_REVISION, ""),
)
# Some devices only have a single accessory - we don't add a
# via_device otherwise it would be self referential.
bridge_serial = self._accessory.connection_info["serial-number"]
if accessory_serial != bridge_serial:
device_info[ATTR_VIA_DEVICE] = (
DOMAIN,
IDENTIFIER_SERIAL_NUMBER,
bridge_serial,
)
return device_info
def get_characteristic_types(self): def get_characteristic_types(self):
"""Define the homekit characteristics the entity cares about.""" """Define the homekit characteristics the entity cares about."""

View File

@ -8,7 +8,7 @@ from aiohomekit.exceptions import (
AccessoryNotFoundError, AccessoryNotFoundError,
EncryptionError, EncryptionError,
) )
from aiohomekit.model import Accessories from aiohomekit.model import Accessories, Accessory
from aiohomekit.model.characteristics import CharacteristicsTypes from aiohomekit.model.characteristics import CharacteristicsTypes
from aiohomekit.model.services import ServicesTypes from aiohomekit.model.services import ServicesTypes
@ -201,24 +201,8 @@ class HKDevice:
return True return True
@callback def device_info_for_accessory(self, accessory: Accessory) -> DeviceInfo:
def async_create_devices(self): """Build a DeviceInfo for a given accessory."""
"""
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 = dr.async_get(self.hass)
devices = {}
# Accessories need to be created in the correct order or setting up
# relationships with ATTR_VIA_DEVICE may fail.
for accessory in sorted(
self.entity_map.accessories, key=lambda accessory: accessory.aid
):
info = accessory.services.first( info = accessory.services.first(
service_type=ServicesTypes.ACCESSORY_INFORMATION, service_type=ServicesTypes.ACCESSORY_INFORMATION,
) )
@ -261,6 +245,28 @@ class HKDevice:
self.connection_info["serial-number"], self.connection_info["serial-number"],
) )
return device_info
@callback
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 = dr.async_get(self.hass)
devices = {}
# Accessories need to be created in the correct order or setting up
# relationships with ATTR_VIA_DEVICE may fail.
for accessory in sorted(
self.entity_map.accessories, key=lambda accessory: accessory.aid
):
device_info = self.device_info_for_accessory(accessory)
device = device_registry.async_get_or_create( device = device_registry.async_get_or_create(
config_entry_id=self.config_entry.entry_id, config_entry_id=self.config_entry.entry_id,
**device_info, **device_info,