mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
feat: Add Meshtastic, Ubertooth, and Offline Mode support
New Features: - Meshtastic LoRa mesh network integration - Real-time message streaming via SSE - Channel configuration with encryption - Node information with RSSI/SNR metrics - Ubertooth One BLE scanner backend - Passive capture across all 40 BLE channels - Raw advertising payload access - Offline mode with bundled assets - Local Leaflet, Chart.js, and fonts - Multiple map tile providers - Settings modal for configuration Technical Changes: - New routes: meshtastic.py, offline.py - New utils: ubertooth_scanner.py, meshtastic.py - New CSS/JS for meshtastic and settings - Updated dashboard templates with conditional asset loading - Added context processor for offline settings Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
102
templates/partials/modes/meshtastic.html
Normal file
102
templates/partials/modes/meshtastic.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<!-- MESHTASTIC MODE -->
|
||||
<div id="meshtasticMode" class="mode-content mesh-sidebar-collapsed">
|
||||
<!-- Hide Sidebar Button -->
|
||||
<button class="mesh-hide-sidebar-btn" onclick="Meshtastic.toggleSidebar()">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polyline points="11 17 6 12 11 7"/>
|
||||
<polyline points="18 17 13 12 18 7"/>
|
||||
</svg>
|
||||
Hide Sidebar
|
||||
</button>
|
||||
|
||||
<!-- Collapse Toggle for Options Panel -->
|
||||
<div class="mesh-sidebar-toggle" onclick="Meshtastic.toggleOptionsPanel()">
|
||||
<span class="mesh-sidebar-toggle-icon" id="meshSidebarIcon">▶</span>
|
||||
<span class="mesh-sidebar-toggle-text">Meshtastic Options</span>
|
||||
</div>
|
||||
|
||||
<!-- Collapsible Content -->
|
||||
<div class="mesh-sidebar-content" id="meshSidebarContent">
|
||||
<!-- Channels Panel - shown when connected -->
|
||||
<div class="section" id="meshChannelsSection" style="display: none;">
|
||||
<h3>Channels</h3>
|
||||
<div id="meshChannelsList">
|
||||
<!-- Populated by JavaScript -->
|
||||
</div>
|
||||
<button class="preset-btn" onclick="Meshtastic.refreshChannels()" style="width: 100%; margin-top: 8px;">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 14px; height: 14px; margin-right: 6px; vertical-align: middle;">
|
||||
<polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
||||
</svg>
|
||||
Refresh Channels
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Help</h3>
|
||||
<button class="preset-btn" onclick="Meshtastic.showHelp()" style="width: 100%;">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 14px; height: 14px; margin-right: 6px; vertical-align: middle;">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
|
||||
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
||||
</svg>
|
||||
About Meshtastic
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Resources</h3>
|
||||
<div style="display: flex; flex-direction: column; gap: 6px;">
|
||||
<a href="https://meshtastic.org" target="_blank" rel="noopener" class="preset-btn" style="text-decoration: none; text-align: center;">
|
||||
Meshtastic.org
|
||||
</a>
|
||||
<a href="https://meshtastic.org/docs/" target="_blank" rel="noopener" class="preset-btn" style="text-decoration: none; text-align: center;">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Channel Configuration Modal -->
|
||||
<div id="meshChannelModal" class="signal-details-modal">
|
||||
<div class="signal-details-modal-backdrop" onclick="Meshtastic.closeChannelModal()"></div>
|
||||
<div class="signal-details-modal-content">
|
||||
<div class="signal-details-modal-header">
|
||||
<h3>Configure Channel <span id="meshModalChannelIndex">0</span></h3>
|
||||
<button class="signal-details-modal-close" onclick="Meshtastic.closeChannelModal()">×</button>
|
||||
</div>
|
||||
<div class="signal-details-modal-body">
|
||||
<div class="signal-details-section">
|
||||
<div class="signal-details-title">Channel Settings</div>
|
||||
<div class="form-group" style="margin-bottom: 12px;">
|
||||
<label style="font-size: 11px; color: var(--text-secondary); margin-bottom: 4px;">Channel Name (max 12 chars)</label>
|
||||
<input type="text" id="meshModalChannelName" maxlength="12" placeholder="MyChannel" style="width: 100%;">
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom: 12px;">
|
||||
<label style="font-size: 11px; color: var(--text-secondary); margin-bottom: 4px;">Encryption (PSK)</label>
|
||||
<select id="meshModalPskFormat" onchange="Meshtastic.onPskFormatChange()" style="width: 100%; margin-bottom: 8px;">
|
||||
<option value="keep">Keep Current</option>
|
||||
<option value="none">None (No Encryption)</option>
|
||||
<option value="default">Default (Public Key - NOT SECURE)</option>
|
||||
<option value="random">Random (Generate AES-256)</option>
|
||||
<option value="simple">Passphrase (simple:...)</option>
|
||||
<option value="base64">Base64 Key</option>
|
||||
<option value="hex">Hex Key (0x...)</option>
|
||||
</select>
|
||||
<div id="meshModalPskInputContainer" style="display: none;">
|
||||
<input type="text" id="meshModalPskValue" placeholder="Enter key..." style="width: 100%;">
|
||||
</div>
|
||||
<div id="meshModalPskWarning" style="display: none; background: rgba(255,193,7,0.1); border: 1px solid var(--accent-yellow); border-radius: 4px; padding: 8px; margin-top: 8px; font-size: 10px;">
|
||||
<strong style="color: var(--accent-yellow);">Warning:</strong>
|
||||
<span style="color: var(--text-secondary);">The default key is publicly known and provides no security.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="signal-details-modal-footer" style="display: flex; gap: 8px;">
|
||||
<button class="preset-btn" onclick="Meshtastic.closeChannelModal()" style="flex: 1;">Cancel</button>
|
||||
<button class="run-btn" onclick="Meshtastic.saveChannelConfig()" style="flex: 1;">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
167
templates/partials/settings-modal.html
Normal file
167
templates/partials/settings-modal.html
Normal file
@@ -0,0 +1,167 @@
|
||||
<!-- Settings Modal -->
|
||||
<div id="settingsModal" class="settings-modal" onclick="if(event.target === this) hideSettings()">
|
||||
<div class="settings-content">
|
||||
<div class="settings-header">
|
||||
<h2>
|
||||
<span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span>
|
||||
Settings
|
||||
</h2>
|
||||
<button class="settings-close" onclick="hideSettings()">×</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-tabs">
|
||||
<button class="settings-tab active" data-tab="offline" onclick="switchSettingsTab('offline')">Offline</button>
|
||||
<button class="settings-tab" data-tab="display" onclick="switchSettingsTab('display')">Display</button>
|
||||
<button class="settings-tab" data-tab="about" onclick="switchSettingsTab('about')">About</button>
|
||||
</div>
|
||||
|
||||
<!-- Offline Section -->
|
||||
<div id="settings-offline" class="settings-section active">
|
||||
<div class="settings-group">
|
||||
<div class="settings-group-title">Offline Mode</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Enable Offline Mode</span>
|
||||
<span class="settings-label-desc">Use local assets instead of CDN</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="offlineEnabled" onchange="Settings.toggleOfflineMode(this.checked)">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="settings-group-title">Asset Sources</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">JavaScript/CSS Libraries</span>
|
||||
<span class="settings-label-desc">Leaflet, Chart.js</span>
|
||||
</div>
|
||||
<select id="assetsSource" class="settings-select" onchange="Settings.setAssetSource(this.value)">
|
||||
<option value="cdn">CDN (Online)</option>
|
||||
<option value="local">Local</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Web Fonts</span>
|
||||
<span class="settings-label-desc">Inter, JetBrains Mono</span>
|
||||
</div>
|
||||
<select id="fontsSource" class="settings-select" onchange="Settings.setFontsSource(this.value)">
|
||||
<option value="cdn">Google Fonts (Online)</option>
|
||||
<option value="local">Local</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="settings-group-title">Map Tiles</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Tile Provider</span>
|
||||
<span class="settings-label-desc">Map background imagery</span>
|
||||
</div>
|
||||
<select id="tileProvider" class="settings-select" onchange="Settings.setTileProvider(this.value)">
|
||||
<option value="openstreetmap">OpenStreetMap</option>
|
||||
<option value="cartodb_dark">CartoDB Dark</option>
|
||||
<option value="cartodb_light">CartoDB Positron</option>
|
||||
<option value="esri_world">ESRI World Imagery</option>
|
||||
<option value="custom">Custom URL</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="settings-row custom-url-row" id="customTileUrlRow" style="display: none;">
|
||||
<div class="settings-label" style="width: 100%;">
|
||||
<span class="settings-label-text">Custom Tile URL</span>
|
||||
<span class="settings-label-desc">e.g., http://localhost:8080/{z}/{x}/{y}.png</span>
|
||||
<input type="text" id="customTileUrl" class="settings-input"
|
||||
placeholder="http://tile-server/{z}/{x}/{y}.png"
|
||||
onchange="Settings.setCustomTileUrl(this.value)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="settings-group-title">Local Asset Status</div>
|
||||
<div class="asset-status" id="assetStatus">
|
||||
<div class="asset-status-row">
|
||||
<span class="asset-name">Leaflet JS/CSS</span>
|
||||
<span class="asset-badge checking" id="statusLeaflet">Checking...</span>
|
||||
</div>
|
||||
<div class="asset-status-row">
|
||||
<span class="asset-name">Chart.js</span>
|
||||
<span class="asset-badge checking" id="statusChartjs">Checking...</span>
|
||||
</div>
|
||||
<div class="asset-status-row">
|
||||
<span class="asset-name">Inter Font</span>
|
||||
<span class="asset-badge checking" id="statusInter">Checking...</span>
|
||||
</div>
|
||||
<div class="asset-status-row">
|
||||
<span class="asset-name">JetBrains Mono</span>
|
||||
<span class="asset-badge checking" id="statusJetBrains">Checking...</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="check-assets-btn" onclick="Settings.checkAssets()">
|
||||
Check Assets
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-info">
|
||||
<strong>Note:</strong> Changes to asset sources require a page reload to take effect.
|
||||
Local assets must be available in <code>/static/vendor/</code>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Display Section -->
|
||||
<div id="settings-display" class="settings-section">
|
||||
<div class="settings-group">
|
||||
<div class="settings-group-title">Visual Preferences</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Theme</span>
|
||||
<span class="settings-label-desc">Color scheme preference</span>
|
||||
</div>
|
||||
<select id="themeSelect" class="settings-select" onchange="setThemePreference(this.value)">
|
||||
<option value="dark">Dark</option>
|
||||
<option value="light">Light</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Animations</span>
|
||||
<span class="settings-label-desc">Enable visual effects and animations</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="animationsEnabled" checked onchange="setAnimationsEnabled(this.checked)">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- About Section -->
|
||||
<div id="settings-about" class="settings-section">
|
||||
<div class="settings-group">
|
||||
<div class="about-info">
|
||||
<p><strong>iNTERCEPT</strong> - Signal Intelligence Platform</p>
|
||||
<p>Version: <span class="about-version">{{ version }}</span></p>
|
||||
<p>
|
||||
A unified web interface for software-defined radio (SDR) tools,
|
||||
supporting pager decoding, sensor monitoring, aircraft tracking,
|
||||
WiFi/Bluetooth scanning, and more.
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://github.com/intercept" target="_blank">GitHub Repository</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user