()
| 1419 | _wifiState.data = d; |
| 1420 | st.textContent = `${d.ap_count} AP(s) · ${new Date(d.timestamp * 1000).toLocaleTimeString()}`; |
| 1421 | wifiRender(); |
| 1422 | }).catch(e => { if (btn) btn.disabled = false; st.textContent = 'Scan failed'; }); |
| 1423 | } |
| 1424 | |
| 1425 | // One auto-refresh tick: the Wi-Fi survey plus any enabled overlays. The |
| 1426 | // Waterfall view streams on its own poll, so it's left alone here. |
| 1427 | function _wifiAutoTick() { |
| 1428 | if (_wifiState.view !== 'waterfall') wifiScan(); |
| 1429 | if (_wifiState.btOn) wifiBtScan(); |
| 1430 | if (_wifiState.zbOn && _wifiState.zbAvailable) wifiZbScan(); |
| 1431 | } |
| 1432 | |
| 1433 | // ---- Bluetooth / BLE 2.4 GHz overlay --------------------------------------- |
| 1434 | const _BT_KIND = { le: { c: '#38bdf8', t: 'BLE' }, classic: { c: '#818cf8', t: 'Classic' }, |
| 1435 | dual: { c: '#a78bfa', t: 'Dual' } }; |
| 1436 | const _BT_PRESSURE_COLOR = { low: '#64748b', moderate: '#f59e0b', high: '#ef4444' }; |
| 1437 | |
| 1438 | function wifiBtScan() { |
| 1439 | if (_wifiState.btBusy) return; |
| 1440 | _wifiState.btBusy = true; |
| 1441 | const st = document.getElementById('wifi-bt-status'); |
| 1442 | if (st) st.textContent = 'Discovering (receive-only)…'; |
| 1443 | fetch('/api/net/bt/scan?duration=12') |
| 1444 | .then(r => r.json()).then(d => { |
| 1445 | _wifiState.btBusy = false; |
| 1446 | if (d.error) { if (st) st.textContent = '⚠ ' + d.error; _wifiState.bt = null; return; } |
| 1447 | _wifiState.bt = d; |
| 1448 | // Drop a selection that dropped out of range this scan. |
| 1449 | if (_wifiState.btSelected && !(d.devices || []).some(v => v.mac === _wifiState.btSelected)) |
| 1450 | _wifiState.btSelected = null; |
| 1451 | const i = d.interference || {}; |
| 1452 | if (st) st.textContent = `${d.device_count} BT/BLE · ${d.controller || '?'}` + |
| 1453 | (d.capture === 'bluetoothctl' ? ' (fallback)' : ''); |
| 1454 | _wifiBtRender(); |
| 1455 | if (_wifiState.data) _wifiDrawSpectrum(); |
nothing calls this directly
no test coverage detected