@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.
- package/README.md +4 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +22 -9
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +5 -20
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +358 -261
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +3 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +133 -52
- package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +12 -12
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +22 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +1 -22
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +50 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +1184 -795
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +422 -78
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +3 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +2532 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +15 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +5 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +2 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +60 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
- package/build/BluetoothSdk.types.d.ts +39 -14
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +1 -0
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +4 -4
- package/build/_internal.js +4 -4
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +11 -3
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +5 -0
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/cameraRequestPayload.d.ts +4 -0
- package/build/_private/cameraRequestPayload.d.ts.map +1 -0
- package/build/_private/cameraRequestPayload.js +25 -0
- package/build/_private/cameraRequestPayload.js.map +1 -0
- package/build/_private/photoRequestPayload.d.ts.map +1 -1
- package/build/_private/photoRequestPayload.js +8 -2
- package/build/_private/photoRequestPayload.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +72 -54
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +40 -12
- package/ios/Source/BluetoothSdkDefaults.swift +2 -30
- package/ios/Source/Bridge.swift +4 -16
- package/ios/Source/DeviceManager.swift +72 -12
- package/ios/Source/MentraBluetoothSDK.swift +120 -50
- package/ios/Source/OtaManifest.swift +13 -13
- package/ios/Source/camera/CameraModels.swift +75 -9
- package/ios/Source/errors/{BluetoothError.swift → BluetoothSdkError.swift} +1 -1
- package/ios/Source/events/BluetoothEvents.swift +5 -27
- package/ios/Source/internal/BluetoothAvailability.swift +5 -5
- package/ios/Source/sgcs/G1.swift +51 -1
- package/ios/Source/sgcs/G2.swift +643 -203
- package/ios/Source/sgcs/MentraLive.swift +53 -39
- package/ios/Source/sgcs/Nimo.swift +1928 -0
- package/ios/Source/sgcs/SGCManager.swift +12 -5
- package/ios/Source/stt/STTTools.swift +1 -1
- package/ios/Source/types/DeviceModels.swift +5 -0
- package/ios/Source/utils/Constants.swift +2 -0
- package/package.json +9 -1
- package/scripts/inject-ios-sdk-version.mjs +65 -0
- package/src/BluetoothSdk.types.ts +42 -17
- package/src/_internal.ts +4 -4
- package/src/_private/BluetoothSdkModule.ts +18 -2
- package/src/_private/cameraRequestPayload.ts +29 -0
- package/src/_private/photoRequestPayload.ts +9 -2
- package/src/index.ts +76 -58
package/README.md
CHANGED
|
@@ -315,7 +315,7 @@ console.log(ledAck.state)
|
|
|
315
315
|
Settings commands that return `SettingsAckSuccessEvent` reject when the ASG reports an error ack. The SDK updates its local settings store only after that ASG ack resolves successfully, so observed SDK state reflects the acknowledged glasses state rather than a queued request. Raw `settings_ack` listener events still use `SettingsAckEvent` because they can include both success and failure statuses. `rgbLedControl(...)` resolves from a successful ASG `rgb_led_control_response` and rejects when the ASG reports `state: "error"`; raw `settings_ack` and `rgb_led_control_response` events remain available through listeners.
|
|
316
316
|
|
|
317
317
|
WiFi, hotspot, and version-info commands resolve from the ASG response path, not local dispatch:
|
|
318
|
-
`requestWifiScan()` resolves from the ASG `wifi_scan_result` completion response with the updated scan list, including `[]` when no networks are found. Intermediate `wifi_scan_result` events can arrive with `scanComplete: false` while the glasses stream discovered networks; the final event uses `scanComplete: true`. `sendWifiCredentials()` resolves when the requested SSID is connected, `forgetWifiNetwork()` resolves when that SSID is no longer connected, `setHotspotState()` resolves when the requested hotspot state is reported, and `requestVersionInfo()` resolves from the ASG `version_info` response instead of local store changes.
|
|
318
|
+
`requestWifiScan()` resolves from the ASG `wifi_scan_result` completion response with the updated scan list, including `[]` when no networks are found. Intermediate `wifi_scan_result` events can arrive with `scanComplete: false` while the glasses stream discovered networks; the final event uses `scanComplete: true`. If older glasses stream non-empty scan results but never send the completion event, the request resolves with the accumulated scan list when the request times out. `sendWifiCredentials()` resolves when the requested SSID is connected, `forgetWifiNetwork()` resolves when that SSID is no longer connected, `setHotspotState()` resolves when the requested hotspot state is reported, and `requestVersionInfo()` resolves from the ASG `version_info` response instead of local store changes.
|
|
319
319
|
|
|
320
320
|
React Native narrows returned values to success shapes where the raw listener event can also report errors:
|
|
321
321
|
|
|
@@ -327,7 +327,7 @@ React Native narrows returned values to success shapes where the raw listener ev
|
|
|
327
327
|
| `rgbLedControl(...)` | `RgbLedControlSuccessResponseEvent` with `state: "success"`. | Rejects when raw `rgb_led_control_response.state === "error"` or the response times out. |
|
|
328
328
|
| `checkForOtaUpdate()` | `boolean`, true when the configured OTA manifest has an ASG APK, MTK, or BES update for the connected glasses; false only when the manifest was checked successfully and no update is available. | Rejects when the glasses are disconnected, version info is unavailable, the manifest cannot be fetched, or the manifest response is invalid/missing required ASG app version fields. |
|
|
329
329
|
|
|
330
|
-
Android and iOS async APIs use `
|
|
330
|
+
Android and iOS async APIs use `BluetoothSdkException` / `BluetoothSdkError` for the same error paths. Their returned event structs are the successful response in normal `try`/`await` code, while raw listener/delegate events still include both success and error payloads.
|
|
331
331
|
|
|
332
332
|
`setMicState(true)` defaults to continuous microphone PCM from the glasses. The SDK does not apply phone-side Voice Activity Detection gating to microphone audio events. Glasses-side Voice Activity Detection is disabled by default for public SDK consumers; use `setVoiceActivityDetectionEnabled(true)` when you want supported glasses to gate microphone audio and emit live speaking status. `voice_activity_detection_status` reports whether glasses-side Voice Activity Detection is enabled, and `speaking_status` reports speaking/not-speaking when supported. Microphone events include the latest `voiceActivityDetectionEnabled` value.
|
|
333
333
|
|
|
@@ -369,8 +369,6 @@ OTA requires Mentra Live glasses firmware that supports the ASG OTA protocol and
|
|
|
369
369
|
|
|
370
370
|
```ts
|
|
371
371
|
const photo = await BluetoothSdk.requestPhoto({
|
|
372
|
-
requestId: `photo-${Date.now()}`,
|
|
373
|
-
appId: 'com.example.app',
|
|
374
372
|
size: 'medium',
|
|
375
373
|
webhookUrl: 'https://api.example.com/mentra/photo',
|
|
376
374
|
authToken: 'optional-token',
|
|
@@ -382,7 +380,7 @@ const photo = await BluetoothSdk.requestPhoto({
|
|
|
382
380
|
console.log('photo delivered', photo.photoUrl ?? photo.uploadUrl, photo.fileSizeBytes)
|
|
383
381
|
```
|
|
384
382
|
|
|
385
|
-
`requestPhoto(...)` resolves only after the full photo action reaches terminal success: capture completed and the photo was delivered to the webhook, either directly from the glasses over Wi-Fi or through the phone's Bluetooth fallback relay. It rejects if the ASG reports `state: "error"`, if phone-side fallback upload fails, if the SDK cannot send the command, or if no terminal `photo_response` arrives before the command timeout. Use `photo_status` for intermediate stages such as `accepted`, `configuring`, `capturing`, `captured`, `uploading`, `ble_fallback_compression`, `ready_for_transfer`, and `transferring`; `photo_status` is progress, while `photo_response` is terminal success/error. The raw `photo_response` event stream still includes both success and error events for subscribers. The webhook should accept multipart form data with a `photo` file and `requestId`. If `authToken` is provided, the uploader adds `Authorization: Bearer <token>`. The camera light is always enabled for photo capture.
|
|
383
|
+
`requestPhoto(...)` resolves only after the full photo action reaches terminal success: capture completed and the photo was delivered to the webhook, either directly from the glasses over Wi-Fi or through the phone's Bluetooth fallback relay. If you omit `requestId`, the SDK generates one and the terminal response includes it. It rejects if the ASG reports `state: "error"`, if phone-side fallback upload fails, if the SDK cannot send the command, or if no terminal `photo_response` arrives before the command timeout. Use `photo_status` for intermediate stages such as `accepted`, `configuring`, `capturing`, `captured`, `uploading`, `ble_fallback_compression`, `ready_for_transfer`, and `transferring`; `photo_status` is progress, while `photo_response` is terminal success/error. The raw `photo_response` event stream still includes both success and error events for subscribers. The webhook should accept multipart form data with a `photo` file and `requestId`. If `authToken` is provided, the uploader adds `Authorization: Bearer <token>`. The camera light is always enabled for photo capture.
|
|
386
384
|
|
|
387
385
|
For one-shot manual capture tuning, pass `exposureTimeNs` and `iso` together. `exposureTimeNs` is sensor exposure time in nanoseconds; `iso` is sensor ISO. If `exposureTimeNs` is omitted, `null`, invalid, or unsupported by the connected glasses, the camera uses auto exposure and ignores `iso`.
|
|
388
386
|
|
|
@@ -477,7 +475,7 @@ For bare native iOS apps, use the public SwiftPM repository:
|
|
|
477
475
|
https://github.com/Mentra-Community/mentra-bluetooth-sdk-ios.git
|
|
478
476
|
```
|
|
479
477
|
|
|
480
|
-
Select version `0.1.
|
|
478
|
+
Select version `0.1.16`, then add the `MentraBluetoothSDK` product to your app target.
|
|
481
479
|
|
|
482
480
|
For local SDK development, add this package folder directly in Xcode:
|
|
483
481
|
|
|
@@ -116,6 +116,10 @@ class BluetoothSdkModule : Module() {
|
|
|
116
116
|
sendEvent("photo_status", event.values)
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
override fun onCameraStatus(event: CameraStatusEvent) {
|
|
120
|
+
sendEvent("camera_status", event.values)
|
|
121
|
+
}
|
|
122
|
+
|
|
119
123
|
override fun onVideoRecordingStatus(event: VideoRecordingStatusEvent) {
|
|
120
124
|
sendEvent("video_recording_status", event.values)
|
|
121
125
|
}
|
|
@@ -136,10 +140,6 @@ class BluetoothSdkModule : Module() {
|
|
|
136
140
|
sendEvent("keep_alive_ack", event.values)
|
|
137
141
|
}
|
|
138
142
|
|
|
139
|
-
override fun onOtaUpdateAvailable(event: OtaUpdateAvailableEvent) {
|
|
140
|
-
sendEvent("ota_update_available", event.values)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
143
|
override fun onOtaStartAck(event: OtaStartAckEvent) {
|
|
144
144
|
sendEvent("ota_start_ack", event.values)
|
|
145
145
|
}
|
|
@@ -183,7 +183,7 @@ class BluetoothSdkModule : Module() {
|
|
|
183
183
|
|
|
184
184
|
private fun requireSdk(): MentraBluetoothSdk =
|
|
185
185
|
sdk
|
|
186
|
-
?: throw
|
|
186
|
+
?: throw BluetoothSdkException(
|
|
187
187
|
"sdk_not_initialized",
|
|
188
188
|
"Bluetooth SDK is not initialized.",
|
|
189
189
|
)
|
|
@@ -216,6 +216,7 @@ class BluetoothSdkModule : Module() {
|
|
|
216
216
|
"hotspot_error",
|
|
217
217
|
"photo_response",
|
|
218
218
|
"photo_status",
|
|
219
|
+
"camera_status",
|
|
219
220
|
"video_recording_status",
|
|
220
221
|
"media_success",
|
|
221
222
|
"media_error",
|
|
@@ -244,7 +245,6 @@ class BluetoothSdkModule : Module() {
|
|
|
244
245
|
"stream_status",
|
|
245
246
|
"keep_alive_ack",
|
|
246
247
|
"mtk_update_complete",
|
|
247
|
-
"ota_update_available",
|
|
248
248
|
"ota_progress",
|
|
249
249
|
"ota_start_ack",
|
|
250
250
|
"ota_status",
|
|
@@ -462,6 +462,10 @@ class BluetoothSdkModule : Module() {
|
|
|
462
462
|
requireSdk().setCameraFov(CameraFov(value, roiPosition)).values
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
+
AsyncFunction("setCameraTuningConfig") { anrOn: Boolean, gainOn: Boolean ->
|
|
466
|
+
requireSdk().setCameraTuningConfig(anrOn, gainOn).values
|
|
467
|
+
}
|
|
468
|
+
|
|
465
469
|
AsyncFunction("queryGalleryStatus") { requireSdk().queryGalleryStatus().values }
|
|
466
470
|
|
|
467
471
|
AsyncFunction("requestPhoto") { params: Map<String, Any?> ->
|
|
@@ -472,11 +476,22 @@ class BluetoothSdkModule : Module() {
|
|
|
472
476
|
}.toMap()
|
|
473
477
|
val req = PhotoRequest.fromMap(sanitized)
|
|
474
478
|
Bridge.log(
|
|
475
|
-
"NATIVE: PHOTO PIPELINE [3/6] BluetoothSdk.requestPhoto requestId=${req.requestId}
|
|
479
|
+
"NATIVE: PHOTO PIPELINE [3/6] BluetoothSdk.requestPhoto requestId=${req.requestId} size=${req.size} compress=${req.compress} sound=${req.sound} exposureTimeNs=${req.exposureTimeNs} iso=${req.iso}"
|
|
476
480
|
)
|
|
477
481
|
requireSdk().requestPhoto(req).values
|
|
478
482
|
}
|
|
479
483
|
|
|
484
|
+
AsyncFunction("warmUpCamera") { params: Map<String, Any?> ->
|
|
485
|
+
val requestId = params["requestId"] as? String
|
|
486
|
+
val size = PhotoSize.fromValue(params["size"] as? String)
|
|
487
|
+
val exposureRaw = (params["exposureTimeNs"] as? Number)?.toDouble()
|
|
488
|
+
val exposureTimeNs =
|
|
489
|
+
if (exposureRaw != null && exposureRaw.isFinite() && exposureRaw > 0) exposureRaw.toLong() else null
|
|
490
|
+
val durationRaw = (params["durationMs"] as? Number)?.toInt() ?: 0
|
|
491
|
+
val durationMs = if (durationRaw > 0) durationRaw else 15000
|
|
492
|
+
requireSdk().warmUpCamera(requestId, size, exposureTimeNs, durationMs).values
|
|
493
|
+
}
|
|
494
|
+
|
|
480
495
|
// MARK: - OTA Commands
|
|
481
496
|
|
|
482
497
|
Function("setOtaVersionUrl") { otaVersionUrl: String ->
|
|
@@ -502,8 +517,6 @@ class BluetoothSdkModule : Module() {
|
|
|
502
517
|
|
|
503
518
|
AsyncFunction("sendOtaQueryStatus") { requireSdk().sendOtaQueryStatus().values }
|
|
504
519
|
|
|
505
|
-
AsyncFunction("retryOtaVersionCheck") { requireSdk().retryOtaVersionCheck().values }
|
|
506
|
-
|
|
507
520
|
// MARK: - Version Info Commands
|
|
508
521
|
|
|
509
522
|
AsyncFunction("requestVersionInfo") { requireSdk().requestVersionInfo().toMap() }
|
|
@@ -374,6 +374,11 @@ public class Bridge private constructor() {
|
|
|
374
374
|
sendTypedMessage("photo_status", statusJson)
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
@JvmStatic
|
|
378
|
+
fun sendCameraStatus(statusJson: Map<String, Any>) {
|
|
379
|
+
sendTypedMessage("camera_status", statusJson)
|
|
380
|
+
}
|
|
381
|
+
|
|
377
382
|
@JvmStatic
|
|
378
383
|
fun sendPhotoResponse(responseJson: Map<String, Any>) {
|
|
379
384
|
sendTypedMessage("photo_response", responseJson)
|
|
@@ -587,26 +592,6 @@ public class Bridge private constructor() {
|
|
|
587
592
|
sendTypedMessage("mtk_update_complete", eventBody as Map<String, Any>)
|
|
588
593
|
}
|
|
589
594
|
|
|
590
|
-
/**
|
|
591
|
-
* Send OTA update available notification - glasses have detected an available update
|
|
592
|
-
* (background mode)
|
|
593
|
-
*/
|
|
594
|
-
@JvmStatic
|
|
595
|
-
fun sendOtaUpdateAvailable(
|
|
596
|
-
versionCode: Long,
|
|
597
|
-
versionName: String,
|
|
598
|
-
updates: List<String>,
|
|
599
|
-
totalSize: Long
|
|
600
|
-
) {
|
|
601
|
-
val eventBody = HashMap<String, Any>()
|
|
602
|
-
eventBody["version_code"] = versionCode
|
|
603
|
-
eventBody["version_name"] = versionName
|
|
604
|
-
eventBody["updates"] = updates
|
|
605
|
-
eventBody["total_size"] = totalSize
|
|
606
|
-
|
|
607
|
-
sendTypedMessage("ota_update_available", eventBody as Map<String, Any>)
|
|
608
|
-
}
|
|
609
|
-
|
|
610
595
|
/** Send ota_start_ack — glasses confirmed receipt of ota_start command */
|
|
611
596
|
@JvmStatic
|
|
612
597
|
fun sendOtaStartAck() {
|