Files
pigway-frame-flow/README.md
T

8.2 KiB
Raw Blame History

Pigway Frame Flow

Pigway Frame Flow is a lightweight self-hosted platform for programmable smart-frame automation.

独立运行的智能画框服务:中英文 Web UI、深浅色模式、设备驱动抽象、KoKonna 全部已公开接口、图片处理、模板、SQLite 调度与播放列表、临时覆盖恢复和 REST API。

KoKonna is a supported device integration and is not affiliated with this project unless otherwise stated.

Quick Start / 快速开始

python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.lock
.venv/bin/python scripts/run_local.py

Open http://127.0.0.1:18787. The local demo creates a private .env with a random FRAME_FLOW_TOKEN. Read that token locally in an editor and enter it on the login screen. Do not paste a KoKonna API Key into Web UI. Demo always forces mock mode and disables real writes. It leaves data persistent in ./data and ./config.

打开页面后输入 .env 中的 FRAME_FLOW_TOKEN;Token 只留在当前页面内存,刷新后需重新输入。

Architecture / 架构

providers → templates / renderers → generated image
                                  ↓
Web UI / external REST → DisplayService → FrameDeviceDriver → KokonnaDriver
Scheduler / Playlist ─────────────┘
                   SQLite: operations / history / jobs / overrides / assets

Package: pigway_frame_flow. Device composition is in core/registry.py; vendor-specific code is in drivers/kokonna. One process, one worker, no Redis, message broker, OpenClaw, Home Assistant or external application dependency.

Configuration / 配置

Copy .env.example to .env, set a strong FRAME_FLOW_TOKEN, chmod 600 .env.

  • KOKONNA_MOCK=true: independent persistent demo, no cloud calls.
  • KOKONNA_WRITE_ENABLED=false: live write protection, mandatory before verification.
  • KOKONNA_API_KEY: device key, never stored in DB or returned to Web UI.
  • *_FILE: optional Docker Secret equivalent.
  • WEB_BIND/WEB_PORT: Compose host bind and port. Internal app binds container network.
  • CORS_ALLOWED_ORIGINS: exact comma-separated origins; default empty.
  • IMAGE_URL_ALLOWED_HOSTS: exact public HTTPS hosts; default disabled. Redirects denied.
  • TZ=Asia/Shanghai; DATA_DIR=/data; CONFIG_DIR=/config.

For multiple devices copy deploy/devices.yml.example into config/devices.yml, reference a separate environment key per device. Jobs bind a device_id. Layout currently targets 1200 × 1600; incompatible geometry is refused before writes.

Docker

cp .env.example .env
# Configure a token; keep API key empty, mock true, writes false.
mkdir -p data/images data/generated data/cache config
sudo chown -R 10333:10333 data config
./scripts/build-verify.sh
docker compose up -d
python3 scripts/smoke-test.py

Image pigway-frame-flow:latest, service/container pigway-frame-flow, AMD64, Python 3.14.5, pinned runtime dependencies, CJK fonts, non-root UID/GID 10333, read-only root filesystem, writable /data and /config, no capabilities, no host network, no Docker socket. Restart unless-stopped. JSON-file logs capped 10m × 3. Healthcheck uses service /health, not cloud availability. No second log database.

Synology NAS

Target /volume2/docker/pigway-frame-flow/. See NAS deployment. Set WEB_BIND to the NAS LAN IP to restrict the published interface, or explicitly 0.0.0.0 when desired. Port 18787 must be free. No router/firewall/reverse proxy rules are modified. Source and persistent volumes stay in this project directory. No artificial logs directory is required. Compose creates its own bridge network.

KoKonna Setup / 真实设备

First deploy Mock and verify persistence. Then set KOKONNA_MOCK=false and fill KOKONNA_API_KEY through private environment/secret. Keep writes false. /ready checks configuration/DB/worker/token without a cloud call. Query device and images metadata; compare model, geometry, timezone, heartbeat, battery, imageId, coverId, sync. If different, stop and report. Do not enable writes or perform the single test until the owner explicitly authorizes it. Test design: REAL_DEVICE_TEST.md.

