diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index b2385d827a1..981e0988639 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -437,8 +437,9 @@ def async_template_from_config( def time( - before: Optional[dt_util.dt.time] = None, - after: Optional[dt_util.dt.time] = None, + hass: HomeAssistant, + before: Optional[Union[dt_util.dt.time, str]] = None, + after: Optional[Union[dt_util.dt.time, str]] = None, weekday: Union[None, str, Container[str]] = None, ) -> bool: """Test if local time condition matches. @@ -453,8 +454,28 @@ def time( if after is None: after = dt_util.dt.time(0) + elif isinstance(after, str): + after_entity = hass.states.get(after) + if not after_entity: + return False + after = dt_util.dt.time( + after_entity.attributes.get("hour", 23), + after_entity.attributes.get("minute", 59), + after_entity.attributes.get("second", 59), + ) + if before is None: before = dt_util.dt.time(23, 59, 59, 999999) + elif isinstance(before, str): + before_entity = hass.states.get(before) + if not before_entity: + return False + before = dt_util.dt.time( + before_entity.attributes.get("hour", 23), + before_entity.attributes.get("minute", 59), + before_entity.attributes.get("second", 59), + 999999, + ) if after < before: if not after <= now_time < before: @@ -488,7 +509,7 @@ def time_from_config( def time_if(hass: HomeAssistant, variables: TemplateVarsType = None) -> bool: """Validate time based if-condition.""" - return time(before, after, weekday) + return time(hass, before, after, weekday) return time_if diff --git a/homeassistant/helpers/config_validation.py b/homeassistant/helpers/config_validation.py index 394500b5170..dd5a8b6522c 100644 --- a/homeassistant/helpers/config_validation.py +++ b/homeassistant/helpers/config_validation.py @@ -956,8 +956,8 @@ TIME_CONDITION_SCHEMA = vol.All( vol.Schema( { vol.Required(CONF_CONDITION): "time", - "before": time, - "after": time, + "before": vol.Any(time, vol.All(str, entity_domain("input_datetime"))), + "after": vol.Any(time, vol.All(str, entity_domain("input_datetime"))), "weekday": weekdays, } ), diff --git a/tests/helpers/test_condition.py b/tests/helpers/test_condition.py index 4b02faec573..afe1c294290 100644 --- a/tests/helpers/test_condition.py +++ b/tests/helpers/test_condition.py @@ -5,6 +5,7 @@ import pytest from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import condition +from homeassistant.setup import async_setup_component from homeassistant.util import dt from tests.async_mock import patch @@ -226,29 +227,118 @@ async def test_time_window(hass): "homeassistant.helpers.condition.dt_util.now", return_value=dt.now().replace(hour=3), ): - assert not condition.time(after=sixam, before=sixpm) - assert condition.time(after=sixpm, before=sixam) + assert not condition.time(hass, after=sixam, before=sixpm) + assert condition.time(hass, after=sixpm, before=sixam) with patch( "homeassistant.helpers.condition.dt_util.now", return_value=dt.now().replace(hour=9), ): - assert condition.time(after=sixam, before=sixpm) - assert not condition.time(after=sixpm, before=sixam) + assert condition.time(hass, after=sixam, before=sixpm) + assert not condition.time(hass, after=sixpm, before=sixam) with patch( "homeassistant.helpers.condition.dt_util.now", return_value=dt.now().replace(hour=15), ): - assert condition.time(after=sixam, before=sixpm) - assert not condition.time(after=sixpm, before=sixam) + assert condition.time(hass, after=sixam, before=sixpm) + assert not condition.time(hass, after=sixpm, before=sixam) with patch( "homeassistant.helpers.condition.dt_util.now", return_value=dt.now().replace(hour=21), ): - assert not condition.time(after=sixam, before=sixpm) - assert condition.time(after=sixpm, before=sixam) + assert not condition.time(hass, after=sixam, before=sixpm) + assert condition.time(hass, after=sixpm, before=sixam) + + +async def test_time_using_input_datetime(hass): + """Test time conditions using input_datetime entities.""" + await async_setup_component( + hass, + "input_datetime", + { + "input_datetime": { + "am": {"has_date": True, "has_time": True}, + "pm": {"has_date": True, "has_time": True}, + } + }, + ) + + await hass.services.async_call( + "input_datetime", + "set_datetime", + { + "entity_id": "input_datetime.am", + "datetime": str( + dt.now() + .replace(hour=6, minute=0, second=0, microsecond=0) + .replace(tzinfo=None) + ), + }, + blocking=True, + ) + + await hass.services.async_call( + "input_datetime", + "set_datetime", + { + "entity_id": "input_datetime.pm", + "datetime": str( + dt.now() + .replace(hour=18, minute=0, second=0, microsecond=0) + .replace(tzinfo=None) + ), + }, + blocking=True, + ) + + with patch( + "homeassistant.helpers.condition.dt_util.now", + return_value=dt.now().replace(hour=3), + ): + assert not condition.time( + hass, after="input_datetime.am", before="input_datetime.pm" + ) + assert condition.time( + hass, after="input_datetime.pm", before="input_datetime.am" + ) + + with patch( + "homeassistant.helpers.condition.dt_util.now", + return_value=dt.now().replace(hour=9), + ): + assert condition.time( + hass, after="input_datetime.am", before="input_datetime.pm" + ) + assert not condition.time( + hass, after="input_datetime.pm", before="input_datetime.am" + ) + + with patch( + "homeassistant.helpers.condition.dt_util.now", + return_value=dt.now().replace(hour=15), + ): + assert condition.time( + hass, after="input_datetime.am", before="input_datetime.pm" + ) + assert not condition.time( + hass, after="input_datetime.pm", before="input_datetime.am" + ) + + with patch( + "homeassistant.helpers.condition.dt_util.now", + return_value=dt.now().replace(hour=21), + ): + assert not condition.time( + hass, after="input_datetime.am", before="input_datetime.pm" + ) + assert condition.time( + hass, after="input_datetime.pm", before="input_datetime.am" + ) + + assert not condition.time(hass, after="input_datetime.not_existing") + assert not condition.time(hass, before="input_datetime.not_existing") async def test_if_numeric_state_not_raise_on_unavailable(hass):