@mentra/bluetooth-sdk 0.1.2 → 0.1.4
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 +75 -19
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +13 -13
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +42 -18
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +19 -21
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +4 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +118 -103
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +110 -38
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +4 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/services/PhoneMic.kt +6 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +3 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +18 -18
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +4 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +6 -6
- package/app.plugin.js +3 -1
- package/build/BluetoothSdk.types.d.ts +126 -27
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +12 -0
- package/build/_internal.d.ts.map +1 -0
- package/build/_internal.js +11 -0
- package/build/_internal.js.map +1 -0
- package/build/{BluetoothSdkModule.d.ts → _private/BluetoothSdkModule.d.ts} +19 -8
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -0
- package/build/{BluetoothSdkModule.js → _private/BluetoothSdkModule.js} +41 -26
- package/build/_private/BluetoothSdkModule.js.map +1 -0
- package/build/index.d.ts +4 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -4
- package/build/index.js.map +1 -1
- package/build/react/index.d.ts +5 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +5 -0
- package/build/react/index.js.map +1 -0
- package/build/react/useBluetoothEvent.d.ts +6 -0
- package/build/react/useBluetoothEvent.d.ts.map +1 -0
- package/build/react/useBluetoothEvent.js +21 -0
- package/build/react/useBluetoothEvent.js.map +1 -0
- package/build/react/useBluetoothScan.d.ts +22 -0
- package/build/react/useBluetoothScan.d.ts.map +1 -0
- package/build/react/useBluetoothScan.js +135 -0
- package/build/react/useBluetoothScan.js.map +1 -0
- package/build/react/useBluetoothStatus.d.ts +16 -0
- package/build/react/useBluetoothStatus.d.ts.map +1 -0
- package/build/react/useBluetoothStatus.js +137 -0
- package/build/react/useBluetoothStatus.js.map +1 -0
- package/build/react/useGlassesConnection.d.ts +29 -0
- package/build/react/useGlassesConnection.d.ts.map +1 -0
- package/build/react/useGlassesConnection.js +140 -0
- package/build/react/useGlassesConnection.js.map +1 -0
- package/ios/BluetoothSdkModule.swift +14 -9
- package/ios/Source/Bridge.swift +44 -12
- package/ios/Source/DeviceManager.swift +27 -26
- package/ios/Source/DeviceStore.swift +4 -4
- package/ios/Source/MentraBluetoothSDK.swift +250 -132
- package/ios/Source/controllers/ControllerManager.swift +5 -5
- package/ios/Source/controllers/R1.swift +3 -3
- package/ios/Source/services/PhoneMic.swift +5 -5
- package/ios/Source/sgcs/Frame.swift +1 -1
- package/ios/Source/sgcs/G1.swift +1 -1
- package/ios/Source/sgcs/G2.swift +3 -3
- package/ios/Source/sgcs/Mach1.swift +1 -1
- package/ios/Source/sgcs/MentraLive.swift +31 -33
- package/ios/Source/sgcs/MentraNex.swift +2 -2
- package/ios/Source/sgcs/SGCManager.swift +5 -5
- package/ios/Source/sgcs/Simulated.swift +3 -3
- package/ios/Source/utils/Constants.swift +6 -6
- package/ios/Source/utils/JSCExperiment.swift +8 -8
- package/ios/Source/utils/TarBz2Extractor.swift +2 -2
- package/package.json +19 -1
- package/src/BluetoothSdk.types.ts +186 -28
- package/src/_internal.ts +11 -0
- package/src/{BluetoothSdkModule.ts → _private/BluetoothSdkModule.ts} +71 -38
- package/src/index.ts +80 -4
- package/src/react/index.ts +22 -0
- package/src/react/useBluetoothEvent.ts +38 -0
- package/src/react/useBluetoothScan.ts +173 -0
- package/src/react/useBluetoothStatus.ts +180 -0
- package/src/react/useGlassesConnection.ts +194 -0
- package/build/BluetoothSdkModule.d.ts.map +0 -1
- package/build/BluetoothSdkModule.js.map +0 -1
|
@@ -197,10 +197,10 @@ public struct Device: Identifiable, Equatable, CustomStringConvertible {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
init?(values: [String: Any]) {
|
|
200
|
-
guard let model = stringValue(values, "model"
|
|
201
|
-
guard let name = stringValue(values, "name"
|
|
202
|
-
let identifier = stringValue(values, "address"
|
|
203
|
-
let rssi = intValue(values["rssi"])
|
|
200
|
+
guard let model = stringValue(values, "model") else { return nil }
|
|
201
|
+
guard let name = stringValue(values, "name") else { return nil }
|
|
202
|
+
let identifier = stringValue(values, "address").flatMap { $0.isEmpty ? nil : $0 }
|
|
203
|
+
let rssi = intValue(values["rssi"])
|
|
204
204
|
self.init(
|
|
205
205
|
model: DeviceModel.fromDeviceType(model),
|
|
206
206
|
name: name,
|
|
@@ -236,8 +236,8 @@ public struct WifiScanResult: CustomStringConvertible {
|
|
|
236
236
|
|
|
237
237
|
init(values: [String: Any]) {
|
|
238
238
|
ssid = stringValue(values, "ssid") ?? ""
|
|
239
|
-
requiresPassword = boolValue(values, "requiresPassword"
|
|
240
|
-
signalStrength = intValue(values["signalStrength"]
|
|
239
|
+
requiresPassword = boolValue(values, "requiresPassword") ?? false
|
|
240
|
+
signalStrength = intValue(values["signalStrength"]) ?? -1
|
|
241
241
|
frequency = intValue(values["frequency"])
|
|
242
242
|
}
|
|
243
243
|
|
|
@@ -355,15 +355,15 @@ public struct GlassesStatus: CustomStringConvertible {
|
|
|
355
355
|
public var connected: Bool { boolValue(values, "connected") ?? false }
|
|
356
356
|
public var micEnabled: Bool { boolValue(values, "micEnabled") ?? false }
|
|
357
357
|
public var connectionState: GlassesConnectionState { GlassesConnectionState(stringValue(values, "connectionState")) }
|
|
358
|
-
public var
|
|
358
|
+
public var bluetoothClassicConnected: Bool { boolValue(values, "bluetoothClassicConnected") ?? false }
|
|
359
359
|
public var signalStrength: Int { intValue(values["signalStrength"]) ?? -1 }
|
|
360
360
|
public var signalStrengthUpdatedAt: Int { intValue(values["signalStrengthUpdatedAt"]) ?? 0 }
|
|
361
361
|
public var deviceModel: String { stringValue(values, "deviceModel") ?? "" }
|
|
362
362
|
public var androidVersion: String { stringValue(values, "androidVersion") ?? "" }
|
|
363
|
-
public var firmwareVersion: String { stringValue(values, "firmwareVersion"
|
|
364
|
-
public var besFirmwareVersion: String { stringValue(values, "
|
|
365
|
-
public var mtkFirmwareVersion: String { stringValue(values, "
|
|
366
|
-
public var
|
|
363
|
+
public var firmwareVersion: String { stringValue(values, "firmwareVersion") ?? "" }
|
|
364
|
+
public var besFirmwareVersion: String { stringValue(values, "besFirmwareVersion") ?? "" }
|
|
365
|
+
public var mtkFirmwareVersion: String { stringValue(values, "mtkFirmwareVersion") ?? "" }
|
|
366
|
+
public var bluetoothMacAddress: String { stringValue(values, "bluetoothMacAddress") ?? "" }
|
|
367
367
|
public var leftMacAddress: String { stringValue(values, "leftMacAddress") ?? "" }
|
|
368
368
|
public var rightMacAddress: String { stringValue(values, "rightMacAddress") ?? "" }
|
|
369
369
|
public var macAddress: String { stringValue(values, "macAddress") ?? "" }
|
|
@@ -413,7 +413,6 @@ public struct GlassesStatus: CustomStringConvertible {
|
|
|
413
413
|
dictionary.removeValue(forKey: "hotspotSsid")
|
|
414
414
|
dictionary.removeValue(forKey: "hotspotPassword")
|
|
415
415
|
dictionary.removeValue(forKey: "hotspotGatewayIp")
|
|
416
|
-
dictionary.removeValue(forKey: "hotspotLocalIp")
|
|
417
416
|
return dictionary
|
|
418
417
|
}
|
|
419
418
|
|
|
@@ -443,7 +442,7 @@ public struct GlassesStatus: CustomStringConvertible {
|
|
|
443
442
|
if let hotspot = (values["hotspot"] as? [String: Any]).flatMap(HotspotStatus.init(values:)) {
|
|
444
443
|
dictionary["hotspot"] = hotspot.values
|
|
445
444
|
}
|
|
446
|
-
} else if hasAnyKey(values, "hotspotEnabled", "hotspotSsid", "hotspotPassword", "hotspotGatewayIp"
|
|
445
|
+
} else if hasAnyKey(values, "hotspotEnabled", "hotspotSsid", "hotspotPassword", "hotspotGatewayIp") {
|
|
447
446
|
if let hotspot = HotspotStatus.fromStoreValues(values) {
|
|
448
447
|
dictionary["hotspot"] = hotspot.values
|
|
449
448
|
}
|
|
@@ -451,7 +450,6 @@ public struct GlassesStatus: CustomStringConvertible {
|
|
|
451
450
|
dictionary.removeValue(forKey: "hotspotSsid")
|
|
452
451
|
dictionary.removeValue(forKey: "hotspotPassword")
|
|
453
452
|
dictionary.removeValue(forKey: "hotspotGatewayIp")
|
|
454
|
-
dictionary.removeValue(forKey: "hotspotLocalIp")
|
|
455
453
|
}
|
|
456
454
|
return dictionary
|
|
457
455
|
}
|
|
@@ -490,6 +488,8 @@ public struct BluetoothStatus: CustomStringConvertible {
|
|
|
490
488
|
public var micEnabled: Bool { boolValue(values, "micEnabled") ?? false }
|
|
491
489
|
public var currentMic: String { stringValue(values, "currentMic") ?? "" }
|
|
492
490
|
public var micRanking: [String] { stringListValue(values, "micRanking") }
|
|
491
|
+
/// Nearby glasses in stable discovery order. Existing entries keep their array position as
|
|
492
|
+
/// details refresh; new glasses append at the end, and removals should not reorder remaining entries.
|
|
493
493
|
public var searchResults: [Device] {
|
|
494
494
|
dictionaryListValue(values, "searchResults").compactMap(Device.init(values:))
|
|
495
495
|
}
|
|
@@ -515,7 +515,7 @@ public struct BluetoothStatus: CustomStringConvertible {
|
|
|
515
515
|
public var dashboardDepth: Int { intValue(values["dashboard_depth"]) ?? 2 }
|
|
516
516
|
public var headUpAngle: Int { intValue(values["head_up_angle"]) ?? 30 }
|
|
517
517
|
public var contextualDashboard: Bool { boolValue(values, "contextual_dashboard") ?? true }
|
|
518
|
-
public var galleryModeAuto: Bool { boolValue(values, "
|
|
518
|
+
public var galleryModeAuto: Bool { boolValue(values, "galleryModeAuto") ?? true }
|
|
519
519
|
public var buttonPhotoSize: ButtonPhotoSize {
|
|
520
520
|
ButtonPhotoSize(rawValue: stringValue(values, "button_photo_size") ?? "") ?? .medium
|
|
521
521
|
}
|
|
@@ -523,7 +523,7 @@ public struct BluetoothStatus: CustomStringConvertible {
|
|
|
523
523
|
public var buttonMaxRecordingTime: Int { intValue(values["button_max_recording_time"]) ?? 10 }
|
|
524
524
|
public var buttonVideoWidth: Int { intValue(values["button_video_width"]) ?? 1280 }
|
|
525
525
|
public var buttonVideoHeight: Int { intValue(values["button_video_height"]) ?? 720 }
|
|
526
|
-
public var
|
|
526
|
+
public var buttonVideoFrameRate: Int { intValue(values["button_video_fps"]) ?? 30 }
|
|
527
527
|
public var shouldSendPcm: Bool { boolValue(values, "should_send_pcm") ?? false }
|
|
528
528
|
public var shouldSendLc3: Bool { boolValue(values, "should_send_lc3") ?? false }
|
|
529
529
|
public var shouldSendTranscript: Bool { boolValue(values, "should_send_transcript") ?? false }
|
|
@@ -559,15 +559,15 @@ public struct GlassesStatusUpdate: CustomStringConvertible {
|
|
|
559
559
|
public var connectionState: GlassesConnectionState? {
|
|
560
560
|
GlassesConnectionState.fromValue(optionalStringValue(values, "connectionState"))
|
|
561
561
|
}
|
|
562
|
-
public var
|
|
562
|
+
public var bluetoothClassicConnected: Bool? { optionalBoolValue(values, "bluetoothClassicConnected") }
|
|
563
563
|
public var signalStrength: Int? { optionalIntValue(values, "signalStrength") }
|
|
564
564
|
public var signalStrengthUpdatedAt: Int? { optionalIntValue(values, "signalStrengthUpdatedAt") }
|
|
565
565
|
public var deviceModel: String? { optionalStringValue(values, "deviceModel") }
|
|
566
566
|
public var androidVersion: String? { optionalStringValue(values, "androidVersion") }
|
|
567
|
-
public var firmwareVersion: String? { optionalStringValue(values, "firmwareVersion"
|
|
568
|
-
public var besFirmwareVersion: String? { optionalStringValue(values, "
|
|
569
|
-
public var mtkFirmwareVersion: String? { optionalStringValue(values, "
|
|
570
|
-
public var
|
|
567
|
+
public var firmwareVersion: String? { optionalStringValue(values, "firmwareVersion") }
|
|
568
|
+
public var besFirmwareVersion: String? { optionalStringValue(values, "besFirmwareVersion") }
|
|
569
|
+
public var mtkFirmwareVersion: String? { optionalStringValue(values, "mtkFirmwareVersion") }
|
|
570
|
+
public var bluetoothMacAddress: String? { optionalStringValue(values, "bluetoothMacAddress") }
|
|
571
571
|
public var leftMacAddress: String? { optionalStringValue(values, "leftMacAddress") }
|
|
572
572
|
public var rightMacAddress: String? { optionalStringValue(values, "rightMacAddress") }
|
|
573
573
|
public var macAddress: String? { optionalStringValue(values, "macAddress") }
|
|
@@ -591,7 +591,7 @@ public struct GlassesStatusUpdate: CustomStringConvertible {
|
|
|
591
591
|
if let hotspot = values["hotspot"] as? [String: Any] {
|
|
592
592
|
return HotspotStatus(values: hotspot)
|
|
593
593
|
}
|
|
594
|
-
if hasAnyKey(values, "hotspotEnabled", "hotspotSsid", "hotspotPassword", "hotspotGatewayIp"
|
|
594
|
+
if hasAnyKey(values, "hotspotEnabled", "hotspotSsid", "hotspotPassword", "hotspotGatewayIp") {
|
|
595
595
|
return HotspotStatus.fromStoreValues(values)
|
|
596
596
|
}
|
|
597
597
|
return nil
|
|
@@ -633,6 +633,9 @@ public struct BluetoothStatusUpdate: CustomStringConvertible {
|
|
|
633
633
|
public var micEnabled: Bool? { optionalBoolValue(values, "micEnabled") }
|
|
634
634
|
public var currentMic: String? { optionalStringValue(values, "currentMic") }
|
|
635
635
|
public var micRanking: [String]? { optionalStringListValue(values, "micRanking") }
|
|
636
|
+
/// Nearby glasses in stable discovery order when included in an update. Existing entries keep their
|
|
637
|
+
/// array position as details refresh; new glasses append at the end, and removals should not reorder
|
|
638
|
+
/// remaining entries.
|
|
636
639
|
public var searchResults: [Device]? {
|
|
637
640
|
optionalDictionaryListValue(values, "searchResults")?.compactMap(Device.init(values:))
|
|
638
641
|
}
|
|
@@ -658,7 +661,7 @@ public struct BluetoothStatusUpdate: CustomStringConvertible {
|
|
|
658
661
|
public var dashboardDepth: Int? { optionalIntValue(values, "dashboard_depth") }
|
|
659
662
|
public var headUpAngle: Int? { optionalIntValue(values, "head_up_angle") }
|
|
660
663
|
public var contextualDashboard: Bool? { optionalBoolValue(values, "contextual_dashboard") }
|
|
661
|
-
public var galleryModeAuto: Bool? { optionalBoolValue(values, "
|
|
664
|
+
public var galleryModeAuto: Bool? { optionalBoolValue(values, "galleryModeAuto") }
|
|
662
665
|
public var buttonPhotoSize: ButtonPhotoSize? {
|
|
663
666
|
optionalStringValue(values, "button_photo_size").flatMap(ButtonPhotoSize.init(rawValue:))
|
|
664
667
|
}
|
|
@@ -666,7 +669,7 @@ public struct BluetoothStatusUpdate: CustomStringConvertible {
|
|
|
666
669
|
public var buttonMaxRecordingTime: Int? { optionalIntValue(values, "button_max_recording_time") }
|
|
667
670
|
public var buttonVideoWidth: Int? { optionalIntValue(values, "button_video_width") }
|
|
668
671
|
public var buttonVideoHeight: Int? { optionalIntValue(values, "button_video_height") }
|
|
669
|
-
public var
|
|
672
|
+
public var buttonVideoFrameRate: Int? { optionalIntValue(values, "button_video_fps") }
|
|
670
673
|
public var shouldSendPcm: Bool? { optionalBoolValue(values, "should_send_pcm") }
|
|
671
674
|
public var shouldSendLc3: Bool? { optionalBoolValue(values, "should_send_lc3") }
|
|
672
675
|
public var shouldSendTranscript: Bool? { optionalBoolValue(values, "should_send_transcript") }
|
|
@@ -703,10 +706,10 @@ public struct DisplayTextRequest {
|
|
|
703
706
|
}
|
|
704
707
|
}
|
|
705
708
|
|
|
706
|
-
|
|
707
|
-
|
|
709
|
+
struct DisplayEventRequest {
|
|
710
|
+
let values: [String: Any]
|
|
708
711
|
|
|
709
|
-
|
|
712
|
+
init(values: [String: Any]) {
|
|
710
713
|
self.values = values
|
|
711
714
|
}
|
|
712
715
|
}
|
|
@@ -721,12 +724,12 @@ public struct DashboardPositionRequest {
|
|
|
721
724
|
}
|
|
722
725
|
}
|
|
723
726
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
727
|
+
struct DashboardMenuItem {
|
|
728
|
+
let title: String
|
|
729
|
+
let packageName: String
|
|
730
|
+
let values: [String: Any]
|
|
728
731
|
|
|
729
|
-
|
|
732
|
+
init(title: String, packageName: String, values: [String: Any] = [:]) {
|
|
730
733
|
self.title = title
|
|
731
734
|
self.packageName = packageName
|
|
732
735
|
self.values = values
|
|
@@ -772,12 +775,12 @@ public struct ButtonPhotoSettings {
|
|
|
772
775
|
public struct ButtonVideoRecordingSettings {
|
|
773
776
|
public let width: Int
|
|
774
777
|
public let height: Int
|
|
775
|
-
public let
|
|
778
|
+
public let frameRate: Int
|
|
776
779
|
|
|
777
|
-
public init(width: Int, height: Int,
|
|
780
|
+
public init(width: Int, height: Int, frameRate: Int) {
|
|
778
781
|
self.width = width
|
|
779
782
|
self.height = height
|
|
780
|
-
self.
|
|
783
|
+
self.frameRate = frameRate
|
|
781
784
|
}
|
|
782
785
|
}
|
|
783
786
|
|
|
@@ -788,32 +791,13 @@ public enum CameraFov {
|
|
|
788
791
|
var value: [String: Int] {
|
|
789
792
|
switch self {
|
|
790
793
|
case .standard:
|
|
791
|
-
["fov": 118, "
|
|
794
|
+
["fov": 118, "roiPosition": 0]
|
|
792
795
|
case .wide:
|
|
793
|
-
["fov": 118, "
|
|
796
|
+
["fov": 118, "roiPosition": 0]
|
|
794
797
|
}
|
|
795
798
|
}
|
|
796
799
|
}
|
|
797
800
|
|
|
798
|
-
public struct MicConfiguration {
|
|
799
|
-
public let sendPcmData: Bool
|
|
800
|
-
public let sendTranscript: Bool
|
|
801
|
-
public let bypassVad: Bool
|
|
802
|
-
public let sendLc3Data: Bool
|
|
803
|
-
|
|
804
|
-
public init(
|
|
805
|
-
sendPcmData: Bool,
|
|
806
|
-
sendTranscript: Bool,
|
|
807
|
-
bypassVad: Bool,
|
|
808
|
-
sendLc3Data: Bool = false
|
|
809
|
-
) {
|
|
810
|
-
self.sendPcmData = sendPcmData
|
|
811
|
-
self.sendTranscript = sendTranscript
|
|
812
|
-
self.bypassVad = bypassVad
|
|
813
|
-
self.sendLc3Data = sendLc3Data
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
|
|
817
801
|
public enum MicPreference: String {
|
|
818
802
|
case auto
|
|
819
803
|
case phone
|
|
@@ -828,7 +812,6 @@ public struct PhotoRequest {
|
|
|
828
812
|
public let webhookUrl: String?
|
|
829
813
|
public let authToken: String?
|
|
830
814
|
public let compress: PhotoCompression?
|
|
831
|
-
public let flash: Bool
|
|
832
815
|
public let sound: Bool
|
|
833
816
|
|
|
834
817
|
public init(
|
|
@@ -838,7 +821,6 @@ public struct PhotoRequest {
|
|
|
838
821
|
webhookUrl: String? = nil,
|
|
839
822
|
authToken: String? = nil,
|
|
840
823
|
compress: PhotoCompression? = nil,
|
|
841
|
-
flash: Bool,
|
|
842
824
|
sound: Bool
|
|
843
825
|
) {
|
|
844
826
|
self.requestId = requestId
|
|
@@ -847,7 +829,6 @@ public struct PhotoRequest {
|
|
|
847
829
|
self.webhookUrl = webhookUrl
|
|
848
830
|
self.authToken = authToken
|
|
849
831
|
self.compress = compress
|
|
850
|
-
self.flash = flash
|
|
851
832
|
self.sound = sound
|
|
852
833
|
}
|
|
853
834
|
}
|
|
@@ -882,10 +863,10 @@ public struct StreamVideoConfig {
|
|
|
882
863
|
init?(values: [String: Any]?) {
|
|
883
864
|
guard let values else { return nil }
|
|
884
865
|
self.init(
|
|
885
|
-
width: intValue(values["width"]
|
|
886
|
-
height: intValue(values["height"]
|
|
887
|
-
bitrate: intValue(values["bitrate"]
|
|
888
|
-
frameRate: intValue(values["frameRate"]
|
|
866
|
+
width: intValue(values["width"]),
|
|
867
|
+
height: intValue(values["height"]),
|
|
868
|
+
bitrate: intValue(values["bitrate"]),
|
|
869
|
+
frameRate: intValue(values["frameRate"])
|
|
889
870
|
)
|
|
890
871
|
}
|
|
891
872
|
}
|
|
@@ -920,10 +901,10 @@ public struct StreamAudioConfig {
|
|
|
920
901
|
init?(values: [String: Any]?) {
|
|
921
902
|
guard let values else { return nil }
|
|
922
903
|
self.init(
|
|
923
|
-
bitrate: intValue(values["bitrate"]
|
|
924
|
-
sampleRate: intValue(values["sampleRate"]
|
|
925
|
-
echoCancellation: values["echoCancellation"] as? Bool
|
|
926
|
-
noiseSuppression: values["noiseSuppression"] as? Bool
|
|
904
|
+
bitrate: intValue(values["bitrate"]),
|
|
905
|
+
sampleRate: intValue(values["sampleRate"]),
|
|
906
|
+
echoCancellation: values["echoCancellation"] as? Bool,
|
|
907
|
+
noiseSuppression: values["noiseSuppression"] as? Bool
|
|
927
908
|
)
|
|
928
909
|
}
|
|
929
910
|
}
|
|
@@ -933,7 +914,6 @@ public struct StreamRequest {
|
|
|
933
914
|
public let streamId: String
|
|
934
915
|
public let keepAlive: Bool
|
|
935
916
|
public let keepAliveIntervalSeconds: Int
|
|
936
|
-
public let flash: Bool
|
|
937
917
|
public let sound: Bool
|
|
938
918
|
public let video: StreamVideoConfig?
|
|
939
919
|
public let audio: StreamAudioConfig?
|
|
@@ -944,7 +924,6 @@ public struct StreamRequest {
|
|
|
944
924
|
streamId: String = "",
|
|
945
925
|
keepAlive: Bool = true,
|
|
946
926
|
keepAliveIntervalSeconds: Int = 15,
|
|
947
|
-
flash: Bool = true,
|
|
948
927
|
sound: Bool = true,
|
|
949
928
|
video: StreamVideoConfig? = nil,
|
|
950
929
|
audio: StreamAudioConfig? = nil,
|
|
@@ -954,7 +933,6 @@ public struct StreamRequest {
|
|
|
954
933
|
self.streamId = streamId
|
|
955
934
|
self.keepAlive = keepAlive
|
|
956
935
|
self.keepAliveIntervalSeconds = keepAliveIntervalSeconds
|
|
957
|
-
self.flash = flash
|
|
958
936
|
self.sound = sound
|
|
959
937
|
self.video = video
|
|
960
938
|
self.audio = audio
|
|
@@ -971,10 +949,9 @@ public struct StreamRequest {
|
|
|
971
949
|
streamId: values["streamId"] as? String ?? "",
|
|
972
950
|
keepAlive: values["keepAlive"] as? Bool ?? true,
|
|
973
951
|
keepAliveIntervalSeconds: intValue(values["keepAliveIntervalSeconds"]) ?? 15,
|
|
974
|
-
flash: values["flash"] as? Bool ?? true,
|
|
975
952
|
sound: values["sound"] as? Bool ?? true,
|
|
976
|
-
video: StreamVideoConfig(values:
|
|
977
|
-
audio: StreamAudioConfig(values:
|
|
953
|
+
video: StreamVideoConfig(values: values["video"] as? [String: Any]),
|
|
954
|
+
audio: StreamAudioConfig(values: values["audio"] as? [String: Any]),
|
|
978
955
|
extraValues: values
|
|
979
956
|
)
|
|
980
957
|
}
|
|
@@ -986,7 +963,8 @@ public struct StreamRequest {
|
|
|
986
963
|
values["streamId"] = streamId
|
|
987
964
|
values["keepAlive"] = keepAlive
|
|
988
965
|
values["keepAliveIntervalSeconds"] = keepAliveIntervalSeconds
|
|
989
|
-
|
|
966
|
+
// The camera light is a privacy indicator and cannot be disabled by SDK callers.
|
|
967
|
+
values["flash"] = true
|
|
990
968
|
values["sound"] = sound
|
|
991
969
|
if let videoValues = video?.dictionary, !videoValues.isEmpty {
|
|
992
970
|
values["video"] = videoValues
|
|
@@ -1044,8 +1022,8 @@ public struct RgbLedRequest {
|
|
|
1044
1022
|
public let packageName: String?
|
|
1045
1023
|
public let action: RgbLedAction
|
|
1046
1024
|
public let color: RgbLedColor?
|
|
1047
|
-
public let
|
|
1048
|
-
public let
|
|
1025
|
+
public let onDurationMs: Int
|
|
1026
|
+
public let offDurationMs: Int
|
|
1049
1027
|
public let count: Int
|
|
1050
1028
|
|
|
1051
1029
|
public init(
|
|
@@ -1053,16 +1031,16 @@ public struct RgbLedRequest {
|
|
|
1053
1031
|
packageName: String?,
|
|
1054
1032
|
action: RgbLedAction,
|
|
1055
1033
|
color: RgbLedColor?,
|
|
1056
|
-
|
|
1057
|
-
|
|
1034
|
+
onDurationMs: Int,
|
|
1035
|
+
offDurationMs: Int,
|
|
1058
1036
|
count: Int
|
|
1059
1037
|
) {
|
|
1060
1038
|
self.requestId = requestId
|
|
1061
1039
|
self.packageName = packageName
|
|
1062
1040
|
self.action = action
|
|
1063
1041
|
self.color = color
|
|
1064
|
-
self.
|
|
1065
|
-
self.
|
|
1042
|
+
self.onDurationMs = onDurationMs
|
|
1043
|
+
self.offDurationMs = offDurationMs
|
|
1066
1044
|
self.count = count
|
|
1067
1045
|
}
|
|
1068
1046
|
}
|
|
@@ -1070,13 +1048,11 @@ public struct RgbLedRequest {
|
|
|
1070
1048
|
public struct VideoRecordingRequest {
|
|
1071
1049
|
public let requestId: String
|
|
1072
1050
|
public let save: Bool
|
|
1073
|
-
public let flash: Bool
|
|
1074
1051
|
public let sound: Bool
|
|
1075
1052
|
|
|
1076
|
-
public init(requestId: String, save: Bool,
|
|
1053
|
+
public init(requestId: String, save: Bool, sound: Bool) {
|
|
1077
1054
|
self.requestId = requestId
|
|
1078
1055
|
self.save = save
|
|
1079
|
-
self.flash = flash
|
|
1080
1056
|
self.sound = sound
|
|
1081
1057
|
}
|
|
1082
1058
|
}
|
|
@@ -1105,11 +1081,11 @@ public struct TouchEvent: CustomStringConvertible {
|
|
|
1105
1081
|
}
|
|
1106
1082
|
|
|
1107
1083
|
public var deviceModel: String? {
|
|
1108
|
-
stringValue(values, "
|
|
1084
|
+
stringValue(values, "deviceModel")
|
|
1109
1085
|
}
|
|
1110
1086
|
|
|
1111
1087
|
public var gestureName: String? {
|
|
1112
|
-
stringValue(values, "
|
|
1088
|
+
stringValue(values, "gestureName")
|
|
1113
1089
|
}
|
|
1114
1090
|
|
|
1115
1091
|
public var timestamp: Int? {
|
|
@@ -1339,7 +1315,7 @@ public enum HotspotStatus: CustomStringConvertible, Equatable {
|
|
|
1339
1315
|
enabled: enabled,
|
|
1340
1316
|
ssid: nonEmptyStringValue(values, "hotspotSsid"),
|
|
1341
1317
|
password: nonEmptyStringValue(values, "hotspotPassword"),
|
|
1342
|
-
localIp: nonEmptyStringValue(values, "hotspotGatewayIp"
|
|
1318
|
+
localIp: nonEmptyStringValue(values, "hotspotGatewayIp")
|
|
1343
1319
|
)
|
|
1344
1320
|
}
|
|
1345
1321
|
|
|
@@ -1438,7 +1414,7 @@ public struct HotspotErrorEvent: CustomStringConvertible {
|
|
|
1438
1414
|
}
|
|
1439
1415
|
|
|
1440
1416
|
public var message: String? {
|
|
1441
|
-
stringValue(values, "
|
|
1417
|
+
stringValue(values, "errorMessage")
|
|
1442
1418
|
}
|
|
1443
1419
|
|
|
1444
1420
|
public var timestamp: Int? {
|
|
@@ -1456,24 +1432,24 @@ public enum PhotoResponse: CustomStringConvertible, Equatable {
|
|
|
1456
1432
|
case error
|
|
1457
1433
|
}
|
|
1458
1434
|
|
|
1459
|
-
case success(requestId: String,
|
|
1435
|
+
case success(requestId: String, uploadUrl: String, timestamp: Int)
|
|
1460
1436
|
case error(requestId: String, errorCode: String?, errorMessage: String, timestamp: Int)
|
|
1461
1437
|
|
|
1462
1438
|
public init(values: [String: Any]) {
|
|
1463
|
-
let requestId = stringValue(values, "requestId"
|
|
1439
|
+
let requestId = stringValue(values, "requestId") ?? ""
|
|
1464
1440
|
let timestamp = intValue(values["timestamp"]) ?? Int(Date().timeIntervalSince1970 * 1000)
|
|
1465
|
-
let state = stringValue(values, "state"
|
|
1466
|
-
if state == State.success.rawValue
|
|
1441
|
+
let state = stringValue(values, "state")?.lowercased()
|
|
1442
|
+
if state == State.success.rawValue {
|
|
1467
1443
|
self = .success(
|
|
1468
1444
|
requestId: requestId,
|
|
1469
|
-
|
|
1445
|
+
uploadUrl: stringValue(values, "uploadUrl") ?? "",
|
|
1470
1446
|
timestamp: timestamp
|
|
1471
1447
|
)
|
|
1472
1448
|
} else {
|
|
1473
1449
|
self = .error(
|
|
1474
1450
|
requestId: requestId,
|
|
1475
|
-
errorCode: stringValue(values, "errorCode"
|
|
1476
|
-
errorMessage: stringValue(values, "errorMessage"
|
|
1451
|
+
errorCode: stringValue(values, "errorCode"),
|
|
1452
|
+
errorMessage: stringValue(values, "errorMessage") ?? "Unknown photo error",
|
|
1477
1453
|
timestamp: timestamp
|
|
1478
1454
|
)
|
|
1479
1455
|
}
|
|
@@ -1504,11 +1480,11 @@ public enum PhotoResponse: CustomStringConvertible, Equatable {
|
|
|
1504
1480
|
|
|
1505
1481
|
public var values: [String: Any] {
|
|
1506
1482
|
switch self {
|
|
1507
|
-
case let .success(requestId,
|
|
1483
|
+
case let .success(requestId, uploadUrl, timestamp):
|
|
1508
1484
|
return [
|
|
1509
1485
|
"state": State.success.rawValue,
|
|
1510
1486
|
"requestId": requestId,
|
|
1511
|
-
"
|
|
1487
|
+
"uploadUrl": uploadUrl,
|
|
1512
1488
|
"timestamp": timestamp,
|
|
1513
1489
|
]
|
|
1514
1490
|
case let .error(requestId, errorCode, errorMessage, timestamp):
|
|
@@ -1607,10 +1583,10 @@ public enum StreamStatus: CustomStringConvertible, Equatable {
|
|
|
1607
1583
|
|
|
1608
1584
|
public init(values: [String: Any]) {
|
|
1609
1585
|
let rawState = stringValue(values, "status")
|
|
1610
|
-
let streamId = stringValue(values, "streamId"
|
|
1586
|
+
let streamId = stringValue(values, "streamId")
|
|
1611
1587
|
let timestamp = intValue(values["timestamp"])
|
|
1612
1588
|
let attempt = optionalIntValue(values, "attempt")
|
|
1613
|
-
let maxAttempts = optionalIntValue(values, "maxAttempts"
|
|
1589
|
+
let maxAttempts = optionalIntValue(values, "maxAttempts") ?? 0
|
|
1614
1590
|
|
|
1615
1591
|
if hasAnyKey(values, "streaming") || hasAnyKey(values, "reconnecting") {
|
|
1616
1592
|
let streaming = boolValue(values, "streaming") == true
|
|
@@ -1652,7 +1628,7 @@ public enum StreamStatus: CustomStringConvertible, Equatable {
|
|
|
1652
1628
|
case .error:
|
|
1653
1629
|
self = .error(
|
|
1654
1630
|
streamId: streamId,
|
|
1655
|
-
errorDetails: stringValue(values, "errorDetails"
|
|
1631
|
+
errorDetails: stringValue(values, "errorDetails")
|
|
1656
1632
|
?? (rawState == "error_not_streaming" ? "not_streaming" : "Unknown stream error"),
|
|
1657
1633
|
timestamp: timestamp
|
|
1658
1634
|
)
|
|
@@ -1798,8 +1774,8 @@ public struct KeepAliveAckEvent: CustomStringConvertible, Equatable {
|
|
|
1798
1774
|
}
|
|
1799
1775
|
|
|
1800
1776
|
public init(values: [String: Any]) {
|
|
1801
|
-
self.streamId = stringValue(values, "streamId"
|
|
1802
|
-
self.ackId = stringValue(values, "ackId"
|
|
1777
|
+
self.streamId = stringValue(values, "streamId") ?? ""
|
|
1778
|
+
self.ackId = stringValue(values, "ackId") ?? ""
|
|
1803
1779
|
self.timestamp = intValue(values["timestamp"])
|
|
1804
1780
|
}
|
|
1805
1781
|
|
|
@@ -1900,6 +1876,26 @@ public enum ScanStopReason {
|
|
|
1900
1876
|
case error
|
|
1901
1877
|
}
|
|
1902
1878
|
|
|
1879
|
+
@MainActor
|
|
1880
|
+
public final class ScanSession {
|
|
1881
|
+
private let stopAction: () -> Void
|
|
1882
|
+
private var stopped = false
|
|
1883
|
+
|
|
1884
|
+
init(stopAction: @escaping () -> Void) {
|
|
1885
|
+
self.stopAction = stopAction
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
public func stop() {
|
|
1889
|
+
guard !stopped else { return }
|
|
1890
|
+
stopped = true
|
|
1891
|
+
stopAction()
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
fileprivate func markStopped() {
|
|
1895
|
+
stopped = true
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1903
1899
|
public struct LocalTranscriptionEvent: CustomStringConvertible {
|
|
1904
1900
|
public let text: String
|
|
1905
1901
|
public let isFinal: Bool
|
|
@@ -1916,6 +1912,38 @@ public struct LocalTranscriptionEvent: CustomStringConvertible {
|
|
|
1916
1912
|
}
|
|
1917
1913
|
}
|
|
1918
1914
|
|
|
1915
|
+
public struct GlassesMediaVolumeGetResult: CustomStringConvertible {
|
|
1916
|
+
public let level: Int?
|
|
1917
|
+
public let statusCode: Int?
|
|
1918
|
+
public let values: [String: Any]
|
|
1919
|
+
|
|
1920
|
+
public init(values: [String: Any]) {
|
|
1921
|
+
self.level = intValue(values["level"])
|
|
1922
|
+
self.statusCode = intValue(values["statusCode"])
|
|
1923
|
+
self.values = values
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
public var description: String {
|
|
1927
|
+
let levelText = level.map(String.init) ?? "unknown"
|
|
1928
|
+
let statusCodeText = statusCode.map(String.init) ?? "unknown"
|
|
1929
|
+
return "GlassesMediaVolumeGetResult(level: \(levelText), statusCode: \(statusCodeText))"
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
public struct GlassesMediaVolumeSetResult: CustomStringConvertible {
|
|
1934
|
+
public let statusCode: Int?
|
|
1935
|
+
public let values: [String: Any]
|
|
1936
|
+
|
|
1937
|
+
public init(values: [String: Any]) {
|
|
1938
|
+
self.statusCode = intValue(values["statusCode"])
|
|
1939
|
+
self.values = values
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
public var description: String {
|
|
1943
|
+
"GlassesMediaVolumeSetResult(statusCode: \(statusCode.map(String.init) ?? "unknown"))"
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1919
1947
|
public enum BluetoothEvent: CustomStringConvertible {
|
|
1920
1948
|
case buttonPress(ButtonPressEvent)
|
|
1921
1949
|
case touch(TouchEvent)
|
|
@@ -1982,6 +2010,26 @@ public extension MentraBluetoothSDKDelegate {
|
|
|
1982
2010
|
func mentraBluetoothSDK(_: MentraBluetoothSDK, didFail _: BluetoothError) {}
|
|
1983
2011
|
}
|
|
1984
2012
|
|
|
2013
|
+
@MainActor
|
|
2014
|
+
private final class ActiveScanSession {
|
|
2015
|
+
let model: DeviceModel
|
|
2016
|
+
let onResults: ([Device]) -> Void
|
|
2017
|
+
let onComplete: ([Device]) -> Void
|
|
2018
|
+
var latestResults: [Device] = []
|
|
2019
|
+
var timeoutTask: Task<Void, Never>?
|
|
2020
|
+
weak var publicSession: ScanSession?
|
|
2021
|
+
|
|
2022
|
+
init(
|
|
2023
|
+
model: DeviceModel,
|
|
2024
|
+
onResults: @escaping ([Device]) -> Void,
|
|
2025
|
+
onComplete: @escaping ([Device]) -> Void
|
|
2026
|
+
) {
|
|
2027
|
+
self.model = model
|
|
2028
|
+
self.onResults = onResults
|
|
2029
|
+
self.onComplete = onComplete
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
|
|
1985
2033
|
@MainActor
|
|
1986
2034
|
public final class MentraBluetoothSDK {
|
|
1987
2035
|
public weak var delegate: MentraBluetoothSDKDelegate?
|
|
@@ -1993,6 +2041,7 @@ public final class MentraBluetoothSDK {
|
|
|
1993
2041
|
private let defaultDeviceKeys: Set<String> = ["default_wearable", "device_name", "device_address"]
|
|
1994
2042
|
private var suppressDefaultDeviceEvents = false
|
|
1995
2043
|
private var defaultDeviceApplyGeneration = 0
|
|
2044
|
+
private var activeScanSessions: [UUID: ActiveScanSession] = [:]
|
|
1996
2045
|
|
|
1997
2046
|
public init(configuration: MentraBluetoothSDKConfiguration = .default) {
|
|
1998
2047
|
self.configuration = configuration
|
|
@@ -2059,9 +2108,50 @@ public final class MentraBluetoothSDK {
|
|
|
2059
2108
|
}
|
|
2060
2109
|
|
|
2061
2110
|
public func stopScan() {
|
|
2111
|
+
stopScan(reason: .cancelled)
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
private func stopScan(reason: ScanStopReason) {
|
|
2062
2115
|
DeviceManager.shared.stopScan()
|
|
2063
2116
|
DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "searching", false)
|
|
2064
|
-
delegate?.mentraBluetoothSDK(self, didStopScan:
|
|
2117
|
+
delegate?.mentraBluetoothSDK(self, didStopScan: reason)
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
@discardableResult
|
|
2121
|
+
public func scan(
|
|
2122
|
+
model: DeviceModel,
|
|
2123
|
+
timeout: TimeInterval = 15,
|
|
2124
|
+
onResults: @escaping ([Device]) -> Void,
|
|
2125
|
+
onComplete: @escaping ([Device]) -> Void = { _ in }
|
|
2126
|
+
) throws -> ScanSession {
|
|
2127
|
+
let normalizedTimeout = timeout > 0 && timeout.isFinite ? timeout : 15
|
|
2128
|
+
let id = UUID()
|
|
2129
|
+
let activeSession = ActiveScanSession(
|
|
2130
|
+
model: model,
|
|
2131
|
+
onResults: onResults,
|
|
2132
|
+
onComplete: onComplete
|
|
2133
|
+
)
|
|
2134
|
+
let publicSession = ScanSession { [weak self] in
|
|
2135
|
+
self?.finishScanSession(id, reason: .cancelled, shouldStopScan: true)
|
|
2136
|
+
}
|
|
2137
|
+
activeSession.publicSession = publicSession
|
|
2138
|
+
activeScanSessions[id] = activeSession
|
|
2139
|
+
|
|
2140
|
+
do {
|
|
2141
|
+
emitScanResults([], forSession: id)
|
|
2142
|
+
try startScan(model: model)
|
|
2143
|
+
emitScanResults(bluetoothStatus.searchResults.filter { $0.model == model }, forSession: id)
|
|
2144
|
+
activeSession.timeoutTask = Task { [weak self] in
|
|
2145
|
+
let nanoseconds = UInt64(normalizedTimeout * 1_000_000_000)
|
|
2146
|
+
try? await Task.sleep(nanoseconds: nanoseconds)
|
|
2147
|
+
await self?.finishScanSession(id, reason: .completed, shouldStopScan: true)
|
|
2148
|
+
}
|
|
2149
|
+
return publicSession
|
|
2150
|
+
} catch {
|
|
2151
|
+
activeScanSessions[id] = nil
|
|
2152
|
+
publicSession.markStopped()
|
|
2153
|
+
throw error
|
|
2154
|
+
}
|
|
2065
2155
|
}
|
|
2066
2156
|
|
|
2067
2157
|
public func connect(to device: Device, options: ConnectOptions = ConnectOptions()) throws {
|
|
@@ -2103,7 +2193,7 @@ public final class MentraBluetoothSDK {
|
|
|
2103
2193
|
DeviceManager.shared.disconnect()
|
|
2104
2194
|
}
|
|
2105
2195
|
|
|
2106
|
-
|
|
2196
|
+
func connectSimulated() {
|
|
2107
2197
|
DeviceManager.shared.connectSimulated()
|
|
2108
2198
|
}
|
|
2109
2199
|
|
|
@@ -2123,7 +2213,7 @@ public final class MentraBluetoothSDK {
|
|
|
2123
2213
|
DeviceManager.shared.displayText(request.dictionary)
|
|
2124
2214
|
}
|
|
2125
2215
|
|
|
2126
|
-
|
|
2216
|
+
func displayEvent(_ request: DisplayEventRequest) async throws {
|
|
2127
2217
|
DeviceManager.shared.displayEvent(request.values)
|
|
2128
2218
|
}
|
|
2129
2219
|
|
|
@@ -2135,14 +2225,14 @@ public final class MentraBluetoothSDK {
|
|
|
2135
2225
|
DeviceManager.shared.showDashboard()
|
|
2136
2226
|
}
|
|
2137
2227
|
|
|
2138
|
-
|
|
2228
|
+
func setBrightness(_ level: Int, autoMode: Bool? = nil) async throws {
|
|
2139
2229
|
if let autoMode {
|
|
2140
2230
|
DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "auto_brightness", autoMode)
|
|
2141
2231
|
}
|
|
2142
2232
|
DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "brightness", level)
|
|
2143
2233
|
}
|
|
2144
2234
|
|
|
2145
|
-
|
|
2235
|
+
func setAutoBrightness(enabled: Bool) async throws {
|
|
2146
2236
|
DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "auto_brightness", enabled)
|
|
2147
2237
|
}
|
|
2148
2238
|
|
|
@@ -2155,7 +2245,7 @@ public final class MentraBluetoothSDK {
|
|
|
2155
2245
|
try await setDashboardPosition(height: request.height, depth: request.depth)
|
|
2156
2246
|
}
|
|
2157
2247
|
|
|
2158
|
-
|
|
2248
|
+
func setDashboardMenu(_ items: [DashboardMenuItem]) async throws {
|
|
2159
2249
|
DeviceStore.shared.apply(
|
|
2160
2250
|
ObservableStore.bluetoothCategory,
|
|
2161
2251
|
"menu_apps",
|
|
@@ -2172,7 +2262,7 @@ public final class MentraBluetoothSDK {
|
|
|
2172
2262
|
}
|
|
2173
2263
|
|
|
2174
2264
|
public func setGalleryMode(_ mode: GalleryMode) async throws {
|
|
2175
|
-
DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "
|
|
2265
|
+
DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "galleryModeAuto", mode == .auto)
|
|
2176
2266
|
}
|
|
2177
2267
|
|
|
2178
2268
|
public func setButtonPhotoSettings(size: ButtonPhotoSize) async throws {
|
|
@@ -2183,14 +2273,14 @@ public final class MentraBluetoothSDK {
|
|
|
2183
2273
|
try await setButtonPhotoSettings(size: settings.size)
|
|
2184
2274
|
}
|
|
2185
2275
|
|
|
2186
|
-
public func setButtonVideoRecordingSettings(width: Int, height: Int,
|
|
2276
|
+
public func setButtonVideoRecordingSettings(width: Int, height: Int, frameRate: Int) async throws {
|
|
2187
2277
|
DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "button_video_width", width)
|
|
2188
2278
|
DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "button_video_height", height)
|
|
2189
|
-
DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "button_video_fps",
|
|
2279
|
+
DeviceStore.shared.apply(ObservableStore.bluetoothCategory, "button_video_fps", frameRate)
|
|
2190
2280
|
}
|
|
2191
2281
|
|
|
2192
2282
|
public func setButtonVideoRecordingSettings(_ settings: ButtonVideoRecordingSettings) async throws {
|
|
2193
|
-
try await setButtonVideoRecordingSettings(width: settings.width, height: settings.height,
|
|
2283
|
+
try await setButtonVideoRecordingSettings(width: settings.width, height: settings.height, frameRate: settings.frameRate)
|
|
2194
2284
|
}
|
|
2195
2285
|
|
|
2196
2286
|
public func setButtonCameraLed(enabled: Bool) async throws {
|
|
@@ -2227,16 +2317,6 @@ public final class MentraBluetoothSDK {
|
|
|
2227
2317
|
)
|
|
2228
2318
|
}
|
|
2229
2319
|
|
|
2230
|
-
@available(*, deprecated, message: "Use setMicState(enabled:useGlassesMic:bypassVad:) instead.")
|
|
2231
|
-
public func setMicState(_ config: MicConfiguration) {
|
|
2232
|
-
applyMicState(
|
|
2233
|
-
sendPcmData: config.sendPcmData,
|
|
2234
|
-
sendTranscript: config.sendTranscript,
|
|
2235
|
-
bypassVad: config.bypassVad,
|
|
2236
|
-
sendLc3Data: config.sendLc3Data
|
|
2237
|
-
)
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
2320
|
private func applyMicState(
|
|
2241
2321
|
sendPcmData: Bool,
|
|
2242
2322
|
sendTranscript: Bool,
|
|
@@ -2258,6 +2338,20 @@ public final class MentraBluetoothSDK {
|
|
|
2258
2338
|
PhoneAudioMonitor.getInstance().setOwnAppAudioPlaying(playing)
|
|
2259
2339
|
}
|
|
2260
2340
|
|
|
2341
|
+
public func getGlassesMediaVolume() async throws -> GlassesMediaVolumeGetResult {
|
|
2342
|
+
GlassesMediaVolumeGetResult(values: try await DeviceManager.shared.getGlassesMediaVolume())
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
public func setGlassesMediaVolume(_ level: Int) async throws -> GlassesMediaVolumeSetResult {
|
|
2346
|
+
guard (0...15).contains(level) else {
|
|
2347
|
+
throw BluetoothError(
|
|
2348
|
+
code: "invalid_volume_level",
|
|
2349
|
+
message: "Glasses media volume must be between 0 and 15."
|
|
2350
|
+
)
|
|
2351
|
+
}
|
|
2352
|
+
return GlassesMediaVolumeSetResult(values: try await DeviceManager.shared.setGlassesMediaVolume(level: level))
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2261
2355
|
public func requestWifiScan() {
|
|
2262
2356
|
DeviceManager.shared.requestWifiScan()
|
|
2263
2357
|
}
|
|
@@ -2275,14 +2369,13 @@ public final class MentraBluetoothSDK {
|
|
|
2275
2369
|
}
|
|
2276
2370
|
|
|
2277
2371
|
public func requestPhoto(_ request: PhotoRequest) {
|
|
2278
|
-
DeviceManager.shared.
|
|
2372
|
+
DeviceManager.shared.requestPhoto(
|
|
2279
2373
|
request.requestId,
|
|
2280
2374
|
request.appId,
|
|
2281
2375
|
request.size.rawValue,
|
|
2282
2376
|
request.webhookUrl,
|
|
2283
2377
|
request.authToken,
|
|
2284
2378
|
request.compress?.rawValue,
|
|
2285
|
-
request.flash,
|
|
2286
2379
|
request.sound
|
|
2287
2380
|
)
|
|
2288
2381
|
}
|
|
@@ -2305,8 +2398,8 @@ public final class MentraBluetoothSDK {
|
|
|
2305
2398
|
packageName: request.packageName,
|
|
2306
2399
|
action: request.action.rawValue,
|
|
2307
2400
|
color: request.color?.rawValue,
|
|
2308
|
-
|
|
2309
|
-
|
|
2401
|
+
onDurationMs: request.onDurationMs,
|
|
2402
|
+
offDurationMs: request.offDurationMs,
|
|
2310
2403
|
count: request.count
|
|
2311
2404
|
)
|
|
2312
2405
|
}
|
|
@@ -2319,7 +2412,6 @@ public final class MentraBluetoothSDK {
|
|
|
2319
2412
|
DeviceManager.shared.startVideoRecording(
|
|
2320
2413
|
request.requestId,
|
|
2321
2414
|
request.save,
|
|
2322
|
-
request.flash,
|
|
2323
2415
|
request.sound
|
|
2324
2416
|
)
|
|
2325
2417
|
}
|
|
@@ -2332,23 +2424,23 @@ public final class MentraBluetoothSDK {
|
|
|
2332
2424
|
DeviceManager.shared.requestVersionInfo()
|
|
2333
2425
|
}
|
|
2334
2426
|
|
|
2335
|
-
|
|
2427
|
+
func sendOtaStart() {
|
|
2336
2428
|
DeviceManager.shared.sendOtaStart()
|
|
2337
2429
|
}
|
|
2338
2430
|
|
|
2339
|
-
|
|
2431
|
+
func sendOtaQueryStatus() {
|
|
2340
2432
|
DeviceManager.shared.sendOtaQueryStatus()
|
|
2341
2433
|
}
|
|
2342
2434
|
|
|
2343
|
-
|
|
2435
|
+
func sendShutdown() {
|
|
2344
2436
|
DeviceManager.shared.sendShutdown()
|
|
2345
2437
|
}
|
|
2346
2438
|
|
|
2347
|
-
|
|
2439
|
+
func sendReboot() {
|
|
2348
2440
|
DeviceManager.shared.sendReboot()
|
|
2349
2441
|
}
|
|
2350
2442
|
|
|
2351
|
-
|
|
2443
|
+
func sendIncidentId(_ incidentId: String, apiBaseUrl: String? = nil) {
|
|
2352
2444
|
DeviceManager.shared.sendIncidentId(incidentId, apiBaseUrl: apiBaseUrl)
|
|
2353
2445
|
}
|
|
2354
2446
|
|
|
@@ -2374,6 +2466,7 @@ public final class MentraBluetoothSDK {
|
|
|
2374
2466
|
dispatchDefaultDeviceChanged()
|
|
2375
2467
|
}
|
|
2376
2468
|
dispatchDiscoveredDevices(changes["searchResults"])
|
|
2469
|
+
dispatchScanResults(changes["searchResults"])
|
|
2377
2470
|
default:
|
|
2378
2471
|
break
|
|
2379
2472
|
}
|
|
@@ -2428,13 +2521,38 @@ public final class MentraBluetoothSDK {
|
|
|
2428
2521
|
private func dispatchDiscoveredDevices(_ rawSearchResults: Any?) {
|
|
2429
2522
|
guard let results = rawSearchResults as? [[String: Any]] else { return }
|
|
2430
2523
|
for result in results {
|
|
2431
|
-
guard let name = result["
|
|
2524
|
+
guard let name = result["name"] as? String else { continue }
|
|
2432
2525
|
guard discoveredDeviceNames.insert(name).inserted else { continue }
|
|
2433
2526
|
guard let device = Device(values: result) else { continue }
|
|
2434
2527
|
delegate?.mentraBluetoothSDK(self, didDiscover: device)
|
|
2435
2528
|
}
|
|
2436
2529
|
}
|
|
2437
2530
|
|
|
2531
|
+
private func dispatchScanResults(_ rawSearchResults: Any?) {
|
|
2532
|
+
guard let results = rawSearchResults as? [[String: Any]] else { return }
|
|
2533
|
+
let devices = results.compactMap(Device.init(values:))
|
|
2534
|
+
for id in Array(activeScanSessions.keys) {
|
|
2535
|
+
guard let activeSession = activeScanSessions[id] else { continue }
|
|
2536
|
+
emitScanResults(devices.filter { $0.model == activeSession.model }, forSession: id)
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
private func emitScanResults(_ devices: [Device], forSession id: UUID) {
|
|
2541
|
+
guard let activeSession = activeScanSessions[id] else { return }
|
|
2542
|
+
activeSession.latestResults = devices
|
|
2543
|
+
activeSession.onResults(devices)
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
private func finishScanSession(_ id: UUID, reason: ScanStopReason, shouldStopScan: Bool) {
|
|
2547
|
+
guard let activeSession = activeScanSessions.removeValue(forKey: id) else { return }
|
|
2548
|
+
activeSession.timeoutTask?.cancel()
|
|
2549
|
+
activeSession.publicSession?.markStopped()
|
|
2550
|
+
if shouldStopScan {
|
|
2551
|
+
stopScan(reason: reason)
|
|
2552
|
+
}
|
|
2553
|
+
activeSession.onComplete(activeSession.latestResults)
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2438
2556
|
private func dispatchBridgeEvent(_ eventName: String, _ data: [String: Any]) {
|
|
2439
2557
|
switch eventName {
|
|
2440
2558
|
case "log":
|