Worth understanding if you are debugging, running many zones, or deciding whether to point the integration at a broker of your own.
One connection, two topic trees
The integration opens exactly one MQTT v5 connection over WebSocket to
wss://mqtt.wxalerts.org/mqtt on port 443: TLS, public, read-only. The
credential wxalerts / wxalerts is published on purpose; the broker’s ACL
denies publish on every topic, so there is nothing to protect.
It subscribes only to what you configured:
wxalerts/nws/v1/same/{same}/# one per distinct county, QoS 1
wxalerts/glm/v1/{g}/{g}/{g}/# one per distinct lightning box, QoS 0
The alert tree is keyed by SAME code and each hazard occupies its own topic, with the VTEC event tracking number as the last level, which is why two simultaneous tornado warnings for one county stay distinct instead of overwriting each other. The tree is documented in full on the MQTT alerts feed page.
The lightning tree is the same one the live map draws from and is documented in full on the MQTT feed page.
The client identifies itself as wxha-<instance>-<entry>, derived from your
Home Assistant instance ID, so two Home Assistants on one network do not fight
over a session.
Retained alerts, and tombstones
Live hazards are retained on the broker. Subscribing replays every one of
them immediately, so a fresh start or a reconnect repopulates every entity in
one burst, with no polling and no window where the sensors say unknown while a
warning is in effect.
A hazard ends by way of a tombstone: an empty retained payload on its topic. The integration drops the alert the moment one arrives.
This matters more than it sounds. Expiring alerts on local clock arithmetic
means a cancelled warning hangs around until its original end time, and a
Home Assistant whose clock has drifted gets it wrong in both directions. The
feed’s status field and its tombstones are trusted over any local
calculation.
Reconnecting
Connection drops are handled with exponential backoff: 2 seconds, doubling to a 5-minute ceiling, reset once a connection has held for two minutes. The backoff is not politeness: every resubscribe replays the entire retained set, so a hot reconnect loop is expensive for both ends.
While disconnected, every entity is unavailable rather than stale. See
Entities and attributes.
Reading the message stream doubles as the liveness check. A held MQTT session with nobody reading the socket does not notice that the other end is gone, which is the classic way an MQTT integration goes quiet for hours without logging anything.
The subscription budget
The broker allows 20 subscriptions per client; the integration spends at most 18 and keeps the rest as headroom. That is roughly nine counties plus nine lightning boxes.
Past that limit, alerts are kept and lightning boxes are dropped from the end, with a warning in the log:
Configuration needs 22 subscriptions but the broker allows 18;
dropping the last 4 lightning subscriptions
Counties and boxes are both deduplicated first, so several zones in one county cost one subscription, and several zones in one lightning box cost one more. Widening the lightning box size is the cheapest way to get back under the cap.
Running against your own broker
If you already bridge the WxAlerts topics into a local Mosquitto or EMQX,
with one upstream connection serving a whole house, the MQTT transport in the
integration is a single swappable object (FeedClient in coordinator.py).
There is no host override in the UI yet. It is the planned path to a
local_push deployment, and the code is arranged for it, but today the broker
address is fixed. Follow
the repo if you want it.
