Handle accessories without a serial number in homekit_controller (#58498)

This commit is contained in:
J. Nick Koston 2021-10-27 06:24:57 -05:00 committed by GitHub
parent 3b7f620aad
commit 5581f58aad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 726 additions and 19 deletions

View file

@ -20,7 +20,15 @@ from homeassistant.helpers.entity import DeviceInfo, Entity
from .config_flow import normalize_hkid
from .connection import HKDevice
from .const import CONTROLLER, DOMAIN, ENTITY_MAP, KNOWN_DEVICES, TRIGGERS
from .const import (
CONTROLLER,
DOMAIN,
ENTITY_MAP,
IDENTIFIER_ACCESSORY_ID,
IDENTIFIER_SERIAL_NUMBER,
KNOWN_DEVICES,
TRIGGERS,
)
from .storage import EntityMapStorage
@ -131,8 +139,12 @@ class HomeKitEntity(Entity):
@property
def unique_id(self) -> str:
"""Return the ID of this device."""
serial = self.accessory_info.value(CharacteristicsTypes.SERIAL_NUMBER)
return f"homekit-{serial}-{self._iid}"
info = self.accessory_info
serial = info.value(CharacteristicsTypes.SERIAL_NUMBER)
if serial:
return f"homekit-{serial}-{self._iid}"
# Some accessories do not have a serial number
return f"homekit-{self._accessory.unique_id}-{self._aid}-{self._iid}"
@property
def name(self) -> str:
@ -149,9 +161,18 @@ class HomeKitEntity(Entity):
"""Return the device info."""
info = self.accessory_info
accessory_serial = info.value(CharacteristicsTypes.SERIAL_NUMBER)
if 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={(DOMAIN, "serial-number", accessory_serial)},
identifiers={identifier},
manufacturer=info.value(CharacteristicsTypes.MANUFACTURER, ""),
model=info.value(CharacteristicsTypes.MODEL, ""),
name=info.value(CharacteristicsTypes.NAME),
@ -162,7 +183,11 @@ class HomeKitEntity(Entity):
# 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, "serial-number", bridge_serial)
device_info[ATTR_VIA_DEVICE] = (
DOMAIN,
IDENTIFIER_SERIAL_NUMBER,
bridge_serial,
)
return device_info

View file

@ -12,7 +12,7 @@ from aiohomekit.model import Accessories
from aiohomekit.model.characteristics import CharacteristicsTypes
from aiohomekit.model.services import ServicesTypes
from homeassistant.const import ATTR_IDENTIFIERS, ATTR_VIA_DEVICE
from homeassistant.const import ATTR_VIA_DEVICE
from homeassistant.core import callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity import DeviceInfo
@ -24,6 +24,8 @@ from .const import (
DOMAIN,
ENTITY_MAP,
HOMEKIT_ACCESSORY_DISPATCH,
IDENTIFIER_ACCESSORY_ID,
IDENTIFIER_SERIAL_NUMBER,
)
from .device_trigger import async_fire_triggers, async_setup_triggers_for_entry
@ -207,33 +209,40 @@ class HKDevice:
service_type=ServicesTypes.ACCESSORY_INFORMATION,
)
device_info = DeviceInfo(
identifiers={
serial_number = info.value(CharacteristicsTypes.SERIAL_NUMBER)
if serial_number:
identifiers = {(DOMAIN, IDENTIFIER_SERIAL_NUMBER, serial_number)}
else:
# Some accessories do not have a serial number
identifiers = {
(
DOMAIN,
"serial-number",
info.value(CharacteristicsTypes.SERIAL_NUMBER),
IDENTIFIER_ACCESSORY_ID,
f"{self.unique_id}_{accessory.aid}",
)
},
}
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.
identifiers.add((DOMAIN, IDENTIFIER_ACCESSORY_ID, self.unique_id))
device_info = DeviceInfo(
identifiers=identifiers,
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[ATTR_IDENTIFIERS].add(
(DOMAIN, "accessory-id", self.unique_id)
)
else:
if accessory.aid != 1:
# 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[ATTR_VIA_DEVICE] = (
DOMAIN,
"serial-number",
IDENTIFIER_SERIAL_NUMBER,
self.connection_info["serial-number"],
)

View file

@ -11,6 +11,10 @@ TRIGGERS = f"{DOMAIN}-triggers"
HOMEKIT_DIR = ".homekit"
PAIRING_FILE = "pairing.json"
IDENTIFIER_SERIAL_NUMBER = "serial-number"
IDENTIFIER_ACCESSORY_ID = "accessory-id"
# Mapping from Homekit type to component.
HOMEKIT_ACCESSORY_DISPATCH = {
"lightbulb": "light",