@tempivo/sensor-beacon 0.4.3 → 1.1.0

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 (45) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +156 -6
  3. package/android/src/main/java/expo/modules/tempivosensorbeacon/SensorBeaconNative.java +17 -2
  4. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/BleConnectCredentials.kt +25 -0
  5. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SdkMeasurementFormat.kt +111 -0
  6. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorBleRuntime.kt +21 -12
  7. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorBleVendor.kt +28 -0
  8. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorModelHint.kt +20 -0
  9. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorSdkScanTelemetry.kt +5 -31
  10. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorBeaconDecoder.kt +1 -1
  11. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorBeaconScanner.kt +5 -6
  12. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorBeaconTypes.kt +2 -0
  13. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorQr.kt +20 -1
  14. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorSession.kt +19 -7
  15. package/dist/connect-helpers.d.ts +52 -0
  16. package/dist/connect-helpers.js +153 -0
  17. package/dist/decoder.d.ts +1 -0
  18. package/dist/decoder.js +7 -1
  19. package/dist/index.d.ts +6 -2
  20. package/dist/index.js +4 -2
  21. package/dist/model.d.ts +5 -0
  22. package/dist/model.js +25 -0
  23. package/dist/native-helpers.js +5 -2
  24. package/dist/native-module.js +2 -2
  25. package/dist/native-types.d.ts +2 -0
  26. package/dist/profile.d.ts +8 -1
  27. package/dist/profile.js +42 -19
  28. package/dist/react-native.d.ts +5 -1
  29. package/dist/react-native.js +3 -1
  30. package/dist/session-types.d.ts +2 -1
  31. package/dist/tempivo-sensor-beacon.aar +0 -0
  32. package/ios/Frameworks/TempivoSensorBridge.xcframework/Info.plist +4 -4
  33. package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64/TempivoSensorBridge.framework/Headers/TempivoSensorBridge.h +28 -1
  34. package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64/TempivoSensorBridge.framework/TempivoSensorBridge +0 -0
  35. package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64-simulator/TempivoSensorBridge.framework/Headers/TempivoSensorBridge.h +28 -1
  36. package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64-simulator/TempivoSensorBridge.framework/TempivoSensorBridge +0 -0
  37. package/ios/Sources/TempivoSensorBeacon/TempivoSensorBeaconDecoder.swift +8 -1
  38. package/ios/Sources/TempivoSensorBeacon/TempivoSensorBeaconScanner.swift +10 -1
  39. package/ios/Sources/TempivoSensorBeacon/TempivoSensorModelHint.swift +29 -0
  40. package/ios/Sources/TempivoSensorBeacon/TempivoSensorProfile.swift +37 -13
  41. package/ios/Sources/TempivoSensorBeacon/TempivoSensorQr.swift +38 -1
  42. package/ios/Sources/TempivoSensorBeacon/TempivoSensorSession.swift +24 -12
  43. package/ios/Sources/TempivoSensorBeacon/TempivoSensorSessionTypes.swift +1 -3
  44. package/ios/TempivoSensorBeaconModule.swift +83 -16
  45. package/package.json +4 -3
