/** * Bluetooth Mode Controller * Uses the new unified Bluetooth API at /api/bluetooth/ */ const BluetoothMode = (function() { 'use strict'; // State let isScanning = false; let eventSource = null; let devices = new Map(); let baselineSet = false; let baselineCount = 0; // DOM elements (cached) let startBtn, stopBtn, messageContainer, deviceContainer; let adapterSelect, scanModeSelect, transportSelect, durationInput, minRssiInput; let baselineStatusEl, capabilityStatusEl; /** * Initialize the Bluetooth mode */ function init() { console.log('[BT] Initializing BluetoothMode'); // Cache DOM elements startBtn = document.getElementById('startBtBtn'); stopBtn = document.getElementById('stopBtBtn'); messageContainer = document.getElementById('btMessageContainer'); deviceContainer = document.getElementById('output'); adapterSelect = document.getElementById('btAdapterSelect'); scanModeSelect = document.getElementById('btScanMode'); transportSelect = document.getElementById('btTransport'); durationInput = document.getElementById('btScanDuration'); minRssiInput = document.getElementById('btMinRssi'); baselineStatusEl = document.getElementById('btBaselineStatus'); capabilityStatusEl = document.getElementById('btCapabilityStatus'); console.log('[BT] DOM elements:', { startBtn: !!startBtn, stopBtn: !!stopBtn, deviceContainer: !!deviceContainer, adapterSelect: !!adapterSelect }); // Check capabilities on load checkCapabilities(); // Check scan status (in case page was reloaded during scan) checkScanStatus(); } /** * Check system capabilities */ async function checkCapabilities() { try { const response = await fetch('/api/bluetooth/capabilities'); const data = await response.json(); if (!data.available) { showCapabilityWarning(['Bluetooth not available on this system']); return; } // Update adapter select if (adapterSelect && data.adapters && data.adapters.length > 0) { adapterSelect.innerHTML = data.adapters.map(a => { const status = a.powered ? 'UP' : 'DOWN'; return ``; }).join(''); } else if (adapterSelect) { adapterSelect.innerHTML = ''; } // Show any issues if (data.issues && data.issues.length > 0) { showCapabilityWarning(data.issues); } else { hideCapabilityWarning(); } // Update scan mode based on preferred backend if (scanModeSelect && data.preferred_backend) { const option = scanModeSelect.querySelector(`option[value="${data.preferred_backend}"]`); if (option) option.selected = true; } } catch (err) { console.error('Failed to check capabilities:', err); showCapabilityWarning(['Failed to check Bluetooth capabilities']); } } /** * Show capability warning */ function showCapabilityWarning(issues) { if (!capabilityStatusEl || !messageContainer) return; capabilityStatusEl.style.display = 'block'; if (typeof MessageCard !== 'undefined') { const card = MessageCard.createCapabilityWarning(issues); if (card) { capabilityStatusEl.innerHTML = ''; capabilityStatusEl.appendChild(card); } } else { capabilityStatusEl.innerHTML = `
${JSON.stringify(device, null, 2)}`;
}
}
}
} catch (err) {
console.error('Failed to get device details:', err);
}
}
/**
* Set baseline
*/
async function setBaseline() {
try {
const response = await fetch('/api/bluetooth/baseline/set', { method: 'POST' });
const data = await response.json();
if (data.status === 'success') {
baselineSet = true;
baselineCount = data.device_count;
updateBaselineStatus();
showBaselineSetMessage(data.device_count);
} else {
showErrorMessage(data.message || 'Failed to set baseline');
}
} catch (err) {
console.error('Failed to set baseline:', err);
showErrorMessage('Failed to set baseline');
}
}
/**
* Clear baseline
*/
async function clearBaseline() {
try {
const response = await fetch('/api/bluetooth/baseline/clear', { method: 'POST' });
const data = await response.json();
if (data.status === 'success') {
baselineSet = false;
baselineCount = 0;
updateBaselineStatus();
}
} catch (err) {
console.error('Failed to clear baseline:', err);
}
}
/**
* Update baseline status display
*/
function updateBaselineStatus() {
if (!baselineStatusEl) return;
if (baselineSet) {
baselineStatusEl.textContent = `Baseline set: ${baselineCount} device${baselineCount !== 1 ? 's' : ''}`;
baselineStatusEl.style.color = '#22c55e';
} else {
baselineStatusEl.textContent = 'No baseline set';
baselineStatusEl.style.color = '';
}
}
/**
* Export data
*/
function exportData(format) {
window.open(`/api/bluetooth/export?format=${format}`, '_blank');
}
/**
* Show scanning message
*/
function showScanningMessage(mode) {
if (!messageContainer || typeof MessageCard === 'undefined') return;
removeScanningMessage();
const card = MessageCard.createScanningCard({
backend: mode,
deviceCount: devices.size
});
messageContainer.appendChild(card);
}
/**
* Remove scanning message
*/
function removeScanningMessage() {
MessageCard?.removeMessage?.('btScanningStatus');
}
/**
* Show scan complete message
*/
function showScanCompleteMessage(deviceCount, duration) {
if (!messageContainer || typeof MessageCard === 'undefined') return;
const card = MessageCard.createScanCompleteCard(deviceCount, duration || 0);
messageContainer.appendChild(card);
}
/**
* Show baseline set message
*/
function showBaselineSetMessage(count) {
if (!messageContainer || typeof MessageCard === 'undefined') return;
const card = MessageCard.createBaselineCard(count, true);
messageContainer.appendChild(card);
}
/**
* Show error message
*/
function showErrorMessage(message) {
if (!messageContainer || typeof MessageCard === 'undefined') return;
const card = MessageCard.createErrorCard(message, () => startScan());
messageContainer.appendChild(card);
}
// Public API
return {
init,
startScan,
stopScan,
checkCapabilities,
setBaseline,
clearBaseline,
exportData,
getDevices: () => Array.from(devices.values()),
isScanning: () => isScanning
};
})();
// Global functions for onclick handlers in HTML
function btStartScan() { BluetoothMode.startScan(); }
function btStopScan() { BluetoothMode.stopScan(); }
function btCheckCapabilities() { BluetoothMode.checkCapabilities(); }
function btSetBaseline() { BluetoothMode.setBaseline(); }
function btClearBaseline() { BluetoothMode.clearBaseline(); }
function btExport(format) { BluetoothMode.exportData(format); }
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
// Only init if we're on a page with Bluetooth mode
if (document.getElementById('bluetoothMode')) {
BluetoothMode.init();
}
});
} else {
if (document.getElementById('bluetoothMode')) {
BluetoothMode.init();
}
}
// Make globally available
window.BluetoothMode = BluetoothMode;