Skip to content

Commit 6ae632d

Browse files
authored
Update chametz sensor logic and add Pesach handling
Refactor chametz sensor classes and add Pesach info retrieval.
1 parent 0c6994f commit 6ae632d

File tree

1 file changed

+79
-55
lines changed

1 file changed

+79
-55
lines changed

custom_components/yidcal/zman_chumetz.py

Lines changed: 79 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,23 @@
1717
from .zman_sensors import get_geo
1818

1919

20-
def _get_bedikat_day_year(today_date: date) -> tuple[int, int]:
21-
"""Return (Hebrew year, day in Nisan) for Chametz search day (14 or 12 Nisan)."""
20+
def _get_pesach_info(today_date: date) -> tuple[int, bool]:
21+
"""Return (Hebrew year, is_deferred) for the upcoming Pesach.
22+
23+
is_deferred is True when 14 Nisan falls on Shabbos (15 Nisan = Sunday).
24+
"""
2225
for delta in (0, 1):
2326
civil_year = today_date.year + delta
2427
hy = pl_dates.GregorianDate(civil_year, 4, 1).to_heb().year
2528

26-
# normally 14 Nisan
27-
candidate = pl_dates.HebrewDate(hy, 1, 14)
28-
# but if 15 Nisan falls on a Sunday, use 12 Nisan
29-
fifteenth = pl_dates.HebrewDate(hy, 1, 15)
30-
if fifteenth.to_pydate().weekday() == 6:
31-
candidate = pl_dates.HebrewDate(hy, 1, 12)
32-
33-
cdate = candidate.to_pydate()
29+
fourteenth = pl_dates.HebrewDate(hy, 1, 14)
30+
cdate = fourteenth.to_pydate()
3431
if cdate >= today_date:
35-
return hy, candidate.day
32+
deferred = (cdate.weekday() == 5) # 14 Nisan is Shabbos
33+
return hy, deferred
3634

37-
# fallback to next year's 14 Nisan
38-
return hy + 1, 14
35+
hy_next = pl_dates.GregorianDate(today_date.year + 2, 4, 1).to_heb().year
36+
return hy_next, False
3937

4038

4139
class _BaseChumetzSensor(YidCalZmanDevice, RestoreEntity, SensorEntity):
@@ -83,21 +81,16 @@ async def async_added_to_hass(self) -> None:
8381
async def _midnight_update(self, now: datetime) -> None:
8482
await self.async_update()
8583

86-
def _compute_target(self, hours_from_dawn: float) -> datetime:
87-
"""Compute dawn + hours * sha'ah_zmanit on the correct Nisan date."""
88-
now_local = dt_util.now().astimezone(self._tz)
89-
hy, day = _get_bedikat_day_year(now_local.date())
90-
91-
# Hebrew→civil date
92-
heb = pl_dates.HebrewDate(hy, 1, day)
93-
g_py = heb.to_pydate()
84+
def _compute_for_date(self, civil_date: date, hours_from_dawn: float) -> tuple[datetime, str]:
85+
"""Compute dawn + hours × sha'ah zmanit for a given civil date.
9486
95-
# get geometric sunrise & sunset
96-
cal = ZmanimCalendar(geo_location=self._geo, date=g_py)
87+
Returns (floored_local_dt, raw_iso_string).
88+
"""
89+
cal = ZmanimCalendar(geo_location=self._geo, date=civil_date)
9790
sunrise = cal.sunrise().astimezone(self._tz)
9891
sunset = cal.sunset().astimezone(self._tz)
9992

100-
# MGA day: dawn = sunrise − havdalah, nightfall = sunset + havdalah
93+
# MGA "day": dawn = sunrise − havdalah, nightfall = sunset + havdalah
10194
dawn = sunrise - timedelta(minutes=self._havdalah)
10295
nightfall = sunset + timedelta(minutes=self._havdalah)
10396

@@ -106,25 +99,22 @@ def _compute_target(self, hours_from_dawn: float) -> datetime:
10699

107100
# raw target
108101
raw = dawn + hour_len * hours_from_dawn
102+
raw_iso = raw.isoformat()
109103

110-
# debug attrs (with seconds)
111-
self._attr_extra_state_attributes = {
112-
#"dawn": dawn.isoformat(),
113-
#"sunrise": sunrise.isoformat(),
114-
#"sunset": sunset.isoformat(),
115-
#"nightfall": nightfall.isoformat(),
116-
#"hour_len_s": hour_len.total_seconds(), # seconds per sha'ah
117-
"Sof_Zman_Chumetz_With_Seconds": raw.isoformat(),
118-
}
104+
# floor to the minute
105+
floored = raw.replace(second=0, microsecond=0)
106+
107+
return floored, raw_iso
119108

120-
# floor to the minute (any seconds 0–59)
121-
return raw.replace(second=0, microsecond=0)
122-
123109
# subclasses implement async_update()
124110

125111

