1 """Calendar platform for Habitica integration."""
3 from __future__
import annotations
5 from datetime
import date, datetime, timedelta
6 from enum
import StrEnum
8 from dateutil.rrule
import rrule
12 CalendarEntityDescription,
19 from .
import HabiticaConfigEntry
20 from .coordinator
import HabiticaDataUpdateCoordinator
21 from .entity
import HabiticaBase
22 from .types
import HabiticaTaskType
23 from .util
import build_rrule, get_recurrence_rule
27 """Habitica calendars."""
31 TODO_REMINDERS =
"todo_reminders"
32 DAILY_REMINDERS =
"daily_reminders"
37 config_entry: HabiticaConfigEntry,
38 async_add_entities: AddEntitiesCallback,
40 """Set up the calendar platform."""
41 coordinator = config_entry.runtime_data
54 """Base Habitica calendar entity."""
58 coordinator: HabiticaDataUpdateCoordinator,
60 """Initialize calendar entity."""
65 """Habitica todos calendar entity."""
68 key=HabiticaCalendar.TODOS,
69 translation_key=HabiticaCalendar.TODOS,
73 self, start_date: datetime, end_date: datetime |
None =
None
74 ) -> list[CalendarEvent]:
75 """Get all dated todos."""
78 for task
in self.coordinator.data.tasks:
80 task[
"type"] == HabiticaTaskType.TODO
81 and not task[
"completed"]
86 start = dt_util.start_of_local_day(datetime.fromisoformat(task[
"date"]))
94 if end_date
and start > end_date:
102 summary=task[
"text"],
103 description=task[
"notes"],
111 self.coordinator.data.user[
"tasksOrder"][
"todos"].index(event.uid),
116 def event(self) -> CalendarEvent | None:
117 """Return the current or next upcoming event."""
119 return next(iter(self.
dated_todosdated_todos(dt_util.now())),
None)
122 self, hass: HomeAssistant, start_date: datetime, end_date: datetime
123 ) -> list[CalendarEvent]:
124 """Return calendar events within a datetime range."""
125 return self.
dated_todosdated_todos(start_date, end_date)
129 """Habitica dailies calendar entity."""
132 key=HabiticaCalendar.DAILIES,
133 translation_key=HabiticaCalendar.DAILIES,
138 """Habitica daystart."""
139 return dt_util.start_of_local_day(
140 datetime.fromisoformat(self.coordinator.data.user[
"lastCron"])
143 def end_date(self, recurrence: datetime, end: datetime |
None =
None) -> date:
144 """Calculate the end date for a yesterdaily.
146 The enddates of events from yesterday move forward to the end
147 of the current day (until the cron resets the dailies) to show them
148 as still active events on the calendar state entity (state: on).
150 Events in the calendar view will show all-day events on their due day
153 return recurrence.date() +
timedelta(days=1)
155 dt_util.start_of_local_day()
if recurrence == self.
todaytoday
else recurrence
159 self, recurrences: rrule, start_date: datetime, end_date: datetime |
None =
None
161 """Calculate recurrence dates based on start_date and end_date."""
163 return recurrences.between(
164 start_date, end_date -
timedelta(days=1), inc=
True
167 return [recurrences.after(self.
todaytoday, inc=
True)]
170 self, start_date: datetime, end_date: datetime |
None =
None
171 ) -> list[CalendarEvent]:
172 """Get dailies and recurrences for a given period or the next upcoming."""
175 if end_date
and end_date < self.
todaytoday:
177 start_date =
max(start_date, self.
todaytoday)
180 for task
in self.coordinator.data.tasks:
182 if not (task[
"type"] == HabiticaTaskType.DAILY
and task[
"everyX"]):
187 recurrences, start_date, end_date
189 for recurrence
in recurrence_dates:
190 is_future_event = recurrence > self.
todaytoday
191 is_current_event = recurrence <= self.
todaytoday
and not task[
"completed"]
193 if not (is_future_event
or is_current_event):
198 start=recurrence.date(),
199 end=self.
end_dateend_date(recurrence, end_date),
200 summary=task[
"text"],
201 description=task[
"notes"],
210 self.coordinator.data.user[
"tasksOrder"][
"dailys"].index(event.uid),
215 def event(self) -> CalendarEvent | None:
216 """Return the next upcoming event."""
220 self, hass: HomeAssistant, start_date: datetime, end_date: datetime
221 ) -> list[CalendarEvent]:
222 """Return calendar events within a datetime range."""
224 return self.
due_dailiesdue_dailies(start_date, end_date)
228 """Return entity specific state attributes."""
235 """Habitica to-do reminders calendar entity."""
238 key=HabiticaCalendar.TODO_REMINDERS,
239 translation_key=HabiticaCalendar.TODO_REMINDERS,
243 self, start_date: datetime, end_date: datetime |
None =
None
244 ) -> list[CalendarEvent]:
245 """Reminders for todos."""
249 for task
in self.coordinator.data.tasks:
250 if task[
"type"] != HabiticaTaskType.TODO
or task[
"completed"]:
253 for reminder
in task.get(
"reminders", []):
257 start = datetime.fromisoformat(reminder[
"time"]).replace(
258 tzinfo=dt_util.DEFAULT_TIME_ZONE, second=0, microsecond=0
266 if end_date
and start > end_date:
274 summary=task[
"text"],
275 description=task[
"notes"],
276 uid=f
"{task["id
"]}_{reminder["id
"]}",
282 key=
lambda event: event.start,
286 def event(self) -> CalendarEvent | None:
287 """Return the next upcoming event."""
288 return next(iter(self.
remindersreminders(dt_util.now())),
None)
291 self, hass: HomeAssistant, start_date: datetime, end_date: datetime
292 ) -> list[CalendarEvent]:
293 """Return calendar events within a datetime range."""
295 return self.
remindersreminders(start_date, end_date)
299 """Habitica daily reminders calendar entity."""
302 key=HabiticaCalendar.DAILY_REMINDERS,
303 translation_key=HabiticaCalendar.DAILY_REMINDERS,
306 def start(self, reminder_time: str, reminder_date: date) -> datetime:
307 """Generate reminder times for dailies.
309 Reminders for dailies have a datetime but the date part is arbitrary,
310 only the time part is evaluated. The dates for the reminders are the
313 return datetime.combine(
315 datetime.fromisoformat(reminder_time)
321 tzinfo=dt_util.DEFAULT_TIME_ZONE,
326 """Habitica daystart."""
327 return dt_util.start_of_local_day(
328 datetime.fromisoformat(self.coordinator.data.user[
"lastCron"])
332 self, recurrences: rrule, start_date: datetime, end_date: datetime |
None =
None
334 """Calculate recurrence dates based on start_date and end_date."""
336 return recurrences.between(
337 start_date, end_date -
timedelta(days=1), inc=
True
340 return [recurrences.after(self.
todaytoday, inc=
True)]
343 self, start_date: datetime, end_date: datetime |
None =
None
344 ) -> list[CalendarEvent]:
345 """Reminders for dailies."""
348 if end_date
and end_date < self.
todaytoday:
350 start_date =
max(start_date, self.
todaytoday)
352 for task
in self.coordinator.data.tasks:
353 if not (task[
"type"] == HabiticaTaskType.DAILY
and task[
"everyX"]):
357 recurrences_start = self.
todaytoday
360 recurrences, recurrences_start, end_date
362 for recurrence
in recurrence_dates:
363 is_future_event = recurrence > self.
todaytoday
364 is_current_event = recurrence <= self.
todaytoday
and not task[
"completed"]
366 if not is_future_event
and not is_current_event:
369 for reminder
in task.get(
"reminders", []):
370 start = self.
startstart(reminder[
"time"], recurrence)
377 if end_date
and start > end_date:
384 summary=task[
"text"],
385 description=task[
"notes"],
386 uid=f
"{task["id
"]}_{reminder["id
"]}",
392 key=
lambda event: event.start,
396 def event(self) -> CalendarEvent | None:
397 """Return the next upcoming event."""
398 return next(iter(self.
remindersreminders(dt_util.now())),
None)
401 self, hass: HomeAssistant, start_date: datetime, end_date: datetime
402 ) -> list[CalendarEvent]:
403 """Return calendar events within a datetime range."""
405 return self.
remindersreminders(start_date, end_date)
CalendarEvent|None event(self)
None __init__(self, HabiticaDataUpdateCoordinator coordinator)
date end_date(self, datetime recurrence, datetime|None end=None)
list[CalendarEvent] due_dailies(self, datetime start_date, datetime|None end_date=None)
CalendarEvent|None event(self)
list[datetime] get_recurrence_dates(self, rrule recurrences, datetime start_date, datetime|None end_date=None)
list[CalendarEvent] async_get_events(self, HomeAssistant hass, datetime start_date, datetime end_date)
dict[str, bool|None]|None extra_state_attributes(self)
list[datetime] get_recurrence_dates(self, rrule recurrences, datetime start_date, datetime|None end_date=None)
list[CalendarEvent] reminders(self, datetime start_date, datetime|None end_date=None)
datetime start(self, str reminder_time, date reminder_date)
CalendarEvent|None event(self)
list[CalendarEvent] async_get_events(self, HomeAssistant hass, datetime start_date, datetime end_date)
CalendarEvent|None event(self)
list[CalendarEvent] reminders(self, datetime start_date, datetime|None end_date=None)
list[CalendarEvent] async_get_events(self, HomeAssistant hass, datetime start_date, datetime end_date)
list[CalendarEvent] dated_todos(self, datetime start_date, datetime|None end_date=None)
CalendarEvent|None event(self)
list[CalendarEvent] async_get_events(self, HomeAssistant hass, datetime start_date, datetime end_date)
None async_setup_entry(HomeAssistant hass, HabiticaConfigEntry config_entry, AddEntitiesCallback async_add_entities)
str get_recurrence_rule(rrule recurrence)
rrule build_rrule(dict[str, Any] task)
bool time(HomeAssistant hass, dt_time|str|None before=None, dt_time|str|None after=None, str|Container[str]|None weekday=None)