@mentra/bluetooth-sdk 0.1.12 → 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 (69) hide show
  1. package/README.md +30 -11
  2. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +47 -12
  3. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +75 -29
  4. package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +1 -5
  5. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +208 -45
  6. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdkDebug.kt +14 -0
  7. package/android/src/main/java/com/mentra/bluetoothsdk/ObservableStore.kt +20 -0
  8. package/android/src/main/java/com/mentra/bluetoothsdk/OtaManifest.kt +153 -0
  9. package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +80 -14
  10. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +4 -16
  11. package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +4 -8
  12. package/android/src/main/java/com/mentra/bluetoothsdk/services/PhoneMic.kt +90 -4
  13. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +3831 -0
  14. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +20 -28
  15. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/{Mach1.java → Mach1.kt} +514 -561
  16. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +8674 -0
  17. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +12 -3
  18. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +5 -17
  19. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +9 -20
  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/build/BluetoothSdk.types.d.ts +48 -17
  23. package/build/BluetoothSdk.types.d.ts.map +1 -1
  24. package/build/BluetoothSdk.types.js.map +1 -1
  25. package/build/_private/BluetoothSdkModule.d.ts +9 -9
  26. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  27. package/build/_private/BluetoothSdkModule.js +0 -6
  28. package/build/_private/BluetoothSdkModule.js.map +1 -1
  29. package/build/_private/photoRequestPayload.d.ts +3 -1
  30. package/build/_private/photoRequestPayload.d.ts.map +1 -1
  31. package/build/_private/photoRequestPayload.js +46 -2
  32. package/build/_private/photoRequestPayload.js.map +1 -1
  33. package/build/debug.d.ts +3 -0
  34. package/build/debug.d.ts.map +1 -0
  35. package/build/debug.js +8 -0
  36. package/build/debug.js.map +1 -0
  37. package/build/index.d.ts +1 -1
  38. package/build/index.d.ts.map +1 -1
  39. package/build/index.js +8 -8
  40. package/build/index.js.map +1 -1
  41. package/ios/BluetoothSdkModule.swift +44 -68
  42. package/ios/Source/BluetoothSdkDefaults.swift +23 -3
  43. package/ios/Source/DeviceManager.swift +38 -33
  44. package/ios/Source/DeviceStore.swift +5 -5
  45. package/ios/Source/MentraBluetoothSDK.swift +217 -50
  46. package/ios/Source/MentraBluetoothSDKDebug.swift +12 -0
  47. package/ios/Source/ObservableStore.swift +11 -0
  48. package/ios/Source/OtaManifest.swift +170 -0
  49. package/ios/Source/camera/CameraModels.swift +219 -13
  50. package/ios/Source/controllers/ControllerManager.swift +3 -7
  51. package/ios/Source/controllers/R1.swift +3 -7
  52. package/ios/Source/sgcs/Frame.swift +0 -2
  53. package/ios/Source/sgcs/G1.swift +7 -8
  54. package/ios/Source/sgcs/G2.swift +21 -20
  55. package/ios/Source/sgcs/Mach1.swift +7 -8
  56. package/ios/Source/sgcs/MentraLive.swift +132 -63
  57. package/ios/Source/sgcs/MentraNex.swift +7 -8
  58. package/ios/Source/sgcs/SGCManager.swift +7 -10
  59. package/ios/Source/sgcs/Simulated.swift +8 -8
  60. package/ios/Source/status/DeviceStatus.swift +1 -9
  61. package/ios/Source/streaming/StreamModels.swift +7 -32
  62. package/package.json +6 -1
  63. package/src/BluetoothSdk.types.ts +49 -17
  64. package/src/_private/BluetoothSdkModule.ts +9 -17
  65. package/src/_private/photoRequestPayload.ts +48 -3
  66. package/src/debug.ts +9 -0
  67. package/src/index.ts +19 -10
  68. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +0 -3974
  69. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +0 -7434
