@mentra/bluetooth-sdk 0.1.7 → 0.1.9
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 +11 -9
- package/android/.project +28 -0
- package/android/build.gradle +3 -0
- package/android/lc3Lib/.project +28 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +15 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +26 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +4 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +136 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +40 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/ControllerManager.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/controllers/R1.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +149 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +297 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/MentraPhotoReceiverModule.kt +120 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +13 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.java +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G2.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.java +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.java +104 -21
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Simulated.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +128 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunkReassembler.java +105 -0
- package/build/BluetoothSdk.types.d.ts +66 -29
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js +3 -0
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +2 -1
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +24 -7
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/index.d.ts +2 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -3
- package/build/index.js.map +1 -1
- package/build/photo-receiver/MentraPhotoReceiver.types.d.ts +18 -0
- package/build/photo-receiver/MentraPhotoReceiver.types.d.ts.map +1 -0
- package/build/photo-receiver/MentraPhotoReceiver.types.js +2 -0
- package/build/photo-receiver/MentraPhotoReceiver.types.js.map +1 -0
- package/build/photo-receiver/MentraPhotoReceiverModule.d.ts +10 -0
- package/build/photo-receiver/MentraPhotoReceiverModule.d.ts.map +1 -0
- package/build/photo-receiver/MentraPhotoReceiverModule.js +3 -0
- package/build/photo-receiver/MentraPhotoReceiverModule.js.map +1 -0
- package/build/photo-receiver/MentraPhotoReceiverModule.web.d.ts +10 -0
- package/build/photo-receiver/MentraPhotoReceiverModule.web.d.ts.map +1 -0
- package/build/photo-receiver/MentraPhotoReceiverModule.web.js +12 -0
- package/build/photo-receiver/MentraPhotoReceiverModule.web.js.map +1 -0
- package/build/photo-receiver/index.d.ts +3 -0
- package/build/photo-receiver/index.d.ts.map +1 -0
- package/build/photo-receiver/index.js +3 -0
- package/build/photo-receiver/index.js.map +1 -0
- package/expo-module.config.json +5 -2
- package/ios/BluetoothSdkModule.swift +19 -3
- package/ios/LocalPhotoUploadServer.swift +411 -0
- package/ios/MentraBluetoothSDK.podspec +8 -2
- package/ios/MentraPhotoReceiverModule.swift +147 -0
- package/ios/Source/Bridge.swift +20 -1
- package/ios/Source/Camera/CameraModels.swift +64 -11
- package/ios/Source/DeviceManager.swift +6 -4
- package/ios/Source/Events/BluetoothEvents.swift +3 -0
- package/ios/Source/MentraBluetoothSDK.swift +111 -5
- package/ios/Source/ObservableStore.swift +2 -2
- package/ios/Source/Streaming/StreamModels.swift +234 -40
- package/ios/Source/controllers/ControllerManager.swift +1 -1
- package/ios/Source/controllers/R1.swift +9 -3
- package/ios/Source/sgcs/G1.swift +5 -4
- package/ios/Source/sgcs/G2.swift +4 -4
- package/ios/Source/sgcs/Mach1.swift +1 -1
- package/ios/Source/sgcs/MentraLive.swift +76 -12
- package/ios/Source/sgcs/MentraNex.swift +1 -1
- package/ios/Source/sgcs/SGCManager.swift +1 -1
- package/ios/Source/sgcs/Simulated.swift +1 -1
- package/ios/Source/utils/MessageChunkReassembler.swift +89 -0
- package/package.json +6 -1
- package/src/BluetoothSdk.types.ts +90 -35
- package/src/_private/BluetoothSdkModule.ts +38 -8
- package/src/index.ts +9 -4
- package/src/photo-receiver/MentraPhotoReceiver.types.ts +20 -0
- package/src/photo-receiver/MentraPhotoReceiverModule.ts +14 -0
- package/src/photo-receiver/MentraPhotoReceiverModule.web.ts +20 -0
- package/src/photo-receiver/index.ts +2 -0
|
@@ -39,17 +39,24 @@ public struct ButtonVideoRecordingSettings {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
public
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
public struct CameraFov {
|
|
43
|
+
public static let minFov = 82
|
|
44
|
+
public static let maxFov = 118
|
|
45
|
+
public static let defaultFov = 102
|
|
46
|
+
public static let minRoiPosition = 0
|
|
47
|
+
public static let maxRoiPosition = 2
|
|
48
|
+
public static let defaultRoiPosition = 0
|
|
49
|
+
|
|
50
|
+
public let fov: Int
|
|
51
|
+
public let roiPosition: Int
|
|
52
|
+
|
|
53
|
+
public init(fov: Int = CameraFov.defaultFov, roiPosition: Int = CameraFov.defaultRoiPosition) {
|
|
54
|
+
self.fov = min(max(fov, CameraFov.minFov), CameraFov.maxFov)
|
|
55
|
+
self.roiPosition = min(max(roiPosition, CameraFov.minRoiPosition), CameraFov.maxRoiPosition)
|
|
56
|
+
}
|
|
45
57
|
|
|
46
58
|
var value: [String: Int] {
|
|
47
|
-
|
|
48
|
-
case .standard:
|
|
49
|
-
["fov": 118, "roi_position": 0]
|
|
50
|
-
case .wide:
|
|
51
|
-
["fov": 118, "roi_position": 0]
|
|
52
|
-
}
|
|
59
|
+
["fov": fov, "roi_position": roiPosition]
|
|
53
60
|
}
|
|
54
61
|
}
|
|
55
62
|
|
|
@@ -64,6 +71,8 @@ public struct PhotoRequest {
|
|
|
64
71
|
public let sound: Bool
|
|
65
72
|
/// Sensor exposure time for this capture only (ns), or nil for auto exposure
|
|
66
73
|
public let exposureTimeNs: Double?
|
|
74
|
+
/// Sensor ISO for this capture only. Only used when exposureTimeNs enables manual exposure.
|
|
75
|
+
public let iso: Int?
|
|
67
76
|
|
|
68
77
|
public init(
|
|
69
78
|
requestId: String,
|
|
@@ -74,7 +83,8 @@ public struct PhotoRequest {
|
|
|
74
83
|
compress: PhotoCompression? = nil,
|
|
75
84
|
flash: Bool = true,
|
|
76
85
|
sound: Bool,
|
|
77
|
-
exposureTimeNs: Double? = nil
|
|
86
|
+
exposureTimeNs: Double? = nil,
|
|
87
|
+
iso: Int? = nil
|
|
78
88
|
) {
|
|
79
89
|
self.requestId = requestId
|
|
80
90
|
self.appId = appId
|
|
@@ -85,6 +95,7 @@ public struct PhotoRequest {
|
|
|
85
95
|
self.flash = flash
|
|
86
96
|
self.sound = sound
|
|
87
97
|
self.exposureTimeNs = exposureTimeNs
|
|
98
|
+
self.iso = iso
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
101
|
|
|
@@ -229,7 +240,7 @@ public struct PhotoResponseEvent: CustomStringConvertible {
|
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
public init(values: [String: Any]) {
|
|
232
|
-
|
|
243
|
+
response = PhotoResponse(values: values)
|
|
233
244
|
}
|
|
234
245
|
|
|
235
246
|
public var requestId: String {
|
|
@@ -246,3 +257,45 @@ public struct PhotoResponseEvent: CustomStringConvertible {
|
|
|
246
257
|
"PhotoResponseEvent(requestId: \(requestId), state: \(response.state.rawValue))"
|
|
247
258
|
}
|
|
248
259
|
}
|
|
260
|
+
|
|
261
|
+
public struct PhotoStatusEvent: CustomStringConvertible {
|
|
262
|
+
public let values: [String: Any]
|
|
263
|
+
|
|
264
|
+
public init(values: [String: Any]) {
|
|
265
|
+
var values = values
|
|
266
|
+
values["type"] = "photo_status"
|
|
267
|
+
self.values = values
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
public var requestId: String {
|
|
271
|
+
stringValue(values, "requestId") ?? ""
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
public var status: String {
|
|
275
|
+
stringValue(values, "status") ?? ""
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
public var timestamp: Int64 {
|
|
279
|
+
if let value = values["timestamp"] as? Int64 { return value }
|
|
280
|
+
if let value = values["timestamp"] as? Int { return Int64(value) }
|
|
281
|
+
if let value = values["timestamp"] as? Double { return Int64(value) }
|
|
282
|
+
if let value = values["timestamp"] as? NSNumber { return value.int64Value }
|
|
283
|
+
return Int64(Date().timeIntervalSince1970 * 1000)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
public var resolvedConfig: [String: Any]? {
|
|
287
|
+
values["resolvedConfig"] as? [String: Any]
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
public var errorCode: String? {
|
|
291
|
+
stringValue(values, "errorCode")
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
public var errorMessage: String? {
|
|
295
|
+
stringValue(values, "errorMessage")
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
public var description: String {
|
|
299
|
+
"PhotoStatusEvent(requestId: \(requestId), status: \(status))"
|
|
300
|
+
}
|
|
301
|
+
}
|
|
@@ -733,7 +733,7 @@ struct ViewState {
|
|
|
733
733
|
if !glassesMicEnabled || !glassesConnected {
|
|
734
734
|
return
|
|
735
735
|
}
|
|
736
|
-
|
|
736
|
+
|
|
737
737
|
let timeSinceLastLc3Event = Date().timeIntervalSince(lastLc3Event ?? Date())
|
|
738
738
|
if timeSinceLastLc3Event > 5 {
|
|
739
739
|
Bridge.log("MAN: No audio activity in the last 5 seconds from glasses, reinitializing glasses mic")
|
|
@@ -1196,10 +1196,12 @@ struct ViewState {
|
|
|
1196
1196
|
_ compress: String?,
|
|
1197
1197
|
_ flash: Bool,
|
|
1198
1198
|
_ sound: Bool,
|
|
1199
|
-
exposureTimeNs: Double? = nil
|
|
1199
|
+
exposureTimeNs: Double? = nil,
|
|
1200
|
+
iso: Int? = nil
|
|
1200
1201
|
) {
|
|
1202
|
+
let manualIso = exposureTimeNs != nil ? iso.flatMap { $0 > 0 ? $0 : nil } : nil
|
|
1201
1203
|
Bridge.log(
|
|
1202
|
-
"MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=\(requestId) appId=\(appId) webhookUrl=\(webhookUrl ?? "nil") size=\(size) compress=\(compress ?? "none") flash=\(flash) sound=\(sound) exposureTimeNs=\(exposureTimeNs.map { String($0) } ?? "nil") sgc=\(sgc != nil ? String(describing: type(of: sgc!)) : "null")"
|
|
1204
|
+
"MAN: PHOTO PIPELINE [4/6] DeviceManager.requestPhoto requestId=\(requestId) appId=\(appId) webhookUrl=\(webhookUrl ?? "nil") size=\(size) compress=\(compress ?? "none") flash=\(flash) sound=\(sound) exposureTimeNs=\(exposureTimeNs.map { String($0) } ?? "nil") iso=\(manualIso.map { String($0) } ?? "auto") sgc=\(sgc != nil ? String(describing: type(of: sgc!)) : "null")"
|
|
1203
1205
|
)
|
|
1204
1206
|
guard let sgc else {
|
|
1205
1207
|
Bridge.log(
|
|
@@ -1209,7 +1211,7 @@ struct ViewState {
|
|
|
1209
1211
|
}
|
|
1210
1212
|
sgc.requestPhoto(
|
|
1211
1213
|
requestId, appId: appId, size: size, webhookUrl: webhookUrl, authToken: authToken,
|
|
1212
|
-
compress: compress, flash: flash, sound: sound, exposureTimeNs: exposureTimeNs
|
|
1214
|
+
compress: compress, flash: flash, sound: sound, exposureTimeNs: exposureTimeNs, iso: manualIso
|
|
1213
1215
|
)
|
|
1214
1216
|
}
|
|
1215
1217
|
|
|
@@ -81,6 +81,7 @@ public enum BluetoothEvent: CustomStringConvertible {
|
|
|
81
81
|
case hotspotStatus(HotspotStatusEvent)
|
|
82
82
|
case hotspotError(HotspotErrorEvent)
|
|
83
83
|
case photoResponse(PhotoResponseEvent)
|
|
84
|
+
case photoStatus(PhotoStatusEvent)
|
|
84
85
|
case streamStatus(StreamStatusEvent)
|
|
85
86
|
case keepAliveAck(KeepAliveAckEvent)
|
|
86
87
|
case localTranscription(LocalTranscriptionEvent)
|
|
@@ -104,6 +105,8 @@ public enum BluetoothEvent: CustomStringConvertible {
|
|
|
104
105
|
event.description
|
|
105
106
|
case let .photoResponse(event):
|
|
106
107
|
event.description
|
|
108
|
+
case let .photoStatus(event):
|
|
109
|
+
event.description
|
|
107
110
|
case let .streamStatus(event):
|
|
108
111
|
event.description
|
|
109
112
|
case let .keepAliveAck(event):
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import CoreBluetooth
|
|
2
2
|
import Foundation
|
|
3
3
|
|
|
4
|
+
@MainActor
|
|
5
|
+
private final class ActiveStreamKeepAlive {
|
|
6
|
+
let streamId: String
|
|
7
|
+
let intervalSeconds: Int
|
|
8
|
+
var pendingAckId: String?
|
|
9
|
+
var missedAckCount = 0
|
|
10
|
+
var task: Task<Void, Never>?
|
|
11
|
+
|
|
12
|
+
init(streamId: String, intervalSeconds: Int) {
|
|
13
|
+
self.streamId = streamId
|
|
14
|
+
self.intervalSeconds = intervalSeconds
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
@MainActor
|
|
5
19
|
private final class ActiveScanSession {
|
|
6
20
|
let model: DeviceModel
|
|
@@ -33,6 +47,7 @@ public final class MentraBluetoothSDK {
|
|
|
33
47
|
private var suppressDefaultDeviceEvents = false
|
|
34
48
|
private var defaultDeviceApplyGeneration = 0
|
|
35
49
|
private var activeScanSessions: [UUID: ActiveScanSession] = [:]
|
|
50
|
+
private var activeStreamKeepAlive: ActiveStreamKeepAlive?
|
|
36
51
|
|
|
37
52
|
public init(configuration: MentraBluetoothSDKConfiguration = .default) {
|
|
38
53
|
self.configuration = configuration
|
|
@@ -388,7 +403,8 @@ public final class MentraBluetoothSDK {
|
|
|
388
403
|
request.compress?.rawValue,
|
|
389
404
|
request.flash,
|
|
390
405
|
request.sound,
|
|
391
|
-
exposureTimeNs: request.exposureTimeNs
|
|
406
|
+
exposureTimeNs: request.exposureTimeNs,
|
|
407
|
+
iso: request.iso
|
|
392
408
|
)
|
|
393
409
|
}
|
|
394
410
|
|
|
@@ -397,10 +413,17 @@ public final class MentraBluetoothSDK {
|
|
|
397
413
|
}
|
|
398
414
|
|
|
399
415
|
public func startStream(_ request: StreamRequest) {
|
|
400
|
-
|
|
416
|
+
var values = request.values
|
|
417
|
+
let streamId = stringValue(values, "streamId").flatMap { $0.isEmpty ? nil : $0 } ?? "sdk-\(UUID().uuidString)"
|
|
418
|
+
values["streamId"] = streamId
|
|
419
|
+
stopStreamKeepAliveMonitor()
|
|
420
|
+
DeviceManager.shared.startStream(values)
|
|
421
|
+
if request.keepAlive, !request.isExternallyManagedKeepAlive {
|
|
422
|
+
startStreamKeepAliveMonitor(streamId: streamId, intervalSeconds: request.keepAliveIntervalSeconds)
|
|
423
|
+
}
|
|
401
424
|
}
|
|
402
425
|
|
|
403
|
-
|
|
426
|
+
func sendCloudStreamKeepAlive(_ request: StreamKeepAliveRequest) {
|
|
404
427
|
DeviceManager.shared.keepStreamAlive(request.values)
|
|
405
428
|
}
|
|
406
429
|
|
|
@@ -417,6 +440,7 @@ public final class MentraBluetoothSDK {
|
|
|
417
440
|
}
|
|
418
441
|
|
|
419
442
|
public func stopStream() {
|
|
443
|
+
stopStreamKeepAliveMonitor()
|
|
420
444
|
DeviceManager.shared.stopStream()
|
|
421
445
|
}
|
|
422
446
|
|
|
@@ -457,6 +481,7 @@ public final class MentraBluetoothSDK {
|
|
|
457
481
|
}
|
|
458
482
|
|
|
459
483
|
public func invalidate() {
|
|
484
|
+
stopStreamKeepAliveMonitor()
|
|
460
485
|
if let bridgeEventSinkId {
|
|
461
486
|
Bridge.removeEventSink(bridgeEventSinkId)
|
|
462
487
|
self.bridgeEventSinkId = nil
|
|
@@ -468,6 +493,80 @@ public final class MentraBluetoothSDK {
|
|
|
468
493
|
delegate = nil
|
|
469
494
|
}
|
|
470
495
|
|
|
496
|
+
private func startStreamKeepAliveMonitor(streamId: String, intervalSeconds requestedIntervalSeconds: Int) {
|
|
497
|
+
let intervalSeconds = requestedIntervalSeconds > 0 ? requestedIntervalSeconds : 5
|
|
498
|
+
let tracker = ActiveStreamKeepAlive(streamId: streamId, intervalSeconds: intervalSeconds)
|
|
499
|
+
activeStreamKeepAlive = tracker
|
|
500
|
+
sendNextStreamKeepAlive(for: tracker)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
private func stopStreamKeepAliveMonitor() {
|
|
504
|
+
activeStreamKeepAlive?.task?.cancel()
|
|
505
|
+
activeStreamKeepAlive = nil
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
private func sendNextStreamKeepAlive(for tracker: ActiveStreamKeepAlive) {
|
|
509
|
+
guard activeStreamKeepAlive === tracker else { return }
|
|
510
|
+
|
|
511
|
+
if tracker.pendingAckId != nil {
|
|
512
|
+
tracker.missedAckCount += 1
|
|
513
|
+
if tracker.missedAckCount >= 3 {
|
|
514
|
+
activeStreamKeepAlive = nil
|
|
515
|
+
tracker.task?.cancel()
|
|
516
|
+
let event = StreamStatusEvent(
|
|
517
|
+
status: .error(
|
|
518
|
+
streamId: tracker.streamId,
|
|
519
|
+
errorDetails: "Stream keep-alive timed out after \(tracker.missedAckCount) missed ACKs",
|
|
520
|
+
timestamp: Int(Date().timeIntervalSince1970 * 1000),
|
|
521
|
+
resolvedConfig: nil
|
|
522
|
+
)
|
|
523
|
+
)
|
|
524
|
+
delegate?.mentraBluetoothSDK(self, didReceive: .streamStatus(event))
|
|
525
|
+
return
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
let ackId = "ack-\(Int(Date().timeIntervalSince1970 * 1000))"
|
|
530
|
+
tracker.pendingAckId = ackId
|
|
531
|
+
DeviceManager.shared.keepStreamAlive(
|
|
532
|
+
StreamKeepAliveRequest(streamId: tracker.streamId, ackId: ackId).values
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
tracker.task?.cancel()
|
|
536
|
+
tracker.task = Task { @MainActor [weak self, weak tracker] in
|
|
537
|
+
guard let tracker else { return }
|
|
538
|
+
try? await Task.sleep(nanoseconds: UInt64(tracker.intervalSeconds) * 1_000_000_000)
|
|
539
|
+
self?.sendNextStreamKeepAlive(for: tracker)
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
private func handleStreamKeepAliveAck(_ event: KeepAliveAckEvent) -> Bool {
|
|
544
|
+
guard let tracker = activeStreamKeepAlive,
|
|
545
|
+
event.streamId == tracker.streamId,
|
|
546
|
+
event.ackId == tracker.pendingAckId
|
|
547
|
+
else {
|
|
548
|
+
return false
|
|
549
|
+
}
|
|
550
|
+
tracker.pendingAckId = nil
|
|
551
|
+
tracker.missedAckCount = 0
|
|
552
|
+
return true
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
private func handleStreamStatusForKeepAlive(_ status: StreamStatus) {
|
|
556
|
+
if let streamId = status.streamId,
|
|
557
|
+
activeStreamKeepAlive?.streamId != streamId
|
|
558
|
+
{
|
|
559
|
+
return
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
switch status.state {
|
|
563
|
+
case .stopped, .stopping, .error, .reconnectFailed:
|
|
564
|
+
stopStreamKeepAliveMonitor()
|
|
565
|
+
default:
|
|
566
|
+
break
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
471
570
|
private func dispatchStoreUpdate(_ category: String, _ changes: [String: Any]) {
|
|
472
571
|
switch ObservableStore.normalizeCategory(category) {
|
|
473
572
|
case "glasses":
|
|
@@ -596,10 +695,17 @@ public final class MentraBluetoothSDK {
|
|
|
596
695
|
delegate?.mentraBluetoothSDK(self, didReceive: .hotspotError(HotspotErrorEvent(values: data)))
|
|
597
696
|
case "photo_response":
|
|
598
697
|
delegate?.mentraBluetoothSDK(self, didReceive: .photoResponse(PhotoResponseEvent(values: data)))
|
|
698
|
+
case "photo_status":
|
|
699
|
+
delegate?.mentraBluetoothSDK(self, didReceive: .photoStatus(PhotoStatusEvent(values: data)))
|
|
599
700
|
case "stream_status":
|
|
600
|
-
|
|
701
|
+
let event = StreamStatusEvent(values: data)
|
|
702
|
+
handleStreamStatusForKeepAlive(event.status)
|
|
703
|
+
delegate?.mentraBluetoothSDK(self, didReceive: .streamStatus(event))
|
|
601
704
|
case "keep_alive_ack":
|
|
602
|
-
|
|
705
|
+
let event = KeepAliveAckEvent(values: data)
|
|
706
|
+
if !handleStreamKeepAliveAck(event) {
|
|
707
|
+
delegate?.mentraBluetoothSDK(self, didReceive: .keepAliveAck(event))
|
|
708
|
+
}
|
|
603
709
|
case "compatible_glasses_search_stop":
|
|
604
710
|
delegate?.mentraBluetoothSDK(self, didStopScan: .completed)
|
|
605
711
|
case "pair_failure":
|
|
@@ -9,7 +9,7 @@ import Foundation
|
|
|
9
9
|
|
|
10
10
|
@MainActor
|
|
11
11
|
class ObservableStore {
|
|
12
|
-
private
|
|
12
|
+
private var values: [String: Any] = [:]
|
|
13
13
|
private var onEmit: ((String, [String: Any]) -> Void)?
|
|
14
14
|
private var listeners: [String: (String, [String: Any]) -> Void] = [:]
|
|
15
15
|
|
|
@@ -54,7 +54,7 @@ class ObservableStore {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
func get(_ category: String, _ key: String) -> Any? {
|
|
58
58
|
values["\(Self.normalizeCategory(category)).\(key)"]
|
|
59
59
|
}
|
|
60
60
|
|