@mentra/bluetooth-sdk 0.1.13 → 0.1.16
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 +18 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +48 -48
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +5 -20
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +360 -269
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +3 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +152 -72
- package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +12 -12
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +25 -11
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +1 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +1 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +1 -22
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +51 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +1184 -800
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +1 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +423 -117
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +4 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +2532 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +17 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +3 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +0 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +16 -33
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +5 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +2 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +60 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
- package/build/BluetoothSdk.types.d.ts +50 -24
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +1 -0
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +4 -4
- package/build/_internal.js +4 -4
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +14 -8
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +4 -3
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/cameraRequestPayload.d.ts +4 -0
- package/build/_private/cameraRequestPayload.d.ts.map +1 -0
- package/build/_private/cameraRequestPayload.js +25 -0
- package/build/_private/cameraRequestPayload.js.map +1 -0
- package/build/_private/photoRequestPayload.d.ts.map +1 -1
- package/build/_private/photoRequestPayload.js +11 -3
- package/build/_private/photoRequestPayload.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +70 -61
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +51 -28
- package/ios/Source/Bridge.swift +4 -16
- package/ios/Source/DeviceManager.swift +76 -22
- package/ios/Source/DeviceStore.swift +0 -4
- package/ios/Source/MentraBluetoothSDK.swift +144 -78
- package/ios/Source/OtaManifest.swift +13 -13
- package/ios/Source/camera/CameraModels.swift +82 -20
- package/ios/Source/controllers/ControllerManager.swift +1 -2
- package/ios/Source/controllers/R1.swift +1 -2
- package/ios/Source/errors/{BluetoothError.swift → BluetoothSdkError.swift} +1 -1
- package/ios/Source/events/BluetoothEvents.swift +5 -27
- package/ios/Source/internal/BluetoothAvailability.swift +5 -5
- package/ios/Source/sgcs/Frame.swift +0 -2
- package/ios/Source/sgcs/G1.swift +52 -4
- package/ios/Source/sgcs/G2.swift +643 -204
- package/ios/Source/sgcs/Mach1.swift +1 -3
- package/ios/Source/sgcs/MentraLive.swift +64 -77
- package/ios/Source/sgcs/MentraNex.swift +1 -3
- package/ios/Source/sgcs/Nimo.swift +1928 -0
- package/ios/Source/sgcs/SGCManager.swift +16 -10
- package/ios/Source/sgcs/Simulated.swift +2 -6
- package/ios/Source/status/DeviceStatus.swift +0 -8
- package/ios/Source/streaming/StreamModels.swift +7 -32
- package/ios/Source/stt/STTTools.swift +1 -1
- package/ios/Source/types/DeviceModels.swift +5 -0
- package/ios/Source/utils/Constants.swift +2 -0
- package/package.json +6 -1
- package/src/BluetoothSdk.types.ts +54 -28
- package/src/_internal.ts +4 -4
- package/src/_private/BluetoothSdkModule.ts +20 -11
- package/src/_private/cameraRequestPayload.ts +29 -0
- package/src/_private/photoRequestPayload.ts +12 -3
- package/src/index.ts +77 -67
|
@@ -22,13 +22,13 @@ protocol SGCManager {
|
|
|
22
22
|
func startStream(_ message: [String: Any])
|
|
23
23
|
func stopStream()
|
|
24
24
|
func sendStreamKeepAlive(_ message: [String: Any])
|
|
25
|
-
func startVideoRecording(requestId: String, save: Bool,
|
|
25
|
+
func startVideoRecording(requestId: String, save: Bool, sound: Bool)
|
|
26
26
|
/// Start video recording with optional per-recording resolution/fps. A width,
|
|
27
27
|
/// height, or fps of 0 means "use the device's saved button-video default".
|
|
28
28
|
/// Defaulted in an extension to delegate to the basic recording path; devices
|
|
29
29
|
/// that support custom settings (e.g. Mentra Live) override this.
|
|
30
30
|
func startVideoRecording(
|
|
31
|
-
requestId: String, save: Bool,
|
|
31
|
+
requestId: String, save: Bool, sound: Bool, width: Int, height: Int, fps: Int,
|
|
32
32
|
maxRecordingTimeMinutes: Int
|
|
33
33
|
)
|
|
34
34
|
func stopVideoRecording(requestId: String)
|
|
@@ -44,7 +44,6 @@ protocol SGCManager {
|
|
|
44
44
|
func sendButtonPhotoSettings()
|
|
45
45
|
func sendButtonVideoRecordingSettings()
|
|
46
46
|
func sendButtonMaxRecordingTime()
|
|
47
|
-
func sendButtonCameraLedSetting()
|
|
48
47
|
func sendCameraFovSetting()
|
|
49
48
|
|
|
50
49
|
// MARK: - Display Control
|
|
@@ -57,6 +56,12 @@ protocol SGCManager {
|
|
|
57
56
|
/// Display a bitmap. Optional `x`/`y`/`width`/`height` position and size the target
|
|
58
57
|
/// container (used by G2; other SGCs ignore positioning and render the bitmap as before).
|
|
59
58
|
func displayBitmap(base64ImageData: String, x: Int32?, y: Int32?, width: Int32?, height: Int32?) async -> Bool
|
|
59
|
+
/// Show text in a positioned container with an optional rounded border.
|
|
60
|
+
/// G2-only capability; default no-op (see protocol extension) so other glasses ignore it.
|
|
61
|
+
func sendPositionedText(
|
|
62
|
+
_ text: String, x: Int32, y: Int32, width: Int32, height: Int32,
|
|
63
|
+
borderWidth: Int32, borderRadius: Int32
|
|
64
|
+
) async
|
|
60
65
|
func showDashboard()
|
|
61
66
|
func setDashboardPosition(_ height: Int, _ depth: Int)
|
|
62
67
|
/// Default implementation sends both via [setDashboardPosition]; Nex overrides to one protobuf.
|
|
@@ -119,7 +124,6 @@ protocol SGCManager {
|
|
|
119
124
|
func sendOtaStart(otaVersionUrl: String?)
|
|
120
125
|
func sendOtaQueryStatus()
|
|
121
126
|
func sendSetSystemTime(_ timestampMs: Int64)
|
|
122
|
-
func sendOtaRetryVersionCheck()
|
|
123
127
|
|
|
124
128
|
// MARK: - User Context (for crash reporting)
|
|
125
129
|
|
|
@@ -146,13 +150,19 @@ protocol SGCManager {
|
|
|
146
150
|
/// doesn't seem to work for concurrency reasons :(
|
|
147
151
|
/// we can make read-only getters for convienence though:
|
|
148
152
|
extension SGCManager {
|
|
153
|
+
/// Default: no-op. Only G2 renders positioned text containers; other glasses ignore it.
|
|
154
|
+
func sendPositionedText(
|
|
155
|
+
_: String, x _: Int32, y _: Int32, width _: Int32, height _: Int32,
|
|
156
|
+
borderWidth _: Int32, borderRadius _: Int32
|
|
157
|
+
) async {}
|
|
158
|
+
|
|
149
159
|
// MARK: - Video recording (default: ignore custom settings, use saved defaults)
|
|
150
160
|
|
|
151
161
|
func startVideoRecording(
|
|
152
|
-
requestId: String, save: Bool,
|
|
162
|
+
requestId: String, save: Bool, sound: Bool, width _: Int, height _: Int,
|
|
153
163
|
fps _: Int, maxRecordingTimeMinutes _: Int
|
|
154
164
|
) {
|
|
155
|
-
startVideoRecording(requestId: requestId, save: save,
|
|
165
|
+
startVideoRecording(requestId: requestId, save: save, sound: sound)
|
|
156
166
|
}
|
|
157
167
|
|
|
158
168
|
func stopVideoRecording(requestId: String, webhookUrl _: String?, authToken _: String?) {
|
|
@@ -202,10 +212,6 @@ extension SGCManager {
|
|
|
202
212
|
Bridge.log("SGC: sendSetSystemTime not supported")
|
|
203
213
|
}
|
|
204
214
|
|
|
205
|
-
func sendOtaRetryVersionCheck() {
|
|
206
|
-
Bridge.log("SGC: sendOtaRetryVersionCheck not supported")
|
|
207
|
-
}
|
|
208
|
-
|
|
209
215
|
// MARK: - Default DeviceStore-backed property implementations
|
|
210
216
|
|
|
211
217
|
var fullyBooted: Bool {
|
|
@@ -80,7 +80,7 @@ class Simulated: SGCManager {
|
|
|
80
80
|
// MARK: - Camera & Media
|
|
81
81
|
|
|
82
82
|
func requestPhoto(_ request: PhotoRequest) {
|
|
83
|
-
Bridge.log("requestPhoto
|
|
83
|
+
Bridge.log("requestPhoto save=\(request.save) sound=\(request.sound)")
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
func startStream(_: [String: Any]) {
|
|
@@ -95,7 +95,7 @@ class Simulated: SGCManager {
|
|
|
95
95
|
Bridge.log("sendStreamKeepAlive")
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
func startVideoRecording(requestId _: String, save _: Bool,
|
|
98
|
+
func startVideoRecording(requestId _: String, save _: Bool, sound _: Bool) {
|
|
99
99
|
Bridge.log("startVideoRecording")
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -113,10 +113,6 @@ class Simulated: SGCManager {
|
|
|
113
113
|
Bridge.log("sendButtonVideoRecordingSettings")
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
func sendButtonCameraLedSetting() {
|
|
117
|
-
Bridge.log("sendButtonCameraLedSetting")
|
|
118
|
-
}
|
|
119
|
-
|
|
120
116
|
func sendCameraFovSetting() {
|
|
121
117
|
Bridge.log("sendCameraFovSetting")
|
|
122
118
|
}
|
|
@@ -552,10 +552,6 @@ struct BluetoothStatus: CustomStringConvertible {
|
|
|
552
552
|
ButtonPhotoSize(rawValue: stringValue(values, "button_photo_size") ?? "") ?? .medium
|
|
553
553
|
}
|
|
554
554
|
|
|
555
|
-
var buttonCameraLed: Bool {
|
|
556
|
-
boolValue(values, "button_camera_led") ?? true
|
|
557
|
-
}
|
|
558
|
-
|
|
559
555
|
var buttonMaxRecordingTime: Int {
|
|
560
556
|
intValue(values["button_max_recording_time"]) ?? 10
|
|
561
557
|
}
|
|
@@ -924,10 +920,6 @@ struct BluetoothStatusUpdate: CustomStringConvertible {
|
|
|
924
920
|
optionalStringValue(values, "button_photo_size").map { ButtonPhotoSize(normalizedRawValue: $0) }
|
|
925
921
|
}
|
|
926
922
|
|
|
927
|
-
var buttonCameraLed: Bool? {
|
|
928
|
-
optionalBoolValue(values, "button_camera_led")
|
|
929
|
-
}
|
|
930
|
-
|
|
931
923
|
var buttonMaxRecordingTime: Int? {
|
|
932
924
|
optionalIntValue(values, "button_max_recording_time")
|
|
933
925
|
}
|
|
@@ -23,6 +23,7 @@ public struct StreamVideoConfig {
|
|
|
23
23
|
if let width { values["width"] = width }
|
|
24
24
|
if let height { values["height"] = height }
|
|
25
25
|
if let bitrate { values["bitrate"] = bitrate }
|
|
26
|
+
// ASG stream parsers shipped with the BLE key named "frameRate".
|
|
26
27
|
if let fps { values["frameRate"] = fps }
|
|
27
28
|
return values
|
|
28
29
|
}
|
|
@@ -219,31 +220,22 @@ public struct StreamResolvedConfig: Equatable {
|
|
|
219
220
|
public struct StreamRequest {
|
|
220
221
|
public let streamUrl: String
|
|
221
222
|
public let streamId: String
|
|
222
|
-
public let keepAlive: Bool
|
|
223
|
-
public let keepAliveIntervalSeconds: Int
|
|
224
223
|
public let sound: Bool
|
|
225
224
|
public let video: StreamVideoConfig?
|
|
226
225
|
public let audio: StreamAudioConfig?
|
|
227
|
-
public let extraValues: [String: Any]
|
|
228
226
|
|
|
229
227
|
public init(
|
|
230
228
|
streamUrl: String,
|
|
231
229
|
streamId: String = "",
|
|
232
|
-
keepAlive: Bool = true,
|
|
233
|
-
keepAliveIntervalSeconds: Int = 5,
|
|
234
230
|
sound: Bool = true,
|
|
235
231
|
video: StreamVideoConfig? = nil,
|
|
236
|
-
audio: StreamAudioConfig? = nil
|
|
237
|
-
extraValues: [String: Any] = [:]
|
|
232
|
+
audio: StreamAudioConfig? = nil
|
|
238
233
|
) {
|
|
239
234
|
self.streamUrl = streamUrl
|
|
240
235
|
self.streamId = streamId
|
|
241
|
-
self.keepAlive = keepAlive
|
|
242
|
-
self.keepAliveIntervalSeconds = keepAliveIntervalSeconds
|
|
243
236
|
self.sound = sound
|
|
244
237
|
self.video = video
|
|
245
238
|
self.audio = audio
|
|
246
|
-
self.extraValues = extraValues
|
|
247
239
|
}
|
|
248
240
|
|
|
249
241
|
init(values: [String: Any]) {
|
|
@@ -254,25 +246,17 @@ public struct StreamRequest {
|
|
|
254
246
|
?? values["whipUrl"] as? String
|
|
255
247
|
?? "",
|
|
256
248
|
streamId: values["streamId"] as? String ?? "",
|
|
257
|
-
keepAlive: values["keepAlive"] as? Bool ?? true,
|
|
258
|
-
keepAliveIntervalSeconds: intValue(values["keepAliveIntervalSeconds"]) ?? 5,
|
|
259
249
|
sound: values["sound"] as? Bool ?? true,
|
|
260
250
|
video: StreamVideoConfig(values: values["video"] as? [String: Any]),
|
|
261
|
-
audio: StreamAudioConfig(values: values["audio"] as? [String: Any])
|
|
262
|
-
extraValues: values
|
|
251
|
+
audio: StreamAudioConfig(values: values["audio"] as? [String: Any])
|
|
263
252
|
)
|
|
264
253
|
}
|
|
265
254
|
|
|
266
255
|
public var values: [String: Any] {
|
|
267
|
-
var values =
|
|
268
|
-
values.removeValue(forKey: "keepAliveMode")
|
|
256
|
+
var values: [String: Any] = [:]
|
|
269
257
|
values["type"] = "start_stream"
|
|
270
258
|
values["streamUrl"] = streamUrl
|
|
271
259
|
values["streamId"] = streamId
|
|
272
|
-
values["keepAlive"] = keepAlive
|
|
273
|
-
values["keepAliveIntervalSeconds"] = keepAliveIntervalSeconds
|
|
274
|
-
// The camera light is a privacy indicator and cannot be disabled by SDK callers.
|
|
275
|
-
values["flash"] = true
|
|
276
260
|
values["sound"] = sound
|
|
277
261
|
if let videoValues = video?.dictionary, !videoValues.isEmpty {
|
|
278
262
|
values["video"] = videoValues
|
|
@@ -284,33 +268,24 @@ public struct StreamRequest {
|
|
|
284
268
|
}
|
|
285
269
|
}
|
|
286
270
|
|
|
287
|
-
extension StreamRequest {
|
|
288
|
-
var isExternallyManagedKeepAlive: Bool {
|
|
289
|
-
stringValue(extraValues, "keepAliveMode") == "external"
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
271
|
struct StreamKeepAliveRequest {
|
|
294
272
|
let streamId: String
|
|
295
273
|
let ackId: String
|
|
296
|
-
let extraValues: [String: Any]
|
|
297
274
|
|
|
298
|
-
init(streamId: String, ackId: String
|
|
275
|
+
init(streamId: String, ackId: String) {
|
|
299
276
|
self.streamId = streamId
|
|
300
277
|
self.ackId = ackId
|
|
301
|
-
self.extraValues = extraValues
|
|
302
278
|
}
|
|
303
279
|
|
|
304
280
|
init(values: [String: Any]) {
|
|
305
281
|
self.init(
|
|
306
282
|
streamId: values["streamId"] as? String ?? "",
|
|
307
|
-
ackId: values["ackId"] as? String ?? ""
|
|
308
|
-
extraValues: values
|
|
283
|
+
ackId: values["ackId"] as? String ?? ""
|
|
309
284
|
)
|
|
310
285
|
}
|
|
311
286
|
|
|
312
287
|
var values: [String: Any] {
|
|
313
|
-
var values =
|
|
288
|
+
var values: [String: Any] = [:]
|
|
314
289
|
values["type"] = "keep_stream_alive"
|
|
315
290
|
values["streamId"] = streamId
|
|
316
291
|
values["ackId"] = ackId
|
|
@@ -7,7 +7,7 @@ class STTTools {
|
|
|
7
7
|
// Send partial result to server witgetConnectedBluetoothNameh proper formatting
|
|
8
8
|
let transcriptionLanguage =
|
|
9
9
|
UserDefaults.standard.string(forKey: "STTModelLanguageCode") ?? "en-US"
|
|
10
|
-
Bridge.log("Mentra: Sending partial transcription: \(text), \(transcriptionLanguage)")
|
|
10
|
+
// Bridge.log("Mentra: Sending partial transcription: \(text), \(transcriptionLanguage)")
|
|
11
11
|
let transcription: [String: Any] = [
|
|
12
12
|
"type": "local_transcription",
|
|
13
13
|
"text": transcriptionLanguage == "en-US" ? text.lowercased() : text,
|
|
@@ -18,6 +18,7 @@ public enum DeviceModel: String {
|
|
|
18
18
|
case mach1
|
|
19
19
|
case z100
|
|
20
20
|
case frame
|
|
21
|
+
case nimo
|
|
21
22
|
case simulated
|
|
22
23
|
case r1
|
|
23
24
|
|
|
@@ -37,6 +38,8 @@ public enum DeviceModel: String {
|
|
|
37
38
|
DeviceTypes.Z100
|
|
38
39
|
case .frame:
|
|
39
40
|
DeviceTypes.FRAME
|
|
41
|
+
case .nimo:
|
|
42
|
+
DeviceTypes.NIMO
|
|
40
43
|
case .simulated:
|
|
41
44
|
DeviceTypes.SIMULATED
|
|
42
45
|
case .r1:
|
|
@@ -60,6 +63,8 @@ public enum DeviceModel: String {
|
|
|
60
63
|
.z100
|
|
61
64
|
case DeviceTypes.FRAME:
|
|
62
65
|
.frame
|
|
66
|
+
case DeviceTypes.NIMO:
|
|
67
|
+
.nimo
|
|
63
68
|
case DeviceTypes.SIMULATED:
|
|
64
69
|
.simulated
|
|
65
70
|
case ControllerTypes.R1:
|
|
@@ -7,6 +7,7 @@ struct DeviceTypes {
|
|
|
7
7
|
static let Z100 = "Vuzix Z100"
|
|
8
8
|
static let NEX = "Mentra Display"
|
|
9
9
|
static let FRAME = "Brilliant Frame"
|
|
10
|
+
static let NIMO = "NIMO"
|
|
10
11
|
|
|
11
12
|
static let ALL = [
|
|
12
13
|
SIMULATED,
|
|
@@ -17,6 +18,7 @@ struct DeviceTypes {
|
|
|
17
18
|
Z100,
|
|
18
19
|
NEX,
|
|
19
20
|
FRAME,
|
|
21
|
+
NIMO,
|
|
20
22
|
]
|
|
21
23
|
|
|
22
24
|
/// Private init to prevent instantiation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mentra/bluetooth-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "SDK for communicating with smart glasses",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"react-native": "src/index.ts",
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
"react-native": "./src/index.ts",
|
|
13
13
|
"default": "./build/index.js"
|
|
14
14
|
},
|
|
15
|
+
"./internal": {
|
|
16
|
+
"types": "./build/_internal.d.ts",
|
|
17
|
+
"react-native": "./src/_internal.ts",
|
|
18
|
+
"default": "./build/_internal.js"
|
|
19
|
+
},
|
|
15
20
|
"./react": {
|
|
16
21
|
"types": "./build/react/index.d.ts",
|
|
17
22
|
"react-native": "./src/react/index.ts",
|
|
@@ -270,6 +270,15 @@ export type PhotoStatusEvent = {
|
|
|
270
270
|
errorMessage?: string
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
export type CameraStatusEvent = {
|
|
274
|
+
type: "camera_status"
|
|
275
|
+
requestId: string
|
|
276
|
+
state: "warming" | "ready" | "stopped" | "error" | string
|
|
277
|
+
timestamp: number
|
|
278
|
+
errorCode?: string
|
|
279
|
+
errorMessage?: string
|
|
280
|
+
}
|
|
281
|
+
|
|
273
282
|
export type VideoRecordingStatusEvent = {
|
|
274
283
|
type: "video_recording_status"
|
|
275
284
|
requestId?: string
|
|
@@ -310,9 +319,7 @@ export type VideoRecordingStoppedStatusEvent = Omit<VideoRecordingStatusEvent, "
|
|
|
310
319
|
status: "recording_stopped"
|
|
311
320
|
}
|
|
312
321
|
|
|
313
|
-
export type VideoRecordingSuccessStatusEvent =
|
|
314
|
-
| VideoRecordingStartedStatusEvent
|
|
315
|
-
| VideoRecordingStoppedStatusEvent
|
|
322
|
+
export type VideoRecordingSuccessStatusEvent = VideoRecordingStartedStatusEvent | VideoRecordingStoppedStatusEvent
|
|
316
323
|
|
|
317
324
|
export type MediaUploadSuccessEvent = {
|
|
318
325
|
type: "media_success"
|
|
@@ -396,9 +403,9 @@ export type SettingsAckSetting =
|
|
|
396
403
|
| "gallery_mode"
|
|
397
404
|
| "button_photo"
|
|
398
405
|
| "button_video_recording"
|
|
399
|
-
| "button_camera_led"
|
|
400
406
|
| "button_max_recording_time"
|
|
401
407
|
| "camera_fov"
|
|
408
|
+
| "camera_tuning"
|
|
402
409
|
|
|
403
410
|
export type SettingsAckEvent = {
|
|
404
411
|
type: "settings_ack"
|
|
@@ -416,6 +423,10 @@ export type SettingsAckEvent = {
|
|
|
416
423
|
fps?: number
|
|
417
424
|
enabled?: boolean
|
|
418
425
|
minutes?: number
|
|
426
|
+
/** ANR enabled flag; present when setting === "camera_tuning" */
|
|
427
|
+
anr?: boolean
|
|
428
|
+
/** Stock-gain flag; present when setting === "camera_tuning" */
|
|
429
|
+
gain?: boolean
|
|
419
430
|
errorCode?: string
|
|
420
431
|
errorMessage?: string
|
|
421
432
|
}
|
|
@@ -431,8 +442,8 @@ export type RgbLedColor = "red" | "green" | "blue" | "orange" | "white"
|
|
|
431
442
|
export type PhotoSize = "low" | "medium" | "high" | "max"
|
|
432
443
|
export type ButtonPhotoSize = "low" | "medium" | "high" | "max"
|
|
433
444
|
|
|
434
|
-
export type
|
|
435
|
-
size
|
|
445
|
+
export type PhotoCaptureDefaults = {
|
|
446
|
+
size?: PhotoSize
|
|
436
447
|
mfnr?: boolean
|
|
437
448
|
zsl?: boolean
|
|
438
449
|
noiseReduction?: boolean
|
|
@@ -448,9 +459,15 @@ export type ButtonPhotoSettings = {
|
|
|
448
459
|
}
|
|
449
460
|
export type PhotoCompression = "none" | "medium" | "heavy"
|
|
450
461
|
|
|
462
|
+
export type VideoRecordingDefaults = {
|
|
463
|
+
width: number
|
|
464
|
+
height: number
|
|
465
|
+
fps: number
|
|
466
|
+
}
|
|
467
|
+
|
|
451
468
|
/**
|
|
452
469
|
* Optional per-recording video settings for {@link startVideoRecording}. When
|
|
453
|
-
* omitted, the glasses fall back to their saved
|
|
470
|
+
* omitted, the glasses fall back to their saved video recording defaults. Any
|
|
454
471
|
* field left undefined is omitted from the BLE command (glasses default applies).
|
|
455
472
|
*/
|
|
456
473
|
export interface VideoRecordingSettings {
|
|
@@ -473,6 +490,7 @@ export const DeviceModels = {
|
|
|
473
490
|
Mach1: "Mentra Mach1",
|
|
474
491
|
Z100: "Vuzix Z100",
|
|
475
492
|
Frame: "Brilliant Frame",
|
|
493
|
+
Nimo: "NIMO",
|
|
476
494
|
R1: "Even Realities R1",
|
|
477
495
|
} as const
|
|
478
496
|
|
|
@@ -523,8 +541,7 @@ export type MicPreference = "auto" | "phone" | "glasses" | "bluetooth"
|
|
|
523
541
|
export type MicMode = "phone" | "glasses" | "bluetoothClassic" | "bluetooth"
|
|
524
542
|
|
|
525
543
|
export type PhotoRequestParams = {
|
|
526
|
-
requestId
|
|
527
|
-
appId: string
|
|
544
|
+
requestId?: string
|
|
528
545
|
size: PhotoSize
|
|
529
546
|
webhookUrl: string | null
|
|
530
547
|
authToken: string | null
|
|
@@ -547,6 +564,13 @@ export type PhotoRequestParams = {
|
|
|
547
564
|
ispAnalogGain?: string
|
|
548
565
|
}
|
|
549
566
|
|
|
567
|
+
export type WarmUpCameraParams = {
|
|
568
|
+
requestId?: string
|
|
569
|
+
size: PhotoSize
|
|
570
|
+
exposureTimeNs?: number | null
|
|
571
|
+
durationMs?: number
|
|
572
|
+
}
|
|
573
|
+
|
|
550
574
|
export type StreamVideoConfig = {
|
|
551
575
|
width?: number
|
|
552
576
|
height?: number
|
|
@@ -565,8 +589,6 @@ export type StreamStartRequest = {
|
|
|
565
589
|
type?: "start_stream"
|
|
566
590
|
streamUrl: string
|
|
567
591
|
streamId?: string
|
|
568
|
-
keepAlive?: boolean
|
|
569
|
-
keepAliveIntervalSeconds?: number
|
|
570
592
|
sound?: boolean
|
|
571
593
|
video?: StreamVideoConfig
|
|
572
594
|
audio?: StreamAudioConfig
|
|
@@ -722,15 +744,6 @@ export type MtkUpdateCompleteEvent = {
|
|
|
722
744
|
timestamp: number
|
|
723
745
|
}
|
|
724
746
|
|
|
725
|
-
export type OtaUpdateAvailableEvent = {
|
|
726
|
-
type: "ota_update_available"
|
|
727
|
-
version_code?: number
|
|
728
|
-
version_name?: string
|
|
729
|
-
updates?: string[]
|
|
730
|
-
total_size?: number
|
|
731
|
-
cache_ready?: boolean
|
|
732
|
-
}
|
|
733
|
-
|
|
734
747
|
/** @deprecated Glasses no longer emit ota_progress; use {@link OtaStatusEvent} and status-store mapping. */
|
|
735
748
|
export type OtaProgressEvent = {
|
|
736
749
|
type: "ota_progress"
|
|
@@ -761,7 +774,7 @@ export type OtaStatusEvent = {
|
|
|
761
774
|
error_message?: string
|
|
762
775
|
}
|
|
763
776
|
|
|
764
|
-
export type OtaQueryResult =
|
|
777
|
+
export type OtaQueryResult = OtaStatusEvent
|
|
765
778
|
|
|
766
779
|
/** Nex BLE protobuf trace (NexEventUtils); payload matches native Map keys. */
|
|
767
780
|
export type BleCommandTraceEvent = {
|
|
@@ -800,6 +813,7 @@ export type BluetoothSdkModuleEvents = {
|
|
|
800
813
|
hotspot_error: (event: HotspotErrorEvent) => void
|
|
801
814
|
photo_response: (event: PhotoResponseEvent) => void
|
|
802
815
|
photo_status: (event: PhotoStatusEvent) => void
|
|
816
|
+
camera_status: (event: CameraStatusEvent) => void
|
|
803
817
|
video_recording_status: (event: VideoRecordingStatusEvent) => void
|
|
804
818
|
media_success: (event: MediaUploadSuccessEvent) => void
|
|
805
819
|
media_error: (event: MediaUploadErrorEvent) => void
|
|
@@ -823,7 +837,6 @@ export type BluetoothSdkModuleEvents = {
|
|
|
823
837
|
stream_status: (event: StreamStatusEvent) => void
|
|
824
838
|
keep_alive_ack: (event: KeepAliveAckEvent) => void
|
|
825
839
|
mtk_update_complete: (event: MtkUpdateCompleteEvent) => void
|
|
826
|
-
ota_update_available: (event: OtaUpdateAvailableEvent) => void
|
|
827
840
|
ota_start_ack: (event: OtaStartAckEvent) => void
|
|
828
841
|
ota_status: (event: OtaStatusEvent) => void
|
|
829
842
|
version_info: (event: VersionInfoEvent) => void
|
|
@@ -877,6 +890,7 @@ export type BluetoothSdkEventMap = {
|
|
|
877
890
|
hotspot_error: HotspotErrorEvent
|
|
878
891
|
photo_response: PhotoResponseEvent
|
|
879
892
|
photo_status: PhotoStatusEvent
|
|
893
|
+
camera_status: CameraStatusEvent
|
|
880
894
|
video_recording_status: VideoRecordingStatusEvent
|
|
881
895
|
media_success: MediaUploadSuccessEvent
|
|
882
896
|
media_error: MediaUploadErrorEvent
|
|
@@ -946,13 +960,27 @@ export interface BluetoothSdkPublicModule {
|
|
|
946
960
|
|
|
947
961
|
setGalleryModeEnabled(enabled: boolean): Promise<SettingsAckSuccessEvent>
|
|
948
962
|
setVoiceActivityDetectionEnabled(enabled: boolean): Promise<void>
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
setButtonMaxRecordingTime(minutes: number): Promise<SettingsAckSuccessEvent>
|
|
963
|
+
setPhotoCaptureDefaults(settings: PhotoCaptureDefaults): Promise<SettingsAckSuccessEvent>
|
|
964
|
+
setVideoRecordingDefaults(settings: VideoRecordingDefaults): Promise<SettingsAckSuccessEvent>
|
|
965
|
+
setMaxVideoRecordingDuration(minutes: number): Promise<SettingsAckSuccessEvent>
|
|
953
966
|
setCameraFov(request: CameraFovRequest): Promise<CameraFovResult>
|
|
967
|
+
/**
|
|
968
|
+
* Configure camera HAL tuning (ANR / gain) on Mentra Live glasses.
|
|
969
|
+
*
|
|
970
|
+
* The phone sends a {@code camera_tuning_config} BLE command; the glasses relay it as a
|
|
971
|
+
* {@code camconfig} broadcast to the camera HAL so parameters take effect without a reboot.
|
|
972
|
+
*
|
|
973
|
+
* **Scan-mode convention**: call with `(false, false)` when activating scan mode to disable ANR
|
|
974
|
+
* and pixsmart gain for sharper text/barcode captures. Call with `(true, true)` to restore
|
|
975
|
+
* defaults when exiting scan mode.
|
|
976
|
+
*
|
|
977
|
+
* @param anrOn `true` = ANR enabled (default), `false` = ANR disabled
|
|
978
|
+
* @param gainOn `true` = stock gain params (default), `false` = pixsmart gain-off params
|
|
979
|
+
*/
|
|
980
|
+
setCameraTuningConfig(anrOn: boolean, gainOn: boolean): Promise<SettingsAckSuccessEvent>
|
|
954
981
|
queryGalleryStatus(): Promise<GalleryStatusEvent>
|
|
955
982
|
requestPhoto(params: PhotoRequestParams): Promise<PhotoSuccessResponseEvent>
|
|
983
|
+
warmUpCamera(params: WarmUpCameraParams): Promise<CameraStatusEvent>
|
|
956
984
|
startVideoRecording(
|
|
957
985
|
requestId: string,
|
|
958
986
|
save: boolean,
|
|
@@ -1057,7 +1085,6 @@ export interface OtaUpdateInfo {
|
|
|
1057
1085
|
versionName: string
|
|
1058
1086
|
updates: string[] // ["apk", "mtk", "bes"]
|
|
1059
1087
|
totalSize: number
|
|
1060
|
-
cacheReady?: boolean
|
|
1061
1088
|
}
|
|
1062
1089
|
|
|
1063
1090
|
export interface OtaProgress {
|
|
@@ -1237,7 +1264,6 @@ export type BluetoothSettingsUpdate = Partial<{
|
|
|
1237
1264
|
button_video_width: number
|
|
1238
1265
|
button_video_height: number
|
|
1239
1266
|
button_video_fps: number
|
|
1240
|
-
button_camera_led: boolean
|
|
1241
1267
|
button_max_recording_time: number
|
|
1242
1268
|
camera_fov: NativeCameraFovSetting
|
|
1243
1269
|
should_send_pcm: boolean
|
package/src/_internal.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MentraOS-only compatibility entrypoint.
|
|
3
3
|
*
|
|
4
|
-
* Apps should import from `@mentra/bluetooth-sdk`. This
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Apps should import from `@mentra/bluetooth-sdk`. This is exported as
|
|
5
|
+
* `@mentra/bluetooth-sdk/internal` only for MentraOS packages that still need
|
|
6
|
+
* the compatibility surface while the app is migrated onto the public SDK
|
|
7
|
+
* surface.
|
|
8
8
|
*/
|
|
9
9
|
export {default} from "./_private/BluetoothSdkModule"
|
|
10
10
|
export type {BluetoothSdkInternalModule} from "./_private/BluetoothSdkModule"
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
BluetoothSettingsUpdate,
|
|
5
5
|
BluetoothSdkModuleEvents,
|
|
6
6
|
BluetoothStatus,
|
|
7
|
-
|
|
7
|
+
PhotoCaptureDefaults,
|
|
8
8
|
CalendarEvent,
|
|
9
9
|
CAMERA_FOV_DEFAULT,
|
|
10
10
|
CAMERA_FOV_MAX,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
CameraFovResult,
|
|
15
15
|
CameraFovSetting,
|
|
16
16
|
CameraRoiPosition,
|
|
17
|
+
CameraStatusEvent,
|
|
17
18
|
ConnectOptions,
|
|
18
19
|
DashboardMenuItem,
|
|
19
20
|
Device,
|
|
@@ -43,9 +44,11 @@ import {
|
|
|
43
44
|
VideoRecordingSettings,
|
|
44
45
|
VideoRecordingStoppedStatusEvent,
|
|
45
46
|
VersionInfoResult,
|
|
47
|
+
WarmUpCameraParams,
|
|
46
48
|
WifiSearchResult,
|
|
47
49
|
WifiStatusChangeEvent,
|
|
48
50
|
} from "../BluetoothSdk.types"
|
|
51
|
+
import {warmUpCameraParamsForNative} from "./cameraRequestPayload"
|
|
49
52
|
import {photoRequestParamsForNative} from "./photoRequestPayload"
|
|
50
53
|
|
|
51
54
|
/**
|
|
@@ -121,14 +124,22 @@ declare class BluetoothSdkNativeModule extends NativeModule<BluetoothSdkModuleEv
|
|
|
121
124
|
// Gallery Commands
|
|
122
125
|
setGalleryModeEnabled(enabled: boolean): Promise<SettingsAckSuccessEvent>
|
|
123
126
|
setVoiceActivityDetectionEnabled(enabled: boolean): Promise<void>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
setButtonCameraLed(enabled: boolean): Promise<SettingsAckSuccessEvent>
|
|
128
|
-
setButtonMaxRecordingTime(minutes: number): Promise<SettingsAckSuccessEvent>
|
|
127
|
+
setPhotoCaptureDefaults(settings: PhotoCaptureDefaults): Promise<SettingsAckSuccessEvent>
|
|
128
|
+
setVideoRecordingDefaults(width: number, height: number, fps: number): Promise<SettingsAckSuccessEvent>
|
|
129
|
+
setMaxVideoRecordingDuration(minutes: number): Promise<SettingsAckSuccessEvent>
|
|
129
130
|
setCameraFov(request: CameraFovRequest): Promise<CameraFovResult>
|
|
131
|
+
/**
|
|
132
|
+
* Configure camera HAL tuning (ANR / gain) on Mentra Live glasses.
|
|
133
|
+
* Sends a {@code camera_tuning_config} BLE message; the ASG client relays it as a
|
|
134
|
+
* {@code camconfig} broadcast so the HAL picks up new parameters without a reboot.
|
|
135
|
+
*
|
|
136
|
+
* Scan-mode convention: pass `{anr: false, gain: false}` when activating scan mode
|
|
137
|
+
* (disables ANR and pixsmart gain) and `{anr: true, gain: true}` to restore defaults.
|
|
138
|
+
*/
|
|
139
|
+
setCameraTuningConfig(anrOn: boolean, gainOn: boolean): Promise<SettingsAckSuccessEvent>
|
|
130
140
|
queryGalleryStatus(): Promise<GalleryStatusEvent>
|
|
131
141
|
requestPhoto(params: PhotoRequestParams): Promise<PhotoSuccessResponseEvent>
|
|
142
|
+
warmUpCamera(params: WarmUpCameraParams): Promise<CameraStatusEvent>
|
|
132
143
|
|
|
133
144
|
// OTA Commands
|
|
134
145
|
setOtaVersionUrl(otaVersionUrl: string): void
|
|
@@ -136,8 +147,6 @@ declare class BluetoothSdkNativeModule extends NativeModule<BluetoothSdkModuleEv
|
|
|
136
147
|
checkForOtaUpdate(): Promise<boolean>
|
|
137
148
|
startOtaUpdate(otaVersionUrl?: string | null): Promise<OtaStartAckEvent>
|
|
138
149
|
sendOtaQueryStatus(): Promise<OtaQueryResult>
|
|
139
|
-
/** Re-run glasses-side OTA version check (called after a clock fix invalidates a TLS failure). */
|
|
140
|
-
retryOtaVersionCheck(): Promise<OtaQueryResult>
|
|
141
150
|
|
|
142
151
|
// Version Info Commands
|
|
143
152
|
requestVersionInfo(): Promise<VersionInfoResult>
|
|
@@ -568,9 +577,9 @@ NativeBluetoothSdkModule.requestPhoto = function (params: PhotoRequestParams) {
|
|
|
568
577
|
return nativeRequestPhoto(photoRequestParamsForNative(params) as unknown as PhotoRequestParams)
|
|
569
578
|
}
|
|
570
579
|
|
|
571
|
-
const
|
|
572
|
-
NativeBluetoothSdkModule.
|
|
573
|
-
return
|
|
580
|
+
const nativeWarmUpCamera = NativeBluetoothSdkModule.warmUpCamera.bind(NativeBluetoothSdkModule)
|
|
581
|
+
NativeBluetoothSdkModule.warmUpCamera = function (params: WarmUpCameraParams) {
|
|
582
|
+
return nativeWarmUpCamera(warmUpCameraParamsForNative(params) as unknown as WarmUpCameraParams)
|
|
574
583
|
}
|
|
575
584
|
|
|
576
585
|
export default NativeBluetoothSdkModule
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type {WarmUpCameraParams} from "../BluetoothSdk.types"
|
|
2
|
+
import {normalizePhotoSizeTier} from "./photoRequestPayload"
|
|
3
|
+
|
|
4
|
+
function nonBlankString(value?: string | null): string | undefined {
|
|
5
|
+
const trimmed = value?.trim()
|
|
6
|
+
return trimmed && trimmed.length > 0 ? trimmed : undefined
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Expo Android bridge rejects null values in Map<String, Any> — omit optional nullish fields. */
|
|
10
|
+
export function warmUpCameraParamsForNative(
|
|
11
|
+
params: WarmUpCameraParams,
|
|
12
|
+
): Record<string, string | number> {
|
|
13
|
+
const payload: Record<string, string | number> = {
|
|
14
|
+
size: normalizePhotoSizeTier(params.size),
|
|
15
|
+
}
|
|
16
|
+
const requestId = nonBlankString(params.requestId)
|
|
17
|
+
if (requestId != null) {
|
|
18
|
+
payload.requestId = requestId
|
|
19
|
+
}
|
|
20
|
+
const exposureTimeNs = params.exposureTimeNs
|
|
21
|
+
if (exposureTimeNs != null && Number.isFinite(exposureTimeNs) && exposureTimeNs > 0) {
|
|
22
|
+
payload.exposureTimeNs = exposureTimeNs
|
|
23
|
+
}
|
|
24
|
+
const durationMs = params.durationMs
|
|
25
|
+
if (durationMs != null && Number.isFinite(durationMs) && durationMs > 0) {
|
|
26
|
+
payload.durationMs = Math.round(durationMs)
|
|
27
|
+
}
|
|
28
|
+
return payload
|
|
29
|
+
}
|
|
@@ -19,19 +19,28 @@ export function normalizePhotoSizeTier(size: string | undefined): PhotoSize {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function nonBlankString(value?: string | null): string | undefined {
|
|
23
|
+
const trimmed = value?.trim()
|
|
24
|
+
return trimmed && trimmed.length > 0 ? trimmed : undefined
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
/** Expo Android bridge rejects null values in Map<String, Any> — omit optional nullish fields. */
|
|
23
28
|
export function photoRequestParamsForNative(
|
|
24
29
|
params: PhotoRequestParams,
|
|
25
30
|
): Record<string, string | number | boolean> {
|
|
26
31
|
const payload: Record<string, string | number | boolean> = {
|
|
27
|
-
requestId: params.requestId,
|
|
28
|
-
appId: params.appId,
|
|
29
32
|
size: normalizePhotoSizeTier(params.size),
|
|
30
33
|
webhookUrl: params.webhookUrl ?? "",
|
|
31
34
|
compress: params.compress,
|
|
32
|
-
flash: true,
|
|
33
35
|
sound: params.sound,
|
|
34
36
|
}
|
|
37
|
+
const requestId = nonBlankString(params.requestId)
|
|
38
|
+
if (requestId != null) {
|
|
39
|
+
payload.requestId = requestId
|
|
40
|
+
}
|
|
41
|
+
if (params.save != null) {
|
|
42
|
+
payload.save = params.save
|
|
43
|
+
}
|
|
35
44
|
if (params.authToken != null && params.authToken.length > 0) {
|
|
36
45
|
payload.authToken = params.authToken
|
|
37
46
|
}
|