Home Assistant Unofficial Reference 2024.12.1
sensor.py
Go to the documentation of this file.
1 """Support for Jewish calendar sensors."""
2 
3 from __future__ import annotations
4 
5 from datetime import date as Date
6 import logging
7 from typing import Any, cast
8 
9 from hdate import HDate, HebrewDate, htables
10 from hdate.zmanim import Zmanim
11 
13  SensorDeviceClass,
14  SensorEntity,
15  SensorEntityDescription,
16 )
17 from homeassistant.const import SUN_EVENT_SUNSET, EntityCategory
18 from homeassistant.core import HomeAssistant
19 from homeassistant.helpers.entity_platform import AddEntitiesCallback
20 from homeassistant.helpers.sun import get_astral_event_date
21 import homeassistant.util.dt as dt_util
22 
23 from .entity import JewishCalendarConfigEntry, JewishCalendarEntity
24 
25 _LOGGER = logging.getLogger(__name__)
26 
27 INFO_SENSORS: tuple[SensorEntityDescription, ...] = (
29  key="date",
30  name="Date",
31  icon="mdi:star-david",
32  translation_key="hebrew_date",
33  ),
35  key="weekly_portion",
36  name="Parshat Hashavua",
37  icon="mdi:book-open-variant",
38  device_class=SensorDeviceClass.ENUM,
39  ),
41  key="holiday",
42  name="Holiday",
43  icon="mdi:calendar-star",
44  device_class=SensorDeviceClass.ENUM,
45  ),
47  key="omer_count",
48  name="Day of the Omer",
49  icon="mdi:counter",
50  entity_registry_enabled_default=False,
51  ),
53  key="daf_yomi",
54  name="Daf Yomi",
55  icon="mdi:book-open-variant",
56  entity_registry_enabled_default=False,
57  ),
58 )
59 
60 TIME_SENSORS: tuple[SensorEntityDescription, ...] = (
62  key="first_light",
63  name="Alot Hashachar", # codespell:ignore alot
64  icon="mdi:weather-sunset-up",
65  entity_registry_enabled_default=False,
66  ),
68  key="talit",
69  name="Talit and Tefillin",
70  icon="mdi:calendar-clock",
71  entity_registry_enabled_default=False,
72  ),
74  key="sunrise",
75  name="Hanetz Hachama",
76  icon="mdi:calendar-clock",
77  ),
79  key="gra_end_shma",
80  name='Latest time for Shma Gr"a',
81  icon="mdi:calendar-clock",
82  entity_registry_enabled_default=False,
83  ),
85  key="mga_end_shma",
86  name='Latest time for Shma MG"A',
87  icon="mdi:calendar-clock",
88  entity_registry_enabled_default=False,
89  ),
91  key="gra_end_tfila",
92  name='Latest time for Tefilla Gr"a',
93  icon="mdi:calendar-clock",
94  entity_registry_enabled_default=False,
95  ),
97  key="mga_end_tfila",
98  name='Latest time for Tefilla MG"A',
99  icon="mdi:calendar-clock",
100  entity_registry_enabled_default=False,
101  ),
103  key="midday",
104  name="Chatzot Hayom",
105  icon="mdi:calendar-clock",
106  entity_registry_enabled_default=False,
107  ),
109  key="big_mincha",
110  name="Mincha Gedola",
111  icon="mdi:calendar-clock",
112  entity_registry_enabled_default=False,
113  ),
115  key="small_mincha",
116  name="Mincha Ketana",
117  icon="mdi:calendar-clock",
118  entity_registry_enabled_default=False,
119  ),
121  key="plag_mincha",
122  name="Plag Hamincha",
123  icon="mdi:weather-sunset-down",
124  entity_registry_enabled_default=False,
125  ),
127  key="sunset",
128  name="Shkia",
129  icon="mdi:weather-sunset",
130  ),
132  key="first_stars",
133  name="T'set Hakochavim",
134  icon="mdi:weather-night",
135  entity_registry_enabled_default=False,
136  ),
138  key="three_stars",
139  name="T'set Hakochavim, 3 stars",
140  icon="mdi:weather-night",
141  entity_registry_enabled_default=False,
142  ),
144  key="upcoming_shabbat_candle_lighting",
145  name="Upcoming Shabbat Candle Lighting",
146  icon="mdi:candle",
147  entity_registry_enabled_default=False,
148  ),
150  key="upcoming_shabbat_havdalah",
151  name="Upcoming Shabbat Havdalah",
152  icon="mdi:weather-night",
153  entity_registry_enabled_default=False,
154  ),
156  key="upcoming_candle_lighting",
157  name="Upcoming Candle Lighting",
158  icon="mdi:candle",
159  ),
161  key="upcoming_havdalah",
162  name="Upcoming Havdalah",
163  icon="mdi:weather-night",
164  ),
165 )
166 
167 
169  hass: HomeAssistant,
170  config_entry: JewishCalendarConfigEntry,
171  async_add_entities: AddEntitiesCallback,
172 ) -> None:
173  """Set up the Jewish calendar sensors ."""
174  sensors = [
175  JewishCalendarSensor(config_entry, description) for description in INFO_SENSORS
176  ]
177  sensors.extend(
178  JewishCalendarTimeSensor(config_entry, description)
179  for description in TIME_SENSORS
180  )
181 
182  async_add_entities(sensors)
183 
184 
186  """Representation of an Jewish calendar sensor."""
187 
188  _attr_entity_category = EntityCategory.DIAGNOSTIC
189 
190  def __init__(
191  self,
192  config_entry: JewishCalendarConfigEntry,
193  description: SensorEntityDescription,
194  ) -> None:
195  """Initialize the Jewish calendar sensor."""
196  super().__init__(config_entry, description)
197  self._attrs_attrs: dict[str, str] = {}
198 
199  async def async_update(self) -> None:
200  """Update the state of the sensor."""
201  now = dt_util.now()
202  _LOGGER.debug("Now: %s Location: %r", now, self._location_location)
203 
204  today = now.date()
205  event_date = get_astral_event_date(self.hasshass, SUN_EVENT_SUNSET, today)
206 
207  if event_date is None:
208  _LOGGER.error("Can't get sunset event date for %s", today)
209  return
210 
211  sunset = dt_util.as_local(event_date)
212 
213  _LOGGER.debug("Now: %s Sunset: %s", now, sunset)
214 
215  daytime_date = HDate(today, diaspora=self._diaspora_diaspora, hebrew=self._hebrew_hebrew)
216 
217  # The Jewish day starts after darkness (called "tzais") and finishes at
218  # sunset ("shkia"). The time in between is a gray area
219  # (aka "Bein Hashmashot" # codespell:ignore
220  # - literally: "in between the sun and the moon").
221 
222  # For some sensors, it is more interesting to consider the date to be
223  # tomorrow based on sunset ("shkia"), for others based on "tzais".
224  # Hence the following variables.
225  after_tzais_date = after_shkia_date = daytime_date
226  today_times = self.make_zmanimmake_zmanim(today)
227 
228  if now > sunset:
229  after_shkia_date = daytime_date.next_day
230 
231  if today_times.havdalah and now > today_times.havdalah:
232  after_tzais_date = daytime_date.next_day
233 
234  self._attr_native_value_attr_native_value = self.get_stateget_state(
235  daytime_date, after_shkia_date, after_tzais_date
236  )
237  _LOGGER.debug(
238  "New value for %s: %s", self.entity_descriptionentity_description.key, self._attr_native_value_attr_native_value
239  )
240 
241  def make_zmanim(self, date: Date) -> Zmanim:
242  """Create a Zmanim object."""
243  return Zmanim(
244  date=date,
245  location=self._location_location,
246  candle_lighting_offset=self._candle_lighting_offset_candle_lighting_offset,
247  havdalah_offset=self._havdalah_offset_havdalah_offset,
248  hebrew=self._hebrew_hebrew,
249  )
250 
251  @property
252  def extra_state_attributes(self) -> dict[str, str]:
253  """Return the state attributes."""
254  return self._attrs_attrs
255 
257  self, daytime_date: HDate, after_shkia_date: HDate, after_tzais_date: HDate
258  ) -> Any | None:
259  """For a given type of sensor, return the state."""
260  # Terminology note: by convention in py-libhdate library, "upcoming"
261  # refers to "current" or "upcoming" dates.
262  if self.entity_descriptionentity_description.key == "date":
263  hdate = cast(HebrewDate, after_shkia_date.hdate)
264  month = htables.MONTHS[hdate.month.value - 1]
265  self._attrs_attrs = {
266  "hebrew_year": hdate.year,
267  "hebrew_month_name": month.hebrew if self._hebrew_hebrew else month.english,
268  "hebrew_day": hdate.day,
269  }
270  return after_shkia_date.hebrew_date
271  if self.entity_descriptionentity_description.key == "weekly_portion":
272  self._attr_options_attr_options = [
273  (p.hebrew if self._hebrew_hebrew else p.english) for p in htables.PARASHAOT
274  ]
275  # Compute the weekly portion based on the upcoming shabbat.
276  return after_tzais_date.upcoming_shabbat.parasha
277  if self.entity_descriptionentity_description.key == "holiday":
278  _id = _type = _type_id = ""
279  _holiday_type = after_shkia_date.holiday_type
280  if isinstance(_holiday_type, list):
281  _id = ", ".join(after_shkia_date.holiday_name)
282  _type = ", ".join([_htype.name for _htype in _holiday_type])
283  _type_id = ", ".join([str(_htype.value) for _htype in _holiday_type])
284  else:
285  _id = after_shkia_date.holiday_name
286  _type = _holiday_type.name
287  _type_id = _holiday_type.value
288  self._attrs_attrs = {"id": _id, "type": _type, "type_id": _type_id}
289  self._attr_options_attr_options = htables.get_all_holidays(self._language_language)
290 
291  return after_shkia_date.holiday_description
292  if self.entity_descriptionentity_description.key == "omer_count":
293  return after_shkia_date.omer_day
294  if self.entity_descriptionentity_description.key == "daf_yomi":
295  return daytime_date.daf_yomi
296 
297  return None
298 
299 
301  """Implement attributes for sensors returning times."""
302 
303  _attr_device_class = SensorDeviceClass.TIMESTAMP
304 
306  self, daytime_date: HDate, after_shkia_date: HDate, after_tzais_date: HDate
307  ) -> Any | None:
308  """For a given type of sensor, return the state."""
309  if self.entity_descriptionentity_description.key == "upcoming_shabbat_candle_lighting":
310  times = self.make_zmanimmake_zmanim(
311  after_tzais_date.upcoming_shabbat.previous_day.gdate
312  )
313  return times.candle_lighting
314  if self.entity_descriptionentity_description.key == "upcoming_candle_lighting":
315  times = self.make_zmanimmake_zmanim(
316  after_tzais_date.upcoming_shabbat_or_yom_tov.first_day.previous_day.gdate
317  )
318  return times.candle_lighting
319  if self.entity_descriptionentity_description.key == "upcoming_shabbat_havdalah":
320  times = self.make_zmanimmake_zmanim(after_tzais_date.upcoming_shabbat.gdate)
321  return times.havdalah
322  if self.entity_descriptionentity_description.key == "upcoming_havdalah":
323  times = self.make_zmanimmake_zmanim(
324  after_tzais_date.upcoming_shabbat_or_yom_tov.last_day.gdate
325  )
326  return times.havdalah
327 
328  times = self.make_zmanimmake_zmanim(dt_util.now()).zmanim
329  return times[self.entity_descriptionentity_description.key]
Any|None get_state(self, HDate daytime_date, HDate after_shkia_date, HDate after_tzais_date)
Definition: sensor.py:258
None __init__(self, JewishCalendarConfigEntry config_entry, SensorEntityDescription description)
Definition: sensor.py:194
Any|None get_state(self, HDate daytime_date, HDate after_shkia_date, HDate after_tzais_date)
Definition: sensor.py:307
None async_setup_entry(HomeAssistant hass, JewishCalendarConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: sensor.py:172
datetime.datetime|None get_astral_event_date(HomeAssistant hass, str event, datetime.date|datetime.datetime|None date=None)
Definition: sun.py:117