@tempivo/sensor-beacon 0.3.3 → 0.4.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 (28) hide show
  1. package/README.md +4 -4
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/AndroidManifest.xml +2 -1
  4. package/android/src/main/java/expo/modules/tempivosensorbeacon/SensorBeaconNative.java +1 -3
  5. package/android/src/main/java/expo/modules/tempivosensorbeacon/TempivoSensorBeaconModule.kt +11 -2
  6. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorBleRuntime.kt +27 -84
  7. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorSdkScanTelemetry.kt +138 -0
  8. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorBeaconDecoder.kt +53 -0
  9. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorBeaconScanner.kt +161 -74
  10. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorQr.kt +7 -8
  11. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorSession.kt +2 -10
  12. package/dist/decoder.d.ts +0 -2
  13. package/dist/decoder.js +0 -17
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/native-module.js +5 -1
  17. package/dist/qr.d.ts +3 -2
  18. package/dist/qr.js +8 -6
  19. package/dist/react-native.d.ts +24 -1
  20. package/dist/react-native.js +22 -1
  21. package/dist/session-types.d.ts +4 -2
  22. package/dist/tempivo-sensor-beacon.aar +0 -0
  23. package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64/TempivoSensorBridge.framework/TempivoSensorBridge +0 -0
  24. package/ios/Sources/TempivoSensorBeacon/TempivoSensorQr.swift +10 -7
  25. package/ios/Sources/TempivoSensorBeacon/TempivoSensorSession.swift +5 -3
  26. package/ios/Sources/TempivoSensorBeacon/TempivoSensorSessionTypes.swift +0 -1
  27. package/ios/TempivoSensorBeaconModule.swift +137 -14
  28. package/package.json +1 -1
