@mentra/bluetooth-sdk 0.1.20 → 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 +27 -12
- 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 +169 -26
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +72 -16
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +127 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +4 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +400 -99
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraLocalNetworkModule.kt +333 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +43 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +18 -2
- 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/G1.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +16 -9
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +342 -42
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +7 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +1 -1
- 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/status/WifiHotspotStatus.kt +19 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +52 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/tts/TTSTools.kt +115 -47
- 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/main/java/com/mentra/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -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 +45 -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 +100 -22
- 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 +3 -0
- package/build/_internal.d.ts.map +1 -1
- package/build/_internal.js +3 -0
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +31 -4
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +26 -3
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/MentraLocalNetworkModule.d.ts +37 -0
- package/build/_private/MentraLocalNetworkModule.d.ts.map +1 -0
- package/build/_private/MentraLocalNetworkModule.js +3 -0
- package/build/_private/MentraLocalNetworkModule.js.map +1 -0
- package/build/_private/cameraRequestPayload.d.ts +1 -1
- package/build/_private/cameraRequestPayload.d.ts.map +1 -1
- package/build/_private/cameraRequestPayload.js +7 -0
- package/build/_private/cameraRequestPayload.js.map +1 -1
- package/build/_private/photoRequestPayload.d.ts.map +1 -1
- package/build/_private/photoRequestPayload.js +10 -0
- 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 +11 -1
- package/build/index.js.map +1 -1
- package/expo-module.config.json +1 -0
- package/ios/BluetoothSdkModule.swift +113 -2
- package/ios/Source/BluetoothSdkDefaults.swift +2 -1
- package/ios/Source/Bridge.swift +87 -21
- package/ios/Source/DeviceManager.swift +118 -20
- package/ios/Source/DeviceStore.swift +4 -0
- package/ios/Source/MentraBluetoothSDK.swift +326 -33
- package/ios/Source/OtaManifest.swift +47 -4
- package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
- package/ios/Source/camera/CameraModels.swift +26 -6
- 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/G1.swift +1 -1
- package/ios/Source/sgcs/G2.swift +19 -7
- package/ios/Source/sgcs/Mach1.swift +1 -1
- package/ios/Source/sgcs/MentraLive.swift +478 -61
- package/ios/Source/sgcs/MentraNex.swift +1 -1
- package/ios/Source/sgcs/Nimo.swift +1 -1
- package/ios/Source/sgcs/SGCManager.swift +13 -2
- package/ios/Source/sgcs/Simulated.swift +1 -1
- package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
- package/ios/Source/status/WifiHotspotStatus.swift +21 -2
- package/ios/Source/streaming/StreamModels.swift +68 -2
- package/ios/Source/tts/TTSTools.swift +1 -1
- 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 -2
- package/src/BluetoothSdk.types.ts +106 -24
- package/src/_internal.ts +3 -0
- package/src/_private/BluetoothSdkModule.ts +83 -18
- package/src/_private/MentraLocalNetworkModule.ts +50 -0
- package/src/_private/cameraRequestPayload.ts +9 -2
- package/src/_private/photoRequestPayload.ts +13 -4
- package/src/_private/sdkOtaManifest.ts +31 -0
- package/src/index.ts +13 -1
|
@@ -217,6 +217,57 @@ public struct StreamResolvedConfig: Equatable {
|
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
private func streamInt64Value(_ value: Any?) -> Int64? {
|
|
221
|
+
if let int64 = value as? Int64 { return int64 }
|
|
222
|
+
if let int = value as? Int { return Int64(int) }
|
|
223
|
+
if let number = value as? NSNumber { return number.int64Value }
|
|
224
|
+
return nil
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/// Live encoder and device telemetry reported by the glasses while streaming.
|
|
228
|
+
public struct StreamLiveStats: Equatable {
|
|
229
|
+
public let bitrate: Int64?
|
|
230
|
+
public let fps: Double?
|
|
231
|
+
public let droppedFrames: Int64?
|
|
232
|
+
public let duration: Int64?
|
|
233
|
+
public let temperatureC: Double?
|
|
234
|
+
|
|
235
|
+
public init(
|
|
236
|
+
bitrate: Int64? = nil,
|
|
237
|
+
fps: Double? = nil,
|
|
238
|
+
droppedFrames: Int64? = nil,
|
|
239
|
+
duration: Int64? = nil,
|
|
240
|
+
temperatureC: Double? = nil
|
|
241
|
+
) {
|
|
242
|
+
self.bitrate = bitrate
|
|
243
|
+
self.fps = fps
|
|
244
|
+
self.droppedFrames = droppedFrames
|
|
245
|
+
self.duration = duration
|
|
246
|
+
self.temperatureC = temperatureC
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
init?(values: [String: Any]?) {
|
|
250
|
+
guard let values else { return nil }
|
|
251
|
+
self.init(
|
|
252
|
+
bitrate: streamInt64Value(values["bitrate"]),
|
|
253
|
+
fps: doubleValue(values["fps"]),
|
|
254
|
+
droppedFrames: streamInt64Value(values["droppedFrames"]),
|
|
255
|
+
duration: streamInt64Value(values["duration"]),
|
|
256
|
+
temperatureC: doubleValue(values["temperatureC"])
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
var values: [String: Any] {
|
|
261
|
+
var values: [String: Any] = [:]
|
|
262
|
+
if let bitrate { values["bitrate"] = bitrate }
|
|
263
|
+
if let fps { values["fps"] = fps }
|
|
264
|
+
if let droppedFrames { values["droppedFrames"] = droppedFrames }
|
|
265
|
+
if let duration { values["duration"] = duration }
|
|
266
|
+
if let temperatureC { values["temperatureC"] = temperatureC }
|
|
267
|
+
return values
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
220
271
|
public struct StreamRequest {
|
|
221
272
|
public let streamUrl: String
|
|
222
273
|
public let streamId: String
|
|
@@ -553,13 +604,22 @@ public enum StreamStatus: CustomStringConvertible, Equatable {
|
|
|
553
604
|
|
|
554
605
|
public struct StreamStatusEvent: CustomStringConvertible {
|
|
555
606
|
public let status: StreamStatus
|
|
556
|
-
|
|
557
|
-
|
|
607
|
+
public let stats: StreamLiveStats?
|
|
608
|
+
/// True when the glasses will retry the failed publisher themselves
|
|
609
|
+
/// (emitting side lands in PR #3488); absent on older firmware and on
|
|
610
|
+
/// events not parsed from a glasses status map. Carried here instead of
|
|
611
|
+
/// as an `.error` associated value so the public case stays unchanged.
|
|
612
|
+
public private(set) var willRetry: Bool?
|
|
613
|
+
|
|
614
|
+
public init(status: StreamStatus, stats: StreamLiveStats? = nil) {
|
|
558
615
|
self.status = status
|
|
616
|
+
self.stats = stats
|
|
559
617
|
}
|
|
560
618
|
|
|
561
619
|
public init(values: [String: Any]) {
|
|
562
620
|
status = StreamStatus(values: values)
|
|
621
|
+
stats = StreamLiveStats(values: values["stats"] as? [String: Any])
|
|
622
|
+
willRetry = boolValue(values, "willRetry")
|
|
563
623
|
}
|
|
564
624
|
|
|
565
625
|
public var state: StreamState {
|
|
@@ -577,6 +637,12 @@ public struct StreamStatusEvent: CustomStringConvertible {
|
|
|
577
637
|
public var values: [String: Any] {
|
|
578
638
|
var values = status.values
|
|
579
639
|
values["type"] = "stream_status"
|
|
640
|
+
if let stats {
|
|
641
|
+
values["stats"] = stats.values
|
|
642
|
+
}
|
|
643
|
+
if let willRetry {
|
|
644
|
+
values["willRetry"] = willRetry
|
|
645
|
+
}
|
|
580
646
|
return values
|
|
581
647
|
}
|
|
582
648
|
|
|
@@ -111,7 +111,7 @@ class TTSTools {
|
|
|
111
111
|
voiceStyle: (modelPath as NSString).appendingPathComponent(kFileVoiceStyle)
|
|
112
112
|
)
|
|
113
113
|
var modelConfig = sherpaOnnxOfflineTtsModelConfig(
|
|
114
|
-
numThreads:
|
|
114
|
+
numThreads: 8,
|
|
115
115
|
supertonic: supertonic
|
|
116
116
|
)
|
|
117
117
|
var config = sherpaOnnxOfflineTtsConfig(model: modelConfig, maxNumSentences: 1)
|
|
@@ -19,6 +19,7 @@ public enum DeviceModel: String {
|
|
|
19
19
|
case z100
|
|
20
20
|
case frame
|
|
21
21
|
case nimo
|
|
22
|
+
case ar99
|
|
22
23
|
case simulated
|
|
23
24
|
case r1
|
|
24
25
|
|
|
@@ -40,6 +41,8 @@ public enum DeviceModel: String {
|
|
|
40
41
|
DeviceTypes.FRAME
|
|
41
42
|
case .nimo:
|
|
42
43
|
DeviceTypes.NIMO
|
|
44
|
+
case .ar99:
|
|
45
|
+
DeviceTypes.AR99
|
|
43
46
|
case .simulated:
|
|
44
47
|
DeviceTypes.SIMULATED
|
|
45
48
|
case .r1:
|
|
@@ -65,6 +68,8 @@ public enum DeviceModel: String {
|
|
|
65
68
|
.frame
|
|
66
69
|
case DeviceTypes.NIMO:
|
|
67
70
|
.nimo
|
|
71
|
+
case DeviceTypes.AR99:
|
|
72
|
+
.ar99
|
|
68
73
|
case DeviceTypes.SIMULATED:
|
|
69
74
|
.simulated
|
|
70
75
|
case ControllerTypes.R1:
|
|
@@ -80,6 +85,7 @@ public struct Device: Identifiable, Equatable, CustomStringConvertible {
|
|
|
80
85
|
public let name: String
|
|
81
86
|
/// CoreBluetooth identifier when available.
|
|
82
87
|
public let identifier: String?
|
|
88
|
+
public let projectName: String?
|
|
83
89
|
public let rssi: Int?
|
|
84
90
|
/// Stable app-facing scan-result key. Do not parse; use typed fields instead.
|
|
85
91
|
public let id: String
|
|
@@ -88,12 +94,14 @@ public struct Device: Identifiable, Equatable, CustomStringConvertible {
|
|
|
88
94
|
model: DeviceModel,
|
|
89
95
|
name: String,
|
|
90
96
|
identifier: String? = nil,
|
|
97
|
+
projectName: String? = nil,
|
|
91
98
|
rssi: Int? = nil,
|
|
92
99
|
id: String? = nil
|
|
93
100
|
) {
|
|
94
101
|
self.model = model
|
|
95
102
|
self.name = name
|
|
96
103
|
self.identifier = identifier
|
|
104
|
+
self.projectName = projectName
|
|
97
105
|
self.rssi = rssi
|
|
98
106
|
self.id = id ?? identifier.flatMap { $0.isEmpty ? nil : $0 } ?? "\(model.deviceType):\(name)"
|
|
99
107
|
}
|
|
@@ -111,6 +119,9 @@ public struct Device: Identifiable, Equatable, CustomStringConvertible {
|
|
|
111
119
|
if let identifier, !identifier.isEmpty {
|
|
112
120
|
values["address"] = identifier
|
|
113
121
|
}
|
|
122
|
+
if let projectName, !projectName.isEmpty {
|
|
123
|
+
values["projectName"] = projectName
|
|
124
|
+
}
|
|
114
125
|
if let rssi {
|
|
115
126
|
values["rssi"] = rssi
|
|
116
127
|
}
|
|
@@ -121,11 +132,13 @@ public struct Device: Identifiable, Equatable, CustomStringConvertible {
|
|
|
121
132
|
guard let model = stringValue(values, "model") else { return nil }
|
|
122
133
|
guard let name = stringValue(values, "name") else { return nil }
|
|
123
134
|
let identifier = stringValue(values, "address").flatMap { $0.isEmpty ? nil : $0 }
|
|
135
|
+
let projectName = stringValue(values, "projectName").flatMap { $0.isEmpty ? nil : $0 }
|
|
124
136
|
let rssi = intValue(values["rssi"])
|
|
125
137
|
self.init(
|
|
126
138
|
model: DeviceModel.fromDeviceType(model),
|
|
127
139
|
name: name,
|
|
128
140
|
identifier: identifier,
|
|
141
|
+
projectName: projectName,
|
|
129
142
|
rssi: rssi,
|
|
130
143
|
id: stringValue(values, "id")
|
|
131
144
|
)
|
|
@@ -19,6 +19,14 @@ enum BleJsonCompact {
|
|
|
19
19
|
"transferMethod": "tm",
|
|
20
20
|
"manual": "m",
|
|
21
21
|
"reconnecting": "rc",
|
|
22
|
+
"stats": "st",
|
|
23
|
+
"width": "w",
|
|
24
|
+
"height": "h",
|
|
25
|
+
"bitrate": "br",
|
|
26
|
+
"fps": "f",
|
|
27
|
+
"droppedFrames": "df",
|
|
28
|
+
"duration": "du",
|
|
29
|
+
"temperatureC": "tc",
|
|
22
30
|
]
|
|
23
31
|
|
|
24
32
|
private static let shortToLong: [String: String] = {
|
|
@@ -8,6 +8,7 @@ struct DeviceTypes {
|
|
|
8
8
|
static let NEX = "Mentra Display"
|
|
9
9
|
static let FRAME = "Brilliant Frame"
|
|
10
10
|
static let NIMO = "NIMO"
|
|
11
|
+
static let AR99 = "AR99"
|
|
11
12
|
|
|
12
13
|
static let ALL = [
|
|
13
14
|
SIMULATED,
|
|
@@ -19,6 +20,7 @@ struct DeviceTypes {
|
|
|
19
20
|
NEX,
|
|
20
21
|
FRAME,
|
|
21
22
|
NIMO,
|
|
23
|
+
AR99,
|
|
22
24
|
]
|
|
23
25
|
|
|
24
26
|
/// Private init to prevent instantiation
|
|
@@ -55,6 +55,10 @@ class PhoneAudioMonitor {
|
|
|
55
55
|
return session.isOtherAudioPlaying || ownAppAudioPlaying
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
func isOwnAppAudioPlaying() -> Bool {
|
|
59
|
+
ownAppAudioPlaying
|
|
60
|
+
}
|
|
61
|
+
|
|
58
62
|
/// Notify the monitor that our own app started/stopped playing audio
|
|
59
63
|
/// Called from RN AudioPlaybackService via Bridge
|
|
60
64
|
func setOwnAppAudioPlaying(_ playing: Bool) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/bluetooth-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21-beta.1",
|
|
4
4
|
"description": "SDK for communicating with smart glasses",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"react-native": "src/index.ts",
|
|
@@ -105,7 +105,6 @@
|
|
|
105
105
|
"access": "public",
|
|
106
106
|
"registry": "https://registry.npmjs.org/"
|
|
107
107
|
},
|
|
108
|
-
"dependencies": {},
|
|
109
108
|
"devDependencies": {
|
|
110
109
|
"@types/node": "^25.9.3",
|
|
111
110
|
"expo-module-scripts": "^55.0.2",
|
|
@@ -5,7 +5,7 @@ export type GlassesNotReadyEvent = {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
// NOTE: unlike most events below, the native module does NOT include a `type`
|
|
8
|
-
// field on the button_press payload
|
|
8
|
+
// field on the button_press payload 闂?it sends only {buttonId, pressType,
|
|
9
9
|
// timestamp} (see BluetoothSdkModule on both iOS and Android). Consumers must
|
|
10
10
|
// filter on `pressType` / the "button_press" listener name, never `event.type`.
|
|
11
11
|
export type ButtonPressEvent = {
|
|
@@ -85,7 +85,7 @@ export function createDisconnectedGlassesStatus(): Partial<GlassesStatus> {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
/** K900 `sr_getvol` response (Mentra Live glasses media step volume 0
|
|
88
|
+
/** K900 `sr_getvol` response (Mentra Live glasses media step volume 0闂?5). */
|
|
89
89
|
export type GlassesMediaVolumeGetResult = {
|
|
90
90
|
level: number
|
|
91
91
|
statusCode: number
|
|
@@ -116,6 +116,16 @@ export function isConnectedWifiStatus(status: WifiStatus): status is ConnectedWi
|
|
|
116
116
|
|
|
117
117
|
export type WifiStatusChangeEvent = WifiStatus & {
|
|
118
118
|
type: "wifi_status_change"
|
|
119
|
+
/**
|
|
120
|
+
* Glasses-reported provisioning failure reason when THIS event is the verdict of a
|
|
121
|
+
* failed connect attempt; absent on routine link-state updates. An attempt property,
|
|
122
|
+
* not a link property 闂?which is why it lives on the event, not on WifiStatus:
|
|
123
|
+
* "connect_timeout" arrives on a disconnected status (never associated), while
|
|
124
|
+
* "connected_to_other_network" arrives on a *connected* status (the attempt failed
|
|
125
|
+
* and the glasses ended up on / fell back to a different SSID than requested).
|
|
126
|
+
* Requires ASG client v40+ 闂?older glasses never send it.
|
|
127
|
+
*/
|
|
128
|
+
error?: string
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
export type HotspotStatus = {state: "disabled"} | {state: "enabled"; ssid: string; password: string; localIp: string}
|
|
@@ -405,6 +415,7 @@ export type SettingsAckSetting =
|
|
|
405
415
|
| "button_video_recording"
|
|
406
416
|
| "button_max_recording_time"
|
|
407
417
|
| "camera_fov"
|
|
418
|
+
| "camera_fov_override"
|
|
408
419
|
| "camera_tuning"
|
|
409
420
|
|
|
410
421
|
export type SettingsAckEvent = {
|
|
@@ -416,6 +427,7 @@ export type SettingsAckEvent = {
|
|
|
416
427
|
fov?: number
|
|
417
428
|
roiPosition?: CameraRoiPositionValue
|
|
418
429
|
hardwareApplied?: boolean
|
|
430
|
+
leaseId?: string
|
|
419
431
|
active?: boolean
|
|
420
432
|
size?: ButtonPhotoSize | string
|
|
421
433
|
width?: number
|
|
@@ -441,17 +453,21 @@ export type RgbLedAction = "on" | "off"
|
|
|
441
453
|
export type RgbLedColor = "red" | "green" | "blue" | "orange" | "white"
|
|
442
454
|
export type PhotoSize = "low" | "medium" | "high" | "max"
|
|
443
455
|
export type PhotoMode = "photo" | "text"
|
|
456
|
+
export type PhotoTransferMethod = "auto" | "direct" | "ble"
|
|
444
457
|
export type ButtonPhotoSize = "low" | "medium" | "high" | "max"
|
|
445
458
|
|
|
446
459
|
/**
|
|
447
460
|
* @deprecated Sticky action-button photo presets via {@link BluetoothSdkPublicModule.setPhotoCaptureDefaults}
|
|
448
461
|
* are deprecated. Prefer per-request {@link BluetoothSdkPublicModule.requestPhoto} options
|
|
449
|
-
* (e.g. `mode: "text"` for
|
|
462
|
+
* (e.g. `mode: "text"` for text sensor size/crop, or explicit `aeExposureDivisor`) instead of
|
|
463
|
+
* persisting button-photo tuning on the glasses.
|
|
450
464
|
*/
|
|
451
465
|
export type PhotoCaptureDefaults = {
|
|
452
466
|
size?: PhotoSize
|
|
453
|
-
|
|
467
|
+
/** ZSL preview buffering for physical camera-button photos. */
|
|
454
468
|
zsl?: boolean
|
|
469
|
+
/** MFNR still capture for physical camera-button photos. */
|
|
470
|
+
mfnr?: boolean
|
|
455
471
|
noiseReduction?: boolean
|
|
456
472
|
edgeEnhancement?: boolean
|
|
457
473
|
ispDigitalGain?: number
|
|
@@ -497,6 +513,7 @@ export const DeviceModels = {
|
|
|
497
513
|
Z100: "Vuzix Z100",
|
|
498
514
|
Frame: "Brilliant Frame",
|
|
499
515
|
Nimo: "NIMO",
|
|
516
|
+
Ar99: "AR99",
|
|
500
517
|
R1: "Even Realities R1",
|
|
501
518
|
} as const
|
|
502
519
|
|
|
@@ -511,7 +528,7 @@ export type DashboardMenuItem = {
|
|
|
511
528
|
|
|
512
529
|
export const CAMERA_FOV_MIN = 62
|
|
513
530
|
export const CAMERA_FOV_MAX = 118
|
|
514
|
-
export const CAMERA_FOV_DEFAULT =
|
|
531
|
+
export const CAMERA_FOV_DEFAULT = CAMERA_FOV_MAX
|
|
515
532
|
|
|
516
533
|
export type CameraRoiPosition = "center" | "bottom" | "top"
|
|
517
534
|
export type CameraRoiPositionValue = 0 | 1 | 2
|
|
@@ -533,6 +550,13 @@ export type CameraFovResult = {
|
|
|
533
550
|
timestamp: number
|
|
534
551
|
}
|
|
535
552
|
|
|
553
|
+
export type CameraFovOverrideRequest = CameraFovRequest & {
|
|
554
|
+
/** Phone-owned lease used to make delayed releases safe. */
|
|
555
|
+
leaseId: string
|
|
556
|
+
/** Safety TTL; refresh the same lease/configuration to extend without a HAL restart. */
|
|
557
|
+
ttlMs?: number
|
|
558
|
+
}
|
|
559
|
+
|
|
536
560
|
export type CameraFovSetting = {
|
|
537
561
|
fov: number
|
|
538
562
|
roiPosition: CameraRoiPositionValue
|
|
@@ -551,6 +575,8 @@ export type PhotoRequestParams = {
|
|
|
551
575
|
appId?: string
|
|
552
576
|
size: PhotoSize
|
|
553
577
|
mode?: PhotoMode
|
|
578
|
+
/** `direct` disables BLE fallback; `ble` skips direct upload and forces phone-relayed transfer. */
|
|
579
|
+
transferMethod?: PhotoTransferMethod
|
|
554
580
|
webhookUrl: string | null
|
|
555
581
|
authToken: string | null
|
|
556
582
|
compress: PhotoCompression
|
|
@@ -566,17 +592,26 @@ export type PhotoRequestParams = {
|
|
|
566
592
|
/** Requested on wire; glasses may log not_implemented. */
|
|
567
593
|
noiseReduction?: boolean
|
|
568
594
|
edgeEnhancement?: boolean
|
|
569
|
-
|
|
595
|
+
/** ZSL buffering. Forced off for manual/scan stills because fixed sensor controls take priority. */
|
|
570
596
|
zsl?: boolean
|
|
597
|
+
/** MFNR still capture. Forced off for manual/scan stills because fixed sensor controls take priority. */
|
|
598
|
+
mfnr?: boolean
|
|
571
599
|
ispDigitalGain?: number
|
|
572
600
|
ispAnalogGain?: string
|
|
573
601
|
}
|
|
574
602
|
|
|
575
603
|
export type WarmUpCameraParams = {
|
|
604
|
+
/** Supply this when the owner needs to call stopCameraWarmUp during teardown. */
|
|
576
605
|
requestId?: string
|
|
577
606
|
size: PhotoSize
|
|
607
|
+
mode?: PhotoMode
|
|
578
608
|
exposureTimeNs?: number | null
|
|
609
|
+
/** Ready-state hold; defaults to 15 seconds and is capped at 60 seconds by ASG. */
|
|
579
610
|
durationMs?: number
|
|
611
|
+
/** ZSL preview buffering for the warm-up session. */
|
|
612
|
+
zsl?: boolean
|
|
613
|
+
/** MFNR still capture for the warm-up session. */
|
|
614
|
+
mfnr?: boolean
|
|
580
615
|
}
|
|
581
616
|
|
|
582
617
|
export type StreamVideoConfig = {
|
|
@@ -697,7 +732,7 @@ export type StreamResolvedConfig = {
|
|
|
697
732
|
}
|
|
698
733
|
}
|
|
699
734
|
|
|
700
|
-
/** Live encoder telemetry
|
|
735
|
+
/** Live encoder and device telemetry emitted periodically by supported glasses firmware. */
|
|
701
736
|
export type StreamLiveStats = {
|
|
702
737
|
/** Current encoded video bitrate in bits per second. */
|
|
703
738
|
bitrate?: number
|
|
@@ -706,7 +741,7 @@ export type StreamLiveStats = {
|
|
|
706
741
|
droppedFrames?: number
|
|
707
742
|
/** Seconds since the stream started. */
|
|
708
743
|
duration?: number
|
|
709
|
-
/** Device temperature in
|
|
744
|
+
/** Device temperature in 闂佺娅i悡? if the hardware reports it. */
|
|
710
745
|
temperatureC?: number
|
|
711
746
|
}
|
|
712
747
|
|
|
@@ -766,6 +801,17 @@ export type MtkUpdateCompleteEvent = {
|
|
|
766
801
|
timestamp: number
|
|
767
802
|
}
|
|
768
803
|
|
|
804
|
+
/**
|
|
805
|
+
* The glasses process restarted while the BES kept the BLE link alive (its `sid`
|
|
806
|
+
* changed, or first appeared after an update from a pre-sid build). There is no
|
|
807
|
+
* physical disconnect for this — treat it as the logical reconnect edge.
|
|
808
|
+
*/
|
|
809
|
+
export type GlassesSessionChangedEvent = {
|
|
810
|
+
type: "glasses_session_changed"
|
|
811
|
+
previous_sid: string
|
|
812
|
+
sid: string
|
|
813
|
+
}
|
|
814
|
+
|
|
769
815
|
/** @deprecated Glasses no longer emit ota_progress; use {@link OtaStatusEvent} and status-store mapping. */
|
|
770
816
|
export type OtaProgressEvent = {
|
|
771
817
|
type: "ota_progress"
|
|
@@ -782,16 +828,6 @@ export type OtaStartAckEvent = {
|
|
|
782
828
|
type: "ota_start_ack"
|
|
783
829
|
timestamp: number
|
|
784
830
|
}
|
|
785
|
-
|
|
786
|
-
export type OtaUpdateAvailableEvent = {
|
|
787
|
-
type: "ota_update_available"
|
|
788
|
-
version_code?: number
|
|
789
|
-
version_name?: string
|
|
790
|
-
updates?: string[]
|
|
791
|
-
total_size?: number
|
|
792
|
-
cache_ready?: boolean
|
|
793
|
-
}
|
|
794
|
-
|
|
795
831
|
export type OtaStatusEvent = {
|
|
796
832
|
type: "ota_status"
|
|
797
833
|
session_id: string
|
|
@@ -838,6 +874,8 @@ export type BluetoothSdkModuleEvents = {
|
|
|
838
874
|
speaking_status: (event: SpeakingStatusEvent) => void
|
|
839
875
|
battery_status: (event: BatteryStatusEvent) => void
|
|
840
876
|
local_transcription: (event: LocalTranscriptionEvent) => void
|
|
877
|
+
phone_notification: (event: PhoneNotificationEvent) => void
|
|
878
|
+
phone_notification_dismissed: (event: PhoneNotificationDismissedEvent) => void
|
|
841
879
|
wifi_status_change: (event: WifiStatusChangeEvent) => void
|
|
842
880
|
wifi_scan_result: (event: WifiScanResultEvent) => void
|
|
843
881
|
hotspot_status_change: (event: HotspotStatusChangeEvent) => void
|
|
@@ -868,9 +906,10 @@ export type BluetoothSdkModuleEvents = {
|
|
|
868
906
|
stream_status: (event: StreamStatusEvent) => void
|
|
869
907
|
keep_alive_ack: (event: KeepAliveAckEvent) => void
|
|
870
908
|
mtk_update_complete: (event: MtkUpdateCompleteEvent) => void
|
|
871
|
-
|
|
909
|
+
glasses_session_changed: (event: GlassesSessionChangedEvent) => void
|
|
872
910
|
ota_start_ack: (event: OtaStartAckEvent) => void
|
|
873
911
|
ota_status: (event: OtaStatusEvent) => void
|
|
912
|
+
ar99_ota_status: (event: Ar99OtaStatusEvent) => void
|
|
874
913
|
version_info: (event: VersionInfoEvent) => void
|
|
875
914
|
send_command_to_ble: (event: BleCommandTraceEvent) => void
|
|
876
915
|
receive_command_from_ble: (event: BleCommandTraceEvent) => void
|
|
@@ -884,6 +923,33 @@ export interface ExtractionProgressEvent {
|
|
|
884
923
|
totalBytes: number
|
|
885
924
|
}
|
|
886
925
|
|
|
926
|
+
export interface Ar99OtaStatusEvent {
|
|
927
|
+
type: "ar99_ota_status"
|
|
928
|
+
phase: string
|
|
929
|
+
progress: number
|
|
930
|
+
offset: number
|
|
931
|
+
total: number
|
|
932
|
+
errorMessage?: string
|
|
933
|
+
error_message?: string
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
export interface PhoneNotificationEvent {
|
|
937
|
+
notificationId: string
|
|
938
|
+
app: string
|
|
939
|
+
title: string
|
|
940
|
+
content: string
|
|
941
|
+
priority: string
|
|
942
|
+
timestamp: number
|
|
943
|
+
packageName: string
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
export interface PhoneNotificationDismissedEvent {
|
|
947
|
+
notificationId: string
|
|
948
|
+
notificationKey: string
|
|
949
|
+
packageName: string
|
|
950
|
+
timestamp: number
|
|
951
|
+
}
|
|
952
|
+
|
|
887
953
|
export type PublicGlassesStatus = Omit<
|
|
888
954
|
GlassesStatus,
|
|
889
955
|
"otaUpdateAvailable" | "otaProgress" | "otaInProgress" | "otaVersionUrl"
|
|
@@ -939,9 +1005,9 @@ export type BluetoothSdkEventMap = {
|
|
|
939
1005
|
mic_pcm: MicPcmEvent
|
|
940
1006
|
mic_lc3: MicLc3Event
|
|
941
1007
|
stream_status: StreamStatusEvent
|
|
942
|
-
ota_update_available: OtaUpdateAvailableEvent
|
|
943
1008
|
ota_start_ack: OtaStartAckEvent
|
|
944
1009
|
ota_status: OtaStatusEvent
|
|
1010
|
+
ar99_ota_status: Ar99OtaStatusEvent
|
|
945
1011
|
version_info: VersionInfoEvent
|
|
946
1012
|
extraction_progress: ExtractionProgressEvent
|
|
947
1013
|
}
|
|
@@ -993,15 +1059,20 @@ export interface BluetoothSdkPublicModule {
|
|
|
993
1059
|
|
|
994
1060
|
setGalleryModeEnabled(enabled: boolean): Promise<SettingsAckSuccessEvent>
|
|
995
1061
|
setVoiceActivityDetectionEnabled(enabled: boolean): Promise<void>
|
|
1062
|
+
setLoudnessGateEnabled(enabled: boolean): Promise<void>
|
|
996
1063
|
/**
|
|
997
1064
|
* @deprecated Sticky action-button photo presets are deprecated. Prefer per-request
|
|
998
|
-
* `requestPhoto(...)` options (e.g. `mode: "text"` for
|
|
1065
|
+
* `requestPhoto(...)` options (e.g. `mode: "text"` for text sensor size/crop, or explicit per-shot
|
|
999
1066
|
* fields). Still functional until removed in a future release.
|
|
1000
1067
|
*/
|
|
1001
1068
|
setPhotoCaptureDefaults(settings: PhotoCaptureDefaults): Promise<SettingsAckSuccessEvent>
|
|
1002
1069
|
setVideoRecordingDefaults(settings: VideoRecordingDefaults): Promise<SettingsAckSuccessEvent>
|
|
1003
1070
|
setMaxVideoRecordingDuration(minutes: number): Promise<SettingsAckSuccessEvent>
|
|
1004
1071
|
setCameraFov(request: CameraFovRequest): Promise<CameraFovResult>
|
|
1072
|
+
/** One-way FOV command for legacy ASG clients that do not send settings acknowledgements. */
|
|
1073
|
+
setLegacyCameraFov(request: CameraFovRequest): Promise<CameraFovResult>
|
|
1074
|
+
setCameraFovOverride(request: CameraFovOverrideRequest): Promise<CameraFovResult>
|
|
1075
|
+
releaseCameraFovOverride(leaseId: string): Promise<SettingsAckSuccessEvent>
|
|
1005
1076
|
/**
|
|
1006
1077
|
* Configure camera HAL tuning (ANR / gain) on Mentra Live glasses.
|
|
1007
1078
|
*
|
|
@@ -1019,6 +1090,8 @@ export interface BluetoothSdkPublicModule {
|
|
|
1019
1090
|
queryGalleryStatus(): Promise<GalleryStatusEvent>
|
|
1020
1091
|
requestPhoto(params: PhotoRequestParams): Promise<PhotoSuccessResponseEvent>
|
|
1021
1092
|
warmUpCamera(params: WarmUpCameraParams): Promise<CameraStatusEvent>
|
|
1093
|
+
/** Release one request-owned warm-up. Opening requests reject with camera_warm_up_cancelled. */
|
|
1094
|
+
stopCameraWarmUp(requestId: string): Promise<void>
|
|
1022
1095
|
startVideoRecording(
|
|
1023
1096
|
requestId: string,
|
|
1024
1097
|
save: boolean,
|
|
@@ -1029,7 +1102,7 @@ export interface BluetoothSdkPublicModule {
|
|
|
1029
1102
|
* Stop the active recording. When {@link webhookUrl} is provided, the glasses
|
|
1030
1103
|
* upload the recorded video to it (multipart) using {@link authToken}. These
|
|
1031
1104
|
* are supplied at stop time (not start) so the token is fresh when the upload
|
|
1032
|
-
* runs
|
|
1105
|
+
* runs 闂?a recording can last arbitrarily long. An empty/omitted webhook keeps
|
|
1033
1106
|
* the video on device (no upload).
|
|
1034
1107
|
*/
|
|
1035
1108
|
stopVideoRecording(
|
|
@@ -1062,6 +1135,10 @@ export interface BluetoothSdkPublicModule {
|
|
|
1062
1135
|
checkForOtaUpdate(): Promise<boolean>
|
|
1063
1136
|
/** Start the OTA flow with the same configured manifest URL used by checkForOtaUpdate(). */
|
|
1064
1137
|
startOtaUpdate(): Promise<OtaStartAckEvent>
|
|
1138
|
+
startAr99OtaFromFile(path: string): Promise<boolean>
|
|
1139
|
+
cancelAr99Ota(): Promise<void>
|
|
1140
|
+
sendAr99FactoryReset(): Promise<void>
|
|
1141
|
+
buildAr99OtaSignature(secret: string, appName: string, currentVersion: string, serialNumber: string, nonce: string): string
|
|
1065
1142
|
|
|
1066
1143
|
// // stt commands (MOVE TO CRUST)
|
|
1067
1144
|
// setSttModelDetails(path: string, languageCode: string): Promise<void>
|
|
@@ -1124,6 +1201,8 @@ export interface OtaUpdateInfo {
|
|
|
1124
1201
|
updates: string[] // ["apk", "mtk", "bes"]
|
|
1125
1202
|
totalSize: number
|
|
1126
1203
|
cacheReady?: boolean
|
|
1204
|
+
/** True when the APK step installs an older build than the glasses currently run (exact-pin manifests only). */
|
|
1205
|
+
isDowngrade?: boolean
|
|
1127
1206
|
}
|
|
1128
1207
|
|
|
1129
1208
|
export interface OtaProgress {
|
|
@@ -1208,13 +1287,15 @@ export interface Device {
|
|
|
1208
1287
|
/**
|
|
1209
1288
|
* Stable app-facing key for this scan result, within the limits of the
|
|
1210
1289
|
* platform identifier available to the SDK. Do not parse this value; use the
|
|
1211
|
-
* typed model, name, address, and rssi fields instead.
|
|
1290
|
+
* typed model, name, address, projectName, and rssi fields instead.
|
|
1212
1291
|
*/
|
|
1213
1292
|
id: string
|
|
1214
1293
|
model: DeviceModel
|
|
1215
1294
|
name: string
|
|
1216
1295
|
/** Platform address/identifier when available: Android Bluetooth address, iOS CoreBluetooth identifier. */
|
|
1217
1296
|
address?: string
|
|
1297
|
+
/** Optional AR99 project discriminator. Supported value: AR99. */
|
|
1298
|
+
projectName?: string
|
|
1218
1299
|
/**
|
|
1219
1300
|
* Optional scan signal strength. It may be undefined at first discovery and
|
|
1220
1301
|
* appear in a later scan update when the platform reports RSSI metadata.
|
|
@@ -1245,7 +1326,7 @@ export interface WifiSearchResult {
|
|
|
1245
1326
|
ssid: string
|
|
1246
1327
|
requiresPassword: boolean
|
|
1247
1328
|
signalStrength: number
|
|
1248
|
-
/** Frequency in MHz (from glasses scan). 5 GHz band is typically 5170
|
|
1329
|
+
/** Frequency in MHz (from glasses scan). 5 GHz band is typically 5170闂?825. Omitted if unknown. */
|
|
1249
1330
|
frequency?: number
|
|
1250
1331
|
}
|
|
1251
1332
|
|
|
@@ -1298,6 +1379,7 @@ export type BluetoothSettingsUpdate = Partial<{
|
|
|
1298
1379
|
twelve_hour_time: boolean
|
|
1299
1380
|
gallery_mode: boolean
|
|
1300
1381
|
voice_activity_detection_enabled: boolean
|
|
1382
|
+
loudness_gate_enabled: boolean
|
|
1301
1383
|
button_photo_size: ButtonPhotoSize
|
|
1302
1384
|
button_video_settings: {width: number; height: number; fps: number}
|
|
1303
1385
|
button_video_width: number
|
package/src/_internal.ts
CHANGED
|
@@ -9,3 +9,6 @@
|
|
|
9
9
|
export {default} from "./_private/BluetoothSdkModule"
|
|
10
10
|
export type {BluetoothSdkInternalModule} from "./_private/BluetoothSdkModule"
|
|
11
11
|
export * from "./BluetoothSdk.types"
|
|
12
|
+
export {default as MentraLocalNetwork} from "./_private/MentraLocalNetworkModule"
|
|
13
|
+
export * from "./_private/MentraLocalNetworkModule"
|
|
14
|
+
export {BLUETOOTH_SDK_VERSION, sdkPinnedOtaManifestUrl} from "./_private/sdkOtaManifest"
|