From ee74a3541734593b6799a8cd4408a4f12eaa83a7 Mon Sep 17 00:00:00 2001 From: Simon Lamon <32477463+silamon@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:37:31 +0100 Subject: [PATCH] Support time entities in time conditions (#124575) Co-authored-by: Mark Bergsma --- homeassistant/helpers/condition.py | 24 +++++-- homeassistant/helpers/config_validation.py | 4 +- tests/helpers/test_condition.py | 79 ++++++++++++++++++++++ 3 files changed, 99 insertions(+), 8 deletions(-) diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index 86965f86d40244..5952e28a1eb9b5 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -821,9 +821,15 @@ def time( after_entity.attributes.get("minute", 59), after_entity.attributes.get("second", 59), ) - elif after_entity.attributes.get( - ATTR_DEVICE_CLASS - ) == SensorDeviceClass.TIMESTAMP and after_entity.state not in ( + elif after_entity.domain == "time" and after_entity.state not in ( + STATE_UNAVAILABLE, + STATE_UNKNOWN, + ): + after = datetime.strptime(after_entity.state, "%H:%M:%S").time() + elif ( + after_entity.attributes.get(ATTR_DEVICE_CLASS) + == SensorDeviceClass.TIMESTAMP + ) and after_entity.state not in ( STATE_UNAVAILABLE, STATE_UNKNOWN, ): @@ -845,9 +851,15 @@ def time( before_entity.attributes.get("minute", 59), before_entity.attributes.get("second", 59), ) - elif before_entity.attributes.get( - ATTR_DEVICE_CLASS - ) == SensorDeviceClass.TIMESTAMP and before_entity.state not in ( + elif before_entity.domain == "time": + try: + before = datetime.strptime(before_entity.state, "%H:%M:%S").time() + except ValueError: + return False + elif ( + before_entity.attributes.get(ATTR_DEVICE_CLASS) + == SensorDeviceClass.TIMESTAMP + ) and before_entity.state not in ( STATE_UNAVAILABLE, STATE_UNKNOWN, ): diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 2b35ebade761b7..3681e941eee8d4 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -1574,10 +1574,10 @@ def STATE_CONDITION_SCHEMA(value: Any) -> dict[str, Any]: **CONDITION_BASE_SCHEMA, vol.Required(CONF_CONDITION): "time", vol.Optional("before"): vol.Any( - time, vol.All(str, entity_domain(["input_datetime", "sensor"])) + time, vol.All(str, entity_domain(["input_datetime", "time", "sensor"])) ), vol.Optional("after"): vol.Any( - time, vol.All(str, entity_domain(["input_datetime", "sensor"])) + time, vol.All(str, entity_domain(["input_datetime", "time", "sensor"])) ), vol.Optional("weekday"): weekdays, } diff --git a/tests/helpers/test_condition.py b/tests/helpers/test_condition.py index 31f813469cc550..1ec78b20535732 100644 --- a/tests/helpers/test_condition.py +++ b/tests/helpers/test_condition.py @@ -15,6 +15,8 @@ CONF_CONDITION, CONF_DEVICE_ID, CONF_DOMAIN, + STATE_UNAVAILABLE, + STATE_UNKNOWN, SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET, ) @@ -992,6 +994,83 @@ async def test_time_using_input_datetime(hass: HomeAssistant) -> None: condition.time(hass, before="input_datetime.not_existing") +async def test_time_using_time(hass: HomeAssistant) -> None: + """Test time conditions using time entities.""" + hass.states.async_set( + "time.am", + "06:00:00", # 6 am local time + ) + hass.states.async_set( + "time.pm", + "18:00:00", # 6 pm local time + ) + hass.states.async_set( + "time.unknown_state", + STATE_UNKNOWN, + ) + hass.states.async_set( + "time.unavailable_state", + STATE_UNAVAILABLE, + ) + + with patch( + "homeassistant.helpers.condition.dt_util.now", + return_value=dt_util.now().replace(hour=3), + ): + assert not condition.time(hass, after="time.am", before="time.pm") + assert condition.time(hass, after="time.pm", before="time.am") + + with patch( + "homeassistant.helpers.condition.dt_util.now", + return_value=dt_util.now().replace(hour=9), + ): + assert condition.time(hass, after="time.am", before="time.pm") + assert not condition.time(hass, after="time.pm", before="time.am") + + with patch( + "homeassistant.helpers.condition.dt_util.now", + return_value=dt_util.now().replace(hour=15), + ): + assert condition.time(hass, after="time.am", before="time.pm") + assert not condition.time(hass, after="time.pm", before="time.am") + + with patch( + "homeassistant.helpers.condition.dt_util.now", + return_value=dt_util.now().replace(hour=21), + ): + assert not condition.time(hass, after="time.am", before="time.pm") + assert condition.time(hass, after="time.pm", before="time.am") + + # Trigger on PM time + with patch( + "homeassistant.helpers.condition.dt_util.now", + return_value=dt_util.now().replace(hour=18, minute=0, second=0), + ): + assert condition.time(hass, after="time.pm", before="time.am") + assert not condition.time(hass, after="time.am", before="time.pm") + assert condition.time(hass, after="time.pm") + assert not condition.time(hass, before="time.pm") + + # Trigger on AM time + with patch( + "homeassistant.helpers.condition.dt_util.now", + return_value=dt_util.now().replace(hour=6, minute=0, second=0), + ): + assert not condition.time(hass, after="time.pm", before="time.am") + assert condition.time(hass, after="time.am", before="time.pm") + assert condition.time(hass, after="time.am") + assert not condition.time(hass, before="time.am") + + assert not condition.time(hass, after="time.unknown_state") + assert not condition.time(hass, before="time.unavailable_state") + + with pytest.raises(ConditionError): + condition.time(hass, after="time.not_existing") + + with pytest.raises(ConditionError): + condition.time(hass, before="time.not_existing") + + async def test_time_using_sensor(hass: HomeAssistant) -> None: """Test time conditions using sensor entities.""" hass.states.async_set(