@mentra/bluetooth-sdk 3.2.0-dev.203 → 3.2.0-dev.206

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 (31) hide show
  1. package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +0 -8
  2. package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
  3. package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +13 -152
  4. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +14 -0
  5. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamControllerProbe.kt +14 -0
  6. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +16 -0
  7. package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamSessionState.kt +32 -0
  8. package/android/src/test/java/com/mentra/bluetoothsdk/streaming/StreamControllerProbeTest.kt +24 -0
  9. package/android/src/test/java/com/mentra/bluetoothsdk/streaming/StreamSessionStateTest.kt +43 -0
  10. package/build/BluetoothSdk.types.d.ts +6 -1
  11. package/build/BluetoothSdk.types.d.ts.map +1 -1
  12. package/build/BluetoothSdk.types.js.map +1 -1
  13. package/build/_private/BluetoothSdkModule.d.ts +1 -3
  14. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  15. package/build/_private/BluetoothSdkModule.js.map +1 -1
  16. package/build/generated/releaseMetadata.js +5 -5
  17. package/build/generated/releaseMetadata.js.map +1 -1
  18. package/ios/BluetoothSdkModule.swift +0 -11
  19. package/ios/Source/BluetoothSdkDefaults.swift +1 -1
  20. package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
  21. package/ios/Source/MentraBluetoothSDK.swift +10 -130
  22. package/ios/Source/sgcs/MentraLive.swift +46 -35
  23. package/ios/Source/streaming/StreamControllerProbe.swift +18 -0
  24. package/ios/Source/streaming/StreamModels.swift +12 -0
  25. package/ios/Source/streaming/StreamSessionState.swift +29 -0
  26. package/ios/Tests/StreamControllerProbeTests.swift +27 -0
  27. package/ios/Tests/StreamSessionStateTests.swift +38 -0
  28. package/package.json +1 -1
  29. package/src/BluetoothSdk.types.ts +6 -1
  30. package/src/_private/BluetoothSdkModule.ts +0 -3
  31. package/src/generated/releaseMetadata.ts +5 -5
