feat(mesh): device-detected setup modal with board images + full radio settings

Plugging in a LoRa radio now pops a global two-step setup modal on any
page: step 1 shows the ACTUAL detected board (25 board SVGs vendored
from the Meshtastic web-flasher, GPL-3.0; matched by USB product string
on native-USB boards, vid:pid heuristics for bridge chips, animated
fallback otherwise); step 2 configures with Archipelago presets — the
LoRa region pre-selected from the node's location (new lat/lon→region
mapping), channel 'archipelago', node name, and an advanced firmware
pin. Options adapt per protocol: region+channel program Meshtastic;
MeshCore shows its community frequency plan (firmware owns RF); RNode
defers to the Reticulum daemon config.

Backend: mesh.configure now accepts lora_region (validated against the
driver's region table) and device_kind (probe pin); mesh.status returns
the persisted values plus per-port USB identity (sysfs vid/pid/product)
for board identification. The Mesh Device tab gains a full settings
form (region with duty-cycle/legality hints, firmware pin, channel,
name, identity broadcast) replacing the read-only panel; the old
Mesh-page-only onboarding modal is superseded by the global flow.

Mock backend: stateful mesh config so the dev UI demos the full
detect→configure round-trip (disable mesh → modal fires with Heltec V3).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-07-14 08:40:18 -04:00
co-authored by Claude Fable 5
parent c014c4b3ee
commit 7b36b5cc34
38 changed files with 5210 additions and 71 deletions
+30 -9
View File
@@ -2632,20 +2632,32 @@ app.post('/rpc/v1', (req, res) => {
// =====================================================================
case 'mesh.status': {
globalThis.__meshHeaders ||= { announce_block_headers: false, receive_block_headers: true }
// Stateful enable/disable so the dev UI can demo the global
// "mesh device detected" setup modal: disable mesh on the Mesh page
// and the modal fires (device present but not connected).
globalThis.__meshCfg ||= { enabled: true, lora_region: 'EU_868', device_kind: null, channel_name: 'archipelago', advert_name: 'archy-228' }
const mc = globalThis.__meshCfg
return res.json({
result: {
enabled: true,
device_type: 'Meshcore',
enabled: mc.enabled,
device_type: mc.enabled ? 'Meshcore' : 'unknown',
device_path: '/dev/ttyUSB0',
device_connected: true,
firmware_version: '2.3.1',
self_node_id: 42,
self_advert_name: 'archy-228',
peer_count: 4,
channel_name: 'archipelago',
device_connected: mc.enabled,
firmware_version: mc.enabled ? '2.3.1' : null,
self_node_id: mc.enabled ? 42 : null,
self_advert_name: mc.advert_name,
peer_count: mc.enabled ? 4 : 0,
channel_name: mc.channel_name,
messages_sent: 23,
messages_received: 47,
detected_devices: ['/dev/ttyUSB0'],
device_present: true,
detected_device_info: [
{ path: '/dev/ttyUSB0', vid: '10c4', pid: 'ea60', product: 'HELTEC LoRa 32 V3', manufacturer: 'Heltec' },
],
lora_region: mc.lora_region,
device_kind: mc.device_kind,
region: mc.enabled ? mc.lora_region : null,
announce_block_headers: globalThis.__meshHeaders.announce_block_headers,
receive_block_headers: globalThis.__meshHeaders.receive_block_headers,
},
@@ -2756,7 +2768,16 @@ app.post('/rpc/v1', (req, res) => {
globalThis.__meshHeaders ||= { announce_block_headers: false, receive_block_headers: true }
if (params && typeof params.announce_block_headers === 'boolean') globalThis.__meshHeaders.announce_block_headers = params.announce_block_headers
if (params && typeof params.receive_block_headers === 'boolean') globalThis.__meshHeaders.receive_block_headers = params.receive_block_headers
return res.json({ result: { configured: true, ...globalThis.__meshHeaders } })
// Stateful radio settings (see mesh.status) — lets the dev UI demo
// the detected-device modal + the Device panel settings round-trip.
globalThis.__meshCfg ||= { enabled: true, lora_region: 'EU_868', device_kind: null, channel_name: 'archipelago', advert_name: 'archy-228' }
const cfg = globalThis.__meshCfg
if (params && typeof params.enabled === 'boolean') cfg.enabled = params.enabled
if (params && typeof params.lora_region === 'string') cfg.lora_region = params.lora_region || null
if (params && typeof params.device_kind === 'string') cfg.device_kind = params.device_kind === 'auto' ? null : params.device_kind
if (params && typeof params.channel_name === 'string') cfg.channel_name = params.channel_name
if (params && typeof params.advert_name === 'string') cfg.advert_name = params.advert_name
return res.json({ result: { configured: true, enabled: cfg.enabled, lora_region: cfg.lora_region, device_kind: cfg.device_kind, ...globalThis.__meshHeaders } })
}
case 'mesh.send-invoice': {