Skip to main content
Selora Homes Selora Homes
Recipe · v1.1.0 · Released May 28, 2026

Tornado Alert Pack

Selora AI watches for tornado watches and warnings from the National Weather Service, then sounds the indoor siren and announces the shelter zone to move to, on every speaker and on your phone.

When the National Weather Service issues a tornado watch for your area, Selora AI gives the household a calm heads-up. When a warning is issued, it sounds the indoor siren and tells everyone which room to move to.

How it works

Selora AI subscribes to the NWS feed for your county and watches for tornado watch and warning codes specifically. Other weather sources roll severe weather alerts together, which makes it hard to act on tornadoes in particular.

A watch is the quiet tier: an announcement on every speaker you picked, plus a push notification, and no siren. A warning is the loud one: the indoor siren turns on, the shelter zone you configured is announced on every speaker, and the same instruction goes to your phone so anyone out of earshot still gets it. You choose the tone of the spoken warning, from calm through terse, when you install the recipe.

Files Selora AI's pipeline reads to install this recipe. The manifest declares roles, inputs, and integrations; the package files are Jinja-templated YAML applied to Home Assistant.

# Tornado Alert — NWS-driven severe weather response recipe.
#
# Subscribes to the US National Weather Service feed for the
# homeowner's METAR station. On a tornado WATCH, sends a calm
# heads-up TTS to every media player. On a tornado WARNING, fires
# the indoor siren and announces the shelter zone on every speaker.
# Both tiers mirror to a push notification.
#
# NOT room-targeted: ``presence_sensors`` and ``door_sensors`` are
# collected below but no package template reads them, so the siren
# fires unconditionally and open doors are never called out. They
# have never been wired in any released version. Either implement
# them or drop the two roles, and keep the copy here honest until
# one of those happens.
#
# Demonstrates the wizard's inline-config-flow path: ``integrations``
# lists ``nws``, and the Match step opens HA's NWS config flow
# directly inside the wizard so the homeowner never has to leave
# the page to wire up the integration.
slug: tornado-alert-pack
version: 1.1.0
title: Tornado Alert Pack
tagline: When the NWS issues a tornado warning, sound the indoor siren and guide everyone to the shelter zone.
description: >-
  Watches the US National Weather Service feed for tornado watches
  and warnings. On a watch, every media player gets a calm heads-up
  and a push notification, and the siren stays silent. On a warning,
  the indoor siren fires and a TTS announcement directs the
  household to the configured shelter zone, on every speaker and on
  their phones. Requires the NWS integration (US-only) — the wizard
  walks you through configuring it.
author: Selora AI
released: "2026-05-28"
tags: [safety, weather, alerts]
roles:
  # Indoor siren — uses HA's dedicated ``siren`` domain so the
  # wizard's candidate list won't surface every switch in the home
  # (AC outlets, decorative-light plugs, etc.) as a "siren". Modern
  # Z-Wave / zigbee siren integrations register their entity under
  # the ``siren.*`` namespace; older devices that report as switches
  # will need a manifest update or a homeowner-side template rename.
  - id: indoor_siren
    title: Indoor siren
    kind: siren
    min_count: 1
    max_count: 1
    selection: required
    description: >-
      Siren entity that fires on a tornado warning. Modern Z-Wave
      and zigbee sirens register under the ``siren.*`` namespace in
      Home Assistant.
  # Occupancy sensors — RESERVED, not yet used. Intended to gate the
  # siren to occupied rooms, but no package template reads this role
  # and ``indoor_siren`` is capped at one entity, so per-room
  # targeting isn't expressible as the roles stand today. Kept so
  # existing installs keep their selection; do not describe it to
  # homeowners as working until a template consumes it.
  - id: presence_sensors
    title: Presence / occupancy sensors
    kind: binary_sensor
    device_class: occupancy
    min_count: 0
    selection: required
    description: >-
      Reserved for a planned room-aware release. The current version
      does not read these sensors: the siren fires whenever a warning
      is issued, whether or not a room is occupied.
  # Exterior door sensors — RESERVED, not yet used. Intended to name
  # any door still open when a warning fires, but no package template
  # reads this role. Same call as presence_sensors above: implement
  # it or drop it, and don't advertise it meanwhile.
  - id: door_sensors
    title: Exterior door sensors
    kind: binary_sensor
    device_class: door
    min_count: 0
    selection: required
    description: >-
      Reserved for a planned release. The current version does not
      read these sensors, and the warning announcement does not call
      out open doors.
  # Where the TTS announcements play. Multi-select so the homeowner
  # can fan out across every speaker (a tornado warning isn't
  # something you want a single bedroom Echo to handle).
  - id: announce_media_players
    title: Announcement speakers
    kind: media_player
    min_count: 1
    selection: required
    description: >-
      Speakers that the TTS watch / warning announcements play on.
      Pick every media player you want included — typically every
      whole-home speaker in the household.
