// BLE Context - Global state for Bluetooth management import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react'; import { bleManager, WPDevice, WiFiNetwork, WiFiStatus, isBLEAvailable } from '@/services/ble'; interface BLEContextType { // State foundDevices: WPDevice[]; isScanning: boolean; connectedDevices: Set; isBLEAvailable: boolean; error: string | null; // Actions scanDevices: () => Promise; stopScan: () => void; connectDevice: (deviceId: string) => Promise; disconnectDevice: (deviceId: string) => Promise; getWiFiList: (deviceId: string) => Promise; setWiFi: (deviceId: string, ssid: string, password: string) => Promise; getCurrentWiFi: (deviceId: string) => Promise; rebootDevice: (deviceId: string) => Promise; cleanupBLE: () => Promise; clearError: () => void; } const BLEContext = createContext(undefined); export function BLEProvider({ children }: { children: ReactNode }) { const [foundDevices, setFoundDevices] = useState([]); const [isScanning, setIsScanning] = useState(false); const [connectedDevices, setConnectedDevices] = useState>(new Set()); const [error, setError] = useState(null); const scanDevices = useCallback(async () => { try { setError(null); setIsScanning(true); const devices = await bleManager.scanDevices(); // Sort by RSSI (strongest first) const sorted = devices.sort((a, b) => b.rssi - a.rssi); setFoundDevices(sorted); } catch (err: any) { setError(err.message || 'Failed to scan for devices'); throw err; } finally { setIsScanning(false); } }, []); const stopScan = useCallback(() => { bleManager.stopScan(); setIsScanning(false); }, []); const connectDevice = useCallback(async (deviceId: string): Promise => { console.log('[BLEContext] connectDevice called:', deviceId); try { setError(null); const success = await bleManager.connectDevice(deviceId); console.log('[BLEContext] connectDevice result:', success); if (success) { setConnectedDevices(prev => new Set(prev).add(deviceId)); } else { // Connection failed but no exception - set user-friendly error setError('Could not connect to sensor. Please move closer and try again.'); } return success; } catch (err: any) { console.error('[BLEContext] connectDevice exception:', err); setError(err.message || 'Failed to connect to device'); return false; } }, []); const disconnectDevice = useCallback(async (deviceId: string): Promise => { try { await bleManager.disconnectDevice(deviceId); setConnectedDevices(prev => { const next = new Set(prev); next.delete(deviceId); return next; }); } catch (err: any) { setError(err.message || 'Failed to disconnect device'); } }, []); const getWiFiList = useCallback(async (deviceId: string): Promise => { console.log('[BLEContext] getWiFiList called:', deviceId); try { setError(null); const networks = await bleManager.getWiFiList(deviceId); console.log('[BLEContext] getWiFiList success, found networks:', networks.length); return networks; } catch (err: any) { console.error('[BLEContext] getWiFiList failed:', err); setError(err.message || 'Failed to get WiFi networks'); throw err; } }, []); const setWiFi = useCallback( async (deviceId: string, ssid: string, password: string): Promise => { try { setError(null); return await bleManager.setWiFi(deviceId, ssid, password); } catch (err: any) { setError(err.message || 'Failed to configure WiFi'); throw err; } }, [] ); const getCurrentWiFi = useCallback( async (deviceId: string): Promise => { try { setError(null); return await bleManager.getCurrentWiFi(deviceId); } catch (err: any) { setError(err.message || 'Failed to get current WiFi'); throw err; } }, [] ); const rebootDevice = useCallback(async (deviceId: string): Promise => { try { setError(null); await bleManager.rebootDevice(deviceId); // Remove from connected devices setConnectedDevices(prev => { const next = new Set(prev); next.delete(deviceId); return next; }); } catch (err: any) { setError(err.message || 'Failed to reboot device'); throw err; } }, []); const clearError = useCallback(() => { setError(null); }, []); const cleanupBLE = useCallback(async () => { try { console.log('[BLEContext] Cleanup called - cleaning up all BLE connections'); // Stop any ongoing scan if (isScanning) { stopScan(); } // Cleanup via bleManager (disconnects all devices) await bleManager.cleanup(); // Clear context state setFoundDevices([]); setConnectedDevices(new Set()); setError(null); console.log('[BLEContext] Cleanup complete'); } catch (err: any) { console.error('[BLEContext] Cleanup error:', err); // Don't throw - we want to allow logout to proceed even if BLE cleanup fails } }, [isScanning, stopScan]); const value: BLEContextType = { foundDevices, isScanning, connectedDevices, isBLEAvailable, error, scanDevices, stopScan, connectDevice, disconnectDevice, getWiFiList, setWiFi, getCurrentWiFi, rebootDevice, cleanupBLE, clearError, }; return {children}; } export function useBLE() { const context = useContext(BLEContext); if (context === undefined) { throw new Error('useBLE must be used within a BLEProvider'); } return context; }