mirror of
https://github.com/home-assistant/core.git
synced 2025-05-15 03:19:15 +00:00

* Reduce overhead to compile statistics statistics uses LazyState for compatibility with State when pulling data from the database. After the previous round of refactoring to modern history, the setters are never called and can be removed. * reduce
135 lines
4.3 KiB
Python
135 lines
4.3 KiB
Python
"""Models states in for Recorder."""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
import logging
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from sqlalchemy.engine.row import Row
|
|
|
|
from homeassistant.const import (
|
|
COMPRESSED_STATE_ATTRIBUTES,
|
|
COMPRESSED_STATE_LAST_CHANGED,
|
|
COMPRESSED_STATE_LAST_UPDATED,
|
|
COMPRESSED_STATE_STATE,
|
|
)
|
|
from homeassistant.core import Context, State
|
|
import homeassistant.util.dt as dt_util
|
|
|
|
from .state_attributes import decode_attributes_from_source
|
|
|
|
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],
|
|
) -> list[int]:
|
|
"""Extract metadata ids from entity_id_to_metadata_id."""
|
|
return [
|
|
metadata_id
|
|
for metadata_id in entity_id_to_metadata_id.values()
|
|
if metadata_id is not None
|
|
]
|
|
|
|
|
|
class LazyState(State):
|
|
"""A lazy version of core State after schema 31."""
|
|
|
|
def __init__( # pylint: disable=super-init-not-called
|
|
self,
|
|
row: Row,
|
|
attr_cache: dict[str, dict[str, Any]],
|
|
start_time_ts: float | None,
|
|
entity_id: str,
|
|
state: str,
|
|
last_updated_ts: float | None,
|
|
no_attributes: bool,
|
|
) -> None:
|
|
"""Init the lazy state."""
|
|
self._row = row
|
|
self.entity_id = entity_id
|
|
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.attr_cache = attr_cache
|
|
self.context = EMPTY_CONTEXT
|
|
|
|
@cached_property # type: ignore[override]
|
|
def attributes(self) -> dict[str, Any]:
|
|
"""State attributes."""
|
|
return decode_attributes_from_source(
|
|
getattr(self._row, "attributes", None), self.attr_cache
|
|
)
|
|
|
|
@cached_property
|
|
def _last_changed_ts(self) -> float | None:
|
|
"""Last changed timestamp."""
|
|
return getattr(self._row, "last_changed_ts", None)
|
|
|
|
@cached_property
|
|
def last_changed(self) -> datetime: # type: ignore[override]
|
|
"""Last changed datetime."""
|
|
return dt_util.utc_from_timestamp(
|
|
self._last_changed_ts or self._last_updated_ts
|
|
)
|
|
|
|
@cached_property
|
|
def last_updated(self) -> datetime: # type: ignore[override]
|
|
"""Last updated datetime."""
|
|
if TYPE_CHECKING:
|
|
assert self._last_updated_ts is not None
|
|
return dt_util.utc_from_timestamp(self._last_updated_ts)
|
|
|
|
def as_dict(self) -> dict[str, Any]: # type: ignore[override]
|
|
"""Return a dict representation of the LazyState.
|
|
|
|
Async friendly.
|
|
|
|
To be used for JSON serialization.
|
|
"""
|
|
last_updated_isoformat = self.last_updated.isoformat()
|
|
if self._last_changed_ts == self._last_updated_ts:
|
|
last_changed_isoformat = last_updated_isoformat
|
|
else:
|
|
last_changed_isoformat = self.last_changed.isoformat()
|
|
return {
|
|
"entity_id": self.entity_id,
|
|
"state": self.state,
|
|
"attributes": self._attributes or self.attributes,
|
|
"last_changed": last_changed_isoformat,
|
|
"last_updated": last_updated_isoformat,
|
|
}
|
|
|
|
|
|
def row_to_compressed_state(
|
|
row: Row,
|
|
attr_cache: dict[str, dict[str, Any]],
|
|
start_time_ts: float | None,
|
|
entity_id: str,
|
|
state: str,
|
|
last_updated_ts: float | None,
|
|
no_attributes: bool,
|
|
) -> dict[str, Any]:
|
|
"""Convert a database row to a compressed state schema 41 and later."""
|
|
comp_state: dict[str, Any] = {COMPRESSED_STATE_STATE: state}
|
|
if not no_attributes:
|
|
comp_state[COMPRESSED_STATE_ATTRIBUTES] = decode_attributes_from_source(
|
|
getattr(row, "attributes", None), attr_cache
|
|
)
|
|
row_last_updated_ts: float = last_updated_ts or start_time_ts # type: ignore[assignment]
|
|
comp_state[COMPRESSED_STATE_LAST_UPDATED] = row_last_updated_ts
|
|
if (
|
|
(row_last_changed_ts := getattr(row, "last_changed_ts", None))
|
|
and row_last_changed_ts
|
|
and row_last_updated_ts != row_last_changed_ts
|
|
):
|
|
comp_state[COMPRESSED_STATE_LAST_CHANGED] = row_last_changed_ts
|
|
return comp_state
|