Store HomeKit generated accessory id against unique_id where possible (#33109)
* HomeKit: Store generated aid against unique_id where possible * Fix conflict * Fix max accessories check * homekit counts the bridge as an accessory * Add coverage for aidmanager * prepare for merge Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
3259ba6116
commit
a80ce60e75
8 changed files with 317 additions and 40 deletions
120
tests/components/homekit/test_aidmanager.py
Normal file
120
tests/components/homekit/test_aidmanager.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
"""Tests for the HomeKit AID manager."""
|
||||
from asynctest import patch
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.homekit.aidmanager import (
|
||||
AccessoryAidStorage,
|
||||
get_system_unique_id,
|
||||
)
|
||||
from homeassistant.helpers import device_registry
|
||||
|
||||
from tests.common import MockConfigEntry, mock_device_registry, mock_registry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def device_reg(hass):
|
||||
"""Return an empty, loaded, registry."""
|
||||
return mock_device_registry(hass)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def entity_reg(hass):
|
||||
"""Return an empty, loaded, registry."""
|
||||
return mock_registry(hass)
|
||||
|
||||
|
||||
async def test_aid_generation(hass, device_reg, entity_reg):
|
||||
"""Test generating aids."""
|
||||
config_entry = MockConfigEntry(domain="test", data={})
|
||||
config_entry.add_to_hass(hass)
|
||||
device_entry = device_reg.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
)
|
||||
light_ent = entity_reg.async_get_or_create(
|
||||
"light", "device", "unique_id", device_id=device_entry.id
|
||||
)
|
||||
light_ent2 = entity_reg.async_get_or_create(
|
||||
"light", "device", "other_unique_id", device_id=device_entry.id
|
||||
)
|
||||
remote_ent = entity_reg.async_get_or_create(
|
||||
"remote", "device", "unique_id", device_id=device_entry.id
|
||||
)
|
||||
hass.states.async_set(light_ent.entity_id, "on")
|
||||
hass.states.async_set(light_ent2.entity_id, "on")
|
||||
hass.states.async_set(remote_ent.entity_id, "on")
|
||||
hass.states.async_set("remote.has_no_unique_id", "on")
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.homekit.aidmanager.AccessoryAidStorage.async_schedule_save"
|
||||
):
|
||||
aid_storage = AccessoryAidStorage(hass)
|
||||
await aid_storage.async_initialize()
|
||||
|
||||
for _ in range(0, 2):
|
||||
assert (
|
||||
aid_storage.get_or_allocate_aid_for_entity_id(light_ent.entity_id)
|
||||
== 1692141785
|
||||
)
|
||||
assert (
|
||||
aid_storage.get_or_allocate_aid_for_entity_id(light_ent2.entity_id)
|
||||
== 2732133210
|
||||
)
|
||||
assert (
|
||||
aid_storage.get_or_allocate_aid_for_entity_id(remote_ent.entity_id)
|
||||
== 1867188557
|
||||
)
|
||||
assert (
|
||||
aid_storage.get_or_allocate_aid_for_entity_id("remote.has_no_unique_id")
|
||||
== 1872038229
|
||||
)
|
||||
|
||||
aid_storage.delete_aid(get_system_unique_id(light_ent))
|
||||
aid_storage.delete_aid(get_system_unique_id(light_ent2))
|
||||
aid_storage.delete_aid(get_system_unique_id(remote_ent))
|
||||
aid_storage.delete_aid("non-existant-one")
|
||||
|
||||
for _ in range(0, 2):
|
||||
assert (
|
||||
aid_storage.get_or_allocate_aid_for_entity_id(light_ent.entity_id)
|
||||
== 1692141785
|
||||
)
|
||||
assert (
|
||||
aid_storage.get_or_allocate_aid_for_entity_id(light_ent2.entity_id)
|
||||
== 2732133210
|
||||
)
|
||||
assert (
|
||||
aid_storage.get_or_allocate_aid_for_entity_id(remote_ent.entity_id)
|
||||
== 1867188557
|
||||
)
|
||||
assert (
|
||||
aid_storage.get_or_allocate_aid_for_entity_id("remote.has_no_unique_id")
|
||||
== 1872038229
|
||||
)
|
||||
|
||||
|
||||
async def test_aid_adler32_collision(hass, device_reg, entity_reg):
|
||||
"""Test generating aids."""
|
||||
config_entry = MockConfigEntry(domain="test", data={})
|
||||
config_entry.add_to_hass(hass)
|
||||
device_entry = device_reg.async_get_or_create(
|
||||
config_entry_id=config_entry.entry_id,
|
||||
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.homekit.aidmanager.AccessoryAidStorage.async_schedule_save"
|
||||
):
|
||||
aid_storage = AccessoryAidStorage(hass)
|
||||
await aid_storage.async_initialize()
|
||||
|
||||
seen_aids = set()
|
||||
|
||||
for unique_id in range(0, 202):
|
||||
ent = entity_reg.async_get_or_create(
|
||||
"light", "device", unique_id, device_id=device_entry.id
|
||||
)
|
||||
hass.states.async_set(ent.entity_id, "on")
|
||||
aid = aid_storage.get_or_allocate_aid_for_entity_id(ent.entity_id)
|
||||
assert aid not in seen_aids
|
||||
seen_aids.add(aid)
|
|
@ -12,10 +12,10 @@ from homeassistant.components.homekit import (
|
|||
STATUS_STOPPED,
|
||||
STATUS_WAIT,
|
||||
HomeKit,
|
||||
generate_aid,
|
||||
)
|
||||
from homeassistant.components.homekit.accessories import HomeBridge
|
||||
from homeassistant.components.homekit.const import (
|
||||
AID_STORAGE,
|
||||
BRIDGE_NAME,
|
||||
CONF_AUTO_START,
|
||||
CONF_SAFE_MODE,
|
||||
|
@ -51,17 +51,6 @@ def debounce_patcher():
|
|||
patcher.stop()
|
||||
|
||||
|
||||
def test_generate_aid():
|
||||
"""Test generate aid method."""
|
||||
aid = generate_aid("demo.entity")
|
||||
assert isinstance(aid, int)
|
||||
assert aid >= 2 and aid <= 18446744073709551615
|
||||
|
||||
with patch(f"{PATH_HOMEKIT}.adler32") as mock_adler32:
|
||||
mock_adler32.side_effect = [0, 1]
|
||||
assert generate_aid("demo.entity") is None
|
||||
|
||||
|
||||
async def test_setup_min(hass):
|
||||
"""Test async_setup with min config options."""
|
||||
with patch(f"{PATH_HOMEKIT}.HomeKit") as mock_homekit:
|
||||
|
@ -223,29 +212,31 @@ async def test_homekit_setup_safe_mode(hass, hk_driver):
|
|||
assert homekit.driver.safe_mode is True
|
||||
|
||||
|
||||
async def test_homekit_add_accessory():
|
||||
async def test_homekit_add_accessory(hass):
|
||||
"""Add accessory if config exists and get_acc returns an accessory."""
|
||||
homekit = HomeKit("hass", None, None, None, lambda entity_id: True, {}, None, None)
|
||||
homekit = HomeKit(hass, None, None, None, lambda entity_id: True, {}, None, None)
|
||||
homekit.driver = "driver"
|
||||
homekit.bridge = mock_bridge = Mock()
|
||||
homekit.bridge.accessories = range(10)
|
||||
|
||||
assert await setup.async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
with patch(f"{PATH_HOMEKIT}.get_accessory") as mock_get_acc:
|
||||
|
||||
mock_get_acc.side_effect = [None, "acc", None]
|
||||
homekit.add_bridge_accessory(State("light.demo", "on"))
|
||||
mock_get_acc.assert_called_with("hass", "driver", ANY, 363398124, {})
|
||||
mock_get_acc.assert_called_with(hass, "driver", ANY, 363398124, {})
|
||||
assert not mock_bridge.add_accessory.called
|
||||
|
||||
homekit.add_bridge_accessory(State("demo.test", "on"))
|
||||
mock_get_acc.assert_called_with("hass", "driver", ANY, 294192020, {})
|
||||
mock_get_acc.assert_called_with(hass, "driver", ANY, 294192020, {})
|
||||
assert mock_bridge.add_accessory.called
|
||||
|
||||
homekit.add_bridge_accessory(State("demo.test_2", "on"))
|
||||
mock_get_acc.assert_called_with("hass", "driver", ANY, 429982757, {})
|
||||
mock_get_acc.assert_called_with(hass, "driver", ANY, 429982757, {})
|
||||
mock_bridge.add_accessory.assert_called_with("acc")
|
||||
|
||||
|
||||
async def test_homekit_remove_accessory():
|
||||
async def test_homekit_remove_accessory(hass):
|
||||
"""Remove accessory from bridge."""
|
||||
homekit = HomeKit("hass", None, None, None, lambda entity_id: True, {}, None, None)
|
||||
homekit.driver = "driver"
|
||||
|
@ -259,8 +250,12 @@ async def test_homekit_remove_accessory():
|
|||
|
||||
async def test_homekit_entity_filter(hass):
|
||||
"""Test the entity filter."""
|
||||
assert await setup.async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
entity_filter = generate_filter(["cover"], ["demo.test"], [], [])
|
||||
homekit = HomeKit(hass, None, None, None, entity_filter, {}, None, None)
|
||||
homekit.bridge = Mock()
|
||||
homekit.bridge.accessories = {}
|
||||
|
||||
with patch(f"{PATH_HOMEKIT}.get_accessory") as mock_get_acc:
|
||||
mock_get_acc.return_value = None
|
||||
|
@ -314,6 +309,8 @@ async def test_homekit_start_with_a_broken_accessory(hass, hk_driver, debounce_p
|
|||
pin = b"123-45-678"
|
||||
entity_filter = generate_filter(["cover", "light"], ["demo.test"], [], [])
|
||||
|
||||
assert await setup.async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
homekit = HomeKit(hass, None, None, None, entity_filter, {}, None, None)
|
||||
homekit.bridge = Mock()
|
||||
homekit.bridge.accessories = []
|
||||
|
@ -366,6 +363,7 @@ async def test_homekit_reset_accessories(hass):
|
|||
entity_id = "light.demo"
|
||||
homekit = HomeKit(hass, None, None, None, {}, {entity_id: {}}, None)
|
||||
homekit.bridge = Mock()
|
||||
homekit.bridge.accessories = {}
|
||||
|
||||
with patch(f"{PATH_HOMEKIT}.HomeKit", return_value=homekit), patch(
|
||||
f"{PATH_HOMEKIT}.HomeKit.setup"
|
||||
|
@ -375,7 +373,7 @@ async def test_homekit_reset_accessories(hass):
|
|||
|
||||
assert await setup.async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
|
||||
aid = generate_aid(entity_id)
|
||||
aid = hass.data[AID_STORAGE].get_or_allocate_aid_for_entity_id(entity_id)
|
||||
homekit.bridge.accessories = {aid: "acc"}
|
||||
homekit.status = STATUS_RUNNING
|
||||
|
||||
|
@ -394,11 +392,17 @@ async def test_homekit_reset_accessories(hass):
|
|||
|
||||
async def test_homekit_too_many_accessories(hass, hk_driver):
|
||||
"""Test adding too many accessories to HomeKit."""
|
||||
homekit = HomeKit(hass, None, None, None, None, None, None)
|
||||
|
||||
entity_filter = generate_filter(["cover", "light"], ["demo.test"], [], [])
|
||||
|
||||
homekit = HomeKit(hass, None, None, None, entity_filter, {}, None, None)
|
||||
homekit.bridge = Mock()
|
||||
homekit.bridge.accessories = range(MAX_DEVICES + 1)
|
||||
# The bridge itself counts as an accessory
|
||||
homekit.bridge.accessories = range(MAX_DEVICES)
|
||||
homekit.driver = hk_driver
|
||||
|
||||
hass.states.async_set("light.demo", "on")
|
||||
|
||||
with patch("pyhap.accessory_driver.AccessoryDriver.start"), patch(
|
||||
"pyhap.accessory_driver.AccessoryDriver.add_accessory"
|
||||
), patch("homeassistant.components.homekit._LOGGER.warning") as mock_warn:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue