@capacitor-community/bluetooth-le 8.0.1 → 8.0.2

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.
Files changed (52) hide show
  1. package/CapacitorCommunityBluetoothLe.podspec +17 -17
  2. package/LICENSE +21 -21
  3. package/Package.swift +27 -27
  4. package/README.md +2 -1
  5. package/android/build.gradle +73 -73
  6. package/android/src/main/AndroidManifest.xml +22 -22
  7. package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/BluetoothLe.kt +1094 -1094
  8. package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/Conversion.kt +51 -51
  9. package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/Device.kt +771 -771
  10. package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/DeviceList.kt +28 -28
  11. package/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/DeviceScanner.kt +189 -189
  12. package/dist/docs.json +43 -43
  13. package/dist/esm/bleClient.d.ts +278 -278
  14. package/dist/esm/bleClient.js +361 -361
  15. package/dist/esm/bleClient.js.map +1 -1
  16. package/dist/esm/config.d.ts +53 -53
  17. package/dist/esm/config.js +2 -2
  18. package/dist/esm/conversion.d.ts +56 -56
  19. package/dist/esm/conversion.js +134 -134
  20. package/dist/esm/conversion.js.map +1 -1
  21. package/dist/esm/definitions.d.ts +352 -352
  22. package/dist/esm/definitions.js +42 -42
  23. package/dist/esm/definitions.js.map +1 -1
  24. package/dist/esm/index.d.ts +5 -5
  25. package/dist/esm/index.js +5 -5
  26. package/dist/esm/plugin.d.ts +2 -2
  27. package/dist/esm/plugin.js +4 -4
  28. package/dist/esm/queue.d.ts +3 -3
  29. package/dist/esm/queue.js +17 -17
  30. package/dist/esm/timeout.d.ts +1 -1
  31. package/dist/esm/timeout.js +9 -9
  32. package/dist/esm/validators.d.ts +1 -1
  33. package/dist/esm/validators.js +11 -11
  34. package/dist/esm/web.d.ts +57 -57
  35. package/dist/esm/web.js +403 -403
  36. package/dist/esm/web.js.map +1 -1
  37. package/dist/plugin.cjs.js +964 -964
  38. package/dist/plugin.cjs.js.map +1 -1
  39. package/dist/plugin.js +964 -964
  40. package/dist/plugin.js.map +1 -1
  41. package/ios/Sources/BluetoothLe/Conversion.swift +83 -83
  42. package/ios/Sources/BluetoothLe/Device.swift +422 -423
  43. package/ios/Sources/BluetoothLe/DeviceListView.swift +121 -121
  44. package/ios/Sources/BluetoothLe/DeviceManager.swift +409 -415
  45. package/ios/Sources/BluetoothLe/Logging.swift +8 -8
  46. package/ios/Sources/BluetoothLe/Plugin.swift +768 -763
  47. package/ios/Sources/BluetoothLe/ScanFilters.swift +114 -114
  48. package/ios/Sources/BluetoothLe/ThreadSafeDictionary.swift +61 -15
  49. package/ios/Tests/BluetoothLeTests/ConversionTests.swift +55 -55
  50. package/ios/Tests/BluetoothLeTests/PluginTests.swift +27 -27
  51. package/ios/Tests/BluetoothLeTests/ScanFiltersTests.swift +153 -153
  52. package/package.json +114 -114
