def _monthly_candidate(self, day1: date) -> date: """Calculate possible date, for monthly frequency.""" if self._monthly_force_week_numbers: for week_order_number in self._week_order_numbers: candidate_date = MonthlyCollection.nth_week_date( week_order_number, day1, WEEKDAYS.index(self._collection_days[0])) # date is today or in the future -> we have the date if candidate_date >= day1: return candidate_date else: for weekday_order_number in self._weekday_order_numbers: candidate_date = MonthlyCollection.nth_weekday_date( weekday_order_number, day1, WEEKDAYS.index(self._collection_days[0]), ) # date is today or in the future -> we have the date if candidate_date >= day1: return candidate_date if day1.month == 12: next_collection_month = date(day1.year + 1, 1, 1) else: next_collection_month = date(day1.year, day1.month + 1, 1) if self._monthly_force_week_numbers: return MonthlyCollection.nth_week_date( self._week_order_numbers[0], next_collection_month, WEEKDAYS.index(self._collection_days[0]), ) return MonthlyCollection.nth_weekday_date( self._weekday_order_numbers[0], next_collection_month, WEEKDAYS.index(self._collection_days[0]), )
async def __async_monthly_candidate(self, day1: date) -> date: if self.__monthly_force_week_numbers: for week_order_number in self._week_order_numbers: candidate_date = nth_week_date( week_order_number, day1, WEEKDAYS.index(self.__collection_days[0]), ) # date is today or in the future -> we have the date if candidate_date >= day1: return candidate_date else: for weekday_order_number in self._weekday_order_numbers: candidate_date = nth_weekday_date( weekday_order_number, day1, WEEKDAYS.index(self.__collection_days[0]), ) # date is today or in the future -> we have the date if candidate_date >= day1: return candidate_date if day1.month == 12: next_collection_month = date(day1.year + 1, 1, 1) else: next_collection_month = date(day1.year, day1.month + 1, 1) if self.__monthly_force_week_numbers: return nth_week_date( self._week_order_numbers[0], next_collection_month, WEEKDAYS.index(self.__collection_days[0]), ) else: return nth_weekday_date( self._weekday_order_numbers[0], next_collection_month, WEEKDAYS.index(self.__collection_days[0]), )
def next_departuredate(departure: list[str]) -> date: """Calculate the next departuredate from an array input of short days.""" today_date = date.today() today_weekday = date.weekday(today_date) if WEEKDAYS[today_weekday] in departure: return today_date for day in departure: next_departure = WEEKDAYS.index(day) if next_departure > today_weekday: return next_weekday(today_date, next_departure) return next_weekday(today_date, WEEKDAYS.index(departure[0]))
def find_candidate_date(self, day1): """Find the next possible date starting from day1, only based on calendar, not lookimg at include/exclude days""" week = day1.isocalendar()[1] weekday = day1.weekday() if self._frequency in [ 'weekly', 'even-weeks', 'odd-weeks', 'every-n-weeks' ]: if self._frequency == 'weekly': period = 1 first_week = 1 elif self._frequency == 'even-weeks': period = 2 first_week = 2 elif self._frequency == 'odd-weeks': period = 2 first_week = 1 else: period = self._period first_week = self._first_week offset = -1 if (week - first_week) % period == 0: # Collection this week for day_name in self._collection_days: day_index = WEEKDAYS.index(day_name) if day_index >= weekday: # Collection still did not happen offset = day_index - weekday break if offset == -1: # look in following weeks in_weeks = period - (week - first_week) % period offset = 7 * in_weeks - weekday + WEEKDAYS.index( self._collection_days[0]) return day1 + timedelta(days=offset) elif self._frequency == 'monthly': # Monthly for monthly_day_order_number in self._monthly_day_order_numbers: candidate_date = nth_weekday_date( monthly_day_order_number, day1, WEEKDAYS.index(self._collection_days[0])) if candidate_date >= day1: # date is today or in the future -> we have the date return candidate_date next_collection_month = datetime( day1.year + 1, 1, 1).date() if day1.month == 12 else datetime( day1.year, day1.month + 1, 1).date() return (nth_weekday_date(self._monthly_day_order_numbers[0], next_collection_month, WEEKDAYS.index(self._collection_days[0]))) else: _LOGGER.debug( f"({self._name}) Unknown frequency {self._frequency}") return None
async def _async_weekly_candidate(self, day1: date, period: int, first_week: int) -> date: """Calculate possible date, for weekly frequency.""" week = day1.isocalendar()[1] weekday = day1.weekday() offset = -1 if (week - first_week) % period == 0: # Collection this week for day_name in self._collection_days: day_index = WEEKDAYS.index(day_name) if day_index >= weekday: # Collection still did not happen offset = day_index - weekday break iterate_by_week = 7 - weekday + WEEKDAYS.index( self._collection_days[0]) while offset == -1: # look in following weeks candidate = day1 + relativedelta(days=iterate_by_week) week = candidate.isocalendar()[1] if (week - first_week) % period == 0: offset = iterate_by_week break iterate_by_week += 7 return day1 + relativedelta(days=offset)
def load(self, config: OrderedDict): """Load schedule data from config""" self.clear() self._time = config[CONF_TIME] self._duration = wash_td(config[CONF_DURATION]) self._name = config.get(CONF_NAME, f"Schedule {self.schedule_index + 1}") if CONF_WEEKDAY in config: self._weekdays = [] for i in config[CONF_WEEKDAY]: self._weekdays.append(WEEKDAYS.index(i)) else: self._weekdays = None if CONF_MONTH in config: self._months = [] for i in config[CONF_MONTH]: self._months.append(MONTHS.index(i) + 1) else: self._months = None self._days = config.get(CONF_DAY, None) return self
def find_candidate_date(self, day1: datetime) -> datetime: """Find the next possible date starting from day1, only based on calendar, not lookimg at include/exclude days""" day1 = day1 week = day1.isocalendar()[1] weekday = day1.weekday() year = day1.year if self.__frequency in [ "weekly", "even-weeks", "odd-weeks", "every-n-weeks" ]: # Everything except montthly # convert to every-n-weeks if self.__frequency == "weekly": period = 1 first_week = 1 elif self.__frequency == "even-weeks": period = 2 first_week = 2 elif self.__frequency == "odd-weeks": period = 2 first_week = 1 else: period = self.__period first_week = self.__first_week offset = -1 if (week - first_week) % period == 0: # Collection this week for day_name in self.__collection_days: day_index = WEEKDAYS.index(day_name) if day_index >= weekday: # Collection still did not happen offset = day_index - weekday break if offset == -1: # look in following weeks in_weeks = period - (week - first_week) % period offset = (7 * in_weeks - weekday + WEEKDAYS.index(self.__collection_days[0])) return day1 + timedelta(days=offset) elif self.__frequency == "monthly": # Monthly for monthly_day_order_number in self.__monthly_day_order_numbers: candidate_date = nth_weekday_date( monthly_day_order_number, day1, WEEKDAYS.index(self.__collection_days[0]), ) # date is today or in the future -> we have the date if candidate_date.date() >= day1.date(): return candidate_date if day1.month == 12: next_collection_month = datetime(year + 1, 1, 1).astimezone() else: next_collection_month = datetime(year, day1.month + 1, 1).astimezone() return nth_weekday_date( self.__monthly_day_order_numbers[0], next_collection_month, WEEKDAYS.index(self.__collection_days[0]), ) elif self.__frequency == "annual": # Annual if self.__date is None: _LOGGER.error( "(%s) Please configure the date for annual collection frequency.", self.__name, ) return None conf_date = datetime.strptime(self.__date, "%m/%d") candidate_date = datetime(year, conf_date.month, conf_date.day).astimezone() if candidate_date.date() < day1.date(): candidate_date = datetime(year + 1, conf_date.month, conf_date.day).astimezone() return candidate_date elif self.__frequency == "group": if self.__entities is None: _LOGGER.error("(%s) Please add entities for the group.", self.__name) return None candidate_date = None for entity in self.__entities: d = self.hass.states.get(entity).attributes.get(ATTR_NEXT_DATE) if candidate_date is None or d.date() < candidate_date.date(): candidate_date = d return candidate_date else: _LOGGER.debug( f"({self.__name}) Unknown frequency {self.__frequency}") return None
async def __async_find_candidate_date(self, day1: date) -> date: """Find the next possible date starting from day1, only based on calendar, not lookimg at include/exclude days""" week = day1.isocalendar()[1] weekday = day1.weekday() year = day1.year if self.__frequency in [ "weekly", "even-weeks", "odd-weeks", "every-n-weeks" ]: # Everything except montthly # convert to every-n-weeks if self.__frequency == "weekly": period = 1 first_week = 1 elif self.__frequency == "even-weeks": period = 2 first_week = 2 elif self.__frequency == "odd-weeks": period = 2 first_week = 1 else: period = self.__period first_week = self.__first_week offset = -1 if (week - first_week) % period == 0: # Collection this week for day_name in self.__collection_days: day_index = WEEKDAYS.index(day_name) if day_index >= weekday: # Collection still did not happen offset = day_index - weekday break if offset == -1: # look in following weeks in_weeks = period - (week - first_week) % period offset = (7 * in_weeks - weekday + WEEKDAYS.index(self.__collection_days[0])) return day1 + relativedelta(days=offset) elif self.__frequency == "every-n-days": if self.__first_date is None or self.__period is None: _LOGGER.error( "(%s) Please configure first_date and period for every-n-days collection frequency.", self.__name, ) return None if (day1 - self.__first_date).days % self.__period == 0: return day1 offset = self.__period - ( (day1 - self.__first_date).days % self.__period) return day1 + relativedelta(days=offset) elif self.__frequency == "monthly": # Monthly if self.__period is None or self.__period == 1: return await self.__async_monthly_candidate(day1) else: candidate_date = await self.__async_monthly_candidate(day1) while (candidate_date.month - self.__first_month) % self.__period != 0: candidate_date = await self.__async_monthly_candidate( candidate_date + relativedelta(days=1)) return candidate_date elif self.__frequency == "annual": # Annual if self.__date is None: _LOGGER.error( "(%s) Please configure the date for annual collection frequency.", self.__name, ) return None conf_date = datetime.strptime(self.__date, "%m/%d").date() candidate_date = date(year, conf_date.month, conf_date.day) if candidate_date < day1: candidate_date = date(year + 1, conf_date.month, conf_date.day) return candidate_date elif self.__frequency == "group": if self.__entities is None: _LOGGER.error("(%s) Please add entities for the group.", self.__name) return None candidate_date = None for entity_id in self.__entities: if (SENSOR_PLATFORM in self.hass.data[DOMAIN] and entity_id in self.hass.data[DOMAIN][SENSOR_PLATFORM]): entity = self.hass.data[DOMAIN][SENSOR_PLATFORM][entity_id] d = await entity.async_find_next_date(day1) if candidate_date is None or d < candidate_date: candidate_date = d return candidate_date else: _LOGGER.debug( f"({self.__name}) Unknown frequency {self.__frequency}") return None
def find_candidate_date(self, day1: date) -> date: """Find the next possible date starting from day1, only based on calendar, not lookimg at include/exclude days""" week = day1.isocalendar()[1] weekday = day1.weekday() year = day1.year if self.__frequency in [ "weekly", "even-weeks", "odd-weeks", "every-n-weeks" ]: # Everything except montthly # convert to every-n-weeks if self.__frequency == "weekly": period = 1 first_week = 1 elif self.__frequency == "even-weeks": period = 2 first_week = 2 elif self.__frequency == "odd-weeks": period = 2 first_week = 1 else: period = self.__period first_week = self.__first_week offset = -1 if (week - first_week) % period == 0: # Collection this week for day_name in self.__collection_days: day_index = WEEKDAYS.index(day_name) if day_index >= weekday: # Collection still did not happen offset = day_index - weekday break if offset == -1: # look in following weeks in_weeks = period - (week - first_week) % period offset = (7 * in_weeks - weekday + WEEKDAYS.index(self.__collection_days[0])) return day1 + timedelta(days=offset) elif self.__frequency == "every-n-days": if self.__first_date is None or self.__period is None: _LOGGER.error( "(%s) Please configure first_date and period for every-n-days collection frequency.", self.__name, ) return None if (day1 - self.__first_date).days % self.__period == 0: return day1 offset = self.__period - ( (day1 - self.__first_date).days % self.__period) return day1 + timedelta(days=offset) elif self.__frequency == "monthly": # Monthly if self.__monthly_force_week_numbers: for week_order_number in self._week_order_numbers: candidate_date = nth_week_date( week_order_number, day1, WEEKDAYS.index(self.__collection_days[0]), ) # date is today or in the future -> we have the date if candidate_date >= day1: return candidate_date else: for weekday_order_number in self._weekday_order_numbers: candidate_date = nth_weekday_date( weekday_order_number, day1, WEEKDAYS.index(self.__collection_days[0]), ) # date is today or in the future -> we have the date if candidate_date >= day1: return candidate_date if day1.month == 12: next_collection_month = date(year + 1, 1, 1) else: next_collection_month = date(year, day1.month + 1, 1) if self.__monthly_force_week_numbers: return nth_week_date( self._week_order_numbers[0], next_collection_month, WEEKDAYS.index(self.__collection_days[0]), ) else: return nth_weekday_date( self._weekday_order_numbers[0], next_collection_month, WEEKDAYS.index(self.__collection_days[0]), ) elif self.__frequency == "annual": # Annual if self.__date is None: _LOGGER.error( "(%s) Please configure the date for annual collection frequency.", self.__name, ) return None conf_date = datetime.strptime(self.__date, "%m/%d").date() candidate_date = date(year, conf_date.month, conf_date.day) if candidate_date < day1: candidate_date = date(year + 1, conf_date.month, conf_date.day) return candidate_date elif self.__frequency == "aws": # Abfallwirtschaft Stuttgart if self.__url is None: _LOGGER.error( "(%s) Please configure the URL for the Stuttgart AWS server.", self.__name, ) return None if self.__event is None: _LOGGER.error( "(%s) Please configure an event name for the Stuttgart AWS server.", self.__name, ) return None conf_url = self.__url conf_event = self.__event myCal = myCalendar(conf_url) next_event, next_date = myCal.getNextDate( specific_event=conf_event) candidate_date = date(next_date.year, next_date.month, next_date.day) if candidate_date < day1: candidate_date = date(year + 1, next_date.month, next_date.day) return candidate_date elif self.__frequency == "group": if self.__entities is None: _LOGGER.error("(%s) Please add entities for the group.", self.__name) return None candidate_date = None for entity in self.__entities: d = self.hass.states.get(entity).attributes.get( ATTR_NEXT_DATE).date() if candidate_date is None or d < candidate_date: candidate_date = d return candidate_date else: _LOGGER.debug( f"({self.__name}) Unknown frequency {self.__frequency}") return None
async def _async_find_candidate_date(self, day1: date): """Find the next possible date starting from day1. Only based on calendar, not looking at include/exclude days. """ if self._frequency == "blank": return None week = day1.isocalendar()[1] weekday = day1.weekday() year = day1.year if self._frequency in [ "weekly", "even-weeks", "odd-weeks", "every-n-weeks" ]: # Everything except montthly # convert to every-n-weeks if self._frequency == "weekly": period = 1 first_week = 1 elif self._frequency == "even-weeks": period = 2 first_week = 2 elif self._frequency == "odd-weeks": period = 2 first_week = 1 else: period = self._period first_week = self._first_week offset = -1 if (week - first_week) % period == 0: # Collection this week for day_name in self._collection_days: day_index = WEEKDAYS.index(day_name) if day_index >= weekday: # Collection still did not happen offset = day_index - weekday break iterate_by_week = 7 - weekday + WEEKDAYS.index( self._collection_days[0]) while offset == -1: # look in following weeks candidate = day1 + relativedelta(days=iterate_by_week) week = candidate.isocalendar()[1] if (week - first_week) % period == 0: offset = iterate_by_week break iterate_by_week += 7 return day1 + relativedelta(days=offset) elif self._frequency == "every-n-days": try: if (day1 - self._first_date).days % self._period == 0: return day1 offset = self._period - ( (day1 - self._first_date).days % self._period) except TypeError: raise ValueError( f"({self._name}) Please configure first_date and period " "for every-n-days collection frequency.") return day1 + relativedelta(days=offset) elif self._frequency == "monthly": # Monthly if self._period is None or self._period == 1: return await self._async_monthly_candidate(day1) else: candidate_date = await self._async_monthly_candidate(day1) while (candidate_date.month - self._first_month) % self._period != 0: candidate_date = await self._async_monthly_candidate( candidate_date + relativedelta(days=1)) return candidate_date elif self._frequency == "annual": # Annual try: conf_date = datetime.strptime(self._date, "%m/%d").date() except TypeError: raise ValueError(f"({self._name}) Please configure the date " "for annual collection frequency.") candidate_date = date(year, conf_date.month, conf_date.day) if candidate_date < day1: candidate_date = date(year + 1, conf_date.month, conf_date.day) return candidate_date elif self._frequency == "group": candidate_date = None # type: ignore try: for entity_id in self._entities: entity = self.hass.data[DOMAIN][SENSOR_PLATFORM][entity_id] d = await entity.async_next_date(day1) if d is not None and (candidate_date is None or d < candidate_date): candidate_date = d except KeyError: raise ValueError except TypeError: _LOGGER.error("(%s) Please add entities for the group.", self._name) raise ValueError return candidate_date _LOGGER.error("(%s) Unknown frequency %s", self._name, self._frequency) raise ValueError
def find_candidate_date(self, day1): """Find the next possible date starting from day1, only based on calendar, not lookimg at include/exclude days""" week = int(day1.strftime('%V')) day = int(day1.strftime('%u')) - 1 month = day1.month year = day1.year if self._frequency in [ 'weekly', 'even-weeks', 'odd-weeks', 'every-n-weeks' ]: if self._frequency == 'weekly': period = 1 first_week = 1 elif self._frequency == 'even-weeks': period = 2 first_week = 2 elif self._frequency == 'odd-weeks': period = 2 first_week = 1 else: period = self._period first_week = self._first_week offset = -1 if (week - first_week) % period == 0: # Collection this week for day_name in self._collection_days: day_index = WEEKDAYS.index(day_name) if day_index >= day: # Collection still did not happen offset = day_index - day break if offset == -1: # look in following weeks in_weeks = period - (week - first_week) % period offset = 7 * in_weeks - day + WEEKDAYS.index( self._collection_days[0]) return day1 + timedelta(days=offset) elif self._frequency == 'monthly': # Monthly first_day = datetime(year, month, 1).date() first_day_day = int(first_day.strftime('%u')) - 1 target_day_day = WEEKDAYS.index(self._collection_days[0]) if target_day_day >= first_day_day: target_day = first_day + timedelta( days=target_day_day - first_day_day + (self._monthly_day_order_number - 1) * 7) else: target_day = first_day + timedelta( days=7 - first_day_day + target_day_day + (self._monthly_day_order_number - 1) * 7) if target_day < day1: if month == 12: first_day = datetime(year + 1, 1, 1).date() else: first_day = datetime(year, month + 1, 1).date() first_day_day = int(first_day.strftime('%u')) - 1 target_day_day = WEEKDAYS.index(self._collection_days[0]) if target_day_day >= first_day_day: target_day = first_day + timedelta( days=target_day_day - first_day_day + (self._monthly_day_order_number - 1) * 7) else: target_day = first_day + timedelta( days=7 - first_day_day + target_day_day + (self._monthly_day_order_number - 1) * 7) return target_day else: _LOGGER.info("(" + self._name + ") Unknown frequency " + self._frequency) return None