126112
class SofZmanAchilasChumetzSensor(_BaseChumetzSensor):
127-
"""סוף-זמן אכילת חמץ עפ\"י המג\"א (4 שעות זמניות)."""
113+
"""סוף-זמן אכילת חמץ עפ\"י המג\"א (4 שעות זמניות).
114+
115+
Always computed on 14 Nisan — even in a deferred year,
116+
the halachic deadline for eating chametz is Shabbos morning.
117+
"""
128118

129119
def __init__(self, hass: HomeAssistant, candle: int, havdalah: int) -> None:
130120
super().__init__(
@@ -140,21 +130,32 @@ def __init__(self, hass: HomeAssistant, candle: int, havdalah: int) -> None:
140130
async def async_update(self, now: datetime | None = None) -> None:
141131
if not self._geo:
142132
return
143-
target = self._compute_target(4.0)
133+
134+
now_local = (now or dt_util.now()).astimezone(self._tz)
135+
hy, _ = _get_pesach_info(now_local.date())
136+
137+
# Always 14 Nisan
138+
civil_14 = pl_dates.HebrewDate(hy, 1, 14).to_pydate()
139+
target, raw_iso = self._compute_for_date(civil_14, 4.0)
140+
144141
self._attr_native_value = target.astimezone(timezone.utc)
145-
146-
# 3. build your human‐readable string
147-
local = target.astimezone(self._tz)
148-
human = self._format_simple_time(local)
149-
150-
# 4. merge it into the existing attributes
151-
attrs = {**(self._attr_extra_state_attributes or {})}
152-
attrs["Sof_Zman_Achilas_Chumetz_Simple"] = human
153-
self._attr_extra_state_attributes = attrs
142+
143+
human = self._format_simple_time(target.astimezone(self._tz))
144+
self._attr_extra_state_attributes = {
145+
"Sof_Zman_Chumetz_With_Seconds": raw_iso,
146+
"Sof_Zman_Achilas_Chumetz_Simple": human,
147+
}
154148

155149

156150
class SofZmanSriefesChumetzSensor(_BaseChumetzSensor):
157-
"""סוף-זמן שריפת חמץ עפ\"י המג\"א (5 שעות זמניות)."""
151+
"""סוף-זמן שריפת חמץ עפ\"י המג\"א (5 שעות זמניות).
152+
153+
Normal year: state + _Simple = 14 Nisan 5th hour.
154+
Deferred year (14 Nisan on Shabbos): state + _Simple = 13 Nisan
155+
Friday 5th hour (physical sriefa before Shabbos). An additional
156+
Sof_Zman_Biur_Simple attribute shows the 14 Nisan Shabbos 5th hour
157+
(halachic deadline for disposing of remaining chametz via bitul/flush).
158+
"""
158159

159160
def __init__(self, hass: HomeAssistant, candle: int, havdalah: int) -> None:
160161
super().__init__(
@@ -170,14 +171,37 @@ def __init__(self, hass: HomeAssistant, candle: int, havdalah: int) -> None:
170171
async def async_update(self, now: datetime | None = None) -> None:
171172
if not self._geo:
172173
return
173-
target = self._compute_target(5.0)
174+
175+
now_local = (now or dt_util.now()).astimezone(self._tz)
176+
hy, deferred = _get_pesach_info(now_local.date())
177+
178+
if deferred:
179+
# Sriefa is Friday (13 Nisan) — state + _Simple
180+
civil_13 = pl_dates.HebrewDate(hy, 1, 13).to_pydate()
181+
target, raw_iso = self._compute_for_date(civil_13, 5.0)
182+
183+
# Biur is Shabbos (14 Nisan) — attribute only
184+
civil_14 = pl_dates.HebrewDate(hy, 1, 14).to_pydate()
185+
biur_target, biur_raw_iso = self._compute_for_date(civil_14, 5.0)
186+
else:
187+
# Normal year — sriefa and biur are the same day
188+
civil_14 = pl_dates.HebrewDate(hy, 1, 14).to_pydate()
189+
target, raw_iso = self._compute_for_date(civil_14, 5.0)
190+
biur_target = None
191+
biur_raw_iso = None
192+
174193
self._attr_native_value = target.astimezone(timezone.utc)
175194

176-
# 3. build your human‐readable string
177-
local = target.astimezone(self._tz)
178-
human = self._format_simple_time(local)
195+
human = self._format_simple_time(target.astimezone(self._tz))
196+
attrs: dict[str, object] = {
197+
"Sof_Zman_Chumetz_With_Seconds": raw_iso,
198+
"Sof_Zman_Sriefes_Chumetz_Simple": human,
199+
}
200+
201+
if biur_target is not None:
202+
attrs["Sof_Zman_Biur_With_Seconds"] = biur_raw_iso
203+
attrs["Sof_Zman_Biur_Simple"] = self._format_simple_time(
204+
biur_target.astimezone(self._tz)
205+
)
179206

180-
# 4. merge it into the existing attributes
181-
attrs = {**(self._attr_extra_state_attributes or {})}
182-
attrs["Sof_Zman_Sriefes_Chumetz_Simple"] = human
183207
self._attr_extra_state_attributes = attrs

0 commit comments

Comments
 (0)