@mentra/bluetooth-sdk 0.1.9 → 0.1.10

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.
Files changed (147) hide show
  1. package/README.md +116 -18
  2. package/android/build.gradle +289 -16
  3. package/android/gradle.properties.example +9 -0
  4. package/android/lc3Lib/build.gradle +95 -6
  5. package/android/settings.gradle +3 -0
  6. package/android/silero/LICENSE.md +17 -0
  7. package/android/silero/build.gradle +42 -0
  8. package/android/silero/proguard-rules.pro +24 -0
  9. package/android/silero/src/androidTest/assets/hello.wav +0 -0
  10. package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
  11. package/android/silero/src/main/assets/LICENSE +21 -0
  12. package/android/silero/src/main/assets/silero_vad.onnx +0 -0
  13. package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
  14. package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
  15. package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
  16. package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
  17. package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
  18. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
  19. package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
  20. package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
  21. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkDefaults.kt +6 -0
  22. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +165 -27
  23. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +89 -21
  24. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +189 -16
  25. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +22 -2
  26. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +787 -85
  27. package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +6 -2
  28. package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +123 -10
  29. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +12 -2
  30. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +8 -2
  31. package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +33 -17
  32. package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +101 -0
  33. package/android/src/main/java/com/mentra/bluetoothsdk/internal/MapParsing.kt +8 -0
  34. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
  35. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +399 -74
  36. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/MentraPhotoReceiverModule.kt +100 -17
  37. package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +17 -0
  38. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +2 -2
  39. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +569 -220
  40. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +2 -2
  41. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +271 -66
  42. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +176 -104
  43. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraosBle.java +12753 -27036
  44. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +54 -2
  45. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +16 -4
  46. package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +60 -3
  47. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +12 -3
  48. package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +87 -36
  49. package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +13 -4
  50. package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +229 -0
  51. package/android/src/main/java/com/mentra/bluetoothsdk/tts/TTSTools.kt +180 -0
  52. package/android/src/main/java/com/mentra/bluetoothsdk/utils/AvifExifStripper.java +573 -0
  53. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +488 -50
  54. package/android/src/main/java/com/mentra/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
  55. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunkReassembler.java +18 -1
  56. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +64 -18
  57. package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +48 -39
  58. package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +23 -0
  59. package/android/src/test/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
  60. package/android/src/test/java/com/mentra/bluetoothsdk/utils/AvifExifStripperTest.java +30 -0
  61. package/android/src/test/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +98 -0
  62. package/android/src/test/resources/avif_with_exif.avif +0 -0
  63. package/build/BluetoothSdk.types.d.ts +241 -43
  64. package/build/BluetoothSdk.types.d.ts.map +1 -1
  65. package/build/BluetoothSdk.types.js +11 -10
  66. package/build/BluetoothSdk.types.js.map +1 -1
  67. package/build/_private/BluetoothSdkModule.d.ts +36 -23
  68. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  69. package/build/_private/BluetoothSdkModule.js +30 -52
  70. package/build/_private/BluetoothSdkModule.js.map +1 -1
  71. package/build/_private/photoRequestPayload.d.ts +4 -0
  72. package/build/_private/photoRequestPayload.d.ts.map +1 -0
  73. package/build/_private/photoRequestPayload.js +25 -0
  74. package/build/_private/photoRequestPayload.js.map +1 -0
  75. package/build/index.d.ts +1 -1
  76. package/build/index.d.ts.map +1 -1
  77. package/build/index.js +23 -0
  78. package/build/index.js.map +1 -1
  79. package/build/react/useMentraBluetooth.d.ts +2 -2
  80. package/build/react/useMentraBluetooth.d.ts.map +1 -1
  81. package/build/react/useMentraBluetooth.js +3 -3
  82. package/build/react/useMentraBluetooth.js.map +1 -1
  83. package/ios/BluetoothSdkModule.swift +174 -70
  84. package/ios/LocalPhotoUploadServer.swift +370 -105
  85. package/ios/MentraBluetoothSDK.podspec +3 -1
  86. package/ios/MentraPhotoReceiverModule.swift +62 -10
  87. package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
  88. package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +668 -26
  89. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +13 -9
  90. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4551 -0
  91. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +1745 -0
  92. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
  93. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4551 -0
  94. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +1745 -0
  95. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
  96. package/ios/Source/BluetoothSdkDefaults.swift +6 -0
  97. package/ios/Source/Bridge.swift +58 -19
  98. package/ios/Source/DeviceManager.swift +149 -17
  99. package/ios/Source/DeviceStore.swift +16 -6
  100. package/ios/Source/MentraBluetoothSDK.swift +803 -47
  101. package/ios/Source/ObservableStore.swift +6 -0
  102. package/ios/Source/{Audio → audio}/AudioModels.swift +9 -7
  103. package/ios/Source/{Camera → camera}/CameraModels.swift +225 -16
  104. package/ios/Source/controllers/ControllerManager.swift +4 -3
  105. package/ios/Source/controllers/R1.swift +3 -3
  106. package/ios/Source/{Events → events}/BluetoothEvents.swift +183 -1
  107. package/ios/Source/{Internal → internal}/ValueParsing.swift +10 -1
  108. package/ios/Source/{Requests → requests}/DisplayRequests.swift +28 -4
  109. package/ios/Source/services/PhoneMic.swift +1 -0
  110. package/ios/Source/sgcs/G1.swift +2 -2
  111. package/ios/Source/sgcs/G2.swift +645 -403
  112. package/ios/Source/sgcs/Mach1.swift +2 -2
  113. package/ios/Source/sgcs/MentraLive.swift +812 -319
  114. package/ios/Source/sgcs/MentraNex.swift +412 -182
  115. package/ios/Source/sgcs/SGCManager.swift +57 -3
  116. package/ios/Source/sgcs/Simulated.swift +7 -3
  117. package/ios/Source/sgcs/mentraos_ble.pb.swift +3073 -3134
  118. package/ios/Source/status/DeviceStatus.swift +974 -0
  119. package/ios/Source/{Status → status}/WifiHotspotStatus.swift +4 -4
  120. package/ios/Source/{Streaming → streaming}/StreamModels.swift +24 -9
  121. package/ios/Source/stt/STTTools.swift +27 -12
  122. package/ios/Source/stt/SherpaOnnxTranscriber.swift +31 -8
  123. package/ios/Source/tts/TTSTools.swift +164 -0
  124. package/ios/Source/utils/JSCExperiment.swift +7 -7
  125. package/ios/Source/utils/MessageChunkReassembler.swift +20 -1
  126. package/ios/Source/utils/MessageChunker.swift +78 -21
  127. package/ios/Source/utils/TarBz2Extractor.swift +53 -2
  128. package/package.json +1 -1
  129. package/plugin/build/withAndroid.js +68 -4
  130. package/src/BluetoothSdk.types.ts +331 -59
  131. package/src/_private/BluetoothSdkModule.ts +113 -102
  132. package/src/_private/photoRequestPayload.ts +28 -0
  133. package/src/index.ts +58 -15
  134. package/src/react/useMentraBluetooth.ts +6 -4
  135. package/android/.project +0 -28
  136. package/android/lc3Lib/.project +0 -28
  137. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/cargs.h +0 -162
  138. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/c-api.h +0 -1852
  139. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Headers/sherpa-onnx/c-api/cxx-api.h +0 -674
  140. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/sherpa-onnx.a +0 -0
  141. package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/sherpa-onnx.a +0 -0
  142. package/ios/Source/Status/DeviceStatus.swift +0 -471
  143. /package/ios/Source/{Connection → connection}/ScanSession.swift +0 -0
  144. /package/ios/Source/{Errors → errors}/BluetoothError.swift +0 -0
  145. /package/ios/Source/{Internal → internal}/BluetoothAvailability.swift +0 -0
  146. /package/ios/Source/{Status → status}/RuntimeState.swift +0 -0
  147. /package/ios/Source/{Types → types}/DeviceModels.swift +0 -0
