Esempio n. 1
0
def _mocked_event_list():
    """Provide fixture to mock a list of events."""
    return [
        CalendarEvent(
            summary="Test event 2",
            start=dtparser.parse("2022-01-04T00:00:00Z"),
            end=dtparser.parse("2022-01-04T05:00:00Z"),
            location="Test location",
            description="Test description",
        ),
        CalendarEvent(
            summary="Test event",
            start=dtparser.parse("2022-01-03T00:00:00Z"),
            end=dtparser.parse("2022-01-03T05:00:00Z"),
            location="Test location",
            description="Test description",
        ),
        CalendarEvent(
            summary="Test event 3",
            start=dtparser.parse("2022-01-05T00:00:00Z"),
            end=dtparser.parse("2022-01-05T05:00:00Z"),
            location="Test location",
            description="Test description",
        ),
    ]
Esempio n. 2
0
 def calendar_event(self) -> CalendarEvent | None:
     """Return the next upcoming calendar event."""
     if not self.event:
         return None
     if not self.event.get(END) or self.event.get(ALL_DAY):
         start = self.event[START].date()
         return CalendarEvent(
             summary=self.event[SUMMARY],
             start=start,
             end=start + timedelta(days=1),
         )
     return CalendarEvent(summary=self.event[SUMMARY],
                          start=self.event[START],
                          end=self.event[END])
Esempio n. 3
0
    async def async_get_events(self, hass, start_date,
                               end_date) -> list[CalendarEvent]:
        """Get all events in a specific time frame."""
        try:
            events = await get_lessons(
                self.client,
                date_from=start_date,
                date_to=end_date,
            )
        except UnauthorizedCertificateException as err:
            raise ConfigEntryAuthFailed(
                "The certificate is not authorized, please authorize integration again"
            ) from err
        except ClientConnectorError as err:
            if self.available:
                _LOGGER.warning(
                    "Connection error - please check your internet connection: %s",
                    err)
            events = []

        event_list = []
        for item in events:
            event = CalendarEvent(
                start=datetime.combine(item["date"], item["time"].from_),
                end=datetime.combine(item["date"], item["time"].to),
                summary=item["lesson"],
                location=item["room"],
                description=item["teacher"],
            )

            event_list.append(event)

        return event_list
 def _ical_event(self, start, end, from_date, event) -> CalendarEvent | None:
     """Ensure that events are within the start and end."""
     # Skip this event if it's in the past
     if end.date() < from_date.date():
         _LOGGER.debug("This event has already ended")
         return None
     # Ignore events that ended this midnight.
     if (
         end.date() == from_date.date()
         and end.hour == 0
         and end.minute == 0
         and end.second == 0
     ):
         _LOGGER.debug("This event has already ended")
         return None
     _LOGGER.debug(
         "Start: %s Tzinfo: %s Default: %s StartAs %s",
         str(start),
         str(start.tzinfo),
         self.timezone,
         start.astimezone(self.timezone),
     )
     cal_event = CalendarEvent(
         description=event.get("DESCRIPTION"),
         end=end.astimezone(self.timezone),
         location=event.get("LOCATION"),
         summary=event.get("SUMMARY", "Unknown"),
         start=start.astimezone(self.timezone),
     )
     _LOGGER.debug("Event to add: %s", str(CalendarEvent))
     return cal_event
    async def async_update(self, hass):
        """Do the update."""
        start_of_day_utc = dt.as_utc(dt.start_of_local_day())
        results = await self.async_o365_get_events(
            hass,
            start_of_day_utc,
            start_of_day_utc + timedelta(days=1),
        )
        if not results:
            return

        results = list(results)
        results.sort(key=lambda x: self.to_datetime(x.start))

        vevent = self._get_root_event(results)

        if vevent is None:
            _LOGGER.debug(
                "No matching event found in the %d results for %s",
                len(results),
                self._entity_id,
            )
            self.event = None
            return

        self.event = CalendarEvent(
            self.get_hass_date(vevent.start, vevent.is_all_day),
            self.get_hass_date(self.get_end_date(vevent), vevent.is_all_day),
            vevent.subject,
            clean_html(vevent.body),
            vevent.location["displayName"],
        )
 def _convert(self, collection) -> CalendarEvent:
     """Convert an collection into a Home Assistant calendar event."""
     return CalendarEvent(
         summary=collection.type,
         start=collection.date,
         end=collection.date + timedelta(days=1),
     )
