diff --git a/routes/settings.py b/routes/settings.py index 8f4d3c2..129d40a 100644 --- a/routes/settings.py +++ b/routes/settings.py @@ -9,8 +9,6 @@ from utils.database import ( set_setting, delete_setting, get_all_settings, - get_signal_history, - add_signal_reading, get_correlations, ) from utils.logging import get_logger @@ -145,66 +143,6 @@ def delete_single_setting(key: str) -> Response: }), 500 -# ============================================================================= -# Signal History Endpoints -# ============================================================================= - -@settings_bp.route('/signal-history//', methods=['GET']) -def get_device_signal_history(mode: str, device_id: str) -> Response: - """Get signal strength history for a device.""" - limit = request.args.get('limit', 100, type=int) - since_minutes = request.args.get('since', 60, type=int) - - # Validate mode - valid_modes = ['wifi', 'bluetooth', 'adsb', 'pager', 'sensor'] - if mode not in valid_modes: - return jsonify({ - 'status': 'error', - 'message': f'Invalid mode. Valid modes: {valid_modes}' - }), 400 - - try: - history = get_signal_history(mode, device_id, limit, since_minutes) - return jsonify({ - 'status': 'success', - 'mode': mode, - 'device_id': device_id, - 'history': history - }) - except Exception as e: - logger.error(f"Error getting signal history: {e}") - return jsonify({ - 'status': 'error', - 'message': str(e) - }), 500 - - -@settings_bp.route('/signal-history', methods=['POST']) -def add_signal_history() -> Response: - """Add a signal strength reading (for internal use).""" - data = request.json or {} - - mode = data.get('mode') - device_id = data.get('device_id') - signal_strength = data.get('signal_strength') - - if not all([mode, device_id, signal_strength is not None]): - return jsonify({ - 'status': 'error', - 'message': 'mode, device_id, and signal_strength are required' - }), 400 - - try: - add_signal_reading(mode, device_id, signal_strength, data.get('metadata')) - return jsonify({'status': 'success'}) - except Exception as e: - logger.error(f"Error adding signal reading: {e}") - return jsonify({ - 'status': 'error', - 'message': str(e) - }), 500 - - # ============================================================================= # Device Correlation Endpoints # ============================================================================= diff --git a/routes/wifi.py b/routes/wifi.py index 911ec1c..3161368 100644 --- a/routes/wifi.py +++ b/routes/wifi.py @@ -105,12 +105,18 @@ def detect_wifi_interfaces(): current_iface = line.split()[1] elif current_iface and 'type' in line: iface_type = line.split()[-1] - interfaces.append({ + iface_info = { 'name': current_iface, 'type': iface_type, 'monitor_capable': True, - 'status': 'up' - }) + 'status': 'up', + 'driver': '', + 'chipset': '', + 'mac': '' + } + # Get additional interface details + iface_info.update(_get_interface_details(current_iface)) + interfaces.append(iface_info) current_iface = None except FileNotFoundError: # Fall back to iwconfig if iw is not available @@ -119,12 +125,17 @@ def detect_wifi_interfaces(): for line in result.stdout.split('\n'): if 'IEEE 802.11' in line: iface = line.split()[0] - interfaces.append({ + iface_info = { 'name': iface, 'type': 'managed', 'monitor_capable': True, - 'status': 'up' - }) + 'status': 'up', + 'driver': '', + 'chipset': '', + 'mac': '' + } + iface_info.update(_get_interface_details(iface)) + interfaces.append(iface_info) except FileNotFoundError: logger.debug("Neither iw nor iwconfig found") except subprocess.SubprocessError as e: @@ -137,6 +148,60 @@ def detect_wifi_interfaces(): return interfaces +def _get_interface_details(iface_name): + """Get additional details about a WiFi interface (driver, chipset, MAC).""" + details = {'driver': '', 'chipset': '', 'mac': ''} + + # Get MAC address + try: + mac_path = f'/sys/class/net/{iface_name}/address' + with open(mac_path, 'r') as f: + details['mac'] = f.read().strip().upper() + except (FileNotFoundError, IOError): + pass + + # Get driver name + try: + driver_link = f'/sys/class/net/{iface_name}/device/driver' + import os + if os.path.islink(driver_link): + driver_path = os.readlink(driver_link) + details['driver'] = os.path.basename(driver_path) + except (FileNotFoundError, IOError, OSError): + pass + + # Get chipset info from USB or PCI + try: + # Check if USB device + device_path = f'/sys/class/net/{iface_name}/device' + import os + if os.path.exists(device_path): + # Try to get USB product name + for usb_path in [f'{device_path}/product', f'{device_path}/../product']: + try: + with open(usb_path, 'r') as f: + details['chipset'] = f.read().strip() + break + except (FileNotFoundError, IOError): + pass + + # If no USB product, try to get from uevent + if not details['chipset']: + try: + uevent_path = f'{device_path}/uevent' + with open(uevent_path, 'r') as f: + for line in f: + if line.startswith('PCI_ID=') or line.startswith('PRODUCT='): + details['chipset'] = line.split('=')[1].strip() + break + except (FileNotFoundError, IOError): + pass + except (FileNotFoundError, IOError, OSError): + pass + + return details + + def parse_airodump_csv(csv_path): """Parse airodump-ng CSV output file.""" networks = {} diff --git a/static/css/index.css b/static/css/index.css index 83a64e2..11f2a25 100644 --- a/static/css/index.css +++ b/static/css/index.css @@ -1309,38 +1309,6 @@ header p { color: var(--accent-red); } -/* Signal Strength Graph */ -.signal-graph-panel { - background: var(--bg-card); - border: 1px solid var(--border-color); - border-radius: 4px; - padding: 12px; - margin-top: 10px; -} - -.signal-graph-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 10px; -} - -.signal-graph-header h4 { - color: var(--accent-cyan); - font-size: 11px; - text-transform: uppercase; - letter-spacing: 1px; - margin: 0; -} - -.signal-graph-device { - font-size: 10px; - color: var(--text-secondary); - font-family: 'JetBrains Mono', monospace; -} - -#signalGraph, -#btSignalGraph, #adsbStatsChart { width: 100%; height: 80px; diff --git a/templates/index.html b/templates/index.html index a1d66b5..1f1d222 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1271,14 +1271,6 @@ - -
-
-

