1 """Calendar platform for a Local Calendar."""
3 from __future__
import annotations
6 from datetime
import date, datetime, timedelta
10 from ical.calendar
import Calendar
11 from ical.calendar_stream
import IcsCalendarStream
12 from ical.event
import Event
13 from ical.exceptions
import CalendarParseError
14 from ical.store
import EventStore, EventStoreError
15 from ical.types
import Range, Recur
16 import voluptuous
as vol
23 CalendarEntityFeature,
32 from .const
import CONF_CALENDAR_NAME, DOMAIN
33 from .store
import LocalCalendarStore
35 _LOGGER = logging.getLogger(__name__)
37 PRODID =
"-//homeassistant.io//local_calendar 1.0//EN"
42 config_entry: ConfigEntry,
43 async_add_entities: AddEntitiesCallback,
45 """Set up the local calendar platform."""
46 store = hass.data[DOMAIN][config_entry.entry_id]
47 ics = await store.async_load()
48 calendar: Calendar = await hass.async_add_executor_job(
49 IcsCalendarStream.calendar_from_ics, ics
51 calendar.prodid = PRODID
53 name = config_entry.data[CONF_CALENDAR_NAME]
59 """A calendar entity backed by a local iCalendar file."""
61 _attr_has_entity_name =
True
62 _attr_supported_features = (
63 CalendarEntityFeature.CREATE_EVENT
64 | CalendarEntityFeature.DELETE_EVENT
65 | CalendarEntityFeature.UPDATE_EVENT
70 store: LocalCalendarStore,
75 """Initialize LocalCalendarEntity."""
79 self.
_event_event: CalendarEvent |
None =
None
84 def event(self) -> CalendarEvent | None:
85 """Return the next upcoming event."""
89 self, hass: HomeAssistant, start_date: datetime, end_date: datetime
90 ) -> list[CalendarEvent]:
91 """Get all events in a specific time frame."""
92 events = self.
_calendar_calendar.timeline_tz(start_date.tzinfo).overlapping(
99 """Update entity state with the next upcoming event."""
101 events = self.
_calendar_calendar.timeline_tz(now.tzinfo).active_after(now)
102 if event := next(events,
None):
108 """Persist the calendar to disk."""
109 content = IcsCalendarStream.calendar_to_ics(self.
_calendar_calendar)
110 await self.
_store_store.async_store(content)
113 """Add a new event to calendar."""
116 event_store = EventStore(self.
_calendar_calendar)
117 await self.
hasshass.async_add_executor_job(event_store.add, event)
124 recurrence_id: str |
None =
None,
125 recurrence_range: str |
None =
None,
127 """Delete an event on the calendar."""
128 range_value: Range = Range.NONE
129 if recurrence_range == Range.THIS_AND_FUTURE:
130 range_value = Range.THIS_AND_FUTURE
135 recurrence_id=recurrence_id,
136 recurrence_range=range_value,
138 except EventStoreError
as err:
146 event: dict[str, Any],
147 recurrence_id: str |
None =
None,
148 recurrence_range: str |
None =
None,
150 """Update an existing event on the calendar."""
152 range_value: Range = Range.NONE
153 if recurrence_range == Range.THIS_AND_FUTURE:
154 range_value = Range.THIS_AND_FUTURE
157 event_store = EventStore(self.
_calendar_calendar)
159 def apply_edit() -> None:
163 recurrence_id=recurrence_id,
164 recurrence_range=range_value,
168 await self.
hasshass.async_add_executor_job(apply_edit)
169 except EventStoreError
as err:
176 """Parse an ical event from a home assistant event dictionary."""
177 if rrule := event.get(EVENT_RRULE):
178 event[EVENT_RRULE] = Recur.from_rrule(rrule)
186 for key
in (EVENT_START, EVENT_END):
188 (value := event[key])
189 and isinstance(value, datetime)
190 and value.tzinfo
is not None
192 event[key] = dt_util.as_local(value).replace(tzinfo=
None)
195 return Event(**event)
196 except CalendarParseError
as err:
197 _LOGGER.debug(
"Error parsing event input fields: %s (%s)", event,
str(err))
198 raise vol.Invalid(
"Error parsing event input fields")
from err
202 """Return a CalendarEvent from an API event."""
203 start: datetime | date
205 if isinstance(event.start, datetime)
and isinstance(event.end, datetime):
206 start = dt_util.as_local(event.start)
207 end = dt_util.as_local(event.end)
208 if (end - start) <=
timedelta(seconds=0):
217 summary=event.summary,
220 description=event.description,
222 rrule=event.rrule.as_rrule_str()
if event.rrule
else None,
223 recurrence_id=event.recurrence_id,
224 location=event.location,
None __init__(self, LocalCalendarStore store, Calendar calendar, str name, str unique_id)
list[CalendarEvent] async_get_events(self, HomeAssistant hass, datetime start_date, datetime end_date)
None async_update_event(self, str uid, dict[str, Any] event, str|None recurrence_id=None, str|None recurrence_range=None)
CalendarEvent|None event(self)
None async_delete_event(self, str uid, str|None recurrence_id=None, str|None recurrence_range=None)
None async_create_event(self, **Any kwargs)
None async_update_ha_state(self, bool force_refresh=False)
web.Response delete(self, web.Request request, str config_key)
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
CalendarEvent _get_calendar_event(Event event)
Event _parse_event(dict[str, Any] event)