1 """Data update coordinator for caldav."""
3 from __future__
import annotations
5 from datetime
import date, datetime, time, timedelta
6 from functools
import partial
9 from typing
import TYPE_CHECKING
18 from .api
import get_attr_value
21 from .
import CalDavConfigEntry
23 _LOGGER = logging.getLogger(__name__)
30 """Class to utilize the calendar dav client object to get next event."""
35 entry: CalDavConfigEntry |
None,
36 calendar: caldav.Calendar,
38 include_all_day: bool,
41 """Set up how we are going to search the WebDav calendar."""
46 name=f
"CalDAV {calendar.name}",
47 update_interval=MIN_TIME_BETWEEN_UPDATES,
53 self.
offsetoffset: timedelta |
None =
None
56 self, hass: HomeAssistant, start_date: datetime, end_date: datetime
57 ) -> list[CalendarEvent]:
58 """Get all events in a specific time frame."""
60 vevent_list = await hass.async_add_executor_job(
70 for event
in vevent_list:
71 if not hasattr(event.instance,
"vevent"):
72 _LOGGER.warning(
"Skipped event with missing 'vevent' property")
74 vevent = event.instance.vevent
80 start=self.
to_localto_local(vevent.dtstart.value),
90 """Get the latest data."""
91 start_of_today = dt_util.start_of_local_day()
92 start_of_tomorrow = dt_util.start_of_local_day() +
timedelta(days=self.
daysdays)
96 results = await self.
hasshass.async_add_executor_job(
100 end=start_of_tomorrow,
110 for event
in results:
111 if not hasattr(event.instance,
"vevent"):
112 _LOGGER.warning(
"Skipped event with missing 'vevent' property")
114 vevent = event.instance.vevent
115 for start_dt
in vevent.getrruleset()
or []:
116 _start_of_today: date | datetime
117 _start_of_tomorrow: datetime | date
119 start_dt = start_dt.date()
120 _start_of_today = start_of_today.date()
121 _start_of_tomorrow = start_of_tomorrow.date()
123 _start_of_today = start_of_today
124 _start_of_tomorrow = start_of_tomorrow
125 if _start_of_today <= start_dt < _start_of_tomorrow:
126 new_event = event.copy()
127 new_vevent = new_event.instance.vevent
128 if hasattr(new_vevent,
"dtend"):
129 dur = new_vevent.dtend.value - new_vevent.dtstart.value
130 new_vevent.dtend.value = start_dt + dur
131 new_vevent.dtstart.value = start_dt
132 new_events.append(new_event)
133 elif _start_of_tomorrow <= start_dt:
136 event.instance.vevent
137 for event
in results + new_events
138 if hasattr(event.instance,
"vevent")
143 vevents.sort(key=
lambda x: self.
to_datetimeto_datetime(x.dtstart.value))
148 for vevent
in vevents
152 and not self.
is_overis_over(vevent)
161 "No matching event found in the %d results for %s",
172 self.
offsetoffset = offset
175 start=self.
to_localto_local(vevent.dtstart.value),
183 """Return if the event matches the filter criteria."""
187 pattern = re.compile(search)
189 hasattr(vevent,
"summary")
190 and pattern.match(vevent.summary.value)
191 or hasattr(vevent,
"location")
192 and pattern.match(vevent.location.value)
193 or hasattr(vevent,
"description")
194 and pattern.match(vevent.description.value)
199 """Return if the event last the whole day."""
200 return not isinstance(vevent.dtstart.value, datetime)
204 """Return if the event is over."""
205 return dt_util.now() >= CalDavUpdateCoordinator.to_datetime(
206 CalDavUpdateCoordinator.get_end_date(vevent)
211 """Return a datetime."""
212 if isinstance(obj, datetime):
213 return CalDavUpdateCoordinator.to_local(obj)
214 return datetime.combine(obj, time.min).replace(
215 tzinfo=dt_util.get_default_time_zone()
219 def to_local(obj: datetime | date) -> datetime | date:
220 """Return a datetime as a local datetime, leaving dates unchanged.
222 This handles giving floating times a timezone for comparison
223 with all day events and dropping the custom timezone object
224 used by the caldav client and dateutil so the datetime can be copied.
226 if isinstance(obj, datetime):
227 return dt_util.as_local(obj)
232 """Return the end datetime as determined by dtend or duration."""
233 if hasattr(obj,
"dtend"):
234 enddate = obj.dtend.value
235 elif hasattr(obj,
"duration"):
236 enddate = obj.dtstart.value + obj.duration.value
238 enddate = obj.dtstart.value +
timedelta(days=1)
243 if not isinstance(enddate, datetime)
and obj.dtstart.value == enddate:
list[CalendarEvent] async_get_events(self, HomeAssistant hass, datetime start_date, datetime end_date)
datetime|date to_local(datetime|date obj)
def is_matching(vevent, search)
CalendarEvent|None _async_update_data(self)
None __init__(self, HomeAssistant hass, CalDavConfigEntry|None entry, caldav.Calendar calendar, int days, bool include_all_day, str|None search)
str|None get_attr_value(caldav.CalendarObjectResource obj, str attribute)
tuple[str, datetime.timedelta] extract_offset(str summary, str offset_prefix)