@mentra/bluetooth-sdk 0.1.14 → 0.1.17
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 +4 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +22 -9
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +5 -20
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +358 -261
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +3 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +133 -52
- package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +12 -12
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +22 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +1 -22
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +50 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +1184 -795
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +422 -78
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +3 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +2532 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +15 -1
- 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 +39 -14
- 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 +11 -3
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +5 -0
- 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 +8 -2
- 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 +72 -54
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +40 -12
- package/ios/Source/BluetoothSdkDefaults.swift +2 -30
- package/ios/Source/Bridge.swift +4 -16
- package/ios/Source/DeviceManager.swift +72 -12
- package/ios/Source/MentraBluetoothSDK.swift +120 -50
- package/ios/Source/OtaManifest.swift +13 -13
- package/ios/Source/camera/CameraModels.swift +75 -9
- 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/G1.swift +51 -1
- package/ios/Source/sgcs/G2.swift +643 -203
- package/ios/Source/sgcs/MentraLive.swift +53 -39
- package/ios/Source/sgcs/Nimo.swift +1928 -0
- package/ios/Source/sgcs/SGCManager.swift +12 -5
- 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 +9 -1
- package/scripts/inject-ios-sdk-version.mjs +65 -0
- package/src/BluetoothSdk.types.ts +42 -17
- package/src/_internal.ts +4 -4
- package/src/_private/BluetoothSdkModule.ts +18 -2
- package/src/_private/cameraRequestPayload.ts +29 -0
- package/src/_private/photoRequestPayload.ts +9 -2
- package/src/index.ts +76 -58
|
@@ -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
|
)
|
|
@@ -350,6 +350,11 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
350
350
|
return try await sdk.setCameraFov(CameraFov(fov: value, roiPosition: roiPosition)).values
|
|
351
351
|
}
|
|
352
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
|
+
|
|
353
358
|
AsyncFunction("queryGalleryStatus") {
|
|
354
359
|
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
355
360
|
return try await sdk.queryGalleryStatus().values
|
|
@@ -358,13 +363,41 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
358
363
|
AsyncFunction("requestPhoto") { (params: [String: Any]) in
|
|
359
364
|
let req = PhotoRequest.from(params: params)
|
|
360
365
|
Bridge.log(
|
|
361
|
-
"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")"
|
|
362
367
|
)
|
|
363
368
|
|
|
364
369
|
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
365
370
|
return try await sdk.requestPhoto(req).values
|
|
366
371
|
}
|
|
367
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
|
+
|
|
368
401
|
// MARK: - OTA Commands
|
|
369
402
|
|
|
370
403
|
Function("setOtaVersionUrl") { (otaVersionUrl: String) in
|
|
@@ -399,11 +432,6 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
399
432
|
return try await sdk.sendOtaQueryStatus().values
|
|
400
433
|
}
|
|
401
434
|
|
|
402
|
-
AsyncFunction("retryOtaVersionCheck") {
|
|
403
|
-
let sdk = await MainActor.run { self.bluetoothSdk() }
|
|
404
|
-
return try await sdk.retryOtaVersionCheck().values
|
|
405
|
-
}
|
|
406
|
-
|
|
407
435
|
// MARK: - Version Info Commands
|
|
408
436
|
|
|
409
437
|
AsyncFunction("requestVersionInfo") {
|
|
@@ -689,6 +717,8 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
689
717
|
sendEvent("photo_response", response.values)
|
|
690
718
|
case let .photoStatus(status):
|
|
691
719
|
sendEvent("photo_status", status.values)
|
|
720
|
+
case let .cameraStatus(status):
|
|
721
|
+
sendEvent("camera_status", status.values)
|
|
692
722
|
case let .videoRecordingStatus(status):
|
|
693
723
|
sendEvent("video_recording_status", status.values)
|
|
694
724
|
case let .mediaUpload(event):
|
|
@@ -699,8 +729,6 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
699
729
|
sendEvent("stream_status", status.values)
|
|
700
730
|
case let .keepAliveAck(ack):
|
|
701
731
|
sendEvent("keep_alive_ack", ack.values)
|
|
702
|
-
case let .otaUpdateAvailable(event):
|
|
703
|
-
sendEvent("ota_update_available", event.values)
|
|
704
732
|
case let .otaStartAck(event):
|
|
705
733
|
sendEvent("ota_start_ack", event.values)
|
|
706
734
|
case let .otaStatus(event):
|
|
@@ -743,7 +771,7 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
|
|
|
743
771
|
}
|
|
744
772
|
|
|
745
773
|
@MainActor
|
|
746
|
-
public func mentraBluetoothSDK(_: MentraBluetoothSDK, didFail error:
|
|
774
|
+
public func mentraBluetoothSDK(_: MentraBluetoothSDK, didFail error: BluetoothSdkError) {
|
|
747
775
|
sendEvent("pair_failure", ["error": error.message])
|
|
748
776
|
}
|
|
749
777
|
}
|
|
@@ -3,45 +3,19 @@ import Foundation
|
|
|
3
3
|
/// Defaults for the public Bluetooth SDK surface.
|
|
4
4
|
enum BluetoothSdkDefaults {
|
|
5
5
|
static var sdkVersion: String? {
|
|
6
|
-
|
|
7
|
-
normalizedSdkVersion(swiftPackageSdkVersion)
|
|
8
|
-
#else
|
|
9
|
-
packageVersion(from: sdkBundle)
|
|
10
|
-
#endif
|
|
6
|
+
normalizedSdkVersion(swiftPackageSdkVersion)
|
|
11
7
|
}
|
|
12
8
|
|
|
13
9
|
static let voiceActivityDetectionEnabled = false
|
|
14
|
-
private static let swiftPackageSdkVersion = "
|
|
10
|
+
private static let swiftPackageSdkVersion = "0.1.17"
|
|
15
11
|
private static let swiftPackageSdkVersionPlaceholder = "__MENTRA" + "_BLUETOOTH_SDK_VERSION__"
|
|
16
12
|
|
|
17
|
-
private static var sdkBundle: Bundle {
|
|
18
|
-
#if SWIFT_PACKAGE
|
|
19
|
-
Bundle.module
|
|
20
|
-
#else
|
|
21
|
-
Bundle(for: BluetoothSdkBundleToken.self)
|
|
22
|
-
#endif
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
private static func packageVersion(from bundle: Bundle) -> String? {
|
|
26
|
-
let version = bundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
|
|
27
|
-
let build = bundle.object(forInfoDictionaryKey: "CFBundleVersion") as? String
|
|
28
|
-
|
|
29
|
-
for candidate in [version, build] {
|
|
30
|
-
if let sdkVersion = normalizedSdkVersion(candidate) {
|
|
31
|
-
return sdkVersion
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return nil
|
|
36
|
-
}
|
|
37
|
-
|
|
38
13
|
private static func normalizedSdkVersion(_ value: String?) -> String? {
|
|
39
14
|
guard let value else {
|
|
40
15
|
return nil
|
|
41
16
|
}
|
|
42
17
|
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
43
18
|
guard !trimmed.isEmpty,
|
|
44
|
-
trimmed != "1.0",
|
|
45
19
|
trimmed != swiftPackageSdkVersionPlaceholder
|
|
46
20
|
else {
|
|
47
21
|
return nil
|
|
@@ -49,5 +23,3 @@ enum BluetoothSdkDefaults {
|
|
|
49
23
|
return trimmed
|
|
50
24
|
}
|
|
51
25
|
}
|
|
52
|
-
|
|
53
|
-
private final class BluetoothSdkBundleToken {}
|
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 =
|
|
@@ -1180,7 +1222,7 @@ struct ViewState {
|
|
|
1180
1222
|
|
|
1181
1223
|
private func liveSgc() throws -> MentraLive {
|
|
1182
1224
|
guard let live = sgc as? MentraLive else {
|
|
1183
|
-
throw
|
|
1225
|
+
throw BluetoothSdkError(code: "unsupported_device", message: "This command requires Mentra Live glasses.")
|
|
1184
1226
|
}
|
|
1185
1227
|
return live
|
|
1186
1228
|
}
|
|
@@ -1212,9 +1254,28 @@ struct ViewState {
|
|
|
1212
1254
|
try liveSgc().sendCameraFovSetting(requestId: requestId, fov: fov, roiPosition: roiPosition)
|
|
1213
1255
|
}
|
|
1214
1256
|
|
|
1215
|
-
func
|
|
1216
|
-
|
|
1217
|
-
|
|
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
|
+
)
|
|
1218
1279
|
}
|
|
1219
1280
|
|
|
1220
1281
|
/// Request version info from glasses.
|
|
@@ -1324,7 +1385,6 @@ struct ViewState {
|
|
|
1324
1385
|
let manualIso = manualExposureNs != nil ? request.iso.flatMap { $0 > 0 ? $0 : nil } : nil
|
|
1325
1386
|
let routed = PhotoRequest(
|
|
1326
1387
|
requestId: request.requestId,
|
|
1327
|
-
appId: request.appId,
|
|
1328
1388
|
size: request.size,
|
|
1329
1389
|
webhookUrl: request.webhookUrl,
|
|
1330
1390
|
authToken: request.authToken,
|
|
@@ -1343,7 +1403,7 @@ struct ViewState {
|
|
|
1343
1403
|
ispAnalogGain: request.ispAnalogGain
|
|
1344
1404
|
)
|
|
1345
1405
|
Bridge.log(
|
|
1346
|
-
"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")"
|
|
1347
1407
|
)
|
|
1348
1408
|
guard let sgc else {
|
|
1349
1409
|
Bridge.log(
|