v10.5.4 — Stream switch unblocked + AUTO-mode self-heals
Stream switch unblocked when prior session expired upstream
HA Stream.stop() could block waiting for a stuck FFmpeg reconnect-loop to exit when the underlying URL was already invalid (e.g. relay-side lifetime cap reached). Both teardown paths now wrap the call in asyncio.wait_for(timeout=5) and force-detach on timeout. Without this fix, a single hung stream.stop() held the per-camera setup lock for >5 minutes and every subsequent switch-ON returned try_live_connection: already in progress for ... — skipping.
REMOTE session lifetime watchdog
Mirror of the existing LOCAL keepalive task: when a stream opens against the cloud relay, a generation-tracked terminator is scheduled and tears the session down cleanly before the relay drops the RTSP TCP with a hard reset. Without this, the URL goes stale silently — switch shows "streaming" but FFmpeg is in a reconnect-loop, the next consumer sees a 2-minute HLS spinner. OFF→ON cycles cancel the watchdog automatically.
AUTO-mode REMOTE fallback self-heals
Four independent fixes reduce the "permanently pinned to Cloud" failure mode that occurred after a transient LAN issue saturated the LOCAL error counter:
record_stream_errorskips the increment when the active connection is REMOTE — Cloud-side hiccups no longer count against the LAN's health budget.- The error counter time-decays in
_try_live_connection_inner's AUTO branch: 5 minutes if the camera's TCP-ping cache says LAN is currently reachable, 30 minutes otherwise. - The status-loop's TCP-ping fast-path actively clears the fallback flag the moment LAN becomes reachable again — the next stream-on attempts LOCAL first.
- During a currently running REMOTE-fallback stream, the same trigger additionally schedules a
try_live_connection(is_renewal=True)so the live HLS session migrates Cloud → LAN viaStream.update_source()without waiting for a re-toggle. ~2-3s re-buffer during the swap. 5-minute cooldown prevents ping-pong.
max_stream_errors raised — per-model thresholds
With self-heal in place a false fallback now recovers automatically, so the gradual-counter path can give LOCAL a fairer chance before giving up. Default bumped from 3 → 5 (indoor / INDOOR, HOME_Eyes_Indoor, default unknown), explicit override 10 for outdoor models (OUTDOOR / CAMERA_EYES, HOME_Eyes_Outdoor / CAMERA_OUTDOOR_GEN2) where real WLAN flap + slower encoder init produce more transient bursts. The watchdog's hard 120 s "no healthy HLS output" escalation is unchanged.
Documentation
New AUTO-mode connection state-machine diagram in README.md showing LOCAL ↔ REMOTE_fallback transitions, counter decay, active promotion, and lifetime-watchdog teardown.
Full changelog: CHANGELOG.md