fix(drone): conform to established SPA patterns throughout

- Device population: move refreshDroneDevices() inline to index.html
  (same pattern as refreshTscmDevices) and call it from switchMode
  alongside DroneMode.init(); remove _refreshDevices/populateSelect
  from drone.js which was never guaranteed to run before lazy-load
  completed, causing selects to stay on "Loading…" permanently

- IIFE pattern: change from named IIFE + window.DroneMode assignment
  to var DroneMode = (function(){...return{...}})() matching OOK/
  SpyStations convention

- Init guard: add _initialized flag (OOK state.initialized pattern);
  re-entry after destroy() re-registers map/SSE cleanly without
  duplicating click listeners on every mode switch

- Lifecycle: destroy() resets _initialized = false so map and SSE
  are correctly rebuilt on re-entry

- Stop phase: add isDroneRunning tracking variable in index.html;
  _setRunningUI() syncs it; switchMode stop phase now POSTs
  /drone/stop when leaving drone mode while active, matching TSCM

- /drone/devices: add monitor_capable field to WiFi interfaces,
  add running_as_root and warnings array to response (mirrors
  /tscm/devices shape); add os import; show privilege warning div
  in drone.html when not running as root

- drone.html: remove for= attribute from SDR label (plain <label>
  inside .form-group matches TSCM convention); add droneDeviceWarnings
  div for privilege warnings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
James Smith
2026-05-13 09:33:38 +01:00
parent 4ba8a40af9
commit 410225d54d
5 changed files with 163 additions and 109 deletions
+66
View File
@@ -4694,6 +4694,9 @@
if (isTscmRunning) {
stopTasks.push(awaitStopAction('tscm', () => stopTscmSweep(), LOCAL_STOP_TIMEOUT_MS));
}
if (isDroneRunning) {
stopTasks.push(awaitStopAction('drone', () => fetch('/drone/stop', { method: 'POST' }), LOCAL_STOP_TIMEOUT_MS));
}
if (stopTasks.length) {
await Promise.allSettled(stopTasks);
@@ -4919,6 +4922,11 @@
refreshTscmDevices();
}
// Initialize Drone mode when selected
if (mode === 'drone') {
refreshDroneDevices();
}
// Module destroy is now handled by moduleDestroyMap above.
// Show/hide Device Intelligence for modes that use it (not for satellite/aircraft/tscm)
@@ -12056,6 +12064,64 @@
// Scanner and receiver logic are handled by Waterfall mode.
// ============================================
// Drone Intelligence Functions
// ============================================
var isDroneRunning = false;
async function refreshDroneDevices() {
try {
const resp = await fetch('/drone/devices');
const data = await resp.json();
const devs = data.devices || {};
const wifiSel = document.getElementById('droneWifiIface');
if (wifiSel) {
wifiSel.innerHTML = '';
if (devs.wifi_interfaces && devs.wifi_interfaces.length > 0) {
devs.wifi_interfaces.forEach(iface => {
const opt = document.createElement('option');
opt.value = iface.name;
opt.textContent = iface.display_name || iface.name;
wifiSel.appendChild(opt);
});
} else {
wifiSel.innerHTML = '<option value="">No WiFi interfaces found</option>';
}
}
const sdrSel = document.getElementById('droneRtlIndex');
if (sdrSel) {
sdrSel.innerHTML = '';
if (devs.sdr_devices && devs.sdr_devices.length > 0) {
devs.sdr_devices.forEach(dev => {
const opt = document.createElement('option');
opt.value = dev.index !== undefined ? dev.index : 0;
opt.textContent = dev.display_name || dev.name || 'SDR Device';
sdrSel.appendChild(opt);
});
} else {
sdrSel.innerHTML = '<option value="">No SDR devices found</option>';
}
}
const warnEl = document.getElementById('droneDeviceWarnings');
if (warnEl) {
if (!data.running_as_root) {
warnEl.textContent = 'Not running as root — WiFi monitor mode may be unavailable.';
warnEl.style.display = 'block';
} else {
warnEl.style.display = 'none';
}
}
} catch (e) {
const wifiSel = document.getElementById('droneWifiIface');
if (wifiSel) wifiSel.innerHTML = '<option value="">Failed to load interfaces</option>';
const sdrSel = document.getElementById('droneRtlIndex');
if (sdrSel) sdrSel.innerHTML = '<option value="">Failed to load devices</option>';
}
}
// ============================================
// TSCM (Counter-Surveillance) Functions
// ============================================
+4 -2
View File
@@ -19,7 +19,7 @@
<div class="section">
<h3>WiFi Interface</h3>
<div class="form-group">
<label for="droneWifiIface">Interface (monitor mode)</label>
<label>Interface (monitor mode)</label>
<select id="droneWifiIface">
<option value="">Loading interfaces…</option>
</select>
@@ -29,7 +29,7 @@
<div class="section">
<h3>SDR Settings</h3>
<div class="form-group">
<label for="droneRtlIndex">RTL-SDR Device (433 MHz)</label>
<label>RTL-SDR Device (433 MHz)</label>
<select id="droneRtlIndex">
<option value="">Loading devices…</option>
</select>
@@ -42,6 +42,8 @@
</div>
</div>
<div id="droneDeviceWarnings" class="info-text" style="display:none; color:var(--accent-yellow); font-size:10px; padding: 0 4px;"></div>
<div class="section">
<div style="display:flex; gap:8px;">
<button id="droneStartBtn" class="run-btn" style="flex:1;">Start</button>