mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-06-11 01:14:45 +02:00
refactor: promote WZDx to first-class adapter with own config namespace
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>
This commit is contained in:
parent
dc64430394
commit
96f14afba8
6 changed files with 253 additions and 185 deletions
|
|
@ -26,6 +26,7 @@ interface EnvConfig {
|
||||||
usgs_quake: { enabled: boolean; tick_seconds: number; feed_url: string; min_magnitude: number; bbox: number[]; region: string; feed_source?: FeedSource }
|
usgs_quake: { enabled: boolean; tick_seconds: number; feed_url: string; min_magnitude: number; bbox: number[]; region: string; feed_source?: FeedSource }
|
||||||
traffic: { enabled: boolean; tick_seconds: number; api_key: string; corridors: { name: string; lat: number; lon: number }[]; feed_source?: FeedSource }
|
traffic: { enabled: boolean; tick_seconds: number; api_key: string; corridors: { name: string; lat: number; lon: number }[]; feed_source?: FeedSource }
|
||||||
roads511: { enabled: boolean; tick_seconds: number; api_key: string; base_url: string; endpoints: string[]; bbox: number[]; feed_source?: FeedSource }
|
roads511: { enabled: boolean; tick_seconds: number; api_key: string; base_url: string; endpoints: string[]; bbox: number[]; feed_source?: FeedSource }
|
||||||
|
wzdx: { enabled: boolean; tick_seconds: number; api_key: string; base_url: string; endpoints: string[]; bbox: number[]; feed_source?: FeedSource }
|
||||||
firms: { enabled: boolean; tick_seconds: number; map_key: string; source: string; bbox: number[]; day_range: number; confidence_min: string; proximity_km: number; feed_source?: FeedSource }
|
firms: { enabled: boolean; tick_seconds: number; map_key: string; source: string; bbox: number[]; day_range: number; confidence_min: string; proximity_km: number; feed_source?: FeedSource }
|
||||||
central?: { enabled: boolean; url: string; durable: string; region: string }
|
central?: { enabled: boolean; url: string; durable: string; region: string }
|
||||||
}
|
}
|
||||||
|
|
@ -51,12 +52,16 @@ interface Roads511Config {
|
||||||
min_severity: string
|
min_severity: string
|
||||||
enabled_categories: string[]
|
enabled_categories: string[]
|
||||||
enabled_sub_types: string[]
|
enabled_sub_types: string[]
|
||||||
work_zone_enabled: boolean
|
|
||||||
work_zone_min_severity: string
|
|
||||||
work_zone_sub_types: string[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TomTom adapter config shape
|
// WZDx adapter config shape
|
||||||
|
interface WzdxConfig {
|
||||||
|
broadcast: boolean
|
||||||
|
min_severity: string
|
||||||
|
sub_types: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NWS adapter config shape
|
||||||
interface NwsConfig {
|
interface NwsConfig {
|
||||||
broadcast_severities: string[]
|
broadcast_severities: string[]
|
||||||
duplicate_allowed_after_seconds: number
|
duplicate_allowed_after_seconds: number
|
||||||
|
|
@ -261,11 +266,14 @@ export default function Environment() {
|
||||||
min_severity: "None",
|
min_severity: "None",
|
||||||
enabled_categories: ["incident", "closure"],
|
enabled_categories: ["incident", "closure"],
|
||||||
enabled_sub_types: ["accident", "road_closed", "closure", "lane_closed", "vehicle_on_fire", "flooding", "debris"],
|
enabled_sub_types: ["accident", "road_closed", "closure", "lane_closed", "vehicle_on_fire", "flooding", "debris"],
|
||||||
work_zone_enabled: false,
|
|
||||||
work_zone_min_severity: "Minor",
|
|
||||||
work_zone_sub_types: ["road_works", "lane_closed", "road_closed"],
|
|
||||||
})
|
})
|
||||||
const [roads511Original, setRoads511Original] = useState<string>("")
|
const [roads511Original, setRoads511Original] = useState<string>("")
|
||||||
|
const [wzdxConfig, setWzdxConfig] = useState<WzdxConfig>({
|
||||||
|
broadcast: false,
|
||||||
|
min_severity: "Minor",
|
||||||
|
sub_types: ["road_works", "lane_closed", "road_closed"],
|
||||||
|
})
|
||||||
|
const [wzdxOriginal, setWzdxOriginal] = useState<string>("")
|
||||||
const [nwsConfig, setNwsConfig] = useState<NwsConfig>({
|
const [nwsConfig, setNwsConfig] = useState<NwsConfig>({
|
||||||
broadcast_severities: ["Extreme", "Severe"],
|
broadcast_severities: ["Extreme", "Severe"],
|
||||||
duplicate_allowed_after_seconds: 3600,
|
duplicate_allowed_after_seconds: 3600,
|
||||||
|
|
@ -338,15 +346,27 @@ export default function Environment() {
|
||||||
min_severity: r511Data.min_severity?.value ?? "None",
|
min_severity: r511Data.min_severity?.value ?? "None",
|
||||||
enabled_categories: r511Data.enabled_categories?.value ?? ["incident", "closure"],
|
enabled_categories: r511Data.enabled_categories?.value ?? ["incident", "closure"],
|
||||||
enabled_sub_types: r511Data.enabled_sub_types?.value ?? ["accident", "road_closed", "closure", "lane_closed", "vehicle_on_fire", "flooding", "debris"],
|
enabled_sub_types: r511Data.enabled_sub_types?.value ?? ["accident", "road_closed", "closure", "lane_closed", "vehicle_on_fire", "flooding", "debris"],
|
||||||
work_zone_enabled: r511Data.work_zone_enabled?.value ?? false,
|
|
||||||
work_zone_min_severity: r511Data.work_zone_min_severity?.value ?? "Minor",
|
|
||||||
work_zone_sub_types: r511Data.work_zone_sub_types?.value ?? ["road_works", "lane_closed", "road_closed"],
|
|
||||||
}
|
}
|
||||||
setRoads511Config(cfg)
|
setRoads511Config(cfg)
|
||||||
setRoads511Original(JSON.stringify(cfg))
|
setRoads511Original(JSON.stringify(cfg))
|
||||||
}
|
}
|
||||||
} catch { /* adapter-config optional */ }
|
} catch { /* adapter-config optional */ }
|
||||||
|
|
||||||
|
// Load adapter-config for wzdx
|
||||||
|
try {
|
||||||
|
const wzdxRes = await fetch("/api/adapter-config/wzdx")
|
||||||
|
if (wzdxRes.ok) {
|
||||||
|
const wzdxData = await wzdxRes.json()
|
||||||
|
const cfg: WzdxConfig = {
|
||||||
|
broadcast: wzdxData.broadcast?.value ?? false,
|
||||||
|
min_severity: wzdxData.min_severity?.value ?? "Minor",
|
||||||
|
sub_types: wzdxData.sub_types?.value ?? ["road_works", "lane_closed", "road_closed"],
|
||||||
|
}
|
||||||
|
setWzdxConfig(cfg)
|
||||||
|
setWzdxOriginal(JSON.stringify(cfg))
|
||||||
|
}
|
||||||
|
} catch { /* adapter-config optional */ }
|
||||||
|
|
||||||
// Load adapter-config for nws
|
// Load adapter-config for nws
|
||||||
try {
|
try {
|
||||||
const nwsRes = await fetch("/api/adapter-config/nws")
|
const nwsRes = await fetch("/api/adapter-config/nws")
|
||||||
|
|
@ -386,8 +406,9 @@ export default function Environment() {
|
||||||
const hasFiresChanges = JSON.stringify(firesConfig) !== firesOriginal
|
const hasFiresChanges = JSON.stringify(firesConfig) !== firesOriginal
|
||||||
const hasTomtomChanges = JSON.stringify(tomtomConfig) !== tomtomOriginal
|
const hasTomtomChanges = JSON.stringify(tomtomConfig) !== tomtomOriginal
|
||||||
const hasRoads511Changes = JSON.stringify(roads511Config) !== roads511Original
|
const hasRoads511Changes = JSON.stringify(roads511Config) !== roads511Original
|
||||||
|
const hasWzdxChanges = JSON.stringify(wzdxConfig) !== wzdxOriginal
|
||||||
const hasNwsChanges = JSON.stringify(nwsConfig) !== nwsOriginal
|
const hasNwsChanges = JSON.stringify(nwsConfig) !== nwsOriginal
|
||||||
const hasChanges = hasEnvChanges || hasWfigsChanges || hasFiresChanges || hasTomtomChanges || hasRoads511Changes || hasNwsChanges
|
const hasChanges = hasEnvChanges || hasWfigsChanges || hasFiresChanges || hasTomtomChanges || hasRoads511Changes || hasWzdxChanges || hasNwsChanges
|
||||||
|
|
||||||
|
|
||||||
const saveAdapterConfig = async (adapterName: string, key: string, value: unknown) => {
|
const saveAdapterConfig = async (adapterName: string, key: string, value: unknown) => {
|
||||||
|
|
@ -482,18 +503,24 @@ const save = async () => {
|
||||||
if (JSON.stringify(roads511Config.enabled_sub_types) !== JSON.stringify(orig.enabled_sub_types)) {
|
if (JSON.stringify(roads511Config.enabled_sub_types) !== JSON.stringify(orig.enabled_sub_types)) {
|
||||||
await saveAdapterConfig("itd_511", "enabled_sub_types", roads511Config.enabled_sub_types)
|
await saveAdapterConfig("itd_511", "enabled_sub_types", roads511Config.enabled_sub_types)
|
||||||
}
|
}
|
||||||
if (roads511Config.work_zone_enabled !== orig.work_zone_enabled) {
|
|
||||||
await saveAdapterConfig("itd_511", "work_zone_enabled", roads511Config.work_zone_enabled)
|
|
||||||
}
|
|
||||||
if (roads511Config.work_zone_min_severity !== orig.work_zone_min_severity) {
|
|
||||||
await saveAdapterConfig("itd_511", "work_zone_min_severity", roads511Config.work_zone_min_severity)
|
|
||||||
}
|
|
||||||
if (JSON.stringify(roads511Config.work_zone_sub_types) !== JSON.stringify(orig.work_zone_sub_types)) {
|
|
||||||
await saveAdapterConfig("itd_511", "work_zone_sub_types", roads511Config.work_zone_sub_types)
|
|
||||||
}
|
|
||||||
setRoads511Original(JSON.stringify(roads511Config))
|
setRoads511Original(JSON.stringify(roads511Config))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save wzdx adapter config changes
|
||||||
|
if (hasWzdxChanges) {
|
||||||
|
const orig = JSON.parse(wzdxOriginal) as WzdxConfig
|
||||||
|
if (wzdxConfig.broadcast !== orig.broadcast) {
|
||||||
|
await saveAdapterConfig("wzdx", "broadcast", wzdxConfig.broadcast)
|
||||||
|
}
|
||||||
|
if (wzdxConfig.min_severity !== orig.min_severity) {
|
||||||
|
await saveAdapterConfig("wzdx", "min_severity", wzdxConfig.min_severity)
|
||||||
|
}
|
||||||
|
if (JSON.stringify(wzdxConfig.sub_types) !== JSON.stringify(orig.sub_types)) {
|
||||||
|
await saveAdapterConfig("wzdx", "sub_types", wzdxConfig.sub_types)
|
||||||
|
}
|
||||||
|
setWzdxOriginal(JSON.stringify(wzdxConfig))
|
||||||
|
}
|
||||||
|
|
||||||
// Save nws adapter config changes
|
// Save nws adapter config changes
|
||||||
if (hasNwsChanges) {
|
if (hasNwsChanges) {
|
||||||
const orig = JSON.parse(nwsOriginal) as NwsConfig
|
const orig = JSON.parse(nwsOriginal) as NwsConfig
|
||||||
|
|
@ -521,6 +548,7 @@ const save = async () => {
|
||||||
setFiresConfig(JSON.parse(firesOriginal || JSON.stringify(firesConfig)))
|
setFiresConfig(JSON.parse(firesOriginal || JSON.stringify(firesConfig)))
|
||||||
setTomtomConfig(JSON.parse(tomtomOriginal || JSON.stringify(tomtomConfig)))
|
setTomtomConfig(JSON.parse(tomtomOriginal || JSON.stringify(tomtomConfig)))
|
||||||
setRoads511Config(JSON.parse(roads511Original || JSON.stringify(roads511Config)))
|
setRoads511Config(JSON.parse(roads511Original || JSON.stringify(roads511Config)))
|
||||||
|
setWzdxConfig(JSON.parse(wzdxOriginal || JSON.stringify(wzdxConfig)))
|
||||||
setNwsConfig(JSON.parse(nwsOriginal || JSON.stringify(nwsConfig)))
|
setNwsConfig(JSON.parse(nwsOriginal || JSON.stringify(nwsConfig)))
|
||||||
}
|
}
|
||||||
const restart = async () => {
|
const restart = async () => {
|
||||||
|
|
@ -747,20 +775,35 @@ const save = async () => {
|
||||||
</div>
|
</div>
|
||||||
</>)
|
</>)
|
||||||
case 'wzdx': return (<>
|
case 'wzdx': return (<>
|
||||||
<div className="space-y-4">
|
{env.wzdx?.feed_source !== 'central' && (
|
||||||
|
<>
|
||||||
|
<TextInput label="Base URL" value={env.wzdx?.base_url ?? ''} onChange={(v) => up({ wzdx: { ...env.wzdx!, base_url: v } })} placeholder="https://511.yourstate.gov/api/v2" />
|
||||||
|
<TextInput label="API Key" value={env.wzdx?.api_key ?? ''} onChange={(v) => up({ wzdx: { ...env.wzdx!, api_key: v } })} type="password" helper="Leave empty if not required" />
|
||||||
|
<NumberInput label="Tick Seconds" value={env.wzdx?.tick_seconds ?? 300} onChange={(v) => up({ wzdx: { ...env.wzdx!, tick_seconds: v } })} min={60} />
|
||||||
|
<ListInput label="Endpoints" value={env.wzdx?.endpoints ?? ['/get/event']} onChange={(v) => up({ wzdx: { ...env.wzdx!, endpoints: v } })} helper="e.g., /get/event" />
|
||||||
|
<div className="grid grid-cols-4 gap-2">
|
||||||
|
{(['West', 'South', 'East', 'North'] as const).map((lbl, i) => (
|
||||||
|
<NumberInput key={lbl} label={lbl} value={env.wzdx?.bbox?.[i] ?? 0} onChange={(v) => { const b = [...(env.wzdx?.bbox || [0, 0, 0, 0])]; b[i] = v; up({ wzdx: { ...env.wzdx!, bbox: b } }) }} step={0.01} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-slate-500">Bounding box [W,S,E,N] geographic filter</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<div className="border-t border-slate-700/50 pt-4 mt-4">
|
||||||
|
<div className="text-xs font-medium text-slate-400 uppercase tracking-wider mb-3">Broadcast Settings</div>
|
||||||
<label className="flex items-center justify-between">
|
<label className="flex items-center justify-between">
|
||||||
<span className="text-sm text-slate-300">Enable Work Zone Broadcasts</span>
|
<span className="text-sm text-slate-300">Broadcast work zone events</span>
|
||||||
<input type="checkbox" checked={roads511Config.work_zone_enabled}
|
<input type="checkbox" checked={wzdxConfig.broadcast}
|
||||||
onChange={(e) => setRoads511Config({...roads511Config, work_zone_enabled: e.target.checked})}
|
onChange={(e) => setWzdxConfig({...wzdxConfig, broadcast: e.target.checked})}
|
||||||
className="w-4 h-4 rounded accent-blue-500" />
|
className="w-4 h-4 rounded accent-blue-500" />
|
||||||
</label>
|
</label>
|
||||||
{roads511Config.work_zone_enabled && (
|
{wzdxConfig.broadcast ? (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3 mt-3">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs text-slate-400 mb-1 block">Min Severity</label>
|
<label className="text-xs text-slate-400 mb-1 block">Min Severity</label>
|
||||||
<select
|
<select
|
||||||
value={roads511Config.work_zone_min_severity}
|
value={wzdxConfig.min_severity}
|
||||||
onChange={(e) => setRoads511Config({...roads511Config, work_zone_min_severity: e.target.value})}
|
onChange={(e) => setWzdxConfig({...wzdxConfig, min_severity: e.target.value})}
|
||||||
className="w-full bg-slate-900 border border-slate-700 rounded px-3 py-2 text-sm"
|
className="w-full bg-slate-900 border border-slate-700 rounded px-3 py-2 text-sm"
|
||||||
>
|
>
|
||||||
<option value="None">None (all)</option>
|
<option value="None">None (all)</option>
|
||||||
|
|
@ -773,8 +816,8 @@ const save = async () => {
|
||||||
<div className="flex gap-6">
|
<div className="flex gap-6">
|
||||||
{([['road_works', 'Road Works'], ['lane_closed', 'Lane Closure'], ['road_closed', 'Road Closed']] as const).map(([val, label]) => (
|
{([['road_works', 'Road Works'], ['lane_closed', 'Lane Closure'], ['road_closed', 'Road Closed']] as const).map(([val, label]) => (
|
||||||
<label key={val} className="flex items-center gap-2 cursor-pointer">
|
<label key={val} className="flex items-center gap-2 cursor-pointer">
|
||||||
<input type="checkbox" checked={roads511Config.work_zone_sub_types.includes(val)}
|
<input type="checkbox" checked={wzdxConfig.sub_types.includes(val)}
|
||||||
onChange={(e) => { const cur = roads511Config.work_zone_sub_types; setRoads511Config({...roads511Config, work_zone_sub_types: e.target.checked ? [...cur, val] : cur.filter(s => s !== val)}) }}
|
onChange={(e) => { const cur = wzdxConfig.sub_types; setWzdxConfig({...wzdxConfig, sub_types: e.target.checked ? [...cur, val] : cur.filter(s => s !== val)}) }}
|
||||||
className="w-4 h-4 rounded accent-blue-500" />
|
className="w-4 h-4 rounded accent-blue-500" />
|
||||||
<span className="text-sm text-slate-300">{label}</span>
|
<span className="text-sm text-slate-300">{label}</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
@ -782,6 +825,8 @@ const save = async () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-xs text-slate-500 mt-2">Work zone events stored for LLM context only {'\u2014'} no mesh broadcasts.</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>)
|
</>)
|
||||||
|
|
@ -917,10 +962,10 @@ const save = async () => {
|
||||||
<AdapterPanel
|
<AdapterPanel
|
||||||
title={META[activeAdapter].label}
|
title={META[activeAdapter].label}
|
||||||
subtitle={META[activeAdapter].subtitle}
|
subtitle={META[activeAdapter].subtitle}
|
||||||
enabled={activeAdapter === 'wzdx' ? (a['roads511']?.enabled ?? false) : (a[activeAdapter]?.enabled ?? false)}
|
enabled={a[activeAdapter]?.enabled ?? false}
|
||||||
onEnabled={(v) => setAdapterField(activeAdapter === 'wzdx' ? 'roads511' : activeAdapter, { enabled: v })}
|
onEnabled={(v) => setAdapterField(activeAdapter, { enabled: v })}
|
||||||
feedSource={activeAdapter === 'wzdx' ? (a['roads511']?.feed_source ?? 'native') : (a[activeAdapter]?.feed_source ?? 'native')}
|
feedSource={a[activeAdapter]?.feed_source ?? 'native'}
|
||||||
onFeedSource={(v) => setAdapterField(activeAdapter === 'wzdx' ? 'roads511' : activeAdapter, { feed_source: v })}
|
onFeedSource={(v) => setAdapterField(activeAdapter, { feed_source: v })}
|
||||||
hasCentral={META[activeAdapter].hasCentral}
|
hasCentral={META[activeAdapter].hasCentral}
|
||||||
nativeOnly={META[activeAdapter].nativeOnly}
|
nativeOnly={META[activeAdapter].nativeOnly}
|
||||||
hasKey={META[activeAdapter].hasKey}
|
hasKey={META[activeAdapter].hasKey}
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ REGISTRY: dict[tuple[str, str], dict[str, Any]] = {
|
||||||
},
|
},
|
||||||
|
|
||||||
# =================================================================
|
# =================================================================
|
||||||
# ITD_511 -- 6 settings (severity gate, category filter, sub-type filter, work zone)
|
# ITD_511 -- 3 settings (severity gate, category filter, sub-type filter)
|
||||||
# =================================================================
|
# =================================================================
|
||||||
("itd_511", "min_severity"): {
|
("itd_511", "min_severity"): {
|
||||||
"default": "None",
|
"default": "None",
|
||||||
|
|
@ -213,17 +213,20 @@ REGISTRY: dict[tuple[str, str], dict[str, Any]] = {
|
||||||
"type": "json",
|
"type": "json",
|
||||||
"description": "Which sub_types to broadcast. Empty list = all.",
|
"description": "Which sub_types to broadcast. Empty list = all.",
|
||||||
},
|
},
|
||||||
("itd_511", "work_zone_enabled"): {
|
# =================================================================
|
||||||
|
# WZDX -- 3 settings (broadcast gate, severity gate, sub-type filter)
|
||||||
|
# =================================================================
|
||||||
|
("wzdx", "broadcast"): {
|
||||||
"default": False,
|
"default": False,
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"description": "Broadcast work zone events (road construction, lane closures). Off by default.",
|
"description": "Broadcast work zone events (road construction, lane closures). Off by default.",
|
||||||
},
|
},
|
||||||
("itd_511", "work_zone_min_severity"): {
|
("wzdx", "min_severity"): {
|
||||||
"default": "Minor",
|
"default": "Minor",
|
||||||
"type": "str",
|
"type": "str",
|
||||||
"description": "Minimum severity to broadcast work zones: None, Minor, Major.",
|
"description": "Minimum severity to broadcast work zones: None, Minor, Major.",
|
||||||
},
|
},
|
||||||
("itd_511", "work_zone_sub_types"): {
|
("wzdx", "sub_types"): {
|
||||||
"default": ["road_works", "lane_closed", "road_closed"],
|
"default": ["road_works", "lane_closed", "road_closed"],
|
||||||
"type": "json",
|
"type": "json",
|
||||||
"description": "Work zone sub-types to broadcast. Empty = all.",
|
"description": "Work zone sub-types to broadcast. Empty = all.",
|
||||||
|
|
@ -632,6 +635,11 @@ ADAPTER_META: dict[str, dict[str, Any]] = {
|
||||||
"include_in_llm_context": True,
|
"include_in_llm_context": True,
|
||||||
"description": "Idaho Transportation Department incident/closure/work-zone feed.",
|
"description": "Idaho Transportation Department incident/closure/work-zone feed.",
|
||||||
},
|
},
|
||||||
|
"wzdx": {
|
||||||
|
"display_name": "WZDx work zones",
|
||||||
|
"include_in_llm_context": True,
|
||||||
|
"description": "Work zone broadcast gate and sub-type/severity filters.",
|
||||||
|
},
|
||||||
"band_conditions": {
|
"band_conditions": {
|
||||||
"display_name": "Band conditions (HF propagation)",
|
"display_name": "Band conditions (HF propagation)",
|
||||||
"include_in_llm_context": True,
|
"include_in_llm_context": True,
|
||||||
|
|
|
||||||
|
|
@ -419,16 +419,16 @@ def _parse_itd_511_incident(envelope: dict, category_raw: str, now: int) -> Opti
|
||||||
"special_event": "special_event",
|
"special_event": "special_event",
|
||||||
}.get((d.get("event_type_short") or "").lower(), "incident")
|
}.get((d.get("event_type_short") or "").lower(), "incident")
|
||||||
|
|
||||||
# Work zone gate -- configurable via adapter_config
|
# Work zone gate -- configurable via adapter_config.wzdx
|
||||||
if kind == "work_zone":
|
if kind == "work_zone":
|
||||||
if not adapter_config.itd_511.work_zone_enabled:
|
if not adapter_config.wzdx.broadcast:
|
||||||
return None
|
return None
|
||||||
# Apply severity filter
|
# Apply severity filter
|
||||||
wz_min_sev = str(adapter_config.itd_511.work_zone_min_severity or "Minor")
|
wz_min_sev = str(adapter_config.wzdx.min_severity or "Minor")
|
||||||
if sev_order.get(event_sev, 0) < sev_order.get(wz_min_sev, 0):
|
if sev_order.get(event_sev, 0) < sev_order.get(wz_min_sev, 0):
|
||||||
return None
|
return None
|
||||||
# Apply sub-type filter
|
# Apply sub-type filter
|
||||||
wz_subs = adapter_config.itd_511.work_zone_sub_types or []
|
wz_subs = adapter_config.wzdx.sub_types or []
|
||||||
if wz_subs and sub_type not in wz_subs:
|
if wz_subs and sub_type not in wz_subs:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -424,6 +424,18 @@ class Roads511Config(_SourcedFeed):
|
||||||
bbox: list = field(default_factory=list) # [west, south, east, north]
|
bbox: list = field(default_factory=list) # [west, south, east, north]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class WZDxConfig(_SourcedFeed):
|
||||||
|
"""WZDx work zone data feed settings."""
|
||||||
|
|
||||||
|
enabled: bool = False
|
||||||
|
tick_seconds: int = 300
|
||||||
|
api_key: str = "" # Supports ${ENV_VAR}
|
||||||
|
base_url: str = "" # e.g. "https://511.idaho.gov/api/v2"
|
||||||
|
endpoints: list = field(default_factory=lambda: ["/get/event"])
|
||||||
|
bbox: list = field(default_factory=list) # [west, south, east, north]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class FIRMSConfig(_SourcedFeed):
|
class FIRMSConfig(_SourcedFeed):
|
||||||
"""NASA FIRMS satellite fire hotspot settings."""
|
"""NASA FIRMS satellite fire hotspot settings."""
|
||||||
|
|
@ -471,6 +483,7 @@ class EnvironmentalConfig:
|
||||||
usgs_quake: USGSQuakeConfig = field(default_factory=USGSQuakeConfig)
|
usgs_quake: USGSQuakeConfig = field(default_factory=USGSQuakeConfig)
|
||||||
traffic: TomTomConfig = field(default_factory=TomTomConfig)
|
traffic: TomTomConfig = field(default_factory=TomTomConfig)
|
||||||
roads511: Roads511Config = field(default_factory=Roads511Config)
|
roads511: Roads511Config = field(default_factory=Roads511Config)
|
||||||
|
wzdx: WZDxConfig = field(default_factory=WZDxConfig)
|
||||||
firms: FIRMSConfig = field(default_factory=FIRMSConfig)
|
firms: FIRMSConfig = field(default_factory=FIRMSConfig)
|
||||||
central: CentralConsumerConfig = field(default_factory=CentralConsumerConfig)
|
central: CentralConsumerConfig = field(default_factory=CentralConsumerConfig)
|
||||||
|
|
||||||
|
|
@ -803,6 +816,8 @@ def _dict_to_dataclass(cls, data: dict):
|
||||||
kwargs[key] = _dict_to_dataclass(TomTomConfig, value)
|
kwargs[key] = _dict_to_dataclass(TomTomConfig, value)
|
||||||
elif key == "roads511" and isinstance(value, dict):
|
elif key == "roads511" and isinstance(value, dict):
|
||||||
kwargs[key] = _dict_to_dataclass(Roads511Config, value)
|
kwargs[key] = _dict_to_dataclass(Roads511Config, value)
|
||||||
|
elif key == "wzdx" and isinstance(value, dict):
|
||||||
|
kwargs[key] = _dict_to_dataclass(WZDxConfig, value)
|
||||||
elif key == "firms" and isinstance(value, dict):
|
elif key == "firms" and isinstance(value, dict):
|
||||||
kwargs[key] = _dict_to_dataclass(FIRMSConfig, value)
|
kwargs[key] = _dict_to_dataclass(FIRMSConfig, value)
|
||||||
elif key == "dashboard" and isinstance(value, dict):
|
elif key == "dashboard" and isinstance(value, dict):
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -8,7 +8,7 @@
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
<script type="module" crossorigin src="/assets/index-BiMKNe5L.js"></script>
|
<script type="module" crossorigin src="/assets/index-De10FgTg.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-Dp9XCfH-.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-Dp9XCfH-.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue