@mentra/bluetooth-sdk 3.2.0-dev.200 → 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.
- package/README.md +46 -45
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +5 -9
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +63 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceManager.kt +5 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedChangelogCatalog.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +298 -181
- package/android/src/main/java/com/mentra/bluetoothsdk/MessageAckPolicy.kt +4 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/WifiRequestResolution.kt +246 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioConnectionTracker.kt +104 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +326 -45
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SGCManager.kt +2 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +14 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamControllerProbe.kt +14 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +16 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamSessionState.kt +32 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/GlassesStatusClassicAudioTest.kt +14 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/MessageAckPolicyTest.kt +16 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/WifiRequestResolutionTest.kt +271 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/ClassicAudioConnectionTrackerTest.kt +131 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/streaming/StreamControllerProbeTest.kt +24 -0
- package/android/src/test/java/com/mentra/bluetoothsdk/streaming/StreamSessionStateTest.kt +43 -0
- package/build/BluetoothSdk.types.d.ts +68 -2
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +3 -4
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/generated/changelogCatalog.d.ts +1 -1
- package/build/generated/changelogCatalog.js +1 -1
- package/build/generated/changelogCatalog.js.map +1 -1
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -0
- package/build/index.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +8 -13
- package/ios/Source/BluetoothSdkDefaults.swift +1 -1
- package/ios/Source/Bridge.swift +63 -0
- package/ios/Source/DeviceManager.swift +6 -3
- package/ios/Source/GeneratedChangelogCatalog.swift +1 -1
- package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
- package/ios/Source/MentraBluetoothSDK.swift +372 -220
- package/ios/Source/WifiRequestResolution.swift +251 -0
- package/ios/Source/sgcs/MentraLive.swift +216 -70
- package/ios/Source/sgcs/SGCManager.swift +13 -1
- package/ios/Source/status/DeviceStatus.swift +28 -0
- package/ios/Source/streaming/StreamControllerProbe.swift +18 -0
- package/ios/Source/streaming/StreamModels.swift +12 -0
- package/ios/Source/streaming/StreamSessionState.swift +29 -0
- package/ios/Tests/StreamControllerProbeTests.swift +27 -0
- package/ios/Tests/StreamSessionStateTests.swift +38 -0
- package/ios/Tests/WifiRequestResolutionTests.swift +320 -0
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +82 -2
- package/src/_private/BluetoothSdkModule.ts +4 -4
- package/src/generated/changelogCatalog.ts +1 -1
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/index.ts +11 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import CoreFoundation
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
private func isWifiProtocolV1(_ raw: Any?) -> Bool {
|
|
5
|
+
guard let number = raw as? NSNumber,
|
|
6
|
+
CFGetTypeID(number) != CFBooleanGetTypeID()
|
|
7
|
+
else { return false }
|
|
8
|
+
return number.doubleValue == 1
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/// Validate raw fields before convenience conversions erase malformed or partial tuples.
|
|
12
|
+
func wifiResponseEnvelopeIsValid(_ values: [String: Any], allowLegacy: Bool) -> Bool {
|
|
13
|
+
for key in ["connected", "dispatched"] where values[key] != nil {
|
|
14
|
+
guard let number = values[key] as? NSNumber, CFGetTypeID(number) == CFBooleanGetTypeID()
|
|
15
|
+
else { return false }
|
|
16
|
+
}
|
|
17
|
+
let hasTuple = ["protocol_version", "requestId", "sid"].contains { values[$0] != nil }
|
|
18
|
+
if !hasTuple { return allowLegacy && values["outcome"] == nil }
|
|
19
|
+
guard let requestId = values["requestId"] as? String,
|
|
20
|
+
let sid = values["sid"] as? String
|
|
21
|
+
else { return false }
|
|
22
|
+
return isWifiProtocolV1(values["protocol_version"]) && wifiSsidIsValid(requestId) && wifiSsidIsValid(sid)
|
|
23
|
+
&& values["dispatched"] == nil
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
func wifiSsidIsValid(_ ssid: String) -> Bool {
|
|
27
|
+
!ssid.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let wifiCapabilityNegotiationTimeoutCode = "capability_negotiation_timeout"
|
|
31
|
+
|
|
32
|
+
enum WifiProtocolCapability: Equatable {
|
|
33
|
+
case unknown
|
|
34
|
+
case supported(version: Int)
|
|
35
|
+
case unsupported
|
|
36
|
+
case legacy
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
enum WifiRequestMode: Equatable {
|
|
40
|
+
case discovering
|
|
41
|
+
case modern
|
|
42
|
+
case legacy
|
|
43
|
+
case unsupported
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
final class WifiSessionCapabilities {
|
|
47
|
+
private(set) var sessionId = ""
|
|
48
|
+
private(set) var epoch: UInt64 = 0
|
|
49
|
+
private(set) var forgetResult: WifiProtocolCapability = .unknown
|
|
50
|
+
private(set) var savedNetworks: WifiProtocolCapability = .unknown
|
|
51
|
+
|
|
52
|
+
func reset(sessionId: String = "") {
|
|
53
|
+
epoch &+= 1
|
|
54
|
+
self.sessionId = sessionId
|
|
55
|
+
forgetResult = .unknown
|
|
56
|
+
savedNetworks = .unknown
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func applyVersionInfo1(_ values: [String: Any]) {
|
|
60
|
+
if let sid = values["sid"] as? String, !sid.isEmpty {
|
|
61
|
+
sessionId = sid
|
|
62
|
+
}
|
|
63
|
+
forgetResult = capability(values["wifiForgetResultVersion"])
|
|
64
|
+
savedNetworks = capability(values["savedWifiNetworksVersion"])
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func forgetMode() -> WifiRequestMode {
|
|
68
|
+
Self.requestMode(forgetResult)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func savedNetworksMode() -> WifiRequestMode {
|
|
72
|
+
Self.requestMode(savedNetworks)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private func capability(_ raw: Any?) -> WifiProtocolCapability {
|
|
76
|
+
guard let raw else { return .legacy }
|
|
77
|
+
return isWifiProtocolV1(raw) && !sessionId.isEmpty ? .supported(version: 1) : .unsupported
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private static func requestMode(_ capability: WifiProtocolCapability) -> WifiRequestMode {
|
|
81
|
+
switch capability {
|
|
82
|
+
case .unknown: .discovering
|
|
83
|
+
case .supported: .modern
|
|
84
|
+
case .legacy: .legacy
|
|
85
|
+
case .unsupported: .unsupported
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public enum WifiForgetOutcome: String {
|
|
91
|
+
case confirmed
|
|
92
|
+
case dispatched
|
|
93
|
+
case notFound = "not_found"
|
|
94
|
+
case unsupported
|
|
95
|
+
case failed
|
|
96
|
+
case legacyUnverified = "legacy_unverified"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func normalizeWifiForgetResultEvent(
|
|
100
|
+
requestId: String,
|
|
101
|
+
sid: String,
|
|
102
|
+
ssid: String,
|
|
103
|
+
protocolVersion: Int,
|
|
104
|
+
outcome: String,
|
|
105
|
+
legacyDispatched: Bool?,
|
|
106
|
+
connected: Bool?,
|
|
107
|
+
currentSsid: String,
|
|
108
|
+
localIp: String,
|
|
109
|
+
error: String?
|
|
110
|
+
) -> [String: Any]? {
|
|
111
|
+
guard wifiSsidIsValid(ssid) else { return nil }
|
|
112
|
+
if !requestId.isEmpty, !sid.isEmpty,
|
|
113
|
+
protocolVersion == 1,
|
|
114
|
+
let modernOutcome = WifiForgetOutcome(rawValue: outcome),
|
|
115
|
+
modernOutcome != .legacyUnverified
|
|
116
|
+
{
|
|
117
|
+
var body: [String: Any] = [
|
|
118
|
+
"mode": "modern",
|
|
119
|
+
"requestId": requestId,
|
|
120
|
+
"sid": sid,
|
|
121
|
+
"ssid": ssid,
|
|
122
|
+
"protocolVersion": protocolVersion,
|
|
123
|
+
"outcome": modernOutcome.rawValue,
|
|
124
|
+
]
|
|
125
|
+
if let connected { body["connected"] = connected }
|
|
126
|
+
if !currentSsid.isEmpty { body["currentSsid"] = currentSsid }
|
|
127
|
+
if !localIp.isEmpty { body["localIp"] = localIp }
|
|
128
|
+
if let error { body["error"] = error }
|
|
129
|
+
return body
|
|
130
|
+
}
|
|
131
|
+
if requestId.isEmpty, sid.isEmpty, protocolVersion == 0, outcome.isEmpty, let legacyDispatched {
|
|
132
|
+
var body: [String: Any] = [
|
|
133
|
+
"mode": "legacy",
|
|
134
|
+
"ssid": ssid,
|
|
135
|
+
"dispatched": legacyDispatched,
|
|
136
|
+
]
|
|
137
|
+
if let connected { body["connected"] = connected }
|
|
138
|
+
if !currentSsid.isEmpty { body["currentSsid"] = currentSsid }
|
|
139
|
+
if !localIp.isEmpty { body["localIp"] = localIp }
|
|
140
|
+
if let error { body["error"] = error }
|
|
141
|
+
return body
|
|
142
|
+
}
|
|
143
|
+
return nil
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public struct WifiForgetResult {
|
|
147
|
+
public let ssid: String
|
|
148
|
+
public let outcome: WifiForgetOutcome
|
|
149
|
+
public let connected: Bool?
|
|
150
|
+
public let currentSsid: String?
|
|
151
|
+
public let localIp: String?
|
|
152
|
+
public let error: String?
|
|
153
|
+
|
|
154
|
+
public var values: [String: Any] {
|
|
155
|
+
var result: [String: Any] = [
|
|
156
|
+
"ssid": ssid,
|
|
157
|
+
"outcome": outcome.rawValue,
|
|
158
|
+
]
|
|
159
|
+
if let connected { result["connected"] = connected }
|
|
160
|
+
if let currentSsid { result["currentSsid"] = currentSsid }
|
|
161
|
+
if let localIp { result["localIp"] = localIp }
|
|
162
|
+
if let error { result["error"] = error }
|
|
163
|
+
return result
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public enum SavedWifiNetworksOutcome: String {
|
|
168
|
+
case confirmed
|
|
169
|
+
case unsupported
|
|
170
|
+
case failed
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
public struct SavedWifiNetworksResult {
|
|
174
|
+
public let outcome: SavedWifiNetworksOutcome
|
|
175
|
+
public let networks: [String]
|
|
176
|
+
public let error: String?
|
|
177
|
+
|
|
178
|
+
public var values: [String: Any] {
|
|
179
|
+
var result: [String: Any] = [
|
|
180
|
+
"outcome": outcome.rawValue,
|
|
181
|
+
"networks": networks,
|
|
182
|
+
]
|
|
183
|
+
if let error { result["error"] = error }
|
|
184
|
+
return result
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
func parseWifiForgetResult(
|
|
189
|
+
expectedRequestId: String,
|
|
190
|
+
expectedSid: String,
|
|
191
|
+
expectedSsid: String,
|
|
192
|
+
capabilityVersion: Int,
|
|
193
|
+
data: [String: Any]
|
|
194
|
+
) -> WifiForgetResult? {
|
|
195
|
+
guard capabilityVersion == 1, !expectedRequestId.isEmpty, !expectedSid.isEmpty,
|
|
196
|
+
data["requestId"] as? String == expectedRequestId,
|
|
197
|
+
data["sid"] as? String == expectedSid,
|
|
198
|
+
data["ssid"] as? String == expectedSsid,
|
|
199
|
+
isWifiProtocolV1(data["protocolVersion"]),
|
|
200
|
+
let rawOutcome = data["outcome"] as? String,
|
|
201
|
+
let outcome = WifiForgetOutcome(rawValue: rawOutcome),
|
|
202
|
+
outcome != .legacyUnverified
|
|
203
|
+
else { return nil }
|
|
204
|
+
return WifiForgetResult(
|
|
205
|
+
ssid: expectedSsid,
|
|
206
|
+
outcome: outcome,
|
|
207
|
+
connected: data["connected"] as? Bool,
|
|
208
|
+
currentSsid: data["currentSsid"] as? String,
|
|
209
|
+
localIp: data["localIp"] as? String,
|
|
210
|
+
error: (data["error"] as? String).flatMap { $0.isEmpty ? nil : $0 }
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
func parseSavedWifiNetworks(
|
|
215
|
+
expectedRequestId: String,
|
|
216
|
+
expectedSid: String,
|
|
217
|
+
capabilityVersion: Int,
|
|
218
|
+
data: [String: Any]
|
|
219
|
+
) -> SavedWifiNetworksResult? {
|
|
220
|
+
guard capabilityVersion == 1, !expectedRequestId.isEmpty, !expectedSid.isEmpty,
|
|
221
|
+
data["requestId"] as? String == expectedRequestId,
|
|
222
|
+
data["sid"] as? String == expectedSid,
|
|
223
|
+
isWifiProtocolV1(data["protocolVersion"]),
|
|
224
|
+
let rawOutcome = data["outcome"] as? String,
|
|
225
|
+
let outcome = SavedWifiNetworksOutcome(rawValue: rawOutcome)
|
|
226
|
+
else { return nil }
|
|
227
|
+
guard let rawNetworks = data["networks"] as? [String], outcome == .confirmed || rawNetworks.isEmpty else { return nil }
|
|
228
|
+
var seen = Set<String>()
|
|
229
|
+
let networks = rawNetworks.filter { network in
|
|
230
|
+
!network.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
|
231
|
+
&& seen.insert(network).inserted
|
|
232
|
+
}
|
|
233
|
+
return SavedWifiNetworksResult(
|
|
234
|
+
outcome: outcome,
|
|
235
|
+
networks: networks,
|
|
236
|
+
error: (data["error"] as? String).flatMap { $0.isEmpty ? nil : $0 }
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
func legacyWifiForgetResult(
|
|
241
|
+
ssid: String
|
|
242
|
+
) -> WifiForgetResult {
|
|
243
|
+
return WifiForgetResult(
|
|
244
|
+
ssid: ssid,
|
|
245
|
+
outcome: .legacyUnverified,
|
|
246
|
+
connected: nil,
|
|
247
|
+
currentSsid: nil,
|
|
248
|
+
localIp: nil,
|
|
249
|
+
error: nil
|
|
250
|
+
)
|
|
251
|
+
}
|