πŸ“ˆ Signal History

- Click a device to track -
- -

πŸ•ΈοΈ Network Topology

@@ -1355,13 +1347,6 @@
Monitoring for AirTags, Tiles, and other trackers...
-
-
-

πŸ“ˆ BT Signal History

- Select a device from the list -
- -
@@ -2340,6 +2325,10 @@ } else { if (reconBtn) reconBtn.style.display = 'inline-block'; if (intelBtn) intelBtn.style.display = 'inline-block'; + // Restore panel visibility based on reconEnabled state + if (reconEnabled) { + document.getElementById('reconPanel').style.display = 'block'; + } } // Show RTL-SDR device section for modes that use it @@ -3599,7 +3588,7 @@
- ${profile.protocol.substring(0, 8)} + ${profile.protocol.substring(0, 10)} ${deviceId.substring(0, 30)}
@@ -4109,221 +4098,6 @@ // ============== NEW FEATURES ============== - // Signal History Graph with Chart.js - let signalHistory = {}; // {mac: [{time, signal}]} - let trackedDevice = null; - let trackedDeviceMode = 'wifi'; // 'wifi' or 'bluetooth' - const maxSignalPoints = 60; - let signalChart = null; - - function trackDeviceSignal(mac, signal, mode = 'wifi') { - if (!signalHistory[mac]) { - signalHistory[mac] = []; - } - const timestamp = Date.now(); - signalHistory[mac].push({ - time: timestamp, - signal: parseInt(signal) || -100 - }); - // Keep only last N points - if (signalHistory[mac].length > maxSignalPoints) { - signalHistory[mac].shift(); - } - // Update graph if this is the tracked device - if (trackedDevice === mac) { - updateSignalChart(); - } - // Persist to server (non-blocking) - fetch('/settings/signal-history', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ mode, device_id: mac, signal_strength: signal }) - }).catch(() => {}); // Ignore errors - } - - function setTrackedDevice(mac, name, mode = 'wifi') { - trackedDevice = mac; - trackedDeviceMode = mode; - document.getElementById('signalGraphDevice').textContent = name || mac; - initSignalChart(); - updateSignalChart(); - } - - function initSignalChart() { - const canvas = document.getElementById('signalGraph'); - if (!canvas) return; - - // Destroy existing chart if any - if (signalChart) { - signalChart.destroy(); - } - - const ctx = canvas.getContext('2d'); - signalChart = new Chart(ctx, { - type: 'line', - data: { - labels: [], - datasets: [{ - label: 'Signal (dBm)', - data: [], - borderColor: '#00d4ff', - backgroundColor: 'rgba(0, 212, 255, 0.1)', - borderWidth: 2, - fill: true, - tension: 0.3, - pointRadius: 0, - pointHoverRadius: 4 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - animation: { duration: 0 }, - plugins: { - legend: { display: false }, - tooltip: { - backgroundColor: 'rgba(0, 0, 0, 0.8)', - titleColor: '#00d4ff', - bodyColor: '#fff' - } - }, - scales: { - x: { - display: false - }, - y: { - min: -100, - max: -20, - reverse: true, - grid: { color: 'rgba(255, 255, 255, 0.1)' }, - ticks: { - color: '#666', - font: { size: 10, family: 'monospace' }, - callback: v => v + ' dBm' - } - } - } - } - }); - } - - function updateSignalChart() { - if (!signalChart || !trackedDevice) return; - - const data = signalHistory[trackedDevice] || []; - if (data.length === 0) return; - - signalChart.data.labels = data.map((_, i) => i); - signalChart.data.datasets[0].data = data.map(p => p.signal); - signalChart.update('none'); - - // Update current value display - const lastSignal = data[data.length - 1].signal; - const deviceLabel = document.getElementById('signalGraphDevice'); - if (deviceLabel && !deviceLabel.textContent.includes('dBm')) { - deviceLabel.textContent += ` (${lastSignal} dBm)`; - } - } - - // Legacy function for backward compatibility - function drawSignalGraph() { - if (!signalChart) { - initSignalChart(); - } - updateSignalChart(); - } - - // Bluetooth Signal History Chart - let btSignalChart = null; - let btTrackedDevice = null; - let btSignalHistory = {}; - - function trackBtDeviceSignal(mac, rssi) { - if (!btSignalHistory[mac]) { - btSignalHistory[mac] = []; - } - btSignalHistory[mac].push({ - time: Date.now(), - signal: parseInt(rssi) || -100 - }); - if (btSignalHistory[mac].length > maxSignalPoints) { - btSignalHistory[mac].shift(); - } - if (btTrackedDevice === mac) { - updateBtSignalChart(); - } - // Persist to server - fetch('/settings/signal-history', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ mode: 'bluetooth', device_id: mac, signal_strength: rssi }) - }).catch(() => {}); - } - - function setBtTrackedDevice(mac, name) { - btTrackedDevice = mac; - document.getElementById('btSignalGraphDevice').textContent = name || mac; - initBtSignalChart(); - updateBtSignalChart(); - } - - function initBtSignalChart() { - const canvas = document.getElementById('btSignalGraph'); - if (!canvas) return; - - if (btSignalChart) { - btSignalChart.destroy(); - } - - const ctx = canvas.getContext('2d'); - btSignalChart = new Chart(ctx, { - type: 'line', - data: { - labels: [], - datasets: [{ - label: 'RSSI (dBm)', - data: [], - borderColor: '#ff6600', - backgroundColor: 'rgba(255, 102, 0, 0.1)', - borderWidth: 2, - fill: true, - tension: 0.3, - pointRadius: 0 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - animation: { duration: 0 }, - plugins: { legend: { display: false } }, - scales: { - x: { display: false }, - y: { - min: -100, - max: -20, - reverse: true, - grid: { color: 'rgba(255, 255, 255, 0.1)' }, - ticks: { - color: '#666', - font: { size: 10, family: 'monospace' }, - callback: v => v + ' dBm' - } - } - } - } - }); - } - - function updateBtSignalChart() { - if (!btSignalChart || !btTrackedDevice) return; - const data = btSignalHistory[btTrackedDevice] || []; - if (data.length === 0) return; - - btSignalChart.data.labels = data.map((_, i) => i); - btSignalChart.data.datasets[0].data = data.map(p => p.signal); - btSignalChart.update('none'); - } - // Network Topology Graph function drawNetworkGraph() { const canvas = document.getElementById('networkGraph'); @@ -4604,7 +4378,6 @@ // Update visualizations periodically setInterval(() => { if (currentMode === 'wifi') { - drawSignalGraph(); drawNetworkGraph(); updateChannelRecommendation(); correlateDevices(); @@ -4620,9 +4393,18 @@ if (data.interfaces.length === 0) { select.innerHTML = ''; } else { - select.innerHTML = data.interfaces.map(i => - `` - ).join(''); + select.innerHTML = data.interfaces.map(i => { + // Build descriptive label with available info + let label = i.name; + let details = []; + if (i.chipset) details.push(i.chipset); + else if (i.driver) details.push(i.driver); + if (i.mac) details.push(i.mac.substring(0, 8) + '...'); + if (details.length > 0) label += ' - ' + details.join(' | '); + label += ` (${i.type})`; + if (i.monitor_capable) label += ' [Monitor OK]'; + return ``; + }).join(''); } // Update tool status @@ -4839,9 +4621,6 @@ const isNew = !wifiNetworks[net.bssid]; wifiNetworks[net.bssid] = net; - // Track signal history for graphs - trackDeviceSignal(net.bssid, net.power); - // Check if this reveals a hidden SSID if (net.essid && net.essid !== 'Hidden' && net.essid !== '[Hidden]') { revealHiddenSsid(net.bssid, net.essid); @@ -4890,9 +4669,6 @@ const isNew = !wifiClients[client.mac]; wifiClients[client.mac] = client; - // Track signal history for graphs - trackDeviceSignal(client.mac, client.power); - if (isNew) { clientCount++; document.getElementById('clientCount').textContent = clientCount; @@ -5059,7 +4835,6 @@ -
`; @@ -5963,10 +5738,6 @@ } btDevices[device.mac] = device; - // Track signal history for graphs - if (device.rssi) { - trackBtDeviceSignal(device.mac, device.rssi); - } if (isNew) { btDeviceCount++; @@ -6062,12 +5833,11 @@ }).join(''); } - // Select a BT device for signal tracking + // Select a BT device for details function selectBtDevice(mac) { selectedBtDevice = mac; const device = btDevices[mac]; if (device) { - document.getElementById('btSignalGraphDevice').textContent = device.name || mac; document.getElementById('btTargetMac').value = mac; updateBtSelectedDevice(device); } @@ -6226,7 +5996,7 @@
Manufacturer
-
${escapeHtml(device.manufacturer)}
+
${escapeHtml(device.manufacturer || 'Unknown')}
${device.findmy ? `
@@ -8733,31 +8503,41 @@ } async function tuneToFrequency(freq, mod) { - // Stop scanner if running - if (isScannerRunning) { - stopScanner(); + try { + // Stop scanner if running + if (isScannerRunning) { + await fetch('/listening/scanner/stop', { method: 'POST' }); + isScannerRunning = false; + document.getElementById('scannerStartBtn').textContent = 'β–Ά Start Scanner'; + document.getElementById('scannerStartBtn').classList.remove('active'); + document.getElementById('scannerStatus').textContent = 'STOPPED'; + document.getElementById('scannerStatus').style.color = 'var(--text-muted)'; + } + + // Stop any current audio and wait for it to complete + if (isAudioPlaying) { + await stopAudio(); + // Extra delay to ensure backend processes are fully stopped + await new Promise(resolve => setTimeout(resolve, 500)); + } + + // Set frequency in manual audio form + document.getElementById('audioFrequency').value = freq.toFixed(3); + document.getElementById('audioPreset').value = 'custom'; + if (mod) { + document.getElementById('audioModulation').value = mod; + } + + // Small delay before starting to ensure backend is ready + await new Promise(resolve => setTimeout(resolve, 200)); + + // Start playing + startAudio(); + showNotification('Tuned', `Now listening to ${freq.toFixed(3)} MHz`); + } catch (err) { + console.error('Error tuning to frequency:', err); + showNotification('Tune Error', 'Failed to tune to frequency: ' + err.message); } - - // Stop any current audio and wait for it to complete - if (isAudioPlaying) { - stopAudio(); - // Wait for audio to fully stop - await new Promise(resolve => setTimeout(resolve, 300)); - } - - // Set frequency in manual audio form - document.getElementById('audioFrequency').value = freq.toFixed(3); - document.getElementById('audioPreset').value = 'custom'; - if (mod) { - document.getElementById('audioModulation').value = mod; - } - - // Small delay before starting to ensure backend is ready - await new Promise(resolve => setTimeout(resolve, 100)); - - // Start playing - startAudio(); - showNotification('Tuned', `Now listening to ${freq.toFixed(3)} MHz`); } function skipSignal() { @@ -9154,7 +8934,7 @@ }); } - function stopAudio() { + async function stopAudio() { // Stop visualizer stopAudioVisualizer(); @@ -9163,18 +8943,18 @@ audioPlayer.pause(); audioPlayer.src = ''; - fetch('/listening/audio/stop', { method: 'POST' }) - .then(r => r.json()) - .then(data => { - releaseDevice('audio'); - isAudioPlaying = false; - document.getElementById('audioStartBtn').textContent = 'β–Ά Play Audio'; - document.getElementById('audioStartBtn').classList.remove('active'); - document.getElementById('audioStatus').textContent = 'STOPPED'; - document.getElementById('audioStatus').style.color = 'var(--text-muted)'; - document.getElementById('audioDeviceStatus').textContent = '--'; - }) - .catch(() => {}); + try { + await fetch('/listening/audio/stop', { method: 'POST' }); + releaseDevice('audio'); + isAudioPlaying = false; + document.getElementById('audioStartBtn').textContent = 'β–Ά Play Audio'; + document.getElementById('audioStartBtn').classList.remove('active'); + document.getElementById('audioStatus').textContent = 'STOPPED'; + document.getElementById('audioStatus').style.color = 'var(--text-muted)'; + document.getElementById('audioDeviceStatus').textContent = '--'; + } catch (e) { + console.error('Error stopping audio:', e); + } } function updateAudioVolume() {