@tempivo/sensor-beacon 1.1.1 → 1.1.4

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 (32) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +38 -23
  3. package/android/build.gradle +2 -2
  4. package/android/src/main/java/expo/modules/tempivosensorbeacon/SensorBeaconNative.java +8 -0
  5. package/android/src/main/java/expo/modules/tempivosensorbeacon/TempivoSensorBeaconModule.kt +8 -0
  6. package/android-aar/library/build.gradle +5 -0
  7. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/EfentoRawConfigTransfer.kt +672 -0
  8. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/PartnerBleRules.kt +188 -52
  9. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorBleRuntime.kt +20 -0
  10. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorProfile.kt +28 -0
  11. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorSession.kt +142 -11
  12. package/android-aar/library/src/test/java/com/tempivo/sensor/beacon/PartnerBleRulesTest.kt +209 -0
  13. package/dist/native-helpers.js +3 -1
  14. package/dist/native-module.d.ts +10 -0
  15. package/dist/native-module.js +46 -3
  16. package/dist/partner-dual-range-fixture.d.ts +28 -0
  17. package/dist/partner-dual-range-fixture.js +29 -0
  18. package/dist/profile.js +66 -32
  19. package/dist/proto/decode-proto-config.d.ts +3 -0
  20. package/dist/proto/decode-proto-config.js +46 -0
  21. package/dist/proto/efento-protobuf-descriptor.d.ts +964 -0
  22. package/dist/proto/efento-protobuf-descriptor.js +727 -0
  23. package/dist/proto/partner-from-proto.d.ts +9 -0
  24. package/dist/proto/partner-from-proto.js +187 -0
  25. package/dist/proto/partner-to-proto.d.ts +12 -0
  26. package/dist/proto/partner-to-proto.js +211 -0
  27. package/dist/tempivo-sensor-beacon.aar +0 -0
  28. package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64/TempivoSensorBridge.framework/TempivoSensorBridge +0 -0
  29. package/ios/Sources/TempivoSensorBeacon/EfentoRawConfigTransfer.swift +601 -0
  30. package/ios/Sources/TempivoSensorBeacon/TempivoSensorSession.swift +99 -29
  31. package/ios/TempivoSensorBeaconModule.swift +9 -0
  32. package/package.json +6 -2
package/CHANGELOG.md CHANGED
@@ -6,6 +6,49 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ <!-- Publish trigger: 1.1.4 not on npm until CI Trusted Publisher (ci.yml) runs. -->
10
+
11
+ ## [1.1.4] - 2026-09-07
12
+
13
+ ### Fixed
14
+
15
+ - Android BLE get/set for alarm rules uses the same raw ProtoConfig GATT path as iOS (0x0C / 0x0D), with a persistent GATT link and `writeProtoConfigBase64`.
16
+ - Partner `getConfiguration` return value is only partner fields (no `configSource` / `protoBase64`).
17
+
18
+ ### Changed
19
+
20
+ - `setConfigurationJson` (JS): on iOS and Android, read baseline proto → patch partner alerts → raw GATT write.
21
+ - Vendor SDK remains for connect, scan, trigger, and calibration.
22
+
23
+ ## [1.1.3] - 2026-09-07
24
+
25
+ ### Fixed
26
+
27
+ - BLE set: if the device already has the same alarm rules, treat it as success (info), not a reject. Fail only when on-device rules actually differ after write.
28
+ - iOS BLE get/set for alarm rules uses raw ProtoConfig GATT (0x0C / 0x0D), not the vendor SDK rule APIs (HC7 SDK get returns empty rules; SDK set often fails with Invalid PIN).
29
+ - iOS: back-to-back config reads and writes no longer time out. The raw config link is now one persistent central/peripheral reused across commands, with one retry on failure.
30
+ - iOS BLE set only writes rules-related fields. Echoing the whole device read back exceeded the single-write BLE limit and would have rewritten server, APN, and token settings.
31
+ - BLE get: device intervals reported as `0` or `null` are omitted instead of failing the whole read.
32
+
33
+ ### Changed
34
+
35
+ - Partner `getConfiguration` / `setConfigurationJson` signatures unchanged.
36
+ - Vendor SDK is still used for connect, scan, trigger, and calibration.
37
+
38
+ ## [1.1.2] - 2026-09-05
39
+
40
+ ### Fixed
41
+
42
+ - BLE get: `schedule: { always: true }` no longer reads back as `weekdays: [6]`.
43
+ - BLE set: dual ambient + probe ranges with `transmitOnReturn` keep alerts on get (interleaved OR after each pair).
44
+ - Native compile fail-closed on bad/missing alert fields or full slots (no silent drop).
45
+ - Post-set readback requires alert count ≥ written count.
46
+ - iOS parse aligned with Android/TS (required thresholds, schedule fields, slot budget).
47
+
48
+ ### Changed
49
+
50
+ - README: range + `transmitOnReturn` uses **3** slots; weekday BLE get caveat.
51
+
9
52
  ## [1.1.1] - 2026-09-03
