mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 12:59:34 +00:00
Use parent_id to find cause of logbook events with new contexts (#44416)
* Use parent_id to find cause of events with new contexts When looking up the causing event for logbook display, use the `parent_id` of the current context if the current context just points back to the current event. This now shows in the logbook the cause of an event in the case that a component has created a new context from an existing context and tied them together via the `Context.parent_id`. * Fix exception when parent event not available * Use async_Log_entry to avoid jump into executor
This commit is contained in:
@@ -282,6 +282,7 @@ def create_state_changed_event_from_old_new(
|
||||
"time_fired"
|
||||
"context_id"
|
||||
"context_user_id"
|
||||
"context_parent_id"
|
||||
"state"
|
||||
"entity_id"
|
||||
"domain"
|
||||
@@ -300,6 +301,7 @@ def create_state_changed_event_from_old_new(
|
||||
row.domain = entity_id and ha.split_entity_id(entity_id)[0]
|
||||
row.context_id = None
|
||||
row.context_user_id = None
|
||||
row.context_parent_id = None
|
||||
row.old_state_id = old_state and 1
|
||||
row.state_id = new_state and 1
|
||||
return logbook.LazyEventPartialState(row)
|
||||
@@ -946,6 +948,187 @@ async def test_logbook_entity_context_id(hass, hass_client):
|
||||
assert json_dict[7]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474"
|
||||
|
||||
|
||||
async def test_logbook_entity_context_parent_id(hass, hass_client):
|
||||
"""Test the logbook view links events via context parent_id."""
|
||||
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_executor_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
||||
|
||||
context = ha.Context(
|
||||
id="ac5bd62de45711eaaeb351041eec8dd9",
|
||||
user_id="b400facee45711eaa9308bfd3d19e474",
|
||||
)
|
||||
|
||||
# An Automation triggering scripts with a new context
|
||||
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,
|
||||
)
|
||||
|
||||
child_context = ha.Context(
|
||||
id="2798bfedf8234b5e9f4009c91f48f30c",
|
||||
parent_id="ac5bd62de45711eaaeb351041eec8dd9",
|
||||
user_id="b400facee45711eaa9308bfd3d19e474",
|
||||
)
|
||||
hass.bus.async_fire(
|
||||
EVENT_SCRIPT_STARTED,
|
||||
{ATTR_NAME: "Mock script", ATTR_ENTITY_ID: "script.mock_script"},
|
||||
context=child_context,
|
||||
)
|
||||
hass.states.async_set(
|
||||
automation_entity_id_test,
|
||||
STATE_ON,
|
||||
{ATTR_FRIENDLY_NAME: "Alarm Automation"},
|
||||
context=child_context,
|
||||
)
|
||||
|
||||
entity_id_test = "alarm_control_panel.area_001"
|
||||
hass.states.async_set(entity_id_test, STATE_OFF, context=child_context)
|
||||
await hass.async_block_till_done()
|
||||
hass.states.async_set(entity_id_test, STATE_ON, context=child_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=child_context)
|
||||
await hass.async_block_till_done()
|
||||
hass.states.async_set(entity_id_second, STATE_ON, context=child_context)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
logbook.async_log_entry(
|
||||
hass,
|
||||
"mock_name",
|
||||
"mock_message",
|
||||
"alarm_control_panel",
|
||||
"alarm_control_panel.area_003",
|
||||
child_context,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
logbook.async_log_entry(
|
||||
hass,
|
||||
"mock_name",
|
||||
"mock_message",
|
||||
"homeassistant",
|
||||
None,
|
||||
child_context,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# A state change via service call with the script as the parent
|
||||
light_turn_off_service_context = ha.Context(
|
||||
id="9c5bd62de45711eaaeb351041eec8dd9",
|
||||
parent_id="2798bfedf8234b5e9f4009c91f48f30c",
|
||||
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()
|
||||
|
||||
# An event with a parent event, but the parent event isn't available
|
||||
missing_parent_context = ha.Context(
|
||||
id="fc40b9a0d1f246f98c34b33c76228ee6",
|
||||
parent_id="c8ce515fe58e442f8664246c65ed964f",
|
||||
user_id="485cacf93ef84d25a99ced3126b921d2",
|
||||
)
|
||||
logbook.async_log_entry(
|
||||
hass,
|
||||
"mock_name",
|
||||
"mock_message",
|
||||
"alarm_control_panel",
|
||||
"alarm_control_panel.area_009",
|
||||
missing_parent_context,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.async_add_executor_job(trigger_db_commit, hass)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_add_executor_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"
|
||||
|
||||
# New context, so this looks to be triggered by the Alarm Automation
|
||||
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"] == "script_started"
|
||||
assert json_dict[2]["context_entity_id"] == "script.mock_script"
|
||||
assert json_dict[2]["context_entity_id_name"] == "mock script"
|
||||
assert json_dict[2]["context_user_id"] == "b400facee45711eaa9308bfd3d19e474"
|
||||
|
||||
assert json_dict[3]["entity_id"] == entity_id_second
|
||||
assert json_dict[3]["context_event_type"] == "script_started"
|
||||
assert json_dict[3]["context_entity_id"] == "script.mock_script"
|
||||
assert json_dict[3]["context_entity_id_name"] == "mock script"
|
||||
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"] == "script_started"
|
||||
assert json_dict[5]["context_entity_id"] == "script.mock_script"
|
||||
assert json_dict[5]["domain"] == "alarm_control_panel"
|
||||
assert json_dict[5]["context_entity_id_name"] == "mock script"
|
||||
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]["context_user_id"] == "9400facee45711eaa9308bfd3d19e474"
|
||||
|
||||
assert json_dict[8]["entity_id"] == "alarm_control_panel.area_009"
|
||||
assert json_dict[8]["domain"] == "alarm_control_panel"
|
||||
assert "context_event_type" not in json_dict[8]
|
||||
assert "context_entity_id" not in json_dict[8]
|
||||
assert "context_entity_id_name" not in json_dict[8]
|
||||
assert json_dict[8]["context_user_id"] == "485cacf93ef84d25a99ced3126b921d2"
|
||||
|
||||
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user