Hourly check of Tempest lightning, PurpleAir PM2.5, and nearby CAL FIRE incidents during an active Red Flag Warning. Alerts only on concerning change. Self-stops when the warning lifts.
Install
npx skillscat add rolandcanyon-cmd/home-consciousness/storm-watch Install via the SkillsCat registry.
Storm Watch (Red Flag Warning monitor)
Runs hourly while a National Weather Service Red Flag Warning (dry lightning risk) is active
and the operator is away from the house. Checks three sources, decides whether the situation
is concerning enough to interrupt the operator, and self-retires once the warning lifts.
State file: .instar/state/storm-watch-state.json — read it first, you'll need it to detect
changes (new fires, worsening containment, lightning getting closer), not just current readings.
Step 1 — Check whether the Red Flag Warning is still active (the stop condition)
- Navigate (Playwright
browser_navigate) tohttps://tempestwx.com/station/125865/(the house's
Tempest station — confirm this URL is still current via FunkyGibbon if in doubt: searchkittenkong/FunkyGibbon for "tempest weather station", readstation_url). - Wait ~2-3s for the page to render, then check whether the text "Red Flag Warning" appears
in the snapshot (browser_findfor "Red Flag Warning" or fullbrowser_snapshot). - If the warning is GONE and
state.stoppedis not already true:- Send ONE final iMessage: plain-English "the Red Flag Warning has lifted — stopping the
hourly storm watch" message. Mention current PM2.5/lightning/fire status briefly as a
clean closing summary if you have time to gather it, but don't block the all-clear on it. - Update the state file: set
"stopped": true,"lastRedFlagWarningSeen": false, updatelastRun. This is what makes the job's gate script (storm-watch-gate.sh) skip all future
scheduled runs — no server restart needed, no further action required. - Stop here. Do not run steps 2-4.
- Send ONE final iMessage: plain-English "the Red Flag Warning has lifted — stopping the
- If the warning is still active, continue below.
Step 2 — Lightning (from the same Tempest session)
- Click the "Observations" button (or navigate to
/station/125865/grid) and find the
lightning card: fields are "Last Detected" (age), "Distance" (of that last detection),
and "Last 3 Hrs" (strike count in the last 3 hours — this is the live indicator). - Compare
Last 3 Hrsagainststate.lastLightning.last3HrStrikes:- Alert-worthy:
Last 3 Hrs> 0 (active nearby lightning detected — the Tempest sensor's
range is roughly 25 miles, so any nonzero count while under a dry-lightning red flag warning
is meaningful) OR the reported distance bucket has gotten closer since the last run. - Otherwise, just update state — no alert on this alone.
- Alert-worthy:
Step 3 — Air quality (PurpleAir Flex, local sensor — no browser needed)
Run: node .claude/scripts/ambient-weather.mjs --json
- Trusted reading is
pm25Category(Good/Moderate/Unhealthy for Sensitive Groups/Unhealthy/Very
Unhealthy/Hazardous) — this is the corroborated PurpleAir Flex reading, the one to alarm on. - Alert-worthy:
pm25Categoryis "Unhealthy for Sensitive Groups" or worse (i.e. PM2.5 > 35.4
µg/m³), ORpm25Categoryjust got worse than last run'sstate.lastPm25.category(e.g.
Good→Moderate is a real trend worth a heads-up even if not yet "bad", since this is a fire/smoke
watch — use judgment: a first-time move off "Good" during an active red-flag/fire situation is
worth a mention, a fluctuation between Moderate and Good repeatedly is not). - If
pm25Unavailableappears (sensor unreachable), note it but don't alarm on a single miss —
only mention if unavailable 2+ consecutive runs (checkstate.lastPm25for a prior unavailable
flag you may have stored). - If
pm25Reference(the regional Open-Meteo model) diverges sharply HIGHER than the local
PurpleAir reading (e.g. reference reads several multiples of the local value) while the local
sensor still reads clean, that can mean smoke aloft not yet at ground level — worth ONE mention
the first time you see it, not every hour.
Step 4 — Nearby fires (CAL FIRE public incident API — no browser needed)
Run: python3 .claude/scripts/calfire-nearby.py --radius 60
This is the same underlying data Watch Duty aggregates from, read directly from CAL FIRE's public
API (far more reliable for unattended hourly automation than scraping Watch Duty's interactive
map). Compare the returned incidents[].uniqueId list against state.knownFireIds:
- Alert-worthy: a genuinely NEW
uniqueIdappears within the radius that wasn't inknownFireIds— this is a brand new nearby fire. - Alert-worthy: an already-known nearby fire (say, within ~30 miles) shows containment
DROPPING or acreage growing substantially (e.g. >25% acreage increase) sincelastFireSnapshot. - Otherwise (same fires, stable/improving), just refresh state — no alert.
Step 5 — Decide and act
- If ANY of steps 2-4 are alert-worthy, send ONE consolidated iMessage covering everything
relevant (don't send three separate texts) — plain English, lead with the most urgent item,
skip anything that's fine (no need to say "air quality: still fine" if lightning is the news). - If NOTHING is alert-worthy, send NOTHING. Silence is correct — this runs hourly for
potentially days, and the operator only wants to hear about it when something changed for
the worse. Do not send a routine "all clear, nothing new" text every hour. - Always update
.instar/state/storm-watch-state.jsonwith the latest readings
(lastLightning,lastPm25,knownFireIds,lastFireSnapshot,lastRun,lastRedFlagWarningSeen) regardless of whether you alerted, so the next run has an accurate
baseline to diff against.
Sending the message
PHONE=$(python3 -c "
import json
d = json.load(open('.instar/config.json'))
phone = d.get('imessage', {}).get('userPhone', '')
if not phone:
for m in d.get('messaging', []):
if m.get('type') == 'imessage':
contacts = m.get('config', {}).get('authorizedContacts', [])
phone = next((c for c in contacts if c.startswith('+')), contacts[0] if contacts else '')
break
print(phone)
")
cat <<MSG | .claude/scripts/imessage-reply.sh "$PHONE"
YOUR MESSAGE HERE
MSGNotes
- This job was set up 2026-08-28 at the operator's request while away from the house for an
active Red Flag Warning (dry lightning risk); Tesla Powerwalls are in storm mode. Runs hourly
via thestorm-watchjob (schedule0 * * * *, gated bystorm-watch-gate.shso it goes
silent for free once stopped — no server restart needed to stop it). - House coordinates: 36.5556, -121.7180 (San Benancio, off Hwy 68, Monterey County).
- Don't fabricate distance/direction detail beyond what the sources actually report.
- If the Tempest page fails to load or the lightning card can't be found, say so plainly in
state/notes rather than guessing — but don't let a Tempest hiccup suppress the fire/air checks.