diff --git a/homeassistant/helpers/condition.py b/homeassistant/helpers/condition.py index b8e08e1ac97..40465f83728 100644 --- a/homeassistant/helpers/condition.py +++ b/homeassistant/helpers/condition.py @@ -321,8 +321,24 @@ def sun( before_offset = before_offset or timedelta(0) after_offset = after_offset or timedelta(0) - sunrise = get_astral_event_date(hass, SUN_EVENT_SUNRISE, today) - sunset = get_astral_event_date(hass, SUN_EVENT_SUNSET, today) + sunrise_today = get_astral_event_date(hass, SUN_EVENT_SUNRISE, today) + sunset_today = get_astral_event_date(hass, SUN_EVENT_SUNSET, today) + + sunrise = sunrise_today + sunset = sunset_today + 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( + cast(datetime, sunset_today) + ).date() and SUN_EVENT_SUNSET in (before, after): + tomorrow = dt_util.as_local(utcnow + timedelta(days=1)).date() + 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): # There is no sunrise today diff --git a/tests/components/automation/test_sun.py b/tests/components/automation/test_sun.py index e04d1599e4c..2668ac97053 100644 --- a/tests/components/automation/test_sun.py +++ b/tests/components/automation/test_sun.py @@ -617,3 +617,227 @@ async def test_if_action_before_and_after_during(hass, calls): hass.bus.async_fire("test_event") await hass.async_block_till_done() assert 3 == len(calls) + + +async def test_if_action_before_sunrise_no_offset_kotzebue(hass, calls): + """ + Test if action was before sunrise. + + Local timezone: Alaska time + Location: Kotzebue, which has a very skewed local timezone with sunrise + at 7 AM and sunset at 3AM during summer + After sunrise is true from sunrise until midnight, local time. + """ + tz = dt_util.get_time_zone("America/Anchorage") + dt_util.set_default_time_zone(tz) + hass.config.latitude = 66.5 + hass.config.longitude = 162.4 + await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: { + "trigger": {"platform": "event", "event_type": "test_event"}, + "condition": {"condition": "sun", "before": SUN_EVENT_SUNRISE}, + "action": {"service": "test.automation"}, + } + }, + ) + + # sunrise: 2015-07-24 07:17:24 local, sunset: 2015-07-25 03:16:27 local + # sunrise: 2015-07-24 15:17:24 UTC, sunset: 2015-07-25 11:16:27 UTC + # now = sunrise + 1s -> 'before sunrise' not true + now = datetime(2015, 7, 24, 15, 17, 25, 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 0 == len(calls) + + # now = sunrise -> 'before sunrise' true + now = datetime(2015, 7, 24, 15, 17, 24, 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 1 == len(calls) + + # now = local midnight -> 'before sunrise' true + now = datetime(2015, 7, 24, 8, 0, 0, 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 2 == len(calls) + + # now = local midnight - 1s -> 'before sunrise' not true + now = datetime(2015, 7, 24, 7, 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 2 == len(calls) + + +async def test_if_action_after_sunrise_no_offset_kotzebue(hass, calls): + """ + Test if action was after sunrise. + + Local timezone: Alaska time + Location: Kotzebue, which has a very skewed local timezone with sunrise + at 7 AM and sunset at 3AM during summer + Before sunrise is true from midnight until sunrise, local time. + """ + tz = dt_util.get_time_zone("America/Anchorage") + dt_util.set_default_time_zone(tz) + hass.config.latitude = 66.5 + hass.config.longitude = 162.4 + await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: { + "trigger": {"platform": "event", "event_type": "test_event"}, + "condition": {"condition": "sun", "after": SUN_EVENT_SUNRISE}, + "action": {"service": "test.automation"}, + } + }, + ) + + # sunrise: 2015-07-24 07:17:24 local, sunset: 2015-07-25 03:16:27 local + # sunrise: 2015-07-24 15:17:24 UTC, sunset: 2015-07-25 11:16:27 UTC + # now = sunrise -> 'after sunrise' true + now = datetime(2015, 7, 24, 15, 17, 24, 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 1 == len(calls) + + # now = sunrise - 1s -> 'after sunrise' not true + now = datetime(2015, 7, 24, 15, 17, 23, 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 1 == len(calls) + + # now = local midnight -> 'after sunrise' not true + now = datetime(2015, 7, 24, 8, 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 1 == len(calls) + + # now = local midnight - 1s -> 'after sunrise' true + now = datetime(2015, 7, 24, 7, 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 2 == len(calls) + + +async def test_if_action_before_sunset_no_offset_kotzebue(hass, calls): + """ + Test if action was before sunrise. + + Local timezone: Alaska time + Location: Kotzebue, which has a very skewed local timezone with sunrise + at 7 AM and sunset at 3AM during summer + Before sunset is true from midnight until sunset, local time. + """ + tz = dt_util.get_time_zone("America/Anchorage") + dt_util.set_default_time_zone(tz) + hass.config.latitude = 66.5 + hass.config.longitude = 162.4 + await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: { + "trigger": {"platform": "event", "event_type": "test_event"}, + "condition": {"condition": "sun", "before": SUN_EVENT_SUNSET}, + "action": {"service": "test.automation"}, + } + }, + ) + + # sunrise: 2015-07-24 07:17:24 local, sunset: 2015-07-25 03:16:27 local + # sunrise: 2015-07-24 15:17:24 UTC, sunset: 2015-07-25 11:16:27 UTC + # now = sunrise + 1s -> 'before sunrise' not true + now = datetime(2015, 7, 25, 11, 16, 28, 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 0 == len(calls) + + # now = sunrise -> 'before sunrise' true + now = datetime(2015, 7, 25, 11, 16, 27, 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 1 == len(calls) + + # now = local midnight -> 'before sunrise' true + now = datetime(2015, 7, 24, 8, 0, 0, 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 2 == len(calls) + + # now = local midnight - 1s -> 'before sunrise' not true + now = datetime(2015, 7, 24, 7, 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 2 == len(calls) + + +async def test_if_action_after_sunset_no_offset_kotzebue(hass, calls): + """ + Test if action was after sunrise. + + Local timezone: Alaska time + Location: Kotzebue, which has a very skewed local timezone with sunrise + at 7 AM and sunset at 3AM during summer + After sunset is true from sunset until midnight, local time. + """ + tz = dt_util.get_time_zone("America/Anchorage") + dt_util.set_default_time_zone(tz) + hass.config.latitude = 66.5 + hass.config.longitude = 162.4 + await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: { + "trigger": {"platform": "event", "event_type": "test_event"}, + "condition": {"condition": "sun", "after": SUN_EVENT_SUNSET}, + "action": {"service": "test.automation"}, + } + }, + ) + + # sunrise: 2015-07-24 07:17:24 local, sunset: 2015-07-25 03:16:27 local + # sunrise: 2015-07-24 15:17:24 UTC, sunset: 2015-07-25 11:16:27 UTC + # now = sunset -> 'after sunset' true + now = datetime(2015, 7, 25, 11, 16, 27, 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 1 == len(calls) + + # now = sunset - 1s -> 'after sunset' not true + now = datetime(2015, 7, 25, 11, 16, 26, 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 1 == len(calls) + + # now = local midnight -> 'after sunset' not true + now = datetime(2015, 7, 24, 8, 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 1 == len(calls) + + # now = local midnight - 1s -> 'after sunset' true + now = datetime(2015, 7, 24, 7, 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 2 == len(calls)