tfaour 8bc0749b42
Build and push server image / test (push) Successful in 19s
Build and push server image / build-and-push (push) Successful in 2m5s
Widget system Phase 0: data model + migration
First step of replacing Frame.mode (one renderer owns the whole panel)
with an Android-home-screen-style widget system -- a frame will hold N
independently placed/sized widgets (photos/calendar/whiteboard), each
with its own config/state, plus fully user-assignable NEXT/BACK button
actions. Full plan at .claude/plans/prancy-snacking-iverson.md.

This phase is additive only and changes no existing behavior -- nothing
reads these new tables yet:

- models.py: Widget (placement) + PhotoWidgetConfig/CalendarWidgetConfig/
  WhiteboardWidgetConfig (per-type 1:1 extension tables, matching this
  codebase's existing convention of dedicated tables for naturally-scoped
  state rather than one wide table) + FrameButtonAction (ordered
  (widget, action) bindings per physical button).
- grid.py: pure snap-to-grid placement math, defined relative to the
  panel's long/short axis so it stays valid across
  logical_render_size(orientation)'s genuine width/height swap for
  portrait, not just a rotation applied at the end.
- db.py: widget_locked(), the widget-scoped equivalent of frame_locked()
  -- deliberately still locks at frame granularity (not a new per-widget
  lock) to avoid a new class of multi-lock deadlock bugs.
- migration.py: _migration_16 creates the new tables; a separate
  _ensure_widgets_backfilled() (ORM-based, not raw SQL -- much less
  error-prone for this much per-mode branching) gives every existing
  frame a widget reproducing its exact current mode/settings, so
  upgrading changes nothing about what a frame displays or what its
  buttons do. calendar_photo_inlay frames specifically get two widgets
  (calendar + photo, split like the old inlay did) rather than silently
  losing the photo half.

10 new tests covering fresh-install backfill, re-run idempotency, the
photo-inlay two-widget case, whiteboard's check_now button mapping, and
migration_16's actual CREATE TABLE path against a simulated pre-existing
database (not just the fresh-install create_all() shortcut). Full suite
(69 tests) passes.
2026-07-24 08:27:47 -04:00
2026-07-22 19:07:29 -04:00

ESPresso Frame

A DIY e-ink photo frame: an ESP32-C6 pulls photos from your Immich library and displays them on a 7.3" full-color e-paper panel, waking on a timer to refresh and spending the rest of its time in deep sleep.

  • No cables to a computer, no SD card shuffling. Provisioning is a captive portal with a QR code drawn on the panel itself -- scan, join, fill in your WiFi and server address, done.
  • The frame never decodes an image. A small self-hosted server does all the work (pulling from Immich, cropping, dithering, packing into the panel's exact pixel format) and hands the device a stream it can write straight to SPI. The ESP32-C6 has no PSRAM and not much SRAM to spare -- keeping it a dumb display client is what makes that workable.
  • Crops toward faces, not just the center, using face bounding boxes Immich already computed for its own People feature -- no bundled face detector.
  • Refresh interval and album are configurable from a web UI, no reflashing needed to change them.

Hardware

See docs/hardware.md for wiring and docs/architecture.md for how the two halves talk to each other.

Getting started

  1. server/ -- run the FastAPI server first (Docker Compose, points at your Immich instance). See server/README.md.
  2. firmware/ -- build and flash the ESP32-C6, then scan the QR codes it draws on first boot to provision it. See firmware/README.md.

Repo layout

firmware/   ESP-IDF project for the ESP32-C6
server/     FastAPI server: Immich -> crop/dither/pack -> the frame
docs/       Wiring and architecture notes

License

MIT -- see LICENSE. A few small pieces of vendored third-party code (a QR code generator, a bitmap font table) keep their own permissive licenses; see LICENSE for details.


Built with substantial assistance from Claude Code.

S
Description
No description provided
Readme MIT
17 MiB
v1.4.2
Latest
2026-08-04 14:39:20 -04:00
Languages
Python 62.9%
C 19.2%
JavaScript 7.4%
HTML 5.6%
CSS 1.7%
Other 3.2%