Query now filters out 100% contained and >7d stale fires. Line format
uses county-only anchor (no Photon geocoder). Greedy 220-byte packing
ensures the digest fits in a single Meshtastic frame.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds (reminders_wfigs, enabled) to REGISTRY (default=False) and
gates _tick_adapter() on the flag — when false, wfigs fire reminders
are skipped entirely. Use the digest instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
A) _render() now emits multi-line format matching Fire/Roads style:
emoji prefix M{mag} — place / Depth · coords / TSUNAMI WARNING
B) Environment.tsx usgs_quake panel replaced — dead min_magnitude/bbox
controls removed, wired to real adapter_config keys: global_mag_floor,
regional_mag_floor, regional_radius_mi, escalate_mag_floor,
broadcast_pager_alerts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move work zone settings out of itd_511 into dedicated wzdx adapter:
- config.py: add WZDxConfig dataclass with feed settings
- defaults.py: migrate 3 work_zone keys to wzdx namespace (broadcast,
min_severity, sub_types) + add ADAPTER_META entry
- incident_handler.py: work zone gate reads adapter_config.wzdx
- Environment.tsx: full WzdxConfig state/load/save/discard, native feed
fields when feed_source!=central, broadcast settings panel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 'wzdx' adapter key with its own META entry and render block.
Move work zone controls (enable toggle, min severity, sub-types)
out of the roads511 panel into the new WZDx tab. Data still
loads/saves via /api/adapter-config/itd_511 using the existing
roads511Config state. The wzdx panel mirrors roads511 enabled and
feed_source since they share the same backend adapter.
Bundle: D045j2lq -> BiMKNe5L.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
defaults.py: add work_zone_enabled (bool, default false),
work_zone_min_severity (str, default Minor), work_zone_sub_types
(json, default [road_works, lane_closed, road_closed]) to itd_511.
incident_handler.py: replace hardcoded work_zone return None with
adapter_config-driven gate. Resolve sub_type and event_sev before
the work_zone check so severity and sub-type filters apply. Non-work-zone
events keep the existing min_severity / enabled_categories / enabled_sub_types
filters unchanged.
Environment.tsx: add work_zone_enabled, work_zone_min_severity,
work_zone_sub_types to Roads511Config. Load/save/discard wired. Work Zones
section in roads511 panel with enable toggle, min severity dropdown, and
sub-type checkboxes (visible only when enabled).
Bundle: KLGUZQYL -> D045j2lq.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Includes NWS broadcast filter controls from 31c464c that were missing
from the previous bundle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
consumer.py: return None immediately for work_zone/road_closure/road_incident
categories instead of routing through format_work_zone_mesh.
incident_handler.py: add work_zone kind to _parse_itd_511_incident and return
None immediately so itd_511 work_zone events never reach change-detection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add NwsConfig adapter config (broadcast_severities, duplicate_allowed_after_seconds)
with load/save/discard/change-detection wiring. When feed_source=central, hide
native-only fields (User Agent, Tick Seconds) and show Broadcast Filters section
with severity checkboxes and re-broadcast cooldown input.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop NWSheadline entirely. Build line 2 from expires_epoch (formatted
to local America/Boise time with timezone abbrev) and first areaDesc
segment (truncated to 50 chars at word boundary).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove all headline casing (title-case, sentence-case, TZ regex). Pass
the NWSheadline string through as-is from the CAP parameters, only
applying word-boundary truncation to 80 UTF-8 bytes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace .title() with manual sentence-casing: capitalize first char,
lowercase the rest, then re-uppercase timezone abbreviations (MDT, MST,
PDT, PST, CDT, CST, EDT, EST, UTC) via regex.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix 1: wind.lower() so 60 MPH winds becomes 60 mph winds.
Fix 2: rstrip trailing period/comma/space from locations text.
Fix 3: bearing is direction storm moves TOWARD, not FROM — remove
the +180 flip and use (deg+22.5)/45 for correct compass bucketing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cut at last space before 80 UTF-8 bytes instead of hard-slicing at 77
chars with trailing dots.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop the instruction line (line 6) from NWS wire output entirely.
Remove the _SAME_INSTRUCTION dict that was added in 503c16d.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Short, actionable instructions keyed by SAME event code (TOR -> "Seek
shelter immediately.", SVR -> "Move indoors now.", etc.). Falls back to
the CAP instruction field when no SAME code matches. Truncates at 40
UTF-8 bytes to keep wire size compact for mesh broadcast.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace single-line _render() with structured 6-line format:
L1: SAME emoji + event type + NWS office (from WMO identifier)
L2: area (first areaDesc segment, max 60 chars)
L3: hazard (from HAZARD.../TORNADO... or maxWindGust/maxHailSize params)
L4: impact (from IMPACT... in description)
L5: expires
L6: instruction (max 80 chars)
Add module-level helpers: _SAME_EMOJI, _NWS_OFFICE_SHORT, _nws_office(),
_parse_nws_description(). Emoji prefers SAME event code, falls back to
_emoji_for_event() substring match. All _render() call sites pass d=d.
Update test to match new format (coordinates removed from wire).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The enrichment pipeline writes to d._enriched, not d._enrichment.
Fix both _parse_state_511_incident and _parse_itd_511_incident.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ITD 511 sometimes sends lowercase direction strings (e.g. "east"
instead of "East"). Add lowercase and abbreviated lowercase keys
so the renderer resolves them without falling through to raw echo.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add min_magnitude dropdown (1-4), drop_non_present and
drop_zero_magnitude toggles to the TomTom Traffic adapter card.
State loads from /api/adapter-config/tomtom_incidents on mount
and saves changed keys on save, following the same pattern as
the WFIGS and fires config panels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add tomtom_incidents.min_magnitude setting (default 4 = severe)
to adapter_config registry. Replace the hardcoded magnitude==0
drop check with a config-driven floor that silently drops any
TomTom event below the configured threshold.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Let road stay None when road_numbers is absent so the renderer
uses the from → to segment format instead of clobbering it with
the raw from string.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Line 2 now falls back to from_loc → to_loc when road is absent
(common for TomTom street-level incidents without road_numbers).
Line 3 renders length (meters from TomTom) as human-readable
distance (mi or m) alongside delay and lanes_affected.
Add length field to _parse_tomtom_incident return dict.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the prefix parameter from _render() and all callers — the
New:/Update: labels are no longer surfaced in the multi-line format.
Add comment field extraction to _parse_state_511_incident and
_parse_itd_511_incident return dicts. Render comment as line 3b
when it provides additional context beyond lanes_affected and is
<=140 chars, skipping duplicates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace single-line _render() with structured multi-line output:
Line 1: emoji + display name + city/state anchor
Line 2: road + full direction (Eastbound) + mile marker
Line 3: lanes affected + delay
Line 4: cause (if non-default)
Add _SUB_TYPE_DISPLAY and _DIRECTION_LONG mappings. Extend
_parse_state_511_incident and _parse_itd_511_incident return dicts
with lanes_affected, cause, description, and mile_marker fields.
Add mile_marker: None to _parse_tomtom_incident for consistency.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Events with severity=immediate skip the per-toggle cooldown check
entirely — they are already rate-controlled by source handler change
detection. Also set cooldown_seconds default to 0 (disabled).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
POST /api/debug/clear-cooldowns clears both in-memory toggle
cooldown map and SQLite dispatcher_cooldowns table.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Disable all standalone FIRMS broadcast paths (cluster, halt, spotting)
by inserting early return None. Growth events now use the shared WFIGS
_render() for consistent multi-line format with movement data, and set
_severity_override=immediate + _cooldown_suffix for per-fire cooldown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each WFIGS fire event now carries its irwin_id as _cooldown_suffix in
event.data. The dispatcher incorporates this suffix into the cooldown
key region field, giving each fire its own independent cooldown slot
instead of sharing one per (toggle, category, region).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove _build_prompt, _llm_render, _terse_fallback and all LLM backend
references. render_digest() now queries the fires table directly and
builds a structured multi-line wire: header with count, up to 5 fires
with acres/containment/anchor, and a +N more overflow line.
FireDigestScheduler no longer accepts or uses llm_backend. Updated the
pipeline __init__.py call site accordingly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fire events are already change-detected by the wfigs handler so the
grouper coalescing window adds no value and causes commit callbacks
to be lost when events are replaced. Setting severity to immediate
unconditionally bypasses the grouper entirely.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply .title() to all town name returns in _location_anchor so
anchors render with proper capitalisation regardless of source
casing. Remove the redundant Near: prefix from the location line
in _render — the anchor text is self-describing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove hamlet, suburb, and locality from _TOWN_OSM_VALUES so the
nearest_town Photon lookup only returns meaningful population centers,
avoiding misleading anchors from tiny named places.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Acres: prepend IncidentSize to _WFIGS_ACRES_RAW_KEYS so the normalizer
picks up the primary size field before falling back to DiscoveryAcres
and FinalAcres.
Location anchor: query the curated town_anchors table before falling
back to the Photon geocoder nearest_town call, giving consistent
anchor names for Idaho fires.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Normalizer: add fire_cause, agency, personnel, unique_fire_id from
WFIGS raw payload to the normalized incident dict.
Renderer: replace single-line wire format with structured multi-line
output — header, size/contained with bold deltas on updates, location
anchor, cause/discovered date, and unique fire ID. Update call sites
pass last_bcast_acres and last_bcast_contained for case-(iii) updates
to enable delta calculation and selective bolding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
event.data can contain callables and internal _on_ callbacks that
cause json.dumps to fail with TypeError. Filter these out before
serializing to SQLite.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WFIGS API returns both "WF" and "wildfire" as IncidentTypeCategory
values. The previous check only accepted "WF", silently dropping
wildfire-typed incidents.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the non-WF filter returns None, the caller must check before
assigning to n[_kind]. Fixes TypeError on non-wildfire incidents.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fire events now carry _severity_override in the data dict:
- New fires (case i/ii): priority by default
- Update fires (case iii): priority by default
- All fires: immediate if acres > 1000 OR contained_pct == 0
consumer.py checks for _severity_override before falling back to
map_severity(inner.severity). This ensures fire broadcasts are
prioritized appropriately in the dispatch queue.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add 'Central' as first tab in FAMILIES array (before Weather)
- Import Server icon from lucide-react
- Remove Central Connection card from header area
- Render Central config in its own tab panel (no adapter sub-tabs)
The Central tab now shows URL, Durable, and Region fields with the
enabled toggle, matching the previous inline card behavior.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>