diff --git a/homeassistant/components/arcam_fmj/device_trigger.py b/homeassistant/components/arcam_fmj/device_trigger.py index 549b4cf4f82..40feb63f9f3 100644 --- a/homeassistant/components/arcam_fmj/device_trigger.py +++ b/homeassistant/components/arcam_fmj/device_trigger.py @@ -59,11 +59,16 @@ async def async_attach_trigger( config = TRIGGER_SCHEMA(config) if config[CONF_TYPE] == "turn_on": + entity_id = config[CONF_ENTITY_ID] @callback def _handle_event(event: Event): - if event.data[ATTR_ENTITY_ID] == config[CONF_ENTITY_ID]: - hass.async_run_job(action({"trigger": config}, context=event.context)) + if event.data[ATTR_ENTITY_ID] == entity_id: + hass.async_run_job( + action, + {"trigger": {**config, "description": f"{DOMAIN} - {entity_id}"}}, + event.context, + ) return hass.bus.async_listen(EVENT_TURN_ON, _handle_event) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 970653cd4df..5d4c4c43b06 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -81,6 +81,7 @@ EVENT_AUTOMATION_RELOADED = "automation_reloaded" EVENT_AUTOMATION_TRIGGERED = "automation_triggered" ATTR_LAST_TRIGGERED = "last_triggered" +ATTR_SOURCE = "source" ATTR_VARIABLES = "variables" SERVICE_TRIGGER = "trigger" @@ -396,10 +397,14 @@ class AutomationEntity(ToggleEntity, RestoreEntity): self.async_set_context(trigger_context) self._last_triggered = utcnow() self.async_write_ha_state() + event_data = { + ATTR_NAME: self._name, + ATTR_ENTITY_ID: self.entity_id, + } + if "trigger" in variables and "description" in variables["trigger"]: + event_data[ATTR_SOURCE] = variables["trigger"]["description"] self.hass.bus.async_fire( - EVENT_AUTOMATION_TRIGGERED, - {ATTR_NAME: self._name, ATTR_ENTITY_ID: self.entity_id}, - context=trigger_context, + EVENT_AUTOMATION_TRIGGERED, event_data, context=trigger_context ) self._logger.info("Executing %s", self._name) diff --git a/homeassistant/components/automation/logbook.py b/homeassistant/components/automation/logbook.py index 2e3ad2475fc..cc44296eaa6 100644 --- a/homeassistant/components/automation/logbook.py +++ b/homeassistant/components/automation/logbook.py @@ -2,7 +2,7 @@ from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME from homeassistant.core import callback -from . import DOMAIN, EVENT_AUTOMATION_TRIGGERED +from . import ATTR_SOURCE, DOMAIN, EVENT_AUTOMATION_TRIGGERED @callback @@ -12,9 +12,13 @@ def async_describe_events(hass, async_describe_event): # type: ignore @callback def async_describe_logbook_event(event): # type: ignore """Describe a logbook event.""" + message = "has been triggered" + if ATTR_SOURCE in event.data: + message = f"{message} by {event.data[ATTR_SOURCE]}" return { "name": event.data.get(ATTR_NAME), - "message": "has been triggered", + "message": message, + "source": event.data.get(ATTR_SOURCE), "entity_id": event.data.get(ATTR_ENTITY_ID), } diff --git a/homeassistant/components/geo_location/trigger.py b/homeassistant/components/geo_location/trigger.py index 92094a751a0..dc8879b51e5 100644 --- a/homeassistant/components/geo_location/trigger.py +++ b/homeassistant/components/geo_location/trigger.py @@ -67,20 +67,20 @@ async def async_attach_trigger(hass, config, action, automation_info): and not to_match ): hass.async_run_job( - action( - { - "trigger": { - "platform": "geo_location", - "source": source, - "entity_id": event.data.get("entity_id"), - "from_state": from_state, - "to_state": to_state, - "zone": zone_state, - "event": trigger_event, - } - }, - context=event.context, - ) + action, + { + "trigger": { + "platform": "geo_location", + "source": source, + "entity_id": event.data.get("entity_id"), + "from_state": from_state, + "to_state": to_state, + "zone": zone_state, + "event": trigger_event, + "description": f"geo_location - {source}", + } + }, + event.context, ) return hass.bus.async_listen(EVENT_STATE_CHANGED, state_change_listener) diff --git a/homeassistant/components/homeassistant/triggers/event.py b/homeassistant/components/homeassistant/triggers/event.py index 2c247280e06..8fa9207c3b0 100644 --- a/homeassistant/components/homeassistant/triggers/event.py +++ b/homeassistant/components/homeassistant/triggers/event.py @@ -48,7 +48,13 @@ async def async_attach_trigger( hass.async_run_job( action, - {"trigger": {"platform": platform_type, "event": event}}, + { + "trigger": { + "platform": platform_type, + "event": event, + "description": f"event '{event.event_type}'", + } + }, event.context, ) diff --git a/homeassistant/components/homeassistant/triggers/homeassistant.py b/homeassistant/components/homeassistant/triggers/homeassistant.py index 6ccb54d034e..baca145e19c 100644 --- a/homeassistant/components/homeassistant/triggers/homeassistant.py +++ b/homeassistant/components/homeassistant/triggers/homeassistant.py @@ -31,7 +31,13 @@ async def async_attach_trigger(hass, config, action, automation_info): """Execute when Home Assistant is shutting down.""" hass.async_run_job( action, - {"trigger": {"platform": "homeassistant", "event": event}}, + { + "trigger": { + "platform": "homeassistant", + "event": event, + "description": "Home Assistant stopping", + } + }, event.context, ) @@ -41,7 +47,14 @@ async def async_attach_trigger(hass, config, action, automation_info): # Check state because a config reload shouldn't trigger it. if automation_info["home_assistant_start"]: hass.async_run_job( - action({"trigger": {"platform": "homeassistant", "event": event}}) + action, + { + "trigger": { + "platform": "homeassistant", + "event": event, + "description": "Home Assistant starting", + } + }, ) return lambda: None diff --git a/homeassistant/components/homeassistant/triggers/numeric_state.py b/homeassistant/components/homeassistant/triggers/numeric_state.py index a1678e0a24c..b5a21aa8404 100644 --- a/homeassistant/components/homeassistant/triggers/numeric_state.py +++ b/homeassistant/components/homeassistant/triggers/numeric_state.py @@ -117,6 +117,7 @@ async def async_attach_trigger( "from_state": from_s, "to_state": to_s, "for": time_delta if not time_delta else period[entity], + "description": f"numeric state of {entity}", } }, to_s.context, diff --git a/homeassistant/components/homeassistant/triggers/state.py b/homeassistant/components/homeassistant/triggers/state.py index d51e63964e9..c580b8daf49 100644 --- a/homeassistant/components/homeassistant/triggers/state.py +++ b/homeassistant/components/homeassistant/triggers/state.py @@ -103,6 +103,7 @@ async def async_attach_trigger( "to_state": to_s, "for": time_delta if not time_delta else period[entity], "attribute": attribute, + "description": f"state of {entity}", } }, event.context, diff --git a/homeassistant/components/homeassistant/triggers/time.py b/homeassistant/components/homeassistant/triggers/time.py index 01eb1a9b85f..a152b1e8571 100644 --- a/homeassistant/components/homeassistant/triggers/time.py +++ b/homeassistant/components/homeassistant/triggers/time.py @@ -1,5 +1,6 @@ """Offer time listening automation rules.""" from datetime import datetime +from functools import partial import logging import voluptuous as vol @@ -38,9 +39,12 @@ async def async_attach_trigger(hass, config, action, automation_info): removes = [] @callback - def time_automation_listener(now): + def time_automation_listener(description, now): """Listen for time changes and calls action.""" - hass.async_run_job(action, {"trigger": {"platform": "time", "now": now}}) + hass.async_run_job( + action, + {"trigger": {"platform": "time", "now": now, "description": description}}, + ) @callback def update_entity_trigger_event(event): @@ -81,13 +85,15 @@ async def async_attach_trigger(hass, config, action, automation_info): # Only set up listener if time is now or in the future. if trigger_dt >= dt_util.now(): remove = async_track_point_in_time( - hass, time_automation_listener, trigger_dt + hass, + partial(time_automation_listener, f"time set in {entity_id}"), + trigger_dt, ) elif has_time: # Else if it has time, then track time change. remove = async_track_time_change( hass, - time_automation_listener, + partial(time_automation_listener, f"time set in {entity_id}"), hour=hour, minute=minute, second=second, @@ -108,7 +114,7 @@ async def async_attach_trigger(hass, config, action, automation_info): removes.append( async_track_time_change( hass, - time_automation_listener, + partial(time_automation_listener, "time"), hour=at_time.hour, minute=at_time.minute, second=at_time.second, diff --git a/homeassistant/components/homeassistant/triggers/time_pattern.py b/homeassistant/components/homeassistant/triggers/time_pattern.py index 41294268ae8..adacc939870 100644 --- a/homeassistant/components/homeassistant/triggers/time_pattern.py +++ b/homeassistant/components/homeassistant/triggers/time_pattern.py @@ -75,7 +75,14 @@ async def async_attach_trigger(hass, config, action, automation_info): def time_automation_listener(now): """Listen for time changes and calls action.""" hass.async_run_job( - action, {"trigger": {"platform": "time_pattern", "now": now}} + action, + { + "trigger": { + "platform": "time_pattern", + "now": now, + "description": "time pattern", + } + }, ) return async_track_time_change( diff --git a/homeassistant/components/kodi/device_trigger.py b/homeassistant/components/kodi/device_trigger.py index a5c93d08f72..85065a8bfe3 100644 --- a/homeassistant/components/kodi/device_trigger.py +++ b/homeassistant/components/kodi/device_trigger.py @@ -66,7 +66,11 @@ def _attach_trigger( @callback def _handle_event(event: Event): if event.data[ATTR_ENTITY_ID] == config[CONF_ENTITY_ID]: - hass.async_run_job(action({"trigger": config}, context=event.context)) + hass.async_run_job( + action, + {"trigger": {**config, "description": event_type}}, + event.context, + ) return hass.bus.async_listen(event_type, _handle_event) diff --git a/homeassistant/components/litejet/trigger.py b/homeassistant/components/litejet/trigger.py index 5924cf3b809..d8b8ede8339 100644 --- a/homeassistant/components/litejet/trigger.py +++ b/homeassistant/components/litejet/trigger.py @@ -50,6 +50,7 @@ async def async_attach_trigger(hass, config, action, automation_info): CONF_NUMBER: number, CONF_HELD_MORE_THAN: held_more_than, CONF_HELD_LESS_THAN: held_less_than, + "description": f"litejet switch #{number}", } }, ) diff --git a/homeassistant/components/mqtt/trigger.py b/homeassistant/components/mqtt/trigger.py index 8bb8ad46041..1ba7905120f 100644 --- a/homeassistant/components/mqtt/trigger.py +++ b/homeassistant/components/mqtt/trigger.py @@ -45,6 +45,7 @@ async def async_attach_trigger(hass, config, action, automation_info): "topic": mqttmsg.topic, "payload": mqttmsg.payload, "qos": mqttmsg.qos, + "description": f"mqtt topic {mqttmsg.topic}", } try: diff --git a/homeassistant/components/sun/trigger.py b/homeassistant/components/sun/trigger.py index c416742f397..c21726cf0ae 100644 --- a/homeassistant/components/sun/trigger.py +++ b/homeassistant/components/sun/trigger.py @@ -31,12 +31,23 @@ async def async_attach_trigger(hass, config, action, automation_info): """Listen for events based on configuration.""" event = config.get(CONF_EVENT) offset = config.get(CONF_OFFSET) + description = event + if offset: + description = f"{description} with offset" @callback def call_action(): """Call action with right context.""" hass.async_run_job( - action, {"trigger": {"platform": "sun", "event": event, "offset": offset}} + action, + { + "trigger": { + "platform": "sun", + "event": event, + "offset": offset, + "description": description, + } + }, ) if event == SUN_EVENT_SUNRISE: diff --git a/homeassistant/components/template/trigger.py b/homeassistant/components/template/trigger.py index b6e6c974807..980faf4d0a8 100644 --- a/homeassistant/components/template/trigger.py +++ b/homeassistant/components/template/trigger.py @@ -62,6 +62,7 @@ async def async_attach_trigger( "from_state": from_s, "to_state": to_s, "for": time_delta if not time_delta else period, + "description": f"{entity_id} via template", } }, (to_s.context if to_s else None), diff --git a/homeassistant/components/webhook/trigger.py b/homeassistant/components/webhook/trigger.py index 38ae9c5e364..cc03f74922f 100644 --- a/homeassistant/components/webhook/trigger.py +++ b/homeassistant/components/webhook/trigger.py @@ -30,6 +30,7 @@ async def _handle_webhook(action, hass, webhook_id, request): result["data"] = await request.post() result["query"] = request.query + result["description"] = "webhook" hass.async_run_job(action, {"trigger": result}) diff --git a/homeassistant/components/zone/trigger.py b/homeassistant/components/zone/trigger.py index f53af0e5d7f..1f0856513dd 100644 --- a/homeassistant/components/zone/trigger.py +++ b/homeassistant/components/zone/trigger.py @@ -1,7 +1,13 @@ """Offer zone automation rules.""" import voluptuous as vol -from homeassistant.const import CONF_ENTITY_ID, CONF_EVENT, CONF_PLATFORM, CONF_ZONE +from homeassistant.const import ( + ATTR_FRIENDLY_NAME, + CONF_ENTITY_ID, + CONF_EVENT, + CONF_PLATFORM, + CONF_ZONE, +) from homeassistant.core import callback from homeassistant.helpers import condition, config_validation as cv, location from homeassistant.helpers.event import async_track_state_change_event @@ -12,6 +18,8 @@ EVENT_ENTER = "enter" EVENT_LEAVE = "leave" DEFAULT_EVENT = EVENT_ENTER +_EVENT_DESCRIPTION = {EVENT_ENTER: "entering", EVENT_LEAVE: "leaving"} + TRIGGER_SCHEMA = vol.Schema( { vol.Required(CONF_PLATFORM): "zone", @@ -56,6 +64,7 @@ async def async_attach_trigger(hass, config, action, automation_info): and from_match and not to_match ): + description = f"{entity} {_EVENT_DESCRIPTION[event]} {zone_state.attributes[ATTR_FRIENDLY_NAME]}" hass.async_run_job( action, { @@ -66,6 +75,7 @@ async def async_attach_trigger(hass, config, action, automation_info): "to_state": to_s, "zone": zone_state, "event": event, + "description": description, } }, to_s.context, diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index d1c2e47a39e..b8f25dc9c46 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -6,6 +6,7 @@ import pytest from homeassistant.components import logbook import homeassistant.components.automation as automation from homeassistant.components.automation import ( + ATTR_SOURCE, DOMAIN, EVENT_AUTOMATION_RELOADED, EVENT_AUTOMATION_TRIGGERED, @@ -324,6 +325,7 @@ async def test_shared_context(hass, calls): # Ensure event data has all attributes set assert args[0].data.get(ATTR_NAME) is not None assert args[0].data.get(ATTR_ENTITY_ID) is not None + assert args[0].data.get(ATTR_SOURCE) is not None # Ensure context set correctly for event fired by 'hello' automation args, _ = first_automation_listener.call_args @@ -341,6 +343,7 @@ async def test_shared_context(hass, calls): # Ensure event data has all attributes set assert args[0].data.get(ATTR_NAME) is not None assert args[0].data.get(ATTR_ENTITY_ID) is not None + assert args[0].data.get(ATTR_SOURCE) is not None # Ensure the service call from the second automation # shares the same context @@ -1089,7 +1092,11 @@ async def test_logbook_humanify_automation_triggered_event(hass): ), MockLazyEventPartialState( EVENT_AUTOMATION_TRIGGERED, - {ATTR_ENTITY_ID: "automation.bye", ATTR_NAME: "Bye Automation"}, + { + ATTR_ENTITY_ID: "automation.bye", + ATTR_NAME: "Bye Automation", + ATTR_SOURCE: "source of trigger", + }, ), ], entity_attr_cache, @@ -1104,5 +1111,5 @@ async def test_logbook_humanify_automation_triggered_event(hass): assert event2["name"] == "Bye Automation" assert event2["domain"] == "automation" - assert event2["message"] == "has been triggered" + assert event2["message"] == "has been triggered by source of trigger" assert event2["entity_id"] == "automation.bye"