@mentra/bluetooth-sdk 0.1.19 → 0.1.21-beta.0

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 (83) hide show
  1. package/README.md +6 -2
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +239 -52
  3. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +65 -14
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +47 -4
  5. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +6 -1
  6. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +381 -97
  7. package/android/src/main/java/com/mentra/bluetoothsdk/MentraLocalNetworkModule.kt +333 -0
  8. package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +16 -0
  9. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +2 -2
  10. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1TextSanitizer.kt +38 -0
  11. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +16 -9
  12. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +1 -1
  13. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +293 -174
  14. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -1
  15. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +1 -1
  16. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +4 -2
  17. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +1 -1
  18. package/android/src/main/java/com/mentra/bluetoothsdk/status/WifiHotspotStatus.kt +19 -3
  19. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +15 -2
  20. package/android/src/main/java/com/mentra/bluetoothsdk/tts/TTSTools.kt +115 -47
  21. package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +2 -3
  22. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +65 -8
  23. package/android/src/main/java/com/mentra/bluetoothsdk/utils/K900ProtocolUtils.java +6 -5
  24. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -0
  25. package/android/src/test/java/com/mentra/bluetoothsdk/BluetoothSdkExceptionTest.kt +18 -0
  26. package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +31 -0
  27. package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +31 -0
  28. package/android/src/test/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +9 -0
  29. package/build/BluetoothSdk.types.d.ts +71 -1
  30. package/build/BluetoothSdk.types.d.ts.map +1 -1
  31. package/build/BluetoothSdk.types.js.map +1 -1
  32. package/build/_internal.d.ts +2 -0
  33. package/build/_internal.d.ts.map +1 -1
  34. package/build/_internal.js +2 -0
  35. package/build/_internal.js.map +1 -1
  36. package/build/_private/BluetoothSdkModule.d.ts +27 -2
  37. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  38. package/build/_private/BluetoothSdkModule.js +20 -0
  39. package/build/_private/BluetoothSdkModule.js.map +1 -1
  40. package/build/_private/MentraLocalNetworkModule.d.ts +37 -0
  41. package/build/_private/MentraLocalNetworkModule.d.ts.map +1 -0
  42. package/build/_private/MentraLocalNetworkModule.js +3 -0
  43. package/build/_private/MentraLocalNetworkModule.js.map +1 -0
  44. package/build/_private/cameraRequestPayload.d.ts.map +1 -1
  45. package/build/_private/cameraRequestPayload.js +1 -0
  46. package/build/_private/cameraRequestPayload.js.map +1 -1
  47. package/build/_private/photoRequestPayload.d.ts.map +1 -1
  48. package/build/_private/photoRequestPayload.js +2 -0
  49. package/build/_private/photoRequestPayload.js.map +1 -1
  50. package/build/index.d.ts +1 -1
  51. package/build/index.d.ts.map +1 -1
  52. package/build/index.js +9 -0
  53. package/build/index.js.map +1 -1
  54. package/expo-module.config.json +1 -0
  55. package/ios/BluetoothSdkModule.swift +72 -0
  56. package/ios/Source/BluetoothSdkDefaults.swift +1 -1
  57. package/ios/Source/Bridge.swift +72 -18
  58. package/ios/Source/DeviceManager.swift +52 -3
  59. package/ios/Source/DeviceStore.swift +6 -1
  60. package/ios/Source/MentraBluetoothSDK.swift +298 -21
  61. package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
  62. package/ios/Source/camera/CameraModels.swift +22 -3
  63. package/ios/Source/sgcs/G1.swift +2 -2
  64. package/ios/Source/sgcs/G2.swift +19 -7
  65. package/ios/Source/sgcs/Mach1.swift +1 -1
  66. package/ios/Source/sgcs/MentraLive.swift +456 -52
  67. package/ios/Source/sgcs/MentraLiveL2capChannel.swift +183 -0
  68. package/ios/Source/sgcs/MentraNex.swift +1 -1
  69. package/ios/Source/sgcs/Nimo.swift +1 -1
  70. package/ios/Source/sgcs/SGCManager.swift +5 -2
  71. package/ios/Source/sgcs/Simulated.swift +1 -1
  72. package/ios/Source/status/WifiHotspotStatus.swift +21 -2
  73. package/ios/Source/streaming/StreamModels.swift +9 -0
  74. package/ios/Source/tts/TTSTools.swift +1 -1
  75. package/ios/Source/utils/G1Text.swift +30 -1
  76. package/package.json +1 -2
  77. package/src/BluetoothSdk.types.ts +75 -0
  78. package/src/_internal.ts +2 -0
  79. package/src/_private/BluetoothSdkModule.ts +68 -13
  80. package/src/_private/MentraLocalNetworkModule.ts +50 -0
  81. package/src/_private/cameraRequestPayload.ts +1 -0
  82. package/src/_private/photoRequestPayload.ts +3 -3
  83. package/src/index.ts +10 -0
