v2.4.0 - Pluggable Weather, Process Supervisor & Proactive Scheduling
New Features
- Pluggable weather/context provider: Refactors the hardwired wttr.in fetch into a
WeatherProviderinterface with three adapters — wttr.in (default; free, no API key), Home Assistant (local, no third-party egress), and manual (offline / testing, zero network). Auto-selects whenWEATHER_PROVIDERis unset; existingZIPCODE-only setups keep working unchanged. SetWEATHER_PROVIDER=noneto disable weather entirely. - Process supervisor for unattended deployments: New
scripts/supervisor.pywrapspython -m jf_sebastian.mainwith exponential-backoff restart, watchdog kill of hung children via heartbeat-file staleness, enriched crash reports, and permanent-failure detection (CRITICAL log + extended backoff after N consecutive crashes). Includes launchd plist (macOS) and systemd unit (Linux) templates. Designed for museum exhibits, eldercare companions, kids' rooms — anywhere the toy needs to keep running across PortAudio/RVC crashes. - Proactive scheduler / ambient mode: Personalities can now define proactive utterances in
personalities/<name>/scheduled_events.yaml— morning greetings, bedtime stories, holiday surprises, scheduled reminders. Tiny schedule syntax (HH:MM,HH:MM weekdays,HH:MM YYYY-MM-DD); each event usessay:(verbatim TTS) orprompt:(LLM in character). Events only fire when state is IDLE — never interrupts an in-progress conversation. Quiet-hours suppression with load-time warnings. - ROADMAP.md: Public roadmap synthesized from a seven-persona peer-reviewed codebase audit. Documents Tier 1-3 prioritization plus excluded items, with 70 enhancement suggestions evaluated by Borda count.
Bug Fixes
- Fix
urlsplitraisingValueErroron a malformedHOME_ASSISTANT_URLand breaking the entire weather pipeline (including auto-fallback to wttr) - Fix
_refresh_in_flightflag leakingTruewhen the weather provider became unconfigured mid-refresh, suppressing all future refreshes - Fix Whisper weather cold-miss (first-conversation timeout caused "I can't check the weather" responses)
- Fix numpy int16 overflow
RuntimeWarningin RMS amplitude calculation - Fix race condition in scheduled-event callback that could re-init PyAudio after
audio_player.cleanup()during shutdown - Fix wake-word detector race during scheduled-event TTS synthesis (added atomic
try_transitionCAS to state machine) - Fix
_pause_wake_for_playbackflag set even whenpause()raised, causing missed resume
Improvements
- Local-first weather security: Bearer token to Home Assistant is refused over plain HTTP to non-private hosts (loopback, RFC1918, link-local,
*.localallowed); URL-quoted entity ID;allow_redirects=Falseto prevent token leak via cross-host redirects; defensive.get()-chain parsing for wttr; Celsius detection tolerates"C","celsius", etc. - Defense-in-depth supervision: launchd/systemd watch the supervisor; supervisor watches the child; heartbeat thread (
utils/heartbeat.py) reports liveness so even hung-PROCESSING is detected and killed viaos.killpgon the child's process group (kills ffmpeg / RVC subprocesses, not just the immediate child) - Crash report enrichment: PID, personality, ran_for, heartbeat age at exit, hostname, Python version, last 100 log lines; pruned to most recent N (default 200) so disk can't fill on a permanent-failure loop
- Rotating logs:
jf_sebastian.logandsupervisor.loguseRotatingFileHandler(10MB × 5) so unattended deployments can't fill disk - Atomic state transitions: New
StateMachine.try_transition(expected, target, trigger)compare-and-swap closes TOCTOU windows for any caller that needs to gate on current state;try_transitionandtransition_toshare a single body via_apply_transition_locked - Cooperative shutdown: SIGTERM/SIGINT use cooperative
_running = Falseinstead ofsys.exit()from a signal handler;SystemExit(0)from handler when SIGTERM arrives during__init__so the supervisor doesn't have to escalate to SIGKILL - Hobbyist-friendly schedule UX:
weekdays/weekendsaliases, friendlier error messages for typos ("empty weekday token"instead of crypticunknown weekday ''), past-dated one-shot warnings at load, items listed in scheduler startup log so users can verify their event loaded - Updated wake word models for Fred, Johnny, KITT, Leopold
- Documentation: New "Running Unattended" section in README; QUICKSTART covers all three weather providers + scheduled events; ARCHITECTURE diagram now shows supervisor + heartbeat layers; CLAUDE.md catalogs the new modules
Configuration
New env vars (all optional, sensible defaults):
# Weather provider (auto-selects from configured vars if unset)
#WEATHER_PROVIDER=wttr # or homeassistant / manual / none / auto
#HOME_ASSISTANT_URL=http://homeassistant.local:8123
#HOME_ASSISTANT_TOKEN=...
#HOME_ASSISTANT_WEATHER_ENTITY=weather.home
#MANUAL_WEATHER=Sunny and 72F
# Proactive scheduler (per-personality scheduled_events.yaml)
SCHEDULER_ENABLED=true
#QUIET_HOURS_START=22:00
#QUIET_HOURS_END=07:00
# Supervisor / watchdog (only relevant when running scripts/supervisor.py)
#HEARTBEAT_FILE=/tmp/jf_sebastian.heartbeat
#WATCHDOG_TIMEOUT=60.0
#RESTART_BACKOFF_INITIAL=1.0
#RESTART_BACKOFF_MAX=60.0
#CRASH_REPORT_DIR=./crash_reports/See .env.example for the full annotated list.