diff --git a/homeassistant/components/recorder/models/state.py b/homeassistant/components/recorder/models/state.py index 73e7798b9f5..5f469638ec0 100644 --- a/homeassistant/components/recorder/models/state.py +++ b/homeassistant/components/recorder/models/state.py @@ -3,7 +3,7 @@ from __future__ import annotations from datetime import datetime import logging -from typing import Any +from typing import TYPE_CHECKING, Any from sqlalchemy.engine.row import Row @@ -17,10 +17,16 @@ from homeassistant.core import Context, State import homeassistant.util.dt as dt_util from .state_attributes import decode_attributes_from_source -from .time import process_timestamp + +if TYPE_CHECKING: + from functools import cached_property +else: + from homeassistant.backports.functools import cached_property _LOGGER = logging.getLogger(__name__) +EMPTY_CONTEXT = Context(id=None) + def extract_metadata_ids( entity_id_to_metadata_id: dict[str, int | None], @@ -36,15 +42,6 @@ def extract_metadata_ids( class LazyState(State): """A lazy version of core State after schema 31.""" - __slots__ = [ - "_row", - "_attributes", - "_last_changed_ts", - "_last_updated_ts", - "_context", - "attr_cache", - ] - def __init__( # pylint: disable=super-init-not-called self, row: Row, @@ -61,61 +58,35 @@ class LazyState(State): self.state = state or "" self._attributes: dict[str, Any] | None = None self._last_updated_ts: float | None = last_updated_ts or start_time_ts - self._last_changed_ts: float | None = None - self._context: Context | None = None self.attr_cache = attr_cache + self.context = EMPTY_CONTEXT - @property # type: ignore[override] + @cached_property # type: ignore[override] def attributes(self) -> dict[str, Any]: """State attributes.""" - if self._attributes is None: - self._attributes = decode_attributes_from_source( - getattr(self._row, "attributes", None), self.attr_cache - ) - return self._attributes + return decode_attributes_from_source( + getattr(self._row, "attributes", None), self.attr_cache + ) - @attributes.setter - def attributes(self, value: dict[str, Any]) -> None: - """Set attributes.""" - self._attributes = value + @cached_property + def _last_changed_ts(self) -> float | None: + """Last changed timestamp.""" + return getattr(self._row, "last_changed_ts", None) - @property - def context(self) -> Context: - """State context.""" - if self._context is None: - self._context = Context(id=None) - return self._context - - @context.setter - def context(self, value: Context) -> None: - """Set context.""" - self._context = value - - @property - def last_changed(self) -> datetime: + @cached_property + def last_changed(self) -> datetime: # type: ignore[override] """Last changed datetime.""" - if self._last_changed_ts is None: - self._last_changed_ts = ( - getattr(self._row, "last_changed_ts", None) or self._last_updated_ts - ) - return dt_util.utc_from_timestamp(self._last_changed_ts) + return dt_util.utc_from_timestamp( + self._last_changed_ts or self._last_updated_ts + ) - @last_changed.setter - def last_changed(self, value: datetime) -> None: - """Set last changed datetime.""" - self._last_changed_ts = process_timestamp(value).timestamp() - - @property - def last_updated(self) -> datetime: + @cached_property + def last_updated(self) -> datetime: # type: ignore[override] """Last updated datetime.""" - assert self._last_updated_ts is not None + if TYPE_CHECKING: + assert self._last_updated_ts is not None return dt_util.utc_from_timestamp(self._last_updated_ts) - @last_updated.setter - def last_updated(self, value: datetime) -> None: - """Set last updated datetime.""" - self._last_updated_ts = process_timestamp(value).timestamp() - def as_dict(self) -> dict[str, Any]: # type: ignore[override] """Return a dict representation of the LazyState. diff --git a/tests/components/recorder/test_models.py b/tests/components/recorder/test_models.py index f5ea8ff1656..8536481dd1f 100644 --- a/tests/components/recorder/test_models.py +++ b/tests/components/recorder/test_models.py @@ -352,22 +352,6 @@ async def test_lazy_state_handles_same_last_updated_and_last_changed( "last_updated": "2021-06-12T03:04:01.000323+00:00", "state": "off", } - lstate.last_updated = datetime(2020, 6, 12, 3, 4, 1, 323, tzinfo=dt_util.UTC) - assert lstate.as_dict() == { - "attributes": {"shared": True}, - "entity_id": "sensor.valid", - "last_changed": "2021-06-12T03:04:01.000323+00:00", - "last_updated": "2020-06-12T03:04:01.000323+00:00", - "state": "off", - } - lstate.last_changed = datetime(2020, 6, 12, 3, 4, 1, 323, tzinfo=dt_util.UTC) - assert lstate.as_dict() == { - "attributes": {"shared": True}, - "entity_id": "sensor.valid", - "last_changed": "2020-06-12T03:04:01.000323+00:00", - "last_updated": "2020-06-12T03:04:01.000323+00:00", - "state": "off", - } @pytest.mark.parametrize(