@mentra/bluetooth-sdk 0.1.5 → 0.1.7

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 (92) hide show
  1. package/README.md +45 -27
  2. package/android/build.gradle +2 -9
  3. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +53 -36
  4. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +22 -9
  5. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +25 -36
  6. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +9 -4
  7. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +68 -56
  8. package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +125 -0
  9. package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +209 -0
  10. package/android/src/main/java/com/mentra/bluetoothsdk/connection/ConnectionModels.kt +78 -0
  11. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +4 -3
  12. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +2 -1
  13. package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +72 -0
  14. package/android/src/main/java/com/mentra/bluetoothsdk/internal/MapParsing.kt +132 -0
  15. package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +39 -0
  16. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +1 -1
  17. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +2 -1
  18. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +1 -1
  19. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +168 -10
  20. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -1
  21. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +7 -3
  22. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +3 -1
  23. package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +607 -0
  24. package/android/src/main/java/com/mentra/bluetoothsdk/status/RuntimeState.kt +201 -0
  25. package/android/src/main/java/com/mentra/bluetoothsdk/status/WifiHotspotStatus.kt +173 -0
  26. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +348 -0
  27. package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +1 -3
  28. package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +75 -0
  29. package/build/BluetoothSdk.types.d.ts +46 -20
  30. package/build/BluetoothSdk.types.d.ts.map +1 -1
  31. package/build/BluetoothSdk.types.js +1 -0
  32. package/build/BluetoothSdk.types.js.map +1 -1
  33. package/build/_private/BluetoothSdkModule.d.ts +6 -5
  34. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  35. package/build/_private/BluetoothSdkModule.js +40 -5
  36. package/build/_private/BluetoothSdkModule.js.map +1 -1
  37. package/build/index.d.ts +1 -1
  38. package/build/index.d.ts.map +1 -1
  39. package/build/index.js +4 -2
  40. package/build/index.js.map +1 -1
  41. package/build/react/useMentraBluetooth.d.ts +5 -3
  42. package/build/react/useMentraBluetooth.d.ts.map +1 -1
  43. package/build/react/useMentraBluetooth.js +15 -4
  44. package/build/react/useMentraBluetooth.js.map +1 -1
  45. package/ios/BluetoothSdkModule.swift +45 -28
  46. package/ios/MentraBluetoothSDK.podspec +0 -3
  47. package/ios/Source/Audio/AudioModels.swift +159 -0
  48. package/ios/Source/Bridge.swift +22 -11
  49. package/ios/Source/Camera/CameraModels.swift +248 -0
  50. package/ios/Source/Connection/ScanSession.swift +27 -0
  51. package/ios/Source/DeviceManager.swift +20 -97
  52. package/ios/Source/DeviceStore.swift +13 -4
  53. package/ios/Source/Errors/BluetoothError.swift +19 -0
  54. package/ios/Source/Events/BluetoothEvents.swift +149 -0
  55. package/ios/Source/Internal/BluetoothAvailability.swift +58 -0
  56. package/ios/Source/Internal/ValueParsing.swift +90 -0
  57. package/ios/Source/MentraBluetoothSDK.swift +50 -2150
  58. package/ios/Source/Requests/DisplayRequests.swift +58 -0
  59. package/ios/Source/Status/DeviceStatus.swift +471 -0
  60. package/ios/Source/Status/RuntimeState.swift +359 -0
  61. package/ios/Source/Status/WifiHotspotStatus.swift +326 -0
  62. package/ios/Source/Streaming/StreamModels.swift +436 -0
  63. package/ios/Source/Types/DeviceModels.swift +134 -0
  64. package/ios/Source/controllers/ControllerManager.swift +3 -3
  65. package/ios/Source/controllers/R1.swift +1 -1
  66. package/ios/Source/sgcs/G1.swift +1 -1
  67. package/ios/Source/sgcs/G2.swift +99 -39
  68. package/ios/Source/sgcs/Mach1.swift +1 -1
  69. package/ios/Source/sgcs/MentraLive.swift +109 -12
  70. package/ios/Source/sgcs/MentraNex.swift +1 -1
  71. package/ios/Source/sgcs/SGCManager.swift +11 -3
  72. package/ios/Source/sgcs/Simulated.swift +2 -2
  73. package/ios/Source/stt/SherpaOnnxTranscriber.swift +1 -4
  74. package/package.json +1 -1
  75. package/src/BluetoothSdk.types.ts +48 -28
  76. package/src/_private/BluetoothSdkModule.ts +47 -21
  77. package/src/index.ts +6 -4
  78. package/src/react/useMentraBluetooth.ts +19 -7
  79. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +0 -1810
  80. package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +0 -221
  81. package/ios/Packages/VAD/Common/VAD/VADQuality.swift +0 -41
  82. package/ios/Packages/VAD/Common/VAD/VADState.swift +0 -26
  83. package/ios/Packages/VAD/Common/VAD/VADStrategy.swift +0 -29
  84. package/ios/Packages/VAD/Common/VAD/VADType.swift +0 -14
  85. package/ios/Packages/VAD/Data/Configuration.swift +0 -68
  86. package/ios/Packages/VAD/Data/FrameSize.swift +0 -39
  87. package/ios/Packages/VAD/Data/Record.swift +0 -13
  88. package/ios/Packages/VAD/Data/Result.swift +0 -22
  89. package/ios/Packages/VAD/Data/SampleRate.swift +0 -48
  90. package/ios/Packages/VAD/Silero/Model/silero_vad.onnx +0 -0
  91. package/ios/Packages/VAD/Silero/SileroVAD.swift +0 -284
  92. package/ios/Packages/VAD/Silero/SileroVADStrategy.swift +0 -64
