@mentra/bluetooth-sdk 0.1.21-beta.0 → 0.1.21-beta.1
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 +3 -0
- package/android/src/main/AndroidManifest.xml +3 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkAnalytics.kt +34 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkDefaults.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +43 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +7 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +82 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +5 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +32 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +43 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +17 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +53 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Ar99.java +2653 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +161 -14
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +3 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +38 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +7 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BleJsonCompact.java +8 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +2 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/PhoneAudioMonitor.kt +45 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/StreamStatusEventTest.kt +29 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +30 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/BleJsonCompactTest.java +20 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
- package/build/BluetoothSdk.types.d.ts +58 -25
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +2 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +1 -0
- package/build/_internal.d.ts.map +1 -1
- package/build/_internal.js +1 -0
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +8 -2
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +6 -3
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/cameraRequestPayload.d.ts +1 -1
- package/build/_private/cameraRequestPayload.d.ts.map +1 -1
- package/build/_private/cameraRequestPayload.js +6 -0
- package/build/_private/cameraRequestPayload.js.map +1 -1
- package/build/_private/photoRequestPayload.d.ts.map +1 -1
- package/build/_private/photoRequestPayload.js +10 -1
- package/build/_private/photoRequestPayload.js.map +1 -1
- package/build/_private/sdkOtaManifest.d.ts +20 -0
- package/build/_private/sdkOtaManifest.d.ts.map +1 -0
- package/build/_private/sdkOtaManifest.js +28 -0
- package/build/_private/sdkOtaManifest.js.map +1 -0
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +7 -1
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +41 -2
- package/ios/Source/BluetoothSdkDefaults.swift +2 -1
- package/ios/Source/Bridge.swift +15 -3
- package/ios/Source/DeviceManager.swift +67 -17
- package/ios/Source/DeviceStore.swift +5 -1
- package/ios/Source/MentraBluetoothSDK.swift +37 -8
- package/ios/Source/OtaManifest.swift +47 -4
- package/ios/Source/camera/CameraModels.swift +20 -5
- package/ios/Source/internal/BleTraceLogger.swift +1 -1
- package/ios/Source/internal/BluetoothSdkAnalytics.swift +39 -0
- package/ios/Source/sgcs/Ar99.swift +2451 -0
- package/ios/Source/sgcs/MentraLive.swift +174 -38
- package/ios/Source/sgcs/SGCManager.swift +8 -0
- package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
- package/ios/Source/streaming/StreamModels.swift +62 -5
- package/ios/Source/types/DeviceModels.swift +13 -0
- package/ios/Source/utils/BleJsonCompact.swift +8 -0
- package/ios/Source/utils/Constants.swift +2 -0
- package/ios/Source/utils/PhoneAudioMonitor.swift +4 -0
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +61 -28
- package/src/_internal.ts +1 -0
- package/src/_private/BluetoothSdkModule.ts +20 -6
- package/src/_private/cameraRequestPayload.ts +8 -2
- package/src/_private/photoRequestPayload.ts +12 -2
- package/src/_private/sdkOtaManifest.ts +31 -0
- package/src/index.ts +9 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
package com.mentra.bluetoothsdk
|
|
2
|
+
|
|
3
|
+
import org.json.JSONObject
|
|
4
|
+
import org.junit.Assert.assertEquals
|
|
5
|
+
import org.junit.Assert.assertFalse
|
|
6
|
+
import org.junit.Assert.assertTrue
|
|
7
|
+
import org.junit.Assert.fail
|
|
8
|
+
import org.junit.Test
|
|
9
|
+
import org.junit.runner.RunWith
|
|
10
|
+
import org.robolectric.RobolectricTestRunner
|
|
11
|
+
import org.robolectric.annotation.Config
|
|
12
|
+
|
|
13
|
+
@RunWith(RobolectricTestRunner::class)
|
|
14
|
+
@Config(sdk = [33])
|
|
15
|
+
class OtaManifestDowngradeTest {
|
|
16
|
+
|
|
17
|
+
private fun manifest(versionCode: Long): JSONObject =
|
|
18
|
+
JSONObject()
|
|
19
|
+
.put("apps", JSONObject().put("com.mentra.asg_client", JSONObject().put("versionCode", versionCode)))
|
|
20
|
+
|
|
21
|
+
private fun hasUpdate(
|
|
22
|
+
currentBuildNumber: String,
|
|
23
|
+
manifest: JSONObject,
|
|
24
|
+
floor: Long = 0L,
|
|
25
|
+
): Boolean =
|
|
26
|
+
OtaManifestChecker.hasUpdate(
|
|
27
|
+
currentBuildNumber = currentBuildNumber,
|
|
28
|
+
currentMtkVersion = "",
|
|
29
|
+
currentBesVersion = "",
|
|
30
|
+
manifest = manifest,
|
|
31
|
+
downgradeFloorVersionCode = floor,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
@Test
|
|
35
|
+
fun upgradeIsAlwaysAnUpdate() {
|
|
36
|
+
assertTrue(hasUpdate("49000000", manifest(49076573L)))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Test
|
|
40
|
+
fun exactPinIsNotAnUpdateEvenWithAnEnabledFloor() {
|
|
41
|
+
assertFalse(hasUpdate("49000000", manifest(49000000L), floor = 49000000L))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Test
|
|
45
|
+
fun downgradeNeedsAnEnabledFloor() {
|
|
46
|
+
// Default floor 0 (fail closed) disables downgrades for native/OEM callers.
|
|
47
|
+
assertFalse(hasUpdate("49076573", manifest(49000000L)))
|
|
48
|
+
// A positive floor at/below the pin enables it.
|
|
49
|
+
assertTrue(hasUpdate("49076573", manifest(49000000L), floor = 49000000L))
|
|
50
|
+
// A pin below the floor is refused.
|
|
51
|
+
assertFalse(hasUpdate("49076573", manifest(48999999L), floor = 49000000L))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Test
|
|
55
|
+
fun exactPinIsNotAnUpdate() {
|
|
56
|
+
assertFalse(hasUpdate("49076573", manifest(49076573L)))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private fun legacyTopLevelManifest(versionCode: Long): JSONObject =
|
|
60
|
+
JSONObject().put("versionCode", versionCode)
|
|
61
|
+
|
|
62
|
+
@Test
|
|
63
|
+
fun legacyTopLevelManifestStaysUpgradeOnly() {
|
|
64
|
+
// No apps entry -> not an exact pin -> upgrade-only, never a false downgrade.
|
|
65
|
+
assertFalse(hasUpdate("49076573", legacyTopLevelManifest(49000000L)))
|
|
66
|
+
assertTrue(hasUpdate("49000000", legacyTopLevelManifest(49076573L)))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Test
|
|
70
|
+
fun zeroedPinOnModernGlassesIsAnError() {
|
|
71
|
+
// An unverifiable pin must never present as "no update" on modern glasses.
|
|
72
|
+
try {
|
|
73
|
+
hasUpdate("49076573", manifest(0L))
|
|
74
|
+
fail("expected invalid_ota_manifest")
|
|
75
|
+
} catch (e: BluetoothSdkException) {
|
|
76
|
+
assertEquals("invalid_ota_manifest", e.code)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@Test
|
|
81
|
+
fun zeroedRescuePinIsInertForLegacyGlasses() {
|
|
82
|
+
// Pre-39 glasses are legitimately checked against the frozen rescue manifests, whose
|
|
83
|
+
// zeroed ASG pin must read as no APK update (the MTK patch chain is what matters there).
|
|
84
|
+
assertFalse(hasUpdate("38", manifest(0L)))
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
package com.mentra.bluetoothsdk
|
|
2
|
+
|
|
3
|
+
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
+
import org.junit.Test
|
|
5
|
+
|
|
6
|
+
class StreamStatusEventTest {
|
|
7
|
+
@Test
|
|
8
|
+
fun `stream telemetry survives native status normalization`() {
|
|
9
|
+
val event =
|
|
10
|
+
StreamStatusEvent(
|
|
11
|
+
mapOf(
|
|
12
|
+
"status" to "streaming",
|
|
13
|
+
"streamId" to "stream-1",
|
|
14
|
+
"stats" to
|
|
15
|
+
mapOf(
|
|
16
|
+
"bitrate" to 912_345L,
|
|
17
|
+
"fps" to 19.8,
|
|
18
|
+
"droppedFrames" to 2L,
|
|
19
|
+
"duration" to 31L,
|
|
20
|
+
"temperatureC" to 54.6,
|
|
21
|
+
),
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
assertThat(event.stats?.bitrate).isEqualTo(912_345L)
|
|
26
|
+
assertThat(event.stats?.temperatureC).isEqualTo(54.6)
|
|
27
|
+
assertThat((event.values["stats"] as Map<*, *>)["duration"]).isEqualTo(31L)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -5,6 +5,7 @@ import com.mentra.bluetoothsdk.PhotoMode
|
|
|
5
5
|
import com.mentra.bluetoothsdk.PhotoRequest
|
|
6
6
|
import com.mentra.bluetoothsdk.PhotoSize
|
|
7
7
|
import org.assertj.core.api.Assertions.assertThat
|
|
8
|
+
import org.assertj.core.api.Assertions.assertThatThrownBy
|
|
8
9
|
import org.junit.Test
|
|
9
10
|
|
|
10
11
|
class PhotoRequestTest {
|
|
@@ -108,4 +109,33 @@ class PhotoRequestTest {
|
|
|
108
109
|
|
|
109
110
|
assertThat(request.transferMethod).isEqualTo("ble")
|
|
110
111
|
}
|
|
112
|
+
|
|
113
|
+
@Test
|
|
114
|
+
fun `fromMap preserves direct transfer without BLE fallback`() {
|
|
115
|
+
val request =
|
|
116
|
+
PhotoRequest.fromMap(
|
|
117
|
+
mapOf(
|
|
118
|
+
"size" to "medium",
|
|
119
|
+
"transferMethod" to "direct",
|
|
120
|
+
"webhookUrl" to "https://example.com/upload",
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
assertThat(request.transferMethod).isEqualTo("direct")
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@Test
|
|
128
|
+
fun `fromMap rejects an unknown transfer method`() {
|
|
129
|
+
assertThatThrownBy {
|
|
130
|
+
PhotoRequest.fromMap(
|
|
131
|
+
mapOf(
|
|
132
|
+
"size" to "medium",
|
|
133
|
+
"transferMethod" to "wifi",
|
|
134
|
+
"webhookUrl" to "https://example.com/upload",
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
.isInstanceOf(IllegalArgumentException::class.java)
|
|
139
|
+
.hasMessage("Invalid transferMethod \"wifi\". Expected auto, direct, or ble.")
|
|
140
|
+
}
|
|
111
141
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package com.mentra.bluetoothsdk.sgcs;
|
|
2
|
+
|
|
3
|
+
import static org.assertj.core.api.Assertions.assertThat;
|
|
4
|
+
|
|
5
|
+
import org.junit.Test;
|
|
6
|
+
|
|
7
|
+
public class Ar99AdvertisementMatcherTest {
|
|
8
|
+
|
|
9
|
+
@Test
|
|
10
|
+
public void supportsOnlyDocumentedProjectIdentifiers() {
|
|
11
|
+
assertThat(Ar99.isSupportedProjectName("AR99")).isTrue();
|
|
12
|
+
assertThat(Ar99.isSupportedProjectName("ar99")).isTrue();
|
|
13
|
+
|
|
14
|
+
assertThat(Ar99.isSupportedProjectName("AF98")).isFalse();
|
|
15
|
+
assertThat(Ar99.isSupportedProjectName("AF99")).isFalse();
|
|
16
|
+
assertThat(Ar99.isSupportedProjectName("HVXM")).isFalse();
|
|
17
|
+
assertThat(Ar99.isSupportedProjectName("HVXF")).isFalse();
|
|
18
|
+
assertThat(Ar99.isSupportedProjectName("AR99X")).isFalse();
|
|
19
|
+
assertThat(Ar99.isSupportedProjectName("")).isFalse();
|
|
20
|
+
assertThat(Ar99.isSupportedProjectName(null)).isFalse();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -135,6 +135,26 @@ public class BleJsonCompactTest {
|
|
|
135
135
|
assertEquals("wifi_scan_result", BleJsonCompact.encode(wifiScan).getString("t"));
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
@Test
|
|
139
|
+
public void streamTelemetryRoundTripsWithCompactKeys() throws Exception {
|
|
140
|
+
JSONObject status =
|
|
141
|
+
new JSONObject(
|
|
142
|
+
"{\"type\":\"stream_status\",\"status\":\"streaming\","
|
|
143
|
+
+ "\"stats\":{\"bitrate\":912345,\"fps\":19.8,"
|
|
144
|
+
+ "\"droppedFrames\":2,\"duration\":31,"
|
|
145
|
+
+ "\"temperatureC\":54.6}}\n");
|
|
146
|
+
|
|
147
|
+
JSONObject wire = BleJsonCompact.encode(status);
|
|
148
|
+
assertEquals(912345L, wire.getJSONObject("st").getLong("br"));
|
|
149
|
+
assertEquals(54.6d, wire.getJSONObject("st").getDouble("tc"), 0.001d);
|
|
150
|
+
|
|
151
|
+
JSONObject restored = BleJsonCompact.decode(wire);
|
|
152
|
+
JSONObject stats = restored.getJSONObject("stats");
|
|
153
|
+
assertEquals(19.8d, stats.getDouble("fps"), 0.001d);
|
|
154
|
+
assertEquals(2L, stats.getLong("droppedFrames"));
|
|
155
|
+
assertEquals(31L, stats.getLong("duration"));
|
|
156
|
+
}
|
|
157
|
+
|
|
138
158
|
@Test
|
|
139
159
|
public void decodeIfSupported_rejectsCompactLowRoi() throws Exception {
|
|
140
160
|
JSONObject compactPing = new JSONObject("{\"t\":\"ping\"}");
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package com.mentra.bluetoothsdk.utils
|
|
2
|
+
|
|
3
|
+
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
+
import org.junit.Test
|
|
5
|
+
|
|
6
|
+
class PhoneAudioMonitorTest {
|
|
7
|
+
@Test
|
|
8
|
+
fun `keeps explicit playback hint while Android reports active media`() {
|
|
9
|
+
assertThat(
|
|
10
|
+
PhoneAudioMonitor.shouldKeepOwnAppAudioSignal(
|
|
11
|
+
signalledPlaying = true,
|
|
12
|
+
systemAudioPlaying = true,
|
|
13
|
+
nowMs = 10_000,
|
|
14
|
+
holdoffUntilMs = 1_000,
|
|
15
|
+
)
|
|
16
|
+
)
|
|
17
|
+
.isTrue()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Test
|
|
21
|
+
fun `keeps explicit playback hint during codec transition holdoff`() {
|
|
22
|
+
assertThat(
|
|
23
|
+
PhoneAudioMonitor.shouldKeepOwnAppAudioSignal(
|
|
24
|
+
signalledPlaying = true,
|
|
25
|
+
systemAudioPlaying = false,
|
|
26
|
+
nowMs = 500,
|
|
27
|
+
holdoffUntilMs = 1_000,
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
.isTrue()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Test
|
|
34
|
+
fun `expires stale playback hint when Android media is inactive`() {
|
|
35
|
+
assertThat(
|
|
36
|
+
PhoneAudioMonitor.shouldKeepOwnAppAudioSignal(
|
|
37
|
+
signalledPlaying = true,
|
|
38
|
+
systemAudioPlaying = false,
|
|
39
|
+
nowMs = 1_000,
|
|
40
|
+
holdoffUntilMs = 1_000,
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
.isFalse()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -58,7 +58,7 @@ export declare function isConnectedGlassesConnectionStatus(status: GlassesConnec
|
|
|
58
58
|
export declare function isReadyGlassesConnectionStatus(status: GlassesConnectionStatus): boolean;
|
|
59
59
|
export declare function isBusyGlassesConnectionStatus(status: GlassesConnectionStatus): boolean;
|
|
60
60
|
export declare function createDisconnectedGlassesStatus(): Partial<GlassesStatus>;
|
|
61
|
-
/** K900 `sr_getvol` response (Mentra Live glasses media step volume 0
|
|
61
|
+
/** K900 `sr_getvol` response (Mentra Live glasses media step volume 0闂?5). */
|
|
62
62
|
export type GlassesMediaVolumeGetResult = {
|
|
63
63
|
level: number;
|
|
64
64
|
statusCode: number;
|
|
@@ -91,11 +91,11 @@ export type WifiStatusChangeEvent = WifiStatus & {
|
|
|
91
91
|
/**
|
|
92
92
|
* Glasses-reported provisioning failure reason when THIS event is the verdict of a
|
|
93
93
|
* failed connect attempt; absent on routine link-state updates. An attempt property,
|
|
94
|
-
* not a link property
|
|
94
|
+
* not a link property 闂?which is why it lives on the event, not on WifiStatus:
|
|
95
95
|
* "connect_timeout" arrives on a disconnected status (never associated), while
|
|
96
96
|
* "connected_to_other_network" arrives on a *connected* status (the attempt failed
|
|
97
97
|
* and the glasses ended up on / fell back to a different SSID than requested).
|
|
98
|
-
* Requires ASG client v40+
|
|
98
|
+
* Requires ASG client v40+ 闂?older glasses never send it.
|
|
99
99
|
*/
|
|
100
100
|
error?: string;
|
|
101
101
|
};
|
|
@@ -360,16 +360,20 @@ export type RgbLedAction = "on" | "off";
|
|
|
360
360
|
export type RgbLedColor = "red" | "green" | "blue" | "orange" | "white";
|
|
361
361
|
export type PhotoSize = "low" | "medium" | "high" | "max";
|
|
362
362
|
export type PhotoMode = "photo" | "text";
|
|
363
|
+
export type PhotoTransferMethod = "auto" | "direct" | "ble";
|
|
363
364
|
export type ButtonPhotoSize = "low" | "medium" | "high" | "max";
|
|
364
365
|
/**
|
|
365
366
|
* @deprecated Sticky action-button photo presets via {@link BluetoothSdkPublicModule.setPhotoCaptureDefaults}
|
|
366
367
|
* are deprecated. Prefer per-request {@link BluetoothSdkPublicModule.requestPhoto} options
|
|
367
|
-
* (e.g. `mode: "text"` for
|
|
368
|
+
* (e.g. `mode: "text"` for text sensor size/crop, or explicit `aeExposureDivisor`) instead of
|
|
369
|
+
* persisting button-photo tuning on the glasses.
|
|
368
370
|
*/
|
|
369
371
|
export type PhotoCaptureDefaults = {
|
|
370
372
|
size?: PhotoSize;
|
|
371
|
-
|
|
373
|
+
/** ZSL preview buffering for physical camera-button photos. */
|
|
372
374
|
zsl?: boolean;
|
|
375
|
+
/** MFNR still capture for physical camera-button photos. */
|
|
376
|
+
mfnr?: boolean;
|
|
373
377
|
noiseReduction?: boolean;
|
|
374
378
|
edgeEnhancement?: boolean;
|
|
375
379
|
ispDigitalGain?: number;
|
|
@@ -413,6 +417,7 @@ export declare const DeviceModels: {
|
|
|
413
417
|
readonly Z100: "Vuzix Z100";
|
|
414
418
|
readonly Frame: "Brilliant Frame";
|
|
415
419
|
readonly Nimo: "NIMO";
|
|
420
|
+
readonly Ar99: "AR99";
|
|
416
421
|
readonly R1: "Even Realities R1";
|
|
417
422
|
};
|
|
418
423
|
export type DeviceModel = (typeof DeviceModels)[keyof typeof DeviceModels];
|
|
@@ -424,7 +429,7 @@ export type DashboardMenuItem = {
|
|
|
424
429
|
};
|
|
425
430
|
export declare const CAMERA_FOV_MIN = 62;
|
|
426
431
|
export declare const CAMERA_FOV_MAX = 118;
|
|
427
|
-
export declare const CAMERA_FOV_DEFAULT =
|
|
432
|
+
export declare const CAMERA_FOV_DEFAULT = 118;
|
|
428
433
|
export type CameraRoiPosition = "center" | "bottom" | "top";
|
|
429
434
|
export type CameraRoiPositionValue = 0 | 1 | 2;
|
|
430
435
|
export type CameraFovPreset = "narrow" | "standard" | "wide";
|
|
@@ -461,8 +466,8 @@ export type PhotoRequestParams = {
|
|
|
461
466
|
appId?: string;
|
|
462
467
|
size: PhotoSize;
|
|
463
468
|
mode?: PhotoMode;
|
|
464
|
-
/** `ble` skips direct upload and forces phone-relayed
|
|
465
|
-
transferMethod?:
|
|
469
|
+
/** `direct` disables BLE fallback; `ble` skips direct upload and forces phone-relayed transfer. */
|
|
470
|
+
transferMethod?: PhotoTransferMethod;
|
|
466
471
|
webhookUrl: string | null;
|
|
467
472
|
authToken: string | null;
|
|
468
473
|
compress: PhotoCompression;
|
|
@@ -478,8 +483,10 @@ export type PhotoRequestParams = {
|
|
|
478
483
|
/** Requested on wire; glasses may log not_implemented. */
|
|
479
484
|
noiseReduction?: boolean;
|
|
480
485
|
edgeEnhancement?: boolean;
|
|
481
|
-
|
|
486
|
+
/** ZSL buffering. Forced off for manual/scan stills because fixed sensor controls take priority. */
|
|
482
487
|
zsl?: boolean;
|
|
488
|
+
/** MFNR still capture. Forced off for manual/scan stills because fixed sensor controls take priority. */
|
|
489
|
+
mfnr?: boolean;
|
|
483
490
|
ispDigitalGain?: number;
|
|
484
491
|
ispAnalogGain?: string;
|
|
485
492
|
};
|
|
@@ -491,6 +498,10 @@ export type WarmUpCameraParams = {
|
|
|
491
498
|
exposureTimeNs?: number | null;
|
|
492
499
|
/** Ready-state hold; defaults to 15 seconds and is capped at 60 seconds by ASG. */
|
|
493
500
|
durationMs?: number;
|
|
501
|
+
/** ZSL preview buffering for the warm-up session. */
|
|
502
|
+
zsl?: boolean;
|
|
503
|
+
/** MFNR still capture for the warm-up session. */
|
|
504
|
+
mfnr?: boolean;
|
|
494
505
|
};
|
|
495
506
|
export type StreamVideoConfig = {
|
|
496
507
|
width?: number;
|
|
@@ -595,7 +606,7 @@ export type StreamResolvedConfig = {
|
|
|
595
606
|
noiseSuppression?: boolean;
|
|
596
607
|
};
|
|
597
608
|
};
|
|
598
|
-
/** Live encoder telemetry
|
|
609
|
+
/** Live encoder and device telemetry emitted periodically by supported glasses firmware. */
|
|
599
610
|
export type StreamLiveStats = {
|
|
600
611
|
/** Current encoded video bitrate in bits per second. */
|
|
601
612
|
bitrate?: number;
|
|
@@ -604,7 +615,7 @@ export type StreamLiveStats = {
|
|
|
604
615
|
droppedFrames?: number;
|
|
605
616
|
/** Seconds since the stream started. */
|
|
606
617
|
duration?: number;
|
|
607
|
-
/** Device temperature in
|
|
618
|
+
/** Device temperature in 闂佺娅i悡? if the hardware reports it. */
|
|
608
619
|
temperatureC?: number;
|
|
609
620
|
};
|
|
610
621
|
type StreamStatusCommon = {
|
|
@@ -653,6 +664,16 @@ export type MtkUpdateCompleteEvent = {
|
|
|
653
664
|
message: string;
|
|
654
665
|
timestamp: number;
|
|
655
666
|
};
|
|
667
|
+
/**
|
|
668
|
+
* The glasses process restarted while the BES kept the BLE link alive (its `sid`
|
|
669
|
+
* changed, or first appeared after an update from a pre-sid build). There is no
|
|
670
|
+
* physical disconnect for this — treat it as the logical reconnect edge.
|
|
671
|
+
*/
|
|
672
|
+
export type GlassesSessionChangedEvent = {
|
|
673
|
+
type: "glasses_session_changed";
|
|
674
|
+
previous_sid: string;
|
|
675
|
+
sid: string;
|
|
676
|
+
};
|
|
656
677
|
/** @deprecated Glasses no longer emit ota_progress; use {@link OtaStatusEvent} and status-store mapping. */
|
|
657
678
|
export type OtaProgressEvent = {
|
|
658
679
|
type: "ota_progress";
|
|
@@ -668,14 +689,6 @@ export type OtaStartAckEvent = {
|
|
|
668
689
|
type: "ota_start_ack";
|
|
669
690
|
timestamp: number;
|
|
670
691
|
};
|
|
671
|
-
export type OtaUpdateAvailableEvent = {
|
|
672
|
-
type: "ota_update_available";
|
|
673
|
-
version_code?: number;
|
|
674
|
-
version_name?: string;
|
|
675
|
-
updates?: string[];
|
|
676
|
-
total_size?: number;
|
|
677
|
-
cache_ready?: boolean;
|
|
678
|
-
};
|
|
679
692
|
export type OtaStatusEvent = {
|
|
680
693
|
type: "ota_status";
|
|
681
694
|
session_id: string;
|
|
@@ -749,9 +762,10 @@ export type BluetoothSdkModuleEvents = {
|
|
|
749
762
|
stream_status: (event: StreamStatusEvent) => void;
|
|
750
763
|
keep_alive_ack: (event: KeepAliveAckEvent) => void;
|
|
751
764
|
mtk_update_complete: (event: MtkUpdateCompleteEvent) => void;
|
|
752
|
-
|
|
765
|
+
glasses_session_changed: (event: GlassesSessionChangedEvent) => void;
|
|
753
766
|
ota_start_ack: (event: OtaStartAckEvent) => void;
|
|
754
767
|
ota_status: (event: OtaStatusEvent) => void;
|
|
768
|
+
ar99_ota_status: (event: Ar99OtaStatusEvent) => void;
|
|
755
769
|
version_info: (event: VersionInfoEvent) => void;
|
|
756
770
|
send_command_to_ble: (event: BleCommandTraceEvent) => void;
|
|
757
771
|
receive_command_from_ble: (event: BleCommandTraceEvent) => void;
|
|
@@ -763,6 +777,15 @@ export interface ExtractionProgressEvent {
|
|
|
763
777
|
bytesRead: number;
|
|
764
778
|
totalBytes: number;
|
|
765
779
|
}
|
|
780
|
+
export interface Ar99OtaStatusEvent {
|
|
781
|
+
type: "ar99_ota_status";
|
|
782
|
+
phase: string;
|
|
783
|
+
progress: number;
|
|
784
|
+
offset: number;
|
|
785
|
+
total: number;
|
|
786
|
+
errorMessage?: string;
|
|
787
|
+
error_message?: string;
|
|
788
|
+
}
|
|
766
789
|
export interface PhoneNotificationEvent {
|
|
767
790
|
notificationId: string;
|
|
768
791
|
app: string;
|
|
@@ -818,9 +841,9 @@ export type BluetoothSdkEventMap = {
|
|
|
818
841
|
mic_pcm: MicPcmEvent;
|
|
819
842
|
mic_lc3: MicLc3Event;
|
|
820
843
|
stream_status: StreamStatusEvent;
|
|
821
|
-
ota_update_available: OtaUpdateAvailableEvent;
|
|
822
844
|
ota_start_ack: OtaStartAckEvent;
|
|
823
845
|
ota_status: OtaStatusEvent;
|
|
846
|
+
ar99_ota_status: Ar99OtaStatusEvent;
|
|
824
847
|
version_info: VersionInfoEvent;
|
|
825
848
|
extraction_progress: ExtractionProgressEvent;
|
|
826
849
|
};
|
|
@@ -857,9 +880,10 @@ export interface BluetoothSdkPublicModule {
|
|
|
857
880
|
setHotspotState(enabled: boolean): Promise<HotspotStatusChangeEvent>;
|
|
858
881
|
setGalleryModeEnabled(enabled: boolean): Promise<SettingsAckSuccessEvent>;
|
|
859
882
|
setVoiceActivityDetectionEnabled(enabled: boolean): Promise<void>;
|
|
883
|
+
setLoudnessGateEnabled(enabled: boolean): Promise<void>;
|
|
860
884
|
/**
|
|
861
885
|
* @deprecated Sticky action-button photo presets are deprecated. Prefer per-request
|
|
862
|
-
* `requestPhoto(...)` options (e.g. `mode: "text"` for
|
|
886
|
+
* `requestPhoto(...)` options (e.g. `mode: "text"` for text sensor size/crop, or explicit per-shot
|
|
863
887
|
* fields). Still functional until removed in a future release.
|
|
864
888
|
*/
|
|
865
889
|
setPhotoCaptureDefaults(settings: PhotoCaptureDefaults): Promise<SettingsAckSuccessEvent>;
|
|
@@ -894,7 +918,7 @@ export interface BluetoothSdkPublicModule {
|
|
|
894
918
|
* Stop the active recording. When {@link webhookUrl} is provided, the glasses
|
|
895
919
|
* upload the recorded video to it (multipart) using {@link authToken}. These
|
|
896
920
|
* are supplied at stop time (not start) so the token is fresh when the upload
|
|
897
|
-
* runs
|
|
921
|
+
* runs 闂?a recording can last arbitrarily long. An empty/omitted webhook keeps
|
|
898
922
|
* the video on device (no upload).
|
|
899
923
|
*/
|
|
900
924
|
stopVideoRecording(requestId: string, webhookUrl?: string, authToken?: string): Promise<VideoRecordingStoppedStatusEvent>;
|
|
@@ -911,6 +935,10 @@ export interface BluetoothSdkPublicModule {
|
|
|
911
935
|
checkForOtaUpdate(): Promise<boolean>;
|
|
912
936
|
/** Start the OTA flow with the same configured manifest URL used by checkForOtaUpdate(). */
|
|
913
937
|
startOtaUpdate(): Promise<OtaStartAckEvent>;
|
|
938
|
+
startAr99OtaFromFile(path: string): Promise<boolean>;
|
|
939
|
+
cancelAr99Ota(): Promise<void>;
|
|
940
|
+
sendAr99FactoryReset(): Promise<void>;
|
|
941
|
+
buildAr99OtaSignature(secret: string, appName: string, currentVersion: string, serialNumber: string, nonce: string): string;
|
|
914
942
|
setSttModelDetails(path: string, languageCode: string): Promise<void>;
|
|
915
943
|
getSttModelPath(): Promise<string>;
|
|
916
944
|
checkSttModelAvailable(): Promise<boolean>;
|
|
@@ -944,6 +972,8 @@ export interface OtaUpdateInfo {
|
|
|
944
972
|
updates: string[];
|
|
945
973
|
totalSize: number;
|
|
946
974
|
cacheReady?: boolean;
|
|
975
|
+
/** True when the APK step installs an older build than the glasses currently run (exact-pin manifests only). */
|
|
976
|
+
isDowngrade?: boolean;
|
|
947
977
|
}
|
|
948
978
|
export interface OtaProgress {
|
|
949
979
|
stage: OtaStage;
|
|
@@ -1015,13 +1045,15 @@ export interface Device {
|
|
|
1015
1045
|
/**
|
|
1016
1046
|
* Stable app-facing key for this scan result, within the limits of the
|
|
1017
1047
|
* platform identifier available to the SDK. Do not parse this value; use the
|
|
1018
|
-
* typed model, name, address, and rssi fields instead.
|
|
1048
|
+
* typed model, name, address, projectName, and rssi fields instead.
|
|
1019
1049
|
*/
|
|
1020
1050
|
id: string;
|
|
1021
1051
|
model: DeviceModel;
|
|
1022
1052
|
name: string;
|
|
1023
1053
|
/** Platform address/identifier when available: Android Bluetooth address, iOS CoreBluetooth identifier. */
|
|
1024
1054
|
address?: string;
|
|
1055
|
+
/** Optional AR99 project discriminator. Supported value: AR99. */
|
|
1056
|
+
projectName?: string;
|
|
1025
1057
|
/**
|
|
1026
1058
|
* Optional scan signal strength. It may be undefined at first discovery and
|
|
1027
1059
|
* appear in a later scan update when the platform reports RSSI metadata.
|
|
@@ -1047,7 +1079,7 @@ export interface WifiSearchResult {
|
|
|
1047
1079
|
ssid: string;
|
|
1048
1080
|
requiresPassword: boolean;
|
|
1049
1081
|
signalStrength: number;
|
|
1050
|
-
/** Frequency in MHz (from glasses scan). 5 GHz band is typically 5170
|
|
1082
|
+
/** Frequency in MHz (from glasses scan). 5 GHz band is typically 5170闂?825. Omitted if unknown. */
|
|
1051
1083
|
frequency?: number;
|
|
1052
1084
|
}
|
|
1053
1085
|
export interface BluetoothStatus {
|
|
@@ -1096,6 +1128,7 @@ export type BluetoothSettingsUpdate = Partial<{
|
|
|
1096
1128
|
twelve_hour_time: boolean;
|
|
1097
1129
|
gallery_mode: boolean;
|
|
1098
1130
|
voice_activity_detection_enabled: boolean;
|
|
1131
|
+
loudness_gate_enabled: boolean;
|
|
1099
1132
|
button_photo_size: ButtonPhotoSize;
|
|
1100
1133
|
button_video_settings: {
|
|
1101
1134
|
width: number;
|