@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.
Files changed (22) hide show
  1. package/CHANGE-LOG.md +13 -0
  2. package/android/src/main/java/com/wisdomgarden/mobile/beacon/Beacon.java +100 -58
  3. package/android/src/main/java/com/wisdomgarden/mobile/beacon/BeaconUtils.java +110 -13
  4. package/ios/Plugin/BeaconUtils.swift +108 -40
  5. package/ios/Plugin/Plugin.swift +109 -128
  6. package/package.json +9 -3
  7. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  8. package/android/gradle/wrapper/gradle-wrapper.properties +0 -5
  9. package/android/gradle.properties +0 -24
  10. package/android/gradlew +0 -188
  11. package/android/gradlew.bat +0 -100
  12. package/android/proguard-rules.pro +0 -21
  13. package/android/settings.gradle +0 -2
  14. package/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java +0 -26
  15. package/android/src/test/java/com/getcapacitor/ExampleUnitTest.java +0 -18
  16. package/ios/Plugin.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/xcschememanagement.plist +0 -14
  17. package/ios/Plugin.xcworkspace/xcuserdata/peixinliu.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  18. package/ios/Pods/Pods.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/Capacitor.xcscheme +0 -58
  19. package/ios/Pods/Pods.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/CapacitorCordova.xcscheme +0 -58
  20. package/ios/Pods/Pods.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/Pods-Plugin.xcscheme +0 -58
  21. package/ios/Pods/Pods.xcodeproj/xcuserdata/peixinliu.xcuserdatad/xcschemes/Pods-PluginTests.xcscheme +0 -58
  22. 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
- public static let WG_UUID = "1ed18c59-a7cc-4752-96b0-51d77a456584"
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
- "2O9AuFNPDx4gtJwS3ye7l1Mq0dEB5HsaKInikRmLhjpG6b8fzQrCcvo"
35
+ "2O9AuFNPDx4gtJwS3ye7l1Mq0dEB5HsaKInikRmLhjpG6b8fzQrCcvo"
9
36
  private static let radix = chars.count
10
37
 
11
38
  public static func encode(_ num: Int) -> String {
12
- if num == 0 { return String(chars[chars.startIndex]) }
39
+ if num <= 0 { return String(chars[chars.startIndex]) }
13
40
  var result = ""
14
- let isNegative = num < 0
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 isNegative ? "-" + result : result
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
- num * radix
33
- + chars.distance(from: chars.startIndex, to: index)
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 rollcallIdStr = String(
64
- message[
65
- ..<message.index(
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, let firstChar = nonce.first else {
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
- ? String(message.prefix(message_max_length)) : message
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
  }
@@ -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
- func startAdvertising() {
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
- super.bridge.logToJs("[Debug] startBroadcasting 11")
51
+ logToJsWithTag("[SEQ-10]", "startBroadcasting invoked")
62
52
 
63
- if let message = call.getString("message"),
64
- message.count == BeaconUtils.message_max_length
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 rollcallId = call.getInt("rollcallId") else {
69
- call.reject("rollcallId is required")
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
- guard let nonce = call.getString("nonce") else {
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
- super.bridge.logToJs("[Debug] startBroadcasting 11.1")
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
- delegate: self,
96
- queue: nil
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
- super.bridge.logToJs("[Debug] startBroadcasting 11.3")
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
- func scanForPeripherals() {
118
- super.bridge.logToJs("[Debug] scanForPeripherals 22")
119
- if centralManager == nil {
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 isMonitoring == true {
124
- return
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
- super.bridge.logToJs("[Debug] scanForPeripherals monitoring 24")
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
- super.bridge.logToJs(
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
- super.bridge.logToJs("[Beacon] Peripheral manager is powered on")
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
- super.bridge.logToJs("[Beacon] Peripheral manager is powered off")
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
- super.bridge.logToJs("[Beacon] Peripheral manager is unauthorized")
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
- super.bridge.logToJs("[Beacon] Peripheral manager is unsupported")
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
- super.bridge.logToJs(
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
- super.bridge.logToJs(
238
- "[Debug] peripheralManagerDidUpdateState needBroadcasting poweredOn"
239
- )
212
+ logToJsWithTag("[SEQ-15-1]","peripheralManagerDidUpdateState needBroadcasting poweredOn")
240
213
  self.startAdvertising()
241
214
  } else {
242
- super.bridge.logToJs(
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
- super.bridge.logToJs(
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
- super.bridge.logToJs("[Beacon] Central manager is powered on")
229
+ logToJsWithTag("[SEQ-34]", "Central manager is powered on")
261
230
  notifyListeners("bluetoothStateChanged", data: ["enabled": true])
262
231
  case .poweredOff:
263
- super.bridge.logToJs("[Beacon] Central manager is powered off")
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
- super.bridge.logToJs("[Beacon] Central manager is unauthorized")
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
- super.bridge.logToJs("[Beacon] Central manager is unsupported")
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
- super.bridge.logToJs(
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
- super.bridge.logToJs(
306
- "[Debug] centralManagerDidUpdateState needMonitoring poweredOn"
307
- )
272
+ logToJsWithTag("[SEQ-34-1]", "centralManagerDidUpdateState needMonitoring poweredOn")
308
273
  self.scanForPeripherals()
309
274
  } else {
310
- super.bridge.logToJs(
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
- if let message = advertisementData[CBAdvertisementDataLocalNameKey]
326
- as? String
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.1",
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
- "WisdomgardenCapacitorPluginBeacon.podspec"
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
  }
@@ -1,5 +0,0 @@
1
- distributionBase=GRADLE_USER_HOME
2
- distributionPath=wrapper/dists
3
- distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
4
- zipStoreBase=GRADLE_USER_HOME
5
- zipStorePath=wrapper/dists
@@ -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