react-native-ble-manager 10.1.4 → 11.0.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 +4 -2
- package/android/src/main/java/it/innove/BleManager.java +5 -5
- package/android/src/main/java/it/innove/DefaultPeripheral.java +10 -5
- package/android/src/main/java/it/innove/DefaultScanManager.java +8 -5
- package/android/src/main/java/it/innove/Peripheral.java +21 -17
- package/dist/cjs/index.js +8 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types.d.ts +3 -2
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/index.js +8 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types.d.ts +3 -2
- package/dist/esm/types.js.map +1 -1
- package/ios/BleManager-Bridging-Header.h +1 -0
- package/ios/BleManager.h +0 -28
- package/ios/BleManager.m +126 -1186
- package/ios/BleManager.swift +1185 -0
- package/ios/BleManager.xcodeproj/project.pbxproj +21 -18
- package/ios/BleManager.xcodeproj/project.xcworkspace/xcuserdata/marco.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/Helper.swift +364 -0
- package/package.json +1 -1
- package/react-native-ble-manager.podspec +2 -2
- package/src/index.ts +7 -3
- package/src/types.ts +3 -2
- package/ios/BLECommandContext.h +0 -10
- package/ios/BLECommandContext.m +0 -5
- package/ios/CBPeripheral+Extensions.h +0 -16
- package/ios/CBPeripheral+Extensions.m +0 -309
- package/ios/NSData+Conversion.h +0 -11
- package/ios/NSData+Conversion.m +0 -41
|
@@ -0,0 +1,1185 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreBluetooth
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@objc(BleManager)
|
|
6
|
+
class BleManager: RCTEventEmitter, CBCentralManagerDelegate, CBPeripheralDelegate {
|
|
7
|
+
|
|
8
|
+
static var shared:BleManager?
|
|
9
|
+
static var sharedManager:CBCentralManager?
|
|
10
|
+
|
|
11
|
+
private var hasListeners:Bool = false
|
|
12
|
+
|
|
13
|
+
private var manager: CBCentralManager?
|
|
14
|
+
private var scanTimer: Timer?
|
|
15
|
+
|
|
16
|
+
private var peripherals: Dictionary<String, Peripheral>
|
|
17
|
+
private var connectCallbacks: Dictionary<String, [RCTResponseSenderBlock]>
|
|
18
|
+
private var readCallbacks: Dictionary<String, [RCTResponseSenderBlock]>
|
|
19
|
+
private var readRSSICallbacks: Dictionary<String, [RCTResponseSenderBlock]>
|
|
20
|
+
private var readDescriptorCallbacks: Dictionary<String, [RCTResponseSenderBlock]>
|
|
21
|
+
private var retrieveServicesCallbacks: Dictionary<String, [RCTResponseSenderBlock]>
|
|
22
|
+
private var writeCallbacks: Dictionary<String, [RCTResponseSenderBlock]>
|
|
23
|
+
private var writeQueue: Array<Any>
|
|
24
|
+
private var notificationCallbacks: Dictionary<String, [RCTResponseSenderBlock]>
|
|
25
|
+
private var stopNotificationCallbacks: Dictionary<String, [RCTResponseSenderBlock]>
|
|
26
|
+
|
|
27
|
+
private var connectedPeripherals: Set<String>
|
|
28
|
+
|
|
29
|
+
private var retrieveServicesLatches: Dictionary<String, Set<CBService>>
|
|
30
|
+
private var characteristicsLatches: Dictionary<String, Set<CBCharacteristic>>
|
|
31
|
+
|
|
32
|
+
private let serialQueue = DispatchQueue(label: "BleManager.serialQueue")
|
|
33
|
+
|
|
34
|
+
private var exactAdvertisingName: [String]
|
|
35
|
+
|
|
36
|
+
static var verboseLogging = false
|
|
37
|
+
|
|
38
|
+
private override init() {
|
|
39
|
+
peripherals = [:]
|
|
40
|
+
connectCallbacks = [:]
|
|
41
|
+
readCallbacks = [:]
|
|
42
|
+
readRSSICallbacks = [:]
|
|
43
|
+
readDescriptorCallbacks = [:]
|
|
44
|
+
retrieveServicesCallbacks = [:]
|
|
45
|
+
writeCallbacks = [:]
|
|
46
|
+
writeQueue = []
|
|
47
|
+
notificationCallbacks = [:]
|
|
48
|
+
stopNotificationCallbacks = [:]
|
|
49
|
+
retrieveServicesLatches = [:]
|
|
50
|
+
characteristicsLatches = [:]
|
|
51
|
+
exactAdvertisingName = []
|
|
52
|
+
connectedPeripherals = []
|
|
53
|
+
|
|
54
|
+
super.init()
|
|
55
|
+
|
|
56
|
+
NSLog("BleManager created");
|
|
57
|
+
|
|
58
|
+
BleManager.shared = self
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
NotificationCenter.default.addObserver(self, selector: #selector(bridgeReloading), name: NSNotification.Name(rawValue: "RCTBridgeWillReloadNotification"), object: nil)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@objc override static func requiresMainQueueSetup() -> Bool { return true }
|
|
65
|
+
|
|
66
|
+
@objc override func supportedEvents() -> [String]! {
|
|
67
|
+
return ["BleManagerDidUpdateValueForCharacteristic", "BleManagerStopScan", "BleManagerDiscoverPeripheral", "BleManagerConnectPeripheral", "BleManagerDisconnectPeripheral", "BleManagerDidUpdateState", "BleManagerCentralManagerWillRestoreState", "BleManagerDidUpdateNotificationStateFor"]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@objc override func startObserving() {
|
|
71
|
+
hasListeners = true
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@objc override func stopObserving() {
|
|
75
|
+
hasListeners = false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@objc func bridgeReloading() {
|
|
79
|
+
if let manager = manager {
|
|
80
|
+
if let scanTimer = self.scanTimer {
|
|
81
|
+
scanTimer.invalidate()
|
|
82
|
+
self.scanTimer = nil
|
|
83
|
+
manager.stopScan()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
manager.delegate = nil
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
serialQueue.sync {
|
|
90
|
+
for p in peripherals.values {
|
|
91
|
+
p.instance.delegate = nil
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
peripherals = [:]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Helper method to find a peripheral by UUID
|
|
99
|
+
func findPeripheral(byUUID uuid: String) -> Peripheral? {
|
|
100
|
+
var foundPeripheral: Peripheral? = nil
|
|
101
|
+
|
|
102
|
+
serialQueue.sync {
|
|
103
|
+
if let peripheral = peripherals[uuid] {
|
|
104
|
+
foundPeripheral = peripheral;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return foundPeripheral
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Helper method to insert callback in different queues
|
|
112
|
+
func insertCallback(_ callback: @escaping RCTResponseSenderBlock, intoDictionary dictionary: inout Dictionary<String, [RCTResponseSenderBlock]>, withKey key: String) {
|
|
113
|
+
serialQueue.sync {
|
|
114
|
+
if var peripheralCallbacks = dictionary[key] {
|
|
115
|
+
peripheralCallbacks.append(callback)
|
|
116
|
+
} else {
|
|
117
|
+
var peripheralCallbacks = [RCTResponseSenderBlock]()
|
|
118
|
+
peripheralCallbacks.append(callback)
|
|
119
|
+
dictionary[key] = peripheralCallbacks
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Helper method to call the callbacks for a specific peripheral and clear the queue
|
|
126
|
+
func invokeAndClearDictionary(_ dictionary: inout Dictionary<String, [RCTResponseSenderBlock]>, withKey key: String, usingParameters parameters: [Any]) {
|
|
127
|
+
serialQueue.sync {
|
|
128
|
+
|
|
129
|
+
if let peripheralCallbacks = dictionary[key] {
|
|
130
|
+
for callback in peripheralCallbacks {
|
|
131
|
+
callback(parameters)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
dictionary.removeValue(forKey: key)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@objc func getContext(_ peripheralUUIDString: String, serviceUUIDString: String, characteristicUUIDString: String, prop: CBCharacteristicProperties, callback: @escaping RCTResponseSenderBlock) -> BLECommandContext? {
|
|
140
|
+
let serviceUUID = CBUUID(string: serviceUUIDString)
|
|
141
|
+
let characteristicUUID = CBUUID(string: characteristicUUIDString)
|
|
142
|
+
|
|
143
|
+
guard let peripheral = peripherals[peripheralUUIDString] else {
|
|
144
|
+
let error = String(format: "Could not find peripheral with UUID %@", peripheralUUIDString)
|
|
145
|
+
NSLog(error)
|
|
146
|
+
callback([error])
|
|
147
|
+
return nil
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
guard let service = Helper.findService(fromUUID: serviceUUID, peripheral: peripheral.instance) else {
|
|
151
|
+
let error = String(format: "Could not find service with UUID %@ on peripheral with UUID %@",
|
|
152
|
+
serviceUUIDString,
|
|
153
|
+
peripheral.instance.uuidAsString())
|
|
154
|
+
NSLog(error)
|
|
155
|
+
callback([error])
|
|
156
|
+
return nil
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
var characteristic = Helper.findCharacteristic(fromUUID: characteristicUUID, service: service, prop: prop)
|
|
160
|
+
|
|
161
|
+
// Special handling for INDICATE. If characteristic with notify is not found, check for indicate.
|
|
162
|
+
if prop == CBCharacteristicProperties.notify && characteristic == nil {
|
|
163
|
+
characteristic = Helper.findCharacteristic(fromUUID: characteristicUUID, service: service, prop: CBCharacteristicProperties.indicate)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// As a last resort, try to find ANY characteristic with this UUID, even if it doesn't have the correct properties
|
|
167
|
+
if characteristic == nil {
|
|
168
|
+
characteristic = Helper.findCharacteristic(fromUUID: characteristicUUID, service: service)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
guard let finalCharacteristic = characteristic else {
|
|
172
|
+
let error = String(format: "Could not find characteristic with UUID %@ on service with UUID %@ on peripheral with UUID %@",
|
|
173
|
+
characteristicUUIDString,
|
|
174
|
+
serviceUUIDString,
|
|
175
|
+
peripheral.instance.uuidAsString())
|
|
176
|
+
NSLog(error)
|
|
177
|
+
callback([error])
|
|
178
|
+
return nil
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let context = BLECommandContext()
|
|
182
|
+
context.peripheral = peripheral
|
|
183
|
+
context.service = service
|
|
184
|
+
context.characteristic = finalCharacteristic
|
|
185
|
+
return context
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@objc public func start(_ options: NSDictionary,
|
|
190
|
+
callback: RCTResponseSenderBlock) {
|
|
191
|
+
if BleManager.verboseLogging {
|
|
192
|
+
NSLog("BleManager initialized")
|
|
193
|
+
}
|
|
194
|
+
var initOptions = [String: Any]()
|
|
195
|
+
|
|
196
|
+
if let showAlert = options["showAlert"] as? Bool {
|
|
197
|
+
initOptions[CBCentralManagerOptionShowPowerAlertKey] = showAlert
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
var queue: DispatchQueue
|
|
201
|
+
if let queueIdentifierKey = options["queueIdentifierKey"] as? String {
|
|
202
|
+
queue = DispatchQueue(label: queueIdentifierKey, qos: DispatchQoS.background)
|
|
203
|
+
} else {
|
|
204
|
+
queue = DispatchQueue.main
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if let restoreIdentifierKey = options["restoreIdentifierKey"] as? String {
|
|
208
|
+
initOptions[CBCentralManagerOptionRestoreIdentifierKey] = restoreIdentifierKey
|
|
209
|
+
|
|
210
|
+
if let sharedManager = BleManager.sharedManager {
|
|
211
|
+
manager = sharedManager
|
|
212
|
+
manager?.delegate = self
|
|
213
|
+
} else {
|
|
214
|
+
manager = CBCentralManager(delegate: self, queue: queue, options: initOptions)
|
|
215
|
+
BleManager.sharedManager = manager
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
manager = CBCentralManager(delegate: self, queue: queue, options: initOptions)
|
|
219
|
+
BleManager.sharedManager = manager
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
callback([])
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@objc public func scan(_ serviceUUIDStrings: [Any],
|
|
226
|
+
timeoutSeconds: NSNumber,
|
|
227
|
+
allowDuplicates: Bool,
|
|
228
|
+
scanningOptions: NSDictionary,
|
|
229
|
+
callback:RCTResponseSenderBlock) {
|
|
230
|
+
if Int(truncating: timeoutSeconds) > 0 {
|
|
231
|
+
NSLog("scan with timeout \(timeoutSeconds)")
|
|
232
|
+
} else {
|
|
233
|
+
NSLog("scan")
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Clear the peripherals before scanning again, otherwise cannot connect again after disconnection
|
|
237
|
+
// Only clear peripherals that are not connected - otherwise connections fail silently (without any
|
|
238
|
+
// onDisconnect* callback).
|
|
239
|
+
serialQueue.sync {
|
|
240
|
+
let disconnectedPeripherals = peripherals.filter({ $0.value.instance.state != .connected && $0.value.instance.state != .connecting })
|
|
241
|
+
disconnectedPeripherals.forEach { (uuid, peripheral) in
|
|
242
|
+
peripheral.instance.delegate = nil
|
|
243
|
+
peripherals.removeValue(forKey: uuid)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
var serviceUUIDs = [CBUUID]()
|
|
248
|
+
if let serviceUUIDStrings = serviceUUIDStrings as? [String] {
|
|
249
|
+
serviceUUIDs = serviceUUIDStrings.map { CBUUID(string: $0) }
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
var options: [String: Any]?
|
|
253
|
+
if allowDuplicates {
|
|
254
|
+
options = [CBCentralManagerScanOptionAllowDuplicatesKey: true]
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
exactAdvertisingName.removeAll()
|
|
258
|
+
if let names = scanningOptions["exactAdvertisingName"] as? [String] {
|
|
259
|
+
exactAdvertisingName.append(contentsOf: names)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
manager?.scanForPeripherals(withServices: serviceUUIDs, options: options)
|
|
263
|
+
|
|
264
|
+
if timeoutSeconds.doubleValue > 0 {
|
|
265
|
+
if let scanTimer = scanTimer {
|
|
266
|
+
scanTimer.invalidate()
|
|
267
|
+
self.scanTimer = nil
|
|
268
|
+
}
|
|
269
|
+
DispatchQueue.main.async {
|
|
270
|
+
self.scanTimer = Timer.scheduledTimer(timeInterval: timeoutSeconds.doubleValue, target: self, selector: #selector(self.stopTimer), userInfo: nil, repeats: false)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
callback([])
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
@objc func stopTimer() {
|
|
278
|
+
NSLog("Stop scan");
|
|
279
|
+
scanTimer = nil;
|
|
280
|
+
manager?.stopScan()
|
|
281
|
+
if hasListeners {
|
|
282
|
+
sendEvent(withName: "BleManagerStopScan", body: ["status": 10])
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@objc public func stopScan(_ callback: @escaping RCTResponseSenderBlock) {
|
|
288
|
+
if let scanTimer = self.scanTimer {
|
|
289
|
+
scanTimer.invalidate()
|
|
290
|
+
self.scanTimer = nil
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
manager?.stopScan()
|
|
294
|
+
|
|
295
|
+
if hasListeners {
|
|
296
|
+
sendEvent(withName: "BleManagerStopScan", body: ["status": 0])
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
callback([])
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
@objc func connect(_ peripheralUUID: String,
|
|
304
|
+
options: NSDictionary,
|
|
305
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
306
|
+
|
|
307
|
+
if let peripheral = peripherals[peripheralUUID] {
|
|
308
|
+
// Found the peripheral, connect to it
|
|
309
|
+
NSLog("Connecting to peripheral with UUID: \(peripheralUUID)")
|
|
310
|
+
|
|
311
|
+
insertCallback(callback, intoDictionary: &connectCallbacks, withKey: peripheral.instance.uuidAsString())
|
|
312
|
+
manager?.connect(peripheral.instance)
|
|
313
|
+
} else {
|
|
314
|
+
// Try to retrieve the peripheral
|
|
315
|
+
NSLog("Retrieving peripheral with UUID: \(peripheralUUID)")
|
|
316
|
+
|
|
317
|
+
if let uuid = UUID(uuidString: peripheralUUID) {
|
|
318
|
+
let peripheralArray = manager?.retrievePeripherals(withIdentifiers: [uuid])
|
|
319
|
+
if let retrievedPeripheral = peripheralArray?.first {
|
|
320
|
+
objc_sync_enter(peripherals)
|
|
321
|
+
peripherals[retrievedPeripheral.uuidAsString()] = Peripheral(peripheral:retrievedPeripheral)
|
|
322
|
+
objc_sync_exit(peripherals)
|
|
323
|
+
NSLog("Successfully retrieved and connecting to peripheral with UUID: \(peripheralUUID)")
|
|
324
|
+
|
|
325
|
+
// Connect to the retrieved peripheral
|
|
326
|
+
insertCallback(callback, intoDictionary: &connectCallbacks, withKey: retrievedPeripheral.uuidAsString())
|
|
327
|
+
manager?.connect(retrievedPeripheral, options: nil)
|
|
328
|
+
} else {
|
|
329
|
+
let error = "Could not find peripheral \(peripheralUUID)."
|
|
330
|
+
NSLog(error)
|
|
331
|
+
callback([error, NSNull()])
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
let error = "Wrong UUID format \(peripheralUUID)"
|
|
335
|
+
callback([error, NSNull()])
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
@objc func disconnect(_ peripheralUUID: String,
|
|
341
|
+
force: Bool,
|
|
342
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
343
|
+
if let peripheral = peripherals[peripheralUUID] {
|
|
344
|
+
NSLog("Disconnecting from peripheral with UUID: \(peripheralUUID)")
|
|
345
|
+
|
|
346
|
+
if let services = peripheral.instance.services {
|
|
347
|
+
for service in services {
|
|
348
|
+
if let characteristics = service.characteristics {
|
|
349
|
+
for characteristic in characteristics {
|
|
350
|
+
if characteristic.isNotifying {
|
|
351
|
+
NSLog("Remove notification from: \(characteristic.uuid)")
|
|
352
|
+
peripheral.instance.setNotifyValue(false, for: characteristic)
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
manager?.cancelPeripheralConnection(peripheral.instance)
|
|
360
|
+
callback([])
|
|
361
|
+
|
|
362
|
+
} else {
|
|
363
|
+
let error = "Could not find peripheral \(peripheralUUID)."
|
|
364
|
+
NSLog(error)
|
|
365
|
+
callback([error])
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
@objc func retrieveServices(_ peripheralUUID: String,
|
|
370
|
+
services: [String],
|
|
371
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
372
|
+
NSLog("retrieveServices \(services)")
|
|
373
|
+
|
|
374
|
+
if let peripheral = peripherals[peripheralUUID], peripheral.instance.state == .connected {
|
|
375
|
+
insertCallback(callback, intoDictionary: &retrieveServicesCallbacks, withKey: peripheral.instance.uuidAsString())
|
|
376
|
+
|
|
377
|
+
var uuids: [CBUUID] = []
|
|
378
|
+
for string in services {
|
|
379
|
+
let uuid = CBUUID(string: string)
|
|
380
|
+
uuids.append(uuid)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if !uuids.isEmpty {
|
|
384
|
+
peripheral.instance.discoverServices(uuids)
|
|
385
|
+
} else {
|
|
386
|
+
peripheral.instance.discoverServices(nil)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
} else {
|
|
390
|
+
callback(["Peripheral not found or not connected"])
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
@objc func readRSSI(_ peripheralUUID: String,
|
|
395
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
396
|
+
NSLog("readRSSI")
|
|
397
|
+
|
|
398
|
+
if let peripheral = peripherals[peripheralUUID], peripheral.instance.state == .connected {
|
|
399
|
+
insertCallback(callback, intoDictionary: &readRSSICallbacks, withKey: peripheral.instance.uuidAsString())
|
|
400
|
+
peripheral.instance.readRSSI()
|
|
401
|
+
} else {
|
|
402
|
+
callback(["Peripheral not found or not connected"])
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
@objc func readDescriptor(_ peripheralUUID: String,
|
|
407
|
+
serviceUUID: String,
|
|
408
|
+
characteristicUUID: String,
|
|
409
|
+
descriptorUUID: String,
|
|
410
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
411
|
+
NSLog("readDescriptor")
|
|
412
|
+
|
|
413
|
+
guard let context = getContext(peripheralUUID, serviceUUIDString: serviceUUID, characteristicUUIDString: characteristicUUID, prop: CBCharacteristicProperties.read, callback: callback) else {
|
|
414
|
+
return
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
let peripheral = context.peripheral
|
|
418
|
+
let characteristic = context.characteristic
|
|
419
|
+
|
|
420
|
+
guard let descriptor = Helper.findDescriptor(fromUUID: CBUUID(string: descriptorUUID), characteristic: characteristic!) else {
|
|
421
|
+
let error = "Could not find descriptor with UUID \(descriptorUUID) on characteristic with UUID \(String(describing: characteristic?.uuid.uuidString)) on peripheral with UUID \(peripheralUUID)"
|
|
422
|
+
NSLog(error)
|
|
423
|
+
callback([error])
|
|
424
|
+
return
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if let peripheral = peripheral?.instance {
|
|
428
|
+
let key = Helper.key(forPeripheral: peripheral, andCharacteristic: characteristic!, andDescriptor: descriptor)
|
|
429
|
+
insertCallback(callback, intoDictionary: &readDescriptorCallbacks, withKey: key)
|
|
430
|
+
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
peripheral?.instance.readValue(for: descriptor)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
@objc func getDiscoveredPeripherals(_ callback: @escaping RCTResponseSenderBlock) {
|
|
437
|
+
NSLog("Get discovered peripherals")
|
|
438
|
+
var discoveredPeripherals: [[String: Any]] = []
|
|
439
|
+
|
|
440
|
+
serialQueue.sync {
|
|
441
|
+
for (_, peripheral) in peripherals {
|
|
442
|
+
discoveredPeripherals.append(peripheral.advertisingInfo())
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
callback([NSNull(), discoveredPeripherals])
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
@objc func getConnectedPeripherals(_ serviceUUIDStrings: [String],
|
|
450
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
451
|
+
NSLog("Get connected peripherals")
|
|
452
|
+
var serviceUUIDs: [CBUUID] = []
|
|
453
|
+
|
|
454
|
+
for uuidString in serviceUUIDStrings {
|
|
455
|
+
serviceUUIDs.append(CBUUID(string: uuidString))
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
var connectedPeripherals: [Peripheral] = []
|
|
459
|
+
|
|
460
|
+
if serviceUUIDs.isEmpty {
|
|
461
|
+
serialQueue.sync {
|
|
462
|
+
connectedPeripherals = peripherals.filter({ $0.value.instance.state == .connected }).map({ p in
|
|
463
|
+
p.value
|
|
464
|
+
})
|
|
465
|
+
}
|
|
466
|
+
} else {
|
|
467
|
+
let connectedCBPeripherals: [CBPeripheral] = manager?.retrieveConnectedPeripherals(withServices: serviceUUIDs) ?? []
|
|
468
|
+
|
|
469
|
+
serialQueue.sync {
|
|
470
|
+
for ph in connectedCBPeripherals {
|
|
471
|
+
if let peripheral = peripherals[ph.uuidAsString()] {
|
|
472
|
+
connectedPeripherals.append(peripheral)
|
|
473
|
+
} else {
|
|
474
|
+
peripherals[ph.uuidAsString()] = Peripheral(peripheral: ph)
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
var foundedPeripherals: [[String: Any]] = []
|
|
481
|
+
|
|
482
|
+
for peripheral in connectedPeripherals {
|
|
483
|
+
foundedPeripherals.append(peripheral.advertisingInfo())
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
callback([NSNull(), foundedPeripherals])
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
@objc func isPeripheralConnected(_ peripheralUUID: String,
|
|
490
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
491
|
+
|
|
492
|
+
if let peripheral = peripherals[peripheralUUID] {
|
|
493
|
+
callback([NSNull(), peripheral.instance.state == .connected])
|
|
494
|
+
} else {
|
|
495
|
+
callback(["Peripheral not found"])
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
@objc func checkState(_ callback: @escaping RCTResponseSenderBlock) {
|
|
500
|
+
if let manager = manager {
|
|
501
|
+
centralManagerDidUpdateState(manager)
|
|
502
|
+
|
|
503
|
+
let stateName = Helper.centralManagerStateToString(manager.state)
|
|
504
|
+
callback([stateName])
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
@objc func write(_ peripheralUUID: String,
|
|
509
|
+
serviceUUID: String,
|
|
510
|
+
characteristicUUID: String,
|
|
511
|
+
message: [UInt8],
|
|
512
|
+
maxByteSize: Int,
|
|
513
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
514
|
+
NSLog("write")
|
|
515
|
+
|
|
516
|
+
// TODO check if the queue is working
|
|
517
|
+
|
|
518
|
+
guard let context = getContext(peripheralUUID, serviceUUIDString: serviceUUID, characteristicUUIDString: characteristicUUID, prop: CBCharacteristicProperties.write, callback: callback) else {
|
|
519
|
+
return
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
let dataMessage = Data(message)
|
|
523
|
+
|
|
524
|
+
if let peripheral = context.peripheral, let characteristic = context.characteristic {
|
|
525
|
+
let key = Helper.key(forPeripheral:peripheral.instance, andCharacteristic: characteristic)
|
|
526
|
+
insertCallback(callback, intoDictionary: &writeCallbacks, withKey: key)
|
|
527
|
+
|
|
528
|
+
if BleManager.verboseLogging {
|
|
529
|
+
NSLog("Message to write(\(dataMessage.count)): \(dataMessage.hexadecimalString())")
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if dataMessage.count > maxByteSize {
|
|
533
|
+
var count = 0
|
|
534
|
+
var offset = 0
|
|
535
|
+
while count < dataMessage.count, (dataMessage.count - count) > maxByteSize {
|
|
536
|
+
let splitMessage = dataMessage.subdata(in: offset..<offset + maxByteSize)
|
|
537
|
+
writeQueue.append(splitMessage)
|
|
538
|
+
count += maxByteSize
|
|
539
|
+
offset += maxByteSize
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if count < dataMessage.count {
|
|
543
|
+
let splitMessage = dataMessage.subdata(in: offset..<dataMessage.count)
|
|
544
|
+
writeQueue.append(splitMessage)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if BleManager.verboseLogging {
|
|
548
|
+
NSLog("Queued splitted message: \(writeQueue.count)")
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if case let firstMessage as Data = writeQueue.removeFirst() {
|
|
552
|
+
peripheral.instance.writeValue(firstMessage, for: characteristic, type: .withResponse)
|
|
553
|
+
}
|
|
554
|
+
} else {
|
|
555
|
+
peripheral.instance.writeValue(dataMessage, for: characteristic, type: .withResponse)
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
@objc func writeWithoutResponse(_ peripheralUUID: String,
|
|
561
|
+
serviceUUID: String,
|
|
562
|
+
characteristicUUID: String,
|
|
563
|
+
message: [UInt8],
|
|
564
|
+
maxByteSize: Int,
|
|
565
|
+
queueSleepTime: Int,
|
|
566
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
567
|
+
NSLog("writeWithoutResponse")
|
|
568
|
+
|
|
569
|
+
guard let context = getContext(peripheralUUID, serviceUUIDString: serviceUUID, characteristicUUIDString: characteristicUUID, prop: CBCharacteristicProperties.writeWithoutResponse, callback: callback) else {
|
|
570
|
+
return
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
let dataMessage = Data(message)
|
|
574
|
+
|
|
575
|
+
if BleManager.verboseLogging {
|
|
576
|
+
NSLog("Message to write(\(dataMessage.count)): \(dataMessage.hexadecimalString())")
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if dataMessage.count > maxByteSize {
|
|
580
|
+
var offset = 0
|
|
581
|
+
let peripheral = context.peripheral
|
|
582
|
+
guard let characteristic = context.characteristic else { return }
|
|
583
|
+
|
|
584
|
+
repeat {
|
|
585
|
+
let thisChunkSize = min(maxByteSize, dataMessage.count - offset)
|
|
586
|
+
let chunk = dataMessage.subdata(in: offset..<offset + thisChunkSize)
|
|
587
|
+
|
|
588
|
+
offset += thisChunkSize
|
|
589
|
+
peripheral?.instance.writeValue(chunk, for: characteristic, type: .withoutResponse)
|
|
590
|
+
|
|
591
|
+
let sleepTimeSeconds = TimeInterval(queueSleepTime) / 1000
|
|
592
|
+
Thread.sleep(forTimeInterval: sleepTimeSeconds)
|
|
593
|
+
} while offset < dataMessage.count
|
|
594
|
+
|
|
595
|
+
callback([])
|
|
596
|
+
} else {
|
|
597
|
+
let peripheral = context.peripheral
|
|
598
|
+
guard let characteristic = context.characteristic else { return }
|
|
599
|
+
|
|
600
|
+
peripheral?.instance.writeValue(dataMessage, for: characteristic, type: .withoutResponse)
|
|
601
|
+
callback([])
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
@objc func read(_ peripheralUUID: String,
|
|
606
|
+
serviceUUID: String,
|
|
607
|
+
characteristicUUID: String,
|
|
608
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
609
|
+
NSLog("read")
|
|
610
|
+
|
|
611
|
+
guard let context = getContext(peripheralUUID, serviceUUIDString: serviceUUID, characteristicUUIDString: characteristicUUID, prop: CBCharacteristicProperties.read, callback: callback) else {
|
|
612
|
+
return
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
let peripheral = context.peripheral
|
|
616
|
+
let characteristic = context.characteristic
|
|
617
|
+
|
|
618
|
+
let key = Helper.key(forPeripheral:peripheral!.instance as CBPeripheral, andCharacteristic: characteristic!)
|
|
619
|
+
insertCallback(callback, intoDictionary: &readCallbacks, withKey: key)
|
|
620
|
+
|
|
621
|
+
peripheral?.instance.readValue(for: characteristic!) // callback sends value
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
@objc func startNotification(_ peripheralUUID: String,
|
|
625
|
+
serviceUUID: String,
|
|
626
|
+
characteristicUUID: String,
|
|
627
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
628
|
+
NSLog("startNotification")
|
|
629
|
+
|
|
630
|
+
guard let context = getContext(peripheralUUID, serviceUUIDString: serviceUUID, characteristicUUIDString: characteristicUUID, prop: CBCharacteristicProperties.notify, callback: callback) else {
|
|
631
|
+
return
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
guard let peripheral = context.peripheral else { return }
|
|
635
|
+
guard let characteristic = context.characteristic else { return }
|
|
636
|
+
|
|
637
|
+
let key = Helper.key(forPeripheral: (peripheral.instance as CBPeripheral?)!, andCharacteristic: characteristic)
|
|
638
|
+
insertCallback(callback, intoDictionary: ¬ificationCallbacks, withKey: key)
|
|
639
|
+
|
|
640
|
+
peripheral.instance.setNotifyValue(true, for: characteristic)
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
@objc func stopNotification(_ peripheralUUID: String,
|
|
644
|
+
serviceUUID: String,
|
|
645
|
+
characteristicUUID: String,
|
|
646
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
647
|
+
NSLog("stopNotification")
|
|
648
|
+
|
|
649
|
+
guard let context = getContext(peripheralUUID, serviceUUIDString: serviceUUID, characteristicUUIDString: characteristicUUID, prop: CBCharacteristicProperties.notify, callback: callback) else {
|
|
650
|
+
return
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
let peripheral = context.peripheral
|
|
654
|
+
guard let characteristic = context.characteristic else { return }
|
|
655
|
+
|
|
656
|
+
if characteristic.isNotifying {
|
|
657
|
+
let key = Helper.key(forPeripheral: (peripheral?.instance as CBPeripheral?)!, andCharacteristic: characteristic)
|
|
658
|
+
insertCallback(callback, intoDictionary: &stopNotificationCallbacks, withKey: key)
|
|
659
|
+
peripheral?.instance.setNotifyValue(false, for: characteristic)
|
|
660
|
+
NSLog("Characteristic stopped notifying")
|
|
661
|
+
} else {
|
|
662
|
+
NSLog("Characteristic is not notifying")
|
|
663
|
+
callback([])
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
@objc func getMaximumWriteValueLengthForWithoutResponse(_ peripheralUUID: String,
|
|
668
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
669
|
+
NSLog("getMaximumWriteValueLengthForWithoutResponse")
|
|
670
|
+
|
|
671
|
+
guard let peripheral = peripherals[peripheralUUID] else {
|
|
672
|
+
callback(["Peripheral not found or not connected"])
|
|
673
|
+
return
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
if peripheral.instance.state == .connected {
|
|
677
|
+
let max = NSNumber(value: peripheral.instance.maximumWriteValueLength(for: .withoutResponse))
|
|
678
|
+
callback([NSNull(), max])
|
|
679
|
+
} else {
|
|
680
|
+
callback(["Peripheral not found or not connected"])
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
@objc func getMaximumWriteValueLengthForWithResponse(_ peripheralUUID: String,
|
|
685
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
686
|
+
NSLog("getMaximumWriteValueLengthForWithResponse")
|
|
687
|
+
|
|
688
|
+
guard let peripheral = peripherals[peripheralUUID] else {
|
|
689
|
+
callback(["Peripheral not found or not connected"])
|
|
690
|
+
return
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if peripheral.instance.state == .connected {
|
|
694
|
+
let max = NSNumber(value: peripheral.instance.maximumWriteValueLength(for: .withResponse))
|
|
695
|
+
callback([NSNull(), max])
|
|
696
|
+
} else {
|
|
697
|
+
callback(["Peripheral not found or not connected"])
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
|
|
702
|
+
if let restoredPeripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral], restoredPeripherals.count > 0 {
|
|
703
|
+
serialQueue.sync {
|
|
704
|
+
var data = [[String: Any]]()
|
|
705
|
+
for peripheral in restoredPeripherals {
|
|
706
|
+
let p = Peripheral(peripheral:peripheral)
|
|
707
|
+
peripherals[peripheral.uuidAsString()] = p
|
|
708
|
+
data.append(p.advertisingInfo())
|
|
709
|
+
peripheral.delegate = self
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
NotificationCenter.default.post(name: Notification.Name("BleManagerCentralManagerWillRestoreState"), object: nil, userInfo: ["peripherals": data])
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
func centralManager(_ central: CBCentralManager,
|
|
719
|
+
didConnect peripheral: CBPeripheral) {
|
|
720
|
+
NSLog("Peripheral Connected: \(peripheral.uuidAsString() ?? "")")
|
|
721
|
+
peripheral.delegate = self
|
|
722
|
+
|
|
723
|
+
/*
|
|
724
|
+
The state of the peripheral isn't necessarily updated until a small
|
|
725
|
+
delay after didConnectPeripheral is called and in the meantime
|
|
726
|
+
didFailToConnectPeripheral may be called
|
|
727
|
+
*/
|
|
728
|
+
DispatchQueue.main.async {
|
|
729
|
+
Timer.scheduledTimer(withTimeInterval: 0.002, repeats: false) { timer in
|
|
730
|
+
// didFailToConnectPeripheral should have been called already if not connected by now
|
|
731
|
+
self.invokeAndClearDictionary(&self.connectCallbacks, withKey: peripheral.uuidAsString(), usingParameters: [NSNull()])
|
|
732
|
+
|
|
733
|
+
if self.hasListeners {
|
|
734
|
+
self.connectedPeripherals.insert(peripheral.uuidAsString())
|
|
735
|
+
self.sendEvent(withName: "BleManagerConnectPeripheral", body: ["peripheral": peripheral.uuidAsString()])
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
func centralManager(_ central: CBCentralManager,
|
|
742
|
+
didFailToConnect peripheral: CBPeripheral,
|
|
743
|
+
error: Error?) {
|
|
744
|
+
let errorStr = "Peripheral connection failure: \(peripheral.uuidAsString() ?? "") (\(error?.localizedDescription ?? "")"
|
|
745
|
+
NSLog(errorStr)
|
|
746
|
+
|
|
747
|
+
invokeAndClearDictionary(&connectCallbacks, withKey: peripheral.uuidAsString(), usingParameters: [errorStr])
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
func centralManager(_ central: CBCentralManager,
|
|
751
|
+
didDisconnectPeripheral peripheral:
|
|
752
|
+
CBPeripheral, error: Error?) {
|
|
753
|
+
let peripheralUUIDString:String = peripheral.uuidAsString()
|
|
754
|
+
NSLog("Peripheral Disconnected: \(peripheralUUIDString)")
|
|
755
|
+
|
|
756
|
+
if let error = error {
|
|
757
|
+
NSLog("Error: \(error)")
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
let errorStr = "Peripheral did disconnect: \(peripheralUUIDString)"
|
|
761
|
+
|
|
762
|
+
invokeAndClearDictionary(&connectCallbacks, withKey: peripheralUUIDString, usingParameters: [errorStr])
|
|
763
|
+
invokeAndClearDictionary(&readRSSICallbacks, withKey: peripheralUUIDString, usingParameters: [errorStr])
|
|
764
|
+
invokeAndClearDictionary(&retrieveServicesCallbacks, withKey: peripheralUUIDString, usingParameters: [errorStr])
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
for key in readCallbacks.keys {
|
|
768
|
+
if let keyString = key as String?, keyString.hasPrefix(peripheralUUIDString) {
|
|
769
|
+
invokeAndClearDictionary(&readCallbacks, withKey: key, usingParameters: [errorStr])
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
for key in writeCallbacks.keys {
|
|
774
|
+
if let keyString = key as String?, keyString.hasPrefix(peripheralUUIDString) {
|
|
775
|
+
invokeAndClearDictionary(&writeCallbacks, withKey: key, usingParameters: [errorStr])
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
for key in notificationCallbacks.keys {
|
|
780
|
+
if let keyString = key as String?, keyString.hasPrefix(peripheralUUIDString) {
|
|
781
|
+
invokeAndClearDictionary(¬ificationCallbacks, withKey: key, usingParameters: [errorStr])
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
for key in readDescriptorCallbacks.keys {
|
|
786
|
+
if let keyString = key as String?, keyString.hasPrefix(peripheralUUIDString) {
|
|
787
|
+
invokeAndClearDictionary(&readDescriptorCallbacks, withKey: key, usingParameters: [errorStr])
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
for key in stopNotificationCallbacks.keys {
|
|
792
|
+
if let keyString = key as String?, keyString.hasPrefix(peripheralUUIDString) {
|
|
793
|
+
invokeAndClearDictionary(&stopNotificationCallbacks, withKey: key, usingParameters: [errorStr])
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
if hasListeners {
|
|
798
|
+
connectedPeripherals.remove(peripheralUUIDString)
|
|
799
|
+
if let e:Error = error {
|
|
800
|
+
sendEvent(withName: "BleManagerDisconnectPeripheral", body: ["peripheral": peripheralUUIDString, "domain": e._domain, "code": e._code, "description": e.localizedDescription])
|
|
801
|
+
} else {
|
|
802
|
+
sendEvent(withName: "BleManagerDisconnectPeripheral", body: ["peripheral": peripheralUUIDString])
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
809
|
+
let stateName = Helper.centralManagerStateToString(central.state)
|
|
810
|
+
if hasListeners {
|
|
811
|
+
sendEvent(withName: "BleManagerDidUpdateState", body: ["state": stateName])
|
|
812
|
+
}
|
|
813
|
+
if stateName == "poweredOff" {
|
|
814
|
+
for peripheralUUID in connectedPeripherals {
|
|
815
|
+
if let peripheral = peripherals[peripheralUUID] {
|
|
816
|
+
if peripheral.instance.state == .disconnected {
|
|
817
|
+
self.centralManager(manager!, didDisconnectPeripheral:peripheral.instance, error: nil)
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
func handleDiscoveredPeripheral(_ peripheral: CBPeripheral,
|
|
825
|
+
advertisementData: [String : Any],
|
|
826
|
+
rssi : NSNumber) {
|
|
827
|
+
if BleManager.verboseLogging {
|
|
828
|
+
NSLog("Discover peripheral: \(peripheral.name ?? "NO NAME")");
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
var cp: Peripheral? = nil
|
|
832
|
+
serialQueue.sync {
|
|
833
|
+
if let p = peripherals[peripheral.uuidAsString()] {
|
|
834
|
+
cp = p
|
|
835
|
+
cp?.setRSSI(rssi)
|
|
836
|
+
cp?.setAdvertisementData(advertisementData)
|
|
837
|
+
} else {
|
|
838
|
+
cp = Peripheral(peripheral:peripheral, rssi:rssi, advertisementData:advertisementData)
|
|
839
|
+
peripherals[peripheral.uuidAsString()] = cp
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
if (hasListeners) {
|
|
844
|
+
sendEvent(withName: "BleManagerDiscoverPeripheral", body: cp?.advertisingInfo())
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
func centralManager(_ central: CBCentralManager,
|
|
849
|
+
didDiscover peripheral: CBPeripheral,
|
|
850
|
+
advertisementData: [String : Any],
|
|
851
|
+
rssi RSSI: NSNumber) {
|
|
852
|
+
if exactAdvertisingName.count > 0 {
|
|
853
|
+
if let peripheralName = peripheral.name {
|
|
854
|
+
if exactAdvertisingName.contains(peripheralName) {
|
|
855
|
+
handleDiscoveredPeripheral(peripheral, advertisementData: advertisementData, rssi: RSSI)
|
|
856
|
+
} else {
|
|
857
|
+
if let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
|
|
858
|
+
if exactAdvertisingName.contains(localName) {
|
|
859
|
+
handleDiscoveredPeripheral(peripheral, advertisementData: advertisementData, rssi: RSSI)
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
} else {
|
|
865
|
+
handleDiscoveredPeripheral(peripheral, advertisementData: advertisementData, rssi: RSSI)
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
func peripheral(_ peripheral: CBPeripheral,
|
|
872
|
+
didDiscoverServices error: Error?) {
|
|
873
|
+
if let error = error {
|
|
874
|
+
NSLog("Error: \(error)")
|
|
875
|
+
return
|
|
876
|
+
}
|
|
877
|
+
if BleManager.verboseLogging {
|
|
878
|
+
NSLog("Services Discover")
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
var servicesForPeripheral = Set<CBService>()
|
|
882
|
+
servicesForPeripheral.formUnion(peripheral.services ?? [])
|
|
883
|
+
retrieveServicesLatches[peripheral.uuidAsString()] = servicesForPeripheral
|
|
884
|
+
|
|
885
|
+
if let services = peripheral.services {
|
|
886
|
+
for service in services {
|
|
887
|
+
if BleManager.verboseLogging {
|
|
888
|
+
NSLog("Service \(service.uuid.uuidString) \(service.description)")
|
|
889
|
+
}
|
|
890
|
+
peripheral.discoverIncludedServices(nil, for: service) // discover included services
|
|
891
|
+
peripheral.discoverCharacteristics(nil, for: service) // discover characteristics for service
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
func peripheral(_ peripheral: CBPeripheral,
|
|
897
|
+
didDiscoverIncludedServicesFor service: CBService,
|
|
898
|
+
error: Error?) {
|
|
899
|
+
if let error = error {
|
|
900
|
+
NSLog("Error: \(error)")
|
|
901
|
+
return
|
|
902
|
+
}
|
|
903
|
+
peripheral.discoverCharacteristics(nil, for: service) // discover characteristics for included service
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
func peripheral(_ peripheral: CBPeripheral,
|
|
907
|
+
didDiscoverCharacteristicsFor service: CBService,
|
|
908
|
+
error: Error?) {
|
|
909
|
+
if let error = error {
|
|
910
|
+
NSLog("Error: \(error)")
|
|
911
|
+
return
|
|
912
|
+
}
|
|
913
|
+
if BleManager.verboseLogging {
|
|
914
|
+
NSLog("Characteristics For Service Discover")
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
var characteristicsForService = Set<CBCharacteristic>()
|
|
918
|
+
characteristicsForService.formUnion(service.characteristics ?? [])
|
|
919
|
+
characteristicsLatches[service.uuid.uuidString] = characteristicsForService
|
|
920
|
+
|
|
921
|
+
if let characteristics = service.characteristics {
|
|
922
|
+
for characteristic in characteristics {
|
|
923
|
+
peripheral.discoverDescriptors(for: characteristic)
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
func peripheral(_ peripheral: CBPeripheral,
|
|
929
|
+
didDiscoverDescriptorsFor characteristic: CBCharacteristic,
|
|
930
|
+
error: Error?) {
|
|
931
|
+
if let error = error {
|
|
932
|
+
NSLog("Error: \(error)")
|
|
933
|
+
return
|
|
934
|
+
}
|
|
935
|
+
let peripheralUUIDString:String = peripheral.uuidAsString()
|
|
936
|
+
let serviceUUIDString:String = (characteristic.service?.uuid.uuidString)!
|
|
937
|
+
|
|
938
|
+
if BleManager.verboseLogging {
|
|
939
|
+
NSLog("Descriptor For Characteristic Discover \(serviceUUIDString) \(characteristic.uuid.uuidString)")
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if var servicesLatch = retrieveServicesLatches[peripheralUUIDString], var characteristicsLatch = characteristicsLatches[serviceUUIDString] {
|
|
943
|
+
|
|
944
|
+
characteristicsLatch.remove(characteristic)
|
|
945
|
+
characteristicsLatches[serviceUUIDString] = characteristicsLatch
|
|
946
|
+
|
|
947
|
+
if characteristicsLatch.isEmpty {
|
|
948
|
+
// All characteristics for this service have been checked
|
|
949
|
+
servicesLatch.remove(characteristic.service!)
|
|
950
|
+
retrieveServicesLatches[peripheralUUIDString] = servicesLatch
|
|
951
|
+
|
|
952
|
+
if servicesLatch.isEmpty {
|
|
953
|
+
// All characteristics and services have been checked
|
|
954
|
+
if let peripheral = peripherals[peripheral.uuidAsString()] {
|
|
955
|
+
invokeAndClearDictionary(&retrieveServicesCallbacks, withKey: peripheralUUIDString, usingParameters: [NSNull(), peripheral.servicesInfo()])
|
|
956
|
+
}
|
|
957
|
+
characteristicsLatches.removeValue(forKey: serviceUUIDString)
|
|
958
|
+
retrieveServicesLatches.removeValue(forKey: peripheralUUIDString)
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
func peripheral(_ peripheral: CBPeripheral,
|
|
966
|
+
didReadRSSI RSSI: NSNumber,
|
|
967
|
+
error: Error?) {
|
|
968
|
+
if BleManager.verboseLogging {
|
|
969
|
+
print("didReadRSSI \(RSSI)")
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
if let error = error {
|
|
973
|
+
invokeAndClearDictionary(&readRSSICallbacks, withKey: peripheral.uuidAsString(), usingParameters: [error.localizedDescription, RSSI])
|
|
974
|
+
} else {
|
|
975
|
+
invokeAndClearDictionary(&readRSSICallbacks, withKey: peripheral.uuidAsString(), usingParameters: [NSNull(), RSSI])
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
func peripheral(_ peripheral: CBPeripheral,
|
|
980
|
+
didUpdateValueFor descriptor: CBDescriptor,
|
|
981
|
+
error: Error?) {
|
|
982
|
+
let key = Helper.key(forPeripheral: peripheral, andCharacteristic: descriptor.characteristic!, andDescriptor: descriptor)
|
|
983
|
+
|
|
984
|
+
if let error = error {
|
|
985
|
+
NSLog("Error reading descriptor value for \(descriptor.uuid) on characteristic \(descriptor.characteristic!.uuid) :\(error)")
|
|
986
|
+
invokeAndClearDictionary(&readDescriptorCallbacks, withKey: key, usingParameters: [error, NSNull()])
|
|
987
|
+
return
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
if let descriptorValue = descriptor.value as? Data {
|
|
991
|
+
NSLog("Read value [descriptor: \(descriptor.uuid), characteristic: \(descriptor.characteristic!.uuid)]: (\(descriptorValue.count)) \(descriptorValue.hexadecimalString())")
|
|
992
|
+
} else {
|
|
993
|
+
NSLog("Read value [descriptor: \(descriptor.uuid), characteristic: \(descriptor.characteristic!.uuid)]: \(String(describing: descriptor.value))")
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
if readDescriptorCallbacks[key] != nil {
|
|
997
|
+
// The most future proof way of doing this that I could find, other option would be running strcmp on CBUUID strings
|
|
998
|
+
// https://developer.apple.com/documentation/corebluetooth/cbuuid/characteristic_descriptors
|
|
999
|
+
if let descriptorValue = descriptor.value as? Data {
|
|
1000
|
+
if (BleManager.verboseLogging) {
|
|
1001
|
+
NSLog("Descriptor value is Data")
|
|
1002
|
+
}
|
|
1003
|
+
invokeAndClearDictionary(&readDescriptorCallbacks, withKey: key, usingParameters: [NSNull(), descriptorValue.toArray()])
|
|
1004
|
+
} else if let descriptorValue = descriptor.value as? NSNumber {
|
|
1005
|
+
if (BleManager.verboseLogging) {
|
|
1006
|
+
NSLog("Descriptor value is NSNumber")
|
|
1007
|
+
}
|
|
1008
|
+
var value = descriptorValue.uint64Value
|
|
1009
|
+
let byteData = Data(bytes: &value, count: MemoryLayout.size(ofValue: value))
|
|
1010
|
+
invokeAndClearDictionary(&readDescriptorCallbacks, withKey: key, usingParameters: [NSNull(), byteData.toArray()])
|
|
1011
|
+
} else if let descriptorValue = descriptor.value as? String {
|
|
1012
|
+
if (BleManager.verboseLogging) {
|
|
1013
|
+
NSLog("Descriptor value is String")
|
|
1014
|
+
}
|
|
1015
|
+
if let byteData = descriptorValue.data(using: .utf8) {
|
|
1016
|
+
invokeAndClearDictionary(&readDescriptorCallbacks, withKey: key, usingParameters: [NSNull(), byteData.toArray()])
|
|
1017
|
+
}
|
|
1018
|
+
} else {
|
|
1019
|
+
NSLog("Unrecognized type of descriptor: (UUID: \(descriptor.uuid), value type: \(type(of: descriptor.value)), value: \(String(describing: descriptor.value)))")
|
|
1020
|
+
if let descriptorValue = descriptor.value as? Data {
|
|
1021
|
+
invokeAndClearDictionary(&readDescriptorCallbacks, withKey: key, usingParameters: [NSNull(), descriptorValue.toArray()])
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
func peripheral(_ peripheral: CBPeripheral,
|
|
1028
|
+
didUpdateValueFor characteristic: CBCharacteristic,
|
|
1029
|
+
error: Error?) {
|
|
1030
|
+
let key = Helper.key(forPeripheral: peripheral, andCharacteristic: characteristic)
|
|
1031
|
+
|
|
1032
|
+
if let error = error {
|
|
1033
|
+
NSLog("Error \(characteristic.uuid) :\(error)")
|
|
1034
|
+
invokeAndClearDictionary(&readCallbacks, withKey: key, usingParameters: [error, NSNull()])
|
|
1035
|
+
return
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
NSLog("Read value [\(characteristic.uuid)]: (\(String(describing: characteristic.value?.hexadecimalString()))")
|
|
1039
|
+
|
|
1040
|
+
if readCallbacks[key] != nil {
|
|
1041
|
+
invokeAndClearDictionary(&readCallbacks, withKey: key, usingParameters: [NSNull(), characteristic.value!])
|
|
1042
|
+
} else {
|
|
1043
|
+
if hasListeners {
|
|
1044
|
+
sendEvent(withName: "BleManagerDidUpdateValueForCharacteristic", body: [
|
|
1045
|
+
"peripheral": peripheral.uuidAsString(),
|
|
1046
|
+
"characteristic": characteristic.uuid.uuidString.lowercased(),
|
|
1047
|
+
"service": characteristic.service!.uuid.uuidString.lowercased(),
|
|
1048
|
+
"value": characteristic.value!.toArray()
|
|
1049
|
+
])
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
func peripheral(_ peripheral: CBPeripheral,
|
|
1055
|
+
didUpdateNotificationStateFor characteristic: CBCharacteristic,
|
|
1056
|
+
error: Error?) {
|
|
1057
|
+
if let error = error {
|
|
1058
|
+
NSLog("Error in didUpdateNotificationStateForCharacteristic: \(error)")
|
|
1059
|
+
|
|
1060
|
+
if hasListeners {
|
|
1061
|
+
sendEvent(withName: "BleManagerDidUpdateNotificationStateFor", body: [
|
|
1062
|
+
"peripheral": peripheral.uuidAsString(),
|
|
1063
|
+
"characteristic": characteristic.uuid.uuidString.lowercased(),
|
|
1064
|
+
"isNotifying": false,
|
|
1065
|
+
"domain": error._domain,
|
|
1066
|
+
"code": error._code
|
|
1067
|
+
])
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
let key = Helper.key(forPeripheral: peripheral, andCharacteristic: characteristic)
|
|
1072
|
+
|
|
1073
|
+
if characteristic.isNotifying {
|
|
1074
|
+
if notificationCallbacks[key] != nil {
|
|
1075
|
+
if let error = error {
|
|
1076
|
+
invokeAndClearDictionary(¬ificationCallbacks, withKey: key, usingParameters: [error])
|
|
1077
|
+
} else {
|
|
1078
|
+
if BleManager.verboseLogging {
|
|
1079
|
+
NSLog("Notification began on \(characteristic.uuid)")
|
|
1080
|
+
}
|
|
1081
|
+
invokeAndClearDictionary(¬ificationCallbacks, withKey: key, usingParameters: [])
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
} else {
|
|
1085
|
+
// Notification has stopped
|
|
1086
|
+
if stopNotificationCallbacks[key] != nil {
|
|
1087
|
+
if error != nil {
|
|
1088
|
+
invokeAndClearDictionary(&stopNotificationCallbacks, withKey: key, usingParameters: [error as Any])
|
|
1089
|
+
} else {
|
|
1090
|
+
if BleManager.verboseLogging {
|
|
1091
|
+
NSLog("Notification ended on \(characteristic.uuid)")
|
|
1092
|
+
}
|
|
1093
|
+
invokeAndClearDictionary(&stopNotificationCallbacks, withKey: key, usingParameters: [])
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
if hasListeners {
|
|
1098
|
+
sendEvent(withName: "BleManagerDidUpdateNotificationStateFor", body: [
|
|
1099
|
+
"peripheral": peripheral.uuidAsString(),
|
|
1100
|
+
"characteristic": characteristic.uuid.uuidString.lowercased(),
|
|
1101
|
+
"isNotifying": characteristic.isNotifying
|
|
1102
|
+
])
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
func peripheral(_ peripheral: CBPeripheral,
|
|
1107
|
+
didWriteValueFor characteristic: CBCharacteristic,
|
|
1108
|
+
error: Error?) {
|
|
1109
|
+
NSLog("didWrite")
|
|
1110
|
+
|
|
1111
|
+
let key = Helper.key(forPeripheral:peripheral, andCharacteristic: characteristic)
|
|
1112
|
+
let peripheralWriteCallbacks = writeCallbacks[key]
|
|
1113
|
+
|
|
1114
|
+
if peripheralWriteCallbacks != nil {
|
|
1115
|
+
if let error = error {
|
|
1116
|
+
NSLog("\(error)")
|
|
1117
|
+
invokeAndClearDictionary(&writeCallbacks, withKey: key, usingParameters: [error.localizedDescription])
|
|
1118
|
+
} else {
|
|
1119
|
+
if writeQueue.isEmpty {
|
|
1120
|
+
invokeAndClearDictionary(&writeCallbacks, withKey: key, usingParameters: [])
|
|
1121
|
+
} else {
|
|
1122
|
+
// Rimuovi e scrivi il messaggio in coda
|
|
1123
|
+
let message = writeQueue.removeFirst() as! Data
|
|
1124
|
+
NSLog("Message to write \(message.hexadecimalString())")
|
|
1125
|
+
peripheral.writeValue(message, for: characteristic, type: .withResponse)
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
|
|
1132
|
+
static func getCentralManager() -> CBCentralManager? {
|
|
1133
|
+
return sharedManager
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
static func getInstance() -> BleManager? {
|
|
1137
|
+
return shared
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
@objc func enableBluetooth(_ callback: @escaping RCTResponseSenderBlock) {
|
|
1141
|
+
callback(["Not supported"])
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
@objc func getBondedPeripherals(_ callback: @escaping RCTResponseSenderBlock) {
|
|
1145
|
+
callback(["Not supported"])
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
@objc func createBond(_ peripheralUUID: String,
|
|
1149
|
+
devicePin: String,
|
|
1150
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
1151
|
+
callback(["Not supported"])
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
@objc func removeBond(_ peripheralUUID: String,
|
|
1155
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
1156
|
+
callback(["Not supported"])
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
@objc func removePeripheral(_ peripheralUUID: String,
|
|
1160
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
1161
|
+
callback(["Not supported"])
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
@objc func requestMTU(_ peripheralUUID: String,
|
|
1165
|
+
mtu: Int,
|
|
1166
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
1167
|
+
callback(["Not supported"])
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
@objc func requestConnectionPriority(_ peripheralUUID: String,
|
|
1171
|
+
mtu: Int,
|
|
1172
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
1173
|
+
callback(["Not supported"])
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
@objc func refreshCache(_ peripheralUUID: String,
|
|
1177
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
1178
|
+
callback(["Not supported"])
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
@objc func setName(_ name: String,
|
|
1182
|
+
callback: @escaping RCTResponseSenderBlock) {
|
|
1183
|
+
callback(["Not supported"])
|
|
1184
|
+
}
|
|
1185
|
+
}
|