Calendar display
Calendar display
The optional calendar plugin reads one or more iCalendar (.ics) feeds and
adds two screen types:
- an event card during the configured lead window (60 minutes by default);
- a short upcoming-agenda glance every configured interval (one minute every half hour by default).
Set calendar_default_enabled=true to keep the upcoming agenda as the preferred
base screen whenever useful events exist outside the commuting window. The
plugin uses the raw display_schedule mode to define that window. By default,
calendar_default_modes=auto,weather,token,token-always, so scheduled transit
periods keep the bus display while other periods prefer the calendar. Change
the comma-separated mode list to fit another installation’s schedule, or leave
the feature disabled to retain glance-only behavior. The preferred agenda
refreshes its clock every calendar_default_refresh_seconds (90 seconds by
default) without relinquishing its low-priority claim.
The final ten minutes before a fresh event are exclusive by default. Earlier
event cards use priority 40, so the default flight (50) and ISS (60)
claims can temporarily override them. Agenda glances use priority 20 and
return automatically to the scheduled transit, weather, or token screen. A
preferred default agenda uses the same low priority, so higher-priority plugin
claims remain able to interrupt it. The normal lead-window event card and its
exclusive final-ten-minute behavior are unchanged.
Google Calendar setup (recommended)
Use the Google Calendar Events API on a Raspberry Pi. It applies timeMin and
timeMax at the server, asks Google to expand recurring events and exceptions
with singleEvents=true, and caps each calendar at
calendar_google_max_events. The response and last-good cache therefore stay
proportional to the next few days, not to the age of the calendar.
- Enable the Google Calendar API in a Google Cloud project and create a service account key.
- Store the downloaded JSON key outside this repository with owner-only file permissions.
- Share each calendar read-only with the service account’s
client_email. - Copy each calendar ID from Settings > Integrate calendar into the
untracked
.env. Calendar IDs and key paths are never logged.
calendar_enabled=true
calendar_source=google_api
calendar_google_credentials_file=/path/to/calendar-reader.json
calendar_google_calendar_ids=REDACTED_CALENDAR_ID
calendar_google_max_events=100
calendar_timezone=Europe/Brussels
calendar_lookahead_days=3
Use comma-separated calendar_google_calendar_ids for multiple calendars. A
Workspace administrator may instead grant domain-wide delegation; set
calendar_google_delegated_user only when that has been intentionally
configured. The implementation requests only the
calendar.events.readonly OAuth scope.
Including Google Tasks
Google Tasks are not Calendar Events API resources, even though Calendar shows
them in the same interface. To include incomplete tasks with due dates, enable
the Google Tasks API and create an OAuth authorized-user JSON file with only the
https://www.googleapis.com/auth/tasks.readonly scope. Store that file outside
the repository with owner-only permissions, then add:
calendar_google_tasks_enabled=true
calendar_google_tasks_credentials_file=/path/to/tasks-reader.json
calendar_google_tasks_max=100
calendar_google_tasks_max_lists=20
The display queries every visible task list with server-side dueMin and
dueMax bounds matching the calendar lookahead window. Completed, deleted,
hidden, undated, and out-of-window tasks are omitted. A hard task cap and a
mode-0600 last-good cache bound memory and stale fallback behavior.
Migrating from a Google secret ICS URL
Replace calendar_source=ics and calendar_ics_urls=... with the API settings
above. Do not enable the calendar again on a memory-limited Pi until the service
account can retrieve the bounded API window. The secret ICS address can then be
removed from .env and revoked if it may have been exposed.
HTTP ICS remains available for small non-Google calendars, and file remains
available for externally synchronized files. Both are legacy compatibility
paths: an ICS feed has no standard timeMin/timeMax request, and recurrence
masters, exceptions, and VTIMEZONE definitions may be separated anywhere in
the file. Pre-filtering VEVENT blocks before a standards-compliant parse can
therefore silently lose or mis-time occurrences. The application must parse an
entire ICS source to preserve those semantics, so do not point the ICS mode at
a large historical Google feed on a constrained device.
For development or an externally synchronized feed, use calendar_source=file
with calendar_ics_file or comma-separated calendar_ics_files paths.
Refresh and failure behavior
Sources refresh every five minutes by default. Google API calls are bounded by
the requested time window and event cap; HTTP ICS feeds use ETag/Last-Modified
conditional requests when available. Successful bounded API results or ICS
responses are cached under cache/ with owner-only permissions. If a source is
temporarily unavailable, the plugin may show its last-good events as stale for
calendar_max_stale_seconds, but stale events never receive the exclusive
pre-event lease.
Cancelled events are ignored. All-day events are excluded by default; when
enabled, they appear only in the agenda and never take a pre-event lease. Set
calendar_show_details=false to render every item as Busy without its
location.
See .env.example for all timing, priority, privacy, and agenda settings.