// WebSocket connection let socket; const syncStatus = document.getElementById('sync-status'); function updateSyncStatus(message) { const timestamp = new Date().toISOString().replace('T', ' ').substring(0, 19); syncStatus.innerHTML += `
[${timestamp}] ${message}
`; syncStatus.scrollTop = syncStatus.scrollHeight; } document.addEventListener('DOMContentLoaded', function() { // Initialize WebSocket function connectWebSocket() { socket = new WebSocket('wss://your-websocket-server.com/ble-mesh'); socket.onopen = () => { updateSyncStatus('WebSocket connected'); }; socket.onmessage = (event) => { updateSyncStatus(`Received data: ${event.data}`); // Handle incoming sync data }; socket.onclose = () => { updateSyncStatus('WebSocket disconnected - attempting reconnect...'); setTimeout(connectWebSocket, 5000); }; } connectWebSocket(); // Enhanced device data with metrics const devices = [ { id: 'node-042', type: 'ESP32 Mesh Node', status: 'active', lastSeen: new Date().toISOString(), lastRtt: 24, aggregated: { summary: 'Stable connection, low latency', metrics: { health: 'ok', packetLoss: '0.2%', uptime: '99.8%' } } }, { id: 'imu-015', type: 'IMU Sensor', status: 'active', lastSeen: new Date(Date.now() - 120000).toISOString(), lastRtt: 58, aggregated: { summary: 'Higher than usual latency', metrics: { health: 'warn', packetLoss: '1.8%', uptime: '98.1%' } } }, { id: 'proxy-007', type: 'GATT Proxy', status: 'inactive', lastSeen: new Date(Date.now() - 900000).toISOString(), lastRtt: null, aggregated: { summary: 'Device unreachable', metrics: { health: 'critical', packetLoss: '100%', uptime: '0%' } } }, { id: 'gateway-001', type: 'Manabox Gateway', status: 'active', lastSeen: new Date().toISOString(), lastRtt: 32, aggregated: { summary: 'Normal operation', metrics: { health: 'ok', packetLoss: '0.1%', uptime: '99.9%' } } }, ]; // Populate device table const tableBody = document.getElementById('device-table-body'); devices.forEach(device => { const row = document.createElement('tr'); row.className = 'border-b border-stone-700 hover:bg-stone-700/50 transition-colors'; const health = device.aggregated?.metrics?.health || ''; const statusClass = health === 'ok' ? 'text-green-400' : health === 'warn' ? 'text-amber-400' : 'text-red-400'; row.innerHTML = ` ${device.id} ${device.type} ${device.status} ${new Date(device.lastSeen).toLocaleTimeString()} ${device.lastRtt ? device.lastRtt + 'ms' : '-'} `; tableBody.appendChild(row); }); // Telemetry stream simulation const telemetryStream = document.getElementById('telemetry-stream'); const startBtn = document.getElementById('start-stream'); const clearBtn = document.getElementById('clear-stream'); let streamInterval; // Bluetooth sync handler document.getElementById('sync-bluetooth').addEventListener('click', async () => { updateSyncStatus('Initiating BLE Mesh sync...'); try { // Mock BLE sync - in real implementation this would use Web Bluetooth API updateSyncStatus('Discovering nearby mesh nodes...'); await new Promise(resolve => setTimeout(resolve, 1000)); updateSyncStatus('Connected to 3 mesh nodes'); await new Promise(resolve => setTimeout(resolve, 500)); updateSyncStatus('Syncing configuration data...'); await new Promise(resolve => setTimeout(resolve, 1500)); updateSyncStatus('Sync complete! Updated 12 devices'); } catch (error) { updateSyncStatus(`BLE Sync failed: ${error.message}`); } }); // WebSocket sync handler document.getElementById('sync-websocket').addEventListener('click', () => { if (socket.readyState === WebSocket.OPEN) { updateSyncStatus('Sending sync request via WebSocket...'); socket.send(JSON.stringify({ type: 'sync_request', devices: devices // Send current device state })); } else { updateSyncStatus('WebSocket not connected - attempting reconnect'); connectWebSocket(); } }); // Data export handler document.getElementById('export-data').addEventListener('click', () => { const config = { timestamp: new Date().toISOString(), devices: devices, settings: { meshId: 'nexus-glow-001', networkKey: '********', appKey: '********' } }; const blob = new Blob([JSON.stringify(config, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `ble-mesh-config-${new Date().toISOString().split('T')[0]}.json`; a.click(); updateSyncStatus('Configuration exported as JSON'); }); startBtn.addEventListener('click', function() { if (streamInterval) { clearInterval(streamInterval); startBtn.innerHTML = ' Start Stream'; feather.replace(); return; } startBtn.innerHTML = ' Stop Stream'; feather.replace(); streamInterval = setInterval(() => { const now = new Date(); const timestamp = now.toISOString().replace('T', ' ').substring(0, 19); const telemetryData = { timestamp, device_id: 'node-' + Math.floor(Math.random() * 100).toString().padStart(3, '0'), accel: { x: (Math.random() * 2 - 1).toFixed(2), y: (Math.random() * 2 - 1).toFixed(2), z: (9.8 + Math.random() * 0.2 - 0.1).toFixed(2) }, buttons: { A: Math.random() > 0.7 ? 1 : 0, B: Math.random() > 0.8 ? 1 : 0, Guard: Math.random() > 0.9 ? 1 : 0 } }; const entry = document.createElement('div'); entry.className = 'mb-2 p-3 bg-stone-700/50 rounded-lg font-mono text-sm'; entry.innerHTML = `${timestamp} ${JSON.stringify(telemetryData)}`; telemetryStream.appendChild(entry); // Auto-scroll to bottom telemetryStream.scrollTop = telemetryStream.scrollHeight; }, 1000); }); clearBtn.addEventListener('click', function() { telemetryStream.innerHTML = ''; }); });