@mentra/bluetooth-sdk 0.1.13 → 0.1.16

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 (80) hide show
  1. package/README.md +18 -8
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +48 -48
  3. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +5 -20
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +360 -269
  5. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +3 -7
  6. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +152 -72
  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 +25 -11
  9. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +1 -2
  10. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +1 -2
  11. package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +1 -22
  12. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +51 -8
  13. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +1184 -800
  14. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +1 -5
  15. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +423 -117
  16. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +4 -6
  17. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Nimo.kt +2532 -0
  18. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +17 -5
  19. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +3 -7
  20. package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +0 -6
  21. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +16 -33
  22. package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +5 -3
  23. package/android/src/main/java/com/mentra/bluetoothsdk/utils/Constants.kt +2 -1
  24. package/android/src/test/java/com/mentra/bluetoothsdk/camera/PhotoRequestTest.kt +60 -1
  25. package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
  26. package/build/BluetoothSdk.types.d.ts +50 -24
  27. package/build/BluetoothSdk.types.d.ts.map +1 -1
  28. package/build/BluetoothSdk.types.js +1 -0
  29. package/build/BluetoothSdk.types.js.map +1 -1
  30. package/build/_internal.d.ts +4 -4
  31. package/build/_internal.js +4 -4
  32. package/build/_internal.js.map +1 -1
  33. package/build/_private/BluetoothSdkModule.d.ts +14 -8
  34. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  35. package/build/_private/BluetoothSdkModule.js +4 -3
  36. package/build/_private/BluetoothSdkModule.js.map +1 -1
  37. package/build/_private/cameraRequestPayload.d.ts +4 -0
  38. package/build/_private/cameraRequestPayload.d.ts.map +1 -0
  39. package/build/_private/cameraRequestPayload.js +25 -0
  40. package/build/_private/cameraRequestPayload.js.map +1 -0
  41. package/build/_private/photoRequestPayload.d.ts.map +1 -1
  42. package/build/_private/photoRequestPayload.js +11 -3
  43. package/build/_private/photoRequestPayload.js.map +1 -1
  44. package/build/index.d.ts +1 -1
  45. package/build/index.d.ts.map +1 -1
  46. package/build/index.js +70 -61
  47. package/build/index.js.map +1 -1
  48. package/ios/BluetoothSdkModule.swift +51 -28
  49. package/ios/Source/Bridge.swift +4 -16
  50. package/ios/Source/DeviceManager.swift +76 -22
  51. package/ios/Source/DeviceStore.swift +0 -4
  52. package/ios/Source/MentraBluetoothSDK.swift +144 -78
  53. package/ios/Source/OtaManifest.swift +13 -13
  54. package/ios/Source/camera/CameraModels.swift +82 -20
  55. package/ios/Source/controllers/ControllerManager.swift +1 -2
  56. package/ios/Source/controllers/R1.swift +1 -2
  57. package/ios/Source/errors/{BluetoothError.swift → BluetoothSdkError.swift} +1 -1
  58. package/ios/Source/events/BluetoothEvents.swift +5 -27
  59. package/ios/Source/internal/BluetoothAvailability.swift +5 -5
  60. package/ios/Source/sgcs/Frame.swift +0 -2
  61. package/ios/Source/sgcs/G1.swift +52 -4
  62. package/ios/Source/sgcs/G2.swift +643 -204
  63. package/ios/Source/sgcs/Mach1.swift +1 -3
  64. package/ios/Source/sgcs/MentraLive.swift +64 -77
  65. package/ios/Source/sgcs/MentraNex.swift +1 -3
  66. package/ios/Source/sgcs/Nimo.swift +1928 -0
  67. package/ios/Source/sgcs/SGCManager.swift +16 -10
  68. package/ios/Source/sgcs/Simulated.swift +2 -6
  69. package/ios/Source/status/DeviceStatus.swift +0 -8
  70. package/ios/Source/streaming/StreamModels.swift +7 -32
  71. package/ios/Source/stt/STTTools.swift +1 -1
  72. package/ios/Source/types/DeviceModels.swift +5 -0
  73. package/ios/Source/utils/Constants.swift +2 -0
  74. package/package.json +6 -1
  75. package/src/BluetoothSdk.types.ts +54 -28
  76. package/src/_internal.ts +4 -4
  77. package/src/_private/BluetoothSdkModule.ts +20 -11
  78. package/src/_private/cameraRequestPayload.ts +29 -0
  79. package/src/_private/photoRequestPayload.ts +12 -3
  80. package/src/index.ts +77 -67
