The /monitor WS proxy cancelled its sibling task on disconnect but then
`except Exception` failed to swallow the resulting CancelledError (a
BaseException), so stopping the stream raised "Exception in ASGI
application". It also only awaited the pending task, leaving the done
task's WebSocketDisconnect unretrieved ("Task exception was never
retrieved"). Await all tasks and catch (CancelledError, Exception).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The SLM live view now consumes SLMM's shared DOD /monitor feed instead of
the per-client DRD /stream. This fixes the single-connection contention
(many viewers share one device feed) and finally puts L1/L10 in the live
chart (DRD couldn't carry percentiles).
- New WS proxy handler /api/slmm/{unit}/monitor -> SLMM /api/nl43/{unit}/monitor.
Uses asyncio.wait(FIRST_COMPLETED) + cancel-sibling instead of gather(), so
it doesn't leave a task sending into a closed socket ("Unexpected ASGI
message after close").
- Live view JS points at /monitor; onmessage reflects feed_status and ignores
heartbeat / unreachable frames so they don't blank the cards or zero-spike
the chart. Adds a small Live/Device-offline badge.
Still on the old /live (DRD): the dashboard live tile (sound_level_meters.html)
— next slice.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>