The coordinator now fires a user notification when the Bosch cloud
transitions between healthy and unreachable — the missing piece that
made the first 503 burst today silent until the user noticed cameras
were unavailable.
- Outage path: requires ≥60 s of continuous failure before announcing,
so a single transient blip does not spam. Fires exactly once.
- Recovery path: fires immediately on the first successful tick after
an announced outage.
- Suppressed while an RSS-announced maintenance window is `active` so
users don't get duplicate alerts about a planned event. Internal
state still tracked so the next genuine outage starts fresh.
- Routes via `alert_notify_system` → falls back to
`alert_notify_service`, same delivery path as TROUBLE_DISCONNECT and
the maintenance lifecycle notifier (v12.4.8).
- Notify-service failure is swallowed and the state is still recorded
so a misconfigured service does not retry-storm every tick.
Plus three hot-follow-ups rolled into the same tag:
- docs: Mermaid sequence diagrams for LAN-fallback + cloud-up/down (HA),
RSS-flow + LAN-fallback (ioBroker), --local + bosch ping (Python CLI),
prefer_local routing + lan_ping (MCP)
- test: coverage uplift for LAN binary-sensor + Gen2 light fallback;
fixes a latent bug where the light-set-component returned False
immediately on missing token instead of trying the LAN-RCP fallback
- fix: cloud-state alerts notify.<svc> prefix splitting + switch.available
cold-start during cloud-down (LAN-fallback was blocked by an empty
_shc_state_cache after HA restart inside an outage)
**Test coverage: 100.00 % (11432/11432 statements)**, 4162 tests
passing, 0 failures, 0 skips. The 8 statements that don't fit a unit
test (manifest-read fallback at module load + 6 socket close()-
exception arms inside daemon-thread bodies whose tracer drops their
records) are marked `# pragma: no cover` — genuinely defensive arms
with no observable behaviour beyond "doesn't crash".
firebase-messaging + smbprotocol added to requirements_test.txt so
the FCM-listener + SMB-backend tests actually exercise their code in
CI (previously silent-skipped via pytest.importorskip).