mirror of
https://github.com/home-assistant/core.git
synced 2025-11-08 18:39:30 +00:00
Use input_weekday in automations
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user