Security

All protected /api/v1 capabilities use Frame Flow Bearer auth and scope checks. SQLite stores token hashes. Device keys live only in env/secret and runtime memory. The optional 30-day Remember login stores only the Frame Flow Token in this browser localStorage. Disconnect, expiry or HTTP 401 clears it; shared browsers should opt out. Cloud original-image endpoint embeds a key upstream; backend proxies it with no redirects and no access/transport logging. Key never enters browser URLs or DB. Request validation and exceptions use sanitized standard errors. Strong tokens, trusted LAN and no public exposure are the intended deployment model.

API

OpenAPI 3.x at /openapi.json; Swagger at /docs; stable /api/v1. Display/render/upload return 202 + operation_id. Poll /api/v1/operations/{id}; completion requires matching image ID and cloud synced flag. Idempotency-Key retained seven days. X-Request-ID traces history. UNKNOWN writes block later mutations; read-only reconciliation and explicit admin acknowledgement release the block, never replay a write. The older /api/display route is retained and deprecated (same v1 payload). See external integration contract.

Scheduler / Playback

Cron (5 fields), daily, weekdays, weekends, date and temporary tasks; Shanghai time. Use source {"type":"restore"} for explicit restore jobs. Unique job/slot and persistent idempotency prevent duplicate dispatch. Overdue tasks >5 minutes are skipped rather than unexpectedly overwriting artwork after downtime. State persists across restart. Overrides use priority then recency; lower priorities wait without uploading. If another app changes the frame, expired overrides do not overwrite its artwork. Frame Flow playlists support ordered/random cloud image IDs and >=5-minute intervals; only one enabled playlist per device. They pause for overrides/unknown writes. Native cloud slideshow mode/interval are readable; membership/editing are unavailable in the official API. Avoid having native and Frame Flow playback compete.

Templates / 图片

Six JSON layout templates: daily-dashboard, weather, calendar, quote, photo-text, system-alert. color and grayscale profiles. Photo data accepts photo_id = local asset ID. Mock weather is clearly labeled; no unreliable lunar date is fabricated. Contain preserves composition with margins; cover crops centrally; smart is an edge-saliency heuristic. Always inspect preview; face-aware guarantees are not claimed. Cloud uploads activate immediately, so gallery-only means local staging.

Development / Testing

.venv/bin/python -m pip install -e '.[test]'
.venv/bin/python -m pytest -q
.venv/bin/python -m ruff check pigway_frame_flow tests scripts
.venv/bin/python -m ruff format --check pigway_frame_flow tests scripts
.venv/bin/python -m mypy pigway_frame_flow
.venv/bin/python -m pip_audit -r requirements.lock --no-deps --disable-pip

Tests mock every cloud request; no real-device writes. scripts/ui-smoke.cjs runs an isolated headless browser and checks settings.mock before acting. CI builds AMD64, validates Compose, starts a container, checks health/ready and restarts for persistence.

Known Limitations / 已知边界

Cloud acknowledgements do not prove physical display pixels. Model fields may be omitted and are explicitly marked configured. No native slideshow membership API, no cloud upload-only API, no automatic retries after uncertain writes. Cache has no automatic quota/GC in 0.1.0; monitor data volume. Rendering assets are local, limited to 25M pixels/20 MiB. This release uses a single worker and is not multi-replica. Gallery reads have no pagination because upstream documents none. Web UI hides secrets and lazy-loads thumbnails; generating a first thumbnail fetches one original. Multi-client provisioning UI/webhook delivery are not implemented. Docker build/NAS status is recorded honestly in docs/ACCEPTANCE.md.

Roadmap

Additional drivers; scoped token administration; signed webhooks; cache retention; face-aware cropping; public weather provider; Google Calendar, Home Assistant, MQTT, NAS monitoring and AI integrations through the same API. External integration is optional.