Esempio n. 7
0
def calendar_data_current() -> CalendarEvent:
    """Representation of a Demo Calendar for a current event."""
    middle_of_event = dt_util.now() - datetime.timedelta(minutes=30)
    return CalendarEvent(
        start=middle_of_event,
        end=middle_of_event + datetime.timedelta(minutes=60),
        summary="Current Event",
    )
Esempio n. 8
0
def calendar_data_future() -> CalendarEvent:
    """Representation of a Demo Calendar for a future event."""
    one_hour_from_now = dt_util.now() + datetime.timedelta(minutes=30)
    return CalendarEvent(
        start=one_hour_from_now,
        end=one_hour_from_now + datetime.timedelta(minutes=60),
        summary="Future Event",
    )
Esempio n. 9
0
def _get_calendar_event(event: Event) -> CalendarEvent:
    """Return a CalendarEvent from an API event."""
    return CalendarEvent(
        summary=event.summary,
        start=event.start.value,
        end=event.end.value,
        description=event.description,
        location=event.location,
    )
Esempio n. 10
0
def _mocked_event():
    """Provide fixture to mock a single event."""
    return CalendarEvent(
        summary="Test event",
        start=dtparser.parse("2022-01-03T00:00:00"),
        end=dtparser.parse("2022-01-03T05:00:00"),
        location="Test location",
        description="Test description",
    )
Esempio n. 11
0
def _mocked_event_allday():
    """Provide fixture to mock a single all day event."""
    return CalendarEvent(
        summary="Test event",
        start=dtparser.parse("2022-01-03").date(),
        end=dtparser.parse("2022-01-04").date(),
        location="Test location",
        description="Test description",
    )
Esempio n. 12
0
 async def async_get_events(
     self, hass: HomeAssistant, start_datetime: datetime, end_datetime: datetime
 ) -> list[CalendarEvent]:
     """Get all tasks in a specific time frame."""
     events: list[CalendarEvent] = []
     if SENSOR_PLATFORM not in hass.data[DOMAIN]:
         return events
     start_date = start_datetime.date()
     end_date = end_datetime.date()
     for entity in self.entities:
         if (
             entity not in hass.data[DOMAIN][SENSOR_PLATFORM]
             or hass.data[DOMAIN][SENSOR_PLATFORM][entity].hidden
         ):
             continue
         garbage_collection = hass.data[DOMAIN][SENSOR_PLATFORM][entity]
         start = garbage_collection.get_next_date(start_date, True)
         while start is not None and start_date <= start <= end_date:
             try:
                 end = start + timedelta(days=1)
             except TypeError:
                 end = start
             name = (
                 garbage_collection.name
                 if garbage_collection.name is not None
                 else "Unknown"
             )
             if garbage_collection.expire_after is None:
                 event = CalendarEvent(
                     summary=name,
                     start=start,
                     end=end,
                 )
             else:
                 event = CalendarEvent(
                     summary=name,
                     start=datetime.combine(start, datetime.min.time()),
                     end=datetime.combine(start, garbage_collection.expire_after),
                 )
             events.append(event)
             start = garbage_collection.get_next_date(
                 start + timedelta(days=1), True
             )
     return events
