<feed xmlns='http://www.w3.org/2005/Atom'>
<title>luke/esp32-poe-scrypted-viewport, branch main</title>
<subtitle>ESP32-POE Scrypted viewport (private)
</subtitle>
<id>https://src.nth.io/luke/esp32-poe-scrypted-viewport/atom?h=main</id>
<link rel='self' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/'/>
<updated>2026-07-18T01:39:31+00:00</updated>
<entry>
<title>docs: prune TESTING.md to its forward-looking content</title>
<updated>2026-07-18T01:39:31+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-18T01:39:31+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=ffd5b40586f6c1a8a0b245603c672afe521eb128'/>
<id>urn:sha1:ffd5b40586f6c1a8a0b245603c672afe521eb128</id>
<content type='text'>
The 730-line milestone-by-milestone bring-up log served its purpose —
every milestone (M1-M9) is verified on hardware, and the per-run
narratives are preserved in git history (last full version: 8eebc0e).
What survives, restructured (~230 lines):

- New-unit bring-up: hardware prereqs (all unknowns resolved), the
  flash -&gt; panel -&gt; touch -&gt; Scrypted sequence, and the panel
  failure-mode signals table (the content you actually need the day a
  second viewport gets provisioned).
- Regression recipes: condensed curl checks for config validation (incl.
  the 54-char name cap), frame push, state machine, OTA acceptance, and
  device-initiated POSTs.
- Outstanding tests: the honestly-never-run backlog (mid-frame cable
  pull, 24h soak, frame storm, brown-out, multi-viewport).
- Performance playbook: kept, with example log formats refreshed to the
  current stream instrumentation (the old examples showed the removed
  pre-stream snapshot path) and thresholds updated to the post-tuning
  baseline (24fps painted, pend_age as the backlog tripwire).

README: pointers at TESTING's old structure updated; the Hardware status
row no longer claims schematic confirmation is pending (those unknowns
were resolved during bring-up — only the per-unit flash-size check
remains).
</content>
</entry>
<entry>
<title>docs: mark M9 verified — live stream shipped as raw TCP :81, not POST /stream</title>
<updated>2026-07-18T01:27:50+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-18T01:27:50+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=8eebc0ea5b6001be6735cf679fb3b94757d0de02'/>
<id>urn:sha1:8eebc0ea5b6001be6735cf679fb3b94757d0de02</id>
<content type='text'>
M9 still carried the abandoned chunked-HTTP design and a pending status
long after the raw TCP data-socket stream was verified at painted =
sent = 24 fps (recv/decode task split d1c8d45 + TCP window/EMAC tuning).
Rewrite the milestone to describe what shipped, flip it ✅, and bump the
README's "M1 – M8" line to M9.
</content>
</entry>
<entry>
<title>docs: sync README + TESTING + scrypted README with v1.4.0 reality</title>
<updated>2026-07-18T01:25:24+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-18T01:25:24+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=482d8f45f18c6cc880bf2dfbbb70e3a4d704ac44'/>
<id>urn:sha1:482d8f45f18c6cc880bf2dfbbb70e3a4d704ac44</id>
<content type='text'>
Drift accumulated across the review-fix, mDNS-discovery, and tooling
work:

- Discovery: the design-era "use a Node mDNS library" flow is replaced
  with what shipped — the sandbox has no such libraries; the script does
  a dependency-free legacy-unicast dgram browse (no :5353 bind), host
  dropdowns, blank-name inheritance, and auto-heal on register failure
  (matched by the new mac TXT record) instead of periodic re-browsing.
- /state example: add mac, ota_state, panel dims, tear_guard_engaged,
  temp_c, and the stream stats object; correct `configured` semantics
  (scrypted URL only) and the never-null MAC-derived name in /config.
