@wisdomgarden/capacitor-plugin-beacon 0.0.1 → 0.0.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/CHANGE-LOG.md +13 -0
- package/android/src/main/java/com/wisdomgarden/mobile/beacon/Beacon.java +100 -58
- package/android/src/main/java/com/wisdomgarden/mobile/beacon/BeaconUtils.java +110 -13
- package/ios/Plugin/BeaconUtils.swift +108 -40
- package/ios/Plugin/Plugin.swift +109 -128
- package/package.json +9 -3
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +0 -5
- package/android/gradle.properties +0 -24
- package/android/gradlew +0 -188
- package/android/gradlew.bat +0 -100
- package/android/proguard-rules.pro +0 -21
- package/android/settings.gradle +0 -2
- package/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java +0 -26
- package/android/src/test/java/com/getcapacitor/ExampleUnitTest.java +0 -18
- package/ios/Plugin.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
- package/ios/Plugin.xcworkspace/xcuserdata/peixinliu.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/Pods/Pods.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/Capacitor.xcscheme +0 -58
- package/ios/Pods/Pods.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/CapacitorCordova.xcscheme +0 -58
- package/ios/Pods/Pods.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/Pods-Plugin.xcscheme +0 -58
- package/ios/Pods/Pods.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/Pods-PluginTests.xcscheme +0 -58
- package/ios/Pods/Pods.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/xcschememanagement.plist +0 -39
|
@@ -1,18 +1,44 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
|
|
3
3
|
public struct BeaconUtils {
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* 16-bit Bluetooth Short UUID (0x5747)
|
|
6
|
+
* Logic & Rationale:
|
|
7
|
+
* 1. Represents the hex characters for 'WG' (W=0x57, G=0x47).
|
|
8
|
+
* 2. CoreBluetooth automatically recognizes 4-character hex strings as 16-bit CBUUIDs.
|
|
9
|
+
* 3. This ID maps directly to the Android side's full UUID string
|
|
10
|
+
* (00005747-0000-1000-8000-00805F9B34FB), allowing precise cross-platform
|
|
11
|
+
* filtering and scanning.
|
|
12
|
+
*/
|
|
13
|
+
public static let WG_UUID = "5747"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Identification prefix for string-based payloads (Device Name).
|
|
18
|
+
* This prefix is used by iOS when advertising the message via the Device Name field.
|
|
19
|
+
* Both platforms use this string to identify and validate Wisdom Garden
|
|
20
|
+
* peripherals during the scanning process.
|
|
21
|
+
*/
|
|
22
|
+
public static let WG_PREFIX = "WG"
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Hexadecimal representation of "WG" (0x57, 0x47).
|
|
26
|
+
* This prefix is specifically designed for Android's ManufacturerData field.
|
|
27
|
+
* It serves as a protocol identifier within the raw byte payload to
|
|
28
|
+
* distinguish our packets from other 0xFFFF manufacturer data.
|
|
29
|
+
*/
|
|
30
|
+
public static let WG_PREFIX_BYTES: [UInt8] = [0x57, 0x47] // "WG"
|
|
31
|
+
|
|
5
32
|
public static let message_max_length = 11
|
|
6
33
|
|
|
7
34
|
private static let chars =
|
|
8
|
-
|
|
35
|
+
"2O9AuFNPDx4gtJwS3ye7l1Mq0dEB5HsaKInikRmLhjpG6b8fzQrCcvo"
|
|
9
36
|
private static let radix = chars.count
|
|
10
37
|
|
|
11
38
|
public static func encode(_ num: Int) -> String {
|
|
12
|
-
if num
|
|
39
|
+
if num <= 0 { return String(chars[chars.startIndex]) }
|
|
13
40
|
var result = ""
|
|
14
|
-
|
|
15
|
-
var n = abs(num)
|
|
41
|
+
var n = num
|
|
16
42
|
|
|
17
43
|
while n > 0 {
|
|
18
44
|
let remainder = n % radix
|
|
@@ -21,7 +47,7 @@ public struct BeaconUtils {
|
|
|
21
47
|
n /= radix
|
|
22
48
|
}
|
|
23
49
|
|
|
24
|
-
return
|
|
50
|
+
return result
|
|
25
51
|
}
|
|
26
52
|
|
|
27
53
|
public static func decode(_ str: String) -> Int {
|
|
@@ -29,8 +55,8 @@ public struct BeaconUtils {
|
|
|
29
55
|
for char in str {
|
|
30
56
|
if let index = chars.firstIndex(of: char) {
|
|
31
57
|
num =
|
|
32
|
-
|
|
33
|
-
|
|
58
|
+
num * radix
|
|
59
|
+
+ chars.distance(from: chars.startIndex, to: index)
|
|
34
60
|
} else {
|
|
35
61
|
return 0
|
|
36
62
|
}
|
|
@@ -38,46 +64,29 @@ public struct BeaconUtils {
|
|
|
38
64
|
return num
|
|
39
65
|
}
|
|
40
66
|
|
|
67
|
+
// 检查字符是否为 nonce 字母,T-Z
|
|
68
|
+
func isNonceLetter(_ c: Character) -> Bool {
|
|
69
|
+
return c >= "T" && c <= "Z"
|
|
70
|
+
}
|
|
71
|
+
|
|
41
72
|
public static func parseMessage(_ message: String?) -> [String: Any] {
|
|
42
73
|
var payload: [String: Any] = [
|
|
43
74
|
"rollcallId": 0,
|
|
44
75
|
"nonce": "",
|
|
45
76
|
]
|
|
46
77
|
|
|
47
|
-
guard let message = message else {
|
|
78
|
+
guard let message = message, !message.isEmpty else {
|
|
48
79
|
return payload
|
|
49
80
|
}
|
|
50
81
|
|
|
51
|
-
if message.count < message_max_length {
|
|
52
|
-
return payload
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// 检查字符是否为 nonce 字母,T-Z
|
|
56
|
-
func isNonceLetter(_ c: Character) -> Bool {
|
|
57
|
-
let code = c.asciiValue ?? 0
|
|
58
|
-
return code >= 84 && code <= 90
|
|
59
|
-
}
|
|
60
|
-
|
|
61
82
|
for (i, char) in message.enumerated() {
|
|
62
83
|
if isNonceLetter(char) {
|
|
63
|
-
let
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
message.startIndex,
|
|
67
|
-
offsetBy: i
|
|
68
|
-
)
|
|
69
|
-
]
|
|
70
|
-
)
|
|
84
|
+
let splitIndex = message.index(message.startIndex, offsetBy: i)
|
|
85
|
+
|
|
86
|
+
let rollcallIdStr = String(message[..<splitIndex])
|
|
71
87
|
payload["rollcallId"] = decode(rollcallIdStr)
|
|
72
|
-
|
|
73
|
-
let nonceStr = String(
|
|
74
|
-
message[
|
|
75
|
-
message.index(
|
|
76
|
-
message.startIndex,
|
|
77
|
-
offsetBy: i
|
|
78
|
-
)...
|
|
79
|
-
]
|
|
80
|
-
)
|
|
88
|
+
|
|
89
|
+
let nonceStr = String(message[splitIndex...])
|
|
81
90
|
payload["nonce"] = nonceStr
|
|
82
91
|
break
|
|
83
92
|
}
|
|
@@ -90,14 +99,73 @@ public struct BeaconUtils {
|
|
|
90
99
|
public static func buildMessage(rollcallId: Int, nonce: String) -> String {
|
|
91
100
|
let rollcallIdStr = encode(rollcallId)
|
|
92
101
|
|
|
93
|
-
guard !nonce.isEmpty
|
|
102
|
+
guard !nonce.isEmpty else {
|
|
94
103
|
return rollcallIdStr + "Z"
|
|
95
104
|
}
|
|
96
105
|
|
|
97
|
-
let remainingChars = String(nonce.dropFirst())
|
|
98
|
-
|
|
99
106
|
let message = rollcallIdStr + nonce
|
|
100
107
|
return message.count > message_max_length
|
|
101
|
-
|
|
108
|
+
? String(message.prefix(message_max_length)) : message
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Prepends the protocol prefix "WG" to the message string.
|
|
114
|
+
* This is used for iOS advertising, where the identifier is placed in the
|
|
115
|
+
* Local Name field of the Scan Response to support cross-platform scanning.
|
|
116
|
+
*/
|
|
117
|
+
public static func addPrefixToString(message: String) -> String {
|
|
118
|
+
return WG_PREFIX + message
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Extracts and validates the message from raw Manufacturer Data bytes.
|
|
124
|
+
* The method verifies the "WG" hexadecimal prefix and ensures the total
|
|
125
|
+
* length matches the protocol specification before returning the decoded string.
|
|
126
|
+
*/
|
|
127
|
+
public static func extractMessageFromBytes(_ data: Data) -> String? {
|
|
128
|
+
// Android Logic: CompanyID(2 bytes) + (Prefix)2 bytes + Msg(11 bytes) = 15
|
|
129
|
+
let minLength = 2 + WG_PREFIX_BYTES.count // 4 bytes
|
|
130
|
+
let maxLength = minLength + message_max_length // 15 bytes
|
|
131
|
+
|
|
132
|
+
guard data.count >= minLength && data.count <= maxLength else {
|
|
133
|
+
print("[extractBytes] Length out of bounds: \(data.count)")
|
|
134
|
+
return nil
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let bytes = [UInt8](data)
|
|
138
|
+
// Check "WG" magic bytes at index 2 and 3 (after 0xFFFF Company ID)
|
|
139
|
+
guard bytes[2] == WG_PREFIX_BYTES[0], bytes[3] == WG_PREFIX_BYTES[1] else {
|
|
140
|
+
return nil
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let messageData = data.subdata(in: 4..<data.count)
|
|
144
|
+
if let extracted = String(data: messageData, encoding: .ascii) {
|
|
145
|
+
print("[extractBytes] Successfully: \(extracted)")
|
|
146
|
+
return extracted
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
print("[extractBytes] ASCII error")
|
|
150
|
+
return nil
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Extracts the message payload from a scanned Device Name string.
|
|
155
|
+
* Specifically designed for iOS-to-Android or iOS-to-iOS communication,
|
|
156
|
+
* where the message is prefixed with "WG" and advertised as the device's name.
|
|
157
|
+
*/
|
|
158
|
+
public static func extractMessageFromDeviceName(_ message: String) -> String? {
|
|
159
|
+
guard message.hasPrefix(WG_PREFIX) else { return nil }
|
|
160
|
+
|
|
161
|
+
let maxFullLength = WG_PREFIX.count + message_max_length
|
|
162
|
+
if message.count <= maxFullLength {
|
|
163
|
+
let extracted = String(message.dropFirst(WG_PREFIX.count))
|
|
164
|
+
print("[extractName] Successfully: \(extracted)")
|
|
165
|
+
return extracted
|
|
166
|
+
} else {
|
|
167
|
+
print("[extractName] Too long: \(message.count)")
|
|
168
|
+
return nil
|
|
169
|
+
}
|
|
102
170
|
}
|
|
103
171
|
}
|
package/ios/Plugin/Plugin.swift
CHANGED
|
@@ -9,6 +9,8 @@ import Foundation
|
|
|
9
9
|
public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
10
10
|
CBCentralManagerDelegate
|
|
11
11
|
{
|
|
12
|
+
private static let TAG = "WisdomGardenBeacon"
|
|
13
|
+
|
|
12
14
|
private var peripheralManager: CBPeripheralManager?
|
|
13
15
|
private var centralManager: CBCentralManager?
|
|
14
16
|
|
|
@@ -20,10 +22,17 @@ public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
|
20
22
|
|
|
21
23
|
private var allowDuplicatesKey = true
|
|
22
24
|
|
|
23
|
-
private var rollcallId = 0
|
|
24
|
-
private var nonce = ""
|
|
25
25
|
private var message = ""
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Helper to send tagged logs to JS bridge with sequence prefix and print to Xcode console.
|
|
29
|
+
*/
|
|
30
|
+
private func logToJsWithTag(_ seq: String, _ msg: String) {
|
|
31
|
+
let formattedMessage = "\(seq): \(msg)"
|
|
32
|
+
print("[\(Beacon.TAG)] \(formattedMessage)")
|
|
33
|
+
super.bridge.logToJs("[\(Beacon.TAG)] \(formattedMessage)")
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
@objc func parseMessage(_ call: CAPPluginCall) {
|
|
28
37
|
let message = call.getString("message")
|
|
29
38
|
let result = BeaconUtils.parseMessage(message)
|
|
@@ -32,147 +41,123 @@ public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
|
32
41
|
|
|
33
42
|
@objc func initialize(_ call: CAPPluginCall) {
|
|
34
43
|
allowDuplicatesKey = call.getBool("allowDuplicatesKey") ?? true
|
|
44
|
+
logToJsWithTag("[SEQ-00]", "Plugin Initialized. allowDuplicates: \(allowDuplicatesKey)")
|
|
35
45
|
call.resolve()
|
|
36
46
|
}
|
|
37
47
|
|
|
38
|
-
|
|
39
|
-
super.bridge.logToJs("[Debug] startAdvertising 12")
|
|
40
|
-
if peripheralManager == nil {
|
|
41
|
-
return
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if isBroadcasting == true {
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
super.bridge.logToJs("[Debug] startAdvertising broadcasting 13")
|
|
48
|
-
isBroadcasting = true
|
|
49
|
-
|
|
50
|
-
let advertisementData: [String: Any] = [
|
|
51
|
-
CBAdvertisementDataServiceUUIDsKey: [CBUUID(string: BeaconUtils.WG_UUID)],
|
|
52
|
-
CBAdvertisementDataLocalNameKey: message,
|
|
53
|
-
]
|
|
54
|
-
|
|
55
|
-
super.bridge.logToJs("[Debug] startAdvertising broadcasting 14")
|
|
56
|
-
peripheralManager?.startAdvertising(advertisementData)
|
|
57
|
-
super.bridge.logToJs("[Debug] startAdvertising broadcasting 15")
|
|
58
|
-
}
|
|
48
|
+
// MARK: - Broadcasting (Peripheral)
|
|
59
49
|
|
|
60
50
|
@objc func startBroadcasting(_ call: CAPPluginCall) {
|
|
61
|
-
|
|
51
|
+
logToJsWithTag("[SEQ-10]", "startBroadcasting invoked")
|
|
62
52
|
|
|
63
|
-
if let
|
|
64
|
-
message
|
|
65
|
-
{
|
|
66
|
-
self.message = message
|
|
53
|
+
if let inputMsg = call.getString("message"), inputMsg.count == BeaconUtils.message_max_length {
|
|
54
|
+
self.message = inputMsg
|
|
67
55
|
} else {
|
|
68
|
-
guard let
|
|
69
|
-
|
|
56
|
+
guard let rId = call.getInt("rollcallId"), let nce = call.getString("nonce") else {
|
|
57
|
+
logToJsWithTag("[ERR]", "Missing rollcallId or nonce")
|
|
58
|
+
call.reject("rollcallId and nonce are required")
|
|
70
59
|
return
|
|
71
60
|
}
|
|
72
|
-
|
|
73
|
-
call.reject("nonce is required")
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
self.rollcallId = rollcallId
|
|
78
|
-
self.nonce = nonce
|
|
79
|
-
|
|
80
|
-
self.message = BeaconUtils.buildMessage(rollcallId: rollcallId, nonce: nonce)
|
|
61
|
+
self.message = BeaconUtils.buildMessage(rollcallId: rId, nonce: nce)
|
|
81
62
|
}
|
|
82
63
|
|
|
83
|
-
|
|
64
|
+
logToJsWithTag("[SEQ-11]", "Payload prepared: \(self.message)")
|
|
84
65
|
|
|
85
66
|
if isBroadcasting {
|
|
67
|
+
logToJsWithTag("[SEQ-12]", "Already broadcasting, skipping")
|
|
86
68
|
call.resolve()
|
|
87
69
|
return
|
|
88
70
|
}
|
|
89
|
-
super.bridge.logToJs("[Debug] startBroadcasting 11.2")
|
|
90
71
|
|
|
91
72
|
needBroadcasting = true
|
|
92
73
|
|
|
93
74
|
if peripheralManager == nil {
|
|
94
|
-
peripheralManager = CBPeripheralManager(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
)
|
|
98
|
-
super.bridge.logToJs("[Debug] startBroadcasting 11.2.1")
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if peripheralManager?.state == .poweredOn {
|
|
75
|
+
peripheralManager = CBPeripheralManager(delegate: self,queue: nil)
|
|
76
|
+
logToJsWithTag("[SEQ-13]", "Initializing Peripheral Manager")
|
|
77
|
+
}else if peripheralManager?.state == .poweredOn {
|
|
102
78
|
self.startAdvertising()
|
|
103
|
-
super.bridge.logToJs("[Debug] startBroadcasting 11.2.2")
|
|
104
79
|
}
|
|
105
|
-
|
|
80
|
+
|
|
81
|
+
logToJsWithTag("[SEQ-16]", "startBroadcasting logic completed")
|
|
106
82
|
|
|
107
83
|
call.resolve()
|
|
108
84
|
}
|
|
109
85
|
|
|
86
|
+
|
|
87
|
+
func startAdvertising() {
|
|
88
|
+
guard let pManager = peripheralManager, pManager.state == .poweredOn else { return }
|
|
89
|
+
if isBroadcasting { return }
|
|
90
|
+
|
|
91
|
+
isBroadcasting = true
|
|
92
|
+
|
|
93
|
+
// iOS Strategy: Use LocalName for the message to support background and cross-platform scanning
|
|
94
|
+
let prefixedMessage = BeaconUtils.addPrefixToString(message: self.message)
|
|
95
|
+
let advertisementData: [String: Any] = [
|
|
96
|
+
CBAdvertisementDataServiceUUIDsKey: [CBUUID(string: BeaconUtils.WG_UUID)],
|
|
97
|
+
CBAdvertisementDataLocalNameKey: prefixedMessage,
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
logToJsWithTag("[SEQ-14]", "Calling peripheralManager.startAdvertising with name: \(prefixedMessage)")
|
|
101
|
+
peripheralManager?.startAdvertising(advertisementData)
|
|
102
|
+
}
|
|
103
|
+
|
|
110
104
|
@objc func stopBroadcasting(_ call: CAPPluginCall) {
|
|
105
|
+
logToJsWithTag("[SEQ-20]", "stopBroadcasting called")
|
|
111
106
|
peripheralManager?.stopAdvertising()
|
|
112
107
|
isBroadcasting = false
|
|
113
108
|
needBroadcasting = false
|
|
114
109
|
call.resolve()
|
|
115
110
|
}
|
|
116
111
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
// MARK: - Monitoring (Central)
|
|
113
|
+
@objc func startMonitoring(_ call: CAPPluginCall) {
|
|
114
|
+
logToJsWithTag("[SEQ-30]", "startMonitoring invoked")
|
|
115
|
+
if isMonitoring {
|
|
116
|
+
logToJsWithTag("[SEQ-31]", "Already monitoring, skipping")
|
|
117
|
+
call.resolve()
|
|
120
118
|
return
|
|
121
119
|
}
|
|
120
|
+
needMonitoring = true
|
|
122
121
|
|
|
123
|
-
if
|
|
124
|
-
|
|
122
|
+
if centralManager == nil {
|
|
123
|
+
centralManager = CBCentralManager(delegate: self, queue: nil)
|
|
124
|
+
logToJsWithTag("[SEQ-32]", "Initializing Central Manager")
|
|
125
|
+
}else if centralManager?.state == .poweredOn {
|
|
126
|
+
self.scanForPeripherals()
|
|
125
127
|
}
|
|
128
|
+
logToJsWithTag("[SEQ-36]", "startMonitoring logic completed")
|
|
129
|
+
|
|
130
|
+
call.resolve()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
func scanForPeripherals() {
|
|
134
|
+
guard let cManager = centralManager, cManager.state == .poweredOn else { return }
|
|
135
|
+
if isMonitoring { return }
|
|
126
136
|
super.bridge.logToJs("[Debug] scanForPeripherals monitoring 23")
|
|
127
137
|
isMonitoring = true
|
|
128
138
|
|
|
129
139
|
let options: [String: Any] = [
|
|
130
140
|
CBCentralManagerScanOptionAllowDuplicatesKey: allowDuplicatesKey,
|
|
131
|
-
CBCentralManagerScanOptionSolicitedServiceUUIDsKey: [
|
|
132
|
-
CBUUID(string: BeaconUtils.WG_UUID)
|
|
133
|
-
],
|
|
134
141
|
]
|
|
135
|
-
|
|
142
|
+
logToJsWithTag("[SEQ-33]", "Calling centralManager.scanForPeripherals")
|
|
136
143
|
|
|
137
144
|
centralManager?.scanForPeripherals(
|
|
138
145
|
withServices: [CBUUID(string: BeaconUtils.WG_UUID)],
|
|
139
146
|
options: options
|
|
140
147
|
)
|
|
141
|
-
super.bridge.logToJs("[Debug] scanForPeripherals monitoring 25")
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
@objc func startMonitoring(_ call: CAPPluginCall) {
|
|
145
|
-
super.bridge.logToJs("[Debug] startMonitoring 21")
|
|
146
|
-
if isMonitoring {
|
|
147
|
-
call.resolve()
|
|
148
|
-
return
|
|
149
|
-
}
|
|
150
|
-
needMonitoring = true
|
|
151
|
-
super.bridge.logToJs("[Debug] startMonitoring 21.1")
|
|
152
|
-
|
|
153
|
-
if centralManager == nil {
|
|
154
|
-
centralManager = CBCentralManager(delegate: self, queue: nil)
|
|
155
|
-
super.bridge.logToJs("[Debug] startMonitoring 21.1.1")
|
|
156
|
-
}
|
|
157
|
-
super.bridge.logToJs("[Debug] startMonitoring 21.2")
|
|
158
|
-
|
|
159
|
-
if centralManager?.state == .poweredOn {
|
|
160
|
-
super.bridge.logToJs("[Debug] startMonitoring 21.2.1")
|
|
161
|
-
self.scanForPeripherals()
|
|
162
|
-
}
|
|
163
|
-
super.bridge.logToJs("[Debug] startMonitoring 21.3")
|
|
164
|
-
|
|
165
|
-
call.resolve()
|
|
166
148
|
}
|
|
167
149
|
|
|
168
150
|
@objc func stopMonitoring(_ call: CAPPluginCall) {
|
|
151
|
+
logToJsWithTag("[SEQ-40]", "stopMonitoring called")
|
|
169
152
|
centralManager?.stopScan()
|
|
170
153
|
isMonitoring = false
|
|
171
154
|
needMonitoring = false
|
|
172
155
|
call.resolve()
|
|
173
156
|
}
|
|
174
157
|
|
|
158
|
+
// MARK: - Lifecycle
|
|
175
159
|
@objc func cleanup(_ call: CAPPluginCall) {
|
|
160
|
+
logToJsWithTag("[SEQ-90]", "Cleanup: Resetting all states")
|
|
176
161
|
peripheralManager?.stopAdvertising()
|
|
177
162
|
centralManager?.stopScan()
|
|
178
163
|
|
|
@@ -191,39 +176,29 @@ public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
|
191
176
|
public func peripheralManagerDidUpdateState(
|
|
192
177
|
_ peripheral: CBPeripheralManager
|
|
193
178
|
) {
|
|
194
|
-
|
|
195
|
-
"[Beacon] Peripheral manager state changed to: \(peripheral.state.rawValue)"
|
|
196
|
-
)
|
|
179
|
+
logToJsWithTag("[EVT]", "Peripheral State: \(peripheral.state.rawValue)")
|
|
197
180
|
|
|
198
181
|
switch peripheral.state {
|
|
199
182
|
case .poweredOn:
|
|
200
|
-
|
|
201
|
-
notifyListeners(
|
|
202
|
-
"stateUpdated",
|
|
203
|
-
data: ["type": "peripheral", "state": "poweredOn"]
|
|
204
|
-
)
|
|
183
|
+
logToJsWithTag("[SEQ-15]", "Peripheral powered on")
|
|
184
|
+
notifyListeners("stateUpdated",data: ["type": "peripheral", "state": "poweredOn"])
|
|
205
185
|
case .poweredOff:
|
|
206
|
-
|
|
207
|
-
notifyListeners(
|
|
208
|
-
"stateUpdated",
|
|
209
|
-
data: ["type": "peripheral", "state": "poweredOff"]
|
|
210
|
-
)
|
|
186
|
+
logToJsWithTag("[SEQ-15]", "Peripheral powered off")
|
|
187
|
+
notifyListeners("stateUpdated",data: ["type": "peripheral", "state": "poweredOff"])
|
|
211
188
|
case .unauthorized:
|
|
212
|
-
|
|
189
|
+
logToJsWithTag("[SEQ-15]", "Peripheral manager is unauthorized")
|
|
213
190
|
notifyListeners(
|
|
214
191
|
"stateUpdated",
|
|
215
192
|
data: ["type": "peripheral", "state": "unauthorized"]
|
|
216
193
|
)
|
|
217
194
|
case .unsupported:
|
|
218
|
-
|
|
195
|
+
logToJsWithTag("[SEQ-15]", "Peripheral manager is unsupported")
|
|
219
196
|
notifyListeners(
|
|
220
197
|
"stateUpdated",
|
|
221
198
|
data: ["type": "peripheral", "state": "unsupported"]
|
|
222
199
|
)
|
|
223
200
|
default:
|
|
224
|
-
|
|
225
|
-
"[Beacon] Peripheral manager state: \(peripheral.state.rawValue)"
|
|
226
|
-
)
|
|
201
|
+
logToJsWithTag("[SEQ-15]", "Peripheral manager state: \(peripheral.state.rawValue)")
|
|
227
202
|
notifyListeners(
|
|
228
203
|
"stateUpdated",
|
|
229
204
|
data: [
|
|
@@ -234,14 +209,10 @@ public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
|
234
209
|
|
|
235
210
|
if needBroadcasting {
|
|
236
211
|
if peripheral.state == .poweredOn {
|
|
237
|
-
|
|
238
|
-
"[Debug] peripheralManagerDidUpdateState needBroadcasting poweredOn"
|
|
239
|
-
)
|
|
212
|
+
logToJsWithTag("[SEQ-15-1]","peripheralManagerDidUpdateState needBroadcasting poweredOn")
|
|
240
213
|
self.startAdvertising()
|
|
241
214
|
} else {
|
|
242
|
-
|
|
243
|
-
"[Debug] peripheralManagerDidUpdateState needBroadcasting but stopAdvertising"
|
|
244
|
-
)
|
|
215
|
+
logToJsWithTag("[SEQ-15-2]","peripheralManagerDidUpdateState needBroadcasting but stopAdvertising")
|
|
245
216
|
peripheralManager?.stopAdvertising()
|
|
246
217
|
isBroadcasting = false
|
|
247
218
|
}
|
|
@@ -251,23 +222,21 @@ public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
|
251
222
|
// MARK: - CBCentralManagerDelegate
|
|
252
223
|
|
|
253
224
|
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
254
|
-
|
|
255
|
-
"[Beacon] Central manager state changed to: \(central.state.rawValue)"
|
|
256
|
-
)
|
|
225
|
+
logToJsWithTag("[EVT]", "Central State: \(central.state.rawValue)")
|
|
257
226
|
|
|
258
227
|
switch central.state {
|
|
259
228
|
case .poweredOn:
|
|
260
|
-
|
|
229
|
+
logToJsWithTag("[SEQ-34]", "Central manager is powered on")
|
|
261
230
|
notifyListeners("bluetoothStateChanged", data: ["enabled": true])
|
|
262
231
|
case .poweredOff:
|
|
263
|
-
|
|
232
|
+
logToJsWithTag("[SEQ-34]", "Central manager is powered off")
|
|
264
233
|
notifyListeners(
|
|
265
234
|
"stateError",
|
|
266
235
|
data: ["type": "central", "state": "poweredOff"]
|
|
267
236
|
)
|
|
268
237
|
notifyListeners("bluetoothStateChanged", data: ["enabled": false])
|
|
269
238
|
case .unauthorized:
|
|
270
|
-
|
|
239
|
+
logToJsWithTag("[SEQ-34]", "Central manager is unauthorized")
|
|
271
240
|
notifyListeners(
|
|
272
241
|
"stateError",
|
|
273
242
|
data: ["type": "central", "state": "unauthorized"]
|
|
@@ -277,7 +246,7 @@ public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
|
277
246
|
data: ["enabled": false, "error": "unauthorized"]
|
|
278
247
|
)
|
|
279
248
|
case .unsupported:
|
|
280
|
-
|
|
249
|
+
logToJsWithTag("[SEQ-34]", "Central manager is unsupported")
|
|
281
250
|
notifyListeners(
|
|
282
251
|
"stateError",
|
|
283
252
|
data: ["type": "central", "state": "unsupported"]
|
|
@@ -287,9 +256,7 @@ public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
|
287
256
|
data: ["enabled": false, "error": "unsupported"]
|
|
288
257
|
)
|
|
289
258
|
default:
|
|
290
|
-
|
|
291
|
-
"[Beacon] Central manager state: \(central.state.rawValue)"
|
|
292
|
-
)
|
|
259
|
+
logToJsWithTag("[SEQ-34]", "Central manager state: \(central.state.rawValue)")
|
|
293
260
|
notifyListeners(
|
|
294
261
|
"stateError",
|
|
295
262
|
data: ["type": "central", "state": central.state.rawValue]
|
|
@@ -302,14 +269,10 @@ public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
|
302
269
|
|
|
303
270
|
if needMonitoring {
|
|
304
271
|
if central.state == .poweredOn {
|
|
305
|
-
|
|
306
|
-
"[Debug] centralManagerDidUpdateState needMonitoring poweredOn"
|
|
307
|
-
)
|
|
272
|
+
logToJsWithTag("[SEQ-34-1]", "centralManagerDidUpdateState needMonitoring poweredOn")
|
|
308
273
|
self.scanForPeripherals()
|
|
309
274
|
} else {
|
|
310
|
-
|
|
311
|
-
"[Debug] centralManagerDidUpdateState needMonitoring but stopScan"
|
|
312
|
-
)
|
|
275
|
+
logToJsWithTag("[SEQ-34-1]", "centralManagerDidUpdateState needMonitoring but stopScan")
|
|
313
276
|
centralManager?.stopScan()
|
|
314
277
|
isMonitoring = false
|
|
315
278
|
}
|
|
@@ -322,9 +285,25 @@ public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
|
322
285
|
advertisementData: [String: Any],
|
|
323
286
|
rssi RSSI: NSNumber
|
|
324
287
|
) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
288
|
+
|
|
289
|
+
var finalMessage: String?
|
|
290
|
+
|
|
291
|
+
// Strategy 1: Primary - Extract from Manufacturer Data (Android-style packets)
|
|
292
|
+
if let vendorData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data {
|
|
293
|
+
finalMessage = BeaconUtils.extractMessageFromBytes(vendorData)
|
|
294
|
+
} else {
|
|
295
|
+
logToJsWithTag("[SEQ-51]", "No valid message found in manufacturer data key")
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Strategy 2: Fallback - Extract from LocalName (iOS-style packets)
|
|
299
|
+
if finalMessage == nil,let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
|
|
300
|
+
finalMessage = BeaconUtils.extractMessageFromDeviceName(name)
|
|
301
|
+
} else {
|
|
302
|
+
logToJsWithTag("[SEQ-52]", "No valid message found in local name key")
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if let message = finalMessage {
|
|
306
|
+
logToJsWithTag("[SEQ-55]", "Valid Beacon Resolved: \(message) | RSSI: \(RSSI)")
|
|
328
307
|
let peripheralId = peripheral.identifier.uuidString
|
|
329
308
|
let rssiValue = RSSI.intValue
|
|
330
309
|
|
|
@@ -337,6 +316,8 @@ public class Beacon: CAPPlugin, CBPeripheralManagerDelegate,
|
|
|
337
316
|
"timestamp": Int64(Date().timeIntervalSince1970 * 1000),
|
|
338
317
|
]
|
|
339
318
|
)
|
|
319
|
+
} else {
|
|
320
|
+
logToJsWithTag("[SEQ-55-F]", "No valid message found in scan result")
|
|
340
321
|
}
|
|
341
322
|
}
|
|
342
323
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wisdomgarden/capacitor-plugin-beacon",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Capacitor plugin for Bluetooth beacon broadcasting and monitoring",
|
|
5
5
|
"main": "dist/plugin.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -36,8 +36,10 @@
|
|
|
36
36
|
"files": [
|
|
37
37
|
"dist/",
|
|
38
38
|
"ios/",
|
|
39
|
-
"android/",
|
|
40
|
-
"
|
|
39
|
+
"android/src/main/",
|
|
40
|
+
"android/build.gradle",
|
|
41
|
+
"WisdomgardenCapacitorPluginBeacon.podspec",
|
|
42
|
+
"CHANGE-LOG.md"
|
|
41
43
|
],
|
|
42
44
|
"keywords": [
|
|
43
45
|
"capacitor",
|
|
@@ -60,5 +62,9 @@
|
|
|
60
62
|
},
|
|
61
63
|
"bugs": {
|
|
62
64
|
"url": "git@gitlab.tronclass.com.cn:lms/capacitor-plugin-beacon.git/issues"
|
|
65
|
+
},
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "public",
|
|
68
|
+
"registry": "https://registry.npmjs.org/"
|
|
63
69
|
}
|
|
64
70
|
}
|
|
Binary file
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# Project-wide Gradle settings.
|
|
2
|
-
|
|
3
|
-
# IDE (e.g. Android Studio) users:
|
|
4
|
-
# Gradle settings configured through the IDE *will override*
|
|
5
|
-
# any settings specified in this file.
|
|
6
|
-
|
|
7
|
-
# For more details on how to configure your build environment visit
|
|
8
|
-
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
|
9
|
-
|
|
10
|
-
# Specifies the JVM arguments used for the daemon process.
|
|
11
|
-
# The setting is particularly useful for tweaking memory settings.
|
|
12
|
-
org.gradle.jvmargs=-Xmx1536m
|
|
13
|
-
|
|
14
|
-
# When configured, Gradle will run in incubating parallel mode.
|
|
15
|
-
# This option should only be used with decoupled projects. More details, visit
|
|
16
|
-
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
|
17
|
-
# org.gradle.parallel=true
|
|
18
|
-
|
|
19
|
-
# AndroidX package structure to make it clearer which packages are bundled with the
|
|
20
|
-
# Android operating system, and which are packaged with your app's APK
|
|
21
|
-
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
|
22
|
-
android.useAndroidX=true
|
|
23
|
-
# Automatically convert third-party libraries to use AndroidX
|
|
24
|
-
android.enableJetifier=true
|