react-native-sdk-ble-middleware-v2 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/Middleware.podspec +21 -0
- package/README.md +187 -0
- package/android/build.gradle +77 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/middleware/MiddlewareModule.kt +23 -0
- package/android/src/main/java/com/middleware/MiddlewarePackage.kt +33 -0
- package/ios/Middleware.h +5 -0
- package/ios/Middleware.mm +21 -0
- package/lib/module/NativeMiddleware.js +3 -0
- package/lib/module/NativeMiddleware.js.map +1 -0
- package/lib/module/context/BLEContext.js +390 -0
- package/lib/module/context/BLEContext.js.map +1 -0
- package/lib/module/hooks/index.js +2 -0
- package/lib/module/hooks/index.js.map +1 -0
- package/lib/module/hooks/useBLE.js +167 -0
- package/lib/module/hooks/useBLE.js.map +1 -0
- package/lib/module/index.js +12 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/services/BLEMiddleware.js +252 -0
- package/lib/module/services/BLEMiddleware.js.map +1 -0
- package/lib/module/services/MockBLEManager.js +94 -0
- package/lib/module/services/MockBLEManager.js.map +1 -0
- package/lib/module/services/index.js +2 -0
- package/lib/module/services/index.js.map +1 -0
- package/lib/module/types/index.js +44 -0
- package/lib/module/types/index.js.map +1 -0
- package/package.json +179 -0
- package/react-native.config.js +8 -0
- package/src/NativeMiddleware.ts +7 -0
- package/src/context/BLEContext.tsx +487 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useBLE.ts +190 -0
- package/src/index.tsx +21 -0
- package/src/services/BLEMiddleware.ts +291 -0
- package/src/services/MockBLEManager.ts +96 -0
- package/src/services/index.ts +1 -0
- package/src/types/index.ts +97 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, useEffect } from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
BLEDevice,
|
|
4
|
+
CharacteristicNotification,
|
|
5
|
+
DeviceConnectionState,
|
|
6
|
+
UserRole,
|
|
7
|
+
} from '../types';
|
|
8
|
+
import bleMiddleware from '../services/BLEMiddleware';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* BLE Context State
|
|
12
|
+
*/
|
|
13
|
+
interface BLEContextState {
|
|
14
|
+
// Connection state
|
|
15
|
+
bluetoothEnabled: boolean;
|
|
16
|
+
isScanning: boolean;
|
|
17
|
+
discoveredDevices: BLEDevice[];
|
|
18
|
+
connectedDevices: Map<string, DeviceConnectionState>;
|
|
19
|
+
|
|
20
|
+
// Legacy single device support (for backward compatibility)
|
|
21
|
+
connectedDeviceId: string | null;
|
|
22
|
+
isInfusionRunning: boolean;
|
|
23
|
+
ff01Notification: CharacteristicNotification | null;
|
|
24
|
+
ff21Notification: CharacteristicNotification | null;
|
|
25
|
+
ff31Notification: CharacteristicNotification | null;
|
|
26
|
+
ff41Notification: CharacteristicNotification | null;
|
|
27
|
+
ff02Notification: CharacteristicNotification | null;
|
|
28
|
+
|
|
29
|
+
// Actions
|
|
30
|
+
startScan: () => Promise<void>;
|
|
31
|
+
stopScan: () => Promise<void>;
|
|
32
|
+
connectToDevice: (device: BLEDevice) => Promise<void>;
|
|
33
|
+
disconnectDevice: (deviceId: string) => Promise<void>;
|
|
34
|
+
requestPermissions: () => Promise<void>;
|
|
35
|
+
|
|
36
|
+
// Multi-device queries
|
|
37
|
+
getDeviceState: (deviceId: string) => DeviceConnectionState | undefined;
|
|
38
|
+
isDeviceConnected: (deviceId: string) => boolean;
|
|
39
|
+
getConnectedDeviceIds: () => string[];
|
|
40
|
+
|
|
41
|
+
// Infusion actions
|
|
42
|
+
startInfusion: (deviceId: string) => Promise<void>;
|
|
43
|
+
stopInfusion: (deviceId: string) => Promise<void>;
|
|
44
|
+
setInfusionLevel: (deviceId: string, level: number) => Promise<void>;
|
|
45
|
+
|
|
46
|
+
// Characteristic operations
|
|
47
|
+
readCharacteristic: (
|
|
48
|
+
deviceId: string,
|
|
49
|
+
serviceUuid: string,
|
|
50
|
+
characteristicUuid: string
|
|
51
|
+
) => Promise<any>;
|
|
52
|
+
writeCharacteristic: (
|
|
53
|
+
deviceId: string,
|
|
54
|
+
serviceUuid: string,
|
|
55
|
+
characteristicUuid: string,
|
|
56
|
+
data: string,
|
|
57
|
+
options?: { withResponse?: boolean }
|
|
58
|
+
) => Promise<void>;
|
|
59
|
+
|
|
60
|
+
// User role management
|
|
61
|
+
setUserRole: (role: UserRole) => void;
|
|
62
|
+
getUserRole: () => UserRole;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const BLEContext = createContext<BLEContextState | undefined>(undefined);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* BLE Provider Component
|
|
69
|
+
* Manages all BLE state and operations
|
|
70
|
+
*/
|
|
71
|
+
export const BLEProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
72
|
+
children,
|
|
73
|
+
}) => {
|
|
74
|
+
const [bluetoothEnabled, setBluetoothEnabled] = useState(false);
|
|
75
|
+
const [isScanning, setIsScanning] = useState(false);
|
|
76
|
+
const [discoveredDevices, setDiscoveredDevices] = useState<BLEDevice[]>([]);
|
|
77
|
+
const [connectedDevices, setConnectedDevices] = useState<
|
|
78
|
+
Map<string, DeviceConnectionState>
|
|
79
|
+
>(new Map());
|
|
80
|
+
|
|
81
|
+
// Legacy single device support (tracks the first/primary connected device)
|
|
82
|
+
const [connectedDeviceId, setConnectedDeviceId] = useState<string | null>(
|
|
83
|
+
null
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Legacy notification states (for the primary device)
|
|
87
|
+
const [ff01Notification, setFF01Notification] =
|
|
88
|
+
useState<CharacteristicNotification | null>(null);
|
|
89
|
+
const [ff21Notification, setFF21Notification] =
|
|
90
|
+
useState<CharacteristicNotification | null>(null);
|
|
91
|
+
const [ff31Notification, setFF31Notification] =
|
|
92
|
+
useState<CharacteristicNotification | null>(null);
|
|
93
|
+
const [ff41Notification, setFF41Notification] =
|
|
94
|
+
useState<CharacteristicNotification | null>(null);
|
|
95
|
+
const [ff02Notification, setFF02Notification] =
|
|
96
|
+
useState<CharacteristicNotification | null>(null);
|
|
97
|
+
|
|
98
|
+
// Legacy infusion state (for the primary device)
|
|
99
|
+
const [isInfusionRunning, setIsInfusionRunning] = useState(false);
|
|
100
|
+
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
const initialize = async () => {
|
|
103
|
+
// Wait for BLE middleware to initialize first
|
|
104
|
+
await bleMiddleware.waitForInitialization();
|
|
105
|
+
|
|
106
|
+
// Then setup BLE and event listeners
|
|
107
|
+
await setupBLE();
|
|
108
|
+
setupEventListeners();
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
initialize();
|
|
112
|
+
|
|
113
|
+
return () => {
|
|
114
|
+
bleMiddleware.removeAllListeners();
|
|
115
|
+
};
|
|
116
|
+
}, []);
|
|
117
|
+
|
|
118
|
+
const setupBLE = async () => {
|
|
119
|
+
try {
|
|
120
|
+
await requestPermissions();
|
|
121
|
+
const enabled = await bleMiddleware.isBluetoothEnabled();
|
|
122
|
+
setBluetoothEnabled(enabled);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error('Failed to setup BLE:', error);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const requestPermissions = async () => {
|
|
129
|
+
try {
|
|
130
|
+
await bleMiddleware.requestPermissions();
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error('Failed to request permissions:', error);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const setupEventListeners = () => {
|
|
137
|
+
// Scan result event
|
|
138
|
+
bleMiddleware.on('scanResult', ({ device }: any) => {
|
|
139
|
+
setDiscoveredDevices((prev) => {
|
|
140
|
+
const exists = prev.find((d) => d.id === device.id);
|
|
141
|
+
if (exists) {
|
|
142
|
+
return prev.map((d) => (d.id === device.id ? device : d));
|
|
143
|
+
}
|
|
144
|
+
return [...prev, device];
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Connected event
|
|
149
|
+
bleMiddleware.on('connected', ({ deviceId }: any) => {
|
|
150
|
+
setConnectedDevices((prev) => {
|
|
151
|
+
const newMap = new Map(prev);
|
|
152
|
+
newMap.set(deviceId, {
|
|
153
|
+
deviceId,
|
|
154
|
+
isConnected: true,
|
|
155
|
+
isInfusionRunning: false,
|
|
156
|
+
infusionLevel: null,
|
|
157
|
+
notifications: {
|
|
158
|
+
ff01: null,
|
|
159
|
+
ff21: null,
|
|
160
|
+
ff31: null,
|
|
161
|
+
ff41: null,
|
|
162
|
+
ff02: null,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
return newMap;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Legacy: Set first connected device as primary
|
|
169
|
+
setConnectedDeviceId((prev) => prev || deviceId);
|
|
170
|
+
console.log('✅ Device connected:', deviceId);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Disconnected event
|
|
174
|
+
bleMiddleware.on('disconnected', ({ deviceId }: any) => {
|
|
175
|
+
setConnectedDevices((prev) => {
|
|
176
|
+
const newMap = new Map(prev);
|
|
177
|
+
newMap.delete(deviceId);
|
|
178
|
+
return newMap;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Legacy: Update primary device
|
|
182
|
+
setConnectedDeviceId((prev) => {
|
|
183
|
+
if (prev === deviceId) {
|
|
184
|
+
// If primary device disconnected, set new primary or null
|
|
185
|
+
const remaining = Array.from(connectedDevices.keys()).filter(
|
|
186
|
+
(id) => id !== deviceId
|
|
187
|
+
);
|
|
188
|
+
const newPrimary = remaining[0] || null;
|
|
189
|
+
// Update legacy infusion state
|
|
190
|
+
if (!newPrimary) {
|
|
191
|
+
setIsInfusionRunning(false);
|
|
192
|
+
}
|
|
193
|
+
return newPrimary;
|
|
194
|
+
}
|
|
195
|
+
return prev;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
console.log('❌ Device disconnected:', deviceId);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Scan failed event
|
|
202
|
+
bleMiddleware.on('BleManagerScanFailed', ({ error }: any) => {
|
|
203
|
+
console.error('BLE Scan Failed:', error);
|
|
204
|
+
setIsScanning(false);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Bluetooth state changed event
|
|
208
|
+
bleMiddleware.on('bluetoothStateChanged', ({ state }: any) => {
|
|
209
|
+
setBluetoothEnabled(state === 'poweredOn');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Characteristic changed event
|
|
213
|
+
bleMiddleware.on('characteristicChanged', (event: any) => {
|
|
214
|
+
console.log('Characteristic Changed Event:', event);
|
|
215
|
+
const { characteristicUuid, deviceId } = event;
|
|
216
|
+
const uuidUpper = characteristicUuid?.toUpperCase();
|
|
217
|
+
|
|
218
|
+
// Update device-specific notifications
|
|
219
|
+
setConnectedDevices((prev) => {
|
|
220
|
+
const newMap = new Map(prev);
|
|
221
|
+
const deviceState = newMap.get(deviceId);
|
|
222
|
+
if (deviceState) {
|
|
223
|
+
const updatedState = { ...deviceState };
|
|
224
|
+
if (uuidUpper === '0000FF01-0000-1000-8000-00805F9B34FB') {
|
|
225
|
+
updatedState.notifications.ff01 = event;
|
|
226
|
+
} else if (uuidUpper === '0000FF21-0000-1000-8000-00805F9B34FB') {
|
|
227
|
+
updatedState.notifications.ff21 = event;
|
|
228
|
+
} else if (uuidUpper === '0000FF31-0000-1000-8000-00805F9B34FB') {
|
|
229
|
+
updatedState.notifications.ff31 = event;
|
|
230
|
+
} else if (uuidUpper === '0000FF41-0000-1000-8000-00805F9B34FB') {
|
|
231
|
+
updatedState.notifications.ff41 = event;
|
|
232
|
+
} else if (uuidUpper === '0000FF02-0000-1000-8000-00805F9B34FB') {
|
|
233
|
+
updatedState.notifications.ff02 = event;
|
|
234
|
+
}
|
|
235
|
+
newMap.set(deviceId, updatedState);
|
|
236
|
+
}
|
|
237
|
+
return newMap;
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Legacy: Update global notifications for primary device
|
|
241
|
+
if (deviceId === connectedDeviceId) {
|
|
242
|
+
if (uuidUpper === '0000FF01-0000-1000-8000-00805F9B34FB') {
|
|
243
|
+
setFF01Notification(event);
|
|
244
|
+
} else if (uuidUpper === '0000FF21-0000-1000-8000-00805F9B34FB') {
|
|
245
|
+
setFF21Notification(event);
|
|
246
|
+
} else if (uuidUpper === '0000FF31-0000-1000-8000-00805F9B34FB') {
|
|
247
|
+
setFF31Notification(event);
|
|
248
|
+
} else if (uuidUpper === '0000FF41-0000-1000-8000-00805F9B34FB') {
|
|
249
|
+
setFF41Notification(event);
|
|
250
|
+
} else if (uuidUpper === '0000FF02-0000-1000-8000-00805F9B34FB') {
|
|
251
|
+
setFF02Notification(event);
|
|
252
|
+
} else {
|
|
253
|
+
console.log('⚠️ Unhandled characteristic:', characteristicUuid);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const startScan = async () => {
|
|
260
|
+
console.log('Starting device scan...');
|
|
261
|
+
try {
|
|
262
|
+
setDiscoveredDevices([]);
|
|
263
|
+
setIsScanning(true);
|
|
264
|
+
await bleMiddleware.startScan({ timeout: 10000, allowDuplicates: false });
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.error('Failed to start scan:', error);
|
|
267
|
+
setIsScanning(false);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const stopScan = async () => {
|
|
272
|
+
try {
|
|
273
|
+
await bleMiddleware.stopScan();
|
|
274
|
+
setIsScanning(false);
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error('Failed to stop scan:', error);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const connectToDevice = async (device: BLEDevice) => {
|
|
281
|
+
console.log('Connecting to device:', device);
|
|
282
|
+
try {
|
|
283
|
+
await bleMiddleware.connect(device.id);
|
|
284
|
+
console.log('Connected successfully');
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error('Failed to connect:', error);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const disconnectDevice = async (deviceId: string) => {
|
|
291
|
+
try {
|
|
292
|
+
await bleMiddleware.disconnect(deviceId);
|
|
293
|
+
// State will be updated in the 'disconnected' event handler
|
|
294
|
+
console.log('Disconnected from device:', deviceId);
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.error('Failed to disconnect:', error);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// Multi-device query helpers
|
|
301
|
+
const getDeviceState = (
|
|
302
|
+
deviceId: string
|
|
303
|
+
): DeviceConnectionState | undefined => {
|
|
304
|
+
return connectedDevices.get(deviceId);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const isDeviceConnected = (deviceId: string): boolean => {
|
|
308
|
+
return connectedDevices.has(deviceId);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const getConnectedDeviceIds = (): string[] => {
|
|
312
|
+
return Array.from(connectedDevices.keys());
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const startInfusion = async (deviceId: string) => {
|
|
316
|
+
try {
|
|
317
|
+
await bleMiddleware.startInfusion(deviceId);
|
|
318
|
+
|
|
319
|
+
// Update device-specific state
|
|
320
|
+
setConnectedDevices((prev) => {
|
|
321
|
+
const newMap = new Map(prev);
|
|
322
|
+
const deviceState = newMap.get(deviceId);
|
|
323
|
+
if (deviceState) {
|
|
324
|
+
newMap.set(deviceId, {
|
|
325
|
+
...deviceState,
|
|
326
|
+
isInfusionRunning: true,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
return newMap;
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Legacy: Update global state if this is the primary device
|
|
333
|
+
if (deviceId === connectedDeviceId) {
|
|
334
|
+
setIsInfusionRunning(true);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
console.log('✅ Infusion started successfully for device:', deviceId);
|
|
338
|
+
} catch (error) {
|
|
339
|
+
console.error('Failed to start infusion:', error);
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
const stopInfusion = async (deviceId: string) => {
|
|
345
|
+
try {
|
|
346
|
+
await bleMiddleware.stopInfusion(deviceId);
|
|
347
|
+
|
|
348
|
+
// Update device-specific state
|
|
349
|
+
setConnectedDevices((prev) => {
|
|
350
|
+
const newMap = new Map(prev);
|
|
351
|
+
const deviceState = newMap.get(deviceId);
|
|
352
|
+
if (deviceState) {
|
|
353
|
+
newMap.set(deviceId, {
|
|
354
|
+
...deviceState,
|
|
355
|
+
isInfusionRunning: false,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
return newMap;
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Legacy: Update global state if this is the primary device
|
|
362
|
+
if (deviceId === connectedDeviceId) {
|
|
363
|
+
setIsInfusionRunning(false);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
console.log('✅ Infusion stopped successfully for device:', deviceId);
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error('Failed to stop infusion:', error);
|
|
369
|
+
throw error;
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
const setInfusionLevel = async (deviceId: string, level: number) => {
|
|
374
|
+
try {
|
|
375
|
+
await bleMiddleware.setInfusionLevel(deviceId, level);
|
|
376
|
+
|
|
377
|
+
// Update device-specific state
|
|
378
|
+
setConnectedDevices((prev) => {
|
|
379
|
+
const newMap = new Map(prev);
|
|
380
|
+
const deviceState = newMap.get(deviceId);
|
|
381
|
+
if (deviceState) {
|
|
382
|
+
newMap.set(deviceId, {
|
|
383
|
+
...deviceState,
|
|
384
|
+
infusionLevel: level,
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
return newMap;
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
console.log(
|
|
391
|
+
`✅ Infusion level set to ${level} successfully for device:`,
|
|
392
|
+
deviceId
|
|
393
|
+
);
|
|
394
|
+
} catch (error) {
|
|
395
|
+
console.error('Failed to set infusion level:', error);
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const readCharacteristic = async (
|
|
401
|
+
deviceId: string,
|
|
402
|
+
serviceUuid: string,
|
|
403
|
+
characteristicUuid: string
|
|
404
|
+
) => {
|
|
405
|
+
try {
|
|
406
|
+
return await bleMiddleware.readCharacteristic(
|
|
407
|
+
deviceId,
|
|
408
|
+
serviceUuid,
|
|
409
|
+
characteristicUuid
|
|
410
|
+
);
|
|
411
|
+
} catch (error) {
|
|
412
|
+
console.error('Failed to read characteristic:', error);
|
|
413
|
+
throw error;
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const writeCharacteristic = async (
|
|
418
|
+
deviceId: string,
|
|
419
|
+
serviceUuid: string,
|
|
420
|
+
characteristicUuid: string,
|
|
421
|
+
data: string,
|
|
422
|
+
options?: { withResponse?: boolean }
|
|
423
|
+
) => {
|
|
424
|
+
try {
|
|
425
|
+
await bleMiddleware.writeCharacteristic(
|
|
426
|
+
deviceId,
|
|
427
|
+
serviceUuid,
|
|
428
|
+
characteristicUuid,
|
|
429
|
+
data,
|
|
430
|
+
options
|
|
431
|
+
);
|
|
432
|
+
} catch (error) {
|
|
433
|
+
console.error('Failed to write characteristic:', error);
|
|
434
|
+
throw error;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
const setUserRole = (role: UserRole) => {
|
|
439
|
+
bleMiddleware.setUserRole(role);
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const getUserRole = (): UserRole => {
|
|
443
|
+
return bleMiddleware.getUserRole();
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const value: BLEContextState = {
|
|
447
|
+
bluetoothEnabled,
|
|
448
|
+
isScanning,
|
|
449
|
+
discoveredDevices,
|
|
450
|
+
connectedDevices,
|
|
451
|
+
connectedDeviceId,
|
|
452
|
+
isInfusionRunning,
|
|
453
|
+
ff01Notification,
|
|
454
|
+
ff21Notification,
|
|
455
|
+
ff31Notification,
|
|
456
|
+
ff41Notification,
|
|
457
|
+
ff02Notification,
|
|
458
|
+
startScan,
|
|
459
|
+
stopScan,
|
|
460
|
+
connectToDevice,
|
|
461
|
+
disconnectDevice,
|
|
462
|
+
requestPermissions,
|
|
463
|
+
getDeviceState,
|
|
464
|
+
isDeviceConnected,
|
|
465
|
+
getConnectedDeviceIds,
|
|
466
|
+
startInfusion,
|
|
467
|
+
stopInfusion,
|
|
468
|
+
setInfusionLevel,
|
|
469
|
+
readCharacteristic,
|
|
470
|
+
writeCharacteristic,
|
|
471
|
+
setUserRole,
|
|
472
|
+
getUserRole,
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
return <BLEContext.Provider value={value}>{children}</BLEContext.Provider>;
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Hook to access BLE context
|
|
480
|
+
*/
|
|
481
|
+
export const useBLEContext = (): BLEContextState => {
|
|
482
|
+
const context = useContext(BLEContext);
|
|
483
|
+
if (!context) {
|
|
484
|
+
throw new Error('useBLEContext must be used within a BLEProvider');
|
|
485
|
+
}
|
|
486
|
+
return context;
|
|
487
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useBLE';
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { useBLEContext } from '../context/BLEContext';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook to access BLE functionality
|
|
5
|
+
* Provides easy access to BLE operations and state
|
|
6
|
+
*/
|
|
7
|
+
export const useBLE = () => {
|
|
8
|
+
const context = useBLEContext();
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
// State
|
|
12
|
+
bluetoothEnabled: context.bluetoothEnabled,
|
|
13
|
+
isScanning: context.isScanning,
|
|
14
|
+
discoveredDevices: context.discoveredDevices,
|
|
15
|
+
|
|
16
|
+
// Multi-device support
|
|
17
|
+
connectedDevices: context.connectedDevices,
|
|
18
|
+
getDeviceState: context.getDeviceState,
|
|
19
|
+
isDeviceConnected: context.isDeviceConnected,
|
|
20
|
+
getConnectedDeviceIds: context.getConnectedDeviceIds,
|
|
21
|
+
|
|
22
|
+
// Legacy single device support (for backward compatibility)
|
|
23
|
+
connectedDeviceId: context.connectedDeviceId,
|
|
24
|
+
isInfusionRunning: context.isInfusionRunning,
|
|
25
|
+
|
|
26
|
+
// Actions
|
|
27
|
+
startScan: context.startScan,
|
|
28
|
+
stopScan: context.stopScan,
|
|
29
|
+
connectToDevice: context.connectToDevice,
|
|
30
|
+
disconnectDevice: context.disconnectDevice,
|
|
31
|
+
requestPermissions: context.requestPermissions,
|
|
32
|
+
|
|
33
|
+
// Infusion control
|
|
34
|
+
startInfusion: context.startInfusion,
|
|
35
|
+
stopInfusion: context.stopInfusion,
|
|
36
|
+
setInfusionLevel: context.setInfusionLevel,
|
|
37
|
+
|
|
38
|
+
// Characteristic operations
|
|
39
|
+
readCharacteristic: context.readCharacteristic,
|
|
40
|
+
writeCharacteristic: context.writeCharacteristic,
|
|
41
|
+
|
|
42
|
+
// User role management
|
|
43
|
+
setUserRole: context.setUserRole,
|
|
44
|
+
getUserRole: context.getUserRole,
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Hook to access characteristic notifications
|
|
50
|
+
*/
|
|
51
|
+
export const useBLENotifications = () => {
|
|
52
|
+
const context = useBLEContext();
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
ff21Notification: context.ff21Notification,
|
|
56
|
+
ff31Notification: context.ff31Notification,
|
|
57
|
+
ff41Notification: context.ff41Notification,
|
|
58
|
+
ff02Notification: context.ff02Notification,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Hook to get connection status
|
|
64
|
+
*/
|
|
65
|
+
export const useBLEConnection = () => {
|
|
66
|
+
const context = useBLEContext();
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
isConnected: context.connectedDeviceId !== null,
|
|
70
|
+
connectedDeviceId: context.connectedDeviceId,
|
|
71
|
+
disconnect: context.disconnectDevice,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Hook to access discovered devices
|
|
77
|
+
*/
|
|
78
|
+
export const useBLEDevices = () => {
|
|
79
|
+
const context = useBLEContext();
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
devices: context.discoveredDevices,
|
|
83
|
+
isScanning: context.isScanning,
|
|
84
|
+
startScan: context.startScan,
|
|
85
|
+
stopScan: context.stopScan,
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Hook for infusion control
|
|
91
|
+
*/
|
|
92
|
+
export const useBLEInfusion = () => {
|
|
93
|
+
const context = useBLEContext();
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
isInfusionRunning: context.isInfusionRunning,
|
|
97
|
+
startInfusion: context.startInfusion,
|
|
98
|
+
stopInfusion: context.stopInfusion,
|
|
99
|
+
setInfusionLevel: context.setInfusionLevel,
|
|
100
|
+
connectedDeviceId: context.connectedDeviceId,
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Hook for characteristic operations
|
|
106
|
+
*/
|
|
107
|
+
export const useBLECharacteristics = () => {
|
|
108
|
+
const context = useBLEContext();
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
readCharacteristic: context.readCharacteristic,
|
|
112
|
+
writeCharacteristic: context.writeCharacteristic,
|
|
113
|
+
connectedDeviceId: context.connectedDeviceId,
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Hook for multi-device management
|
|
119
|
+
* Provides access to all connected devices and their states
|
|
120
|
+
*/
|
|
121
|
+
export const useBLEMultiDevice = () => {
|
|
122
|
+
const context = useBLEContext();
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
connectedDevices: context.connectedDevices,
|
|
126
|
+
getDeviceState: context.getDeviceState,
|
|
127
|
+
isDeviceConnected: context.isDeviceConnected,
|
|
128
|
+
getConnectedDeviceIds: context.getConnectedDeviceIds,
|
|
129
|
+
connectToDevice: context.connectToDevice,
|
|
130
|
+
disconnectDevice: context.disconnectDevice,
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Hook for specific device notifications
|
|
136
|
+
* @param deviceId - The device ID to get notifications for
|
|
137
|
+
*/
|
|
138
|
+
export const useBLEDeviceNotifications = (deviceId: string | null) => {
|
|
139
|
+
const context = useBLEContext();
|
|
140
|
+
|
|
141
|
+
if (!deviceId) {
|
|
142
|
+
return {
|
|
143
|
+
ff21Notification: null,
|
|
144
|
+
ff31Notification: null,
|
|
145
|
+
ff41Notification: null,
|
|
146
|
+
ff02Notification: null,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const deviceState = context.getDeviceState(deviceId);
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
ff21Notification: deviceState?.notifications.ff21 || null,
|
|
154
|
+
ff31Notification: deviceState?.notifications.ff31 || null,
|
|
155
|
+
ff41Notification: deviceState?.notifications.ff41 || null,
|
|
156
|
+
ff02Notification: deviceState?.notifications.ff02 || null,
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Hook for specific device infusion control
|
|
162
|
+
* @param deviceId - The device ID to control infusion for
|
|
163
|
+
*/
|
|
164
|
+
export const useBLEDeviceInfusion = (deviceId: string | null) => {
|
|
165
|
+
const context = useBLEContext();
|
|
166
|
+
|
|
167
|
+
const deviceState = deviceId ? context.getDeviceState(deviceId) : undefined;
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
deviceId,
|
|
171
|
+
isConnected: deviceId ? context.isDeviceConnected(deviceId) : false,
|
|
172
|
+
isInfusionRunning: deviceState?.isInfusionRunning || false,
|
|
173
|
+
infusionLevel: deviceState?.infusionLevel || null,
|
|
174
|
+
startInfusion: deviceId
|
|
175
|
+
? () => context.startInfusion(deviceId)
|
|
176
|
+
: async () => {
|
|
177
|
+
throw new Error('No device selected');
|
|
178
|
+
},
|
|
179
|
+
stopInfusion: deviceId
|
|
180
|
+
? () => context.stopInfusion(deviceId)
|
|
181
|
+
: async () => {
|
|
182
|
+
throw new Error('No device selected');
|
|
183
|
+
},
|
|
184
|
+
setInfusionLevel: deviceId
|
|
185
|
+
? (level: number) => context.setInfusionLevel(deviceId, level)
|
|
186
|
+
: async () => {
|
|
187
|
+
throw new Error('No device selected');
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
};
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Export types
|
|
2
|
+
export * from './types';
|
|
3
|
+
|
|
4
|
+
// Export BLE Middleware service
|
|
5
|
+
export { default as bleMiddleware } from './services/BLEMiddleware';
|
|
6
|
+
|
|
7
|
+
// Export Context and Provider
|
|
8
|
+
export { BLEProvider, useBLEContext } from './context/BLEContext';
|
|
9
|
+
|
|
10
|
+
// Export hooks
|
|
11
|
+
export {
|
|
12
|
+
useBLE,
|
|
13
|
+
useBLENotifications,
|
|
14
|
+
useBLEConnection,
|
|
15
|
+
useBLEDevices,
|
|
16
|
+
useBLEInfusion,
|
|
17
|
+
useBLECharacteristics,
|
|
18
|
+
useBLEMultiDevice,
|
|
19
|
+
useBLEDeviceNotifications,
|
|
20
|
+
useBLEDeviceInfusion,
|
|
21
|
+
} from './hooks/useBLE';
|