Esempio n. 13
0
    async def async_get_events(self, hass: HomeAssistant, start_date: datetime,
                               end_date: datetime) -> list[CalendarEvent]:
        """Return calendar events within a datetime range."""
        events: list[CalendarEvent] = []
        for waste_type, waste_dates in self.coordinator.data.items():
            events.extend(
                CalendarEvent(
                    summary=WASTE_TYPE_TO_DESCRIPTION[waste_type],
                    start=waste_date,
                    end=waste_date,
                ) for waste_date in waste_dates
                if start_date.date() <= waste_date <= end_date.date())

        return events
Esempio n. 14
0
    async def async_get_events(self, hass, start_date, end_date):
        """Get all tasks in a specific time frame."""
        if self._id is None:
            project_task_data = [
                task
                for task in self._api.state[TASKS]
                if not self._project_id_whitelist
                or task[PROJECT_ID] in self._project_id_whitelist
            ]
        else:
            project_data = await hass.async_add_executor_job(
                self._api.projects.get_data, self._id
            )
            project_task_data = project_data[TASKS]

        events = []
        for task in project_task_data:
            if task["due"] is None:
                continue
            # @NOTE: _parse_due_date always returns the date in UTC time.
            due_date: datetime | None = _parse_due_date(
                task["due"], self._api.state["user"]["tz_info"]["hours"]
            )
            if not due_date:
                continue
            midnight = dt.as_utc(
                dt.parse_datetime(
                    due_date.strftime("%Y-%m-%d")
                    + "T00:00:00"
                    + self._api.state["user"]["tz_info"]["gmt_string"]
                )
            )

            if start_date < due_date < end_date:
                due_date_value: datetime | date = due_date
                if due_date == midnight:
                    # If the due date has no time data, return just the date so that it
                    # will render correctly as an all day event on a calendar.
                    due_date_value = due_date.date()
                event = CalendarEvent(
                    summary=task["content"],
                    start=due_date_value,
                    end=due_date_value,
                )
                events.append(event)
        return events
Esempio n. 15
0
    async def async_update(self) -> None:
        """Get the latest data."""

        try:
            events = await get_lessons(self.client)

            if not self.available:
                _LOGGER.info("Restored connection with API")
                self._attr_available = True

            if events == []:
                events = await get_lessons(
                    self.client,
                    date_to=date.today() + timedelta(days=7),
                )
                if events == []:
                    self._event = None
                    return
        except UnauthorizedCertificateException as err:
            raise ConfigEntryAuthFailed(
                "The certificate is not authorized, please authorize integration again"
            ) from err
        except ClientConnectorError as err:
            if self.available:
                _LOGGER.warning(
                    "Connection error - please check your internet connection: %s",
                    err)
                self._attr_available = False
            return

        new_event = min(
            events,
            key=lambda d: (
                datetime.combine(d["date"], d["time"].to) < datetime.now(),
                abs(
                    datetime.combine(d["date"], d["time"].to) - datetime.now()
                ),
            ),
        )
        self._event = CalendarEvent(
            start=datetime.combine(new_event["date"], new_event["time"].from_),
            end=datetime.combine(new_event["date"], new_event["time"].to),
            summary=new_event["lesson"],
            location=new_event["room"],
            description=new_event["teacher"],
        )
Esempio n. 16
0
 async def async_update(self) -> None:
     """Get the latest data."""
     next_dates = {}
     for entity in self.entities:
         if self._hass.data[DOMAIN][SENSOR_PLATFORM][entity].next_date is not None:
             next_dates[entity] = self._hass.data[DOMAIN][SENSOR_PLATFORM][
                 entity
             ].next_date
     if len(next_dates) > 0:
         entity_id = min(next_dates.keys(), key=(lambda k: next_dates[k]))
         start = next_dates[entity_id]
         end = start + timedelta(days=1)
         name = self._hass.data[DOMAIN][SENSOR_PLATFORM][entity_id].name
         self.event = CalendarEvent(
             summary=name,
             start=start,
             end=end,
         )
