Add id to automation triggers (#48464)

This commit is contained in:
Erik Montnemery 2021-03-31 14:56:04 +02:00 committed by GitHub
parent 450f3740cd
commit b26779a27a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 168 additions and 56 deletions

View file

@ -56,6 +56,7 @@ async def async_attach_trigger(
automation_info: dict, automation_info: dict,
) -> CALLBACK_TYPE: ) -> CALLBACK_TYPE:
"""Attach a trigger.""" """Attach a trigger."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
job = HassJob(action) job = HassJob(action)
if config[CONF_TYPE] == "turn_on": if config[CONF_TYPE] == "turn_on":
@ -66,7 +67,13 @@ async def async_attach_trigger(
if event.data[ATTR_ENTITY_ID] == entity_id: if event.data[ATTR_ENTITY_ID] == entity_id:
hass.async_run_hass_job( hass.async_run_hass_job(
job, job,
{"trigger": {**config, "description": f"{DOMAIN} - {entity_id}"}}, {
"trigger": {
**config,
"description": f"{DOMAIN} - {entity_id}",
"id": trigger_id,
}
},
event.context, event.context,
) )

View file

@ -455,7 +455,11 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
automation_trace.set_trigger_description(trigger_description) automation_trace.set_trigger_description(trigger_description)
# Add initial variables as the trigger step # Add initial variables as the trigger step
trace_element = TraceElement(variables, "trigger") if "trigger" in variables and "id" in variables["trigger"]:
trigger_path = f"trigger/{variables['trigger']['id']}"
else:
trigger_path = "trigger"
trace_element = TraceElement(variables, trigger_path)
trace_append_element(trace_element) trace_append_element(trace_element)
if ( if (

View file

@ -33,6 +33,7 @@ def source_match(state, source):
async def async_attach_trigger(hass, config, action, automation_info): async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for state changes based on configuration.""" """Listen for state changes based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
source = config.get(CONF_SOURCE).lower() source = config.get(CONF_SOURCE).lower()
zone_entity_id = config.get(CONF_ZONE) zone_entity_id = config.get(CONF_ZONE)
trigger_event = config.get(CONF_EVENT) trigger_event = config.get(CONF_EVENT)
@ -74,6 +75,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
"zone": zone_state, "zone": zone_state,
"event": trigger_event, "event": trigger_event,
"description": f"geo_location - {source}", "description": f"geo_location - {source}",
"id": trigger_id,
} }
}, },
event.context, event.context,

View file

@ -31,6 +31,7 @@ async def async_attach_trigger(
hass, config, action, automation_info, *, platform_type="event" hass, config, action, automation_info, *, platform_type="event"
): ):
"""Listen for events based on configuration.""" """Listen for events based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
variables = None variables = None
if automation_info: if automation_info:
variables = automation_info.get("variables") variables = automation_info.get("variables")
@ -95,6 +96,7 @@ async def async_attach_trigger(
"platform": platform_type, "platform": platform_type,
"event": event, "event": event,
"description": f"event '{event.event_type}'", "description": f"event '{event.event_type}'",
"id": trigger_id,
} }
}, },
event.context, event.context,

View file

@ -19,6 +19,7 @@ TRIGGER_SCHEMA = vol.Schema(
async def async_attach_trigger(hass, config, action, automation_info): async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for events based on configuration.""" """Listen for events based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
event = config.get(CONF_EVENT) event = config.get(CONF_EVENT)
job = HassJob(action) job = HassJob(action)
@ -34,6 +35,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
"platform": "homeassistant", "platform": "homeassistant",
"event": event, "event": event,
"description": "Home Assistant stopping", "description": "Home Assistant stopping",
"id": trigger_id,
} }
}, },
event.context, event.context,
@ -51,6 +53,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
"platform": "homeassistant", "platform": "homeassistant",
"event": event, "event": event,
"description": "Home Assistant starting", "description": "Home Assistant starting",
"id": trigger_id,
} }
}, },
) )

View file

@ -78,6 +78,7 @@ async def async_attach_trigger(
attribute = config.get(CONF_ATTRIBUTE) attribute = config.get(CONF_ATTRIBUTE)
job = HassJob(action) job = HassJob(action)
trigger_id = automation_info.get("trigger_id") if automation_info else None
_variables = {} _variables = {}
if automation_info: if automation_info:
_variables = automation_info.get("variables") or {} _variables = automation_info.get("variables") or {}
@ -139,6 +140,7 @@ async def async_attach_trigger(
"to_state": to_s, "to_state": to_s,
"for": time_delta if not time_delta else period[entity_id], "for": time_delta if not time_delta else period[entity_id],
"description": f"numeric state of {entity_id}", "description": f"numeric state of {entity_id}",
"id": trigger_id,
} }
}, },
to_s.context, to_s.context,

View file

