mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 17:31:34 +00:00
feat(dashboard): MeshCore transport + per-family routing GUI controls (#10)
* feat(dashboard): MeshCore transport + per-family routing GUI controls
Add Transport mode selector (Meshtastic/MeshCore/Both) and MeshCore
host/port fields to the Config Connection section, and an independent
per-family "MeshCore channel" number input in Notifications (blank = not
broadcast on MeshCore, sends null). Extends the ConnectionConfig and
per-family toggle TS types.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* refactor(routing): MeshCore routing by channel name, not index
MeshCore channels are {name,PSK} (up to 40+ slots, not Meshtastic's 0-7).
The send index is a fragile slot position, so store the channel NAME per
family and resolve name->slot against the companion's live channel table
at send time; never blind-send to an unresolved slot. GUI field becomes a
channel-name text box. meshtastic path unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(routing): thread per-family meshcore_channel through the broadcast send path
MeshBroadcastChannel now carries the rule's meshcore_channel name and
passes it to send_message, so per-family MeshCore routing actually fires
end-to-end (dispatcher -> channel -> composite -> MeshCoreTransport).
Meshtastic path unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6bc57709a2
commit
24cb6a31df
14 changed files with 358 additions and 45 deletions
|
|
@ -23,6 +23,9 @@ interface ConnectionConfig {
|
|||
serial_port: string
|
||||
tcp_host: string
|
||||
tcp_port: number
|
||||
transport?: string
|
||||
meshcore_host?: string
|
||||
meshcore_port?: number
|
||||
}
|
||||
|
||||
interface ResponseConfig {
|
||||
|
|
@ -707,9 +710,23 @@ function BotSection({ data, onChange }: { data: BotConfig; onChange: (d: BotConf
|
|||
}
|
||||
|
||||
function ConnectionSection({ data, onChange }: { data: ConnectionConfig; onChange: (d: ConnectionConfig) => void }) {
|
||||
const transport = data.transport ?? 'meshtastic'
|
||||
const showMeshCore = transport === 'meshcore' || transport === 'both'
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<SectionDescription text={SECTION_DESCRIPTIONS.connection} />
|
||||
<SelectInput
|
||||
label="Transport Mode"
|
||||
value={transport}
|
||||
onChange={(v) => onChange({ ...data, transport: v })}
|
||||
options={[
|
||||
{ value: 'meshtastic', label: 'Meshtastic' },
|
||||
{ value: 'meshcore', label: 'MeshCore' },
|
||||
{ value: 'both', label: 'Both' },
|
||||
]}
|
||||
helper="Which radio transport(s) MeshAI uses"
|
||||
info="Meshtastic: connect to a Meshtastic radio only. MeshCore: connect to a MeshCore node only. Both: connect to both simultaneously for dual-transport operation."
|
||||
/>
|
||||
<SelectInput
|
||||
label="Connection Type"
|
||||
value={data.type}
|
||||
|
|
@ -749,6 +766,29 @@ function ConnectionSection({ data, onChange }: { data: ConnectionConfig; onChang
|
|||
/>
|
||||
</div>
|
||||
)}
|
||||
{showMeshCore && (
|
||||
<div className="space-y-4 pt-2 border-t border-[#1e2a3a]">
|
||||
<div className="text-xs text-slate-500 uppercase tracking-wide">MeshCore Connection</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<TextInput
|
||||
label="MeshCore Host"
|
||||
value={data.meshcore_host ?? ''}
|
||||
onChange={(v) => onChange({ ...data, meshcore_host: v })}
|
||||
placeholder="192.168.1.100"
|
||||
helper="IP or hostname of the MeshCore node"
|
||||
info="Address of the MeshCore node to connect to."
|
||||
/>
|
||||
<NumberInput
|
||||
label="MeshCore Port"
|
||||
value={data.meshcore_port ?? 5525}
|
||||
onChange={(v) => onChange({ ...data, meshcore_port: v })}
|
||||
min={1}
|
||||
max={65535}
|
||||
helper="MeshCore TCP port (default 5525)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ interface NotificationToggle {
|
|||
regions: string[]
|
||||
severity_channels: Record<string, string[]>
|
||||
broadcast_channel: number | null
|
||||
meshcore_channel?: string | null
|
||||
node_ids: string[]
|
||||
smtp_host: string
|
||||
smtp_port: number
|
||||
|
|
@ -1611,6 +1612,17 @@ function MasterToggles({ toggles, onChange }: {
|
|||
</table>
|
||||
<ListInput label="Regions (empty = all)" value={t.regions || []} onChange={(v) => upd(key, { regions: v })} placeholder="Add region..." /> <div className="text-xs text-slate-500 pt-1">Channel config</div>
|
||||
<NumberInput label="Broadcast channel" value={t.broadcast_channel ?? 0} onChange={(v) => upd(key, { broadcast_channel: v })} />
|
||||
<div className="space-y-1">
|
||||
<label className="flex items-center text-xs text-slate-500 uppercase tracking-wide">MeshCore channel</label>
|
||||
<input
|
||||
type="text"
|
||||
value={t.meshcore_channel != null ? t.meshcore_channel : ''}
|
||||
onChange={(e) => upd(key, { meshcore_channel: e.target.value === '' ? null : e.target.value })}
|
||||
placeholder=""
|
||||
className="w-full px-3 py-2 bg-[#0a0e17] border border-[#1e2a3a] rounded text-sm text-slate-200 font-mono focus:outline-none focus:border-accent"
|
||||
/>
|
||||
<p className="text-xs text-slate-600">MeshCore channel name on your companion (e.g. AIDA); blank = not broadcast on MeshCore.</p>
|
||||
</div>
|
||||
<ListInput label="DM node IDs" value={t.node_ids || []} onChange={(v) => upd(key, { node_ids: v })} placeholder="!nodeid" />
|
||||
<ListInput label="Email recipients" value={t.recipients || []} onChange={(v) => upd(key, { recipients: v })} placeholder="ops@example.com" />
|
||||
<TextInput label="SMTP host" value={t.smtp_host || ''} onChange={(v) => upd(key, { smtp_host: v })} placeholder="smtp.example.com" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue