Overview
Fetches daily + hourly forecasts, crafts a concise message, optional AI rephrase that preserves facts, then speaks on selected speakers with a pre-roll delay.
Precipitation callouts require hourly
precipitation_probability. If missing, the precip line is skipped by design.Requirements
weatherentity supportingweather.get_forecasts(daily + hourly).ttsengine and at least onemedia_player.- Optional: an
ai_taskentity for rephrasing; Conversation integration for voice.
Inputs overview
| Group | Key(s) | Description |
|---|---|---|
| Weather | weather_entity | Source of current, daily, hourly. |
| TTS | tts_engine, speakers, voice, volume_level, preroll_ms | Engine, targets, voice, volume, wake-up delay. |
| AI | ai_task_entity, use_ai_rewrite, ai_rewrite_prompt | Optional AI pass with strict no-fact-change rules. |
| Time | enable_time_based, hour_pattern, minute_offset, start_time, end_time, days_of_week | Schedule and windowing. |
| Sensor | enable_sensor_triggered, presence_sensors | Announce when sensors turn on. |
| Webhook | enable_alarm_announce, webhook_id, personal_name, alarm_volume_level | On-demand personal report. |
| Change | enable_current_change_announce, current_change_volume_level, enable_upcoming_change_announce, upcoming_change_volume_level, minutes_before_announce | Real-time and impending alerts. |
| Precip | precip_threshold, hours_ahead | Threshold and search window. |
| Voice | enable_voice_satellite, conversation_command | Conversation trigger phrases. |
Automation Editor Mock
Time patternid: time_based
Hours:
!input hour_pattern • Minutes: !input minute_offsetRuns only between
!input start_time–!input end_time on chosen !input days_of_week when enabled.State (presence sensors)id: sensor_trigger
Entity:
!input presence_sensors → onGreat for “arrive home and speak forecast.”
State (weather entity change)id: current_change
Entity:
!input weather_entitySpeaks when condition changes, e.g., “It’s now 45° and cloudy.”
Time pattern (upcoming change poll)id: upcoming_change
Minutes:
/5Find next precip start within
!input minutes_before_announce if not precipitating now.Webhookid: alarm_webhook
URL:
http://homeassistant.local:8123/api/webhook/<!input webhook_id> • Methods: POST, PUT, GET, HEAD • Local onlyConversationid: voice_satellite
Commands: lines in
!input conversation_commandMaster OR across scenarios
- Time-based: between
!input start_time–!input end_time,!input enable_time_basedtrue, and day ∈!input days_of_week. - Sensor-triggered:
!input enable_sensor_triggeredtrue. - Current change:
!input enable_current_change_announcetrue. - Upcoming change:
!input enable_upcoming_change_announcetrue. - Alarm webhook:
!input enable_alarm_announcetrue. - Voice satellite:
!input enable_voice_satellitetrue.
If any branch is true, its Actions sequence runs.
Time-based / Sensor-triggered
- Call
weather.get_forecastsdaily →d; hourly →h. - Compose message with greeting, “Right now,” “Today,” and first precip window within
!input hours_aheadif≥ !input precip_threshold. - Optional AI pass via
ai_task.generate_datawhen!input use_ai_rewriteand!input ai_task_entityset. media_player.volume_setto!input volume_level.- For each
!input speakers: delay!input preroll_msms, thentts.speakwithoptions.voice = !input voice.
Current change
- On weather state change → msg “It’s now <temp>° and <cond>.”
- Optional AI pass; set volume to
!input current_change_volume_level; per-speaker delay + speak.
Upcoming change
- Hourly fetch → find next precip start in ≤
!input minutes_before_announceif not precipitating now. - Msg “<rain/snow> in about <minutes> minutes.” → set volume to
!input upcoming_change_volume_level→ per-speaker delay + speak.
Alarm webhook
- Daily + hourly fetch → personalized greeting with
!input personal_name. - Optional AI pass; set volume to
!input alarm_volume_level; per-speaker delay + speak.
Variables & example values
| Variable | Source | Meaning | Example |
|---|---|---|---|
key | !input weather_entity | Primary weather entity id | weather.openweathermap |
d, h | weather.get_forecasts | Daily/hourly service responses | { forecast: [...] } |
now_hour | now().hour | Current hour 0-23 | 8 |
day_name | timestamp_custom('%A') | Day of week | Saturday |
greeting | templated from now_hour | Morning/Afternoon/Evening | Good morning |
now_temp | state_attr(key,'temperature') | Current temp | 57 |
now_hum | state_attr(key,'humidity') | Relative humidity % | 62 |
now_cond_h | states(key) normalized | Readable condition | partly cloudy |
daily_list | d[key].forecast | Array of daily periods | [{ temperature: 63, templow: 49, ... }] |
today | daily_list[0] | Today’s daily item | { temperature: 63, templow: 49, condition:'partlycloudy' } |
high | today.temperature | High ° | 63 |
low | today.templow/temperature_low | Low ° | 49 |
hourly_list | h[key].forecast | Array of next hours | [{ datetime:'2025-11-08T09:00:00+00:00', precipitation_probability:40, condition:'rain', ... }] |
next_hours | slice of hourly_list | Hours within !input hours_ahead | first 24 items |
hits | filter on precipitation_probability ≥ !input precip_threshold | Candidate precip hours | [{..40% rain..}, …] |
first_hit | hits[0] | Earliest candidate | { datetime:'…09:00…', precipitation_probability:40 } |
hit_pp | first_hit.precipitation_probability | Precip % | 40 |
hit_clock | format of first_hit.datetime | 12-hour time | 9 AM |
hit_daypart | by hour number | this morning/afternoon/evening/overnight | this morning |
precip_kind | contains “snow/sleet/hail/rain” | Human term | rain |
msg | assembled text | Final base string | Good morning! Saturday's forecast. Right now… |
spoken | AI or base | Text actually spoken | Good morning! Saturday’s forecast… 40% rain this morning around 9 AM. |
Examples assume: clear morning, first precip at 9 AM with 40%.
Logic details
Message assembly
- Greeting + day: based on
now().hourandtimestamp_custom('%A'). - Right now: condition → temperature → humidity if present.
- Today: “Expect <today_cond_h>” when today’s condition differs from “Right now,” then “High … Low …”.
- Precip window: scan
hourly_list[:!input hours_ahead]; first hour withprecipitation_probability ≥ !input precip_thresholdbecomes the callout, including daypart andhit_clock.
Upcoming change alert
- Every 5 minutes: find next hour after
now(). If it is precip and current is not precip, and minutes-to-start ≤!input minutes_before_announce, speak a short alert.
AI rewrite
- Optional. Only changes wording. Facts, numbers, and order must remain identical; prompt enforces it.
- If AI is unavailable, the base
msgis used.
Webhook alarm: Android & iOS
Canonical URL format
http://homeassistant.local:8123/api/webhook/<WEBHOOK_ID_FROM_INPUT>
Android (Shortcuts / Automate)
- Create a shortcut named Weather Alarm.
- Add HTTP POST.
- URL: the webhook above using your
webhook_id. - Headers: none on LAN. For remote, use your external URL.
- Body: empty.
- Run to trigger the “Alarm webhook” branch.
iOS (Shortcuts)
- Shortcuts → “+” → Add Action → “Get Contents of URL”.
- Method: POST.
- URL:
http://homeassistant.local:8123/api/webhook/<ID> - Request Body: None.
- Optional: Add “Set Volume” first.
- Run to test.
Troubleshooting
- No audio: Ensure
speakersincludes at least onemedia_player. Increasepreroll_msif audio starts late. - No precip line: Your hourly data may lack
precipitation_probability. This is expected; the line is omitted. - Voice not responding: Conversation integration must be enabled. Ensure phrases match lines in
conversation_command. - AI task fails: Disable
use_ai_rewriteto confirm base messaging, then validate theai_taskentity.
FAQ
How do I schedule every 3 hours at minute 3?
Set
hour_pattern to /3 and minute_offset to 3.How do I change the voice?
Set
voice to a valid voice for your TTS engine, e.g., en_US-carlin-high.What’s the correct webhook URL?
Use
http://homeassistant.local:8123/api/webhook/<WEBHOOK_ID_FROM_INPUT>. Replace the placeholder with your blueprint input.Repo layout
- /weather-forecast.yaml — Production blueprint
- /backup/ — Snapshots
- /development/ — Drafts
- /docs/index.html — This page