diff --git a/homeassistant/components/homeassistant/triggers/state.py b/homeassistant/components/homeassistant/triggers/state.py index c580b8daf49..dad7314f4fa 100644 --- a/homeassistant/components/homeassistant/triggers/state.py +++ b/homeassistant/components/homeassistant/triggers/state.py @@ -37,7 +37,10 @@ TRIGGER_SCHEMA = vol.All( vol.Optional(CONF_ATTRIBUTE): cv.match_all, } ), - cv.key_dependency(CONF_FOR, CONF_TO), + vol.Any( + cv.key_dependency(CONF_FOR, CONF_TO), + cv.key_dependency(CONF_FOR, CONF_FROM), + ), ) @@ -141,6 +144,9 @@ async def async_attach_trigger( else: cur_value = new_st.attributes.get(attribute) + if CONF_TO not in config: + return cur_value != old_value + return cur_value == new_value unsub_track_same[entity] = async_track_same_state( diff --git a/tests/components/homeassistant/triggers/test_state.py b/tests/components/homeassistant/triggers/test_state.py index 9cce567ca68..0ae7c340ea8 100644 --- a/tests/components/homeassistant/triggers/test_state.py +++ b/tests/components/homeassistant/triggers/test_state.py @@ -925,6 +925,64 @@ async def test_if_fires_on_change_with_for_template_3(hass, calls): 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( + hass, + automation.DOMAIN, + { + automation.DOMAIN: { + "trigger": { + "platform": "state", + "entity_id": "media_player.foo", + "from": "playing", + "for": "00:00:30", + }, + "action": {"service": "test.automation"}, + } + }, + ) + + hass.states.async_set("media_player.foo", "playing") + await hass.async_block_till_done() + hass.states.async_set("media_player.foo", "paused") + await hass.async_block_till_done() + hass.states.async_set("media_player.foo", "stopped") + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=1)) + await hass.async_block_till_done() + assert len(calls) == 1 + + +async def test_if_not_fires_on_change_from_with_for(hass, calls): + """Test for firing on change with from/for.""" + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: { + "trigger": { + "platform": "state", + "entity_id": "media_player.foo", + "from": "playing", + "for": "00:00:30", + }, + "action": {"service": "test.automation"}, + } + }, + ) + + hass.states.async_set("media_player.foo", "playing") + await hass.async_block_till_done() + hass.states.async_set("media_player.foo", "paused") + await hass.async_block_till_done() + hass.states.async_set("media_player.foo", "playing") + await hass.async_block_till_done() + async_fire_time_changed(hass, dt_util.utcnow() + timedelta(minutes=1)) + await hass.async_block_till_done() + assert len(calls) == 0 + + async def test_invalid_for_template_1(hass, calls): """Test for invalid for template.""" assert await async_setup_component(