Refactor Jewish Calendar to use EntityDescription (#54852)

This commit is contained in:
Yuval Aboulafia 2021-08-25 13:05:58 +03:00 committed by GitHub
parent 51361fbd2b
commit ebe48e78b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 173 additions and 81 deletions

View File

@ -10,39 +10,6 @@ from homeassistant.helpers.discovery import async_load_platform
DOMAIN = "jewish_calendar" DOMAIN = "jewish_calendar"
SENSOR_TYPES = {
"binary": {
"issur_melacha_in_effect": ["Issur Melacha in Effect", "mdi:power-plug-off"]
},
"data": {
"date": ["Date", "mdi:judaism"],
"weekly_portion": ["Parshat Hashavua", "mdi:book-open-variant"],
"holiday": ["Holiday", "mdi:calendar-star"],
"omer_count": ["Day of the Omer", "mdi:counter"],
"daf_yomi": ["Daf Yomi", "mdi:book-open-variant"],
},
"time": {
"first_light": ["Alot Hashachar", "mdi:weather-sunset-up"],
"talit": ["Talit and Tefillin", "mdi:calendar-clock"],
"gra_end_shma": ['Latest time for Shma Gr"a', "mdi:calendar-clock"],
"mga_end_shma": ['Latest time for Shma MG"A', "mdi:calendar-clock"],
"gra_end_tfila": ['Latest time for Tefilla Gr"a', "mdi:calendar-clock"],
"mga_end_tfila": ['Latest time for Tefilla MG"A', "mdi:calendar-clock"],
"big_mincha": ["Mincha Gedola", "mdi:calendar-clock"],
"small_mincha": ["Mincha Ketana", "mdi:calendar-clock"],
"plag_mincha": ["Plag Hamincha", "mdi:weather-sunset-down"],
"sunset": ["Shkia", "mdi:weather-sunset"],
"first_stars": ["T'set Hakochavim", "mdi:weather-night"],
"upcoming_shabbat_candle_lighting": [
"Upcoming Shabbat Candle Lighting",
"mdi:candle",
],
"upcoming_shabbat_havdalah": ["Upcoming Shabbat Havdalah", "mdi:weather-night"],
"upcoming_candle_lighting": ["Upcoming Candle Lighting", "mdi:candle"],
"upcoming_havdalah": ["Upcoming Havdalah", "mdi:weather-night"],
},
}
CONF_DIASPORA = "diaspora" CONF_DIASPORA = "diaspora"
CONF_LANGUAGE = "language" CONF_LANGUAGE = "language"
CONF_CANDLE_LIGHT_MINUTES = "candle_lighting_minutes_before_sunset" CONF_CANDLE_LIGHT_MINUTES = "candle_lighting_minutes_before_sunset"

View File

@ -1,40 +1,51 @@
"""Support for Jewish Calendar binary sensors.""" """Support for Jewish Calendar binary sensors."""
from __future__ import annotations
import datetime as dt import datetime as dt
import hdate import hdate
from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.components.binary_sensor import (
from homeassistant.core import callback BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import event from homeassistant.helpers import event
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from . import DOMAIN, SENSOR_TYPES from . import DOMAIN
BINARY_SENSORS = BinarySensorEntityDescription(
key="issur_melacha_in_effect",
name="Issur Melacha in Effect",
icon="mdi:power-plug-off",
)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
):
"""Set up the Jewish Calendar binary sensor devices.""" """Set up the Jewish Calendar binary sensor devices."""
if discovery_info is None: if discovery_info is None:
return return
async_add_entities( async_add_entities([JewishCalendarBinarySensor(hass.data[DOMAIN], BINARY_SENSORS)])
[
JewishCalendarBinarySensor(hass.data[DOMAIN], sensor, sensor_info)
for sensor, sensor_info in SENSOR_TYPES["binary"].items()
]
)
class JewishCalendarBinarySensor(BinarySensorEntity): class JewishCalendarBinarySensor(BinarySensorEntity):
"""Representation of an Jewish Calendar binary sensor.""" """Representation of an Jewish Calendar binary sensor."""
def __init__(self, data, sensor, sensor_info): _attr_should_poll = False
def __init__(self, data, description: BinarySensorEntityDescription) -> None:
"""Initialize the binary sensor.""" """Initialize the binary sensor."""
self._type = sensor self._attr_name = f"{data['name']} {description.name}"
self._prefix = data["prefix"] self._attr_unique_id = f"{data['prefix']}_{description.key}"
self._attr_name = f"{data['name']} {sensor_info[0]}"
self._attr_unique_id = f"{self._prefix}_{self._type}"
self._attr_icon = sensor_info[1]
self._attr_should_poll = False
self._location = data["location"] self._location = data["location"]
self._hebrew = data["language"] == "hebrew" self._hebrew = data["language"] == "hebrew"
self._candle_lighting_offset = data["candle_lighting_offset"] self._candle_lighting_offset = data["candle_lighting_offset"]
@ -42,7 +53,7 @@ class JewishCalendarBinarySensor(BinarySensorEntity):
self._update_unsub = None self._update_unsub = None
@property @property
def is_on(self): def is_on(self) -> bool:
"""Return true if sensor is on.""" """Return true if sensor is on."""
return self._get_zmanim().issur_melacha_in_effect return self._get_zmanim().issur_melacha_in_effect
@ -56,7 +67,7 @@ class JewishCalendarBinarySensor(BinarySensorEntity):
hebrew=self._hebrew, hebrew=self._hebrew,
) )
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Run when entity about to be added to hass.""" """Run when entity about to be added to hass."""
await super().async_added_to_hass() await super().async_added_to_hass()
self._schedule_update() self._schedule_update()

View File

@ -1,31 +1,147 @@
"""Platform to retrieve Jewish calendar information for Home Assistant.""" """Platform to retrieve Jewish calendar information for Home Assistant."""
from __future__ import annotations
from datetime import datetime from datetime import datetime
import logging import logging
import hdate import hdate
from homeassistant.components.sensor import SensorEntity from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.const import DEVICE_CLASS_TIMESTAMP, SUN_EVENT_SUNSET from homeassistant.const import DEVICE_CLASS_TIMESTAMP, SUN_EVENT_SUNSET
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.sun import get_astral_event_date from homeassistant.helpers.sun import get_astral_event_date
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from . import DOMAIN, SENSOR_TYPES from . import DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DATA_SENSORS = (
SensorEntityDescription(
key="date",
name="Date",
icon="mdi:judaism",
),
SensorEntityDescription(
key="weekly_portion",
name="Parshat Hashavua",
icon="mdi:book-open-variant",
),
SensorEntityDescription(
key="holiday",
name="Holiday",
icon="mdi:calendar-star",
),
SensorEntityDescription(
key="omer_count",
name="Day of the Omer",
icon="mdi:counter",
),
SensorEntityDescription(
key="daf_yomi",
name="Daf Yomi",
icon="mdi:book-open-variant",
),
)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): TIME_SENSORS = (
SensorEntityDescription(
key="first_light",
name="Alot Hashachar",
icon="mdi:weather-sunset-up",
),
SensorEntityDescription(
key="talit",
name="Talit and Tefillin",
icon="mdi:calendar-clock",
),
SensorEntityDescription(
key="gra_end_shma",
name='Latest time for Shma Gr"a',
icon="mdi:calendar-clock",
),
SensorEntityDescription(
key="mga_end_shma",
name='Latest time for Shma MG"A',
icon="mdi:calendar-clock",
),
SensorEntityDescription(
key="gra_end_tfila",
name='Latest time for Tefilla Gr"a',
icon="mdi:calendar-clock",
),
SensorEntityDescription(
key="mga_end_tfila",
name='Latest time for Tefilla MG"A',
icon="mdi:calendar-clock",
),
SensorEntityDescription(
key="big_mincha",
name="Mincha Gedola",
icon="mdi:calendar-clock",
),
SensorEntityDescription(
key="small_mincha",
name="Mincha Ketana",
icon="mdi:calendar-clock",
),
SensorEntityDescription(
key="plag_mincha",
name="Plag Hamincha",
icon="mdi:weather-sunset-down",
),
SensorEntityDescription(
key="sunset",
name="Shkia",
icon="mdi:weather-sunset",
),
SensorEntityDescription(
key="first_stars",
name="T'set Hakochavim",
icon="mdi:weather-night",
),
SensorEntityDescription(
key="upcoming_shabbat_candle_lighting",
name="Upcoming Shabbat Candle Lighting",
icon="mdi:candle",
),
SensorEntityDescription(
key="upcoming_shabbat_havdalah",
name="Upcoming Shabbat Havdalah",
icon="mdi:weather-night",
),
SensorEntityDescription(
key="upcoming_candle_lighting",
name="Upcoming Candle Lighting",
icon="mdi:candle",
),
SensorEntityDescription(
key="upcoming_havdalah",
name="Upcoming Havdalah",
icon="mdi:weather-night",
),
)
async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
):
"""Set up the Jewish calendar sensor platform.""" """Set up the Jewish calendar sensor platform."""
if discovery_info is None: if discovery_info is None:
return return
sensors = [ sensors = [
JewishCalendarSensor(hass.data[DOMAIN], sensor, sensor_info) JewishCalendarSensor(hass.data[DOMAIN], description)
for sensor, sensor_info in SENSOR_TYPES["data"].items() for description in DATA_SENSORS
] ]
sensors.extend( sensors.extend(
JewishCalendarTimeSensor(hass.data[DOMAIN], sensor, sensor_info) JewishCalendarTimeSensor(hass.data[DOMAIN], description)
for sensor, sensor_info in SENSOR_TYPES["time"].items() for description in TIME_SENSORS
) )
async_add_entities(sensors) async_add_entities(sensors)
@ -34,20 +150,18 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
class JewishCalendarSensor(SensorEntity): class JewishCalendarSensor(SensorEntity):
"""Representation of an Jewish calendar sensor.""" """Representation of an Jewish calendar sensor."""
def __init__(self, data, sensor, sensor_info): def __init__(self, data, description: SensorEntityDescription) -> None:
"""Initialize the Jewish calendar sensor.""" """Initialize the Jewish calendar sensor."""
self._type = sensor self.entity_description = description
self._prefix = data["prefix"] self._attr_name = f"{data['name']} {description.name}"
self._attr_name = f"{data['name']} {sensor_info[0]}" self._attr_unique_id = f"{data['prefix']}_{description.key}"
self._attr_unique_id = f"{self._prefix}_{self._type}"
self._attr_icon = sensor_info[1]
self._location = data["location"] self._location = data["location"]
self._hebrew = data["language"] == "hebrew" self._hebrew = data["language"] == "hebrew"
self._candle_lighting_offset = data["candle_lighting_offset"] self._candle_lighting_offset = data["candle_lighting_offset"]
self._havdalah_offset = data["havdalah_offset"] self._havdalah_offset = data["havdalah_offset"]
self._diaspora = data["diaspora"] self._diaspora = data["diaspora"]
self._state = None self._state = None
self._holiday_attrs = {} self._holiday_attrs: dict[str, str] = {}
@property @property
def native_value(self): def native_value(self):
@ -87,7 +201,7 @@ class JewishCalendarSensor(SensorEntity):
after_tzais_date = daytime_date.next_day after_tzais_date = daytime_date.next_day
self._state = self.get_state(daytime_date, after_shkia_date, after_tzais_date) self._state = self.get_state(daytime_date, after_shkia_date, after_tzais_date)
_LOGGER.debug("New value for %s: %s", self._type, self._state) _LOGGER.debug("New value for %s: %s", self.entity_description.key, self._state)
def make_zmanim(self, date): def make_zmanim(self, date):
"""Create a Zmanim object.""" """Create a Zmanim object."""
@ -100,9 +214,9 @@ class JewishCalendarSensor(SensorEntity):
) )
@property @property
def extra_state_attributes(self): def extra_state_attributes(self) -> dict[str, str]:
"""Return the state attributes.""" """Return the state attributes."""
if self._type != "holiday": if self.entity_description.key != "holiday":
return {} return {}
return self._holiday_attrs return self._holiday_attrs
@ -110,19 +224,19 @@ class JewishCalendarSensor(SensorEntity):
"""For a given type of sensor, return the state.""" """For a given type of sensor, return the state."""
# Terminology note: by convention in py-libhdate library, "upcoming" # Terminology note: by convention in py-libhdate library, "upcoming"
# refers to "current" or "upcoming" dates. # refers to "current" or "upcoming" dates.
if self._type == "date": if self.entity_description.key == "date":
return after_shkia_date.hebrew_date return after_shkia_date.hebrew_date
if self._type == "weekly_portion": if self.entity_description.key == "weekly_portion":
# Compute the weekly portion based on the upcoming shabbat. # Compute the weekly portion based on the upcoming shabbat.
return after_tzais_date.upcoming_shabbat.parasha return after_tzais_date.upcoming_shabbat.parasha
if self._type == "holiday": if self.entity_description.key == "holiday":
self._holiday_attrs["id"] = after_shkia_date.holiday_name self._holiday_attrs["id"] = after_shkia_date.holiday_name
self._holiday_attrs["type"] = after_shkia_date.holiday_type.name self._holiday_attrs["type"] = after_shkia_date.holiday_type.name
self._holiday_attrs["type_id"] = after_shkia_date.holiday_type.value self._holiday_attrs["type_id"] = after_shkia_date.holiday_type.value
return after_shkia_date.holiday_description return after_shkia_date.holiday_description
if self._type == "omer_count": if self.entity_description.key == "omer_count":
return after_shkia_date.omer_day return after_shkia_date.omer_day
if self._type == "daf_yomi": if self.entity_description.key == "daf_yomi":
return daytime_date.daf_yomi return daytime_date.daf_yomi
return None return None
@ -141,9 +255,9 @@ class JewishCalendarTimeSensor(JewishCalendarSensor):
return dt_util.as_utc(self._state).isoformat() return dt_util.as_utc(self._state).isoformat()
@property @property
def extra_state_attributes(self): def extra_state_attributes(self) -> dict[str, str]:
"""Return the state attributes.""" """Return the state attributes."""
attrs = {} attrs: dict[str, str] = {}
if self._state is None: if self._state is None:
return attrs return attrs
@ -152,24 +266,24 @@ class JewishCalendarTimeSensor(JewishCalendarSensor):
def get_state(self, daytime_date, after_shkia_date, after_tzais_date): def get_state(self, daytime_date, after_shkia_date, after_tzais_date):
"""For a given type of sensor, return the state.""" """For a given type of sensor, return the state."""
if self._type == "upcoming_shabbat_candle_lighting": if self.entity_description.key == "upcoming_shabbat_candle_lighting":
times = self.make_zmanim( times = self.make_zmanim(
after_tzais_date.upcoming_shabbat.previous_day.gdate after_tzais_date.upcoming_shabbat.previous_day.gdate
) )
return times.candle_lighting return times.candle_lighting
if self._type == "upcoming_candle_lighting": if self.entity_description.key == "upcoming_candle_lighting":
times = self.make_zmanim( times = self.make_zmanim(
after_tzais_date.upcoming_shabbat_or_yom_tov.first_day.previous_day.gdate after_tzais_date.upcoming_shabbat_or_yom_tov.first_day.previous_day.gdate
) )
return times.candle_lighting return times.candle_lighting
if self._type == "upcoming_shabbat_havdalah": if self.entity_description.key == "upcoming_shabbat_havdalah":
times = self.make_zmanim(after_tzais_date.upcoming_shabbat.gdate) times = self.make_zmanim(after_tzais_date.upcoming_shabbat.gdate)
return times.havdalah return times.havdalah
if self._type == "upcoming_havdalah": if self.entity_description.key == "upcoming_havdalah":
times = self.make_zmanim( times = self.make_zmanim(
after_tzais_date.upcoming_shabbat_or_yom_tov.last_day.gdate after_tzais_date.upcoming_shabbat_or_yom_tov.last_day.gdate
) )
return times.havdalah return times.havdalah
times = self.make_zmanim(dt_util.now()).zmanim times = self.make_zmanim(dt_util.now()).zmanim
return times[self._type] return times[self.entity_description.key]