@capacitor-community/bluetooth-le 7.2.0 → 7.3.1
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 +68 -161
- package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/BluetoothLe.kt +43 -19
- package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/Device.kt +82 -78
- package/dist/docs.json +943 -886
- package/dist/esm/bleClient.js +15 -1
- package/dist/esm/bleClient.js.map +1 -1
- package/dist/esm/conversion.d.ts +12 -0
- package/dist/esm/conversion.js +33 -0
- package/dist/esm/conversion.js.map +1 -1
- package/dist/esm/definitions.d.ts +41 -2
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +1 -0
- package/dist/esm/web.js +66 -3
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +115 -3
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +115 -3
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/DeviceListView.swift +121 -0
- package/ios/Plugin/DeviceManager.swift +74 -61
- package/ios/Plugin/Plugin.swift +55 -12
- package/ios/Plugin/ScanFilters.swift +114 -0
- package/package.json +13 -3
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import CoreBluetooth
|
|
3
3
|
|
|
4
|
+
enum DeviceListMode {
|
|
5
|
+
case none
|
|
6
|
+
case alert
|
|
7
|
+
case list
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
5
11
|
typealias Callback = (_ success: Bool, _ message: String) -> Void
|
|
6
12
|
typealias StateReceiver = (_ enabled: Bool) -> Void
|
|
@@ -15,12 +21,15 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
|
15
21
|
private var timeoutMap = [String: DispatchWorkItem]()
|
|
16
22
|
private var stopScanWorkItem: DispatchWorkItem?
|
|
17
23
|
private var alertController: UIAlertController?
|
|
24
|
+
private var deviceListView: DeviceListView?
|
|
25
|
+
private var popoverController: UIPopoverPresentationController?
|
|
18
26
|
private var discoveredDevices = [String: Device]()
|
|
19
27
|
private var deviceNameFilter: String?
|
|
20
28
|
private var deviceNamePrefixFilter: String?
|
|
21
|
-
private var
|
|
29
|
+
private var deviceListMode: DeviceListMode = .none
|
|
22
30
|
private var allowDuplicates = false
|
|
23
31
|
private var manufacturerDataFilters: [ManufacturerDataFilter]?
|
|
32
|
+
private var serviceDataFilters: [ServiceDataFilter]?
|
|
24
33
|
|
|
25
34
|
init(_ viewController: UIViewController?, _ displayStrings: [String: String], _ callback: @escaping Callback) {
|
|
26
35
|
super.init()
|
|
@@ -81,8 +90,9 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
|
81
90
|
_ name: String?,
|
|
82
91
|
_ namePrefix: String?,
|
|
83
92
|
_ manufacturerDataFilters: [ManufacturerDataFilter]?,
|
|
93
|
+
_ serviceDataFilters: [ServiceDataFilter]?,
|
|
84
94
|
_ allowDuplicates: Bool,
|
|
85
|
-
_
|
|
95
|
+
_ deviceListMode: DeviceListMode,
|
|
86
96
|
_ scanDuration: Double?,
|
|
87
97
|
_ callback: @escaping Callback,
|
|
88
98
|
_ scanResultCallback: @escaping ScanResultCallback
|
|
@@ -92,13 +102,14 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
|
92
102
|
|
|
93
103
|
if self.centralManager.isScanning == false {
|
|
94
104
|
self.discoveredDevices = [String: Device]()
|
|
95
|
-
self.
|
|
105
|
+
self.deviceListMode = deviceListMode
|
|
96
106
|
self.allowDuplicates = allowDuplicates
|
|
97
107
|
self.deviceNameFilter = name
|
|
98
108
|
self.deviceNamePrefixFilter = namePrefix
|
|
99
109
|
self.manufacturerDataFilters = manufacturerDataFilters
|
|
110
|
+
self.serviceDataFilters = serviceDataFilters
|
|
100
111
|
|
|
101
|
-
if
|
|
112
|
+
if deviceListMode != .none {
|
|
102
113
|
self.showDeviceList()
|
|
103
114
|
}
|
|
104
115
|
|
|
@@ -113,7 +124,7 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
|
113
124
|
options: [CBCentralManagerScanOptionAllowDuplicatesKey: allowDuplicates]
|
|
114
125
|
)
|
|
115
126
|
|
|
116
|
-
if
|
|
127
|
+
if deviceListMode == .none {
|
|
117
128
|
self.resolve("startScanning", "Scan started.")
|
|
118
129
|
}
|
|
119
130
|
} else {
|
|
@@ -128,10 +139,22 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
|
128
139
|
self.stopScanWorkItem?.cancel()
|
|
129
140
|
self.stopScanWorkItem = nil
|
|
130
141
|
DispatchQueue.main.async { [weak self] in
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
self
|
|
142
|
+
guard let self = self else { return }
|
|
143
|
+
switch self.deviceListMode {
|
|
144
|
+
case .alert:
|
|
145
|
+
if self.discoveredDevices.count == 0 {
|
|
146
|
+
self.alertController?.title = self.displayStrings["noDeviceFound"]
|
|
147
|
+
} else {
|
|
148
|
+
self.alertController?.title = self.displayStrings["availableDevices"]
|
|
149
|
+
}
|
|
150
|
+
case .list:
|
|
151
|
+
if self.discoveredDevices.count == 0 {
|
|
152
|
+
self.deviceListView?.setTitle(self.displayStrings["noDeviceFound"])
|
|
153
|
+
} else {
|
|
154
|
+
self.deviceListView?.setTitle(self.displayStrings["availableDevices"])
|
|
155
|
+
}
|
|
156
|
+
case .none:
|
|
157
|
+
break
|
|
135
158
|
}
|
|
136
159
|
}
|
|
137
160
|
}
|
|
@@ -155,7 +178,8 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
|
155
178
|
|
|
156
179
|
guard self.passesNameFilter(peripheralName: peripheral.name) else { return }
|
|
157
180
|
guard self.passesNamePrefixFilter(peripheralName: peripheral.name) else { return }
|
|
158
|
-
guard
|
|
181
|
+
guard ScanFilterUtils.passesManufacturerDataFilter(advertisementData, filters: self.manufacturerDataFilters) else { return }
|
|
182
|
+
guard ScanFilterUtils.passesServiceDataFilter(advertisementData, filters: self.serviceDataFilters) else { return }
|
|
159
183
|
|
|
160
184
|
let device: Device
|
|
161
185
|
if self.allowDuplicates, let knownDevice = discoveredDevices.first(where: { $0.key == peripheral.identifier.uuidString })?.value {
|
|
@@ -166,7 +190,12 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
|
166
190
|
}
|
|
167
191
|
log("New device found: ", device.getName() ?? "Unknown")
|
|
168
192
|
|
|
169
|
-
|
|
193
|
+
switch deviceListMode {
|
|
194
|
+
case .none:
|
|
195
|
+
if self.scanResultCallback != nil {
|
|
196
|
+
self.scanResultCallback!(device, advertisementData, RSSI)
|
|
197
|
+
}
|
|
198
|
+
case .alert:
|
|
170
199
|
DispatchQueue.main.async { [weak self] in
|
|
171
200
|
self?.alertController?.addAction(UIAlertAction(title: device.getName() ?? "Unknown", style: UIAlertAction.Style.default, handler: { (_) in
|
|
172
201
|
log("Selected device")
|
|
@@ -174,14 +203,29 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
|
174
203
|
self?.resolve("startScanning", device.getId())
|
|
175
204
|
}))
|
|
176
205
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
self.
|
|
206
|
+
case .list:
|
|
207
|
+
DispatchQueue.main.async { [weak self] in
|
|
208
|
+
self?.deviceListView?.addItem(device.getName() ?? "Unknown", action: {
|
|
209
|
+
log("Selected device")
|
|
210
|
+
self?.stopScan()
|
|
211
|
+
self?.resolve("startScanning", device.getId())
|
|
212
|
+
})
|
|
180
213
|
}
|
|
181
214
|
}
|
|
182
215
|
}
|
|
183
216
|
|
|
184
217
|
func showDeviceList() {
|
|
218
|
+
switch deviceListMode {
|
|
219
|
+
case .none:
|
|
220
|
+
break
|
|
221
|
+
case .alert:
|
|
222
|
+
showDeviceListAlert()
|
|
223
|
+
case .list:
|
|
224
|
+
showDeviceListView()
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
func showDeviceListAlert() {
|
|
185
229
|
DispatchQueue.main.async { [weak self] in
|
|
186
230
|
self?.alertController = UIAlertController(title: self?.displayStrings["scanning"], message: nil, preferredStyle: UIAlertController.Style.alert)
|
|
187
231
|
self?.alertController?.addAction(UIAlertAction(title: self?.displayStrings["cancel"], style: UIAlertAction.Style.cancel, handler: { (_) in
|
|
@@ -193,6 +237,22 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
|
193
237
|
}
|
|
194
238
|
}
|
|
195
239
|
|
|
240
|
+
func showDeviceListView() {
|
|
241
|
+
DispatchQueue.main.async { [weak self] in
|
|
242
|
+
self?.deviceListView = DeviceListView()
|
|
243
|
+
if #available(macCatalyst 15.0, iOS 15.0, *) {
|
|
244
|
+
self?.deviceListView?.sheetPresentationController?.detents = [.medium()]
|
|
245
|
+
}
|
|
246
|
+
self?.viewController?.present((self?.deviceListView)!, animated: true, completion: nil)
|
|
247
|
+
self?.deviceListView?.setTitle(self?.displayStrings["scanning"])
|
|
248
|
+
self?.deviceListView?.setCancelButton(self?.displayStrings["cancel"], action: {
|
|
249
|
+
log("Cancelled request device.")
|
|
250
|
+
self?.stopScan()
|
|
251
|
+
self?.reject("startScanning", "requestDevice cancelled.")
|
|
252
|
+
})
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
196
256
|
func getDevices(
|
|
197
257
|
_ deviceUUIDs: [UUID]
|
|
198
258
|
) -> [CBPeripheral] {
|
|
@@ -300,53 +360,6 @@ class DeviceManager: NSObject, CBCentralManagerDelegate {
|
|
|
300
360
|
return name.hasPrefix(prefix)
|
|
301
361
|
}
|
|
302
362
|
|
|
303
|
-
private func passesManufacturerDataFilter(_ advertisementData: [String: Any]) -> Bool {
|
|
304
|
-
guard let filters = self.manufacturerDataFilters, !filters.isEmpty else {
|
|
305
|
-
return true // No filters means everything passes
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
guard let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data,
|
|
309
|
-
manufacturerData.count >= 2 else {
|
|
310
|
-
return false // If there's no valid manufacturer data, fail
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
let companyIdentifier = manufacturerData.prefix(2).withUnsafeBytes {
|
|
314
|
-
$0.load(as: UInt16.self).littleEndian // Manufacturer ID is little-endian
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
let payload = manufacturerData.dropFirst(2)
|
|
318
|
-
|
|
319
|
-
for filter in filters {
|
|
320
|
-
if filter.companyIdentifier != companyIdentifier {
|
|
321
|
-
continue // Skip if company ID does not match
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if let dataPrefix = filter.dataPrefix {
|
|
325
|
-
if payload.count < dataPrefix.count {
|
|
326
|
-
continue // Payload too short, does not match
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
if let mask = filter.mask {
|
|
330
|
-
var matches = true
|
|
331
|
-
for i in 0..<dataPrefix.count {
|
|
332
|
-
if (payload[i] & mask[i]) != (dataPrefix[i] & mask[i]) {
|
|
333
|
-
matches = false
|
|
334
|
-
break
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if matches {
|
|
338
|
-
return true
|
|
339
|
-
}
|
|
340
|
-
} else if payload.starts(with: dataPrefix) {
|
|
341
|
-
return true
|
|
342
|
-
}
|
|
343
|
-
} else {
|
|
344
|
-
return true // Company ID matched, and no dataPrefix required
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
return false // If none matched, return false
|
|
349
|
-
}
|
|
350
363
|
|
|
351
364
|
private func resolve(_ key: String, _ value: String) {
|
|
352
365
|
let callback = self.callbackMap[key]
|
package/ios/Plugin/Plugin.swift
CHANGED
|
@@ -7,12 +7,6 @@ import CoreBluetooth
|
|
|
7
7
|
let CONNECTION_TIMEOUT: Double = 10
|
|
8
8
|
let DEFAULT_TIMEOUT: Double = 5
|
|
9
9
|
|
|
10
|
-
struct ManufacturerDataFilter {
|
|
11
|
-
let companyIdentifier: UInt16
|
|
12
|
-
let dataPrefix: Data?
|
|
13
|
-
let mask: Data?
|
|
14
|
-
}
|
|
15
|
-
|
|
16
10
|
@objc(BluetoothLe)
|
|
17
11
|
public class BluetoothLe: CAPPlugin {
|
|
18
12
|
typealias BleDevice = [String: Any]
|
|
@@ -117,14 +111,23 @@ public class BluetoothLe: CAPPlugin {
|
|
|
117
111
|
let name = call.getString("name")
|
|
118
112
|
let namePrefix = call.getString("namePrefix")
|
|
119
113
|
let manufacturerDataFilters = self.getManufacturerDataFilters(call)
|
|
114
|
+
let serviceDataFilters = self.getServiceDataFilters(call)
|
|
115
|
+
|
|
116
|
+
let displayModeString = (call.getString("displayMode") ?? "alert").lowercased()
|
|
117
|
+
guard ["alert", "list"].contains(displayModeString) else {
|
|
118
|
+
call.reject("Invalid displayMode '\(call.getString("displayMode") ?? "")'. Use 'alert' or 'list'.")
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
let deviceListMode: DeviceListMode = displayModeString == "list" ? .list : .alert
|
|
120
122
|
|
|
121
123
|
deviceManager.startScanning(
|
|
122
124
|
serviceUUIDs,
|
|
123
125
|
name,
|
|
124
126
|
namePrefix,
|
|
125
127
|
manufacturerDataFilters,
|
|
128
|
+
serviceDataFilters,
|
|
126
129
|
false,
|
|
127
|
-
|
|
130
|
+
deviceListMode,
|
|
128
131
|
30,
|
|
129
132
|
{(success, message) in
|
|
130
133
|
if success {
|
|
@@ -151,14 +154,16 @@ public class BluetoothLe: CAPPlugin {
|
|
|
151
154
|
let namePrefix = call.getString("namePrefix")
|
|
152
155
|
let allowDuplicates = call.getBool("allowDuplicates", false)
|
|
153
156
|
let manufacturerDataFilters = self.getManufacturerDataFilters(call)
|
|
157
|
+
let serviceDataFilters = self.getServiceDataFilters(call)
|
|
154
158
|
|
|
155
159
|
deviceManager.startScanning(
|
|
156
160
|
serviceUUIDs,
|
|
157
161
|
name,
|
|
158
162
|
namePrefix,
|
|
159
163
|
manufacturerDataFilters,
|
|
164
|
+
serviceDataFilters,
|
|
160
165
|
allowDuplicates,
|
|
161
|
-
|
|
166
|
+
.none,
|
|
162
167
|
nil,
|
|
163
168
|
{ (success, message) in
|
|
164
169
|
if success {
|
|
@@ -546,13 +551,13 @@ public class BluetoothLe: CAPPlugin {
|
|
|
546
551
|
}
|
|
547
552
|
|
|
548
553
|
let dataPrefix: Data? = {
|
|
549
|
-
guard let
|
|
550
|
-
return
|
|
554
|
+
guard let prefixString = dataObject["dataPrefix"] as? String else { return nil }
|
|
555
|
+
return stringToData(prefixString)
|
|
551
556
|
}()
|
|
552
557
|
|
|
553
558
|
let mask: Data? = {
|
|
554
|
-
guard let
|
|
555
|
-
return
|
|
559
|
+
guard let maskString = dataObject["mask"] as? String else { return nil }
|
|
560
|
+
return stringToData(maskString)
|
|
556
561
|
}()
|
|
557
562
|
|
|
558
563
|
let manufacturerFilter = ManufacturerDataFilter(
|
|
@@ -567,6 +572,44 @@ public class BluetoothLe: CAPPlugin {
|
|
|
567
572
|
return manufacturerDataFilters
|
|
568
573
|
}
|
|
569
574
|
|
|
575
|
+
private func getServiceDataFilters(_ call: CAPPluginCall) -> [ServiceDataFilter]? {
|
|
576
|
+
guard let serviceDataArray = call.getArray("serviceData") else {
|
|
577
|
+
return nil
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
var serviceDataFilters: [ServiceDataFilter] = []
|
|
581
|
+
|
|
582
|
+
for index in 0..<serviceDataArray.count {
|
|
583
|
+
guard let dataObject = serviceDataArray[index] as? JSObject,
|
|
584
|
+
let serviceUuidString = dataObject["serviceUuid"] as? String else {
|
|
585
|
+
// Invalid or missing service UUID
|
|
586
|
+
return nil
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
let serviceUuid = CBUUID(string: serviceUuidString)
|
|
590
|
+
|
|
591
|
+
let dataPrefix: Data? = {
|
|
592
|
+
guard let prefixString = dataObject["dataPrefix"] as? String else { return nil }
|
|
593
|
+
return stringToData(prefixString)
|
|
594
|
+
}()
|
|
595
|
+
|
|
596
|
+
let mask: Data? = {
|
|
597
|
+
guard let maskString = dataObject["mask"] as? String else { return nil }
|
|
598
|
+
return stringToData(maskString)
|
|
599
|
+
}()
|
|
600
|
+
|
|
601
|
+
let serviceDataFilter = ServiceDataFilter(
|
|
602
|
+
serviceUuid: serviceUuid,
|
|
603
|
+
dataPrefix: dataPrefix,
|
|
604
|
+
mask: mask
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
serviceDataFilters.append(serviceDataFilter)
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return serviceDataFilters
|
|
611
|
+
}
|
|
612
|
+
|
|
570
613
|
private func getDevice(_ call: CAPPluginCall, checkConnection: Bool = true) -> Device? {
|
|
571
614
|
guard let deviceId = call.getString("deviceId") else {
|
|
572
615
|
call.reject("deviceId required.")
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreBluetooth
|
|
3
|
+
|
|
4
|
+
struct ManufacturerDataFilter {
|
|
5
|
+
let companyIdentifier: UInt16
|
|
6
|
+
let dataPrefix: Data?
|
|
7
|
+
let mask: Data?
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
struct ServiceDataFilter {
|
|
11
|
+
let serviceUuid: CBUUID
|
|
12
|
+
let dataPrefix: Data?
|
|
13
|
+
let mask: Data?
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class ScanFilterUtils {
|
|
17
|
+
|
|
18
|
+
static func passesManufacturerDataFilter(_ advertisementData: [String: Any], filters: [ManufacturerDataFilter]?) -> Bool {
|
|
19
|
+
guard let filters = filters, !filters.isEmpty else {
|
|
20
|
+
return true // No filters means everything passes
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
guard let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data,
|
|
24
|
+
manufacturerData.count >= 2 else {
|
|
25
|
+
return false // If there's no valid manufacturer data, fail
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let companyIdentifier = manufacturerData.prefix(2).withUnsafeBytes {
|
|
29
|
+
$0.load(as: UInt16.self).littleEndian // Manufacturer ID is little-endian
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let payload = Data(manufacturerData.dropFirst(2))
|
|
33
|
+
|
|
34
|
+
for filter in filters {
|
|
35
|
+
if filter.companyIdentifier != companyIdentifier {
|
|
36
|
+
continue // Skip if company ID does not match
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if let dataPrefix = filter.dataPrefix {
|
|
40
|
+
if payload.count < dataPrefix.count {
|
|
41
|
+
continue // Payload too short, does not match
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if let mask = filter.mask {
|
|
45
|
+
// Validate that mask length matches dataPrefix length
|
|
46
|
+
if mask.count != dataPrefix.count {
|
|
47
|
+
continue // Skip this filter if mask length is invalid
|
|
48
|
+
}
|
|
49
|
+
var matches = true
|
|
50
|
+
for i in 0..<dataPrefix.count {
|
|
51
|
+
if (payload[i] & mask[i]) != (dataPrefix[i] & mask[i]) {
|
|
52
|
+
matches = false
|
|
53
|
+
break
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if matches {
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
} else if payload.starts(with: dataPrefix) {
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
return true // Company ID matched, and no dataPrefix required
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false // If none matched, return false
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static func passesServiceDataFilter(_ advertisementData: [String: Any], filters: [ServiceDataFilter]?) -> Bool {
|
|
71
|
+
guard let filters = filters, !filters.isEmpty else {
|
|
72
|
+
return true // No filters means everything passes
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
guard let serviceDataDict = advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID: Data] else {
|
|
76
|
+
return false // If there's no service data, fail
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for filter in filters {
|
|
80
|
+
guard let serviceData = serviceDataDict[filter.serviceUuid] else {
|
|
81
|
+
continue // Skip if service UUID does not match
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if let dataPrefix = filter.dataPrefix {
|
|
85
|
+
if serviceData.count < dataPrefix.count {
|
|
86
|
+
continue // Service data too short, does not match
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if let mask = filter.mask {
|
|
90
|
+
// Validate that mask length matches dataPrefix length
|
|
91
|
+
if mask.count != dataPrefix.count {
|
|
92
|
+
continue // Skip this filter if mask length is invalid
|
|
93
|
+
}
|
|
94
|
+
var matches = true
|
|
95
|
+
for i in 0..<dataPrefix.count {
|
|
96
|
+
if (serviceData[i] & mask[i]) != (dataPrefix[i] & mask[i]) {
|
|
97
|
+
matches = false
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if matches {
|
|
102
|
+
return true
|
|
103
|
+
}
|
|
104
|
+
} else if serviceData.starts(with: dataPrefix) {
|
|
105
|
+
return true
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
return true // Service UUID matched, and no dataPrefix required
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return false // If none matched, return false
|
|
113
|
+
}
|
|
114
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capacitor-community/bluetooth-le",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.1",
|
|
4
4
|
"description": "Capacitor plugin for Bluetooth Low Energy ",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"prettier": "prettier \"**/*.{css,html,ts,js}\"",
|
|
20
20
|
"swiftlint": "node-swiftlint",
|
|
21
21
|
"docgen": "docgen --api BleClientInterface --output-readme README.md --output-json dist/docs.json",
|
|
22
|
-
"postdocgen": "prettier README.md --write",
|
|
22
|
+
"postdocgen": "node scripts/fix-docgen-types.js && prettier README.md --write",
|
|
23
23
|
"build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
|
|
24
24
|
"clean": "rimraf ./dist",
|
|
25
25
|
"watch": "tsc --watch",
|
|
@@ -85,7 +85,17 @@
|
|
|
85
85
|
"prettier": "@ionic/prettier-config",
|
|
86
86
|
"swiftlint": "@ionic/swiftlint-config",
|
|
87
87
|
"eslintConfig": {
|
|
88
|
-
"extends": "@ionic/eslint-config/recommended"
|
|
88
|
+
"extends": "@ionic/eslint-config/recommended",
|
|
89
|
+
"rules": {
|
|
90
|
+
"@typescript-eslint/no-unused-vars": [
|
|
91
|
+
"error",
|
|
92
|
+
{
|
|
93
|
+
"argsIgnorePattern": "^_",
|
|
94
|
+
"varsIgnorePattern": "^_",
|
|
95
|
+
"caughtErrorsIgnorePattern": "^_"
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
89
99
|
},
|
|
90
100
|
"repository": {
|
|
91
101
|
"type": "git",
|