Home Assistant Unofficial Reference 2024.12.1
state.py
Go to the documentation of this file.
1 """Models states in for Recorder."""
2 
3 from __future__ import annotations
4 
5 from datetime import datetime
6 import logging
7 from typing import TYPE_CHECKING, Any
8 
9 from propcache import cached_property
10 from sqlalchemy.engine.row import Row
11 
12 from homeassistant.const import (
13  COMPRESSED_STATE_ATTRIBUTES,
14  COMPRESSED_STATE_LAST_CHANGED,
15  COMPRESSED_STATE_LAST_UPDATED,
16  COMPRESSED_STATE_STATE,
17 )
18 from homeassistant.core import Context, State
19 import homeassistant.util.dt as dt_util
20 
21 from .state_attributes import decode_attributes_from_source
22 
23 _LOGGER = logging.getLogger(__name__)
24 
25 EMPTY_CONTEXT = Context(id=None)
26 
27 
29  entity_id_to_metadata_id: dict[str, int | None],
30 ) -> list[int]:
31  """Extract metadata ids from entity_id_to_metadata_id."""
32  return [
33  metadata_id
34  for metadata_id in entity_id_to_metadata_id.values()
35  if metadata_id is not None
36  ]
37 
38 
40  """A lazy version of core State after schema 31."""
41 
42  def __init__( # pylint: disable=super-init-not-called
43  self,
44  row: Row,
45  attr_cache: dict[str, dict[str, Any]],
46  start_time_ts: float | None,
47  entity_id: str,
48  state: str,
49  last_updated_ts: float | None,
50  no_attributes: bool,
51  ) -> None:
52  """Init the lazy state."""
53  self._row_row = row
54  self.entity_identity_identity_id = entity_id
55  self.statestatestate = state or ""
56  self._attributes: dict[str, Any] | None = None
57  self._last_updated_ts: float | None = last_updated_ts or start_time_ts
58  self.attr_cacheattr_cache = attr_cache
59  self.contextcontextcontext = EMPTY_CONTEXT
60 
61  @cached_property # type: ignore[override]
62  def attributes(self) -> dict[str, Any]:
63  """State attributes."""
65  getattr(self._row_row, "attributes", None), self.attr_cacheattr_cache
66  )
67 
68  @cached_property
69  def _last_changed_ts(self) -> float | None:
70  """Last changed timestamp."""
71  return getattr(self._row_row, "last_changed_ts", None)
72 
73  @cached_property
74  def last_changed(self) -> datetime: # type: ignore[override]
75  """Last changed datetime."""
76  return dt_util.utc_from_timestamp(
77  self._last_changed_ts_last_changed_ts_last_changed_ts or self._last_updated_ts # type: ignore[arg-type]
78  )
79 
80  @cached_property
81  def _last_reported_ts(self) -> float | None:
82  """Last reported timestamp."""
83  return getattr(self._row_row, "last_reported_ts", None)
84 
85  @cached_property
86  def last_reported(self) -> datetime: # type: ignore[override]
87  """Last reported datetime."""
88  return dt_util.utc_from_timestamp(
89  self._last_reported_ts_last_reported_ts or self._last_updated_ts # type: ignore[arg-type]
90  )
91 
92  @cached_property
93  def last_updated(self) -> datetime: # type: ignore[override]
94  """Last updated datetime."""
95  if TYPE_CHECKING:
96  assert self._last_updated_ts is not None
97  return dt_util.utc_from_timestamp(self._last_updated_ts)
98 
99  def as_dict(self) -> dict[str, Any]: # type: ignore[override]
100  """Return a dict representation of the LazyState.
101 
102  Async friendly.
103 
104  To be used for JSON serialization.
105  """
106  last_updated_isoformat = self.last_updatedlast_updatedlast_updated.isoformat()
107  if self._last_changed_ts_last_changed_ts_last_changed_ts == self._last_updated_ts:
108  last_changed_isoformat = last_updated_isoformat
109  else:
110  last_changed_isoformat = self.last_changedlast_changedlast_changed.isoformat()
111  return {
112  "entity_id": self.entity_identity_identity_id,
113  "state": self.statestatestate,
114  "attributes": self._attributes or self.attributesattributesattributes,
115  "last_changed": last_changed_isoformat,
116  "last_updated": last_updated_isoformat,
117  }
118 
119 
121  row: Row,
122  attr_cache: dict[str, dict[str, Any]],
123  start_time_ts: float | None,
124  entity_id: str,
125  state: str,
126  last_updated_ts: float | None,
127  no_attributes: bool,
128 ) -> dict[str, Any]:
129  """Convert a database row to a compressed state schema 41 and later."""
130  comp_state: dict[str, Any] = {COMPRESSED_STATE_STATE: state}
131  if not no_attributes:
132  comp_state[COMPRESSED_STATE_ATTRIBUTES] = decode_attributes_from_source(
133  getattr(row, "attributes", None), attr_cache
134  )
135  row_last_updated_ts: float = last_updated_ts or start_time_ts # type: ignore[assignment]
136  comp_state[COMPRESSED_STATE_LAST_UPDATED] = row_last_updated_ts
137  if (
138  (row_last_changed_ts := getattr(row, "last_changed_ts", None))
139  and row_last_changed_ts
140  and row_last_updated_ts != row_last_changed_ts
141  ):
142  comp_state[COMPRESSED_STATE_LAST_CHANGED] = row_last_changed_ts
143  return comp_state
None __init__(self, Row row, dict[str, dict[str, Any]] attr_cache, float|None start_time_ts, str entity_id, str state, float|None last_updated_ts, bool no_attributes)
Definition: state.py:51
dict[str, Any] decode_attributes_from_source(Any source, dict[str, dict[str, Any]] attr_cache)
dict[str, Any] row_to_compressed_state(Row row, dict[str, dict[str, Any]] attr_cache, float|None start_time_ts, str entity_id, str state, float|None last_updated_ts, bool no_attributes)
Definition: state.py:128
list[int] extract_metadata_ids(dict[str, int|None] entity_id_to_metadata_id)
Definition: state.py:30