@mentra/bluetooth-sdk 0.1.4 → 0.1.6
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 +61 -42
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +20 -12
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +38 -12
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +2 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +45 -51
- package/android/src/main/java/com/mentra/bluetoothsdk/audio/AudioModels.kt +125 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/camera/CameraModels.kt +187 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/connection/ConnectionModels.kt +78 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/events/BluetoothEvents.kt +60 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/internal/MapParsing.kt +132 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/requests/DisplayRequests.kt +39 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/DeviceStatus.kt +605 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/RuntimeState.kt +199 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/status/WifiHotspotStatus.kt +173 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/streaming/StreamModels.kt +348 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/types/DeviceModels.kt +75 -0
- package/build/BluetoothSdk.types.d.ts +24 -7
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_internal.d.ts +1 -1
- package/build/_internal.js +1 -1
- package/build/_internal.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +5 -4
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +4 -1
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/index.d.ts +4 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +81 -2
- package/build/index.js.map +1 -1
- package/build/react/index.d.ts +2 -2
- package/build/react/index.d.ts.map +1 -1
- package/build/react/index.js +1 -2
- package/build/react/index.js.map +1 -1
- package/build/react/useBluetoothStatus.js +1 -1
- package/build/react/useBluetoothStatus.js.map +1 -1
- package/build/react/useGlassesConnection.d.ts.map +1 -1
- package/build/react/useGlassesConnection.js +13 -0
- package/build/react/useGlassesConnection.js.map +1 -1
- package/build/react/useMentraBluetooth.d.ts +98 -0
- package/build/react/useMentraBluetooth.d.ts.map +1 -0
- package/build/react/useMentraBluetooth.js +156 -0
- package/build/react/useMentraBluetooth.js.map +1 -0
- package/ios/BluetoothSdkModule.swift +9 -9
- package/ios/Source/Audio/AudioModels.swift +159 -0
- package/ios/Source/Bridge.swift +42 -8
- package/ios/Source/Camera/CameraModels.swift +246 -0
- package/ios/Source/Connection/ScanSession.swift +27 -0
- package/ios/Source/DeviceManager.swift +0 -1
- package/ios/Source/DeviceStore.swift +2 -1
- package/ios/Source/Errors/BluetoothError.swift +19 -0
- package/ios/Source/Events/BluetoothEvents.swift +115 -0
- package/ios/Source/Internal/BluetoothAvailability.swift +58 -0
- package/ios/Source/Internal/ValueParsing.swift +90 -0
- package/ios/Source/MentraBluetoothSDK.swift +32 -2040
- package/ios/Source/Requests/DisplayRequests.swift +58 -0
- package/ios/Source/Status/DeviceStatus.swift +463 -0
- package/ios/Source/Status/RuntimeState.swift +350 -0
- package/ios/Source/Status/WifiHotspotStatus.swift +326 -0
- package/ios/Source/Streaming/StreamModels.swift +436 -0
- package/ios/Source/Types/DeviceModels.swift +134 -0
- package/ios/Source/sgcs/G2.swift +97 -36
- package/package.json +1 -1
- package/src/BluetoothSdk.types.ts +24 -7
- package/src/_internal.ts +1 -1
- package/src/_private/BluetoothSdkModule.ts +10 -5
- package/src/index.ts +93 -4
- package/src/react/index.ts +13 -11
- package/src/react/useBluetoothStatus.ts +1 -1
- package/src/react/useGlassesConnection.ts +15 -0
- package/src/react/useMentraBluetooth.ts +307 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +0 -1725
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public struct StreamVideoConfig {
|
|
4
|
+
public let width: Int?
|
|
5
|
+
public let height: Int?
|
|
6
|
+
public let bitrate: Int?
|
|
7
|
+
public let frameRate: Int?
|
|
8
|
+
|
|
9
|
+
public init(
|
|
10
|
+
width: Int? = nil,
|
|
11
|
+
height: Int? = nil,
|
|
12
|
+
bitrate: Int? = nil,
|
|
13
|
+
frameRate: Int? = nil
|
|
14
|
+
) {
|
|
15
|
+
self.width = width
|
|
16
|
+
self.height = height
|
|
17
|
+
self.bitrate = bitrate
|
|
18
|
+
self.frameRate = frameRate
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
var dictionary: [String: Any] {
|
|
22
|
+
var values: [String: Any] = [:]
|
|
23
|
+
if let width { values["width"] = width }
|
|
24
|
+
if let height { values["height"] = height }
|
|
25
|
+
if let bitrate { values["bitrate"] = bitrate }
|
|
26
|
+
if let frameRate { values["frameRate"] = frameRate }
|
|
27
|
+
return values
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
init?(values: [String: Any]?) {
|
|
31
|
+
guard let values else { return nil }
|
|
32
|
+
self.init(
|
|
33
|
+
width: intValue(values["width"]),
|
|
34
|
+
height: intValue(values["height"]),
|
|
35
|
+
bitrate: intValue(values["bitrate"]),
|
|
36
|
+
frameRate: intValue(values["frameRate"])
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public struct StreamAudioConfig {
|
|
42
|
+
public let bitrate: Int?
|
|
43
|
+
public let sampleRate: Int?
|
|
44
|
+
public let echoCancellation: Bool?
|
|
45
|
+
public let noiseSuppression: Bool?
|
|
46
|
+
|
|
47
|
+
public init(
|
|
48
|
+
bitrate: Int? = nil,
|
|
49
|
+
sampleRate: Int? = nil,
|
|
50
|
+
echoCancellation: Bool? = nil,
|
|
51
|
+
noiseSuppression: Bool? = nil
|
|
52
|
+
) {
|
|
53
|
+
self.bitrate = bitrate
|
|
54
|
+
self.sampleRate = sampleRate
|
|
55
|
+
self.echoCancellation = echoCancellation
|
|
56
|
+
self.noiseSuppression = noiseSuppression
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
var dictionary: [String: Any] {
|
|
60
|
+
var values: [String: Any] = [:]
|
|
61
|
+
if let bitrate { values["bitrate"] = bitrate }
|
|
62
|
+
if let sampleRate { values["sampleRate"] = sampleRate }
|
|
63
|
+
if let echoCancellation { values["echoCancellation"] = echoCancellation }
|
|
64
|
+
if let noiseSuppression { values["noiseSuppression"] = noiseSuppression }
|
|
65
|
+
return values
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
init?(values: [String: Any]?) {
|
|
69
|
+
guard let values else { return nil }
|
|
70
|
+
self.init(
|
|
71
|
+
bitrate: intValue(values["bitrate"]),
|
|
72
|
+
sampleRate: intValue(values["sampleRate"]),
|
|
73
|
+
echoCancellation: values["echoCancellation"] as? Bool,
|
|
74
|
+
noiseSuppression: values["noiseSuppression"] as? Bool
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public struct StreamRequest {
|
|
80
|
+
public let streamUrl: String
|
|
81
|
+
public let streamId: String
|
|
82
|
+
public let keepAlive: Bool
|
|
83
|
+
public let keepAliveIntervalSeconds: Int
|
|
84
|
+
public let sound: Bool
|
|
85
|
+
public let video: StreamVideoConfig?
|
|
86
|
+
public let audio: StreamAudioConfig?
|
|
87
|
+
public let extraValues: [String: Any]
|
|
88
|
+
|
|
89
|
+
public init(
|
|
90
|
+
streamUrl: String,
|
|
91
|
+
streamId: String = "",
|
|
92
|
+
keepAlive: Bool = true,
|
|
93
|
+
keepAliveIntervalSeconds: Int = 15,
|
|
94
|
+
sound: Bool = true,
|
|
95
|
+
video: StreamVideoConfig? = nil,
|
|
96
|
+
audio: StreamAudioConfig? = nil,
|
|
97
|
+
extraValues: [String: Any] = [:]
|
|
98
|
+
) {
|
|
99
|
+
self.streamUrl = streamUrl
|
|
100
|
+
self.streamId = streamId
|
|
101
|
+
self.keepAlive = keepAlive
|
|
102
|
+
self.keepAliveIntervalSeconds = keepAliveIntervalSeconds
|
|
103
|
+
self.sound = sound
|
|
104
|
+
self.video = video
|
|
105
|
+
self.audio = audio
|
|
106
|
+
self.extraValues = extraValues
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
init(values: [String: Any]) {
|
|
110
|
+
self.init(
|
|
111
|
+
streamUrl: values["streamUrl"] as? String
|
|
112
|
+
?? values["rtmpUrl"] as? String
|
|
113
|
+
?? values["srtUrl"] as? String
|
|
114
|
+
?? values["whipUrl"] as? String
|
|
115
|
+
?? "",
|
|
116
|
+
streamId: values["streamId"] as? String ?? "",
|
|
117
|
+
keepAlive: values["keepAlive"] as? Bool ?? true,
|
|
118
|
+
keepAliveIntervalSeconds: intValue(values["keepAliveIntervalSeconds"]) ?? 15,
|
|
119
|
+
sound: values["sound"] as? Bool ?? true,
|
|
120
|
+
video: StreamVideoConfig(values: values["video"] as? [String: Any]),
|
|
121
|
+
audio: StreamAudioConfig(values: values["audio"] as? [String: Any]),
|
|
122
|
+
extraValues: values
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public var values: [String: Any] {
|
|
127
|
+
var values = extraValues
|
|
128
|
+
values["type"] = "start_stream"
|
|
129
|
+
values["streamUrl"] = streamUrl
|
|
130
|
+
values["streamId"] = streamId
|
|
131
|
+
values["keepAlive"] = keepAlive
|
|
132
|
+
values["keepAliveIntervalSeconds"] = keepAliveIntervalSeconds
|
|
133
|
+
// The camera light is a privacy indicator and cannot be disabled by SDK callers.
|
|
134
|
+
values["flash"] = true
|
|
135
|
+
values["sound"] = sound
|
|
136
|
+
if let videoValues = video?.dictionary, !videoValues.isEmpty {
|
|
137
|
+
values["video"] = videoValues
|
|
138
|
+
}
|
|
139
|
+
if let audioValues = audio?.dictionary, !audioValues.isEmpty {
|
|
140
|
+
values["audio"] = audioValues
|
|
141
|
+
}
|
|
142
|
+
return values
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
public struct StreamKeepAliveRequest {
|
|
147
|
+
public let streamId: String
|
|
148
|
+
public let ackId: String
|
|
149
|
+
public let extraValues: [String: Any]
|
|
150
|
+
|
|
151
|
+
public init(streamId: String, ackId: String, extraValues: [String: Any] = [:]) {
|
|
152
|
+
self.streamId = streamId
|
|
153
|
+
self.ackId = ackId
|
|
154
|
+
self.extraValues = extraValues
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
init(values: [String: Any]) {
|
|
158
|
+
self.init(
|
|
159
|
+
streamId: values["streamId"] as? String ?? "",
|
|
160
|
+
ackId: values["ackId"] as? String ?? "",
|
|
161
|
+
extraValues: values
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public var values: [String: Any] {
|
|
166
|
+
var values = extraValues
|
|
167
|
+
values["type"] = "keep_stream_alive"
|
|
168
|
+
values["streamId"] = streamId
|
|
169
|
+
values["ackId"] = ackId
|
|
170
|
+
return values
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public enum StreamState: String, Equatable {
|
|
175
|
+
case initializing
|
|
176
|
+
case streaming
|
|
177
|
+
case stopping
|
|
178
|
+
case stopped
|
|
179
|
+
case reconnecting
|
|
180
|
+
case reconnected
|
|
181
|
+
case reconnectFailed = "reconnect_failed"
|
|
182
|
+
case error
|
|
183
|
+
|
|
184
|
+
fileprivate static func from(_ value: String?) -> StreamState? {
|
|
185
|
+
switch value?.lowercased() {
|
|
186
|
+
case "initializing", "starting", "connecting":
|
|
187
|
+
return .initializing
|
|
188
|
+
case "streaming", "streaming_started", "active":
|
|
189
|
+
return .streaming
|
|
190
|
+
case "stopping":
|
|
191
|
+
return .stopping
|
|
192
|
+
case "stopped", "not_streaming", "disconnected", "timeout":
|
|
193
|
+
return .stopped
|
|
194
|
+
case "reconnecting":
|
|
195
|
+
return .reconnecting
|
|
196
|
+
case "reconnected":
|
|
197
|
+
return .reconnected
|
|
198
|
+
case "reconnect_failed":
|
|
199
|
+
return .reconnectFailed
|
|
200
|
+
case "error", "error_not_streaming":
|
|
201
|
+
return .error
|
|
202
|
+
default:
|
|
203
|
+
return nil
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
public enum StreamStatusKind: String, Equatable {
|
|
209
|
+
case lifecycle
|
|
210
|
+
case reconnect
|
|
211
|
+
case error
|
|
212
|
+
case snapshot
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public enum StreamStatus: CustomStringConvertible, Equatable {
|
|
216
|
+
case lifecycle(state: StreamState, streamId: String?, timestamp: Int?)
|
|
217
|
+
case reconnecting(streamId: String?, attempt: Int, maxAttempts: Int, reason: String, timestamp: Int?)
|
|
218
|
+
case reconnected(streamId: String?, attempt: Int, timestamp: Int?)
|
|
219
|
+
case reconnectFailed(streamId: String?, maxAttempts: Int, timestamp: Int?)
|
|
220
|
+
case error(streamId: String?, errorDetails: String, timestamp: Int?)
|
|
221
|
+
case snapshot(state: StreamState, streaming: Bool, reconnecting: Bool, streamId: String?, attempt: Int?, timestamp: Int?)
|
|
222
|
+
|
|
223
|
+
public init(values: [String: Any]) {
|
|
224
|
+
let rawState = stringValue(values, "status")
|
|
225
|
+
let streamId = stringValue(values, "streamId")
|
|
226
|
+
let timestamp = intValue(values["timestamp"])
|
|
227
|
+
let attempt = optionalIntValue(values, "attempt")
|
|
228
|
+
let maxAttempts = optionalIntValue(values, "maxAttempts") ?? 0
|
|
229
|
+
|
|
230
|
+
if hasAnyKey(values, "streaming") || hasAnyKey(values, "reconnecting") {
|
|
231
|
+
let streaming = boolValue(values, "streaming") == true
|
|
232
|
+
let reconnecting = boolValue(values, "reconnecting") == true
|
|
233
|
+
let snapshotState: StreamState = reconnecting ? .reconnecting : (streaming ? .streaming : .stopped)
|
|
234
|
+
self = .snapshot(
|
|
235
|
+
state: snapshotState,
|
|
236
|
+
streaming: streaming,
|
|
237
|
+
reconnecting: reconnecting,
|
|
238
|
+
streamId: streamId,
|
|
239
|
+
attempt: attempt,
|
|
240
|
+
timestamp: timestamp
|
|
241
|
+
)
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
guard let state = StreamState.from(rawState) else {
|
|
246
|
+
self = .error(
|
|
247
|
+
streamId: streamId,
|
|
248
|
+
errorDetails: rawState.map { "Unknown stream status: \($0)" } ?? "Missing stream status",
|
|
249
|
+
timestamp: timestamp
|
|
250
|
+
)
|
|
251
|
+
return
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
switch state {
|
|
255
|
+
case .reconnecting:
|
|
256
|
+
self = .reconnecting(
|
|
257
|
+
streamId: streamId,
|
|
258
|
+
attempt: attempt ?? 0,
|
|
259
|
+
maxAttempts: maxAttempts,
|
|
260
|
+
reason: stringValue(values, "reason") ?? "",
|
|
261
|
+
timestamp: timestamp
|
|
262
|
+
)
|
|
263
|
+
case .reconnected:
|
|
264
|
+
self = .reconnected(streamId: streamId, attempt: attempt ?? 0, timestamp: timestamp)
|
|
265
|
+
case .reconnectFailed:
|
|
266
|
+
self = .reconnectFailed(streamId: streamId, maxAttempts: maxAttempts, timestamp: timestamp)
|
|
267
|
+
case .error:
|
|
268
|
+
self = .error(
|
|
269
|
+
streamId: streamId,
|
|
270
|
+
errorDetails: stringValue(values, "errorDetails")
|
|
271
|
+
?? (rawState == "error_not_streaming" ? "not_streaming" : "Unknown stream error"),
|
|
272
|
+
timestamp: timestamp
|
|
273
|
+
)
|
|
274
|
+
default:
|
|
275
|
+
self = .lifecycle(state: state, streamId: streamId, timestamp: timestamp)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
public var kind: StreamStatusKind {
|
|
280
|
+
switch self {
|
|
281
|
+
case .lifecycle:
|
|
282
|
+
.lifecycle
|
|
283
|
+
case .reconnecting, .reconnected, .reconnectFailed:
|
|
284
|
+
.reconnect
|
|
285
|
+
case .error:
|
|
286
|
+
.error
|
|
287
|
+
case .snapshot:
|
|
288
|
+
.snapshot
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
public var state: StreamState {
|
|
293
|
+
switch self {
|
|
294
|
+
case let .lifecycle(state, _, _):
|
|
295
|
+
state
|
|
296
|
+
case .reconnecting:
|
|
297
|
+
.reconnecting
|
|
298
|
+
case .reconnected:
|
|
299
|
+
.reconnected
|
|
300
|
+
case .reconnectFailed:
|
|
301
|
+
.reconnectFailed
|
|
302
|
+
case .error:
|
|
303
|
+
.error
|
|
304
|
+
case let .snapshot(state, _, _, _, _, _):
|
|
305
|
+
state
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
public var streamId: String? {
|
|
310
|
+
switch self {
|
|
311
|
+
case let .lifecycle(_, streamId, _),
|
|
312
|
+
let .reconnecting(streamId, _, _, _, _),
|
|
313
|
+
let .reconnected(streamId, _, _),
|
|
314
|
+
let .reconnectFailed(streamId, _, _),
|
|
315
|
+
let .error(streamId, _, _),
|
|
316
|
+
let .snapshot(_, _, _, streamId, _, _):
|
|
317
|
+
streamId
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
public var timestamp: Int? {
|
|
322
|
+
switch self {
|
|
323
|
+
case let .lifecycle(_, _, timestamp),
|
|
324
|
+
let .reconnecting(_, _, _, _, timestamp),
|
|
325
|
+
let .reconnected(_, _, timestamp),
|
|
326
|
+
let .reconnectFailed(_, _, timestamp),
|
|
327
|
+
let .error(_, _, timestamp),
|
|
328
|
+
let .snapshot(_, _, _, _, _, timestamp):
|
|
329
|
+
timestamp
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
public var values: [String: Any] {
|
|
334
|
+
var values: [String: Any] = [
|
|
335
|
+
"kind": kind.rawValue,
|
|
336
|
+
"status": state.rawValue,
|
|
337
|
+
]
|
|
338
|
+
if let streamId, !streamId.isEmpty {
|
|
339
|
+
values["streamId"] = streamId
|
|
340
|
+
}
|
|
341
|
+
if let timestamp {
|
|
342
|
+
values["timestamp"] = timestamp
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
switch self {
|
|
346
|
+
case .lifecycle:
|
|
347
|
+
break
|
|
348
|
+
case let .reconnecting(_, attempt, maxAttempts, reason, _):
|
|
349
|
+
values["attempt"] = attempt
|
|
350
|
+
values["maxAttempts"] = maxAttempts
|
|
351
|
+
values["reason"] = reason
|
|
352
|
+
case let .reconnected(_, attempt, _):
|
|
353
|
+
values["attempt"] = attempt
|
|
354
|
+
case let .reconnectFailed(_, maxAttempts, _):
|
|
355
|
+
values["maxAttempts"] = maxAttempts
|
|
356
|
+
case let .error(_, errorDetails, _):
|
|
357
|
+
values["errorDetails"] = errorDetails
|
|
358
|
+
case let .snapshot(_, streaming, reconnecting, _, attempt, _):
|
|
359
|
+
values["streaming"] = streaming
|
|
360
|
+
values["reconnecting"] = reconnecting
|
|
361
|
+
if let attempt {
|
|
362
|
+
values["attempt"] = attempt
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return values
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
public var description: String {
|
|
370
|
+
"StreamStatus(kind: \(kind.rawValue), status: \(state.rawValue), streamId: \(streamId ?? "none"))"
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
public struct StreamStatusEvent: CustomStringConvertible {
|
|
375
|
+
public let status: StreamStatus
|
|
376
|
+
|
|
377
|
+
public init(status: StreamStatus) {
|
|
378
|
+
self.status = status
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
public init(values: [String: Any]) {
|
|
382
|
+
self.status = StreamStatus(values: values)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
public var state: StreamState {
|
|
386
|
+
status.state
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
public var streamId: String? {
|
|
390
|
+
status.streamId
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
public var values: [String: Any] {
|
|
394
|
+
var values = status.values
|
|
395
|
+
values["type"] = "stream_status"
|
|
396
|
+
return values
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
public var description: String {
|
|
400
|
+
"StreamStatusEvent(kind: \(status.kind.rawValue), status: \(state.rawValue), streamId: \(streamId ?? "none"))"
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
public struct KeepAliveAckEvent: CustomStringConvertible, Equatable {
|
|
405
|
+
public let streamId: String
|
|
406
|
+
public let ackId: String
|
|
407
|
+
public let timestamp: Int?
|
|
408
|
+
|
|
409
|
+
public init(streamId: String, ackId: String, timestamp: Int? = nil) {
|
|
410
|
+
self.streamId = streamId
|
|
411
|
+
self.ackId = ackId
|
|
412
|
+
self.timestamp = timestamp
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
public init(values: [String: Any]) {
|
|
416
|
+
self.streamId = stringValue(values, "streamId") ?? ""
|
|
417
|
+
self.ackId = stringValue(values, "ackId") ?? ""
|
|
418
|
+
self.timestamp = intValue(values["timestamp"])
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
public var values: [String: Any] {
|
|
422
|
+
var values: [String: Any] = [
|
|
423
|
+
"type": "keep_alive_ack",
|
|
424
|
+
"streamId": streamId,
|
|
425
|
+
"ackId": ackId,
|
|
426
|
+
]
|
|
427
|
+
if let timestamp {
|
|
428
|
+
values["timestamp"] = timestamp
|
|
429
|
+
}
|
|
430
|
+
return values
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
public var description: String {
|
|
434
|
+
"KeepAliveAckEvent(streamId: \(streamId), ackId: \(ackId))"
|
|
435
|
+
}
|
|
436
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public struct MentraBluetoothSDKConfiguration {
|
|
4
|
+
public static let `default` = MentraBluetoothSDKConfiguration()
|
|
5
|
+
|
|
6
|
+
public init() {}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
public enum DeviceModel: String {
|
|
10
|
+
case g1
|
|
11
|
+
case g2
|
|
12
|
+
case mentraLive
|
|
13
|
+
case mentraNex
|
|
14
|
+
case mach1
|
|
15
|
+
case z100
|
|
16
|
+
case frame
|
|
17
|
+
case simulated
|
|
18
|
+
case r1
|
|
19
|
+
|
|
20
|
+
public var deviceType: String {
|
|
21
|
+
switch self {
|
|
22
|
+
case .g1:
|
|
23
|
+
DeviceTypes.G1
|
|
24
|
+
case .g2:
|
|
25
|
+
DeviceTypes.G2
|
|
26
|
+
case .mentraLive:
|
|
27
|
+
DeviceTypes.LIVE
|
|
28
|
+
case .mentraNex:
|
|
29
|
+
DeviceTypes.NEX
|
|
30
|
+
case .mach1:
|
|
31
|
+
DeviceTypes.MACH1
|
|
32
|
+
case .z100:
|
|
33
|
+
DeviceTypes.Z100
|
|
34
|
+
case .frame:
|
|
35
|
+
DeviceTypes.FRAME
|
|
36
|
+
case .simulated:
|
|
37
|
+
DeviceTypes.SIMULATED
|
|
38
|
+
case .r1:
|
|
39
|
+
ControllerTypes.R1
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static func fromDeviceType(_ deviceType: String?) -> DeviceModel {
|
|
44
|
+
switch deviceType {
|
|
45
|
+
case DeviceTypes.G1:
|
|
46
|
+
.g1
|
|
47
|
+
case DeviceTypes.G2:
|
|
48
|
+
.g2
|
|
49
|
+
case DeviceTypes.LIVE:
|
|
50
|
+
.mentraLive
|
|
51
|
+
case DeviceTypes.NEX:
|
|
52
|
+
.mentraNex
|
|
53
|
+
case DeviceTypes.MACH1:
|
|
54
|
+
.mach1
|
|
55
|
+
case DeviceTypes.Z100:
|
|
56
|
+
.z100
|
|
57
|
+
case DeviceTypes.FRAME:
|
|
58
|
+
.frame
|
|
59
|
+
case DeviceTypes.SIMULATED:
|
|
60
|
+
.simulated
|
|
61
|
+
case ControllerTypes.R1:
|
|
62
|
+
.r1
|
|
63
|
+
default:
|
|
64
|
+
.mentraLive
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public struct Device: Identifiable, Equatable, CustomStringConvertible {
|
|
70
|
+
public let model: DeviceModel
|
|
71
|
+
public let name: String
|
|
72
|
+
/// CoreBluetooth identifier when available.
|
|
73
|
+
public let identifier: String?
|
|
74
|
+
public let rssi: Int?
|
|
75
|
+
/// Stable app-facing scan-result key. Do not parse; use typed fields instead.
|
|
76
|
+
public let id: String
|
|
77
|
+
|
|
78
|
+
public init(
|
|
79
|
+
model: DeviceModel,
|
|
80
|
+
name: String,
|
|
81
|
+
identifier: String? = nil,
|
|
82
|
+
rssi: Int? = nil,
|
|
83
|
+
id: String? = nil
|
|
84
|
+
) {
|
|
85
|
+
self.model = model
|
|
86
|
+
self.name = name
|
|
87
|
+
self.identifier = identifier
|
|
88
|
+
self.rssi = rssi
|
|
89
|
+
self.id = id ?? identifier.flatMap { $0.isEmpty ? nil : $0 } ?? "\(model.deviceType):\(name)"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public var description: String {
|
|
93
|
+
"Device(model: \(model), name: \(name))"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
var dictionary: [String: Any] {
|
|
97
|
+
var values: [String: Any] = [
|
|
98
|
+
"id": id,
|
|
99
|
+
"model": model.deviceType,
|
|
100
|
+
"name": name,
|
|
101
|
+
]
|
|
102
|
+
if let identifier, !identifier.isEmpty {
|
|
103
|
+
values["address"] = identifier
|
|
104
|
+
}
|
|
105
|
+
if let rssi {
|
|
106
|
+
values["rssi"] = rssi
|
|
107
|
+
}
|
|
108
|
+
return values
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
init?(values: [String: Any]) {
|
|
112
|
+
guard let model = stringValue(values, "model") else { return nil }
|
|
113
|
+
guard let name = stringValue(values, "name") else { return nil }
|
|
114
|
+
let identifier = stringValue(values, "address").flatMap { $0.isEmpty ? nil : $0 }
|
|
115
|
+
let rssi = intValue(values["rssi"])
|
|
116
|
+
self.init(
|
|
117
|
+
model: DeviceModel.fromDeviceType(model),
|
|
118
|
+
name: name,
|
|
119
|
+
identifier: identifier,
|
|
120
|
+
rssi: rssi,
|
|
121
|
+
id: stringValue(values, "id")
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public struct ConnectOptions {
|
|
127
|
+
public let saveAsDefault: Bool
|
|
128
|
+
public let cancelExistingConnectionAttempt: Bool
|
|
129
|
+
|
|
130
|
+
public init(saveAsDefault: Bool = true, cancelExistingConnectionAttempt: Bool = true) {
|
|
131
|
+
self.saveAsDefault = saveAsDefault
|
|
132
|
+
self.cancelExistingConnectionAttempt = cancelExistingConnectionAttempt
|
|
133
|
+
}
|
|
134
|
+
}
|