Include the first seen context data in the logbook api (#39194)
* Include the context_entity_id in the logbook api context_entity_id is the first entity seen during a time period that includes the context * update test * more of them * include friendly name * pylint wants a ternary * Refactor * performance * fix homekit context * Fix self describing events * Fix external_events
This commit is contained in:
parent
b1c0d8fb6c
commit
6b7a7939d2
9 changed files with 473 additions and 101 deletions
|
@ -31,7 +31,7 @@ from homeassistant.const import (
|
||||||
UNIT_PERCENTAGE,
|
UNIT_PERCENTAGE,
|
||||||
__version__,
|
__version__,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback as ha_callback, split_entity_id
|
from homeassistant.core import Context, callback as ha_callback, split_entity_id
|
||||||
from homeassistant.helpers.event import (
|
from homeassistant.helpers.event import (
|
||||||
async_track_state_change_event,
|
async_track_state_change_event,
|
||||||
track_point_in_utc_time,
|
track_point_in_utc_time,
|
||||||
|
@ -490,9 +490,12 @@ class HomeAccessory(Accessory):
|
||||||
ATTR_SERVICE: service,
|
ATTR_SERVICE: service,
|
||||||
ATTR_VALUE: value,
|
ATTR_VALUE: value,
|
||||||
}
|
}
|
||||||
|
context = Context()
|
||||||
|
|
||||||
self.hass.bus.async_fire(EVENT_HOMEKIT_CHANGED, event_data)
|
self.hass.bus.async_fire(EVENT_HOMEKIT_CHANGED, event_data, context=context)
|
||||||
await self.hass.services.async_call(domain, service, service_data)
|
await self.hass.services.async_call(
|
||||||
|
domain, service, service_data, context=context
|
||||||
|
)
|
||||||
|
|
||||||
@ha_callback
|
@ha_callback
|
||||||
def async_stop(self):
|
def async_stop(self):
|
||||||
|
|
|
@ -9,6 +9,7 @@ from sqlalchemy.orm import aliased
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components import sun
|
from homeassistant.components import sun
|
||||||
|
from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED
|
||||||
from homeassistant.components.history import sqlalchemy_filter_from_include_exclude_conf
|
from homeassistant.components.history import sqlalchemy_filter_from_include_exclude_conf
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.components.recorder.models import (
|
from homeassistant.components.recorder.models import (
|
||||||
|
@ -18,12 +19,15 @@ from homeassistant.components.recorder.models import (
|
||||||
process_timestamp_to_utc_isoformat,
|
process_timestamp_to_utc_isoformat,
|
||||||
)
|
)
|
||||||
from homeassistant.components.recorder.util import session_scope
|
from homeassistant.components.recorder.util import session_scope
|
||||||
|
from homeassistant.components.script import EVENT_SCRIPT_STARTED
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_DEVICE_CLASS,
|
ATTR_DEVICE_CLASS,
|
||||||
ATTR_DOMAIN,
|
ATTR_DOMAIN,
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
ATTR_FRIENDLY_NAME,
|
ATTR_FRIENDLY_NAME,
|
||||||
ATTR_NAME,
|
ATTR_NAME,
|
||||||
|
ATTR_SERVICE,
|
||||||
|
EVENT_CALL_SERVICE,
|
||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START,
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
EVENT_LOGBOOK_ENTRY,
|
EVENT_LOGBOOK_ENTRY,
|
||||||
|
@ -70,7 +74,15 @@ HOMEASSISTANT_EVENTS = [
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
]
|
]
|
||||||
|
|
||||||
ALL_EVENT_TYPES = [EVENT_STATE_CHANGED, EVENT_LOGBOOK_ENTRY, *HOMEASSISTANT_EVENTS]
|
ALL_EVENT_TYPES = [
|
||||||
|
EVENT_STATE_CHANGED,
|
||||||
|
EVENT_LOGBOOK_ENTRY,
|
||||||
|
EVENT_CALL_SERVICE,
|
||||||
|
*HOMEASSISTANT_EVENTS,
|
||||||
|
]
|
||||||
|
|
||||||
|
SCRIPT_AUTOMATION_EVENTS = [EVENT_AUTOMATION_TRIGGERED, EVENT_SCRIPT_STARTED]
|
||||||
|
|
||||||
|
|
||||||
LOG_MESSAGE_SCHEMA = vol.Schema(
|
LOG_MESSAGE_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
|
@ -83,13 +95,13 @@ LOG_MESSAGE_SCHEMA = vol.Schema(
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
def log_entry(hass, name, message, domain=None, entity_id=None):
|
def log_entry(hass, name, message, domain=None, entity_id=None, context=None):
|
||||||
"""Add an entry to the logbook."""
|
"""Add an entry to the logbook."""
|
||||||
hass.add_job(async_log_entry, hass, name, message, domain, entity_id)
|
hass.add_job(async_log_entry, hass, name, message, domain, entity_id, context)
|
||||||
|
|
||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
def async_log_entry(hass, name, message, domain=None, entity_id=None):
|
def async_log_entry(hass, name, message, domain=None, entity_id=None, context=None):
|
||||||
"""Add an entry to the logbook."""
|
"""Add an entry to the logbook."""
|
||||||
data = {ATTR_NAME: name, ATTR_MESSAGE: message}
|
data = {ATTR_NAME: name, ATTR_MESSAGE: message}
|
||||||
|
|
||||||
|
@ -97,7 +109,7 @@ def async_log_entry(hass, name, message, domain=None, entity_id=None):
|
||||||
data[ATTR_DOMAIN] = domain
|
data[ATTR_DOMAIN] = domain
|
||||||
if entity_id is not None:
|
if entity_id is not None:
|
||||||
data[ATTR_ENTITY_ID] = entity_id
|
data[ATTR_ENTITY_ID] = entity_id
|
||||||
hass.bus.async_fire(EVENT_LOGBOOK_ENTRY, data)
|
hass.bus.async_fire(EVENT_LOGBOOK_ENTRY, data, context=context)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup(hass, config):
|
async def async_setup(hass, config):
|
||||||
|
@ -203,7 +215,6 @@ class LogbookView(HomeAssistantView):
|
||||||
return self.json(
|
return self.json(
|
||||||
_get_events(
|
_get_events(
|
||||||
hass,
|
hass,
|
||||||
self.config,
|
|
||||||
start_day,
|
start_day,
|
||||||
end_day,
|
end_day,
|
||||||
entity_id,
|
entity_id,
|
||||||
|
@ -215,7 +226,7 @@ class LogbookView(HomeAssistantView):
|
||||||
return await hass.async_add_executor_job(json_events)
|
return await hass.async_add_executor_job(json_events)
|
||||||
|
|
||||||
|
|
||||||
def humanify(hass, events, entity_attr_cache):
|
def humanify(hass, events, entity_attr_cache, context_lookup):
|
||||||
"""Generate a converted list of events into Entry objects.
|
"""Generate a converted list of events into Entry objects.
|
||||||
|
|
||||||
Will try to group events if possible:
|
Will try to group events if possible:
|
||||||
|
@ -263,7 +274,18 @@ def humanify(hass, events, entity_attr_cache):
|
||||||
data = describe_event(event)
|
data = describe_event(event)
|
||||||
data["when"] = event.time_fired_isoformat
|
data["when"] = event.time_fired_isoformat
|
||||||
data["domain"] = domain
|
data["domain"] = domain
|
||||||
|
if event.context_user_id:
|
||||||
data["context_user_id"] = event.context_user_id
|
data["context_user_id"] = event.context_user_id
|
||||||
|
context_event = context_lookup.get(event.context_id)
|
||||||
|
if context_event:
|
||||||
|
_augment_data_with_context(
|
||||||
|
data,
|
||||||
|
data.get(ATTR_ENTITY_ID),
|
||||||
|
event,
|
||||||
|
context_event,
|
||||||
|
entity_attr_cache,
|
||||||
|
external_events,
|
||||||
|
)
|
||||||
yield data
|
yield data
|
||||||
|
|
||||||
if event.event_type == EVENT_STATE_CHANGED:
|
if event.event_type == EVENT_STATE_CHANGED:
|
||||||
|
@ -277,21 +299,34 @@ def humanify(hass, events, entity_attr_cache):
|
||||||
# Skip all but the last sensor state
|
# Skip all but the last sensor state
|
||||||
continue
|
continue
|
||||||
|
|
||||||
name = entity_attr_cache.get(
|
data = {
|
||||||
entity_id, ATTR_FRIENDLY_NAME, event
|
|
||||||
) or split_entity_id(entity_id)[1].replace("_", " ")
|
|
||||||
|
|
||||||
yield {
|
|
||||||
"when": event.time_fired_isoformat,
|
"when": event.time_fired_isoformat,
|
||||||
"name": name,
|
"name": _entity_name_from_event(
|
||||||
|
entity_id, event, entity_attr_cache
|
||||||
|
),
|
||||||
"message": _entry_message_from_event(
|
"message": _entry_message_from_event(
|
||||||
hass, entity_id, domain, event, entity_attr_cache
|
entity_id, domain, event, entity_attr_cache
|
||||||
),
|
),
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
"entity_id": entity_id,
|
"entity_id": entity_id,
|
||||||
"context_user_id": event.context_user_id,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if event.context_user_id:
|
||||||
|
data["context_user_id"] = event.context_user_id
|
||||||
|
|
||||||
|
context_event = context_lookup.get(event.context_id)
|
||||||
|
if context_event and context_event != event:
|
||||||
|
_augment_data_with_context(
|
||||||
|
data,
|
||||||
|
entity_id,
|
||||||
|
event,
|
||||||
|
context_event,
|
||||||
|
entity_attr_cache,
|
||||||
|
external_events,
|
||||||
|
)
|
||||||
|
|
||||||
|
yield data
|
||||||
|
|
||||||
elif event.event_type == EVENT_HOMEASSISTANT_START:
|
elif event.event_type == EVENT_HOMEASSISTANT_START:
|
||||||
if start_stop_events.get(event.time_fired_minute) == 2:
|
if start_stop_events.get(event.time_fired_minute) == 2:
|
||||||
continue
|
continue
|
||||||
|
@ -301,7 +336,6 @@ def humanify(hass, events, entity_attr_cache):
|
||||||
"name": "Home Assistant",
|
"name": "Home Assistant",
|
||||||
"message": "started",
|
"message": "started",
|
||||||
"domain": HA_DOMAIN,
|
"domain": HA_DOMAIN,
|
||||||
"context_user_id": event.context_user_id,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elif event.event_type == EVENT_HOMEASSISTANT_STOP:
|
elif event.event_type == EVENT_HOMEASSISTANT_STOP:
|
||||||
|
@ -315,7 +349,6 @@ def humanify(hass, events, entity_attr_cache):
|
||||||
"name": "Home Assistant",
|
"name": "Home Assistant",
|
||||||
"message": action,
|
"message": action,
|
||||||
"domain": HA_DOMAIN,
|
"domain": HA_DOMAIN,
|
||||||
"context_user_id": event.context_user_id,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elif event.event_type == EVENT_LOGBOOK_ENTRY:
|
elif event.event_type == EVENT_LOGBOOK_ENTRY:
|
||||||
|
@ -328,25 +361,42 @@ def humanify(hass, events, entity_attr_cache):
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
yield {
|
data = {
|
||||||
"when": event.time_fired_isoformat,
|
"when": event.time_fired_isoformat,
|
||||||
"name": event_data.get(ATTR_NAME),
|
"name": event_data.get(ATTR_NAME),
|
||||||
"message": event_data.get(ATTR_MESSAGE),
|
"message": event_data.get(ATTR_MESSAGE),
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
"entity_id": entity_id,
|
"entity_id": entity_id,
|
||||||
}
|
}
|
||||||
|
if event.context_user_id:
|
||||||
|
data["context_user_id"] = event.context_user_id
|
||||||
|
|
||||||
|
context_event = context_lookup.get(event.context_id)
|
||||||
|
if context_event and context_event != event:
|
||||||
|
_augment_data_with_context(
|
||||||
|
data,
|
||||||
|
entity_id,
|
||||||
|
event,
|
||||||
|
context_event,
|
||||||
|
entity_attr_cache,
|
||||||
|
external_events,
|
||||||
|
)
|
||||||
|
|
||||||
|
yield data
|
||||||
|
|
||||||
|
|
||||||
def _get_events(
|
def _get_events(
|
||||||
hass, config, start_day, end_day, entity_id=None, filters=None, entities_filter=None
|
hass, start_day, end_day, entity_id=None, filters=None, entities_filter=None
|
||||||
):
|
):
|
||||||
"""Get events for a period of time."""
|
"""Get events for a period of time."""
|
||||||
entity_attr_cache = EntityAttributeCache(hass)
|
entity_attr_cache = EntityAttributeCache(hass)
|
||||||
|
context_lookup = {None: None}
|
||||||
|
|
||||||
def yield_events(query):
|
def yield_events(query):
|
||||||
"""Yield Events that are not filtered away."""
|
"""Yield Events that are not filtered away."""
|
||||||
for row in query.yield_per(1000):
|
for row in query.yield_per(1000):
|
||||||
event = LazyEventPartialState(row)
|
event = LazyEventPartialState(row)
|
||||||
|
context_lookup.setdefault(event.context_id, event)
|
||||||
if _keep_event(hass, event, entities_filter):
|
if _keep_event(hass, event, entities_filter):
|
||||||
yield event
|
yield event
|
||||||
|
|
||||||
|
@ -366,6 +416,7 @@ def _get_events(
|
||||||
Events.event_type,
|
Events.event_type,
|
||||||
Events.event_data,
|
Events.event_data,
|
||||||
Events.time_fired,
|
Events.time_fired,
|
||||||
|
Events.context_id,
|
||||||
Events.context_user_id,
|
Events.context_user_id,
|
||||||
States.state,
|
States.state,
|
||||||
States.entity_id,
|
States.entity_id,
|
||||||
|
@ -424,7 +475,9 @@ def _get_events(
|
||||||
entity_filter | (Events.event_type != EVENT_STATE_CHANGED)
|
entity_filter | (Events.event_type != EVENT_STATE_CHANGED)
|
||||||
)
|
)
|
||||||
|
|
||||||
return list(humanify(hass, yield_events(query), entity_attr_cache))
|
return list(
|
||||||
|
humanify(hass, yield_events(query), entity_attr_cache, context_lookup)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _keep_event(hass, event, entities_filter):
|
def _keep_event(hass, event, entities_filter):
|
||||||
|
@ -439,10 +492,12 @@ def _keep_event(hass, event, entities_filter):
|
||||||
if domain is None:
|
if domain is None:
|
||||||
return False
|
return False
|
||||||
entity_id = f"{domain}."
|
entity_id = f"{domain}."
|
||||||
|
elif event.event_type == EVENT_CALL_SERVICE:
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
event_data = event.data
|
event_data = event.data
|
||||||
entity_id = event_data.get(ATTR_ENTITY_ID)
|
entity_id = event_data.get(ATTR_ENTITY_ID)
|
||||||
if entity_id is None:
|
if not entity_id:
|
||||||
domain = event_data.get(ATTR_DOMAIN)
|
domain = event_data.get(ATTR_DOMAIN)
|
||||||
if domain is None:
|
if domain is None:
|
||||||
return False
|
return False
|
||||||
|
@ -451,7 +506,7 @@ def _keep_event(hass, event, entities_filter):
|
||||||
return entities_filter is None or entities_filter(entity_id)
|
return entities_filter is None or entities_filter(entity_id)
|
||||||
|
|
||||||
|
|
||||||
def _entry_message_from_event(hass, entity_id, domain, event, entity_attr_cache):
|
def _entry_message_from_event(entity_id, domain, event, entity_attr_cache):
|
||||||
"""Convert a state to a message for the logbook."""
|
"""Convert a state to a message for the logbook."""
|
||||||
# We pass domain in so we don't have to split entity_id again
|
# We pass domain in so we don't have to split entity_id again
|
||||||
state_state = event.state
|
state_state = event.state
|
||||||
|
@ -539,6 +594,68 @@ def _entry_message_from_event(hass, entity_id, domain, event, entity_attr_cache)
|
||||||
return f"changed to {state_state}"
|
return f"changed to {state_state}"
|
||||||
|
|
||||||
|
|
||||||
|
def _augment_data_with_context(
|
||||||
|
data, entity_id, event, context_event, entity_attr_cache, external_events
|
||||||
|
):
|
||||||
|
event_type = context_event.event_type
|
||||||
|
|
||||||
|
# State change
|
||||||
|
context_entity_id = context_event.entity_id
|
||||||
|
|
||||||
|
if entity_id and context_entity_id == entity_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
if context_entity_id:
|
||||||
|
data["context_entity_id"] = context_entity_id
|
||||||
|
data["context_entity_id_name"] = _entity_name_from_event(
|
||||||
|
context_entity_id, context_event, entity_attr_cache
|
||||||
|
)
|
||||||
|
data["context_event_type"] = event_type
|
||||||
|
return
|
||||||
|
|
||||||
|
event_data = context_event.data
|
||||||
|
|
||||||
|
# Call service
|
||||||
|
if event_type == EVENT_CALL_SERVICE:
|
||||||
|
event_data = context_event.data
|
||||||
|
data["context_domain"] = event_data.get(ATTR_DOMAIN)
|
||||||
|
data["context_service"] = event_data.get(ATTR_SERVICE)
|
||||||
|
data["context_event_type"] = event_type
|
||||||
|
return
|
||||||
|
|
||||||
|
if not entity_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
attr_entity_id = event_data.get(ATTR_ENTITY_ID)
|
||||||
|
if not attr_entity_id or (
|
||||||
|
event_type in SCRIPT_AUTOMATION_EVENTS and attr_entity_id == entity_id
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
if context_event == event:
|
||||||
|
return
|
||||||
|
|
||||||
|
data["context_entity_id"] = attr_entity_id
|
||||||
|
data["context_entity_id_name"] = _entity_name_from_event(
|
||||||
|
attr_entity_id, context_event, entity_attr_cache
|
||||||
|
)
|
||||||
|
data["context_event_type"] = event_type
|
||||||
|
|
||||||
|
if event_type in external_events:
|
||||||
|
domain, describe_event = external_events[event_type]
|
||||||
|
data["context_domain"] = domain
|
||||||
|
name = describe_event(context_event).get(ATTR_NAME)
|
||||||
|
if name:
|
||||||
|
data["context_name"] = name
|
||||||
|
|
||||||
|
|
||||||
|
def _entity_name_from_event(entity_id, event, entity_attr_cache):
|
||||||
|
"""Extract the entity name from the event using the cache if possible."""
|
||||||
|
return entity_attr_cache.get(
|
||||||
|
entity_id, ATTR_FRIENDLY_NAME, event
|
||||||
|
) or split_entity_id(entity_id)[1].replace("_", " ")
|
||||||
|
|
||||||
|
|
||||||
class LazyEventPartialState:
|
class LazyEventPartialState:
|
||||||
"""A lazy version of core Event with limited State joined in."""
|
"""A lazy version of core Event with limited State joined in."""
|
||||||
|
|
||||||
|
@ -552,6 +669,9 @@ class LazyEventPartialState:
|
||||||
"entity_id",
|
"entity_id",
|
||||||
"state",
|
"state",
|
||||||
"domain",
|
"domain",
|
||||||
|
"context_id",
|
||||||
|
"context_user_id",
|
||||||
|
"time_fired_minute",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, row):
|
def __init__(self, row):
|
||||||
|
@ -565,11 +685,9 @@ class LazyEventPartialState:
|
||||||
self.entity_id = self._row.entity_id
|
self.entity_id = self._row.entity_id
|
||||||
self.state = self._row.state
|
self.state = self._row.state
|
||||||
self.domain = self._row.domain
|
self.domain = self._row.domain
|
||||||
|
self.context_id = self._row.context_id
|
||||||
@property
|
self.context_user_id = self._row.context_user_id
|
||||||
def context_user_id(self):
|
self.time_fired_minute = self._row.time_fired.minute
|
||||||
"""Context user id of event."""
|
|
||||||
return self._row.context_user_id
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def attributes(self):
|
def attributes(self):
|
||||||
|
@ -594,11 +712,6 @@ class LazyEventPartialState:
|
||||||
self._event_data = json.loads(self._row.event_data)
|
self._event_data = json.loads(self._row.event_data)
|
||||||
return self._event_data
|
return self._event_data
|
||||||
|
|
||||||
@property
|
|
||||||
def time_fired_minute(self):
|
|
||||||
"""Minute the event was fired not converted."""
|
|
||||||
return self._row.time_fired.minute
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def time_fired(self):
|
def time_fired(self):
|
||||||
"""Time event was fired in utc."""
|
"""Time event was fired in utc."""
|
||||||
|
|
|
@ -66,6 +66,9 @@ class _TemplateAttribute:
|
||||||
last_result: Optional[str],
|
last_result: Optional[str],
|
||||||
result: Union[str, TemplateError],
|
result: Union[str, TemplateError],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
if event:
|
||||||
|
self._entity.async_set_context(event.context)
|
||||||
|
|
||||||
if isinstance(result, TemplateError):
|
if isinstance(result, TemplateError):
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"TemplateError('%s') "
|
"TemplateError('%s') "
|
||||||
|
|
|
@ -231,7 +231,7 @@ async def _logbook_filtering(hass, last_changed, last_updated):
|
||||||
|
|
||||||
start = timer()
|
start = timer()
|
||||||
|
|
||||||
list(logbook.humanify(hass, yield_events(event), entity_attr_cache))
|
list(logbook.humanify(hass, yield_events(event), entity_attr_cache, {}))
|
||||||
|
|
||||||
return timer() - start
|
return timer() - start
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ async def test_humanify_alexa_event(hass):
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
entity_attr_cache,
|
entity_attr_cache,
|
||||||
|
{},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1089,6 +1089,7 @@ async def test_logbook_humanify_automation_triggered_event(hass):
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
entity_attr_cache,
|
entity_attr_cache,
|
||||||
|
{},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ async def test_humanify_homekit_changed_event(hass, hk_driver):
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
entity_attr_cache,
|
entity_attr_cache,
|
||||||
|
{},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,16 @@ from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED
|
||||||
from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat
|
from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat
|
||||||
from homeassistant.components.script import EVENT_SCRIPT_STARTED
|
from homeassistant.components.script import EVENT_SCRIPT_STARTED
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
ATTR_DOMAIN,
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
|
ATTR_FRIENDLY_NAME,
|
||||||
ATTR_NAME,
|
ATTR_NAME,
|
||||||
|
ATTR_SERVICE,
|
||||||
CONF_DOMAINS,
|
CONF_DOMAINS,
|
||||||
CONF_ENTITIES,
|
CONF_ENTITIES,
|
||||||
CONF_EXCLUDE,
|
CONF_EXCLUDE,
|
||||||
CONF_INCLUDE,
|
CONF_INCLUDE,
|
||||||
|
EVENT_CALL_SERVICE,
|
||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START,
|
||||||
EVENT_HOMEASSISTANT_STOP,
|
EVENT_HOMEASSISTANT_STOP,
|
||||||
EVENT_STATE_CHANGED,
|
EVENT_STATE_CHANGED,
|
||||||
|
@ -96,7 +100,6 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
events = list(
|
events = list(
|
||||||
logbook._get_events(
|
logbook._get_events(
|
||||||
self.hass,
|
self.hass,
|
||||||
{},
|
|
||||||
dt_util.utcnow() - timedelta(hours=1),
|
dt_util.utcnow() - timedelta(hours=1),
|
||||||
dt_util.utcnow() + timedelta(hours=1),
|
dt_util.utcnow() + timedelta(hours=1),
|
||||||
)
|
)
|
||||||
|
@ -152,7 +155,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
eventC = self.create_state_changed_event(pointC, entity_id, 30)
|
eventC = self.create_state_changed_event(pointC, entity_id, 30)
|
||||||
|
|
||||||
entries = list(
|
entries = list(
|
||||||
logbook.humanify(self.hass, (eventA, eventB, eventC), entity_attr_cache)
|
logbook.humanify(self.hass, (eventA, eventB, eventC), entity_attr_cache, {})
|
||||||
)
|
)
|
||||||
|
|
||||||
assert len(entries) == 2
|
assert len(entries) == 2
|
||||||
|
@ -191,7 +194,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
)
|
)
|
||||||
if logbook._keep_event(self.hass, e, entities_filter)
|
if logbook._keep_event(self.hass, e, entities_filter)
|
||||||
]
|
]
|
||||||
entries = list(logbook.humanify(self.hass, events, entity_attr_cache))
|
entries = list(logbook.humanify(self.hass, events, entity_attr_cache, {}))
|
||||||
|
|
||||||
assert len(entries) == 2
|
assert len(entries) == 2
|
||||||
self.assert_entry(
|
self.assert_entry(
|
||||||
|
@ -229,7 +232,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
)
|
)
|
||||||
if logbook._keep_event(self.hass, e, entities_filter)
|
if logbook._keep_event(self.hass, e, entities_filter)
|
||||||
]
|
]
|
||||||
entries = list(logbook.humanify(self.hass, events, entity_attr_cache))
|
entries = list(logbook.humanify(self.hass, events, entity_attr_cache, {}))
|
||||||
|
|
||||||
assert len(entries) == 2
|
assert len(entries) == 2
|
||||||
self.assert_entry(
|
self.assert_entry(
|
||||||
|
@ -276,7 +279,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
)
|
)
|
||||||
if logbook._keep_event(self.hass, e, entities_filter)
|
if logbook._keep_event(self.hass, e, entities_filter)
|
||||||
]
|
]
|
||||||
entries = list(logbook.humanify(self.hass, events, entity_attr_cache))
|
entries = list(logbook.humanify(self.hass, events, entity_attr_cache, {}))
|
||||||
|
|
||||||
assert len(entries) == 2
|
assert len(entries) == 2
|
||||||
self.assert_entry(
|
self.assert_entry(
|
||||||
|
@ -318,7 +321,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
)
|
)
|
||||||
if logbook._keep_event(self.hass, e, entities_filter)
|
if logbook._keep_event(self.hass, e, entities_filter)
|
||||||
]
|
]
|
||||||
entries = list(logbook.humanify(self.hass, events, entity_attr_cache))
|
entries = list(logbook.humanify(self.hass, events, entity_attr_cache, {}))
|
||||||
|
|
||||||
assert len(entries) == 2
|
assert len(entries) == 2
|
||||||
self.assert_entry(
|
self.assert_entry(
|
||||||
|
@ -364,7 +367,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
)
|
)
|
||||||
if logbook._keep_event(self.hass, e, entities_filter)
|
if logbook._keep_event(self.hass, e, entities_filter)
|
||||||
]
|
]
|
||||||
entries = list(logbook.humanify(self.hass, events, entity_attr_cache))
|
entries = list(logbook.humanify(self.hass, events, entity_attr_cache, {}))
|
||||||
|
|
||||||
assert len(entries) == 3
|
assert len(entries) == 3
|
||||||
self.assert_entry(
|
self.assert_entry(
|
||||||
|
@ -418,7 +421,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
)
|
)
|
||||||
if logbook._keep_event(self.hass, e, entities_filter)
|
if logbook._keep_event(self.hass, e, entities_filter)
|
||||||
]
|
]
|
||||||
entries = list(logbook.humanify(self.hass, events, entity_attr_cache))
|
entries = list(logbook.humanify(self.hass, events, entity_attr_cache, {}))
|
||||||
|
|
||||||
assert len(entries) == 4
|
assert len(entries) == 4
|
||||||
self.assert_entry(
|
self.assert_entry(
|
||||||
|
@ -475,7 +478,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
)
|
)
|
||||||
if logbook._keep_event(self.hass, e, entities_filter)
|
if logbook._keep_event(self.hass, e, entities_filter)
|
||||||
]
|
]
|
||||||
entries = list(logbook.humanify(self.hass, events, entity_attr_cache))
|
entries = list(logbook.humanify(self.hass, events, entity_attr_cache, {}))
|
||||||
|
|
||||||
assert len(entries) == 5
|
assert len(entries) == 5
|
||||||
self.assert_entry(
|
self.assert_entry(
|
||||||
|
@ -549,7 +552,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
)
|
)
|
||||||
if logbook._keep_event(self.hass, e, entities_filter)
|
if logbook._keep_event(self.hass, e, entities_filter)
|
||||||
]
|
]
|
||||||
entries = list(logbook.humanify(self.hass, events, entity_attr_cache))
|
entries = list(logbook.humanify(self.hass, events, entity_attr_cache, {}))
|
||||||
|
|
||||||
assert len(entries) == 6
|
assert len(entries) == 6
|
||||||
self.assert_entry(
|
self.assert_entry(
|
||||||
|
@ -585,6 +588,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
MockLazyEventPartialState(EVENT_HOMEASSISTANT_START),
|
MockLazyEventPartialState(EVENT_HOMEASSISTANT_START),
|
||||||
),
|
),
|
||||||
entity_attr_cache,
|
entity_attr_cache,
|
||||||
|
{},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -607,6 +611,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
self.create_state_changed_event(pointA, entity_id, 10),
|
self.create_state_changed_event(pointA, entity_id, 10),
|
||||||
),
|
),
|
||||||
entity_attr_cache,
|
entity_attr_cache,
|
||||||
|
{},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -628,21 +633,21 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
# message for a device state change
|
# message for a device state change
|
||||||
eventA = self.create_state_changed_event(pointA, "switch.bla", 10)
|
eventA = self.create_state_changed_event(pointA, "switch.bla", 10)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "changed to 10"
|
assert message == "changed to 10"
|
||||||
|
|
||||||
# message for a switch turned on
|
# message for a switch turned on
|
||||||
eventA = self.create_state_changed_event(pointA, "switch.bla", STATE_ON)
|
eventA = self.create_state_changed_event(pointA, "switch.bla", STATE_ON)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "turned on"
|
assert message == "turned on"
|
||||||
|
|
||||||
# message for a switch turned off
|
# message for a switch turned off
|
||||||
eventA = self.create_state_changed_event(pointA, "switch.bla", STATE_OFF)
|
eventA = self.create_state_changed_event(pointA, "switch.bla", STATE_OFF)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "turned off"
|
assert message == "turned off"
|
||||||
|
|
||||||
|
@ -656,14 +661,14 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "device_tracker.john", STATE_NOT_HOME
|
pointA, "device_tracker.john", STATE_NOT_HOME
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is away"
|
assert message == "is away"
|
||||||
|
|
||||||
# message for a device tracker "home" state
|
# message for a device tracker "home" state
|
||||||
eventA = self.create_state_changed_event(pointA, "device_tracker.john", "work")
|
eventA = self.create_state_changed_event(pointA, "device_tracker.john", "work")
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is at work"
|
assert message == "is at work"
|
||||||
|
|
||||||
|
@ -675,14 +680,14 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
# message for a device tracker "not home" state
|
# message for a device tracker "not home" state
|
||||||
eventA = self.create_state_changed_event(pointA, "person.john", STATE_NOT_HOME)
|
eventA = self.create_state_changed_event(pointA, "person.john", STATE_NOT_HOME)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is away"
|
assert message == "is away"
|
||||||
|
|
||||||
# message for a device tracker "home" state
|
# message for a device tracker "home" state
|
||||||
eventA = self.create_state_changed_event(pointA, "person.john", "work")
|
eventA = self.create_state_changed_event(pointA, "person.john", "work")
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is at work"
|
assert message == "is at work"
|
||||||
|
|
||||||
|
@ -696,7 +701,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "sun.sun", sun.STATE_ABOVE_HORIZON
|
pointA, "sun.sun", sun.STATE_ABOVE_HORIZON
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "has risen"
|
assert message == "has risen"
|
||||||
|
|
||||||
|
@ -705,7 +710,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "sun.sun", sun.STATE_BELOW_HORIZON
|
pointA, "sun.sun", sun.STATE_BELOW_HORIZON
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "has set"
|
assert message == "has set"
|
||||||
|
|
||||||
|
@ -720,7 +725,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.battery", STATE_ON, attributes
|
pointA, "binary_sensor.battery", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is low"
|
assert message == "is low"
|
||||||
|
|
||||||
|
@ -729,7 +734,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.battery", STATE_OFF, attributes
|
pointA, "binary_sensor.battery", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is normal"
|
assert message == "is normal"
|
||||||
|
|
||||||
|
@ -744,7 +749,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.connectivity", STATE_ON, attributes
|
pointA, "binary_sensor.connectivity", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is connected"
|
assert message == "is connected"
|
||||||
|
|
||||||
|
@ -753,7 +758,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.connectivity", STATE_OFF, attributes
|
pointA, "binary_sensor.connectivity", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is disconnected"
|
assert message == "is disconnected"
|
||||||
|
|
||||||
|
@ -768,7 +773,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.door", STATE_ON, attributes
|
pointA, "binary_sensor.door", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is opened"
|
assert message == "is opened"
|
||||||
|
|
||||||
|
@ -777,7 +782,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.door", STATE_OFF, attributes
|
pointA, "binary_sensor.door", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is closed"
|
assert message == "is closed"
|
||||||
|
|
||||||
|
@ -792,7 +797,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.garage_door", STATE_ON, attributes
|
pointA, "binary_sensor.garage_door", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is opened"
|
assert message == "is opened"
|
||||||
|
|
||||||
|
@ -801,7 +806,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.garage_door", STATE_OFF, attributes
|
pointA, "binary_sensor.garage_door", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is closed"
|
assert message == "is closed"
|
||||||
|
|
||||||
|
@ -816,7 +821,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.opening", STATE_ON, attributes
|
pointA, "binary_sensor.opening", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is opened"
|
assert message == "is opened"
|
||||||
|
|
||||||
|
@ -825,7 +830,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.opening", STATE_OFF, attributes
|
pointA, "binary_sensor.opening", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is closed"
|
assert message == "is closed"
|
||||||
|
|
||||||
|
@ -840,7 +845,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.window", STATE_ON, attributes
|
pointA, "binary_sensor.window", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is opened"
|
assert message == "is opened"
|
||||||
|
|
||||||
|
@ -849,7 +854,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.window", STATE_OFF, attributes
|
pointA, "binary_sensor.window", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is closed"
|
assert message == "is closed"
|
||||||
|
|
||||||
|
@ -864,7 +869,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.lock", STATE_ON, attributes
|
pointA, "binary_sensor.lock", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is unlocked"
|
assert message == "is unlocked"
|
||||||
|
|
||||||
|
@ -873,7 +878,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.lock", STATE_OFF, attributes
|
pointA, "binary_sensor.lock", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is locked"
|
assert message == "is locked"
|
||||||
|
|
||||||
|
@ -888,7 +893,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.plug", STATE_ON, attributes
|
pointA, "binary_sensor.plug", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is plugged in"
|
assert message == "is plugged in"
|
||||||
|
|
||||||
|
@ -897,7 +902,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.plug", STATE_OFF, attributes
|
pointA, "binary_sensor.plug", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is unplugged"
|
assert message == "is unplugged"
|
||||||
|
|
||||||
|
@ -912,7 +917,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.presence", STATE_ON, attributes
|
pointA, "binary_sensor.presence", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is at home"
|
assert message == "is at home"
|
||||||
|
|
||||||
|
@ -921,7 +926,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.presence", STATE_OFF, attributes
|
pointA, "binary_sensor.presence", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is away"
|
assert message == "is away"
|
||||||
|
|
||||||
|
@ -936,7 +941,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.safety", STATE_ON, attributes
|
pointA, "binary_sensor.safety", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is unsafe"
|
assert message == "is unsafe"
|
||||||
|
|
||||||
|
@ -945,7 +950,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.safety", STATE_OFF, attributes
|
pointA, "binary_sensor.safety", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "is safe"
|
assert message == "is safe"
|
||||||
|
|
||||||
|
@ -960,7 +965,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.cold", STATE_ON, attributes
|
pointA, "binary_sensor.cold", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected cold"
|
assert message == "detected cold"
|
||||||
|
|
||||||
|
@ -969,7 +974,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.cold", STATE_OFF, attributes
|
pointA, "binary_sensor.cold", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no cold detected)"
|
assert message == "cleared (no cold detected)"
|
||||||
|
|
||||||
|
@ -984,7 +989,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.gas", STATE_ON, attributes
|
pointA, "binary_sensor.gas", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected gas"
|
assert message == "detected gas"
|
||||||
|
|
||||||
|
@ -993,7 +998,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.gas", STATE_OFF, attributes
|
pointA, "binary_sensor.gas", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no gas detected)"
|
assert message == "cleared (no gas detected)"
|
||||||
|
|
||||||
|
@ -1008,7 +1013,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.heat", STATE_ON, attributes
|
pointA, "binary_sensor.heat", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected heat"
|
assert message == "detected heat"
|
||||||
|
|
||||||
|
@ -1017,7 +1022,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.heat", STATE_OFF, attributes
|
pointA, "binary_sensor.heat", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no heat detected)"
|
assert message == "cleared (no heat detected)"
|
||||||
|
|
||||||
|
@ -1032,7 +1037,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.light", STATE_ON, attributes
|
pointA, "binary_sensor.light", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected light"
|
assert message == "detected light"
|
||||||
|
|
||||||
|
@ -1041,7 +1046,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.light", STATE_OFF, attributes
|
pointA, "binary_sensor.light", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no light detected)"
|
assert message == "cleared (no light detected)"
|
||||||
|
|
||||||
|
@ -1056,7 +1061,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.moisture", STATE_ON, attributes
|
pointA, "binary_sensor.moisture", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected moisture"
|
assert message == "detected moisture"
|
||||||
|
|
||||||
|
@ -1065,7 +1070,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.moisture", STATE_OFF, attributes
|
pointA, "binary_sensor.moisture", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no moisture detected)"
|
assert message == "cleared (no moisture detected)"
|
||||||
|
|
||||||
|
@ -1080,7 +1085,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.motion", STATE_ON, attributes
|
pointA, "binary_sensor.motion", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected motion"
|
assert message == "detected motion"
|
||||||
|
|
||||||
|
@ -1089,7 +1094,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.motion", STATE_OFF, attributes
|
pointA, "binary_sensor.motion", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no motion detected)"
|
assert message == "cleared (no motion detected)"
|
||||||
|
|
||||||
|
@ -1104,7 +1109,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.occupancy", STATE_ON, attributes
|
pointA, "binary_sensor.occupancy", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected occupancy"
|
assert message == "detected occupancy"
|
||||||
|
|
||||||
|
@ -1113,7 +1118,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.occupancy", STATE_OFF, attributes
|
pointA, "binary_sensor.occupancy", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no occupancy detected)"
|
assert message == "cleared (no occupancy detected)"
|
||||||
|
|
||||||
|
@ -1128,7 +1133,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.power", STATE_ON, attributes
|
pointA, "binary_sensor.power", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected power"
|
assert message == "detected power"
|
||||||
|
|
||||||
|
@ -1137,7 +1142,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.power", STATE_OFF, attributes
|
pointA, "binary_sensor.power", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no power detected)"
|
assert message == "cleared (no power detected)"
|
||||||
|
|
||||||
|
@ -1152,7 +1157,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.problem", STATE_ON, attributes
|
pointA, "binary_sensor.problem", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected problem"
|
assert message == "detected problem"
|
||||||
|
|
||||||
|
@ -1161,7 +1166,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.problem", STATE_OFF, attributes
|
pointA, "binary_sensor.problem", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no problem detected)"
|
assert message == "cleared (no problem detected)"
|
||||||
|
|
||||||
|
@ -1176,7 +1181,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.smoke", STATE_ON, attributes
|
pointA, "binary_sensor.smoke", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected smoke"
|
assert message == "detected smoke"
|
||||||
|
|
||||||
|
@ -1185,7 +1190,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.smoke", STATE_OFF, attributes
|
pointA, "binary_sensor.smoke", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no smoke detected)"
|
assert message == "cleared (no smoke detected)"
|
||||||
|
|
||||||
|
@ -1200,7 +1205,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.sound", STATE_ON, attributes
|
pointA, "binary_sensor.sound", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected sound"
|
assert message == "detected sound"
|
||||||
|
|
||||||
|
@ -1209,7 +1214,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.sound", STATE_OFF, attributes
|
pointA, "binary_sensor.sound", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no sound detected)"
|
assert message == "cleared (no sound detected)"
|
||||||
|
|
||||||
|
@ -1224,7 +1229,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.vibration", STATE_ON, attributes
|
pointA, "binary_sensor.vibration", STATE_ON, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "detected vibration"
|
assert message == "detected vibration"
|
||||||
|
|
||||||
|
@ -1233,7 +1238,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
pointA, "binary_sensor.vibration", STATE_OFF, attributes
|
pointA, "binary_sensor.vibration", STATE_OFF, attributes
|
||||||
)
|
)
|
||||||
message = logbook._entry_message_from_event(
|
message = logbook._entry_message_from_event(
|
||||||
self.hass, eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
eventA.entity_id, eventA.domain, eventA, entity_attr_cache
|
||||||
)
|
)
|
||||||
assert message == "cleared (no vibration detected)"
|
assert message == "cleared (no vibration detected)"
|
||||||
|
|
||||||
|
@ -1258,6 +1263,7 @@ class TestComponentLogbook(unittest.TestCase):
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
entity_attr_cache,
|
entity_attr_cache,
|
||||||
|
{},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1652,7 +1658,6 @@ async def test_logbook_entity_filter_with_automations(hass, hass_client):
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
json_dict = await response.json()
|
json_dict = await response.json()
|
||||||
|
|
||||||
assert len(json_dict) == 5
|
|
||||||
assert json_dict[0]["entity_id"] == entity_id_test
|
assert json_dict[0]["entity_id"] == entity_id_test
|
||||||
assert json_dict[1]["entity_id"] == entity_id_second
|
assert json_dict[1]["entity_id"] == entity_id_second
|
||||||
assert json_dict[2]["entity_id"] == "automation.mock_automation"
|
assert json_dict[2]["entity_id"] == "automation.mock_automation"
|
||||||
|
@ -1837,6 +1842,245 @@ async def test_exclude_attribute_changes(hass, hass_client):
|
||||||
assert response_json[2]["entity_id"] == "light.kitchen"
|
assert response_json[2]["entity_id"] == "light.kitchen"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_logbook_entity_context_id(hass, hass_client):
|
||||||
|
"""Test the logbook view with end_time and entity with automations and scripts."""
|
||||||
|
await hass.async_add_executor_job(init_recorder_component, hass)
|
||||||
|
await async_setup_component(hass, "logbook", {})
|
||||||
|
await async_setup_component(hass, "automation", {})
|
||||||
|
await async_setup_component(hass, "script", {})
|
||||||
|
|
||||||
|
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
||||||
|
|
||||||
|
context = ha.Context(
|
||||||
|
id="ac5bd62de45711eaaeb351041eec8dd9",
|
||||||
|
user_id="b400facee45711eaa9308bfd3d19e474",
|
||||||
|
)
|
||||||
|
|
||||||
|
# An Automation
|
||||||
|
automation_entity_id_test = "automation.alarm"
|
||||||
|
hass.bus.async_fire(
|
||||||
|
EVENT_AUTOMATION_TRIGGERED,
|
||||||
|
{ATTR_NAME: "Mock automation", ATTR_ENTITY_ID: automation_entity_id_test},
|
||||||
|
context=context,
|
||||||
|
)
|
||||||
|
hass.bus.async_fire(
|
||||||
|
EVENT_SCRIPT_STARTED,
|
||||||
|
{ATTR_NAME: "Mock script", ATTR_ENTITY_ID: "script.mock_script"},
|
||||||
|
context=context,
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
automation_entity_id_test,
|
||||||
|
STATE_ON,
|
||||||
|
{ATTR_FRIENDLY_NAME: "Alarm Automation"},
|
||||||
|
context=context,
|
||||||
|
)
|
||||||
|
|
||||||
|
entity_id_test = "alarm_control_panel.area_001"
|
||||||
|
hass.states.async_set(entity_id_test, STATE_OFF, context=context)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
hass.states.async_set(entity_id_test, STATE_ON, context=context)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
entity_id_second = "alarm_control_panel.area_002"
|
||||||
|
hass.states.async_set(entity_id_second, STATE_OFF, context=context)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
hass.states.async_set(entity_id_second, STATE_ON, context=context)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
await hass.async_add_job(
|
||||||
|
logbook.log_entry,
|
||||||
|
hass,
|
||||||
|
"mock_name",
|
||||||
|
"mock_message",
|
||||||
|
"alarm_control_panel",
|
||||||
|
"alarm_control_panel.area_003",
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
await hass.async_add_job(
|
||||||
|
logbook.log_entry,
|
||||||
|
hass,
|
||||||
|
"mock_name",
|
||||||
|
"mock_message",
|
||||||
|
"homeassistant",
|
||||||
|
None,
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# A service call
|
||||||
|
light_turn_off_service_context = ha.Context(
|
||||||
|
id="9c5bd62de45711eaaeb351041eec8dd9",
|
||||||
|
user_id="9400facee45711eaa9308bfd3d19e474",
|
||||||
|
)
|
||||||
|
hass.states.async_set("light.switch", STATE_ON)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
hass.bus.async_fire(
|
||||||
|
EVENT_CALL_SERVICE,
|
||||||
|
{
|
||||||
|
ATTR_DOMAIN: "light",
|
||||||
|
ATTR_SERVICE: "turn_off",
|
||||||
|
ATTR_ENTITY_ID: "light.switch",
|
||||||
|
},
|
||||||
|
context=light_turn_off_service_context,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
hass.states.async_set(
|
||||||
|
"light.switch", STATE_OFF, context=light_turn_off_service_context
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
await hass.async_add_job(trigger_db_commit, hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
||||||
|
|
||||||
|
client = await hass_client()
|
||||||
|
|
||||||
|
# Today time 00:00:00
|
||||||
|
start = dt_util.utcnow().date()
|
||||||
|
start_date = datetime(start.year, start.month, start.day)
|
||||||
|
|
||||||
|
# Test today entries with filter by end_time
|
||||||
|
end_time = start + timedelta(hours=24)
|
||||||
|
response = await client.get(
|
||||||
|
f"/api/logbook/{start_date.isoformat()}?end_time={end_time}"
|
||||||
|
)
|
||||||
|
assert response.status == 200
|
||||||
|
json_dict = await response.json()
|
||||||
|
|
||||||
|
assert json_dict[0]["entity_id"] == "automation.alarm"
|
||||||
|
assert "context_entity_id" not in json_dict[0]
|
||||||
|
assert json_dict[0]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474"
|
||||||
|
|
||||||
|
assert json_dict[1]["entity_id"] == "script.mock_script"
|
||||||
|
assert json_dict[1]["context_event_type"] == "automation_triggered"
|
||||||
|
assert json_dict[1]["context_entity_id"] == "automation.alarm"
|
||||||
|
assert json_dict[1]["context_entity_id_name"] == "Alarm Automation"
|
||||||
|
assert json_dict[1]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474"
|
||||||
|
|
||||||
|
assert json_dict[2]["entity_id"] == entity_id_test
|
||||||
|
assert json_dict[2]["context_event_type"] == "automation_triggered"
|
||||||
|
assert json_dict[2]["context_entity_id"] == "automation.alarm"
|
||||||
|
assert json_dict[2]["context_entity_id_name"] == "Alarm Automation"
|
||||||
|
assert json_dict[2]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474"
|
||||||
|
|
||||||
|
assert json_dict[3]["entity_id"] == entity_id_second
|
||||||
|
assert json_dict[3]["context_event_type"] == "automation_triggered"
|
||||||
|
assert json_dict[3]["context_entity_id"] == "automation.alarm"
|
||||||
|
assert json_dict[3]["context_entity_id_name"] == "Alarm Automation"
|
||||||
|
assert json_dict[3]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474"
|
||||||
|
|
||||||
|
assert json_dict[4]["domain"] == "homeassistant"
|
||||||
|
|
||||||
|
assert json_dict[5]["entity_id"] == "alarm_control_panel.area_003"
|
||||||
|
assert json_dict[5]["context_event_type"] == "automation_triggered"
|
||||||
|
assert json_dict[5]["context_entity_id"] == "automation.alarm"
|
||||||
|
assert json_dict[5]["domain"] == "alarm_control_panel"
|
||||||
|
assert json_dict[5]["context_entity_id_name"] == "Alarm Automation"
|
||||||
|
assert json_dict[5]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474"
|
||||||
|
|
||||||
|
assert json_dict[6]["domain"] == "homeassistant"
|
||||||
|
assert json_dict[6]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474"
|
||||||
|
|
||||||
|
assert json_dict[7]["entity_id"] == "light.switch"
|
||||||
|
assert json_dict[7]["context_event_type"] == "call_service"
|
||||||
|
assert json_dict[7]["context_domain"] == "light"
|
||||||
|
assert json_dict[7]["context_service"] == "turn_off"
|
||||||
|
assert json_dict[7]["domain"] == "light"
|
||||||
|
assert json_dict[7]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_logbook_context_from_template(hass, hass_client):
|
||||||
|
"""Test the logbook view with end_time and entity with automations and scripts."""
|
||||||
|
await hass.async_add_executor_job(init_recorder_component, hass)
|
||||||
|
await async_setup_component(hass, "logbook", {})
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
"switch",
|
||||||
|
{
|
||||||
|
"switch": {
|
||||||
|
"platform": "template",
|
||||||
|
"switches": {
|
||||||
|
"test_template_switch": {
|
||||||
|
"value_template": "{{ states.switch.test_state.state }}",
|
||||||
|
"turn_on": {
|
||||||
|
"service": "switch.turn_on",
|
||||||
|
"entity_id": "switch.test_state",
|
||||||
|
},
|
||||||
|
"turn_off": {
|
||||||
|
"service": "switch.turn_off",
|
||||||
|
"entity_id": "switch.test_state",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# Entity added (should not be logged)
|
||||||
|
hass.states.async_set("switch.test_state", STATE_ON)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# First state change (should be logged)
|
||||||
|
hass.states.async_set("switch.test_state", STATE_OFF)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
switch_turn_off_context = ha.Context(
|
||||||
|
id="9c5bd62de45711eaaeb351041eec8dd9",
|
||||||
|
user_id="9400facee45711eaa9308bfd3d19e474",
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"switch.test_state", STATE_ON, context=switch_turn_off_context
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
await hass.async_add_job(trigger_db_commit, hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
||||||
|
|
||||||
|
client = await hass_client()
|
||||||
|
|
||||||
|
# Today time 00:00:00
|
||||||
|
start = dt_util.utcnow().date()
|
||||||
|
start_date = datetime(start.year, start.month, start.day)
|
||||||
|
|
||||||
|
# Test today entries with filter by end_time
|
||||||
|
end_time = start + timedelta(hours=24)
|
||||||
|
response = await client.get(
|
||||||
|
f"/api/logbook/{start_date.isoformat()}?end_time={end_time}"
|
||||||
|
)
|
||||||
|
assert response.status == 200
|
||||||
|
json_dict = await response.json()
|
||||||
|
|
||||||
|
assert json_dict[0]["domain"] == "homeassistant"
|
||||||
|
assert "context_entity_id" not in json_dict[0]
|
||||||
|
|
||||||
|
assert json_dict[1]["entity_id"] == "switch.test_template_switch"
|
||||||
|
|
||||||
|
assert json_dict[2]["entity_id"] == "switch.test_state"
|
||||||
|
|
||||||
|
assert json_dict[3]["entity_id"] == "switch.test_template_switch"
|
||||||
|
assert json_dict[3]["context_entity_id"] == "switch.test_state"
|
||||||
|
assert json_dict[3]["context_entity_id_name"] == "test state"
|
||||||
|
|
||||||
|
assert json_dict[4]["entity_id"] == "switch.test_state"
|
||||||
|
assert json_dict[4]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474"
|
||||||
|
|
||||||
|
assert json_dict[5]["entity_id"] == "switch.test_template_switch"
|
||||||
|
assert json_dict[5]["context_entity_id"] == "switch.test_state"
|
||||||
|
assert json_dict[5]["context_entity_id_name"] == "test state"
|
||||||
|
assert json_dict[5]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474"
|
||||||
|
|
||||||
|
|
||||||
class MockLazyEventPartialState(ha.Event):
|
class MockLazyEventPartialState(ha.Event):
|
||||||
"""Minimal mock of a Lazy event."""
|
"""Minimal mock of a Lazy event."""
|
||||||
|
|
||||||
|
@ -1850,6 +2094,11 @@ class MockLazyEventPartialState(ha.Event):
|
||||||
"""Context user id of event."""
|
"""Context user id of event."""
|
||||||
return self.context.user_id
|
return self.context.user_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def context_id(self):
|
||||||
|
"""Context id of event."""
|
||||||
|
return self.context.id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def time_fired_isoformat(self):
|
def time_fired_isoformat(self):
|
||||||
"""Time event was fired in utc isoformat."""
|
"""Time event was fired in utc isoformat."""
|
||||||
|
|
|
@ -516,6 +516,7 @@ async def test_logbook_humanify_script_started_event(hass):
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
entity_attr_cache,
|
entity_attr_cache,
|
||||||
|
{},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue