@mentra/bluetooth-sdk 0.1.8 → 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 +8 -2
  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
@@ -23,10 +23,12 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
23
23
  "speaking_status",
24
24
  "battery_status",
25
25
  "wifi_status_change",
26
+ "wifi_scan_result",
26
27
  "hotspot_status_change",
27
28
  "hotspot_error",
28
29
  "photo_response",
29
30
  "photo_status",
31
+ "video_recording_status",
30
32
  "gallery_status",
31
33
  "compatible_glasses_search_stop",
32
34
  "heartbeat_sent",
@@ -34,6 +36,8 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
34
36
  "swipe_volume_status",
35
37
  "switch_status",
36
38
  "rgb_led_control_response",
39
+ "settings_ack",
40
+ "version_info",
37
41
  "pair_failure",
38
42
  "audio_pairing_needed",
39
43
  "audio_connected",
@@ -56,7 +60,8 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
56
60
  "send_command_to_ble",
57
61
  "receive_command_from_ble",
58
62
  "miniapp_selected",
59
- "captions_tester_incident"
63
+ "captions_tester_incident",
64
+ "extraction_progress"
60
65
  )
61
66
 
62
67
  OnCreate {
@@ -241,7 +246,7 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
241
246
  JSCExperiment.spawn(count: count)
242
247
  }
243
248
 
244
- Function("jscKillAll") { () -> Void in
249
+ Function("jscKillAll") { () in
245
250
  JSCExperiment.killAll()
246
251
  }
247
252
 
@@ -253,7 +258,7 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
253
258
  JSCExperiment.spawnAndMeasure(count: count, baselineMB: baselineMB)
254
259
  }
255
260
 