- /config: document the 54-char name cap (mDNS label limit).
- scrypted/README: add-device + settings tables gain the discovered-host
  dropdown, Wake triggers on create, and the canonical "Viewport name"
  rename field (the Scrypted pencil doesn't propagate — by design);
  trigger-scoped subscriptions; device-wake immediate-204 semantics;
  idle=0 no longer force-slept by the safety timer; "manual IP" dropped
  from limitations; discovery section reframed with the diagnostic.ts
  probe and bridge-networking caveat.
- Source map: dedupe the doubled touch.{h,c} row, add stream_server /
  ota / chip_temp rows, fix nvs_config + endpoint-count drift.
- Build/TESTING: point iteration at `make ota` (USB stays for first
  flash); TXT expectations include mac; DHCP-renumber failure mode now
  documents the verified auto-heal path.
</content>
</entry>
<entry>
<title>scrypted: plan the Scripts-script -&gt; real-plugin conversion</title>
<updated>2026-07-18T01:19:55+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-18T01:19:55+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=8c2862b17b9bd9ae194d18fc912785f899524d8f'/>
<id>urn:sha1:8c2862b17b9bd9ae194d18fc912785f899524d8f</id>
<content type='text'>
PLUGIN-CONVERSION.md captures the full plan so a future session can pick
it up cold: why (the Scripts sandbox's reload-leak machinery — cleaner
registry, drain ordering, name-drift workarounds — exists only because
of the paste-deploy model), the port map (what moves 1:1, what changes,
what dies), open questions to verify (orphaned ffmpeg on worker restart,
storage attach at startup), the cutover order (never run script + plugin
together), and the verification suite. Linked from the README's Related
docs and Status table.
</content>
</entry>
<entry>
<title>readme: document the Makefile build/OTA/verify workflow</title>
<updated>2026-07-18T01:13:06+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-18T01:13:06+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=903d1cf44e9e4b6a6733fbc5d405182b658e5d84'/>
<id>urn:sha1:903d1cf44e9e4b6a6733fbc5d405182b658e5d84</id>
<content type='text'>
</content>
</entry>
<entry>
<title>tools: ota.sh — a `}` inside a ${1:?...} message truncates the expansion; use a usage() fn</title>
<updated>2026-07-18T01:10:49+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-18T01:10:49+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=eb7535332975e3b5b3db6823970aea839e34a7e6'/>
<id>urn:sha1:eb7535332975e3b5b3db6823970aea839e34a7e6</id>
<content type='text'>
</content>
</entry>
<entry>
<title>release: v1.4.0</title>
<updated>2026-07-18T01:09:51+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-18T01:09:51+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=49c51b4e909053dc71350f4abe215acd29a714c6'/>
<id>urn:sha1:49c51b4e909053dc71350f4abe215acd29a714c6</id>
<content type='text'>
</content>
</entry>
<entry>
<title>tools: make targets + ota.sh for the build/OTA/verify loop</title>
<updated>2026-07-18T01:09:51+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-18T01:09:51+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=6c16b354e00e916b7e252b2304b71af87981f518'/>
<id>urn:sha1:6c16b354e00e916b7e252b2304b71af87981f518</id>
<content type='text'>
make build / cleanbuild / ota / verify / check wrap the recurring dev
loop, sourcing the ESP-IDF env per-recipe. `make ota` reconfigures first
(the embedded git hash stamps at configure time only), builds, pushes,
and verifies. tools/ota.sh encodes the OTA acceptance criterion — the
new image must show pending-verify on a fresh boot before flipping to
valid; "valid" with pending-verify never seen means the device silently
booted the old slot — and re-pushes once automatically on the known
first-push rollback quirk.
</content>
</entry>
<entry>
<title>scrypted: stamp SCRIPT_VERSION = 118a04f</title>
<updated>2026-07-18T01:04:05+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-18T01:04:05+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=81306bf3039f24f7ace8ce9cafca31ae747ee630'/>
<id>urn:sha1:81306bf3039f24f7ace8ce9cafca31ae747ee630</id>
<content type='text'>
</content>
</entry>
<entry>
<title>scrypted: subscribe only to selected wake-trigger interfaces</title>
<updated>2026-07-18T01:04:05+00:00</updated>
<author>
<name>Luke Hoersten</name>
<email>luke@hoersten.org</email>
</author>
<published>2026-07-18T01:04:05+00:00</published>
<link rel='alternate' type='text/html' href='https://src.nth.io/luke/esp32-poe-scrypted-viewport/commit/?id=118a04f9a56ab338da746f566fca8799fbd3e13d'/>
<id>urn:sha1:118a04f9a56ab338da746f566fca8799fbd3e13d</id>
<content type='text'>
attachListener listened on BinarySensor+MotionSensor+ObjectDetector
unconditionally and relied on handleCameraEvent to filter by trigger —
correct, but a doorbell-only viewport still took a callback per motion
re-assert on a chatty camera, and the "subscribed to [...]" log implied
all three were wake sources. The listener set now follows the selected
triggers (zero triggers = tap-only: no listeners at all), and the
idempotency key becomes a "cameraId|triggers" signature
(attachedCameraId -&gt; attachedListenerSig) so a trigger change forces a
re-attach through the existing rebind path. Event-time filtering stays
as defense in depth (ObjectDetector still needs the person-class check).
</content>
</entry>
</feed>