@@ -1,114 +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
- }
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
+ }
@@ -1,15 +1,61 @@
1
- import Foundation
2
-
3
- class ThreadSafeDictionary<K: Hashable, T> {
4
- private var dictionary: [K: T] = [:]
5
- private let queue = DispatchQueue(label: "threadSafeDictionaryQueue", attributes: .concurrent)
6
-
7
- subscript(key: K) -> T? {
8
- get {
9
- return queue.sync { dictionary[key] }
10
- }
11
- set {
12
- queue.async(flags: .barrier) { self.dictionary[key] = newValue }
13
- }
14
- }
15
- }
1
+ import Foundation
2
+
3
+ class ThreadSafeDictionary<K: Hashable, T> {
4
+ private var dictionary: [K: T] = [:]
5
+ private let queue = DispatchQueue(label: "threadSafeDictionaryQueue", attributes: .concurrent)
6
+
7
+ subscript(key: K) -> T? {
8
+ get {
9
+ return queue.sync { dictionary[key] }
10
+ }
11
+ set {
12
+ queue.async(flags: .barrier) { self.dictionary[key] = newValue }
13
+ }
14
+ }
15
+
16
+ func removeValue(forKey key: K) -> T? {
17
+ return queue.sync(flags: .barrier) {
18
+ return dictionary.removeValue(forKey: key)
19
+ }
20
+ }
21
+
22
+ var count: Int {
23
+ return queue.sync { dictionary.count }
24
+ }
25
+
26
+ func removeAll() {
27
+ queue.async(flags: .barrier) {
28
+ self.dictionary.removeAll()
29
+ }
30
+ }
31
+
32
+ /// Atomically gets existing value or inserts and returns new value
33
+ /// The create closure is only called if the key doesn't exist
34
+ /// Returns tuple of (value, wasInserted) where wasInserted indicates if a new value was created
35
+ func getOrInsert(key: K, create: () -> T) -> (value: T, wasInserted: Bool) {
36
+ return queue.sync(flags: .barrier) {
37
+ if let existing = dictionary[key] {
38
+ return (existing, false)
39
+ }
40
+ let newValue = create()
41
+ dictionary[key] = newValue
42
+ return (newValue, true)
43
+ }
44
+ }
45
+
46
+ /// Atomically gets existing value (calling update on it) or inserts new value
47
+ /// The create closure is only called if the key doesn't exist
48
+ /// The update closure is called on existing values before returning
49
+ /// Returns tuple of (value, wasInserted) where wasInserted indicates if a new value was created
50
+ func getOrInsert(key: K, create: () -> T, update: (T) -> Void) -> (value: T, wasInserted: Bool) {
51
+ return queue.sync(flags: .barrier) {
52
+ if let existing = dictionary[key] {
53
+ update(existing)
54
+ return (existing, false)
55
+ }
56
+ let newValue = create()
57
+ dictionary[key] = newValue
58
+ return (newValue, true)
59
+ }
60
+ }
61
+ }
@@ -1,55 +1,55 @@
1
- import Foundation
2
- import CoreBluetooth
3
- import XCTest
4
- @testable import BluetoothLe
5
-
6
- class ConversionTests: XCTestCase {
7
-
8
- func testDataToString() throws {
9
- let input = Data([0xA1, 0x2E, 0x38, 0xD4, 0x89, 0xC3])
10
- let output = dataToString(input)
11
- XCTAssertEqual(output, "a12e38d489c3")
12
- }
13
-
14
- func testStringToData() throws {
15
- let input = "a12e38d489c3"
16
- let output = stringToData(input)
17
- let expected = Data([0xA1, 0x2E, 0x38, 0xD4, 0x89, 0xC3])
18
- for (index, byte) in output.enumerated() {
19
- XCTAssertEqual(byte, expected[index])
20
- }
21
- }
22
-
23
- func testEmptyStringToData() throws {
24
- let input = ""
25
- let output = stringToData(input)
26
- XCTAssertEqual(output, Data([]))
27
- }
28
-
29
- func testCbuuidToString() throws {
30
- XCTAssertEqual(cbuuidToString(CBUUID(string: "180D")), "0000180d-0000-1000-8000-00805f9b34fb")
31
- XCTAssertEqual(cbuuidToString(CBUUID(string: "AAAA180D")), "aaaa180d-0000-1000-8000-00805f9b34fb")
32
- XCTAssertEqual(cbuuidToString(CBUUID(string: "fb005c80-02e7-f387-1cad-8acd2d8df0c8")), "fb005c80-02e7-f387-1cad-8acd2d8df0c8")
33
- }
34
-
35
- func testCbuuidToStringUppercase() throws {
36
- XCTAssertEqual(cbuuidToStringUppercase(CBUUID(string: "180D")), "0000180D-0000-1000-8000-00805F9B34FB")
37
- XCTAssertEqual(cbuuidToStringUppercase(CBUUID(string: "fb005c80-02e7-f387-1cad-8acd2d8df0c8")), "FB005C80-02E7-F387-1CAD-8ACD2D8DF0C8")
38
- }
39
-
40
- func testOptionalStringConversion() throws {
41
- let str: String? = "180D"
42
- XCTAssertEqual("\(str)", "Optional(\"180D\")")
43
- XCTAssertEqual("\(str!)", "180D")
44
- }
45
-
46
- func testDescriptorValueToString() throws {
47
- XCTAssertEqual(descriptorValueToString("Hello"), "48656c6c6f")
48
- XCTAssertEqual(descriptorValueToString(Data([0, 5, 255])), "0005ff")
49
- XCTAssertEqual(descriptorValueToString(UInt16(258)), "0201")
50
- XCTAssertEqual(descriptorValueToString(UInt16(1)), "0100")
51
- XCTAssertEqual(descriptorValueToString(NSNumber(1)), "0100")
52
- XCTAssertEqual(descriptorValueToString(0), "")
53
- }
54
-
55
- }
1
+ import Foundation
2
+ import CoreBluetooth
3
+ import XCTest
4
+ @testable import BluetoothLe
5
+
6
+ class ConversionTests: XCTestCase {
7
+
8
+ func testDataToString() throws {
9
+ let input = Data([0xA1, 0x2E, 0x38, 0xD4, 0x89, 0xC3])
10
+ let output = dataToString(input)
11
+ XCTAssertEqual(output, "a12e38d489c3")
12
+ }
13
+
14
+ func testStringToData() throws {
15
+ let input = "a12e38d489c3"
16
+ let output = stringToData(input)
17
+ let expected = Data([0xA1, 0x2E, 0x38, 0xD4, 0x89, 0xC3])
18
+ for (index, byte) in output.enumerated() {
19
+ XCTAssertEqual(byte, expected[index])
20
+ }
21
+ }
22
+
23
+ func testEmptyStringToData() throws {
24
+ let input = ""
25
+ let output = stringToData(input)
26
+ XCTAssertEqual(output, Data([]))
27
+ }
28
+
29
+ func testCbuuidToString() throws {
30
+ XCTAssertEqual(cbuuidToString(CBUUID(string: "180D")), "0000180d-0000-1000-8000-00805f9b34fb")
31
+ XCTAssertEqual(cbuuidToString(CBUUID(string: "AAAA180D")), "aaaa180d-0000-1000-8000-00805f9b34fb")
32
+ XCTAssertEqual(cbuuidToString(CBUUID(string: "fb005c80-02e7-f387-1cad-8acd2d8df0c8")), "fb005c80-02e7-f387-1cad-8acd2d8df0c8")
33
+ }
34
+
35
+ func testCbuuidToStringUppercase() throws {
36
+ XCTAssertEqual(cbuuidToStringUppercase(CBUUID(string: "180D")), "0000180D-0000-1000-8000-00805F9B34FB")
37
+ XCTAssertEqual(cbuuidToStringUppercase(CBUUID(string: "fb005c80-02e7-f387-1cad-8acd2d8df0c8")), "FB005C80-02E7-F387-1CAD-8ACD2D8DF0C8")
38
+ }
39
+
40
+ func testOptionalStringConversion() throws {
41
+ let str: String? = "180D"
42
+ XCTAssertEqual("\(str)", "Optional(\"180D\")")
43
+ XCTAssertEqual("\(str!)", "180D")
44
+ }
45
+
46
+ func testDescriptorValueToString() throws {
47
+ XCTAssertEqual(descriptorValueToString("Hello"), "48656c6c6f")
48
+ XCTAssertEqual(descriptorValueToString(Data([0, 5, 255])), "0005ff")
49
+ XCTAssertEqual(descriptorValueToString(UInt16(258)), "0201")
50
+ XCTAssertEqual(descriptorValueToString(UInt16(1)), "0100")
51
+ XCTAssertEqual(descriptorValueToString(NSNumber(1)), "0100")
52
+ XCTAssertEqual(descriptorValueToString(0), "")
53
+ }
54
+
55
+ }
@@ -1,27 +1,27 @@
1
- import XCTest
2
- import Capacitor
3
- @testable import BluetoothLe
4
-
5
- class PluginTests: XCTestCase {
6
-
7
- func testEcho() {
8
- // This is an example of a functional test case for a plugin.
9
- // Use XCTAssert and related functions to verify your tests produce the correct results.
10
-
11
- let value = "Hello, World!"
12
- XCTAssertEqual(1, 1)
13
-
14
- // let plugin = MyPlugin()
15
- //
16
- // let call = CAPPluginCall(callbackId: "test", options: [
17
- // "value": value
18
- // ], success: { (result, _) in
19
- // let resultValue = result!.data["value"] as? String
20
- // XCTAssertEqual(value, resultValue)
21
- // }, error: { (_) in
22
- // XCTFail("Error shouldn't have been called")
23
- // })
24
- //
25
- // plugin.echo(call!)
26
- }
27
- }
1
+ import XCTest
2
+ import Capacitor
3
+ @testable import BluetoothLe
4
+
5
+ class PluginTests: XCTestCase {
6
+
7
+ func testEcho() {
8
+ // This is an example of a functional test case for a plugin.
9
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
10
+
11
+ let value = "Hello, World!"
12
+ XCTAssertEqual(1, 1)
13
+
14
+ // let plugin = MyPlugin()
15
+ //
16
+ // let call = CAPPluginCall(callbackId: "test", options: [
17
+ // "value": value
18
+ // ], success: { (result, _) in
19
+ // let resultValue = result!.data["value"] as? String
20
+ // XCTAssertEqual(value, resultValue)
21
+ // }, error: { (_) in
22
+ // XCTFail("Error shouldn't have been called")
23
+ // })
24
+ //
25
+ // plugin.echo(call!)
26
+ }
27
+ }