diff --git a/CODEOWNERS b/CODEOWNERS index 68d0d4a833dac45aee2b53933ff692ba48170759..b6ce8c04909786879f4adcdab6c074b50750b952 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -72,6 +72,7 @@ homeassistant/components/sensor/airvisual.py @bachya homeassistant/components/sensor/filter.py @dgomes homeassistant/components/sensor/gearbest.py @HerrHofrat homeassistant/components/sensor/irish_rail_transport.py @ttroy50 +homeassistant/components/sensor/jewish_calendar.py @tsvi homeassistant/components/sensor/miflora.py @danielhiversen @ChristianKuehnel homeassistant/components/sensor/nsw_fuel_station.py @nickw444 homeassistant/components/sensor/pollen.py @bachya diff --git a/homeassistant/components/sensor/jewish_calendar.py b/homeassistant/components/sensor/jewish_calendar.py new file mode 100644 index 0000000000000000000000000000000000000000..544b7be0c5d232b37797f8dfdd9577d001114ed3 --- /dev/null +++ b/homeassistant/components/sensor/jewish_calendar.py @@ -0,0 +1,134 @@ +""" +Platform to retrieve Jewish calendar information for Home Assistant. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.jewish_calendar/ +""" +import logging + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.entity import Entity +import homeassistant.util.dt as dt_util + +REQUIREMENTS = ['hdate==0.6.3'] + +_LOGGER = logging.getLogger(__name__) + +SENSOR_TYPES = { + 'date': ['Date', 'mdi:judaism'], + 'weekly_portion': ['Parshat Hashavua', 'mdi:book-open-variant'], + 'holiday_name': ['Holiday', 'mdi:calendar-star'], + 'holyness': ['Holyness', 'mdi:counter'], + 'first_light': ['Alot Hashachar', 'mdi:weather-sunset-up'], + 'gra_end_shma': ['Latest time for Shm"a GR"A', 'mdi:calendar-clock'], + 'mga_end_shma': ['Latest time for Shm"a MG"A', 'mdi:calendar-clock'], + 'plag_mincha': ['Plag Hamincha', 'mdi:weather-sunset-down'], + 'first_stars': ['T\'set Hakochavim', 'mdi:weather-night'], +} + +CONF_DIASPORA = 'diaspora' +CONF_LANGUAGE = 'language' +CONF_SENSORS = 'sensors' + +DEFAULT_NAME = 'Jewish Calendar' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_DIASPORA, default=False): cv.boolean, + vol.Optional(CONF_LATITUDE): cv.latitude, + vol.Optional(CONF_LONGITUDE): cv.longitude, + vol.Optional(CONF_LANGUAGE, default='english'): + vol.In(['hebrew', 'english']), + vol.Optional(CONF_SENSORS, default=['date']): + vol.All(cv.ensure_list, vol.Length(min=1), [vol.In(SENSOR_TYPES)]), +}) + + +async def async_setup_platform( + hass, config, async_add_entities, discovery_info=None): + """Set up the Jewish calendar sensor platform.""" + language = config.get(CONF_LANGUAGE) + name = config.get(CONF_NAME) + latitude = config.get(CONF_LATITUDE, hass.config.latitude) + longitude = config.get(CONF_LONGITUDE, hass.config.longitude) + diaspora = config.get(CONF_DIASPORA) + + if None in (latitude, longitude): + _LOGGER.error("Latitude or longitude not set in Home Assistant config") + return + + dev = [] + for sensor_type in config[CONF_SENSORS]: + dev.append(JewishCalSensor( + name, language, sensor_type, latitude, longitude, diaspora)) + async_add_entities(dev, True) + + +class JewishCalSensor(Entity): + """Representation of an Jewish calendar sensor.""" + + def __init__( + self, name, language, sensor_type, latitude, longitude, diaspora): + """Initialize the Jewish calendar sensor.""" + self.client_name = name + self._name = SENSOR_TYPES[sensor_type][0] + self.type = sensor_type + self._hebrew = (language == 'hebrew') + self._state = None + self.latitude = latitude + self.longitude = longitude + self.diaspora = diaspora + _LOGGER.debug("Sensor %s initialized", self.type) + + @property + def name(self): + """Return the name of the sensor.""" + return '{} {}'.format(self.client_name, self._name) + + @property + def icon(self): + """Icon to display in the front end.""" + return SENSOR_TYPES[self.type][1] + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + async def async_update(self): + """Update the state of the sensor.""" + import hdate + + today = dt_util.now().date() + + date = hdate.HDate( + today, diaspora=self.diaspora, hebrew=self._hebrew) + + if self.type == 'date': + self._state = hdate.date.get_hebrew_date( + date.h_day, date.h_month, date.h_year, hebrew=self._hebrew) + elif self.type == 'weekly_portion': + self._state = hdate.date.get_parashe( + date.get_reading(self.diaspora), hebrew=self._hebrew) + elif self.type == 'holiday_name': + try: + self._state = next( + x.description[self._hebrew].long + for x in hdate.htables.HOLIDAYS + if x.index == date.get_holyday()) + except StopIteration: + self._state = None + elif self.type == 'holyness': + self._state = hdate.date.get_holyday_type(date.get_holyday()) + else: + times = hdate.Zmanim( + date=today, + latitude=self.latitude, longitude=self.longitude, + hebrew=self._hebrew).zmanim + self._state = times[self.type].time() + + _LOGGER.debug("New value: %s", self._state) diff --git a/requirements_all.txt b/requirements_all.txt index 13cf9082e6c7103eea94bfe7d5e7eec0d18a53fa..96e5ed0b1e3555a371ff6f38bbbc0621877c0a59 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -435,6 +435,9 @@ haversine==0.4.5 # homeassistant.components.mqtt.server hbmqtt==0.9.4 +# homeassistant.components.sensor.jewish_calendar +hdate==0.6.3 + # homeassistant.components.climate.heatmiser heatmiserV3==0.9.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8693627ba9510c65286180da37fadc00136fd026..42561336ff95edee45a03c869c2ac479ed70fd9d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -80,6 +80,9 @@ haversine==0.4.5 # homeassistant.components.mqtt.server hbmqtt==0.9.4 +# homeassistant.components.sensor.jewish_calendar +hdate==0.6.3 + # homeassistant.components.binary_sensor.workday holidays==0.9.7 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index d1d29affeff44cebc7575f73d7a6a42298367a62..3cde13a2a9755ee914354fb0513dcf24b4ef105b 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -55,6 +55,7 @@ TEST_REQUIREMENTS = ( 'ha-ffmpeg', 'haversine', 'hbmqtt', + 'hdate', 'holidays', 'home-assistant-frontend', 'homematicip', diff --git a/tests/components/sensor/test_jewish_calendar.py b/tests/components/sensor/test_jewish_calendar.py new file mode 100644 index 0000000000000000000000000000000000000000..3260512495fd7791e03aeb1dfad31f52003975cf --- /dev/null +++ b/tests/components/sensor/test_jewish_calendar.py @@ -0,0 +1,134 @@ +"""The tests for the Jewish calendar sensor platform.""" +import unittest +from datetime import datetime as dt +from unittest.mock import patch + +from homeassistant.util.async_ import run_coroutine_threadsafe +from homeassistant.setup import setup_component +from homeassistant.components.sensor.jewish_calendar import JewishCalSensor +from tests.common import get_test_home_assistant + + +class TestJewishCalenderSensor(unittest.TestCase): + """Test the Jewish Calendar sensor.""" + + TEST_LATITUDE = 31.778 + TEST_LONGITUDE = 35.235 + + def setUp(self): + """Set up things to run when tests begin.""" + self.hass = get_test_home_assistant() + + def tearDown(self): + """Stop everything that was started.""" + self.hass.stop() + + def checkForLoggingErrors(self): + """Check whether logger spitted out errors.""" + errors = [rec for rec in self.cm.records if rec.levelname == "ERROR"] + self.assertFalse(errors, ("Logger reported error(s): ", + [err.getMessage() for err in errors])) + + def test_jewish_calendar_min_config(self): + """Test minimum jewish calendar configuration.""" + config = { + 'sensor': { + 'platform': 'jewish_calendar' + } + } + with self.assertLogs() as self.cm: + assert setup_component(self.hass, 'sensor', config) + self.checkForLoggingErrors() + + def test_jewish_calendar_hebrew(self): + """Test jewish calendar sensor with language set to hebrew.""" + config = { + 'sensor': { + 'platform': 'jewish_calendar', + 'language': 'hebrew', + } + } + with self.assertLogs() as self.cm: + assert setup_component(self.hass, 'sensor', config) + self.checkForLoggingErrors() + + def test_jewish_calendar_multiple_sensors(self): + """Test jewish calendar sensor with multiple sensors setup.""" + config = { + 'sensor': { + 'platform': 'jewish_calendar', + 'sensors': [ + 'date', 'weekly_portion', 'holiday_name', + 'holyness', 'first_light', 'gra_end_shma', + 'mga_end_shma', 'plag_mincha', 'first_stars' + ] + } + } + with self.assertLogs() as self.cm: + assert setup_component(self.hass, 'sensor', config) + self.checkForLoggingErrors() + + def test_jewish_calendar_sensor_date_output(self): + """Test Jewish calendar sensor date output.""" + test_time = dt(2018, 9, 3) + sensor = JewishCalSensor( + name='test', language='english', sensor_type='date', + latitude=self.TEST_LATITUDE, longitude=self.TEST_LONGITUDE, + diaspora=False) + with patch('homeassistant.util.dt.now', return_value=test_time): + run_coroutine_threadsafe( + sensor.async_update(), + self.hass.loop).result() + self.assertEqual(sensor.state, '23 Elul 5778') + + def test_jewish_calendar_sensor_date_output_hebrew(self): + """Test Jewish calendar sensor date output in hebrew.""" + test_time = dt(2018, 9, 3) + sensor = JewishCalSensor( + name='test', language='hebrew', sensor_type='date', + latitude=self.TEST_LATITUDE, longitude=self.TEST_LONGITUDE, + diaspora=False) + with patch('homeassistant.util.dt.now', return_value=test_time): + run_coroutine_threadsafe( + sensor.async_update(), + self.hass.loop).result() + self.assertEqual(sensor.state, "×›\"×’ ב×לול ×”\' תשע\"×—") + + def test_jewish_calendar_sensor_holiday_name(self): + """Test Jewish calendar sensor date output in hebrew.""" + test_time = dt(2018, 9, 10) + sensor = JewishCalSensor( + name='test', language='hebrew', sensor_type='holiday_name', + latitude=self.TEST_LATITUDE, longitude=self.TEST_LONGITUDE, + diaspora=False) + with patch('homeassistant.util.dt.now', return_value=test_time): + run_coroutine_threadsafe( + sensor.async_update(), + self.hass.loop).result() + self.assertEqual(sensor.state, "×\' ר×ש ×”×©× ×”") + + def test_jewish_calendar_sensor_holyness(self): + """Test Jewish calendar sensor date output in hebrew.""" + test_time = dt(2018, 9, 10) + sensor = JewishCalSensor( + name='test', language='hebrew', sensor_type='holyness', + latitude=self.TEST_LATITUDE, longitude=self.TEST_LONGITUDE, + diaspora=False) + with patch('homeassistant.util.dt.now', return_value=test_time): + run_coroutine_threadsafe( + sensor.async_update(), + self.hass.loop).result() + self.assertEqual(sensor.state, 1) + + def test_jewish_calendar_sensor_torah_reading(self): + """Test Jewish calendar sensor date output in hebrew.""" + test_time = dt(2018, 9, 8) + sensor = JewishCalSensor( + name='test', language='hebrew', sensor_type='weekly_portion', + latitude=self.TEST_LATITUDE, longitude=self.TEST_LONGITUDE, + diaspora=False) + with patch('homeassistant.util.dt.now', return_value=test_time): + run_coroutine_threadsafe( + sensor.async_update(), + self.hass.loop).result() + self.assertEqual(sensor.state, "פרשת × ×¦×‘×™×")