Fix homekit bridge iid allocations (#81613)

fixes undefined
pull/81784/head
J. Nick Koston 2022-11-08 04:15:16 -06:00 committed by Franck Nijhof
parent b9757235a7
commit ac15f2cf9d
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
13 changed files with 806 additions and 59 deletions

View File

@ -76,13 +76,7 @@ from . import ( # noqa: F401
type_switches,
type_thermostats,
)
from .accessories import (
HomeAccessory,
HomeBridge,
HomeDriver,
HomeIIDManager,
get_accessory,
)
from .accessories import HomeAccessory, HomeBridge, HomeDriver, get_accessory
from .aidmanager import AccessoryAidStorage
from .const import (
ATTR_INTEGRATION,
@ -551,7 +545,7 @@ class HomeKit:
async_zeroconf_instance=async_zeroconf_instance,
zeroconf_server=f"{uuid}-hap.local.",
loader=get_loader(),
iid_manager=HomeIIDManager(self.iid_storage),
iid_storage=self.iid_storage,
)
# If we do not load the mac address will be wrong

View File

@ -270,7 +270,7 @@ class HomeAccessory(Accessory): # type: ignore[misc]
driver=driver,
display_name=cleanup_name_for_homekit(name),
aid=aid,
iid_manager=driver.iid_manager,
iid_manager=HomeIIDManager(driver.iid_storage),
*args,
**kwargs,
)
@ -570,7 +570,7 @@ class HomeBridge(Bridge): # type: ignore[misc]
def __init__(self, hass: HomeAssistant, driver: HomeDriver, name: str) -> None:
"""Initialize a Bridge object."""
super().__init__(driver, name, iid_manager=driver.iid_manager)
super().__init__(driver, name, iid_manager=HomeIIDManager(driver.iid_storage))
self.set_info_service(
firmware_revision=format_version(__version__),
manufacturer=MANUFACTURER,
@ -603,7 +603,7 @@ class HomeDriver(AccessoryDriver): # type: ignore[misc]
entry_id: str,
bridge_name: str,
entry_title: str,
iid_manager: HomeIIDManager,
iid_storage: AccessoryIIDStorage,
**kwargs: Any,
) -> None:
"""Initialize a AccessoryDriver object."""
@ -612,7 +612,7 @@ class HomeDriver(AccessoryDriver): # type: ignore[misc]
self._entry_id = entry_id
self._bridge_name = bridge_name
self._entry_title = entry_title
self.iid_manager = iid_manager
self.iid_storage = iid_storage
@pyhap_callback # type: ignore[misc]
def pair(

View File

@ -31,6 +31,8 @@ async def async_get_config_entry_diagnostics(
"options": dict(entry.options),
},
}
if homekit.iid_storage:
data["iid_storage"] = homekit.iid_storage.allocations
if not homekit.driver: # not started yet or startup failed
return data
driver: AccessoryDriver = homekit.driver

View File

