@mentra/bluetooth-sdk 0.1.13 → 0.1.14

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 (53) hide show
  1. package/README.md +15 -3
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +27 -40
  3. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +7 -13
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +0 -4
  5. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +19 -20
  6. package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +3 -5
  7. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +1 -2
  8. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +1 -2
  9. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +1 -5
  10. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +0 -5
  11. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +1 -5
  12. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +1 -39
  13. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -2
  14. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +2 -4
  15. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +3 -7
  16. package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +0 -6
  17. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +16 -33
  18. package/build/BluetoothSdk.types.d.ts +12 -11
  19. package/build/BluetoothSdk.types.d.ts.map +1 -1
  20. package/build/BluetoothSdk.types.js.map +1 -1
  21. package/build/_private/BluetoothSdkModule.d.ts +4 -6
  22. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  23. package/build/_private/BluetoothSdkModule.js +0 -4
  24. package/build/_private/BluetoothSdkModule.js.map +1 -1
  25. package/build/_private/photoRequestPayload.d.ts.map +1 -1
  26. package/build/_private/photoRequestPayload.js +3 -1
  27. package/build/_private/photoRequestPayload.js.map +1 -1
  28. package/build/index.d.ts +1 -1
  29. package/build/index.d.ts.map +1 -1
  30. package/build/index.js +3 -12
  31. package/build/index.js.map +1 -1
  32. package/ios/BluetoothSdkModule.swift +11 -16
  33. package/ios/Source/DeviceManager.swift +5 -11
  34. package/ios/Source/DeviceStore.swift +0 -4
  35. package/ios/Source/MentraBluetoothSDK.swift +24 -28
  36. package/ios/Source/camera/CameraModels.swift +7 -11
  37. package/ios/Source/controllers/ControllerManager.swift +1 -2
  38. package/ios/Source/controllers/R1.swift +1 -2
  39. package/ios/Source/sgcs/Frame.swift +0 -2
  40. package/ios/Source/sgcs/G1.swift +1 -3
  41. package/ios/Source/sgcs/G2.swift +1 -2
  42. package/ios/Source/sgcs/Mach1.swift +1 -3
  43. package/ios/Source/sgcs/MentraLive.swift +12 -39
  44. package/ios/Source/sgcs/MentraNex.swift +1 -3
  45. package/ios/Source/sgcs/SGCManager.swift +4 -5
  46. package/ios/Source/sgcs/Simulated.swift +2 -6
  47. package/ios/Source/status/DeviceStatus.swift +0 -8
  48. package/ios/Source/streaming/StreamModels.swift +7 -32
  49. package/package.json +1 -1
  50. package/src/BluetoothSdk.types.ts +12 -11
  51. package/src/_private/BluetoothSdkModule.ts +4 -11
  52. package/src/_private/photoRequestPayload.ts +3 -1
  53. package/src/index.ts +7 -15
