Skip to content

Add extension calendar format kind #346

Description

@marcoaperez

Why

A very common self-hosted use case for a TRMNL/BYOS screen is showing an upcoming-events
calendar (iCloud, Google, Nextcloud, Outlook — all publish .ics feeds). Today Terminus
can poll JSON/CSV/XML/plain/image sources, but not an ICS feed, so users have to run a
separate side-car service that converts text/calendar → JSON just to feed a Poll extension.
Native ICS support would remove that side-car entirely and make "calendar on a TRMNL" a
first-class, no-extra-container setup.

Current behavior

The fetcher client only recognizes a fixed set of MIME types and rejects everything else:

app/aspects/extensions/fetchers/client.rb

def parse type, body
  case type
    when %r(application/([[:alnum:]][\w!#&\-^$]*\+)?json) then source.from_json body
    when %r(image/.+) then source.from_image body
    when "text/csv" then source.from_csv body
    when "text/plain" then source.from_text body
    when "text/xml", "application/xml", "application/rss+xml", "application/atom+xml"
      source.from_xml body
    else Failure "Unknown MIME Type: #{type}."   # <-- text/calendar lands here
  end
end

And the Source module has no calendar decoder:

app/aspects/extensions/source.rb

module Source
  def from_csv body   ... end
  def from_image(body) ... end
  def from_json body  ... end
  def from_text body  ... end
  def from_xml body   ... end
  # no from_ical
end

So a Poll extension pointed at an .ics URL (served as text/calendar) fails with
Unknown MIME Type: text/calendar.

Proposed change

  1. Add a from_ical decoder to Source that parses the ICS body and returns an array of
    event hashes
    (mirroring how from_csv returns an array of row hashes), so existing
    Liquid templates can iterate events the same way they iterate CSV rows. Suggested per-event
    shape: { summary:, description:, location:, starts_at:, ends_at:, all_day:, url: }, with
    times normalized to ISO-8601. Recurrence expansion (RRULE) into a bounded future window is
    desirable but could be a follow-up.
  2. Register text/calendar in Client#parse.

A well-maintained gem such as icalendar handles the
parsing (VEVENT, DTSTART/DTEND, all-day vs timed, TZID). Failure cases return Failure like the
other decoders.

Prior art / duplication check

I searched the full issue history (open + closed) — there is no existing issue or PR about
calendar / ICS / iCal support. Discussions are disabled on the repo, so an issue is the right
place to gauge interest.

Offer

Happy to implement this as a PR (decoder + MIME registration + specs) if the direction is
welcome. Would you prefer the event array shape above, or a different normalized schema?

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions