diff --git a/homeassistant/components/homeassistant/triggers/state.py b/homeassistant/components/homeassistant/triggers/state.py index 5dd7335f56e..8a03905d98d 100644 --- a/homeassistant/components/homeassistant/triggers/state.py +++ b/homeassistant/components/homeassistant/triggers/state.py @@ -85,6 +85,10 @@ async def async_attach_trigger( attribute = config.get(CONF_ATTRIBUTE) job = HassJob(action) + _variables = {} + if automation_info: + _variables = automation_info.get("variables") or {} + @callback def state_automation_listener(event: Event): """Listen for state changes and calls action.""" @@ -143,7 +147,7 @@ async def async_attach_trigger( call_action() return - variables = { + trigger_info = { "trigger": { "platform": "state", "entity_id": entity, @@ -151,6 +155,7 @@ async def async_attach_trigger( "to_state": to_s, } } + variables = {**_variables, **trigger_info} try: period[entity] = cv.positive_time_period( diff --git a/tests/components/homeassistant/triggers/test_state.py b/tests/components/homeassistant/triggers/test_state.py index dd98dbc429c..2cf2081f018 100644 --- a/tests/components/homeassistant/triggers/test_state.py +++ b/tests/components/homeassistant/triggers/test_state.py @@ -984,6 +984,33 @@ async def test_if_fires_on_change_with_for_template_3(hass, calls): assert len(calls) == 1 +async def test_if_fires_on_change_with_for_template_4(hass, calls): + """Test for firing on change with for template.""" + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: { + "trigger_variables": {"seconds": 5}, + "trigger": { + "platform": "state", + "entity_id": "test.entity", + "to": "world", + "for": {"seconds": "{{ seconds }}"}, + }, + "action": {"service": "test.automation"}, + } + }, + ) + + hass.states.async_set("test.entity", "world") + await hass.async_block_till_done() + assert len(calls) == 0 + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert len(calls) == 1 + + async def test_if_fires_on_change_from_with_for(hass, calls): """Test for firing on change with from/for.""" assert await async_setup_component( @@ -1269,3 +1296,64 @@ async def test_attribute_if_fires_on_entity_change_with_both_filters_boolean( hass.states.async_set("test.entity", "bla", {"happening": True}) await hass.async_block_till_done() assert len(calls) == 1 + + +async def test_variables_priority(hass, calls): + """Test an externally defined trigger variable is overridden.""" + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: { + "trigger_variables": {"trigger": "illegal"}, + "trigger": { + "platform": "state", + "entity_id": ["test.entity_1", "test.entity_2"], + "to": "world", + "for": '{{ 5 if trigger.entity_id == "test.entity_1"' + " else 10 }}", + }, + "action": { + "service": "test.automation", + "data_template": { + "some": "{{ trigger.entity_id }} - {{ trigger.for }}" + }, + }, + } + }, + ) + await hass.async_block_till_done() + + utcnow = dt_util.utcnow() + with patch("homeassistant.core.dt_util.utcnow") as mock_utcnow: + mock_utcnow.return_value = utcnow + hass.states.async_set("test.entity_1", "world") + await hass.async_block_till_done() + mock_utcnow.return_value += timedelta(seconds=1) + async_fire_time_changed(hass, mock_utcnow.return_value) + hass.states.async_set("test.entity_2", "world") + await hass.async_block_till_done() + mock_utcnow.return_value += timedelta(seconds=1) + async_fire_time_changed(hass, mock_utcnow.return_value) + hass.states.async_set("test.entity_2", "hello") + await hass.async_block_till_done() + mock_utcnow.return_value += timedelta(seconds=1) + async_fire_time_changed(hass, mock_utcnow.return_value) + hass.states.async_set("test.entity_2", "world") + await hass.async_block_till_done() + assert len(calls) == 0 + mock_utcnow.return_value += timedelta(seconds=3) + async_fire_time_changed(hass, mock_utcnow.return_value) + await hass.async_block_till_done() + assert len(calls) == 1 + assert calls[0].data["some"] == "test.entity_1 - 0:00:05" + + mock_utcnow.return_value += timedelta(seconds=3) + async_fire_time_changed(hass, mock_utcnow.return_value) + await hass.async_block_till_done() + assert len(calls) == 1 + mock_utcnow.return_value += timedelta(seconds=5) + async_fire_time_changed(hass, mock_utcnow.return_value) + await hass.async_block_till_done() + assert len(calls) == 2 + assert calls[1].data["some"] == "test.entity_2 - 0:00:10"