mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 04:37:06 +00:00
Add created_at/modified_at to entity registry (#122444)
This commit is contained in:
parent
8d14095cb9
commit
0d765a27c9
@ -48,6 +48,7 @@ from homeassistant.core import (
|
||||
from homeassistant.exceptions import MaxLengthExceeded
|
||||
from homeassistant.loader import async_suggest_report_issue
|
||||
from homeassistant.util import slugify, uuid as uuid_util
|
||||
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
|
||||
@ -74,7 +75,7 @@ EVENT_ENTITY_REGISTRY_UPDATED: EventType[EventEntityRegistryUpdatedData] = Event
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
STORAGE_VERSION_MAJOR = 1
|
||||
STORAGE_VERSION_MINOR = 14
|
||||
STORAGE_VERSION_MINOR = 15
|
||||
STORAGE_KEY = "core.entity_registry"
|
||||
|
||||
CLEANUP_INTERVAL = 3600 * 24
|
||||
@ -174,6 +175,7 @@ class RegistryEntry:
|
||||
categories: dict[str, str] = attr.ib(factory=dict)
|
||||
capabilities: Mapping[str, Any] | None = attr.ib(default=None)
|
||||
config_entry_id: str | None = attr.ib(default=None)
|
||||
created_at: datetime = attr.ib(factory=utcnow)
|
||||
device_class: str | None = attr.ib(default=None)
|
||||
device_id: str | None = attr.ib(default=None)
|
||||
domain: str = attr.ib(init=False, repr=False)
|
||||
@ -187,6 +189,7 @@ class RegistryEntry:
|
||||
)
|
||||
has_entity_name: bool = attr.ib(default=False)
|
||||
labels: set[str] = attr.ib(factory=set)
|
||||
modified_at: datetime = attr.ib(factory=utcnow)
|
||||
name: str | None = attr.ib(default=None)
|
||||
options: ReadOnlyEntityOptionsType = attr.ib(
|
||||
default=None, converter=_protect_entity_options
|
||||
@ -271,6 +274,7 @@ class RegistryEntry:
|
||||
"area_id": self.area_id,
|
||||
"categories": self.categories,
|
||||
"config_entry_id": self.config_entry_id,
|
||||
"created_at": self.created_at.timestamp(),
|
||||
"device_id": self.device_id,
|
||||
"disabled_by": self.disabled_by,
|
||||
"entity_category": self.entity_category,
|
||||
@ -280,6 +284,7 @@ class RegistryEntry:
|
||||
"icon": self.icon,
|
||||
"id": self.id,
|
||||
"labels": list(self.labels),
|
||||
"modified_at": self.modified_at.timestamp(),
|
||||
"name": self.name,
|
||||
"options": self.options,
|
||||
"original_name": self.original_name,
|
||||
@ -330,6 +335,7 @@ class RegistryEntry:
|
||||
"categories": self.categories,
|
||||
"capabilities": self.capabilities,
|
||||
"config_entry_id": self.config_entry_id,
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"device_class": self.device_class,
|
||||
"device_id": self.device_id,
|
||||
"disabled_by": self.disabled_by,
|
||||
@ -340,6 +346,7 @@ class RegistryEntry:
|
||||
"id": self.id,
|
||||
"has_entity_name": self.has_entity_name,
|
||||
"labels": list(self.labels),
|
||||
"modified_at": self.modified_at.isoformat(),
|
||||
"name": self.name,
|
||||
"options": self.options,
|
||||
"original_device_class": self.original_device_class,
|
||||
@ -395,6 +402,8 @@ class DeletedRegistryEntry:
|
||||
domain: str = attr.ib(init=False, repr=False)
|
||||
id: str = attr.ib()
|
||||
orphaned_timestamp: float | None = attr.ib()
|
||||
created_at: datetime = attr.ib(factory=utcnow)
|
||||
modified_at: datetime = attr.ib(factory=utcnow)
|
||||
|
||||
@domain.default
|
||||
def _domain_default(self) -> str:
|
||||
@ -408,8 +417,10 @@ class DeletedRegistryEntry:
|
||||
json_bytes(
|
||||
{
|
||||
"config_entry_id": self.config_entry_id,
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"entity_id": self.entity_id,
|
||||
"id": self.id,
|
||||
"modified_at": self.modified_at.isoformat(),
|
||||
"orphaned_timestamp": self.orphaned_timestamp,
|
||||
"platform": self.platform,
|
||||
"unique_id": self.unique_id,
|
||||
@ -429,88 +440,97 @@ class EntityRegistryStore(storage.Store[dict[str, list[dict[str, Any]]]]):
|
||||
) -> dict:
|
||||
"""Migrate to the new version."""
|
||||
data = old_data
|
||||
if old_major_version == 1 and old_minor_version < 2:
|
||||
# Version 1.2 implements migration and freezes the available keys
|
||||
for entity in data["entities"]:
|
||||
# Populate keys which were introduced before version 1.2
|
||||
entity.setdefault("area_id", None)
|
||||
entity.setdefault("capabilities", {})
|
||||
entity.setdefault("config_entry_id", None)
|
||||
entity.setdefault("device_class", None)
|
||||
entity.setdefault("device_id", None)
|
||||
entity.setdefault("disabled_by", None)
|
||||
entity.setdefault("entity_category", None)
|
||||
entity.setdefault("icon", None)
|
||||
entity.setdefault("name", None)
|
||||
entity.setdefault("original_icon", None)
|
||||
entity.setdefault("original_name", None)
|
||||
entity.setdefault("supported_features", 0)
|
||||
entity.setdefault("unit_of_measurement", None)
|
||||
if old_major_version == 1:
|
||||
if old_minor_version < 2:
|
||||
# Version 1.2 implements migration and freezes the available keys
|
||||
for entity in data["entities"]:
|
||||
# Populate keys which were introduced before version 1.2
|
||||
entity.setdefault("area_id", None)
|
||||
entity.setdefault("capabilities", {})
|
||||
entity.setdefault("config_entry_id", None)
|
||||
entity.setdefault("device_class", None)
|
||||
entity.setdefault("device_id", None)
|
||||
entity.setdefault("disabled_by", None)
|
||||
entity.setdefault("entity_category", None)
|
||||
entity.setdefault("icon", None)
|
||||
entity.setdefault("name", None)
|
||||
entity.setdefault("original_icon", None)
|
||||
entity.setdefault("original_name", None)
|
||||
entity.setdefault("supported_features", 0)
|
||||
entity.setdefault("unit_of_measurement", None)
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 3:
|
||||
# Version 1.3 adds original_device_class
|
||||
for entity in data["entities"]:
|
||||
# Move device_class to original_device_class
|
||||
entity["original_device_class"] = entity["device_class"]
|
||||
entity["device_class"] = None
|
||||
if old_minor_version < 3:
|
||||
# Version 1.3 adds original_device_class
|
||||
for entity in data["entities"]:
|
||||
# Move device_class to original_device_class
|
||||
entity["original_device_class"] = entity["device_class"]
|
||||
entity["device_class"] = None
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 4:
|
||||
# Version 1.4 adds id
|
||||
for entity in data["entities"]:
|
||||
entity["id"] = uuid_util.random_uuid_hex()
|
||||
if old_minor_version < 4:
|
||||
# Version 1.4 adds id
|
||||
for entity in data["entities"]:
|
||||
entity["id"] = uuid_util.random_uuid_hex()
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 5:
|
||||
# Version 1.5 adds entity options
|
||||
for entity in data["entities"]:
|
||||
entity["options"] = {}
|
||||
if old_minor_version < 5:
|
||||
# Version 1.5 adds entity options
|
||||
for entity in data["entities"]:
|
||||
entity["options"] = {}
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 6:
|
||||
# Version 1.6 adds hidden_by
|
||||
for entity in data["entities"]:
|
||||
entity["hidden_by"] = None
|
||||
if old_minor_version < 6:
|
||||
# Version 1.6 adds hidden_by
|
||||
for entity in data["entities"]:
|
||||
entity["hidden_by"] = None
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 7:
|
||||
# Version 1.7 adds has_entity_name
|
||||
for entity in data["entities"]:
|
||||
entity["has_entity_name"] = False
|
||||
if old_minor_version < 7:
|
||||
# Version 1.7 adds has_entity_name
|
||||
for entity in data["entities"]:
|
||||
entity["has_entity_name"] = False
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 8:
|
||||
# Cleanup after frontend bug which incorrectly updated device_class
|
||||
# Fixed by frontend PR #13551
|
||||
for entity in data["entities"]:
|
||||
domain = split_entity_id(entity["entity_id"])[0]
|
||||
if domain in [Platform.BINARY_SENSOR, Platform.COVER]:
|
||||
continue
|
||||
entity["device_class"] = None
|
||||
if old_minor_version < 8:
|
||||
# Cleanup after frontend bug which incorrectly updated device_class
|
||||
# Fixed by frontend PR #13551
|
||||
for entity in data["entities"]:
|
||||
domain = split_entity_id(entity["entity_id"])[0]
|
||||
if domain in [Platform.BINARY_SENSOR, Platform.COVER]:
|
||||
continue
|
||||
entity["device_class"] = None
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 9:
|
||||
# Version 1.9 adds translation_key
|
||||
for entity in data["entities"]:
|
||||
entity["translation_key"] = None
|
||||
if old_minor_version < 9:
|
||||
# Version 1.9 adds translation_key
|
||||
for entity in data["entities"]:
|
||||
entity["translation_key"] = None
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 10:
|
||||
# Version 1.10 adds aliases
|
||||
for entity in data["entities"]:
|
||||
entity["aliases"] = []
|
||||
if old_minor_version < 10:
|
||||
# Version 1.10 adds aliases
|
||||
for entity in data["entities"]:
|
||||
entity["aliases"] = []
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 11:
|
||||
# Version 1.11 adds deleted_entities
|
||||
data["deleted_entities"] = data.get("deleted_entities", [])
|
||||
if old_minor_version < 11:
|
||||
# Version 1.11 adds deleted_entities
|
||||
data["deleted_entities"] = data.get("deleted_entities", [])
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 12:
|
||||
# Version 1.12 adds previous_unique_id
|
||||
for entity in data["entities"]:
|
||||
entity["previous_unique_id"] = None
|
||||
if old_minor_version < 12:
|
||||
# Version 1.12 adds previous_unique_id
|
||||
for entity in data["entities"]:
|
||||
entity["previous_unique_id"] = None
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 13:
|
||||
# Version 1.13 adds labels
|
||||
for entity in data["entities"]:
|
||||
entity["labels"] = []
|
||||
if old_minor_version < 13:
|
||||
# Version 1.13 adds labels
|
||||
for entity in data["entities"]:
|
||||
entity["labels"] = []
|
||||
|
||||
if old_major_version == 1 and old_minor_version < 14:
|
||||
# Version 1.14 adds categories
|
||||
for entity in data["entities"]:
|
||||
entity["categories"] = {}
|
||||
if old_minor_version < 14:
|
||||
# Version 1.14 adds categories
|
||||
for entity in data["entities"]:
|
||||
entity["categories"] = {}
|
||||
|
||||
if old_minor_version < 15:
|
||||
# Version 1.15 adds created_at and modified_at
|
||||
created_at = utc_from_timestamp(0).isoformat()
|
||||
for entity in data["entities"]:
|
||||
entity["created_at"] = entity["modified_at"] = created_at
|
||||
for entity in data["deleted_entities"]:
|
||||
entity["created_at"] = entity["modified_at"] = created_at
|
||||
|
||||
if old_major_version > 1:
|
||||
raise NotImplementedError
|
||||
@ -837,10 +857,12 @@ class EntityRegistry(BaseRegistry):
|
||||
)
|
||||
|
||||
entity_registry_id: str | None = None
|
||||
created_at = utcnow()
|
||||
deleted_entity = self.deleted_entities.pop((domain, platform, unique_id), None)
|
||||
if deleted_entity is not None:
|
||||
# Restore id
|
||||
entity_registry_id = deleted_entity.id
|
||||
created_at = deleted_entity.created_at
|
||||
|
||||
entity_id = self.async_generate_entity_id(
|
||||
domain,
|
||||
@ -865,6 +887,7 @@ class EntityRegistry(BaseRegistry):
|
||||
entry = RegistryEntry(
|
||||
capabilities=none_if_undefined(capabilities),
|
||||
config_entry_id=none_if_undefined(config_entry_id),
|
||||
created_at=created_at,
|
||||
device_id=none_if_undefined(device_id),
|
||||
disabled_by=disabled_by,
|
||||
entity_category=none_if_undefined(entity_category),
|
||||
@ -906,6 +929,7 @@ class EntityRegistry(BaseRegistry):
|
||||
orphaned_timestamp = None if config_entry_id else time.time()
|
||||
self.deleted_entities[key] = DeletedRegistryEntry(
|
||||
config_entry_id=config_entry_id,
|
||||
created_at=entity.created_at,
|
||||
entity_id=entity_id,
|
||||
id=entity.id,
|
||||
orphaned_timestamp=orphaned_timestamp,
|
||||
@ -1093,6 +1117,8 @@ class EntityRegistry(BaseRegistry):
|
||||
if not new_values:
|
||||
return old
|
||||
|
||||
new_values["modified_at"] = utcnow()
|
||||
|
||||
self.hass.verify_event_loop_thread("entity_registry.async_update_entity")
|
||||
|
||||
new = self.entities[entity_id] = attr.evolve(old, **new_values)
|
||||
@ -1260,6 +1286,7 @@ class EntityRegistry(BaseRegistry):
|
||||
categories=entity["categories"],
|
||||
capabilities=entity["capabilities"],
|
||||
config_entry_id=entity["config_entry_id"],
|
||||
created_at=entity["created_at"],
|
||||
device_class=entity["device_class"],
|
||||
device_id=entity["device_id"],
|
||||
disabled_by=RegistryEntryDisabler(entity["disabled_by"])
|
||||
@ -1276,6 +1303,7 @@ class EntityRegistry(BaseRegistry):
|
||||
id=entity["id"],
|
||||
has_entity_name=entity["has_entity_name"],
|
||||
labels=set(entity["labels"]),
|
||||
modified_at=entity["modified_at"],
|
||||
name=entity["name"],
|
||||
options=entity["options"],
|
||||
original_device_class=entity["original_device_class"],
|
||||
@ -1307,8 +1335,10 @@ class EntityRegistry(BaseRegistry):
|
||||
)
|
||||
deleted_entities[key] = DeletedRegistryEntry(
|
||||
config_entry_id=entity["config_entry_id"],
|
||||
created_at=entity["created_at"],
|
||||
entity_id=entity["entity_id"],
|
||||
id=entity["id"],
|
||||
modified_at=entity["modified_at"],
|
||||
orphaned_timestamp=entity["orphaned_timestamp"],
|
||||
platform=entity["platform"],
|
||||
unique_id=entity["unique_id"],
|
||||
|
@ -1,5 +1,8 @@
|
||||
"""Test entity_registry API."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from pytest_unordered import unordered
|
||||
|
||||
@ -13,6 +16,7 @@ from homeassistant.helpers.entity_registry import (
|
||||
RegistryEntryDisabler,
|
||||
RegistryEntryHider,
|
||||
)
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from tests.common import (
|
||||
ANY,
|
||||
@ -33,6 +37,7 @@ async def client(
|
||||
return await hass_ws_client(hass)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("freezer")
|
||||
async def test_list_entities(
|
||||
hass: HomeAssistant, client: MockHAClientWebSocket
|
||||
) -> None:
|
||||
@ -62,6 +67,7 @@ async def test_list_entities(
|
||||
"area_id": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": utcnow().timestamp(),
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
"entity_category": None,
|
||||
@ -71,6 +77,7 @@ async def test_list_entities(
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": utcnow().timestamp(),
|
||||
"name": "Hello World",
|
||||
"options": {},
|
||||
"original_name": None,
|
||||
@ -82,6 +89,7 @@ async def test_list_entities(
|
||||
"area_id": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": utcnow().timestamp(),
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
"entity_category": None,
|
||||
@ -91,6 +99,7 @@ async def test_list_entities(
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": utcnow().timestamp(),
|
||||
"name": None,
|
||||
"options": {},
|
||||
"original_name": None,
|
||||
@ -129,6 +138,7 @@ async def test_list_entities(
|
||||
"area_id": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": utcnow().timestamp(),
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
"entity_category": None,
|
||||
@ -138,6 +148,7 @@ async def test_list_entities(
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": utcnow().timestamp(),
|
||||
"name": "Hello World",
|
||||
"options": {},
|
||||
"original_name": None,
|
||||
@ -325,6 +336,8 @@ async def test_list_entities_for_display(
|
||||
|
||||
async def test_get_entity(hass: HomeAssistant, client: MockHAClientWebSocket) -> None:
|
||||
"""Test get entry."""
|
||||
name_created_at = datetime(1994, 2, 14, 12, 0, 0)
|
||||
no_name_created_at = datetime(2024, 2, 14, 12, 0, 1)
|
||||
mock_registry(
|
||||
hass,
|
||||
{
|
||||
@ -333,11 +346,15 @@ async def test_get_entity(hass: HomeAssistant, client: MockHAClientWebSocket) ->
|
||||
unique_id="1234",
|
||||
platform="test_platform",
|
||||
name="Hello World",
|
||||
created_at=name_created_at,
|
||||
modified_at=name_created_at,
|
||||
),
|
||||
"test_domain.no_name": RegistryEntry(
|
||||
entity_id="test_domain.no_name",
|
||||
unique_id="6789",
|
||||
platform="test_platform",
|
||||
created_at=no_name_created_at,
|
||||
modified_at=no_name_created_at,
|
||||
),
|
||||
},
|
||||
)
|
||||
@ -353,6 +370,7 @@ async def test_get_entity(hass: HomeAssistant, client: MockHAClientWebSocket) ->
|
||||
"capabilities": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": name_created_at.timestamp(),
|
||||
"device_class": None,
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -363,6 +381,7 @@ async def test_get_entity(hass: HomeAssistant, client: MockHAClientWebSocket) ->
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": name_created_at.timestamp(),
|
||||
"name": "Hello World",
|
||||
"options": {},
|
||||
"original_device_class": None,
|
||||
@ -387,6 +406,7 @@ async def test_get_entity(hass: HomeAssistant, client: MockHAClientWebSocket) ->
|
||||
"capabilities": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": no_name_created_at.timestamp(),
|
||||
"device_class": None,
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -397,6 +417,7 @@ async def test_get_entity(hass: HomeAssistant, client: MockHAClientWebSocket) ->
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": no_name_created_at.timestamp(),
|
||||
"name": None,
|
||||
"options": {},
|
||||
"original_device_class": None,
|
||||
@ -410,6 +431,8 @@ async def test_get_entity(hass: HomeAssistant, client: MockHAClientWebSocket) ->
|
||||
|
||||
async def test_get_entities(hass: HomeAssistant, client: MockHAClientWebSocket) -> None:
|
||||
"""Test get entry."""
|
||||
name_created_at = datetime(1994, 2, 14, 12, 0, 0)
|
||||
no_name_created_at = datetime(2024, 2, 14, 12, 0, 1)
|
||||
mock_registry(
|
||||
hass,
|
||||
{
|
||||
@ -418,11 +441,15 @@ async def test_get_entities(hass: HomeAssistant, client: MockHAClientWebSocket)
|
||||
unique_id="1234",
|
||||
platform="test_platform",
|
||||
name="Hello World",
|
||||
created_at=name_created_at,
|
||||
modified_at=name_created_at,
|
||||
),
|
||||
"test_domain.no_name": RegistryEntry(
|
||||
entity_id="test_domain.no_name",
|
||||
unique_id="6789",
|
||||
platform="test_platform",
|
||||
created_at=no_name_created_at,
|
||||
modified_at=no_name_created_at,
|
||||
),
|
||||
},
|
||||
)
|
||||
@ -446,6 +473,7 @@ async def test_get_entities(hass: HomeAssistant, client: MockHAClientWebSocket)
|
||||
"capabilities": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": name_created_at.timestamp(),
|
||||
"device_class": None,
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -456,6 +484,7 @@ async def test_get_entities(hass: HomeAssistant, client: MockHAClientWebSocket)
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": name_created_at.timestamp(),
|
||||
"name": "Hello World",
|
||||
"options": {},
|
||||
"original_device_class": None,
|
||||
@ -471,6 +500,7 @@ async def test_get_entities(hass: HomeAssistant, client: MockHAClientWebSocket)
|
||||
"capabilities": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": no_name_created_at.timestamp(),
|
||||
"device_class": None,
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -481,6 +511,7 @@ async def test_get_entities(hass: HomeAssistant, client: MockHAClientWebSocket)
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": no_name_created_at.timestamp(),
|
||||
"name": None,
|
||||
"options": {},
|
||||
"original_device_class": None,
|
||||
@ -495,9 +526,11 @@ async def test_get_entities(hass: HomeAssistant, client: MockHAClientWebSocket)
|
||||
|
||||
|
||||
async def test_update_entity(
|
||||
hass: HomeAssistant, client: MockHAClientWebSocket
|
||||
hass: HomeAssistant, client: MockHAClientWebSocket, freezer: FrozenDateTimeFactory
|
||||
) -> None:
|
||||
"""Test updating entity."""
|
||||
created = datetime.fromisoformat("2024-02-14T12:00:00.900075+00:00")
|
||||
freezer.move_to(created)
|
||||
registry = mock_registry(
|
||||
hass,
|
||||
{
|
||||
@ -520,6 +553,9 @@ async def test_update_entity(
|
||||
assert state.name == "before update"
|
||||
assert state.attributes[ATTR_ICON] == "icon:before update"
|
||||
|
||||
modified = datetime.fromisoformat("2024-07-17T13:30:00.900075+00:00")
|
||||
freezer.move_to(modified)
|
||||
|
||||
# Update area, categories, device_class, hidden_by, icon, labels & name
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
@ -544,6 +580,7 @@ async def test_update_entity(
|
||||
"area_id": "mock-area-id",
|
||||
"capabilities": None,
|
||||
"categories": {"scope1": "id", "scope2": "id"},
|
||||
"created_at": created.timestamp(),
|
||||
"config_entry_id": None,
|
||||
"device_class": "custom_device_class",
|
||||
"device_id": None,
|
||||
@ -555,6 +592,7 @@ async def test_update_entity(
|
||||
"icon": "icon:after update",
|
||||
"id": ANY,
|
||||
"labels": unordered(["label1", "label2"]),
|
||||
"modified_at": modified.timestamp(),
|
||||
"name": "after update",
|
||||
"options": {},
|
||||
"original_device_class": None,
|
||||
@ -570,6 +608,9 @@ async def test_update_entity(
|
||||
assert state.name == "after update"
|
||||
assert state.attributes[ATTR_ICON] == "icon:after update"
|
||||
|
||||
modified = datetime.fromisoformat("2024-07-20T00:00:00.900075+00:00")
|
||||
freezer.move_to(modified)
|
||||
|
||||
# Update hidden_by to illegal value
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
@ -597,9 +638,13 @@ async def test_update_entity(
|
||||
assert msg["success"]
|
||||
|
||||
assert hass.states.get("test_domain.world") is None
|
||||
assert (
|
||||
registry.entities["test_domain.world"].disabled_by is RegistryEntryDisabler.USER
|
||||
)
|
||||
entry = registry.entities["test_domain.world"]
|
||||
assert entry.disabled_by is RegistryEntryDisabler.USER
|
||||
assert entry.created_at == created
|
||||
assert entry.modified_at == modified
|
||||
|
||||
modified = datetime.fromisoformat("2024-07-21T00:00:00.900075+00:00")
|
||||
freezer.move_to(modified)
|
||||
|
||||
# Update disabled_by to None
|
||||
await client.send_json_auto_id(
|
||||
@ -619,6 +664,7 @@ async def test_update_entity(
|
||||
"capabilities": None,
|
||||
"categories": {"scope1": "id", "scope2": "id"},
|
||||
"config_entry_id": None,
|
||||
"created_at": created.timestamp(),
|
||||
"device_class": "custom_device_class",
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -629,6 +675,7 @@ async def test_update_entity(
|
||||
"icon": "icon:after update",
|
||||
"id": ANY,
|
||||
"labels": unordered(["label1", "label2"]),
|
||||
"modified_at": modified.timestamp(),
|
||||
"name": "after update",
|
||||
"options": {},
|
||||
"original_device_class": None,
|
||||
@ -641,6 +688,9 @@ async def test_update_entity(
|
||||
"require_restart": True,
|
||||
}
|
||||
|
||||
modified = datetime.fromisoformat("2024-07-22T00:00:00.900075+00:00")
|
||||
freezer.move_to(modified)
|
||||
|
||||
# Update entity option
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
@ -660,6 +710,7 @@ async def test_update_entity(
|
||||
"capabilities": None,
|
||||
"categories": {"scope1": "id", "scope2": "id"},
|
||||
"config_entry_id": None,
|
||||
"created_at": created.timestamp(),
|
||||
"device_class": "custom_device_class",
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -670,6 +721,7 @@ async def test_update_entity(
|
||||
"icon": "icon:after update",
|
||||
"id": ANY,
|
||||
"labels": unordered(["label1", "label2"]),
|
||||
"modified_at": modified.timestamp(),
|
||||
"name": "after update",
|
||||
"options": {"sensor": {"unit_of_measurement": "beard_second"}},
|
||||
"original_device_class": None,
|
||||
@ -681,6 +733,9 @@ async def test_update_entity(
|
||||
},
|
||||
}
|
||||
|
||||
modified = datetime.fromisoformat("2024-07-23T00:00:00.900075+00:00")
|
||||
freezer.move_to(modified)
|
||||
|
||||
# Add a category to the entity
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
@ -700,6 +755,7 @@ async def test_update_entity(
|
||||
"capabilities": None,
|
||||
"categories": {"scope1": "id", "scope2": "id", "scope3": "id"},
|
||||
"config_entry_id": None,
|
||||
"created_at": created.timestamp(),
|
||||
"device_class": "custom_device_class",
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -710,6 +766,7 @@ async def test_update_entity(
|
||||
"icon": "icon:after update",
|
||||
"id": ANY,
|
||||
"labels": unordered(["label1", "label2"]),
|
||||
"modified_at": modified.timestamp(),
|
||||
"name": "after update",
|
||||
"options": {"sensor": {"unit_of_measurement": "beard_second"}},
|
||||
"original_device_class": None,
|
||||
@ -721,6 +778,9 @@ async def test_update_entity(
|
||||
},
|
||||
}
|
||||
|
||||
modified = datetime.fromisoformat("2024-07-24T00:00:00.900075+00:00")
|
||||
freezer.move_to(modified)
|
||||
|
||||
# Move the entity to a different category
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
@ -740,6 +800,7 @@ async def test_update_entity(
|
||||
"capabilities": None,
|
||||
"categories": {"scope1": "id", "scope2": "id", "scope3": "other_id"},
|
||||
"config_entry_id": None,
|
||||
"created_at": created.timestamp(),
|
||||
"device_class": "custom_device_class",
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -750,6 +811,7 @@ async def test_update_entity(
|
||||
"icon": "icon:after update",
|
||||
"id": ANY,
|
||||
"labels": unordered(["label1", "label2"]),
|
||||
"modified_at": modified.timestamp(),
|
||||
"name": "after update",
|
||||
"options": {"sensor": {"unit_of_measurement": "beard_second"}},
|
||||
"original_device_class": None,
|
||||
@ -761,6 +823,9 @@ async def test_update_entity(
|
||||
},
|
||||
}
|
||||
|
||||
modified = datetime.fromisoformat("2024-07-23T10:00:00.900075+00:00")
|
||||
freezer.move_to(modified)
|
||||
|
||||
# Move the entity to a different category
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
@ -780,6 +845,7 @@ async def test_update_entity(
|
||||
"capabilities": None,
|
||||
"categories": {"scope1": "id", "scope3": "other_id"},
|
||||
"config_entry_id": None,
|
||||
"created_at": created.timestamp(),
|
||||
"device_class": "custom_device_class",
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -790,6 +856,7 @@ async def test_update_entity(
|
||||
"icon": "icon:after update",
|
||||
"id": ANY,
|
||||
"labels": unordered(["label1", "label2"]),
|
||||
"modified_at": modified.timestamp(),
|
||||
"name": "after update",
|
||||
"options": {"sensor": {"unit_of_measurement": "beard_second"}},
|
||||
"original_device_class": None,
|
||||
@ -803,9 +870,11 @@ async def test_update_entity(
|
||||
|
||||
|
||||
async def test_update_entity_require_restart(
|
||||
hass: HomeAssistant, client: MockHAClientWebSocket
|
||||
hass: HomeAssistant, client: MockHAClientWebSocket, freezer: FrozenDateTimeFactory
|
||||
) -> None:
|
||||
"""Test updating entity."""
|
||||
created = datetime.fromisoformat("2024-02-14T12:00:00+00:00")
|
||||
freezer.move_to(created)
|
||||
entity_id = "test_domain.test_platform_1234"
|
||||
config_entry = MockConfigEntry(domain="test_platform")
|
||||
config_entry.add_to_hass(hass)
|
||||
@ -817,6 +886,9 @@ async def test_update_entity_require_restart(
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
|
||||
modified = datetime.fromisoformat("2024-07-20T13:30:00+00:00")
|
||||
freezer.move_to(modified)
|
||||
|
||||
# UPDATE DISABLED_BY TO NONE
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
@ -835,6 +907,7 @@ async def test_update_entity_require_restart(
|
||||
"capabilities": None,
|
||||
"categories": {},
|
||||
"config_entry_id": config_entry.entry_id,
|
||||
"created_at": created.timestamp(),
|
||||
"device_class": None,
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -845,6 +918,7 @@ async def test_update_entity_require_restart(
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": created.timestamp(),
|
||||
"name": None,
|
||||
"options": {},
|
||||
"original_device_class": None,
|
||||
@ -909,9 +983,11 @@ async def test_enable_entity_disabled_device(
|
||||
|
||||
|
||||
async def test_update_entity_no_changes(
|
||||
hass: HomeAssistant, client: MockHAClientWebSocket
|
||||
hass: HomeAssistant, client: MockHAClientWebSocket, freezer: FrozenDateTimeFactory
|
||||
) -> None:
|
||||
"""Test update entity with no changes."""
|
||||
created = datetime.fromisoformat("2024-02-14T12:00:00.900075+00:00")
|
||||
freezer.move_to(created)
|
||||
mock_registry(
|
||||
hass,
|
||||
{
|
||||
@ -932,6 +1008,9 @@ async def test_update_entity_no_changes(
|
||||
assert state is not None
|
||||
assert state.name == "name of entity"
|
||||
|
||||
modified = datetime.fromisoformat("2024-07-20T13:30:00.900075+00:00")
|
||||
freezer.move_to(modified)
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
"type": "config/entity_registry/update",
|
||||
@ -949,6 +1028,7 @@ async def test_update_entity_no_changes(
|
||||
"capabilities": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": created.timestamp(),
|
||||
"device_class": None,
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -959,6 +1039,7 @@ async def test_update_entity_no_changes(
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": created.timestamp(),
|
||||
"name": "name of entity",
|
||||
"options": {},
|
||||
"original_device_class": None,
|
||||
@ -1002,9 +1083,11 @@ async def test_update_nonexisting_entity(client: MockHAClientWebSocket) -> None:
|
||||
|
||||
|
||||
async def test_update_entity_id(
|
||||
hass: HomeAssistant, client: MockHAClientWebSocket
|
||||
hass: HomeAssistant, client: MockHAClientWebSocket, freezer: FrozenDateTimeFactory
|
||||
) -> None:
|
||||
"""Test update entity id."""
|
||||
created = datetime.fromisoformat("2024-02-14T12:00:00.900075+00:00")
|
||||
freezer.move_to(created)
|
||||
mock_registry(
|
||||
hass,
|
||||
{
|
||||
@ -1022,6 +1105,9 @@ async def test_update_entity_id(
|
||||
|
||||
assert hass.states.get("test_domain.world") is not None
|
||||
|
||||
modified = datetime.fromisoformat("2024-07-20T13:30:00.900075+00:00")
|
||||
freezer.move_to(modified)
|
||||
|
||||
await client.send_json_auto_id(
|
||||
{
|
||||
"type": "config/entity_registry/update",
|
||||
@ -1039,6 +1125,7 @@ async def test_update_entity_id(
|
||||
"capabilities": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": created.timestamp(),
|
||||
"device_class": None,
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -1049,6 +1136,7 @@ async def test_update_entity_id(
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": modified.timestamp(),
|
||||
"name": None,
|
||||
"options": {},
|
||||
"original_device_class": None,
|
||||
|
@ -287,6 +287,8 @@ async def test_snapshots(
|
||||
entry = asdict(entity_entry)
|
||||
entry.pop("id", None)
|
||||
entry.pop("device_id", None)
|
||||
entry.pop("created_at", None)
|
||||
entry.pop("modified_at", None)
|
||||
|
||||
entities.append({"entry": entry, "state": state_dict})
|
||||
|
||||
|
@ -1422,6 +1422,7 @@ async def test_entity_hidden_by_integration(
|
||||
assert entry_hidden.hidden_by is er.RegistryEntryHider.INTEGRATION
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("freezer")
|
||||
async def test_entity_info_added_to_entity_registry(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
) -> None:
|
||||
@ -1450,11 +1451,13 @@ async def test_entity_info_added_to_entity_registry(
|
||||
"default",
|
||||
"test_domain",
|
||||
capabilities={"max": 100},
|
||||
created_at=dt_util.utcnow(),
|
||||
device_class=None,
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
has_entity_name=True,
|
||||
icon=None,
|
||||
id=ANY,
|
||||
modified_at=dt_util.utcnow(),
|
||||
name=None,
|
||||
original_device_class="mock-device-class",
|
||||
original_icon="nice:icon",
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""Tests for the Entity Registry."""
|
||||
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta
|
||||
from functools import partial
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
@ -21,6 +21,7 @@ from homeassistant.exceptions import MaxLengthExceeded
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
from tests.common import (
|
||||
ANY,
|
||||
MockConfigEntry,
|
||||
async_capture_events,
|
||||
async_fire_time_changed,
|
||||
@ -69,9 +70,14 @@ def test_get_or_create_suggested_object_id(entity_registry: er.EntityRegistry) -
|
||||
assert entry.entity_id == "light.beer"
|
||||
|
||||
|
||||
def test_get_or_create_updates_data(entity_registry: er.EntityRegistry) -> None:
|
||||
def test_get_or_create_updates_data(
|
||||
entity_registry: er.EntityRegistry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test that we update data in get_or_create."""
|
||||
orig_config_entry = MockConfigEntry(domain="light")
|
||||
created = datetime.fromisoformat("2024-02-14T12:00:00.0+00:00")
|
||||
freezer.move_to(created)
|
||||
|
||||
orig_entry = entity_registry.async_get_or_create(
|
||||
"light",
|
||||
@ -100,6 +106,7 @@ def test_get_or_create_updates_data(entity_registry: er.EntityRegistry) -> None:
|
||||
"hue",
|
||||
capabilities={"max": 100},
|
||||
config_entry_id=orig_config_entry.entry_id,
|
||||
created_at=created,
|
||||
device_class=None,
|
||||
device_id="mock-dev-id",
|
||||
disabled_by=er.RegistryEntryDisabler.HASS,
|
||||
@ -108,6 +115,7 @@ def test_get_or_create_updates_data(entity_registry: er.EntityRegistry) -> None:
|
||||
hidden_by=er.RegistryEntryHider.INTEGRATION,
|
||||
icon=None,
|
||||
id=orig_entry.id,
|
||||
modified_at=created,
|
||||
name=None,
|
||||
original_device_class="mock-device-class",
|
||||
original_icon="initial-original_icon",
|
||||
@ -118,6 +126,8 @@ def test_get_or_create_updates_data(entity_registry: er.EntityRegistry) -> None:
|
||||
)
|
||||
|
||||
new_config_entry = MockConfigEntry(domain="light")
|
||||
modified = created + timedelta(minutes=5)
|
||||
freezer.move_to(modified)
|
||||
|
||||
new_entry = entity_registry.async_get_or_create(
|
||||
"light",
|
||||
@ -146,6 +156,7 @@ def test_get_or_create_updates_data(entity_registry: er.EntityRegistry) -> None:
|
||||
area_id=None,
|
||||
capabilities={"new-max": 150},
|
||||
config_entry_id=new_config_entry.entry_id,
|
||||
created_at=created,
|
||||
device_class=None,
|
||||
device_id="new-mock-dev-id",
|
||||
disabled_by=er.RegistryEntryDisabler.HASS, # Should not be updated
|
||||
@ -154,6 +165,7 @@ def test_get_or_create_updates_data(entity_registry: er.EntityRegistry) -> None:
|
||||
hidden_by=er.RegistryEntryHider.INTEGRATION, # Should not be updated
|
||||
icon=None,
|
||||
id=orig_entry.id,
|
||||
modified_at=modified,
|
||||
name=None,
|
||||
original_device_class="new-mock-device-class",
|
||||
original_icon="updated-original_icon",
|
||||
@ -164,6 +176,8 @@ def test_get_or_create_updates_data(entity_registry: er.EntityRegistry) -> None:
|
||||
)
|
||||
|
||||
assert set(entity_registry.async_device_ids()) == {"new-mock-dev-id"}
|
||||
modified = created + timedelta(minutes=5)
|
||||
freezer.move_to(modified)
|
||||
|
||||
new_entry = entity_registry.async_get_or_create(
|
||||
"light",
|
||||
@ -192,6 +206,7 @@ def test_get_or_create_updates_data(entity_registry: er.EntityRegistry) -> None:
|
||||
area_id=None,
|
||||
capabilities=None,
|
||||
config_entry_id=None,
|
||||
created_at=created,
|
||||
device_class=None,
|
||||
device_id=None,
|
||||
disabled_by=er.RegistryEntryDisabler.HASS, # Should not be updated
|
||||
@ -200,6 +215,7 @@ def test_get_or_create_updates_data(entity_registry: er.EntityRegistry) -> None:
|
||||
hidden_by=er.RegistryEntryHider.INTEGRATION, # Should not be updated
|
||||
icon=None,
|
||||
id=orig_entry.id,
|
||||
modified_at=modified,
|
||||
name=None,
|
||||
original_device_class=None,
|
||||
original_icon=None,
|
||||
@ -309,8 +325,12 @@ async def test_loading_saving_data(
|
||||
|
||||
assert orig_entry1 == new_entry1
|
||||
assert orig_entry2 == new_entry2
|
||||
assert orig_entry3 == new_entry3
|
||||
assert orig_entry4 == new_entry4
|
||||
|
||||
# By converting a deleted device to a active device, the modified_at will be updated
|
||||
assert orig_entry3.modified_at < new_entry3.modified_at
|
||||
assert attr.evolve(orig_entry3, modified_at=new_entry3.modified_at) == new_entry3
|
||||
assert orig_entry4.modified_at < new_entry4.modified_at
|
||||
assert attr.evolve(orig_entry4, modified_at=new_entry4.modified_at) == new_entry4
|
||||
|
||||
assert new_entry2.area_id == "mock-area-id"
|
||||
assert new_entry2.categories == {"scope", "id"}
|
||||
@ -453,6 +473,7 @@ async def test_load_bad_data(
|
||||
"capabilities": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": "2024-02-14T12:00:00.900075+00:00",
|
||||
"device_class": None,
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -463,6 +484,7 @@ async def test_load_bad_data(
|
||||
"icon": None,
|
||||
"id": "00001",
|
||||
"labels": [],
|
||||
"modified_at": "2024-02-14T12:00:00.900075+00:00",
|
||||
"name": None,
|
||||
"options": None,
|
||||
"original_device_class": None,
|
||||
@ -481,6 +503,7 @@ async def test_load_bad_data(
|
||||
"capabilities": None,
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": "2024-02-14T12:00:00.900075+00:00",
|
||||
"device_class": None,
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
@ -491,6 +514,7 @@ async def test_load_bad_data(
|
||||
"icon": None,
|
||||
"id": "00002",
|
||||
"labels": [],
|
||||
"modified_at": "2024-02-14T12:00:00.900075+00:00",
|
||||
"name": None,
|
||||
"options": None,
|
||||
"original_device_class": None,
|
||||
@ -507,16 +531,20 @@ async def test_load_bad_data(
|
||||
"deleted_entities": [
|
||||
{
|
||||
"config_entry_id": None,
|
||||
"created_at": "2024-02-14T12:00:00.900075+00:00",
|
||||
"entity_id": "test.test3",
|
||||
"id": "00003",
|
||||
"modified_at": "2024-02-14T12:00:00.900075+00:00",
|
||||
"orphaned_timestamp": None,
|
||||
"platform": "super_platform",
|
||||
"unique_id": 234, # Should not load
|
||||
},
|
||||
{
|
||||
"config_entry_id": None,
|
||||
"created_at": "2024-02-14T12:00:00.900075+00:00",
|
||||
"entity_id": "test.test4",
|
||||
"id": "00004",
|
||||
"modified_at": "2024-02-14T12:00:00.900075+00:00",
|
||||
"orphaned_timestamp": None,
|
||||
"platform": "super_platform",
|
||||
"unique_id": ["also", "not", "valid"], # Should not load
|
||||
@ -695,6 +723,49 @@ async def test_migration_1_1(hass: HomeAssistant, hass_storage: dict[str, Any])
|
||||
assert entry.device_class is None
|
||||
assert entry.original_device_class == "best_class"
|
||||
|
||||
# Check we store migrated data
|
||||
await flush_store(registry._store)
|
||||
assert hass_storage[er.STORAGE_KEY] == {
|
||||
"version": er.STORAGE_VERSION_MAJOR,
|
||||
"minor_version": er.STORAGE_VERSION_MINOR,
|
||||
"key": er.STORAGE_KEY,
|
||||
"data": {
|
||||
"entities": [
|
||||
{
|
||||
"aliases": [],
|
||||
"area_id": None,
|
||||
"capabilities": {},
|
||||
"categories": {},
|
||||
"config_entry_id": None,
|
||||
"created_at": "1970-01-01T00:00:00+00:00",
|
||||
"device_id": None,
|
||||
"disabled_by": None,
|
||||
"entity_category": None,
|
||||
"entity_id": "test.entity",
|
||||
"has_entity_name": False,
|
||||
"hidden_by": None,
|
||||
"icon": None,
|
||||
"id": ANY,
|
||||
"labels": [],
|
||||
"modified_at": "1970-01-01T00:00:00+00:00",
|
||||
"name": None,
|
||||
"options": {},
|
||||
"original_device_class": "best_class",
|
||||
"original_icon": None,
|
||||
"original_name": None,
|
||||
"platform": "super_platform",
|
||||
"previous_unique_id": None,
|
||||
"supported_features": 0,
|
||||
"translation_key": None,
|
||||
"unique_id": "very_unique",
|
||||
"unit_of_measurement": None,
|
||||
"device_class": None,
|
||||
}
|
||||
],
|
||||
"deleted_entities": [],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("load_registries", [False])
|
||||
async def test_migration_1_7(hass: HomeAssistant, hass_storage: dict[str, Any]) -> None:
|
||||
|
@ -181,7 +181,7 @@ class HomeAssistantSnapshotSerializer(AmberDataSerializer):
|
||||
}
|
||||
)
|
||||
serialized.pop("categories")
|
||||
return serialized
|
||||
return cls._remove_created_and_modified_at(serialized)
|
||||
|
||||
@classmethod
|
||||
def _serializable_flow_result(cls, data: FlowResult) -> SerializableData:
|
||||
|
Loading…
x
Reference in New Issue
Block a user