@tempivo/sensor-beacon 0.3.3 → 0.4.7
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/CHANGELOG.md +29 -0
- package/README.md +159 -9
- package/android/build.gradle +2 -2
- package/android/src/main/AndroidManifest.xml +2 -1
- package/android/src/main/java/expo/modules/tempivosensorbeacon/SensorBeaconNative.java +18 -5
- package/android/src/main/java/expo/modules/tempivosensorbeacon/TempivoSensorBeaconModule.kt +11 -2
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/BleConnectCredentials.kt +25 -0
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SdkMeasurementFormat.kt +111 -0
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorBleRuntime.kt +39 -87
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorBleVendor.kt +28 -0
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorModelHint.kt +20 -0
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorSdkScanTelemetry.kt +112 -0
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorBeaconDecoder.kt +54 -1
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorBeaconScanner.kt +160 -74
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorBeaconTypes.kt +2 -0
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorQr.kt +27 -9
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorSession.kt +20 -16
- package/dist/connect-helpers.d.ts +52 -0
- package/dist/connect-helpers.js +153 -0
- package/dist/decoder.d.ts +1 -2
- package/dist/decoder.js +7 -18
- package/dist/index.d.ts +7 -3
- package/dist/index.js +5 -3
- package/dist/model.d.ts +5 -0
- package/dist/model.js +25 -0
- package/dist/native-helpers.js +5 -2
- package/dist/native-module.js +7 -3
- package/dist/native-types.d.ts +2 -0
- package/dist/profile.d.ts +8 -1
- package/dist/profile.js +42 -19
- package/dist/qr.d.ts +3 -2
- package/dist/qr.js +8 -6
- package/dist/react-native.d.ts +28 -1
- package/dist/react-native.js +24 -1
- package/dist/session-types.d.ts +6 -3
- package/dist/tempivo-sensor-beacon.aar +0 -0
- package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64/TempivoSensorBridge.framework/Headers/TempivoSensorBridge.h +28 -1
- package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64/TempivoSensorBridge.framework/TempivoSensorBridge +0 -0
- package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64-simulator/TempivoSensorBridge.framework/Headers/TempivoSensorBridge.h +28 -1
- package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64-simulator/TempivoSensorBridge.framework/TempivoSensorBridge +0 -0
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorBeaconDecoder.swift +8 -1
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorBeaconScanner.swift +10 -1
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorModelHint.swift +29 -0
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorProfile.swift +37 -13
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorQr.swift +48 -8
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorSession.swift +24 -10
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorSessionTypes.swift +1 -4
- package/ios/TempivoSensorBeaconModule.swift +214 -24
- package/package.json +3 -2
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import CoreBluetooth
|
|
2
2
|
import ExpoModulesCore
|
|
3
|
+
import Foundation
|
|
3
4
|
|
|
4
|
-
|
|
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 sdkScanHandler: SensorSdkScanHandler?
|
|
13
|
+
fileprivate var sdkScanActive = false
|
|
14
|
+
private var lastSeenBySerial: [String: (deviceId: String, mac: String)] = [:]
|
|
15
|
+
#else
|
|
5
16
|
private var scanner: TempivoSensorBeaconScanner?
|
|
17
|
+
#endif
|
|
6
18
|
private let session = TempivoSensorSession()
|
|
7
19
|
|
|
8
20
|
public func definition() -> ModuleDefinition {
|
|
@@ -10,26 +22,60 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
|
|
|
10
22
|
Events("onDeviceFound")
|
|
11
23
|
|
|
12
24
|
AsyncFunction("requestPermissions") { () -> [String: Any] in
|
|
13
|
-
|
|
25
|
+
#if canImport(TempivoSensorBridge)
|
|
26
|
+
self.scanBridge.initialize()
|
|
27
|
+
#else
|
|
28
|
+
_ = self.requireCbScanner()
|
|
29
|
+
#endif
|
|
14
30
|
return self.permissionPayload()
|
|
15
31
|
}
|
|
16
32
|
|
|
17
33
|
AsyncFunction("startScan") {
|
|
18
|
-
|
|
34
|
+
#if canImport(TempivoSensorBridge)
|
|
35
|
+
self.scanBridge.initialize()
|
|
36
|
+
self.sdkScanActive = true
|
|
37
|
+
if self.sdkScanHandler == nil {
|
|
38
|
+
self.sdkScanHandler = SensorSdkScanHandler(module: self)
|
|
39
|
+
}
|
|
40
|
+
self.scanBridge.setDeviceScanHandler(handler: self.sdkScanHandler)
|
|
41
|
+
self.scanBridge.startDeviceScan()
|
|
42
|
+
#else
|
|
43
|
+
self.requireCbScanner().startScanning()
|
|
44
|
+
#endif
|
|
19
45
|
}
|
|
20
46
|
|
|
21
47
|
Function("stopScan") {
|
|
48
|
+
#if canImport(TempivoSensorBridge)
|
|
49
|
+
self.sdkScanActive = false
|
|
50
|
+
self.scanBridge.stopDeviceScan()
|
|
51
|
+
self.scanBridge.setDeviceScanHandler(handler: nil)
|
|
52
|
+
#else
|
|
22
53
|
self.scanner?.stopScanning()
|
|
54
|
+
#endif
|
|
23
55
|
}
|
|
24
56
|
|
|
25
57
|
Function("isScanning") { () -> Bool in
|
|
26
|
-
|
|
58
|
+
#if canImport(TempivoSensorBridge)
|
|
59
|
+
return self.sdkScanActive
|
|
60
|
+
#else
|
|
61
|
+
return self.scanner?.scanning ?? false
|
|
62
|
+
#endif
|
|
27
63
|
}
|
|
28
64
|
|
|
29
65
|
AsyncFunction("connect") { (qrJson: String) -> [String: Any] in
|
|
30
66
|
do {
|
|
67
|
+
#if canImport(TempivoSensorBridge)
|
|
68
|
+
self.pauseSdkScanForGatt()
|
|
69
|
+
#else
|
|
70
|
+
self.scanner?.stopScanning()
|
|
71
|
+
#endif
|
|
31
72
|
let qr = try TempivoSensorQrParser.parse(qrJson)
|
|
32
|
-
|
|
73
|
+
let resolved = self.resolveConnectTarget(qr: qr)
|
|
74
|
+
try self.session.connect(
|
|
75
|
+
qr: qr,
|
|
76
|
+
deviceId: resolved.deviceId,
|
|
77
|
+
bluetoothMac: resolved.bluetoothMac
|
|
78
|
+
)
|
|
33
79
|
return self.qrPayload(qr)
|
|
34
80
|
} catch {
|
|
35
81
|
throw self.mapError(error)
|
|
@@ -59,7 +105,7 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
|
|
|
59
105
|
AsyncFunction("triggerTransmission") { () -> [String: Any] in
|
|
60
106
|
do {
|
|
61
107
|
let result = try self.session.triggerTransmission()
|
|
62
|
-
return ["ok": result.ok
|
|
108
|
+
return ["ok": result.ok]
|
|
63
109
|
} catch {
|
|
64
110
|
throw self.mapError(error)
|
|
65
111
|
}
|
|
@@ -68,33 +114,29 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
|
|
|
68
114
|
AsyncFunction("getCalibration") { () -> [String: Any] in
|
|
69
115
|
do {
|
|
70
116
|
let cal = try self.session.getCalibration()
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if let ts = cal.laboratoryCalibrationTimestamp {
|
|
76
|
-
payload["laboratoryCalibrationTimestamp"] = Int(ts)
|
|
77
|
-
}
|
|
78
|
-
return payload
|
|
117
|
+
return [
|
|
118
|
+
"laboratoryCalibrationDate": cal.laboratoryCalibrationDate as Any,
|
|
119
|
+
"laboratoryCalibrationTimestamp": cal.laboratoryCalibrationTimestamp.map { Int($0) } as Any,
|
|
120
|
+
]
|
|
79
121
|
} catch {
|
|
80
122
|
throw self.mapError(error)
|
|
81
123
|
}
|
|
82
124
|
}
|
|
83
125
|
|
|
84
126
|
OnDestroy {
|
|
127
|
+
#if canImport(TempivoSensorBridge)
|
|
128
|
+
self.sdkScanActive = false
|
|
129
|
+
self.scanBridge.stopDeviceScan()
|
|
130
|
+
self.scanBridge.setDeviceScanHandler(handler: nil)
|
|
131
|
+
#else
|
|
85
132
|
self.scanner?.stopScanning()
|
|
133
|
+
#endif
|
|
86
134
|
self.session.disconnect()
|
|
87
135
|
}
|
|
88
136
|
}
|
|
89
137
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
didDiscover discovery: SensorBeaconDiscovery
|
|
93
|
-
) {
|
|
94
|
-
sendEvent("onDeviceFound", devicePayload(discovery))
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
private func requireScanner() -> TempivoSensorBeaconScanner {
|
|
138
|
+
#if !canImport(TempivoSensorBridge)
|
|
139
|
+
private func requireCbScanner() -> TempivoSensorBeaconScanner {
|
|
98
140
|
if let scanner {
|
|
99
141
|
return scanner
|
|
100
142
|
}
|
|
@@ -103,11 +145,129 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
|
|
|
103
145
|
scanner = created
|
|
104
146
|
return created
|
|
105
147
|
}
|
|
148
|
+
#endif
|
|
149
|
+
|
|
150
|
+
#if canImport(TempivoSensorBridge)
|
|
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]? {
|
|
181
|
+
guard let data = json.data(using: .utf8),
|
|
182
|
+
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
183
|
+
return nil
|
|
184
|
+
}
|
|
185
|
+
guard let deviceId = dict["deviceId"] as? String else { return nil }
|
|
186
|
+
var payload: [String: Any] = ["deviceId": deviceId]
|
|
187
|
+
if let rssi = dict["rssi"] as? Int {
|
|
188
|
+
payload["rssi"] = rssi
|
|
189
|
+
} else if let rssi = dict["rssi"] as? Double {
|
|
190
|
+
payload["rssi"] = Int(rssi)
|
|
191
|
+
}
|
|
192
|
+
if let serial = dict["serialNumber"] as? String {
|
|
193
|
+
payload["serialNumber"] = serial
|
|
194
|
+
}
|
|
195
|
+
if let mac = dict["bluetoothMacAddress"] as? String {
|
|
196
|
+
payload["bluetoothMacAddress"] = mac
|
|
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
|
+
}
|
|
206
|
+
if let summary = dict["advertisementSummary"] as? String, !summary.isEmpty {
|
|
207
|
+
payload["summary"] = summary
|
|
208
|
+
}
|
|
209
|
+
if let telem = dict["advertisementTelemetry"] as? [String: Any] {
|
|
210
|
+
payload["telemetry"] = telemetryPayloadFromBridge(telem)
|
|
211
|
+
if payload["summary"] == nil, let s = telem["summary"] as? String, !s.isEmpty {
|
|
212
|
+
payload["summary"] = s
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if payload["summary"] == nil {
|
|
216
|
+
payload["summary"] = payload["serialNumber"] as? String ?? deviceId
|
|
217
|
+
}
|
|
218
|
+
rememberSeenDevice(payload)
|
|
219
|
+
return payload
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private func telemetryPayloadFromBridge(_ telem: [String: Any]) -> [String: Any] {
|
|
223
|
+
var payload: [String: Any] = [:]
|
|
224
|
+
if let firmware = telem["firmware"] as? String { payload["firmware"] = firmware }
|
|
225
|
+
if let batteryOk = telem["batteryOk"] as? Bool { payload["batteryOk"] = batteryOk }
|
|
226
|
+
if let encryptionEnabled = telem["encryptionEnabled"] as? Bool {
|
|
227
|
+
payload["encryptionEnabled"] = encryptionEnabled
|
|
228
|
+
}
|
|
229
|
+
if let cellularStatus = telem["cellularStatus"] as? String {
|
|
230
|
+
payload["cellularStatus"] = cellularStatus
|
|
231
|
+
}
|
|
232
|
+
if let measurementCounter = telem["measurementCounter"] as? Int {
|
|
233
|
+
payload["measurementCounter"] = measurementCounter
|
|
234
|
+
} else if let measurementCounter = telem["measurementCounter"] as? Int64 {
|
|
235
|
+
payload["measurementCounter"] = Int(measurementCounter)
|
|
236
|
+
}
|
|
237
|
+
if let readingTimestampUnix = telem["readingTimestampUnix"] as? Int {
|
|
238
|
+
payload["readingTimestampUnix"] = readingTimestampUnix
|
|
239
|
+
} else if let readingTimestampUnix = telem["readingTimestampUnix"] as? Int64 {
|
|
240
|
+
payload["readingTimestampUnix"] = Int(readingTimestampUnix)
|
|
241
|
+
}
|
|
242
|
+
if let periodBase = telem["periodBaseSeconds"] as? Int,
|
|
243
|
+
let periodFactor = telem["periodFactor"] as? Int,
|
|
244
|
+
periodBase > 0 {
|
|
245
|
+
payload["measurementIntervalSeconds"] = periodBase * max(periodFactor, 1)
|
|
246
|
+
}
|
|
247
|
+
if let readings = telem["readings"] as? [[String: Any]] {
|
|
248
|
+
payload["readings"] = readings.map { row in
|
|
249
|
+
var out: [String: Any] = [:]
|
|
250
|
+
if let typeHex = row["typeHex"] as? String { out["typeHex"] = typeHex }
|
|
251
|
+
if let text = row["text"] as? String { out["text"] = text }
|
|
252
|
+
if let raw24 = row["raw24"] as? Int { out["raw24"] = raw24 }
|
|
253
|
+
return out
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
payload["readings"] = [] as [[String: Any]]
|
|
257
|
+
}
|
|
258
|
+
if let readingsCount = telem["readingsCount"] as? Int {
|
|
259
|
+
payload["readingsCount"] = readingsCount
|
|
260
|
+
}
|
|
261
|
+
if let summary = telem["summary"] as? String {
|
|
262
|
+
payload["summary"] = summary
|
|
263
|
+
}
|
|
264
|
+
return payload
|
|
265
|
+
}
|
|
266
|
+
#endif
|
|
106
267
|
|
|
107
268
|
private func permissionPayload() -> [String: Any] {
|
|
108
269
|
if #available(iOS 13.1, *) {
|
|
109
270
|
let status = CBManager.authorization
|
|
110
|
-
let granted = status == .allowedAlways
|
|
111
271
|
let statusName: String
|
|
112
272
|
switch status {
|
|
113
273
|
case .allowedAlways: statusName = "granted"
|
|
@@ -115,7 +275,7 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
|
|
|
115
275
|
default: statusName = "denied"
|
|
116
276
|
}
|
|
117
277
|
return [
|
|
118
|
-
"granted":
|
|
278
|
+
"granted": status == .allowedAlways || status == .notDetermined,
|
|
119
279
|
"status": statusName,
|
|
120
280
|
"canAskAgain": status == .notDetermined,
|
|
121
281
|
"expires": "never",
|
|
@@ -156,6 +316,9 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
|
|
|
156
316
|
if let reading = discovery.reading {
|
|
157
317
|
payload["summary"] = reading.summary
|
|
158
318
|
payload["telemetry"] = telemetryPayload(reading)
|
|
319
|
+
payload["model"] = TempivoSensorModelHint.fromFirmware(reading.firmware)
|
|
320
|
+
} else {
|
|
321
|
+
payload["model"] = TempivoSensorModelHint.defaultModel
|
|
159
322
|
}
|
|
160
323
|
return payload
|
|
161
324
|
}
|
|
@@ -193,6 +356,33 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
|
|
|
193
356
|
}
|
|
194
357
|
}
|
|
195
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
|
+
|
|
375
|
+
#if !canImport(TempivoSensorBridge)
|
|
376
|
+
extension TempivoSensorBeaconModule: TempivoSensorBeaconScannerDelegate {
|
|
377
|
+
public func sensorBeaconScanner(
|
|
378
|
+
_ scanner: TempivoSensorBeaconScanner,
|
|
379
|
+
didDiscover discovery: SensorBeaconDiscovery
|
|
380
|
+
) {
|
|
381
|
+
sendEvent("onDeviceFound", devicePayload(discovery))
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
#endif
|
|
385
|
+
|
|
196
386
|
internal final class SensorBeaconException: Exception {
|
|
197
387
|
private let customCode: String
|
|
198
388
|
private let customReason: String
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tempivo/sensor-beacon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.7",
|
|
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": {
|
|
@@ -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"
|