fix(alarmclock): fiabiliser le rafraîchissement du calendrier ICS
- Ajouter deux tentatives de reprise après 2 puis 5 minutes en cas d’échec. - Mémoriser le dernier succès et adapter le niveau des logs à l’ancienneté du calendrier. - Distinguer les erreurs réseau, HTTP et les données ICS invalides. - Définir les délais de connexion et de lecture et vérifier le statut HTTP. - Actualiser le statut du jour travaillé et l’heure du réveil après une reprise.
This commit is contained in:
1 parent
ff39f4cb2f
commit
9802982d73
1 file changed
+81
-8
+81
-8
@@ -32,7 +32,19 @@ class CalendarEvent:
|
||||
|
||||
class AlarmClock(SmartObject):
|
||||
|
||||
CALENDAR_RETRY_DELAYS = (2 * 60, 5 * 60)
|
||||
|
||||
@property
|
||||
def calendar_last_success(self):
|
||||
return self.dataset.get('calendar_last_success')
|
||||
|
||||
@calendar_last_success.setter
|
||||
def calendar_last_success(self, value):
|
||||
self.dataset['calendar_last_success'] = value
|
||||
|
||||
def on_initialize_smart_object(self):
|
||||
if self.dataset is None:
|
||||
self.dataset = {}
|
||||
self.switch_alarmclock_main = self.args['switch_alarmclock_main']
|
||||
self.switch_alarmclock_secondary = self.args['switch_alarmclock_secondary']
|
||||
self.inputdate_alarmclock_main = self.args['inputdate_alarmclock_main']
|
||||
@@ -46,6 +58,8 @@ class AlarmClock(SmartObject):
|
||||
self.handle_program_next_wakueup_cb = None
|
||||
self.handle_prewakueup_cb = None
|
||||
self.handle_wakueup_cb = None
|
||||
self.calendar_retry_handle = None
|
||||
self.calendar_retry_attempt = 0
|
||||
self.handle_switch_reveil_principal = self.listen_state(self.on_alarm_clock_set, self.switch_alarmclock_main)
|
||||
self.handle_switch_reveil_secondaire = self.listen_state(self.on_alarm_clock_set, self.switch_alarmclock_secondary)
|
||||
self.listen_state(self.on_alarm_clock_set, self.inputdate_alarmclock_main)
|
||||
@@ -64,17 +78,64 @@ class AlarmClock(SmartObject):
|
||||
|
||||
def refresh_cal(self):
|
||||
with self.log_group("refresh_cal"):
|
||||
new_cal = list()
|
||||
new_cal = list()
|
||||
#if self.load_cal_from_google_calendar(new_cal):
|
||||
if self.load_cal_from_ics(new_cal):
|
||||
if self.load_cal_from_ics(new_cal):
|
||||
self.calendar_last_success = datetime.datetime.now(datetime.timezone.utc)
|
||||
self.calendar_retry_attempt = 0
|
||||
self._cancel_calendar_retry()
|
||||
if not self.are_cal_identical(self.calendar,new_cal):
|
||||
self.calendar = new_cal
|
||||
self.log_info("Calendar has been updated!")
|
||||
self.print_cal(self.calendar)
|
||||
return True
|
||||
|
||||
else:
|
||||
self._schedule_calendar_retry()
|
||||
|
||||
return False
|
||||
|
||||
def _log_calendar_failure(self, reason):
|
||||
last_success = self.calendar_last_success
|
||||
if last_success is None:
|
||||
log = self.log_error
|
||||
freshness = 'no successful refresh recorded'
|
||||
else:
|
||||
age = datetime.datetime.now(datetime.timezone.utc) - last_success
|
||||
if age < datetime.timedelta(hours=24):
|
||||
log = self.log_info
|
||||
elif age < datetime.timedelta(hours=48):
|
||||
log = self.log_warning
|
||||
else:
|
||||
log = self.log_error
|
||||
freshness = (f'last_success={last_success.isoformat()}, '
|
||||
f'age={age.total_seconds() / 3600:.2f}h')
|
||||
log(f"Couldn't load calendar: {reason}; {freshness}")
|
||||
|
||||
def _schedule_calendar_retry(self):
|
||||
if self.calendar_retry_handle is not None:
|
||||
return
|
||||
if self.calendar_retry_attempt >= len(self.CALENDAR_RETRY_DELAYS):
|
||||
return
|
||||
delay = self.CALENDAR_RETRY_DELAYS[self.calendar_retry_attempt]
|
||||
self.calendar_retry_attempt += 1
|
||||
self.calendar_retry_handle = self.run_in(self.on_calendar_retry, delay)
|
||||
self.log_info(f"Calendar refresh retry scheduled in {delay // 60} minutes")
|
||||
|
||||
def _cancel_calendar_retry(self):
|
||||
if self.calendar_retry_handle is None:
|
||||
return
|
||||
if self.timer_running(self.calendar_retry_handle):
|
||||
self.cancel_timer(self.calendar_retry_handle)
|
||||
self.calendar_retry_handle = None
|
||||
|
||||
def on_calendar_retry(self, kwargs):
|
||||
self.calendar_retry_handle = None
|
||||
calendar_changed = self.refresh_cal()
|
||||
self.update_workday_status()
|
||||
if calendar_changed:
|
||||
self.update_wakeup_time()
|
||||
|
||||
def are_cal_identical(self,calendar1,calendar2):
|
||||
if len(calendar1) != len(calendar2): return False
|
||||
|
||||
@@ -93,7 +154,8 @@ class AlarmClock(SmartObject):
|
||||
|
||||
try:
|
||||
self.log_info(f"Retrieving calengar from outlook.office365.com")
|
||||
r = get("https://outlook.office365.com/owa/calendar/0c64359c70eb41cd9713fe92c470d3df@ubisoft.com/5edb3fb5150f4203b66d9cf8db3184be11626033572842691749/calendar.ics", timeout=10)#, headers=headers, verify=False)
|
||||
r = get("https://outlook.office365.com/owa/calendar/0c64359c70eb41cd9713fe92c470d3df@ubisoft.com/5edb3fb5150f4203b66d9cf8db3184be11626033572842691749/calendar.ics", timeout=(3, 5))#, headers=headers, verify=False)
|
||||
r.raise_for_status()
|
||||
#self.log_info(f"Processing Calendar")
|
||||
|
||||
today = datetime.datetime.combine(datetime.date.today(), datetime.time(0,0, 0))
|
||||
@@ -101,7 +163,7 @@ class AlarmClock(SmartObject):
|
||||
end_load_date = today + datetime.timedelta(days=2)
|
||||
#self.log_info("[" + str(start_load_date) + "]-[" + str(end_load_date) + "]")
|
||||
|
||||
calendar_ubi = icalendar.Calendar.from_ical(r.text)
|
||||
calendar_ubi = icalendar.Calendar.from_ical(r.content)
|
||||
for component in calendar_ubi.walk():
|
||||
if component.name == "VEVENT":
|
||||
event_summary = component.get('summary')
|
||||
@@ -137,11 +199,20 @@ class AlarmClock(SmartObject):
|
||||
calendar.append(CalendarEvent(start_datetime,end_datetime,event_type))
|
||||
else: self.log_warning(f"[Unknow event : {event_summary}]Start at {start_datetime},Finish at {end_datetime}")
|
||||
|
||||
except ValueError:
|
||||
self.log_error("Couldn't load calendar")
|
||||
except requests.exceptions.Timeout as error:
|
||||
self._log_calendar_failure(f"request timed out ({type(error).__name__})")
|
||||
return False
|
||||
except (requests.exceptions.ConnectionError,requests.exceptions.ReadTimeout):
|
||||
self.log_warning("Couldn't load calendar, request Timeout")
|
||||
except requests.exceptions.ConnectionError:
|
||||
self._log_calendar_failure("connection failed")
|
||||
return False
|
||||
except requests.exceptions.HTTPError as error:
|
||||
self._log_calendar_failure(f"HTTP {error.response.status_code}")
|
||||
return False
|
||||
except requests.exceptions.RequestException as error:
|
||||
self._log_calendar_failure(type(error).__name__)
|
||||
return False
|
||||
except (ValueError, TypeError, KeyError):
|
||||
self._log_calendar_failure("invalid ICS data")
|
||||
return False
|
||||
|
||||
#self.log_info(f"Calendar loaded")
|
||||
@@ -151,6 +222,8 @@ class AlarmClock(SmartObject):
|
||||
self.update_wakeup_time()
|
||||
|
||||
def on_refresh_cal(self, kwargs):
|
||||
self._cancel_calendar_retry()
|
||||
self.calendar_retry_attempt = 0
|
||||
calendar_changed = self.refresh_cal()
|
||||
self.update_workday_status()
|
||||
if calendar_changed:
|
||||
|
||||
Reference in new issue
Block a user