screentinker/docs/134-device-reporting.md
screentinker 1f2e923005
fix(#134): quiet false "reconnect" log + report HDMI output and UI render resolution (#136)
Two device-REPORTING fixes from the #134 investigation (the PiP rendering itself
was #135).

1) "Device reconnects every ~45s" was a logging artifact, not instability. The
   player re-emits a full device:register on the SAME socket every ~45-60s
   (requestPlaylistRefresh) to pull a fresh playlist; the server logged
   "Device reconnected" for every register of a known device. The attached 4-day
   log showed 1415 "reconnected" vs 30 real socket connects and 0 heartbeat
   timeouts — the socket never dropped, so #134's "PiP lost between reconnects"
   was a misdiagnosis. Fix: only log a genuine reconnect (new socket); a
   same-socket re-register is a refresh (currentDeviceId === device_id) and stays
   quiet. The playlist still refreshes.

2) Device reported 720p while the monitor showed a 1080 signal. DeviceInfo
   reported getRealMetrics() — the UI RENDER SURFACE — but TV boxes render the UI
   at 720p and upscale to a 1080p HDMI signal. Now report BOTH: screen_width/height
   = the output mode (Display.Mode.physicalWidth/Height), render_width/height =
   the render surface (getRealMetrics). Two new nullable devices columns, stored on
   pairing INSERT + reconnect UPDATE, exposed via the device API, shown on the
   dashboard as "1920x1080 (UI 1280x720)" when they differ.

Backward compatible (required + verified on emulator): a device that omits
render_* — or sends no device_info at all — still registers, with render_* = null,
on both the INSERT and UPDATE paths. New columns nullable; stores use
`?? null` / `|| null`. All 167 server tests pass.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 15:32:11 -05:00

81 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# #134 follow-ups — device reporting fixes
Two issues surfaced while investigating #134 (the PiP bug report). Neither is the
PiP rendering itself (that was #135) — both are device *reporting* problems.
## 1. "Device reconnects every ~45s" — a logging artifact, not instability
**Symptom (#134):** the server log shows the device reconnecting every ~45s, read
as an unstable WebSocket that could drop PiP commands.
**Reality:** the connection is stable. The player calls `requestPlaylistRefresh()`
every ~4560s, which **re-emits a full `device:register` on the *same* socket** to
pull a fresh playlist. The server's register handler logged `Device reconnected`
for *every* register of a known device, so a healthy device that re-registers
~2000×/day looked like it was flapping.
Evidence from the attached 4-day log:
| Signal | Count |
|--------|-------|
| `Device reconnected` | **1415** |
| `Device socket connected` (real) | 30 |
| `Device disconnected` (real) | 21 |
| `marked offline (heartbeat timeout)` | **0** |
1415 "reconnects" vs 30 real socket connects, and the socket **never** timed out.
So #134's "PiP lost between reconnects / queue TTL-expired" was a misdiagnosis —
the socket doesn't drop; the PiP failure was the rendering bugs fixed in #135.
**Fix** (`ws/deviceSocket.js`): a re-register on the *same* socket
(`currentDeviceId === device_id`) is a playlist refresh, not a reconnect — only
log `Device reconnected` for a genuinely new socket. The refresh still resends the
playlist; it just no longer spams the log / reads as instability.
Verified on the emulator: a periodic refresh was processed (device received a new
playlist) while the server's `Device reconnected` count stayed flat; two genuine
reconnects logged exactly twice.
> Follow-up (not done here): the full re-register every ~45s is heavier than it
> needs to be (re-runs fingerprint/token/eviction + resends the playlist). A
> lightweight `device:request-playlist` event would cut that churn. Left as a
> separate optimization.
## 2. Reports 720p while the monitor shows a 1080 signal
**Symptom (#134-adjacent):** a panel receiving a real 1080p HDMI signal was
reported as 720p.
**Cause:** `DeviceInfo` reported `getRealMetrics()` — the **UI render surface**.
Many Android TV boxes/sticks (YaOS, Fire TV, etc.) render the UI at 1280×720 and
let the hardware scaler upscale to a 1920×1080 (or 4K) HDMI signal. `getRealMetrics`
honestly reports the 720p render surface; the monitor sees the 1080p output mode.
They are two different numbers.
**Fix:** report **both**, so neither is lost:
- `screen_width` / `screen_height` = the **HDMI/panel output** mode, from
`Display.getMode().getPhysicalWidth()/getPhysicalHeight()` (orientation-independent;
the panel doesn't rotate when the stage is software-rotated). This is the headline
resolution and now reads 1080 on those boxes.
- `render_width` / `render_height` = the **UI render surface**, from `getRealMetrics()`.
Wiring: Android `DeviceInfo.getDeviceInfo()` → two new nullable `devices` columns
(`render_width`, `render_height`, migration) → stored in both the pairing INSERT and
the reconnect UPDATE → exposed via the device API (`SELECT d.*`) → the dashboard
device detail shows `1920x1080 (UI 1280x720)` when they differ.
### Backward compatibility
Required and verified: a device that doesn't report the new fields must still be
accepted.
- New columns are **nullable**; the store uses `device_info.render_width ?? null`
(reconnect) and `device_info?.render_width || null` (pairing); `device_info`
itself remains optional.
- Verified on the emulator: an old-style register with `screen_*` but no `render_*`,
and a register with **no `device_info` at all**, both succeed with `render_*` =
null — on both the INSERT (pairing) and UPDATE (reconnect) paths.
- The dashboard only appends `(UI …)` when `render_*` is present and differs, so
legacy devices render as before.