@@ -629,6 +629,10 @@ public enum StreamStatus: CustomStringConvertible, Equatable {
629
629
  public struct StreamStatusEvent: CustomStringConvertible {
630
630
  public let status: StreamStatus
631
631
  public let stats: StreamLiveStats?
632
+ public private(set) var processSessionId: String?
633
+ public private(set) var revision: Int?
634
+ public private(set) var terminal: Bool?
635
+ public private(set) var errorDetails: String?
632
636
  /// True when the glasses will retry the failed publisher themselves
633
637
  /// (emitting side lands in PR #3488); absent on older firmware and on
634
638
  /// events not parsed from a glasses status map. Carried here instead of
@@ -644,6 +648,10 @@ public struct StreamStatusEvent: CustomStringConvertible {
644
648
  status = StreamStatus(values: values)
645
649
  stats = StreamLiveStats(values: values["stats"] as? [String: Any])
646
650
  willRetry = boolValue(values, "willRetry")
651
+ processSessionId = stringValue(values, "sid")
652
+ revision = optionalIntValue(values, "revision")
653
+ terminal = boolValue(values, "terminal")
654
+ errorDetails = stringValue(values, "errorDetails")
647
655
  }
648
656
 
649
657
  public var state: StreamState {
@@ -661,6 +669,10 @@ public struct StreamStatusEvent: CustomStringConvertible {
661
669
  public var values: [String: Any] {
662
670
  var values = status.values
663
671
  values["type"] = "stream_status"
672
+ if let processSessionId { values["sid"] = processSessionId }
673
+ if let revision { values["revision"] = revision }
674
+ if let terminal { values["terminal"] = terminal }
675
+ if let errorDetails { values["errorDetails"] = errorDetails }
664
676
  if let stats {
665
677
  values["stats"] = stats.values
666
678
  }
@@ -0,0 +1,29 @@
1
+ import Foundation
2
+
3
+ /// Observes glasses-owned streaming; delivery gaps never imply publisher failure.
4
+ final class StreamSessionState {
5
+ private(set) var processSessionId: String?
6
+ private(set) var currentStreamId: String?
7
+ private(set) var supported = false
8
+ private var revision: Int = -1
9
+
10
+ func ready(sessionId: String?, controlVersion: Int?) {
11
+ supported = controlVersion == 1 && !(sessionId ?? "").isEmpty
12
+ if processSessionId != sessionId {
13
+ currentStreamId = nil
14
+ revision = -1
15
+ }
16
+ processSessionId = sessionId
17
+ }
18
+
19
+ func accept(_ values: [String: Any]) -> Bool {
20
+ guard supported,
21
+ let sid = values["sid"] as? String, sid == processSessionId,
22
+ let incomingRevision = values["revision"] as? Int,
23
+ incomingRevision >= revision
24
+ else { return false }
25
+ revision = incomingRevision
26
+ currentStreamId = values["terminal"] as? Bool == true ? nil : values["streamId"] as? String
27
+ return true
28
+ }
29
+ }
@@ -0,0 +1,27 @@
1
+ @testable import MentraBluetoothSDK
2
+ import XCTest
3
+
4
+ final class StreamControllerProbeTests: XCTestCase {
5
+ private func probe() -> [String: Any] {
6
+ ["protocolVersion": 1, "controllerId": StreamControllerProbe.controllerId,
7
+ "streamId": "stream", "probeId": "nonce"]
8
+ }
9
+
10
+ func testAnswersCurrentProcessChallengeWithoutJsOrTimer() {
11
+ let response = StreamControllerProbe.response(probe())
12
+ XCTAssertEqual(response?["type"] as? String, "stream_controller_response")
13
+ XCTAssertEqual(response?["probeId"] as? String, "nonce")
14
+ XCTAssertEqual(response?["streamId"] as? String, "stream")
15
+ }
16
+
17
+ func testRejectsPreviousProcessAndMalformedChallenges() {
18
+ for (key, value) in [("controllerId", "previous-process" as Any),
19
+ ("protocolVersion", 2), ("protocolVersion", true),
20
+ ("probeId", ""), ("streamId", "")]
21
+ {
22
+ var input = probe()
23
+ input[key] = value
24
+ XCTAssertNil(StreamControllerProbe.response(input))
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,38 @@
1
+ @testable import MentraBluetoothSDK
2
+ import XCTest
3
+
4
+ final class StreamSessionStateTests: XCTestCase {
5
+ func testReadinessRequiresKnownProtocolAndProcessIdentity() {
6
+ let state = StreamSessionState()
7
+ state.ready(sessionId: "asg", controlVersion: nil)
8
+ XCTAssertFalse(state.supported)
9
+ state.ready(sessionId: "asg", controlVersion: 2)
10
+ XCTAssertFalse(state.supported)
11
+ state.ready(sessionId: "", controlVersion: 1)
12
+ XCTAssertFalse(state.supported)
13
+ state.ready(sessionId: "asg", controlVersion: 1)
14
+ XCTAssertTrue(state.supported)
15
+ }
16
+
17
+ func testReconnectionReconcilesTerminalStateWithoutHeartbeatTimeouts() {
18
+ let state = StreamSessionState()
19
+ state.ready(sessionId: "asg", controlVersion: 1)
20
+ XCTAssertTrue(state.accept(["sid": "asg", "revision": 1, "streamId": "stream", "terminal": false]))
21
+ state.ready(sessionId: "asg", controlVersion: 1)
22
+ XCTAssertEqual(state.currentStreamId, "stream")
23
+ XCTAssertTrue(state.accept(["sid": "asg", "revision": 3, "streamId": "stream", "terminal": true]))
24
+ XCTAssertNil(state.currentStreamId)
25
+ XCTAssertFalse(state.accept(["sid": "asg", "revision": 2, "streamId": "stream", "terminal": false]))
26
+ XCTAssertNil(state.currentStreamId)
27
+ }
28
+
29
+ func testProcessRestartRejectsOldStatusAndAcceptsNewStoppedSnapshot() {
30
+ let state = StreamSessionState()
31
+ state.ready(sessionId: "old", controlVersion: 1)
32
+ XCTAssertTrue(state.accept(["sid": "old", "revision": 8, "streamId": "stream", "terminal": false]))
33
+ state.ready(sessionId: "new", controlVersion: 1)
34
+ XCTAssertFalse(state.accept(["sid": "old", "revision": 9, "streamId": "stream", "terminal": false]))
35
+ XCTAssertTrue(state.accept(["sid": "new", "revision": 0, "terminal": true]))
36
+ XCTAssertNil(state.currentStreamId)
37
+ }
38
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/bluetooth-sdk",
3
- "version": "3.2.0-dev.203",
3
+ "version": "3.2.0-dev.206",
4
4
  "description": "SDK for communicating with smart glasses",
5
5
  "main": "build/index.js",
6
6
  "react-native": "src/index.ts",
@@ -864,6 +864,11 @@ export type StreamLiveStats = {
864
864
  type StreamStatusCommon = {
865
865
  type: "stream_status"
866
866
  streamId?: string
867
+ /** ASG process identity and monotonic revision for reconnect reconciliation. */
868
+ sid?: string
869
+ revision?: number
870
+ terminal?: boolean
871
+ errorDetails?: string
867
872
  timestamp?: number
868
873
  resolvedConfig?: StreamResolvedConfig
869
874
  stats?: StreamLiveStats
@@ -898,7 +903,7 @@ export type StreamStatusEvent =
898
903
  })
899
904
  | (StreamStatusCommon & {
900
905
  kind: "snapshot"
901
- status: "streaming" | "reconnecting" | "stopped"
906
+ status: StreamStatusState
902
907
  streaming: boolean
903
908
  reconnecting: boolean
904
909
  attempt?: number
@@ -46,7 +46,6 @@ import {
46
46
  ScanModelOptions,
47
47
  ScanOptions,
48
48
  SettingsAckSuccessEvent,
49
- StreamKeepAliveRequest,
50
49
  StreamStartRequest,
51
50
  StreamStatusEvent,
52
51
  VideoRecordingStartedStatusEvent,
@@ -205,9 +204,7 @@ declare class BluetoothSdkNativeModule extends NativeModule<BluetoothSdkModuleEv
205
204
 
206
205
  // Stream Commands
207
206
  startStream(params: StreamStartRequest): Promise<StreamStatusEvent>
208
- startExternallyManagedStream(params: StreamStartRequest): Promise<StreamStatusEvent>
209
207
  stopStream(): Promise<StreamStatusEvent>
210
- sendExternallyManagedStreamKeepAlive(params: StreamKeepAliveRequest): Promise<void>
211
208
 
212
209
  // Microphone Commands
213
210
  setMicState(enabled: boolean, useGlassesMic?: boolean, sendTranscript?: boolean, sendLc3Data?: boolean): Promise<void>
@@ -12,9 +12,9 @@ export interface BluetoothSdkReleaseMetadata {
12
12
  export const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({
13
13
  "schemaVersion": 1,
14
14
  "familyBaseVersion": "3.2.0",
15
- "releaseIdentity": "3.2.0-dev.203",
16
- "releaseSetId": "mentra-3.2.0-dev.203",
17
- "sourceCommit": "7eb4deefff27d498c2b4562d628607f399aa1f40",
18
- "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.203.json",
19
- "otaManifestSha256": "0ae026238c8e70970be4e181cc1ae73dd905418292edebddb610012f4f0a9b6f"
15
+ "releaseIdentity": "3.2.0-dev.206",
16
+ "releaseSetId": "mentra-3.2.0-dev.206",
17
+ "sourceCommit": "3c22b92a5a53ce608c087503417ca5b3d16cc926",
18
+ "otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.206.json",
19
+ "otaManifestSha256": "7bc5e09f2757b2c11da58c93459bb0a554f406dd0ae5b50b58db28b64b92c838"
20
20
  })