inputs:
  # METAR station identifier (e.g. KOKC). Auto-resolved by the
  # ``nws_station_from_location`` resolver from the home's lat/lon
  # in HA config — the wizard never shows this field; the homeowner
  # doesn't have to look anything up. If the resolver fails
  # (location not set, NWS doesn't cover the home, network down)
  # the pipeline halts at the validate stage with a punch list
  # entry the homeowner can act on.
  - id: station_code
    type: string
    label: NWS station code
    description: >-
      4-letter METAR identifier for the nearest NWS station. Resolved
      automatically from your Home Assistant location via api.weather.gov.
    resolver: nws_station_from_location
    default: ""
  - id: shelter_zone
    type: string
    label: Shelter location
    description: >-
      Name of the shelter zone, spoken in announcements. Usually a
      windowless interior room — basement, interior bathroom, or
      central hallway.
    default: "the basement"
  - id: warning_message
    type: select
    label: Warning announcement tone
    description: How the warning TTS reads — calm, urgent, or terse.
    default: "urgent"
    choices:
      - "calm"
      - "urgent"
      - "terse"
  # TTS engine entity for the spoken announcements. Auto-resolved by the
  # ``tts_engine`` resolver from the home's configured tts.* entities
  # (picking whichever TTS engine the home has configured) — the wizard never shows this field. Recipes used to call
  # ``tts.cloud_say`` directly, which only exists with a paid Home Assistant
  # Cloud subscription, so the announcement silently did nothing on every
  # other home. ``tts.speak`` + the resolved engine works everywhere. The
  # resolver returns an empty string when the home has no TTS engine at all;
  # the templates guard on it and simply skip the announcement (the siren and
  # push notification still install). The default below is only used by the
  # offline recipe-validation gate, which has no resolver to run; at install
  # the resolver always overwrites it.
  - id: tts_engine
    type: string
    label: Text-to-Speech engine
    description: >-
      Speech engine entity used for spoken announcements. Resolved
      automatically from the Text-to-Speech engines configured in Home
      Assistant.
    resolver: tts_engine
    default: "tts.piper"
integrations:
  # Recipe sets up NWS hands-off — clicking "Set up" submits the
  # config flow with values the recipe and HA already know: a
  # placeholder api_key (NWS doesn't enforce keys on its public
  # endpoints), the home's lat/lon from HA config, and the nearest
  # METAR station resolved from coordinates via api.weather.gov.
  # The homeowner never sees the form.
  - domain: nws
    title: National Weather Service
    auto_setup:
      values:
        api_key: "selora-ai-recipe"
      resolved:
        latitude: hass_config_latitude
        longitude: hass_config_longitude
        station: nws_station_from_location
