5
5
from collections .abc import Callable
6
6
from dataclasses import dataclass
7
7
import datetime as dt
8
+ import logging
8
9
9
10
from hdate import HDateInfo , Zmanim
10
11
from hdate .holidays import HolidayDatabase
15
16
SensorEntity ,
16
17
SensorEntityDescription ,
17
18
)
18
- from homeassistant .const import EntityCategory
19
+ from homeassistant .const import SUN_EVENT_SUNSET , EntityCategory
19
20
from homeassistant .core import HomeAssistant
20
- from homeassistant .helpers .entity import EntityDescription
21
21
from homeassistant .helpers .entity_platform import AddConfigEntryEntitiesCallback
22
+ from homeassistant .helpers .sun import get_astral_event_date
23
+ from homeassistant .util import dt as dt_util
22
24
23
25
from .entity import (
24
26
JewishCalendarConfigEntry ,
25
27
JewishCalendarDataResults ,
26
28
JewishCalendarEntity ,
27
29
)
28
30
31
+ _LOGGER = logging .getLogger (__name__ )
32
+
33
+
34
+ @dataclass (frozen = True , kw_only = True )
35
+ class JewishCalendarBaseSensorDescription (SensorEntityDescription ):
36
+ """Base class describing Jewish Calendar sensor entities."""
37
+
38
+ value_fn : Callable | None
39
+
29
40
30
41
@dataclass (frozen = True , kw_only = True )
31
- class JewishCalendarSensorDescription (SensorEntityDescription ):
42
+ class JewishCalendarSensorDescription (JewishCalendarBaseSensorDescription ):
32
43
"""Class describing Jewish Calendar sensor entities."""
33
44
34
45
value_fn : Callable [[JewishCalendarDataResults ], str | int ]
35
46
36
47
37
48
@dataclass (frozen = True , kw_only = True )
38
- class JewishCalendarTimestampSensorDescription (SensorEntityDescription ):
39
- """Class describing Jewish Calendar sensor entities."""
49
+ class JewishCalendarTimestampSensorDescription (JewishCalendarBaseSensorDescription ):
50
+ """Class describing Jewish Calendar sensor timestamp entities."""
40
51
41
52
value_fn : (
42
53
Callable [[HDateInfo , Callable [[dt .date ], Zmanim ]], dt .datetime | None ] | None
@@ -209,29 +220,75 @@ async def async_setup_entry(
209
220
async_add_entities : AddConfigEntryEntitiesCallback ,
210
221
) -> None :
211
222
"""Set up the Jewish calendar sensors ."""
212
- async_add_entities (
213
- [
214
- JewishCalendarSensor (config_entry , description )
215
- for description in INFO_SENSORS
216
- ]
217
- )
218
- async_add_entities (
219
- [
220
- JewishCalendarTimeSensor (config_entry , description )
221
- for description in TIME_SENSORS
222
- ]
223
+ sensors : list [JewishCalendarBaseSensor ] = [
224
+ JewishCalendarSensor (config_entry , description ) for description in INFO_SENSORS
225
+ ]
226
+ sensors .extend (
227
+ JewishCalendarTimeSensor (config_entry , description )
228
+ for description in TIME_SENSORS
223
229
)
230
+ async_add_entities (sensors )
224
231
225
232
226
- class JewishCalendarSensor (JewishCalendarEntity , SensorEntity ):
227
- """Representation of an Jewish calendar sensor ."""
233
+ class JewishCalendarBaseSensor (JewishCalendarEntity , SensorEntity ):
234
+ """Base class for Jewish calendar sensors ."""
228
235
229
236
_attr_entity_category = EntityCategory .DIAGNOSTIC
230
237
238
+ async def async_added_to_hass (self ) -> None :
239
+ """Call when entity is added to hass."""
240
+ await super ().async_added_to_hass ()
241
+ await self .async_update_data ()
242
+
243
+ async def async_update_data (self ) -> None :
244
+ """Update the state of the sensor."""
245
+ now = dt_util .now ()
246
+ _LOGGER .debug ("Now: %s Location: %r" , now , self .data .location )
247
+
248
+ today = now .date ()
249
+ event_date = get_astral_event_date (self .hass , SUN_EVENT_SUNSET , today )
250
+
251
+ if event_date is None :
252
+ _LOGGER .error ("Can't get sunset event date for %s" , today )
253
+ return
254
+
255
+ sunset = dt_util .as_local (event_date )
256
+
257
+ _LOGGER .debug ("Now: %s Sunset: %s" , now , sunset )
258
+
259
+ daytime_date = HDateInfo (today , diaspora = self .data .diaspora )
260
+
261
+ # The Jewish day starts after darkness (called "tzais") and finishes at
262
+ # sunset ("shkia"). The time in between is a gray area
263
+ # (aka "Bein Hashmashot" # codespell:ignore
264
+ # - literally: "in between the sun and the moon").
265
+
266
+ # For some sensors, it is more interesting to consider the date to be
267
+ # tomorrow based on sunset ("shkia"), for others based on "tzais".
268
+ # Hence the following variables.
269
+ after_tzais_date = after_shkia_date = daytime_date
270
+ today_times = self .make_zmanim (today )
271
+
272
+ if now > sunset :
273
+ after_shkia_date = daytime_date .next_day
274
+
275
+ if today_times .havdalah and now > today_times .havdalah :
276
+ after_tzais_date = daytime_date .next_day
277
+
278
+ self .data .results = JewishCalendarDataResults (
279
+ daytime_date , after_shkia_date , after_tzais_date , today_times
280
+ )
281
+
282
+
283
+ class JewishCalendarSensor (JewishCalendarBaseSensor ):
284
+ """Representation of an Jewish calendar sensor."""
285
+
231
286
entity_description : JewishCalendarSensorDescription
232
287
233
288
def __init__ (
234
- self , config_entry : JewishCalendarConfigEntry , description : EntityDescription
289
+ self ,
290
+ config_entry : JewishCalendarConfigEntry ,
291
+ description : SensorEntityDescription ,
235
292
) -> None :
236
293
"""Initialize the Jewish calendar sensor."""
237
294
super ().__init__ (config_entry , description )
@@ -249,11 +306,6 @@ def native_value(self) -> str | int | dt.datetime | None:
249
306
return None
250
307
return self .entity_description .value_fn (self .data .results )
251
308
252
- async def async_added_to_hass (self ) -> None :
253
- """Call when entity is added to hass."""
254
- await super ().async_added_to_hass ()
255
- await self .async_update_data ()
256
-
257
309
@property
258
310
def extra_state_attributes (self ) -> dict [str , str ]:
259
311
"""Return the state attributes."""
@@ -276,12 +328,10 @@ def extra_state_attributes(self) -> dict[str, str]:
276
328
return {}
277
329
278
330
279
- class JewishCalendarTimeSensor (JewishCalendarEntity , SensorEntity ):
331
+ class JewishCalendarTimeSensor (JewishCalendarBaseSensor ):
280
332
"""Implement attributes for sensors returning times."""
281
333
282
- _attr_entity_category = EntityCategory .DIAGNOSTIC
283
334
_attr_device_class = SensorDeviceClass .TIMESTAMP
284
-
285
335
entity_description : JewishCalendarTimestampSensorDescription
286
336
287
337
@property
@@ -294,8 +344,3 @@ def native_value(self) -> dt.datetime | None:
294
344
return self .entity_description .value_fn (
295
345
self .data .results .after_tzais_date , self .make_zmanim
296
346
)
297
-
298
- async def async_added_to_hass (self ) -> None :
299
- """Call when entity is added to hass."""
300
- await super ().async_added_to_hass ()
301
- await self .async_update_data ()
0 commit comments