@@ -34,8 +34,6 @@ class Mach1: UltraliteBaseViewController, SGCManager {
34
34
 
35
35
  func sendButtonMaxRecordingTime(_: Int) {}
36
36
 
37
- func sendButtonCameraLedSetting() {}
38
-
39
37
  func sendCameraFovSetting() {}
40
38
 
41
39
  func exit() {}
@@ -87,7 +85,7 @@ class Mach1: UltraliteBaseViewController, SGCManager {
87
85
 
88
86
  func sendStreamKeepAlive(_: [String: Any]) {}
89
87
 
90
- func startVideoRecording(requestId _: String, save _: Bool, flash _: Bool, sound _: Bool) {}
88
+ func startVideoRecording(requestId _: String, save _: Bool, sound _: Bool) {}
91
89
 
92
90
  func stopVideoRecording(requestId _: String) {}
93
91
 
@@ -1616,13 +1616,12 @@ class MentraLive: NSObject, SGCManager {
1616
1616
 
1617
1617
  func requestPhoto(_ request: PhotoRequest) {
1618
1618
  Bridge.log(
1619
- "LIVE: PHOTO PIPELINE [5/6] requestPhoto() entry requestId=\(request.requestId) appId=\(request.appId) flash=\(request.flash) save=\(request.save) sound=\(request.sound) iso=\(request.iso.map { String($0) } ?? "auto") aeDivisor=\(request.aeExposureDivisor.map { String($0) } ?? "nil")"
1619
+ "LIVE: PHOTO PIPELINE [5/6] requestPhoto() entry requestId=\(request.requestId) save=\(request.save) sound=\(request.sound) iso=\(request.iso.map { String($0) } ?? "auto") aeDivisor=\(request.aeExposureDivisor.map { String($0) } ?? "nil")"
1620
1620
  )
1621
1621
 
1622
1622
  var json: [String: Any] = [
1623
1623
  "type": "take_photo",
1624
1624
  "requestId": request.requestId,
1625
- "appId": request.appId,
1626
1625
  ]
1627
1626
 
1628
1627
  // Always generate BLE ID for potential fallback
@@ -1656,7 +1655,6 @@ class MentraLive: NSObject, SGCManager {
1656
1655
  json["size"] = allowedSizes.contains(size) ? size : "medium"
1657
1656
 
1658
1657
  json["compress"] = request.compress?.rawValue ?? "none"
1659
- json["flash"] = request.flash
1660
1658
  json["save"] = request.save
1661
1659
  json["sound"] = request.sound
1662
1660
 
@@ -1676,6 +1674,32 @@ class MentraLive: NSObject, SGCManager {
1676
1674
  sendJson(json, wakeUp: true)
1677
1675
  }
1678
1676
 
1677
+ func warmUpCamera(
1678
+ requestId: String,
1679
+ size: PhotoSize,
1680
+ exposureTimeNs: Double?,
1681
+ durationMs: Int
1682
+ ) {
1683
+ Bridge.log(
1684
+ "LIVE: warmUpCamera() entry requestId=\(requestId) size=\(size.rawValue) durationMs=\(durationMs)"
1685
+ )
1686
+
1687
+ let allowedSizes = ["low", "medium", "high", "max"]
1688
+ let sizeRaw = size.rawValue
1689
+ var json: [String: Any] = [
1690
+ "type": "camera_warm_up",
1691
+ "requestId": requestId,
1692
+ "size": allowedSizes.contains(sizeRaw) ? sizeRaw : "medium",
1693
+ "durationMs": durationMs > 0 ? durationMs : 15000,
1694
+ ]
1695
+
1696
+ if let e = exposureTimeNs, e.isFinite, e > 0, e <= Double(Int64.max) {
1697
+ json["exposureTimeNs"] = Int64(e)
1698
+ }
1699
+
1700
+ sendJson(json, wakeUp: true)
1701
+ }
1702
+
1679
1703
  func startStream(_ message: [String: Any]) {
1680
1704
  Bridge.log("Starting stream")
1681
1705
  var json = message
@@ -2173,6 +2197,9 @@ class MentraLive: NSObject, SGCManager {
2173
2197
  case "photo_status":
2174
2198
  emitPhotoStatus(json)
2175
2199
 
2200
+ case "camera_status":
2201
+ emitCameraStatus(json)
2202
+
2176
2203
  case "photo_response":
2177
2204
  emitPhotoResponse(json)
2178
2205
 
@@ -2284,32 +2311,6 @@ class MentraLive: NSObject, SGCManager {
2284
2311
  // Send to React Native via Bridge
2285
2312
  Bridge.sendMtkUpdateComplete(message: updateMessage, timestamp: timestamp)
2286
2313
 
2287
- case "ota_update_available":
2288
- // Process OTA update available notification from glasses (background mode)
2289
- Bridge.log("📱 Received ota_update_available from glasses")
2290
-
2291
- let versionCode = json["version_code"] as? Int64 ?? 0
2292
- let versionName = json["version_name"] as? String ?? ""
2293
- let totalSize = json["total_size"] as? Int64 ?? 0
2294
-
2295
- // Parse updates array
2296
- var updates: [String] = []
2297
- if let updatesArray = json["updates"] as? [String] {
2298
- updates = updatesArray
2299
- }
2300
-
2301
- Bridge.log(
2302
- "📱 OTA available - version: \(versionName) (\(versionCode)), updates: \(updates), size: \(totalSize) bytes"
2303
- )
2304
-
2305
- // Send to React Native
2306
- Bridge.sendOtaUpdateAvailable(
2307
- versionCode: versionCode,
2308
- versionName: versionName,
2309
- updates: updates,
2310
- totalSize: totalSize
2311
- )
2312
-
2313
2314
  case "ota_start_ack":
2314
2315
  // Glasses acknowledged receipt of ota_start — phone can cancel its retry timer
2315
2316
  Bridge.log("LIVE: 📱 Received ota_start_ack from glasses")
@@ -2905,17 +2906,6 @@ class MentraLive: NSObject, SGCManager {
2905
2906
  sendJson(json, wakeUp: true)
2906
2907
  }
2907
2908
 
2908
- func sendOtaRetryVersionCheck() {
2909
- Bridge.log("LIVE: ⏰ Sending ota_retry_version_check command to glasses")
2910
-
2911
- let json: [String: Any] = [
2912
- "type": "ota_retry_version_check",
2913
- "timestamp": Int(Date().timeIntervalSince1970 * 1000),
2914
- ]
2915
-
2916
- sendJson(json, wakeUp: true)
2917
- }
2918
-
2919
2909
  func keepAwake() {
2920
2910
  Bridge.log("LIVE: 📱 Sending keep_awake command to glasses")
2921
2911
 
@@ -4347,6 +4337,10 @@ class MentraLive: NSObject, SGCManager {
4347
4337
  Bridge.sendPhotoStatus(json)
4348
4338
  }
4349
4339
 
4340
+ private func emitCameraStatus(_ json: [String: Any]) {
4341
+ Bridge.sendCameraStatus(json)
4342
+ }
4343
+
4350
4344
  private func emitPhotoResponse(_ json: [String: Any]) {
4351
4345
  Bridge.sendPhotoResponse(json)
4352
4346
  }
@@ -5085,9 +5079,6 @@ extension MentraLive {
5085
5079
  // Send button photo settings
5086
5080
  sendButtonPhotoSettings()
5087
5081
 
5088
- // Send button camera LED setting
5089
- sendButtonCameraLedSetting()
5090
-
5091
5082
  // Send camera FOV setting (K900 / Mentra Live)
5092
5083
  sendCameraFovSetting()
5093
5084
 
@@ -5197,7 +5188,9 @@ extension MentraLive {
5197
5188
  }
5198
5189
 
5199
5190
  func sendButtonPhotoSettings() {
5200
- let size = DeviceStore.shared.get("bluetooth", "button_photo_size") as? String
5191
+ let size = (DeviceStore.shared.get("bluetooth", "button_photo_size") as? String).flatMap { rawSize in
5192
+ rawSize.isEmpty ? nil : PhotoSize(normalizedRawValue: rawSize)
5193
+ }
5201
5194
  let mfnr = DeviceStore.shared.get("bluetooth", "button_photo_mfnr") as? Bool
5202
5195
  let zsl = DeviceStore.shared.get("bluetooth", "button_photo_zsl") as? Bool
5203
5196
  let noiseReduction = DeviceStore.shared.get("bluetooth", "button_photo_noise_reduction") as? Bool
@@ -5209,8 +5202,8 @@ extension MentraLive {
5209
5202
  let compressStr = DeviceStore.shared.get("bluetooth", "button_photo_compress") as? String
5210
5203
  let sound = DeviceStore.shared.get("bluetooth", "button_photo_sound") as? Bool
5211
5204
 
5212
- let settings = ButtonPhotoSettings(
5213
- size: ButtonPhotoSize(normalizedRawValue: size ?? "medium"),
5205
+ let settings = PhotoCaptureDefaults(
5206
+ size: size,
5214
5207
  mfnr: mfnr,
5215
5208
  zsl: zsl,
5216
5209
  noiseReduction: noiseReduction,
@@ -5228,10 +5221,10 @@ extension MentraLive {
5228
5221
  }
5229
5222
 
5230
5223
  func sendButtonPhotoSettings(requestId: String?, size: String) {
5231
- sendButtonPhotoSettings(requestId: requestId, settings: ButtonPhotoSettings(size: ButtonPhotoSize(normalizedRawValue: size)))
5224
+ sendButtonPhotoSettings(requestId: requestId, settings: PhotoCaptureDefaults(size: PhotoSize(normalizedRawValue: size)))
5232
5225
  }
5233
5226
 
5234
- func sendButtonPhotoSettings(requestId: String?, settings: ButtonPhotoSettings) {
5227
+ func sendButtonPhotoSettings(requestId: String?, settings: PhotoCaptureDefaults) {
5235
5228
  var details = settings.size.map { "size=\($0.rawValue)" } ?? "size=unchanged"
5236
5229
  if let mfnr = settings.mfnr {
5237
5230
  details += ", mfnr=\(mfnr)"
@@ -5315,29 +5308,6 @@ extension MentraLive {
5315
5308
  sendJson(json, wakeUp: true)
5316
5309
  }
5317
5310
 
5318
- func sendButtonCameraLedSetting() {
5319
- let enabled = DeviceStore.shared.get("bluetooth", "button_camera_led") as! Bool
5320
- sendButtonCameraLedSetting(requestId: nil, enabled: enabled)
5321
- }
5322
-
5323
- func sendButtonCameraLedSetting(requestId: String?, enabled: Bool) {
5324
- Bridge.log("Sending button camera LED setting: \(enabled)")
5325
-
5326
- guard connectionState == ConnTypes.CONNECTED else {
5327
- Bridge.log("Cannot send button camera LED setting - not connected")
5328
- return
5329
- }
5330
-
5331
- var json: [String: Any] = [
5332
- "type": "button_camera_led",
5333
- "enabled": enabled,
5334
- ]
5335
- if let requestId, !requestId.isEmpty {
5336
- json["request_id"] = requestId
5337
- }
5338
- sendJson(json, wakeUp: true)
5339
- }
5340
-
5341
5311
  func sendCameraFovSetting() {
5342
5312
  let settings = DeviceStore.shared.get("bluetooth", "camera_fov") as? [String: Any] ?? ["fov": 118, "roi_position": 0]
5343
5313
  let fov = settings["fov"] as? Int ?? 118
@@ -5366,9 +5336,28 @@ extension MentraLive {
5366
5336
  sendJson(json, wakeUp: true)
5367
5337
  }
5368
5338
 
5369
- func startVideoRecording(requestId: String, save: Bool, flash: Bool, sound: Bool) {
5339
+ func sendCameraTuningConfig(requestId: String?, anrOn: Bool, gainOn: Bool) {
5340
+ Bridge.log("Sending camera tuning config: anr=\(anrOn), gain=\(gainOn)")
5341
+
5342
+ guard connectionState == ConnTypes.CONNECTED else {
5343
+ Bridge.log("Cannot send camera tuning config - not connected")
5344
+ return
5345
+ }
5346
+
5347
+ var json: [String: Any] = [
5348
+ "type": "camera_tuning_config",
5349
+ "anr": anrOn,
5350
+ "gain": gainOn,
5351
+ ]
5352
+ if let requestId, !requestId.isEmpty {
5353
+ json["request_id"] = requestId
5354
+ }
5355
+ sendJson(json, wakeUp: true)
5356
+ }
5357
+
5358
+ func startVideoRecording(requestId: String, save: Bool, sound: Bool) {
5370
5359
  startVideoRecording(
5371
- requestId: requestId, save: save, flash: flash, sound: sound, width: 0, height: 0, fps: 0,
5360
+ requestId: requestId, save: save, sound: sound, width: 0, height: 0, fps: 0,
5372
5361
  maxRecordingTimeMinutes: 0
5373
5362
  )
5374
5363
  }
@@ -5380,11 +5369,11 @@ extension MentraLive {
5380
5369
  }
5381
5370
 
5382
5371
  func startVideoRecording(
5383
- requestId: String, save: Bool, flash: Bool, sound: Bool, width: Int, height: Int, fps: Int,
5372
+ requestId: String, save: Bool, sound: Bool, width: Int, height: Int, fps: Int,
5384
5373
  maxRecordingTimeMinutes: Int
5385
5374
  ) {
5386
5375
  Bridge.log(
5387
- "Starting video recording on glasses: requestId=\(requestId), save=\(save), flash=\(flash), sound=\(sound), resolution=\(width)x\(height)@\(fps)fps, maxRecordingTimeMinutes=\(maxRecordingTimeMinutes)"
5376
+ "Starting video recording on glasses: requestId=\(requestId), save=\(save), sound=\(sound), resolution=\(width)x\(height)@\(fps)fps, maxRecordingTimeMinutes=\(maxRecordingTimeMinutes)"
5388
5377
  )
5389
5378
 
5390
5379
  guard connectionState == ConnTypes.CONNECTED else {
@@ -5396,10 +5385,8 @@ extension MentraLive {
5396
5385
  "type": "start_video_recording",
5397
5386
  "requestId": requestId,
5398
5387
  "save": save,
5399
- "flash": flash,
5400
5388
  "sound": sound,
5401
5389
  ]
5402
-
5403
5390
  // Auto-stop timer; only sent when set (> 0). 0 = record until stopped.
5404
5391
  if maxRecordingTimeMinutes > 0 {
5405
5392
  json["maxRecordingTimeMinutes"] = maxRecordingTimeMinutes
@@ -59,7 +59,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
59
59
 
60
60
  func sendStreamKeepAlive(_: [String: Any]) {}
61
61
 
62
- func startVideoRecording(requestId _: String, save _: Bool, flash _: Bool, sound _: Bool) {}
62
+ func startVideoRecording(requestId _: String, save _: Bool, sound _: Bool) {}
63
63
 
64
64
  func stopVideoRecording(requestId _: String) {}
65
65
 
@@ -69,8 +69,6 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
69
69
 
70
70
  func sendButtonMaxRecordingTime() {}
71
71
 
72
- func sendButtonCameraLedSetting() {}
73
-
74
72
  func sendCameraFovSetting() {}
75
73
 
76
74
  func setBrightness(_ level: Int, autoMode: Bool) {