react-native-ble-nitro 1.11.0 → 1.13.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/README.md +5 -0
- package/android/src/main/java/com/margelo/nitro/co/zyke/ble/BleNitroBleManager.kt +72 -26
- package/ios/BleNitroBleManager.swift +71 -9
- package/ios/BlePeripheralDelegate.swift +68 -9
- package/lib/commonjs/index.d.ts +1 -1
- package/lib/commonjs/index.d.ts.map +1 -1
- package/lib/commonjs/index.js +2 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/manager.d.ts +17 -1
- package/lib/commonjs/manager.d.ts.map +1 -1
- package/lib/commonjs/manager.js +54 -9
- package/lib/commonjs/manager.js.map +1 -1
- package/lib/commonjs/specs/NativeBleNitro.nitro.d.ts +3 -0
- package/lib/commonjs/specs/NativeBleNitro.nitro.d.ts.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/manager.d.ts +17 -1
- package/lib/manager.js +52 -8
- package/lib/specs/NativeBleNitro.nitro.d.ts +3 -0
- package/nitrogen/generated/android/c++/JHybridNativeBleNitroSpec.cpp +9 -0
- package/nitrogen/generated/android/c++/JHybridNativeBleNitroSpec.hpp +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/co/zyke/ble/HybridNativeBleNitroSpec.kt +13 -0
- package/nitrogen/generated/ios/c++/HybridNativeBleNitroSpecSwift.hpp +14 -0
- package/nitrogen/generated/ios/swift/HybridNativeBleNitroSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridNativeBleNitroSpec_cxx.swift +28 -0
- package/nitrogen/generated/shared/c++/HybridNativeBleNitroSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridNativeBleNitroSpec.hpp +2 -0
- package/package.json +1 -1
- package/src/__tests__/index.test.ts +127 -0
- package/src/index.ts +1 -0
- package/src/manager.ts +81 -9
- package/src/specs/NativeBleNitro.nitro.ts +3 -0
package/README.md
CHANGED
|
@@ -285,6 +285,11 @@ await subscription.remove();
|
|
|
285
285
|
|
|
286
286
|
// Or unsubscribe directly
|
|
287
287
|
await ble.unsubscribeFromCharacteristic(deviceId, serviceUUID, characteristicUUID);
|
|
288
|
+
|
|
289
|
+
// Check if a characteristic is currently subscribed to notifications
|
|
290
|
+
const isSubscribed = ble.isSubscribedToCharacteristic(deviceId, serviceUUID, characteristicUUID);
|
|
291
|
+
// Returns: boolean — synchronous, no async overhead
|
|
292
|
+
// Returns false for disconnected or unknown devices without throwing
|
|
288
293
|
```
|
|
289
294
|
|
|
290
295
|
### Real-World Examples
|
|
@@ -75,7 +75,9 @@ class BleNitroBleManager : HybridNativeBleNitroSpec() {
|
|
|
75
75
|
var connectCallback: ((success: Boolean, deviceId: String, error: String) -> Unit)? = null,
|
|
76
76
|
var disconnectCallback: ((deviceId: String, interrupted: Boolean, error: String) -> Unit)? = null,
|
|
77
77
|
var serviceDiscoveryCallback: ((success: Boolean, error: String) -> Unit)? = null,
|
|
78
|
-
|
|
78
|
+
// Key: "serviceId:characteristicId" for correct scoping when the same
|
|
79
|
+
// characteristic UUID exists under multiple services.
|
|
80
|
+
var characteristicSubscriptions: MutableMap<String, (characteristicId: String, data: ArrayBuffer) -> Unit> = ConcurrentHashMap()
|
|
79
81
|
)
|
|
80
82
|
|
|
81
83
|
// Sealed class for GATT operations that need to be queued
|
|
@@ -264,18 +266,21 @@ class BleNitroBleManager : HybridNativeBleNitroSpec() {
|
|
|
264
266
|
private fun createGattCallback(deviceId: String): BluetoothGattCallback {
|
|
265
267
|
return object : BluetoothGattCallback() {
|
|
266
268
|
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
|
267
|
-
val callbacks = deviceCallbacks[deviceId]
|
|
268
|
-
|
|
269
269
|
when (newState) {
|
|
270
270
|
BluetoothProfile.STATE_CONNECTED -> {
|
|
271
|
-
|
|
271
|
+
var connectCb: ((Boolean, String, String) -> Unit)? = null
|
|
272
|
+
deviceCallbacks.compute(deviceId) { _, callbacks ->
|
|
273
|
+
connectCb = callbacks?.connectCallback
|
|
274
|
+
callbacks?.copy(connectCallback = null)
|
|
275
|
+
}
|
|
276
|
+
connectCb?.invoke(true, deviceId, "")
|
|
272
277
|
}
|
|
273
278
|
BluetoothProfile.STATE_DISCONNECTED -> {
|
|
274
279
|
// Clean up
|
|
275
280
|
connectedDevices.remove(deviceId)
|
|
276
281
|
val interrupted = status != BluetoothGatt.GATT_SUCCESS
|
|
277
|
-
|
|
278
|
-
|
|
282
|
+
val cb = deviceCallbacks.remove(deviceId)
|
|
283
|
+
cb?.disconnectCallback?.invoke(deviceId, interrupted, if (interrupted) "Connection lost" else "")
|
|
279
284
|
gatt.close()
|
|
280
285
|
}
|
|
281
286
|
}
|
|
@@ -352,17 +357,23 @@ class BleNitroBleManager : HybridNativeBleNitroSpec() {
|
|
|
352
357
|
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
|
|
353
358
|
// Handle characteristic notifications
|
|
354
359
|
val characteristicId = characteristic.uuid.toString()
|
|
360
|
+
val serviceId = characteristic.service?.uuid?.toString()
|
|
361
|
+
if (serviceId == null) {
|
|
362
|
+
android.util.Log.w("BleNitro", "onCharacteristicChanged: characteristic.service is null for $characteristicId")
|
|
363
|
+
return
|
|
364
|
+
}
|
|
365
|
+
val subscriptionKey = "$serviceId:$characteristicId"
|
|
355
366
|
val value = characteristic.value ?: byteArrayOf()
|
|
356
|
-
|
|
367
|
+
|
|
357
368
|
// Create direct ByteBuffer as required by ArrayBuffer.wrap()
|
|
358
369
|
val directBuffer = java.nio.ByteBuffer.allocateDirect(value.size)
|
|
359
370
|
directBuffer.put(value)
|
|
360
371
|
directBuffer.flip()
|
|
361
|
-
|
|
372
|
+
|
|
362
373
|
val data = ArrayBuffer.wrap(directBuffer)
|
|
363
|
-
|
|
374
|
+
|
|
364
375
|
val callbacks = deviceCallbacks[deviceId]
|
|
365
|
-
callbacks?.characteristicSubscriptions?.get(
|
|
376
|
+
callbacks?.characteristicSubscriptions?.get(subscriptionKey)?.invoke(characteristicId, data)
|
|
366
377
|
}
|
|
367
378
|
|
|
368
379
|
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
|
|
@@ -649,6 +660,12 @@ class BleNitroBleManager : HybridNativeBleNitroSpec() {
|
|
|
649
660
|
try {
|
|
650
661
|
val gatt = connectedDevices[deviceId]
|
|
651
662
|
if (gatt != null) {
|
|
663
|
+
var pendingConnect: ((Boolean, String, String) -> Unit)? = null
|
|
664
|
+
deviceCallbacks.compute(deviceId) { _, callbacks ->
|
|
665
|
+
pendingConnect = callbacks?.connectCallback
|
|
666
|
+
callbacks?.copy(connectCallback = null)
|
|
667
|
+
}
|
|
668
|
+
pendingConnect?.invoke(false, deviceId, "Connection cancelled")
|
|
652
669
|
gatt.disconnect()
|
|
653
670
|
callback(true, "")
|
|
654
671
|
} else {
|
|
@@ -733,6 +750,12 @@ class BleNitroBleManager : HybridNativeBleNitroSpec() {
|
|
|
733
750
|
}
|
|
734
751
|
}
|
|
735
752
|
|
|
753
|
+
override fun discoverServicesWithCharacteristics(deviceId: String, callback: (success: Boolean, error: String) -> Unit) {
|
|
754
|
+
// On Android, discoverServices() already discovers characteristics automatically.
|
|
755
|
+
// Delegate directly to discoverServices.
|
|
756
|
+
discoverServices(deviceId, callback)
|
|
757
|
+
}
|
|
758
|
+
|
|
736
759
|
override fun getServices(deviceId: String): Array<String> {
|
|
737
760
|
return try {
|
|
738
761
|
val gatt = connectedDevices[deviceId]
|
|
@@ -896,14 +919,11 @@ class BleNitroBleManager : HybridNativeBleNitroSpec() {
|
|
|
896
919
|
return
|
|
897
920
|
}
|
|
898
921
|
|
|
899
|
-
|
|
900
|
-
val callbacks = deviceCallbacks[deviceId]
|
|
901
|
-
if (callbacks != null) {
|
|
902
|
-
callbacks.characteristicSubscriptions[characteristicId] = updateCallback
|
|
903
|
-
}
|
|
922
|
+
val subscriptionKey = "$serviceId:$characteristicId"
|
|
904
923
|
|
|
905
|
-
// Write to the CCCD descriptor to enable notifications on the remote device
|
|
906
|
-
//
|
|
924
|
+
// Write to the CCCD descriptor to enable notifications on the remote device.
|
|
925
|
+
// The update callback is stored only after the descriptor write succeeds
|
|
926
|
+
// so that isSubscribedToCharacteristic reflects actual BLE state.
|
|
907
927
|
val descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))
|
|
908
928
|
if (descriptor != null) {
|
|
909
929
|
enqueueGattOperation(
|
|
@@ -912,11 +932,19 @@ class BleNitroBleManager : HybridNativeBleNitroSpec() {
|
|
|
912
932
|
descriptor = descriptor,
|
|
913
933
|
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE,
|
|
914
934
|
deviceId = deviceId,
|
|
915
|
-
callback =
|
|
935
|
+
callback = { success, error ->
|
|
936
|
+
if (success) {
|
|
937
|
+
val callbacks = deviceCallbacks[deviceId]
|
|
938
|
+
callbacks?.characteristicSubscriptions?.set(subscriptionKey, updateCallback)
|
|
939
|
+
}
|
|
940
|
+
completionCallback(success, error)
|
|
941
|
+
}
|
|
916
942
|
)
|
|
917
943
|
)
|
|
918
944
|
} else {
|
|
919
|
-
// No CCCD descriptor -
|
|
945
|
+
// No CCCD descriptor - store callback and report success
|
|
946
|
+
val callbacks = deviceCallbacks[deviceId]
|
|
947
|
+
callbacks?.characteristicSubscriptions?.set(subscriptionKey, updateCallback)
|
|
920
948
|
completionCallback(true, "")
|
|
921
949
|
}
|
|
922
950
|
|
|
@@ -957,12 +985,12 @@ class BleNitroBleManager : HybridNativeBleNitroSpec() {
|
|
|
957
985
|
return
|
|
958
986
|
}
|
|
959
987
|
|
|
960
|
-
|
|
961
|
-
val callbacks = deviceCallbacks[deviceId]
|
|
962
|
-
callbacks?.characteristicSubscriptions?.remove(characteristicId)
|
|
988
|
+
val subscriptionKey = "$serviceId:$characteristicId"
|
|
963
989
|
|
|
964
|
-
// Write to the CCCD descriptor to disable notifications on the remote device
|
|
965
|
-
//
|
|
990
|
+
// Write to the CCCD descriptor to disable notifications on the remote device.
|
|
991
|
+
// The subscription entry is removed only after the descriptor write succeeds,
|
|
992
|
+
// mirroring the subscribe pattern to avoid a stale-entry race when
|
|
993
|
+
// subscribe and unsubscribe are called in rapid succession.
|
|
966
994
|
val descriptor = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))
|
|
967
995
|
if (descriptor != null) {
|
|
968
996
|
enqueueGattOperation(
|
|
@@ -971,11 +999,19 @@ class BleNitroBleManager : HybridNativeBleNitroSpec() {
|
|
|
971
999
|
descriptor = descriptor,
|
|
972
1000
|
value = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE,
|
|
973
1001
|
deviceId = deviceId,
|
|
974
|
-
callback =
|
|
1002
|
+
callback = { success, error ->
|
|
1003
|
+
if (success) {
|
|
1004
|
+
val callbacks = deviceCallbacks[deviceId]
|
|
1005
|
+
callbacks?.characteristicSubscriptions?.remove(subscriptionKey)
|
|
1006
|
+
}
|
|
1007
|
+
callback(success, error)
|
|
1008
|
+
}
|
|
975
1009
|
)
|
|
976
1010
|
)
|
|
977
1011
|
} else {
|
|
978
|
-
// No CCCD descriptor -
|
|
1012
|
+
// No CCCD descriptor - remove immediately
|
|
1013
|
+
val callbacks = deviceCallbacks[deviceId]
|
|
1014
|
+
callbacks?.characteristicSubscriptions?.remove(subscriptionKey)
|
|
979
1015
|
callback(true, "")
|
|
980
1016
|
}
|
|
981
1017
|
|
|
@@ -984,6 +1020,16 @@ class BleNitroBleManager : HybridNativeBleNitroSpec() {
|
|
|
984
1020
|
}
|
|
985
1021
|
}
|
|
986
1022
|
|
|
1023
|
+
override fun isSubscribedToCharacteristic(
|
|
1024
|
+
deviceId: String,
|
|
1025
|
+
serviceId: String,
|
|
1026
|
+
characteristicId: String
|
|
1027
|
+
): Boolean {
|
|
1028
|
+
val callbacks = deviceCallbacks[deviceId] ?: return false
|
|
1029
|
+
val subscriptionKey = "$serviceId:$characteristicId"
|
|
1030
|
+
return callbacks.characteristicSubscriptions.containsKey(subscriptionKey)
|
|
1031
|
+
}
|
|
1032
|
+
|
|
987
1033
|
// Bluetooth state management
|
|
988
1034
|
override fun requestBluetoothEnable(callback: (success: Boolean, error: String) -> Unit) {
|
|
989
1035
|
try {
|
|
@@ -255,17 +255,22 @@ public class BleNitroBleManager: HybridNativeBleNitroSpec {
|
|
|
255
255
|
|
|
256
256
|
public func disconnect(deviceId: String, callback: @escaping (Bool, String) -> Void) throws {
|
|
257
257
|
ensureCentralManager()
|
|
258
|
-
guard let peripheral = connectedPeripherals[deviceId] else {
|
|
259
|
-
callback(false, "Peripheral not
|
|
258
|
+
guard let peripheral = connectedPeripherals[deviceId] ?? findPeripheral(by: deviceId) else {
|
|
259
|
+
callback(false, "Peripheral not found")
|
|
260
260
|
return
|
|
261
261
|
}
|
|
262
|
-
|
|
262
|
+
|
|
263
|
+
if peripheral.state == .connecting, let delegate = peripheralDelegates[deviceId] {
|
|
264
|
+
delegate.connectionCallback?(false, "", "Connection cancelled")
|
|
265
|
+
delegate.connectionCallback = nil
|
|
266
|
+
}
|
|
267
|
+
|
|
263
268
|
// Store disconnect callback in delegate
|
|
264
269
|
peripheralDelegates[deviceId]?.disconnectionCallback = callback
|
|
265
|
-
|
|
270
|
+
|
|
266
271
|
// Mark this as an intentional disconnection
|
|
267
272
|
intentionalDisconnections.insert(deviceId)
|
|
268
|
-
|
|
273
|
+
|
|
269
274
|
centralManager.cancelPeripheralConnection(peripheral)
|
|
270
275
|
}
|
|
271
276
|
|
|
@@ -310,10 +315,50 @@ public class BleNitroBleManager: HybridNativeBleNitroSpec {
|
|
|
310
315
|
callback(false, "Peripheral not connected")
|
|
311
316
|
return
|
|
312
317
|
}
|
|
313
|
-
|
|
314
|
-
peripheralDelegates[deviceId]
|
|
318
|
+
|
|
319
|
+
guard let delegate = peripheralDelegates[deviceId] else {
|
|
320
|
+
callback(false, "Peripheral delegate not found")
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// If services already discovered, resolve immediately to avoid
|
|
325
|
+
// CoreBluetooth silently skipping the didDiscoverServices callback.
|
|
326
|
+
if delegate.servicesDiscovered, let services = peripheral.services, !services.isEmpty {
|
|
327
|
+
callback(true, "")
|
|
328
|
+
return
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
delegate.serviceDiscoveryCallback = callback
|
|
315
332
|
peripheral.discoverServices(nil)
|
|
316
333
|
}
|
|
334
|
+
|
|
335
|
+
public func discoverServicesWithCharacteristics(deviceId: String, callback: @escaping (Bool, String) -> Void) throws {
|
|
336
|
+
guard let peripheral = connectedPeripherals[deviceId] else {
|
|
337
|
+
callback(false, "Peripheral not connected")
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
guard let delegate = peripheralDelegates[deviceId] else {
|
|
342
|
+
callback(false, "Peripheral delegate not found")
|
|
343
|
+
return
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// If services and all characteristics are already discovered, resolve immediately.
|
|
347
|
+
if delegate.servicesDiscovered,
|
|
348
|
+
let services = peripheral.services, !services.isEmpty,
|
|
349
|
+
services.allSatisfy({ $0.characteristics != nil }) {
|
|
350
|
+
callback(true, "")
|
|
351
|
+
return
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
let isFirstCaller = delegate.fullDiscoveryCallbacks.isEmpty
|
|
355
|
+
delegate.fullDiscoveryCallbacks.append(callback)
|
|
356
|
+
// Only trigger discovery if this is the first caller; subsequent
|
|
357
|
+
// callers piggyback on the in-flight discovery via the callback array.
|
|
358
|
+
if isFirstCaller {
|
|
359
|
+
peripheral.discoverServices(nil)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
317
362
|
|
|
318
363
|
public func getServices(deviceId: String) throws -> [String] {
|
|
319
364
|
guard let peripheral = connectedPeripherals[deviceId] else {
|
|
@@ -464,7 +509,24 @@ public class BleNitroBleManager: HybridNativeBleNitroSpec {
|
|
|
464
509
|
|
|
465
510
|
characteristic.service?.peripheral?.setNotifyValue(false, for: characteristic)
|
|
466
511
|
}
|
|
467
|
-
|
|
512
|
+
|
|
513
|
+
// `throws` is required by the Nitro-generated HybridNativeBleNitroSpec protocol
|
|
514
|
+
// for all synchronous methods, even though this implementation never throws.
|
|
515
|
+
public func isSubscribedToCharacteristic(
|
|
516
|
+
deviceId: String,
|
|
517
|
+
serviceId: String,
|
|
518
|
+
characteristicId: String
|
|
519
|
+
) throws -> Bool {
|
|
520
|
+
guard let characteristic = findCharacteristic(
|
|
521
|
+
deviceId: deviceId,
|
|
522
|
+
serviceId: serviceId,
|
|
523
|
+
characteristicId: characteristicId
|
|
524
|
+
) else {
|
|
525
|
+
return false
|
|
526
|
+
}
|
|
527
|
+
return characteristic.isNotifying
|
|
528
|
+
}
|
|
529
|
+
|
|
468
530
|
// MARK: - Helper Methods
|
|
469
531
|
private func findPeripheral(by deviceId: String) -> CBPeripheral? {
|
|
470
532
|
ensureCentralManager()
|
|
@@ -657,7 +719,7 @@ public class BleNitroBleManager: HybridNativeBleNitroSpec {
|
|
|
657
719
|
rssi: Double
|
|
658
720
|
) -> BLEDevice {
|
|
659
721
|
let deviceId = peripheral.identifier.uuidString
|
|
660
|
-
let deviceName =
|
|
722
|
+
let deviceName = advertisementData[CBAdvertisementDataLocalNameKey] as? String ?? peripheral.name ?? "Unknown"
|
|
661
723
|
|
|
662
724
|
// Extract service UUIDs
|
|
663
725
|
let serviceUUIDs = (advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID])?.map { $0.uuidString } ?? []
|
|
@@ -18,7 +18,14 @@ class BlePeripheralDelegate: NSObject, CBPeripheralDelegate {
|
|
|
18
18
|
var disconnectEventCallback: ((String, Bool, String) -> Void)?
|
|
19
19
|
var serviceDiscoveryCallback: ((Bool, String) -> Void)?
|
|
20
20
|
var characteristicDiscoveryCallbacks: [String: (Bool, String) -> Void] = [:]
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
// Full discovery (services + characteristics) callbacks and state
|
|
23
|
+
var fullDiscoveryCallbacks: [(Bool, String) -> Void] = []
|
|
24
|
+
var servicesDiscovered = false
|
|
25
|
+
var characteristicsDiscoveredCount = 0
|
|
26
|
+
var expectedCharacteristicsCount = 0
|
|
27
|
+
var characteristicDiscoveryError: String?
|
|
28
|
+
|
|
22
29
|
// RSSI callback
|
|
23
30
|
var rssiCallback: ((Bool, Double, String) -> Void)?
|
|
24
31
|
|
|
@@ -45,15 +52,34 @@ class BlePeripheralDelegate: NSObject, CBPeripheralDelegate {
|
|
|
45
52
|
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
|
46
53
|
if let error = error {
|
|
47
54
|
serviceDiscoveryCallback?(false, error.localizedDescription)
|
|
55
|
+
serviceDiscoveryCallback = nil
|
|
56
|
+
resolveFullDiscoveryCallbacks(success: false, error: error.localizedDescription)
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
servicesDiscovered = true
|
|
61
|
+
|
|
62
|
+
// Resolve service-only callback immediately
|
|
63
|
+
serviceDiscoveryCallback?(true, "")
|
|
64
|
+
serviceDiscoveryCallback = nil
|
|
65
|
+
|
|
66
|
+
// Trigger characteristic discovery only for services that still need it.
|
|
67
|
+
// CoreBluetooth may skip the didDiscoverCharacteristicsFor callback for
|
|
68
|
+
// services whose characteristics are already cached, which would leave the
|
|
69
|
+
// counter stuck below expectedCharacteristicsCount.
|
|
70
|
+
let services = peripheral.services ?? []
|
|
71
|
+
let undiscovered = services.filter { $0.characteristics == nil }
|
|
72
|
+
|
|
73
|
+
if undiscovered.isEmpty {
|
|
74
|
+
resolveFullDiscoveryCallbacks(success: true, error: "")
|
|
48
75
|
} else {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
76
|
+
expectedCharacteristicsCount = undiscovered.count
|
|
77
|
+
characteristicsDiscoveredCount = 0
|
|
78
|
+
characteristicDiscoveryError = nil
|
|
79
|
+
for service in undiscovered {
|
|
53
80
|
peripheral.discoverCharacteristics(nil, for: service)
|
|
54
81
|
}
|
|
55
82
|
}
|
|
56
|
-
serviceDiscoveryCallback = nil
|
|
57
83
|
}
|
|
58
84
|
|
|
59
85
|
func peripheral(
|
|
@@ -62,13 +88,27 @@ class BlePeripheralDelegate: NSObject, CBPeripheralDelegate {
|
|
|
62
88
|
error: Error?
|
|
63
89
|
) {
|
|
64
90
|
let serviceId = service.uuid.uuidString
|
|
65
|
-
|
|
91
|
+
|
|
66
92
|
if let error = error {
|
|
67
93
|
characteristicDiscoveryCallbacks[serviceId]?(false, error.localizedDescription)
|
|
94
|
+
// Record the first error for the full discovery callback
|
|
95
|
+
if characteristicDiscoveryError == nil {
|
|
96
|
+
characteristicDiscoveryError = "Characteristic discovery failed for service \(serviceId): \(error.localizedDescription)"
|
|
97
|
+
}
|
|
68
98
|
} else {
|
|
69
99
|
characteristicDiscoveryCallbacks[serviceId]?(true, "")
|
|
70
100
|
}
|
|
71
101
|
characteristicDiscoveryCallbacks.removeValue(forKey: serviceId)
|
|
102
|
+
|
|
103
|
+
// Track full discovery progress
|
|
104
|
+
characteristicsDiscoveredCount += 1
|
|
105
|
+
if characteristicsDiscoveredCount >= expectedCharacteristicsCount {
|
|
106
|
+
if let discoveryError = characteristicDiscoveryError {
|
|
107
|
+
resolveFullDiscoveryCallbacks(success: false, error: discoveryError)
|
|
108
|
+
} else {
|
|
109
|
+
resolveFullDiscoveryCallbacks(success: true, error: "")
|
|
110
|
+
}
|
|
111
|
+
}
|
|
72
112
|
}
|
|
73
113
|
|
|
74
114
|
// MARK: - CBPeripheralDelegate - Characteristic Read
|
|
@@ -197,10 +237,23 @@ class BlePeripheralDelegate: NSObject, CBPeripheralDelegate {
|
|
|
197
237
|
// MARK: - CBPeripheralDelegate - Connection Events
|
|
198
238
|
|
|
199
239
|
func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
|
|
200
|
-
|
|
201
|
-
|
|
240
|
+
resolveFullDiscoveryCallbacks(success: false, error: "Services were invalidated during discovery")
|
|
241
|
+
servicesDiscovered = false
|
|
242
|
+
characteristicsDiscoveredCount = 0
|
|
243
|
+
expectedCharacteristicsCount = 0
|
|
244
|
+
characteristicDiscoveryError = nil
|
|
202
245
|
}
|
|
203
246
|
|
|
247
|
+
// MARK: - Full Discovery Helpers
|
|
248
|
+
|
|
249
|
+
private func resolveFullDiscoveryCallbacks(success: Bool, error: String) {
|
|
250
|
+
let callbacks = fullDiscoveryCallbacks
|
|
251
|
+
fullDiscoveryCallbacks.removeAll()
|
|
252
|
+
for callback in callbacks {
|
|
253
|
+
callback(success, error)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
204
257
|
func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
|
|
205
258
|
// Handle write without response ready state
|
|
206
259
|
// Can be used to implement queuing for write operations
|
|
@@ -208,11 +261,17 @@ class BlePeripheralDelegate: NSObject, CBPeripheralDelegate {
|
|
|
208
261
|
|
|
209
262
|
// MARK: - Cleanup
|
|
210
263
|
func cleanup() {
|
|
264
|
+
// Reject in-flight discovery callbacks before clearing
|
|
265
|
+
resolveFullDiscoveryCallbacks(success: false, error: "Peripheral disconnected during discovery")
|
|
211
266
|
// Clear all callbacks to prevent memory leaks
|
|
212
267
|
connectionCallback = nil
|
|
213
268
|
disconnectionCallback = nil
|
|
214
269
|
disconnectEventCallback = nil
|
|
215
270
|
serviceDiscoveryCallback = nil
|
|
271
|
+
servicesDiscovered = false
|
|
272
|
+
characteristicsDiscoveredCount = 0
|
|
273
|
+
expectedCharacteristicsCount = 0
|
|
274
|
+
characteristicDiscoveryError = nil
|
|
216
275
|
characteristicDiscoveryCallbacks.removeAll()
|
|
217
276
|
rssiCallback = nil
|
|
218
277
|
readCallbacks.removeAll()
|
package/lib/commonjs/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { type ByteArray, type ScanFilter, type BLEDevice, type ScanCallback, type ManufacturerDataEntry, type ManufacturerData, type ConnectionCallback, type DisconnectEventCallback, type OperationCallback, type CharacteristicUpdateCallback, type Subscription, type AsyncSubscription, type BleNitroManagerOptions, BLEState, AndroidScanMode, BleNitroManager, } from "./manager";
|
|
1
|
+
export { type ByteArray, type ScanFilter, type BLEDevice, type ScanCallback, type ManufacturerDataEntry, type ManufacturerData, type ConnectionCallback, type DisconnectEventCallback, type OperationCallback, type CharacteristicUpdateCallback, type Subscription, type AsyncSubscription, type BleNitroManagerOptions, BLEState, AndroidScanMode, BleNitroManager, BleTimeoutError, } from "./manager";
|
|
2
2
|
export { BleNitro } from './singleton';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,4BAA4B,EACjC,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,QAAQ,EACR,eAAe,EACf,eAAe,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,4BAA4B,EACjC,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,QAAQ,EACR,eAAe,EACf,eAAe,EACf,eAAe,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BleNitro = exports.BleNitroManager = exports.AndroidScanMode = exports.BLEState = void 0;
|
|
3
|
+
exports.BleNitro = exports.BleTimeoutError = exports.BleNitroManager = exports.AndroidScanMode = exports.BLEState = void 0;
|
|
4
4
|
var manager_1 = require("./manager");
|
|
5
5
|
Object.defineProperty(exports, "BLEState", { enumerable: true, get: function () { return manager_1.BLEState; } });
|
|
6
6
|
Object.defineProperty(exports, "AndroidScanMode", { enumerable: true, get: function () { return manager_1.AndroidScanMode; } });
|
|
7
7
|
Object.defineProperty(exports, "BleNitroManager", { enumerable: true, get: function () { return manager_1.BleNitroManager; } });
|
|
8
|
+
Object.defineProperty(exports, "BleTimeoutError", { enumerable: true, get: function () { return manager_1.BleTimeoutError; } });
|
|
8
9
|
var singleton_1 = require("./singleton");
|
|
9
10
|
Object.defineProperty(exports, "BleNitro", { enumerable: true, get: function () { return singleton_1.BleNitro; } });
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;AAAA,qCAkBmB;AAJjB,mGAAA,QAAQ,OAAA;AACR,0GAAA,eAAe,OAAA;AACf,0GAAA,eAAe,OAAA;AACf,0GAAA,eAAe,OAAA;AAGjB,yCAAuC;AAA9B,qGAAA,QAAQ,OAAA"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { BLEDevice as NativeBLEDevice, BLEState as NativeBLEState, AndroidScanMode as NativeAndroidScanMode } from './specs/NativeBleNitro';
|
|
2
|
+
export declare class BleTimeoutError extends Error {
|
|
3
|
+
constructor(operation: string, ms: number);
|
|
4
|
+
}
|
|
2
5
|
export type ByteArray = number[];
|
|
3
6
|
export interface ScanFilter {
|
|
4
7
|
serviceUUIDs?: string[];
|
|
@@ -173,7 +176,10 @@ export declare class BleNitroManager {
|
|
|
173
176
|
*/
|
|
174
177
|
getCharacteristics(deviceId: string, serviceId: string): string[];
|
|
175
178
|
/**
|
|
176
|
-
* Get services and characteristics for a connected device
|
|
179
|
+
* Get services and characteristics for a connected device.
|
|
180
|
+
* Uses a native method that waits for both service and characteristic
|
|
181
|
+
* discovery to complete before reading, avoiding the CoreBluetooth race
|
|
182
|
+
* where didDiscoverServices may not re-fire for cached services.
|
|
177
183
|
* @param deviceId ID of the device
|
|
178
184
|
* @returns Promise resolving to array of service and characteristic UUIDs
|
|
179
185
|
* @see getServices
|
|
@@ -183,6 +189,8 @@ export declare class BleNitroManager {
|
|
|
183
189
|
uuid: string;
|
|
184
190
|
characteristics: string[];
|
|
185
191
|
}[]>;
|
|
192
|
+
private static readonly DISCOVERY_TIMEOUT_MS;
|
|
193
|
+
private _discoverServicesWithCharacteristics;
|
|
186
194
|
/**
|
|
187
195
|
* Read a characteristic value
|
|
188
196
|
* @param deviceId ID of the device
|
|
@@ -218,6 +226,14 @@ export declare class BleNitroManager {
|
|
|
218
226
|
* @returns Promise resolving when unsubscription is complete
|
|
219
227
|
*/
|
|
220
228
|
unsubscribeFromCharacteristic(deviceId: string, serviceId: string, characteristicId: string): Promise<void>;
|
|
229
|
+
/**
|
|
230
|
+
* Check if currently subscribed to a characteristic's notifications
|
|
231
|
+
* @param deviceId ID of the device
|
|
232
|
+
* @param serviceId ID of the service
|
|
233
|
+
* @param characteristicId ID of the characteristic
|
|
234
|
+
* @returns Boolean indicating if subscribed to notifications
|
|
235
|
+
*/
|
|
236
|
+
isSubscribedToCharacteristic(deviceId: string, serviceId: string, characteristicId: string): boolean;
|
|
221
237
|
/**
|
|
222
238
|
* Check if Bluetooth is enabled
|
|
223
239
|
* @returns returns Boolean according to Bluetooth state
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"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"}
|
package/lib/commonjs/manager.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.BleNitroManager = exports.AndroidScanMode = exports.BLEState = void 0;
|
|
6
|
+
exports.BleNitroManager = exports.AndroidScanMode = exports.BLEState = exports.BleTimeoutError = void 0;
|
|
7
7
|
exports.mapNativeBLEStateToBLEState = mapNativeBLEStateToBLEState;
|
|
8
8
|
exports.mapAndroidScanModeToNativeAndroidScanMode = mapAndroidScanModeToNativeAndroidScanMode;
|
|
9
9
|
exports.convertNativeBleDeviceToBleDevice = convertNativeBleDeviceToBleDevice;
|
|
@@ -11,6 +11,20 @@ exports.arrayBufferToByteArray = arrayBufferToByteArray;
|
|
|
11
11
|
exports.byteArrayToArrayBuffer = byteArrayToArrayBuffer;
|
|
12
12
|
const NativeBleNitroFactory_1 = __importDefault(require("./specs/NativeBleNitroFactory"));
|
|
13
13
|
const NativeBleNitro_1 = require("./specs/NativeBleNitro");
|
|
14
|
+
class BleTimeoutError extends Error {
|
|
15
|
+
constructor(operation, ms) {
|
|
16
|
+
super(`${operation} timed out after ${ms}ms`);
|
|
17
|
+
this.name = 'BleTimeoutError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.BleTimeoutError = BleTimeoutError;
|
|
21
|
+
function withTimeout(promise, ms, operation) {
|
|
22
|
+
let timer;
|
|
23
|
+
const timeout = new Promise((_, reject) => {
|
|
24
|
+
timer = setTimeout(() => reject(new BleTimeoutError(operation, ms)), ms);
|
|
25
|
+
});
|
|
26
|
+
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
27
|
+
}
|
|
14
28
|
var BLEState;
|
|
15
29
|
(function (BLEState) {
|
|
16
30
|
BLEState["Unknown"] = "Unknown";
|
|
@@ -372,21 +386,39 @@ class BleNitroManager {
|
|
|
372
386
|
return BleNitroManager.normalizeGattUUIDs(characteristics);
|
|
373
387
|
}
|
|
374
388
|
/**
|
|
375
|
-
* Get services and characteristics for a connected device
|
|
389
|
+
* Get services and characteristics for a connected device.
|
|
390
|
+
* Uses a native method that waits for both service and characteristic
|
|
391
|
+
* discovery to complete before reading, avoiding the CoreBluetooth race
|
|
392
|
+
* where didDiscoverServices may not re-fire for cached services.
|
|
376
393
|
* @param deviceId ID of the device
|
|
377
394
|
* @returns Promise resolving to array of service and characteristic UUIDs
|
|
378
395
|
* @see getServices
|
|
379
396
|
* @see getCharacteristics
|
|
380
397
|
*/
|
|
381
398
|
async getServicesWithCharacteristics(deviceId) {
|
|
382
|
-
await this.
|
|
383
|
-
const services =
|
|
384
|
-
return services.map((service) => {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
399
|
+
await this._discoverServicesWithCharacteristics(deviceId);
|
|
400
|
+
const services = this.Instance.getServices(deviceId);
|
|
401
|
+
return BleNitroManager.normalizeGattUUIDs(services).map((service) => ({
|
|
402
|
+
uuid: service,
|
|
403
|
+
characteristics: this.getCharacteristics(deviceId, service),
|
|
404
|
+
}));
|
|
405
|
+
}
|
|
406
|
+
_discoverServicesWithCharacteristics(deviceId) {
|
|
407
|
+
const inner = new Promise((resolve, reject) => {
|
|
408
|
+
if (!this.isConnected(deviceId)) {
|
|
409
|
+
reject(new Error('Device not connected'));
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
this.Instance.discoverServicesWithCharacteristics(deviceId, (success, error) => {
|
|
413
|
+
if (success) {
|
|
414
|
+
resolve();
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
reject(new Error(error));
|
|
418
|
+
}
|
|
419
|
+
});
|
|
389
420
|
});
|
|
421
|
+
return withTimeout(inner, BleNitroManager.DISCOVERY_TIMEOUT_MS, 'discoverServicesWithCharacteristics');
|
|
390
422
|
}
|
|
391
423
|
/**
|
|
392
424
|
* Read a characteristic value
|
|
@@ -495,6 +527,18 @@ class BleNitroManager {
|
|
|
495
527
|
});
|
|
496
528
|
});
|
|
497
529
|
}
|
|
530
|
+
/**
|
|
531
|
+
* Check if currently subscribed to a characteristic's notifications
|
|
532
|
+
* @param deviceId ID of the device
|
|
533
|
+
* @param serviceId ID of the service
|
|
534
|
+
* @param characteristicId ID of the characteristic
|
|
535
|
+
* @returns Boolean indicating if subscribed to notifications
|
|
536
|
+
*/
|
|
537
|
+
isSubscribedToCharacteristic(deviceId, serviceId, characteristicId) {
|
|
538
|
+
// No isConnected guard — both native implementations already return false
|
|
539
|
+
// for disconnected devices, and an extra check would introduce a TOCTOU race.
|
|
540
|
+
return this.Instance.isSubscribedToCharacteristic(deviceId, BleNitroManager.normalizeGattUUID(serviceId), BleNitroManager.normalizeGattUUID(characteristicId));
|
|
541
|
+
}
|
|
498
542
|
/**
|
|
499
543
|
* Check if Bluetooth is enabled
|
|
500
544
|
* @returns returns Boolean according to Bluetooth state
|
|
@@ -556,4 +600,5 @@ class BleNitroManager {
|
|
|
556
600
|
}
|
|
557
601
|
}
|
|
558
602
|
exports.BleNitroManager = BleNitroManager;
|
|
603
|
+
BleNitroManager.DISCOVERY_TIMEOUT_MS = 30000;
|
|
559
604
|
//# sourceMappingURL=manager.js.map
|