diff --git a/homeassistant/components/cover/device_trigger.py b/homeassistant/components/cover/device_trigger.py index 119bfc835f586c..f9f78d6537bf19 100644 --- a/homeassistant/components/cover/device_trigger.py +++ b/homeassistant/components/cover/device_trigger.py @@ -16,6 +16,7 @@ CONF_DEVICE_ID, CONF_DOMAIN, CONF_ENTITY_ID, + CONF_FOR, CONF_PLATFORM, CONF_TYPE, CONF_VALUE_TEMPLATE, @@ -59,6 +60,7 @@ { vol.Required(CONF_ENTITY_ID): cv.entity_id, vol.Required(CONF_TYPE): vol.In(STATE_TRIGGER_TYPES), + vol.Optional(CONF_FOR): cv.positive_time_period_dict, } ) @@ -146,8 +148,12 @@ async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict]: async def async_get_trigger_capabilities(hass: HomeAssistant, config: dict) -> dict: """List trigger capabilities.""" - if config[CONF_TYPE] not in ["position", "tilt_position"]: - return {} + if config[CONF_TYPE] not in POSITION_TRIGGER_TYPES: + return { + "extra_fields": vol.Schema( + {vol.Optional(CONF_FOR): cv.positive_time_period_dict} + ) + } return { "extra_fields": vol.Schema( @@ -170,8 +176,6 @@ async def async_attach_trigger( automation_info: dict, ) -> CALLBACK_TYPE: """Attach a trigger.""" - config = TRIGGER_SCHEMA(config) - if config[CONF_TYPE] in STATE_TRIGGER_TYPES: if config[CONF_TYPE] == "opened": to_state = STATE_OPEN @@ -187,6 +191,8 @@ async def async_attach_trigger( CONF_ENTITY_ID: config[CONF_ENTITY_ID], state_trigger.CONF_TO: to_state, } + if CONF_FOR in config: + state_config[CONF_FOR] = config[CONF_FOR] state_config = state_trigger.TRIGGER_SCHEMA(state_config) return await state_trigger.async_attach_trigger( hass, state_config, action, automation_info, platform_type="device" diff --git a/tests/components/cover/test_device_trigger.py b/tests/components/cover/test_device_trigger.py index e0f6d6d5fb9b71..7ff5a434e5b81d 100644 --- a/tests/components/cover/test_device_trigger.py +++ b/tests/components/cover/test_device_trigger.py @@ -1,4 +1,6 @@ """The tests for Cover device triggers.""" +from datetime import timedelta + import pytest import homeassistant.components.automation as automation @@ -12,10 +14,12 @@ ) from homeassistant.helpers import device_registry from homeassistant.setup import async_setup_component +import homeassistant.util.dt as dt_util from tests.common import ( MockConfigEntry, assert_lists_same, + async_fire_time_changed, async_get_device_automation_capabilities, async_get_device_automations, async_mock_service, @@ -234,7 +238,11 @@ async def test_get_trigger_capabilities(hass, device_reg, entity_reg): capabilities = await async_get_device_automation_capabilities( hass, "trigger", trigger ) - assert capabilities == {"extra_fields": []} + assert capabilities == { + "extra_fields": [ + {"name": "for", "optional": True, "type": "positive_time_period_dict"} + ] + } async def test_get_trigger_capabilities_set_pos(hass, device_reg, entity_reg): @@ -284,7 +292,15 @@ async def test_get_trigger_capabilities_set_pos(hass, device_reg, entity_reg): if trigger["type"] == "position": assert capabilities == expected_capabilities else: - assert capabilities == {"extra_fields": []} + assert capabilities == { + "extra_fields": [ + { + "name": "for", + "optional": True, + "type": "positive_time_period_dict", + } + ] + } async def test_get_trigger_capabilities_set_tilt_pos(hass, device_reg, entity_reg): @@ -334,7 +350,15 @@ async def test_get_trigger_capabilities_set_tilt_pos(hass, device_reg, entity_re if trigger["type"] == "tilt_position": assert capabilities == expected_capabilities else: - assert capabilities == {"extra_fields": []} + assert capabilities == { + "extra_fields": [ + { + "name": "for", + "optional": True, + "type": "positive_time_period_dict", + } + ] + } async def test_if_fires_on_state_change(hass, calls): @@ -459,6 +483,61 @@ async def test_if_fires_on_state_change(hass, calls): ] == "closing - device - {} - opening - closing - None".format("cover.entity") +async def test_if_fires_on_state_change_with_for(hass, calls): + """Test for triggers firing with delay.""" + entity_id = "cover.entity" + hass.states.async_set(entity_id, STATE_CLOSED) + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + { + "trigger": { + "platform": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": entity_id, + "type": "opened", + "for": {"seconds": 5}, + }, + "action": { + "service": "test.automation", + "data_template": { + "some": "turn_off {{ trigger.%s }}" + % "}} - {{ trigger.".join( + ( + "platform", + "entity_id", + "from_state.state", + "to_state.state", + "for", + ) + ) + }, + }, + } + ] + }, + ) + await hass.async_block_till_done() + assert hass.states.get(entity_id).state == STATE_CLOSED + assert len(calls) == 0 + + hass.states.async_set(entity_id, STATE_OPEN) + 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 + await hass.async_block_till_done() + assert ( + calls[0].data["some"] + == f"turn_off device - {entity_id} - closed - open - 0:00:05" + ) + + async def test_if_fires_on_position(hass, calls): """Test for position triggers.""" platform = getattr(hass.components, f"test.{DOMAIN}")