package/README.md CHANGED
@@ -405,8 +405,8 @@ await BluetoothSdk.startStream({
405
405
  await BluetoothSdk.stopStream()
406
406
  ```
407
407
 
408
- Use `rtmp://` or `rtmps://` for RTMP, `srt://` for SRT, and `http://` or `https://` for WHIP/WebRTC ingest. `startStream()` resolves with the correlated `stream_status` event once the glasses report `status: "streaming"`; `stopStream()` resolves when the glasses report `status: "stopped"` or confirms the stream was already stopped / not streaming. `stopStream()` returns a normalized stopped event for that already-stopped case. Stream starts reject if the glasses report an error before streaming; stream stops reject for real stop errors, send failure, another stop in flight, or timeout. The SDK sends stream keep-alives automatically while streaming and reports keep-alive failures through `stream_status`. The camera light is always enabled while streaming.
409
- `stream_status` events may include `resolvedConfig`, which reports the effective transport, video, and audio settings after glasses defaults, clamps, and camera preflight.
408
+ Use `rtmp://` or `rtmps://` for RTMP, `srt://` for SRT, and `http://` or `https://` for WHIP/WebRTC ingest. `startStream()` resolves with the correlated `stream_status` event once the glasses report `status: "streaming"`; `stopStream()` resolves when the glasses report `status: "stopped"` or confirms the stream was already stopped / not streaming. `stopStream()` returns a normalized stopped event for that already-stopped case. Stream starts reject if the glasses report an error before streaming; stream stops reject for real stop errors, send failure, another stop in flight, or timeout. Stream video input fields are `width`, `height`, `bitrate`, and `fps`. The SDK sends stream keep-alives automatically while streaming and reports keep-alive failures through `stream_status`. The camera light is always enabled while streaming.
409
+ `stream_status` events may include `resolvedConfig`, which reports the effective transport, video, and audio settings after glasses defaults, clamps, and camera preflight. The resolved effective frame rate is reported as `resolvedConfig.video.fps`.
410
410
 
411
411
  ## Events
412
412
 
@@ -459,13 +459,25 @@ MENTRA_BLUETOOTH_SDK_PACKAGE_PATH=/path/to/MentraOS/mobile/modules/bluetooth-sdk
459
459
 
460
460
  Use `bunx expo run:android` for Android. Keep local paths in your shell or CI environment, not in committed app config.
461
461
 
462
+ For local Android source compile checks inside this monorepo, run from the
463
+ MentraOS repo root:
464
+
465
+ ```sh
466
+ ./scripts/check-android-compile.sh bluetooth-sdk
467
+ ```
468
+
469
+ The `android/` folder in this package is source for the generated Expo Android
470
+ project, not the local Gradle entrypoint. The check script prepares
471
+ `mobile/android` and uses its Gradle wrapper with `-PmentraPublicSdk=true`,
472
+ matching the CI release workflow's public SDK dependency mode.
473
+
462
474
  For bare native iOS apps, use the public SwiftPM repository:
463
475
 
464
476
  ```text
465
477
  https://github.com/Mentra-Community/mentra-bluetooth-sdk-ios.git
466
478
  ```
467
479
 
468
- Select version `0.1.13`, then add the `MentraBluetoothSDK` product to your app target.
480
+ Select version `0.1.14`, then add the `MentraBluetoothSDK` product to your app target.
469
481
 
470
482
  For local SDK development, add this package folder directly in Xcode:
471
483
 
@@ -441,49 +441,16 @@ class BluetoothSdkModule : Module() {
441
441
  sdk?.setVoiceActivityDetectionEnabled(enabled)
442
442
  }
443
443
 
444
- AsyncFunction("setButtonPhotoCaptureSettings") { params: Map<String, Any?> ->
445
- val size = (params["size"] as? String)?.let { ButtonPhotoSize.fromValue(it) }
446
- val mfnr = params["mfnr"] as? Boolean
447
- val zsl = params["zsl"] as? Boolean
448
- val noiseReduction = params["noiseReduction"] as? Boolean
449
- val edgeEnhancement = params["edgeEnhancement"] as? Boolean
450
- val ispDigitalGain = (params["ispDigitalGain"] as? Number)?.toInt()
451
- val ispAnalogGain = params["ispAnalogGain"] as? String
452
- val aeExposureDivisor = (params["aeExposureDivisor"] as? Number)?.toInt()
453
- val isoCap = (params["isoCap"] as? Number)?.toInt()
454
- val compress = params["compress"] as? String
455
- val sound = params["sound"] as? Boolean
456
- val resetCaptureTuning = params["resetCaptureTuning"] as? Boolean == true
457
- requireSdk()
458
- .setButtonPhotoSettings(
459
- ButtonPhotoSettings(
460
- size = size,
461
- mfnr = mfnr,
462
- zsl = zsl,
463
- noiseReduction = noiseReduction,
464
- edgeEnhancement = edgeEnhancement,
465
- ispDigitalGain = ispDigitalGain,
466
- ispAnalogGain = ispAnalogGain,
467
- aeExposureDivisor = aeExposureDivisor,
468
- isoCap = isoCap,
469
- compress = compress,
470
- sound = sound,
471
- resetCaptureTuning = resetCaptureTuning,
472
- ),
473
- )
474
- .values
475
- }
476
-
477
- AsyncFunction("setButtonVideoRecordingSettings") { width: Int, height: Int, fps: Int ->
478
- requireSdk().setButtonVideoRecordingSettings(width, height, fps).values
444
+ AsyncFunction("setPhotoCaptureDefaults") { params: Map<String, Any?> ->
445
+ requireSdk().setPhotoCaptureDefaults(params.toPhotoCaptureDefaults()).values
479
446
  }
480
447
 
481
- AsyncFunction("setButtonCameraLed") { enabled: Boolean ->
482
- requireSdk().setButtonCameraLed(enabled).values
448
+ AsyncFunction("setVideoRecordingDefaults") { width: Int, height: Int, fps: Int ->
449
+ requireSdk().setVideoRecordingDefaults(VideoRecordingDefaults(width, height, fps)).values
483
450
  }
484
451
 
485
- AsyncFunction("setButtonMaxRecordingTime") { minutes: Int ->
486
- requireSdk().setButtonMaxRecordingTime(minutes).values
452
+ AsyncFunction("setMaxVideoRecordingDuration") { minutes: Int ->
453
+ requireSdk().setMaxVideoRecordingDuration(minutes).values
487
454
  }
488
455
 
489
456
  AsyncFunction("setCameraFov") { fov: Map<String, Any> ->
@@ -505,7 +472,7 @@ class BluetoothSdkModule : Module() {
505
472
  }.toMap()
506
473
  val req = PhotoRequest.fromMap(sanitized)
507
474
  Bridge.log(
508
- "NATIVE: PHOTO PIPELINE [3/6] BluetoothSdk.requestPhoto requestId=${req.requestId} appId=${req.appId} size=${req.size} compress=${req.compress} flash=${req.flash} sound=${req.sound} exposureTimeNs=${req.exposureTimeNs} iso=${req.iso}"
475
+ "NATIVE: PHOTO PIPELINE [3/6] BluetoothSdk.requestPhoto requestId=${req.requestId} appId=${req.appId} size=${req.size} compress=${req.compress} sound=${req.sound} exposureTimeNs=${req.exposureTimeNs} iso=${req.iso}"
509
476
  )
510
477
  requireSdk().requestPhoto(req).values
511
478
  }
@@ -586,6 +553,10 @@ class BluetoothSdkModule : Module() {
586
553
  requireSdk().startStream(StreamRequest.fromMap(params)).values
587
554
  }
588
555
 
556
+ AsyncFunction("startExternallyManagedStream") { params: Map<String, Any> ->
557
+ requireSdk().startExternallyManagedStream(StreamRequest.fromMap(params)).values
558
+ }
559
+
589
560
  AsyncFunction("stopStream") { requireSdk().stopStream().values }
590
561
 
591
562
  AsyncFunction("sendExternallyManagedStreamKeepAlive") { params: Map<String, Any> ->
@@ -775,6 +746,22 @@ private fun Map<String, Any>?.toMentraDevice(): Device? {
775
746
  )
776
747
  }
777
748
 
749
+ private fun Map<String, Any?>.toPhotoCaptureDefaults(): PhotoCaptureDefaults =
750
+ PhotoCaptureDefaults(
751
+ size = (this["size"] as? String)?.let { PhotoSize.fromValue(it) },
752
+ mfnr = this["mfnr"] as? Boolean,
753
+ zsl = this["zsl"] as? Boolean,
754
+ noiseReduction = this["noiseReduction"] as? Boolean,
755
+ edgeEnhancement = this["edgeEnhancement"] as? Boolean,
756
+ ispDigitalGain = (this["ispDigitalGain"] as? Number)?.toInt(),
757
+ ispAnalogGain = this["ispAnalogGain"] as? String,
758
+ aeExposureDivisor = (this["aeExposureDivisor"] as? Number)?.toInt(),
759
+ isoCap = (this["isoCap"] as? Number)?.toInt(),
760
+ compress = this["compress"] as? String,
761
+ sound = this["sound"] as? Boolean,
762
+ resetCaptureTuning = this["resetCaptureTuning"] as? Boolean == true,
763
+ )
764
+
778
765
  private fun Map<String, Any>?.toMentraConnectOptions(): ConnectOptions {
779
766
  val values = this ?: return ConnectOptions()
780
767
  return ConnectOptions(
@@ -1365,7 +1365,6 @@ class DeviceManager {
1365
1365
 
1366
1366
  fun startStream(message: MutableMap<String, Any>) {
1367
1367
  Bridge.log("MAN: startStream")
1368
- message["flash"] = true
1369
1368
  sgc?.startStream(message)
1370
1369
  }
1371
1370
 
@@ -1435,10 +1434,10 @@ class DeviceManager {
1435
1434
  }
1436
1435
 
1437
1436
  fun sendButtonPhotoSettings(requestId: String, size: String) {
1438
- sendButtonPhotoSettings(requestId, ButtonPhotoSettings(ButtonPhotoSize.fromValue(size)))
1437
+ sendButtonPhotoSettings(requestId, PhotoCaptureDefaults(PhotoSize.fromValue(size)))
1439
1438
  }
1440
1439
 
1441
- fun sendButtonPhotoSettings(requestId: String, settings: ButtonPhotoSettings) {
1440
+ fun sendButtonPhotoSettings(requestId: String, settings: PhotoCaptureDefaults) {
1442
1441
  val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
1443
1442
  live.sendButtonPhotoSettings(
1444
1443
  requestId,
@@ -1462,11 +1461,6 @@ class DeviceManager {
1462
1461
  live.sendButtonVideoRecordingSettings(requestId, width, height, fps)
1463
1462
  }
1464
1463
 
1465
- fun sendButtonCameraLedSetting(requestId: String, enabled: Boolean) {
1466
- val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
1467
- live.sendButtonCameraLedSetting(requestId, enabled)
1468
- }
1469
-
1470
1464
  fun sendButtonMaxRecordingTime(requestId: String, minutes: Int) {
1471
1465
  val live = sgc as? MentraLive ?: throw IllegalStateException("unsupported_device")
1472
1466
  live.sendButtonMaxRecordingTime(requestId, minutes)
@@ -1565,11 +1559,11 @@ class DeviceManager {
1565
1559
  maxRecordingTimeMinutes: Int = 0,
1566
1560
  ) {
1567
1561
  Bridge.log(
1568
- "MAN: onStartVideoRecording: requestId=$requestId, save=$save, flash=true, sound=$sound, " +
1562
+ "MAN: onStartVideoRecording: requestId=$requestId, save=$save, sound=$sound, " +
1569
1563
  "resolution=${width}x${height}@${fps}fps, maxRecordingTimeMinutes=$maxRecordingTimeMinutes"
1570
1564
  )
1571
1565
  sgc?.startVideoRecording(
1572
- requestId, save, true, sound, width, height, fps, maxRecordingTimeMinutes)
1566
+ requestId, save, sound, width, height, fps, maxRecordingTimeMinutes)
1573
1567
  }
1574
1568
 
1575
1569
  fun stopVideoRecording(requestId: String, webhookUrl: String?, authToken: String?) {
@@ -1606,11 +1600,11 @@ class DeviceManager {
1606
1600
  val manualIso = if (exposureNs != null) request.iso?.takeIf { it > 0 } else null
1607
1601
  val routed =
1608
1602
  request.copy(
1609
- exposureTimeNs = exposureNs?.toDouble(),
1610
- iso = manualIso,
1603
+ exposureTimeNs = exposureNs?.toDouble(),
1604
+ iso = manualIso,
1611
1605
  )
1612
1606
  Bridge.log(
1613
- "MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=${routed.requestId} appId=${routed.appId} size=${routed.size.value} compress=${routed.compress.value} flash=${routed.flash} save=${routed.save} sound=${routed.sound} exposureTimeNs=$exposureNs iso=${manualIso ?: "auto"} aeDivisor=${routed.aeExposureDivisor} isoCap=${routed.isoCap} sgc=${sgc?.javaClass?.simpleName ?: "null"}"
1607
+ "MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=${routed.requestId} appId=${routed.appId} 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"}"
1614
1608
  )
1615
1609
  val activeSgc = sgc
1616
1610
  if (activeSgc == null) {
@@ -127,7 +127,6 @@ object DeviceStore {
127
127
  store.set("bluetooth", "voice_activity_detection_enabled", BluetoothSdkDefaults.VOICE_ACTIVITY_DETECTION_ENABLED)
128
128
  store.set("bluetooth", "screen_disabled", false)
129
129
  store.set("bluetooth", "button_photo_size", "max")
130
- store.set("bluetooth", "button_camera_led", true)
131
130
  store.set("bluetooth", "button_max_recording_time", 10)
132
131
  store.set("bluetooth", "camera_fov", mapOf("fov" to 118, "roi_position" to 0))
133
132
  store.set("bluetooth", "button_video_width", 1280)
@@ -287,9 +286,6 @@ object DeviceStore {
287
286
  "bluetooth" to "button_photo_size" -> {
288
287
  DeviceManager.getInstance().sgc?.sendButtonPhotoSettings()
289
288
  }
290
- "bluetooth" to "button_camera_led" -> {
291
- DeviceManager.getInstance().sgc?.sendButtonCameraLedSetting()
292
- }
293
289
  "bluetooth" to "button_max_recording_time" -> {
294
290
  DeviceManager.getInstance().sgc?.sendButtonMaxRecordingTime()
295
291
  }
@@ -474,7 +474,7 @@ class MentraBluetoothSdk private constructor(
474
474
  DeviceStore.apply(ObservableStore.BLUETOOTH_CATEGORY, "voice_activity_detection_enabled", enabled)
475
475
  }
476
476
 
477
- fun setButtonPhotoSettings(settings: ButtonPhotoSettings): SettingsAckEvent =
477
+ fun setPhotoCaptureDefaults(settings: PhotoCaptureDefaults): SettingsAckEvent =
478
478
  performSettingsCommand(
479
479
  setting = "button_photo",
480
480
  updateStore = { _ ->
@@ -527,30 +527,20 @@ class MentraBluetoothSdk private constructor(
527
527
  send = { requestId -> deviceManager.sendButtonPhotoSettings(requestId, settings) },
528
528
  )
529
529
 
530
- fun setButtonVideoRecordingSettings(width: Int, height: Int, fps: Int): SettingsAckEvent =
530
+ fun setVideoRecordingDefaults(defaults: VideoRecordingDefaults): SettingsAckEvent =
531
531
  performSettingsCommand(
532
532
  setting = "button_video_recording",
533
533
  updateStore = { _ ->
534
- DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_width", width)
535
- DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_height", height)
536
- DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_fps", fps)
534
+ DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_width", defaults.width)
535
+ DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_height", defaults.height)
536
+ DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_video_fps", defaults.fps)
537
537
  },
538
538
  send = { requestId ->
539
- deviceManager.sendButtonVideoRecordingSettings(requestId, width, height, fps)
539
+ deviceManager.sendButtonVideoRecordingSettings(requestId, defaults.width, defaults.height, defaults.fps)
540
540
  },
541
541
  )
542
542
 
543
- fun setButtonVideoRecordingSettings(settings: ButtonVideoRecordingSettings): SettingsAckEvent =
544
- setButtonVideoRecordingSettings(width = settings.width, height = settings.height, fps = settings.fps)
545
-
546
- fun setButtonCameraLed(enabled: Boolean): SettingsAckEvent =
547
- performSettingsCommand(
548
- setting = "button_camera_led",
549
- updateStore = { _ -> DeviceStore.set(ObservableStore.BLUETOOTH_CATEGORY, "button_camera_led", enabled) },
550
- send = { requestId -> deviceManager.sendButtonCameraLedSetting(requestId, enabled) },
551
- )
552
-
553
- fun setButtonMaxRecordingTime(minutes: Int): SettingsAckEvent =
543
+ fun setMaxVideoRecordingDuration(minutes: Int): SettingsAckEvent =
554
544
  performSettingsCommand(
555
545
  setting = "button_max_recording_time",
556
546
  updateStore = { _ ->
@@ -754,7 +744,16 @@ class MentraBluetoothSdk private constructor(
754
744
  }
755
745
  }
756
746
 
757
- fun startStream(request: StreamRequest): StreamStatusEvent {
747
+ fun startStream(request: StreamRequest): StreamStatusEvent =
748
+ startStream(request, startSdkKeepAlive = true)
749
+
750
+ internal fun startExternallyManagedStream(request: StreamRequest): StreamStatusEvent =
751
+ startStream(request, startSdkKeepAlive = false)
752
+
753
+ private fun startStream(
754
+ request: StreamRequest,
755
+ startSdkKeepAlive: Boolean,
756
+ ): StreamStatusEvent {
758
757
  val message = request.toMap().toMutableMap()
759
758
  val streamId = (message["streamId"] as? String)?.takeIf { it.isNotBlank() }
760
759
  ?: "sdk-${UUID.randomUUID()}"
@@ -765,8 +764,8 @@ class MentraBluetoothSdk private constructor(
765
764
  try {
766
765
  deviceManager.startStream(message)
767
766
  val event = pending.await(STREAM_START_TIMEOUT_MS)
768
- if (request.keepAlive && !request.isExternallyManagedKeepAlive()) {
769
- startStreamKeepAliveMonitor(streamId, request.keepAliveIntervalSeconds)
767
+ if (startSdkKeepAlive) {
768
+ startStreamKeepAliveMonitor(streamId, DEFAULT_STREAM_KEEP_ALIVE_INTERVAL_SECONDS)
770
769
  }
771
770
  return event
772
771
  } finally {
@@ -56,8 +56,8 @@ enum class PhotoCompression(val value: String) {
56
56
  }
57
57
  }
58
58
 
59
- data class ButtonPhotoSettings(
60
- val size: ButtonPhotoSize? = null,
59
+ data class PhotoCaptureDefaults(
60
+ val size: PhotoSize? = null,
61
61
  val mfnr: Boolean? = null,
62
62
  val zsl: Boolean? = null,
63
63
  val noiseReduction: Boolean? = null,
@@ -71,7 +71,7 @@ data class ButtonPhotoSettings(
71
71
  val resetCaptureTuning: Boolean = false,
72
72
  )
73
73
 
74
- data class ButtonVideoRecordingSettings(
74
+ data class VideoRecordingDefaults(
75
75
  val width: Int,
76
76
  val height: Int,
77
77
  val fps: Int,
@@ -167,7 +167,6 @@ data class PhotoRequest @JvmOverloads constructor(
167
167
  val webhookUrl: String,
168
168
  val authToken: String? = null,
169
169
  val compress: PhotoCompression = PhotoCompression.MEDIUM,
170
- val flash: Boolean = true,
171
170
  val save: Boolean = false,
172
171
  val sound: Boolean = true,
173
172
  /** Sensor exposure time for this capture only (ns), or null for auto exposure */
@@ -218,7 +217,6 @@ data class PhotoRequest @JvmOverloads constructor(
218
217
  webhookUrl = stringValue(values, "webhookUrl", "webhook_url").orEmpty(),
219
218
  authToken = stringValue(values, "authToken", "auth_token")?.takeIf { it.isNotBlank() },
220
219
  compress = PhotoCompression.fromValue(stringValue(values, "compress") ?: "none"),
221
- flash = boolValue(values, "flash") ?: true,
222
220
  save = boolValue(values, "save", "saveToGallery") ?: false,
223
221
  sound = boolValue(values, "sound") ?: true,
224
222
  exposureTimeNs = exposureTimeNs,
@@ -20,14 +20,13 @@ abstract class ControllerManager {
20
20
  abstract fun startStream(message: Map<String, Any>)
21
21
  abstract fun stopStream()
22
22
  abstract fun sendStreamKeepAlive(message: Map<String, Any>)
23
- abstract fun startVideoRecording(requestId: String, save: Boolean, flash: Boolean, sound: Boolean)
23
+ abstract fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean)
24
24
  abstract fun stopVideoRecording(requestId: String)
25
25
 
26
26
  // Button Settings
27
27
  abstract fun sendButtonPhotoSettings()
28
28
  abstract fun sendButtonVideoRecordingSettings()
29
29
  abstract fun sendButtonMaxRecordingTime()
30
- abstract fun sendButtonCameraLedSetting()
31
30
 
32
31
  // Display Control
33
32
  abstract fun setBrightness(level: Int, autoMode: Boolean)
@@ -863,7 +863,7 @@ class R1 : ControllerManager() {
863
863
  override fun sortMicRanking(list: MutableList<String>): MutableList<String> = list
864
864
  override fun sendJson(jsonOriginal: Map<String, Any>, wakeUp: Boolean, requireAck: Boolean) {}
865
865
  override fun requestPhoto(request: PhotoRequest) {}
866
- override fun startVideoRecording(requestId: String, save: Boolean, flash: Boolean, sound: Boolean) {}
866
+ override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {}
867
867
  override fun stopVideoRecording(requestId: String) {}
868
868
  override fun startStream(message: Map<String, Any>) {}
869
869
  override fun stopStream() {}
@@ -871,7 +871,6 @@ class R1 : ControllerManager() {
871
871
  override fun sendButtonPhotoSettings() {}
872
872
  override fun sendButtonVideoRecordingSettings() {}
873
873
  override fun sendButtonMaxRecordingTime() {}
874
- override fun sendButtonCameraLedSetting() {}
875
874
  override fun setBrightness(level: Int, autoMode: Boolean) {}
876
875
  override fun clearDisplay() {}
877
876
  override fun sendTextWall(text: String) {}
@@ -1584,7 +1584,7 @@ class G1 : SGCManager() {
1584
1584
 
1585
1585
  }
1586
1586
 
1587
- override fun startVideoRecording(requestId: String, save: Boolean, flash: Boolean, sound: Boolean) {
1587
+ override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {
1588
1588
 
1589
1589
  }
1590
1590
 
@@ -1600,10 +1600,6 @@ class G1 : SGCManager() {
1600
1600
 
1601
1601
  }
1602
1602
 
1603
- override fun sendButtonCameraLedSetting() {
1604
-
1605
- }
1606
-
1607
1603
  override fun sendCameraFovSetting() {
1608
1604
  }
1609
1605
 
@@ -2660,7 +2660,6 @@ class G2 : SGCManager() {
2660
2660
  override fun startVideoRecording(
2661
2661
  requestId: String,
2662
2662
  save: Boolean,
2663
- flash: Boolean,
2664
2663
  sound: Boolean
2665
2664
  ) {
2666
2665
  Bridge.log("G2: startVideoRecording - not supported")
@@ -2683,10 +2682,6 @@ class G2 : SGCManager() {
2683
2682
  Bridge.log("G2: sendButtonMaxRecordingTime")
2684
2683
  }
2685
2684
 
2686
- override fun sendButtonCameraLedSetting() {
2687
- Bridge.log("G2: sendButtonCameraLedSetting")
2688
- }
2689
-
2690
2685
  override fun sendCameraFovSetting() {
2691
2686
  Bridge.log("G2: sendCameraFovSetting")
2692
2687
  }
@@ -183,7 +183,7 @@ class Mach1 : SGCManager() {
183
183
 
184
184
  }
185
185
 
186
- override fun startVideoRecording(requestId: String, save: Boolean, flash: Boolean, sound: Boolean) {
186
+ override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {
187
187
 
188
188
  }
189
189
 
@@ -203,10 +203,6 @@ class Mach1 : SGCManager() {
203
203
 
204
204
  }
205
205
 
206
- override fun sendButtonCameraLedSetting() {
207
-
208
- }
209
-
210
206
  override fun sendCameraFovSetting() {
211
207
  }
212
208
 
@@ -5207,7 +5207,6 @@ class MentraLive : SGCManager() {
5207
5207
  val webhookUrl = request.webhookUrl
5208
5208
  val authToken = request.authToken
5209
5209
  val compress = request.compress.value
5210
- val flash = request.flash
5211
5210
  val save = request.save
5212
5211
  val sound = request.sound
5213
5212
  val exposureTimeNs = request.exposureTimeNs
@@ -5226,8 +5225,6 @@ class MentraLive : SGCManager() {
5226
5225
  (if (hasAuthToken) "***" else "none") +
5227
5226
  ", compress=" +
5228
5227
  compress +
5229
- ", flash=" +
5230
- flash +
5231
5228
  ", save=" +
5232
5229
  save +
5233
5230
  ", sound=" +
@@ -5267,7 +5264,6 @@ class MentraLive : SGCManager() {
5267
5264
  } else {
5268
5265
  json.put("compress", "none")
5269
5266
  }
5270
- json.put("flash", flash)
5271
5267
  json.put("save", save)
5272
5268
  json.put("sound", sound)
5273
5269
  if (exposureTimeNs != null && exposureTimeNs > 0L) {
@@ -6144,25 +6140,6 @@ class MentraLive : SGCManager() {
6144
6140
  }
6145
6141
  }
6146
6142
 
6147
- fun sendButtonCameraLedSetting(enabled: Boolean) {
6148
- sendButtonCameraLedSetting(null, enabled)
6149
- }
6150
-
6151
- fun sendButtonCameraLedSetting(requestId: String?, enabled: Boolean) {
6152
- // Send LED setting to glasses
6153
- val command = JSONObject()
6154
- try {
6155
- command.put("type", "button_camera_led")
6156
- if (requestId != null && !requestId.isEmpty()) {
6157
- command.put("request_id", requestId)
6158
- }
6159
- command.put("enabled", enabled)
6160
- sendJson(command, true)
6161
- } catch (e: Exception) {
6162
- Log.e(TAG, "Error sending button camera LED setting", e)
6163
- }
6164
- }
6165
-
6166
6143
  override fun sendText(text: String) {
6167
6144
  Bridge.log("LIVE: [STUB] Device has no display. Text would show: " + text)
6168
6145
  }
@@ -8073,9 +8050,6 @@ class MentraLive : SGCManager() {
8073
8050
  // Send button photo settings
8074
8051
  sendButtonPhotoSettings()
8075
8052
 
8076
- // Send button camera LED setting
8077
- sendButtonCameraLedSetting()
8078
-
8079
8053
  // Send camera FOV setting (K900 / Mentra Live)
8080
8054
  sendCameraFovSetting()
8081
8055
 
@@ -8144,12 +8118,6 @@ class MentraLive : SGCManager() {
8144
8118
  )
8145
8119
  }
8146
8120
 
8147
- /** Send button camera LED setting to glasses */
8148
- override fun sendButtonCameraLedSetting() {
8149
- val enabled = DeviceStore.get("bluetooth", "button_camera_led") as Boolean
8150
- sendButtonCameraLedSetting(null, enabled)
8151
- }
8152
-
8153
8121
  /** Send camera FOV setting to glasses (K900 / Mentra Live). */
8154
8122
  override fun sendCameraFovSetting() {
8155
8123
  var fov = 118
@@ -8228,17 +8196,15 @@ class MentraLive : SGCManager() {
8228
8196
  override fun startVideoRecording(
8229
8197
  requestId: String,
8230
8198
  save: Boolean,
8231
- flash: Boolean,
8232
8199
  sound: Boolean
8233
8200
  ) {
8234
- startVideoRecording(requestId, save, flash, sound, 0, 0, 0, 0) // Use defaults
8201
+ startVideoRecording(requestId, save, sound, 0, 0, 0, 0) // Use defaults
8235
8202
  }
8236
8203
 
8237
8204
  /**
8238
8205
  * Start video recording with optional resolution settings
8239
8206
  * @param requestId Request ID for tracking
8240
8207
  * @param save Whether to save the video
8241
- * @param flash Whether to enable privacy flash LED
8242
8208
  * @param sound Whether to enable start/stop sounds
8243
8209
  * @param width Video width (0 for default)
8244
8210
  * @param height Video height (0 for default)
@@ -8248,7 +8214,6 @@ class MentraLive : SGCManager() {
8248
8214
  override fun startVideoRecording(
8249
8215
  requestId: String,
8250
8216
  save: Boolean,
8251
- flash: Boolean,
8252
8217
  sound: Boolean,
8253
8218
  width: Int,
8254
8219
  height: Int,
@@ -8260,8 +8225,6 @@ class MentraLive : SGCManager() {
8260
8225
  requestId +
8261
8226
  ", save=" +
8262
8227
  save +
8263
- ", flash=" +
8264
- flash +
8265
8228
  ", sound=" +
8266
8229
  sound +
8267
8230
  ", resolution=" +
@@ -8285,7 +8248,6 @@ class MentraLive : SGCManager() {
8285
8248
  json.put("type", "start_video_recording")
8286
8249
  json.put("requestId", requestId)
8287
8250
  json.put("save", save)
8288
- json.put("flash", flash)
8289
8251
  json.put("sound", sound)
8290
8252
 
8291
8253
  // Auto-stop timer; only sent when set (> 0). 0 = record until stopped.
@@ -343,14 +343,13 @@ class MentraNex : SGCManager() {
343
343
  override fun startStream(message: MutableMap<String, Any>) { Bridge.log("Nex: startStream operation not supported") }
344
344
  override fun stopStream() { Bridge.log("Nex: stopStream operation not supported") }
345
345
  override fun sendStreamKeepAlive(message: MutableMap<String, Any>) { Bridge.log("Nex: sendStreamKeepAlive operation not supported") }
346
- override fun startVideoRecording(requestId: String, save: Boolean, flash: Boolean, sound: Boolean) { Bridge.log("Nex: startVideoRecording operation not supported") }
346
+ override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) { Bridge.log("Nex: startVideoRecording operation not supported") }
347
347
  override fun stopVideoRecording(requestId: String) { Bridge.log("Nex: stopVideoRecording operation not supported") }
348
348
 
349
349
  // Button Settings: Not supported on Nex
350
350
  override fun sendButtonPhotoSettings() { Bridge.log("Nex: sendButtonPhotoSettings operation not supported") }
351
351
  override fun sendButtonVideoRecordingSettings() { Bridge.log("Nex: sendButtonVideoRecordingSettings operation not supported") }
352
352
  override fun sendButtonMaxRecordingTime() { Bridge.log("Nex: sendButtonMaxRecordingTime operation not supported") }
353
- override fun sendButtonCameraLedSetting() { Bridge.log("Nex: sendButtonCameraLedSetting operation not supported") }
354
353
 
355
354
  override fun sendCameraFovSetting() { Bridge.log("Nex: sendCameraFovSetting operation not supported") }
356
355
 
@@ -20,7 +20,7 @@ abstract class SGCManager {
20
20
  abstract fun startStream(message: MutableMap<String, Any>)
21
21
  abstract fun stopStream()
22
22
  abstract fun sendStreamKeepAlive(message: MutableMap<String, Any>)
23
- abstract fun startVideoRecording(requestId: String, save: Boolean, flash: Boolean, sound: Boolean)
23
+ abstract fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean)
24
24
 
25
25
  /**
26
26
  * Start video recording with optional per-recording resolution/fps. A width,
@@ -32,14 +32,13 @@ abstract class SGCManager {
32
32
  open fun startVideoRecording(
33
33
  requestId: String,
34
34
  save: Boolean,
35
- flash: Boolean,
36
35
  sound: Boolean,
37
36
  width: Int,
38
37
  height: Int,
39
38
  fps: Int,
40
39
  maxRecordingTimeMinutes: Int,
41
40
  ) {
42
- startVideoRecording(requestId, save, flash, sound)
41
+ startVideoRecording(requestId, save, sound)
43
42
  }
44
43
 
45
44
  abstract fun stopVideoRecording(requestId: String)
@@ -59,7 +58,6 @@ abstract class SGCManager {
59
58
  abstract fun sendButtonPhotoSettings()
60
59
  abstract fun sendButtonVideoRecordingSettings()
61
60
  abstract fun sendButtonMaxRecordingTime()
62
- abstract fun sendButtonCameraLedSetting()
63
61
  abstract fun sendCameraFovSetting()
64
62
 
65
63
  // Display Control
@@ -34,7 +34,7 @@ class Simulated : SGCManager() {
34
34
 
35
35
  // Camera & Media
36
36
  override fun requestPhoto(request: PhotoRequest) {
37
- Bridge.log("requestPhoto flash=${request.flash}, save=${request.save}, sound=${request.sound}")
37
+ Bridge.log("requestPhoto save=${request.save}, sound=${request.sound}")
38
38
  }
39
39
 
40
40
  override fun startStream(message: MutableMap<String, Any>) {
@@ -49,8 +49,8 @@ class Simulated : SGCManager() {
49
49
  Bridge.log("sendStreamKeepAlive")
50
50
  }
51
51
 
52
- override fun startVideoRecording(requestId: String, save: Boolean, flash: Boolean, sound: Boolean) {
53
- Bridge.log("startVideoRecording flash=$flash, sound=$sound")
52
+ override fun startVideoRecording(requestId: String, save: Boolean, sound: Boolean) {
53
+ Bridge.log("startVideoRecording sound=$sound")
54
54
  }
55
55
 
56
56
  override fun stopVideoRecording(requestId: String) {
@@ -70,10 +70,6 @@ class Simulated : SGCManager() {
70
70
  Bridge.log("sendButtonMaxRecordingTime")
71
71
  }
72
72
 
73
- override fun sendButtonCameraLedSetting() {
74
- Bridge.log("sendButtonCameraLedSetting")
75
- }
76
-
77
73
  override fun sendCameraFovSetting() {
78
74
  Bridge.log("sendCameraFovSetting")
79
75
  }