1818from homeassistant .exceptions import ConfigEntryNotReady
1919from homeassistant .helpers import config_validation as cv
2020from homeassistant .helpers .dispatcher import async_dispatcher_send
21- from homeassistant .helpers .event import async_call_later , async_track_point_in_time
21+ from homeassistant .helpers .event import async_call_later , async_track_point_in_time , async_track_time_change
2222import homeassistant .util .dt as dt_util
2323# from homeassistant.helpers import aiohttp_client
2424
2727 API ,
2828 CONF_CALC_METHOD ,
2929 DATA_UPDATED ,
30+ UPDATE_TIME ,
3031 DEFAULT_CALC_METHOD ,
3132 DOMAIN ,
3233 USERNAME ,
@@ -163,7 +164,7 @@ def get_new_prayer_times(self):
163164 mosque_id = uuid_servers [indice ]
164165
165166 # We get the prayer times of the year from pray_time.txt
166- f = open ('{dir}/data/pray_time.txt' .format (dir = current_dir , name = "" ) )
167+ f = open ('{dir}/data/pray_time.txt' .format (dir = current_dir ), "r" )
167168
168169 data = json .load (f )
169170 calendar = data ["calendar" ]
@@ -176,15 +177,19 @@ def get_new_prayer_times(self):
176177 index_day = today .day
177178 day_times = month_times [str (index_day )] # Today's times
178179
179- prayer_names = ["Fajr" , "Shurouq" , "Dhuhr" , "Asr" , "Maghrib" , "Isha" ]
180+ prayer_names = ["Fajr" , "Shurouq" , "Dhuhr" , "Asr" , "Maghrib" , "Isha" ]
180181 res = {prayer_names [i ]: day_times [i ] for i in range (len (prayer_names ))}
181182
182183 try :
183184 day_times_tomorrow = month_times [str (index_day + 1 )]
184185 except KeyError :
185186 # If index_day + 1 == 32 (or 31) and the month contains only 31 (or 30) days
186- # We take the first prayer of the following month
187- day_times_tomorrow = calendar [index_month + 1 ]["1" ]
187+ # We take the first day of the following month (reset 0 if we're in december)
188+ if index_month == 11 :
189+ index_next_month = 0
190+ else :
191+ index_next_month = index_month + 1
192+ day_times_tomorrow = calendar [index_next_month ]["1" ]
188193
189194 now = today .time ().strftime ("%H:%M" )
190195
@@ -208,10 +213,9 @@ def get_new_prayer_times(self):
208213 res ['Next Salat Time' ] = next_prayer .split (" " , 1 )[1 ].rsplit (':' , 1 )[0 ]
209214 res ['Next Salat Name' ] = prayer_names [prayers .index (next_prayer )]
210215
211- # 15 minutes Before Next Prayer
212216 countdown_next_prayer = 15
217+ # 15 minutes Before Next Prayer
213218 res ['Next Salat Preparation' ] = (datetime .strptime (next_prayer , '%Y-%m-%d %H:%M:%S' )- timedelta (minutes = countdown_next_prayer )).strftime ('%Y-%m-%d %H:%M:%S' ).split (" " , 1 )[1 ].rsplit (':' , 1 )[0 ]
214-
215219
216220 # if Jumu'a is set as Dhuhr, then Jumu'a time is the same as Friday's Dhuhr time
217221 if data ["jumuaAsDuhr" ]:
@@ -263,70 +267,60 @@ def get_new_prayer_times(self):
263267 res2 = {** res , ** res1 }
264268
265269 return res2
270+
271+
272+ async def async_update_next_salat_sensor (self , * _ ):
273+ salat_before_update = self .prayer_times_info ['Next Salat Name' ]
274+ prayers = ["Fajr" , "Dhuhr" , "Asr" , "Maghrib" , "Isha" ]
275+ if salat_before_update != "Isha" : # We just retrieve the next salat of the day.
276+ index = prayers .index (salat_before_update ) + 1
277+ self .prayer_times_info ['Next Salat Name' ] = prayers [index ]
278+ self .prayer_times_info ['Next Salat Time' ] = self .prayer_times_info [prayers [index ]]
279+
280+ else : # We retrieve the next Fajr (more calcualtions).
281+ current_dir = os .path .dirname (os .path .realpath (__file__ ))
282+ f = open ('{dir}/data/pray_time.txt' .format (dir = current_dir ), "r" )
283+ data = json .load (f )
284+ calendar = data ["calendar" ]
266285
286+ today = datetime .today ()
287+ index_month = today .month - 1
288+ month_times = calendar [index_month ]
267289
268- async def async_schedule_future_update (self ):
269- """Schedule future update for sensors.
270-
271- Midnight is a calculated time. The specifics of the calculation
272- depends on the method of the prayer time calculation. This calculated
273- midnight is the time at which the time to pray the Isha prayers have
274- expired.
275-
276- Calculated Midnight: The Mawaqit midnight.
277- Traditional Midnight: Isha time + 1 minute
278-
279- Update logic for prayer times:
280-
281- If the Calculated Midnight is before the traditional midnight then wait
282- until the traditional midnight to run the update. This way the day
283- will have changed over and we don't need to do any fancy calculations.
284-
285- If the Calculated Midnight is after the traditional midnight, then wait
286- until after the calculated Midnight. We don't want to update the prayer
287- times too early or else the timings might be incorrect.
288-
289- Example:
290- calculated midnight = 11:23PM (before traditional midnight)
291- Update time: 12:00AM
292-
293- calculated midnight = 1:35AM (after traditional midnight)
294- update time: 1:36AM.
295-
296- """
297- _LOGGER .debug ("Scheduling next update for Mawaqit prayer times" )
298-
299- now = dt_util .utcnow ()
300- now = dt_util .now ()
301-
302- midnight_dt = self .prayer_times_info ["Next Salat Time" ]
303- Fajr_dt = self .prayer_times_info ["Fajr" ]
304- Dhuhr_dt = self .prayer_times_info ["Dhuhr" ]
305- Asr_dt = self .prayer_times_info ["Asr" ]
306- Maghrib_dt = self .prayer_times_info ["Maghrib" ]
307- Isha_dt = self .prayer_times_info ["Isha" ]
308-
309- prayer_times = [Fajr_dt , Dhuhr_dt , Asr_dt , Maghrib_dt , Isha_dt ]
310-
311- midnight_dt = min (prayer_times )
290+ maghrib_hour = self .prayer_times_info ['Maghrib' ]
291+ maghrib_hour = maghrib_hour .strftime ("%H:%M" )
292+
293+ # isha + 1 minute because this function is launched 1 minute after 'Isha, (useful only if 'Isha is at 11:59 PM)
294+ isha_hour = self .prayer_times_info ['Isha' ] + timedelta (minutes = 1 )
295+ isha_hour = isha_hour .strftime ("%H:%M" )
296+
297+ # If 'Isha is before 12 AM (Maghrib hour < 'Isha hour), we need to get the next day's Fajr.
298+ # Else, we get the current day's Fajr.
299+ if maghrib_hour < isha_hour :
300+ index_day = today .day + 1
301+ else :
302+ index_day = today .day
303+
304+ try :
305+ day_times = month_times [str (index_day )]
306+ except KeyError :
307+ # If index_day + 1 == 32 (or 31) and the month contains only 31 (or 30) days
308+ # We take the first day of the following month (reset 0 if we're in december)
309+ if index_month == 11 :
310+ index_next_month = 0
311+ else :
312+ index_next_month = index_month + 1
313+ day_times = calendar [index_next_month ]["1" ]
314+ fajr_hour = day_times [0 ]
312315
313- if now > dt_util .as_utc (midnight_dt ):
314- next_update_at = midnight_dt + timedelta (days = 0 , minutes = 1 , seconds = 0 )
315- _LOGGER .debug (
316- "Midnight is after day the changes so schedule update for after Midnight the next day"
317- )
318- else :
319- _LOGGER .debug (
320- "Midnight is before the day changes so schedule update for the next start of day"
321- )
322- next_update_at = dt_util .start_of_local_day (now + timedelta (days = 1 ))
323- next_update_at = midnight_dt + timedelta (days = 0 , minutes = 1 )
316+ self .prayer_times_info ['Next Salat Name' ] = "Fajr"
317+ self .prayer_times_info ['Next Salat Time' ] = dt_util .parse_datetime (f"{ today .year } -{ today .month } -{ index_day } { fajr_hour } :00" )
324318
325- _LOGGER .info ("Next update scheduled for: %s" , next_update_at )
319+ countdown_next_prayer = 15
320+ self .prayer_times_info ['Next Salat Preparation' ] = self .prayer_times_info ['Next Salat Time' ] - timedelta (minutes = countdown_next_prayer )
326321
327- self .event_unsub = async_track_point_in_time (
328- self .hass , self .async_update , next_update_at
329- )
322+ _LOGGER .debug ("Next salat info updated, updating sensors" )
323+ async_dispatcher_send (self .hass , DATA_UPDATED )
330324
331325 async def async_update (self , * _ ):
332326 """Update sensors with new prayer times."""
@@ -366,9 +360,16 @@ async def async_update(self, *_):
366360 )
367361 else :
368362 self .prayer_times_info [prayer ] = time
369-
370-
371- await self .async_schedule_future_update ()
363+
364+ # We schedule updates for next_salat_time and next_salat_name at each prayer time + 1 minute.
365+ prayers = ["Fajr" , "Dhuhr" , "Asr" , "Maghrib" , "Isha" ]
366+ prayer_times = [self .prayer_times_info [prayer ] for prayer in prayers ]
367+
368+ for prayer in prayer_times :
369+ next_update_at = prayer + timedelta (minutes = 1 )
370+ async_track_point_in_time (
371+ self .hass , self .async_update_next_salat_sensor , next_update_at
372+ )
372373
373374 _LOGGER .debug ("New prayer times retrieved. Updating sensors." )
374375 async_dispatcher_send (self .hass , DATA_UPDATED )
@@ -386,6 +387,10 @@ async def async_setup(self):
386387 await self .async_update ()
387388 self .config_entry .add_update_listener (self .async_options_updated )
388389
390+ # We update time prayers every day.
391+ h , m , s = UPDATE_TIME
392+ async_track_time_change (self .hass , self .async_update , hour = h , minute = m , second = s )
393+
389394 return True
390395
391396 async def async_add_options (self ):
@@ -404,3 +409,4 @@ async def async_options_updated(hass, entry):
404409 if hass .data [DOMAIN ].event_unsub :
405410 hass .data [DOMAIN ].event_unsub ()
406411 await hass .data [DOMAIN ].async_update ()
412+
0 commit comments