Migrate HomeKit Controller to use stable identifiers (#80064)

This commit is contained in:
J. Nick Koston 2022-10-11 11:26:03 -10:00 committed by GitHub
parent e3a3f93441
commit f23b1750e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 781 additions and 234 deletions

View file

@ -21,7 +21,7 @@ from aiohomekit.model.services import Service
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_VIA_DEVICE
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.event import async_track_time_interval
@ -79,9 +79,7 @@ class HKDevice:
connection: Controller = hass.data[CONTROLLER]
self.pairing = connection.load_pairing(
self.pairing_data["AccessoryPairingID"], self.pairing_data
)
self.pairing = connection.load_pairing(self.unique_id, self.pairing_data)
# A list of callbacks that turn HK accessories into entities
self.accessory_factories: list[AddAccessoryCb] = []
@ -253,7 +251,12 @@ class HKDevice:
identifiers.add((IDENTIFIER_SERIAL_NUMBER, accessory.serial_number))
device_info = DeviceInfo(
identifiers=identifiers,
identifiers={
(
IDENTIFIER_ACCESSORY_ID,
f"{self.unique_id}:aid:{accessory.aid}",
)
},
name=accessory.name,
manufacturer=accessory.manufacturer,
model=accessory.model,
@ -317,27 +320,87 @@ class HKDevice:
self.unique_id,
accessory.aid,
)
device_registry.async_update_device(
device.id,
new_identifiers={
(
IDENTIFIER_ACCESSORY_ID,
f"{self.unique_id}:aid:{accessory.aid}",
)
},
)
new_identifiers = {
@callback
def async_migrate_unique_id(
self, old_unique_id: str, new_unique_id: str, platform: str
) -> None:
"""Migrate legacy unique IDs to new format."""
_LOGGER.debug(
"Checking if unique ID %s on %s needs to be migrated",
old_unique_id,
platform,
)
entity_registry = er.async_get(self.hass)
# async_get_entity_id wants the "homekit_controller" domain
# in the platform field and the actual platform in the domain
# field for historical reasons since everything used to be
# PLATFORM.INTEGRATION instead of INTEGRATION.PLATFORM
if (
entity_id := entity_registry.async_get_entity_id(
platform, DOMAIN, old_unique_id
)
) is None:
_LOGGER.debug("Unique ID %s does not need to be migrated", old_unique_id)
return
if new_entity_id := entity_registry.async_get_entity_id(
platform, DOMAIN, new_unique_id
):
_LOGGER.debug(
"Unique ID %s is already in use by %s (system may have been downgraded)",
new_unique_id,
new_entity_id,
)
return
_LOGGER.debug(
"Migrating unique ID for entity %s (%s -> %s)",
entity_id,
old_unique_id,
new_unique_id,
)
entity_registry.async_update_entity(entity_id, new_unique_id=new_unique_id)
@callback
def async_remove_legacy_device_serial_numbers(self) -> None:
"""Migrate remove legacy serial numbers from devices.
We no longer use serial numbers as device identifiers
since they are not reliable, and the HomeKit spec
does not require them to be stable.
"""
_LOGGER.debug(
"Removing legacy serial numbers from device registry entries for pairing %s",
self.unique_id,
)
device_registry = dr.async_get(self.hass)
for accessory in self.entity_map.accessories:
identifiers = {
(
IDENTIFIER_ACCESSORY_ID,
f"{self.unique_id}:aid:{accessory.aid}",
)
}
if not self.unreliable_serial_numbers:
new_identifiers.add((IDENTIFIER_SERIAL_NUMBER, accessory.serial_number))
else:
_LOGGER.debug(
"Not migrating serial number identifier for %s:aid:%s (it is wrong, not unique or unreliable)",
self.unique_id,
accessory.aid,
)
device_registry.async_update_device(
device.id, new_identifiers=new_identifiers
legacy_serial_identifier = (
IDENTIFIER_SERIAL_NUMBER,
accessory.serial_number,
)
device = device_registry.async_get_device(identifiers=identifiers)
if not device or legacy_serial_identifier not in device.identifiers:
continue
device_registry.async_update_device(device.id, new_identifiers=identifiers)
@callback
def async_create_devices(self) -> None:
"""
@ -416,6 +479,9 @@ class HKDevice:
# Migrate to new device ids
self.async_migrate_devices()
# Remove any of the legacy serial numbers from the device registry
self.async_remove_legacy_device_serial_numbers()
self.async_create_devices()
# Load any triggers for this config entry