@tempivo/sensor-beacon 0.3.2 → 0.3.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/README.md +22 -11
- package/android/build.gradle +13 -5
- package/android/src/main/java/expo/modules/tempivosensorbeacon/SensorBeaconNative.java +5 -13
- package/android/src/main/java/expo/modules/tempivosensorbeacon/TempivoSensorBeaconModule.kt +2 -4
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/PartnerBleRules.kt +17 -6
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorBleRuntime.kt +50 -83
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorBeaconDecoder.kt +46 -18
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorBeaconScanner.kt +19 -6
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorError.kt +0 -17
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorProfile.kt +59 -2
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorQr.kt +0 -9
- package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorSession.kt +9 -30
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/native-helpers.d.ts +0 -1
- package/dist/native-helpers.js +3 -18
- package/dist/native-module.js +0 -1
- package/dist/normalize.d.ts +6 -0
- package/dist/normalize.js +29 -0
- package/dist/profile.d.ts +5 -0
- package/dist/profile.js +47 -1
- package/dist/qr.d.ts +2 -6
- package/dist/qr.js +2 -19
- package/dist/session-types.d.ts +8 -3
- package/dist/tempivo-sensor-beacon.aar +0 -0
- package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64/TempivoSensorBridge.framework/Headers/TempivoSensorBridge.h +1 -3
- 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 +1 -3
- package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64-simulator/TempivoSensorBridge.framework/TempivoSensorBridge +0 -0
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorBeaconDecoder.swift +12 -2
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorBeaconScanner.swift +4 -80
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorBeaconTypes.swift +0 -15
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorError.swift +1 -15
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorProfile.swift +57 -3
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorQr.swift +0 -17
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorSession.swift +8 -36
- package/ios/Sources/TempivoSensorBeacon/TempivoSensorSessionTypes.swift +10 -1
- package/ios/TempivoSensorBeaconModule.swift +2 -11
- package/package.json +6 -1
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import CoreBluetooth
|
|
2
2
|
import Foundation
|
|
3
3
|
|
|
4
|
-
#if canImport(TempivoSensorBridge)
|
|
5
|
-
import TempivoSensorBridge
|
|
6
|
-
#endif
|
|
7
|
-
|
|
8
4
|
public protocol TempivoSensorBeaconScannerDelegate: AnyObject {
|
|
9
5
|
func sensorBeaconScanner(_ scanner: TempivoSensorBeaconScanner, didDiscover discovery: SensorBeaconDiscovery)
|
|
10
6
|
func sensorBeaconScanner(_ scanner: TempivoSensorBeaconScanner, didUpdateState state: CBManagerState)
|
|
@@ -14,21 +10,15 @@ public extension TempivoSensorBeaconScannerDelegate {
|
|
|
14
10
|
func sensorBeaconScanner(_ scanner: TempivoSensorBeaconScanner, didUpdateState state: CBManagerState) {}
|
|
15
11
|
}
|
|
16
12
|
|
|
17
|
-
/// Active BLE scan
|
|
18
|
-
/// Manufacturer-only CoreBluetooth misses HC7 / FW 7+ on iPhone.
|
|
13
|
+
/// Active BLE scan for Tempivo sensor beacons (manufacturer `0x026C`). Advertising only. No GATT or connect.
|
|
19
14
|
public final class TempivoSensorBeaconScanner: NSObject {
|
|
20
15
|
|
|
21
16
|
public weak var delegate: TempivoSensorBeaconScannerDelegate?
|
|
22
17
|
|
|
23
18
|
private var central: CBCentralManager!
|
|
24
19
|
private var frameCache = SensorBeaconFrameCache()
|
|
25
|
-
private var lastSeen: [String: TempivoSensorSeenDevice] = [:]
|
|
26
20
|
private var isScanning = false
|
|
27
21
|
private var pendingStart = false
|
|
28
|
-
#if canImport(TempivoSensorBridge)
|
|
29
|
-
private let runtime = TempivoSensorBridge()
|
|
30
|
-
private var useRuntimeScan: Bool { !ProcessInfo.processInfo.isiOSAppOnMac }
|
|
31
|
-
#endif
|
|
32
22
|
|
|
33
23
|
public override init() {
|
|
34
24
|
super.init()
|
|
@@ -46,25 +36,10 @@ public final class TempivoSensorBeaconScanner: NSObject {
|
|
|
46
36
|
/// Clears cached `0x03` / `0x04` frames. Does not stop an active scan.
|
|
47
37
|
public func clearCache() {
|
|
48
38
|
frameCache.clear()
|
|
49
|
-
lastSeen.removeAll()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
public func lookup(serial: String) -> TempivoSensorSeenDevice? {
|
|
53
|
-
lastSeen[TempivoSensorBeaconDecoder.macKey(serial)]
|
|
54
39
|
}
|
|
55
40
|
|
|
56
|
-
/// Starts an active scan
|
|
41
|
+
/// Starts an active scan (duplicate advertisements allowed). Requires Bluetooth powered on.
|
|
57
42
|
public func startScanning() {
|
|
58
|
-
#if canImport(TempivoSensorBridge)
|
|
59
|
-
if useRuntimeScan {
|
|
60
|
-
runtime.initialize()
|
|
61
|
-
isScanning = true
|
|
62
|
-
runtime.startDeviceScan { [weak self] json in
|
|
63
|
-
self?.handleRuntimeScanJson(json)
|
|
64
|
-
}
|
|
65
|
-
return
|
|
66
|
-
}
|
|
67
|
-
#endif
|
|
68
43
|
pendingStart = true
|
|
69
44
|
if central.state == .poweredOn {
|
|
70
45
|
beginScanningIfNeeded()
|
|
@@ -74,12 +49,8 @@ public final class TempivoSensorBeaconScanner: NSObject {
|
|
|
74
49
|
/// Stops scanning and clears pending start.
|
|
75
50
|
public func stopScanning() {
|
|
76
51
|
pendingStart = false
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
#endif
|
|
80
|
-
if isScanning {
|
|
81
|
-
central.stopScan()
|
|
82
|
-
}
|
|
52
|
+
guard isScanning else { return }
|
|
53
|
+
central.stopScan()
|
|
83
54
|
isScanning = false
|
|
84
55
|
}
|
|
85
56
|
|
|
@@ -162,55 +133,8 @@ public final class TempivoSensorBeaconScanner: NSObject {
|
|
|
162
133
|
bluetoothMacAddress: bluetoothMacAddress,
|
|
163
134
|
reading: reading
|
|
164
135
|
)
|
|
165
|
-
if let serial = (serialNumber ?? reading?.serialNumber).map({ TempivoSensorBeaconDecoder.macKey($0) }),
|
|
166
|
-
!serial.isEmpty {
|
|
167
|
-
let seen = TempivoSensorSeenDevice(
|
|
168
|
-
deviceId: deviceId,
|
|
169
|
-
bluetoothMac: bluetoothMacAddress ?? reading?.bluetoothMacAddress,
|
|
170
|
-
serial: serial,
|
|
171
|
-
firmware: reading?.firmware
|
|
172
|
-
)
|
|
173
|
-
lastSeen[serial] = seen
|
|
174
|
-
}
|
|
175
136
|
delegate?.sensorBeaconScanner(self, didDiscover: discovery)
|
|
176
137
|
}
|
|
177
|
-
|
|
178
|
-
private func handleRuntimeScanJson(_ raw: String) {
|
|
179
|
-
guard let data = raw.data(using: .utf8),
|
|
180
|
-
let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
181
|
-
else { return }
|
|
182
|
-
let deviceId = (obj["deviceId"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
183
|
-
let serial = (obj["serialNumber"] as? String).map { TempivoSensorBeaconDecoder.macKey($0) } ?? ""
|
|
184
|
-
guard !deviceId.isEmpty, !serial.isEmpty else { return }
|
|
185
|
-
let mac = obj["bluetoothMacAddress"] as? String
|
|
186
|
-
let rssi = (obj["rssi"] as? NSNumber)?.intValue ?? (obj["rssi"] as? Int) ?? 0
|
|
187
|
-
let firmware = obj["firmware"] as? String
|
|
188
|
-
let reading = SensorBeaconReading(
|
|
189
|
-
serialNumber: serial,
|
|
190
|
-
bluetoothMacAddress: mac,
|
|
191
|
-
firmware: firmware
|
|
192
|
-
)
|
|
193
|
-
rememberSeen(deviceId: deviceId, bluetoothMac: mac, serial: serial, firmware: firmware)
|
|
194
|
-
delegate?.sensorBeaconScanner(
|
|
195
|
-
self,
|
|
196
|
-
didDiscover: SensorBeaconDiscovery(
|
|
197
|
-
deviceId: deviceId,
|
|
198
|
-
rssi: rssi,
|
|
199
|
-
serialNumber: serial,
|
|
200
|
-
bluetoothMacAddress: mac,
|
|
201
|
-
reading: reading
|
|
202
|
-
)
|
|
203
|
-
)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
private func rememberSeen(deviceId: String, bluetoothMac: String?, serial: String, firmware: String?) {
|
|
207
|
-
lastSeen[serial] = TempivoSensorSeenDevice(
|
|
208
|
-
deviceId: deviceId,
|
|
209
|
-
bluetoothMac: bluetoothMac,
|
|
210
|
-
serial: serial,
|
|
211
|
-
firmware: firmware
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
138
|
}
|
|
215
139
|
|
|
216
140
|
extension TempivoSensorBeaconScanner: CBCentralManagerDelegate {
|
|
@@ -101,18 +101,3 @@ public struct SensorBeaconDiscovery: Sendable, Equatable {
|
|
|
101
101
|
self.reading = reading
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
|
|
105
|
-
/// Last advertisement used to resolve GATT connect (scan device id + firmware).
|
|
106
|
-
public struct TempivoSensorSeenDevice: Sendable, Equatable {
|
|
107
|
-
public let deviceId: String
|
|
108
|
-
public let bluetoothMac: String?
|
|
109
|
-
public let serial: String
|
|
110
|
-
public let firmware: String?
|
|
111
|
-
|
|
112
|
-
public init(deviceId: String, bluetoothMac: String?, serial: String, firmware: String?) {
|
|
113
|
-
self.deviceId = deviceId
|
|
114
|
-
self.bluetoothMac = bluetoothMac
|
|
115
|
-
self.serial = serial
|
|
116
|
-
self.firmware = firmware
|
|
117
|
-
}
|
|
118
|
-
}
|
|
@@ -17,21 +17,7 @@ public struct TempivoSensorError: Error, LocalizedError, Sendable {
|
|
|
17
17
|
|
|
18
18
|
public init(_ code: TempivoSensorErrorCode, _ message: String) {
|
|
19
19
|
self.code = code
|
|
20
|
-
self.message =
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
public static func sanitizeClientMessage(_ raw: String) -> String {
|
|
24
|
-
var text = raw
|
|
25
|
-
let full = NSRange(location: 0, length: text.utf16.count)
|
|
26
|
-
if let regex = try? NSRegularExpression(pattern: #"(?i)pl\.[A-Za-z][A-Za-z0-9_.$]*"#) {
|
|
27
|
-
text = regex.stringByReplacingMatches(in: text, range: full, withTemplate: "")
|
|
28
|
-
}
|
|
29
|
-
let full2 = NSRange(location: 0, length: text.utf16.count)
|
|
30
|
-
if let regex = try? NSRegularExpression(pattern: #"(?i)\befento[A-Za-z0-9_$]*"#) {
|
|
31
|
-
text = regex.stringByReplacingMatches(in: text, range: full2, withTemplate: "")
|
|
32
|
-
}
|
|
33
|
-
text = text.replacingOccurrences(of: #"\s+"#, with: " ", options: .regularExpression)
|
|
34
|
-
return text.trimmingCharacters(in: CharacterSet(charactersIn: " :-.\n\t/"))
|
|
20
|
+
self.message = message
|
|
35
21
|
}
|
|
36
22
|
|
|
37
23
|
public var errorDescription: String? { message }
|
|
@@ -16,7 +16,11 @@ public enum TempivoSensorProfile {
|
|
|
16
16
|
}
|
|
17
17
|
let body: [String: Any]
|
|
18
18
|
if let nested = obj["profile"] as? [String: Any],
|
|
19
|
-
nested["temperatureAlerts"] != nil ||
|
|
19
|
+
nested["temperatureAlerts"] != nil ||
|
|
20
|
+
nested["schedule"] != nil ||
|
|
21
|
+
nested["slug"] != nil ||
|
|
22
|
+
nested["measurementIntervalMinutes"] != nil ||
|
|
23
|
+
nested["transmissionIntervalSeconds"] != nil {
|
|
20
24
|
body = nested
|
|
21
25
|
} else {
|
|
22
26
|
body = obj
|
|
@@ -26,7 +30,13 @@ public enum TempivoSensorProfile {
|
|
|
26
30
|
}
|
|
27
31
|
let rawAlerts = body["temperatureAlerts"] as? [Any] ?? []
|
|
28
32
|
let alerts = try rawAlerts.enumerated().map { try parseAlert($0.element, index: $0.offset) }
|
|
29
|
-
|
|
33
|
+
let intervals = try parseOptionalIntervals(body)
|
|
34
|
+
return TempivoSensorConfiguration(
|
|
35
|
+
temperatureAlerts: alerts,
|
|
36
|
+
schedule: try parseSchedule(body["schedule"] as? [String: Any]),
|
|
37
|
+
measurementIntervalMinutes: intervals.measurement,
|
|
38
|
+
transmissionIntervalSeconds: intervals.transmission
|
|
39
|
+
)
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
public static func jsonString(_ cfg: TempivoSensorConfiguration) throws -> String {
|
|
@@ -56,11 +66,55 @@ public enum TempivoSensorProfile {
|
|
|
56
66
|
"utcOffsetMinutes": cfg.schedule.utcOffsetMinutes,
|
|
57
67
|
]
|
|
58
68
|
}
|
|
59
|
-
|
|
69
|
+
var root: [String: Any] = ["temperatureAlerts": alerts, "schedule": schedule]
|
|
70
|
+
if let m = cfg.measurementIntervalMinutes { root["measurementIntervalMinutes"] = m }
|
|
71
|
+
if let t = cfg.transmissionIntervalSeconds { root["transmissionIntervalSeconds"] = t }
|
|
60
72
|
let data = try JSONSerialization.data(withJSONObject: root, options: [])
|
|
61
73
|
return String(data: data, encoding: .utf8) ?? "{}"
|
|
62
74
|
}
|
|
63
75
|
|
|
76
|
+
private static let minMeasurementMinutes: Double = 60
|
|
77
|
+
private static let maxMeasurementMinutes: Double = 600
|
|
78
|
+
private static let minTransmissionSeconds = 3600
|
|
79
|
+
private static let maxTransmissionSeconds = 604_800
|
|
80
|
+
|
|
81
|
+
private static func parseOptionalIntervals(_ body: [String: Any]) throws -> (measurement: Double?, transmission: Int?) {
|
|
82
|
+
var measurement: Double?
|
|
83
|
+
var transmission: Int?
|
|
84
|
+
if body["measurementIntervalMinutes"] != nil {
|
|
85
|
+
guard let n = number(body["measurementIntervalMinutes"]), n > 0 else {
|
|
86
|
+
throw TempivoSensorError(.invalidConfig, "measurementIntervalMinutes must be a positive number.")
|
|
87
|
+
}
|
|
88
|
+
if n < minMeasurementMinutes {
|
|
89
|
+
throw TempivoSensorError(.invalidConfig, "measurementIntervalMinutes must be at least \(Int(minMeasurementMinutes)) (1 hour).")
|
|
90
|
+
}
|
|
91
|
+
if n > maxMeasurementMinutes {
|
|
92
|
+
throw TempivoSensorError(.invalidConfig, "measurementIntervalMinutes must be at most \(Int(maxMeasurementMinutes)).")
|
|
93
|
+
}
|
|
94
|
+
measurement = n
|
|
95
|
+
}
|
|
96
|
+
if body["transmissionIntervalSeconds"] != nil {
|
|
97
|
+
guard let n = number(body["transmissionIntervalSeconds"]), n > 0, n == floor(n) else {
|
|
98
|
+
throw TempivoSensorError(.invalidConfig, "transmissionIntervalSeconds must be a positive integer.")
|
|
99
|
+
}
|
|
100
|
+
let sec = Int(n)
|
|
101
|
+
if sec < minTransmissionSeconds {
|
|
102
|
+
throw TempivoSensorError(.invalidConfig, "transmissionIntervalSeconds must be at least \(minTransmissionSeconds) (1 hour).")
|
|
103
|
+
}
|
|
104
|
+
if sec > maxTransmissionSeconds {
|
|
105
|
+
throw TempivoSensorError(.invalidConfig, "transmissionIntervalSeconds must be at most \(maxTransmissionSeconds).")
|
|
106
|
+
}
|
|
107
|
+
transmission = sec
|
|
108
|
+
}
|
|
109
|
+
if let measurement, let transmission {
|
|
110
|
+
let maxTx = measurement * 60 * 60
|
|
111
|
+
if Double(transmission) > maxTx {
|
|
112
|
+
throw TempivoSensorError(.invalidConfig, "transmissionIntervalSeconds must be at most 60 times the measurement interval.")
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return (measurement, transmission)
|
|
116
|
+
}
|
|
117
|
+
|
|
64
118
|
private static func parseAlert(_ raw: Any, index: Int) throws -> TempivoTemperatureAlert {
|
|
65
119
|
guard let item = raw as? [String: Any] else {
|
|
66
120
|
throw TempivoSensorError(.invalidConfig, "temperatureAlerts[\(index)] must be an object.")
|
|
@@ -30,23 +30,6 @@ public enum TempivoSensorQrParser {
|
|
|
30
30
|
public static let defaultModel = "HC7"
|
|
31
31
|
|
|
32
32
|
public static func sessionType(fromModel model: String?) -> TempivoSensorSessionType {
|
|
33
|
-
sessionType(fromModel: model, firmware: nil)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
public static func sessionType(fromModel model: String?, firmware: String?) -> TempivoSensorSessionType {
|
|
37
|
-
if let firmware {
|
|
38
|
-
let t = firmware.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
39
|
-
if t.range(of: "^FW\\s*5", options: [.regularExpression, .caseInsensitive]) != nil {
|
|
40
|
-
return .legacy
|
|
41
|
-
}
|
|
42
|
-
if let match = t.range(of: "^(\\d+)\\.\\d+", options: .regularExpression) {
|
|
43
|
-
let majorStr = String(t[match]).split(separator: ".").first.flatMap { Int($0) }
|
|
44
|
-
if let major = majorStr {
|
|
45
|
-
if major >= 7 { return .modern }
|
|
46
|
-
if major >= 1 { return .legacy }
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
33
|
guard let model, !model.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
|
51
34
|
return .modern
|
|
52
35
|
}
|
|
@@ -15,19 +15,10 @@ public final class TempivoSensorSession: @unchecked Sendable {
|
|
|
15
15
|
|
|
16
16
|
public init() {}
|
|
17
17
|
|
|
18
|
-
public func connect(
|
|
19
|
-
|
|
20
|
-
bluetoothMac: String,
|
|
21
|
-
pin: String,
|
|
22
|
-
sessionType: TempivoSensorSessionType = .modern,
|
|
23
|
-
deviceId: String? = nil,
|
|
24
|
-
firmware: String? = nil
|
|
25
|
-
) throws {
|
|
26
|
-
let trimmedPin = pin.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
27
|
-
guard let pinInt = Int32(trimmedPin) else {
|
|
18
|
+
public func connect(serial: String, bluetoothMac: String, pin: String, sessionType: TempivoSensorSessionType = .modern) throws {
|
|
19
|
+
guard let pinInt = Int32(pin.trimmingCharacters(in: .whitespacesAndNewlines)) else {
|
|
28
20
|
throw TempivoSensorError(.invalidPin, "PIN must be numeric.")
|
|
29
21
|
}
|
|
30
|
-
let resolved = TempivoSensorQrParser.sessionType(fromModel: sessionType == .legacy ? "HC5" : "HC7", firmware: firmware)
|
|
31
22
|
#if canImport(TempivoSensorBridge)
|
|
32
23
|
lock.lock()
|
|
33
24
|
defer { lock.unlock() }
|
|
@@ -37,9 +28,7 @@ public final class TempivoSensorSession: @unchecked Sendable {
|
|
|
37
28
|
serial: TempivoSensorQrParser.normalizeSerial(serial),
|
|
38
29
|
bluetoothMac: bluetoothMac,
|
|
39
30
|
pin: pinInt,
|
|
40
|
-
legacy:
|
|
41
|
-
deviceId: deviceId?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "",
|
|
42
|
-
encryptionKey: trimmedPin
|
|
31
|
+
legacy: sessionType == .legacy
|
|
43
32
|
)
|
|
44
33
|
)
|
|
45
34
|
try throwIfBridgeError(result)
|
|
@@ -49,22 +38,8 @@ public final class TempivoSensorSession: @unchecked Sendable {
|
|
|
49
38
|
#endif
|
|
50
39
|
}
|
|
51
40
|
|
|
52
|
-
public func connect(qr: TempivoSensorQr
|
|
53
|
-
|
|
54
|
-
if deviceId.isEmpty {
|
|
55
|
-
throw TempivoSensorError(
|
|
56
|
-
.connectFailed,
|
|
57
|
-
"Sensor not in last scan. Scan first, then connect."
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
try connect(
|
|
61
|
-
serial: qr.serial,
|
|
62
|
-
bluetoothMac: seen?.bluetoothMac ?? qr.bluetoothMac,
|
|
63
|
-
pin: qr.pin,
|
|
64
|
-
sessionType: qr.sessionType,
|
|
65
|
-
deviceId: deviceId,
|
|
66
|
-
firmware: seen?.firmware
|
|
67
|
-
)
|
|
41
|
+
public func connect(qr: TempivoSensorQr) throws {
|
|
42
|
+
try connect(serial: qr.serial, bluetoothMac: qr.bluetoothMac, pin: qr.pin, sessionType: qr.sessionType)
|
|
68
43
|
}
|
|
69
44
|
|
|
70
45
|
public func disconnect() {
|
|
@@ -173,17 +148,14 @@ public final class TempivoSensorSession: @unchecked Sendable {
|
|
|
173
148
|
|
|
174
149
|
private func throwIfBridgeError(_ result: [String: Any]) throws {
|
|
175
150
|
guard let code = intValue(result["errorCode"]), code != 0 else { return }
|
|
176
|
-
let detail = TempivoSensorError.sanitizeClientMessage(
|
|
177
|
-
(result["message"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
178
|
-
)
|
|
179
151
|
switch code {
|
|
180
152
|
case 1: throw TempivoSensorError(.invalidPin, "Invalid PIN.")
|
|
181
153
|
case 2: throw TempivoSensorError(.notConnected, "Not connected.")
|
|
182
154
|
case 3: throw TempivoSensorError(.unsupportedCommand, "Unsupported command.")
|
|
183
155
|
case 4: throw TempivoSensorError(.runtimeUnavailable, "Sensor GATT is not available on this device.")
|
|
184
|
-
case 5: throw TempivoSensorError(.connectFailed,
|
|
185
|
-
case 6: throw TempivoSensorError(.invalidConfig,
|
|
186
|
-
default: throw TempivoSensorError(.unknown,
|
|
156
|
+
case 5: throw TempivoSensorError(.connectFailed, "Could not connect.")
|
|
157
|
+
case 6: throw TempivoSensorError(.invalidConfig, (result["message"] as? String) ?? "Invalid configuration.")
|
|
158
|
+
default: throw TempivoSensorError(.unknown, "Sensor request failed.")
|
|
187
159
|
}
|
|
188
160
|
}
|
|
189
161
|
}
|
|
@@ -65,10 +65,19 @@ public struct TempivoSchedule: Sendable, Equatable {
|
|
|
65
65
|
public struct TempivoSensorConfiguration: Sendable, Equatable {
|
|
66
66
|
public var temperatureAlerts: [TempivoTemperatureAlert]
|
|
67
67
|
public var schedule: TempivoSchedule
|
|
68
|
+
public var measurementIntervalMinutes: Double?
|
|
69
|
+
public var transmissionIntervalSeconds: Int?
|
|
68
70
|
|
|
69
|
-
public init(
|
|
71
|
+
public init(
|
|
72
|
+
temperatureAlerts: [TempivoTemperatureAlert],
|
|
73
|
+
schedule: TempivoSchedule,
|
|
74
|
+
measurementIntervalMinutes: Double? = nil,
|
|
75
|
+
transmissionIntervalSeconds: Int? = nil
|
|
76
|
+
) {
|
|
70
77
|
self.temperatureAlerts = temperatureAlerts
|
|
71
78
|
self.schedule = schedule
|
|
79
|
+
self.measurementIntervalMinutes = measurementIntervalMinutes
|
|
80
|
+
self.transmissionIntervalSeconds = transmissionIntervalSeconds
|
|
72
81
|
}
|
|
73
82
|
}
|
|
74
83
|
|
|
@@ -28,13 +28,8 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
|
|
|
28
28
|
|
|
29
29
|
AsyncFunction("connect") { (qrJson: String) -> [String: Any] in
|
|
30
30
|
do {
|
|
31
|
-
self.scanner?.stopScanning()
|
|
32
|
-
if !Thread.isMainThread {
|
|
33
|
-
Thread.sleep(forTimeInterval: 0.2)
|
|
34
|
-
}
|
|
35
31
|
let qr = try TempivoSensorQrParser.parse(qrJson)
|
|
36
|
-
|
|
37
|
-
try self.session.connect(qr: qr, seen: seen)
|
|
32
|
+
try self.session.connect(qr: qr)
|
|
38
33
|
return self.qrPayload(qr)
|
|
39
34
|
} catch {
|
|
40
35
|
throw self.mapError(error)
|
|
@@ -194,11 +189,7 @@ public class TempivoSensorBeaconModule: Module, TempivoSensorBeaconScannerDelega
|
|
|
194
189
|
if let sensor = error as? TempivoSensorError {
|
|
195
190
|
return SensorBeaconException(code: sensor.code.rawValue, reason: sensor.message)
|
|
196
191
|
}
|
|
197
|
-
|
|
198
|
-
return SensorBeaconException(
|
|
199
|
-
code: "unknown",
|
|
200
|
-
reason: reason.isEmpty ? "Sensor request failed." : reason
|
|
201
|
-
)
|
|
192
|
+
return SensorBeaconException(code: "unknown", reason: error.localizedDescription)
|
|
202
193
|
}
|
|
203
194
|
}
|
|
204
195
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tempivo/sensor-beacon",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.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
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/tempivo/tempivo-app.git",
|
|
9
|
+
"directory": "packages/tempivo-sensor-beacon"
|
|
10
|
+
},
|
|
6
11
|
"author": "Tempivo",
|
|
7
12
|
"type": "module",
|
|
8
13
|
"main": "dist/index.js",
|