Esempio n. 17
0
    def get_event_list(self, start, end,
                       include_all_day: bool) -> list[CalendarEvent]:
        """Get a list of events.

        Gets the events from start to end, including or excluding all day
        events.
        :param start the earliest start time of events to return
        :type datetime
        :param end the latest start time of events to return
        :type datetime
        :param include_all_day if true, all day events will be included.
        :type boolean
        :returns a list of events, or an empty list
        :rtype list[CalendarEvent]
        """
        event_list = []

        if self._calendar is not None:
            # ics 0.8 takes datetime not Arrow objects
            # ar_start = start
            # ar_end = end
            ar_start = arrowget(start)
            ar_end = arrowget(end)

            for event in self._calendar.timeline.included(ar_start, ar_end):
                if event.all_day and not include_all_day:
                    continue
                summary = ""
                # ics 0.8 uses 'summary' reliably, older versions use 'name'
                # if hasattr(event, "summary"):
                #    summary = event.summary
                # elif hasattr(event, "name"):
                summary = event.name
                calendar_event = CalendarEvent(
                    summary=summary,
                    start=ParserICS.get_date(event.begin, event.all_day),
                    end=ParserICS.get_date(event.end, event.all_day),
                    location=event.location,
                    description=event.description,
                )
                event_list.append(calendar_event)

        return event_list
Esempio n. 18
0
    def _handle_coordinator_update(self) -> None:
        """Handle updated data from the coordinator."""
        next_waste_pickup_type = None
        next_waste_pickup_date = None
        for waste_type, waste_dates in self.coordinator.data.items():
            if (waste_dates and (next_waste_pickup_date is None
                                 or waste_dates[0]  # type: ignore[unreachable]
                                 < next_waste_pickup_date)
                    and waste_dates[0] >= dt_util.now().date()):
                next_waste_pickup_date = waste_dates[0]
                next_waste_pickup_type = waste_type

        self._event = None
        if next_waste_pickup_date is not None and next_waste_pickup_type is not None:
            self._event = CalendarEvent(
                summary=WASTE_TYPE_TO_DESCRIPTION[next_waste_pickup_type],
                start=next_waste_pickup_date,
                end=next_waste_pickup_date,
            )

        super()._handle_coordinator_update()
Esempio n. 19
0
    def get_current_event(self, include_all_day: bool, now: datetime,
                          days: int) -> Optional[CalendarEvent]:
        """Get the current or next event.

        Gets the current event, or the next upcoming event with in the
        specified number of days, if there is no current event.
        :param include_all_day if true, all day events will be included.
        :type boolean
        :param now the current date and time
        :type datetime
        :param days the number of days to check for an upcoming event
        :type int
        :returns a CalendarEvent or None
        """
        if self._calendar is None:
            return None

        temp_event = None
        end = now + timedelta(days=days)
        for event in self._calendar.timeline.included(arrowget(now),
                                                      arrowget(end)):
            if event.all_day and not include_all_day:
                continue
            if ParserICS.is_event_newer(temp_event, event):
                temp_event = event

        if temp_event is None:
            return None
        # if hasattr(event, "summary"):
        # summary = temp_event.summary
        # elif hasattr(event, "name"):
        summary = temp_event.name
        return CalendarEvent(
            summary=summary,
            start=ParserICS.get_date(temp_event.begin, temp_event.all_day),
            end=ParserICS.get_date(temp_event.end, temp_event.all_day),
            location=temp_event.location,
            description=temp_event.description,
        )
