@mentra/bluetooth-sdk 0.1.4 → 0.1.6

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