From 2391134d26a3eb152a227b1ec953c5b347422fb5 Mon Sep 17 00:00:00 2001 From: amitfin Date: Fri, 9 Apr 2021 13:26:55 +0300 Subject: [PATCH] Update "issur_melacha_in_effect" via time tracking (#42485) --- .../jewish_calendar/binary_sensor.py | 48 +++- .../jewish_calendar/test_binary_sensor.py | 213 ++++++++++++++++-- 2 files changed, 236 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/jewish_calendar/binary_sensor.py b/homeassistant/components/jewish_calendar/binary_sensor.py index 6edcc7b27c3..bda2bd5a117 100644 --- a/homeassistant/components/jewish_calendar/binary_sensor.py +++ b/homeassistant/components/jewish_calendar/binary_sensor.py @@ -1,7 +1,11 @@ """Support for Jewish Calendar binary sensors.""" +import datetime as dt + import hdate from homeassistant.components.binary_sensor import BinarySensorEntity +from homeassistant.core import callback +from homeassistant.helpers import event import homeassistant.util.dt as dt_util from . import DOMAIN, SENSOR_TYPES @@ -32,8 +36,8 @@ class JewishCalendarBinarySensor(BinarySensorEntity): self._hebrew = data["language"] == "hebrew" self._candle_lighting_offset = data["candle_lighting_offset"] self._havdalah_offset = data["havdalah_offset"] - self._state = False self._prefix = data["prefix"] + self._update_unsub = None @property def icon(self): @@ -53,11 +57,16 @@ class JewishCalendarBinarySensor(BinarySensorEntity): @property def is_on(self): """Return true if sensor is on.""" - return self._state + return self._get_zmanim().issur_melacha_in_effect - async def async_update(self): - """Update the state of the sensor.""" - zmanim = hdate.Zmanim( + @property + def should_poll(self): + """No polling needed.""" + return False + + def _get_zmanim(self): + """Return the Zmanim object for now().""" + return hdate.Zmanim( date=dt_util.now(), location=self._location, candle_lighting_offset=self._candle_lighting_offset, @@ -65,4 +74,31 @@ class JewishCalendarBinarySensor(BinarySensorEntity): hebrew=self._hebrew, ) - self._state = zmanim.issur_melacha_in_effect + async def async_added_to_hass(self): + """Run when entity about to be added to hass.""" + await super().async_added_to_hass() + self._schedule_update() + + @callback + def _update(self, now=None): + """Update the state of the sensor.""" + self._update_unsub = None + self._schedule_update() + self.async_write_ha_state() + + def _schedule_update(self): + """Schedule the next update of the sensor.""" + now = dt_util.now() + zmanim = self._get_zmanim() + update = zmanim.zmanim["sunrise"] + dt.timedelta(days=1) + candle_lighting = zmanim.candle_lighting + if candle_lighting is not None and now < candle_lighting < update: + update = candle_lighting + havdalah = zmanim.havdalah + if havdalah is not None and now < havdalah < update: + update = havdalah + if self._update_unsub: + self._update_unsub() + self._update_unsub = event.async_track_point_in_time( + self.hass, self._update, update + ) diff --git a/tests/components/jewish_calendar/test_binary_sensor.py b/tests/components/jewish_calendar/test_binary_sensor.py index ca31381f164..c2121196226 100644 --- a/tests/components/jewish_calendar/test_binary_sensor.py +++ b/tests/components/jewish_calendar/test_binary_sensor.py @@ -19,19 +19,118 @@ from . import ( from tests.common import async_fire_time_changed MELACHA_PARAMS = [ - make_nyc_test_params(dt(2018, 9, 1, 16, 0), STATE_ON), - make_nyc_test_params(dt(2018, 9, 1, 20, 21), STATE_OFF), - make_nyc_test_params(dt(2018, 9, 7, 13, 1), STATE_OFF), - make_nyc_test_params(dt(2018, 9, 8, 21, 25), STATE_OFF), - make_nyc_test_params(dt(2018, 9, 9, 21, 25), STATE_ON), - make_nyc_test_params(dt(2018, 9, 10, 21, 25), STATE_ON), - make_nyc_test_params(dt(2018, 9, 28, 21, 25), STATE_ON), - make_nyc_test_params(dt(2018, 9, 29, 21, 25), STATE_OFF), - make_nyc_test_params(dt(2018, 9, 30, 21, 25), STATE_ON), - make_nyc_test_params(dt(2018, 10, 1, 21, 25), STATE_ON), - make_jerusalem_test_params(dt(2018, 9, 29, 21, 25), STATE_OFF), - make_jerusalem_test_params(dt(2018, 9, 30, 21, 25), STATE_ON), - make_jerusalem_test_params(dt(2018, 10, 1, 21, 25), STATE_OFF), + make_nyc_test_params( + dt(2018, 9, 1, 16, 0), + { + "state": STATE_ON, + "update": dt(2018, 9, 1, 20, 14), + "new_state": STATE_OFF, + }, + ), + make_nyc_test_params( + dt(2018, 9, 1, 20, 21), + { + "state": STATE_OFF, + "update": dt(2018, 9, 2, 6, 21), + "new_state": STATE_OFF, + }, + ), + make_nyc_test_params( + dt(2018, 9, 7, 13, 1), + { + "state": STATE_OFF, + "update": dt(2018, 9, 7, 19, 4), + "new_state": STATE_ON, + }, + ), + make_nyc_test_params( + dt(2018, 9, 8, 21, 25), + { + "state": STATE_OFF, + "update": dt(2018, 9, 9, 6, 27), + "new_state": STATE_OFF, + }, + ), + make_nyc_test_params( + dt(2018, 9, 9, 21, 25), + { + "state": STATE_ON, + "update": dt(2018, 9, 10, 6, 28), + "new_state": STATE_ON, + }, + ), + make_nyc_test_params( + dt(2018, 9, 10, 21, 25), + { + "state": STATE_ON, + "update": dt(2018, 9, 11, 6, 29), + "new_state": STATE_ON, + }, + ), + make_nyc_test_params( + dt(2018, 9, 11, 11, 25), + { + "state": STATE_ON, + "update": dt(2018, 9, 11, 19, 57), + "new_state": STATE_OFF, + }, + ), + make_nyc_test_params( + dt(2018, 9, 29, 16, 25), + { + "state": STATE_ON, + "update": dt(2018, 9, 29, 19, 25), + "new_state": STATE_OFF, + }, + ), + make_nyc_test_params( + dt(2018, 9, 29, 21, 25), + { + "state": STATE_OFF, + "update": dt(2018, 9, 30, 6, 48), + "new_state": STATE_OFF, + }, + ), + make_nyc_test_params( + dt(2018, 9, 30, 21, 25), + { + "state": STATE_ON, + "update": dt(2018, 10, 1, 6, 49), + "new_state": STATE_ON, + }, + ), + make_nyc_test_params( + dt(2018, 10, 1, 21, 25), + { + "state": STATE_ON, + "update": dt(2018, 10, 2, 6, 50), + "new_state": STATE_ON, + }, + ), + make_jerusalem_test_params( + dt(2018, 9, 29, 21, 25), + { + "state": STATE_OFF, + "update": dt(2018, 9, 30, 6, 29), + "new_state": STATE_OFF, + }, + ), + make_jerusalem_test_params( + dt(2018, 10, 1, 11, 25), + { + "state": STATE_ON, + "update": dt(2018, 10, 1, 19, 2), + "new_state": STATE_OFF, + }, + ), + make_jerusalem_test_params( + dt(2018, 10, 1, 21, 25), + { + "state": STATE_OFF, + "update": dt(2018, 10, 2, 6, 31), + "new_state": STATE_OFF, + }, + ), ] MELACHA_TEST_IDS = [ @@ -40,7 +139,8 @@ MELACHA_TEST_IDS = [ "friday_upcoming_shabbat", "upcoming_rosh_hashana", "currently_rosh_hashana", - "second_day_rosh_hashana", + "second_day_rosh_hashana_night", + "second_day_rosh_hashana_day", "currently_shabbat_chol_hamoed", "upcoming_two_day_yomtov_in_diaspora", "currently_first_day_of_two_day_yomtov_in_diaspora", @@ -103,13 +203,9 @@ async def test_issur_melacha_sensor( ) await hass.async_block_till_done() - future = dt_util.utcnow() + timedelta(seconds=30) - async_fire_time_changed(hass, future) - await hass.async_block_till_done() - assert ( hass.states.get("binary_sensor.test_issur_melacha_in_effect").state - == result + == result["state"] ) entity = registry.async_get("binary_sensor.test_issur_melacha_in_effect") target_uid = "_".join( @@ -129,3 +225,82 @@ async def test_issur_melacha_sensor( ) ) assert entity.unique_id == target_uid + + with alter_time(result["update"]): + async_fire_time_changed(hass, result["update"]) + await hass.async_block_till_done() + assert ( + hass.states.get("binary_sensor.test_issur_melacha_in_effect").state + == result["new_state"] + ) + + +@pytest.mark.parametrize( + [ + "now", + "candle_lighting", + "havdalah", + "diaspora", + "tzname", + "latitude", + "longitude", + "result", + ], + [ + make_nyc_test_params( + dt(2020, 10, 23, 17, 46, 59, 999999), [STATE_OFF, STATE_ON] + ), + make_nyc_test_params( + dt(2020, 10, 24, 18, 44, 59, 999999), [STATE_ON, STATE_OFF] + ), + ], + ids=["before_candle_lighting", "before_havdalah"], +) +async def test_issur_melacha_sensor_update( + hass, + legacy_patchable_time, + now, + candle_lighting, + havdalah, + diaspora, + tzname, + latitude, + longitude, + result, +): + """Test Issur Melacha sensor output.""" + time_zone = dt_util.get_time_zone(tzname) + test_time = time_zone.localize(now) + + hass.config.time_zone = time_zone + hass.config.latitude = latitude + hass.config.longitude = longitude + + with alter_time(test_time): + assert await async_setup_component( + hass, + jewish_calendar.DOMAIN, + { + "jewish_calendar": { + "name": "test", + "language": "english", + "diaspora": diaspora, + "candle_lighting_minutes_before_sunset": candle_lighting, + "havdalah_minutes_after_sunset": havdalah, + } + }, + ) + await hass.async_block_till_done() + assert ( + hass.states.get("binary_sensor.test_issur_melacha_in_effect").state + == result[0] + ) + + test_time += timedelta(microseconds=1) + with alter_time(test_time): + async_fire_time_changed(hass, test_time) + await hass.async_block_till_done() + assert ( + hass.states.get("binary_sensor.test_issur_melacha_in_effect").state + == result[1] + )