2022-05-18 06:58:30 +00:00
|
|
|
"""Devices queries for logbook."""
|
2024-03-08 14:01:29 +00:00
|
|
|
|
2022-05-18 06:58:30 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
from collections.abc import Iterable
|
|
|
|
|
2022-06-24 13:43:35 +00:00
|
|
|
import sqlalchemy
|
2022-06-23 18:59:55 +00:00
|
|
|
from sqlalchemy import lambda_stmt, select
|
2023-02-08 14:17:32 +00:00
|
|
|
from sqlalchemy.sql.elements import BooleanClauseList
|
2022-06-23 18:59:55 +00:00
|
|
|
from sqlalchemy.sql.lambdas import StatementLambdaElement
|
2023-02-08 14:17:32 +00:00
|
|
|
from sqlalchemy.sql.selectable import CTE, CompoundSelect, Select
|
2022-05-18 06:58:30 +00:00
|
|
|
|
2022-06-07 12:41:43 +00:00
|
|
|
from homeassistant.components.recorder.db_schema import (
|
2022-06-02 21:54:06 +00:00
|
|
|
DEVICE_ID_IN_EVENT,
|
|
|
|
EventData,
|
|
|
|
Events,
|
2023-03-11 19:54:55 +00:00
|
|
|
EventTypes,
|
2022-06-02 21:54:06 +00:00
|
|
|
States,
|
2023-03-12 20:01:58 +00:00
|
|
|
StatesMeta,
|
2022-06-02 21:54:06 +00:00
|
|
|
)
|
2022-05-18 06:58:30 +00:00
|
|
|
|
|
|
|
from .common import (
|
2022-06-02 21:54:06 +00:00
|
|
|
apply_events_context_hints,
|
|
|
|
apply_states_context_hints,
|
2022-05-18 06:58:30 +00:00
|
|
|
select_events_context_id_subquery,
|
|
|
|
select_events_context_only,
|
|
|
|
select_events_without_states,
|
|
|
|
select_states_context_only,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def _select_device_id_context_ids_sub_query(
|
2023-01-02 23:26:08 +00:00
|
|
|
start_day: float,
|
|
|
|
end_day: float,
|
2023-04-18 03:40:03 +00:00
|
|
|
event_type_ids: tuple[int, ...],
|
2022-06-23 18:59:55 +00:00
|
|
|
json_quotable_device_ids: list[str],
|
2023-02-08 14:17:32 +00:00
|
|
|
) -> Select:
|
2022-05-18 06:58:30 +00:00
|
|
|
"""Generate a subquery to find context ids for multiple devices."""
|
2023-03-09 12:03:08 +00:00
|
|
|
inner = (
|
2023-04-18 03:40:03 +00:00
|
|
|
select_events_context_id_subquery(start_day, end_day, event_type_ids)
|
2023-03-09 12:03:08 +00:00
|
|
|
.where(apply_event_device_id_matchers(json_quotable_device_ids))
|
|
|
|
.subquery()
|
2022-05-18 06:58:30 +00:00
|
|
|
)
|
2023-03-09 00:51:45 +00:00
|
|
|
return select(inner.c.context_id_bin).group_by(inner.c.context_id_bin)
|
2022-05-18 06:58:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _apply_devices_context_union(
|
2023-02-08 14:17:32 +00:00
|
|
|
sel: Select,
|
2023-01-02 23:26:08 +00:00
|
|
|
start_day: float,
|
|
|
|
end_day: float,
|
2023-04-18 03:40:03 +00:00
|
|
|
event_type_ids: tuple[int, ...],
|
2022-06-23 18:59:55 +00:00
|
|
|
json_quotable_device_ids: list[str],
|
2022-05-18 17:33:58 +00:00
|
|
|
) -> CompoundSelect:
|
2022-05-18 06:58:30 +00:00
|
|
|
"""Generate a CTE to find the device context ids and a query to find linked row."""
|
2022-05-18 17:33:58 +00:00
|
|
|
devices_cte: CTE = _select_device_id_context_ids_sub_query(
|
2022-05-18 06:58:30 +00:00
|
|
|
start_day,
|
|
|
|
end_day,
|
2023-04-18 03:40:03 +00:00
|
|
|
event_type_ids,
|
2022-06-23 18:59:55 +00:00
|
|
|
json_quotable_device_ids,
|
2022-05-18 06:58:30 +00:00
|
|
|
).cte()
|
2023-02-08 14:17:32 +00:00
|
|
|
return sel.union_all(
|
2022-06-02 21:54:06 +00:00
|
|
|
apply_events_context_hints(
|
|
|
|
select_events_context_only()
|
|
|
|
.select_from(devices_cte)
|
2023-03-09 00:51:45 +00:00
|
|
|
.outerjoin(Events, devices_cte.c.context_id_bin == Events.context_id_bin)
|
2023-03-11 19:54:55 +00:00
|
|
|
.outerjoin(EventTypes, (Events.event_type_id == EventTypes.event_type_id))
|
|
|
|
.outerjoin(EventData, (Events.data_id == EventData.data_id)),
|
|
|
|
),
|
2022-06-02 21:54:06 +00:00
|
|
|
apply_states_context_hints(
|
|
|
|
select_states_context_only()
|
|
|
|
.select_from(devices_cte)
|
2023-03-09 00:51:45 +00:00
|
|
|
.outerjoin(States, devices_cte.c.context_id_bin == States.context_id_bin)
|
2023-03-12 20:01:58 +00:00
|
|
|
.outerjoin(StatesMeta, (States.metadata_id == StatesMeta.metadata_id))
|
2022-06-02 21:54:06 +00:00
|
|
|
),
|
2022-05-18 06:58:30 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def devices_stmt(
|
2023-01-02 23:26:08 +00:00
|
|
|
start_day: float,
|
|
|
|
end_day: float,
|
2023-04-18 03:40:03 +00:00
|
|
|
event_type_ids: tuple[int, ...],
|
2022-06-23 18:59:55 +00:00
|
|
|
json_quotable_device_ids: list[str],
|
|
|
|
) -> StatementLambdaElement:
|
2022-05-18 06:58:30 +00:00
|
|
|
"""Generate a logbook query for multiple devices."""
|
2024-04-06 09:07:37 +00:00
|
|
|
return lambda_stmt(
|
2022-06-23 18:59:55 +00:00
|
|
|
lambda: _apply_devices_context_union(
|
2023-04-18 03:40:03 +00:00
|
|
|
select_events_without_states(start_day, end_day, event_type_ids).where(
|
2022-06-23 18:59:55 +00:00
|
|
|
apply_event_device_id_matchers(json_quotable_device_ids)
|
|
|
|
),
|
|
|
|
start_day,
|
|
|
|
end_day,
|
2023-04-18 03:40:03 +00:00
|
|
|
event_type_ids,
|
2022-06-23 18:59:55 +00:00
|
|
|
json_quotable_device_ids,
|
2023-01-02 23:26:08 +00:00
|
|
|
).order_by(Events.time_fired_ts)
|
2022-06-23 18:59:55 +00:00
|
|
|
)
|
2022-05-18 06:58:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
def apply_event_device_id_matchers(
|
2022-06-23 18:59:55 +00:00
|
|
|
json_quotable_device_ids: Iterable[str],
|
2023-02-08 14:17:32 +00:00
|
|
|
) -> BooleanClauseList:
|
2022-05-18 06:58:30 +00:00
|
|
|
"""Create matchers for the device_ids in the event_data."""
|
2022-06-24 13:43:35 +00:00
|
|
|
return DEVICE_ID_IN_EVENT.is_not(None) & sqlalchemy.cast(
|
|
|
|
DEVICE_ID_IN_EVENT, sqlalchemy.Text()
|
|
|
|
).in_(json_quotable_device_ids)
|