@@ -1,471 +0,0 @@
1
- import Foundation
2
-
3
- public struct WifiScanResult: CustomStringConvertible {
4
- public let ssid: String
5
- public let requiresPassword: Bool
6
- public let signalStrength: Int
7
- public let frequency: Int?
8
-
9
- public init(ssid: String, requiresPassword: Bool, signalStrength: Int, frequency: Int? = nil) {
10
- self.ssid = ssid
11
- self.requiresPassword = requiresPassword
12
- self.signalStrength = signalStrength
13
- self.frequency = frequency
14
- }
15
-
16
- init(values: [String: Any]) {
17
- ssid = stringValue(values, "ssid") ?? ""
18
- requiresPassword = boolValue(values, "requiresPassword") ?? false
19
- signalStrength = intValue(values["signalStrength"]) ?? -1
20
- frequency = intValue(values["frequency"])
21
- }
22
-
23
- var dictionary: [String: Any] {
24
- var values: [String: Any] = [
25
- "ssid": ssid,
26
- "requiresPassword": requiresPassword,
27
- "signalStrength": signalStrength,
28
- ]
29
- if let frequency {
30
- values["frequency"] = frequency
31
- }
32
- return values
33
- }
34
-
35
- public var description: String {
36
- "WifiScanResult(ssid: \(ssid), signalStrength: \(signalStrength))"
37
- }
38
- }
39
-
40
- public enum GlassesConnectionState: String, CustomStringConvertible, Equatable {
41
- case disconnected = "DISCONNECTED"
42
- case scanning = "SCANNING"
43
- case connecting = "CONNECTING"
44
- case bonding = "BONDING"
45
- case connected = "CONNECTED"
46
-
47
- public init(_ value: String?) {
48
- self = Self.fromValue(value) ?? .disconnected
49
- }
50
-
51
- public static func fromValue(_ value: String?) -> GlassesConnectionState? {
52
- guard let normalized = value?.trimmingCharacters(in: .whitespacesAndNewlines).uppercased(),
53
- !normalized.isEmpty
54
- else {
55
- return nil
56
- }
57
- return Self(rawValue: normalized)
58
- }
59
-
60
- public var isConnected: Bool {
61
- self == .connected
62
- }
63
-
64
- public var isBusy: Bool {
65
- self == .scanning || self == .connecting || self == .bonding
66
- }
67
-
68
- func statusValues(connected: Bool, fullyBooted: Bool) -> [String: Any] {
69
- if self == .connected || connected || fullyBooted {
70
- return ["state": "connected", "fullyBooted": fullyBooted]
71
- }
72
- switch self {
73
- case .scanning:
74
- return ["state": "scanning"]
75
- case .connecting:
76
- return ["state": "connecting"]
77
- case .bonding:
78
- return ["state": "bonding"]
79
- case .connected:
80
- return ["state": "connected", "fullyBooted": fullyBooted]
81
- case .disconnected:
82
- return ["state": "disconnected"]
83
- }
84
- }
85
-
86
- public var description: String {
87
- rawValue
88
- }
89
- }
90
-
91
- struct GlassesStatus: CustomStringConvertible {
92
- let values: [String: Any]
93
-
94
- init(values: [String: Any]) {
95
- self.values = values
96
- }
97
-
98
- func applying(_ update: GlassesStatusUpdate) -> GlassesStatus {
99
- GlassesStatus(values: values.merging(update.values) { _, new in new })
100
- }
101
-
102
- func withBattery(level: Int, charging: Bool) -> GlassesStatus {
103
- applying(GlassesStatusUpdate(values: ["batteryLevel": level, "charging": charging]))
104
- }
105
-
106
- func withWifi(_ wifi: WifiStatus) -> GlassesStatus {
107
- applying(GlassesStatusUpdate(values: wifi.storeValues))
108
- }
109
-
110
- func withHotspot(_ hotspot: HotspotStatus) -> GlassesStatus {
111
- applying(GlassesStatusUpdate(values: hotspot.storeValues))
112
- }
113
-
114
- func disconnected() -> GlassesStatus {
115
- applying(GlassesStatusUpdate(values: [
116
- "connected": false,
117
- "connectionState": "DISCONNECTED",
118
- "fullyBooted": false,
119
- "batteryLevel": -1,
120
- "charging": false,
121
- "hotspotEnabled": false,
122
- "hotspotGatewayIp": "",
123
- "hotspotPassword": "",
124
- "hotspotSsid": "",
125
- "wifiConnected": false,
126
- "wifiSsid": "",
127
- "wifiLocalIp": "",
128
- "signalStrength": -1,
129
- "signalStrengthUpdatedAt": 0,
130
- ]))
131
- }
132
-
133
- var fullyBooted: Bool { boolValue(values, "fullyBooted") ?? false }
134
- var connected: Bool { boolValue(values, "connected") ?? false }
135
- var micEnabled: Bool { boolValue(values, "micEnabled") ?? false }
136
- var voiceActivityDetectionEnabled: Bool { boolValue(values, "voiceActivityDetectionEnabled") ?? true }
137
- var connectionState: GlassesConnectionState { GlassesConnectionState(stringValue(values, "connectionState")) }
138
- var bluetoothClassicConnected: Bool { boolValue(values, "bluetoothClassicConnected") ?? false }
139
- var signalStrength: Int { intValue(values["signalStrength"]) ?? -1 }
140
- var signalStrengthUpdatedAt: Int { intValue(values["signalStrengthUpdatedAt"]) ?? 0 }
141
- var deviceModel: String { stringValue(values, "deviceModel") ?? "" }
142
- var androidVersion: String { stringValue(values, "androidVersion") ?? "" }
143
- var firmwareVersion: String { stringValue(values, "firmwareVersion") ?? "" }
144
- var besFirmwareVersion: String { stringValue(values, "besFirmwareVersion") ?? "" }
145
- var mtkFirmwareVersion: String { stringValue(values, "mtkFirmwareVersion") ?? "" }
146
- var bluetoothMacAddress: String { stringValue(values, "bluetoothMacAddress") ?? "" }
147
- var leftMacAddress: String { stringValue(values, "leftMacAddress") ?? "" }
148
- var rightMacAddress: String { stringValue(values, "rightMacAddress") ?? "" }
149
- var macAddress: String { stringValue(values, "macAddress") ?? "" }
150
- var buildNumber: String { stringValue(values, "buildNumber") ?? "" }
151
- var otaVersionUrl: String { stringValue(values, "otaVersionUrl") ?? "" }
152
- var appVersion: String { stringValue(values, "appVersion") ?? "" }
153
- var bluetoothName: String { stringValue(values, "bluetoothName") ?? "" }
154
- var serialNumber: String { stringValue(values, "serialNumber") ?? "" }
155
- var style: String { stringValue(values, "style") ?? "" }
156
- var color: String { stringValue(values, "color") ?? "" }
157
- var wifi: WifiStatus { WifiStatus.fromStoreValues(values) ?? .disconnected }
158
- var hotspot: HotspotStatus { HotspotStatus.fromStoreValues(values) ?? .disabled }
159
- var dictionary: [String: Any] { Self.dictionary(from: values) }
160
- var batteryLevel: Int { intValue(values["batteryLevel"]) ?? -1 }
161
- var charging: Bool { boolValue(values, "charging") ?? false }
162
- var caseBatteryLevel: Int { intValue(values["caseBatteryLevel"]) ?? -1 }
163
- var caseCharging: Bool { boolValue(values, "caseCharging") ?? false }
164
- var caseOpen: Bool { boolValue(values, "caseOpen") ?? true }
165
- var caseRemoved: Bool { boolValue(values, "caseRemoved") ?? true }
166
- var headUp: Bool { boolValue(values, "headUp") ?? false }
167
- var controllerConnected: Bool { boolValue(values, "controllerConnected") ?? false }
168
- var controllerFullyBooted: Bool { boolValue(values, "controllerFullyBooted") ?? false }
169
- var controllerMacAddress: String { stringValue(values, "controllerMacAddress") ?? "" }
170
- var controllerBatteryLevel: Int { intValue(values["controllerBatteryLevel"]) ?? -1 }
171
- var controllerSignalStrength: Int { intValue(values["controllerSignalStrength"]) ?? -1 }
172
- var ringSignalStrength: Int { intValue(values["ringSignalStrength"]) ?? -1 }
173
-
174
- var description: String {
175
- values.description
176
- }
177
-
178
- static func dictionary(from values: [String: Any]) -> [String: Any] {
179
- var dictionary = values
180
- dictionary["connection"] = GlassesConnectionState(stringValue(values, "connectionState")).statusValues(
181
- connected: boolValue(values, "connected") ?? false,
182
- fullyBooted: boolValue(values, "fullyBooted") ?? false
183
- )
184
- dictionary.removeValue(forKey: "connected")
185
- dictionary.removeValue(forKey: "fullyBooted")
186
- dictionary.removeValue(forKey: "connectionState")
187
- dictionary["wifi"] = (WifiStatus.fromStoreValues(values) ?? .disconnected).values
188
- dictionary["hotspot"] = (HotspotStatus.fromStoreValues(values) ?? .disabled).values
189
- dictionary.removeValue(forKey: "wifiConnected")
190
- dictionary.removeValue(forKey: "wifiSsid")
191
- dictionary.removeValue(forKey: "wifiLocalIp")
192
- dictionary.removeValue(forKey: "hotspotEnabled")
193
- dictionary.removeValue(forKey: "hotspotSsid")
194
- dictionary.removeValue(forKey: "hotspotPassword")
195
- dictionary.removeValue(forKey: "hotspotGatewayIp")
196
- return dictionary
197
- }
198
-
199
- static func updateDictionary(from values: [String: Any]) -> [String: Any] {
200
- var dictionary = values
201
- if hasAnyKey(values, "connection", "connected", "fullyBooted", "connectionState") {
202
- dictionary["connection"] = (values["connection"] as? [String: Any])
203
- ?? GlassesConnectionState(stringValue(values, "connectionState")).statusValues(
204
- connected: boolValue(values, "connected") ?? false,
205
- fullyBooted: boolValue(values, "fullyBooted") ?? false
206
- )
207
- dictionary.removeValue(forKey: "connected")
208
- dictionary.removeValue(forKey: "fullyBooted")
209
- dictionary.removeValue(forKey: "connectionState")
210
- }
211
- if hasAnyKey(values, "wifi", "wifiConnected", "wifiSsid", "wifiLocalIp") {
212
- let wifi = (values["wifi"] as? [String: Any]).flatMap(WifiStatus.init(values:))
213
- ?? WifiStatus.fromStoreValues(values)
214
- if let wifi {
215
- dictionary["wifi"] = wifi.values
216
- }
217
- dictionary.removeValue(forKey: "wifiConnected")
218
- dictionary.removeValue(forKey: "wifiSsid")
219
- dictionary.removeValue(forKey: "wifiLocalIp")
220
- }
221
- if hasAnyKey(values, "hotspot") {
222
- if let hotspot = (values["hotspot"] as? [String: Any]).flatMap(HotspotStatus.init(values:)) {
223
- dictionary["hotspot"] = hotspot.values
224
- }
225
- } else if hasAnyKey(values, "hotspotEnabled", "hotspotSsid", "hotspotPassword", "hotspotGatewayIp") {
226
- if let hotspot = HotspotStatus.fromStoreValues(values) {
227
- dictionary["hotspot"] = hotspot.values
228
- }
229
- dictionary.removeValue(forKey: "hotspotEnabled")
230
- dictionary.removeValue(forKey: "hotspotSsid")
231
- dictionary.removeValue(forKey: "hotspotPassword")
232
- dictionary.removeValue(forKey: "hotspotGatewayIp")
233
- }
234
- return dictionary
235
- }
236
- }
237
-
238
- struct BluetoothStatus: CustomStringConvertible {
239
- let values: [String: Any]
240
-
241
- init(values: [String: Any]) {
242
- self.values = Self.normalized(values)
243
- }
244
-
245
- private static func normalized(_ values: [String: Any]) -> [String: Any] {
246
- var normalizedValues = values
247
- if let searchResults = values["searchResults"] as? [[String: Any]] {
248
- normalizedValues["searchResults"] = searchResults.compactMap { Device(values: $0)?.dictionary }
249
- }
250
- if let galleryMode = boolValue(values, "gallery_mode") {
251
- normalizedValues["galleryModeEnabled"] = galleryMode
252
- normalizedValues.removeValue(forKey: "gallery_mode")
253
- }
254
- return normalizedValues
255
- }
256
-
257
- func applying(_ update: BluetoothStatusUpdate) -> BluetoothStatus {
258
- BluetoothStatus(values: values.merging(update.values) { _, new in new })
259
- }
260
-
261
- func withDefaultDevice(_ device: Device?) -> BluetoothStatus {
262
- applying(BluetoothStatusUpdate(values: [
263
- "default_wearable": device?.model.deviceType ?? "",
264
- "device_name": device?.name ?? "",
265
- "device_address": device?.identifier ?? "",
266
- ]))
267
- }
268
-
269
- var searching: Bool { boolValue(values, "searching") ?? false }
270
- var searchingController: Bool { boolValue(values, "searchingController") ?? false }
271
- var systemMicUnavailable: Bool { boolValue(values, "systemMicUnavailable") ?? false }
272
- var micEnabled: Bool { boolValue(values, "micEnabled") ?? false }
273
- var currentMic: String { stringValue(values, "currentMic") ?? "" }
274
- var micRanking: [String] { stringListValue(values, "micRanking") }
275
- /// Nearby glasses in stable discovery order. Existing entries keep their array position as
276
- /// details refresh; new glasses append at the end, and removals should not reorder remaining entries.
277
- var searchResults: [Device] {
278
- dictionaryListValue(values, "searchResults").compactMap(Device.init(values:))
279
- }
280
- var wifiScanResults: [WifiScanResult] {
281
- dictionaryListValue(values, "wifiScanResults").map(WifiScanResult.init(values:))
282
- }
283
- var lastLog: [String] { stringListValue(values, "lastLog") }
284
- var otherBtConnected: Bool { boolValue(values, "otherBtConnected") ?? false }
285
- var defaultWearable: String { stringValue(values, "default_wearable") ?? "" }
286
- var pendingWearable: String { stringValue(values, "pending_wearable") ?? "" }
287
- var deviceName: String { stringValue(values, "device_name") ?? "" }
288
- var deviceAddress: String { stringValue(values, "device_address") ?? "" }
289
- var defaultController: String { stringValue(values, "default_controller") ?? "" }
290
- var pendingController: String { stringValue(values, "pending_controller") ?? "" }
291
- var controllerDeviceName: String { stringValue(values, "controller_device_name") ?? "" }
292
- var screenDisabled: Bool { boolValue(values, "screen_disabled") ?? false }
293
- var preferredMic: String { stringValue(values, "preferred_mic") ?? "auto" }
294
- var sensingEnabled: Bool { boolValue(values, "sensing_enabled") ?? true }
295
- var powerSavingMode: Bool { boolValue(values, "power_saving_mode") ?? false }
296
- var brightness: Int { intValue(values["brightness"]) ?? 50 }
297
- var autoBrightness: Bool { boolValue(values, "auto_brightness") ?? true }
298
- var dashboardHeight: Int { intValue(values["dashboard_height"]) ?? 4 }
299
- var dashboardDepth: Int { intValue(values["dashboard_depth"]) ?? 2 }
300
- var headUpAngle: Int { intValue(values["head_up_angle"]) ?? 30 }
301
- var contextualDashboard: Bool { boolValue(values, "contextual_dashboard") ?? true }
302
- var galleryModeEnabled: Bool { boolValue(values, "gallery_mode") ?? boolValue(values, "galleryModeEnabled") ?? true }
303
- var buttonPhotoSize: ButtonPhotoSize {
304
- ButtonPhotoSize(rawValue: stringValue(values, "button_photo_size") ?? "") ?? .medium
305
- }
306
- var buttonCameraLed: Bool { boolValue(values, "button_camera_led") ?? true }
307
- var buttonMaxRecordingTime: Int { intValue(values["button_max_recording_time"]) ?? 10 }
308
- var buttonVideoWidth: Int { intValue(values["button_video_width"]) ?? 1280 }
309
- var buttonVideoHeight: Int { intValue(values["button_video_height"]) ?? 720 }
310
- var buttonVideoFrameRate: Int { intValue(values["button_video_fps"]) ?? 30 }
311
- var shouldSendPcm: Bool { boolValue(values, "should_send_pcm") ?? false }
312
- var shouldSendLc3: Bool { boolValue(values, "should_send_lc3") ?? false }
313
- var shouldSendTranscript: Bool { boolValue(values, "should_send_transcript") ?? false }
314
- var offlineCaptionsRunning: Bool { boolValue(values, "offline_captions_running") ?? false }
315
- var localSttFallbackActive: Bool { boolValue(values, "local_stt_fallback_active") ?? false }
316
- var shouldSendBootingMessage: Bool { boolValue(values, "shouldSendBootingMessage") ?? true }
317
-
318
- var defaultDevice: Device? {
319
- guard !defaultWearable.isEmpty else { return nil }
320
- return Device(
321
- model: DeviceModel.fromDeviceType(defaultWearable),
322
- name: deviceName,
323
- identifier: deviceAddress.isEmpty ? nil : deviceAddress
324
- )
325
- }
326
-
327
- var description: String {
328
- values.description
329
- }
330
- }
331
-
332
- struct GlassesStatusUpdate: CustomStringConvertible {
333
- let values: [String: Any]
334
-
335
- init(values: [String: Any]) {
336
- self.values = values
337
- }
338
-
339
- var fullyBooted: Bool? { optionalBoolValue(values, "fullyBooted") }
340
- var connected: Bool? { optionalBoolValue(values, "connected") }
341
- var micEnabled: Bool? { optionalBoolValue(values, "micEnabled") }
342
- var voiceActivityDetectionEnabled: Bool? { optionalBoolValue(values, "voiceActivityDetectionEnabled") }
343
- var connectionState: GlassesConnectionState? {
344
- GlassesConnectionState.fromValue(optionalStringValue(values, "connectionState"))
345
- }
346
- var bluetoothClassicConnected: Bool? { optionalBoolValue(values, "bluetoothClassicConnected") }
347
- var signalStrength: Int? { optionalIntValue(values, "signalStrength") }
348
- var signalStrengthUpdatedAt: Int? { optionalIntValue(values, "signalStrengthUpdatedAt") }
349
- var deviceModel: String? { optionalStringValue(values, "deviceModel") }
350
- var androidVersion: String? { optionalStringValue(values, "androidVersion") }
351
- var firmwareVersion: String? { optionalStringValue(values, "firmwareVersion") }
352
- var besFirmwareVersion: String? { optionalStringValue(values, "besFirmwareVersion") }
353
- var mtkFirmwareVersion: String? { optionalStringValue(values, "mtkFirmwareVersion") }
354
- var bluetoothMacAddress: String? { optionalStringValue(values, "bluetoothMacAddress") }
355
- var leftMacAddress: String? { optionalStringValue(values, "leftMacAddress") }
356
- var rightMacAddress: String? { optionalStringValue(values, "rightMacAddress") }
357
- var macAddress: String? { optionalStringValue(values, "macAddress") }
358
- var buildNumber: String? { optionalStringValue(values, "buildNumber") }
359
- var otaVersionUrl: String? { optionalStringValue(values, "otaVersionUrl") }
360
- var appVersion: String? { optionalStringValue(values, "appVersion") }
361
- var bluetoothName: String? { optionalStringValue(values, "bluetoothName") }
362
- var serialNumber: String? { optionalStringValue(values, "serialNumber") }
363
- var style: String? { optionalStringValue(values, "style") }
364
- var color: String? { optionalStringValue(values, "color") }
365
- var wifi: WifiStatus? {
366
- if let wifi = values["wifi"] as? [String: Any] {
367
- return WifiStatus(values: wifi)
368
- }
369
- if hasAnyKey(values, "wifiConnected", "wifiSsid", "wifiLocalIp") {
370
- return WifiStatus.fromStoreValues(values)
371
- }
372
- return nil
373
- }
374
- var hotspot: HotspotStatus? {
375
- if let hotspot = values["hotspot"] as? [String: Any] {
376
- return HotspotStatus(values: hotspot)
377
- }
378
- if hasAnyKey(values, "hotspotEnabled", "hotspotSsid", "hotspotPassword", "hotspotGatewayIp") {
379
- return HotspotStatus.fromStoreValues(values)
380
- }
381
- return nil
382
- }
383
- var dictionary: [String: Any] { GlassesStatus.updateDictionary(from: values) }
384
- var batteryLevel: Int? { optionalIntValue(values, "batteryLevel") }
385
- var charging: Bool? { optionalBoolValue(values, "charging") }
386
- var caseBatteryLevel: Int? { optionalIntValue(values, "caseBatteryLevel") }
387
- var caseCharging: Bool? { optionalBoolValue(values, "caseCharging") }
388
- var caseOpen: Bool? { optionalBoolValue(values, "caseOpen") }
389
- var caseRemoved: Bool? { optionalBoolValue(values, "caseRemoved") }
390
- var headUp: Bool? { optionalBoolValue(values, "headUp") }
391
- var controllerConnected: Bool? { optionalBoolValue(values, "controllerConnected") }
392
- var controllerFullyBooted: Bool? { optionalBoolValue(values, "controllerFullyBooted") }
393
- var controllerMacAddress: String? { optionalStringValue(values, "controllerMacAddress") }
394
- var controllerBatteryLevel: Int? { optionalIntValue(values, "controllerBatteryLevel") }
395
- var controllerSignalStrength: Int? { optionalIntValue(values, "controllerSignalStrength") }
396
- var ringSignalStrength: Int? { optionalIntValue(values, "ringSignalStrength") }
397
-
398
- var description: String {
399
- values.description
400
- }
401
- }
402
-
403
- struct BluetoothStatusUpdate: CustomStringConvertible {
404
- let values: [String: Any]
405
-
406
- init(values: [String: Any]) {
407
- var normalizedValues = values
408
- if let searchResults = values["searchResults"] as? [[String: Any]] {
409
- normalizedValues["searchResults"] = searchResults.compactMap { Device(values: $0)?.dictionary }
410
- }
411
- if let galleryMode = optionalBoolValue(values, "gallery_mode") {
412
- normalizedValues["galleryModeEnabled"] = galleryMode
413
- normalizedValues.removeValue(forKey: "gallery_mode")
414
- }
415
- self.values = normalizedValues
416
- }
417
-
418
- var searching: Bool? { optionalBoolValue(values, "searching") }
419
- var searchingController: Bool? { optionalBoolValue(values, "searchingController") }
420
- var systemMicUnavailable: Bool? { optionalBoolValue(values, "systemMicUnavailable") }
421
- var micEnabled: Bool? { optionalBoolValue(values, "micEnabled") }
422
- var currentMic: String? { optionalStringValue(values, "currentMic") }
423
- var micRanking: [String]? { optionalStringListValue(values, "micRanking") }
424
- /// Nearby glasses in stable discovery order when included in an update. Existing entries keep their
425
- /// array position as details refresh; new glasses append at the end, and removals should not reorder
426
- /// remaining entries.
427
- var searchResults: [Device]? {
428
- optionalDictionaryListValue(values, "searchResults")?.compactMap(Device.init(values:))
429
- }
430
- var wifiScanResults: [WifiScanResult]? {
431
- optionalDictionaryListValue(values, "wifiScanResults")?.map(WifiScanResult.init(values:))
432
- }
433
- var lastLog: [String]? { optionalStringListValue(values, "lastLog") }
434
- var otherBtConnected: Bool? { optionalBoolValue(values, "otherBtConnected") }
435
- var defaultWearable: String? { optionalStringValue(values, "default_wearable") }
436
- var pendingWearable: String? { optionalStringValue(values, "pending_wearable") }
437
- var deviceName: String? { optionalStringValue(values, "device_name") }
438
- var deviceAddress: String? { optionalStringValue(values, "device_address") }
439
- var defaultController: String? { optionalStringValue(values, "default_controller") }
440
- var pendingController: String? { optionalStringValue(values, "pending_controller") }
441
- var controllerDeviceName: String? { optionalStringValue(values, "controller_device_name") }
442
- var screenDisabled: Bool? { optionalBoolValue(values, "screen_disabled") }
443
- var preferredMic: String? { optionalStringValue(values, "preferred_mic") }
444
- var sensingEnabled: Bool? { optionalBoolValue(values, "sensing_enabled") }
445
- var powerSavingMode: Bool? { optionalBoolValue(values, "power_saving_mode") }
446
- var brightness: Int? { optionalIntValue(values, "brightness") }
447
- var autoBrightness: Bool? { optionalBoolValue(values, "auto_brightness") }
448
- var dashboardHeight: Int? { optionalIntValue(values, "dashboard_height") }
449
- var dashboardDepth: Int? { optionalIntValue(values, "dashboard_depth") }
450
- var headUpAngle: Int? { optionalIntValue(values, "head_up_angle") }
451
- var contextualDashboard: Bool? { optionalBoolValue(values, "contextual_dashboard") }
452
- var galleryModeEnabled: Bool? { optionalBoolValue(values, "gallery_mode") ?? optionalBoolValue(values, "galleryModeEnabled") }
453
- var buttonPhotoSize: ButtonPhotoSize? {
454
- optionalStringValue(values, "button_photo_size").flatMap(ButtonPhotoSize.init(rawValue:))
455
- }
456
- var buttonCameraLed: Bool? { optionalBoolValue(values, "button_camera_led") }
457
- var buttonMaxRecordingTime: Int? { optionalIntValue(values, "button_max_recording_time") }
458
- var buttonVideoWidth: Int? { optionalIntValue(values, "button_video_width") }
459
- var buttonVideoHeight: Int? { optionalIntValue(values, "button_video_height") }
460
- var buttonVideoFrameRate: Int? { optionalIntValue(values, "button_video_fps") }
461
- var shouldSendPcm: Bool? { optionalBoolValue(values, "should_send_pcm") }
462
- var shouldSendLc3: Bool? { optionalBoolValue(values, "should_send_lc3") }
463
- var shouldSendTranscript: Bool? { optionalBoolValue(values, "should_send_transcript") }
464
- var offlineCaptionsRunning: Bool? { optionalBoolValue(values, "offline_captions_running") }
465
- var localSttFallbackActive: Bool? { optionalBoolValue(values, "local_stt_fallback_active") }
466
- var shouldSendBootingMessage: Bool? { optionalBoolValue(values, "shouldSendBootingMessage") }
467
-
468
- var description: String {
469
- values.description
470
- }
471
- }
File without changes