diff options
| author | Luke Hoersten <[email protected]> | 2026-06-13 23:01:23 -0500 |
|---|---|---|
| committer | Luke Hoersten <[email protected]> | 2026-06-13 23:01:23 -0500 |
| commit | 1d6cbb222ced226fcb482c0b19130774d181c8f8 (patch) | |
| tree | 768d964dc962e111c8c596e10e2da95748bc738a /TESTING.md | |
| parent | 57c93fd2978ed5b5e64f4620803b7a2cd7767ed2 (diff) | |
M7: touch + outbound /state POST to Scrypted
state_client.{h,c}:
- Worker task drains a depth-1 queue (xQueueOverwrite gives the
replace-on-full semantics from the spec — in-flight POST is never
cancelled; the next queued entry is overwritten by newer state).
- esp_http_client POST to <scrypted>/state with Content-Type application/
json, User-Agent ScryptedViewport/<version>, Connection: close, 1s
timeout. Body: {"viewport":"<name>","state":"wake"|"sleep"}.
- Any non-2xx or transport error increments state_post_failures and is
otherwise ignored. Silently drops if no Scrypted URL is configured.
touch.{h,c}:
- FT5426 capacitive touch on the shared I2C bus at 0x38.
- 30ms polling task; tracks down/up transitions, detects taps as
down-then-up within 500ms with a 150ms debounce.
- On tap, toggles wake/sleep via state_machine_set_local(), which drives
the local transition AND fires state_client_post() at Scrypted.
state_machine adds state_machine_set_local(): runs the same transition
as state_machine_set() then enqueues an outbound POST. Idle-timer
expiry now uses this path so Scrypted sees idle-driven sleeps too
(the spec's "tighten the race with /frame 409" path stays in place as
the fallback).
display.h exposes display_i2c_bus(); touch.c uses it instead of
re-initializing the same I2C port.
app_main starts state_client right after the state machine and starts
touch after display init (touch is skipped if display isn't up since
they share the bus).
CMakeLists.txt: add esp_http_client to REQUIRES.
Build clean against ESP-IDF 5.4 (binary ~860 KB; jumped ~210 KB from
M6 because esp_http_client + cJSON path pulls in tcp_transport, mbedtls,
http_parser).
TESTING.md M7: flask-based test receiver, tap dispatch verification,
failure-path check (kill receiver, confirm counter increments),
queue-coalescing behavior, no-POST-on-Scrypted-initiated, no-POST-
when-unconfigured.
Diffstat (limited to 'TESTING.md')
| -rw-r--r-- | TESTING.md | 67 |
1 files changed, 53 insertions, 14 deletions
@@ -385,32 +385,71 @@ curl -i -X POST -d 'not json' \ ## M7 — Touch + Outbound `/state` POST -**Acceptance**: tap toggles wake/sleep locally; device POSTs `{viewport,state}` to `<scrypted>/state`. +**Acceptance**: tap toggles wake/sleep locally; idle timer firing posts sleep; device POSTs `{viewport,state}` to `<scrypted>/state` for every local transition. -**How to verify** - -Stand up a test HTTP receiver: +**Test receiver** (run on the host you'll point `scrypted` at): ```bash -# In a separate terminal on Scrypted host: -python3 -m http.server 11080 # or a small flask app that logs body +# Single-file flask receiver that echoes every POST: +pip install flask +cat > /tmp/recv.py <<'EOF' +from flask import Flask, request +app = Flask(__name__) [email protected]("/state") +def state(): + print("RX", request.get_json()) + return "", 204 +app.run(host="0.0.0.0", port=11080) +EOF +python3 /tmp/recv.py ``` -Configure the device to point at it: +Configure the device: ```bash curl -X POST -H "Content-Type: application/json" \ - -d '{"viewport":"mudroom","scrypted":"http://<host>:11080"}' \ + -d '{"viewport":"mudroom","scrypted":"http://<host>:11080","idle_timeout_ms":10000}' \ http://<device-ip>/config ``` -Then: -- Tap while asleep → device wakes, receiver logs `POST /state {"viewport":"mudroom","state":"wake"}`. -- Tap while awake → device sleeps, receiver logs `POST /state {"viewport":"mudroom","state":"sleep"}`. -- Wait `idle_timeout_ms` with no `/frame` → receiver logs same sleep POST. -- Kill receiver, tap → `/state` `state_post_failures` increments. +**Tap dispatch** -**Status**: ⬜ pending. +- Tap while asleep → device wakes; receiver prints `RX {'viewport': 'mudroom', 'state': 'wake'}`. +- Tap while awake → device sleeps; receiver prints same with `state: 'sleep'`. +- Idle timer expires (10s here) → receiver prints `state: 'sleep'`. + +Each successful POST also logs on serial: + +``` +I (xxx) state_client: POST http://<host>:11080/state {state:wake} -> 204 +``` + +**Failure path** + +- Stop the receiver, then tap. Serial: + + ``` + W (xxx) state_client: POST http://<host>:11080/state failed: err=ESP_OK status=-1 + ``` + + Then `GET /state` shows `state_post_failures` incremented. + +- Local state change still happens (backlight toggles). The POST is best-effort, not a precondition. + +**Depth-1 queue with replace-on-full** + +Tap rapidly (faster than the receiver can ack) and confirm the receiver only sees the most recent state transition between in-flight POSTs. Intermediate flips are coalesced. With a 1s receiver delay, only the final state of each ~1s window should hit the receiver. + +**No POST when Scrypted-initiated** + +- `curl -X POST -d '{"state":"sleep"}' /state` — receiver should print **nothing** (Scrypted already knows it initiated). +- Same for `/state {wake}` and `/frame` (asleep → 409, no callback either). + +**No POST when unconfigured** + +- After factory reset, tapping the screen does nothing (no Scrypted URL to call). No queue entries dropped to disk. + +**Status**: 🟡 builds clean against ESP-IDF 5.4. Hardware verification needs the FT5426 touch jumpered + reachable on I2C 0x38. --- |
