diff --git a/templates/adsb_dashboard.html b/templates/adsb_dashboard.html index dcc1616..8d69414 100644 --- a/templates/adsb_dashboard.html +++ b/templates/adsb_dashboard.html @@ -440,6 +440,7 @@ let panelSelectionStageTimer = null; let mapCrosshairRequestId = 0; let detectedDevicesPromise = null; + let deviceDetectionRetryTimer = null; let clockInterval = null; let cleanupInterval = null; let delayedGpsInitTimer = null; @@ -1733,6 +1734,7 @@ ACARS: ${r.statistics.acarsMessages} messages`; if (delayedGpsInitTimer) { clearTimeout(delayedGpsInitTimer); delayedGpsInitTimer = null; } if (delayedDriverCheckTimer) { clearTimeout(delayedDriverCheckTimer); delayedDriverCheckTimer = null; } if (delayedAircraftDbTimer) { clearTimeout(delayedAircraftDbTimer); delayedAircraftDbTimer = null; } + if (deviceDetectionRetryTimer) { clearTimeout(deviceDetectionRetryTimer); deviceDetectionRetryTimer = null; } }); function ensureAdsbMapBootstrapped() { @@ -1769,6 +1771,7 @@ ACARS: ${r.statistics.acarsMessages} messages`; console.error('ADS-B Bias-T bootstrap warning:', e); } + showDeviceDetectionPendingState(); initDeviceSelectors() .then((devices) => checkAdsbTools(devices)) .catch((e) => { @@ -1776,6 +1779,22 @@ ACARS: ${r.statistics.acarsMessages} messages`; checkAdsbTools([]); }); + deviceDetectionRetryTimer = setTimeout(() => { + deviceDetectionRetryTimer = null; + const adsbSelect = document.getElementById('adsbDeviceSelect'); + const emptyText = adsbSelect?.options?.[0]?.textContent || ''; + const stillWaitingForDevices = adsbSelect && adsbSelect.options.length === 1 + && /No SDR|Detecting SDR/i.test(emptyText); + + if (!stillWaitingForDevices) return; + + initDeviceSelectors(true, 20000) + .then((devices) => checkAdsbTools(devices)) + .catch((e) => { + console.error('ADS-B device selector retry warning:', e); + }); + }, 6000); + try { updateClock(); clockInterval = setInterval(updateClock, 1000); @@ -1848,21 +1867,46 @@ ACARS: ${r.statistics.acarsMessages} messages`; }); } - function getDetectedDevices(force = false) { + function showDeviceDetectionPendingState() { + populateCompositeDeviceSelect(document.getElementById('adsbDeviceSelect'), [], 'Detecting SDRs...'); + populateCompositeDeviceSelect(document.getElementById('airbandDeviceSelect'), [], 'Detecting SDRs...'); + populateCompositeDeviceSelect(document.getElementById('acarsDeviceSelect'), [], 'Detecting SDRs...'); + populateCompositeDeviceSelect(document.getElementById('vdl2DeviceSelect'), [], 'Detecting SDRs...'); + } + + function getDetectedDevices(force = false, timeoutMs = 12000) { + if (force) { + detectedDevicesPromise = null; + } + if (!force && detectedDevicesPromise) { return detectedDevicesPromise; } - detectedDevicesPromise = fetchJsonWithTimeout('/devices', {}, 4000) + + detectedDevicesPromise = fetchJsonWithTimeout('/devices', {}, timeoutMs) .then((r) => r.ok ? r.json() : []) + .then((devices) => { + if (!Array.isArray(devices)) { + detectedDevicesPromise = null; + return []; + } + + if (devices.length === 0) { + detectedDevicesPromise = null; + } + + return devices; + }) .catch((err) => { console.warn('[ADS-B] Device detection failed:', err?.message || err); + detectedDevicesPromise = null; return []; }); return detectedDevicesPromise; } - function initDeviceSelectors() { - return getDetectedDevices().then((devices) => { + function initDeviceSelectors(force = false, timeoutMs = 12000) { + return getDetectedDevices(force, timeoutMs).then((devices) => { const adsbSelect = document.getElementById('adsbDeviceSelect'); const airbandSelect = document.getElementById('airbandDeviceSelect'); const acarsSelect = document.getElementById('acarsDeviceSelect');