Fix before sunrise OR after sunset condition (#76143)

Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Ben Randall <veleek@gmail.com>
This commit is contained in:
Janick Bergeron 2022-10-14 04:11:10 -07:00 committed by GitHub
parent a534c136a1
commit f21a004aa9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 157 additions and 20 deletions

View File

@ -586,31 +586,46 @@ def sun(
before_offset = before_offset or timedelta(0) before_offset = before_offset or timedelta(0)
after_offset = after_offset or timedelta(0) after_offset = after_offset or timedelta(0)
sunrise_today = get_astral_event_date(hass, SUN_EVENT_SUNRISE, today) sunrise = get_astral_event_date(hass, SUN_EVENT_SUNRISE, today)
sunset_today = get_astral_event_date(hass, SUN_EVENT_SUNSET, today) sunset = get_astral_event_date(hass, SUN_EVENT_SUNSET, today)
sunrise = sunrise_today has_sunrise_condition = SUN_EVENT_SUNRISE in (before, after)
sunset = sunset_today has_sunset_condition = SUN_EVENT_SUNSET in (before, after)
if today > dt_util.as_local(
cast(datetime, sunrise_today)
).date() and SUN_EVENT_SUNRISE in (before, after):
tomorrow = dt_util.as_local(utcnow + timedelta(days=1)).date()
sunrise_tomorrow = get_astral_event_date(hass, SUN_EVENT_SUNRISE, tomorrow)
sunrise = sunrise_tomorrow
if today > dt_util.as_local( after_sunrise = today > dt_util.as_local(cast(datetime, sunrise)).date()
cast(datetime, sunset_today) if after_sunrise and has_sunrise_condition:
).date() and SUN_EVENT_SUNSET in (before, after): tomorrow = today + timedelta(days=1)
tomorrow = dt_util.as_local(utcnow + timedelta(days=1)).date() sunrise = get_astral_event_date(hass, SUN_EVENT_SUNRISE, tomorrow)
sunset_tomorrow = get_astral_event_date(hass, SUN_EVENT_SUNSET, tomorrow)
sunset = sunset_tomorrow
if sunrise is None and SUN_EVENT_SUNRISE in (before, after): after_sunset = today > dt_util.as_local(cast(datetime, 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
if sunrise is None and has_sunrise_condition:
# There is no sunrise today # There is no sunrise today
condition_trace_set_result(False, message="no sunrise today") condition_trace_set_result(False, message="no sunrise today")
return False return False
if sunset is None and SUN_EVENT_SUNSET in (before, after): if sunset is None and has_sunset_condition:
# There is no sunset today # There is no sunset today
condition_trace_set_result(False, message="no sunset today") condition_trace_set_result(False, message="no sunset today")
return False return False

View File

@ -2735,9 +2735,9 @@ async def test_if_action_after_sunset_with_offset(hass, hass_ws_client, calls):
) )
async def test_if_action_before_and_after_during(hass, hass_ws_client, calls): async def test_if_action_after_and_before_during(hass, hass_ws_client, calls):
""" """
Test if action was after sunset and before sunrise. Test if action was after sunrise and before sunset.
This is true from sunrise until sunset. This is true from sunrise until sunset.
""" """
@ -2837,6 +2837,128 @@ async def test_if_action_before_and_after_during(hass, hass_ws_client, calls):
) )
async def test_if_action_before_or_after_during(hass, hass_ws_client, calls):
"""
Test if action was before sunrise or after sunset.
This is true from midnight until sunrise and from sunset until midnight
"""
await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: {
"id": "sun",
"trigger": {"platform": "event", "event_type": "test_event"},
"condition": {
"condition": "sun",
"before": SUN_EVENT_SUNRISE,
"after": SUN_EVENT_SUNSET,
},
"action": {"service": "test.automation"},
}
},
)
# sunrise: 2015-09-16 06:33:18 local, sunset: 2015-09-16 18:53:45 local
# sunrise: 2015-09-16 13:33:18 UTC, sunset: 2015-09-17 01:53:45 UTC
# now = sunrise - 1s -> 'before sunrise' | 'after sunset' true
now = datetime(2015, 9, 16, 13, 33, 17, tzinfo=dt_util.UTC)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 1
await assert_automation_condition_trace(
hass_ws_client,
"sun",
{
"result": True,
"wanted_time_after": "2015-09-17T01:53:44.723614+00:00",
"wanted_time_before": "2015-09-16T13:33:18.342542+00:00",
},
)
# now = sunset + 1s -> 'before sunrise' | 'after sunset' true
now = datetime(2015, 9, 17, 1, 53, 46, tzinfo=dt_util.UTC)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 2
await assert_automation_condition_trace(
hass_ws_client,
"sun",
{
"result": True,
"wanted_time_after": "2015-09-17T01:53:44.723614+00:00",
"wanted_time_before": "2015-09-16T13:33:18.342542+00:00",
},
)
# now = sunrise + 1s -> 'before sunrise' | 'after sunset' false
now = datetime(2015, 9, 16, 13, 33, 19, tzinfo=dt_util.UTC)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 2
await assert_automation_condition_trace(
hass_ws_client,
"sun",
{
"result": False,
"wanted_time_after": "2015-09-17T01:53:44.723614+00:00",
"wanted_time_before": "2015-09-16T13:33:18.342542+00:00",
},
)
# now = sunset - 1s -> 'before sunrise' | 'after sunset' false
now = datetime(2015, 9, 17, 1, 53, 44, tzinfo=dt_util.UTC)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 2
await assert_automation_condition_trace(
hass_ws_client,
"sun",
{
"result": False,
"wanted_time_after": "2015-09-17T01:53:44.723614+00:00",
"wanted_time_before": "2015-09-16T13:33:18.342542+00:00",
},
)
# now = midnight + 1s local -> 'before sunrise' | 'after sunset' true
now = datetime(2015, 9, 16, 7, 0, 1, tzinfo=dt_util.UTC)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 3
await assert_automation_condition_trace(
hass_ws_client,
"sun",
{
"result": True,
"wanted_time_after": "2015-09-17T01:53:44.723614+00:00",
"wanted_time_before": "2015-09-16T13:33:18.342542+00:00",
},
)
# now = midnight - 1s local -> 'before sunrise' | 'after sunset' true
now = datetime(2015, 9, 17, 6, 59, 59, tzinfo=dt_util.UTC)
with patch("homeassistant.util.dt.utcnow", return_value=now):
hass.bus.async_fire("test_event")
await hass.async_block_till_done()
assert len(calls) == 4
await assert_automation_condition_trace(
hass_ws_client,
"sun",
{
"result": True,
"wanted_time_after": "2015-09-17T01:53:44.723614+00:00",
"wanted_time_before": "2015-09-16T13:33:18.342542+00:00",
},
)
async def test_if_action_before_sunrise_no_offset_kotzebue(hass, hass_ws_client, calls): async def test_if_action_before_sunrise_no_offset_kotzebue(hass, hass_ws_client, calls):
""" """
Test if action was before sunrise. Test if action was before sunrise.