Use input_weekday in automations

This commit is contained in:
Wendelin
2025-10-09 08:48:41 +02:00
parent 77f078e57d
commit a32ada3155
4 changed files with 223 additions and 11 deletions

View File

@@ -72,15 +72,21 @@ _TIME_TRIGGER_SCHEMA = vol.Any(
),
)
_WEEKDAY_SCHEMA = vol.Any(
vol.In(WEEKDAYS),
vol.All(cv.ensure_list, [vol.In(WEEKDAYS)]),
cv.entity_domain(["input_weekday"]),
msg=(
"Expected a weekday (mon, tue, wed, thu, fri, sat, sun), "
"a list of weekdays, or an Entity ID with domain 'input_weekday'"
),
)
TRIGGER_SCHEMA = cv.TRIGGER_BASE_SCHEMA.extend(
{
vol.Required(CONF_PLATFORM): "time",
vol.Required(CONF_AT): vol.All(cv.ensure_list, [_TIME_TRIGGER_SCHEMA]),
vol.Optional(CONF_WEEKDAY): vol.Any(
vol.In(WEEKDAYS),
vol.All(cv.ensure_list, [vol.In(WEEKDAYS)]),
),
vol.Optional(CONF_WEEKDAY): _WEEKDAY_SCHEMA,
}
)
@@ -117,7 +123,14 @@ async def async_attach_trigger( # noqa: C901
# Check if current weekday matches the configuration
if isinstance(weekday_config, str):
if current_weekday != weekday_config:
# Could be a single weekday string or an entity_id
if weekday_config.startswith("input_weekday."):
if (weekday_state := hass.states.get(weekday_config)) is None:
return
entity_weekdays = weekday_state.attributes.get("weekdays", [])
if current_weekday not in entity_weekdays:
return
elif current_weekday != weekday_config:
return
elif current_weekday not in weekday_config:
return

View File

@@ -954,6 +954,20 @@ def time(
if weekday is not None:
now_weekday = WEEKDAYS[now.weekday()]
# Check if weekday is an entity_id
if isinstance(weekday, str) and weekday.startswith("input_weekday."):
if (weekday_state := hass.states.get(weekday)) is None:
condition_trace_update_result(weekday=weekday, now_weekday=now_weekday)
return False
entity_weekdays = weekday_state.attributes.get("weekdays", [])
condition_trace_update_result(
weekday=weekday,
now_weekday=now_weekday,
entity_weekdays=entity_weekdays,
)
if now_weekday not in entity_weekdays:
return False
else:
condition_trace_update_result(weekday=weekday, now_weekday=now_weekday)
if (
isinstance(weekday, str) and weekday != now_weekday

View File

@@ -843,7 +843,10 @@ def time_zone(value: str) -> str:
)
weekdays = vol.All(ensure_list, [vol.In(WEEKDAYS)])
weekdays = vol.Any(
vol.All(ensure_list, [vol.In(WEEKDAYS)]),
entity_domain(["input_weekday"]),
)
def socket_timeout(value: Any | None) -> object:

View File

@@ -1061,6 +1061,14 @@ def test_weekday_validation() -> None:
}
time.TRIGGER_SCHEMA(valid_config)
# Valid input_weekday entity
valid_config = {
"platform": "time",
"at": "5:00:00",
"weekday": "input_weekday.workdays",
}
time.TRIGGER_SCHEMA(valid_config)
# Invalid weekday
invalid_config = {"platform": "time", "at": "5:00:00", "weekday": "invalid"}
with pytest.raises(vol.Invalid):
@@ -1074,3 +1082,177 @@ def test_weekday_validation() -> None:
}
with pytest.raises(vol.Invalid):
time.TRIGGER_SCHEMA(invalid_config)
# Invalid entity domain
invalid_config = {
"platform": "time",
"at": "5:00:00",
"weekday": "input_boolean.my_bool",
}
with pytest.raises(vol.Invalid):
time.TRIGGER_SCHEMA(invalid_config)
async def test_if_fires_using_weekday_input_weekday_entity(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
service_calls: list[ServiceCall],
) -> None:
"""Test for firing on weekday using input_weekday entity."""
# Setup input_weekday helper
await async_setup_component(
hass,
"input_weekday",
{"input_weekday": {"workdays": {"name": "Work Days"}}},
)
# Set workdays to Mon, Tue, Wed
hass.states.async_set(
"input_weekday.workdays",
"3",
{"weekdays": ["mon", "tue", "wed"], "editable": True},
)
# Freeze time to Monday, January 2, 2023 at 5:00:00
monday_trigger = dt_util.as_utc(datetime(2023, 1, 2, 5, 0, 0, 0))
freezer.move_to(monday_trigger)
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "time",
"at": "5:00:00",
"weekday": "input_weekday.workdays",
},
"action": {
"service": "test.automation",
"data_template": {
"some": "{{ trigger.platform }} - {{ trigger.now.strftime('%A') }}",
},
},
}
},
)
await hass.async_block_till_done()
# Fire on Monday - should trigger (Monday is in workdays)
async_fire_time_changed(hass, monday_trigger + timedelta(seconds=1))
await hass.async_block_till_done()
automation_calls = [call for call in service_calls if call.domain == "test"]
assert len(automation_calls) == 1
assert "Monday" in automation_calls[0].data["some"]
# Fire on Tuesday - should trigger (Tuesday is in workdays)
tuesday_trigger = dt_util.as_utc(datetime(2023, 1, 3, 5, 0, 0, 0))
async_fire_time_changed(hass, tuesday_trigger)
await hass.async_block_till_done()
automation_calls = [call for call in service_calls if call.domain == "test"]
assert len(automation_calls) == 2
assert "Tuesday" in automation_calls[1].data["some"]
# Fire on Thursday - should not trigger (Thursday is not in workdays)
thursday_trigger = dt_util.as_utc(datetime(2023, 1, 5, 5, 0, 0, 0))
async_fire_time_changed(hass, thursday_trigger)
await hass.async_block_till_done()
automation_calls = [call for call in service_calls if call.domain == "test"]
assert len(automation_calls) == 2
# Fire on Saturday - should not trigger (Saturday is not in workdays)
saturday_trigger = dt_util.as_utc(datetime(2023, 1, 7, 5, 0, 0, 0))
async_fire_time_changed(hass, saturday_trigger)
await hass.async_block_till_done()
automation_calls = [call for call in service_calls if call.domain == "test"]
assert len(automation_calls) == 2
async def test_if_action_weekday_input_weekday_entity(
hass: HomeAssistant, service_calls: list[ServiceCall]
) -> None:
"""Test time condition with input_weekday entity."""
# Setup input_weekday helper
await async_setup_component(
hass,
"input_weekday",
{"input_weekday": {"weekend": {"name": "Weekend Days"}}},
)
# Set weekend to Sat, Sun
hass.states.async_set(
"input_weekday.weekend",
"2",
{"weekdays": ["sat", "sun"], "editable": True},
)
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {"platform": "event", "event_type": "test_event"},
"condition": {"condition": "time", "weekday": "input_weekday.weekend"},
"action": {"service": "test.automation"},
}
},
)
await hass.async_block_till_done()
days_past_monday = dt_util.now().weekday()
monday = dt_util.now() - timedelta(days=days_past_monday)
saturday = monday + timedelta(days=5)
sunday = saturday + timedelta(days=1)
# Test on Monday - should not trigger (not in weekend)
with patch("homeassistant.helpers.condition.dt_util.now", return_value=monday):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(service_calls) == 0
# Test on Saturday - should trigger
with patch("homeassistant.helpers.condition.dt_util.now", return_value=saturday):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(service_calls) == 1
# Test on Sunday - should trigger
with patch("homeassistant.helpers.condition.dt_util.now", return_value=sunday):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(service_calls) == 2
async def test_if_fires_weekday_entity_unavailable(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
service_calls: list[ServiceCall],
) -> None:
"""Test that trigger does not fire when input_weekday entity is unavailable."""
# Freeze time to Monday, January 2, 2023 at 5:00:00
monday_trigger = dt_util.as_utc(datetime(2023, 1, 2, 5, 0, 0, 0))
freezer.move_to(monday_trigger)
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"trigger": {
"platform": "time",
"at": "5:00:00",
"weekday": "input_weekday.nonexistent",
},
"action": {
"service": "test.automation",
},
}
},
)
await hass.async_block_till_done()
# Fire on Monday - should not trigger (entity doesn't exist)
async_fire_time_changed(hass, monday_trigger + timedelta(seconds=1))
await hass.async_block_till_done()
automation_calls = [call for call in service_calls if call.domain == "test"]
assert len(automation_calls) == 0