Skip to content

About

Ical Event List Extension for https://github.com/glanceapp/glance

Resources

Stars

19 stars

Watchers

0 watching

Forks

Repository files navigation

iCal Event Widget for Glance

A lightweight API service that fetches iCal/ICS feeds and displays them as a custom widget in Glance. Perfect for showing birthdays or recurring events from Nextcloud, SOGo, or any other calendar source right in your dashboard.

Note

Project status: feature-complete, no longer actively maintained. This is a small service that turns ICS feeds into JSON for Glance's custom-api widget, and it does what it's meant to do. I no longer use Glance myself and have moved to a self-built dashboard, so I won't be adding features anymore.

It should keep working for a long time to come. Feel free to fork it if you need changes. I may still do dependency updates or answer an issue when I feel like it, but there are no guarantees.

✨ Features

  • Displays ongoing and upcoming events in separate lists
  • Supports event links (one per widget) (url)
  • Supports basic HTTP Authentication (username, password)
  • Configurable limit for collapsible event lists
  • Tested with Nextcloud & SOGo
  • Works with Docker and NixOS

⚠️ Disclaimer

  • This project is no longer actively maintained (see status above).
  • No guarantee for stability or support – use at your own risk.
  • The API is not hardened → do not expose directly to the internet. (Safe to run behind Glance itself.)
  • I may have used LLMs to maintain this code. (Use at your own risk)

📦 Installation

Prerequisites

  • Docker & Docker Compose
  • Glance stack (compose) or standalone installation
  • Access to a valid ICS feed URL

Setup

  1. Add the service definition from compose.yml to your Glance compose stack (or run it standalone).

  2. Start the stack:

    docker compose up -d --remove-orphans

Advanced: NixOS

For NixOS users, there is a dedicated setup using flakes. 👉 See NIX_USAGE.md


🚀 Usage

  1. Add the widget configuration to your glance.yml.
  2. Set the url parameter to your raw ICS feed URL, exactly as your calendar provider gave it to you (no manual URL-encoding needed — Glance encodes parameter values itself when it calls this API).
  3. Reload or restart Glance.

Tip: For stability, pin a version tag like v1.0 instead of tracking main.

⚠️ Don't pre-encode the URL unless you use encoded_url (see below). Earlier versions of this README suggested encoding the ICS URL yourself (e.g. with urlencode.sh) before adding it to glance.yml. Don't do this with the url parameter — Glance's custom-api widget already percent-encodes parameter values before sending the request, so encoding it yourself first results in it being encoded twice (e.g. Google Calendar's %40 becomes %2540), which breaks the fetch. The service also defensively detects and repairs a doubly-encoded url, but if you'd rather be explicit about it, use encoded_url instead.


⚙️ Configuration

Parameters

Parameter Description Example / Default
url Raw (not pre-encoded) ICS feed URL https://example.com/cal.ics
encoded_url Alternative to url — provide this instead if your ICS URL is already percent-encoded and you want it decoded explicitly rather than relying on auto-detection. Mutually exclusive with url. https%3A%2F%2Fexample.com%2Fcal.ics
limit Number of events returned (applied AFTER ongoing events are prioritized) 5 (omit for all)
lookback_days How many days back from now to include events that already started (ensures ongoing coverage) 14 (default)
horizon_days How many days into the future to fetch (upper bound to limit processing) 3650 (default, ~10 years)
username Username to use for basic HTTP authentication admin (default, null)
password Password to use for basic HTTP authentication 12345 (default, null)

Notes:

  • Ongoing events (already started, not yet ended) are always placed first before upcoming, regardless of limit.
  • limit is applied only after sorting (so ongoing events are never excluded by the limit).
  • Accepted ranges (safety clamped server‑side): lookback_days 0–90, horizon_days 1–3660.
  • Additional per‑event fields you can use in your Glance template: ongoing, secondsUntilStart, secondsUntilEnd, durationSeconds, daysRemaining (for all‑day), and source (icalevents or fallback).

Example Widget

- type: custom-api
  title: iCal Events
  cache: 15m
  url: http://glances-ical-api:8076/events
  parameters:
    url: https://example.com/cal.ics
    limit: 5
  template: |
    {{ $events := .JSON.Array "events" }}
    {{ $count  := len $events }}
    {{ $limit  := 3 }}  <!-- how many upcoming to show before collapse -->

    {{ if eq $count 0 }}
      <div style="padding:8px 10px; border-radius:10px; background:var(--surface-2);">
        No entries found.
      </div>
    {{ end }}

    <!-- 1) Ongoing first (never collapsible) -->
    <ul class="list list-gap-10">
      {{ range $i, $e := $events }}
        {{ $ongoing := $e.Bool "ongoing" }}
        {{ if $ongoing }}
          {{ $start := $e.String "start" | parseTime "rfc3339" }}
          {{ $end   := $e.String "end"   | parseTime "rfc3339" }}
          {{ $name  := $e.String "name" }}
          {{ $url   := $e.String "url" }}
          <li>
            <div class="flex items-center justify-between gap-10">
              <!-- Left: name (highlight) + absolute date -->
              <div>
                {{ if $url }}
                  <a class="size-h3 color-highlight block text-truncate" href="{{ $url }}" target="_blank" rel="noreferrer" title="{{ $name }}">{{ $name }}</a>
                {{ else }}
                  <span class="size-h3 color-highlight block text-truncate" title="{{ $name }}">{{ $name }}</span>
                {{ end }}
                <div style="font-size:.85em;">{{ $start | formatTime "Mon, 02 Jan 2006" }}</div>
              </div>
              <!-- Right: relative time until END -->
              <div class="size-h3 color-primary" style="white-space:nowrap;">
                ends <span {{ $end | toRelativeTime }}></span>
              </div>
            </div>
          </li>
        {{ end }}
      {{ end }}
    </ul>

    <!-- 2) Upcoming, collapsible after $limit -->
    {{ $shown := 0 }}
    <ul class="list list-gap-10 collapsible-container" data-collapse-after="{{ $limit }}">
      {{ range $i, $e := $events }}
        {{ $ongoing := $e.Bool "ongoing" }}
        {{ if not $ongoing }}
          {{ $start := $e.String "start" | parseTime "rfc3339" }}
          {{ $name  := $e.String "name" }}
          {{ $url   := $e.String "url" }}
          <li {{ if ge $shown $limit }}class="collapsible-item"{{ end }}>
            <div class="flex items-center justify-between gap-10">
              <!-- Left: name (highlight) + absolute date -->
              <div>
                {{ if $url }}
                  <a class="size-h3 color-highlight block text-truncate" href="{{ $url }}" target="_blank" rel="noreferrer" title="{{ $name }}">{{ $name }}</a>
                {{ else }}
                  <span class="size-h3 color-highlight block text-truncate" title="{{ $name }}">{{ $name }}</span>
                {{ end }}
                <div style="font-size:.85em;">{{ $start | formatTime "Mon, 02 Jan 2006" }}</div>
              </div>
              <!-- Right: relative time until START -->
              <div class="size-h3 color-primary" style="white-space:nowrap;" {{ $start | toRelativeTime }}></div>
            </div>
          </li>
          {{ $shown = add $shown 1 }}
        {{ end }}
      {{ end }}
    </ul>

📸 Screenshots

Small Widget

Small Widget

About

Ical Event List Extension for https://github.com/glanceapp/glance

Resources

Stars

19 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages