Skip to content

App Triggers — per-app event-driven display interrupts#39

Open
csader wants to merge 14 commits into
mainfrom
feat/app-triggers
Open

App Triggers — per-app event-driven display interrupts#39
csader wants to merge 14 commits into
mainfrom
feat/app-triggers

Conversation

@csader
Copy link
Copy Markdown
Owner

@csader csader commented May 15, 2026

Summary

  • New Triggers page in hamburger menu with enable/disable master switch
  • Bell icon on app cards for trigger-capable apps (links to add trigger)
  • Add/edit trigger modal with dynamic condition fields from manifest schema
  • Trigger list shows last fired time, enable toggle, edit/delete
  • GET/POST /triggers API; /installed_apps now includes has_trigger + trigger_conditions
  • Background trigger loop checks every 10s, respects quiet hours
  • Full developer contract documented in APPS_README.md

13 trigger-capable apps

App Conditions
Sports game_start, score_change, close_game, final, overtime, playoff, comeback, rival, shutout
BirdNET any, new_today, first_today, specific, watchlist, high_confidence, busy_feeder
Stocks pct_change, price_target, 52w_extreme, market_hours
Weather severe, temp_above, temp_below, rain_starting, rapid_temp_change, uv_high, wind_high
ISS overhead, visible_pass, crew_change
Crypto pct_change, price_target
YouTube new_video, view_milestone
Metro arriving, alert
Planes Overhead any, keyword, military, altitude
News Headlines keyword filter
BTC Fear & Greed extreme_fear, extreme_greed, either
YT Comments keyword filter
Countdown 30d/7d/1d/1h/arrival milestones
Moon Phase full moon, new moon
World Clock business open/close
Time Since 100d/1y/2y/5y/10y milestones

Test plan

  • Open Triggers page from hamburger menu
  • Add a trigger via bell icon on an app card — app should be preselected
  • Configure conditions, save — modal should close
  • Verify trigger fires and interrupts display
  • Disable trigger — verify it no longer fires
  • Quiet hours active — verify triggers suppressed

🤖 Generated with Claude Code

@csader csader force-pushed the feat/app-triggers branch 3 times, most recently from a6d64bc to be10680 Compare May 16, 2026 03:38
Chris and others added 14 commits May 17, 2026 14:38
Full trigger developer guide including:
- manifest.json trigger_conditions schema
- trigger(settings, conditions) function contract
- State persistence pattern between calls
- BirdNET rare species example end-to-end
- Triggers without conditions (ISS-style)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: trigger() function loading alongside fetch() in plugin system
- _plugin_triggers dict, _trigger_cooldowns, _trigger_last_check tracking
- _trigger_loop() daemon thread checks every 10s, respects quiet hours
- GET/POST /triggers routes; /installed_apps now includes has_trigger + trigger_conditions
- Triggers page in hamburger menu with enable/disable master switch
- Bell icon on app cards for trigger-capable apps (links to add trigger)
- Add/edit trigger modal: name, app picker, dynamic condition fields from manifest,
  display duration, cooldown
- Trigger list shows last fired time, enable toggle, edit/delete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sports: fires on game_start, score_change, close_game (within 5), or final.
BirdNET: fires on any detection, new species today, or specific species.
Stocks: fires when any ticker moves beyond a % threshold (up/down/either).
Weather: fires on severe weather or temp crossing a threshold (Open-Meteo).
ISS: fires when ISS ground track is within 500km of user's zip code.

All manifests updated with trigger schema. Bell icon moved to left side
of app tiles for visual separation from settings/remove buttons.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…38)

Crypto: fires when any followed coin moves beyond a % threshold (up/down/either).

YouTube: fires when a new video is posted to the followed channel.
  Seeds last_video_id on first run to avoid false fire on install.

Countdown: fires at configurable milestones — 30d, 7d, 1d, 1h, or arrival.
  Uses state to fire once per milestone per target date.

Moon Phase: fires on full moon or new moon (configurable).
  Resets state between phases so each occurrence fires once.

World Clock: fires when business hours open (9am) or close (5pm) in any
  followed timezone. Fires once per event per day.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Metro: fires when the next train on the followed route/stop is arriving
  within a configurable number of minutes. Supports direction filter.

Planes Overhead: fires when a new aircraft appears in the search radius.
  Reuses fetch() cached flight data to avoid extra API calls. Supports
  callsign keyword filter (e.g. UAL, N12).

News Headlines: fires when a new headline containing configured keywords
  appears in the RSS feed. Empty keywords = any new headline. Tracks
  seen titles to avoid re-firing on the same story.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BTC Fear & Greed: fires when the index enters extreme fear or extreme
  greed territory. Configurable zone and threshold. Fires once per
  zone entry, resets when index leaves the zone.

YT Comments: fires when a new comment appears on the followed video.
  Supports optional keyword filter. Seeds seen IDs on first run.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fires when elapsed time hits a round milestone: 100 days, 1 year,
2 years, 5 years, or 10 years. Fires once per milestone per event date.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sports: add overtime/extra time and playoff/postseason event types.

Stocks: add price target condition (fires when ticker crosses a specific
  price, resets when price moves away). Condition type toggle selects
  between % move and price target.

BirdNET: add watchlist (comma-separated species list) and high_confidence
  (configurable % threshold) filter options.

Weather: add rain_starting (dry→rain transition), rapid_temp_change
  (configurable °F delta), uv_high, and wind_high conditions. Now
  fetches UV index and wind speed from Open-Meteo alongside temp/code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sports: add comeback alert (was down by N, now within 3).

Stocks: add 52-week high/low condition. Condition type toggle now
  includes pct_change, price_target, and 52w_extreme.

Crypto: add price target condition (mirrors stocks pattern).
  Condition type toggle selects between % move and price target.

YouTube: add view milestone condition (requires API key).
  Condition type toggle selects between new_video and view_milestone.

Metro: add service alert condition (fires on DELAY, SUSPENSION,
  SHUTTLE, STOP_CLOSURE, DETOUR). Condition type toggle selects
  between arriving and alert.

Planes Overhead: add military aircraft filter using common US
  military callsign prefixes (RCH, SPAR, SAM, VENUS, etc.).

BirdNET: add busy feeder condition (N detections within M minutes).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- overhead: existing behavior (within ~500km ground track)
- visible_pass: overhead + nighttime (8pm–5am local) + cloud cover ≤30%
  Uses Open-Meteo for cloud cover (keyless)
- crew_change: fires when ISS crew roster changes (arrival or departure)
  Seeds on first run to avoid false fire on install

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… first_today (#38)

Sports: rival matchup (fire when followed team plays a configured rival)
  and shutout in progress (one team has 0 after scoring begins).

Stocks: market open/close condition (9:30am and 4:00pm ET on weekdays,
  no API needed). Condition type toggle now has 4 options.

Planes Overhead: altitude threshold filter (fire only for aircraft
  above a configurable altitude in feet). OpenSky poll now captures
  altitude_m from state vector.

BirdNET: first detection of the day (fires once per day on the first
  qualifying detection regardless of species).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- openAddTrigger() now lazily fetches installed_apps if _triggerApps
  is empty (happens when called from app card before Triggers page loads)
- _saveTriggerModal() now receives button reference and uses
  btn.closest('.modal-overlay') instead of querySelector which could
  grab a stale/wrong overlay

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- flex-wrap:wrap so buttons flow to next row when there are many options
- min-width:0 and white-space:normal so buttons shrink and wrap text
- Slightly smaller font (.75rem) for dense option sets like BirdNET

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Modal now uses width:max-content with min 320px and max 600px (or 90vw),
so it expands to fit wide toggle sets like BirdNET without overflow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@csader csader force-pushed the feat/app-triggers branch from be10680 to a98d309 Compare May 17, 2026 19:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant