# 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 / 快速开始 ```sh 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. 网页使用账号密码登录。通过 `python scripts/create-account.py --username YOUR_NAME` 创建账号,再启动服务;密码不会写入代码或明文配置。勾选记住登录可保持 30 天,设置页面可修改密码或退出。 ## 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 ```sh 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](docs/NAS.md). 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](docs/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. Browser login uses SQLite accounts with salted scrypt password hashes and revocable, HttpOnly/SameSite=Strict cookies (30 days when remembered, 12 hours otherwise). Mutations require a session CSRF header. Logout and password changes revoke sessions. Passwords and browser sessions are never stored in localStorage. Bootstrap hashes in private `config/accounts.json` seed new accounts only; restarting does not undo a password change. Five failed logins per source IP cause a five-minute lockout. The browser needs no API Token. Bearer authentication remains compatible for external clients; the KoKonna key is never used for account login. The LAN deployment uses HTTP; use an explicitly configured HTTPS endpoint if transport encryption is needed. No reverse proxy or firewall is changed automatically. ## 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](docs/integrations/TICKTAR.md). ## 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 ```sh .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. ## Template editing Templates provide bilingual form fields, event lists, local photo selection, advanced JSON, color/grayscale previews, persistent defaults and reset to built-in defaults. Changing content invalidates the previous preview. Weather uses the labeled Mock provider until a real provider is configured. Live display controls stay disabled until real writes are separately approved. Browser smoke tests use a disposable Mock account via FRAME_FLOW_TEST_USERNAME and FRAME_FLOW_TEST_PASSWORD; never run the mutation smoke suite against live devices.