Add FastAPI server: pulls from Immich, pre-processes for the panel
Implements the server side of the architecture decided on: the ESP32-C6 has no PSRAM and a tight RAM budget, so all the heavy lifting (JPEG decode, resize, Floyd-Steinberg dithering, 6-color quantization, 4bpp packing) happens here instead of on-device. The frame just does a single GET and streams the response straight to SPI. - GET /frame/image: looks up the current cursor's asset in the configured Immich album, downloads its preview thumbnail, and returns it packed into the panel's exact 800x480/4bpp/2px-per-byte format (application/octet-stream, always exactly 192,000 bytes). - GET / + POST /api/config + GET /api/albums: a small web UI for entering the Immich URL/API key and picking an album, rather than cramming that into the ESP32's captive portal form. - Config (Immich creds, selected album, cursor) persists to a JSON file via a docker-compose volume mount. Verified locally with a venv (Docker isn't available in this environment): unit-tested image_pipeline against a synthetic image (exact byte count, valid panel color codes only), and ran a full end-to-end pass against a mock Immich HTTP server exercising the real /frame/image path. Pinned dependency versions in requirements.txt after hitting a real bug with unpinned floors: the latest starlette (1.3.1) resolved by `pip install fastapi` breaks Jinja2Templates outright. Not yet wired to the ESP32 side (task 6) or authenticated -- /frame/image is unauthenticated for now, fine on a trusted LAN but worth revisiting once the firmware sends a shared device token.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
# ESPresso Frame Server
|
||||
|
||||
Pulls photos from an [Immich](https://immich.app) album, resizes/dithers/quantizes
|
||||
them to the E Ink Spectra 6 panel's exact 6-color format, and serves the
|
||||
frame a ready-to-display image once an hour. All the image processing
|
||||
happens here so the ESP32 never has to decode a JPEG or run a dithering
|
||||
algorithm itself -- it just streams the response straight to the panel.
|
||||
|
||||
## Setup
|
||||
|
||||
1. **Get an Immich API key**: in Immich, go to Account Settings -> API Keys
|
||||
-> New API Key. Read-only access to albums/assets is enough.
|
||||
2. **Run the server**:
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
3. Open `http://<this-machine>:8420/` in a browser, enter your Immich URL
|
||||
and API key, click **Load Albums**, pick one, and **Save**.
|
||||
4. On the ESP32's captive portal setup form, set the **Tools Server** field
|
||||
to `<this-machine>:8420`.
|
||||
|
||||
## Endpoints
|
||||
|
||||
- `GET /` -- config UI
|
||||
- `GET /api/albums` -- lists Immich albums (used by the config UI)
|
||||
- `POST /api/config` -- saves Immich URL/API key/album/order
|
||||
- `GET /frame/image` -- returns the current photo pre-processed into the
|
||||
panel's raw 800x480, 4-bit-per-pixel, 2-pixels-per-byte format
|
||||
(`application/octet-stream`, exactly 192,000 bytes)
|
||||
- `GET /health` -- liveness check
|
||||
|
||||
## Notes
|
||||
|
||||
- Config (including the Immich API key) is stored in `./data/config.json`
|
||||
on the host via the compose volume mount.
|
||||
- `/frame/image` isn't authenticated yet. That's fine on a trusted home
|
||||
LAN for now, but worth revisiting once the ESP32 side is wired up to
|
||||
send a shared device token.
|
||||
- The 6-color palette RGB values in `app/image_pipeline.py` are
|
||||
approximations, not measured values (Waveshare doesn't publish exact
|
||||
color primaries for this panel) -- tune them once you can compare a
|
||||
rendered test image against the real panel.
|
||||
|
||||
## Local development (without Docker)
|
||||
|
||||
```
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
CONFIG_PATH=./data/config.json uvicorn app.main:app --reload --port 8420
|
||||
```
|
||||
Reference in New Issue
Block a user