Node SDRs: pin OP25 and each service to a dongle by serial (node-26#11)

Pairs with node-26 feat/sdr-pins. NodeRecord gains sdr_pins
(service -> serial), sdr_devices and op25_sdr_serial, mirrored from the
node's checkin. PATCH /nodes/{id} validates pins (known services, one
dongle per service) and sends priority/pins as a 'set_sdr_config'
command, never a config re-push. The node restarts OP25 only when OP25's
own dongle changes. The node page's section becomes 'SDRs' with an OP25
SDR dropdown ('Automatic (first SDR)' + detected dongles) and a
per-service dongle dropdown ('Any spare SDR'), plus duplicate-serial and
double-pin warnings.

Verified: c2-core pytest 490 passed; frontend tsc --noEmit clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-09-27 14:36:49 -04:00
co-authored by Claude Opus 5.5
parent c95498e7fe
commit fb2d737d6f
9 changed files with 315 additions and 167 deletions
+2 -2
View File
@@ -8,7 +8,7 @@ import { useSystems } from "@/lib/useSystems";
import { useCalls } from "@/lib/useCalls";
import { StatusBadge } from "@/components/StatusBadge";
import { NodeConfigModal } from "@/components/NodeConfigModal";
import { SecondarySdrPriority } from "@/components/SecondarySdrPriority";
import { SdrSettings } from "@/components/SdrSettings";
import { CallRow } from "@/components/CallRow";
import { MachineOutputNotice } from "@/components/ui/MachineOutputNotice";
import { useAuth } from "@/components/AuthProvider";
@@ -337,7 +337,7 @@ export default function NodeDetailPage() {
)}
</div>
<SecondarySdrPriority node={node} canEdit={isAdmin} />
<SdrSettings node={node} canEdit={isAdmin} />
{/* Recent calls */}
<section>