@@ -329,6 +329,11 @@ class DeviceManager {
329
329
  return
330
330
  }
331
331
 
332
+ if (sgc?.isMicSuspendedForAudio == true) {
333
+ Bridge.log("MAN: Glasses mic intentionally suspended for phone audio; skipping mic recovery")
334
+ return
335
+ }
336
+
332
337
  // When no frame has ever been received, treat elapsed as "forever" so we
333
338
  // actually attempt recovery (was 0 before, which made the watchdog a no-op).
334
339
  val timeSinceLastLc3Event = System.currentTimeMillis() - (lastLc3Event ?: 0L)
@@ -1556,10 +1561,10 @@ class DeviceManager {
1556
1561
  sgc?.sendStreamKeepAlive(message)
1557
1562
  }
1558
1563
 
1559
- fun requestWifiScan() {
1564
+ fun requestWifiScan(scanId: String? = null) {
1560
1565
  Bridge.log("MAN: Requesting wifi scan")
1561
1566
  DeviceStore.apply("bluetooth", "wifiScanResults", emptyList<Any>())
1562
- sgc?.requestWifiScan()
1567
+ sgc?.requestWifiScan(scanId)
1563
1568
  }
1564
1569
 
1565
1570
  fun sendIncidentId(incidentId: String, apiBaseUrl: String? = null) {
@@ -1649,6 +1654,38 @@ class DeviceManager {
1649
1654
  live.sendCameraFovSetting(requestId, fov, roiPosition)
1650
1655
  }
1651
1656
 
1657
+ /** Sends the pre-lease FOV command used by ASG clients that do not send an acknowledgement. */
1658
+ fun sendLegacyCameraFovSetting(fov: Int, roiPosition: Int) {
1659
+ val glassesConnected = DeviceStore.get("glasses", "connected") as? Boolean ?: false
1660
+ if (!glassesConnected) throw IllegalStateException("not_connected")
1661
+ val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
1662
+ live.sendCameraFovSetting(null, fov, roiPosition)
1663
+ }
1664
+
1665
+ /** Replays the persistent base FOV without waiting for an acknowledgement. */
1666
+ fun restoreLegacyCameraFovSetting() {
1667
+ val glassesConnected = DeviceStore.get("glasses", "connected") as? Boolean ?: false
1668
+ if (!glassesConnected) throw IllegalStateException("not_connected")
1669
+ val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
1670
+ live.sendCameraFovSetting()
1671
+ }
1672
+
1673
+ fun sendCameraFovOverride(
1674
+ requestId: String,
1675
+ leaseId: String,
1676
+ fov: Int,
1677
+ roiPosition: Int,
1678
+ ttlMs: Int,
1679
+ ) {
1680
+ val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
1681
+ live.sendCameraFovOverride(requestId, leaseId, fov, roiPosition, ttlMs)
1682
+ }
1683
+
1684
+ fun releaseCameraFovOverride(requestId: String, leaseId: String) {
1685
+ val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
1686
+ live.releaseCameraFovOverride(requestId, leaseId)
1687
+ }
1688
+
1652
1689
  fun sendCameraTuningConfig(requestId: String, anrOn: Boolean, gainOn: Boolean) {
1653
1690
  val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
1654
1691
  live.sendCameraTuningConfig(requestId, anrOn, gainOn)
@@ -1657,6 +1694,7 @@ class DeviceManager {
1657
1694
  fun warmUpCamera(
1658
1695
  requestId: String,
1659
1696
  size: PhotoSize,
1697
+ mode: PhotoMode = PhotoMode.PHOTO,
1660
1698
  exposureTimeNs: Long?,
1661
1699
  durationMs: Int,
1662
1700
  ) {
@@ -1668,7 +1706,12 @@ class DeviceManager {
1668
1706
  "unsupported_device",
1669
1707
  "This command requires Mentra Live glasses.",
1670
1708
  )
1671
- live.warmUpCamera(requestId, size, exposureTimeNs, durationMs)
1709
+ live.warmUpCamera(requestId, size, mode, exposureTimeNs, durationMs)
1710
+ }
1711
+
1712
+ fun stopCameraWarmUp(requestId: String) {
1713
+ val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
1714
+ live.stopCameraWarmUp(requestId)
1672
1715
  }
1673
1716
 
1674
1717
  /**
@@ -1800,7 +1843,7 @@ class DeviceManager {
1800
1843
  iso = manualIso,
1801
1844
  )
1802
1845
  Bridge.log(
1803
- "MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=${routed.requestId} size=${routed.size.value} compress=${routed.compress.value} save=${routed.save} sound=${routed.sound} exposureTimeNs=$exposureNs iso=${manualIso ?: "auto"} aeDivisor=${routed.aeExposureDivisor} isoCap=${routed.isoCap} sgc=${sgc?.javaClass?.simpleName ?: "null"}"
1846
+ "MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=${routed.requestId} size=${routed.size.value} mode=${routed.mode.value} compress=${routed.compress.value} save=${routed.save} sound=${routed.sound} exposureTimeNs=$exposureNs iso=${manualIso ?: "auto"} aeDivisor=${routed.aeExposureDivisor} isoCap=${routed.isoCap} sgc=${sgc?.javaClass?.simpleName ?: "null"}"
1804
1847
  )
1805
1848
  val activeSgc = sgc
1806
1849
  if (activeSgc == null) {
@@ -128,7 +128,7 @@ object DeviceStore {
128
128
  store.set("bluetooth", "screen_disabled", false)
129
129
  store.set("bluetooth", "button_photo_size", "max")
130
130
  store.set("bluetooth", "button_max_recording_time", 10)
131
- store.set("bluetooth", "camera_fov", mapOf("fov" to 118, "roi_position" to 0))
131
+ store.set("bluetooth", "camera_fov", mapOf("fov" to 102, "roi_position" to 0))
132
132
  store.set("bluetooth", "button_video_width", 1280)
133
133
  store.set("bluetooth", "button_video_height", 720)
134
134
  store.set("bluetooth", "button_video_fps", 30)
@@ -292,6 +292,11 @@ object DeviceStore {
292
292
  "bluetooth" to "camera_fov" -> {
293
293
  DeviceManager.getInstance().sgc?.sendCameraFovSetting()
294
294
  }
295
+ "bluetooth" to "button_video_settings" -> {
296
+ DeviceManager.getInstance().sgc?.sendButtonVideoRecordingSettings()
297
+ }
298
+ // Legacy scalar keys remain supported for older hosts. New code should write the
299
+ // canonical button_video_settings object so width/height/fps update atomically.
295
300
  "bluetooth" to "button_video_width",
296
301
  "bluetooth" to "button_video_height",
297
302
  "bluetooth" to "button_video_fps" -> {