package_files:
  - package/automations/warning.yaml.j2
  - package/automations/watch.yaml.j2
{# Tornado WARNING automation — fires the siren, announces the
   shelter zone. The siren is unconditional: the manifest's
   presence_sensors role is not read here (see manifest.yaml).

   Triggered on any state change of weather.<station>; the condition
   filters down to the "Tornado Warning" string. The NWS HA
   integration surfaces severe weather codes as the weather entity's
   state value.

   ``value_template`` is single-quoted so YAML doesn't read the leading
   ``{{`` as a flow mapping. #}
automation:
  - id: selora_recipe_{{ slug | replace('-', '_') }}_warning
    alias: "Tornado Alert — warning response"
    description: >-
      When the NWS feed flips to a tornado warning, fire the indoor
      siren and announce the shelter zone.
    mode: single
    trigger:
      - platform: state
        entity_id: weather.{{ inputs.station_code | lower }}
    condition:
      - condition: template
        value_template: "{% raw %}{{ 'tornado warning' in trigger.to_state.state | lower }}{% endraw %}"
    action:
      # 1. Fire the siren, unconditionally. Single role with
      # max_count=1 so index 0.
      # ``siren.turn_on`` is the HA service for the siren domain;
      # most modern Z-Wave / zigbee sirens accept it directly. If the
      # homeowner's siren is shimmed as a switch, the manifest will
      # need a binding-mode that translates this for them.
      - service: siren.turn_on
        target:
          entity_id: {{ roles.indoor_siren[0] }}
{% if roles.announce_media_players and inputs.tts_engine %}
      # 2. Announce on every configured speaker via the resolved TTS engine.
      #    tts.speak is engine-agnostic (works without Home Assistant Cloud);
      #    the engine entity is auto-resolved and the speakers are passed as
      #    media_player_entity_id. Tone copy is picked at render time from the
      #    homeowner's warning_message input.
      - service: tts.speak
        target:
          entity_id: {{ inputs.tts_engine }}
        data:
          media_player_entity_id:
{% for player in roles.announce_media_players %}
            - {{ player }}
{% endfor %}
{% if inputs.warning_message == 'calm' %}
          message: "A tornado warning is in effect for your area. Please move to {{ inputs.shelter_zone }}."
{% elif inputs.warning_message == 'terse' %}
          message: "Tornado warning. Move to {{ inputs.shelter_zone }} now."
{% else %}
          message: "Tornado warning. Move to {{ inputs.shelter_zone }} immediately."
{% endif %}
{% endif %}
      # 3. Push notification mirrors the announcement so anyone away
      #    from a speaker still gets the alert on their phone.
      - service: notify.notify
        data:
          title: "Tornado warning"
          message: "Tornado warning in effect. Move to {{ inputs.shelter_zone }}."
{# Tornado WATCH automation — calmer heads-up. A watch means
   conditions are favourable for tornadoes; a warning means one's
   been sighted. The watch automation gives the homeowner a moment
   to glance at the sky and pre-stage.

   ``value_template`` is single-quoted so YAML doesn't read the
   leading ``{{`` as a flow mapping. #}
automation:
  - id: selora_recipe_{{ slug | replace('-', '_') }}_watch
    alias: "Tornado Alert — watch heads-up"
    description: >-
      When the NWS feed signals a tornado watch, send a calm TTS
      announcement to every configured speaker. No siren.
    mode: single
    trigger:
      - platform: state
        entity_id: weather.{{ inputs.station_code | lower }}
    condition:
      - condition: template
        value_template: "{% raw %}{{ 'tornado watch' in trigger.to_state.state | lower }}{% endraw %}"
    action:
{% if roles.announce_media_players and inputs.tts_engine %}
      # Announce via the auto-resolved TTS engine. tts.speak works without a
      # Home Assistant Cloud subscription; the speakers go in
      # media_player_entity_id and the engine entity is the target.
      - service: tts.speak
        target:
          entity_id: {{ inputs.tts_engine }}
        data:
          media_player_entity_id:
{% for player in roles.announce_media_players %}
            - {{ player }}
{% endfor %}
          message: "Tornado watch active for your area. Selora is monitoring. If you hear the siren, move to {{ inputs.shelter_zone }}."
{% endif %}
      - service: notify.notify
        data:
          title: "Tornado watch"
          message: "Tornado watch active. Conditions favour tornadoes — stay alert."

Author-maintained release notes. Each release of the recipe lists what changed.

Changelog

v1.1.0 - 2026-06-29

  • Spoken announcements now use tts.speak with an auto-resolved TTS engine instead of tts.cloud_say. tts.cloud_say only exists with a paid Home Assistant Cloud (Nabu Casa) subscription, so the watch/warning announcements were silent on every home without it. The engine is detected automatically (Home Assistant Cloud, then Piper, then Google, else the first available); no new question in the wizard.
  • On a home with no Text-to-Speech engine configured, the announcement is skipped and the siren plus push notification still install.

v1.0.0 - 2026-05-28

Initial release.

  • Subscribes to the National Weather Service for the homeowner’s county and watches for tornado watch and warning codes.
  • Sounds the indoor siren on a warning. A watch stays quiet: announcement and push notification only.
  • Announces the alert on every selected speaker over Text-to-Speech, and mirrors it to a push notification so anyone out of earshot still gets it.
  • Routes occupants to a homeowner-configured shelter zone.

Note: this entry previously described occupancy-gated sirens and open-door announcements. Neither shipped in v1.0.0, and neither is implemented in any released version; the wording has been corrected rather than left standing.

Type to search across cities, counties, and installers

↑↓ navigate open
⌘K search