Add tsunami alerts as a new global domain - #30
Draft
turbo5000c wants to merge 4 commits into
Draft
Conversation
Tsunami existed in this integration as one line: three strings inside SevereWeatherAlertBinarySensor._SEVERE_EVENTS, lumped in with tornadoes and thunderstorms. A user could not tell a tsunami warning from a hailstorm, and got none of the data a tsunami alert actually carries. This adds a NOAA Tsunami device (global, shared across config entries, on the same ownership-transfer pattern as NOAA Hurricane) with six entities everyone gets and three more for the 26 coastal offices in OFFICE_TSUNAMI_CENTERS. The ten Great Lakes offices get no location-specific entities rather than permanently-dead ones. Data comes from api.weather.gov/alerts/active filtered on the VTEC-derived codes TSW/TSA/TSY, plus the NTWC and PTWC Atom and CAP 1.2 feeds at tsunami.gov for the source earthquake and per-location wave arrival times. CAP and Atom are parsed with the standard library, so no new dependency. Three choices are driven by this being life-safety data: - Threat level is None (Home Assistant "unknown") when no fetch has succeeded and the string "None" only when a fetch genuinely found nothing. Collapsing the two would let a dead feed read as an all-clear. - binary_sensor.noaa_tsunami_data_stale turns on when the feed stops answering, because a silent failure is the failure mode that hurts people. - The NWS query polls every 2 minutes; a near-field tsunami can reach the coast in under fifteen. The warning-center feeds are only fetched when an alert is active or roughly half-hourly, so a quiet install is not requesting unchanged bytes from tsunami.gov 3,600 times a day. Monthly NWS communications tests never move the threat level or trip the alert sensor, but do populate a last_test_message attribute — on a normal install that is the only traffic this domain will ever see, and it is how a user confirms the pipeline still works between real events. The tsunami strings in _SEVERE_EVENTS are deliberately left in place so existing automations keep firing, with a regression test to enforce it. XML is size-capped and DOCTYPE-refused before parsing, since xml.etree.ElementTree is not hardened against entity expansion and defusedxml is not a dependency. 141 new tests; 821 pass, flake8 clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0154K7X9gXPjnvkaYrQVQc4a
Adds image.noaa_tsunami_map to the NOAA Tsunami device, on the existing image.py ImageEntity pattern with the same added-once ownership transfer the hurricane images use. The map switches source based on what is actually happening. During an event it serves the energy propagation forecast from the warning center that issued the alert — the RIFT model's directional beam of wave energy across the ocean, which is the map people picture. The centers only generate one during a real event, so the rest of the time it serves the NDBC DART buoy network map instead. An empty panel during an event reads as "nothing is happening", which is the same failure mode the data-stale sensor exists to prevent. Sources are tried in order and a non-200 or non-image response falls through to the next rather than raising, so a wrong URL degrades to the fallback instead of a broken tile. The image URLs could not be verified against live NOAA — the development sandbox blocks every NOAA host — so they are isolated in const.py behind a comment saying so. Correcting one is a single-line change and no logic moves. Center attribution reads the Atom products first and falls back to the alert sender name. That fallback needed a spelled-out hint table: alerts identify the sender as "NWS National Tsunami Warning Center", so matching on the literal "NTWC" never fired. Caught by a test before it shipped. 20 new tests; 841 pass, flake8 clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0154K7X9gXPjnvkaYrQVQc4a
A live install showed the NOAA Tsunami device with Last Message carrying a real timestamp — so the tsunami.gov Atom feed was being fetched and parsed correctly — while Source Earthquake sat on Unknown. Two separate bugs. The magnitude pattern was anchored on a bare "M", so against "preliminary magnitude 6.2" it matched the leading letter of "magnitude" and then failed on the letters after it. That spelled-out form is how the centers usually write it, so the common case never matched while the fixture's "M 7.8" did. Several wordings are now tried in order, spelled-out first, with a 0-10 sanity bound so a message number cannot be read as an earthquake. Only products[0] was inspected. The newest product is frequently a routine statement carrying no quake parameters at all, which blanked the sensor while the answer sat one entry further down. find_source_earthquake now scans back through recent products and falls back to the newest entry's region and link, so the sensor is never a dead end. Both of these mattered more than they look: the centers publish a statement for every notable earthquake and most come to nothing, so this sensor is what the domain has to say on a quiet day. Reporting the last quake the centers looked at and dismissed is the difference between an interesting panel and a column of Unknowns. Also: - Wave Arrival said "unknown" whenever no event was in progress, which reads like a fault. It now says "No active event" when the feed is healthy and reserves unknown for "nothing fetched" — the same distinction the threat level already makes. - The quiet-day map is now a list of candidate URLs tried in turn rather than one unverified guess, and the URL that works is logged at info level so it can be identified from a real install's log and the list trimmed. 858 pass, flake8 clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0154K7X9gXPjnvkaYrQVQc4a
The map never rendered. Six guessed URLs across two revisions — RIFT energy propagation forecasts and NDBC DART network maps — and every one 404'd on a live install. Web search could not surface the real ones either, and the tsunami.gov homepage map appears to be an interactive viewer with no single image behind it. The user supplied a URL that actually exists: https://www.tsunami.gov/previous.events/08-29-2018_LoyaltyIslands/Images/Location.jpg The warning centers archive every past tsunami under previous.events/ with a location map at a predictable path, and list them at /recent_tsunamis/. So the map is now the location map of the most recent tsunami in that archive, and every guessed URL is deleted rather than kept as a fallback — an unverifiable fallback only makes the real failure harder to diagnose. Adds sensor.noaa_tsunami_latest_event alongside it. This is the one entity in the domain with something to say every single day: the last tsunami that actually happened anywhere in the world. It changes only when the centers add an event, which makes it a usable automation trigger in a way the alert sensors — correctly silent for years at a time — are not. parse_recent_tsunami_events reads the listing with a regex over the HTML, the same approach ForecastDiscussionCoordinator already uses for AFD text. Only dated event directories match, so a layout change degrades to an empty list and a logged warning rather than to nonsense. The listing rides the slow half-hourly cadence, not the 2-minute alert poll. Also removes TSUNAMI_CENTER_SENDER_HINTS, which only existed to attribute an energy map to a center and is dead now. 870 pass, flake8 clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0154K7X9gXPjnvkaYrQVQc4a
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Tsunami existed in this integration as one line — three strings in
binary_sensor.py:154, insideSevereWeatherAlertBinarySensor._SEVERE_EVENTS, lumped in with tornadoes and thunderstorms. A user could not tell a tsunami warning from a hailstorm, and got none of the data a tsunami alert actually carries: wave arrival time, source earthquake, evacuation instruction, or which warning center issued it. The fourth alert level — Information Statement — was missing entirely.This adds a
NOAA Tsunamidevice: global, created once, shared across config entries, on the same ownership-transfer pattern asNOAA Hurricane.Seven global entities, every install:
sensor.noaa_tsunami_threat_levelWarning/Advisory/Watch/Information/Nonesensor.noaa_tsunami_active_alertssensor.noaa_tsunami_source_earthquakesensor.noaa_tsunami_last_messagebinary_sensor.noaa_tsunami_alert_activesafety)binary_sensor.noaa_tsunami_data_staleproblem)image.noaa_tsunami_mapThree more for the 26 coastal offices in the new
OFFICE_TSUNAMI_CENTERStable — local threat, wave arrival, evacuation status. The ten Great Lakes offices get none of these rather than permanently-dead entities.Data sources:
api.weather.gov/alerts/activefiltered on the VTEC-derived codesTSW/TSA/TSY(more stable than matching event-name strings), plus the NTWC and PTWC Atom and CAP 1.2 feeds at tsunami.gov. Parsed with the standard library — no new dependency.The local threat sensor reads the existing
NWSAlertsCoordinator, which already queriesalerts/active?point=lat,lon— so it costs zero additional requests.The map
image.noaa_tsunami_mapswitches source based on what is actually happening. During an event it serves the energy propagation forecast from the warning center that issued the alert — the RIFT model's directional beam of wave energy across the ocean, which is the map people picture when they think "tsunami map". The centers only generate one during a real event, so the rest of the time it serves the NDBC DART buoy network map instead. An empty panel during an event reads as "nothing is happening", which is the same failure mode the data-stale sensor exists to prevent.Sources are tried in order; a non-200 or non-image response falls through to the next rather than raising.
map_type,source_urlandactive_centerattributes report which one is on screen.Three choices driven by this being life-safety data
unknown≠ "None". Threat level is PythonNone(HAunknown) when no fetch has succeeded, and the string"None"only when a fetch genuinely found nothing. Collapsing the two would let a dead feed read as an all-clear. Two tests enforce it.binary_sensor.noaa_tsunami_data_staleexists because a silent failure on life-safety data is the failure mode that actually hurts people.Testability: these sensors will read
Nonefor years, which makes the feature hard to trust. Monthly NWS communications tests never move the threat level or trip the alert sensor, but do populate alast_test_messageattribute. If that is updating, the pipeline works.Back-compat: the tsunami strings in
_SEVERE_EVENTSare deliberately left in place, with a regression test. Existing automations keep firing.Security: XML is size-capped at 512 KB and refused outright if it carries a
DOCTYPE, sincexml.etree.ElementTreeis not hardened against entity expansion anddefusedxmlis not a dependency here.Fixed against a live install (commit 3)
A screenshot from a real Home Assistant install running this branch showed
Last Messagecarrying a genuine timestamp — so the tsunami.gov Atom feed is reachable and parsing correctly — whileSource Earthquakesat onUnknown. Two bugs, both of which the test suite was green through:M. Against "preliminary magnitude 6.2" it matched the leading letter of "magnitude" and then failed on the letters after it. The fixture saidM 7.8, which matched fine — so the tests passed while the wording the centers actually use never did. Several forms are now tried in order, spelled-out first, with a 0–10 sanity bound so a message number cannot be read as an earthquake.products[0]was inspected. The newest product is frequently a routine statement with no quake parameters, which blanked the sensor while the answer sat one entry down.find_source_earthquakenow scans back.This also reframed the sensor: the centers publish a statement for every notable earthquake and most come to nothing, so
Source Earthquakereports the last quake the centers evaluated and dismissed, not only ones that caused a tsunami. That is what makes the domain worth looking at on a quiet day.Wave Arrivalalso now readsNo active eventwhen the feed is healthy, rather thanunknown— which on a working feed reads like a fault.Type
Checklist
Verification
python -m pytest tests/ -v→ 858 passed (680 before, +178 new)python -m flake8 --max-line-length=120 custom_components/ tests/→ cleanTwo things worth a reviewer's attention
1. The map URLs are still unverified — and the map is still blank on a live install. The sandbox's egress proxy blocks every NOAA host, so nothing here was written from captured bytes. The Atom feed URLs are now confirmed working by the live install above; the CAP structure and the map image URLs remain guesses.
TSUNAMI_DART_MAP_URLSis deliberately a list of candidates tried in turn, and the URL that succeeds is logged at info level:If that line appears in a real install's log, it names the working URL and the list can be trimmed to it. If it never appears, all four candidates are wrong and the real address is needed. Still outstanding for someone with network access:
tsunami.gov/events/xml/PAAQCAP.xmlagainstparse_tsunami_cap, replacing the hand-written fixture with real bytesTSUNAMI_ENERGY_MAP_URLSandTSUNAMI_DART_MAP_URLS2. Pre-existing bug left alone:
README.mdhas a duplicated### Dashboard Card Examplesheading. Out of scope for this PR, not touched.