@mentra/bluetooth-sdk 0.1.14 → 0.1.17

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 (67) hide show
  1. package/README.md +4 -6
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +22 -9
  3. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +5 -20
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +358 -261
  5. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +3 -3
  6. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +133 -52
  7. package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +12 -12
  8. package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +22 -6
  9. package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +1 -22
  10. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +50 -3
  11. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +1184 -795
  12. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +422 -78
  13. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +3 -4
  14. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +2532 -0
  15. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +15 -1
  16. package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +5 -3
  17. package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +2 -1
  18. package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +60 -1
  19. package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
  20. package/build/BluetoothSdk.types.d.ts +39 -14
  21. package/build/BluetoothSdk.types.d.ts.map +1 -1
  22. package/build/BluetoothSdk.types.js +1 -0
  23. package/build/BluetoothSdk.types.js.map +1 -1
  24. package/build/_internal.d.ts +4 -4
  25. package/build/_internal.js +4 -4
  26. package/build/_internal.js.map +1 -1
  27. package/build/_private/BluetoothSdkModule.d.ts +11 -3
  28. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  29. package/build/_private/BluetoothSdkModule.js +5 -0
  30. package/build/_private/BluetoothSdkModule.js.map +1 -1
  31. package/build/_private/cameraRequestPayload.d.ts +4 -0
  32. package/build/_private/cameraRequestPayload.d.ts.map +1 -0
  33. package/build/_private/cameraRequestPayload.js +25 -0
  34. package/build/_private/cameraRequestPayload.js.map +1 -0
  35. package/build/_private/photoRequestPayload.d.ts.map +1 -1
  36. package/build/_private/photoRequestPayload.js +8 -2
  37. package/build/_private/photoRequestPayload.js.map +1 -1
  38. package/build/index.d.ts +1 -1
  39. package/build/index.d.ts.map +1 -1
  40. package/build/index.js +72 -54
  41. package/build/index.js.map +1 -1
  42. package/ios/BluetoothSdkModule.swift +40 -12
  43. package/ios/Source/BluetoothSdkDefaults.swift +2 -30
  44. package/ios/Source/Bridge.swift +4 -16
  45. package/ios/Source/DeviceManager.swift +72 -12
  46. package/ios/Source/MentraBluetoothSDK.swift +120 -50
  47. package/ios/Source/OtaManifest.swift +13 -13
  48. package/ios/Source/camera/CameraModels.swift +75 -9
  49. package/ios/Source/errors/{BluetoothError.swift → BluetoothSdkError.swift} +1 -1
  50. package/ios/Source/events/BluetoothEvents.swift +5 -27
  51. package/ios/Source/internal/BluetoothAvailability.swift +5 -5
  52. package/ios/Source/sgcs/G1.swift +51 -1
  53. package/ios/Source/sgcs/G2.swift +643 -203
  54. package/ios/Source/sgcs/MentraLive.swift +53 -39
  55. package/ios/Source/sgcs/Nimo.swift +1928 -0
  56. package/ios/Source/sgcs/SGCManager.swift +12 -5
  57. package/ios/Source/stt/STTTools.swift +1 -1
  58. package/ios/Source/types/DeviceModels.swift +5 -0
  59. package/ios/Source/utils/Constants.swift +2 -0
  60. package/package.json +9 -1
  61. package/scripts/inject-ios-sdk-version.mjs +65 -0
  62. package/src/BluetoothSdk.types.ts +42 -17
  63. package/src/_internal.ts +4 -4
  64. package/src/_private/BluetoothSdkModule.ts +18 -2
  65. package/src/_private/cameraRequestPayload.ts +29 -0
  66. package/src/_private/photoRequestPayload.ts +9 -2
  67. package/src/index.ts +76 -58
@@ -39,27 +39,6 @@ data class SpeakingStatusEvent(
39
39
  val values: Map<String, Any>,
40
40
  )
41
41
 
