mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-06-11 01:14:45 +02:00
feat(v0.5.11): band conditions scheduled broadcaster (3x/day HF propagation)
First clock-driven broadcaster in meshai, distinct from the v0.5.8b/v0.5.9/v0.5.10 event-driven adapters. The same persistence + dispatcher + cold-start patterns apply, but the trigger is the wall clock at 06:00 / 14:00 / 22:00 Mountain Time (default; GUI-configurable per Rule 17). Components: (1) meshai/notifications/scheduled/band_conditions.py with BandConditionsScheduler (asyncio loop, mirrors the existing DigestScheduler shape), compute_band_ratings() with two-tier data sourcing -- (a) latest swpc_kindex + swpc_alerts F10.7 rows from persistence within the last 6h, (b) HamQSL.com solarxml.php fallback when SWPC is stale or incomplete, (c) silent skip when both fail, format_band_conditions_wire() multi-line MEDIUM output (~115-120B). (2) v3 schema migration adding band_conditions_broadcasts(broadcast_id PK AUTO, sent_at, scheduled_for UNIQUE, ratings_json, source). UNIQUE(scheduled_for) enforces per-slot dedup so a retry storm cannot double-broadcast. (3) Dispatcher.dispatch_scheduled_broadcast() bypasses the toggle / rules / freshness-gate pipeline but DOES honour the v0.5.8b cold-start grace -- first scheduled broadcast within the grace window after meshai starts is suppressed, mesh_broadcasts_out audit row only inserted on actual delivery. Channel selection routes through the rf_propagation toggle\'s broadcast_channel since band conditions IS RF-propagation info. (4) NotificationsConfig gains band_conditions_enabled (default true), band_conditions_schedule (list of HH:MM strings, default ["06:00","14:00","22:00"]), band_conditions_tz (default "America/Boise" so DST handles automatically). (5) Notifications.tsx grows a Band Conditions card between Cold-Start Grace and Master Toggles with the enable toggle + 3 TimeInput slots + a one-liner explaining the source priority. (6) build_pipeline + start_pipeline spawn the BandConditionsScheduler alongside the existing DigestScheduler -- best-effort, scheduler failures must NOT break notifications startup. Wire format examples (multi-line, all under 130B target): ☀️ Day Propagation 📡 Band Conditions: 80-40m: 🟡 Fair 30-20m: 🟢 Good 17-15m: 🟢 Good 12-10m: 🟡 Fair 🌞 Day Propagation (14:00 slot when storm onset, Kp=6 SFI=110) 📡 Band Conditions: 80-40m: 🔴 Poor 30-20m: 🔴 Poor 17-15m: 🔴 Poor 12-10m: 🟡 Fair 🌙 Night Propagation (22:00 slot, recovery, Kp=4 SFI=120) 📡 Band Conditions: 80-40m: 🟡 Fair 30-20m: 🟡 Fair 17-15m: 🔴 Poor 12-10m: 🔴 Poor Tests: was 686 (v0.5.10 baseline), now 704 (+18 net new -- quiet/storm condition ratings, HamQSL XML parse fallback, both-fail silent-skip path, is_day_slot per HH:MM, wire format for all 3 slot variants, byte-size guard, 6-line shape, fire_slot record row, dedup via UNIQUE constraint, silent-skip path, slot_epoch DST alignment summer + winter). Synthetic 24h probe verified the 3 expected slots fire correctly with quiet/storm/recovery scenarios + the 4th no-data scenario lands as source=\'skipped_no_data\' with no broadcast. usgs_nwis deferred to v0.5.12 (threshold-curation work). Master OFF in prod. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
de35f9c748
commit
0da83e0d3d
9 changed files with 957 additions and 1 deletions
|
|
@ -65,6 +65,8 @@ interface NotificationsConfig {
|
|||
quiet_hours_start: string
|
||||
quiet_hours_end: string
|
||||
cold_start_grace_seconds?: number
|
||||
band_conditions_enabled?: boolean
|
||||
band_conditions_schedule?: string[]
|
||||
rules: NotificationRuleConfig[]
|
||||
toggles?: Record<string, NotificationToggle>
|
||||
}
|
||||
|
|
@ -2135,6 +2137,55 @@ export default function Notifications() {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{/* Band Conditions -- v0.5.11 */}
|
||||
<div className="space-y-3 p-4 bg-[#0a0e17] rounded-lg border border-[#1e2a3a]">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-xs text-slate-500 uppercase tracking-wide">Band Conditions (HF propagation)</label>
|
||||
</div>
|
||||
<Toggle
|
||||
label="Enable scheduled band-conditions broadcasts"
|
||||
checked={config.band_conditions_enabled ?? true}
|
||||
onChange={(v) => setConfig({ ...config, band_conditions_enabled: v })}
|
||||
helper="3x/day HF propagation summary (Day/Night ratings per band group)"
|
||||
info="Source priority: (1) recent SWPC readings persisted locally; (2) HamQSL.com fallback; (3) silent skip if both fail. Persistence rows are written either way for an audit trail."
|
||||
/>
|
||||
{(config.band_conditions_enabled ?? true) && (
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<TimeInput
|
||||
label="Slot 1"
|
||||
value={(config.band_conditions_schedule ?? ['06:00','14:00','22:00'])[0] || '06:00'}
|
||||
onChange={(v) => {
|
||||
const s = [...(config.band_conditions_schedule ?? ['06:00','14:00','22:00'])]
|
||||
s[0] = v
|
||||
setConfig({ ...config, band_conditions_schedule: s })
|
||||
}}
|
||||
helper="Morning (default 06:00 MT)"
|
||||
/>
|
||||
<TimeInput
|
||||
label="Slot 2"
|
||||
value={(config.band_conditions_schedule ?? ['06:00','14:00','22:00'])[1] || '14:00'}
|
||||
onChange={(v) => {
|
||||
const s = [...(config.band_conditions_schedule ?? ['06:00','14:00','22:00'])]
|
||||
s[1] = v
|
||||
setConfig({ ...config, band_conditions_schedule: s })
|
||||
}}
|
||||
helper="Afternoon (default 14:00 MT)"
|
||||
/>
|
||||
<TimeInput
|
||||
label="Slot 3"
|
||||
value={(config.band_conditions_schedule ?? ['06:00','14:00','22:00'])[2] || '22:00'}
|
||||
onChange={(v) => {
|
||||
const s = [...(config.band_conditions_schedule ?? ['06:00','14:00','22:00'])]
|
||||
s[2] = v
|
||||
setConfig({ ...config, band_conditions_schedule: s })
|
||||
}}
|
||||
helper="Night (default 22:00 MT)"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-slate-600">All times are Mountain Time (America/Boise). DST handled automatically.</p>
|
||||
</div>
|
||||
|
||||
{/* Master Toggles */}
|
||||
{config.toggles && (
|
||||
<MasterToggles
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue