@mentra/bluetooth-sdk 0.1.21-beta.2 → 0.1.21-beta.5
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 +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +17 -9
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +4 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/BesOtaHeartbeatGuard.kt +49 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/BesOtaProgressMapper.kt +27 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +222 -110
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/BesOtaHeartbeatGuardTest.kt +73 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/BesOtaProgressMapperTest.kt +67 -0
- package/build/BluetoothSdk.types.d.ts +2 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/ios/Source/BluetoothSdkDefaults.swift +1 -1
- package/ios/Source/DeviceManager.swift +17 -11
- package/ios/Source/DeviceStore.swift +3 -0
- package/ios/Source/sgcs/G2.swift +108 -41
- package/ios/Source/sgcs/MentraLive.swift +76 -61
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +2 -0
package/README.md
CHANGED
|
@@ -158,7 +158,7 @@ enterprise device deployments:
|
|
|
158
158
|
|
|
159
159
|
- `bluetooth_sdk_started`: sent once per app runtime after the native SDK starts.
|
|
160
160
|
- `bluetooth_sdk_glasses_connected`: sent when SDK status transitions from not connected to connected.
|
|
161
|
-
- `bluetooth_sdk_glasses_identified`: sent once per connection after the SDK receives a valid manufacturing serial from the glasses. This fires for every supported model that reports a serial. G1 and Ar99 decode the serial from the glasses' BLE advertisement
|
|
161
|
+
- `bluetooth_sdk_glasses_identified`: sent once per connection after the SDK receives a valid manufacturing serial from the glasses. This fires for every supported model that reports a serial. G1 and Ar99 decode the serial from the glasses' BLE advertisement. Mentra Live reports the product serial provisioned by its Android firmware through `asg_client`.
|
|
162
162
|
|
|
163
163
|
Analytics delivery is fire-and-forget: events are submitted asynchronously, do
|
|
164
164
|
not block Bluetooth SDK behavior, and are not retried if delivery fails.
|
|
@@ -948,15 +948,20 @@ class DeviceManager {
|
|
|
948
948
|
|
|
949
949
|
when (currentViewState.layoutType) {
|
|
950
950
|
"text_wall" -> {
|
|
951
|
-
sgc?.sendTextWall(currentViewState.text)
|
|
951
|
+
sgc?.sendTextWall(parsePlaceholders(currentViewState.text))
|
|
952
952
|
}
|
|
953
953
|
|
|
954
954
|
"double_text_wall" -> {
|
|
955
|
-
sgc?.sendDoubleTextWall(
|
|
955
|
+
sgc?.sendDoubleTextWall(
|
|
956
|
+
parsePlaceholders(currentViewState.topText),
|
|
957
|
+
parsePlaceholders(currentViewState.bottomText)
|
|
958
|
+
)
|
|
956
959
|
}
|
|
957
960
|
|
|
958
961
|
"reference_card" -> {
|
|
959
|
-
|
|
962
|
+
val title = parsePlaceholders(currentViewState.title)
|
|
963
|
+
val text = parsePlaceholders(currentViewState.text)
|
|
964
|
+
sgc?.sendTextWall("$title\n\n$text")
|
|
960
965
|
}
|
|
961
966
|
|
|
962
967
|
"bitmap_view" -> {
|
|
@@ -973,7 +978,7 @@ class DeviceManager {
|
|
|
973
978
|
|
|
974
979
|
"positioned_text" -> {
|
|
975
980
|
sgc?.sendPositionedText(
|
|
976
|
-
currentViewState.text,
|
|
981
|
+
parsePlaceholders(currentViewState.text),
|
|
977
982
|
currentViewState.bmpX ?: 0,
|
|
978
983
|
currentViewState.bmpY ?: 0,
|
|
979
984
|
currentViewState.bmpWidth ?: 576,
|
|
@@ -994,6 +999,8 @@ class DeviceManager {
|
|
|
994
999
|
}
|
|
995
1000
|
|
|
996
1001
|
private fun parsePlaceholders(text: String): String {
|
|
1002
|
+
if (!text.contains('$')) return text
|
|
1003
|
+
|
|
997
1004
|
val dateFormatter = SimpleDateFormat("M/dd, h:mm", Locale.getDefault())
|
|
998
1005
|
val formattedDate = dateFormatter.format(Date())
|
|
999
1006
|
|
|
@@ -2050,11 +2057,13 @@ class DeviceManager {
|
|
|
2050
2057
|
shouldSendBootingMessage = true // Reset for next first connect
|
|
2051
2058
|
// clear glasses properties:
|
|
2052
2059
|
DeviceStore.apply("glasses", "deviceModel", "")
|
|
2053
|
-
//
|
|
2054
|
-
// previously connected pair
|
|
2055
|
-
// connection (e.g. switching from G1/Ar99, which populate it from the
|
|
2056
|
-
// advertisement, to a model that never writes it, like G2).
|
|
2060
|
+
// Device identifiers are session-bound. Clear them on every disconnect so a
|
|
2061
|
+
// previously connected pair can never be reported for the next connection.
|
|
2057
2062
|
DeviceStore.apply("glasses", "serialNumber", "")
|
|
2063
|
+
DeviceStore.apply("glasses", "bluetoothMacAddress", "")
|
|
2064
|
+
DeviceStore.apply("glasses", "leftMacAddress", "")
|
|
2065
|
+
DeviceStore.apply("glasses", "rightMacAddress", "")
|
|
2066
|
+
DeviceStore.apply("glasses", "macAddress", "")
|
|
2058
2067
|
DeviceStore.apply("glasses", "fullyBooted", false)
|
|
2059
2068
|
DeviceStore.apply("glasses", "connected", false)
|
|
2060
2069
|
DeviceStore.apply(
|
|
@@ -2164,4 +2173,3 @@ class DeviceManager {
|
|
|
2164
2173
|
}
|
|
2165
2174
|
}
|
|
2166
2175
|
}
|
|
2167
|
-
|
|
@@ -73,6 +73,10 @@ object DeviceStore {
|
|
|
73
73
|
store.set("glasses", "caseCharging", false)
|
|
74
74
|
store.set("glasses", "caseBatteryLevel", -1)
|
|
75
75
|
store.set("glasses", "headUp", false)
|
|
76
|
+
store.set("glasses", "bluetoothMacAddress", "")
|
|
77
|
+
store.set("glasses", "leftMacAddress", "")
|
|
78
|
+
store.set("glasses", "rightMacAddress", "")
|
|
79
|
+
store.set("glasses", "macAddress", "")
|
|
76
80
|
store.set("glasses", "serialNumber", "")
|
|
77
81
|
store.set("glasses", "style", "")
|
|
78
82
|
store.set("glasses", "color", "")
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
package com.mentra.bluetoothsdk.sgcs
|
|
2
|
+
|
|
3
|
+
import java.util.Locale
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Temporarily suppresses phone heartbeats while BES OTA owns the ASG-to-BES UART.
|
|
7
|
+
*
|
|
8
|
+
* The lease is renewable so a lost terminal status cannot disable heartbeats permanently. It is
|
|
9
|
+
* deliberately longer than ASG's 30-second BES response watchdog: after the lease expires, the
|
|
10
|
+
* raw transfer has already completed or failed and ordinary UART liveness traffic is safe again.
|
|
11
|
+
*/
|
|
12
|
+
internal class BesOtaHeartbeatGuard(
|
|
13
|
+
private val leaseDurationMs: Long = DEFAULT_LEASE_DURATION_MS,
|
|
14
|
+
) {
|
|
15
|
+
@Volatile private var suppressionDeadlineMs = 0L
|
|
16
|
+
|
|
17
|
+
init {
|
|
18
|
+
require(leaseDurationMs > 0) { "leaseDurationMs must be positive" }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
fun refresh(nowMs: Long) {
|
|
22
|
+
suppressionDeadlineMs =
|
|
23
|
+
if (nowMs > Long.MAX_VALUE - leaseDurationMs) Long.MAX_VALUE
|
|
24
|
+
else nowMs + leaseDurationMs
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
fun clear() {
|
|
28
|
+
suppressionDeadlineMs = 0L
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fun observeOtaStatus(stepType: String, phase: String, status: String, nowMs: Long) {
|
|
32
|
+
if (!stepType.equals("bes", ignoreCase = true) ||
|
|
33
|
+
!phase.equals("install", ignoreCase = true)
|
|
34
|
+
) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
when (status.lowercase(Locale.US)) {
|
|
39
|
+
"failed", "complete", "step_complete", "finished", "success" -> clear()
|
|
40
|
+
else -> refresh(nowMs)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fun shouldSuppress(nowMs: Long): Boolean = nowMs < suppressionDeadlineMs
|
|
45
|
+
|
|
46
|
+
companion object {
|
|
47
|
+
internal const val DEFAULT_LEASE_DURATION_MS = 120_000L
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package com.mentra.bluetoothsdk.sgcs
|
|
2
|
+
|
|
3
|
+
internal data class BesOtaProgressMapping(
|
|
4
|
+
val status: String,
|
|
5
|
+
val progress: Int,
|
|
6
|
+
val errorMessage: String? = null,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
/** Map the BES sr_adota protocol without inferring terminal state from transfer progress. */
|
|
10
|
+
internal fun mapBesOtaProgress(
|
|
11
|
+
type: String,
|
|
12
|
+
rawProgress: Int,
|
|
13
|
+
message: String?,
|
|
14
|
+
): BesOtaProgressMapping {
|
|
15
|
+
val roundedProgress = (((rawProgress.coerceIn(0, 100) + 2) / 5) * 5).coerceAtMost(100)
|
|
16
|
+
return when {
|
|
17
|
+
type == "success" -> BesOtaProgressMapping("FINISHED", 100)
|
|
18
|
+
type == "error" || type == "fail" ->
|
|
19
|
+
BesOtaProgressMapping(
|
|
20
|
+
"FAILED",
|
|
21
|
+
roundedProgress,
|
|
22
|
+
message?.takeIf { it.isNotBlank() } ?: "BES update failed",
|
|
23
|
+
)
|
|
24
|
+
// A raw update:100 is emitted before whole-image CRC/apply and is not terminal.
|
|
25
|
+
else -> BesOtaProgressMapping("PROGRESS", minOf(roundedProgress, 95))
|
|
26
|
+
}
|
|
27
|
+
}
|