diff --git a/static/css/index.css b/static/css/index.css
index d84de78..555e661 100644
--- a/static/css/index.css
+++ b/static/css/index.css
@@ -3855,6 +3855,16 @@ header h1 .tagline {
.wifi-zone.mid .wifi-zone-count { color: var(--accent-yellow); }
.wifi-zone.far .wifi-zone-count { color: var(--accent-red); }
+.wifi-radar-sweep {
+ transform-origin: 105px 105px;
+ animation: wifi-radar-rotate 3s linear infinite;
+}
+
+@keyframes wifi-radar-rotate {
+ from { transform: rotate(0deg); }
+ to { transform: rotate(360deg); }
+}
+
/* WiFi Analysis Panel (RIGHT) */
.wifi-analysis-panel {
display: flex;
diff --git a/static/js/modes/wifi.js b/static/js/modes/wifi.js
index 631249a..1efabbf 100644
--- a/static/js/modes/wifi.js
+++ b/static/js/modes/wifi.js
@@ -167,7 +167,6 @@ const WiFiMode = (function() {
initScanModeTabs();
initNetworkFilters();
initSortControls();
- initProximityRadar();
initChannelChart();
scheduleRender({ table: true, stats: true, radar: true, chart: true });
@@ -201,7 +200,6 @@ const WiFiMode = (function() {
networkFilters: document.getElementById('wifiNetworkFilters'),
// Visualizations
- proximityRadar: document.getElementById('wifiProximityRadar'),
channelChart: document.getElementById('wifiChannelChart'),
channelBandTabs: document.getElementById('wifiChannelBandTabs'),
@@ -1077,7 +1075,7 @@ const WiFiMode = (function() {
if (pendingRender.table) renderNetworks();
if (pendingRender.stats) updateStats();
- if (pendingRender.radar) updateProximityRadar();
+ if (pendingRender.radar) renderRadar(Array.from(networks.values()));
if (pendingRender.chart) updateChannelChart();
if (pendingRender.detail && selectedBssid) {
updateDetailPanel(selectedBssid, { refreshClients: false });
@@ -1506,36 +1504,58 @@ const WiFiMode = (function() {
// Proximity Radar
// ==========================================================================
- function initProximityRadar() {
- if (!elements.proximityRadar) return;
-
- // Initialize radar component
- if (typeof ProximityRadar !== 'undefined') {
- ProximityRadar.init('wifiProximityRadar', {
- mode: 'wifi',
- size: 280,
- onDeviceClick: (bssid) => selectNetwork(bssid),
- });
+ // Simple hash of BSSID string → stable angle in radians
+ function bssidToAngle(bssid) {
+ let hash = 0;
+ for (let i = 0; i < bssid.length; i++) {
+ hash = (hash * 31 + bssid.charCodeAt(i)) & 0xffffffff;
}
+ return (hash >>> 0) / 0xffffffff * 2 * Math.PI;
}
- function updateProximityRadar() {
- if (typeof ProximityRadar === 'undefined') return;
+ function renderRadar(networksList) {
+ const dotsGroup = document.getElementById('wifiRadarDots');
+ if (!dotsGroup) return;
- // Convert networks to radar-compatible format
- const devices = Array.from(networks.values()).map(n => ({
- device_key: n.bssid,
- device_id: n.bssid,
- name: n.essid || '[Hidden]',
- rssi_current: n.rssi_current,
- rssi_ema: n.rssi_ema,
- proximity_band: n.proximity_band,
- estimated_distance_m: n.estimated_distance_m,
- is_new: n.is_new,
- heuristic_flags: n.heuristic_flags || [],
- }));
+ const dots = [];
+ const zoneCounts = { immediate: 0, near: 0, far: 0 };
- ProximityRadar.updateDevices(devices);
+ networksList.forEach(network => {
+ const rssi = network.rssi_current ?? -100;
+ const strength = Math.max(0, Math.min(1, (rssi + 100) / 80));
+ const dotR = 5 + (1 - strength) * 90; // stronger = closer to centre
+ const angle = bssidToAngle(network.bssid);
+ const cx = 105 + dotR * Math.cos(angle);
+ const cy = 105 + dotR * Math.sin(angle);
+
+ // Zone counts
+ if (dotR < 35) zoneCounts.immediate++;
+ else if (dotR < 70) zoneCounts.near++;
+ else zoneCounts.far++;
+
+ // Visual radius by zone
+ const vr = dotR < 35 ? 6 : dotR < 70 ? 4.5 : 3;
+
+ // Colour by security
+ const sec = (network.security || '').toLowerCase();
+ const colour = sec === 'open' || sec === '' ? '#e25d5d'
+ : sec.includes('wpa') ? '#38c180'
+ : sec.includes('wep') ? '#d6a85e'
+ : '#484f58';
+
+ dots.push(`
+