Skip to content

Commit

Permalink
Support time entities in time conditions (#124575)
Browse files Browse the repository at this point in the history
Co-authored-by: Mark Bergsma <[email protected]>
  • Loading branch information
silamon and markbergsma authored Nov 26, 2024
1 parent 147679f commit ee74a35
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 8 deletions.
24 changes: 18 additions & 6 deletions homeassistant/helpers/condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
):
Expand All @@ -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,
):
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/helpers/config_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
79 changes: 79 additions & 0 deletions tests/helpers/test_condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
CONF_CONDITION,
CONF_DEVICE_ID,
CONF_DOMAIN,
STATE_UNAVAILABLE,
STATE_UNKNOWN,
SUN_EVENT_SUNRISE,
SUN_EVENT_SUNSET,
)
Expand Down Expand Up @@ -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(
Expand Down

0 comments on commit ee74a35

Please sign in to comment.