256
- Function("jscRunBenchmark") { () -> Void in
261
+ Function("jscRunBenchmark") { () in
257
262
  JSCExperiment.runBenchmark()
258
263
  }
259
264
 
@@ -268,26 +273,39 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
268
273
  // MARK: - WiFi Commands
269
274
 
270
275
  AsyncFunction("requestWifiScan") {
271
- await MainActor.run {
272
- self.bluetoothSdk().requestWifiScan()
273
- }
276
+ let sdk = await MainActor.run { self.bluetoothSdk() }
277
+ return try await sdk.requestWifiScan().map(\.dictionary)
274
278
  }
275
279
 
276
280
  AsyncFunction("sendWifiCredentials") { (ssid: String, password: String) in
277
- await MainActor.run {
278
- self.bluetoothSdk().sendWifiCredentials(ssid: ssid, password: password)
279
- }
281
+ let sdk = await MainActor.run { self.bluetoothSdk() }
282
+ return try await sdk.sendWifiCredentials(ssid: ssid, password: password).values
280
283
  }
281
284
 
282
285
  AsyncFunction("forgetWifiNetwork") { (ssid: String) in
283
- await MainActor.run {
284
- self.bluetoothSdk().forgetWifiNetwork(ssid: ssid)
285
- }
286
+ let sdk = await MainActor.run { self.bluetoothSdk() }
287
+ return try await sdk.forgetWifiNetwork(ssid: ssid).values
286
288
  }
287
289
 
288
290
  AsyncFunction("setHotspotState") { (enabled: Bool) in
291
+ let sdk = await MainActor.run { self.bluetoothSdk() }
292
+ return try await sdk.setHotspotState(enabled: enabled).values
293
+ }
294
+
295
+ AsyncFunction("setSystemTime") { (timestampMs: Double) in
296
+ let maxTimestamp = Double(Int64.max).nextDown
297
+ guard timestampMs.isFinite,
298
+ timestampMs >= Double(Int64.min),
299
+ timestampMs <= maxTimestamp
300
+ else {
301
+ throw BluetoothError(
302
+ code: "invalid_timestamp",
303
+ message: "setSystemTime timestampMs must be a finite Int64 millisecond timestamp."
304
+ )
305
+ }
306
+ let timestamp = Int64(timestampMs)
289
307
  await MainActor.run {
290
- self.bluetoothSdk().setHotspotState(enabled: enabled)
308
+ self.bluetoothSdk().setSystemTime(timestampMs: timestamp)
291
309
  }
292
310
  }
293
311
 
@@ -295,7 +313,7 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
295
313
 
296
314
  AsyncFunction("setGalleryModeEnabled") { (enabled: Bool) in
297
315
  let sdk = await MainActor.run { self.bluetoothSdk() }
298
- try await sdk.setGalleryModeEnabled(enabled)
316
+ return try await sdk.setGalleryModeEnabled(enabled).values
299
317
  }
300
318
 
301
319
  AsyncFunction("setVoiceActivityDetectionEnabled") { (enabled: Bool) in
@@ -303,10 +321,38 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
303
321
  try await sdk.setVoiceActivityDetectionEnabled(enabled)
304
322
  }
305
323
 
324
+ AsyncFunction("setButtonPhotoSettings") { (size: String) in
325
+ let sdk = await MainActor.run { self.bluetoothSdk() }
326
+ return try await sdk.setButtonPhotoSettings(size: ButtonPhotoSize(rawValue: size) ?? .medium).values
327
+ }
328
+
329
+ AsyncFunction("setButtonVideoRecordingSettings") { (width: Int, height: Int, fps: Int) in
330
+ let sdk = await MainActor.run { self.bluetoothSdk() }
331
+ return try await sdk.setButtonVideoRecordingSettings(width: width, height: height, fps: fps).values
332
+ }
333
+
334
+ AsyncFunction("setButtonCameraLed") { (enabled: Bool) in
335
+ let sdk = await MainActor.run { self.bluetoothSdk() }
336
+ return try await sdk.setButtonCameraLed(enabled: enabled).values
337
+ }
338
+
339
+ AsyncFunction("setButtonMaxRecordingTime") { (minutes: Int) in
340
+ let sdk = await MainActor.run { self.bluetoothSdk() }
341
+ return try await sdk.setButtonMaxRecordingTime(minutes: minutes).values
342
+ }
343
+
344
+ AsyncFunction("setCameraFov") { (fov: [String: Any]) in
345
+ let value = intValue(fov["fov"]) ?? CameraFov.defaultFov
346
+ let roiPosition = CameraRoiPosition.from(
347
+ rawValue: intValue(fov["roiPosition"]) ?? intValue(fov["roi_position"])
348
+ )
349
+ let sdk = await MainActor.run { self.bluetoothSdk() }
350
+ return try await sdk.setCameraFov(CameraFov(fov: value, roiPosition: roiPosition)).values
351
+ }
352
+
306
353
  AsyncFunction("queryGalleryStatus") {
307
- await MainActor.run {
308
- self.bluetoothSdk().queryGalleryStatus()
309
- }
354
+ let sdk = await MainActor.run { self.bluetoothSdk() }
355
+ return try await sdk.queryGalleryStatus().values
310
356
  }
311
357
 
312
358
  AsyncFunction("requestPhoto") { (params: [String: Any]) in
@@ -320,6 +366,7 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
320
366
  let authToken = params["authToken"] as? String ?? ""
321
367
  let compress = params["compress"] as? String ?? "none"
322
368
  let flash = params["flash"] as? Bool ?? true
369
+ let save = params["save"] as? Bool ?? params["saveToGallery"] as? Bool ?? false
323
370
  let sound = params["sound"] as? Bool ?? true
324
371
  let exposureTimeNs: Double?
325
372
  switch params["exposureTimeNs"] {
@@ -337,7 +384,8 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
337
384
  case let value as Int:
338
385
  iso = value > 0 ? value : nil
339
386
  case let value as Double:
340
- iso = value.isFinite && value > 0 ? Int(value) : nil
387
+ // Guard against Int(Double) trapping on out-of-range values.
388
+ iso = (value.isFinite && value > 0 && value < Double(Int.max)) ? Int(value) : nil
341
389
  case let value as NSNumber:
342
390
  let intValue = value.intValue
343
391
  iso = intValue > 0 ? intValue : nil
@@ -345,44 +393,46 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
345
393
  iso = nil
346
394
  }
347
395
 
348
- await MainActor.run {
349
- self.bluetoothSdk().requestPhoto(
350
- PhotoRequest(
351
- requestId: requestId,
352
- appId: appId,
353
- size: PhotoSize(rawValue: size) ?? .medium,
354
- webhookUrl: webhookUrl,
355
- authToken: authToken,
356
- compress: PhotoCompression(rawValue: compress),
357
- flash: flash,
358
- sound: sound,
359
- exposureTimeNs: exposureTimeNs,
360
- iso: iso
361
- )
396
+ let sdk = await MainActor.run { self.bluetoothSdk() }
397
+ return try await sdk.requestPhoto(
398
+ PhotoRequest(
399
+ requestId: requestId,
400
+ appId: appId,
401
+ size: PhotoSize(rawValue: size) ?? .medium,
402
+ webhookUrl: webhookUrl,
403
+ authToken: authToken,
404
+ compress: PhotoCompression(rawValue: compress),
405
+ flash: flash,
406
+ save: save,
407
+ sound: sound,
408
+ exposureTimeNs: exposureTimeNs,
409
+ iso: iso
362
410
  )
363
- }
411
+ ).values
364
412
  }
365
413
 
366
414
  // MARK: - OTA Commands
367
415
 
368
416
  AsyncFunction("sendOtaStart") {
369
- await MainActor.run {
370
- self.bluetoothSdk().sendOtaStart()
371
- }
417
+ let sdk = await MainActor.run { self.bluetoothSdk() }
418
+ return try await sdk.sendOtaStart().values
372
419
  }
373
420
 
374
421
  AsyncFunction("sendOtaQueryStatus") {
375
- await MainActor.run {
376
- self.bluetoothSdk().sendOtaQueryStatus()
377
- }
422
+ let sdk = await MainActor.run { self.bluetoothSdk() }
423
+ return try await sdk.sendOtaQueryStatus().values
424
+ }
425
+
426
+ AsyncFunction("retryOtaVersionCheck") {
427
+ let sdk = await MainActor.run { self.bluetoothSdk() }
428
+ return try await sdk.retryOtaVersionCheck().values
378
429
  }
379
430
 
380
431
  // MARK: - Version Info Commands
381
432
 
382
433
  AsyncFunction("requestVersionInfo") {
383
- await MainActor.run {
384
- self.bluetoothSdk().requestVersionInfo()
385
- }
434
+ let sdk = await MainActor.run { self.bluetoothSdk() }
435
+ return try await sdk.requestVersionInfo().dictionary
386
436
  }
387
437
 
388
438
  // MARK: - Power Control Commands
@@ -401,37 +451,43 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
401
451
 
402
452
  // MARK: - Video Recording Commands
403
453
 
404
- AsyncFunction("startVideoRecording") { (requestId: String, save: Bool, sound: Bool) in
405
- await MainActor.run {
406
- self.bluetoothSdk().startVideoRecording(
407
- VideoRecordingRequest(requestId: requestId, save: save, sound: sound)
408
- )
454
+ AsyncFunction("startVideoRecording") {
455
+ (requestId: String, save: Bool, sound: Bool, settings: [String: Any]?) in
456
+ // Optional per-recording {width,height,fps}. Absent fields stay 0, which
457
+ // the glasses treat as "use the saved button-video default". JS numbers
458
+ // arrive as Double across the bridge, so coerce to Int.
459
+ func dim(_ key: String) -> Int {
460
+ (settings?[key] as? NSNumber)?.intValue ?? 0
409
461
  }
462
+ let sdk = await MainActor.run { self.bluetoothSdk() }
463
+ return try await sdk.startVideoRecording(
464
+ VideoRecordingRequest(
465
+ requestId: requestId, save: save, sound: sound,
466
+ width: dim("width"), height: dim("height"), fps: dim("fps")
467
+ )
468
+ ).values
410
469
  }
411
470
 
412
471
  AsyncFunction("stopVideoRecording") { (requestId: String) in
413
- await MainActor.run {
414
- self.bluetoothSdk().stopVideoRecording(requestId: requestId)
415
- }
472
+ let sdk = await MainActor.run { self.bluetoothSdk() }
473
+ return try await sdk.stopVideoRecording(requestId: requestId).values
416
474
  }
417
475
 
418
476
  // MARK: - Stream Commands
419
477
 
420
478
  AsyncFunction("startStream") { (params: [String: Any]) in
421
- await MainActor.run {
422
- self.bluetoothSdk().startStream(StreamRequest(values: params))
423
- }
479
+ let sdk = await MainActor.run { self.bluetoothSdk() }
480
+ return try await sdk.startStream(StreamRequest(values: params)).values
424
481
  }
425
482
 
426
483
  AsyncFunction("stopStream") {
427
- await MainActor.run {
428
- self.bluetoothSdk().stopStream()
429
- }
484
+ let sdk = await MainActor.run { self.bluetoothSdk() }
485
+ return try await sdk.stopStream().values
430
486
  }
431
487
 
432
- AsyncFunction("sendCloudStreamKeepAlive") { (params: [String: Any]) in
488
+ AsyncFunction("sendExternallyManagedStreamKeepAlive") { (params: [String: Any]) in
433
489
  await MainActor.run {
434
- self.bluetoothSdk().sendCloudStreamKeepAlive(StreamKeepAliveRequest(values: params))
490
+ self.bluetoothSdk().sendExternallyManagedStreamKeepAlive(StreamKeepAliveRequest(values: params))
435
491
  }
436
492
  }
437
493
 
@@ -458,19 +514,18 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
458
514
  requestId: String, packageName: String?, action: String, color: String?,
459
515
  onDurationMs: Int, offDurationMs: Int, count: Int
460
516
  ) in
461
- await MainActor.run {
462
- self.bluetoothSdk().rgbLedControl(
463
- RgbLedRequest(
464
- requestId: requestId,
465
- packageName: packageName,
466
- action: RgbLedAction(rawValue: action) ?? .off,
467
- color: color.flatMap(RgbLedColor.init(rawValue:)),
468
- onDurationMs: onDurationMs,
469
- offDurationMs: offDurationMs,
470
- count: count
471
- )
517
+ let sdk = await MainActor.run { self.bluetoothSdk() }
518
+ return try await sdk.rgbLedControl(
519
+ RgbLedRequest(
520
+ requestId: requestId,
521
+ packageName: packageName,
522
+ action: RgbLedAction(rawValue: action) ?? .off,
523
+ color: color.flatMap(RgbLedColor.init(rawValue:)),
524
+ onDurationMs: onDurationMs,
525
+ offDurationMs: offDurationMs,
526
+ count: count
472
527
  )
473
- }
528
+ ).values
474
529
  }
475
530
 
476
531
  // MARK: - Microphone Commands
@@ -525,6 +580,39 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
525
580
  AsyncFunction("extractTarBz2") { (sourcePath: String, destinationPath: String) -> Bool in
526
581
  return STTTools.extractTarBz2(sourcePath: sourcePath, destinationPath: destinationPath)
527
582
  }
583
+
584
+ // MARK: - TTS Model Management
585
+
586
+ AsyncFunction("setTtsModelDetails") { (path: String, languageCode: String) in
587
+ TTSTools.setTtsModelDetails(path, languageCode)
588
+ }
589
+
590
+ AsyncFunction("getTtsModelPath") { () -> String in
591
+ return TTSTools.getTtsModelPath()
592
+ }
593
+
594
+ AsyncFunction("getTtsModelLanguage") { () -> String in
595
+ return TTSTools.getTtsModelLanguage()
596
+ }
597
+
598
+ AsyncFunction("checkTtsModelAvailable") { () -> Bool in
599
+ return TTSTools.checkTTSModelAvailable()
600
+ }
601
+
602
+ AsyncFunction("validateTtsModel") { (path: String) -> Bool in
603
+ return TTSTools.validateTTSModel(path)
604
+ }
605
+
606
+ AsyncFunction("generateTtsAudio") {
607
+ (text: String, modelPath: String, outputPath: String, speakerId: Int, speed: Double) -> Bool in
608
+ return TTSTools.generateTtsAudio(
609
+ text: text,
610
+ modelPath: modelPath,
611
+ outputPath: outputPath,
612
+ speakerId: speakerId,
613
+ speed: speed
614
+ )
615
+ }
528
616
  }
529
617
 
530
618
  @MainActor
@@ -610,10 +698,26 @@ public class BluetoothSdkModule: Module, MentraBluetoothSDKDelegate {
610
698
  sendEvent("photo_response", response.values)
611
699
  case let .photoStatus(status):
612
700
  sendEvent("photo_status", status.values)
701
+ case let .videoRecordingStatus(status):
702
+ sendEvent("video_recording_status", status.values)
703
+ case let .rgbLedControlResponse(response):
704
+ sendEvent("rgb_led_control_response", response.values)
613
705
  case let .streamStatus(status):
614
706
  sendEvent("stream_status", status.values)
615
707
  case let .keepAliveAck(ack):
616
708
  sendEvent("keep_alive_ack", ack.values)
709
+ case let .otaUpdateAvailable(event):
710
+ sendEvent("ota_update_available", event.values)
711
+ case let .otaStartAck(event):
712
+ sendEvent("ota_start_ack", event.values)
713
+ case let .otaStatus(event):
714
+ sendEvent("ota_status", event.values)
715
+ case let .settingsAck(event):
716
+ sendEvent("settings_ack", event.values)
717
+ case let .versionInfo(event):
718
+ var values = event.dictionary
719
+ values["type"] = "version_info"
720
+ sendEvent("version_info", values)
617
721
  case let .localTranscription(transcription):
618
722
  sendEvent("local_transcription", transcription.values)
619
723
  case let .raw(name, values):