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]),
     )
Beispiel #2
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]),
         )
Beispiel #3
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]))
Beispiel #4
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
Beispiel #5
0
 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)
Beispiel #6
0
    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
Beispiel #7
0
 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
Beispiel #8
0
 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
Beispiel #9
0
    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
Beispiel #10
0
    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
Beispiel #11
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 = 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