Immich Integration

Immich Integration for the Photo Widget

1. Context & Objective

Gladys will soon have a Photo Widget for the dashboard (box.type = 'photo') that displays
a slideshow from a manually entered list of URLs ({ url, caption }). The images are
retrieved via a server proxy (GET /api/v1/dashboard/photo/proxy?url=) to remain
accessible remotely (Gladys Plus).

Objective: Allow the user to automatically populate this slideshow from an Immich server
(self-hosted photo manager).

The user connects their Immich once, then selects a dynamic source (an album,
or their memories “on this day”); the widget displays the corresponding photos and
refreshes itself.

Scope v1

  • Sources: Album of choice + Memories “on this day”
  • Image quality: preview (~1440px)
  • Architecture: full Immich service (config page + authenticated proxy)

2. The Immich API — What It Enables

2.1 Authentication

  • All requests include the header x-api-key: <key>.
  • The key is generated in Immich: Account Settings → API Keys. Minimum required
    permissions: album.read, asset.read, memory.read.
  • Base URL = Immich server address, e.g., http://192.168.1.20:2283. All paths
    below are prefixed by /api.

Key point: The current Gladys proxy (dashboard.getPhoto.js) makes a GET without
headers → it cannot talk to Immich. Therefore, a dedicated Immich proxy is needed that
injects x-api-key and points to the configured base URL.

2.2 Choosing Which Photos to Display (Sources)

Source Endpoint Useful Response
List of albums GET /api/albums [{ id, albumName, assetCount, albumThumbnailAssetId, shared }] — used to populate the album selector
Contents of an album GET /api/albums/{id} { albumName, assets: [{ id, type, originalFileName, fileCreatedAt, exifInfo }] }
Memories “on this day” GET /api/memories [{ id, type:"on_this_day", memoryAt, data:{ year }, assets:[{ id, ... }] }] — a group per year on the same date

2.3 Retrieving the Image File of an Asset

Each photo is identified by a UUID asset.id. Three renditions:

Variant Endpoint Usage
Preview (~1440px) :white_check_mark: v1 GET /api/assets/{id}/thumbnail?size=preview Good quality/size trade-off for a slideshow
Thumbnail GET /api/assets/{id}/thumbnail?size=thumbnail Small thumbnail (timeline)
Original GET /api/assets/{id}/original Max quality, potentially large files (>5 MB)

Response = binary image/*. The Immich proxy converts it to the format already expected by
the widget: "<contentType>;base64,<data>".

2.4 Asset Fields Usable for Captions

originalFileName, fileCreatedAt / localDateTime, and exifInfo (description, city,
dateTimeOriginal). Allows auto-generating a caption (e.g., “Rome — August 12, 2019”).

3. Expected Functional Behavior

3.1 Connecting the Integration (Once)

  • New Immich card in the list of integrations.
  • Config page asking for Server URL + API Key.
  • “Test Connection” button → calls GET /api/albums to validate URL + key, and
    returns a clear error if failed (unreachable URL, 401 invalid key).

3.2 Widget Configuration (By the User, When Editing the Dashboard)

The Photo Widget gains a choice of source mode:

  1. Manual URLs — current behavior, unchanged (full backward compatibility).
  2. Immich — Album — a dropdown lists the albums (via GET /api/albums);
    the user selects one.
  3. Immich — Memories — displays photos returned by GET /api/memories
    (“X years ago, on this day”).

Existing options are preserved and applicable to all modes: framing (cover/contain),
slideshow interval, showing/hiding captions, widget title.

For Immich modes, the caption can be auto-generated from the asset metadata (date + location) instead of manually entered.

3.3 Display (Runtime)

  • On opening, the widget resolves the Immich source to a list of assets (album or memories),
    then displays each image in preview via the authenticated Immich proxy.
  • Slideshow: automatic scrolling according to the interval, forward/backward navigation and
    indicators (already present in PhotoBox.jsx).
  • Image cache in memory + preloading of the next image (already present), reused as is.
  • Refreshing the list: The list of assets (especially “memories,” which changes
    every day) is periodically re-queried / when the widget is mounted, to be decided.

3.4 Edge Cases & Functional Decisions to Validate

  • Empty album / empty memories for the day → explicit empty state (message), no error.
  • Videos in an album (asset.type = VIDEO) → filtered (only IMAGE kept) or
    display their poster? → To be decided (proposal: ignore videos in v1).
  • Large albums → limit the number of loaded assets (e.g., ceiling + possible random order)
    to avoid thousands of entries? → To be decided.
  • Display order: chronological (by fileCreatedAt), reverse, or random?
    To be decided (proposal: most recent first).
  • 5 MB proxy limit: preview remains well below the limit, so kept. Only to be reviewed if
    we add the “original” or video mode later.

4. API Sources (Reference)

  • Immich API Doc: API | Immich · endpoints: Immich - API Documentation
  • Albums: getAllAlbums, getAlbumInfo · Memories: searchMemories (GET /api/memories)
  • Image: viewAsset / GET /api/assets/{id}/thumbnail?size=preview
  • Random (ref.): POST /api/search/random · Metadata: POST /api/search/metadata

5. Open Questions Before Implementation

  1. Videos in albums: ignore or display a poster? (proposal: ignore in v1)
  2. Display order: reverse chronological, chronological, or random?
  3. Limit on the number of photos per source (performance)?
  4. Caption: auto-generated (date + location) by default, or no caption for Immich?

Model Ideas