10
53
 
11
54
  ### Changed
package/README.md CHANGED
@@ -59,36 +59,40 @@ const sub = await addDeviceFoundListener((device) => {
59
59
  });
60
60
 
61
61
  await startScan();
62
+ // Stop scan before GATT connect. While connected, the sensor does not advertise.
62
63
 
63
64
  const qr = parseSensorQrJson(stickerQrText);
64
65
  await connect(qr);
65
- await setConfigurationJson({
66
- schedule: { always: true },
67
- temperatureAlerts: [
68
- {
69
- type: 'range',
70
- channel: 'probe',
71
- lowC: -30,
72
- highC: -10,
73
- hysteresisC: 0.5,
74
- },
75
- ],
76
- });
77
- // BLE write is on-device only. Cloud pending / last report update after a cellular uplink:
78
- await triggerTransmission();
79
- // Tempivo then drops from the cloud queue only fields the device already matches;
80
- // other queued Cellular fields can still downlink. To cancel the whole queue:
81
- // DELETE /api/v1/devices/{serial}/config (see docs/cellular-api.md).
82
- const applied = await getConfiguration();
83
- const cal = await getCalibration();
84
- await disconnect();
66
+ try {
67
+ await setConfigurationJson({
68
+ schedule: { always: true },
69
+ temperatureAlerts: [
70
+ {
71
+ type: 'range',
72
+ channel: 'probe',
73
+ lowC: -30,
74
+ highC: -10,
75
+ hysteresisC: 0.5,
76
+ },
77
+ ],
78
+ });
79
+ // BLE write is on-device only. Cloud pending / last report update after a cellular uplink:
80
+ await triggerTransmission();
81
+ // Tempivo then drops from the cloud queue only fields the device already matches;
82
+ // other queued Cellular fields can still downlink. To cancel the whole queue:
83
+ // DELETE /api/v1/devices/{serial}/config (see docs/cellular-api.md).
84
+ const applied = await getConfiguration();
85
+ const cal = await getCalibration();
86
+ } finally {
87
+ await disconnect(); // required so advertising / startScan can see the sensor again
88
+ }
85
89
  stopScan();
86
90
  sub.remove();
87
91
  ```
88
92
 
89
93
  `startScan` and `connect` request Bluetooth permission. Sticker PIN is required on `connect`. Wrong PIN is `invalidPin`.
90
94
 
91
- **Connect tips:** stop scan before GATT connect. Pass the scan row’s `deviceId` and MAC when they differ from the sticker serial (`parseSensorQrForConnect`). 5–6 digit PINs map to an encryption key internally; numeric `resetCode` alone is not enough on all devices.
95
+ **Connect tips:** stop scan before GATT connect. Pass the scan row’s `deviceId` and MAC when they differ from the sticker serial (`parseSensorQrForConnect`). 5–6 digit PINs map to an encryption key internally; numeric `resetCode` alone is not enough on all devices. Call `disconnect()` when finished. **While any device holds a GATT connection, the sensor does not advertise**, so `startScan` will not show beacon / live readings for that sensor until everyone disconnects (including other apps).
92
96
 
93
97
  If a bundler misses native BLE, import `@tempivo/sensor-beacon/react-native`.
94
98
 
@@ -129,6 +133,12 @@ const draft = draftSensorQrConnectJsonFromScanDevice(device);
129
133
  // Connect: parse sticker + merge selected row deviceId / MAC
130
134
  const qr = parseSensorQrForConnect(stickerJson, selectedDevice);
131
135
  await connect(qr);
136
+ try {
137
+ await setConfigurationJson(profileJson);
138
+ await getConfiguration();
139
+ } finally {
140
+ await disconnect();
141
+ }
132
142
 
133
143
  // Scan list: same reading line as in-app
134
144
  formatSensorReadingLine({ typeHex: 'temperature', text: '23.5 °C' });
@@ -263,7 +273,9 @@ When **both** are set on write, uplink must be at most **60×** the measurement
263
273
 
264
274
  Use `parseSensorConfigurationJson` to validate before write. Use `parseSensorConfigurationFromDevice` / `parseSensorConfigurationFromDeviceJson` when parsing a **readback** from the device (relaxed interval rules). For `GET /devices/config-profiles/{slug}`, use `configurationFromApiProfile(response.profile)` or pass the `{ profile }` envelope directly to `setConfigurationJson`.
265
275
 
266
- **Alert slot budget:** the device has **12** rule slots. A `range` with default `transmitOnReturn: true` uses **two** slots (breach + return). Up to **4** such ranges, or more min/max alerts if you disable `transmitOnReturn` on ranges.
276
+ **Alert slot budget:** the device has **12** rule slots. A `range` with default `transmitOnReturn: true` uses **three** slots (`low` + `high` + OR). Up to **4** such ranges. A lone `min`/`max` uses one; with return on a range disabled, a range uses two.
277
+
278
+ **Weekday schedules** (`Mon–Fri`, etc.): BLE **get** may not round-trip weekdays on current partner firmware (day-mask decode). Prefer `{ "always": true }` when possible. After set, if get looks wrong (e.g. only Sunday), trust what you wrote.
267
279
 
268
280
  **Each `temperatureAlerts[]` item**
269
281
 
@@ -309,6 +321,8 @@ Sticker QR is JSON. Parse with `parseSensorQrJson`.
309
321
 
310
322
  Filter manufacturer data on company id **0x026C**. Active scanning is on so scan-response packets arrive.
311
323
 
324
+ **Advertising vs GATT:** beacon packets (manufacturer `0x026C`, frames `0x03` / `0x04`) are only sent while the sensor is **not** in a GATT session. If your app, another phone, or another tool is connected, scan results for that sensor disappear or freeze until `disconnect()` (or the link drops). Do not expect live `telemetry.readings` during `connect`.
325
+
312
326
  | Field | Meaning |
313
327
  |------|---------|
314
328
  | `deviceId` | Platform BLE id. Often randomized. Not the sticker serial. |
@@ -391,6 +405,7 @@ try {
391
405
  val applied = session.getConfiguration()
392
406
  val cal = session.getCalibration()
393
407
  } finally {
408
+ // Without disconnect, the sensor will not advertise for startScan.
394
409
  session.disconnect()
395
410
  }
396
411
  ```
