Home Assistant Unofficial Reference 2024.12.1
entities_and_devices.py
Go to the documentation of this file.
1 """Entities and Devices queries for logbook."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Collection, Iterable
6 
7 from sqlalchemy import lambda_stmt, select, union_all
8 from sqlalchemy.sql.elements import ColumnElement
9 from sqlalchemy.sql.lambdas import StatementLambdaElement
10 from sqlalchemy.sql.selectable import CTE, CompoundSelect, Select
11 
13  EventData,
14  Events,
15  EventTypes,
16  States,
17  StatesMeta,
18 )
19 
20 from .common import (
21  apply_events_context_hints,
22  apply_states_context_hints,
23  select_events_context_id_subquery,
24  select_events_context_only,
25  select_events_without_states,
26  select_states_context_only,
27 )
28 from .devices import apply_event_device_id_matchers
29 from .entities import (
30  apply_entities_hints,
31  apply_event_entity_id_matchers,
32  states_select_for_entity_ids,
33 )
34 
35 
37  start_day: float,
38  end_day: float,
39  event_type_ids: tuple[int, ...],
40  states_metadata_ids: Collection[int],
41  json_quoted_entity_ids: list[str],
42  json_quoted_device_ids: list[str],
43 ) -> Select:
44  """Generate a subquery to find context ids for multiple entities and multiple devices."""
45  union = union_all(
46  select_events_context_id_subquery(start_day, end_day, event_type_ids).where(
48  json_quoted_entity_ids, json_quoted_device_ids
49  )
50  ),
51  apply_entities_hints(select(States.context_id_bin))
52  .filter(
53  (States.last_updated_ts > start_day) & (States.last_updated_ts < end_day)
54  )
55  .where(States.metadata_id.in_(states_metadata_ids)),
56  ).subquery()
57  return select(union.c.context_id_bin).group_by(union.c.context_id_bin)
58 
59 
61  sel: Select,
62  start_day: float,
63  end_day: float,
64  event_type_ids: tuple[int, ...],
65  states_metadata_ids: Collection[int],
66  json_quoted_entity_ids: list[str],
67  json_quoted_device_ids: list[str],
68 ) -> CompoundSelect:
69  devices_entities_cte: CTE = _select_entities_device_id_context_ids_sub_query(
70  start_day,
71  end_day,
72  event_type_ids,
73  states_metadata_ids,
74  json_quoted_entity_ids,
75  json_quoted_device_ids,
76  ).cte()
77  # We used to optimize this to exclude rows we already in the union with
78  # a States.metadata_id.not_in(states_metadata_ids) but that made the
79  # query much slower on MySQL, and since we already filter them away
80  # in the python code anyways since they will have context_only
81  # set on them the impact is minimal.
82  return sel.union_all(
83  states_select_for_entity_ids(start_day, end_day, states_metadata_ids),
86  .select_from(devices_entities_cte)
87  .outerjoin(
88  Events, devices_entities_cte.c.context_id_bin == Events.context_id_bin
89  )
90  .outerjoin(EventTypes, (Events.event_type_id == EventTypes.event_type_id))
91  .outerjoin(EventData, (Events.data_id == EventData.data_id)),
92  ),
95  .select_from(devices_entities_cte)
96  .outerjoin(
97  States, devices_entities_cte.c.context_id_bin == States.context_id_bin
98  )
99  .outerjoin(StatesMeta, (States.metadata_id == StatesMeta.metadata_id))
100  ),
101  )
102 
103 
105  start_day: float,
106  end_day: float,
107  event_type_ids: tuple[int, ...],
108  states_metadata_ids: Collection[int],
109  json_quoted_entity_ids: list[str],
110  json_quoted_device_ids: list[str],
111 ) -> StatementLambdaElement:
112  """Generate a logbook query for multiple entities."""
113  return lambda_stmt(
115  select_events_without_states(start_day, end_day, event_type_ids).where(
117  json_quoted_entity_ids, json_quoted_device_ids
118  )
119  ),
120  start_day,
121  end_day,
122  event_type_ids,
123  states_metadata_ids,
124  json_quoted_entity_ids,
125  json_quoted_device_ids,
126  ).order_by(Events.time_fired_ts)
127  )
128 
129 
131  json_quoted_entity_ids: Iterable[str], json_quoted_device_ids: Iterable[str]
132 ) -> ColumnElement[bool]:
133  """Create matchers for the device_id and entity_id in the event_data."""
135  json_quoted_entity_ids
136  ) | apply_event_device_id_matchers(json_quoted_device_ids)
Select select_events_without_states(float start_day, float end_day, tuple[int,...] event_type_ids)
Definition: common.py:147
Select select_events_context_id_subquery(float start_day, float end_day, tuple[int,...] event_type_ids)
Definition: common.py:114
BooleanClauseList apply_event_device_id_matchers(Iterable[str] json_quotable_device_ids)
Definition: devices.py:100
CompoundSelect _apply_entities_devices_context_union(Select sel, float start_day, float end_day, tuple[int,...] event_type_ids, Collection[int] states_metadata_ids, list[str] json_quoted_entity_ids, list[str] json_quoted_device_ids)
ColumnElement[bool] _apply_event_entity_id_device_id_matchers(Iterable[str] json_quoted_entity_ids, Iterable[str] json_quoted_device_ids)
Select _select_entities_device_id_context_ids_sub_query(float start_day, float end_day, tuple[int,...] event_type_ids, Collection[int] states_metadata_ids, list[str] json_quoted_entity_ids, list[str] json_quoted_device_ids)
StatementLambdaElement entities_devices_stmt(float start_day, float end_day, tuple[int,...] event_type_ids, Collection[int] states_metadata_ids, list[str] json_quoted_entity_ids, list[str] json_quoted_device_ids)
ColumnElement[bool] apply_event_entity_id_matchers(Iterable[str] json_quoted_entity_ids)
Definition: entities.py:129
Select states_select_for_entity_ids(float start_day, float end_day, Collection[int] states_metadata_ids)
Definition: entities.py:120