iCal / calendar feed export of the resolved event schedule #16

Closed
opened 2026-07-07 16:52:29 +00:00 by claude-bot · 1 comment
Contributor

Goal

Publish the resolved event calendar (with the day's primary event / scheme) as a subscribable iCal feed.

Why it's valuable

Iris already computes a rich, priority-resolved, multi-source calendar of holidays and events. Exposing it as an .ics feed lets users see "what will the lights do" inside Google Calendar / Apple Calendar / their HA dashboard — reusing data Iris already has.

Sketch

  • GET /export/calendar.ics (token-scoped or behind auth) producing a VEVENT per resolved event day.
  • Include event name, phase, and primary scheme name in the description.
  • Respect the selected year / a rolling window.

Acceptance criteria

  • Valid iCal feed served for the resolved calendar
  • Subscribable in a standard calendar client
  • Access-controlled

Proposed enhancement (not in the original spec).

## Goal Publish the resolved event calendar (with the day's primary event / scheme) as a subscribable iCal feed. ## Why it's valuable Iris already computes a rich, priority-resolved, multi-source calendar of holidays and events. Exposing it as an `.ics` feed lets users see "what will the lights do" inside Google Calendar / Apple Calendar / their HA dashboard — reusing data Iris already has. ## Sketch - `GET /export/calendar.ics` (token-scoped or behind auth) producing a VEVENT per resolved event day. - Include event name, phase, and primary scheme name in the description. - Respect the selected year / a rolling window. ## Acceptance criteria - [ ] Valid iCal feed served for the resolved calendar - [ ] Subscribable in a standard calendar client - [ ] Access-controlled Proposed enhancement (not in the original spec).
claude-bot added this to the v1.1.0 milestone 2026-07-15 15:32:59 +00:00
Author
Contributor

Done — #120 merged, all seven CI jobs green.

  • Valid iCal feed for the resolved calendar — days resolved individually (priority, pins and shadowing can change the winner day to day) then coalesced, so consecutive days sharing an event and phase become one VEVENT. Unapproved days are flagged rather than hidden; a day that will run nothing is the thing worth spotting.
  • Subscribable in a standard calendar client — all-day VEVENTs with VALUE=DATE, X-PUBLISHED-TTL, exclusive DTEND.
  • Access-controlled — feed token, revealed on request from Settings, regenerable.

The two criteria conflict

A calendar client polls a URL and cannot complete an OIDC flow, so "subscribable" and "access-controlled" cannot both be met by the session cookie. Resolved the way Nextcloud and Radicale do: an install-wide token in the URL, kept out of GET /settings because it is a bearer credential rather than a preference, and rotatable to revoke every subscriber at once. The endpoint accepts a session as well, so the UI can link to it.

Two things in the auth path worth recording:

  • token_matches deliberately does not create a token, so an unauthenticated request cannot bring a credential into existence as a side effect.
  • _has_session mirrors get_current_user's expiry check rather than merely looking the row up — without it a stale cookie would read the feed indefinitely, a lower bar than every other endpoint applies.

Validity is checked with a real parser

icalendar was added as a dev-only dependency and the generated document is parsed back, rather than asserting against our own strings — which would prove only self-consistency, and "valid iCal feed" is the first criterion. That is what covers the parts of RFC 5545 that are quietly easy to get wrong:

  • Exclusive DTEND — treat it as inclusive and every event renders a day short.
  • 75-octet folding — folding counts bytes, so a naive split can cut a UTF-8 sequence in half and produce a document that will not decode at all. Tested with 🎄 × 60 and a long accented name.
  • Escaping — an unescaped ; or , is read as a property separator and corrupts the document.

Note for #51

The complement (iCal import) will need icalendar at runtime rather than dev-only. Flagging here so the dependency move is not a surprise in that PR.

Done — #120 merged, all seven CI jobs green. - [x] **Valid iCal feed for the resolved calendar** — days resolved individually (priority, pins and shadowing can change the winner day to day) then coalesced, so consecutive days sharing an event and phase become one VEVENT. Unapproved days are flagged rather than hidden; a day that will run nothing is the thing worth spotting. - [x] **Subscribable in a standard calendar client** — all-day VEVENTs with `VALUE=DATE`, `X-PUBLISHED-TTL`, exclusive `DTEND`. - [x] **Access-controlled** — feed token, revealed on request from Settings, regenerable. ### The two criteria conflict A calendar client polls a URL and cannot complete an OIDC flow, so "subscribable" and "access-controlled" cannot both be met by the session cookie. Resolved the way Nextcloud and Radicale do: an install-wide token in the URL, kept out of `GET /settings` because it is a bearer credential rather than a preference, and rotatable to revoke every subscriber at once. The endpoint accepts a session as well, so the UI can link to it. Two things in the auth path worth recording: - `token_matches` deliberately does **not** create a token, so an unauthenticated request cannot bring a credential into existence as a side effect. - `_has_session` mirrors `get_current_user`'s **expiry** check rather than merely looking the row up — without it a stale cookie would read the feed indefinitely, a lower bar than every other endpoint applies. ### Validity is checked with a real parser `icalendar` was added as a **dev-only** dependency and the generated document is parsed back, rather than asserting against our own strings — which would prove only self-consistency, and "valid iCal feed" is the first criterion. That is what covers the parts of RFC 5545 that are quietly easy to get wrong: - **Exclusive `DTEND`** — treat it as inclusive and every event renders a day short. - **75-octet folding** — folding counts *bytes*, so a naive split can cut a UTF-8 sequence in half and produce a document that will not decode at all. Tested with `🎄 × 60` and a long accented name. - **Escaping** — an unescaped `;` or `,` is read as a property separator and corrupts the document. ### Note for #51 The complement (iCal *import*) will need `icalendar` at runtime rather than dev-only. Flagging here so the dependency move is not a surprise in that PR.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Iris-WLED#16
No description provided.