Firmware build check / build-check (push) Successful in 5m37s
Build and release firmware / build-and-release (push) Successful in 5m36s
Build and push server image / test (push) Successful in 1m37s
Build and push server image / build-and-push (push) Successful in 4m18s
Build and push server image / deploy (push) Failing after 1m20s
Server: migration 41 drops the pre-widget-system Frame columns (mode/album_id/current_asset_id/queue/calendar_*/whiteboard_*, etc) docs/widgets.md flagged as the deliberately-deferred Phase 6 cleanup, with a raw-SQL backfill safety net for any frame that still somehow lacks a Widget. Also drops legacy_token_enabled and the shared MANAGEMENT_TOKEN fallback it gated in require_device/require_browser -- the per-frame manage_token/device_token flow (and the /m/ page) fully supersede it now; MANAGEMENT_TOKEN's only remaining role is the optional pre-setup claim gate. Confirmed with the maintainer that the deployed frame is already off the shared token before removing the server-side fallback. Firmware: the captive portal's "Access Token" field and its NVS/ build_url plumbing only ever mattered for pointing new firmware at an old pre-multi-frame server -- gone along with the server-side fallback it fed. Version bump to publish the change.
5.5 KiB
5.5 KiB
espresso_frame
A DIY e-ink photo frame: an ESP32-C6 (firmware/, ESP-IDF) driving a
Waveshare 7.3" E Ink Spectra 6 panel (800x480, 6-color, SPI), paired with a
self-hosted FastAPI server (server/) that pulls from Immich, does all
image processing (crop/dither/quantize/pack), and serves a placeable
photos/calendar/whiteboard/weather widget system to the device.
CURRENT TODO -sharing layouts with linked users -a "coming up this week" widget -on reset dismiss the menu.
Start here, don't re-derive from scratch:
docs/architecture.md-- how firmware and server talk (sequence diagram, boot flow).docs/widgets.md-- the server-side widget system (data model, grid placement, compositor, button-action dispatch).docs/hardware.md-- wiring.server/README.md,firmware/README.md-- per-component setup, config, and a lot of accumulated gotchas (Immich API shape, TLS trust-anchor details, button GPIO wakeup quirks, etc.) -- check these before assuming something is a new bug.
Conventions specific to this repo
- No
Co-Authored-By: Claudetrailers in commits. Attribution lives in the rootREADME.mdinstead (see its last line) -- the maintainer's explicit preference, not the default. - Copyleft dependencies need an explicit flag, not a silent decision.
Before adding anything LGPL/GPL/AGPL (or unclear), verify the actual
license via
pip show/package metadata -- including transitive deps, not just the top-level package -- and present the finding and tradeoff in plain text rather than picking an approach unilaterally (hand-rolling an alternative, swapping packages, silently accepting it). This project has knowingly accepted AGPL-3.0-or-later exposure once already (icalendar-searcher, a transitive dep ofcaldav) as a deliberate, explicit call -- not a precedent for skipping the check next time. - Scope new auth/access-control broadly, not just to the literal
endpoint named. When a request changes the trust model (e.g. adding
public-internet exposure), apply the new gate to every endpoint serving
real data or performing a real action, and call out anything you're
tempted to exclude and why. This repo shipped a token gate once that
covered
/api/*but left/frame/image-- the actual photo bytes -- open; caught immediately in production. - Commit and push once a task is verified working, without waiting to
be asked separately. Once tests pass (and, for UI changes, the
browser check has been done), stage the relevant files, write a normal
commit message, and push to the current branch -- the maintainer's
standing authorization for the commit/push step itself. This doesn't
relax anything else: still run
git status/review the diff before staging, still never force-push/amend a pushed commit/skip hooks, and still surface anything that looks like it needs a real decision (e.g. a change that would triggermain's deploy workflow, see below) instead of pushing through it silently.
Working in this repo
- Server tests:
cd server && pytest(SQLite, fixtures wipe/reseed between tests -- seetests/conftest.py). Migration changes need a matching test intests/test_migrations.py; anything touchingrequire_frame_view/require_frame_controlboundaries needs a same-shape permission test (seetests/test_permission_boundaries.pyandtests/test_button_actions.pyfor the pattern: owner, linked user, unrelated user, logged out). - UI changes: verify in a real browser (Playwright), not just by
reading the JS -- this project has hit multiple bugs that only showed up
live (mobile viewport CSS collapse, a dialog's status message landing
behind its own backdrop, a JSON/form-urlencoded body mismatch). Spin up
uvicorn app.main:appagainst a scratchDATABASE_URL/CONFIG_PATHsqlite file, don't touch the real deployment's data..claude/skills/run-server/(/run-server) has a driver for exactly this. - New/changed UI must work at both desktop and mobile widths --
screenshot both, don't assume one implies the other. The layout
genuinely forks at the 860px breakpoint (
theme.css): the sidebar goes off-canvas behind a hamburger below it. A dialog, header control, or new widget that looks right at a wide viewport can overflow, overlap the mobile bar, or mis-center at phone widths.run-server's driver has aviewportcommand for exactly this (defaults to a phone size; switch to1280 900for desktop). - Deploy: Gitea Actions at
git.thumeit.com/tfaour/espresso_frame(.gitea/workflows/server-docker-build.yml:test->build-and-push->deployon any push tomaintouchingserver/**;deploySSHes into the host asespressoframe_deployerand runsdocker compose pull && docker compose up -d). A separate workflow (firmware-release-build.yml) builds+publishes firmware binaries as Gitea release assets whenfirmware/version.txtchanges. Poll CI status withcurl https://git.thumeit.com/api/v1/repos/tfaour/espresso_frame/actions/tasksrather than asking the user to check. - Device-facing paths are frozen.
/frame/image,/frame/advance,/frame/back,/frame/config,/frame/battery,/frame/firmwareand their exact JSON key names (refresh_interval_s,firmware_version, etc.) are baked into deployed firmware -- never rename or restructure these without a firmware-side migration story to match.