@ -17,7 +17,7 @@ from homeassistant.helpers.storage import Store
from .util import get_iid_storage_filename_for_entry_id
IID_MANAGER_STORAGE_VERSION = 1
IID_MANAGER_STORAGE_VERSION = 2
IID_MANAGER_SAVE_DELAY = 2
ALLOCATIONS_KEY = "allocations"
@ -26,6 +26,40 @@ IID_MIN = 1
IID_MAX = 18446744073709551615
ACCESSORY_INFORMATION_SERVICE = "3E"
class IIDStorage(Store):
"""Storage class for IIDManager."""
async def _async_migrate_func(
self,
old_major_version: int,
old_minor_version: int,
old_data: dict,
):
"""Migrate to the new version."""
if old_major_version == 1:
# Convert v1 to v2 format which uses a unique iid set per accessory
# instead of per pairing since we need the ACCESSORY_INFORMATION_SERVICE
# to always have iid 1 for each bridged accessory as well as the bridge
old_allocations: dict[str, int] = old_data.pop(ALLOCATIONS_KEY, {})
new_allocation: dict[str, dict[str, int]] = {}
old_data[ALLOCATIONS_KEY] = new_allocation
for allocation_key, iid in old_allocations.items():
aid_str, new_allocation_key = allocation_key.split("_", 1)
service_type, _, char_type, *_ = new_allocation_key.split("_")
accessory_allocation = new_allocation.setdefault(aid_str, {})
if service_type == ACCESSORY_INFORMATION_SERVICE and not char_type:
accessory_allocation[new_allocation_key] = 1
elif iid != 1:
accessory_allocation[new_allocation_key] = iid
return old_data
raise NotImplementedError
class AccessoryIIDStorage:
"""
Provide stable allocation of IIDs for the lifetime of an accessory.
@ -37,15 +71,15 @@ class AccessoryIIDStorage:
def __init__(self, hass: HomeAssistant, entry_id: str) -> None:
"""Create a new iid store."""
self.hass = hass
self.allocations: dict[str, int] = {}
self.allocated_iids: list[int] = []
self.allocations: dict[str, dict[str, int]] = {}
self.allocated_iids: dict[str, list[int]] = {}
self.entry_id = entry_id
self.store: Store | None = None
self.store: IIDStorage | None = None
async def async_initialize(self) -> None:
"""Load the latest IID data."""
iid_store = get_iid_storage_filename_for_entry_id(self.entry_id)
self.store = Store(self.hass, IID_MANAGER_STORAGE_VERSION, iid_store)
self.store = IIDStorage(self.hass, IID_MANAGER_STORAGE_VERSION, iid_store)
if not (raw_storage := await self.store.async_load()):
# There is no data about iid allocations yet
@ -53,7 +87,8 @@ class AccessoryIIDStorage:
assert isinstance(raw_storage, dict)
self.allocations = raw_storage.get(ALLOCATIONS_KEY, {})
self.allocated_iids = sorted(self.allocations.values())
for aid_str, allocations in self.allocations.items():
self.allocated_iids[aid_str] = sorted(allocations.values())
def get_or_allocate_iid(
self,
@ -68,16 +103,25 @@ class AccessoryIIDStorage:
char_hap_type: str | None = uuid_to_hap_type(char_uuid) if char_uuid else None
# Allocation key must be a string since we are saving it to JSON
allocation_key = (
f'{aid}_{service_hap_type}_{service_unique_id or ""}_'
f'{service_hap_type}_{service_unique_id or ""}_'
f'{char_hap_type or ""}_{char_unique_id or ""}'
)
if allocation_key in self.allocations:
return self.allocations[allocation_key]
next_iid = self.allocated_iids[-1] + 1 if self.allocated_iids else 1
self.allocations[allocation_key] = next_iid
self.allocated_iids.append(next_iid)
# AID must be a string since JSON keys cannot be int
aid_str = str(aid)
accessory_allocation = self.allocations.setdefault(aid_str, {})
accessory_allocated_iids = self.allocated_iids.setdefault(aid_str, [])
if service_hap_type == ACCESSORY_INFORMATION_SERVICE and char_uuid is None:
allocated_iid = 1
elif allocation_key in accessory_allocation:
return accessory_allocation[allocation_key]
elif accessory_allocated_iids:
allocated_iid = accessory_allocated_iids[-1] + 1
else:
allocated_iid = 2
accessory_allocation[allocation_key] = allocated_iid
accessory_allocated_iids.append(allocated_iid)
self._async_schedule_save()
return next_iid
return allocated_iid
@callback
def _async_schedule_save(self) -> None:
@ -91,6 +135,6 @@ class AccessoryIIDStorage:
return await self.store.async_save(self._data_to_save())
@callback
def _data_to_save(self) -> dict[str, dict[str, int]]:
def _data_to_save(self) -> dict[str, dict[str, dict[str, int]]]:
"""Return data of entity map to store in a file."""
return {ALLOCATIONS_KEY: self.allocations}

View File

@ -6,7 +6,7 @@ from unittest.mock import patch
import pytest
from homeassistant.components.device_tracker.legacy import YAML_DEVICES
from homeassistant.components.homekit.accessories import HomeDriver, HomeIIDManager
from homeassistant.components.homekit.accessories import HomeDriver
from homeassistant.components.homekit.const import BRIDGE_NAME, EVENT_HOMEKIT_CHANGED
from homeassistant.components.homekit.iidmanager import AccessoryIIDStorage
@ -39,7 +39,7 @@ def run_driver(hass, loop, iid_storage):
entry_id="",
entry_title="mock entry",
bridge_name=BRIDGE_NAME,
iid_manager=HomeIIDManager(iid_storage),
iid_storage=iid_storage,
address="127.0.0.1",
loop=loop,
)
@ -63,7 +63,7 @@ def hk_driver(hass, loop, iid_storage):
entry_id="",
entry_title="mock entry",
bridge_name=BRIDGE_NAME,
iid_manager=HomeIIDManager(iid_storage),
iid_storage=iid_storage,
address="127.0.0.1",
loop=loop,
)
@ -91,7 +91,7 @@ def mock_hap(hass, loop, iid_storage, mock_zeroconf):
entry_id="",
entry_title="mock entry",
bridge_name=BRIDGE_NAME,
iid_manager=HomeIIDManager(iid_storage),
iid_storage=iid_storage,
address="127.0.0.1",
loop=loop,
)

View File

@ -0,0 +1,249 @@
{
"version": 1,
"minor_version": 1,
"key": "homekit.v1.iids",
"data": {
"allocations": {
"1_3E___": 1,
"1_3E__14_": 2,
"1_3E__20_": 3,
"1_3E__21_": 4,
"1_3E__23_": 5,
"1_3E__30_": 6,
"1_3E__52_": 7,
"1_A2___": 8,
"1_A2__37_": 9,
"935391877_3E___": 10,
"935391877_3E__14_": 11,
"935391877_3E__20_": 12,
"935391877_3E__21_": 13,
"935391877_3E__23_": 14,
"935391877_3E__30_": 15,
"935391877_3E__52_": 16,
"935391877_4A___": 17,
"935391877_4A__F_": 18,
"935391877_4A__33_": 19,
"935391877_4A__11_": 20,
"935391877_4A__35_": 21,
"935391877_4A__36_": 22,
"935391877_4A__D_": 23,
"935391877_4A__12_": 24,
"935391877_4A__34_": 25,
"935391877_4A__10_": 26,
"935391877_B7___": 27,
"935391877_B7__B0_": 28,
"935391877_B7__BF_": 29,
"935391877_B7__AF_": 30,
"985724734_3E___": 31,
"985724734_3E__14_": 32,
"985724734_3E__20_": 33,
"985724734_3E__21_": 34,
"985724734_3E__23_": 35,
"985724734_3E__30_": 36,
"985724734_3E__52_": 37,
"985724734_4A___": 38,
"985724734_4A__F_": 39,
"985724734_4A__33_": 40,
"985724734_4A__11_": 41,
"985724734_4A__35_": 42,
"985724734_4A__36_": 43,
"985724734_4A__D_": 44,
"985724734_4A__12_": 45,
"985724734_4A__34_": 46,
"985724734_4A__10_": 47,
"985724734_B7___": 48,
"985724734_B7__B0_": 49,
"985724734_B7__BF_": 50,
"985724734_B7__AF_": 51,
"3083074204_3E___": 52,
"3083074204_3E__14_": 53,
"3083074204_3E__20_": 54,
"3083074204_3E__21_": 55,
"3083074204_3E__23_": 56,
"3083074204_3E__30_": 57,
"3083074204_3E__52_": 58,
"3083074204_4A___": 59,
"3083074204_4A__F_": 60,
"3083074204_4A__33_": 61,
"3083074204_4A__11_": 62,
"3083074204_4A__35_": 63,
"3083074204_4A__36_": 64,
"3083074204_4A__D_": 65,
"3083074204_4A__12_": 66,
"3083074204_4A__34_": 67,
"3083074204_4A__10_": 68,
"3083074204_B7___": 69,
"3083074204_B7__B0_": 70,
"3083074204_B7__BF_": 71,
"3083074204_B7__AF_": 72,
"3032741347_3E___": 73,
"3032741347_3E__14_": 74,
"3032741347_3E__20_": 75,
"3032741347_3E__21_": 76,
"3032741347_3E__23_": 77,
"3032741347_3E__30_": 78,
"3032741347_3E__52_": 79,
"3032741347_4A___": 80,
"3032741347_4A__F_": 81,
"3032741347_4A__33_": 82,
"3032741347_4A__11_": 83,
"3032741347_4A__35_": 84,
"3032741347_4A__36_": 85,
"3032741347_4A__D_": 86,
"3032741347_4A__12_": 87,
"3032741347_4A__34_": 88,
"3032741347_4A__10_": 89,
"3032741347_B7___": 90,
"3032741347_B7__B0_": 91,
"3032741347_B7__BF_": 92,
"3032741347_B7__AF_": 93,
"3681509609_3E___": 94,
"3681509609_3E__14_": 95,
"3681509609_3E__20_": 96,
"3681509609_3E__21_": 97,
"3681509609_3E__23_": 98,
"3681509609_3E__30_": 99,
"3681509609_3E__52_": 100,
"3681509609_4A___": 101,
"3681509609_4A__F_": 102,
"3681509609_4A__33_": 103,
"3681509609_4A__11_": 104,
"3681509609_4A__35_": 105,
"3681509609_4A__36_": 106,
"3681509609_4A__D_": 107,
"3681509609_4A__12_": 108,
"3681509609_4A__34_": 109,
"3681509609_4A__10_": 110,
"3681509609_B7___": 111,
"3681509609_B7__B0_": 112,
"3681509609_B7__BF_": 113,
"3681509609_B7__AF_": 114,
"3866063418_3E___": 115,
"3866063418_3E__14_": 116,
"3866063418_3E__20_": 117,
"3866063418_3E__21_": 118,
"3866063418_3E__23_": 119,
"3866063418_3E__30_": 120,
"3866063418_3E__52_": 121,
"3866063418_4A___": 122,
"3866063418_4A__F_": 123,
"3866063418_4A__33_": 124,
"3866063418_4A__11_": 125,
"3866063418_4A__35_": 126,
"3866063418_4A__36_": 127,
"3866063418_4A__D_": 128,
"3866063418_4A__12_": 129,
"3866063418_4A__34_": 130,
"3866063418_4A__10_": 131,
"3866063418_B7___": 132,
"3866063418_B7__B0_": 133,
"3866063418_B7__BF_": 134,
"3866063418_B7__AF_": 135,
"3239498961_3E___": 136,
"3239498961_3E__14_": 137,
"3239498961_3E__20_": 138,
"3239498961_3E__21_": 139,
"3239498961_3E__23_": 140,
"3239498961_3E__30_": 141,
"3239498961_3E__52_": 142,
"3239498961_4A___": 143,
"3239498961_4A__F_": 144,
"3239498961_4A__33_": 145,
"3239498961_4A__11_": 146,
"3239498961_4A__35_": 147,
"3239498961_4A__36_": 148,
"3239498961_4A__D_": 149,
"3239498961_4A__12_": 150,
"3239498961_4A__34_": 151,
"3239498961_4A__10_": 152,
"3239498961_B7___": 153,
"3239498961_B7__B0_": 154,
"3239498961_B7__BF_": 155,
"3239498961_B7__AF_": 156,
"3289831818_3E___": 157,
"3289831818_3E__14_": 158,
"3289831818_3E__20_": 159,
"3289831818_3E__21_": 160,
"3289831818_3E__23_": 161,
"3289831818_3E__30_": 162,
"3289831818_3E__52_": 163,
"3289831818_4A___": 164,
"3289831818_4A__F_": 165,
"3289831818_4A__33_": 166,
"3289831818_4A__11_": 167,
"3289831818_4A__35_": 168,
"3289831818_4A__36_": 169,
"3289831818_4A__D_": 170,
"3289831818_4A__12_": 171,
"3289831818_4A__34_": 172,
"3289831818_4A__10_": 173,
"3289831818_B7___": 174,
"3289831818_B7__B0_": 175,
"3289831818_B7__BF_": 176,
"3289831818_B7__AF_": 177,
"3071722771_3E___": 178,
"3071722771_3E__14_": 179,
"3071722771_3E__20_": 180,
"3071722771_3E__21_": 181,
"3071722771_3E__23_": 182,
"3071722771_3E__30_": 183,
"3071722771_3E__52_": 184,
"3071722771_4A___": 185,
"3071722771_4A__F_": 186,
"3071722771_4A__33_": 187,
"3071722771_4A__11_": 188,
"3071722771_4A__35_": 189,
"3071722771_4A__36_": 190,
"3071722771_4A__D_": 191,
"3071722771_4A__12_": 192,
"3071722771_4A__34_": 193,
"3071722771_4A__10_": 194,
"3071722771_B7___": 195,
"3071722771_B7__B0_": 196,
"3071722771_B7__BF_": 197,
"3071722771_B7__AF_": 198,
"3391630365_3E___": 199,
"3391630365_3E__14_": 200,
"3391630365_3E__20_": 201,
"3391630365_3E__21_": 202,
"3391630365_3E__23_": 203,
"3391630365_3E__30_": 204,
"3391630365_3E__52_": 205,
"3391630365_4A___": 206,
"3391630365_4A__F_": 207,
"3391630365_4A__33_": 208,
"3391630365_4A__11_": 209,
"3391630365_4A__35_": 210,
"3391630365_4A__36_": 211,
"3391630365_4A__D_": 212,
"3391630365_4A__12_": 213,
"3391630365_4A__34_": 214,
"3391630365_4A__10_": 215,
"3391630365_B7___": 216,
"3391630365_B7__B0_": 217,
"3391630365_B7__BF_": 218,
"3391630365_B7__AF_": 219,
"3274187032_3E___": 220,
"3274187032_3E__14_": 221,
"3274187032_3E__20_": 222,
"3274187032_3E__21_": 223,
"3274187032_3E__23_": 224,
"3274187032_3E__30_": 225,
"3274187032_3E__52_": 226,
"3274187032_4A___": 227,
"3274187032_4A__F_": 228,
"3274187032_4A__33_": 229,
"3274187032_4A__11_": 230,
"3274187032_4A__35_": 231,
"3274187032_4A__36_": 232,
"3274187032_4A__D_": 233,
"3274187032_4A__12_": 234,
"3274187032_4A__34_": 235,
"3274187032_4A__10_": 236,
"3274187032_B7___": 237,
"3274187032_B7__B0_": 238,
"3274187032_B7__BF_": 239,
"3274187032_B7__AF_": 240
}
}
}

View File

@ -0,0 +1,50 @@
{
"version": 1,
"minor_version": 1,
"key": "homekit.8a47205bd97c07d7a908f10166ebe636.iids",
"data": {
"allocations": {
"1_3E___": 1,
"1_3E__14_": 2,
"1_3E__20_": 3,
"1_3E__21_": 4,
"1_3E__23_": 5,
"1_3E__30_": 6,
"1_3E__52_": 7,
"1_A2___": 8,
"1_A2__37_": 9,
"1973560704_3E___": 10,
"1973560704_3E__14_": 11,
"1973560704_3E__20_": 12,
"1973560704_3E__21_": 13,
"1973560704_3E__23_": 14,
"1973560704_3E__30_": 15,
"1973560704_3E__52_": 16,
"1973560704_3E__53_": 17,
"1973560704_89_pressed-__": 18,
"1973560704_89_pressed-_73_": 19,
"1973560704_89_pressed-_23_": 20,
"1973560704_89_pressed-_CB_": 21,
"1973560704_CC_pressed-__": 22,
"1973560704_CC_pressed-_CD_": 23,
"1973560704_89_changed_states-__": 24,
"1973560704_89_changed_states-_73_": 25,
"1973560704_89_changed_states-_23_": 26,
"1973560704_89_changed_states-_CB_": 27,
"1973560704_CC_changed_states-__": 28,
"1973560704_CC_changed_states-_CD_": 29,
"1973560704_89_turned_off-__": 30,
"1973560704_89_turned_off-_73_": 31,
"1973560704_89_turned_off-_23_": 32,
"1973560704_89_turned_off-_CB_": 33,
"1973560704_CC_turned_off-__": 34,
"1973560704_CC_turned_off-_CD_": 35,
"1973560704_89_turned_on-__": 36,
"1973560704_89_turned_on-_73_": 37,
"1973560704_89_turned_on-_23_": 38,
"1973560704_89_turned_on-_CB_": 39,
"1973560704_CC_turned_on-__": 40,
"1973560704_CC_turned_on-_CD_": 41
}
}
}

View File

@ -0,0 +1,273 @@
{
"version": 2,
"minor_version": 1,
"key": "homekit.v2.iids",
"data": {
"allocations": {
"1": {
"3E___": 1,
"3E__14_": 2,
"3E__20_": 3,
"3E__21_": 4,
"3E__23_": 5,
"3E__30_": 6,
"3E__52_": 7,
"A2___": 8,
"A2__37_": 9
},
"935391877": {
"3E___": 1,
"3E__14_": 11,
"3E__20_": 12,
"3E__21_": 13,
"3E__23_": 14,
"3E__30_": 15,
"3E__52_": 16,
"4A___": 17,
"4A__F_": 18,
"4A__33_": 19,
"4A__11_": 20,
"4A__35_": 21,
"4A__36_": 22,
"4A__D_": 23,
"4A__12_": 24,
"4A__34_": 25,
"4A__10_": 26,
"B7___": 27,
"B7__B0_": 28,
"B7__BF_": 29,
"B7__AF_": 30
},
"985724734": {
"3E___": 1,
"3E__14_": 32,
"3E__20_": 33,
"3E__21_": 34,
"3E__23_": 35,
"3E__30_": 36,
"3E__52_": 37,
"4A___": 38,
"4A__F_": 39,
"4A__33_": 40,
"4A__11_": 41,
"4A__35_": 42,
"4A__36_": 43,
"4A__D_": 44,
"4A__12_": 45,
"4A__34_": 46,
"4A__10_": 47,
"B7___": 48,
"B7__B0_": 49,
"B7__BF_": 50,
"B7__AF_": 51
},
"3083074204": {
"3E___": 1,
"3E__14_": 53,
"3E__20_": 54,
"3E__21_": 55,
"3E__23_": 56,
"3E__30_": 57,
"3E__52_": 58,
"4A___": 59,
"4A__F_": 60,
"4A__33_": 61,
"4A__11_": 62,
"4A__35_": 63,
"4A__36_": 64,
"4A__D_": 65,
"4A__12_": 66,
"4A__34_": 67,
"4A__10_": 68,
"B7___": 69,
"B7__B0_": 70,
"B7__BF_": 71,
"B7__AF_": 72
},
"3032741347": {
"3E___": 1,
"3E__14_": 74,
"3E__20_": 75,
"3E__21_": 76,
"3E__23_": 77,
"3E__30_": 78,
"3E__52_": 79,
"4A___": 80,
"4A__F_": 81,
"4A__33_": 82,
"4A__11_": 83,
"4A__35_": 84,
"4A__36_": 85,
"4A__D_": 86,
"4A__12_": 87,
"4A__34_": 88,
"4A__10_": 89,
"B7___": 90,
"B7__B0_": 91,
"B7__BF_": 92,
"B7__AF_": 93
},
"3681509609": {
"3E___": 1,
"3E__14_": 95,
"3E__20_": 96,
"3E__21_": 97,
"3E__23_": 98,
"3E__30_": 99,
"3E__52_": 100,
"4A___": 101,
"4A__F_": 102,
"4A__33_": 103,
"4A__11_": 104,
"4A__35_": 105,
"4A__36_": 106,
"4A__D_": 107,
"4A__12_": 108,
"4A__34_": 109,
"4A__10_": 110,
"B7___": 111,
"B7__B0_": 112,
"B7__BF_": 113,
"B7__AF_": 114
},
"3866063418": {
"3E___": 1,
"3E__14_": 116,
"3E__20_": 117,
"3E__21_": 118,
"3E__23_": 119,
"3E__30_": 120,
"3E__52_": 121,
"4A___": 122,
"4A__F_": 123,
"4A__33_": 124,
"4A__11_": 125,
"4A__35_": 126,
"4A__36_": 127,
"4A__D_": 128,
"4A__12_": 129,
"4A__34_": 130,
"4A__10_": 131,
"B7___": 132,
"B7__B0_": 133,
"B7__BF_": 134,
"B7__AF_": 135
},
"3239498961": {
"3E___": 1,
"3E__14_": 137,
"3E__20_": 138,
"3E__21_": 139,
"3E__23_": 140,
"3E__30_": 141,
"3E__52_": 142,
"4A___": 143,
"4A__F_": 144,
"4A__33_": 145,
"4A__11_": 146,
"4A__35_": 147,
"4A__36_": 148,
"4A__D_": 149,
"4A__12_": 150,
"4A__34_": 151,
"4A__10_": 152,
"B7___": 153,
"B7__B0_": 154,
"B7__BF_": 155,
"B7__AF_": 156
},
"3289831818": {
"3E___": 1,
"3E__14_": 158,
"3E__20_": 159,
"3E__21_": 160,
"3E__23_": 161,
"3E__30_": 162,
"3E__52_": 163,
"4A___": 164,
"4A__F_": 165,
"4A__33_": 166,
"4A__11_": 167,
"4A__35_": 168,
"4A__36_": 169,
"4A__D_": 170,
"4A__12_": 171,
"4A__34_": 172,
"4A__10_": 173,
"B7___": 174,
"B7__B0_": 175,
"B7__BF_": 176,
"B7__AF_": 177
},
"3071722771": {
"3E___": 1,
"3E__14_": 179,
"3E__20_": 180,
"3E__21_": 181,
"3E__23_": 182,
"3E__30_": 183,
"3E__52_": 184,
"4A___": 185,
"4A__F_": 186,
"4A__33_": 187,
"4A__11_": 188,
"4A__35_": 189,
"4A__36_": 190,
"4A__D_": 191,
"4A__12_": 192,
"4A__34_": 193,
"4A__10_": 194,
"B7___": 195,
"B7__B0_": 196,
"B7__BF_": 197,
"B7__AF_": 198
},
"3391630365": {
"3E___": 1,
"3E__14_": 200,
"3E__20_": 201,
"3E__21_": 202,
"3E__23_": 203,
"3E__30_": 204,
"3E__52_": 205,
"4A___": 206,
"4A__F_": 207,
"4A__33_": 208,
"4A__11_": 209,
"4A__35_": 210,
"4A__36_": 211,
"4A__D_": 212,
"4A__12_": 213,
"4A__34_": 214,
"4A__10_": 215,
"B7___": 216,
"B7__B0_": 217,
"B7__BF_": 218,
"B7__AF_": 219
},
"3274187032": {
"3E___": 1,
"3E__14_": 221,
"3E__20_": 222,
"3E__21_": 223,
"3E__23_": 224,
"3E__30_": 225,
"3E__52_": 226,
"4A___": 227,
"4A__F_": 228,
"4A__33_": 229,
"4A__11_": 230,
"4A__35_": 231,
"4A__36_": 232,
"4A__D_": 233,
"4A__12_": 234,
"4A__34_": 235,
"4A__10_": 236,
"B7___": 237,
"B7__B0_": 238,
"B7__BF_": 239,
"B7__AF_": 240
}
}
}
}

View File

@ -0,0 +1,54 @@
{
"version": 2,
"minor_version": 1,
"key": "homekit.8a47205bd97c07d7a908f10166ebe636.iids",
"data": {
"allocations": {
"1": {
"3E___": 1,
"3E__14_": 2,
"3E__20_": 3,
"3E__21_": 4,
"3E__23_": 5,
"3E__30_": 6,
"3E__52_": 7,
"A2___": 8,
"A2__37_": 9
},
"1973560704": {
"3E___": 1,
"3E__14_": 11,
"3E__20_": 12,
"3E__21_": 13,
"3E__23_": 14,
"3E__30_": 15,
"3E__52_": 16,
"3E__53_": 17,
"89_pressed-__": 18,
"89_pressed-_73_": 19,
"89_pressed-_23_": 20,
"89_pressed-_CB_": 21,
"CC_pressed-__": 22,
"CC_pressed-_CD_": 23,
"89_changed_states-__": 24,
"89_changed_states-_73_": 25,
"89_changed_states-_23_": 26,
"89_changed_states-_CB_": 27,
"CC_changed_states-__": 28,
"CC_changed_states-_CD_": 29,
"89_turned_off-__": 30,
"89_turned_off-_73_": 31,
"89_turned_off-_23_": 32,
"89_turned_off-_CB_": 33,
"CC_turned_off-__": 34,
"CC_turned_off-_CD_": 35,
"89_turned_on-__": 36,
"89_turned_on-_73_": 37,
"89_turned_on-_23_": 38,
"89_turned_on-_CB_": 39,
"CC_turned_on-__": 40,
"CC_turned_on-_CD_": 41
}
}
}
}

View File

@ -10,7 +10,6 @@ from homeassistant.components.homekit.accessories import (
HomeAccessory,
HomeBridge,
HomeDriver,
HomeIIDManager,
)
from homeassistant.components.homekit.const import (
ATTR_DISPLAY_NAME,
@ -724,7 +723,7 @@ def test_home_driver(iid_storage):
"entry_id",
"name",
"title",
iid_manager=HomeIIDManager(iid_storage),
iid_storage=iid_storage,
address=ip_address,
port=port,
persist_file=path,
@ -752,22 +751,3 @@ def test_home_driver(iid_storage):
mock_unpair.assert_called_with("client_uuid")
mock_show_msg.assert_called_with("hass", "entry_id", "title (any)", pin, "X-HM://0")
async def test_iid_collision_raises(hass, hk_driver):
"""Test iid collision raises.
If we try to allocate the same IID to the an accessory twice, we should
raise an exception.
"""
entity_id = "light.accessory"
entity_id2 = "light.accessory2"
hass.states.async_set(entity_id, STATE_OFF)
hass.states.async_set(entity_id2, STATE_OFF)
HomeAccessory(hass, hk_driver, "Home Accessory", entity_id, 2, {})
with pytest.raises(RuntimeError):
HomeAccessory(hass, hk_driver, "Home Accessory", entity_id2, 2, {})

View File

@ -43,6 +43,19 @@ async def test_config_entry_running(hass, hass_client, hk_driver, mock_async_zer
diag = await get_diagnostics_for_config_entry(hass, hass_client, entry)
assert diag == {
"bridge": {},
"iid_storage": {
"1": {
"3E__14_": 2,
"3E__20_": 3,
"3E__21_": 4,
"3E__23_": 5,
"3E__30_": 6,
"3E__52_": 7,
"3E___": 1,
"A2__37_": 9,
"A2___": 8,
}
},
"accessories": [
{
"aid": 1,
@ -257,6 +270,21 @@ async def test_config_entry_accessory(
},
"config_version": 2,
"pairing_id": ANY,
"iid_storage": {
"1": {
"3E__14_": 2,
"3E__20_": 3,
"3E__21_": 4,
"3E__23_": 5,
"3E__30_": 6,
"3E__52_": 7,
"3E___": 1,
"43__25_": 11,
"43___": 10,
"A2__37_": 9,
"A2___": 8,
}
},
"status": 1,
}
with patch("pyhap.accessory_driver.AccessoryDriver.async_start"), patch(

View File

@ -262,7 +262,7 @@ async def test_homekit_setup(hass, hk_driver, mock_async_zeroconf):
async_zeroconf_instance=zeroconf_mock,
zeroconf_server=f"{uuid}-hap.local.",
loader=ANY,
iid_manager=ANY,
iid_storage=ANY,
)
assert homekit.driver.safe_mode is False
@ -306,7 +306,7 @@ async def test_homekit_setup_ip_address(hass, hk_driver, mock_async_zeroconf):
async_zeroconf_instance=mock_async_zeroconf,
zeroconf_server=f"{uuid}-hap.local.",
loader=ANY,
iid_manager=ANY,
iid_storage=ANY,
)
@ -350,7 +350,7 @@ async def test_homekit_setup_advertise_ip(hass, hk_driver, mock_async_zeroconf):
async_zeroconf_instance=async_zeroconf_instance,
zeroconf_server=f"{uuid}-hap.local.",
loader=ANY,
iid_manager=ANY,
iid_storage=ANY,
)

View File

@ -1,6 +1,5 @@
"""Tests for the HomeKit IID manager."""
from uuid import UUID
from homeassistant.components.homekit.const import DOMAIN
@ -8,9 +7,10 @@ from homeassistant.components.homekit.iidmanager import (
AccessoryIIDStorage,
get_iid_storage_filename_for_entry_id,
)
from homeassistant.helpers.json import json_loads
from homeassistant.util.uuid import random_uuid_hex
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, load_fixture
async def test_iid_generation_and_restore(hass, iid_storage, hass_storage):
@ -77,9 +77,6 @@ async def test_iid_generation_and_restore(hass, iid_storage, hass_storage):
unique_service_unique_char_new_aid_iid1
== unique_service_unique_char_new_aid_iid2
)
assert unique_service_unique_char_new_aid_iid1 != iid1
assert unique_service_unique_char_new_aid_iid1 != unique_service_unique_char_iid1
await iid_storage.async_save()
iid_storage2 = AccessoryIIDStorage(hass, entry.entry_id)
@ -99,3 +96,79 @@ async def test_iid_storage_filename(hass, iid_storage, hass_storage):
assert iid_storage.store.path.endswith(
get_iid_storage_filename_for_entry_id(entry.entry_id)
)
async def test_iid_migration_to_v2(hass, iid_storage, hass_storage):
"""Test iid storage migration."""
v1_iids = json_loads(load_fixture("iids_v1", DOMAIN))
v2_iids = json_loads(load_fixture("iids_v2", DOMAIN))
hass_storage["homekit.v1.iids"] = v1_iids
hass_storage["homekit.v2.iids"] = v2_iids
iid_storage_v2 = AccessoryIIDStorage(hass, "v1")
await iid_storage_v2.async_initialize()
iid_storage_v1 = AccessoryIIDStorage(hass, "v2")
await iid_storage_v1.async_initialize()
assert iid_storage_v1.allocations == iid_storage_v2.allocations
assert iid_storage_v1.allocated_iids == iid_storage_v2.allocated_iids
assert len(iid_storage_v2.allocations) == 12
for allocations in iid_storage_v2.allocations.values():
assert allocations["3E___"] == 1
async def test_iid_migration_to_v2_with_underscore(hass, iid_storage, hass_storage):
"""Test iid storage migration with underscore."""
v1_iids = json_loads(load_fixture("iids_v1_with_underscore", DOMAIN))
v2_iids = json_loads(load_fixture("iids_v2_with_underscore", DOMAIN))
hass_storage["homekit.v1_with_underscore.iids"] = v1_iids
hass_storage["homekit.v2_with_underscore.iids"] = v2_iids
iid_storage_v2 = AccessoryIIDStorage(hass, "v1_with_underscore")
await iid_storage_v2.async_initialize()
iid_storage_v1 = AccessoryIIDStorage(hass, "v2_with_underscore")
await iid_storage_v1.async_initialize()
assert iid_storage_v1.allocations == iid_storage_v2.allocations
assert iid_storage_v1.allocated_iids == iid_storage_v2.allocated_iids
assert len(iid_storage_v2.allocations) == 2
for allocations in iid_storage_v2.allocations.values():
assert allocations["3E___"] == 1
async def test_iid_generation_and_restore_v2(hass, iid_storage, hass_storage):
"""Test generating iids and restoring them from storage."""
entry = MockConfigEntry(domain=DOMAIN)
iid_storage = AccessoryIIDStorage(hass, entry.entry_id)
await iid_storage.async_initialize()
not_accessory_info_service_iid = iid_storage.get_or_allocate_iid(
1, "000000AA-0000-1000-8000-0026BB765291", None, None, None
)
assert not_accessory_info_service_iid == 2
not_accessory_info_service_iid_2 = iid_storage.get_or_allocate_iid(
1, "000000BB-0000-1000-8000-0026BB765291", None, None, None
)
assert not_accessory_info_service_iid_2 == 3
not_accessory_info_service_iid_2 = iid_storage.get_or_allocate_iid(
1, "000000BB-0000-1000-8000-0026BB765291", None, None, None
)
assert not_accessory_info_service_iid_2 == 3
accessory_info_service_iid = iid_storage.get_or_allocate_iid(
1, "0000003E-0000-1000-8000-0026BB765291", None, None, None
)
assert accessory_info_service_iid == 1
accessory_info_service_iid = iid_storage.get_or_allocate_iid(
1, "0000003E-0000-1000-8000-0026BB765291", None, None, None
)
assert accessory_info_service_iid == 1
accessory_info_service_iid = iid_storage.get_or_allocate_iid(
2, "0000003E-0000-1000-8000-0026BB765291", None, None, None
)
assert accessory_info_service_iid == 1