Add created_at/modified_at to device registry (#122369)
This commit is contained in:
parent
19d9a91392
commit
4c853803f1
6 changed files with 147 additions and 1 deletions
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||
|
||||
from collections import defaultdict
|
||||
from collections.abc import Mapping
|
||||
from datetime import datetime
|
||||
from enum import StrEnum
|
||||
from functools import cached_property, lru_cache, partial
|
||||
import logging
|
||||
|
@ -23,6 +24,7 @@ from homeassistant.core import (
|
|||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.loader import async_suggest_report_issue
|
||||
from homeassistant.util.dt import utc_from_timestamp, utcnow
|
||||
from homeassistant.util.event_type import EventType
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
from homeassistant.util.json import format_unserializable_data
|
||||
|
@ -94,6 +96,7 @@ class DeviceInfo(TypedDict, total=False):
|
|||
|
||||
configuration_url: str | URL | None
|
||||
connections: set[tuple[str, str]]
|
||||
created_at: str
|
||||
default_manufacturer: str
|
||||
default_model: str
|
||||
default_name: str
|
||||
|
@ -102,6 +105,7 @@ class DeviceInfo(TypedDict, total=False):
|
|||
manufacturer: str | None
|
||||
model: str | None
|
||||
model_id: str | None
|
||||
modified_at: str
|
||||
name: str | None
|
||||
serial_number: str | None
|
||||
suggested_area: str | None
|
||||
|
@ -281,6 +285,7 @@ class DeviceEntry:
|
|||
config_entries: set[str] = attr.ib(converter=set, factory=set)
|
||||
configuration_url: str | None = attr.ib(default=None)
|
||||
connections: set[tuple[str, str]] = attr.ib(converter=set, factory=set)
|
||||
created_at: datetime = attr.ib(factory=utcnow)
|
||||
disabled_by: DeviceEntryDisabler | None = attr.ib(default=None)
|
||||
entry_type: DeviceEntryType | None = attr.ib(default=None)
|
||||
hw_version: str | None = attr.ib(default=None)
|
||||
|
@ -290,6 +295,7 @@ class DeviceEntry:
|
|||
manufacturer: str | None = attr.ib(default=None)
|
||||
model: str | None = attr.ib(default=None)
|
||||
model_id: str | None = attr.ib(default=None)
|
||||
modified_at: datetime = attr.ib(factory=utcnow)
|
||||
name_by_user: str | None = attr.ib(default=None)
|
||||
name: str | None = attr.ib(default=None)
|
||||
primary_config_entry: str | None = attr.ib(default=None)
|
||||
|
@ -316,6 +322,7 @@ class DeviceEntry:
|
|||
"configuration_url": self.configuration_url,
|
||||
"config_entries": list(self.config_entries),
|
||||
"connections": list(self.connections),
|
||||
"created_at": self.created_at.timestamp(),
|
||||
"disabled_by": self.disabled_by,
|
||||
"entry_type": self.entry_type,
|
||||
"hw_version": self.hw_version,
|
||||
|
@ -325,6 +332,7 @@ class DeviceEntry:
|
|||
"manufacturer": self.manufacturer,
|
||||
"model": self.model,
|
||||
"model_id": self.model_id,
|
||||
"modified_at": self.modified_at.timestamp(),
|
||||
"name_by_user": self.name_by_user,
|
||||
"name": self.name,
|
||||
"primary_config_entry": self.primary_config_entry,
|
||||
|
@ -359,6 +367,7 @@ class DeviceEntry:
|
|||
"config_entries": list(self.config_entries),
|
||||
"configuration_url": self.configuration_url,
|
||||
"connections": list(self.connections),
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"disabled_by": self.disabled_by,
|
||||
"entry_type": self.entry_type,
|
||||
"hw_version": self.hw_version,
|
||||
|
@ -368,6 +377,7 @@ class DeviceEntry:
|
|||
"manufacturer": self.manufacturer,
|
||||
"model": self.model,
|
||||
"model_id": self.model_id,
|
||||
"modified_at": self.modified_at.isoformat(),
|
||||
"name_by_user": self.name_by_user,
|
||||
"name": self.name,
|
||||
"primary_config_entry": self.primary_config_entry,
|
||||
|
@ -388,6 +398,8 @@ class DeletedDeviceEntry:
|
|||
identifiers: set[tuple[str, str]] = attr.ib()
|
||||
id: str = attr.ib()
|
||||
orphaned_timestamp: float | None = attr.ib()
|
||||
created_at: datetime = attr.ib(factory=utcnow)
|
||||
modified_at: datetime = attr.ib(factory=utcnow)
|
||||
|
||||
def to_device_entry(
|
||||
self,
|
||||
|
@ -400,6 +412,7 @@ class DeletedDeviceEntry:
|
|||
# type ignores: likely https://github.com/python/mypy/issues/8625
|
||||
config_entries={config_entry_id}, # type: ignore[arg-type]
|
||||
connections=self.connections & connections, # type: ignore[arg-type]
|
||||
created_at=self.created_at,
|
||||
identifiers=self.identifiers & identifiers, # type: ignore[arg-type]
|
||||
id=self.id,
|
||||
is_new=True,
|
||||
|
@ -413,9 +426,11 @@ class DeletedDeviceEntry:
|
|||
{
|
||||
"config_entries": list(self.config_entries),
|
||||
"connections": list(self.connections),
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"identifiers": list(self.identifiers),
|
||||
"id": self.id,
|
||||
"orphaned_timestamp": self.orphaned_timestamp,
|
||||
"modified_at": self.modified_at.isoformat(),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -490,8 +505,12 @@ class DeviceRegistryStore(storage.Store[dict[str, list[dict[str, Any]]]]):
|
|||
device.setdefault("primary_config_entry", None)
|
||||
if old_minor_version < 7:
|
||||
# Introduced in 2024.8
|
||||
created_at = utc_from_timestamp(0).isoformat()
|
||||
for device in old_data["devices"]:
|
||||
device.setdefault("model_id", None)
|
||||
device["created_at"] = device["modified_at"] = created_at
|
||||
for device in old_data["deleted_devices"]:
|
||||
device["created_at"] = device["modified_at"] = created_at
|
||||
|
||||
if old_major_version > 1:
|
||||
raise NotImplementedError
|
||||
|
@ -688,6 +707,7 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
|||
config_entry_id: str,
|
||||
configuration_url: str | URL | None | UndefinedType = UNDEFINED,
|
||||
connections: set[tuple[str, str]] | None | UndefinedType = UNDEFINED,
|
||||
created_at: str | datetime | UndefinedType = UNDEFINED, # will be ignored
|
||||
default_manufacturer: str | None | UndefinedType = UNDEFINED,
|
||||
default_model: str | None | UndefinedType = UNDEFINED,
|
||||
default_name: str | None | UndefinedType = UNDEFINED,
|
||||
|
@ -699,6 +719,7 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
|||
manufacturer: str | None | UndefinedType = UNDEFINED,
|
||||
model: str | None | UndefinedType = UNDEFINED,
|
||||
model_id: str | None | UndefinedType = UNDEFINED,
|
||||
modified_at: str | datetime | UndefinedType = UNDEFINED, # will be ignored
|
||||
name: str | None | UndefinedType = UNDEFINED,
|
||||
serial_number: str | None | UndefinedType = UNDEFINED,
|
||||
suggested_area: str | None | UndefinedType = UNDEFINED,
|
||||
|
@ -1035,6 +1056,10 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
|||
if not new_values:
|
||||
return old
|
||||
|
||||
if not RUNTIME_ONLY_ATTRS.issuperset(new_values):
|
||||
# Change modified_at if we are changing something that we store
|
||||
new_values["modified_at"] = utcnow()
|
||||
|
||||
self.hass.verify_event_loop_thread("device_registry.async_update_device")
|
||||
new = attr.evolve(old, **new_values)
|
||||
self.devices[device_id] = new
|
||||
|
@ -1114,6 +1139,7 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
|||
self.deleted_devices[device_id] = DeletedDeviceEntry(
|
||||
config_entries=device.config_entries,
|
||||
connections=device.connections,
|
||||
created_at=device.created_at,
|
||||
identifiers=device.identifiers,
|
||||
id=device.id,
|
||||
orphaned_timestamp=None,
|
||||
|
@ -1149,6 +1175,7 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
|||
tuple(conn) # type: ignore[misc]
|
||||
for conn in device["connections"]
|
||||
},
|
||||
created_at=datetime.fromisoformat(device["created_at"]),
|
||||
disabled_by=(
|
||||
DeviceEntryDisabler(device["disabled_by"])
|
||||
if device["disabled_by"]
|
||||
|
@ -1169,6 +1196,7 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
|||
manufacturer=device["manufacturer"],
|
||||
model=device["model"],
|
||||
model_id=device["model_id"],
|
||||
modified_at=datetime.fromisoformat(device["modified_at"]),
|
||||
name_by_user=device["name_by_user"],
|
||||
name=device["name"],
|
||||
primary_config_entry=device["primary_config_entry"],
|
||||
|
@ -1181,8 +1209,10 @@ class DeviceRegistry(BaseRegistry[dict[str, list[dict[str, Any]]]]):
|
|||
deleted_devices[device["id"]] = DeletedDeviceEntry(
|
||||
config_entries=set(device["config_entries"]),
|
||||
connections={tuple(conn) for conn in device["connections"]},
|
||||
created_at=datetime.fromisoformat(device["created_at"]),
|
||||
identifiers={tuple(iden) for iden in device["identifiers"]},
|
||||
id=device["id"],
|
||||
modified_at=datetime.fromisoformat(device["modified_at"]),
|
||||
orphaned_timestamp=device["orphaned_timestamp"],
|
||||
)
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
"""Test device_registry API."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from pytest_unordered import unordered
|
||||
|
||||
|
@ -7,6 +10,7 @@ from homeassistant.components.config import device_registry
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from tests.common import MockConfigEntry, MockModule, mock_integration
|
||||
from tests.typing import MockHAClientWebSocket, WebSocketGenerator
|
||||
|
@ -26,6 +30,7 @@ async def client_fixture(
|
|||
return await hass_ws_client(hass)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("freezer")
|
||||
async def test_list_devices(
|
||||
hass: HomeAssistant,
|
||||
client: MockHAClientWebSocket,
|
||||
|
@ -61,6 +66,7 @@ async def test_list_devices(
|
|||
"config_entries": [entry.entry_id],
|
||||
"configuration_url": None,
|
||||
"connections": [["ethernet", "12:34:56:78:90:AB:CD:EF"]],
|
||||
"created_at": utcnow().timestamp(),
|
||||
"disabled_by": None,
|
||||
"entry_type": None,
|
||||
"hw_version": None,
|
||||
|
@ -69,6 +75,7 @@ async def test_list_devices(
|
|||
"manufacturer": "manufacturer",
|
||||
"model": "model",
|
||||
"model_id": None,
|
||||
"modified_at": utcnow().timestamp(),
|
||||
"name_by_user": None,
|
||||
"name": None,
|
||||
"primary_config_entry": entry.entry_id,
|
||||
|
@ -81,6 +88,7 @@ async def test_list_devices(
|
|||
"config_entries": [entry.entry_id],
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": utcnow().timestamp(),
|
||||
"disabled_by": None,
|
||||
"entry_type": dr.DeviceEntryType.SERVICE,
|
||||
"hw_version": None,
|
||||
|
@ -89,6 +97,7 @@ async def test_list_devices(
|
|||
"manufacturer": "manufacturer",
|
||||
"model": "model",
|
||||
"model_id": None,
|
||||
"modified_at": utcnow().timestamp(),
|
||||
"name_by_user": None,
|
||||
"name": None,
|
||||
"primary_config_entry": entry.entry_id,
|
||||
|
@ -113,6 +122,7 @@ async def test_list_devices(
|
|||
"config_entries": [entry.entry_id],
|
||||
"configuration_url": None,
|
||||
"connections": [["ethernet", "12:34:56:78:90:AB:CD:EF"]],
|
||||
"created_at": utcnow().timestamp(),
|
||||
"disabled_by": None,
|
||||
"entry_type": None,
|
||||
"hw_version": None,
|
||||
|
@ -122,6 +132,7 @@ async def test_list_devices(
|
|||
"manufacturer": "manufacturer",
|
||||
"model": "model",
|
||||
"model_id": None,
|
||||
"modified_at": utcnow().timestamp(),
|
||||
"name_by_user": None,
|
||||
"name": None,
|
||||
"primary_config_entry": entry.entry_id,
|
||||
|
@ -151,12 +162,15 @@ async def test_update_device(
|
|||
hass: HomeAssistant,
|
||||
client: MockHAClientWebSocket,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
payload_key: str,
|
||||
payload_value: str | dr.DeviceEntryDisabler | None,
|
||||
) -> None:
|
||||
"""Test update entry."""
|
||||
entry = MockConfigEntry(title=None)
|
||||
entry.add_to_hass(hass)
|
||||
created_at = datetime.fromisoformat("2024-07-16T13:30:00.900075+00:00")
|
||||
freezer.move_to(created_at)
|
||||
device = device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
connections={("ethernet", "12:34:56:78:90:AB:CD:EF")},
|
||||
|
@ -167,6 +181,9 @@ async def test_update_device(
|
|||
|
||||
assert not getattr(device, payload_key)
|
||||
|
||||
modified_at = datetime.fromisoformat("2024-07-16T13:45:00.900075+00:00")
|
||||
freezer.move_to(modified_at)
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
"type": "config/device_registry/update",
|
||||
|
@ -186,6 +203,12 @@ async def test_update_device(
|
|||
|
||||
assert msg["result"][payload_key] == payload_value
|
||||
assert getattr(device, payload_key) == payload_value
|
||||
for key, value in (
|
||||
("created_at", created_at),
|
||||
("modified_at", modified_at if payload_value is not None else created_at),
|
||||
):
|
||||
assert msg["result"][key] == value.timestamp()
|
||||
assert getattr(device, key) == value
|
||||
|
||||
assert isinstance(device.disabled_by, (dr.DeviceEntryDisabler, type(None)))
|
||||
|
||||
|
@ -194,10 +217,13 @@ async def test_update_device_labels(
|
|||
hass: HomeAssistant,
|
||||
client: MockHAClientWebSocket,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test update entry labels."""
|
||||
entry = MockConfigEntry(title=None)
|
||||
entry.add_to_hass(hass)
|
||||
created_at = datetime.fromisoformat("2024-07-16T13:30:00.900075+00:00")
|
||||
freezer.move_to(created_at)
|
||||
device = device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
connections={("ethernet", "12:34:56:78:90:AB:CD:EF")},
|
||||
|
@ -207,6 +233,8 @@ async def test_update_device_labels(
|
|||
)
|
||||
|
||||
assert not device.labels
|
||||
modified_at = datetime.fromisoformat("2024-07-16T13:45:00.900075+00:00")
|
||||
freezer.move_to(modified_at)
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
|
@ -227,6 +255,12 @@ async def test_update_device_labels(
|
|||
|
||||
assert msg["result"]["labels"] == unordered(["label1", "label2"])
|
||||
assert device.labels == {"label1", "label2"}
|
||||
for key, value in (
|
||||
("created_at", created_at),
|
||||
("modified_at", modified_at),
|
||||
):
|
||||
assert msg["result"][key] == value.timestamp()
|
||||
assert getattr(device, key) == value
|
||||
|
||||
|
||||
async def test_remove_config_entry_from_device(
|
||||
|
|
|
@ -26,6 +26,8 @@ TO_EXCLUDE = {
|
|||
"last_updated",
|
||||
"last_changed",
|
||||
"last_reported",
|
||||
"created_at",
|
||||
"modified_at",
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -293,6 +293,8 @@ async def test_snapshots(
|
|||
device_dict = asdict(device)
|
||||
device_dict.pop("id", None)
|
||||
device_dict.pop("via_device_id", None)
|
||||
device_dict.pop("created_at", None)
|
||||
device_dict.pop("modified_at", None)
|
||||
devices.append({"device": device_dict, "entities": entities})
|
||||
|
||||
assert snapshot == devices
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
from collections.abc import Iterable
|
||||
from contextlib import AbstractContextManager, nullcontext
|
||||
from datetime import datetime
|
||||
from functools import partial
|
||||
import time
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from yarl import URL
|
||||
|
||||
|
@ -19,6 +21,7 @@ from homeassistant.helpers import (
|
|||
device_registry as dr,
|
||||
entity_registry as er,
|
||||
)
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
|
@ -177,12 +180,15 @@ async def test_multiple_config_entries(
|
|||
|
||||
|
||||
@pytest.mark.parametrize("load_registries", [False])
|
||||
@pytest.mark.usefixtures("freezer")
|
||||
async def test_loading_from_storage(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test loading stored devices on start."""
|
||||
created_at = "2024-01-01T00:00:00+00:00"
|
||||
modified_at = "2024-02-01T00:00:00+00:00"
|
||||
hass_storage[dr.STORAGE_KEY] = {
|
||||
"version": dr.STORAGE_VERSION_MAJOR,
|
||||
"minor_version": dr.STORAGE_VERSION_MINOR,
|
||||
|
@ -193,6 +199,7 @@ async def test_loading_from_storage(
|
|||
"config_entries": [mock_config_entry.entry_id],
|
||||
"configuration_url": "https://example.com/config",
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": created_at,
|
||||
"disabled_by": dr.DeviceEntryDisabler.USER,
|
||||
"entry_type": dr.DeviceEntryType.SERVICE,
|
||||
"hw_version": "hw_version",
|
||||
|
@ -202,6 +209,7 @@ async def test_loading_from_storage(
|
|||
"manufacturer": "manufacturer",
|
||||
"model": "model",
|
||||
"model_id": "model_id",
|
||||
"modified_at": modified_at,
|
||||
"name_by_user": "Test Friendly Name",
|
||||
"name": "name",
|
||||
"primary_config_entry": mock_config_entry.entry_id,
|
||||
|
@ -214,8 +222,10 @@ async def test_loading_from_storage(
|
|||
{
|
||||
"config_entries": [mock_config_entry.entry_id],
|
||||
"connections": [["Zigbee", "23.45.67.89.01"]],
|
||||
"created_at": created_at,
|
||||
"id": "bcdefghijklmn",
|
||||
"identifiers": [["serial", "3456ABCDEF12"]],
|
||||
"modified_at": modified_at,
|
||||
"orphaned_timestamp": None,
|
||||
}
|
||||
],
|
||||
|
@ -227,6 +237,16 @@ async def test_loading_from_storage(
|
|||
assert len(registry.devices) == 1
|
||||
assert len(registry.deleted_devices) == 1
|
||||
|
||||
assert registry.deleted_devices["bcdefghijklmn"] == dr.DeletedDeviceEntry(
|
||||
config_entries={mock_config_entry.entry_id},
|
||||
connections={("Zigbee", "23.45.67.89.01")},
|
||||
created_at=datetime.fromisoformat(created_at),
|
||||
id="bcdefghijklmn",
|
||||
identifiers={("serial", "3456ABCDEF12")},
|
||||
modified_at=datetime.fromisoformat(modified_at),
|
||||
orphaned_timestamp=None,
|
||||
)
|
||||
|
||||
entry = registry.async_get_or_create(
|
||||
config_entry_id=mock_config_entry.entry_id,
|
||||
connections={("Zigbee", "01.23.45.67.89")},
|
||||
|
@ -239,6 +259,7 @@ async def test_loading_from_storage(
|
|||
config_entries={mock_config_entry.entry_id},
|
||||
configuration_url="https://example.com/config",
|
||||
connections={("Zigbee", "01.23.45.67.89")},
|
||||
created_at=datetime.fromisoformat(created_at),
|
||||
disabled_by=dr.DeviceEntryDisabler.USER,
|
||||
entry_type=dr.DeviceEntryType.SERVICE,
|
||||
hw_version="hw_version",
|
||||
|
@ -248,6 +269,7 @@ async def test_loading_from_storage(
|
|||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
model_id="model_id",
|
||||
modified_at=datetime.fromisoformat(modified_at),
|
||||
name_by_user="Test Friendly Name",
|
||||
name="name",
|
||||
primary_config_entry=mock_config_entry.entry_id,
|
||||
|
@ -270,10 +292,12 @@ async def test_loading_from_storage(
|
|||
assert entry == dr.DeviceEntry(
|
||||
config_entries={mock_config_entry.entry_id},
|
||||
connections={("Zigbee", "23.45.67.89.01")},
|
||||
created_at=datetime.fromisoformat(created_at),
|
||||
id="bcdefghijklmn",
|
||||
identifiers={("serial", "3456ABCDEF12")},
|
||||
manufacturer="manufacturer",
|
||||
model="model",
|
||||
modified_at=utcnow(),
|
||||
primary_config_entry=mock_config_entry.entry_id,
|
||||
)
|
||||
assert entry.id == "bcdefghijklmn"
|
||||
|
@ -283,6 +307,7 @@ async def test_loading_from_storage(
|
|||
|
||||
|
||||
@pytest.mark.parametrize("load_registries", [False])
|
||||
@pytest.mark.usefixtures("freezer")
|
||||
async def test_migration_1_1_to_1_7(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
|
@ -367,6 +392,7 @@ async def test_migration_1_1_to_1_7(
|
|||
"config_entries": [mock_config_entry.entry_id],
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": "service",
|
||||
"hw_version": None,
|
||||
|
@ -376,6 +402,7 @@ async def test_migration_1_1_to_1_7(
|
|||
"manufacturer": "manufacturer",
|
||||
"model": "model",
|
||||
"model_id": None,
|
||||
"modified_at": utcnow().isoformat(),
|
||||
"name": "name",
|
||||
"name_by_user": None,
|
||||
"primary_config_entry": mock_config_entry.entry_id,
|
||||
|
@ -388,6 +415,7 @@ async def test_migration_1_1_to_1_7(
|
|||
"config_entries": [None],
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": None,
|
||||
"hw_version": None,
|
||||
|
@ -397,6 +425,7 @@ async def test_migration_1_1_to_1_7(
|
|||
"manufacturer": None,
|
||||
"model": None,
|
||||
"model_id": None,
|
||||
"modified_at": "1970-01-01T00:00:00+00:00",
|
||||
"name_by_user": None,
|
||||
"name": None,
|
||||
"primary_config_entry": None,
|
||||
|
@ -409,8 +438,10 @@ async def test_migration_1_1_to_1_7(
|
|||
{
|
||||
"config_entries": ["123456"],
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"id": "deletedid",
|
||||
"identifiers": [["serial", "123456ABCDFF"]],
|
||||
"modified_at": "1970-01-01T00:00:00+00:00",
|
||||
"orphaned_timestamp": None,
|
||||
}
|
||||
],
|
||||
|
@ -419,6 +450,7 @@ async def test_migration_1_1_to_1_7(
|
|||
|
||||
|
||||
@pytest.mark.parametrize("load_registries", [False])
|
||||
@pytest.mark.usefixtures("freezer")
|
||||
async def test_migration_1_2_to_1_7(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
|
@ -442,6 +474,7 @@ async def test_migration_1_2_to_1_7(
|
|||
"identifiers": [["serial", "123456ABCDEF"]],
|
||||
"manufacturer": "manufacturer",
|
||||
"model": "model",
|
||||
"modified_at": utcnow().isoformat(),
|
||||
"name": "name",
|
||||
"name_by_user": None,
|
||||
"sw_version": "version",
|
||||
|
@ -458,6 +491,7 @@ async def test_migration_1_2_to_1_7(
|
|||
"identifiers": [["serial", "mock-id-invalid-entry"]],
|
||||
"manufacturer": None,
|
||||
"model": None,
|
||||
"modified_at": "1970-01-01T00:00:00+00:00",
|
||||
"name_by_user": None,
|
||||
"name": None,
|
||||
"sw_version": None,
|
||||
|
@ -502,6 +536,7 @@ async def test_migration_1_2_to_1_7(
|
|||
"config_entries": [mock_config_entry.entry_id],
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": "service",
|
||||
"hw_version": None,
|
||||
|
@ -511,6 +546,7 @@ async def test_migration_1_2_to_1_7(
|
|||
"manufacturer": "manufacturer",
|
||||
"model": "model",
|
||||
"model_id": None,
|
||||
"modified_at": utcnow().isoformat(),
|
||||
"name": "name",
|
||||
"name_by_user": None,
|
||||
"primary_config_entry": mock_config_entry.entry_id,
|
||||
|
@ -523,6 +559,7 @@ async def test_migration_1_2_to_1_7(
|
|||
"config_entries": [None],
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": None,
|
||||
"hw_version": None,
|
||||
|
@ -532,6 +569,7 @@ async def test_migration_1_2_to_1_7(
|
|||
"manufacturer": None,
|
||||
"model": None,
|
||||
"model_id": None,
|
||||
"modified_at": "1970-01-01T00:00:00+00:00",
|
||||
"name_by_user": None,
|
||||
"name": None,
|
||||
"primary_config_entry": None,
|
||||
|
@ -546,6 +584,7 @@ async def test_migration_1_2_to_1_7(
|
|||
|
||||
|
||||
@pytest.mark.parametrize("load_registries", [False])
|
||||
@pytest.mark.usefixtures("freezer")
|
||||
async def test_migration_1_3_to_1_7(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
|
@ -631,6 +670,7 @@ async def test_migration_1_3_to_1_7(
|
|||
"config_entries": [mock_config_entry.entry_id],
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": "service",
|
||||
"hw_version": "hw_version",
|
||||
|
@ -640,6 +680,7 @@ async def test_migration_1_3_to_1_7(
|
|||
"manufacturer": "manufacturer",
|
||||
"model": "model",
|
||||
"model_id": None,
|
||||
"modified_at": utcnow().isoformat(),
|
||||
"name": "name",
|
||||
"name_by_user": None,
|
||||
"primary_config_entry": mock_config_entry.entry_id,
|
||||
|
@ -652,6 +693,7 @@ async def test_migration_1_3_to_1_7(
|
|||
"config_entries": [None],
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": None,
|
||||
"hw_version": None,
|
||||
|
@ -661,6 +703,7 @@ async def test_migration_1_3_to_1_7(
|
|||
"manufacturer": None,
|
||||
"model": None,
|
||||
"model_id": None,
|
||||
"modified_at": "1970-01-01T00:00:00+00:00",
|
||||
"name": None,
|
||||
"name_by_user": None,
|
||||
"primary_config_entry": None,
|
||||
|
@ -675,6 +718,7 @@ async def test_migration_1_3_to_1_7(
|
|||
|
||||
|
||||
@pytest.mark.parametrize("load_registries", [False])
|
||||
@pytest.mark.usefixtures("freezer")
|
||||
async def test_migration_1_4_to_1_7(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
|
@ -762,6 +806,7 @@ async def test_migration_1_4_to_1_7(
|
|||
"config_entries": [mock_config_entry.entry_id],
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": "service",
|
||||
"hw_version": "hw_version",
|
||||
|
@ -771,6 +816,7 @@ async def test_migration_1_4_to_1_7(
|
|||
"manufacturer": "manufacturer",
|
||||
"model": "model",
|
||||
"model_id": None,
|
||||
"modified_at": utcnow().isoformat(),
|
||||
"name": "name",
|
||||
"name_by_user": None,
|
||||
"primary_config_entry": mock_config_entry.entry_id,
|
||||
|
@ -783,6 +829,7 @@ async def test_migration_1_4_to_1_7(
|
|||
"config_entries": [None],
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": None,
|
||||
"hw_version": None,
|
||||
|
@ -792,6 +839,7 @@ async def test_migration_1_4_to_1_7(
|
|||
"manufacturer": None,
|
||||
"model": None,
|
||||
"model_id": None,
|
||||
"modified_at": "1970-01-01T00:00:00+00:00",
|
||||
"name_by_user": None,
|
||||
"name": None,
|
||||
"primary_config_entry": None,
|
||||
|
@ -806,6 +854,7 @@ async def test_migration_1_4_to_1_7(
|
|||
|
||||
|
||||
@pytest.mark.parametrize("load_registries", [False])
|
||||
@pytest.mark.usefixtures("freezer")
|
||||
async def test_migration_1_5_to_1_7(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
|
@ -895,6 +944,7 @@ async def test_migration_1_5_to_1_7(
|
|||
"config_entries": [mock_config_entry.entry_id],
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": "service",
|
||||
"hw_version": "hw_version",
|
||||
|
@ -905,6 +955,7 @@ async def test_migration_1_5_to_1_7(
|
|||
"model": "model",
|
||||
"name": "name",
|
||||
"model_id": None,
|
||||
"modified_at": utcnow().isoformat(),
|
||||
"name_by_user": None,
|
||||
"primary_config_entry": mock_config_entry.entry_id,
|
||||
"serial_number": None,
|
||||
|
@ -916,6 +967,7 @@ async def test_migration_1_5_to_1_7(
|
|||
"config_entries": [None],
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": None,
|
||||
"hw_version": None,
|
||||
|
@ -925,6 +977,7 @@ async def test_migration_1_5_to_1_7(
|
|||
"manufacturer": None,
|
||||
"model": None,
|
||||
"model_id": None,
|
||||
"modified_at": "1970-01-01T00:00:00+00:00",
|
||||
"name_by_user": None,
|
||||
"name": None,
|
||||
"primary_config_entry": None,
|
||||
|
@ -939,6 +992,7 @@ async def test_migration_1_5_to_1_7(
|
|||
|
||||
|
||||
@pytest.mark.parametrize("load_registries", [False])
|
||||
@pytest.mark.usefixtures("freezer")
|
||||
async def test_migration_1_6_to_1_7(
|
||||
hass: HomeAssistant,
|
||||
hass_storage: dict[str, Any],
|
||||
|
@ -1030,6 +1084,7 @@ async def test_migration_1_6_to_1_7(
|
|||
"config_entries": [mock_config_entry.entry_id],
|
||||
"configuration_url": None,
|
||||
"connections": [["Zigbee", "01.23.45.67.89"]],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": "service",
|
||||
"hw_version": "hw_version",
|
||||
|
@ -1040,6 +1095,7 @@ async def test_migration_1_6_to_1_7(
|
|||
"model": "model",
|
||||
"name": "name",
|
||||
"model_id": None,
|
||||
"modified_at": "1970-01-01T00:00:00+00:00",
|
||||
"name_by_user": None,
|
||||
"primary_config_entry": mock_config_entry.entry_id,
|
||||
"serial_number": None,
|
||||
|
@ -1051,6 +1107,7 @@ async def test_migration_1_6_to_1_7(
|
|||
"config_entries": [None],
|
||||
"configuration_url": None,
|
||||
"connections": [],
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": None,
|
||||
"hw_version": None,
|
||||
|
@ -1060,6 +1117,7 @@ async def test_migration_1_6_to_1_7(
|
|||
"manufacturer": None,
|
||||
"model": None,
|
||||
"model_id": None,
|
||||
"modified_at": "1970-01-01T00:00:00+00:00",
|
||||
"name_by_user": None,
|
||||
"name": None,
|
||||
"primary_config_entry": None,
|
||||
|
@ -1546,8 +1604,11 @@ async def test_update(
|
|||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Verify that we can update some attributes of a device."""
|
||||
created_at = datetime.fromisoformat("2024-01-01T01:00:00+00:00")
|
||||
freezer.move_to(created_at)
|
||||
update_events = async_capture_events(hass, dr.EVENT_DEVICE_REGISTRY_UPDATED)
|
||||
entry = device_registry.async_get_or_create(
|
||||
config_entry_id=mock_config_entry.entry_id,
|
||||
|
@ -1559,7 +1620,11 @@ async def test_update(
|
|||
assert not entry.area_id
|
||||
assert not entry.labels
|
||||
assert not entry.name_by_user
|
||||
assert entry.created_at == created_at
|
||||
assert entry.modified_at == created_at
|
||||
|
||||
modified_at = datetime.fromisoformat("2024-02-01T01:00:00+00:00")
|
||||
freezer.move_to(modified_at)
|
||||
with patch.object(device_registry, "async_schedule_save") as mock_save:
|
||||
updated_entry = device_registry.async_update_device(
|
||||
entry.id,
|
||||
|
@ -1589,6 +1654,7 @@ async def test_update(
|
|||
config_entries={mock_config_entry.entry_id},
|
||||
configuration_url="https://example.com/config",
|
||||
connections={("mac", "65:43:21:fe:dc:ba")},
|
||||
created_at=created_at,
|
||||
disabled_by=dr.DeviceEntryDisabler.USER,
|
||||
entry_type=dr.DeviceEntryType.SERVICE,
|
||||
hw_version="hw_version",
|
||||
|
@ -1598,6 +1664,7 @@ async def test_update(
|
|||
manufacturer="Test Producer",
|
||||
model="Test Model",
|
||||
model_id="Test Model Name",
|
||||
modified_at=modified_at,
|
||||
name_by_user="Test Friendly Name",
|
||||
name="name",
|
||||
serial_number="serial_no",
|
||||
|
@ -2616,6 +2683,7 @@ async def test_loading_invalid_configuration_url_from_storage(
|
|||
"config_entries": ["1234"],
|
||||
"configuration_url": "invalid",
|
||||
"connections": [],
|
||||
"created_at": "2024-01-01T00:00:00+00:00",
|
||||
"disabled_by": None,
|
||||
"entry_type": dr.DeviceEntryType.SERVICE,
|
||||
"hw_version": None,
|
||||
|
@ -2625,6 +2693,7 @@ async def test_loading_invalid_configuration_url_from_storage(
|
|||
"manufacturer": None,
|
||||
"model": None,
|
||||
"model_id": None,
|
||||
"modified_at": "2024-02-01T00:00:00+00:00",
|
||||
"name_by_user": None,
|
||||
"name": None,
|
||||
"primary_config_entry": "1234",
|
||||
|
|
|
@ -155,7 +155,16 @@ class HomeAssistantSnapshotSerializer(AmberDataSerializer):
|
|||
serialized["via_device_id"] = ANY
|
||||
if serialized["primary_config_entry"] is not None:
|
||||
serialized["primary_config_entry"] = ANY
|
||||
return serialized
|
||||
return cls._remove_created_and_modified_at(serialized)
|
||||
|
||||
@classmethod
|
||||
def _remove_created_and_modified_at(
|
||||
cls, data: SerializableData
|
||||
) -> SerializableData:
|
||||
"""Remove created_at and modified_at from the data."""
|
||||
data.pop("created_at", None)
|
||||
data.pop("modified_at", None)
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def _serializable_entity_registry_entry(
|
||||
|
|
Loading…
Add table
Reference in a new issue