From cba3b6ad944408b9ffd906f4da5e5f5fd615b174 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Tue, 27 Sep 2022 09:08:54 +0200 Subject: [PATCH] Add serial_number to device registry entries (#77713) --- .../components/config/device_registry.py | 1 + homeassistant/helpers/device_registry.py | 13 +- .../components/config/test_device_registry.py | 3 + tests/helpers/test_device_registry.py | 136 +++++++++++++++++- 4 files changed, 148 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/config/device_registry.py b/homeassistant/components/config/device_registry.py index 8edd9f1f4d39..118b898ec4cd 100644 --- a/homeassistant/components/config/device_registry.py +++ b/homeassistant/components/config/device_registry.py @@ -167,6 +167,7 @@ def _entry_dict(entry): "model": entry.model, "name_by_user": entry.name_by_user, "name": entry.name, + "serial_number": entry.serial_number, "sw_version": entry.sw_version, "via_device_id": entry.via_device_id, } diff --git a/homeassistant/helpers/device_registry.py b/homeassistant/helpers/device_registry.py index 908db74d40df..16ff67bed191 100644 --- a/homeassistant/helpers/device_registry.py +++ b/homeassistant/helpers/device_registry.py @@ -32,7 +32,7 @@ DATA_REGISTRY = "device_registry" EVENT_DEVICE_REGISTRY_UPDATED = "device_registry_updated" STORAGE_KEY = "core.device_registry" STORAGE_VERSION_MAJOR = 1 -STORAGE_VERSION_MINOR = 3 +STORAGE_VERSION_MINOR = 4 SAVE_DELAY = 10 CLEANUP_DELAY = 10 @@ -83,6 +83,7 @@ class DeviceEntry: model: str | None = attr.ib(default=None) name_by_user: str | None = attr.ib(default=None) name: str | None = attr.ib(default=None) + serial_number: str | None = attr.ib(default=None) suggested_area: str | None = attr.ib(default=None) sw_version: str | None = attr.ib(default=None) via_device_id: str | None = attr.ib(default=None) @@ -180,6 +181,10 @@ class DeviceRegistryStore(storage.Store[dict[str, list[dict[str, Any]]]]): # Introduced in 2022.2 for device in old_data["devices"]: device["hw_version"] = device.get("hw_version") + if old_minor_version < 4: + # Introduced in 2022.10 + for device in old_data["devices"]: + device["serial_number"] = device.get("serial_number") if old_major_version > 1: raise NotImplementedError @@ -301,6 +306,7 @@ class DeviceRegistry: manufacturer: str | None | UndefinedType = UNDEFINED, model: str | None | UndefinedType = UNDEFINED, name: str | None | UndefinedType = UNDEFINED, + serial_number: str | None | UndefinedType = UNDEFINED, suggested_area: str | None | UndefinedType = UNDEFINED, sw_version: str | None | UndefinedType = UNDEFINED, via_device: tuple[str, str] | None = None, @@ -366,6 +372,7 @@ class DeviceRegistry: merge_identifiers=identifiers or UNDEFINED, model=model, name=name, + serial_number=serial_number, suggested_area=suggested_area, sw_version=sw_version, via_device_id=via_device_id, @@ -395,6 +402,7 @@ class DeviceRegistry: name: str | None | UndefinedType = UNDEFINED, new_identifiers: set[tuple[str, str]] | UndefinedType = UNDEFINED, remove_config_entry_id: str | UndefinedType = UNDEFINED, + serial_number: str | None | UndefinedType = UNDEFINED, suggested_area: str | None | UndefinedType = UNDEFINED, sw_version: str | None | UndefinedType = UNDEFINED, via_device_id: str | None | UndefinedType = UNDEFINED, @@ -479,6 +487,7 @@ class DeviceRegistry: ("model", model), ("name", name), ("name_by_user", name_by_user), + ("serial_number", serial_number), ("suggested_area", suggested_area), ("sw_version", sw_version), ("via_device_id", via_device_id), @@ -566,6 +575,7 @@ class DeviceRegistry: model=device["model"], name_by_user=device["name_by_user"], name=device["name"], + serial_number=device["serial_number"], sw_version=device["sw_version"], via_device_id=device["via_device_id"], ) @@ -608,6 +618,7 @@ class DeviceRegistry: "model": entry.model, "name_by_user": entry.name_by_user, "name": entry.name, + "serial_number": entry.serial_number, "sw_version": entry.sw_version, "via_device_id": entry.via_device_id, } diff --git a/tests/components/config/test_device_registry.py b/tests/components/config/test_device_registry.py index 4f47e463751d..0903c81a424f 100644 --- a/tests/components/config/test_device_registry.py +++ b/tests/components/config/test_device_registry.py @@ -64,6 +64,7 @@ async def test_list_devices(hass, client, registry): "model": "model", "name_by_user": None, "name": None, + "serial_number": None, "sw_version": None, "via_device_id": None, }, @@ -80,6 +81,7 @@ async def test_list_devices(hass, client, registry): "model": "model", "name_by_user": None, "name": None, + "serial_number": None, "sw_version": None, "via_device_id": dev1, }, @@ -106,6 +108,7 @@ async def test_list_devices(hass, client, registry): "model": "model", "name_by_user": None, "name": None, + "serial_number": None, "sw_version": None, "via_device_id": None, } diff --git a/tests/helpers/test_device_registry.py b/tests/helpers/test_device_registry.py index 2c9a7956874b..fe702b9c294e 100644 --- a/tests/helpers/test_device_registry.py +++ b/tests/helpers/test_device_registry.py @@ -189,6 +189,7 @@ async def test_loading_from_storage(hass, hass_storage): "model": "model", "name_by_user": "Test Friendly Name", "name": "name", + "serial_number": "serial_no", "sw_version": "version", "via_device_id": None, } @@ -231,6 +232,7 @@ async def test_loading_from_storage(hass, hass_storage): model="model", name_by_user="Test Friendly Name", name="name", + serial_number="serial_no", suggested_area=None, # Not stored sw_version="version", ) @@ -261,8 +263,8 @@ async def test_loading_from_storage(hass, hass_storage): @pytest.mark.parametrize("load_registries", [False]) -async def test_migration_1_1_to_1_3(hass, hass_storage): - """Test migration from version 1.1 to 1.3.""" +async def test_migration_1_1_to_1_4(hass, hass_storage): + """Test migration from version 1.1 to 1.4.""" hass_storage[device_registry.STORAGE_KEY] = { "version": 1, "minor_version": 1, @@ -350,6 +352,7 @@ async def test_migration_1_1_to_1_3(hass, hass_storage): "model": "model", "name": "name", "name_by_user": None, + "serial_number": None, "sw_version": "new_version", "via_device_id": None, }, @@ -367,6 +370,7 @@ async def test_migration_1_1_to_1_3(hass, hass_storage): "model": None, "name_by_user": None, "name": None, + "serial_number": None, "sw_version": None, "via_device_id": None, }, @@ -385,8 +389,8 @@ async def test_migration_1_1_to_1_3(hass, hass_storage): @pytest.mark.parametrize("load_registries", [False]) -async def test_migration_1_2_to_1_3(hass, hass_storage): - """Test migration from version 1.2 to 1.3.""" +async def test_migration_1_2_to_1_4(hass, hass_storage): + """Test migration from version 1.2 to 1.4.""" hass_storage[device_registry.STORAGE_KEY] = { "version": 1, "minor_version": 2, @@ -473,6 +477,7 @@ async def test_migration_1_2_to_1_3(hass, hass_storage): "model": "model", "name": "name", "name_by_user": None, + "serial_number": None, "sw_version": "new_version", "via_device_id": None, }, @@ -490,6 +495,126 @@ async def test_migration_1_2_to_1_3(hass, hass_storage): "model": None, "name_by_user": None, "name": None, + "serial_number": None, + "sw_version": None, + "via_device_id": None, + }, + ], + "deleted_devices": [], + }, + } + + +@pytest.mark.parametrize("load_registries", [False]) +async def test_migration_1_3_to_1_4(hass, hass_storage): + """Test migration from version 1.3 to 1.4.""" + hass_storage[device_registry.STORAGE_KEY] = { + "version": 1, + "minor_version": 3, + "key": device_registry.STORAGE_KEY, + "data": { + "devices": [ + { + "area_id": None, + "config_entries": ["1234"], + "configuration_url": None, + "connections": [["Zigbee", "01.23.45.67.89"]], + "disabled_by": None, + "entry_type": "service", + "hw_version": "hw_version", + "id": "abcdefghijklm", + "identifiers": [["serial", "12:34:56:AB:CD:EF"]], + "manufacturer": "manufacturer", + "model": "model", + "name": "name", + "name_by_user": None, + "sw_version": "version", + "via_device_id": None, + }, + { + "area_id": None, + "config_entries": [None], + "configuration_url": None, + "connections": [], + "disabled_by": None, + "entry_type": None, + "hw_version": None, + "id": "invalid-entry-type", + "identifiers": [["serial", "mock-id-invalid-entry"]], + "manufacturer": None, + "model": None, + "name_by_user": None, + "name": None, + "sw_version": None, + "via_device_id": None, + }, + ], + "deleted_devices": [], + }, + } + + await device_registry.async_load(hass) + registry = device_registry.async_get(hass) + + # Test data was loaded + 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")}, + ) + assert entry.id == "abcdefghijklm" + + # Update to trigger a store + 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")}, + sw_version="new_version", + ) + assert entry.id == "abcdefghijklm" + + # Check we store migrated data + await flush_store(registry._store) + + assert hass_storage[device_registry.STORAGE_KEY] == { + "version": device_registry.STORAGE_VERSION_MAJOR, + "minor_version": device_registry.STORAGE_VERSION_MINOR, + "key": device_registry.STORAGE_KEY, + "data": { + "devices": [ + { + "area_id": None, + "config_entries": ["1234"], + "configuration_url": None, + "connections": [["Zigbee", "01.23.45.67.89"]], + "disabled_by": None, + "entry_type": "service", + "hw_version": "hw_version", + "id": "abcdefghijklm", + "identifiers": [["serial", "12:34:56:AB:CD:EF"]], + "manufacturer": "manufacturer", + "model": "model", + "name": "name", + "name_by_user": None, + "serial_number": None, + "sw_version": "new_version", + "via_device_id": None, + }, + { + "area_id": None, + "config_entries": [None], + "configuration_url": None, + "connections": [], + "disabled_by": None, + "entry_type": None, + "hw_version": None, + "id": "invalid-entry-type", + "identifiers": [["serial", "mock-id-invalid-entry"]], + "manufacturer": None, + "model": None, + "name_by_user": None, + "name": None, + "serial_number": None, "sw_version": None, "via_device_id": None, }, @@ -918,6 +1043,7 @@ async def test_update(hass, registry, update_events): name_by_user="Test Friendly Name", name="name", new_identifiers=new_identifiers, + serial_number="serial_no", suggested_area="suggested_area", sw_version="version", via_device_id="98765B", @@ -939,6 +1065,7 @@ async def test_update(hass, registry, update_events): model="Test Model", name_by_user="Test Friendly Name", name="name", + serial_number="serial_no", suggested_area="suggested_area", sw_version="version", via_device_id="98765B", @@ -978,6 +1105,7 @@ async def test_update(hass, registry, update_events): "model": None, "name": None, "name_by_user": None, + "serial_number": None, "suggested_area": None, "sw_version": None, "via_device_id": None,