@ -87,6 +87,7 @@ async def async_attach_trigger(
attribute = config.get(CONF_ATTRIBUTE) attribute = config.get(CONF_ATTRIBUTE)
job = HassJob(action) job = HassJob(action)
trigger_id = automation_info.get("trigger_id") if automation_info else None
_variables = {} _variables = {}
if automation_info: if automation_info:
_variables = automation_info.get("variables") or {} _variables = automation_info.get("variables") or {}
@ -140,6 +141,7 @@ async def async_attach_trigger(
"for": time_delta if not time_delta else period[entity], "for": time_delta if not time_delta else period[entity],
"attribute": attribute, "attribute": attribute,
"description": f"state of {entity}", "description": f"state of {entity}",
"id": trigger_id,
} }
}, },
event.context, event.context,

View file

@ -39,6 +39,7 @@ TRIGGER_SCHEMA = vol.Schema(
async def async_attach_trigger(hass, config, action, automation_info): async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for state changes based on configuration.""" """Listen for state changes based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
entities = {} entities = {}
removes = [] removes = []
job = HassJob(action) job = HassJob(action)
@ -54,6 +55,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
"now": now, "now": now,
"description": description, "description": description,
"entity_id": entity_id, "entity_id": entity_id,
"id": trigger_id,
} }
}, },
) )

View file

@ -57,6 +57,7 @@ TRIGGER_SCHEMA = vol.All(
async def async_attach_trigger(hass, config, action, automation_info): async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for state changes based on configuration.""" """Listen for state changes based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
hours = config.get(CONF_HOURS) hours = config.get(CONF_HOURS)
minutes = config.get(CONF_MINUTES) minutes = config.get(CONF_MINUTES)
seconds = config.get(CONF_SECONDS) seconds = config.get(CONF_SECONDS)
@ -78,6 +79,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
"platform": "time_pattern", "platform": "time_pattern",
"now": now, "now": now,
"description": "time pattern", "description": "time pattern",
"id": trigger_id,
} }
}, },
) )

View file

@ -75,11 +75,14 @@ class TriggerSource:
automation_info: dict, automation_info: dict,
) -> CALLBACK_TYPE: ) -> CALLBACK_TYPE:
"""Attach a trigger.""" """Attach a trigger."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
def event_handler(char): def event_handler(char):
if config[CONF_SUBTYPE] != HK_TO_HA_INPUT_EVENT_VALUES[char["value"]]: if config[CONF_SUBTYPE] != HK_TO_HA_INPUT_EVENT_VALUES[char["value"]]:
return return
self._hass.async_create_task(action({"trigger": config})) self._hass.async_create_task(
action({"trigger": {**config, "id": trigger_id}})
)
trigger = self._triggers[config[CONF_TYPE], config[CONF_SUBTYPE]] trigger = self._triggers[config[CONF_TYPE], config[CONF_SUBTYPE]]
iid = trigger["characteristic"] iid = trigger["characteristic"]

View file

@ -61,8 +61,13 @@ async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict]:
@callback @callback
def _attach_trigger( def _attach_trigger(
hass: HomeAssistant, config: ConfigType, action: AutomationActionType, event_type hass: HomeAssistant,
config: ConfigType,
action: AutomationActionType,
event_type,
automation_info: dict,
): ):
trigger_id = automation_info.get("trigger_id") if automation_info else None
job = HassJob(action) job = HassJob(action)
@callback @callback
@ -70,7 +75,7 @@ def _attach_trigger(
if event.data[ATTR_ENTITY_ID] == config[CONF_ENTITY_ID]: if event.data[ATTR_ENTITY_ID] == config[CONF_ENTITY_ID]:
hass.async_run_hass_job( hass.async_run_hass_job(
job, job,
{"trigger": {**config, "description": event_type}}, {"trigger": {**config, "description": event_type, "id": trigger_id}},
event.context, event.context,
) )
@ -85,9 +90,9 @@ async def async_attach_trigger(
) -> CALLBACK_TYPE: ) -> CALLBACK_TYPE:
"""Attach a trigger.""" """Attach a trigger."""
if config[CONF_TYPE] == "turn_on": if config[CONF_TYPE] == "turn_on":
return _attach_trigger(hass, config, action, EVENT_TURN_ON) return _attach_trigger(hass, config, action, EVENT_TURN_ON, automation_info)
if config[CONF_TYPE] == "turn_off": if config[CONF_TYPE] == "turn_off":
return _attach_trigger(hass, config, action, EVENT_TURN_OFF) return _attach_trigger(hass, config, action, EVENT_TURN_OFF, automation_info)
return lambda: None return lambda: None

View file

@ -31,6 +31,7 @@ TRIGGER_SCHEMA = vol.Schema(
async def async_attach_trigger(hass, config, action, automation_info): async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for events based on configuration.""" """Listen for events based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
number = config.get(CONF_NUMBER) number = config.get(CONF_NUMBER)
held_more_than = config.get(CONF_HELD_MORE_THAN) held_more_than = config.get(CONF_HELD_MORE_THAN)
held_less_than = config.get(CONF_HELD_LESS_THAN) held_less_than = config.get(CONF_HELD_LESS_THAN)
@ -50,6 +51,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
CONF_HELD_MORE_THAN: held_more_than, CONF_HELD_MORE_THAN: held_more_than,
CONF_HELD_LESS_THAN: held_less_than, CONF_HELD_LESS_THAN: held_less_than,
"description": f"litejet switch #{number}", "description": f"litejet switch #{number}",
"id": trigger_id,
} }
}, },
) )

View file

@ -37,6 +37,7 @@ _LOGGER = logging.getLogger(__name__)
async def async_attach_trigger(hass, config, action, automation_info): async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for state changes based on configuration.""" """Listen for state changes based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
topic = config[CONF_TOPIC] topic = config[CONF_TOPIC]
wanted_payload = config.get(CONF_PAYLOAD) wanted_payload = config.get(CONF_PAYLOAD)
value_template = config.get(CONF_VALUE_TEMPLATE) value_template = config.get(CONF_VALUE_TEMPLATE)
@ -78,6 +79,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
"payload": mqttmsg.payload, "payload": mqttmsg.payload,
"qos": mqttmsg.qos, "qos": mqttmsg.qos,
"description": f"mqtt topic {mqttmsg.topic}", "description": f"mqtt topic {mqttmsg.topic}",
"id": trigger_id,
} }
with suppress(ValueError): with suppress(ValueError):

View file

@ -45,6 +45,7 @@ async def async_attach_trigger(
automation_info: dict, automation_info: dict,
) -> CALLBACK_TYPE | None: ) -> CALLBACK_TYPE | None:
"""Attach a trigger.""" """Attach a trigger."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
registry: DeviceRegistry = await async_get_registry(hass) registry: DeviceRegistry = await async_get_registry(hass)
if config[CONF_TYPE] == TRIGGER_TYPE_TURN_ON: if config[CONF_TYPE] == TRIGGER_TYPE_TURN_ON:
variables = { variables = {
@ -53,6 +54,7 @@ async def async_attach_trigger(
"domain": DOMAIN, "domain": DOMAIN,
"device_id": config[CONF_DEVICE_ID], "device_id": config[CONF_DEVICE_ID],
"description": f"philips_js '{config[CONF_TYPE]}' event", "description": f"philips_js '{config[CONF_TYPE]}' event",
"id": trigger_id,
} }
} }