@@ -9,7 +9,9 @@ import TempivoSensorBridge
9
9
  public class TempivoSensorBeaconModule: Module {
10
10
  #if canImport(TempivoSensorBridge)
11
11
  private let scanBridge = TempivoSensorBridge()
12
- private var sdkScanActive = false
12
+ private var sdkScanHandler: SensorSdkScanHandler?
13
+ fileprivate var sdkScanActive = false
14
+ private var lastSeenBySerial: [String: (deviceId: String, mac: String)] = [:]
13
15
  #else
14
16
  private var scanner: TempivoSensorBeaconScanner?
15
17
  #endif
@@ -32,11 +34,11 @@ public class TempivoSensorBeaconModule: Module {
32
34
  #if canImport(TempivoSensorBridge)
33
35
  self.scanBridge.initialize()
34
36
  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)
37
+ if self.sdkScanHandler == nil {
38
+ self.sdkScanHandler = SensorSdkScanHandler(module: self)
39
39
  }
40
+ self.scanBridge.setDeviceScanHandler(handler: self.sdkScanHandler)
41
+ self.scanBridge.startDeviceScan()
40
42
  #else
41
43
  self.requireCbScanner().startScanning()
42
44
  #endif
@@ -46,6 +48,7 @@ public class TempivoSensorBeaconModule: Module {
46
48
  #if canImport(TempivoSensorBridge)
47
49
  self.sdkScanActive = false
48
50
  self.scanBridge.stopDeviceScan()
51
+ self.scanBridge.setDeviceScanHandler(handler: nil)
49
52
  #else
50
53
  self.scanner?.stopScanning()
51
54
  #endif
@@ -61,8 +64,18 @@ public class TempivoSensorBeaconModule: Module {
61
64
 
62
65
  AsyncFunction("connect") { (qrJson: String) -> [String: Any] in
63
66
  do {
67
+ #if canImport(TempivoSensorBridge)
68
+ self.pauseSdkScanForGatt()
69
+ #else
70
+ self.scanner?.stopScanning()
71
+ #endif
64
72
  let qr = try TempivoSensorQrParser.parse(qrJson)
65
- try self.session.connect(qr: qr)
73
+ let resolved = self.resolveConnectTarget(qr: qr)
74
+ try self.session.connect(
75
+ qr: qr,
76
+ deviceId: resolved.deviceId,
77
+ bluetoothMac: resolved.bluetoothMac
78
+ )
66
79
  return self.qrPayload(qr)
67
80
  } catch {
68
81
  throw self.mapError(error)
@@ -92,7 +105,7 @@ public class TempivoSensorBeaconModule: Module {
92
105
  AsyncFunction("triggerTransmission") { () -> [String: Any] in
93
106
  do {
94
107
  let result = try self.session.triggerTransmission()
95
- return ["ok": result.ok, "supported": result.supported]
108
+ return ["ok": result.ok]
96
109
  } catch {
97
110
  throw self.mapError(error)
98
111
  }
@@ -101,14 +114,10 @@ public class TempivoSensorBeaconModule: Module {
101
114
  AsyncFunction("getCalibration") { () -> [String: Any] in
102
115
  do {
103
116
  let cal = try self.session.getCalibration()
104
- var payload: [String: Any] = [:]
105
- if let date = cal.laboratoryCalibrationDate {
106
- payload["laboratoryCalibrationDate"] = date
107
- }
108
- if let ts = cal.laboratoryCalibrationTimestamp {
109
- payload["laboratoryCalibrationTimestamp"] = Int(ts)
110
- }
111
- return payload
117
+ return [
118
+ "laboratoryCalibrationDate": cal.laboratoryCalibrationDate as Any,
119
+ "laboratoryCalibrationTimestamp": cal.laboratoryCalibrationTimestamp.map { Int($0) } as Any,
120
+ ]
112
121
  } catch {
113
122
  throw self.mapError(error)
114
123
  }
@@ -118,6 +127,7 @@ public class TempivoSensorBeaconModule: Module {
118
127
  #if canImport(TempivoSensorBridge)
119
128
  self.sdkScanActive = false
120
129
  self.scanBridge.stopDeviceScan()
130
+ self.scanBridge.setDeviceScanHandler(handler: nil)
121
131
  #else
122
132
  self.scanner?.stopScanning()
123
133
  #endif
@@ -138,7 +148,36 @@ public class TempivoSensorBeaconModule: Module {
138
148
  #endif
139
149
 
140
150
  #if canImport(TempivoSensorBridge)
141
- private func devicePayloadFromBridgeJson(_ json: String) -> [String: Any]? {
151
+ fileprivate func rememberSeenDevice(_ payload: [String: Any]) {
152
+ guard let deviceId = payload["deviceId"] as? String,
153
+ let mac = payload["bluetoothMacAddress"] as? String else { return }
154
+ let serial = (payload["serialNumber"] as? String)?
155
+ .trimmingCharacters(in: .whitespacesAndNewlines)
156
+ .uppercased()
157
+ if let serial, !serial.isEmpty {
158
+ lastSeenBySerial[serial] = (deviceId, mac)
159
+ }
160
+ let macKey = mac.replacingOccurrences(of: ":", with: "").uppercased()
161
+ if macKey.count == 12 {
162
+ lastSeenBySerial[macKey] = (deviceId, mac)
163
+ }
164
+ }
165
+
166
+ fileprivate func pauseSdkScanForGatt() {
167
+ sdkScanActive = false
168
+ scanBridge.stopDeviceScan()
169
+ scanBridge.setDeviceScanHandler(handler: nil)
170
+ }
171
+
172
+ private func resolveConnectTarget(qr: TempivoSensorQr) -> (deviceId: String, bluetoothMac: String) {
173
+ let serialKey = TempivoSensorQrParser.normalizeSerial(qr.serial)
174
+ let seen = lastSeenBySerial[serialKey] ?? lastSeenBySerial[String(serialKey.suffix(12))]
175
+ let deviceId = qr.deviceId ?? seen?.deviceId ?? serialKey
176
+ let mac = seen?.mac ?? qr.bluetoothMac
177
+ return (deviceId, mac)
178
+ }
179
+
180
+ fileprivate func devicePayloadFromBridgeJson(_ json: String) -> [String: Any]? {
142
181
  guard let data = json.data(using: .utf8),
143
182
  let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
144
183
  return nil
@@ -156,6 +195,14 @@ public class TempivoSensorBeaconModule: Module {
156
195
  if let mac = dict["bluetoothMacAddress"] as? String {
157
196
  payload["bluetoothMacAddress"] = mac
158
197
  }
198
+ if let model = dict["model"] as? String, !model.isEmpty {
199
+ payload["model"] = model
200
+ } else if let telem = dict["advertisementTelemetry"] as? [String: Any],
201
+ let firmware = telem["firmware"] as? String {
202
+ payload["model"] = TempivoSensorModelHint.fromFirmware(firmware)
203
+ } else {
204
+ payload["model"] = TempivoSensorModelHint.defaultModel
205
+ }
159
206
  if let summary = dict["advertisementSummary"] as? String, !summary.isEmpty {
160
207
  payload["summary"] = summary
161
208
  }
@@ -168,6 +215,7 @@ public class TempivoSensorBeaconModule: Module {
168
215
  if payload["summary"] == nil {
169
216
  payload["summary"] = payload["serialNumber"] as? String ?? deviceId
170
217
  }
218
+ rememberSeenDevice(payload)
171
219
  return payload
172
220
  }
173
221
 
@@ -268,6 +316,9 @@ public class TempivoSensorBeaconModule: Module {
268
316
  if let reading = discovery.reading {
269
317
  payload["summary"] = reading.summary
270
318
  payload["telemetry"] = telemetryPayload(reading)
319
+ payload["model"] = TempivoSensorModelHint.fromFirmware(reading.firmware)
320
+ } else {
321
+ payload["model"] = TempivoSensorModelHint.defaultModel
271
322
  }
272
323
  return payload
273
324
  }
@@ -305,6 +356,22 @@ public class TempivoSensorBeaconModule: Module {
305
356
  }
306
357
  }
307
358
 
359
+ #if canImport(TempivoSensorBridge)
360
+ private final class SensorSdkScanHandler: SensorDeviceScanHandler {
361
+ private weak var module: TempivoSensorBeaconModule?
362
+
363
+ init(module: TempivoSensorBeaconModule) {
364
+ self.module = module
365
+ }
366
+
367
+ override func onDevice(deviceJson: String) {
368
+ guard let module, module.sdkScanActive else { return }
369
+ guard let payload = module.devicePayloadFromBridgeJson(deviceJson) else { return }
370
+ module.sendEvent("onDeviceFound", payload)
371
+ }
372
+ }
373
+ #endif
374
+
308
375
  #if !canImport(TempivoSensorBridge)
309
376
  extension TempivoSensorBeaconModule: TempivoSensorBeaconScannerDelegate {
310
377
  public func sensorBeaconScanner(
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tempivo/sensor-beacon",
3
- "version": "0.4.3",
4
- "description": "Tempivo sensor BLE SDK for Expo, React Native, Android, iOS, and Node.",
3
+ "version": "1.1.0",
4
+ "description": "Tempivo sensor BLE SDK. Native scan and GATT for Expo, React Native, Android, and iOS. Node helpers decode advertisements and parse QR or config (no radio).",
5
5
  "homepage": "https://app.tempivo.com/integrations",
6
6
  "repository": {
7
7
  "type": "git",
@@ -40,7 +40,8 @@
40
40
  "expo-module.config.json",
41
41
  "ios",
42
42
  "LICENSE",
43
- "README.md"
43
+ "README.md",
44
+ "CHANGELOG.md"
44
45
  ],
45
46
  "publishConfig": {
46
47
  "access": "public"