@@ -1234,6 +1234,9 @@ class MentraLive: NSObject, SGCManager {
1234
1234
  func getBatteryStatus() {}
1235
1235
  func setBrightness(_: Int, autoMode _: Bool) {}
1236
1236
  func clearDisplay() {}
1237
+ func sendText(_ text: String) async {
1238
+ await sendTextWall(text)
1239
+ }
1237
1240
  func sendTextWall(_: String) async {}
1238
1241
  func ping() {
1239
1242
  Bridge.log("LIVE: ping()")
@@ -1611,18 +1614,15 @@ class MentraLive: NSObject, SGCManager {
1611
1614
  }
1612
1615
  }
1613
1616
 
1614
- func requestPhoto(
1615
- _ requestId: String, appId: String, size: String?, webhookUrl: String?, authToken: String?,
1616
- compress: String?, flash: Bool, save: Bool, sound: Bool, exposureTimeNs: Double?, iso: Int?
1617
- ) {
1617
+ func requestPhoto(_ request: PhotoRequest) {
1618
1618
  Bridge.log(
1619
- "LIVE: PHOTO PIPELINE [5/6] requestPhoto() entry requestId=\(requestId) appId=\(appId) flash=\(flash) save=\(save) sound=\(sound) iso=\(iso.map { String($0) } ?? "auto")"
1619
+ "LIVE: PHOTO PIPELINE [5/6] requestPhoto() entry requestId=\(request.requestId) appId=\(request.appId) 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
- "requestId": requestId,
1625
- "appId": appId,
1624
+ "requestId": request.requestId,
1625
+ "appId": request.appId,
1626
1626
  ]
1627
1627
 
1628
1628
  // Always generate BLE ID for potential fallback
@@ -1631,15 +1631,15 @@ class MentraLive: NSObject, SGCManager {
1631
1631
  json["bleImgId"] = bleImgId
1632
1632
  json["transferMethod"] = "auto"
1633
1633
 
1634
- if let webhookUrl, !webhookUrl.isEmpty {
1634
+ if let webhookUrl = request.webhookUrl, !webhookUrl.isEmpty {
1635
1635
  json["webhookUrl"] = webhookUrl
1636
1636
 
1637
1637
  var transfer = BlePhotoTransfer(
1638
- bleImgId: bleImgId, requestId: requestId, webhookUrl: webhookUrl
1638
+ bleImgId: bleImgId, requestId: request.requestId, webhookUrl: webhookUrl
1639
1639
  )
1640
1640
 
1641
1641
  // Store authToken for BLE transfer if provided
1642
- if let authToken, !authToken.isEmpty {
1642
+ if let authToken = request.authToken, !authToken.isEmpty {
1643
1643
  transfer.authToken = authToken
1644
1644
  }
1645
1645
 
@@ -1647,32 +1647,27 @@ class MentraLive: NSObject, SGCManager {
1647
1647
  }
1648
1648
 
1649
1649
  // Add authToken to JSON if provided
1650
- if let authToken, !authToken.isEmpty {
1650
+ if let authToken = request.authToken, !authToken.isEmpty {
1651
1651
  json["authToken"] = authToken
1652
1652
  }
1653
1653
 
1654
- // propagate size (default to medium if invalid)
1655
- if let size, ["small", "medium", "large", "full"].contains(size) {
1656
- json["size"] = size
1657
- } else {
1658
- json["size"] = "medium"
1659
- }
1660
-
1661
- // Add compress parameter
1662
- json["compress"] = compress ?? "none"
1654
+ let allowedSizes = ["low", "medium", "high", "max"]
1655
+ let size = request.size.rawValue
1656
+ json["size"] = allowedSizes.contains(size) ? size : "medium"
1663
1657
 
1664
- json["flash"] = flash
1665
- json["save"] = save
1666
- json["sound"] = sound
1658
+ json["compress"] = request.compress?.rawValue ?? "none"
1659
+ json["save"] = request.save
1660
+ json["sound"] = request.sound
1667
1661
 
1668
- if let e = exposureTimeNs, e.isFinite, e > 0, e <= Double(Int64.max) {
1669
- Bridge.log("LIVE: Using manual exposure time for photo request \(requestId): \(Int64(e)) ns")
1662
+ if let e = request.exposureTimeNs, e.isFinite, e > 0, e <= Double(Int64.max) {
1663
+ Bridge.log("LIVE: Using manual exposure time for photo request \(request.requestId): \(Int64(e)) ns")
1670
1664
  json["exposureTimeNs"] = Int64(e)
1671
1665
  }
1672
- if let iso, iso > 0 {
1673
- Bridge.log("LIVE: Using manual ISO for photo request \(requestId): ISO \(iso)")
1666
+ if let iso = request.iso, iso > 0 {
1667
+ Bridge.log("LIVE: Using manual ISO for photo request \(request.requestId): ISO \(iso)")
1674
1668
  json["iso"] = iso
1675
1669
  }
1670
+ request.appendScanFields(to: &json)
1676
1671
 
1677
1672
  Bridge.log("LIVE: PHOTO PIPELINE [5b/6] take_photo JSON ready bleImgId=\(bleImgId) transferMethod=auto")
1678
1673
  Bridge.log("LIVE: PHOTO PIPELINE [6/6] Dispatching take_photo to sendJson()")
@@ -2883,13 +2878,17 @@ class MentraLive: NSObject, SGCManager {
2883
2878
  /// Send OTA start command to glasses.
2884
2879
  /// Called when user approves an update (onboarding or background mode).
2885
2880
  /// Triggers glasses to begin download and installation.
2886
- func sendOtaStart() {
2881
+ func sendOtaStart(otaVersionUrl: String?) {
2887
2882
  Bridge.log("LIVE: 📱 Sending ota_start command to glasses")
2888
2883
 
2889
- let json: [String: Any] = [
2884
+ var json: [String: Any] = [
2890
2885
  "type": "ota_start",
2891
2886
  "timestamp": Int(Date().timeIntervalSince1970 * 1000),
2892
2887
  ]
2888
+ if let otaVersionUrl = otaVersionUrl?.trimmingCharacters(in: .whitespacesAndNewlines),
2889
+ !otaVersionUrl.isEmpty {
2890
+ json["ota_version_url"] = otaVersionUrl
2891
+ }
2893
2892
 
2894
2893
  sendJson(json, wakeUp: true)
2895
2894
  }
@@ -5085,9 +5084,6 @@ extension MentraLive {
5085
5084
  // Send button photo settings
5086
5085
  sendButtonPhotoSettings()
5087
5086
 
5088
- // Send button camera LED setting
5089
- sendButtonCameraLedSetting()
5090
-
5091
5087
  // Send camera FOV setting (K900 / Mentra Live)
5092
5088
  sendCameraFovSetting()
5093
5089
 
@@ -5197,12 +5193,75 @@ extension MentraLive {
5197
5193
  }
5198
5194
 
5199
5195
  func sendButtonPhotoSettings() {
5200
- let size = DeviceStore.shared.get("bluetooth", "button_photo_size") as! String
5201
- sendButtonPhotoSettings(requestId: nil, size: size)
5196
+ let size = (DeviceStore.shared.get("bluetooth", "button_photo_size") as? String).flatMap { rawSize in
5197
+ rawSize.isEmpty ? nil : PhotoSize(normalizedRawValue: rawSize)
5198
+ }
5199
+ let mfnr = DeviceStore.shared.get("bluetooth", "button_photo_mfnr") as? Bool
5200
+ let zsl = DeviceStore.shared.get("bluetooth", "button_photo_zsl") as? Bool
5201
+ let noiseReduction = DeviceStore.shared.get("bluetooth", "button_photo_noise_reduction") as? Bool
5202
+ let edgeEnhancement = DeviceStore.shared.get("bluetooth", "button_photo_edge_enhancement") as? Bool
5203
+ let ispDigitalGain = DeviceStore.shared.get("bluetooth", "button_photo_isp_digital_gain") as? Int
5204
+ let ispAnalogGain = DeviceStore.shared.get("bluetooth", "button_photo_isp_analog_gain") as? String
5205
+ let aeExposureDivisor = DeviceStore.shared.get("bluetooth", "button_photo_ae_exposure_divisor") as? Int
5206
+ let isoCap = DeviceStore.shared.get("bluetooth", "button_photo_iso_cap") as? Int
5207
+ let compressStr = DeviceStore.shared.get("bluetooth", "button_photo_compress") as? String
5208
+ let sound = DeviceStore.shared.get("bluetooth", "button_photo_sound") as? Bool
5209
+
5210
+ let settings = PhotoCaptureDefaults(
5211
+ size: size,
5212
+ mfnr: mfnr,
5213
+ zsl: zsl,
5214
+ noiseReduction: noiseReduction,
5215
+ edgeEnhancement: edgeEnhancement,
5216
+ ispDigitalGain: ispDigitalGain,
5217
+ ispAnalogGain: ispAnalogGain,
5218
+ aeExposureDivisor: aeExposureDivisor,
5219
+ isoCap: isoCap,
5220
+ compress: compressStr,
5221
+ sound: sound,
5222
+ resetCaptureTuning: false
5223
+ )
5224
+
5225
+ sendButtonPhotoSettings(requestId: nil, settings: settings)
5202
5226
  }
5203
5227
 
5204
5228
  func sendButtonPhotoSettings(requestId: String?, size: String) {
5205
- Bridge.log("Sending button photo setting: \(size)")
5229
+ sendButtonPhotoSettings(requestId: requestId, settings: PhotoCaptureDefaults(size: PhotoSize(normalizedRawValue: size)))
5230
+ }
5231
+
5232
+ func sendButtonPhotoSettings(requestId: String?, settings: PhotoCaptureDefaults) {
5233
+ var details = settings.size.map { "size=\($0.rawValue)" } ?? "size=unchanged"
5234
+ if let mfnr = settings.mfnr {
5235
+ details += ", mfnr=\(mfnr)"
5236
+ }
5237
+ if let zsl = settings.zsl {
5238
+ details += ", zsl=\(zsl)"
5239
+ }
5240
+ if let noiseReduction = settings.noiseReduction {
5241
+ details += ", noiseReduction=\(noiseReduction)"
5242
+ }
5243
+ if let edgeEnhancement = settings.edgeEnhancement {
5244
+ details += ", edgeEnhancement=\(edgeEnhancement)"
5245
+ }
5246
+ if let ispDigitalGain = settings.ispDigitalGain {
5247
+ details += ", ispDigitalGain=\(ispDigitalGain)"
5248
+ }
5249
+ if let ispAnalogGain = settings.ispAnalogGain {
5250
+ details += ", ispAnalogGain=\(ispAnalogGain)"
5251
+ }
5252
+ if let aeExposureDivisor = settings.aeExposureDivisor {
5253
+ details += ", aeExposureDivisor=\(aeExposureDivisor)"
5254
+ }
5255
+ if let isoCap = settings.isoCap {
5256
+ details += ", isoCap=\(isoCap)"
5257
+ }
5258
+ if let compress = settings.compress {
5259
+ details += ", compress=\(compress)"
5260
+ }
5261
+ if let sound = settings.sound {
5262
+ details += ", sound=\(sound)"
5263
+ }
5264
+ Bridge.log("Sending button photo setting: \(details)")
5206
5265
 
5207
5266
  guard connectionState == ConnTypes.CONNECTED else {
5208
5267
  Bridge.log("Cannot send button photo settings - not connected")
@@ -5211,33 +5270,45 @@ extension MentraLive {
5211
5270
 
5212
5271
  var json: [String: Any] = [
5213
5272
  "type": "button_photo_setting",
5214
- "size": size,
5215
5273
  ]
5274
+ if let size = settings.size {
5275
+ json["size"] = size.rawValue
5276
+ }
5216
5277
  if let requestId, !requestId.isEmpty {
5217
5278
  json["request_id"] = requestId
5218
5279
  }
5219
- sendJson(json, wakeUp: true)
5220
- }
5221
-
5222
- func sendButtonCameraLedSetting() {
5223
- let enabled = DeviceStore.shared.get("bluetooth", "button_camera_led") as! Bool
5224
- sendButtonCameraLedSetting(requestId: nil, enabled: enabled)
5225
- }
5226
-
5227
- func sendButtonCameraLedSetting(requestId: String?, enabled: Bool) {
5228
- Bridge.log("Sending button camera LED setting: \(enabled)")
5229
-
5230
- guard connectionState == ConnTypes.CONNECTED else {
5231
- Bridge.log("Cannot send button camera LED setting - not connected")
5232
- return
5280
+ if let mfnr = settings.mfnr {
5281
+ json["mfnr"] = mfnr
5233
5282
  }
5234
-
5235
- var json: [String: Any] = [
5236
- "type": "button_camera_led",
5237
- "enabled": enabled,
5238
- ]
5239
- if let requestId, !requestId.isEmpty {
5240
- json["request_id"] = requestId
5283
+ if let zsl = settings.zsl {
5284
+ json["zsl"] = zsl
5285
+ }
5286
+ if let noiseReduction = settings.noiseReduction {
5287
+ json["noiseReduction"] = noiseReduction
5288
+ }
5289
+ if let edgeEnhancement = settings.edgeEnhancement {
5290
+ json["edgeEnhancement"] = edgeEnhancement
5291
+ }
5292
+ if let ispDigitalGain = settings.ispDigitalGain {
5293
+ json["ispDigitalGain"] = ispDigitalGain
5294
+ }
5295
+ if let ispAnalogGain = settings.ispAnalogGain, !ispAnalogGain.isEmpty {
5296
+ json["ispAnalogGain"] = ispAnalogGain
5297
+ }
5298
+ if let aeExposureDivisor = settings.aeExposureDivisor, aeExposureDivisor > 1 {
5299
+ json["aeExposureDivisor"] = aeExposureDivisor
5300
+ }
5301
+ if let isoCap = settings.isoCap, isoCap > 0 {
5302
+ json["isoCap"] = isoCap
5303
+ }
5304
+ if let compress = settings.compress, !compress.isEmpty {
5305
+ json["compress"] = compress
5306
+ }
5307
+ if let sound = settings.sound {
5308
+ json["sound"] = sound
5309
+ }
5310
+ if settings.resetCaptureTuning == true {
5311
+ json["resetCaptureTuning"] = true
5241
5312
  }
5242
5313
  sendJson(json, wakeUp: true)
5243
5314
  }
@@ -5270,9 +5341,9 @@ extension MentraLive {
5270
5341
  sendJson(json, wakeUp: true)
5271
5342
  }
5272
5343
 
5273
- func startVideoRecording(requestId: String, save: Bool, flash: Bool, sound: Bool) {
5344
+ func startVideoRecording(requestId: String, save: Bool, sound: Bool) {
5274
5345
  startVideoRecording(
5275
- requestId: requestId, save: save, flash: flash, sound: sound, width: 0, height: 0, fps: 0,
5346
+ requestId: requestId, save: save, sound: sound, width: 0, height: 0, fps: 0,
5276
5347
  maxRecordingTimeMinutes: 0
5277
5348
  )
5278
5349
  }
@@ -5284,11 +5355,11 @@ extension MentraLive {
5284
5355
  }
5285
5356
 
5286
5357
  func startVideoRecording(
5287
- requestId: String, save: Bool, flash: Bool, sound: Bool, width: Int, height: Int, fps: Int,
5358
+ requestId: String, save: Bool, sound: Bool, width: Int, height: Int, fps: Int,
5288
5359
  maxRecordingTimeMinutes: Int
5289
5360
  ) {
5290
5361
  Bridge.log(
5291
- "Starting video recording on glasses: requestId=\(requestId), save=\(save), flash=\(flash), sound=\(sound), resolution=\(width)x\(height)@\(fps)fps, maxRecordingTimeMinutes=\(maxRecordingTimeMinutes)"
5362
+ "Starting video recording on glasses: requestId=\(requestId), save=\(save), sound=\(sound), resolution=\(width)x\(height)@\(fps)fps, maxRecordingTimeMinutes=\(maxRecordingTimeMinutes)"
5292
5363
  )
5293
5364
 
5294
5365
  guard connectionState == ConnTypes.CONNECTED else {
@@ -5300,10 +5371,8 @@ extension MentraLive {
5300
5371
  "type": "start_video_recording",
5301
5372
  "requestId": requestId,
5302
5373
  "save": save,
5303
- "flash": flash,
5304
5374
  "sound": sound,
5305
5375
  ]
5306
-
5307
5376
  // Auto-stop timer; only sent when set (> 0). 0 = record until stopped.
5308
5377
  if maxRecordingTimeMinutes > 0 {
5309
5378
  json["maxRecordingTimeMinutes"] = maxRecordingTimeMinutes
@@ -51,10 +51,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
51
51
  }
52
52
  }
53
53
 
54
- func requestPhoto(
55
- _: String, appId _: String, size _: String?, webhookUrl _: String?, authToken _: String?,
56
- compress _: String?, flash _: Bool, save _: Bool, sound _: Bool, exposureTimeNs _: Double?, iso _: Int?
57
- ) {}
54
+ func requestPhoto(_: PhotoRequest) {}
58
55
 
59
56
  func startStream(_: [String: Any]) {}
60
57
 
@@ -62,7 +59,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
62
59
 
63
60
  func sendStreamKeepAlive(_: [String: Any]) {}
64
61
 
65
- func startVideoRecording(requestId _: String, save _: Bool, flash _: Bool, sound _: Bool) {}
62
+ func startVideoRecording(requestId _: String, save _: Bool, sound _: Bool) {}
66
63
 
67
64
  func stopVideoRecording(requestId _: String) {}
68
65
 
@@ -72,8 +69,6 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
72
69
 
73
70
  func sendButtonMaxRecordingTime() {}
74
71
 
75
- func sendButtonCameraLedSetting() {}
76
-
77
72
  func sendCameraFovSetting() {}
78
73
 
79
74
  func setBrightness(_ level: Int, autoMode: Bool) {
@@ -81,6 +76,10 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
81
76
  updateGlassesAutoBrightness(autoMode)
82
77
  }
83
78
 
79
+ func sendText(_ text: String) async {
80
+ await sendTextWall(text)
81
+ }
82
+
84
83
  func sendDoubleTextWall(_ top: String, _ bottom: String) async {
85
84
  await sendTextWall("\(top)\n\(bottom)")
86
85
  }
@@ -178,7 +177,7 @@ class MentraNexSGC: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, SG
178
177
 
179
178
  func sendHotspotState(_: Bool) {}
180
179
 
181
- func sendOtaStart() {}
180
+ func sendOtaStart(otaVersionUrl: String?) {}
182
181
  func sendOtaQueryStatus() {}
183
182
 
184
183
  func sendUserEmailToGlasses(_: String) {}
@@ -18,20 +18,17 @@ protocol SGCManager {
18
18
 
19
19
  // MARK: - Camera & Media
20
20
 
21
- func requestPhoto(
22
- _ requestId: String, appId: String, size: String?, webhookUrl: String?, authToken: String?,
23
- compress: String?, flash: Bool, save: Bool, sound: Bool, exposureTimeNs: Double?, iso: Int?
24
- )
21
+ func requestPhoto(_ request: PhotoRequest)
25
22
  func startStream(_ message: [String: Any])
26
23
  func stopStream()
27
24
  func sendStreamKeepAlive(_ message: [String: Any])
28
- func startVideoRecording(requestId: String, save: Bool, flash: Bool, sound: Bool)
25
+ func startVideoRecording(requestId: String, save: Bool, sound: Bool)
29
26
  /// Start video recording with optional per-recording resolution/fps. A width,
30
27
  /// height, or fps of 0 means "use the device's saved button-video default".
31
28
  /// Defaulted in an extension to delegate to the basic recording path; devices
32
29
  /// that support custom settings (e.g. Mentra Live) override this.
33
30
  func startVideoRecording(
34
- requestId: String, save: Bool, flash: Bool, sound: Bool, width: Int, height: Int, fps: Int,
31
+ requestId: String, save: Bool, sound: Bool, width: Int, height: Int, fps: Int,
35
32
  maxRecordingTimeMinutes: Int
36
33
  )
37
34
  func stopVideoRecording(requestId: String)
@@ -47,13 +44,13 @@ protocol SGCManager {
47
44
  func sendButtonPhotoSettings()
48
45
  func sendButtonVideoRecordingSettings()
49
46
  func sendButtonMaxRecordingTime()
50
- func sendButtonCameraLedSetting()
51
47
  func sendCameraFovSetting()
52
48
 
53
49
  // MARK: - Display Control
54
50
 
55
51
  func setBrightness(_ level: Int, autoMode: Bool)
56
52
  func clearDisplay()
53
+ func sendText(_ text: String) async
57
54
  func sendTextWall(_ text: String) async
58
55
  func sendDoubleTextWall(_ top: String, _ bottom: String) async
59
56
  /// Display a bitmap. Optional `x`/`y`/`width`/`height` position and size the target
@@ -118,7 +115,7 @@ protocol SGCManager {
118
115
  func sendWifiCredentials(_ ssid: String, _ password: String)
119
116
  func forgetWifiNetwork(_ ssid: String)
120
117
  func sendHotspotState(_ enabled: Bool)
121
- func sendOtaStart()
118
+ func sendOtaStart(otaVersionUrl: String?)
122
119
  func sendOtaQueryStatus()
123
120
  func sendSetSystemTime(_ timestampMs: Int64)
124
121
  func sendOtaRetryVersionCheck()
@@ -151,10 +148,10 @@ extension SGCManager {
151
148
  // MARK: - Video recording (default: ignore custom settings, use saved defaults)
152
149
 
153
150
  func startVideoRecording(
154
- requestId: String, save: Bool, flash: Bool, sound: Bool, width _: Int, height _: Int,
151
+ requestId: String, save: Bool, sound: Bool, width _: Int, height _: Int,
155
152
  fps _: Int, maxRecordingTimeMinutes _: Int
156
153
  ) {
157
- startVideoRecording(requestId: requestId, save: save, flash: flash, sound: sound)
154
+ startVideoRecording(requestId: requestId, save: save, sound: sound)
158
155
  }
159
156
 
160
157
  func stopVideoRecording(requestId: String, webhookUrl _: String?, authToken _: String?) {
@@ -79,8 +79,8 @@ class Simulated: SGCManager {
79
79
 
80
80
  // MARK: - Camera & Media
81
81
 
82
- func requestPhoto(_: String, appId _: String, size _: String?, webhookUrl _: String?, authToken _: String?, compress _: String?, flash _: Bool, save _: Bool, sound _: Bool, exposureTimeNs _: Double?, iso _: Int?) {
83
- Bridge.log("requestPhoto")
82
+ func requestPhoto(_ request: PhotoRequest) {
83
+ Bridge.log("requestPhoto save=\(request.save) sound=\(request.sound)")
84
84
  }
85
85
 
86
86
  func startStream(_: [String: Any]) {
@@ -95,7 +95,7 @@ class Simulated: SGCManager {
95
95
  Bridge.log("sendStreamKeepAlive")
96
96
  }
97
97
 
98
- func startVideoRecording(requestId _: String, save _: Bool, flash _: Bool, sound _: Bool) {
98
+ func startVideoRecording(requestId _: String, save _: Bool, sound _: Bool) {
99
99
  Bridge.log("startVideoRecording")
100
100
  }
101
101
 
@@ -113,10 +113,6 @@ class Simulated: SGCManager {
113
113
  Bridge.log("sendButtonVideoRecordingSettings")
114
114
  }
115
115
 
116
- func sendButtonCameraLedSetting() {
117
- Bridge.log("sendButtonCameraLedSetting")
118
- }
119
-
120
116
  func sendCameraFovSetting() {
121
117
  Bridge.log("sendCameraFovSetting")
122
118
  }
@@ -133,6 +129,10 @@ class Simulated: SGCManager {
133
129
  Bridge.log("clearDisplay")
134
130
  }
135
131
 
132
+ func sendText(_ text: String) async {
133
+ await sendTextWall(text)
134
+ }
135
+
136
136
  func sendTextWall(_: String) async {
137
137
  Bridge.log("sendTextWall")
138
138
  }
@@ -246,7 +246,7 @@ class Simulated: SGCManager {
246
246
  Bridge.log("sendUserEmailToGlasses: \(email)")
247
247
  }
248
248
 
249
- func sendOtaStart() {
249
+ func sendOtaStart(otaVersionUrl: String?) {
250
250
  Bridge.log("sendOtaStart")
251
251
  }
252
252
 
@@ -552,10 +552,6 @@ struct BluetoothStatus: CustomStringConvertible {
552
552
  ButtonPhotoSize(rawValue: stringValue(values, "button_photo_size") ?? "") ?? .medium
553
553
  }
554
554
 
555
- var buttonCameraLed: Bool {
556
- boolValue(values, "button_camera_led") ?? true
557
- }
558
-
559
555
  var buttonMaxRecordingTime: Int {
560
556
  intValue(values["button_max_recording_time"]) ?? 10
561
557
  }
@@ -921,11 +917,7 @@ struct BluetoothStatusUpdate: CustomStringConvertible {
921
917
  }
922
918
 
923
919
  var buttonPhotoSize: ButtonPhotoSize? {
924
- optionalStringValue(values, "button_photo_size").flatMap(ButtonPhotoSize.init(rawValue:))
925
- }
926
-
927
- var buttonCameraLed: Bool? {
928
- optionalBoolValue(values, "button_camera_led")
920
+ optionalStringValue(values, "button_photo_size").map { ButtonPhotoSize(normalizedRawValue: $0) }
929
921
  }
930
922
 
931
923
  var buttonMaxRecordingTime: Int? {
@@ -23,6 +23,7 @@ public struct StreamVideoConfig {
23
23
  if let width { values["width"] = width }
24
24
  if let height { values["height"] = height }
25
25
  if let bitrate { values["bitrate"] = bitrate }
26
+ // ASG stream parsers shipped with the BLE key named "frameRate".
26
27
  if let fps { values["frameRate"] = fps }
27
28
  return values
28
29
  }
@@ -219,31 +220,22 @@ public struct StreamResolvedConfig: Equatable {
219
220
  public struct StreamRequest {
220
221
  public let streamUrl: String
221
222
  public let streamId: String
222
- public let keepAlive: Bool
223
- public let keepAliveIntervalSeconds: Int
224
223
  public let sound: Bool
225
224
  public let video: StreamVideoConfig?
226
225
  public let audio: StreamAudioConfig?
227
- public let extraValues: [String: Any]
228
226
 
229
227
  public init(
230
228
  streamUrl: String,
231
229
  streamId: String = "",
232
- keepAlive: Bool = true,
233
- keepAliveIntervalSeconds: Int = 5,
234
230
  sound: Bool = true,
235
231
  video: StreamVideoConfig? = nil,
236
- audio: StreamAudioConfig? = nil,
237
- extraValues: [String: Any] = [:]
232
+ audio: StreamAudioConfig? = nil
238
233
  ) {
239
234
  self.streamUrl = streamUrl
240
235
  self.streamId = streamId
241
- self.keepAlive = keepAlive
242
- self.keepAliveIntervalSeconds = keepAliveIntervalSeconds
243
236
  self.sound = sound
244
237
  self.video = video
245
238
  self.audio = audio
246
- self.extraValues = extraValues
247
239
  }
248
240
 
249
241
  init(values: [String: Any]) {
@@ -254,25 +246,17 @@ public struct StreamRequest {
254
246
  ?? values["whipUrl"] as? String
255
247
  ?? "",
256
248
  streamId: values["streamId"] as? String ?? "",
257
- keepAlive: values["keepAlive"] as? Bool ?? true,
258
- keepAliveIntervalSeconds: intValue(values["keepAliveIntervalSeconds"]) ?? 5,
259
249
  sound: values["sound"] as? Bool ?? true,
260
250
  video: StreamVideoConfig(values: values["video"] as? [String: Any]),
261
- audio: StreamAudioConfig(values: values["audio"] as? [String: Any]),
262
- extraValues: values
251
+ audio: StreamAudioConfig(values: values["audio"] as? [String: Any])
263
252
  )
264
253
  }
265
254
 
266
255
  public var values: [String: Any] {
267
- var values = extraValues
268
- values.removeValue(forKey: "keepAliveMode")
256
+ var values: [String: Any] = [:]
269
257
  values["type"] = "start_stream"
270
258
  values["streamUrl"] = streamUrl
271
259
  values["streamId"] = streamId
272
- values["keepAlive"] = keepAlive
273
- values["keepAliveIntervalSeconds"] = keepAliveIntervalSeconds
274
- // The camera light is a privacy indicator and cannot be disabled by SDK callers.
275
- values["flash"] = true
276
260
  values["sound"] = sound
277
261
  if let videoValues = video?.dictionary, !videoValues.isEmpty {
278
262
  values["video"] = videoValues
@@ -284,33 +268,24 @@ public struct StreamRequest {
284
268
  }
285
269
  }
286
270
 
287
- extension StreamRequest {
288
- var isExternallyManagedKeepAlive: Bool {
289
- stringValue(extraValues, "keepAliveMode") == "external"
290
- }
291
- }
292
-
293
271
  struct StreamKeepAliveRequest {
294
272
  let streamId: String
295
273
  let ackId: String
296
- let extraValues: [String: Any]
297
274
 
298
- init(streamId: String, ackId: String, extraValues: [String: Any] = [:]) {
275
+ init(streamId: String, ackId: String) {
299
276
  self.streamId = streamId
300
277
  self.ackId = ackId
301
- self.extraValues = extraValues
302
278
  }
303
279
 
304
280
  init(values: [String: Any]) {
305
281
  self.init(
306
282
  streamId: values["streamId"] as? String ?? "",
307
- ackId: values["ackId"] as? String ?? "",
308
- extraValues: values
283
+ ackId: values["ackId"] as? String ?? ""
309
284
  )
310
285
  }
311
286
 
312
287
  var values: [String: Any] {
313
- var values = extraValues
288
+ var values: [String: Any] = [:]
314
289
  values["type"] = "keep_stream_alive"
315
290
  values["streamId"] = streamId
316
291
  values["ackId"] = ackId
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/bluetooth-sdk",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "SDK for communicating with smart glasses",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -22,6 +22,11 @@
22
22
  "react-native": "./src/photo-receiver/index.ts",
23
23
  "default": "./build/photo-receiver/index.js"
24
24
  },
25
+ "./debug": {
26
+ "types": "./build/debug.d.ts",
27
+ "react-native": "./src/debug.ts",
28
+ "default": "./build/debug.js"
29
+ },
25
30
  "./_private/*": null,
26
31
  "./build/*": null,
27
32
  "./src/*": null,