@@ -15,7 +15,7 @@ public final class TempivoSensorSession: @unchecked Sendable {
15
15
 
16
16
  public init() {}
17
17
 
18
- public func connect(serial: String, bluetoothMac: String, pin: String, sessionType: TempivoSensorSessionType = .modern) throws {
18
+ public func connect(serial: String, bluetoothMac: String, pin: String) throws {
19
19
  guard let pinInt = Int32(pin.trimmingCharacters(in: .whitespacesAndNewlines)) else {
20
20
  throw TempivoSensorError(.invalidPin, "PIN must be numeric.")
21
21
  }
@@ -28,7 +28,9 @@ public final class TempivoSensorSession: @unchecked Sendable {
28
28
  serial: TempivoSensorQrParser.normalizeSerial(serial),
29
29
  bluetoothMac: bluetoothMac,
30
30
  pin: pinInt,
31
- legacy: sessionType == .legacy
31
+ legacy: false,
32
+ deviceId: TempivoSensorQrParser.normalizeSerial(serial),
33
+ encryptionKey: ""
32
34
  )
33
35
  )
34
36
  try throwIfBridgeError(result)
@@ -39,7 +41,7 @@ public final class TempivoSensorSession: @unchecked Sendable {
39
41
  }
40
42
 
41
43
  public func connect(qr: TempivoSensorQr) throws {
42
- try connect(serial: qr.serial, bluetoothMac: qr.bluetoothMac, pin: qr.pin, sessionType: qr.sessionType)
44
+ try connect(serial: qr.serial, bluetoothMac: qr.bluetoothMac, pin: qr.pin)
43
45
  }
44
46
 
45
47
  public func disconnect() {
@@ -2,7 +2,6 @@ import Foundation
2
2
 
3
3
  public enum TempivoSensorSessionType: String, Sendable {
4
4
  case modern
5
- case legacy
6
5
  }
7
6
 
8
7
  public enum TempivoTemperatureChannel: String, Sendable {
@@ -1,8 +1,18 @@
1
1
  import CoreBluetooth
2
2
  import ExpoModulesCore
3
+ import Foundation
3
4
 
4
- public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelegate {
5
+ #if canImport(TempivoSensorBridge)
6
+ import TempivoSensorBridge
7
+ #endif
8
+
9
+ public class TempivoSensorBeaconModule: Module {
10
+ #if canImport(TempivoSensorBridge)
11
+ private let scanBridge = TempivoSensorBridge()
12
+ private var sdkScanActive = false
13
+ #else
5
14
  private var scanner: TempivoSensorBeaconScanner?
15
+ #endif
6
16
  private let session = TempivoSensorSession()
7
17
 
8
18
  public func definition() -> ModuleDefinition {
@@ -10,20 +20,43 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
10
20
  Events("onDeviceFound")
11
21
 
12
22
  AsyncFunction("requestPermissions") { () -> [String: Any] in
13
- _ = self.requireScanner()
23
+ #if canImport(TempivoSensorBridge)
24
+ self.scanBridge.initialize()
25
+ #else
26
+ _ = self.requireCbScanner()
27
+ #endif
14
28
  return self.permissionPayload()
15
29
  }
16
30
 
17
31
  AsyncFunction("startScan") {
18
- self.requireScanner().startScanning()
32
+ #if canImport(TempivoSensorBridge)
33
+ self.scanBridge.initialize()
34
+ self.sdkScanActive = true
35
+ self.scanBridge.startDeviceScan { json in
36
+ guard self.sdkScanActive else { return }
37
+ guard let payload = self.devicePayloadFromBridgeJson(json) else { return }
38
+ self.sendEvent("onDeviceFound", payload)
39
+ }
40
+ #else
41
+ self.requireCbScanner().startScanning()
42
+ #endif
19
43
  }
20
44
 
21
45
  Function("stopScan") {
46
+ #if canImport(TempivoSensorBridge)
47
+ self.sdkScanActive = false
48
+ self.scanBridge.stopDeviceScan()
49
+ #else
22
50
  self.scanner?.stopScanning()
51
+ #endif
23
52
  }
24
53
 
25
54
  Function("isScanning") { () -> Bool in
26
- self.scanner?.scanning ?? false
55
+ #if canImport(TempivoSensorBridge)
56
+ return self.sdkScanActive
57
+ #else
58
+ return self.scanner?.scanning ?? false
59
+ #endif
27
60
  }
28
61
 
29
62
  AsyncFunction("connect") { (qrJson: String) -> [String: Any] in
@@ -82,19 +115,18 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
82
115
  }
83
116
 
84
117
  OnDestroy {
118
+ #if canImport(TempivoSensorBridge)
119
+ self.sdkScanActive = false
120
+ self.scanBridge.stopDeviceScan()
121
+ #else
85
122
  self.scanner?.stopScanning()
123
+ #endif
86
124
  self.session.disconnect()
87
125
  }
88
126
  }
89
127
 
90
- public func sensorBeaconScanner(
91
- _ scanner: TempivoSensorBeaconScanner,
92
- didDiscover discovery: SensorBeaconDiscovery
93
- ) {
94
- sendEvent("onDeviceFound", devicePayload(discovery))
95
- }
96
-
97
- private func requireScanner() -> TempivoSensorBeaconScanner {
128
+ #if !canImport(TempivoSensorBridge)
129
+ private func requireCbScanner() -> TempivoSensorBeaconScanner {
98
130
  if let scanner {
99
131
  return scanner
100
132
  }
@@ -103,11 +135,91 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
103
135
  scanner = created
104
136
  return created
105
137
  }
138
+ #endif
139
+
140
+ #if canImport(TempivoSensorBridge)
141
+ private func devicePayloadFromBridgeJson(_ json: String) -> [String: Any]? {
142
+ guard let data = json.data(using: .utf8),
143
+ let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
144
+ return nil
145
+ }
146
+ guard let deviceId = dict["deviceId"] as? String else { return nil }
147
+ var payload: [String: Any] = ["deviceId": deviceId]
148
+ if let rssi = dict["rssi"] as? Int {
149
+ payload["rssi"] = rssi
150
+ } else if let rssi = dict["rssi"] as? Double {
151
+ payload["rssi"] = Int(rssi)
152
+ }
153
+ if let serial = dict["serialNumber"] as? String {
154
+ payload["serialNumber"] = serial
155
+ }
156
+ if let mac = dict["bluetoothMacAddress"] as? String {
157
+ payload["bluetoothMacAddress"] = mac
158
+ }
159
+ if let summary = dict["advertisementSummary"] as? String, !summary.isEmpty {
160
+ payload["summary"] = summary
161
+ }
162
+ if let telem = dict["advertisementTelemetry"] as? [String: Any] {
163
+ payload["telemetry"] = telemetryPayloadFromBridge(telem)
164
+ if payload["summary"] == nil, let s = telem["summary"] as? String, !s.isEmpty {
165
+ payload["summary"] = s
166
+ }
167
+ }
168
+ if payload["summary"] == nil {
169
+ payload["summary"] = payload["serialNumber"] as? String ?? deviceId
170
+ }
171
+ return payload
172
+ }
173
+
174
+ private func telemetryPayloadFromBridge(_ telem: [String: Any]) -> [String: Any] {
175
+ var payload: [String: Any] = [:]
176
+ if let firmware = telem["firmware"] as? String { payload["firmware"] = firmware }
177
+ if let batteryOk = telem["batteryOk"] as? Bool { payload["batteryOk"] = batteryOk }
178
+ if let encryptionEnabled = telem["encryptionEnabled"] as? Bool {
179
+ payload["encryptionEnabled"] = encryptionEnabled
180
+ }
181
+ if let cellularStatus = telem["cellularStatus"] as? String {
182
+ payload["cellularStatus"] = cellularStatus
183
+ }
184
+ if let measurementCounter = telem["measurementCounter"] as? Int {
185
+ payload["measurementCounter"] = measurementCounter
186
+ } else if let measurementCounter = telem["measurementCounter"] as? Int64 {
187
+ payload["measurementCounter"] = Int(measurementCounter)
188
+ }
189
+ if let readingTimestampUnix = telem["readingTimestampUnix"] as? Int {
190
+ payload["readingTimestampUnix"] = readingTimestampUnix
191
+ } else if let readingTimestampUnix = telem["readingTimestampUnix"] as? Int64 {
192
+ payload["readingTimestampUnix"] = Int(readingTimestampUnix)
193
+ }
194
+ if let periodBase = telem["periodBaseSeconds"] as? Int,
195
+ let periodFactor = telem["periodFactor"] as? Int,
196
+ periodBase > 0 {
197
+ payload["measurementIntervalSeconds"] = periodBase * max(periodFactor, 1)
198
+ }
199
+ if let readings = telem["readings"] as? [[String: Any]] {
200
+ payload["readings"] = readings.map { row in
201
+ var out: [String: Any] = [:]
202
+ if let typeHex = row["typeHex"] as? String { out["typeHex"] = typeHex }
203
+ if let text = row["text"] as? String { out["text"] = text }
204
+ if let raw24 = row["raw24"] as? Int { out["raw24"] = raw24 }
205
+ return out
206
+ }
207
+ } else {
208
+ payload["readings"] = [] as [[String: Any]]
209
+ }
210
+ if let readingsCount = telem["readingsCount"] as? Int {
211
+ payload["readingsCount"] = readingsCount
212
+ }
213
+ if let summary = telem["summary"] as? String {
214
+ payload["summary"] = summary
215
+ }
216
+ return payload
217
+ }
218
+ #endif
106
219
 
107
220
  private func permissionPayload() -> [String: Any] {
108
221
  if #available(iOS 13.1, *) {
109
222
  let status = CBManager.authorization
110
- let granted = status == .allowedAlways
111
223
  let statusName: String
112
224
  switch status {
113
225
  case .allowedAlways: statusName = "granted"
@@ -115,7 +227,7 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
115
227
  default: statusName = "denied"
116
228
  }
117
229
  return [
118
- "granted": granted,
230
+ "granted": status == .allowedAlways || status == .notDetermined,
119
231
  "status": statusName,
120
232
  "canAskAgain": status == .notDetermined,
121
233
  "expires": "never",
@@ -193,6 +305,17 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
193
305
  }
194
306
  }
195
307
 
308
+ #if !canImport(TempivoSensorBridge)
309
+ extension TempivoSensorBeaconModule: TempivoSensorBeaconScannerDelegate {
310
+ public func sensorBeaconScanner(
311
+ _ scanner: TempivoSensorBeaconScanner,
312
+ didDiscover discovery: SensorBeaconDiscovery
313
+ ) {
314
+ sendEvent("onDeviceFound", devicePayload(discovery))
315
+ }
316
+ }
317
+ #endif
318
+
196
319
  internal final class SensorBeaconException: Exception {
197
320
  private let customCode: String
198
321
  private let customReason: String
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tempivo/sensor-beacon",
3
- "version": "0.3.3",
3
+ "version": "0.4.3",
4
4
  "description": "Tempivo sensor BLE SDK for Expo, React Native, Android, iOS, and Node.",
5
5
  "homepage": "https://app.tempivo.com/integrations",
6
6
  "repository": {