Add additional context data to logbook events (#71721)

This commit is contained in:
J. Nick Koston 2022-05-12 20:21:14 -04:00 committed by GitHub
parent 1d9fb4bca8
commit 24a0007785
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 254 additions and 124 deletions

View File

@ -15,7 +15,7 @@ def async_describe_events(hass: HomeAssistant, async_describe_event): # type: i
def async_describe_logbook_event(event: LazyEventPartialState): # type: ignore[no-untyped-def] def async_describe_logbook_event(event: LazyEventPartialState): # type: ignore[no-untyped-def]
"""Describe a logbook event.""" """Describe a logbook event."""
data = event.data data = event.data
message = "has been triggered" message = "triggered"
if ATTR_SOURCE in data: if ATTR_SOURCE in data:
message = f"{message} by {data[ATTR_SOURCE]}" message = f"{message} by {data[ATTR_SOURCE]}"

View File

@ -136,7 +136,7 @@ def async_describe_events(
return { return {
"name": f"{deconz_alarm_event.device.name}", "name": f"{deconz_alarm_event.device.name}",
"message": f"fired event '{data}'.", "message": f"fired event '{data}'",
} }
@callback @callback
@ -158,26 +158,26 @@ def async_describe_events(
if not data: if not data:
return { return {
"name": f"{deconz_event.device.name}", "name": f"{deconz_event.device.name}",
"message": "fired an unknown event.", "message": "fired an unknown event",
} }
# No device event match # No device event match
if not action: if not action:
return { return {
"name": f"{deconz_event.device.name}", "name": f"{deconz_event.device.name}",
"message": f"fired event '{data}'.", "message": f"fired event '{data}'",
} }
# Gesture event # Gesture event
if not interface: if not interface:
return { return {
"name": f"{deconz_event.device.name}", "name": f"{deconz_event.device.name}",
"message": f"fired event '{ACTIONS[action]}'.", "message": f"fired event '{ACTIONS[action]}'",
} }
return { return {
"name": f"{deconz_event.device.name}", "name": f"{deconz_event.device.name}",
"message": f"'{ACTIONS[action]}' event for '{INTERFACES[interface]}' was fired.", "message": f"'{ACTIONS[action]}' event for '{INTERFACES[interface]}' was fired",
} }
async_describe_event( async_describe_event(

View File

@ -17,7 +17,7 @@ def async_describe_events(hass, async_describe_event):
return { return {
"name": "Doorbird", "name": "Doorbird",
"message": f"Event {event.event_type} was fired.", "message": f"Event {event.event_type} was fired",
"entity_id": hass.data[DOMAIN][DOOR_STATION_EVENT_ENTITY_IDS].get( "entity_id": hass.data[DOMAIN][DOOR_STATION_EVENT_ENTITY_IDS].get(
doorbird_event, event.data.get(ATTR_ENTITY_ID) doorbird_event, event.data.get(ATTR_ENTITY_ID)
), ),

View File

@ -0,0 +1,39 @@
"""Describe homeassistant logbook events."""
from __future__ import annotations
from collections.abc import Callable
from homeassistant.components.logbook import (
LOGBOOK_ENTRY_ICON,
LOGBOOK_ENTRY_MESSAGE,
LOGBOOK_ENTRY_NAME,
)
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import Event, HomeAssistant, callback
from . import DOMAIN
EVENT_TO_NAME = {
EVENT_HOMEASSISTANT_STOP: "stopped",
EVENT_HOMEASSISTANT_START: "started",
}
@callback
def async_describe_events(
hass: HomeAssistant,
async_describe_event: Callable[[str, str, Callable[[Event], dict[str, str]]], None],
) -> None:
"""Describe logbook events."""
@callback
def async_describe_hass_event(event: Event) -> dict[str, str]:
"""Describe homeassisant logbook event."""
return {
LOGBOOK_ENTRY_NAME: "Home Assistant",
LOGBOOK_ENTRY_MESSAGE: EVENT_TO_NAME[event.event_type],
LOGBOOK_ENTRY_ICON: "mdi:home-assistant",
}
async_describe_event(DOMAIN, EVENT_HOMEASSISTANT_STOP, async_describe_hass_event)
async_describe_event(DOMAIN, EVENT_HOMEASSISTANT_START, async_describe_hass_event)

View File

@ -37,13 +37,10 @@ from homeassistant.const import (
ATTR_NAME, ATTR_NAME,
ATTR_SERVICE, ATTR_SERVICE,
EVENT_CALL_SERVICE, EVENT_CALL_SERVICE,
EVENT_HOMEASSISTANT_START,
EVENT_HOMEASSISTANT_STOP,
EVENT_LOGBOOK_ENTRY, EVENT_LOGBOOK_ENTRY,
EVENT_STATE_CHANGED, EVENT_STATE_CHANGED,
) )
from homeassistant.core import ( from homeassistant.core import (
DOMAIN as HA_DOMAIN,
Context, Context,
Event, Event,
HomeAssistant, HomeAssistant,
@ -70,7 +67,6 @@ from .queries import statement_for_request
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
FRIENDLY_NAME_JSON_EXTRACT = re.compile('"friendly_name": ?"([^"]+)"') FRIENDLY_NAME_JSON_EXTRACT = re.compile('"friendly_name": ?"([^"]+)"')
ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": ?"([^"]+)"') ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": ?"([^"]+)"')
DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"') DOMAIN_JSON_EXTRACT = re.compile('"domain": ?"([^"]+)"')
@ -79,19 +75,28 @@ ATTR_MESSAGE = "message"
DOMAIN = "logbook" DOMAIN = "logbook"
HA_DOMAIN_ENTITY_ID = f"{HA_DOMAIN}._"
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{DOMAIN: INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA}, extra=vol.ALLOW_EXTRA {DOMAIN: INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA}, extra=vol.ALLOW_EXTRA
) )
HOMEASSISTANT_EVENTS = {EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP} CONTEXT_USER_ID = "context_user_id"
CONTEXT_ENTITY_ID = "context_entity_id"
CONTEXT_ENTITY_ID_NAME = "context_entity_id_name"
CONTEXT_EVENT_TYPE = "context_event_type"
CONTEXT_DOMAIN = "context_domain"
CONTEXT_SERVICE = "context_service"
CONTEXT_NAME = "context_name"
CONTEXT_MESSAGE = "context_message"
ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED = ( LOGBOOK_ENTRY_DOMAIN = "domain"
EVENT_LOGBOOK_ENTRY, LOGBOOK_ENTRY_ENTITY_ID = "entity_id"
EVENT_CALL_SERVICE, LOGBOOK_ENTRY_ICON = "icon"
*HOMEASSISTANT_EVENTS, LOGBOOK_ENTRY_MESSAGE = "message"
) LOGBOOK_ENTRY_NAME = "name"
LOGBOOK_ENTRY_STATE = "state"
LOGBOOK_ENTRY_WHEN = "when"
ALL_EVENT_TYPES_EXCEPT_STATE_CHANGED = {EVENT_LOGBOOK_ENTRY, EVENT_CALL_SERVICE}
SCRIPT_AUTOMATION_EVENTS = {EVENT_AUTOMATION_TRIGGERED, EVENT_SCRIPT_STARTED} SCRIPT_AUTOMATION_EVENTS = {EVENT_AUTOMATION_TRIGGERED, EVENT_SCRIPT_STARTED}
@ -133,12 +138,12 @@ def async_log_entry(
context: Context | None = None, context: Context | None = None,
) -> None: ) -> None:
"""Add an entry to the logbook.""" """Add an entry to the logbook."""
data = {ATTR_NAME: name, ATTR_MESSAGE: message} data = {LOGBOOK_ENTRY_NAME: name, LOGBOOK_ENTRY_MESSAGE: message}
if domain is not None: if domain is not None:
data[ATTR_DOMAIN] = domain data[LOGBOOK_ENTRY_DOMAIN] = domain
if entity_id is not None: if entity_id is not None:
data[ATTR_ENTITY_ID] = entity_id data[LOGBOOK_ENTRY_ENTITY_ID] = entity_id
hass.bus.async_fire(EVENT_LOGBOOK_ENTRY, data, context=context) hass.bus.async_fire(EVENT_LOGBOOK_ENTRY, data, context=context)
@ -162,7 +167,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
message.hass = hass message.hass = hass
message = message.async_render(parse_result=False) message = message.async_render(parse_result=False)
async_log_entry(hass, name, message, domain, entity_id) async_log_entry(hass, name, message, domain, entity_id, service.context)
frontend.async_register_built_in_panel( frontend.async_register_built_in_panel(
hass, "logbook", "logbook", "hass:format-list-bulleted-type" hass, "logbook", "logbook", "hass:format-list-bulleted-type"
@ -375,13 +380,13 @@ def _humanify(
continue continue
data = { data = {
"when": format_time(row), LOGBOOK_ENTRY_WHEN: format_time(row),
"name": entity_name_cache.get(entity_id, row), LOGBOOK_ENTRY_NAME: entity_name_cache.get(entity_id, row),
"state": row.state, LOGBOOK_ENTRY_STATE: row.state,
"entity_id": entity_id, LOGBOOK_ENTRY_ENTITY_ID: entity_id,
} }
if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT): if icon := _row_attributes_extract(row, ICON_JSON_EXTRACT):
data["icon"] = icon data[LOGBOOK_ENTRY_ICON] = icon
context_augmenter.augment(data, entity_id, row) context_augmenter.augment(data, entity_id, row)
yield data yield data
@ -389,26 +394,11 @@ def _humanify(
elif event_type in external_events: elif event_type in external_events:
domain, describe_event = external_events[event_type] domain, describe_event = external_events[event_type]
data = describe_event(event_cache.get(row)) data = describe_event(event_cache.get(row))
data["when"] = format_time(row) data[LOGBOOK_ENTRY_WHEN] = format_time(row)
data["domain"] = domain data[LOGBOOK_ENTRY_DOMAIN] = domain
context_augmenter.augment(data, data.get(ATTR_ENTITY_ID), row) context_augmenter.augment(data, data.get(ATTR_ENTITY_ID), row)
yield data yield data
elif event_type == EVENT_HOMEASSISTANT_START:
yield {
"when": format_time(row),
"name": "Home Assistant",
"message": "started",
"domain": HA_DOMAIN,
}
elif event_type == EVENT_HOMEASSISTANT_STOP:
yield {
"when": format_time(row),
"name": "Home Assistant",
"message": "stopped",
"domain": HA_DOMAIN,
}
elif event_type == EVENT_LOGBOOK_ENTRY: elif event_type == EVENT_LOGBOOK_ENTRY:
event = event_cache.get(row) event = event_cache.get(row)
event_data = event.data event_data = event.data
@ -419,11 +409,11 @@ def _humanify(
domain = split_entity_id(str(entity_id))[0] domain = split_entity_id(str(entity_id))[0]
data = { data = {
"when": format_time(row), LOGBOOK_ENTRY_WHEN: format_time(row),
"name": event_data.get(ATTR_NAME), LOGBOOK_ENTRY_NAME: event_data.get(ATTR_NAME),
"message": event_data.get(ATTR_MESSAGE), LOGBOOK_ENTRY_MESSAGE: event_data.get(ATTR_MESSAGE),
"domain": domain, LOGBOOK_ENTRY_DOMAIN: domain,
"entity_id": entity_id, LOGBOOK_ENTRY_ENTITY_ID: entity_id,
} }
context_augmenter.augment(data, entity_id, row) context_augmenter.augment(data, entity_id, row)
yield data yield data
@ -505,9 +495,6 @@ def _keep_row(
row: Row, row: Row,
entities_filter: EntityFilter | Callable[[str], bool] | None = None, entities_filter: EntityFilter | Callable[[str], bool] | None = None,
) -> bool: ) -> bool:
if event_type in HOMEASSISTANT_EVENTS:
return entities_filter is None or entities_filter(HA_DOMAIN_ENTITY_ID)
if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT): if entity_id := _row_event_data_extract(row, ENTITY_ID_JSON_EXTRACT):
return entities_filter is None or entities_filter(entity_id) return entities_filter is None or entities_filter(entity_id)
@ -544,7 +531,7 @@ 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: if context_user_id := row.context_user_id:
data["context_user_id"] = 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
@ -567,43 +554,40 @@ class ContextAugmenter:
# State change # State change
if context_entity_id := context_row.entity_id: if context_entity_id := context_row.entity_id:
data["context_entity_id"] = context_entity_id data[CONTEXT_ENTITY_ID] = context_entity_id
data["context_entity_id_name"] = self.entity_name_cache.get( data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get(
context_entity_id, context_row context_entity_id, context_row
) )
data["context_event_type"] = event_type data[CONTEXT_EVENT_TYPE] = event_type
return return
# Call service # Call service
if event_type == EVENT_CALL_SERVICE: if event_type == EVENT_CALL_SERVICE:
event = self.event_cache.get(context_row) event = self.event_cache.get(context_row)
event_data = event.data event_data = event.data
data["context_domain"] = event_data.get(ATTR_DOMAIN) data[CONTEXT_DOMAIN] = event_data.get(ATTR_DOMAIN)
data["context_service"] = event_data.get(ATTR_SERVICE) data[CONTEXT_SERVICE] = event_data.get(ATTR_SERVICE)
data["context_event_type"] = event_type data[CONTEXT_EVENT_TYPE] = event_type
return return
if not entity_id: if event_type not in self.external_events:
return return
attr_entity_id = _row_event_data_extract(context_row, ENTITY_ID_JSON_EXTRACT) domain, describe_event = self.external_events[event_type]
if attr_entity_id is None or ( data[CONTEXT_EVENT_TYPE] = event_type
event_type in SCRIPT_AUTOMATION_EVENTS and attr_entity_id == entity_id data[CONTEXT_DOMAIN] = domain
): event = self.event_cache.get(context_row)
described = describe_event(event)
if name := described.get(ATTR_NAME):
data[CONTEXT_NAME] = name
if message := described.get(ATTR_MESSAGE):
data[CONTEXT_MESSAGE] = message
if not (attr_entity_id := described.get(ATTR_ENTITY_ID)):
return return
data[CONTEXT_ENTITY_ID] = attr_entity_id
data["context_entity_id"] = attr_entity_id data[CONTEXT_ENTITY_ID_NAME] = self.entity_name_cache.get(
data["context_entity_id_name"] = self.entity_name_cache.get(
attr_entity_id, context_row attr_entity_id, context_row
) )
data["context_event_type"] = event_type
if event_type in self.external_events:
domain, describe_event = self.external_events[event_type]
data["context_domain"] = domain
event = self.event_cache.get(context_row)
if name := describe_event(event).get(ATTR_NAME):
data["context_name"] = name
def _is_sensor_continuous( def _is_sensor_continuous(
@ -627,11 +611,14 @@ def _is_sensor_continuous(
def _rows_match(row: Row, other_row: Row) -> bool: def _rows_match(row: Row, other_row: Row) -> bool:
"""Check of rows match by using the same method as Events __hash__.""" """Check of rows match by using the same method as Events __hash__."""
return bool( if (
row.event_type == other_row.event_type (state_id := row.state_id) is not None
and row.context_id == other_row.context_id and state_id == other_row.state_id
and row.time_fired == other_row.time_fired or (event_id := row.event_id) is not None
) and event_id == other_row.event_id
):
return True
return False
def _row_event_data_extract(row: Row, extractor: re.Pattern) -> str | None: def _row_event_data_extract(row: Row, extractor: re.Pattern) -> str | None:

View File

@ -33,6 +33,7 @@ UNIT_OF_MEASUREMENT_JSON_LIKE = f"%{UNIT_OF_MEASUREMENT_JSON}%"
EVENT_COLUMNS = ( EVENT_COLUMNS = (
Events.event_id.label("event_id"),
Events.event_type.label("event_type"), Events.event_type.label("event_type"),
Events.event_data.label("event_data"), Events.event_data.label("event_data"),
Events.time_fired.label("time_fired"), Events.time_fired.label("time_fired"),
@ -42,6 +43,7 @@ EVENT_COLUMNS = (
) )
STATE_COLUMNS = ( STATE_COLUMNS = (
States.state_id.label("state_id"),
States.state.label("state"), States.state.label("state"),
States.entity_id.label("entity_id"), States.entity_id.label("entity_id"),
States.attributes.label("attributes"), States.attributes.label("attributes"),
@ -49,6 +51,7 @@ STATE_COLUMNS = (
) )
EMPTY_STATE_COLUMNS = ( EMPTY_STATE_COLUMNS = (
literal(value=None, type_=sqlalchemy.String).label("state_id"),
literal(value=None, type_=sqlalchemy.String).label("state"), literal(value=None, type_=sqlalchemy.String).label("state"),
literal(value=None, type_=sqlalchemy.String).label("entity_id"), literal(value=None, type_=sqlalchemy.String).label("entity_id"),
literal(value=None, type_=sqlalchemy.Text).label("attributes"), literal(value=None, type_=sqlalchemy.Text).label("attributes"),
@ -294,6 +297,7 @@ def _select_states(start_day: dt, end_day: dt) -> Select:
old_state = aliased(States, name="old_state") old_state = aliased(States, name="old_state")
return ( return (
select( select(
literal(value=None, type_=sqlalchemy.Text).label("event_id"),
literal(value=EVENT_STATE_CHANGED, type_=sqlalchemy.String).label( literal(value=EVENT_STATE_CHANGED, type_=sqlalchemy.String).label(
"event_type" "event_type"
), ),

View File

@ -49,7 +49,7 @@ def async_describe_events(
return { return {
"name": "Shelly", "name": "Shelly",
"message": f"'{click_type}' click event for {input_name} Input was fired.", "message": f"'{click_type}' click event for {input_name} Input was fired",
} }
async_describe_event(DOMAIN, EVENT_SHELLY_CLICK, async_describe_shelly_click_event) async_describe_event(DOMAIN, EVENT_SHELLY_CLICK, async_describe_shelly_click_event)

View File

@ -1243,12 +1243,12 @@ async def test_logbook_humanify_automation_triggered_event(hass):
assert event1["name"] == "Hello Automation" assert event1["name"] == "Hello Automation"
assert event1["domain"] == "automation" assert event1["domain"] == "automation"
assert event1["message"] == "has been triggered" assert event1["message"] == "triggered"
assert event1["entity_id"] == "automation.hello" assert event1["entity_id"] == "automation.hello"
assert event2["name"] == "Bye Automation" assert event2["name"] == "Bye Automation"
assert event2["domain"] == "automation" assert event2["domain"] == "automation"
assert event2["message"] == "has been triggered by source of trigger" assert event2["message"] == "triggered by source of trigger"
assert event2["entity_id"] == "automation.bye" assert event2["entity_id"] == "automation.bye"

View File

@ -37,13 +37,13 @@ async def test_humanify_automation_trigger_event(hass):
) )
assert event1["name"] == "Bla" assert event1["name"] == "Bla"
assert event1["message"] == "has been triggered by state change of input_boolean.yo" assert event1["message"] == "triggered by state change of input_boolean.yo"
assert event1["source"] == "state change of input_boolean.yo" assert event1["source"] == "state change of input_boolean.yo"
assert event1["context_id"] == context.id assert event1["context_id"] == context.id
assert event1["entity_id"] == "automation.bla" assert event1["entity_id"] == "automation.bla"
assert event2["name"] == "Bla" assert event2["name"] == "Bla"
assert event2["message"] == "has been triggered" assert event2["message"] == "triggered"
assert event2["source"] is None assert event2["source"] is None
assert event2["context_id"] == context.id assert event2["context_id"] == context.id
assert event2["entity_id"] == "automation.bla" assert event2["entity_id"] == "automation.bla"

View File

@ -85,7 +85,7 @@ async def test_humanifying_deconz_alarm_event(hass, aioclient_mock):
assert events[0]["name"] == "Keypad" assert events[0]["name"] == "Keypad"
assert events[0]["domain"] == "deconz" assert events[0]["domain"] == "deconz"
assert events[0]["message"] == "fired event 'armed_away'." assert events[0]["message"] == "fired event 'armed_away'"
async def test_humanifying_deconz_event(hass, aioclient_mock): async def test_humanifying_deconz_event(hass, aioclient_mock):
@ -214,20 +214,20 @@ async def test_humanifying_deconz_event(hass, aioclient_mock):
assert events[0]["name"] == "Switch 1" assert events[0]["name"] == "Switch 1"
assert events[0]["domain"] == "deconz" assert events[0]["domain"] == "deconz"
assert events[0]["message"] == "fired event '2000'." assert events[0]["message"] == "fired event '2000'"
assert events[1]["name"] == "Hue remote" assert events[1]["name"] == "Hue remote"
assert events[1]["domain"] == "deconz" assert events[1]["domain"] == "deconz"
assert events[1]["message"] == "'Long press' event for 'Dim up' was fired." assert events[1]["message"] == "'Long press' event for 'Dim up' was fired"
assert events[2]["name"] == "Xiaomi cube" assert events[2]["name"] == "Xiaomi cube"
assert events[2]["domain"] == "deconz" assert events[2]["domain"] == "deconz"
assert events[2]["message"] == "fired event 'Shake'." assert events[2]["message"] == "fired event 'Shake'"
assert events[3]["name"] == "Xiaomi cube" assert events[3]["name"] == "Xiaomi cube"
assert events[3]["domain"] == "deconz" assert events[3]["domain"] == "deconz"
assert events[3]["message"] == "fired event 'unsupported_gesture'." assert events[3]["message"] == "fired event 'unsupported_gesture'"
assert events[4]["name"] == "Faulty event" assert events[4]["name"] == "Faulty event"
assert events[4]["domain"] == "deconz" assert events[4]["domain"] == "deconz"
assert events[4]["message"] == "fired an unknown event." assert events[4]["message"] == "fired an unknown event"

View File

@ -1,5 +1,6 @@
"""The tests for the logbook component.""" """The tests for the logbook component."""
# pylint: disable=protected-access,invalid-name # pylint: disable=protected-access,invalid-name
import asyncio
import collections import collections
from datetime import datetime, timedelta from datetime import datetime, timedelta
from http import HTTPStatus from http import HTTPStatus
@ -208,8 +209,10 @@ 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_not_grouped(hass_): async def test_home_assistant_start_stop_not_grouped(hass_):
"""Test if HA start and stop events are no longer grouped.""" """Test if HA start and stop events are no longer grouped."""
await async_setup_component(hass_, "homeassistant", {})
await hass_.async_block_till_done()
entries = mock_humanify( entries = mock_humanify(
hass_, hass_,
( (
@ -223,8 +226,10 @@ def test_home_assistant_start_stop_not_grouped(hass_):
assert_entry(entries[1], name="Home Assistant", message="started", domain=ha.DOMAIN) assert_entry(entries[1], name="Home Assistant", message="started", domain=ha.DOMAIN)
def test_home_assistant_start(hass_): async def test_home_assistant_start(hass_):
"""Test if HA start is not filtered or converted into a restart.""" """Test if HA start is not filtered or converted into a restart."""
await async_setup_component(hass_, "homeassistant", {})
await hass_.async_block_till_done()
entity_id = "switch.bla" entity_id = "switch.bla"
pointA = dt_util.utcnow() pointA = dt_util.utcnow()
@ -604,9 +609,12 @@ async def test_logbook_view_end_time_entity(hass, hass_client, recorder_mock):
async def test_logbook_entity_filter_with_automations(hass, hass_client, recorder_mock): async def test_logbook_entity_filter_with_automations(hass, hass_client, recorder_mock):
"""Test the logbook view with end_time and entity with automations and scripts.""" """Test the logbook view with end_time and entity with automations and scripts."""
await async_setup_component(hass, "logbook", {}) await asyncio.gather(
await async_setup_component(hass, "automation", {}) *[
await async_setup_component(hass, "script", {}) async_setup_component(hass, comp, {})
for comp in ("homeassistant", "logbook", "automation", "script")
]
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
@ -749,7 +757,12 @@ async def test_filter_continuous_sensor_values(
async def test_exclude_new_entities(hass, hass_client, recorder_mock, set_utc): async def test_exclude_new_entities(hass, hass_client, recorder_mock, set_utc):
"""Test if events are excluded on first update.""" """Test if events are excluded on first update."""
await async_setup_component(hass, "logbook", {}) await asyncio.gather(
*[
async_setup_component(hass, comp, {})
for comp in ("homeassistant", "logbook")
]
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
entity_id = "climate.bla" entity_id = "climate.bla"
@ -781,7 +794,12 @@ async def test_exclude_new_entities(hass, hass_client, recorder_mock, set_utc):
async def test_exclude_removed_entities(hass, hass_client, recorder_mock, set_utc): async def test_exclude_removed_entities(hass, hass_client, recorder_mock, set_utc):
"""Test if events are excluded on last update.""" """Test if events are excluded on last update."""
await async_setup_component(hass, "logbook", {}) await asyncio.gather(
*[
async_setup_component(hass, comp, {})
for comp in ("homeassistant", "logbook")
]
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
entity_id = "climate.bla" entity_id = "climate.bla"
@ -820,7 +838,12 @@ async def test_exclude_removed_entities(hass, hass_client, recorder_mock, set_ut
async def test_exclude_attribute_changes(hass, hass_client, recorder_mock, set_utc): async def test_exclude_attribute_changes(hass, hass_client, recorder_mock, set_utc):
"""Test if events of attribute changes are filtered.""" """Test if events of attribute changes are filtered."""
await async_setup_component(hass, "logbook", {}) await asyncio.gather(
*[
async_setup_component(hass, comp, {})
for comp in ("homeassistant", "logbook")
]
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
@ -855,9 +878,12 @@ async def test_exclude_attribute_changes(hass, hass_client, recorder_mock, set_u
async def test_logbook_entity_context_id(hass, recorder_mock, hass_client): async def test_logbook_entity_context_id(hass, recorder_mock, hass_client):
"""Test the logbook view with end_time and entity with automations and scripts.""" """Test the logbook view with end_time and entity with automations and scripts."""
await async_setup_component(hass, "logbook", {}) await asyncio.gather(
await async_setup_component(hass, "automation", {}) *[
await async_setup_component(hass, "script", {}) async_setup_component(hass, comp, {})
for comp in ("homeassistant", "logbook", "automation", "script")
]
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
@ -1004,9 +1030,12 @@ async def test_logbook_context_id_automation_script_started_manually(
hass, recorder_mock, hass_client hass, recorder_mock, hass_client
): ):
"""Test the logbook populates context_ids for scripts and automations started manually.""" """Test the logbook populates context_ids for scripts and automations started manually."""
await async_setup_component(hass, "logbook", {}) await asyncio.gather(
await async_setup_component(hass, "automation", {}) *[
await async_setup_component(hass, "script", {}) async_setup_component(hass, comp, {})
for comp in ("homeassistant", "logbook", "automation", "script")
]
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
@ -1032,6 +1061,19 @@ async def test_logbook_context_id_automation_script_started_manually(
) )
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
script_2_context = ha.Context(
id="1234",
user_id="b400facee45711eaa9308bfd3d19e474",
)
hass.bus.async_fire(
EVENT_SCRIPT_STARTED,
{ATTR_NAME: "Mock script"},
context=script_2_context,
)
hass.states.async_set("switch.new", STATE_ON, context=script_2_context)
hass.states.async_set("switch.new", STATE_OFF, context=script_2_context)
await hass.async_block_till_done() await hass.async_block_till_done()
await async_wait_recording_done(hass) await async_wait_recording_done(hass)
@ -1061,12 +1103,28 @@ async def test_logbook_context_id_automation_script_started_manually(
assert json_dict[2]["domain"] == "homeassistant" assert json_dict[2]["domain"] == "homeassistant"
assert json_dict[3]["entity_id"] is None
assert json_dict[3]["name"] == "Mock script"
assert "context_entity_id" not in json_dict[1]
assert json_dict[3]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474"
assert json_dict[3]["context_id"] == "1234"
assert json_dict[4]["entity_id"] == "switch.new"
assert json_dict[4]["state"] == "off"
assert "context_entity_id" not in json_dict[1]
assert json_dict[4]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474"
assert json_dict[4]["context_event_type"] == "script_started"
assert json_dict[4]["context_domain"] == "script"
async def test_logbook_entity_context_parent_id(hass, hass_client, recorder_mock): async def test_logbook_entity_context_parent_id(hass, hass_client, recorder_mock):
"""Test the logbook view links events via context parent_id.""" """Test the logbook view links events via context parent_id."""
await async_setup_component(hass, "logbook", {}) await asyncio.gather(
await async_setup_component(hass, "automation", {}) *[
await async_setup_component(hass, "script", {}) async_setup_component(hass, comp, {})
for comp in ("homeassistant", "logbook", "automation", "script")
]
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
@ -1240,7 +1298,13 @@ async def test_logbook_entity_context_parent_id(hass, hass_client, recorder_mock
async def test_logbook_context_from_template(hass, hass_client, recorder_mock): async def test_logbook_context_from_template(hass, hass_client, recorder_mock):
"""Test the logbook view with end_time and entity with automations and scripts.""" """Test the logbook view with end_time and entity with automations and scripts."""
await async_setup_component(hass, "logbook", {}) await asyncio.gather(
*[
async_setup_component(hass, comp, {})
for comp in ("homeassistant", "logbook")
]
)
assert await async_setup_component( assert await async_setup_component(
hass, hass,
"switch", "switch",
@ -1637,7 +1701,13 @@ async def test_logbook_invalid_entity(hass, hass_client, recorder_mock):
async def test_icon_and_state(hass, hass_client, recorder_mock): async def test_icon_and_state(hass, hass_client, recorder_mock):
"""Test to ensure state and custom icons are returned.""" """Test to ensure state and custom icons are returned."""
await async_setup_component(hass, "logbook", {}) await asyncio.gather(
*[
async_setup_component(hass, comp, {})
for comp in ("homeassistant", "logbook")
]
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
@ -1705,6 +1775,7 @@ async def test_exclude_events_domain(hass, hass_client, recorder_mock):
entity_id = "switch.bla" entity_id = "switch.bla"
entity_id2 = "sensor.blu" entity_id2 = "sensor.blu"
await async_setup_component(hass, "homeassistant", {})
config = logbook.CONFIG_SCHEMA( config = logbook.CONFIG_SCHEMA(
{ {
ha.DOMAIN: {}, ha.DOMAIN: {},
@ -1750,7 +1821,10 @@ async def test_exclude_events_domain_glob(hass, hass_client, recorder_mock):
}, },
} }
) )
await async_setup_component(hass, "logbook", config) await asyncio.gather(
async_setup_component(hass, "homeassistant", {}),
async_setup_component(hass, "logbook", config),
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
@ -1789,7 +1863,10 @@ async def test_include_events_entity(hass, hass_client, recorder_mock):
}, },
} }
) )
await async_setup_component(hass, "logbook", config) await asyncio.gather(
async_setup_component(hass, "homeassistant", {}),
async_setup_component(hass, "logbook", config),
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
@ -1821,7 +1898,10 @@ async def test_exclude_events_entity(hass, hass_client, recorder_mock):
logbook.DOMAIN: {CONF_EXCLUDE: {CONF_ENTITIES: [entity_id]}}, logbook.DOMAIN: {CONF_EXCLUDE: {CONF_ENTITIES: [entity_id]}},
} }
) )
await async_setup_component(hass, "logbook", config) await asyncio.gather(
async_setup_component(hass, "homeassistant", {}),
async_setup_component(hass, "logbook", config),
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
@ -1854,7 +1934,10 @@ async def test_include_events_domain(hass, hass_client, recorder_mock):
}, },
} }
) )
await async_setup_component(hass, "logbook", config) await asyncio.gather(
async_setup_component(hass, "homeassistant", {}),
async_setup_component(hass, "logbook", config),
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
@ -1897,7 +1980,10 @@ async def test_include_events_domain_glob(hass, hass_client, recorder_mock):
}, },
} }
) )
await async_setup_component(hass, "logbook", config) await asyncio.gather(
async_setup_component(hass, "homeassistant", {}),
async_setup_component(hass, "logbook", config),
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
@ -1948,7 +2034,10 @@ async def test_include_exclude_events(hass, hass_client, recorder_mock):
}, },
} }
) )
await async_setup_component(hass, "logbook", config) await asyncio.gather(
async_setup_component(hass, "homeassistant", {}),
async_setup_component(hass, "logbook", config),
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
@ -2004,7 +2093,10 @@ async def test_include_exclude_events_with_glob_filters(
}, },
} }
) )
await async_setup_component(hass, "logbook", config) await asyncio.gather(
async_setup_component(hass, "homeassistant", {}),
async_setup_component(hass, "logbook", config),
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
@ -2047,7 +2139,10 @@ async def test_empty_config(hass, hass_client, recorder_mock):
logbook.DOMAIN: {}, logbook.DOMAIN: {},
} }
) )
await async_setup_component(hass, "logbook", config) await asyncio.gather(
async_setup_component(hass, "homeassistant", {}),
async_setup_component(hass, "logbook", config),
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
@ -2141,7 +2236,12 @@ def _assert_entry(
async def test_get_events(hass, hass_ws_client, recorder_mock): async def test_get_events(hass, hass_ws_client, recorder_mock):
"""Test logbook get_events.""" """Test logbook get_events."""
now = dt_util.utcnow() now = dt_util.utcnow()
await async_setup_component(hass, "logbook", {}) await asyncio.gather(
*[
async_setup_component(hass, comp, {})
for comp in ("homeassistant", "logbook")
]
)
await async_recorder_block_till_done(hass) await async_recorder_block_till_done(hass)
hass.bus.async_fire(EVENT_HOMEASSISTANT_START) hass.bus.async_fire(EVENT_HOMEASSISTANT_START)

View File

@ -46,14 +46,14 @@ async def test_humanify_shelly_click_event_block_device(hass, coap_wrapper):
assert event1["domain"] == DOMAIN assert event1["domain"] == DOMAIN
assert ( assert (
event1["message"] event1["message"]
== "'single' click event for Test name channel 1 Input was fired." == "'single' click event for Test name channel 1 Input was fired"
) )
assert event2["name"] == "Shelly" assert event2["name"] == "Shelly"
assert event2["domain"] == DOMAIN assert event2["domain"] == DOMAIN
assert ( assert (
event2["message"] event2["message"]
== "'long' click event for shellyswitch25-12345678 channel 2 Input was fired." == "'long' click event for shellyswitch25-12345678 channel 2 Input was fired"
) )
@ -91,12 +91,12 @@ async def test_humanify_shelly_click_event_rpc_device(hass, rpc_wrapper):
assert event1["domain"] == DOMAIN assert event1["domain"] == DOMAIN
assert ( assert (
event1["message"] event1["message"]
== "'single_push' click event for test switch_0 Input was fired." == "'single_push' click event for test switch_0 Input was fired"
) )
assert event2["name"] == "Shelly" assert event2["name"] == "Shelly"
assert event2["domain"] == DOMAIN assert event2["domain"] == DOMAIN
assert ( assert (
event2["message"] event2["message"]
== "'btn_down' click event for shellypro4pm-12345678 channel 2 Input was fired." == "'btn_down' click event for shellypro4pm-12345678 channel 2 Input was fired"
) )