CAP Alerts: an event-driven approach to severe weather alerts (v0.3.1)

Hi all :waving_hand:

I’ve been working on a custom integration and companion Lovelace card that take a different approach to severe weather alerts, and I’d love feedback from people running HA in regions with active weather.

Status, August 2026: cap_alerts is at v0.2.0, released 30 July. The headline changes are real-time ECCC ingestion and a fix for live alerts going missing from Canada’s sanctioned NAAD host. Full notes in this reply.

The problem

If you use Home Assistant for severe weather notifications (NWS, MeteoAlarm, ECCC), you’ve probably run into the same structural limits that motivated this effort. The maintainers of the existing integrations do excellent work, but they’re all fighting the same constraints in the HA state machine:

  • The 16 KB attribute limit. The recorder won’t store a state attribute payload larger than ~16 KB. When a big storm rolls in with detailed multipolygon geometry and long-form text, that data can get dropped before it’s persisted.
  • The single-sensor bottleneck. Packing several concurrent alerts into one sensor’s attributes means a complex regional outbreak has to share one payload, which compounds the size problem above and makes per-alert automations awkward.
  • Lifecycle continuity. When a provider upgrades a watch to a warning (or cancels it), it’s hard to track that as one evolving event without churning state history or spawning duplicate entities.

The experiment: cap_alerts

Instead of cramming everything into sensor attributes, cap_alerts uses an event-driven model that creates exactly one entity per active alert, spawned and removed as alerts come and go. Alert data is modeled on CAP (Common Alerting Protocol) field names, so the same shape works across providers.

The goal here is NOT to replace the existing integrations. I’m aiming to field-test an architectural pattern and see whether it holds up across providers and regions. If it does, I’d like to write it up as a proposal that could make life easier for all weather integration maintainers.

Here’s what that looks like in practice; several concurrent placeholder alerts in a single card:

weather_alerts_card showing multiple concurrent severe weather alerts with severity and event-driven theming

Two color themes side by side (left to right: normalized severity tiers, NWS event colors) in the default and compact layouts.

How it works

  • Externalized payloads. Heavy data like GeoJSON polygons and long descriptions is held in a bounded in-memory cache and served over a lightweight HTTP endpoint the integration registers, rather than stuffed into state attributes. This sidesteps the 16 KB limit and avoids hammering the recorder (and your SD card) with large writes.
  • Lifecycle-aware identity. Each alert gets a stable ID hashed from its event identity rather than the message URL (for NWS that’s the VTEC event tuple, for ECCC a language-independent key). That keeps one entity (and its state history) intact across the New → Update → Cancelled/Expired lifecycle, instead of churning a new entity on every revision.
  • Normalized severity. Severity is mapped to canonical CAP tiers (extreme, severe, moderate, minor) across providers, so a single automation can react to “anything severe or above” regardless of source.
  • Real-time ingestion where the provider offers it. ECCC alerts arrive over NAAD’s TCP streaming feed as they’re issued, rather than on a poll cycle. Polling is retained as reconnect backfill and a periodic safety resync.

Ships with four providers today: NWS (US), ECCC (Environment Canada), MeteoAlarm (EUMETNET / Europe), and WMO severe-weather feeds.

The companion card

I also maintain weather_alerts_card, which consumes this entity model natively: severity-driven theming and badges for phase transitions.

It now (as of v3.1.0) even puts those externalized payloads to work! Because the full GeoJSON is served from the integration’s cache instead of being truncated at the attribute limit, the card can draw the complete affected-area polygon over a map basemap in the alert’s detail panel:

A CAP alert's affected-area polygon rendered over a map basemap in the card's detail panel

The affected area over a CARTO basemap; adaptive svg above, change your theme to see the inverse light/dark example.

Looking for testers and feedback

Before I write up anything formal, I want to make sure the model survives contact with reality. I can’t honestly evaluate every condition and region with the attention they deserve; frankly, I need help testing this approach.

Install: add https://github.com/seevee/cap_alerts to HACS as a custom repository (category Integration), then Add Integration → CAP Alerts.

Links:

If you’re somewhere with active weather, I’d love for you to run this alongside your current setup and compare. Feedback on edge cases, dropped or duplicated alerts, and UI quirks would all be hugely appreciated. Thanks!

2 Likes

