# 2026-03-13 ## Work Log ### FRE-245 — Fire TV integration: ADB-over-IP (Universal Remote project) **Status**: Completed **Context**: User reported device discovery still not working after previous DIAL implementation. Errors: `NSNetServicesErrorCode = -72008` (mDNS), sockets opening/closing rapidly. **Root causes identified and fixed**: 1. **mDNS -72008**: `react-native-zeroconf.scan()` restarts `NSNetServiceBrowser` on every call. Calling it 6x in a loop meant only the last service ran, and rapid start/stop → -72008. Fix: one Zeroconf instance per service type. 2. **IP scan missed Fire TV**: Port 8009 `GET /` → 404 text/plain. DIAL detection checked `contentType.includes("xml")` — always false. Fix: probeHost() calls probeDIAL() first. 3. **probeDIAL() couldn't identify Fire TV**: `/apps/system` body has no Amazon/Fire text. Fix: probe `/apps/AmazonInstantVideo` (Fire TV exclusive). 4. **Info.plist**: `_amzn-wplay._tcp` missing from `NSBonjourServices` (iOS 14 blocks mDNS for unlisted services). Added it. 5. **Fire TV SSDP is disabled**: Confirmed via direct Python probe — no SSDP response. Added "Add by IP" manual entry UI (+ button, bottom-sheet modal, probeManual() method). **Device confirmed**: `192.168.50.31` is Fire TV, responds on port 8009 (DIAL). **Commits**: `49081a0` **Files changed**: - `src/services/discovery/DiscoveryEngine.ts` - `src/hooks/useDiscovery.ts` - `app/(tabs)/index.tsx` - `ios/UniversalRemote/Info.plist` --- ### FRE-245 — Follow-up session (2026-03-12, continued) **Status**: Done — committed and Paperclip closed **New root causes found (this session)**: 1. **`AbortSignal.timeout` not available in React Native** — abort-controller v3.0.0 polyfill predates `AbortSignal.timeout` spec addition. All fetch probes failed silently (error swallowed by `catch (_err) {}`). Fixed with `abortAfter(ms)` helper using `AbortController + setTimeout`. 2. **mDNS -72008 (second instance)** — previous fix created one Zeroconf instance per service type but started them all concurrently. `scan()` calls `[self stop]` each time, killing the previous `NSNetServiceBrowser`. Fixed by scanning service types **sequentially**, each getting `timeoutMs / serviceCount` ms. 3. **probeDIAL fingerprinting** — `/apps/AmazonInstantVideo` returns 404 on this FireOS version. Confirmed `/apps/YouTube` → 403 is a reliable Fire TV discriminator (tested via curl). **Commit**: `c5e973b` **Paperclip**: FRE-245 marked `done` --- ### FRE-245 — Third session (FireTVController broken, user reopened) **Status**: Done — committed `2066298`, Paperclip closed **Root cause:** `FireTVController.ts` had unresolved git merge conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) from an incomplete stash/rebase. TypeScript was failing to parse the file at runtime. **Additional bugs fixed:** - Removed duplicate local `abortAfter()` function (returned plain `AbortSignal`). Consolidated to shared `utils/network` `abortAfter()` (returns `[signal, cancel]` tuple). - PIN verify token: Fire TV REST API returns token in `description` field, not `clientToken`. Fixed to `data.description ?? data.clientToken`. - Added `connect()` step logging for visibility. **Device note:** Fire TV at `192.168.50.31` is FireOS 7 or earlier. Port 8080 (FireOS 8 REST API) is not open. Controller falls back to DIAL. Nav keys → `UnsupportedKeyError` (expected for this firmware). App launching works via DIAL. --- ### FRE-245 — Fifth session (heartbeat: PIN modal wiring) **Status**: Done — committed `cdd27e6`, Paperclip closed (FRE-245 `done`) **Root cause:** `useConnection.connect()` created a fresh controller but never set `onPinRequired` before calling `controller.connect()`. FireTVController immediately threw `PairingRequiredError` — PIN never appeared on TV. **Fix (`cdd27e6`):** - `src/hooks/useConnection.ts`: `connect(device, onPinRequired?)` wires callback onto controller before connect - `app/pair/[id].tsx`: FireTV hint copy + PIN entry modal; callback passed to `connect()` - `app/device/[id].tsx`: same modal for token-expiry re-pairing on reconnect **Note:** FRE-245 status was `todo` on new heartbeat despite previous PATCH to `done` — previous run's `PAPERCLIP_RUN_ID` had expired. Re-checked out and closed successfully. --- ### FRE-41 — Resumed (heartbeat: CTO unblocked 2026-03-10) **Status**: Done — committed `79bbcc4`, Paperclip closed (FRE-41 `done`) **Context:** CTO posted unblocking comment 2026-03-10 (after my last blocked comment 2026-03-09). Modified approach: no GPU required, build+config+startup tests sufficient. **Findings:** - Image already built and pushed: `freno/audiobook-gpu-worker:v1.0.0` (sha256:e892...) - Image size: **17.7 GB** — exceeds the "< 10 GB" acceptance criterion (models are ~10 GB alone) - All non-GPU tests pass: startup, deps, entrypoint syntax, compose config **Action:** Added `GPU-TESTING-CHECKLIST.md` documenting the full GPU validation sequence for external host. Flagged image size issue — recommend runtime model mounting vs baking into image. --- ### FRE-245 — Fourth session (user: "other apps show PIN on screen") **Status**: Done — committed `bb99d3d`, Paperclip closed **Finding:** Fire TV IS FireOS 8 with REST API available on port 8080. Port was closed/sleeping during earlier probes — giving false "FireOS 7" diagnosis. `POST https://192.168.50.31:8080/v1/FireTV/pin/display` → `200 {"description":"OK"}` confirmed. **Root cause of probe failure:** - Device was in deep sleep → TCP port 8080 not accepting connections - Probe timeout was only 1500ms — TLS+HTTP/2 handshake exceeds this on a waking device **Fixes:** - `_wakeViaDIAL()`: POST to `/apps/FireTVRemote` on DIAL port 8009 before REST probe (best-effort wake) - `_probeFireOS8()` timeout: 1500ms → 5000ms - PIN display body: added `friendlyName: 'TV Remote'` so TV shows labelled prompt