Fully type Jewish Calendar (#56232)

This commit is contained in:
Yuval Aboulafia 2021-10-23 00:11:41 +03:00 committed by GitHub
parent fe0151491e
commit a9ccd70e71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 39 deletions

View File

@ -60,6 +60,7 @@ homeassistant.components.hyperion.*
homeassistant.components.image_processing.* homeassistant.components.image_processing.*
homeassistant.components.integration.* homeassistant.components.integration.*
homeassistant.components.iqvia.* homeassistant.components.iqvia.*
homeassistant.components.jewish_calendar.*
homeassistant.components.knx.* homeassistant.components.knx.*
homeassistant.components.kraken.* homeassistant.components.kraken.*
homeassistant.components.lcn.* homeassistant.components.lcn.*

View File

@ -1,12 +1,14 @@
"""The jewish_calendar component.""" """The jewish_calendar component."""
from __future__ import annotations from __future__ import annotations
import hdate from hdate import Location
import voluptuous as vol import voluptuous as vol
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.typing import ConfigType
DOMAIN = "jewish_calendar" DOMAIN = "jewish_calendar"
@ -43,7 +45,7 @@ CONFIG_SCHEMA = vol.Schema(
def get_unique_prefix( def get_unique_prefix(
location: hdate.Location, location: Location,
language: str, language: str,
candle_lighting_offset: int | None, candle_lighting_offset: int | None,
havdalah_offset: int | None, havdalah_offset: int | None,
@ -63,7 +65,7 @@ def get_unique_prefix(
return f"{prefix}" return f"{prefix}"
async def async_setup(hass, config): async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Jewish Calendar component.""" """Set up the Jewish Calendar component."""
name = config[DOMAIN][CONF_NAME] name = config[DOMAIN][CONF_NAME]
language = config[DOMAIN][CONF_LANGUAGE] language = config[DOMAIN][CONF_LANGUAGE]
@ -75,7 +77,7 @@ async def async_setup(hass, config):
candle_lighting_offset = config[DOMAIN][CONF_CANDLE_LIGHT_MINUTES] candle_lighting_offset = config[DOMAIN][CONF_CANDLE_LIGHT_MINUTES]
havdalah_offset = config[DOMAIN][CONF_HAVDALAH_OFFSET_MINUTES] havdalah_offset = config[DOMAIN][CONF_HAVDALAH_OFFSET_MINUTES]
location = hdate.Location( location = Location(
latitude=latitude, latitude=latitude,
longitude=longitude, longitude=longitude,
timezone=hass.config.time_zone, timezone=hass.config.time_zone,

View File

@ -2,14 +2,17 @@
from __future__ import annotations from __future__ import annotations
import datetime as dt import datetime as dt
from datetime import datetime
from typing import cast
import hdate import hdate
from hdate.zmanim import Zmanim
from homeassistant.components.binary_sensor import ( from homeassistant.components.binary_sensor import (
BinarySensorEntity, BinarySensorEntity,
BinarySensorEntityDescription, BinarySensorEntityDescription,
) )
from homeassistant.core import HomeAssistant, callback from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
from homeassistant.helpers import event from homeassistant.helpers import event
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
@ -29,7 +32,7 @@ async def async_setup_platform(
config: ConfigType, config: ConfigType,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = 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
@ -42,7 +45,11 @@ class JewishCalendarBinarySensor(BinarySensorEntity):
_attr_should_poll = False _attr_should_poll = False
def __init__(self, data, description: BinarySensorEntityDescription) -> None: def __init__(
self,
data: dict[str, str | bool | int | float],
description: BinarySensorEntityDescription,
) -> None:
"""Initialize the binary sensor.""" """Initialize the binary sensor."""
self._attr_name = f"{data['name']} {description.name}" self._attr_name = f"{data['name']} {description.name}"
self._attr_unique_id = f"{data['prefix']}_{description.key}" self._attr_unique_id = f"{data['prefix']}_{description.key}"
@ -50,14 +57,14 @@ class JewishCalendarBinarySensor(BinarySensorEntity):
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._update_unsub = None self._update_unsub: CALLBACK_TYPE | None = None
@property @property
def is_on(self) -> bool: 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 cast(bool, self._get_zmanim().issur_melacha_in_effect)
def _get_zmanim(self): def _get_zmanim(self) -> Zmanim:
"""Return the Zmanim object for now().""" """Return the Zmanim object for now()."""
return hdate.Zmanim( return hdate.Zmanim(
date=dt_util.now(), date=dt_util.now(),
@ -73,13 +80,13 @@ class JewishCalendarBinarySensor(BinarySensorEntity):
self._schedule_update() self._schedule_update()
@callback @callback
def _update(self, now=None): def _update(self, now: datetime | None = None) -> None:
"""Update the state of the sensor.""" """Update the state of the sensor."""
self._update_unsub = None self._update_unsub = None
self._schedule_update() self._schedule_update()
self.async_write_ha_state() self.async_write_ha_state()
def _schedule_update(self): def _schedule_update(self) -> None:
"""Schedule the next update of the sensor.""" """Schedule the next update of the sensor."""
now = dt_util.now() now = dt_util.now()
zmanim = self._get_zmanim() zmanim = self._get_zmanim()

View File

@ -1,17 +1,19 @@
"""Platform to retrieve Jewish calendar information for Home Assistant.""" """Platform to retrieve Jewish calendar information for Home Assistant."""
from __future__ import annotations from __future__ import annotations
from datetime import datetime from datetime import date as Date, datetime
import logging import logging
from typing import Any
import hdate from hdate import HDate
from hdate.zmanim import Zmanim
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription 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.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback 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 from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType, StateType
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from . import DOMAIN from . import DOMAIN
@ -130,7 +132,7 @@ async def async_setup_platform(
config: ConfigType, config: ConfigType,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = 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
@ -150,7 +152,11 @@ async def async_setup_platform(
class JewishCalendarSensor(SensorEntity): class JewishCalendarSensor(SensorEntity):
"""Representation of an Jewish calendar sensor.""" """Representation of an Jewish calendar sensor."""
def __init__(self, data, description: SensorEntityDescription) -> None: def __init__(
self,
data: dict[str, str | bool | int | float],
description: SensorEntityDescription,
) -> None:
"""Initialize the Jewish calendar sensor.""" """Initialize the Jewish calendar sensor."""
self.entity_description = description self.entity_description = description
self._attr_name = f"{data['name']} {description.name}" self._attr_name = f"{data['name']} {description.name}"
@ -160,29 +166,33 @@ class JewishCalendarSensor(SensorEntity):
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: datetime | None = None
self._holiday_attrs: dict[str, str] = {} self._holiday_attrs: dict[str, str] = {}
@property @property
def native_value(self): def native_value(self) -> StateType:
"""Return the state of the sensor.""" """Return the state of the sensor."""
if isinstance(self._state, datetime): if isinstance(self._state, datetime):
return self._state.isoformat() return self._state.isoformat()
return self._state return self._state
async def async_update(self): async def async_update(self) -> None:
"""Update the state of the sensor.""" """Update the state of the sensor."""
now = dt_util.now() now = dt_util.now()
_LOGGER.debug("Now: %s Location: %r", now, self._location) _LOGGER.debug("Now: %s Location: %r", now, self._location)
today = now.date() today = now.date()
sunset = dt_util.as_local( event_date = get_astral_event_date(self.hass, SUN_EVENT_SUNSET, today)
get_astral_event_date(self.hass, SUN_EVENT_SUNSET, today)
) if event_date is None:
_LOGGER.error("Can't get sunset event date for %s", today)
return
sunset = dt_util.as_local(event_date)
_LOGGER.debug("Now: %s Sunset: %s", now, sunset) _LOGGER.debug("Now: %s Sunset: %s", now, sunset)
daytime_date = hdate.HDate(today, diaspora=self._diaspora, hebrew=self._hebrew) daytime_date = HDate(today, diaspora=self._diaspora, hebrew=self._hebrew)
# The Jewish day starts after darkness (called "tzais") and finishes at # The Jewish day starts after darkness (called "tzais") and finishes at
# sunset ("shkia"). The time in between is a gray area (aka "Bein # sunset ("shkia"). The time in between is a gray area (aka "Bein
@ -203,9 +213,9 @@ class JewishCalendarSensor(SensorEntity):
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.entity_description.key, 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: Date) -> Zmanim:
"""Create a Zmanim object.""" """Create a Zmanim object."""
return hdate.Zmanim( return Zmanim(
date=date, date=date,
location=self._location, location=self._location,
candle_lighting_offset=self._candle_lighting_offset, candle_lighting_offset=self._candle_lighting_offset,
@ -220,7 +230,9 @@ class JewishCalendarSensor(SensorEntity):
return {} return {}
return self._holiday_attrs return self._holiday_attrs
def get_state(self, daytime_date, after_shkia_date, after_tzais_date): def get_state(
self, daytime_date: HDate, after_shkia_date: HDate, after_tzais_date: HDate
) -> Any | None:
"""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.
@ -250,23 +262,15 @@ class JewishCalendarTimeSensor(JewishCalendarSensor):
_attr_device_class = DEVICE_CLASS_TIMESTAMP _attr_device_class = DEVICE_CLASS_TIMESTAMP
@property @property
def native_value(self): def native_value(self) -> StateType | None:
"""Return the state of the sensor.""" """Return the state of the sensor."""
if self._state is None: if self._state is None:
return None return None
return dt_util.as_utc(self._state).isoformat() return dt_util.as_utc(self._state).isoformat()
@property def get_state(
def extra_state_attributes(self) -> dict[str, str]: self, daytime_date: HDate, after_shkia_date: HDate, after_tzais_date: HDate
"""Return the state attributes.""" ) -> Any | None:
attrs: dict[str, str] = {}
if self._state is None:
return attrs
return attrs
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.entity_description.key == "upcoming_shabbat_candle_lighting": if self.entity_description.key == "upcoming_shabbat_candle_lighting":
times = self.make_zmanim( times = self.make_zmanim(

View File

@ -671,6 +671,17 @@ no_implicit_optional = true
warn_return_any = true warn_return_any = true
warn_unreachable = true warn_unreachable = true
[mypy-homeassistant.components.jewish_calendar.*]
check_untyped_defs = true
disallow_incomplete_defs = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
no_implicit_optional = true
warn_return_any = true
warn_unreachable = true
[mypy-homeassistant.components.knx.*] [mypy-homeassistant.components.knx.*]
check_untyped_defs = true check_untyped_defs = true
disallow_incomplete_defs = true disallow_incomplete_defs = true