931 lines
32 KiB
Python
931 lines
32 KiB
Python
"""Tests for the Device Registry."""
|
|
import asyncio
|
|
|
|
import pytest
|
|
|
|
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED
|
|
from homeassistant.core import CoreState, callback
|
|
from homeassistant.helpers import device_registry, entity_registry
|
|
|
|
from tests.async_mock import patch
|
|
from tests.common import MockConfigEntry, flush_store, mock_device_registry
|
|
|
|
|
|
@pytest.fixture
|
|
def registry(hass):
|
|
"""Return an empty, loaded, registry."""
|
|
return mock_device_registry(hass)
|
|
|
|
|
|
@pytest.fixture
|
|
def update_events(hass):
|
|
"""Capture update events."""
|
|
events = []
|
|
|
|
@callback
|
|
def async_capture(event):
|
|
events.append(event.data)
|
|
|
|
hass.bus.async_listen(device_registry.EVENT_DEVICE_REGISTRY_UPDATED, async_capture)
|
|
|
|
return events
|
|
|
|
|
|
async def test_get_or_create_returns_same_entry(hass, registry, update_events):
|
|
"""Make sure we do not duplicate entries."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
sw_version="sw-version",
|
|
name="name",
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "11:22:33:66:77:88")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry3 = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
)
|
|
|
|
assert len(registry.devices) == 1
|
|
assert entry.id == entry2.id
|
|
assert entry.id == entry3.id
|
|
assert entry.identifiers == {("bridgeid", "0123")}
|
|
|
|
assert entry3.manufacturer == "manufacturer"
|
|
assert entry3.model == "model"
|
|
assert entry3.name == "name"
|
|
assert entry3.sw_version == "sw-version"
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
# Only 2 update events. The third entry did not generate any changes.
|
|
assert len(update_events) == 2
|
|
assert update_events[0]["action"] == "create"
|
|
assert update_events[0]["device_id"] == entry.id
|
|
assert update_events[1]["action"] == "update"
|
|
assert update_events[1]["device_id"] == entry.id
|
|
|
|
|
|
async def test_requirement_for_identifier_or_connection(registry):
|
|
"""Make sure we do require some descriptor of device."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers=set(),
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections=set(),
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry3 = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections=set(),
|
|
identifiers=set(),
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
|
|
assert len(registry.devices) == 2
|
|
assert entry
|
|
assert entry2
|
|
assert entry3 is None
|
|
|
|
|
|
async def test_multiple_config_entries(registry):
|
|
"""Make sure we do not get duplicate entries."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="456",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry3 = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
|
|
assert len(registry.devices) == 1
|
|
assert entry.id == entry2.id
|
|
assert entry.id == entry3.id
|
|
assert entry2.config_entries == {"123", "456"}
|
|
|
|
|
|
async def test_loading_from_storage(hass, hass_storage):
|
|
"""Test loading stored devices on start."""
|
|
hass_storage[device_registry.STORAGE_KEY] = {
|
|
"version": device_registry.STORAGE_VERSION,
|
|
"data": {
|
|
"devices": [
|
|
{
|
|
"config_entries": ["1234"],
|
|
"connections": [["Zigbee", "01.23.45.67.89"]],
|
|
"id": "abcdefghijklm",
|
|
"identifiers": [["serial", "12:34:56:AB:CD:EF"]],
|
|
"manufacturer": "manufacturer",
|
|
"model": "model",
|
|
"name": "name",
|
|
"sw_version": "version",
|
|
"entry_type": "service",
|
|
"area_id": "12345A",
|
|
"name_by_user": "Test Friendly Name",
|
|
}
|
|
],
|
|
"deleted_devices": [
|
|
{
|
|
"config_entries": ["1234"],
|
|
"connections": [["Zigbee", "23.45.67.89.01"]],
|
|
"id": "bcdefghijklmn",
|
|
"identifiers": [["serial", "34:56:AB:CD:EF:12"]],
|
|
}
|
|
],
|
|
},
|
|
}
|
|
|
|
registry = await device_registry.async_get_registry(hass)
|
|
assert len(registry.devices) == 1
|
|
assert len(registry.deleted_devices) == 1
|
|
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={("Zigbee", "01.23.45.67.89")},
|
|
identifiers={("serial", "12:34:56:AB:CD:EF")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
assert entry.id == "abcdefghijklm"
|
|
assert entry.area_id == "12345A"
|
|
assert entry.name_by_user == "Test Friendly Name"
|
|
assert entry.entry_type == "service"
|
|
assert isinstance(entry.config_entries, set)
|
|
assert isinstance(entry.connections, set)
|
|
assert isinstance(entry.identifiers, set)
|
|
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={("Zigbee", "23.45.67.89.01")},
|
|
identifiers={("serial", "34:56:AB:CD:EF:12")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
assert entry.id == "bcdefghijklmn"
|
|
assert isinstance(entry.config_entries, set)
|
|
assert isinstance(entry.connections, set)
|
|
assert isinstance(entry.identifiers, set)
|
|
|
|
|
|
async def test_removing_config_entries(hass, registry, update_events):
|
|
"""Make sure we do not get duplicate entries."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="456",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry3 = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "34:56:78:CD:EF:12")},
|
|
identifiers={("bridgeid", "4567")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
|
|
assert len(registry.devices) == 2
|
|
assert entry.id == entry2.id
|
|
assert entry.id != entry3.id
|
|
assert entry2.config_entries == {"123", "456"}
|
|
|
|
registry.async_clear_config_entry("123")
|
|
entry = registry.async_get_device({("bridgeid", "0123")}, set())
|
|
entry3_removed = registry.async_get_device({("bridgeid", "4567")}, set())
|
|
|
|
assert entry.config_entries == {"456"}
|
|
assert entry3_removed is None
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(update_events) == 5
|
|
assert update_events[0]["action"] == "create"
|
|
assert update_events[0]["device_id"] == entry.id
|
|
assert update_events[1]["action"] == "update"
|
|
assert update_events[1]["device_id"] == entry2.id
|
|
assert update_events[2]["action"] == "create"
|
|
assert update_events[2]["device_id"] == entry3.id
|
|
assert update_events[3]["action"] == "update"
|
|
assert update_events[3]["device_id"] == entry.id
|
|
assert update_events[4]["action"] == "remove"
|
|
assert update_events[4]["device_id"] == entry3.id
|
|
|
|
|
|
async def test_deleted_device_removing_config_entries(hass, registry, update_events):
|
|
"""Make sure we do not get duplicate entries."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="456",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry3 = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "34:56:78:CD:EF:12")},
|
|
identifiers={("bridgeid", "4567")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
|
|
assert len(registry.devices) == 2
|
|
assert len(registry.deleted_devices) == 0
|
|
assert entry.id == entry2.id
|
|
assert entry.id != entry3.id
|
|
assert entry2.config_entries == {"123", "456"}
|
|
|
|
registry.async_remove_device(entry.id)
|
|
registry.async_remove_device(entry3.id)
|
|
|
|
assert len(registry.devices) == 0
|
|
assert len(registry.deleted_devices) == 2
|
|
|
|
await hass.async_block_till_done()
|
|
assert len(update_events) == 5
|
|
assert update_events[0]["action"] == "create"
|
|
assert update_events[0]["device_id"] == entry.id
|
|
assert update_events[1]["action"] == "update"
|
|
assert update_events[1]["device_id"] == entry2.id
|
|
assert update_events[2]["action"] == "create"
|
|
assert update_events[2]["device_id"] == entry3.id
|
|
assert update_events[3]["action"] == "remove"
|
|
assert update_events[3]["device_id"] == entry.id
|
|
assert update_events[4]["action"] == "remove"
|
|
assert update_events[4]["device_id"] == entry3.id
|
|
|
|
registry.async_clear_config_entry("123")
|
|
assert len(registry.devices) == 0
|
|
assert len(registry.deleted_devices) == 1
|
|
|
|
registry.async_clear_config_entry("456")
|
|
assert len(registry.devices) == 0
|
|
assert len(registry.deleted_devices) == 0
|
|
|
|
# No event when a deleted device is purged
|
|
await hass.async_block_till_done()
|
|
assert len(update_events) == 5
|
|
|
|
# Re-add, expect new device id
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
|
|
assert entry.id != entry2.id
|
|
|
|
|
|
async def test_removing_area_id(registry):
|
|
"""Make sure we can clear area id."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
|
|
entry_w_area = registry.async_update_device(entry.id, area_id="12345A")
|
|
|
|
registry.async_clear_area_id("12345A")
|
|
entry_wo_area = registry.async_get_device({("bridgeid", "0123")}, set())
|
|
|
|
assert not entry_wo_area.area_id
|
|
assert entry_w_area != entry_wo_area
|
|
|
|
|
|
async def test_deleted_device_removing_area_id(registry):
|
|
"""Make sure we can clear area id of deleted device."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
|
|
entry_w_area = registry.async_update_device(entry.id, area_id="12345A")
|
|
|
|
registry.async_remove_device(entry.id)
|
|
registry.async_clear_area_id("12345A")
|
|
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
assert entry.id == entry2.id
|
|
|
|
entry_wo_area = registry.async_get_device({("bridgeid", "0123")}, set())
|
|
|
|
assert not entry_wo_area.area_id
|
|
assert entry_w_area != entry_wo_area
|
|
|
|
|
|
async def test_specifying_via_device_create(registry):
|
|
"""Test specifying a via_device and updating."""
|
|
via = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("hue", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="via",
|
|
)
|
|
|
|
light = registry.async_get_or_create(
|
|
config_entry_id="456",
|
|
connections=set(),
|
|
identifiers={("hue", "456")},
|
|
manufacturer="manufacturer",
|
|
model="light",
|
|
via_device=("hue", "0123"),
|
|
)
|
|
|
|
assert light.via_device_id == via.id
|
|
|
|
|
|
async def test_specifying_via_device_update(registry):
|
|
"""Test specifying a via_device and updating."""
|
|
light = registry.async_get_or_create(
|
|
config_entry_id="456",
|
|
connections=set(),
|
|
identifiers={("hue", "456")},
|
|
manufacturer="manufacturer",
|
|
model="light",
|
|
via_device=("hue", "0123"),
|
|
)
|
|
|
|
assert light.via_device_id is None
|
|
|
|
via = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("hue", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="via",
|
|
)
|
|
|
|
light = registry.async_get_or_create(
|
|
config_entry_id="456",
|
|
connections=set(),
|
|
identifiers={("hue", "456")},
|
|
manufacturer="manufacturer",
|
|
model="light",
|
|
via_device=("hue", "0123"),
|
|
)
|
|
|
|
assert light.via_device_id == via.id
|
|
|
|
|
|
async def test_loading_saving_data(hass, registry):
|
|
"""Test that we load/save data correctly."""
|
|
orig_via = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("hue", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="via",
|
|
name="Original Name",
|
|
sw_version="Orig SW 1",
|
|
entry_type="device",
|
|
)
|
|
|
|
orig_light = registry.async_get_or_create(
|
|
config_entry_id="456",
|
|
connections=set(),
|
|
identifiers={("hue", "456")},
|
|
manufacturer="manufacturer",
|
|
model="light",
|
|
via_device=("hue", "0123"),
|
|
)
|
|
|
|
orig_light2 = registry.async_get_or_create(
|
|
config_entry_id="456",
|
|
connections=set(),
|
|
identifiers={("hue", "789")},
|
|
manufacturer="manufacturer",
|
|
model="light",
|
|
via_device=("hue", "0123"),
|
|
)
|
|
|
|
registry.async_remove_device(orig_light2.id)
|
|
|
|
assert len(registry.devices) == 2
|
|
assert len(registry.deleted_devices) == 1
|
|
|
|
orig_via = registry.async_update_device(
|
|
orig_via.id, area_id="mock-area-id", name_by_user="mock-name-by-user"
|
|
)
|
|
|
|
# Now load written data in new registry
|
|
registry2 = device_registry.DeviceRegistry(hass)
|
|
await flush_store(registry._store)
|
|
await registry2.async_load()
|
|
|
|
# Ensure same order
|
|
assert list(registry.devices) == list(registry2.devices)
|
|
assert list(registry.deleted_devices) == list(registry2.deleted_devices)
|
|
|
|
new_via = registry2.async_get_device({("hue", "0123")}, set())
|
|
new_light = registry2.async_get_device({("hue", "456")}, set())
|
|
|
|
assert orig_via == new_via
|
|
assert orig_light == new_light
|
|
|
|
|
|
async def test_no_unnecessary_changes(registry):
|
|
"""Make sure we do not consider devices changes."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={("ethernet", "12:34:56:78:90:AB:CD:EF")},
|
|
identifiers={("hue", "456"), ("bla", "123")},
|
|
)
|
|
with patch(
|
|
"homeassistant.helpers.device_registry.DeviceRegistry.async_schedule_save"
|
|
) as mock_save:
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="1234", identifiers={("hue", "456")}
|
|
)
|
|
|
|
assert entry.id == entry2.id
|
|
assert len(mock_save.mock_calls) == 0
|
|
|
|
|
|
async def test_format_mac(registry):
|
|
"""Make sure we normalize mac addresses."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
)
|
|
for mac in ["123456ABCDEF", "123456abcdef", "12:34:56:ab:cd:ef", "1234.56ab.cdef"]:
|
|
test_entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, mac)},
|
|
)
|
|
assert test_entry.id == entry.id, mac
|
|
assert test_entry.connections == {
|
|
(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:ab:cd:ef")
|
|
}
|
|
|
|
# This should not raise
|
|
for invalid in [
|
|
"invalid_mac",
|
|
"123456ABCDEFG", # 1 extra char
|
|
"12:34:56:ab:cdef", # not enough :
|
|
"12:34:56:ab:cd:e:f", # too many :
|
|
"1234.56abcdef", # not enough .
|
|
"123.456.abc.def", # too many .
|
|
]:
|
|
invalid_mac_entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, invalid)},
|
|
)
|
|
assert list(invalid_mac_entry.connections)[0][1] == invalid
|
|
|
|
|
|
async def test_update(registry):
|
|
"""Verify that we can update some attributes of a device."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("hue", "456"), ("bla", "123")},
|
|
)
|
|
new_identifiers = {("hue", "654"), ("bla", "321")}
|
|
assert not entry.area_id
|
|
assert not entry.name_by_user
|
|
|
|
with patch.object(registry, "async_schedule_save") as mock_save:
|
|
updated_entry = registry.async_update_device(
|
|
entry.id,
|
|
area_id="12345A",
|
|
manufacturer="Test Producer",
|
|
model="Test Model",
|
|
name_by_user="Test Friendly Name",
|
|
new_identifiers=new_identifiers,
|
|
via_device_id="98765B",
|
|
)
|
|
|
|
assert mock_save.call_count == 1
|
|
assert updated_entry != entry
|
|
assert updated_entry.area_id == "12345A"
|
|
assert updated_entry.manufacturer == "Test Producer"
|
|
assert updated_entry.model == "Test Model"
|
|
assert updated_entry.name_by_user == "Test Friendly Name"
|
|
assert updated_entry.identifiers == new_identifiers
|
|
assert updated_entry.via_device_id == "98765B"
|
|
|
|
assert registry.async_get_device({("hue", "456")}, {}) is None
|
|
assert registry.async_get_device({("bla", "123")}, {}) is None
|
|
|
|
assert registry.async_get_device({("hue", "654")}, {}) == updated_entry
|
|
assert registry.async_get_device({("bla", "321")}, {}) == updated_entry
|
|
|
|
assert (
|
|
registry.async_get_device(
|
|
{}, {(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}
|
|
)
|
|
== updated_entry
|
|
)
|
|
|
|
assert registry.async_get(updated_entry.id) is not None
|
|
|
|
|
|
async def test_update_remove_config_entries(hass, registry, update_events):
|
|
"""Make sure we do not get duplicate entries."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="456",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry3 = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "34:56:78:CD:EF:12")},
|
|
identifiers={("bridgeid", "4567")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
|
|
assert len(registry.devices) == 2
|
|
assert entry.id == entry2.id
|
|
assert entry.id != entry3.id
|
|
assert entry2.config_entries == {"123", "456"}
|
|
|
|
updated_entry = registry.async_update_device(
|
|
entry2.id, remove_config_entry_id="123"
|
|
)
|
|
removed_entry = registry.async_update_device(
|
|
entry3.id, remove_config_entry_id="123"
|
|
)
|
|
|
|
assert updated_entry.config_entries == {"456"}
|
|
assert removed_entry is None
|
|
|
|
removed_entry = registry.async_get_device({("bridgeid", "4567")}, set())
|
|
|
|
assert removed_entry is None
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(update_events) == 5
|
|
assert update_events[0]["action"] == "create"
|
|
assert update_events[0]["device_id"] == entry.id
|
|
assert update_events[1]["action"] == "update"
|
|
assert update_events[1]["device_id"] == entry2.id
|
|
assert update_events[2]["action"] == "create"
|
|
assert update_events[2]["device_id"] == entry3.id
|
|
assert update_events[3]["action"] == "update"
|
|
assert update_events[3]["device_id"] == entry.id
|
|
assert update_events[4]["action"] == "remove"
|
|
assert update_events[4]["device_id"] == entry3.id
|
|
|
|
|
|
async def test_loading_race_condition(hass):
|
|
"""Test only one storage load called when concurrent loading occurred ."""
|
|
with patch(
|
|
"homeassistant.helpers.device_registry.DeviceRegistry.async_load"
|
|
) as mock_load:
|
|
results = await asyncio.gather(
|
|
device_registry.async_get_registry(hass),
|
|
device_registry.async_get_registry(hass),
|
|
)
|
|
|
|
mock_load.assert_called_once_with()
|
|
assert results[0] == results[1]
|
|
|
|
|
|
async def test_update_sw_version(registry):
|
|
"""Verify that we can update software version of a device."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bla", "123")},
|
|
)
|
|
assert not entry.sw_version
|
|
sw_version = "0x20020263"
|
|
|
|
with patch.object(registry, "async_schedule_save") as mock_save:
|
|
updated_entry = registry.async_update_device(entry.id, sw_version=sw_version)
|
|
|
|
assert mock_save.call_count == 1
|
|
assert updated_entry != entry
|
|
assert updated_entry.sw_version == sw_version
|
|
|
|
|
|
async def test_cleanup_device_registry(hass, registry):
|
|
"""Test cleanup works."""
|
|
config_entry = MockConfigEntry(domain="hue")
|
|
config_entry.add_to_hass(hass)
|
|
|
|
d1 = registry.async_get_or_create(
|
|
identifiers={("hue", "d1")}, config_entry_id=config_entry.entry_id
|
|
)
|
|
registry.async_get_or_create(
|
|
identifiers={("hue", "d2")}, config_entry_id=config_entry.entry_id
|
|
)
|
|
d3 = registry.async_get_or_create(
|
|
identifiers={("hue", "d3")}, config_entry_id=config_entry.entry_id
|
|
)
|
|
registry.async_get_or_create(
|
|
identifiers={("something", "d4")}, config_entry_id="non_existing"
|
|
)
|
|
|
|
ent_reg = await entity_registry.async_get_registry(hass)
|
|
ent_reg.async_get_or_create("light", "hue", "e1", device_id=d1.id)
|
|
ent_reg.async_get_or_create("light", "hue", "e2", device_id=d1.id)
|
|
ent_reg.async_get_or_create("light", "hue", "e3", device_id=d3.id)
|
|
|
|
device_registry.async_cleanup(hass, registry, ent_reg)
|
|
|
|
assert registry.async_get_device({("hue", "d1")}, set()) is not None
|
|
assert registry.async_get_device({("hue", "d2")}, set()) is not None
|
|
assert registry.async_get_device({("hue", "d3")}, set()) is not None
|
|
assert registry.async_get_device({("something", "d4")}, set()) is None
|
|
|
|
|
|
async def test_cleanup_startup(hass):
|
|
"""Test we run a cleanup on startup."""
|
|
hass.state = CoreState.not_running
|
|
await device_registry.async_get_registry(hass)
|
|
|
|
with patch(
|
|
"homeassistant.helpers.device_registry.Debouncer.async_call"
|
|
) as mock_call:
|
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(mock_call.mock_calls) == 1
|
|
|
|
|
|
async def test_cleanup_entity_registry_change(hass):
|
|
"""Test we run a cleanup when entity registry changes."""
|
|
await device_registry.async_get_registry(hass)
|
|
ent_reg = await entity_registry.async_get_registry(hass)
|
|
|
|
with patch(
|
|
"homeassistant.helpers.device_registry.Debouncer.async_call"
|
|
) as mock_call:
|
|
entity = ent_reg.async_get_or_create("light", "hue", "e1")
|
|
await hass.async_block_till_done()
|
|
assert len(mock_call.mock_calls) == 0
|
|
|
|
# Normal update does not trigger
|
|
ent_reg.async_update_entity(entity.entity_id, name="updated")
|
|
await hass.async_block_till_done()
|
|
assert len(mock_call.mock_calls) == 0
|
|
|
|
# Device ID update triggers
|
|
ent_reg.async_get_or_create("light", "hue", "e1", device_id="bla")
|
|
await hass.async_block_till_done()
|
|
assert len(mock_call.mock_calls) == 1
|
|
|
|
# Removal also triggers
|
|
ent_reg.async_remove(entity.entity_id)
|
|
await hass.async_block_till_done()
|
|
assert len(mock_call.mock_calls) == 2
|
|
|
|
|
|
async def test_restore_device(hass, registry, update_events):
|
|
"""Make sure device id is stable."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
|
|
assert len(registry.devices) == 1
|
|
assert len(registry.deleted_devices) == 0
|
|
|
|
registry.async_remove_device(entry.id)
|
|
|
|
assert len(registry.devices) == 0
|
|
assert len(registry.deleted_devices) == 1
|
|
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "34:56:78:CD:EF:12")},
|
|
identifiers={("bridgeid", "4567")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
entry3 = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
manufacturer="manufacturer",
|
|
model="model",
|
|
)
|
|
|
|
assert entry.id == entry3.id
|
|
assert entry.id != entry2.id
|
|
assert len(registry.devices) == 2
|
|
assert len(registry.deleted_devices) == 0
|
|
|
|
assert isinstance(entry3.config_entries, set)
|
|
assert isinstance(entry3.connections, set)
|
|
assert isinstance(entry3.identifiers, set)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(update_events) == 4
|
|
assert update_events[0]["action"] == "create"
|
|
assert update_events[0]["device_id"] == entry.id
|
|
assert update_events[1]["action"] == "remove"
|
|
assert update_events[1]["device_id"] == entry.id
|
|
assert update_events[2]["action"] == "create"
|
|
assert update_events[2]["device_id"] == entry2.id
|
|
assert update_events[3]["action"] == "create"
|
|
assert update_events[3]["device_id"] == entry3.id
|
|
|
|
|
|
async def test_restore_simple_device(hass, registry, update_events):
|
|
"""Make sure device id is stable."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
)
|
|
|
|
assert len(registry.devices) == 1
|
|
assert len(registry.deleted_devices) == 0
|
|
|
|
registry.async_remove_device(entry.id)
|
|
|
|
assert len(registry.devices) == 0
|
|
assert len(registry.deleted_devices) == 1
|
|
|
|
entry2 = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "34:56:78:CD:EF:12")},
|
|
identifiers={("bridgeid", "4567")},
|
|
)
|
|
entry3 = registry.async_get_or_create(
|
|
config_entry_id="123",
|
|
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
|
|
identifiers={("bridgeid", "0123")},
|
|
)
|
|
|
|
assert entry.id == entry3.id
|
|
assert entry.id != entry2.id
|
|
assert len(registry.devices) == 2
|
|
assert len(registry.deleted_devices) == 0
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert len(update_events) == 4
|
|
assert update_events[0]["action"] == "create"
|
|
assert update_events[0]["device_id"] == entry.id
|
|
assert update_events[1]["action"] == "remove"
|
|
assert update_events[1]["device_id"] == entry.id
|
|
assert update_events[2]["action"] == "create"
|
|
assert update_events[2]["device_id"] == entry2.id
|
|
assert update_events[3]["action"] == "create"
|
|
assert update_events[3]["device_id"] == entry3.id
|
|
|
|
|
|
async def test_get_or_create_empty_then_set_default_values(hass, registry):
|
|
"""Test creating an entry, then setting default name, model, manufacturer."""
|
|
entry = registry.async_get_or_create(
|
|
identifiers={("bridgeid", "0123")}, config_entry_id="1234"
|
|
)
|
|
assert entry.name is None
|
|
assert entry.model is None
|
|
assert entry.manufacturer is None
|
|
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
identifiers={("bridgeid", "0123")},
|
|
default_name="default name 1",
|
|
default_model="default model 1",
|
|
default_manufacturer="default manufacturer 1",
|
|
)
|
|
assert entry.name == "default name 1"
|
|
assert entry.model == "default model 1"
|
|
assert entry.manufacturer == "default manufacturer 1"
|
|
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
identifiers={("bridgeid", "0123")},
|
|
default_name="default name 2",
|
|
default_model="default model 2",
|
|
default_manufacturer="default manufacturer 2",
|
|
)
|
|
assert entry.name == "default name 1"
|
|
assert entry.model == "default model 1"
|
|
assert entry.manufacturer == "default manufacturer 1"
|
|
|
|
|
|
async def test_get_or_create_empty_then_update(hass, registry):
|
|
"""Test creating an entry, then setting name, model, manufacturer."""
|
|
entry = registry.async_get_or_create(
|
|
identifiers={("bridgeid", "0123")}, config_entry_id="1234"
|
|
)
|
|
assert entry.name is None
|
|
assert entry.model is None
|
|
assert entry.manufacturer is None
|
|
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
identifiers={("bridgeid", "0123")},
|
|
name="name 1",
|
|
model="model 1",
|
|
manufacturer="manufacturer 1",
|
|
)
|
|
assert entry.name == "name 1"
|
|
assert entry.model == "model 1"
|
|
assert entry.manufacturer == "manufacturer 1"
|
|
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
identifiers={("bridgeid", "0123")},
|
|
default_name="default name 1",
|
|
default_model="default model 1",
|
|
default_manufacturer="default manufacturer 1",
|
|
)
|
|
assert entry.name == "name 1"
|
|
assert entry.model == "model 1"
|
|
assert entry.manufacturer == "manufacturer 1"
|
|
|
|
|
|
async def test_get_or_create_sets_default_values(hass, registry):
|
|
"""Test creating an entry, then setting default name, model, manufacturer."""
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
identifiers={("bridgeid", "0123")},
|
|
default_name="default name 1",
|
|
default_model="default model 1",
|
|
default_manufacturer="default manufacturer 1",
|
|
)
|
|
assert entry.name == "default name 1"
|
|
assert entry.model == "default model 1"
|
|
assert entry.manufacturer == "default manufacturer 1"
|
|
|
|
entry = registry.async_get_or_create(
|
|
config_entry_id="1234",
|
|
identifiers={("bridgeid", "0123")},
|
|
default_name="default name 2",
|
|
default_model="default model 2",
|
|
default_manufacturer="default manufacturer 2",
|
|
)
|
|
assert entry.name == "default name 1"
|
|
assert entry.model == "default model 1"
|
|
assert entry.manufacturer == "default manufacturer 1"
|