18 Commits
Author SHA1 Message Date
tfaour 474b92a282 Add server-side support for a second panel (13.3in Spectra 6 / EE02) and scaffold its firmware target
Build and push server image / test (push) Successful in 45s
Firmware build check / build-check (push) Successful in 2m50s
Build and push server image / build-and-push (push) Successful in 4m36s
Build and push server image / deploy (push) Failing after 1m34s
Server: Frame.panel_type (new column + migration) is auto-derived from
the device's reported board (X-Frame-Board), never user-set -- the
panel is a property of the hardware, not a picker in the UI.
image_pipeline's packing/render pipeline is parameterized by panel
geometry instead of hardcoded 800x480 globals, with the real confirmed
13.3in geometry (1600x1200) registered alongside the original 7.3in
panel. Existing 7.3in frames are unaffected (column default + board
mapping both resolve to the original panel).

Board identifiers are also renamed (devkit/xiao -> devkit_esp32c6/
xiao_esp32c6, plus new "ee02") since the EE02 board also carries a XIAO
module -- "xiao" alone stopped disambiguating hardware. The server
keeps accepting the legacy bare names indefinitely for already-flashed
devices.

Firmware: scaffolds a third build target (ee02, ESP32-S3 -- a real
chip-target change, not just a same-chip Kconfig variant like xiao) and
a new epd13in3e driver component skeleton. The actual panel init/LUT/
refresh register sequence isn't ported from vendor demo code yet (none
was available), so that component deliberately fails to compile
(#error) rather than risk sending unverified register values to real
hardware -- devkit/xiao are unaffected and build identically to before.
CI's ee02 build step is continue-on-error for the same reason.
2026-08-04 20:08:22 +00:00
tfaour 09119e775f Deploy: retry "docker compose up -d" instead of guessing at a stale container
Build and push server image / test (push) Successful in 41s
Build and push server image / build-and-push (push) Successful in 3m56s
Build and push server image / deploy (push) Failing after 1m37s
The previous fix (kill anything on port 8420 before up) didn't help --
confirmed nothing was actually squatting on the port. The real cause,
per the maintainer: "up -d" run manually a few seconds after "down"
always succeeds, but scripted straight through (down && pull && up,
pull sometimes a no-op if the image is already cached) fails every
time. That's "down" returning before the OS/docker-proxy has actually
released port 8420 yet, not an orphaned container -- a timing race, not
a stuck process. Retrying "up -d" a few times with a short pause rides
out that race without needing to guess a fixed sleep long enough to
always cover it.
2026-08-01 12:46:05 +00:00
tfaour f209880fd0 Deploy: kill anything holding port 8420 before bringing the new container up
Build and push server image / test (push) Successful in 47s
Build and push server image / build-and-push (push) Successful in 4m14s
Build and push server image / deploy (push) Failing after 1m27s
"docker compose down" before "pull/up" (previous commit) didn't fix the
port conflict -- it only tears down containers this compose project
itself tracks, so a stale/orphaned container from an earlier deploy (or
anything else bound to 8420, especially with restart: unless-stopped
fighting back) slips through untouched and the new "up" fails with
"port is already allocated". This is root-cause-agnostic instead: find
and stop/remove *any* container publishing 8420, compose-managed or
not, right before pull/up. --remove-orphans on the down step too, for
services that used to be in the compose file and aren't anymore.
2026-08-01 12:05:58 +00:00
tfaour 466efdb873 Deploy: docker compose down before pull/up, not just up -d
Build and push server image / test (push) Successful in 40s
Build and push server image / build-and-push (push) Successful in 3m29s
Build and push server image / deploy (push) Failing after 1m21s
up -d alone assumes the previous container releases port 8420 cleanly
before the new one binds -- it doesn't force that. The last three
deploys all failed with "port is already allocated" at exactly that
step; running compose down first (confirmed working when done manually
over SSH) guarantees the old container is fully stopped and removed
before the new one starts, closing the race.
2026-07-31 12:35:14 +00:00
tfaour 63751a79ad Update .gitea/workflows/server-docker-build.yml
Build and push server image / test (push) Successful in 19s
Build and push server image / build-and-push (push) Successful in 2m30s
Build and push server image / deploy (push) Successful in 54s
Fix user name
2026-07-24 13:39:02 -04:00
tfaour 86b94a9e64 Auto-deploy the server image to production over SSH after a build
Build and push server image / test (push) Successful in 19s
Build and push server image / build-and-push (push) Successful in 1m56s
Build and push server image / deploy (push) Failing after 1s
Adds a deploy job to the existing build-and-push workflow: SSHes into
the deploy host as a dedicated espressoframeuser account and runs
docker compose pull && up -d. Runs only after build-and-push succeeds,
using a key/host pulled from repo secrets (DEPLOY_SSH_KEY, DEPLOY_HOST,
optional DEPLOY_PORT).
2026-07-24 13:34:50 -04:00
tfaour 1100580c2c Live-update whiteboard/tasks source display; add firmware CI build check
Build and push server image / test (push) Successful in 15s
Firmware build check / build-check (push) Successful in 2m6s
Build and push server image / build-and-push (push) Successful in 2m26s
Whiteboard and tasks-source save/clear used to just tell the user to
reload the page to see the change. Both endpoints always assign a
successful "set" to the calling user, so the new state is fully known
client-side already -- rewrite the "Currently using/showing ..." block
in place instead, no server round trip or reload needed.

Also adds .gitea/workflows/firmware-build-check.yml: builds both board
variants (devkit, xiao) on every push touching firmware/**, unlike
firmware-release-build.yml which only builds on a version.txt bump.
Verified both builds succeed locally against the actual ESP-IDF
toolchain before wiring this in.
2026-07-23 19:08:01 -04:00
tfaour 31adc34a19 Add a real pytest suite, gating the CI build
Build and push server image / test (push) Successful in 1m18s
Build and push server image / build-and-push (push) Successful in 2m12s
64 tests covering: auth/setup and the CSRF gate, the "owner adds their
own data, anyone linked can mute it" permission pattern shared across
calendar-select/tasks-source/whiteboard-source, migration correctness
(fresh install, idempotent re-run, expected columns), battery estimate
outlier rejection, calendar_feed's fetch/merge/partial-failure handling,
webdav_client's fetch/list-directory, the whiteboard force-refresh
throttle bypass and browse endpoint, and render-size invariants across
calendar views/orientations.

No DB/HTTP fixtures need Docker, Node, or a real Immich/CalDAV/WebDAV
server -- a fresh temp SQLite file plus a couple of small local HTTP
servers as test doubles cover it all. Table data is wiped and reseeded
between tests rather than relying on SQLAlchemy's transaction-rollback
isolation pattern, which needs a pysqlite event-listener workaround
app/db.py's engine doesn't have and has no reason to gain just for tests.

Wired into .gitea/workflows/server-docker-build.yml as its own job that
build-and-push now depends on, so a failing suite blocks the image push
rather than just running alongside it for show.
2026-07-23 18:51:54 -04:00
tfaour afbe9db409 Revert "Push server image to local registry instead of git.thumeit.com"
This reverts commit 8ae09f238b.
2026-07-23 17:17:46 -04:00
tfaour 49794b4973 Revert "Configure BuildKit to allow plain HTTP to the local registry"
This reverts commit 1f62653118.
2026-07-23 17:17:46 -04:00
tfaour 1f62653118 Configure BuildKit to allow plain HTTP to the local registry
Build and push server image / build-and-push (push) Failing after 10s
Docker defaults to HTTPS for any non-docker.io registry, and
10.0.0.246:3000 (a bare LAN IP:port) isn't serving valid HTTPS -- the
push kept going out over HTTPS and failing. Adds a buildkitd config
telling BuildKit specifically to use HTTP for that host.

This only fixes the actual build+push step (BuildKit). The "Log in"
step runs a plain `docker login` through the classic Docker CLI/daemon
instead, which doesn't read this config -- that one still needs
10.0.0.246:3000 added to "insecure-registries" in the actual Docker
daemon's /etc/docker/daemon.json on whatever host runs the Gitea
Actions runner, followed by a daemon restart. That's runner-host
infrastructure outside this repo.
2026-07-23 17:12:33 -04:00
tfaour 8ae09f238b Push server image to local registry instead of git.thumeit.com
Build and push server image / build-and-push (push) Failing after 9s
Cloudflare (fronting git.thumeit.com) started rejecting image pushes
with 413 Payload Too Large once whiteboard mode's Node runtime + native
resvg bindings made the image significantly bigger than before. Switched
the build/push target to a LAN-local registry (10.0.0.246:3000) that has
nothing in front of it to hit that limit, and updated
docker-compose.yml.example to match.
2026-07-23 17:10:21 -04:00
tfaour ad1fa99064 Fix firmware release workflow: build against ESP-IDF v6.0, not v5.3
The build container was pinned to espressif/idf:release-v5.3, but the
firmware is actually developed against v6.0.2 (this machine's
$IDF_PATH). That gap caused real compile failures in CI --
esp_sleep_enable_gpio_wakeup_on_hp_periph_powerdown and
ESP_NETIF_CAPTIVEPORTAL_URI are both newer than v5.3 has, plus a
transitive stdbool.h include that changed between versions -- none of
it a real bug in the source.
2026-07-21 21:41:36 -04:00
tfaour ecc5b8d197 Fix firmware release workflow: docker cp instead of a bind-mount
The runner's job workspace lives in a named Docker volume, not a real
host path, so a nested docker run -v "\$PWD:/workspace" bind-mounted an
empty directory when talking to the host daemon (which has no notion of
paths inside the calling container) -- build_for_board.sh: No such file
or directory. docker create/cp/start streams the checkout into the
build container through the Docker API instead, which works regardless
of what backs either side's workspace.
2026-07-21 21:35:30 -04:00
tfaour 9e630c0c6e Fix firmware release workflow: run checkout outside the ESP-IDF container
espressif/idf:release-v5.3 has no Node.js, and actions/checkout (a Node
action) runs inside whatever container: the job specifies -- it failed
immediately with "node: not found" on the first real run. Checkout now
runs on the plain runner; only the two build steps spin up the ESP-IDF
image themselves via docker run, using the runner's already-mounted
docker socket.
2026-07-21 21:32:00 -04:00
tfaour 94d67f7767 Firmware CI releases + Gitea auto-update in the server
Build and push server image / build-and-push (push) Successful in 39s
New Gitea Actions workflow builds both board variants and publishes
them as release assets whenever firmware/version.txt is bumped. The
server can now poll that repo's releases (next to the existing manual
upload) and either surface an "Update frame" button or, with
"Automatically apply updates" checked, stage the new build itself --
the frame still only updates on its own next wake either way.
2026-07-21 19:25:08 -04:00
tfaour 2cd7283ce9 Fix Gitea Actions registry auth: use a PAT instead of the auto token
Build and push server image / build-and-push (push) Successful in 41s
The automatic GITHUB_TOKEN doesn't reliably authenticate against Gitea's
Container Registry -- confirmed as a known, still-open limitation across
several Gitea versions (multiple upstream issues, consistent with the
401/unauthorized error hit here). Gitea's own community guidance is to
use a real Personal Access Token instead. Switches docker/login-action to
a REGISTRY_TOKEN repo secret (a PAT with package write scope, created in
Gitea's user settings) with an explicit username rather than the
gitea.actor context, and drops the now-unused permissions: packages:
write block that only applied to the auto token.
2026-07-18 15:50:11 -04:00
tfaour 1c1d9c6767 Add Gitea Actions workflow to build/push the server image
Build and push server image / build-and-push (push) Failing after 2m46s
Builds server/Dockerfile and pushes to this repo's Gitea Container
Registry (git.thumeit.com/tfaour/espresso-frame-server) on every push to
main that touches server/, tagged both latest and the commit SHA.

docker-compose.yml now sets both image: and build: -- deploy hosts can
docker compose pull to grab the CI-built image without needing this
repo's build context, while local dev can still docker compose build
against Dockerfile changes directly.
2026-07-18 14:38:55 -04:00