import { Platform, PermissionsAndroid } from 'react-native'; import type { ScanOptions, BLEEventType, UserRole } from '../types'; /** * BLEMiddleware - Wrapper class for react-native-sdk-ble-v3 * Handles all BLE operations and event management */ class BLEMiddleware { private bleManager: any = null; private isInitialized: boolean = false; private initializationPromise: Promise | null = null; private currentUserRole: UserRole = 'patient'; // Default role constructor() { // Start initialization immediately this.initializationPromise = this.initializeBLEManager(); } private async initializeBLEManager(): Promise { if (this.isInitialized) { return; } try { // Import the real BLE SDK const BleModule = require('react-native-sdk-ble-v3'); this.bleManager = BleModule.default || BleModule; this.isInitialized = true; console.log('✅ BLE Manager initialized successfully'); } catch (error) { console.error('❌ Failed to initialize BLE Manager:', error); console.error( 'Make sure react-native-sdk-ble-v3 is installed in your app as a peer dependency' ); console.error('Run: npm install react-native-sdk-ble-v3'); this.isInitialized = false; throw new Error( 'react-native-sdk-ble-v3 is required but not installed. Please install it in your app: npm install react-native-sdk-ble-v3' ); } } private async ensureInitialized(): Promise { // Wait for initialization to complete if (this.initializationPromise) { await this.initializationPromise; } if (!this.isInitialized || !this.bleManager) { throw new Error( 'BLE Manager is not initialized. Please ensure react-native-sdk-ble-v2 is installed: npm install react-native-sdk-ble-v3' ); } } /** * Check if Bluetooth is enabled on the device */ async isBluetoothEnabled(): Promise { await this.ensureInitialized(); return await this.bleManager.isBluetoothEnabled(); } /** * Request necessary permissions for BLE operations */ async requestPermissions(): Promise { if (Platform.OS === 'android') { const granted = await PermissionsAndroid.requestMultiple([ PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN, PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT, PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, ]); const allGranted = Object.values(granted).every( (status) => status === PermissionsAndroid.RESULTS.GRANTED ); if (!allGranted) { console.warn('Not all BLE permissions were granted'); } } await this.ensureInitialized(); await this.bleManager.requestPermissions(); } /** * Start scanning for BLE devices */ async startScan(options: ScanOptions = {}): Promise { await this.ensureInitialized(); const defaultOptions = { timeout: 10000, allowDuplicates: false, ...options, }; await this.bleManager.startScan(defaultOptions); } /** * Stop scanning for BLE devices */ async stopScan(): Promise { await this.ensureInitialized(); await this.bleManager.stopScan(); } /** * Connect to a BLE device */ async connect(deviceId: string): Promise { await this.ensureInitialized(); await this.bleManager.connect(deviceId); // Automatically send user role command after connection await this.sendUserRoleCommand(); } /** * Send user role command to device * @private */ private async sendUserRoleCommand(): Promise { try { await this.ensureInitialized(); if (this.bleManager && this.bleManager.setUserRole) { await this.bleManager.setUserRole(this.currentUserRole); console.log(`✅ User role '${this.currentUserRole}' sent to device`); } else { console.warn('⚠️ setUserRole method not available in BLE SDK'); } } catch (error) { console.warn(`⚠️ Failed to send user role:`, error); // Don't throw - allow connection to proceed even if role command fails } } /** * Disconnect from a BLE device */ async disconnect(deviceId: string): Promise { await this.ensureInitialized(); await this.bleManager.disconnect(deviceId); } /** * Register event listener */ on(event: BLEEventType, callback: (data: any) => void): void { // For event listeners, we check synchronously but warn if not ready if (!this.isInitialized || !this.bleManager) { console.warn( `BLE Manager not ready yet. Event listener for '${event}' will be registered once initialization completes.` ); // Queue the event listener to be registered after initialization this.initializationPromise?.then(() => { if (this.isInitialized && this.bleManager) { this.bleManager.on(event, callback); } }); return; } this.bleManager.on(event, callback); } /** * Remove all event listeners */ removeAllListeners(): void { if (this.isInitialized && this.bleManager) { this.bleManager.removeAllListeners(); } } /** * Get the underlying BLE manager instance * Use with caution - prefer using the wrapper methods */ async getRawManager(): Promise { await this.ensureInitialized(); return this.bleManager; } /** * Check if the BLE manager is ready */ isReady(): boolean { return this.isInitialized && this.bleManager !== null; } /** * Wait for initialization to complete */ async waitForInitialization(): Promise { if (this.initializationPromise) { await this.initializationPromise; } } /** * Read characteristic value */ async readCharacteristic( deviceId: string, serviceUuid: string, characteristicUuid: string ): Promise { await this.ensureInitialized(); return await this.bleManager.readCharacteristic( deviceId, serviceUuid, characteristicUuid ); } /** * Write characteristic value */ async writeCharacteristic( deviceId: string, serviceUuid: string, characteristicUuid: string, data: string, options?: { withResponse?: boolean } ): Promise { await this.ensureInitialized(); await this.bleManager.writeCharacteristic( deviceId, serviceUuid, characteristicUuid, data, options ); } /** * Start infusion - Sends command A4 01 01 */ async startInfusion(deviceId: string): Promise { await this.ensureInitialized(); await this.bleManager.startInfusion(deviceId); console.log('✅ Infusion started - Command A4 01 01 sent'); } /** * Stop infusion - Sends command A4 01 00 */ async stopInfusion(deviceId: string): Promise { await this.ensureInitialized(); await this.bleManager.stopInfusion(deviceId); console.log('✅ Infusion stopped - Command A4 01 00 sent'); } /** * Set infusion level - Sends command A5 01 0{level} * @param deviceId - The device ID to send the command to * @param level - Infusion level (1-9) */ async setInfusionLevel(deviceId: string, level: number): Promise { await this.ensureInitialized(); if (level < 0 || level > 7) { throw new Error('Infusion level must be between 0 and 7'); } await this.bleManager.setInfusionLevel(deviceId, level); console.log( `✅ Infusion level set to ${level} - Command A5 01 0${level} sent` ); } /** * Set user role * @param role - The user role to set ('patient', 'nurse', or 'engineer') */ setUserRole(role: UserRole): void { this.currentUserRole = role; console.log(`✅ User role set to: ${role}`); } /** * Get current user role * @returns The current user role */ getUserRole(): UserRole { return this.currentUserRole; } } // Export singleton instance export const bleMiddleware = new BLEMiddleware(); export default bleMiddleware;