diff --git a/src/rf_mapper/web/static/js/app.js b/src/rf_mapper/web/static/js/app.js
index d68ff4a..53fe68a 100644
--- a/src/rf_mapper/web/static/js/app.js
+++ b/src/rf_mapper/web/static/js/app.js
@@ -33,6 +33,10 @@ let liveTrackingEnabled = false;
let liveTrackingInterval = null;
const LIVE_TRACKING_INTERVAL_MS = 4000; // 4 seconds
+// WebSocket state
+let wsEnabled = true; // Try WebSocket first
+let wsConnected = false;
+
// Statistical movement detection
const SAMPLE_HISTORY_SIZE = 5; // Number of samples to keep for averaging
const MOVEMENT_THRESHOLD = 1.5; // meters - movement must exceed this + stddev margin
@@ -134,6 +138,9 @@ document.addEventListener('DOMContentLoaded', () => {
map3dInitialized = true;
}, 100);
+ // Initialize WebSocket connection
+ initWebSocket();
+
// Start BT live tracking by default after a short delay
setTimeout(() => {
startLiveTracking();
@@ -141,6 +148,152 @@ document.addEventListener('DOMContentLoaded', () => {
}, 2000);
});
+// Initialize WebSocket connection for real-time updates
+function initWebSocket() {
+ if (!wsEnabled) return;
+
+ // Check if rfMapperWS is available (websocket.js loaded)
+ if (typeof rfMapperWS === 'undefined') {
+ console.log('[App] WebSocket module not loaded, using HTTP polling');
+ return;
+ }
+
+ const connected = rfMapperWS.connect();
+ if (!connected) {
+ console.log('[App] WebSocket not available, using HTTP polling');
+ return;
+ }
+
+ rfMapperWS.on('connected', () => {
+ wsConnected = true;
+ console.log('[App] WebSocket connected');
+
+ // Stop HTTP polling if running (WS will handle updates)
+ if (liveTrackingInterval) {
+ clearInterval(liveTrackingInterval);
+ liveTrackingInterval = null;
+ console.log('[App] Stopped HTTP polling (using WebSocket)');
+ }
+ });
+
+ rfMapperWS.on('disconnected', (data) => {
+ wsConnected = false;
+ console.log('[App] WebSocket disconnected:', data?.reason);
+
+ // Resume HTTP polling if live tracking is enabled
+ if (liveTrackingEnabled && !liveTrackingInterval) {
+ liveTrackingInterval = setInterval(performLiveBTScan, LIVE_TRACKING_INTERVAL_MS);
+ console.log('[App] Resumed HTTP polling');
+ }
+ });
+
+ rfMapperWS.on('scanUpdate', (data) => {
+ handleWebSocketScanUpdate(data);
+ });
+}
+
+// Handle scan updates received via WebSocket
+function handleWebSocketScanUpdate(data) {
+ if (!liveTrackingEnabled) return;
+
+ console.log('[WS] Scan update:', data.type, data.devices?.length, 'devices');
+
+ // Handle Bluetooth scan results
+ if (data.type === 'bluetooth' && data.devices) {
+ const newBt = data.devices;
+
+ // Track which devices were detected in this scan
+ const detectedAddresses = new Set(newBt.map(d => d.address));
+
+ if (scanData) {
+ const existingBt = scanData.bluetooth_devices || [];
+
+ // Update existing devices with new RSSI, add new devices
+ newBt.forEach(newDev => {
+ const existing = existingBt.find(d => d.address === newDev.address);
+ const newDist = newDev.estimated_distance_m;
+
+ // Check for movement using statistical analysis
+ const moving = isDeviceMoving(newDev.address, newDist);
+
+ // Reset miss count - device was detected
+ deviceMissCount[newDev.address] = 0;
+
+ if (existing) {
+ // Update RSSI and estimated distance, preserve custom values
+ existing.rssi = newDev.rssi;
+ existing.estimated_distance_m = newDev.estimated_distance_m;
+ existing.signal_quality = newDev.signal_quality;
+ existing.is_moving = moving;
+ existing.miss_count = 0;
+ // Preserve floor and custom_distance_m if set
+ } else {
+ // New device, add it
+ newDev.is_moving = moving;
+ existingBt.push(newDev);
+ }
+ });
+
+ // Increment miss count for devices not detected in this scan
+ existingBt.forEach(dev => {
+ if (!detectedAddresses.has(dev.address)) {
+ deviceMissCount[dev.address] = (deviceMissCount[dev.address] || 0) + 1;
+ dev.miss_count = deviceMissCount[dev.address];
+ }
+ });
+
+ // Filter out devices that have been missed too many times
+ const filteredBt = existingBt.filter(dev => {
+ const missCount = deviceMissCount[dev.address] || 0;
+ if (missCount >= MAX_MISSED_SCANS) {
+ // Clean up tracking data for removed device
+ delete deviceMissCount[dev.address];
+ delete deviceDistanceHistory[dev.address];
+ // Clear trail if showing
+ if (deviceTrails[dev.address]) {
+ clearDeviceTrail(dev.address);
+ }
+ console.log(`[WS] Removed ${dev.name} (missed ${missCount} scans)`);
+ return false;
+ }
+ return true;
+ });
+
+ scanData.bluetooth_devices = filteredBt;
+ } else {
+ // No existing scan data, use BT-only data
+ newBt.forEach(dev => {
+ // Initialize history with first sample, not moving yet
+ isDeviceMoving(dev.address, dev.estimated_distance_m);
+ dev.is_moving = false;
+ deviceMissCount[dev.address] = 0;
+ });
+ scanData = {
+ wifi_networks: [],
+ bluetooth_devices: newBt,
+ timestamp: data.timestamp
+ };
+ }
+
+ // Update visualizations
+ const status = document.getElementById('scan-status');
+ if (status) {
+ const movingCount = scanData.bluetooth_devices.filter(d => d.is_moving).length;
+ const wsIndicator = wsConnected ? '[WS]' : '';
+ status.textContent = `Live${wsIndicator}: ${scanData.bluetooth_devices.length} BT (${movingCount} moving) @ ${new Date().toLocaleTimeString()}`;
+ }
+
+ // Update BT count
+ document.getElementById('bt-count').textContent = scanData.bluetooth_devices.length;
+ document.getElementById('bt-list-count').textContent = scanData.bluetooth_devices.length;
+
+ // Refresh views
+ drawRadar();
+ update3DMarkers();
+ updateMapMarkers();
+ }
+}
+
// Toggle filter
function toggleFilter(type) {
filters[type] = !filters[type];
@@ -1925,17 +2078,23 @@ function toggleLiveTracking() {
function startLiveTracking() {
if (liveTrackingInterval) {
clearInterval(liveTrackingInterval);
+ liveTrackingInterval = null;
}
liveTrackingEnabled = true;
updateLiveTrackingUI();
- console.log('Live BT tracking started');
- // Do initial scan
- performLiveBTScan();
-
- // Set up interval
- liveTrackingInterval = setInterval(performLiveBTScan, LIVE_TRACKING_INTERVAL_MS);
+ if (wsConnected) {
+ // WebSocket mode - updates come automatically via 'scanUpdate' events
+ // Still need to trigger initial scan
+ performLiveBTScan();
+ console.log('[Live] Started (WebSocket mode)');
+ } else {
+ // HTTP polling fallback
+ liveTrackingInterval = setInterval(performLiveBTScan, LIVE_TRACKING_INTERVAL_MS);
+ performLiveBTScan();
+ console.log('[Live] Started (HTTP polling mode)');
+ }
}
// Stop live BT tracking
diff --git a/src/rf_mapper/web/templates/index.html b/src/rf_mapper/web/templates/index.html
index 24d7bee..9f7954e 100644
--- a/src/rf_mapper/web/templates/index.html
+++ b/src/rf_mapper/web/templates/index.html
@@ -166,5 +166,10 @@
{% endblock %}
{% block extra_js %}
+
+
+
+
+
{% endblock %}