View file

@ -26,6 +26,7 @@ TRIGGER_SCHEMA = vol.Schema(
async def async_attach_trigger(hass, config, action, automation_info): async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for events based on configuration.""" """Listen for events based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
event = config.get(CONF_EVENT) event = config.get(CONF_EVENT)
offset = config.get(CONF_OFFSET) offset = config.get(CONF_OFFSET)
description = event description = event
@ -44,6 +45,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
"event": event, "event": event,
"offset": offset, "offset": offset,
"description": description, "description": description,
"id": trigger_id,
} }
}, },
) )

View file

@ -18,6 +18,7 @@ TRIGGER_SCHEMA = vol.Schema(
async def async_attach_trigger(hass, config, action, automation_info): async def async_attach_trigger(hass, config, action, automation_info):
"""Listen for tag_scanned events based on configuration.""" """Listen for tag_scanned events based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
tag_ids = set(config[TAG_ID]) tag_ids = set(config[TAG_ID])
device_ids = set(config[DEVICE_ID]) if DEVICE_ID in config else None device_ids = set(config[DEVICE_ID]) if DEVICE_ID in config else None
@ -37,6 +38,7 @@ async def async_attach_trigger(hass, config, action, automation_info):
"platform": DOMAIN, "platform": DOMAIN,
"event": event, "event": event,
"description": "Tag scanned", "description": "Tag scanned",
"id": trigger_id,
} }
}, },
event.context, event.context,

View file

@ -31,6 +31,7 @@ async def async_attach_trigger(
hass, config, action, automation_info, *, platform_type="template" hass, config, action, automation_info, *, platform_type="template"
): ):
"""Listen for state changes based on configuration.""" """Listen for state changes based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
value_template = config.get(CONF_VALUE_TEMPLATE) value_template = config.get(CONF_VALUE_TEMPLATE)
value_template.hass = hass value_template.hass = hass
time_delta = config.get(CONF_FOR) time_delta = config.get(CONF_FOR)
@ -100,6 +101,7 @@ async def async_attach_trigger(
trigger_variables = { trigger_variables = {
"for": time_delta, "for": time_delta,
"description": description, "description": description,
"id": trigger_id,
} }
@callback @callback

View file

@ -17,7 +17,7 @@ TRIGGER_SCHEMA = vol.Schema(
) )
async def _handle_webhook(job, hass, webhook_id, request): async def _handle_webhook(job, trigger_id, hass, webhook_id, request):
"""Handle incoming webhook.""" """Handle incoming webhook."""
result = {"platform": "webhook", "webhook_id": webhook_id} result = {"platform": "webhook", "webhook_id": webhook_id}
@ -28,18 +28,20 @@ async def _handle_webhook(job, hass, webhook_id, request):
result["query"] = request.query result["query"] = request.query
result["description"] = "webhook" result["description"] = "webhook"
result["id"] = trigger_id
hass.async_run_hass_job(job, {"trigger": result}) hass.async_run_hass_job(job, {"trigger": result})
async def async_attach_trigger(hass, config, action, automation_info): async def async_attach_trigger(hass, config, action, automation_info):
"""Trigger based on incoming webhooks.""" """Trigger based on incoming webhooks."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
webhook_id = config.get(CONF_WEBHOOK_ID) webhook_id = config.get(CONF_WEBHOOK_ID)
job = HassJob(action) job = HassJob(action)
hass.components.webhook.async_register( hass.components.webhook.async_register(
automation_info["domain"], automation_info["domain"],
automation_info["name"], automation_info["name"],
webhook_id, webhook_id,
partial(_handle_webhook, job), partial(_handle_webhook, job, trigger_id),
) )
@callback @callback

View file

@ -37,6 +37,7 @@ async def async_attach_trigger(
hass, config, action, automation_info, *, platform_type: str = "zone" hass, config, action, automation_info, *, platform_type: str = "zone"
) -> CALLBACK_TYPE: ) -> CALLBACK_TYPE:
"""Listen for state changes based on configuration.""" """Listen for state changes based on configuration."""
trigger_id = automation_info.get("trigger_id") if automation_info else None
entity_id = config.get(CONF_ENTITY_ID) entity_id = config.get(CONF_ENTITY_ID)
zone_entity_id = config.get(CONF_ZONE) zone_entity_id = config.get(CONF_ZONE)
event = config.get(CONF_EVENT) event = config.get(CONF_EVENT)
@ -80,6 +81,7 @@ async def async_attach_trigger(
"zone": zone_state, "zone": zone_state,
"event": event, "event": event,
"description": description, "description": description,
"id": trigger_id,
} }
}, },
to_s.context, to_s.context,

View file

@ -72,8 +72,9 @@ async def async_initialize_triggers(
} }
triggers = [] triggers = []
for conf in trigger_config: for idx, conf in enumerate(trigger_config):
platform = await _async_get_trigger_platform(hass, conf) platform = await _async_get_trigger_platform(hass, conf)
info = {**info, "trigger_id": f"{idx}"}
triggers.append(platform.async_attach_trigger(hass, conf, action, info)) triggers.append(platform.async_attach_trigger(hass, conf, action, info))
attach_results = await asyncio.gather(*triggers, return_exceptions=True) attach_results = await asyncio.gather(*triggers, return_exceptions=True)

View file

@ -87,7 +87,8 @@ async def test_if_fires_on_state_change(hass, calls):
"some": ( "some": (
"turn_on - {{ trigger.platform}} - " "turn_on - {{ trigger.platform}} - "
"{{ trigger.entity_id}} - {{ trigger.from_state.state}} - " "{{ trigger.entity_id}} - {{ trigger.from_state.state}} - "
"{{ trigger.to_state.state}} - {{ trigger.for }}" "{{ trigger.to_state.state}} - {{ trigger.for }} - "
"{{ trigger.id}}"
) )
}, },
}, },
@ -106,7 +107,8 @@ async def test_if_fires_on_state_change(hass, calls):
"some": ( "some": (
"turn_off - {{ trigger.platform}} - " "turn_off - {{ trigger.platform}} - "
"{{ trigger.entity_id}} - {{ trigger.from_state.state}} - " "{{ trigger.entity_id}} - {{ trigger.from_state.state}} - "
"{{ trigger.to_state.state}} - {{ trigger.for }}" "{{ trigger.to_state.state}} - {{ trigger.for }} - "
"{{ trigger.id}}"
) )
}, },
}, },
@ -119,14 +121,14 @@ async def test_if_fires_on_state_change(hass, calls):
hass.states.async_set("NEW_DOMAIN.entity", STATE_ON) hass.states.async_set("NEW_DOMAIN.entity", STATE_ON)
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["some"] == "turn_on - device - {} - off - on - None".format( assert calls[0].data[
"NEW_DOMAIN.entity" "some"
) ] == "turn_on - device - {} - off - on - None - 0".format("NEW_DOMAIN.entity")
# Fake that the entity is turning off. # Fake that the entity is turning off.
hass.states.async_set("NEW_DOMAIN.entity", STATE_OFF) hass.states.async_set("NEW_DOMAIN.entity", STATE_OFF)
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 2 assert len(calls) == 2
assert calls[1].data["some"] == "turn_off - device - {} - on - off - None".format( assert calls[1].data[
"NEW_DOMAIN.entity" "some"
) ] == "turn_off - device - {} - on - off - None - 0".format("NEW_DOMAIN.entity")

View file

@ -82,7 +82,10 @@ async def test_if_fires_on_turn_on_request(hass, calls, player_setup, state):
}, },
"action": { "action": {
"service": "test.automation", "service": "test.automation",
"data_template": {"some": "{{ trigger.entity_id }}"}, "data_template": {
"some": "{{ trigger.entity_id }}",
"id": "{{ trigger.id }}",
},
}, },
} }
] ]
@ -99,3 +102,4 @@ async def test_if_fires_on_turn_on_request(hass, calls, player_setup, state):
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["some"] == player_setup assert calls[0].data["some"] == player_setup
assert calls[0].data["id"] == 0

View file

@ -68,6 +68,7 @@ async def test_if_fires_on_zone_enter(hass, calls):
"from_state.state", "from_state.state",
"to_state.state", "to_state.state",
"zone.name", "zone.name",
"id",
) )
) )
}, },
@ -88,7 +89,7 @@ async def test_if_fires_on_zone_enter(hass, calls):
assert calls[0].context.parent_id == context.id assert calls[0].context.parent_id == context.id
assert ( assert (
calls[0].data["some"] calls[0].data["some"]
== "geo_location - geo_location.entity - hello - hello - test" == "geo_location - geo_location.entity - hello - hello - test - 0"
) )
# Set out of zone again so we can trigger call # Set out of zone again so we can trigger call

View file

@ -37,7 +37,10 @@ async def test_if_fires_on_event(hass, calls):
{ {
automation.DOMAIN: { automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"}, "trigger": {"platform": "event", "event_type": "test_event"},
"action": {"service": "test.automation"}, "action": {
"service": "test.automation",
"data_template": {"id": "{{ trigger.id}}"},
},
} }
}, },
) )
@ -57,6 +60,7 @@ async def test_if_fires_on_event(hass, calls):
hass.bus.async_fire("test_event") hass.bus.async_fire("test_event")
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0
async def test_if_fires_on_templated_event(hass, calls): async def test_if_fires_on_templated_event(hass, calls):

View file

@ -16,7 +16,10 @@ async def test_if_fires_on_hass_start(hass):
automation.DOMAIN: { automation.DOMAIN: {
"alias": "hello", "alias": "hello",
"trigger": {"platform": "homeassistant", "event": "start"}, "trigger": {"platform": "homeassistant", "event": "start"},
"action": {"service": "test.automation"}, "action": {
"service": "test.automation",
"data_template": {"id": "{{ trigger.id}}"},
},
} }
} }
@ -39,6 +42,7 @@ async def test_if_fires_on_hass_start(hass):
assert automation.is_on(hass, "automation.hello") assert automation.is_on(hass, "automation.hello")
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0
async def test_if_fires_on_hass_shutdown(hass): async def test_if_fires_on_hass_shutdown(hass):
@ -53,7 +57,10 @@ async def test_if_fires_on_hass_shutdown(hass):
automation.DOMAIN: { automation.DOMAIN: {
"alias": "hello", "alias": "hello",
"trigger": {"platform": "homeassistant", "event": "shutdown"}, "trigger": {"platform": "homeassistant", "event": "shutdown"},
"action": {"service": "test.automation"}, "action": {
"service": "test.automation",
"data_template": {"id": "{{ trigger.id}}"},
},
} }
}, },
) )
@ -68,3 +75,4 @@ async def test_if_fires_on_hass_shutdown(hass):
with patch.object(hass.loop, "stop"): with patch.object(hass.loop, "stop"):
await hass.async_stop() await hass.async_stop()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0

View file

@ -92,7 +92,10 @@ async def test_if_fires_on_entity_change_below(hass, calls, below):
"entity_id": "test.entity", "entity_id": "test.entity",
"below": below, "below": below,
}, },
"action": {"service": "test.automation"}, "action": {
"service": "test.automation",
"data_template": {"id": "{{ trigger.id}}"},
},
} }
}, },
) )
@ -114,6 +117,7 @@ async def test_if_fires_on_entity_change_below(hass, calls, below):
hass.states.async_set("test.entity", 9) hass.states.async_set("test.entity", 9)
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0
@pytest.mark.parametrize("below", (10, "input_number.value_10")) @pytest.mark.parametrize("below", (10, "input_number.value_10"))

View file

@ -55,6 +55,7 @@ async def test_if_fires_on_entity_change(hass, calls):
"from_state.state", "from_state.state",
"to_state.state", "to_state.state",
"for", "for",
"id",
) )
) )
}, },
@ -68,7 +69,7 @@ async def test_if_fires_on_entity_change(hass, calls):
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].context.parent_id == context.id assert calls[0].context.parent_id == context.id
assert calls[0].data["some"] == "state - test.entity - hello - world - None" assert calls[0].data["some"] == "state - test.entity - hello - world - None - 0"
await hass.services.async_call( await hass.services.async_call(
automation.DOMAIN, automation.DOMAIN,

View file

@ -51,7 +51,8 @@ async def test_if_fires_using_at(hass, calls):
"action": { "action": {
"service": "test.automation", "service": "test.automation",
"data_template": { "data_template": {
"some": "{{ trigger.platform }} - {{ trigger.now.hour }}" "some": "{{ trigger.platform }} - {{ trigger.now.hour }}",
"id": "{{ trigger.id}}",
}, },
}, },
} }
@ -64,6 +65,7 @@ async def test_if_fires_using_at(hass, calls):
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["some"] == "time - 5" assert calls[0].data["some"] == "time - 5"
assert calls[0].data["id"] == 0
@pytest.mark.parametrize( @pytest.mark.parametrize(

View file

@ -46,7 +46,10 @@ async def test_if_fires_when_hour_matches(hass, calls):
"minutes": "*", "minutes": "*",
"seconds": "*", "seconds": "*",
}, },
"action": {"service": "test.automation"}, "action": {
"service": "test.automation",
"data_template": {"id": "{{ trigger.id}}"},
},
} }
}, },
) )
@ -65,6 +68,7 @@ async def test_if_fires_when_hour_matches(hass, calls):
async_fire_time_changed(hass, now.replace(year=now.year + 1, hour=0)) async_fire_time_changed(hass, now.replace(year=now.year + 1, hour=0))
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0
async def test_if_fires_when_minute_matches(hass, calls): async def test_if_fires_when_minute_matches(hass, calls):

View file

@ -214,7 +214,8 @@ async def test_handle_events(hass, utcnow, calls):
"data_template": { "data_template": {
"some": ( "some": (
"{{ trigger.platform}} - " "{{ trigger.platform}} - "
"{{ trigger.type }} - {{ trigger.subtype }}" "{{ trigger.type }} - {{ trigger.subtype }} - "
"{{ trigger.id }}"
) )
}, },
}, },
@ -233,7 +234,8 @@ async def test_handle_events(hass, utcnow, calls):
"data_template": { "data_template": {
"some": ( "some": (
"{{ trigger.platform}} - " "{{ trigger.platform}} - "
"{{ trigger.type }} - {{ trigger.subtype }}" "{{ trigger.type }} - {{ trigger.subtype }} - "
"{{ trigger.id }}"
) )
}, },
}, },
@ -249,7 +251,7 @@ async def test_handle_events(hass, utcnow, calls):
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["some"] == "device - button1 - single_press" assert calls[0].data["some"] == "device - button1 - single_press - 0"
# Make sure automation doesn't trigger for long press # Make sure automation doesn't trigger for long press
helper.pairing.testing.update_named_service( helper.pairing.testing.update_named_service(
@ -274,7 +276,7 @@ async def test_handle_events(hass, utcnow, calls):
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 2 assert len(calls) == 2
assert calls[1].data["some"] == "device - button2 - long_press" assert calls[1].data["some"] == "device - button2 - long_press - 0"
# Turn the automations off # Turn the automations off
await hass.services.async_call( await hass.services.async_call(

View file

@ -95,7 +95,9 @@ async def test_if_fires_on_state_change(hass, calls, kodi_media_player):
"action": { "action": {
"service": "test.automation", "service": "test.automation",
"data_template": { "data_template": {
"some": ("turn_on - {{ trigger.entity_id }}") "some": (
"turn_on - {{ trigger.entity_id }} - {{ trigger.id}}"
)
}, },
}, },
}, },
@ -110,7 +112,9 @@ async def test_if_fires_on_state_change(hass, calls, kodi_media_player):
"action": { "action": {
"service": "test.automation", "service": "test.automation",
"data_template": { "data_template": {
"some": ("turn_off - {{ trigger.entity_id }}") "some": (
"turn_off - {{ trigger.entity_id }} - {{ trigger.id}}"
)
}, },
}, },
}, },
@ -128,7 +132,7 @@ async def test_if_fires_on_state_change(hass, calls, kodi_media_player):
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["some"] == f"turn_on - {kodi_media_player}" assert calls[0].data["some"] == f"turn_on - {kodi_media_player} - 0"
await hass.services.async_call( await hass.services.async_call(
MP_DOMAIN, MP_DOMAIN,
@ -139,4 +143,4 @@ async def test_if_fires_on_state_change(hass, calls, kodi_media_player):
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 2 assert len(calls) == 2
assert calls[1].data["some"] == f"turn_off - {kodi_media_player}" assert calls[1].data["some"] == f"turn_off - {kodi_media_player} - 0"

View file

@ -82,7 +82,10 @@ async def setup_automation(hass, trigger):
{ {
"alias": "My Test", "alias": "My Test",
"trigger": trigger, "trigger": trigger,
"action": {"service": "test.automation"}, "action": {
"service": "test.automation",
"data_template": {"id": "{{ trigger.id}}"},
},
} }
] ]
}, },
@ -100,6 +103,7 @@ async def test_simple(hass, calls, mock_litejet):
await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER)
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0
async def test_held_more_than_short(hass, calls, mock_litejet): async def test_held_more_than_short(hass, calls, mock_litejet):
@ -134,6 +138,7 @@ async def test_held_more_than_long(hass, calls, mock_litejet):
assert len(calls) == 0 assert len(calls) == 0
await simulate_time(hass, mock_litejet, timedelta(seconds=0.3)) await simulate_time(hass, mock_litejet, timedelta(seconds=0.3))
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0
await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER)
assert len(calls) == 1 assert len(calls) == 1
@ -154,6 +159,7 @@ async def test_held_less_than_short(hass, calls, mock_litejet):
assert len(calls) == 0 assert len(calls) == 0
await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER)
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0
async def test_held_less_than_long(hass, calls, mock_litejet): async def test_held_less_than_long(hass, calls, mock_litejet):
@ -211,6 +217,7 @@ async def test_held_in_range_just_right(hass, calls, mock_litejet):
assert len(calls) == 0 assert len(calls) == 0
await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER) await simulate_release(hass, mock_litejet, ENTITY_OTHER_SWITCH_NUMBER)
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0
async def test_held_in_range_long(hass, calls, mock_litejet): async def test_held_in_range_long(hass, calls, mock_litejet):

View file

@ -34,9 +34,9 @@ async def test_if_fires_on_topic_match(hass, calls):
"action": { "action": {
"service": "test.automation", "service": "test.automation",
"data_template": { "data_template": {
"some": "{{ trigger.platform }} - {{ trigger.topic }}" "some": "{{ trigger.platform }} - {{ trigger.topic }} - "
" - {{ trigger.payload }} - " "{{ trigger.payload }} - {{ trigger.payload_json.hello }} - "
"{{ trigger.payload_json.hello }}" "{{ trigger.id }}"
}, },
}, },
} }
@ -46,7 +46,9 @@ async def test_if_fires_on_topic_match(hass, calls):
async_fire_mqtt_message(hass, "test-topic", '{ "hello": "world" }') async_fire_mqtt_message(hass, "test-topic", '{ "hello": "world" }')
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["some"] == 'mqtt - test-topic - { "hello": "world" } - world' assert (
calls[0].data["some"] == 'mqtt - test-topic - { "hello": "world" } - world - 0'
)
await hass.services.async_call( await hass.services.async_call(
automation.DOMAIN, automation.DOMAIN,

View file

@ -54,7 +54,10 @@ async def test_if_fires_on_turn_on_request(
}, },
"action": { "action": {
"service": "test.automation", "service": "test.automation",
"data_template": {"some": "{{ trigger.device_id }}"}, "data_template": {
"some": "{{ trigger.device_id }}",
"id": "{{ trigger.id}}",
},
}, },
} }
] ]
@ -71,3 +74,4 @@ async def test_if_fires_on_turn_on_request(
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["some"] == mock_device.id assert calls[0].data["some"] == mock_device.id
assert calls[0].data["id"] == 0

View file

@ -56,7 +56,10 @@ async def test_sunset_trigger(hass, calls, legacy_patchable_time):
{ {
automation.DOMAIN: { automation.DOMAIN: {
"trigger": {"platform": "sun", "event": SUN_EVENT_SUNSET}, "trigger": {"platform": "sun", "event": SUN_EVENT_SUNSET},
"action": {"service": "test.automation"}, "action": {
"service": "test.automation",
"data_template": {"id": "{{ trigger.id}}"},
},
} }
}, },
) )
@ -83,6 +86,7 @@ async def test_sunset_trigger(hass, calls, legacy_patchable_time):
async_fire_time_changed(hass, trigger_time) async_fire_time_changed(hass, trigger_time)
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0
async def test_sunrise_trigger(hass, calls, legacy_patchable_time): async def test_sunrise_trigger(hass, calls, legacy_patchable_time):

View file

@ -50,7 +50,10 @@ async def test_triggers(hass, tag_setup, calls):
"trigger": {"platform": DOMAIN, TAG_ID: "abc123"}, "trigger": {"platform": DOMAIN, TAG_ID: "abc123"},
"action": { "action": {
"service": "test.automation", "service": "test.automation",
"data": {"message": "service called"}, "data_template": {
"message": "service called",
"id": "{{ trigger.id}}",
},
}, },
} }
] ]
@ -64,6 +67,7 @@ async def test_triggers(hass, tag_setup, calls):
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["message"] == "service called" assert calls[0].data["message"] == "service called"
assert calls[0].data["id"] == 0
await hass.services.async_call( await hass.services.async_call(
automation.DOMAIN, automation.DOMAIN,

View file

@ -45,7 +45,10 @@ async def test_if_fires_on_change_bool(hass, calls):
"platform": "template", "platform": "template",
"value_template": '{{ states.test.entity.state == "world" and true }}', "value_template": '{{ states.test.entity.state == "world" and true }}',
}, },
"action": {"service": "test.automation"}, "action": {
"service": "test.automation",
"data_template": {"id": "{{ trigger.id}}"},
},
} }
}, },
) )
@ -66,6 +69,7 @@ async def test_if_fires_on_change_bool(hass, calls):
hass.states.async_set("test.entity", "planet") hass.states.async_set("test.entity", "planet")
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["id"] == 0
async def test_if_fires_on_change_str(hass, calls): async def test_if_fires_on_change_str(hass, calls):

View file

@ -112,7 +112,7 @@ async def test_get_trace(hass, hass_ws_client, domain, prefix):
trace = response["result"] trace = response["result"]
if domain == "automation": if domain == "automation":
assert len(trace["trace"]) == 2 assert len(trace["trace"]) == 2
assert set(trace["trace"]) == {"trigger", f"{prefix}/0"} assert set(trace["trace"]) == {"trigger/0", f"{prefix}/0"}
else: else:
assert len(trace["trace"]) == 1 assert len(trace["trace"]) == 1
assert set(trace["trace"]) == {f"{prefix}/0"} assert set(trace["trace"]) == {f"{prefix}/0"}
@ -163,7 +163,7 @@ async def test_get_trace(hass, hass_ws_client, domain, prefix):
trace = response["result"] trace = response["result"]
if domain == "automation": if domain == "automation":
assert len(trace["trace"]) == 3 assert len(trace["trace"]) == 3
assert set(trace["trace"]) == {"trigger", "condition/0", f"{prefix}/0"} assert set(trace["trace"]) == {"trigger/0", "condition/0", f"{prefix}/0"}
else: else:
assert len(trace["trace"]) == 1 assert len(trace["trace"]) == 1
assert set(trace["trace"]) == {f"{prefix}/0"} assert set(trace["trace"]) == {f"{prefix}/0"}
@ -217,12 +217,7 @@ async def test_get_trace(hass, hass_ws_client, domain, prefix):
response = await client.receive_json() response = await client.receive_json()
assert response["success"] assert response["success"]
trace = response["result"] trace = response["result"]
if domain == "automation": assert set(trace["trace"]) == {"trigger/1", "condition/0"}
assert len(trace["trace"]) == 2
assert set(trace["trace"]) == {"trigger", "condition/0"}
else:
assert len(trace["trace"]) == 1
assert set(trace["trace"]) == {f"{prefix}/0"}
assert len(trace["trace"]["condition/0"]) == 1 assert len(trace["trace"]["condition/0"]) == 1
assert trace["trace"]["condition/0"][0]["result"] == {"result": False} assert trace["trace"]["condition/0"][0]["result"] == {"result": False}
assert trace["config"] == moon_config assert trace["config"] == moon_config
@ -261,7 +256,7 @@ async def test_get_trace(hass, hass_ws_client, domain, prefix):
assert response["success"] assert response["success"]
trace = response["result"] trace = response["result"]
assert len(trace["trace"]) == 3 assert len(trace["trace"]) == 3
assert set(trace["trace"]) == {"trigger", "condition/0", f"{prefix}/0"} assert set(trace["trace"]) == {"trigger/0", "condition/0", f"{prefix}/0"}
assert len(trace["trace"][f"{prefix}/0"]) == 1 assert len(trace["trace"][f"{prefix}/0"]) == 1
assert "error" not in trace["trace"][f"{prefix}/0"][0] assert "error" not in trace["trace"][f"{prefix}/0"][0]
assert trace["trace"][f"{prefix}/0"][0]["result"] == moon_action assert trace["trace"][f"{prefix}/0"][0]["result"] == moon_action
@ -591,7 +586,7 @@ async def test_nested_traces(hass, hass_ws_client, domain, prefix):
trace = response["result"] trace = response["result"]
if domain == "automation": if domain == "automation":
assert len(trace["trace"]) == 2 assert len(trace["trace"]) == 2
assert set(trace["trace"]) == {"trigger", f"{prefix}/0"} assert set(trace["trace"]) == {"trigger/0", f"{prefix}/0"}
else: else:
assert len(trace["trace"]) == 1 assert len(trace["trace"]) == 1
assert set(trace["trace"]) == {f"{prefix}/0"} assert set(trace["trace"]) == {f"{prefix}/0"}

View file

@ -36,7 +36,10 @@ async def test_webhook_json(hass, aiohttp_client):
"trigger": {"platform": "webhook", "webhook_id": "json_webhook"}, "trigger": {"platform": "webhook", "webhook_id": "json_webhook"},
"action": { "action": {
"event": "test_success", "event": "test_success",
"event_data_template": {"hello": "yo {{ trigger.json.hello }}"}, "event_data_template": {
"hello": "yo {{ trigger.json.hello }}",
"id": "{{ trigger.id}}",
},
}, },
} }
}, },
@ -50,6 +53,7 @@ async def test_webhook_json(hass, aiohttp_client):
assert len(events) == 1 assert len(events) == 1
assert events[0].data["hello"] == "yo world" assert events[0].data["hello"] == "yo world"
assert events[0].data["id"] == 0
async def test_webhook_post(hass, aiohttp_client): async def test_webhook_post(hass, aiohttp_client):

View file

@ -66,6 +66,7 @@ async def test_if_fires_on_zone_enter(hass, calls):
"from_state.state", "from_state.state",
"to_state.state", "to_state.state",
"zone.name", "zone.name",
"id",
) )
) )
}, },
@ -84,7 +85,7 @@ async def test_if_fires_on_zone_enter(hass, calls):
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].context.parent_id == context.id assert calls[0].context.parent_id == context.id
assert calls[0].data["some"] == "zone - test.entity - hello - hello - test" assert calls[0].data["some"] == "zone - test.entity - hello - hello - test - 0"
# Set out of zone again so we can trigger call # Set out of zone again so we can trigger call
hass.states.async_set( hass.states.async_set(