@@ -0,0 +1,607 @@
1
+ package com.mentra.bluetoothsdk
2
+
3
+ data class WifiScanResult(
4
+ val ssid: String,
5
+ val requiresPassword: Boolean,
6
+ val signalStrength: Int,
7
+ val frequency: Int? = null,
8
+ ) {
9
+ internal fun toMap(): Map<String, Any> {
10
+ val map =
11
+ mutableMapOf<String, Any>(
12
+ "ssid" to ssid,
13
+ "requiresPassword" to requiresPassword,
14
+ "signalStrength" to signalStrength,
15
+ )
16
+ frequency?.let { map["frequency"] = it }
17
+ return map
18
+ }
19
+
20
+ companion object {
21
+ internal fun fromMap(values: Map<String, Any>): WifiScanResult =
22
+ WifiScanResult(
23
+ ssid = stringValue(values, "ssid") ?: "",
24
+ requiresPassword = boolValue(values, "requiresPassword") ?: false,
25
+ signalStrength = numberValue(values, "signalStrength") ?: -1,
26
+ frequency = numberValue(values, "frequency"),
27
+ )
28
+ }
29
+ }
30
+
31
+ internal data class GlassesStatus(
32
+ val fullyBooted: Boolean,
33
+ val connected: Boolean,
34
+ val micEnabled: Boolean,
35
+ val voiceActivityDetectionEnabled: Boolean,
36
+ val connectionState: GlassesConnectionState,
37
+ val bluetoothClassicConnected: Boolean,
38
+ val signalStrength: Int,
39
+ val signalStrengthUpdatedAt: Long,
40
+ val deviceModel: String,
41
+ val androidVersion: String,
42
+ val firmwareVersion: String,
43
+ val besFirmwareVersion: String,
44
+ val mtkFirmwareVersion: String,
45
+ val bluetoothMacAddress: String,
46
+ val leftMacAddress: String,
47
+ val rightMacAddress: String,
48
+ val macAddress: String,
49
+ val buildNumber: String,
50
+ val otaVersionUrl: String,
51
+ val appVersion: String,
52
+ val bluetoothName: String,
53
+ val serialNumber: String,
54
+ val style: String,
55
+ val color: String,
56
+ val wifi: WifiStatus,
57
+ val batteryLevel: Int,
58
+ val charging: Boolean,
59
+ val caseBatteryLevel: Int,
60
+ val caseCharging: Boolean,
61
+ val caseOpen: Boolean,
62
+ val caseRemoved: Boolean,
63
+ val hotspot: HotspotStatus,
64
+ val headUp: Boolean,
65
+ val controllerConnected: Boolean,
66
+ val controllerFullyBooted: Boolean,
67
+ val controllerMacAddress: String,
68
+ val controllerBatteryLevel: Int,
69
+ val controllerSignalStrength: Int,
70
+ val ringSignalStrength: Int,
71
+ ) {
72
+ internal fun toMap(): Map<String, Any> =
73
+ mapOf(
74
+ "connection" to connectionState.toStatusMap(connected, fullyBooted),
75
+ "micEnabled" to micEnabled,
76
+ "voiceActivityDetectionEnabled" to voiceActivityDetectionEnabled,
77
+ "bluetoothClassicConnected" to bluetoothClassicConnected,
78
+ "signalStrength" to signalStrength,
79
+ "signalStrengthUpdatedAt" to signalStrengthUpdatedAt,
80
+ "deviceModel" to deviceModel,
81
+ "androidVersion" to androidVersion,
82
+ "firmwareVersion" to firmwareVersion,
83
+ "besFirmwareVersion" to besFirmwareVersion,
84
+ "mtkFirmwareVersion" to mtkFirmwareVersion,
85
+ "bluetoothMacAddress" to bluetoothMacAddress,
86
+ "leftMacAddress" to leftMacAddress,
87
+ "rightMacAddress" to rightMacAddress,
88
+ "macAddress" to macAddress,
89
+ "buildNumber" to buildNumber,
90
+ "otaVersionUrl" to otaVersionUrl,
91
+ "appVersion" to appVersion,
92
+ "bluetoothName" to bluetoothName,
93
+ "serialNumber" to serialNumber,
94
+ "style" to style,
95
+ "color" to color,
96
+ "wifi" to wifi.toMap(),
97
+ "batteryLevel" to batteryLevel,
98
+ "charging" to charging,
99
+ "caseBatteryLevel" to caseBatteryLevel,
100
+ "caseCharging" to caseCharging,
101
+ "caseOpen" to caseOpen,
102
+ "caseRemoved" to caseRemoved,
103
+ "hotspot" to hotspot.toMap(),
104
+ "headUp" to headUp,
105
+ "controllerConnected" to controllerConnected,
106
+ "controllerFullyBooted" to controllerFullyBooted,
107
+ "controllerMacAddress" to controllerMacAddress,
108
+ "controllerBatteryLevel" to controllerBatteryLevel,
109
+ "controllerSignalStrength" to controllerSignalStrength,
110
+ "ringSignalStrength" to ringSignalStrength,
111
+ )
112
+
113
+ companion object {
114
+ internal fun fromMap(values: Map<String, Any>): GlassesStatus =
115
+ GlassesStatus(
116
+ fullyBooted = boolValue(values, "fullyBooted") ?: false,
117
+ connected = boolValue(values, "connected") ?: false,
118
+ micEnabled = boolValue(values, "micEnabled") ?: false,
119
+ voiceActivityDetectionEnabled = boolValue(values, "voiceActivityDetectionEnabled") ?: true,
120
+ connectionState = GlassesConnectionState.fromValue(stringValue(values, "connectionState")),
121
+ bluetoothClassicConnected = boolValue(values, "bluetoothClassicConnected") ?: false,
122
+ signalStrength = numberValue(values, "signalStrength") ?: -1,
123
+ signalStrengthUpdatedAt = longValue(values, "signalStrengthUpdatedAt") ?: 0L,
124
+ deviceModel = stringValue(values, "deviceModel") ?: "",
125
+ androidVersion = stringValue(values, "androidVersion") ?: "",
126
+ firmwareVersion = stringValue(values, "firmwareVersion") ?: "",
127
+ besFirmwareVersion = stringValue(values, "besFirmwareVersion") ?: "",
128
+ mtkFirmwareVersion = stringValue(values, "mtkFirmwareVersion") ?: "",
129
+ bluetoothMacAddress = stringValue(values, "bluetoothMacAddress") ?: "",
130
+ leftMacAddress = stringValue(values, "leftMacAddress") ?: "",
131
+ rightMacAddress = stringValue(values, "rightMacAddress") ?: "",
132
+ macAddress = stringValue(values, "macAddress") ?: "",
133
+ buildNumber = stringValue(values, "buildNumber") ?: "",
134
+ otaVersionUrl = stringValue(values, "otaVersionUrl") ?: "",
135
+ appVersion = stringValue(values, "appVersion") ?: "",
136
+ bluetoothName = stringValue(values, "bluetoothName") ?: "",
137
+ serialNumber = stringValue(values, "serialNumber") ?: "",
138
+ style = stringValue(values, "style") ?: "",
139
+ color = stringValue(values, "color") ?: "",
140
+ wifi = WifiStatus.fromStoreMap(values) ?: WifiStatus.Disconnected,
141
+ batteryLevel = numberValue(values, "batteryLevel") ?: -1,
142
+ charging = boolValue(values, "charging") ?: false,
143
+ caseBatteryLevel = numberValue(values, "caseBatteryLevel") ?: -1,
144
+ caseCharging = boolValue(values, "caseCharging") ?: false,
145
+ caseOpen = boolValue(values, "caseOpen") ?: true,
146
+ caseRemoved = boolValue(values, "caseRemoved") ?: true,
147
+ hotspot = HotspotStatus.fromStoreMap(values) ?: HotspotStatus.Disabled,
148
+ headUp = boolValue(values, "headUp") ?: false,
149
+ controllerConnected = boolValue(values, "controllerConnected") ?: false,
150
+ controllerFullyBooted = boolValue(values, "controllerFullyBooted") ?: false,
151
+ controllerMacAddress = stringValue(values, "controllerMacAddress") ?: "",
152
+ controllerBatteryLevel = numberValue(values, "controllerBatteryLevel") ?: -1,
153
+ controllerSignalStrength = numberValue(values, "controllerSignalStrength") ?: -1,
154
+ ringSignalStrength = numberValue(values, "ringSignalStrength") ?: -1,
155
+ )
156
+ }
157
+ }
158
+
159
+ internal data class BluetoothStatus(
160
+ val searching: Boolean,
161
+ val searchingController: Boolean,
162
+ val systemMicUnavailable: Boolean,
163
+ val micEnabled: Boolean,
164
+ val currentMic: String,
165
+ val micRanking: List<String>,
166
+ /**
167
+ * Nearby glasses in stable discovery order. Existing entries keep their array position as
168
+ * details refresh; new glasses append at the end, and removals should not reorder remaining
169
+ * entries.
170
+ */
171
+ val searchResults: List<Device>,
172
+ val wifiScanResults: List<WifiScanResult>,
173
+ val lastLog: List<String>,
174
+ val otherBtConnected: Boolean,
175
+ val defaultWearable: String,
176
+ val pendingWearable: String,
177
+ val deviceName: String,
178
+ val deviceAddress: String,
179
+ val defaultController: String,
180
+ val pendingController: String,
181
+ val controllerDeviceName: String,
182
+ val screenDisabled: Boolean,
183
+ val preferredMic: String,
184
+ val sensingEnabled: Boolean,
185
+ val powerSavingMode: Boolean,
186
+ val brightness: Int,
187
+ val autoBrightness: Boolean,
188
+ val dashboardHeight: Int,
189
+ val dashboardDepth: Int,
190
+ val headUpAngle: Int,
191
+ val contextualDashboard: Boolean,
192
+ val galleryModeEnabled: Boolean,
193
+ val buttonPhotoSize: ButtonPhotoSize,
194
+ val buttonCameraLed: Boolean,
195
+ val buttonMaxRecordingTime: Int,
196
+ val buttonVideoWidth: Int,
197
+ val buttonVideoHeight: Int,
198
+ val buttonVideoFrameRate: Int,
199
+ val shouldSendPcm: Boolean,
200
+ val shouldSendLc3: Boolean,
201
+ val shouldSendTranscript: Boolean,
202
+ val offlineCaptionsRunning: Boolean,
203
+ val localSttFallbackActive: Boolean,
204
+ val shouldSendBootingMessage: Boolean,
205
+ ) {
206
+ val defaultDevice: Device?
207
+ get() =
208
+ defaultWearable.takeIf { it.isNotBlank() }?.let {
209
+ Device(
210
+ model = DeviceModel.fromDeviceType(it),
211
+ name = deviceName,
212
+ address = deviceAddress.takeIf(String::isNotBlank),
213
+ )
214
+ }
215
+
216
+ internal fun toMap(): Map<String, Any> =
217
+ mapOf(
218
+ "searching" to searching,
219
+ "searchingController" to searchingController,
220
+ "systemMicUnavailable" to systemMicUnavailable,
221
+ "micEnabled" to micEnabled,
222
+ "currentMic" to currentMic,
223
+ "micRanking" to micRanking,
224
+ "searchResults" to searchResults.map(Device::toMap),
225
+ "wifiScanResults" to wifiScanResults.map { it.toMap() },
226
+ "lastLog" to lastLog,
227
+ "otherBtConnected" to otherBtConnected,
228
+ "default_wearable" to defaultWearable,
229
+ "pending_wearable" to pendingWearable,
230
+ "device_name" to deviceName,
231
+ "device_address" to deviceAddress,
232
+ "default_controller" to defaultController,
233
+ "pending_controller" to pendingController,
234
+ "controller_device_name" to controllerDeviceName,
235
+ "screen_disabled" to screenDisabled,
236
+ "preferred_mic" to preferredMic,
237
+ "sensing_enabled" to sensingEnabled,
238
+ "power_saving_mode" to powerSavingMode,
239
+ "brightness" to brightness,
240
+ "auto_brightness" to autoBrightness,
241
+ "dashboard_height" to dashboardHeight,
242
+ "dashboard_depth" to dashboardDepth,
243
+ "head_up_angle" to headUpAngle,
244
+ "contextual_dashboard" to contextualDashboard,
245
+ "galleryModeEnabled" to galleryModeEnabled,
246
+ "button_photo_size" to buttonPhotoSize.value,
247
+ "button_camera_led" to buttonCameraLed,
248
+ "button_max_recording_time" to buttonMaxRecordingTime,
249
+ "button_video_width" to buttonVideoWidth,
250
+ "button_video_height" to buttonVideoHeight,
251
+ "button_video_fps" to buttonVideoFrameRate,
252
+ "should_send_pcm" to shouldSendPcm,
253
+ "should_send_lc3" to shouldSendLc3,
254
+ "should_send_transcript" to shouldSendTranscript,
255
+ "offline_captions_running" to offlineCaptionsRunning,
256
+ "local_stt_fallback_active" to localSttFallbackActive,
257
+ "shouldSendBootingMessage" to shouldSendBootingMessage,
258
+ )
259
+
260
+ companion object {
261
+ internal fun fromMap(values: Map<String, Any>): BluetoothStatus =
262
+ BluetoothStatus(
263
+ searching = boolValue(values, "searching") ?: false,
264
+ searchingController = boolValue(values, "searchingController") ?: false,
265
+ systemMicUnavailable = boolValue(values, "systemMicUnavailable") ?: false,
266
+ micEnabled = boolValue(values, "micEnabled") ?: false,
267
+ currentMic = stringValue(values, "currentMic") ?: "",
268
+ micRanking = stringListValue(values, "micRanking"),
269
+ searchResults =
270
+ mapListValue(values, "searchResults").mapNotNull(Device::fromMap),
271
+ wifiScanResults =
272
+ mapListValue(values, "wifiScanResults").map(WifiScanResult::fromMap),
273
+ lastLog = stringListValue(values, "lastLog"),
274
+ otherBtConnected = boolValue(values, "otherBtConnected") ?: false,
275
+ defaultWearable = stringValue(values, "default_wearable") ?: "",
276
+ pendingWearable = stringValue(values, "pending_wearable") ?: "",
277
+ deviceName = stringValue(values, "device_name") ?: "",
278
+ deviceAddress = stringValue(values, "device_address") ?: "",
279
+ defaultController = stringValue(values, "default_controller") ?: "",
280
+ pendingController = stringValue(values, "pending_controller") ?: "",
281
+ controllerDeviceName = stringValue(values, "controller_device_name") ?: "",
282
+ screenDisabled = boolValue(values, "screen_disabled") ?: false,
283
+ preferredMic = stringValue(values, "preferred_mic") ?: "auto",
284
+ sensingEnabled = boolValue(values, "sensing_enabled") ?: true,
285
+ powerSavingMode = boolValue(values, "power_saving_mode") ?: false,
286
+ brightness = numberValue(values, "brightness") ?: 50,
287
+ autoBrightness = boolValue(values, "auto_brightness") ?: true,
288
+ dashboardHeight = numberValue(values, "dashboard_height") ?: 4,
289
+ dashboardDepth = numberValue(values, "dashboard_depth") ?: 2,
290
+ headUpAngle = numberValue(values, "head_up_angle") ?: 30,
291
+ contextualDashboard = boolValue(values, "contextual_dashboard") ?: true,
292
+ galleryModeEnabled =
293
+ boolValue(values, "gallery_mode") ?: boolValue(values, "galleryModeEnabled") ?: true,
294
+ buttonPhotoSize = ButtonPhotoSize.fromValue(stringValue(values, "button_photo_size")),
295
+ buttonCameraLed = boolValue(values, "button_camera_led") ?: true,
296
+ buttonMaxRecordingTime = numberValue(values, "button_max_recording_time") ?: 10,
297
+ buttonVideoWidth = numberValue(values, "button_video_width") ?: 1280,
298
+ buttonVideoHeight = numberValue(values, "button_video_height") ?: 720,
299
+ buttonVideoFrameRate = numberValue(values, "button_video_fps") ?: 30,
300
+ shouldSendPcm = boolValue(values, "should_send_pcm") ?: false,
301
+ shouldSendLc3 = boolValue(values, "should_send_lc3") ?: false,
302
+ shouldSendTranscript = boolValue(values, "should_send_transcript") ?: false,
303
+ offlineCaptionsRunning = boolValue(values, "offline_captions_running") ?: false,
304
+ localSttFallbackActive = boolValue(values, "local_stt_fallback_active") ?: false,
305
+ shouldSendBootingMessage = boolValue(values, "shouldSendBootingMessage") ?: true,
306
+ )
307
+ }
308
+ }
309
+
310
+ internal data class GlassesStatusUpdate(
311
+ val fullyBooted: Boolean? = null,
312
+ val connected: Boolean? = null,
313
+ val micEnabled: Boolean? = null,
314
+ val voiceActivityDetectionEnabled: Boolean? = null,
315
+ val connectionState: GlassesConnectionState? = null,
316
+ val bluetoothClassicConnected: Boolean? = null,
317
+ val signalStrength: Int? = null,
318
+ val signalStrengthUpdatedAt: Long? = null,
319
+ val deviceModel: String? = null,
320
+ val androidVersion: String? = null,
321
+ val firmwareVersion: String? = null,
322
+ val besFirmwareVersion: String? = null,
323
+ val mtkFirmwareVersion: String? = null,
324
+ val bluetoothMacAddress: String? = null,
325
+ val leftMacAddress: String? = null,
326
+ val rightMacAddress: String? = null,
327
+ val macAddress: String? = null,
328
+ val buildNumber: String? = null,
329
+ val otaVersionUrl: String? = null,
330
+ val appVersion: String? = null,
331
+ val bluetoothName: String? = null,
332
+ val serialNumber: String? = null,
333
+ val style: String? = null,
334
+ val color: String? = null,
335
+ val wifi: WifiStatus? = null,
336
+ val batteryLevel: Int? = null,
337
+ val charging: Boolean? = null,
338
+ val caseBatteryLevel: Int? = null,
339
+ val caseCharging: Boolean? = null,
340
+ val caseOpen: Boolean? = null,
341
+ val caseRemoved: Boolean? = null,
342
+ val hotspot: HotspotStatus? = null,
343
+ val headUp: Boolean? = null,
344
+ val controllerConnected: Boolean? = null,
345
+ val controllerFullyBooted: Boolean? = null,
346
+ val controllerMacAddress: String? = null,
347
+ val controllerBatteryLevel: Int? = null,
348
+ val controllerSignalStrength: Int? = null,
349
+ val ringSignalStrength: Int? = null,
350
+ ) {
351
+ internal fun toMap(): Map<String, Any> =
352
+ buildMap {
353
+ if (fullyBooted != null || connected != null || connectionState != null) {
354
+ val state =
355
+ connectionState
356
+ ?: if (connected == true || fullyBooted == true) {
357
+ GlassesConnectionState.CONNECTED
358
+ } else {
359
+ GlassesConnectionState.DISCONNECTED
360
+ }
361
+ put("connection", state.toStatusMap(connected == true, fullyBooted == true))
362
+ }
363
+ putIfNotNull("micEnabled", micEnabled)
364
+ putIfNotNull("voiceActivityDetectionEnabled", voiceActivityDetectionEnabled)
365
+ putIfNotNull("bluetoothClassicConnected", bluetoothClassicConnected)
366
+ putIfNotNull("signalStrength", signalStrength)
367
+ putIfNotNull("signalStrengthUpdatedAt", signalStrengthUpdatedAt)
368
+ putIfNotNull("deviceModel", deviceModel)
369
+ putIfNotNull("androidVersion", androidVersion)
370
+ putIfNotNull("firmwareVersion", firmwareVersion)
371
+ putIfNotNull("besFirmwareVersion", besFirmwareVersion)
372
+ putIfNotNull("mtkFirmwareVersion", mtkFirmwareVersion)
373
+ putIfNotNull("bluetoothMacAddress", bluetoothMacAddress)
374
+ putIfNotNull("leftMacAddress", leftMacAddress)
375
+ putIfNotNull("rightMacAddress", rightMacAddress)
376
+ putIfNotNull("macAddress", macAddress)
377
+ putIfNotNull("buildNumber", buildNumber)
378
+ putIfNotNull("otaVersionUrl", otaVersionUrl)
379
+ putIfNotNull("appVersion", appVersion)
380
+ putIfNotNull("bluetoothName", bluetoothName)
381
+ putIfNotNull("serialNumber", serialNumber)
382
+ putIfNotNull("style", style)
383
+ putIfNotNull("color", color)
384
+ wifi?.let {
385
+ put("wifi", it.toMap())
386
+ }
387
+ putIfNotNull("batteryLevel", batteryLevel)
388
+ putIfNotNull("charging", charging)
389
+ putIfNotNull("caseBatteryLevel", caseBatteryLevel)
390
+ putIfNotNull("caseCharging", caseCharging)
391
+ putIfNotNull("caseOpen", caseOpen)
392
+ putIfNotNull("caseRemoved", caseRemoved)
393
+ hotspot?.let {
394
+ put("hotspot", it.toMap())
395
+ }
396
+ putIfNotNull("headUp", headUp)
397
+ putIfNotNull("controllerConnected", controllerConnected)
398
+ putIfNotNull("controllerFullyBooted", controllerFullyBooted)
399
+ putIfNotNull("controllerMacAddress", controllerMacAddress)
400
+ putIfNotNull("controllerBatteryLevel", controllerBatteryLevel)
401
+ putIfNotNull("controllerSignalStrength", controllerSignalStrength)
402
+ putIfNotNull("ringSignalStrength", ringSignalStrength)
403
+ }
404
+
405
+ companion object {
406
+ internal fun fromMap(values: Map<String, Any>): GlassesStatusUpdate =
407
+ GlassesStatusUpdate(
408
+ fullyBooted = optionalBoolValue(values, "fullyBooted"),
409
+ connected = optionalBoolValue(values, "connected"),
410
+ micEnabled = optionalBoolValue(values, "micEnabled"),
411
+ voiceActivityDetectionEnabled = optionalBoolValue(values, "voiceActivityDetectionEnabled"),
412
+ connectionState = GlassesConnectionState.optionalFromValue(optionalStringValue(values, "connectionState")),
413
+ bluetoothClassicConnected = optionalBoolValue(values, "bluetoothClassicConnected"),
414
+ signalStrength = optionalNumberValue(values, "signalStrength"),
415
+ signalStrengthUpdatedAt = optionalLongValue(values, "signalStrengthUpdatedAt"),
416
+ deviceModel = optionalStringValue(values, "deviceModel"),
417
+ androidVersion = optionalStringValue(values, "androidVersion"),
418
+ firmwareVersion = optionalStringValue(values, "firmwareVersion"),
419
+ besFirmwareVersion = optionalStringValue(values, "besFirmwareVersion"),
420
+ mtkFirmwareVersion = optionalStringValue(values, "mtkFirmwareVersion"),
421
+ bluetoothMacAddress = optionalStringValue(values, "bluetoothMacAddress"),
422
+ leftMacAddress = optionalStringValue(values, "leftMacAddress"),
423
+ rightMacAddress = optionalStringValue(values, "rightMacAddress"),
424
+ macAddress = optionalStringValue(values, "macAddress"),
425
+ buildNumber = optionalStringValue(values, "buildNumber"),
426
+ otaVersionUrl = optionalStringValue(values, "otaVersionUrl"),
427
+ appVersion = optionalStringValue(values, "appVersion"),
428
+ bluetoothName = optionalStringValue(values, "bluetoothName"),
429
+ serialNumber = optionalStringValue(values, "serialNumber"),
430
+ style = optionalStringValue(values, "style"),
431
+ color = optionalStringValue(values, "color"),
432
+ wifi =
433
+ if (hasAnyKey(values, "wifi")) {
434
+ WifiStatus.fromMap(values)
435
+ } else if (hasAnyKey(values, "wifiConnected", "wifiSsid", "wifiLocalIp")) {
436
+ WifiStatus.fromStoreMap(values)
437
+ } else {
438
+ null
439
+ },
440
+ batteryLevel = optionalNumberValue(values, "batteryLevel"),
441
+ charging = optionalBoolValue(values, "charging"),
442
+ caseBatteryLevel = optionalNumberValue(values, "caseBatteryLevel"),
443
+ caseCharging = optionalBoolValue(values, "caseCharging"),
444
+ caseOpen = optionalBoolValue(values, "caseOpen"),
445
+ caseRemoved = optionalBoolValue(values, "caseRemoved"),
446
+ hotspot =
447
+ if (hasAnyKey(values, "hotspot")) {
448
+ HotspotStatus.fromMap(values)
449
+ } else if (hasAnyKey(values, "hotspotEnabled", "hotspotSsid", "hotspotPassword", "hotspotGatewayIp")) {
450
+ HotspotStatus.fromStoreMap(values)
451
+ } else {
452
+ null
453
+ },
454
+ headUp = optionalBoolValue(values, "headUp"),
455
+ controllerConnected = optionalBoolValue(values, "controllerConnected"),
456
+ controllerFullyBooted = optionalBoolValue(values, "controllerFullyBooted"),
457
+ controllerMacAddress = optionalStringValue(values, "controllerMacAddress"),
458
+ controllerBatteryLevel = optionalNumberValue(values, "controllerBatteryLevel"),
459
+ controllerSignalStrength = optionalNumberValue(values, "controllerSignalStrength"),
460
+ ringSignalStrength = optionalNumberValue(values, "ringSignalStrength"),
461
+ )
462
+ }
463
+ }
464
+
465
+ internal data class BluetoothStatusUpdate(
466
+ val searching: Boolean? = null,
467
+ val searchingController: Boolean? = null,
468
+ val systemMicUnavailable: Boolean? = null,
469
+ val micEnabled: Boolean? = null,
470
+ val currentMic: String? = null,
471
+ val micRanking: List<String>? = null,
472
+ /**
473
+ * Nearby glasses in stable discovery order when included in an update. Existing entries keep
474
+ * their array position as details refresh; new glasses append at the end, and removals should
475
+ * not reorder remaining entries.
476
+ */
477
+ val searchResults: List<Device>? = null,
478
+ val wifiScanResults: List<WifiScanResult>? = null,
479
+ val lastLog: List<String>? = null,
480
+ val otherBtConnected: Boolean? = null,
481
+ val defaultWearable: String? = null,
482
+ val pendingWearable: String? = null,
483
+ val deviceName: String? = null,
484
+ val deviceAddress: String? = null,
485
+ val defaultController: String? = null,
486
+ val pendingController: String? = null,
487
+ val controllerDeviceName: String? = null,
488
+ val screenDisabled: Boolean? = null,
489
+ val preferredMic: String? = null,
490
+ val sensingEnabled: Boolean? = null,
491
+ val powerSavingMode: Boolean? = null,
492
+ val brightness: Int? = null,
493
+ val autoBrightness: Boolean? = null,
494
+ val dashboardHeight: Int? = null,
495
+ val dashboardDepth: Int? = null,
496
+ val headUpAngle: Int? = null,
497
+ val contextualDashboard: Boolean? = null,
498
+ val galleryModeEnabled: Boolean? = null,
499
+ val buttonPhotoSize: ButtonPhotoSize? = null,
500
+ val buttonCameraLed: Boolean? = null,
501
+ val buttonMaxRecordingTime: Int? = null,
502
+ val buttonVideoWidth: Int? = null,
503
+ val buttonVideoHeight: Int? = null,
504
+ val buttonVideoFrameRate: Int? = null,
505
+ val shouldSendPcm: Boolean? = null,
506
+ val shouldSendLc3: Boolean? = null,
507
+ val shouldSendTranscript: Boolean? = null,
508
+ val offlineCaptionsRunning: Boolean? = null,
509
+ val localSttFallbackActive: Boolean? = null,
510
+ val shouldSendBootingMessage: Boolean? = null,
511
+ ) {
512
+ internal fun toMap(): Map<String, Any> =
513
+ buildMap {
514
+ putIfNotNull("searching", searching)
515
+ putIfNotNull("searchingController", searchingController)
516
+ putIfNotNull("systemMicUnavailable", systemMicUnavailable)
517
+ putIfNotNull("micEnabled", micEnabled)
518
+ putIfNotNull("currentMic", currentMic)
519
+ putIfNotNull("micRanking", micRanking)
520
+ searchResults?.let { put("searchResults", it.map(Device::toMap)) }
521
+ wifiScanResults?.let { put("wifiScanResults", it.map(WifiScanResult::toMap)) }
522
+ putIfNotNull("lastLog", lastLog)
523
+ putIfNotNull("otherBtConnected", otherBtConnected)
524
+ putIfNotNull("default_wearable", defaultWearable)
525
+ putIfNotNull("pending_wearable", pendingWearable)
526
+ putIfNotNull("device_name", deviceName)
527
+ putIfNotNull("device_address", deviceAddress)
528
+ putIfNotNull("default_controller", defaultController)
529
+ putIfNotNull("pending_controller", pendingController)
530
+ putIfNotNull("controller_device_name", controllerDeviceName)
531
+ putIfNotNull("screen_disabled", screenDisabled)
532
+ putIfNotNull("preferred_mic", preferredMic)
533
+ putIfNotNull("sensing_enabled", sensingEnabled)
534
+ putIfNotNull("power_saving_mode", powerSavingMode)
535
+ putIfNotNull("brightness", brightness)
536
+ putIfNotNull("auto_brightness", autoBrightness)
537
+ putIfNotNull("dashboard_height", dashboardHeight)
538
+ putIfNotNull("dashboard_depth", dashboardDepth)
539
+ putIfNotNull("head_up_angle", headUpAngle)
540
+ putIfNotNull("contextual_dashboard", contextualDashboard)
541
+ putIfNotNull("galleryModeEnabled", galleryModeEnabled)
542
+ buttonPhotoSize?.let { put("button_photo_size", it.value) }
543
+ putIfNotNull("button_camera_led", buttonCameraLed)
544
+ putIfNotNull("button_max_recording_time", buttonMaxRecordingTime)
545
+ putIfNotNull("button_video_width", buttonVideoWidth)
546
+ putIfNotNull("button_video_height", buttonVideoHeight)
547
+ putIfNotNull("button_video_fps", buttonVideoFrameRate)
548
+ putIfNotNull("should_send_pcm", shouldSendPcm)
549
+ putIfNotNull("should_send_lc3", shouldSendLc3)
550
+ putIfNotNull("should_send_transcript", shouldSendTranscript)
551
+ putIfNotNull("offline_captions_running", offlineCaptionsRunning)
552
+ putIfNotNull("local_stt_fallback_active", localSttFallbackActive)
553
+ putIfNotNull("shouldSendBootingMessage", shouldSendBootingMessage)
554
+ }
555
+
556
+ companion object {
557
+ internal fun fromMap(values: Map<String, Any>): BluetoothStatusUpdate =
558
+ BluetoothStatusUpdate(
559
+ searching = optionalBoolValue(values, "searching"),
560
+ searchingController = optionalBoolValue(values, "searchingController"),
561
+ systemMicUnavailable = optionalBoolValue(values, "systemMicUnavailable"),
562
+ micEnabled = optionalBoolValue(values, "micEnabled"),
563
+ currentMic = optionalStringValue(values, "currentMic"),
564
+ micRanking = optionalStringListValue(values, "micRanking"),
565
+ searchResults =
566
+ optionalMapListValue(values, "searchResults")
567
+ ?.mapNotNull(Device::fromMap),
568
+ wifiScanResults =
569
+ optionalMapListValue(values, "wifiScanResults")
570
+ ?.map(WifiScanResult::fromMap),
571
+ lastLog = optionalStringListValue(values, "lastLog"),
572
+ otherBtConnected = optionalBoolValue(values, "otherBtConnected"),
573
+ defaultWearable = optionalStringValue(values, "default_wearable"),
574
+ pendingWearable = optionalStringValue(values, "pending_wearable"),
575
+ deviceName = optionalStringValue(values, "device_name"),
576
+ deviceAddress = optionalStringValue(values, "device_address"),
577
+ defaultController = optionalStringValue(values, "default_controller"),
578
+ pendingController = optionalStringValue(values, "pending_controller"),
579
+ controllerDeviceName = optionalStringValue(values, "controller_device_name"),
580
+ screenDisabled = optionalBoolValue(values, "screen_disabled"),
581
+ preferredMic = optionalStringValue(values, "preferred_mic"),
582
+ sensingEnabled = optionalBoolValue(values, "sensing_enabled"),
583
+ powerSavingMode = optionalBoolValue(values, "power_saving_mode"),
584
+ brightness = optionalNumberValue(values, "brightness"),
585
+ autoBrightness = optionalBoolValue(values, "auto_brightness"),
586
+ dashboardHeight = optionalNumberValue(values, "dashboard_height"),
587
+ dashboardDepth = optionalNumberValue(values, "dashboard_depth"),
588
+ headUpAngle = optionalNumberValue(values, "head_up_angle"),
589
+ contextualDashboard = optionalBoolValue(values, "contextual_dashboard"),
590
+ galleryModeEnabled =
591
+ optionalBoolValue(values, "gallery_mode") ?: optionalBoolValue(values, "galleryModeEnabled"),
592
+ buttonPhotoSize =
593
+ optionalStringValue(values, "button_photo_size")?.let(ButtonPhotoSize::fromValue),
594
+ buttonCameraLed = optionalBoolValue(values, "button_camera_led"),
595
+ buttonMaxRecordingTime = optionalNumberValue(values, "button_max_recording_time"),
596
+ buttonVideoWidth = optionalNumberValue(values, "button_video_width"),
597
+ buttonVideoHeight = optionalNumberValue(values, "button_video_height"),
598
+ buttonVideoFrameRate = optionalNumberValue(values, "button_video_fps"),
599
+ shouldSendPcm = optionalBoolValue(values, "should_send_pcm"),
600
+ shouldSendLc3 = optionalBoolValue(values, "should_send_lc3"),
601
+ shouldSendTranscript = optionalBoolValue(values, "should_send_transcript"),
602
+ offlineCaptionsRunning = optionalBoolValue(values, "offline_captions_running"),
603
+ localSttFallbackActive = optionalBoolValue(values, "local_stt_fallback_active"),
604
+ shouldSendBootingMessage = optionalBoolValue(values, "shouldSendBootingMessage"),
605
+ )
606
+ }
607
+ }