@mentra/bluetooth-sdk 0.1.10 → 0.1.11
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.
- package/README.md +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +39 -16
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +14 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +22 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +8 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +2 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +829 -643
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +40 -64
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +11 -1
- package/build/BluetoothSdk.types.d.ts +12 -1
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +1 -0
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +14 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +3 -0
- package/ios/Source/Bridge.swift +10 -0
- package/ios/Source/DeviceManager.swift +24 -21
- package/ios/Source/DeviceStore.swift +8 -2
- package/ios/Source/camera/CameraModels.swift +1 -0
- package/ios/Source/sgcs/G1.swift +3 -3
- package/ios/Source/sgcs/G2.swift +1022 -640
- package/ios/Source/sgcs/Mach1.swift +2 -2
- package/ios/Source/sgcs/MentraLive.swift +2 -2
- package/ios/Source/sgcs/MentraNex.swift +215 -85
- package/ios/Source/sgcs/SGCManager.swift +13 -4
- package/ios/Source/sgcs/Simulated.swift +2 -2
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +13 -1
- package/src/_private/BluetoothSdkModule.ts +22 -3
- package/src/index.ts +3 -0
package/README.md
CHANGED
|
@@ -409,7 +409,7 @@ For bare native iOS apps, use the public SwiftPM repository:
|
|
|
409
409
|
https://github.com/Mentra-Community/mentra-bluetooth-sdk-ios.git
|
|
410
410
|
```
|
|
411
411
|
|
|
412
|
-
Add the `MentraBluetoothSDK` product to your app target at version `0.1.
|
|
412
|
+
Add the `MentraBluetoothSDK` product to your app target at version `0.1.11` or newer.
|
|
413
413
|
|
|
414
414
|
For local SDK development, add this package folder directly in Xcode:
|
|
415
415
|
|
|
@@ -2,8 +2,11 @@ package com.mentra.bluetoothsdk
|
|
|
2
2
|
|
|
3
3
|
import com.mentra.bluetoothsdk.debug.BleTraceLogger
|
|
4
4
|
import com.mentra.bluetoothsdk.utils.DeviceTypes
|
|
5
|
+
import expo.modules.kotlin.functions.Coroutine
|
|
5
6
|
import expo.modules.kotlin.modules.Module
|
|
6
7
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
8
|
+
import kotlinx.coroutines.Dispatchers
|
|
9
|
+
import kotlinx.coroutines.withContext
|
|
7
10
|
|
|
8
11
|
class BluetoothSdkModule : Module() {
|
|
9
12
|
private var sdk: MentraBluetoothSdk? = null
|
|
@@ -195,6 +198,9 @@ class BluetoothSdkModule : Module() {
|
|
|
195
198
|
"glasses_not_ready",
|
|
196
199
|
"button_press",
|
|
197
200
|
"touch_event",
|
|
201
|
+
"accel_event",
|
|
202
|
+
"CompassHeadingEvent",
|
|
203
|
+
"CompassCalibrationEvent",
|
|
198
204
|
"head_up",
|
|
199
205
|
"voice_activity_detection_status",
|
|
200
206
|
"speaking_status",
|
|
@@ -534,7 +540,12 @@ class BluetoothSdkModule : Module() {
|
|
|
534
540
|
)
|
|
535
541
|
}
|
|
536
542
|
|
|
537
|
-
|
|
543
|
+
// Runs on Dispatchers.IO, not the shared Expo AsyncFunctionQueue: restart()
|
|
544
|
+
// does a synchronous JNI model reload that would otherwise block every other
|
|
545
|
+
// native call in the app until it completes.
|
|
546
|
+
AsyncFunction("restartTranscriber") Coroutine { ->
|
|
547
|
+
withContext(Dispatchers.IO) { deviceManager?.restartTranscriber() }
|
|
548
|
+
}
|
|
538
549
|
|
|
539
550
|
// MARK: - Audio Playback Monitoring
|
|
540
551
|
|
|
@@ -542,14 +553,17 @@ class BluetoothSdkModule : Module() {
|
|
|
542
553
|
sdk?.setOwnAppAudioPlaying(playing)
|
|
543
554
|
}
|
|
544
555
|
|
|
545
|
-
|
|
556
|
+
// *Blocking on Dispatchers.IO, not the shared AsyncFunctionQueue: these wait on
|
|
557
|
+
// a CountDownLatch (up to 5s) for a BLE round-trip, which would otherwise stall
|
|
558
|
+
// every other native call queued behind them.
|
|
559
|
+
AsyncFunction("getGlassesMediaVolume") Coroutine { ->
|
|
546
560
|
val cm = deviceManager ?: throw IllegalStateException("device_manager_null")
|
|
547
|
-
cm.getGlassesMediaVolumeBlocking()
|
|
561
|
+
withContext(Dispatchers.IO) { cm.getGlassesMediaVolumeBlocking() }
|
|
548
562
|
}
|
|
549
563
|
|
|
550
|
-
AsyncFunction("setGlassesMediaVolume") { level: Int ->
|
|
564
|
+
AsyncFunction("setGlassesMediaVolume") Coroutine { level: Int ->
|
|
551
565
|
val cm = deviceManager ?: throw IllegalStateException("device_manager_null")
|
|
552
|
-
cm.setGlassesMediaVolumeBlocking(level)
|
|
566
|
+
withContext(Dispatchers.IO) { cm.setGlassesMediaVolumeBlocking(level) }
|
|
553
567
|
}
|
|
554
568
|
|
|
555
569
|
// MARK: - RGB LED Control
|
|
@@ -605,8 +619,13 @@ class BluetoothSdkModule : Module() {
|
|
|
605
619
|
com.mentra.bluetoothsdk.stt.STTTools.validateSTTModel(path)
|
|
606
620
|
}
|
|
607
621
|
|
|
608
|
-
|
|
609
|
-
|
|
622
|
+
// Runs on Dispatchers.IO, not the shared Expo AsyncFunctionQueue: bz2/tar
|
|
623
|
+
// extraction of the 100–350MB model is a multi-minute, CPU-bound job. On the
|
|
624
|
+
// shared queue it froze every other native call in the app until it finished.
|
|
625
|
+
AsyncFunction("extractTarBz2") Coroutine { sourcePath: String, destinationPath: String ->
|
|
626
|
+
withContext(Dispatchers.IO) {
|
|
627
|
+
com.mentra.bluetoothsdk.stt.STTTools.extractTarBz2(sourcePath, destinationPath)
|
|
628
|
+
}
|
|
610
629
|
}
|
|
611
630
|
|
|
612
631
|
// MARK: - TTS Commands
|
|
@@ -647,7 +666,9 @@ class BluetoothSdkModule : Module() {
|
|
|
647
666
|
com.mentra.core.tts.TTSTools.validateTTSModel(path)
|
|
648
667
|
}
|
|
649
668
|
|
|
650
|
-
|
|
669
|
+
// Runs on Dispatchers.IO, not the shared Expo AsyncFunctionQueue: TTS synthesis
|
|
670
|
+
// is a synchronous JNI call that would otherwise block other native calls.
|
|
671
|
+
AsyncFunction("generateTtsAudio") Coroutine {
|
|
651
672
|
text: String,
|
|
652
673
|
modelPath: String,
|
|
653
674
|
outputPath: String,
|
|
@@ -657,14 +678,16 @@ class BluetoothSdkModule : Module() {
|
|
|
657
678
|
appContext.reactContext
|
|
658
679
|
?: appContext.currentActivity
|
|
659
680
|
?: throw IllegalStateException("No context available")
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
681
|
+
withContext(Dispatchers.IO) {
|
|
682
|
+
com.mentra.core.tts.TTSTools.generateTtsAudio(
|
|
683
|
+
context,
|
|
684
|
+
text,
|
|
685
|
+
modelPath,
|
|
686
|
+
outputPath,
|
|
687
|
+
speakerId,
|
|
688
|
+
speed.toFloat()
|
|
689
|
+
)
|
|
690
|
+
}
|
|
668
691
|
}
|
|
669
692
|
}
|
|
670
693
|
}
|
|
@@ -681,6 +681,20 @@ public class Bridge private constructor() {
|
|
|
681
681
|
sendTypedMessage("imu_gesture_event", eventBody as Map<String, Any>)
|
|
682
682
|
}
|
|
683
683
|
|
|
684
|
+
/**
|
|
685
|
+
* Send a single accelerometer reading from the glasses IMU - matches iOS
|
|
686
|
+
* Bridge.sendAccelEvent. A richer combined IMU event (gyro + magnetometer) is future work.
|
|
687
|
+
*/
|
|
688
|
+
@JvmStatic
|
|
689
|
+
fun sendAccelEvent(x: Float, y: Float, z: Float, timestamp: Long) {
|
|
690
|
+
val body = HashMap<String, Any>()
|
|
691
|
+
body["x"] = x
|
|
692
|
+
body["y"] = y
|
|
693
|
+
body["z"] = z
|
|
694
|
+
body["timestamp"] = timestamp
|
|
695
|
+
sendTypedMessage("accel_event", body as Map<String, Any>)
|
|
696
|
+
}
|
|
697
|
+
|
|
684
698
|
// Arbitrary WS Comms (don't use these, make a dedicated function for your use case):
|
|
685
699
|
|
|
686
700
|
/** Send WebSocket text message */
|
|
@@ -27,6 +27,9 @@ import com.mentra.bluetoothsdk.utils.MicMap
|
|
|
27
27
|
import com.mentra.bluetoothsdk.utils.MicTypes
|
|
28
28
|
import com.mentra.lc3Lib.Lc3Cpp
|
|
29
29
|
import com.mentra.bluetoothsdk.stt.SherpaOnnxTranscriber
|
|
30
|
+
import kotlinx.coroutines.CoroutineScope
|
|
31
|
+
import kotlinx.coroutines.Dispatchers
|
|
32
|
+
import kotlinx.coroutines.launch
|
|
30
33
|
import java.text.SimpleDateFormat
|
|
31
34
|
import java.util.*
|
|
32
35
|
import java.util.concurrent.CountDownLatch
|
|
@@ -1124,8 +1127,22 @@ class DeviceManager {
|
|
|
1124
1127
|
|
|
1125
1128
|
syncSystemTimeOnceForConnection(readyKey)
|
|
1126
1129
|
|
|
1127
|
-
//
|
|
1128
|
-
|
|
1130
|
+
// re-apply display height/depth after reconnection
|
|
1131
|
+
mainHandler.postDelayed(
|
|
1132
|
+
{
|
|
1133
|
+
val h =
|
|
1134
|
+
(DeviceStore.store.get("bluetooth", "dashboard_height") as? Number)
|
|
1135
|
+
?.toInt()
|
|
1136
|
+
?: 4
|
|
1137
|
+
val rawDepth =
|
|
1138
|
+
(DeviceStore.store.get("bluetooth", "dashboard_depth") as? Number)
|
|
1139
|
+
?.toInt()
|
|
1140
|
+
?: dashboardDepth // canonical default (2), not 1
|
|
1141
|
+
val d = rawDepth.coerceIn(1, 4)
|
|
1142
|
+
sgc?.setDashboardPosition(h, d)
|
|
1143
|
+
},
|
|
1144
|
+
2000
|
|
1145
|
+
)
|
|
1129
1146
|
|
|
1130
1147
|
// Show welcome message on first connect for all display glasses
|
|
1131
1148
|
if (shouldSendBootingMessage) {
|
|
@@ -1288,7 +1305,9 @@ class DeviceManager {
|
|
|
1288
1305
|
}
|
|
1289
1306
|
|
|
1290
1307
|
fun showNotificationsPanel() {
|
|
1291
|
-
|
|
1308
|
+
CoroutineScope(Dispatchers.Main).launch {
|
|
1309
|
+
sgc?.showNotificationsPanel()
|
|
1310
|
+
}
|
|
1292
1311
|
}
|
|
1293
1312
|
|
|
1294
1313
|
fun ping() {
|
|
@@ -121,6 +121,7 @@ object DeviceStore {
|
|
|
121
121
|
store.set("bluetooth", "dashboard_height", 4)
|
|
122
122
|
store.set("bluetooth", "dashboard_depth", 2)
|
|
123
123
|
store.set("bluetooth", "head_up_angle", 30)
|
|
124
|
+
store.set("bluetooth", "imu_enabled", false)
|
|
124
125
|
store.set("bluetooth", "contextual_dashboard", true)
|
|
125
126
|
store.set("bluetooth", "gallery_mode", true)
|
|
126
127
|
store.set("bluetooth", "voice_activity_detection_enabled", BluetoothSdkDefaults.VOICE_ACTIVITY_DETECTION_ENABLED)
|
|
@@ -238,6 +239,13 @@ object DeviceStore {
|
|
|
238
239
|
DeviceManager.getInstance().sgc?.setHeadUpAngle(angle)
|
|
239
240
|
}
|
|
240
241
|
}
|
|
242
|
+
"bluetooth" to "imu_enabled" -> {
|
|
243
|
+
(value as? Boolean)?.let { enabled ->
|
|
244
|
+
CoroutineScope(Dispatchers.Main).launch {
|
|
245
|
+
DeviceManager.getInstance().sgc?.setImuEnabled(enabled)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
241
249
|
"bluetooth" to "menu_apps" -> {
|
|
242
250
|
@Suppress("UNCHECKED_CAST")
|
|
243
251
|
(value as? List<Map<String, Any>>)?.let { items ->
|