@@ -410,7 +425,7 @@ Needs a physical device for GATT. Simulator can scan advertisements only. Set **
410
425
  let qr = try TempivoSensorQrParser.parse(stickerQrText)
411
426
  let session = TempivoSensorSession()
412
427
  try session.connect(qr: qr)
413
- defer { session.disconnect() }
428
+ defer { session.disconnect() } // advertising resumes after disconnect
414
429
  try session.setConfigurationJson(profileJson)
415
430
  _ = try session.triggerTransmission()
416
431
  let applied = try session.getConfiguration()
@@ -4,14 +4,14 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'com.tempivo.sensor.beacon'
7
- version = '0.4.3'
7
+ version = '0.4.4'
8
8
 
9
9
  android {
10
10
  namespace "expo.modules.tempivosensorbeacon"
11
11
  defaultConfig {
12
12
  minSdk 26
13
13
  versionCode 1
14
- versionName "0.4.3"
14
+ versionName "0.4.4"
15
15
  }
16
16
  compileOptions {
17
17
  sourceCompatibility JavaVersion.VERSION_17
@@ -95,6 +95,14 @@ final class SensorBeaconNative {
95
95
  }
96
96
  }
97
97
 
98
+ void writeProtoConfigBase64(String base64) {
99
+ try {
100
+ requireSessionConnected().writeProtoConfigBase64Blocking(base64);
101
+ } catch (Throwable error) {
102
+ throw wrap(error);
103
+ }
104
+ }
105
+
98
106
  void setConfigurationJson(String json) {
99
107
  try {
100
108
  requireSessionConnected().setConfigurationJsonBlocking(json);
@@ -68,6 +68,14 @@ class TempivoSensorBeaconModule : Module() {
68
68
  }
69
69
  }
70
70
 
71
+ AsyncFunction("writeProtoConfigBase64") { base64: String ->
72
+ try {
73
+ native.writeProtoConfigBase64(base64)
74
+ } catch (error: Throwable) {
75
+ throw error.toSensorException()
76
+ }
77
+ }
78
+
71
79
  AsyncFunction("setConfigurationJson") { json: String ->
72
80
  try {
73
81
  native.setConfigurationJson(json)
@@ -20,6 +20,10 @@ android {
20
20
  kotlinOptions {
21
21
  jvmTarget = "17"
22
22
  }
23
+
24
+ testOptions {
25
+ unitTests.returnDefaultValues = true
26
+ }
23
27
  }
24
28
 
25
29
  dependencies {
@@ -27,4 +31,5 @@ dependencies {
27
31
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2"
28
32
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2"
29
33
  implementation "pl.efento.mobile:bluetooth:2.3.0"
34
+ testImplementation "junit:junit:4.13.2"
30
35
  }