mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 16:57:10 +00:00
Always show the start and stop event in logbook (#71600)
This commit is contained in:
parent
5430b51358
commit
2560d35f1c
@ -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
|
||||||
|
|
||||||
|
@ -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_):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user