Fix exception in legacy sun condition (#174811)

This commit is contained in:
Erik Montnemery
2026-06-27 11:28:33 +00:00
committed by Franck Nijhof
parent f20f86a067
commit 70aba68326
2 changed files with 19 additions and 36 deletions
+14 -20
View File
@@ -85,34 +85,18 @@ def sun(
has_sunrise_condition = SUN_EVENT_SUNRISE in (before, after)
has_sunset_condition = SUN_EVENT_SUNSET in (before, after)
after_sunrise = today > dt_util.as_local(cast(datetime, sunrise)).date()
after_sunrise = sunrise is not None and today > dt_util.as_local(sunrise).date()
if after_sunrise and has_sunrise_condition:
tomorrow = today + timedelta(days=1)
sunrise = get_astral_event_date(hass, SUN_EVENT_SUNRISE, tomorrow)
after_sunset = today > dt_util.as_local(cast(datetime, sunset)).date()
after_sunset = sunset is not None and today > dt_util.as_local(sunset).date()
if after_sunset and has_sunset_condition:
tomorrow = today + timedelta(days=1)
sunset = get_astral_event_date(hass, SUN_EVENT_SUNSET, tomorrow)
# Special case: before sunrise OR after sunset
# This will handle the very rare case in the polar region when the sun rises/sets
# but does not set/rise.
# However this entire condition does not handle those full days of darkness
# or light, the following should be used instead:
#
# condition:
# condition: state
# entity_id: sun.sun
# state: 'above_horizon' (or 'below_horizon')
#
if before == SUN_EVENT_SUNRISE and after == SUN_EVENT_SUNSET:
wanted_time_before = cast(datetime, sunrise) + before_offset
condition_trace_update_result(wanted_time_before=wanted_time_before)
wanted_time_after = cast(datetime, sunset) + after_offset
condition_trace_update_result(wanted_time_after=wanted_time_after)
return utcnow < wanted_time_before or utcnow > wanted_time_after
# A missing sunrise/sunset means the sun doesn't rise/set on this day, which
# happens in polar regions.
if sunrise is None and has_sunrise_condition:
# There is no sunrise today
condition_trace_set_result(False, message="no sunrise today")
@@ -123,6 +107,16 @@ def sun(
condition_trace_set_result(False, message="no sunset today")
return False
# "before: sunrise" combined with "after: sunset" describes the dark period
# around midnight, so it is evaluated as an OR (true before sunrise or after
# sunset) rather than the usual AND of the two bounds.
if before == SUN_EVENT_SUNRISE and after == SUN_EVENT_SUNSET:
wanted_time_before = cast(datetime, sunrise) + before_offset
condition_trace_update_result(wanted_time_before=wanted_time_before)
wanted_time_after = cast(datetime, sunset) + after_offset
condition_trace_update_result(wanted_time_after=wanted_time_after)
return utcnow < wanted_time_before or utcnow > wanted_time_after
if before == SUN_EVENT_SUNRISE:
wanted_time_before = cast(datetime, sunrise) + before_offset
condition_trace_update_result(wanted_time_before=wanted_time_before)
+5 -16
View File
@@ -85,16 +85,6 @@ async def assert_automation_condition_trace(hass_ws_client, automation_id, expec
assert condition_trace["result"] == expected
async def assert_automation_condition_trace_error(
hass_ws_client, automation_id, expected
):
"""Test the error of automation condition."""
condition_trace = await _get_automation_condition_trace(
hass_ws_client, automation_id
)
assert condition_trace["error"] == expected
async def test_if_action_before_sunrise_no_offset(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
@@ -1299,10 +1289,9 @@ async def test_if_action_no_sun_event_in_polar_regions(
"""Test a sun condition where the requested event never occurs.
During midnight sun and polar night the sun neither rises nor sets, so
``get_astral_event_date`` returns None for both events. This documents the
legacy condition crashing on the missing event (it passes None to
``dt_util.as_local``); the crash fix and the matching "no sunrise/sunset
today" results are a follow-up.
``get_astral_event_date`` returns None for the requested event. The
condition cannot be satisfied and reports "no sunrise today" / "no sunset
today" instead of raising.
"""
latitude, longitude, time_zone = location
await hass.config.async_set_time_zone(time_zone)
@@ -1328,10 +1317,10 @@ async def test_if_action_no_sun_event_in_polar_regions(
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(service_calls) == 0
await assert_automation_condition_trace_error(
await assert_automation_condition_trace(
hass_ws_client,
"sun",
"'NoneType' object has no attribute 'tzinfo'",
{"result": False, "message": f"no {event} today"},
)