Esempio n. 20
0
    async def async_get_events(self, hass: HomeAssistant, start_date: datetime,
                               end_date: datetime) -> list[CalendarEvent]:
        """Get all events in a specific time frame."""
        # Get event list from the current calendar
        vevent_list = await hass.async_add_executor_job(
            self.calendar.date_search, start_date, end_date)
        event_list = []
        for event in vevent_list:
            if not hasattr(event.instance, "vevent"):
                _LOGGER.warning("Skipped event with missing 'vevent' property")
                continue
            vevent = event.instance.vevent
            if not self.is_matching(vevent, self.search):
                continue
            event_list.append(
                CalendarEvent(
                    summary=vevent.summary.value,
                    start=vevent.dtstart.value,
                    end=self.get_end_date(vevent),
                    location=self.get_attr_value(vevent, "location"),
                    description=self.get_attr_value(vevent, "description"),
                ))

        return event_list
    async def async_get_events(self, hass, start_date, end_date):
        """Get the via async."""
        results = await self.async_o365_get_events(hass, start_date, end_date)
        if not results:
            return
        vevent_list = list(results)
        vevent_list.sort(key=attrgetter("start"))
        event_list = []
        for vevent in vevent_list:
            # data = format_event_data(event, self.calendar.calendar_id)
            # data["start"] = self.get_hass_date(data["start"], event.is_all_day)
            # data["end"] = self.get_hass_date(data["end"], event.is_all_day)
            # event_list.append(data)
            event = CalendarEvent(
                self.get_hass_date(vevent.start, vevent.is_all_day),
                self.get_hass_date(self.get_end_date(vevent),
                                   vevent.is_all_day),
                vevent.subject,
                clean_html(vevent.body),
                vevent.location["displayName"],
            )
            event_list.append(event)

        return event_list
Esempio n. 22
0
    def update(self):
        """Get the latest data."""
        start_of_today = dt.start_of_local_day()
        start_of_tomorrow = dt.start_of_local_day() + timedelta(days=self.days)

        # We have to retrieve the results for the whole day as the server
        # won't return events that have already started
        results = self.calendar.date_search(start_of_today, start_of_tomorrow)

        # Create new events for each recurrence of an event that happens today.
        # For recurring events, some servers return the original event with recurrence rules
        # and they would not be properly parsed using their original start/end dates.
        new_events = []
        for event in results:
            if not hasattr(event.instance, "vevent"):
                _LOGGER.warning("Skipped event with missing 'vevent' property")
                continue
            vevent = event.instance.vevent
            for start_dt in vevent.getrruleset() or []:
                _start_of_today = start_of_today
                _start_of_tomorrow = start_of_tomorrow
                if self.is_all_day(vevent):
                    start_dt = start_dt.date()
                    _start_of_today = _start_of_today.date()
                    _start_of_tomorrow = _start_of_tomorrow.date()
                if _start_of_today <= start_dt < _start_of_tomorrow:
                    new_event = event.copy()
                    new_vevent = new_event.instance.vevent
                    if hasattr(new_vevent, "dtend"):
                        dur = new_vevent.dtend.value - new_vevent.dtstart.value
                        new_vevent.dtend.value = start_dt + dur
                    new_vevent.dtstart.value = start_dt
                    new_events.append(new_event)
                elif _start_of_tomorrow <= start_dt:
                    break
        vevents = [
            event.instance.vevent for event in results + new_events
            if hasattr(event.instance, "vevent")
        ]

        # dtstart can be a date or datetime depending if the event lasts a
        # whole day. Convert everything to datetime to be able to sort it
        vevents.sort(key=lambda x: self.to_datetime(x.dtstart.value))

        vevent = next(
            (vevent for vevent in vevents
             if (self.is_matching(vevent, self.search) and (
                 not self.is_all_day(vevent) or self.include_all_day)
                 and not self.is_over(vevent))),
            None,
        )

        # If no matching event could be found
        if vevent is None:
            _LOGGER.debug(
                "No matching event found in the %d results for %s",
                len(vevents),
                self.calendar.name,
            )
            self.event = None
            return

        # Populate the entity attributes with the event values
        (summary, offset) = extract_offset(vevent.summary.value, OFFSET)
        self.event = CalendarEvent(
            summary=summary,
            start=vevent.dtstart.value,
            end=self.get_end_date(vevent),
            location=self.get_attr_value(vevent, "location"),
            description=self.get_attr_value(vevent, "description"),
        )
        self.offset = offset