munim-bluetooth 0.3.1 → 0.3.3
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/ios/HybridMunimBluetooth.swift +260 -266
- package/package.json +1 -1
|
@@ -10,18 +10,18 @@ import CoreBluetooth
|
|
|
10
10
|
import NitroModules
|
|
11
11
|
import React
|
|
12
12
|
|
|
13
|
-
class HybridMunimBluetooth:
|
|
13
|
+
class HybridMunimBluetooth: HybridMunimBluetoothSpec {
|
|
14
14
|
// Peripheral Manager
|
|
15
15
|
private var peripheralManager: CBPeripheralManager?
|
|
16
16
|
private var peripheralServices: [CBMutableService] = []
|
|
17
|
-
private var currentAdvertisingData:
|
|
17
|
+
private var currentAdvertisingData: AdvertisingDataTypes?
|
|
18
18
|
|
|
19
19
|
// Central Manager
|
|
20
20
|
private var centralManager: CBCentralManager?
|
|
21
21
|
private var discoveredPeripherals: [String: CBPeripheral] = [:]
|
|
22
22
|
private var connectedPeripherals: [String: CBPeripheral] = [:]
|
|
23
23
|
private var peripheralCharacteristics: [String: [CBCharacteristic]] = [:]
|
|
24
|
-
private var scanOptions:
|
|
24
|
+
private var scanOptions: ScanOptions?
|
|
25
25
|
private var isScanning = false
|
|
26
26
|
|
|
27
27
|
// Event emitter
|
|
@@ -36,7 +36,7 @@ class HybridMunimBluetooth: NSObject, HybridMunimBluetoothSpec {
|
|
|
36
36
|
|
|
37
37
|
// MARK: - Peripheral Features
|
|
38
38
|
|
|
39
|
-
func startAdvertising(options:
|
|
39
|
+
override func startAdvertising(options: AdvertisingOptions) throws {
|
|
40
40
|
guard let peripheralManager = peripheralManager,
|
|
41
41
|
peripheralManager.state == .poweredOn else {
|
|
42
42
|
throw NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Bluetooth is not powered on"])
|
|
@@ -44,31 +44,33 @@ class HybridMunimBluetooth: NSObject, HybridMunimBluetoothSpec {
|
|
|
44
44
|
|
|
45
45
|
var advertisingData: [String: Any] = [:]
|
|
46
46
|
|
|
47
|
-
//
|
|
48
|
-
if
|
|
49
|
-
|
|
47
|
+
// Service UUIDs
|
|
48
|
+
if !options.serviceUUIDs.isEmpty {
|
|
49
|
+
let uuids = options.serviceUUIDs.compactMap { CBUUID(string: $0) }
|
|
50
|
+
advertisingData[CBAdvertisementDataServiceUUIDsKey] = uuids
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
//
|
|
53
|
-
if let localName = options
|
|
53
|
+
// Local name
|
|
54
|
+
if let localName = options.localName {
|
|
54
55
|
advertisingData[CBAdvertisementDataLocalNameKey] = localName
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
advertisingData[CBAdvertisementDataServiceUUIDsKey] = uuids
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if let manufacturerData = options["manufacturerData"] as? String,
|
|
58
|
+
// Manufacturer data
|
|
59
|
+
if let manufacturerData = options.manufacturerData,
|
|
63
60
|
let data = hexStringToData(manufacturerData) {
|
|
64
61
|
advertisingData[CBAdvertisementDataManufacturerDataKey] = data
|
|
65
62
|
}
|
|
66
63
|
|
|
67
|
-
|
|
64
|
+
// Advertising data
|
|
65
|
+
if let advertisingDataTypes = options.advertisingData {
|
|
66
|
+
processAdvertisingData(advertisingDataTypes, into: &advertisingData)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
currentAdvertisingData = options.advertisingData
|
|
68
70
|
peripheralManager.startAdvertising(advertisingData as? [String: Any])
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
func updateAdvertisingData(advertisingData:
|
|
73
|
+
override func updateAdvertisingData(advertisingData: AdvertisingDataTypes) throws {
|
|
72
74
|
guard let peripheralManager = peripheralManager,
|
|
73
75
|
peripheralManager.state == .poweredOn else {
|
|
74
76
|
throw NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Bluetooth is not powered on"])
|
|
@@ -79,361 +81,353 @@ class HybridMunimBluetooth: NSObject, HybridMunimBluetoothSpec {
|
|
|
79
81
|
var newAdvertisingData: [String: Any] = [:]
|
|
80
82
|
processAdvertisingData(advertisingData, into: &newAdvertisingData)
|
|
81
83
|
|
|
82
|
-
currentAdvertisingData =
|
|
84
|
+
currentAdvertisingData = advertisingData
|
|
83
85
|
peripheralManager.startAdvertising(newAdvertisingData as? [String: Any])
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
func getAdvertisingData() throws ->
|
|
87
|
-
return
|
|
88
|
+
override func getAdvertisingData() throws -> Promise<AdvertisingDataTypes> {
|
|
89
|
+
return Promise { resolver in
|
|
90
|
+
resolver.resolve(self.currentAdvertisingData ?? AdvertisingDataTypes())
|
|
91
|
+
}
|
|
88
92
|
}
|
|
89
93
|
|
|
90
|
-
func stopAdvertising() throws {
|
|
94
|
+
override func stopAdvertising() throws {
|
|
91
95
|
peripheralManager?.stopAdvertising()
|
|
92
|
-
currentAdvertisingData =
|
|
96
|
+
currentAdvertisingData = nil
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
func setServices(services: [
|
|
99
|
+
override func setServices(services: [GATTService]) throws {
|
|
96
100
|
peripheralServices.removeAll()
|
|
97
101
|
|
|
98
|
-
for
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
let serviceUUID = CBUUID(string: uuidString)
|
|
102
|
-
let service = CBMutableService(type: serviceUUID, primary: true)
|
|
102
|
+
for service in services {
|
|
103
|
+
let serviceUUID = CBUUID(string: service.uuid)
|
|
104
|
+
let mutableService = CBMutableService(type: serviceUUID, primary: true)
|
|
103
105
|
|
|
104
106
|
var characteristics: [CBMutableCharacteristic] = []
|
|
105
107
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
default:
|
|
125
|
-
break
|
|
126
|
-
}
|
|
127
|
-
}
|
|
108
|
+
for characteristic in service.characteristics {
|
|
109
|
+
let charUUID = CBUUID(string: characteristic.uuid)
|
|
110
|
+
|
|
111
|
+
var properties: CBCharacteristicProperties = []
|
|
112
|
+
for prop in characteristic.properties {
|
|
113
|
+
switch prop {
|
|
114
|
+
case "read":
|
|
115
|
+
properties.insert(.read)
|
|
116
|
+
case "write":
|
|
117
|
+
properties.insert(.write)
|
|
118
|
+
case "writeWithoutResponse":
|
|
119
|
+
properties.insert(.writeWithoutResponse)
|
|
120
|
+
case "notify":
|
|
121
|
+
properties.insert(.notify)
|
|
122
|
+
case "indicate":
|
|
123
|
+
properties.insert(.indicate)
|
|
124
|
+
default:
|
|
125
|
+
break
|
|
128
126
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
var value: Data?
|
|
130
|
+
if let valueString = characteristic.value {
|
|
131
|
+
value = hexStringToData(valueString)
|
|
132
|
+
if !properties.contains(.read) {
|
|
133
133
|
properties.insert(.read)
|
|
134
134
|
}
|
|
135
|
-
|
|
136
|
-
let permissions: CBAttributePermissions = value != nil ? .readable : [.readable, .writeable]
|
|
137
|
-
|
|
138
|
-
let characteristic = CBMutableCharacteristic(
|
|
139
|
-
type: charUUID,
|
|
140
|
-
properties: properties,
|
|
141
|
-
value: value,
|
|
142
|
-
permissions: permissions
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
characteristics.append(characteristic)
|
|
146
135
|
}
|
|
136
|
+
|
|
137
|
+
let permissions: CBAttributePermissions = value != nil ? .readable : [.readable, .writeable]
|
|
138
|
+
|
|
139
|
+
let mutableChar = CBMutableCharacteristic(
|
|
140
|
+
type: charUUID,
|
|
141
|
+
properties: properties,
|
|
142
|
+
value: value,
|
|
143
|
+
permissions: permissions
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
characteristics.append(mutableChar)
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
peripheralServices.append(
|
|
149
|
+
mutableService.characteristics = characteristics
|
|
150
|
+
peripheralServices.append(mutableService)
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
peripheralManager?.removeAllServices()
|
|
154
153
|
for service in peripheralServices {
|
|
155
154
|
peripheralManager?.add(service)
|
|
156
155
|
}
|
|
157
156
|
}
|
|
158
157
|
|
|
159
|
-
// MARK: - Central Features
|
|
158
|
+
// MARK: - Central/Manager Features
|
|
160
159
|
|
|
161
|
-
func isBluetoothEnabled() throws -> Bool {
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
override func isBluetoothEnabled() throws -> Promise<Bool> {
|
|
161
|
+
return Promise { resolver in
|
|
162
|
+
let isEnabled = self.centralManager?.state == .poweredOn
|
|
163
|
+
resolver.resolve(isEnabled ?? false)
|
|
164
|
+
}
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
func requestBluetoothPermission() throws -> Bool {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
override func requestBluetoothPermission() throws -> Promise<Bool> {
|
|
168
|
+
return Promise { resolver in
|
|
169
|
+
// In iOS, permissions are handled by CBPeripheralManager/CBCentralManager
|
|
170
|
+
resolver.resolve(true)
|
|
171
|
+
}
|
|
171
172
|
}
|
|
172
173
|
|
|
173
|
-
func startScan(options:
|
|
174
|
+
override func startScan(options: ScanOptions?) throws {
|
|
174
175
|
guard let centralManager = centralManager,
|
|
175
176
|
centralManager.state == .poweredOn else {
|
|
176
177
|
throw NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Bluetooth is not powered on"])
|
|
177
178
|
}
|
|
178
179
|
|
|
179
|
-
guard !isScanning else { return }
|
|
180
|
-
|
|
181
180
|
scanOptions = options
|
|
182
181
|
isScanning = true
|
|
183
|
-
discoveredPeripherals.removeAll()
|
|
184
182
|
|
|
185
|
-
var
|
|
186
|
-
if let
|
|
187
|
-
|
|
183
|
+
var scanOptions: [String: Any] = [:]
|
|
184
|
+
if let options = options {
|
|
185
|
+
scanOptions[CBCentralManagerScanOptionAllowDuplicatesKey] = options.allowDuplicates ?? false
|
|
188
186
|
}
|
|
189
187
|
|
|
190
|
-
centralManager.scanForPeripherals(withServices:
|
|
191
|
-
CBCentralManagerScanOptionAllowDuplicatesKey: options?["allowDuplicates"] as? Bool ?? false
|
|
192
|
-
])
|
|
188
|
+
centralManager.scanForPeripherals(withServices: nil, options: scanOptions as [String : Any])
|
|
193
189
|
}
|
|
194
190
|
|
|
195
|
-
func stopScan() throws {
|
|
196
|
-
guard isScanning else { return }
|
|
191
|
+
override func stopScan() throws {
|
|
197
192
|
centralManager?.stopScan()
|
|
198
193
|
isScanning = false
|
|
199
|
-
scanOptions = nil
|
|
200
194
|
}
|
|
201
195
|
|
|
202
|
-
func connect(deviceId: String) throws {
|
|
203
|
-
|
|
204
|
-
|
|
196
|
+
override func connect(deviceId: String) throws -> Promise<Void> {
|
|
197
|
+
return Promise { resolver in
|
|
198
|
+
guard let peripheral = self.discoveredPeripherals[deviceId] else {
|
|
199
|
+
resolver.reject(NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Device not found"]))
|
|
200
|
+
return
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
self.centralManager?.connect(peripheral, options: nil)
|
|
204
|
+
self.connectedPeripherals[deviceId] = peripheral
|
|
205
|
+
resolver.resolve(())
|
|
205
206
|
}
|
|
206
|
-
|
|
207
|
-
centralManager?.connect(peripheral, options: nil)
|
|
208
207
|
}
|
|
209
208
|
|
|
210
|
-
func disconnect(deviceId: String) throws {
|
|
211
|
-
guard let peripheral = connectedPeripherals[deviceId]
|
|
212
|
-
return
|
|
213
|
-
}
|
|
214
|
-
|
|
209
|
+
override func disconnect(deviceId: String) throws {
|
|
210
|
+
guard let peripheral = connectedPeripherals[deviceId] else { return }
|
|
215
211
|
centralManager?.cancelPeripheralConnection(peripheral)
|
|
212
|
+
connectedPeripherals.removeValue(forKey: deviceId)
|
|
216
213
|
}
|
|
217
214
|
|
|
218
|
-
func discoverServices(deviceId: String) throws -> [
|
|
219
|
-
|
|
220
|
-
|
|
215
|
+
override func discoverServices(deviceId: String) throws -> Promise<[GATTService]> {
|
|
216
|
+
return Promise { resolver in
|
|
217
|
+
guard let peripheral = self.connectedPeripherals[deviceId] else {
|
|
218
|
+
resolver.reject(NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Device not connected"]))
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
peripheral.discoverServices(nil)
|
|
223
|
+
resolver.resolve([])
|
|
221
224
|
}
|
|
222
|
-
|
|
223
|
-
peripheral.discoverServices(nil)
|
|
224
|
-
|
|
225
|
-
// Wait for services to be discovered (in real implementation, this would be async)
|
|
226
|
-
// For now, return empty array - services will be discovered via delegate callbacks
|
|
227
|
-
return []
|
|
228
225
|
}
|
|
229
226
|
|
|
230
|
-
func readCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws ->
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
guard let characteristics = peripheralCharacteristics[deviceId],
|
|
236
|
-
let characteristic = characteristics.first(where: {
|
|
237
|
-
$0.service?.uuid == CBUUID(string: serviceUUID) &&
|
|
238
|
-
$0.uuid == CBUUID(string: characteristicUUID)
|
|
239
|
-
}) else {
|
|
240
|
-
throw NSError(domain: "MunimBluetooth", code: 3, userInfo: [NSLocalizedDescriptionKey: "Characteristic not found"])
|
|
227
|
+
override func readCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws -> Promise<CharacteristicValue> {
|
|
228
|
+
return Promise { resolver in
|
|
229
|
+
resolver.reject(NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Not implemented"]))
|
|
241
230
|
}
|
|
242
|
-
|
|
243
|
-
peripheral.readValue(for: characteristic)
|
|
244
|
-
|
|
245
|
-
// Return empty for now - value will come via delegate callback
|
|
246
|
-
return [
|
|
247
|
-
"value": "",
|
|
248
|
-
"serviceUUID": serviceUUID,
|
|
249
|
-
"characteristicUUID": characteristicUUID
|
|
250
|
-
]
|
|
251
231
|
}
|
|
252
232
|
|
|
253
|
-
func writeCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String, value: String, writeType:
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
guard let characteristics = peripheralCharacteristics[deviceId],
|
|
259
|
-
let characteristic = characteristics.first(where: {
|
|
260
|
-
$0.service?.uuid == CBUUID(string: serviceUUID) &&
|
|
261
|
-
$0.uuid == CBUUID(string: characteristicUUID)
|
|
262
|
-
}) else {
|
|
263
|
-
throw NSError(domain: "MunimBluetooth", code: 3, userInfo: [NSLocalizedDescriptionKey: "Characteristic not found"])
|
|
233
|
+
override func writeCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String, value: String, writeType: WriteType?) throws -> Promise<Void> {
|
|
234
|
+
return Promise { resolver in
|
|
235
|
+
resolver.reject(NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Not implemented"]))
|
|
264
236
|
}
|
|
265
|
-
|
|
266
|
-
guard let data = hexStringToData(value) else {
|
|
267
|
-
throw NSError(domain: "MunimBluetooth", code: 4, userInfo: [NSLocalizedDescriptionKey: "Invalid hex string"])
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
let type: CBCharacteristicWriteType = (writeType == "writeWithoutResponse") ? .withoutResponse : .withResponse
|
|
271
|
-
peripheral.writeValue(data, for: characteristic, type: type)
|
|
272
237
|
}
|
|
273
238
|
|
|
274
|
-
func subscribeToCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws {
|
|
275
|
-
|
|
276
|
-
throw NSError(domain: "MunimBluetooth", code: 2, userInfo: [NSLocalizedDescriptionKey: "Device not connected"])
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
guard let characteristics = peripheralCharacteristics[deviceId],
|
|
280
|
-
let characteristic = characteristics.first(where: {
|
|
281
|
-
$0.service?.uuid == CBUUID(string: serviceUUID) &&
|
|
282
|
-
$0.uuid == CBUUID(string: characteristicUUID)
|
|
283
|
-
}) else {
|
|
284
|
-
throw NSError(domain: "MunimBluetooth", code: 3, userInfo: [NSLocalizedDescriptionKey: "Characteristic not found"])
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
peripheral.setNotifyValue(true, for: characteristic)
|
|
239
|
+
override func subscribeToCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws {
|
|
240
|
+
// Not implemented
|
|
288
241
|
}
|
|
289
242
|
|
|
290
|
-
func unsubscribeFromCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws {
|
|
291
|
-
|
|
292
|
-
throw NSError(domain: "MunimBluetooth", code: 2, userInfo: [NSLocalizedDescriptionKey: "Device not connected"])
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
guard let characteristics = peripheralCharacteristics[deviceId],
|
|
296
|
-
let characteristic = characteristics.first(where: {
|
|
297
|
-
$0.service?.uuid == CBUUID(string: serviceUUID) &&
|
|
298
|
-
$0.uuid == CBUUID(string: characteristicUUID)
|
|
299
|
-
}) else {
|
|
300
|
-
throw NSError(domain: "MunimBluetooth", code: 3, userInfo: [NSLocalizedDescriptionKey: "Characteristic not found"])
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
peripheral.setNotifyValue(false, for: characteristic)
|
|
243
|
+
override func unsubscribeFromCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws {
|
|
244
|
+
// Not implemented
|
|
304
245
|
}
|
|
305
246
|
|
|
306
|
-
func getConnectedDevices() throws -> [String] {
|
|
307
|
-
return
|
|
247
|
+
override func getConnectedDevices() throws -> Promise<[String]> {
|
|
248
|
+
return Promise { resolver in
|
|
249
|
+
resolver.resolve(Array(self.connectedPeripherals.keys))
|
|
250
|
+
}
|
|
308
251
|
}
|
|
309
252
|
|
|
310
|
-
func readRSSI(deviceId: String) throws -> Double {
|
|
311
|
-
|
|
312
|
-
|
|
253
|
+
override func readRSSI(deviceId: String) throws -> Promise<Double> {
|
|
254
|
+
return Promise { resolver in
|
|
255
|
+
guard let peripheral = self.connectedPeripherals[deviceId] else {
|
|
256
|
+
resolver.reject(NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Device not connected"]))
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
peripheral.readRSSI()
|
|
261
|
+
resolver.resolve(0)
|
|
313
262
|
}
|
|
314
|
-
|
|
315
|
-
peripheral.readRSSI()
|
|
316
|
-
// RSSI will come via delegate callback
|
|
317
|
-
return 0.0
|
|
318
263
|
}
|
|
319
264
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
func addListener(eventName: String) throws {
|
|
323
|
-
// Event listeners are handled by the event emitter
|
|
324
|
-
// This is a no-op as Nitro modules handle events differently
|
|
265
|
+
override func addListener(eventName: String) throws {
|
|
266
|
+
// Event management
|
|
325
267
|
}
|
|
326
268
|
|
|
327
|
-
func removeListeners(count: Double) throws {
|
|
328
|
-
// Event
|
|
329
|
-
// This is a no-op as Nitro modules handle events differently
|
|
269
|
+
override func removeListeners(count: Double) throws {
|
|
270
|
+
// Event management
|
|
330
271
|
}
|
|
331
272
|
|
|
332
273
|
// MARK: - Helper Methods
|
|
333
274
|
|
|
334
|
-
private func
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
275
|
+
private func hexStringToData(_ hex: String) -> Data? {
|
|
276
|
+
var data = Data()
|
|
277
|
+
var hex = hex
|
|
278
|
+
|
|
279
|
+
if hex.count % 2 != 0 {
|
|
280
|
+
hex = "0" + hex
|
|
339
281
|
}
|
|
340
282
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
addServiceUUIDs(dataDict["completeServiceUUIDs128"], to: &advertisingData, key: CBAdvertisementDataServiceUUIDsKey)
|
|
283
|
+
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
|
|
284
|
+
regex.enumerateMatches(in: hex, range: NSRange(hex.startIndex..., in: hex)) { match, _, _ in
|
|
285
|
+
let byteStr = (hex as NSString).substring(with: match!.range)
|
|
286
|
+
let num = UInt8(byteStr, radix: 16)!
|
|
287
|
+
data.append(num)
|
|
288
|
+
}
|
|
348
289
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
290
|
+
return data.isEmpty ? nil : data
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private func processAdvertisingData(_ data: AdvertisingDataTypes, into advertisingData: inout [String: Any]) {
|
|
294
|
+
if let flags = data.flags {
|
|
295
|
+
advertisingData[CBAdvertisementDataIsConnectable] = true
|
|
352
296
|
}
|
|
353
|
-
|
|
354
|
-
|
|
297
|
+
|
|
298
|
+
if let completeLocalName = data.completeLocalName {
|
|
299
|
+
advertisingData[CBAdvertisementDataLocalNameKey] = completeLocalName
|
|
355
300
|
}
|
|
356
301
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
302
|
+
if let txPowerLevel = data.txPowerLevel {
|
|
303
|
+
advertisingData[CBAdvertisementDataTxPowerLevelKey] = txPowerLevel
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// MARK: - CBPeripheralManagerDelegate
|
|
309
|
+
extension HybridMunimBluetooth: CBPeripheralManagerDelegate {
|
|
310
|
+
guard let peripheralManager = peripheralManager,
|
|
311
|
+
peripheralManager.state == .poweredOn else {
|
|
312
|
+
throw NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Bluetooth is not powered on"])
|
|
360
313
|
}
|
|
361
314
|
|
|
362
|
-
|
|
363
|
-
addServiceUUIDs(dataDict["serviceSolicitationUUIDs16"], to: &advertisingData, key: CBAdvertisementDataSolicitedServiceUUIDsKey)
|
|
364
|
-
addServiceUUIDs(dataDict["serviceSolicitationUUIDs128"], to: &advertisingData, key: CBAdvertisementDataSolicitedServiceUUIDsKey)
|
|
365
|
-
addServiceUUIDs(dataDict["serviceSolicitationUUIDs32"], to: &advertisingData, key: CBAdvertisementDataSolicitedServiceUUIDsKey)
|
|
315
|
+
var advertisingData: [String: Any] = [:]
|
|
366
316
|
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
317
|
+
// Process comprehensive advertising data
|
|
318
|
+
if let advertisingDataDict = options["advertisingData"] as? [String: Any] {
|
|
319
|
+
processAdvertisingData(advertisingDataDict, into: &advertisingData)
|
|
320
|
+
}
|
|
371
321
|
|
|
372
|
-
//
|
|
373
|
-
if let
|
|
374
|
-
|
|
375
|
-
|
|
322
|
+
// Legacy support
|
|
323
|
+
if let localName = options["localName"] as? String {
|
|
324
|
+
advertisingData[CBAdvertisementDataLocalNameKey] = localName
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if let serviceUUIDs = options["serviceUUIDs"] as? [String], !serviceUUIDs.isEmpty {
|
|
328
|
+
let uuids = serviceUUIDs.compactMap { CBUUID(string: $0) }
|
|
329
|
+
advertisingData[CBAdvertisementDataServiceUUIDsKey] = uuids
|
|
376
330
|
}
|
|
377
331
|
|
|
378
|
-
|
|
379
|
-
if let manufacturerData = dataDict["manufacturerData"] as? String,
|
|
332
|
+
if let manufacturerData = options["manufacturerData"] as? String,
|
|
380
333
|
let data = hexStringToData(manufacturerData) {
|
|
381
334
|
advertisingData[CBAdvertisementDataManufacturerDataKey] = data
|
|
382
335
|
}
|
|
336
|
+
|
|
337
|
+
currentAdvertisingData = advertisingData
|
|
338
|
+
peripheralManager.startAdvertising(advertisingData as? [String: Any])
|
|
383
339
|
}
|
|
384
340
|
|
|
385
|
-
|
|
386
|
-
guard let
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if !cbUUIDs.isEmpty {
|
|
390
|
-
if var existing = advertisingData[key] as? [CBUUID] {
|
|
391
|
-
existing.append(contentsOf: cbUUIDs)
|
|
392
|
-
advertisingData[key] = existing
|
|
393
|
-
} else {
|
|
394
|
-
advertisingData[key] = cbUUIDs
|
|
395
|
-
}
|
|
341
|
+
func updateAdvertisingData(advertisingData: [String: Any]) throws {
|
|
342
|
+
guard let peripheralManager = peripheralManager,
|
|
343
|
+
peripheralManager.state == .poweredOn else {
|
|
344
|
+
throw NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Bluetooth is not powered on"])
|
|
396
345
|
}
|
|
346
|
+
|
|
347
|
+
peripheralManager.stopAdvertising()
|
|
348
|
+
|
|
349
|
+
var newAdvertisingData: [String: Any] = [:]
|
|
350
|
+
processAdvertisingData(advertisingData, into: &newAdvertisingData)
|
|
351
|
+
|
|
352
|
+
currentAdvertisingData = newAdvertisingData
|
|
353
|
+
peripheralManager.startAdvertising(newAdvertisingData as? [String: Any])
|
|
397
354
|
}
|
|
398
355
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
for serviceData in array {
|
|
403
|
-
guard let uuid = serviceData["uuid"] as? String,
|
|
404
|
-
let dataString = serviceData["data"] as? String,
|
|
405
|
-
let data = hexStringToData(dataString) else { continue }
|
|
406
|
-
|
|
407
|
-
advertisingData[uuid] = data
|
|
408
|
-
}
|
|
356
|
+
func getAdvertisingData() throws -> [String: Any] {
|
|
357
|
+
return currentAdvertisingData
|
|
409
358
|
}
|
|
410
359
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
360
|
+
func stopAdvertising() throws {
|
|
361
|
+
peripheralManager?.stopAdvertising()
|
|
362
|
+
currentAdvertisingData = [:]
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
func setServices(services: [[String: Any]]) throws {
|
|
366
|
+
peripheralServices.removeAll()
|
|
417
367
|
|
|
418
|
-
|
|
419
|
-
let
|
|
420
|
-
guard nextIndex <= cleanHex.endIndex else { break }
|
|
368
|
+
for serviceDict in services {
|
|
369
|
+
guard let uuidString = serviceDict["uuid"] as? String else { continue }
|
|
421
370
|
|
|
422
|
-
let
|
|
423
|
-
|
|
424
|
-
|
|
371
|
+
let serviceUUID = CBUUID(string: uuidString)
|
|
372
|
+
let service = CBMutableService(type: serviceUUID, primary: true)
|
|
373
|
+
|
|
374
|
+
var characteristics: [CBMutableCharacteristic] = []
|
|
375
|
+
|
|
376
|
+
if let characteristicsArray = serviceDict["characteristics"] as? [[String: Any]] {
|
|
377
|
+
for charDict in characteristicsArray {
|
|
378
|
+
guard let charUUIDString = charDict["uuid"] as? String else { continue }
|
|
379
|
+
|
|
380
|
+
let charUUID = CBUUID(string: charUUIDString)
|
|
381
|
+
var properties: CBCharacteristicProperties = []
|
|
382
|
+
|
|
383
|
+
if let propertiesArray = charDict["properties"] as? [String] {
|
|
384
|
+
for prop in propertiesArray {
|
|
385
|
+
switch prop {
|
|
386
|
+
case "read":
|
|
387
|
+
properties.insert(.read)
|
|
388
|
+
case "write":
|
|
389
|
+
properties.insert(.write)
|
|
390
|
+
case "notify":
|
|
391
|
+
properties.insert(.notify)
|
|
392
|
+
case "indicate":
|
|
393
|
+
properties.insert(.indicate)
|
|
394
|
+
default:
|
|
395
|
+
break
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
var value: Data?
|
|
401
|
+
if let valueString = charDict["value"] as? String {
|
|
402
|
+
value = valueString.data(using: .utf8)
|
|
403
|
+
properties.insert(.read)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
let permissions: CBAttributePermissions = value != nil ? .readable : [.readable, .writeable]
|
|
407
|
+
|
|
408
|
+
let characteristic = CBMutableCharacteristic(
|
|
409
|
+
type: charUUID,
|
|
410
|
+
properties: properties,
|
|
411
|
+
value: value,
|
|
412
|
+
permissions: permissions
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
characteristics.append(characteristic)
|
|
416
|
+
}
|
|
425
417
|
}
|
|
426
418
|
|
|
427
|
-
|
|
419
|
+
service.characteristics = characteristics
|
|
420
|
+
peripheralServices.append(service)
|
|
428
421
|
}
|
|
429
422
|
|
|
430
|
-
|
|
423
|
+
peripheralManager?.removeAllServices()
|
|
424
|
+
for service in peripheralServices {
|
|
425
|
+
peripheralManager?.add(service)
|
|
426
|
+
}
|
|
431
427
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
extension HybridMunimBluetooth: CBPeripheralManagerDelegate {
|
|
428
|
+
|
|
429
|
+
// MARK: - CBPeripheralManagerDelegate
|
|
430
|
+
extension HybridMunimBluetooth: CBPeripheralManagerDelegate {
|
|
437
431
|
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
|
|
438
432
|
// Handle state updates
|
|
439
433
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "munim-bluetooth",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "A comprehensive React Native library for all your Bluetooth Low Energy (BLE) needs, supporting both peripheral and central roles with Expo support",
|
|
5
5
|
"main": "./lib/commonjs/index.js",
|
|
6
6
|
"module": "./lib/module/index.js",
|