@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
|
@@ -31,6 +31,7 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
31
31
|
"hotspot_error",
|
|
32
32
|
"photo_response",
|
|
33
33
|
"photo_status",
|
|
34
|
+
"camera_status",
|
|
34
35
|
"video_recording_status",
|
|
35
36
|
"media_success",
|
|
36
37
|
"media_error",
|
|
@@ -58,7 +59,6 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
58
59
|
"stream_status",
|
|
59
60
|
"keep_alive_ack",
|
|
60
61
|
"mtk_update_complete",
|
|
61
|
-
"ota_update_available",
|
|
62
62
|
"ota_progress",
|
|
63
63
|
"ota_start_ack",
|
|
64
64
|
"ota_status",
|
|
@@ -154,7 +154,7 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
154
154
|
AsyncFunction("connectWithOptions") { (device: [String: Any], options: [String: Any]) in
|
|
155
155
|
try await MainActor.run {
|
|
156
156
|
guard let target = Device(dictionary: device) else {
|
|
157
|
-
throw
|
|
157
|
+
throw BluetoothSdkError(
|
|
158
158
|
code: "invalid_device",
|
|
159
159
|
message: "connect requires a Device with model and name."
|
|
160
160
|
)
|
|
@@ -303,7 +303,7 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
303
303
|
timestampMs >= Double(Int64.min),
|
|
304
304
|
timestampMs <= maxTimestamp
|
|
305
305
|
else {
|
|
306
|
-
throw
|
|
306
|
+
throw BluetoothSdkError(
|
|
307
307
|
code: "invalid_timestamp",
|
|
308
308
|
message: "setSystemTime timestampMs must be a finite Int64 millisecond timestamp."
|
|
309
309
|
)
|
|
@@ -326,29 +326,19 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
326
326
|
try await sdk.setVoiceActivityDetectionEnabled(enabled)
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
AsyncFunction("
|
|
329
|
+
AsyncFunction("setPhotoCaptureDefaults") { (params: [String: Any]) in
|
|
330
330
|
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
331
|
-
return try await sdk.
|
|
331
|
+
return try await sdk.setPhotoCaptureDefaults(PhotoCaptureDefaults.from(params: params)).values
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
-
AsyncFunction("
|
|
334
|
+
AsyncFunction("setVideoRecordingDefaults") { (width: Int, height: Int, fps: Int) in
|
|
335
335
|
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
336
|
-
return try await sdk.
|
|
336
|
+
return try await sdk.setVideoRecordingDefaults(VideoRecordingDefaults(width: width, height: height, fps: fps)).values
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
AsyncFunction("
|
|
339
|
+
AsyncFunction("setMaxVideoRecordingDuration") { (minutes: Int) in
|
|
340
340
|
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
341
|
-
return try await sdk.
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
AsyncFunction("setButtonCameraLed") { (enabled: Bool) in
|
|
345
|
-
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
346
|
-
return try await sdk.setButtonCameraLed(enabled: enabled).values
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
AsyncFunction("setButtonMaxRecordingTime") { (minutes: Int) in
|
|
350
|
-
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
351
|
-
return try await sdk.setButtonMaxRecordingTime(minutes: minutes).values
|
|
341
|
+
return try await sdk.setMaxVideoRecordingDuration(minutes: minutes).values
|
|
352
342
|
}
|
|
353
343
|
|
|
354
344
|
AsyncFunction("setCameraFov") { (fov: [String: Any]) in
|
|
@@ -360,6 +350,11 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
360
350
|
return try await sdk.setCameraFov(CameraFov(fov: value, roiPosition: roiPosition)).values
|
|
361
351
|
}
|
|
362
352
|
|
|
353
|
+
AsyncFunction("setCameraTuningConfig") { (anrOn: Bool, gainOn: Bool) in
|
|
354
|
+
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
355
|
+
return try await sdk.setCameraTuningConfig(anrOn: anrOn, gainOn: gainOn).values
|
|
356
|
+
}
|
|
357
|
+
|
|
363
358
|
AsyncFunction("queryGalleryStatus") {
|
|
364
359
|
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
365
360
|
return try await sdk.queryGalleryStatus().values
|
|
@@ -368,13 +363,41 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
368
363
|
AsyncFunction("requestPhoto") { (params: [String: Any]) in
|
|
369
364
|
let req = PhotoRequest.from(params: params)
|
|
370
365
|
Bridge.log(
|
|
371
|
-
"NATIVE: PHOTO PIPELINE [3/6] BluetoothSdk.requestPhoto requestId=\(req.requestId)
|
|
366
|
+
"NATIVE: PHOTO PIPELINE [3/6] BluetoothSdk.requestPhoto requestId=\(req.requestId) size=\(req.size.rawValue) compress=\(req.compress?.rawValue ?? "none") aeDivisor=\(req.aeExposureDivisor.map { String($0) } ?? "nil")"
|
|
372
367
|
)
|
|
373
368
|
|
|
374
369
|
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
375
370
|
return try await sdk.requestPhoto(req).values
|
|
376
371
|
}
|
|
377
372
|
|
|
373
|
+
AsyncFunction("warmUpCamera") { (params: [String: Any]) in
|
|
374
|
+
let requestId = params["requestId"] as? String
|
|
375
|
+
let sizeRaw = params["size"] as? String ?? "medium"
|
|
376
|
+
let size = PhotoSize(normalizedRawValue: sizeRaw)
|
|
377
|
+
let exposureTimeNs: Double?
|
|
378
|
+
switch params["exposureTimeNs"] {
|
|
379
|
+
case let value as Double:
|
|
380
|
+
exposureTimeNs = value.isFinite && value > 0 ? value : nil
|
|
381
|
+
case let value as Int:
|
|
382
|
+
exposureTimeNs = value > 0 ? Double(value) : nil
|
|
383
|
+
case let value as NSNumber:
|
|
384
|
+
let d = value.doubleValue
|
|
385
|
+
exposureTimeNs = d.isFinite && d > 0 ? d : nil
|
|
386
|
+
default:
|
|
387
|
+
exposureTimeNs = nil
|
|
388
|
+
}
|
|
389
|
+
let durationRaw = intValue(params["durationMs"]) ?? 0
|
|
390
|
+
let durationMs = durationRaw > 0 ? durationRaw : 15000
|
|
391
|
+
|
|
392
|
+
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
393
|
+
return try await sdk.warmUpCamera(
|
|
394
|
+
requestId: requestId,
|
|
395
|
+
size: size,
|
|
396
|
+
exposureTimeNs: exposureTimeNs,
|
|
397
|
+
durationMs: durationMs
|
|
398
|
+
).values
|
|
399
|
+
}
|
|
400
|
+
|
|
378
401
|
// MARK: - OTA Commands
|
|
379
402
|
|
|
380
403
|
Function("setOtaVersionUrl") { (otaVersionUrl: String) in
|
|
@@ -409,11 +432,6 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
409
432
|
return try await sdk.sendOtaQueryStatus().values
|
|
410
433
|
}
|
|
411
434
|
|
|
412
|
-
AsyncFunction("retryOtaVersionCheck") {
|
|
413
|
-
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
414
|
-
return try await sdk.retryOtaVersionCheck().values
|
|
415
|
-
}
|
|
416
|
-
|
|
417
435
|
// MARK: - Version Info Commands
|
|
418
436
|
|
|
419
437
|
AsyncFunction("requestVersionInfo") {
|
|
@@ -472,6 +490,11 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
472
490
|
return try await sdk.startStream(StreamRequest(values: params)).values
|
|
473
491
|
}
|
|
474
492
|
|
|
493
|
+
AsyncFunction("startExternallyManagedStream") { (params: [String: Any]) in
|
|
494
|
+
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
495
|
+
return try await sdk.startExternallyManagedStream(StreamRequest(values: params)).values
|
|
496
|
+
}
|
|
497
|
+
|
|
475
498
|
AsyncFunction("stopStream") {
|
|
476
499
|
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
477
500
|
return try await sdk.stopStream().values
|
|
@@ -694,6 +717,8 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
694
717
|
sendEvent("photo_response", response.values)
|
|
695
718
|
case let .photoStatus(status):
|
|
696
719
|
sendEvent("photo_status", status.values)
|
|
720
|
+
case let .cameraStatus(status):
|
|
721
|
+
sendEvent("camera_status", status.values)
|
|
697
722
|
case let .videoRecordingStatus(status):
|
|
698
723
|
sendEvent("video_recording_status", status.values)
|
|
699
724
|
case let .mediaUpload(event):
|
|
@@ -704,8 +729,6 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
704
729
|
sendEvent("stream_status", status.values)
|
|
705
730
|
case let .keepAliveAck(ack):
|
|
706
731
|
sendEvent("keep_alive_ack", ack.values)
|
|
707
|
-
case let .otaUpdateAvailable(event):
|
|
708
|
-
sendEvent("ota_update_available", event.values)
|
|
709
732
|
case let .otaStartAck(event):
|
|
710
733
|
sendEvent("ota_start_ack", event.values)
|
|
711
734
|
case let .otaStatus(event):
|
|
@@ -748,7 +771,7 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
748
771
|
}
|
|
749
772
|
|
|
750
773
|
@MainActor
|
|
751
|
-
public func mentraBluetoothSDK(_: MentraBluetoothSDK, didFail error:
|
|
774
|
+
public func mentraBluetoothSDK(_: MentraBluetoothSDK, didFail error: BluetoothSdkError) {
|
|
752
775
|
sendEvent("pair_failure", ["error": error.message])
|
|
753
776
|
}
|
|
754
777
|
}
|
package/ios/Source/Bridge.swift
CHANGED
|
@@ -347,6 +347,10 @@ class Bridge {
|
|
|
347
347
|
Bridge.sendTypedMessage("photo_status", body: status)
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
+
static func sendCameraStatus(_ status: [String: Any]) {
|
|
351
|
+
Bridge.sendTypedMessage("camera_status", body: status)
|
|
352
|
+
}
|
|
353
|
+
|
|
350
354
|
static func sendPhotoResponse(_ response: [String: Any]) {
|
|
351
355
|
Bridge.sendTypedMessage("photo_response", body: response)
|
|
352
356
|
}
|
|
@@ -434,22 +438,6 @@ class Bridge {
|
|
|
434
438
|
Bridge.sendTypedMessage("ota_start_ack", body: eventBody)
|
|
435
439
|
}
|
|
436
440
|
|
|
437
|
-
/// Send OTA update available notification - glasses have detected an available update (background mode)
|
|
438
|
-
static func sendOtaUpdateAvailable(
|
|
439
|
-
versionCode: Int64,
|
|
440
|
-
versionName: String,
|
|
441
|
-
updates: [String],
|
|
442
|
-
totalSize: Int64
|
|
443
|
-
) {
|
|
444
|
-
let eventBody: [String: Any] = [
|
|
445
|
-
"version_code": versionCode,
|
|
446
|
-
"version_name": versionName,
|
|
447
|
-
"updates": updates,
|
|
448
|
-
"total_size": totalSize,
|
|
449
|
-
]
|
|
450
|
-
Bridge.sendTypedMessage("ota_update_available", body: eventBody)
|
|
451
|
-
}
|
|
452
|
-
|
|
453
441
|
static func sendOtaStatus(
|
|
454
442
|
sessionId: String,
|
|
455
443
|
totalSteps: Int,
|
|
@@ -22,11 +22,14 @@ struct ViewState {
|
|
|
22
22
|
var text: String
|
|
23
23
|
var data: String?
|
|
24
24
|
var animationData: [String: Any]?
|
|
25
|
-
// Optional
|
|
26
|
-
var bmpX: Int32?
|
|
27
|
-
var bmpY: Int32?
|
|
28
|
-
var bmpWidth: Int32?
|
|
29
|
-
var bmpHeight: Int32?
|
|
25
|
+
// Optional container position/size — used by bitmap_view and positioned_text (G2; ignored by others)
|
|
26
|
+
var bmpX: Int32? = nil
|
|
27
|
+
var bmpY: Int32? = nil
|
|
28
|
+
var bmpWidth: Int32? = nil
|
|
29
|
+
var bmpHeight: Int32? = nil
|
|
30
|
+
// Optional positioned_text border (used by G2; ignored by others)
|
|
31
|
+
var borderWidth: Int32? = nil
|
|
32
|
+
var borderRadius: Int32? = nil
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
@MainActor
|
|
@@ -620,6 +623,8 @@ struct ViewState {
|
|
|
620
623
|
sgc = G2()
|
|
621
624
|
} else if wearable.contains(DeviceTypes.LIVE) {
|
|
622
625
|
sgc = MentraLive()
|
|
626
|
+
} else if wearable.contains(DeviceTypes.NIMO) {
|
|
627
|
+
sgc = Nimo()
|
|
623
628
|
} else if wearable.contains(DeviceTypes.FRAME) {
|
|
624
629
|
// sgc = FrameManager()
|
|
625
630
|
}
|
|
@@ -714,6 +719,19 @@ struct ViewState {
|
|
|
714
719
|
width: currentViewState.bmpWidth,
|
|
715
720
|
height: currentViewState.bmpHeight
|
|
716
721
|
)
|
|
722
|
+
case "positioned_text":
|
|
723
|
+
Bridge.log(
|
|
724
|
+
"MAN: positioned_text → text='\(currentViewState.text)' rect=\(currentViewState.bmpX ?? 0),\(currentViewState.bmpY ?? 0) \(currentViewState.bmpWidth ?? 576)x\(currentViewState.bmpHeight ?? 288)"
|
|
725
|
+
)
|
|
726
|
+
await sgc?.sendPositionedText(
|
|
727
|
+
currentViewState.text,
|
|
728
|
+
x: currentViewState.bmpX ?? 0,
|
|
729
|
+
y: currentViewState.bmpY ?? 0,
|
|
730
|
+
width: currentViewState.bmpWidth ?? 576,
|
|
731
|
+
height: currentViewState.bmpHeight ?? 288,
|
|
732
|
+
borderWidth: currentViewState.borderWidth ?? 0,
|
|
733
|
+
borderRadius: currentViewState.borderRadius ?? 0
|
|
734
|
+
)
|
|
717
735
|
case "clear_view":
|
|
718
736
|
sgc?.clearDisplay()
|
|
719
737
|
default:
|
|
@@ -1039,6 +1057,8 @@ struct ViewState {
|
|
|
1039
1057
|
let bmpY = (layout["y"] as? NSNumber).map { $0.int32Value }
|
|
1040
1058
|
let bmpWidth = (layout["width"] as? NSNumber).map { $0.int32Value }
|
|
1041
1059
|
let bmpHeight = (layout["height"] as? NSNumber).map { $0.int32Value }
|
|
1060
|
+
let borderWidth = (layout["borderWidth"] as? NSNumber).map { $0.int32Value }
|
|
1061
|
+
let borderRadius = (layout["borderRadius"] as? NSNumber).map { $0.int32Value }
|
|
1042
1062
|
|
|
1043
1063
|
text = parsePlaceholders(text)
|
|
1044
1064
|
topText = parsePlaceholders(topText)
|
|
@@ -1048,7 +1068,8 @@ struct ViewState {
|
|
|
1048
1068
|
var newViewState = ViewState(
|
|
1049
1069
|
topText: topText, bottomText: bottomText, title: title, layoutType: layoutType,
|
|
1050
1070
|
text: text, data: data, animationData: nil,
|
|
1051
|
-
bmpX: bmpX, bmpY: bmpY, bmpWidth: bmpWidth, bmpHeight: bmpHeight
|
|
1071
|
+
bmpX: bmpX, bmpY: bmpY, bmpWidth: bmpWidth, bmpHeight: bmpHeight,
|
|
1072
|
+
borderWidth: borderWidth, borderRadius: borderRadius
|
|
1052
1073
|
)
|
|
1053
1074
|
|
|
1054
1075
|
if layoutType == "bitmap_animation" {
|
|
@@ -1069,6 +1090,27 @@ struct ViewState {
|
|
|
1069
1090
|
}
|
|
1070
1091
|
}
|
|
1071
1092
|
|
|
1093
|
+
// positioned_text is a sticky overlay container (e.g. the nav trip-stats
|
|
1094
|
+
// label) that lives ALONGSIDE the main view. It must NOT flow through the
|
|
1095
|
+
// single replaceable viewState[stateIndex] — otherwise the constantly-
|
|
1096
|
+
// refreshing main text_wall (maneuver text) and the minimap bitmap_view
|
|
1097
|
+
// overwrite it every frame and it never renders. Route it straight to the
|
|
1098
|
+
// SGC, which keeps its own persistent text container for that rect.
|
|
1099
|
+
if layoutType == "positioned_text" {
|
|
1100
|
+
Bridge.log(
|
|
1101
|
+
"MAN: positioned_text (sticky) → text='\(text)' rect=\(bmpX ?? 0),\(bmpY ?? 0) \(bmpWidth ?? 576)x\(bmpHeight ?? 288)"
|
|
1102
|
+
)
|
|
1103
|
+
Task { [weak self] in
|
|
1104
|
+
await self?.sgc?.sendPositionedText(
|
|
1105
|
+
text,
|
|
1106
|
+
x: bmpX ?? 0, y: bmpY ?? 0,
|
|
1107
|
+
width: bmpWidth ?? 576, height: bmpHeight ?? 288,
|
|
1108
|
+
borderWidth: borderWidth ?? 0, borderRadius: borderRadius ?? 0
|
|
1109
|
+
)
|
|
1110
|
+
}
|
|
1111
|
+
return
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1072
1114
|
let cS = viewStates[stateIndex]
|
|
1073
1115
|
let nS = newViewState
|
|
1074
1116
|
let currentState =
|
|
@@ -1115,7 +1157,6 @@ struct ViewState {
|
|
|
1115
1157
|
|
|
1116
1158
|
func startStream(_ message: [String: Any]) {
|
|
1117
1159
|
var message = message
|
|
1118
|
-
message["flash"] = true
|
|
1119
1160
|
Bridge.log("MAN: startStream: \(message)")
|
|
1120
1161
|
sgc?.startStream(message)
|
|
1121
1162
|
}
|
|
@@ -1181,7 +1222,7 @@ struct ViewState {
|
|
|
1181
1222
|
|
|
1182
1223
|
private func liveSgc() throws -> MentraLive {
|
|
1183
1224
|
guard let live = sgc as? MentraLive else {
|
|
1184
|
-
throw
|
|
1225
|
+
throw BluetoothSdkError(code: "unsupported_device", message: "This command requires Mentra Live glasses.")
|
|
1185
1226
|
}
|
|
1186
1227
|
return live
|
|
1187
1228
|
}
|
|
@@ -1193,11 +1234,11 @@ struct ViewState {
|
|
|
1193
1234
|
func sendButtonPhotoSettings(requestId: String, size: String) throws {
|
|
1194
1235
|
try sendButtonPhotoSettings(
|
|
1195
1236
|
requestId: requestId,
|
|
1196
|
-
settings:
|
|
1237
|
+
settings: PhotoCaptureDefaults(size: PhotoSize(normalizedRawValue: size))
|
|
1197
1238
|
)
|
|
1198
1239
|
}
|
|
1199
1240
|
|
|
1200
|
-
func sendButtonPhotoSettings(requestId: String, settings:
|
|
1241
|
+
func sendButtonPhotoSettings(requestId: String, settings: PhotoCaptureDefaults) throws {
|
|
1201
1242
|
try liveSgc().sendButtonPhotoSettings(requestId: requestId, settings: settings)
|
|
1202
1243
|
}
|
|
1203
1244
|
|
|
@@ -1205,10 +1246,6 @@ struct ViewState {
|
|
|
1205
1246
|
try liveSgc().sendButtonVideoRecordingSettings(requestId: requestId, width: width, height: height, fps: fps)
|
|
1206
1247
|
}
|
|
1207
1248
|
|
|
1208
|
-
func sendButtonCameraLedSetting(requestId: String, enabled: Bool) throws {
|
|
1209
|
-
try liveSgc().sendButtonCameraLedSetting(requestId: requestId, enabled: enabled)
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
1249
|
func sendButtonMaxRecordingTime(requestId: String, minutes: Int) throws {
|
|
1213
1250
|
try liveSgc().sendButtonMaxRecordingTime(requestId: requestId, minutes: minutes)
|
|
1214
1251
|
}
|
|
@@ -1217,9 +1254,28 @@ struct ViewState {
|
|
|
1217
1254
|
try liveSgc().sendCameraFovSetting(requestId: requestId, fov: fov, roiPosition: roiPosition)
|
|
1218
1255
|
}
|
|
1219
1256
|
|
|
1220
|
-
func
|
|
1221
|
-
|
|
1222
|
-
|
|
1257
|
+
func sendCameraTuningConfig(requestId: String, anrOn: Bool, gainOn: Bool) throws {
|
|
1258
|
+
try liveSgc().sendCameraTuningConfig(requestId: requestId, anrOn: anrOn, gainOn: gainOn)
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
func warmUpCamera(
|
|
1262
|
+
requestId: String,
|
|
1263
|
+
size: PhotoSize,
|
|
1264
|
+
exposureTimeNs: Double?,
|
|
1265
|
+
durationMs: Int
|
|
1266
|
+
) throws {
|
|
1267
|
+
guard let live = sgc as? MentraLive else {
|
|
1268
|
+
// Fail fast like other camera commands so the SDK promise rejects immediately instead
|
|
1269
|
+
// of hanging until the request timeout with no camera_status.
|
|
1270
|
+
throw BluetoothSdkError(
|
|
1271
|
+
code: "unsupported_device", message: "This command requires Mentra Live glasses.")
|
|
1272
|
+
}
|
|
1273
|
+
live.warmUpCamera(
|
|
1274
|
+
requestId: requestId,
|
|
1275
|
+
size: size,
|
|
1276
|
+
exposureTimeNs: exposureTimeNs,
|
|
1277
|
+
durationMs: durationMs
|
|
1278
|
+
)
|
|
1223
1279
|
}
|
|
1224
1280
|
|
|
1225
1281
|
/// Request version info from glasses.
|
|
@@ -1248,10 +1304,10 @@ struct ViewState {
|
|
|
1248
1304
|
_ fps: Int = 0, _ maxRecordingTimeMinutes: Int = 0
|
|
1249
1305
|
) {
|
|
1250
1306
|
Bridge.log(
|
|
1251
|
-
"MAN: onStartVideoRecording: requestId=\(requestId), save=\(save),
|
|
1307
|
+
"MAN: onStartVideoRecording: requestId=\(requestId), save=\(save), sound=\(sound), resolution=\(width)x\(height)@\(fps)fps, maxRecordingTimeMinutes=\(maxRecordingTimeMinutes)"
|
|
1252
1308
|
)
|
|
1253
1309
|
sgc?.startVideoRecording(
|
|
1254
|
-
requestId: requestId, save: save,
|
|
1310
|
+
requestId: requestId, save: save, sound: sound, width: width, height: height,
|
|
1255
1311
|
fps: fps, maxRecordingTimeMinutes: maxRecordingTimeMinutes
|
|
1256
1312
|
)
|
|
1257
1313
|
}
|
|
@@ -1329,12 +1385,10 @@ struct ViewState {
|
|
|
1329
1385
|
let manualIso = manualExposureNs != nil ? request.iso.flatMap { $0 > 0 ? $0 : nil } : nil
|
|
1330
1386
|
let routed = PhotoRequest(
|
|
1331
1387
|
requestId: request.requestId,
|
|
1332
|
-
appId: request.appId,
|
|
1333
1388
|
size: request.size,
|
|
1334
1389
|
webhookUrl: request.webhookUrl,
|
|
1335
1390
|
authToken: request.authToken,
|
|
1336
1391
|
compress: request.compress,
|
|
1337
|
-
flash: request.flash,
|
|
1338
1392
|
save: request.save,
|
|
1339
1393
|
sound: request.sound,
|
|
1340
1394
|
exposureTimeNs: manualExposureNs,
|
|
@@ -1349,7 +1403,7 @@ struct ViewState {
|
|
|
1349
1403
|
ispAnalogGain: request.ispAnalogGain
|
|
1350
1404
|
)
|
|
1351
1405
|
Bridge.log(
|
|
1352
|
-
"MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=\(routed.requestId)
|
|
1406
|
+
"MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=\(routed.requestId) webhookUrl=\(routed.webhookUrl ?? "nil") size=\(routed.size.rawValue) compress=\(routed.compress?.rawValue ?? "none") save=\(routed.save) sound=\(routed.sound) exposureTimeNs=\(manualExposureNs.map { String($0) } ?? "nil") iso=\(manualIso.map { String($0) } ?? "auto") aeDivisor=\(routed.aeExposureDivisor.map { String($0) } ?? "nil") isoCap=\(routed.isoCap.map { String($0) } ?? "nil") sgc=\(sgc != nil ? String(describing: type(of: sgc!)) : "null")"
|
|
1353
1407
|
)
|
|
1354
1408
|
guard let sgc else {
|
|
1355
1409
|
Bridge.log(
|
|
@@ -92,7 +92,6 @@ class DeviceStore {
|
|
|
92
92
|
store.set("bluetooth", "nex_chinese_captions", false)
|
|
93
93
|
store.set("bluetooth", "screen_disabled", false)
|
|
94
94
|
store.set("bluetooth", "button_photo_size", "max")
|
|
95
|
-
store.set("bluetooth", "button_camera_led", true)
|
|
96
95
|
store.set("bluetooth", "button_max_recording_time", 10)
|
|
97
96
|
store.set("bluetooth", "camera_fov", ["fov": 118, "roi_position": 0])
|
|
98
97
|
store.set("bluetooth", "button_video_width", 1280)
|
|
@@ -258,9 +257,6 @@ class DeviceStore {
|
|
|
258
257
|
case ("bluetooth", "button_photo_size"):
|
|
259
258
|
DeviceManager.shared.sgc?.sendButtonPhotoSettings()
|
|
260
259
|
|
|
261
|
-
case ("bluetooth", "button_camera_led"):
|
|
262
|
-
DeviceManager.shared.sgc?.sendButtonCameraLedSetting()
|
|
263
|
-
|
|
264
260
|
case ("bluetooth", "button_max_recording_time"):
|
|
265
261
|
DeviceManager.shared.sgc?.sendButtonMaxRecordingTime()
|
|
266
262
|
|