router-status: replace Cockpit's Van Router plugin with MQTT -> Home Assistant

Cockpit's login was unreliable (Firefox rejected sometimes, Android failed
outright with a TLS handshake error) and it was a resource hog on this Pi.
van-router-mqtt publishes the same AP/client/WAN/Starlink/modem status and
accepts the same control actions (AP/modem restart, WAN connect/disconnect,
prefer) via MQTT with HA discovery, using the local Mosquitto broker already
running here. It republishes van-failover/van-thermal's own state.json files
verbatim rather than modifying either daemon (van-thermal is deliberately
stdlib-only; van-failover's control loop wasn't worth an added dependency).

Adds a Lovelace dashboard (ha/dashboards/van-router.yaml) consuming those
entities. Cockpit's plugin is left deployed for now, not yet retired.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S2tpeaiHUriQbbnQ4SCbkf
This commit is contained in:
Andreas Wrede
2026-09-14 16:28:01 -04:00
co-authored by Claude Sonnet 5
parent 918003d2e3
commit ec5130ea7d
6 changed files with 772 additions and 1 deletions
+139
View File
@@ -0,0 +1,139 @@
title: Van Router
views:
- title: Van Router
path: van-router
icon: mdi:router-wireless
cards:
- type: glance
title: At a glance
entities:
- entity: sensor.van_router_ap_5ghz
name: 5GHz AP
- entity: sensor.van_router_ap_2_4ghz
name: 2.4GHz AP
- entity: sensor.van_router_starlink
name: Starlink
- entity: sensor.van_router_wan_failover
name: Active WAN
- entity: sensor.van_router_connected_clients
name: Clients
- entity: sensor.van_router_thermal
name: Thermal
- type: vertical-stack
cards:
- type: entities
title: 5GHz AP
entities:
- type: attribute
entity: sensor.van_router_ap_5ghz
attribute: ssid
name: SSID
- type: attribute
entity: sensor.van_router_ap_5ghz
attribute: channel
name: Channel
- type: attribute
entity: sensor.van_router_ap_5ghz
attribute: client_count
name: Clients
- entity: button.van_router_restart_5ghz_ap
name: Restart
- type: entities
title: 2.4GHz AP
entities:
- type: attribute
entity: sensor.van_router_ap_2_4ghz
attribute: ssid
name: SSID
- type: attribute
entity: sensor.van_router_ap_2_4ghz
attribute: channel
name: Channel
- type: attribute
entity: sensor.van_router_ap_2_4ghz
attribute: client_count
name: Clients
- entity: button.van_router_restart_2_4ghz_ap
name: Restart
- type: markdown
title: Connected clients
content: |
{%- set rows = state_attr('sensor.van_router_connected_clients', 'rows') or [] -%}
{%- if rows | length == 0 %}
No clients connected.
{%- else %}
| Host | IP | MAC | Connection | Signal | TX rate |
|---|---|---|---|---|---|
{%- for r in rows %}
| {{ (r.host or '—').replace('-', '‑').replace(' ', ' ') }} | {{ r.ip or '—' }} | {{ r.mac }} | {{ r.conn }} | {{ r.sig or '—' }} | {{ (r.tx.split(' ')[:2] | join(' ')).replace('/', '⁠/⁠') if r.tx else '—' }} |
{%- endfor -%}
{%- endif %}
- type: entities
title: WAN failover
entities:
- entity: select.van_router_preferred_wan
name: Preferred WAN
- entity: switch.van_router_wan_wifi
name: Wi-Fi WAN
- entity: switch.van_router_wan_starlink
name: Starlink WAN
- entity: switch.van_router_wan_cellular
name: Cellular WAN
- entity: button.van_router_restart_modem
name: Restart modem
- type: markdown
title: WAN detail
content: |
{%- set wans = state_attr('sensor.van_router_wan_failover', 'wans') or [] -%}
| WAN | Priority | Device | Status | Metric |
|---|---|---|---|---|
{%- for w in wans %}
| {{ '★ ' if w.active else '' }}{{ w.name }} | {{ w.priority }}{{ ' (preferred)' if w.preferred else '' }} | {{ w.device or '—' }} | {{ 'up' if w.up else ('absent' if not w.present else 'down') }} | {{ w.route_metric if w.route_metric is not none else '—' }} |
{%- endfor %}
- type: entities
title: Starlink
entities:
- type: attribute
entity: sensor.van_router_starlink
attribute: latency_ms
name: Latency (PoP)
- type: attribute
entity: sensor.van_router_starlink
attribute: downlink_bps
name: Downlink (bps)
- type: attribute
entity: sensor.van_router_starlink
attribute: uplink_bps
name: Uplink (bps)
- type: attribute
entity: sensor.van_router_starlink
attribute: obstructed_pct
name: Obstructed %
- type: markdown
title: Thermal
content: |
{%- set sensors = state_attr('sensor.van_router_thermal', 'sensors') or {} -%}
| Sensor | Value | Level |
|---|---|---|
{%- for name, s in sensors.items() %}
| {{ name }} | {{ s.temp if s.kind == 'temp' else (s.rpm ~ ' rpm' if s.kind == 'fan' else s.now) }} | {{ s.level }} |
{%- endfor %}
- type: conditional
conditions:
- condition: state
entity: sensor.van_router_deploy_warnings
state_not: "0"
card:
type: markdown
title: Deploy warnings
content: |
{%- for w in state_attr('sensor.van_router_deploy_warnings', 'warnings') or [] %}
- {{ w }}
{%- endfor %}