Just stumbled upon this because I've been trying to get things working for multiple alerts, thanks so much! Installing now to test!

I don't know what any of this entails, but any chance of getting Pirate Weather added as it's my provider of choice?

1 Like

Seemed to be working the other day, but got another alert tonight and it's not showing any alerts, even though there's an ongoing one.

1 Like

You're most welcome, thanks for testing it out!

Quick clarification: Pirate Weather support lives in the card, rather than cap_alerts (which doesn't have a PW provider). I probably won't add one, either. In the US, PW's alerts come straight from NWS, which cap_alerts already ingests directly (same with WMO for global alerts), but PW flattens them, dropping the VTEC, zone, and lifecycle details present in the raw feed. Pointing cap_alerts at NWS gives you the highest-fidelity alert data.

As for the alert that didn't show, that's separate. How did you set up cap_alerts: NWS - by zone, GPS coordinates or device tracker? If by zone, mind sharing the code? And does the missing alert appear at https://api.weather.gov/alerts/active?point=YOUR_LAT,YOUR_LON ?

Also - feel free to open a new issue at github for this; I'm more likely to be watching that for updates when I'm actively fixing bugs.

Understood - thanks for the clarification!

I set it up by zone, NJZ008. I don't have answers to the other questions as it was a one-off thing I think, but I'll keep an eye on it should it happen again.

Thanks again for all the hard work!

1 Like

cap_alerts v0.2.0 is out

v0.2.0 shipped on 30 July, the first feature release since 0.1.1. It’s also the first one I promoted on the strength of reports from other people’s installs rather than my own testing.

:link: Release v0.2.0 · seevee/cap_alerts · GitHub

ECCC alerts now arrive in real time

Canadian alerts are ingested over NAAD’s TCP streaming feed the moment they’re issued, instead of waiting up to five minutes for the next poll. The NAADS user guide names streaming as the intended channel for automated systems running around the clock.

It’s on by default for ECCC entries. GeoRSS polling is kept as startup and reconnect backfill, plus a 30-minute safety resync, with a heartbeat watchdog and exponential backoff. Reconnect backfills are floored at the old poll cadence, so a flapping socket can’t generate more traffic than plain polling did. A new diagnostic entity, binary_sensor.cap_alerts_eccc_real_time_stream, reports whether the socket is up. If you’d rather not stream, turning it off falls back to the old behaviour.

The sanctioned Canadian feed is dropping live alerts

While migrating ECCC to alertready.ca ahead of the legacy endpoint’s retirement, live soaking showed that host persistently omitting in-effect Actual/Public alerts that were present on the legacy Pelmorex host at the same moment. One of them was at severity=Extreme.

This isn’t propagation lag. Individual alerts stayed missing across hours of consecutive samples, and the set of missing alerts changed from one minute to the next.

I’ve had a sampler running since the release, and every sample so far has shown a gap. Dozens of distinct alerts are absent, mostly severe thunderstorm and heat or air quality warnings. Some of the missing ones are alerts that have ended, so affected users would miss the all-clear as well as the alert itself. It’s reported upstream and acknowledged, with no fix yet.

The mitigation is in 0.2.0. The default auto feed source now fetches both hosts and unions their entries by CAP OID, so neither host’s gaps can hide an alert from you. If you’re in Canada and running this, you’re already covered, and there’s nothing you need to do.

Also in this release

  • Ended alerts no longer linger. ECCC publishes one <info> block per area group, each with its own status. The provider was reading the wrong block and never saw the termination signal, so alerts hung around until their expiry time. Entities now clear when the alert actually ends.
  • Truncated feed downloads no longer lose a poll’s alerts. The ~7 MB chunked feed arrives without a Content-Length, so an early-terminated stream produced a partial body that failed to parse at a random offset. Incomplete documents are now detected and retried.
  • Device-tracker location for every provider. ECCC, MeteoAlarm and WMO gained the GPS-tracker mode NWS already had. MeteoAlarm also gets a fully mobile mode: a tracker for polygon narrowing plus a country-source entity, so crossing a border re-resolves which national feed to query.
  • MeteoFrance entity churn is fixed. It embeds the issue timestamp in its CAP identifier, so every re-issue of a warning was minting a new entity and orphaning a stale one.
  • A refresh button. button.cap_alerts_<provider>_refresh, diagnostic, forces an off-cycle fetch on any provider.
  • Opt-in marine filter and typed multi-scheme geocodes (geocodes["EMMA_ID"], ["NUTS3"], ["WARNCELLID"] and so on) for feeds that mix coding schemes across countries.

:warning: One breaking change

MeteoAlarm area codes have moved. EMMA_ID values were previously written into geocode_same, an NWS-scheme field they never belonged in. They now live under geocodes["EMMA_ID"]. If you have templates or card config reading geocode_same on MeteoAlarm entities, they need updating. NWS and ECCC entities are unaffected.

Otherwise no reconfiguration is required. Existing entries pick up streaming, the auto feed source and the fixes on restart, and the marine filter stays off unless you turn it on. New options are under Settings → Devices & Services → CAP Alerts → Configure.

@OvalZyre

Your missing NWS alert back in June never got a resolution, and I want to be straight that nothing in this release specifically targets an NWS-side gap. The coverage problem above is Canadian, and I have no evidence of an equivalent one on the NWS feed.

Two things should make the next occurrence easier to pin down, though. The refresh button lets you force an off-cycle fetch as soon as you notice something missing. If the alert shows up right after a manual refresh, that’s a very different bug from one where it never appears at all, and knowing which would tell me where to look. The truncated-download fix is also a plausible candidate for your symptom, since it produced polls that silently returned nothing usable, though I can’t confirm that’s what you hit.

Still looking for testers

The ask is the same as the original post, but I can be more specific about where it would help most. Canada is the obvious one, since the streaming work and the union fix were built for that specific case. Europe is the other, because the next release is a MeteoAlarm-focused pass and this is the point where your edge cases can still shape it. And if the WMO feed is your only coverage, that path gets the least live exercise of the four (though will also be getting some attention in the next release).

Bugs are better on GitHub than in the thread, since I watch issues much more reliably when I’m actively fixing things:

Thanks to everyone who’s been running this. The reports coming back are what made the 0.2.0 promotion decision possible.

Thanks so much. I don’t believe I’ve had the issue since, and recently I’ve had multiple (upwards of 3 or 4) alerts at once and seemingly haven’t had anything missing. I’ll still keep an eye out, but all seems well.

1 Like

cap_alerts v0.3.1 is out

0.3.0 shipped on 5 August, and 0.3.1 followed within the half hour with an icon fix I didn’t want to sit on, so 0.3.1 is the one to install. This is the MeteoAlarm pass I promised at the end of the 0.2.0 post, and it was shaped almost entirely by reports from people on the tracker.

:link: Release v0.3.1 · seevee/cap_alerts · GitHub

A multi-day French warning is one entity now

MeteoFrance publishes one bulletin per calendar day. A four-day heat episode therefore arrived as four entities, each one minted at midnight and orphaning the one before it, so anything pointing at that sensor.cap_alert_* broke every night. Consecutive days now merge into a single episode keyed without the day component, and the ID survives midnight from here on.

In region-picker mode the bulletin is also exploded per department, so area_desc narrows from the bulletin’s full list (16 to 83 departments, depending on the day) to the one department your entity actually covers.

@Cyberdr3am reported this in July and then soaked the fix against a live episode to confirm it. There’s a breaking change attached to it, at the bottom of this post.

Icons no longer depend on which language you read alerts in

Per-alert icons were classified by matching keywords against the alert’s event text, which is free text in whatever language the feed publishes. Tuulivaroitus maa-alueille matched nothing, so a Finnish reader got mdi:alert on every warning. My first attempt at this read the alert’s English sibling block instead, which doesn’t work either: on FMI’s fi/sv/en documents the block you get is Swedish, and some alerts carry no English block at all.

MeteoAlarm alerts are now classified on awareness_type, the EUMETNET hazard code. It’s language-independent and required on every alert, so the icon is the same whether you read the alert in Finnish, Greek or English. The code table is pinned to the MeteoAlarm CAP Profile v2.0 §2.2.17 rather than to the third-party lists floating around, which disagree with the wire on where flooding sits.

Measured across 1705 live alerts in DE/ES/FI/NO/SE under a Finnish locale, fallback icons went from 29 to zero. Two corrections nobody had reported fell out of the same sweep: Swedish “Near gale” alerts carrying 7; coastalevent were drawing the wind icon instead of waves, and a Norwegian “Heavy rainshowers” carrying 13; rain-flood was drawing plain rain. Both are cases where the text matched and was still wrong — the argument for letting the code take precedence rather than just fill gaps.

Thanks to @vhtkrk, whose raw attribute dump is what showed the English-sibling approach was insufficient, and to @MF-142 for the original report on Chinese alerts.

Language selection reaches your language

There are two separate language axes here and 0.3.x fixed both. The UI now ships a Simplified Chinese translation for the config and options flows. Separately, alert content is now selected by matching your Home Assistant locale against each CAP <info> block’s declared language, instead of taking whichever block came first in the document.

Two matching bugs came out of that work. Norwegian alerts always fell back to English because MeteoAlarm tags them no while HA’s locales are nb and nn, and bare primary subtags like de weren’t matching de-DE. Both are fixed, and the fallback chain is now exact tag, then primary subtag, then English, then first block.

Region picker fixes

Three of them, all MeteoAlarm. An area that publishes several geocodes only offered the first one, so some regions were unselectable. Feeds that repeat their region tree once per language offered you every region two or three times over. And the picker was combining distinct Finnish warning regions into one entry, reported by @vhtkrk back in July.

An escape hatch for feeds with no geometry

Some WMO sources publish no per-alert polygons at all, which leaves country-wide as the only usable location mode. For China’s CMA feed that means one entry producing a couple hundred entities, enough to visibly destabilize the frontend while it populates.

There’s now an opt-in Area codes (prefix match) option under Configure, on every provider. Give it comma-separated prefixes and an alert is kept when any area code it publishes starts with one of them. Codes are hierarchical, so 13 is Hebei and 1307 is Zhangjiakou; on that Chinese feed it takes a country-wide entry from roughly 234 alerts to 28. Prefer the leading digits over pasting a full code, since code lengths vary within a scheme. Empty by default, so nothing changes unless you set it.

Also in this release

  • Point and circle geometry. CAP <circle> elements are parsed, and a zero-radius circle is published as a point. That’s how CAP-AU feeds like NSW RFS mark an incident location.
  • Polygons that validate. The GeoJSON coming out of the geometry endpoint was emitting unclosed linear rings, which violates RFC 7946 and can upset strict consumers. Rings are closed now, and there’s a live conformance check running against real feeds to keep it that way.
  • High-volume sources fit inside the poll timeout. Feeds that require fetching hundreds of CAP bodies were exceeding the timeout and returning nothing for the cycle.
  • Green MeteoFrance markers stay out. MeteoFrance publishes no-warning markers for quiet departments; those were becoming entities.
  • Fewer needless reloads. The options update listener now decides when a reload is actually required, instead of tearing the entry down on every save.

Under the hood, the provider-specific rules that had accumulated across the model, the normalizer and the individual providers now live in one declarative table. No behavior change, but it’s why the FMI work in the next release was a table entry rather than a second copy of the France logic.

:warning: One breaking change

MeteoFrance alert entity IDs are recreated once on upgrade. That’s the cost of the episode fix above: the new IDs no longer carry a day component, so they can’t match the old ones. Dashboard cards and automations referencing a sensor.cap_alert_* entity for a French warning need repointing, and the old entities hang around as unavailable until you remove them. It’s a one-time break — and midnight stops breaking them after this.

No other provider is affected, and nothing to do on NWS, ECCC, WMO or any non-French MeteoAlarm service.

What’s next

0.4.0 is in alpha now. It carries the same midnight merge for Finland (FMI splits on window edges rather than calendar days, so it needed its own rule), an active/upcoming split on the count sensor, and an answer to @jackwarden-eng’s question about telling an early cancellation apart from a plain expiry: the removal event now says why an alert went away instead of just that it did. Same upgrade note applies as above for Finnish alerts.

Still looking for testers

Europe is where the help would go furthest right now. This release was shaped by France and Finland because that’s who reported, and MeteoAlarm has around 37 member services with real differences between them. If you’re on a national feed nobody has exercised yet, running this alongside your current setup and telling me what looks wrong is exactly the thing that turns into a fix. The WMO path still gets the least live exercise of the four providers.

Bugs are better on GitHub than in the thread, since I watch issues much more reliably when I’m actively fixing things: Issues · seevee/cap_alerts

Thanks again to everyone who reported something this cycle. Nearly every line above started as somebody else’s bug report.