Always show the start and stop event in logbook (#71600)

This commit is contained in:
J. Nick Koston 2022-05-09 15:21:21 -05:00 committed by GitHub
parent 5430b51358
commit 2560d35f1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 124 deletions

View File

@ -5,7 +5,6 @@ from collections.abc import Callable, Generator, Iterable
from contextlib import suppress from contextlib import suppress
from datetime import datetime as dt, timedelta from datetime import datetime as dt, timedelta
from http import HTTPStatus from http import HTTPStatus
from itertools import groupby
import json import json
import re import re
from typing import Any, cast from typing import Any, cast
@ -338,127 +337,76 @@ def _humanify(
""" """
external_events = hass.data.get(DOMAIN, {}) external_events = hass.data.get(DOMAIN, {})
# Continuous sensors, will be excluded from the logbook # Continuous sensors, will be excluded from the logbook
continuous_sensors = {} continuous_sensors: dict[str, bool] = {}
# Group events in batches of GROUP_BY_MINUTES # Process events
for _, g_rows in groupby( for row in rows:
rows, lambda row: row.time_fired.minute // GROUP_BY_MINUTES # type: ignore[no-any-return] event_type = row.event_type
): if event_type == EVENT_STATE_CHANGED:
entity_id = row.entity_id
assert entity_id is not None
# Skip continuous sensors
if (
is_continuous := continuous_sensors.get(entity_id)
) is None and split_entity_id(entity_id)[0] == SENSOR_DOMAIN:
is_continuous = _is_sensor_continuous(hass, entity_id)
continuous_sensors[entity_id] = is_continuous
if is_continuous:
continue
rows_batch = list(g_rows) data = {
"when": _row_time_fired_isoformat(row),
"name": entity_name_cache.get(entity_id, row),
"state": row.state,
"entity_id": entity_id,
}
if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT):
data["icon"] = icon
# Group HA start/stop events context_augmenter.augment(data, entity_id, row)
# Maps minute of event to 1: stop, 2: stop + start yield data
start_stop_events = {}
# Process events elif event_type in external_events:
for row in rows_batch: domain, describe_event = external_events[event_type]
if row.event_type == EVENT_STATE_CHANGED: data = describe_event(event_cache.get(row))
entity_id = row.entity_id data["when"] = _row_time_fired_isoformat(row)
if ( data["domain"] = domain
entity_id in continuous_sensors context_augmenter.augment(data, data.get(ATTR_ENTITY_ID), row)
or split_entity_id(entity_id)[0] != SENSOR_DOMAIN yield data
):
continue
assert entity_id is not None
continuous_sensors[entity_id] = _is_sensor_continuous(hass, entity_id)
elif row.event_type == EVENT_HOMEASSISTANT_STOP: elif event_type == EVENT_HOMEASSISTANT_START:
if row.time_fired.minute in start_stop_events: yield {
continue "when": _row_time_fired_isoformat(row),
"name": "Home Assistant",
"message": "started",
"domain": HA_DOMAIN,
}
elif event_type == EVENT_HOMEASSISTANT_STOP:
yield {
"when": _row_time_fired_isoformat(row),
"name": "Home Assistant",
"message": "stopped",
"domain": HA_DOMAIN,
}
start_stop_events[row.time_fired.minute] = 1 elif event_type == EVENT_LOGBOOK_ENTRY:
event = event_cache.get(row)
event_data = event.data
domain = event_data.get(ATTR_DOMAIN)
entity_id = event_data.get(ATTR_ENTITY_ID)
if domain is None and entity_id is not None:
with suppress(IndexError):
domain = split_entity_id(str(entity_id))[0]
elif row.event_type == EVENT_HOMEASSISTANT_START: data = {
if row.time_fired.minute not in start_stop_events: "when": _row_time_fired_isoformat(row),
continue "name": event_data.get(ATTR_NAME),
"message": event_data.get(ATTR_MESSAGE),
start_stop_events[row.time_fired.minute] = 2 "domain": domain,
"entity_id": entity_id,
# Yield entries }
for row in rows_batch: context_augmenter.augment(data, entity_id, row)
if row.event_type == EVENT_STATE_CHANGED: yield data
entity_id = row.entity_id
assert entity_id is not None
if continuous_sensors.get(entity_id):
# Skip continuous sensors
continue
data = {
"when": _row_time_fired_isoformat(row),
"name": entity_name_cache.get(entity_id, row),
"state": row.state,
"entity_id": entity_id,
}
if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT):
data["icon"] = icon
if row.context_user_id:
data["context_user_id"] = row.context_user_id
context_augmenter.augment(data, entity_id, row)
yield data
elif row.event_type in external_events:
domain, describe_event = external_events[row.event_type]
data = describe_event(event_cache.get(row))
data["when"] = _row_time_fired_isoformat(row)
data["domain"] = domain
if row.context_user_id:
data["context_user_id"] = row.context_user_id
entity_id = data.get(ATTR_ENTITY_ID)
context_augmenter.augment(data, entity_id, row)
yield data
elif row.event_type == EVENT_HOMEASSISTANT_START:
if start_stop_events.get(row.time_fired.minute) == 2:
continue
yield {
"when": _row_time_fired_isoformat(row),
"name": "Home Assistant",
"message": "started",
"domain": HA_DOMAIN,
}
elif row.event_type == EVENT_HOMEASSISTANT_STOP:
if start_stop_events.get(row.time_fired.minute) == 2:
action = "restarted"
else:
action = "stopped"
yield {
"when": _row_time_fired_isoformat(row),
"name": "Home Assistant",
"message": action,
"domain": HA_DOMAIN,
}
elif row.event_type == EVENT_LOGBOOK_ENTRY:
event = event_cache.get(row)
event_data = event.data
domain = event_data.get(ATTR_DOMAIN)
entity_id = event_data.get(ATTR_ENTITY_ID)
if domain is None and entity_id is not None:
with suppress(IndexError):
domain = split_entity_id(str(entity_id))[0]
data = {
"when": _row_time_fired_isoformat(row),
"name": event_data.get(ATTR_NAME),
"message": event_data.get(ATTR_MESSAGE),
"domain": domain,
"entity_id": entity_id,
}
if row.context_user_id:
data["context_user_id"] = row.context_user_id
context_augmenter.augment(data, entity_id, row)
yield data
def _get_events( def _get_events(
@ -746,6 +694,9 @@ class ContextAugmenter:
def augment(self, data: dict[str, Any], entity_id: str | None, row: Row) -> None: def augment(self, data: dict[str, Any], entity_id: str | None, row: Row) -> None:
"""Augment data from the row and cache.""" """Augment data from the row and cache."""
if context_user_id := row.context_user_id:
data["context_user_id"] = context_user_id
if not (context_row := self.context_lookup.get(row.context_id)): if not (context_row := self.context_lookup.get(row.context_id)):
return return

View File

@ -210,11 +210,8 @@ async def test_filter_sensor(hass_: ha.HomeAssistant, hass_client):
_assert_entry(entries[2], name="ble", entity_id=entity_id4, state="10") _assert_entry(entries[2], name="ble", entity_id=entity_id4, state="10")
def test_home_assistant_start_stop_grouped(hass_): def test_home_assistant_start_stop_not_grouped(hass_):
"""Test if HA start and stop events are grouped. """Test if HA start and stop events are no longer grouped."""
Events that are occurring in the same minute.
"""
entries = mock_humanify( entries = mock_humanify(
hass_, hass_,
( (
@ -223,10 +220,9 @@ def test_home_assistant_start_stop_grouped(hass_):
), ),
) )
assert len(entries) == 1 assert len(entries) == 2
assert_entry( assert_entry(entries[0], name="Home Assistant", message="stopped", domain=ha.DOMAIN)
entries[0], name="Home Assistant", message="restarted", domain=ha.DOMAIN assert_entry(entries[1], name="Home Assistant", message="started", domain=ha.DOMAIN)
)
def test_home_assistant_start(hass_): def test_home_assistant_start(hass_):