42
- data class OtaUpdateAvailableEvent(
43
- val versionCode: Long?,
44
- val versionName: String?,
45
- val updates: List<String>,
46
- val totalSize: Long?,
47
- val cacheReady: Boolean?,
48
- val values: Map<String, Any>,
49
- ) {
50
- companion object {
51
- internal fun fromMap(values: Map<String, Any>): OtaUpdateAvailableEvent =
52
- OtaUpdateAvailableEvent(
53
- versionCode = longValue(values, "version_code"),
54
- versionName = stringValue(values, "version_name"),
55
- updates = (values["updates"] as? List<*>)?.filterIsInstance<String>() ?: emptyList(),
56
- totalSize = longValue(values, "total_size"),
57
- cacheReady = boolValue(values, "cache_ready"),
58
- values = values,
59
- )
60
- }
61
- }
62
-
63
42
  data class OtaStartAckEvent(
64
43
  val timestamp: Long?,
65
44
  val values: Map<String, Any>,
@@ -153,12 +132,12 @@ interface MentraBluetoothSdkListener {
153
132
  fun onGalleryStatus(event: GalleryStatusEvent) {}
154
133
  fun onPhotoResponse(event: PhotoResponseEvent) {}
155
134
  fun onPhotoStatus(event: PhotoStatusEvent) {}
135
+ fun onCameraStatus(event: CameraStatusEvent) {}
156
136
  fun onVideoRecordingStatus(event: VideoRecordingStatusEvent) {}
157
137
  fun onMediaUpload(event: MediaUploadEvent) {}
158
138
  fun onRgbLedControlResponse(event: RgbLedControlResponseEvent) {}
159
139
  fun onStreamStatus(event: StreamStatusEvent) {}
160
140
  fun onKeepAliveAck(event: KeepAliveAckEvent) {}
161
- fun onOtaUpdateAvailable(event: OtaUpdateAvailableEvent) {}
162
141
  fun onOtaStartAck(event: OtaStartAckEvent) {}
163
142
  fun onOtaStatus(event: OtaStatusEvent) {}
164
143
  fun onSettingsAck(event: SettingsAckEvent) {}
@@ -1614,25 +1614,72 @@ class G1 : SGCManager() {
1614
1614
 
1615
1615
  override fun clearDisplay() {
1616
1616
  Bridge.log("G1: clearDisplay() - sending space")
1617
- sendTextWall(" ")
1617
+ // Bypass the throttle (a clear must always land) and drop any pending caption so it can't
1618
+ // overwrite the clear after the fact.
1619
+ cancelPendingThrottledText()
1620
+ displayTextWall(" ")
1621
+ }
1622
+
1623
+ // G1-specific display throttle (300ms, last-wins) — see G1.swift. G1 firmware can't absorb
1624
+ // rapid text-wall updates; coalesce to the latest within a 300ms window. Belongs in the G1 SGC
1625
+ // (G1 hardware quirk); G2 deliberately does NOT throttle (it must show every caption). The
1626
+ // trailing flush always sends the most recent text, so the final caption is never dropped —
1627
+ // only intermediate frames within a window are coalesced.
1628
+ private val textThrottleHandler = Handler(Looper.getMainLooper())
1629
+ private var textThrottlePending: String? = null
1630
+ private var textThrottleLastSent: Long = 0L
1631
+ private var textThrottleScheduled = false
1632
+ private val TEXT_THROTTLE_WINDOW_MS = 300L
1633
+
1634
+ /** Drop any pending throttled text-wall flush so it can't later overwrite a newer, non-text
1635
+ * display write (a clear, double-text-wall, or bitmap). The posted flush no-ops when
1636
+ * `textThrottlePending` is null. Called from every G1 display path that bypasses the throttle. */
1637
+ private fun cancelPendingThrottledText() {
1638
+ textThrottlePending = null
1639
+ }
1640
+
1641
+ private fun throttledTextWall(text: String) {
1642
+ val now = android.os.SystemClock.uptimeMillis()
1643
+ val sinceLast = now - textThrottleLastSent
1644
+ if (sinceLast >= TEXT_THROTTLE_WINDOW_MS) {
1645
+ // Past the window — send now.
1646
+ textThrottleLastSent = now
1647
+ textThrottlePending = null
1648
+ displayTextWall(text)
1649
+ } else {
1650
+ // Inside the window — keep only the latest and schedule one trailing flush.
1651
+ textThrottlePending = text
1652
+ if (!textThrottleScheduled) {
1653
+ textThrottleScheduled = true
1654
+ textThrottleHandler.postDelayed({
1655
+ textThrottleScheduled = false
1656
+ val pending = textThrottlePending ?: return@postDelayed
1657
+ textThrottlePending = null
1658
+ textThrottleLastSent = android.os.SystemClock.uptimeMillis()
1659
+ displayTextWall(pending)
1660
+ }, TEXT_THROTTLE_WINDOW_MS - sinceLast)
1661
+ }
1662
+ }
1618
1663
  }
1619
1664
 
1620
1665
  override fun sendText(text: String) {
1621
1666
  Bridge.log("G1: sendText() - text: " + text)
1622
- displayTextWall(text)
1667
+ throttledTextWall(text)
1623
1668
  }
1624
1669
 
1625
1670
  override fun sendTextWall(text: String) {
1626
1671
  // Bridge.log("G1: sendTextWall() - text: " + text);
1627
- displayTextWall(text)
1672
+ throttledTextWall(text)
1628
1673
  }
1629
1674
 
1630
1675
  override fun sendDoubleTextWall(top: String, bottom: String) {
1676
+ cancelPendingThrottledText() // a newer layout supersedes any pending caption text
1631
1677
  Bridge.log("G1: sendDoubleTextWall() - top: " + top + ", bottom: " + bottom)
1632
1678
  displayDoubleTextWall(top, bottom)
1633
1679
  }
1634
1680
 
1635
1681
  override fun displayBitmap(base64ImageData: String, x: Int?, y: Int?, width: Int?, height: Int?): Boolean {
1682
+ cancelPendingThrottledText() // a bitmap supersedes any pending caption text
1636
1683
  try {
1637
1684
  // Decode base64 to byte array
1638
1685
  val bmpData = android.util.Base64.decode(base64ImageData, android.util.Base64.DEFAULT)