src.nth.io/

summaryrefslogtreecommitdiff
AgeCommit message (Collapse)AuthorFilesLines
2026-06-13Switch scaffold to C; update boot TODOs to match current specLuke Hoersten1-4/+4
2026-06-13Rename M7: Touch Callback -> Touch + Outbound /state POSTLuke Hoersten1-1/+1
2026-06-13Reframe protocol as REST peers; mDNS-SD discovery; explicit no-ackLuke Hoersten2-165/+367
Three structural shifts: 1. Discovery section spells out mDNS-SD. The ESP32 runs the ESP-IDF mdns responder and serves all .local records itself — A, SRV, PTR, TXT. Scrypted browses _scrypted-viewport._tcp.local with a Node mDNS-SD library that hits the multicast layer directly. Scrypted uses the IP from the SRV/A record, NOT OS-level .local hostname resolution. Re-browse every few minutes for DHCP renumbering. Manual host:port fallback for non-mDNS deployments. 2. Drop "callback" framing. The device and Scrypted are HTTP peers, both exposing POST /state with the same body shape {viewport,state}. Either side can push to the other to set state. Section is renamed "Device -> Scrypted POST /state". No new vocabulary or semantics — just the truth about what the protocol is. - state_post_failures (was: callback_failures) - state_client.c (was: callback_client.c) - "callback" replaced with "outbound POST" / "inbound POST" / just "POST /state" throughout. 3. Explicit no-application-level-ack clause. HTTP 2xx is transport- only. Device does not retry, does not block subsequent state changes on the response, does not treat 5xx as anything beyond a counter increment. Idempotency + /frame 409 + each side's idle timer cover every failure mode. Scrypted-side logic must not wait for the device to confirm a state change. Also: Scrypted Integration code in v2 guide rewritten to discover via bonjour-service browse rather than hardcoded viewport-<name>.local URLs. urlFor(name) function resolves the discovered IP per call. mdns_service.c module spec now explicitly notes Scrypted uses the browse, not the hostname.
2026-06-13Consolidate REST API: 4 endpoints, partial /config, /state, GET /configLuke Hoersten2-195/+224
API surface is now: GET /state runtime status (replaces /health) GET /config persisted config POST /config partial update; brightness is now a field here POST /state {state: wake|sleep} (replaces /wake and /sleep) POST /frame unchanged POST /config now does a partial merge — only present fields are written. To tweak only brightness, POST {"brightness": 50}. POST /state replaces POST /wake and POST /sleep with a single endpoint that takes {state: "wake"|"sleep"}. Idempotent: no-op if already in the requested state. Brightness is no longer its own endpoint; it lives in /config with all the other persisted settings. PWM remains gamma-corrected. Callback body renamed: was: {event: wake|sleep, type: tap|timeout} now: {state: wake|sleep, event: tap|timeout} The new naming matches POST /state: "state" is the resulting wake/ sleep state and the imperative for Scrypted; "event" is the cause (tap, timeout, future swipes). Doc sync: README API/Wake-Sleep/Idempotency/Integration sections and v2 impl guide API contract, modules, milestones, acceptance criteria all updated to the new model.
2026-06-13Reorder milestones so Config Persistence comes before Frame/Wake/SleepLuke Hoersten1-23/+30
Previously M5 (Wake/Sleep + idle timer) and M4 (Frame) implicitly depended on NVS values (orientation, idle_timeout_ms) that did not exist until M6 (Config Persistence). Moving Config to M4 lets the downstream endpoints read their parameters from NVS instead of working around hardcoded defaults. New order: M1 Board Bring-Up M2 HTTP + mDNS M3 Display Bring-Up M4 Config Persistence (was M6) M5 JPEG Frame Push (was M4) M6 Wake/Sleep/Brightness (was M5) M7 Touch Callback M8 Local Screens + BOOT button M9 Live Stream (/stream) M4 now also validates orientation and idle_timeout_ms per spec, applies orientation immediately (mDNS TXT + /health reflect it), and establishes the brightness default (80) on first boot. M5's test image is 480x800 by default (portrait); switch to 800x480 if /config sets landscape. M6's idle timer reads idle_timeout_ms from NVS.
2026-06-13Tighten protocol; add /wake, callback types, and orientationLuke Hoersten2-219/+338
State model is now strictly wake/sleep, owned by the device: - Local triggers: tap (toggle), idle timeout (sleep only). - Remote triggers: POST /wake, POST /sleep. Scrypted decides why (doorbell, motion, person, schedule) — the device does not know. - /frame is pure paint: 204 when awake, 409 when asleep, no implicit wake. Eliminates the race where an in-flight frame could re-wake a device that just slept on a tap. Orientation: - Configurable via /config; default portrait. - Effective resolution: 480x800 portrait, 800x480 landscape. - Scrypted must render at the effective resolution. Device does not rotate or scale JPEG content. - Orientation flows through mDNS TXT, /health, IP/Loading screens. Callbacks: - Add `type` field carrying the cause: tap or timeout (forward-compat for swipes etc). - Drop wall-clock timestamp (no RTC, no SNTP). Other: - Boot: configured device boots to sleep (backlight off). - /health adds state, last_frame_ms_ago, frame counters, error counters, brightness, orientation, idle_timeout_ms. - idle_timeout_ms: 0 disables; non-zero must be >= 5000; else 400. - First-boot brightness default 80; apply gamma curve to PWM. - Watchdog reboots hung tasks; recovers state from NVS. - On-board status LED indicates network/config state. - mDNS hostname-collision warning; ops notes. - Idempotency table summarizing endpoint safety. - Each viewport is bound 1:1 to a camera in Scrypted-side code. - Reframe "no rendering engine" honestly as "no general-purpose UI framework"; IP and Loading screens are the only locally-drawn UI. Doc sync: v2 implementation guide endpoints, modules, milestones, acceptance criteria, error handling, and tests all updated to match.
2026-06-13Spec wake/sleep, idle timeout, IP screen, BOOT buttonLuke Hoersten2-69/+194
- Couple backlight and Scrypted streaming as a single wake/sleep state owned by the device. - Callback events are idempotent imperatives: wake = "start streaming", sleep = "stop streaming". Scrypted does not track viewport state. - Each viewport is bound to one camera in Scrypted-side code; camera events (doorbell, person, motion) drive streaming. - idle_timeout_ms set by Scrypted via /config (default 60000). Same value on both sides; timers run independently and either can end a session. - Tap input only (long_press/swipes out of scope for v1). Tap is internal; callback carries the resulting state. - IP screen on first boot, after factory reset, and as a 15s BOOT short-press overlay. - Loading screen between wake and the next /frame. - BOOT: short press = IP overlay, hold 5s = factory reset. - Note that Scrypted side is code (Script for v1, custom plugin for v2 with /stream), not configuration.
2026-06-13Flesh out protocol details and add implementation guideLuke Hoersten2-18/+995
Expand README with per-endpoint contracts (response codes, persistence, size limits, idle/wake semantics) and a Scrypted integration section covering registration, frame push, and touch handling. Add v2 implementation guide adapted from the DoorView spec. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
2026-06-13Initial Scrypted Viewport scaffoldLuke Hoersten8-0/+177
Plain ESP-IDF project targeting Waveshare ESP32-P4-ETH-POE with a 5" 800x480 MIPI-DSI capacitive touch panel. Stub app_main with TODOs for Ethernet, mDNS, HTTP API (/health, /config, /frame, /sleep, /brightness), JPEG decode, and touch callback delivery per the v1 spec in README.md.