@mentra/bluetooth-sdk 0.1.5 → 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 +29 -13
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +15 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/DeviceStore.kt +1 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +34 -46
- 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 +4 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +3 -0
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/ios/BluetoothSdkModule.swift +4 -4
- package/ios/Source/Audio/AudioModels.swift +159 -0
- package/ios/Source/Camera/CameraModels.swift +246 -0
- package/ios/Source/Connection/ScanSession.swift +27 -0
- package/ios/Source/DeviceStore.swift +1 -0
- 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 +25 -2140
- 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 +4 -0
- package/src/_private/BluetoothSdkModule.ts +3 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothModels.kt +0 -1810
|
@@ -1,2120 +1,6 @@
|
|
|
1
1
|
import CoreBluetooth
|
|
2
2
|
import Foundation
|
|
3
3
|
|
|
4
|
-
private func intValue(_ value: Any?) -> Int? {
|
|
5
|
-
if let int = value as? Int { return int }
|
|
6
|
-
if let double = value as? Double { return Int(double) }
|
|
7
|
-
if let number = value as? NSNumber { return number.intValue }
|
|
8
|
-
return nil
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
private func stringValue(_ values: [String: Any], _ keys: String...) -> String? {
|
|
12
|
-
stringValue(values, keys)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
private func stringValue(_ values: [String: Any], _ keys: [String]) -> String? {
|
|
16
|
-
for key in keys {
|
|
17
|
-
if let value = values[key] as? String {
|
|
18
|
-
return value
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return nil
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
private func boolValue(_ values: [String: Any], _ keys: String...) -> Bool? {
|
|
25
|
-
boolValue(values, keys)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
private func boolValue(_ values: [String: Any], _ keys: [String]) -> Bool? {
|
|
29
|
-
for key in keys {
|
|
30
|
-
if let value = values[key] as? Bool { return value }
|
|
31
|
-
if let value = values[key] as? NSNumber { return value.boolValue }
|
|
32
|
-
}
|
|
33
|
-
return nil
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
private func hasAnyKey(_ values: [String: Any], _ keys: String...) -> Bool {
|
|
37
|
-
hasAnyKey(values, keys)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
private func hasAnyKey(_ values: [String: Any], _ keys: [String]) -> Bool {
|
|
41
|
-
keys.contains { values.keys.contains($0) }
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
private func optionalStringValue(_ values: [String: Any], _ keys: String...) -> String? {
|
|
45
|
-
hasAnyKey(values, keys) ? (stringValue(values, keys) ?? "") : nil
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
private func nonEmptyStringValue(_ values: [String: Any], _ keys: String...) -> String? {
|
|
49
|
-
for key in keys {
|
|
50
|
-
guard let value = values[key] as? String else { continue }
|
|
51
|
-
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
52
|
-
if !trimmed.isEmpty {
|
|
53
|
-
return value
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return nil
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
private func optionalIntValue(_ values: [String: Any], _ keys: String...) -> Int? {
|
|
60
|
-
guard hasAnyKey(values, keys) else { return nil }
|
|
61
|
-
for key in keys {
|
|
62
|
-
if let value = intValue(values[key]) { return value }
|
|
63
|
-
}
|
|
64
|
-
return nil
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private func optionalBoolValue(_ values: [String: Any], _ keys: String...) -> Bool? {
|
|
68
|
-
hasAnyKey(values, keys) ? (boolValue(values, keys) ?? false) : nil
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
private func stringListValue(_ values: [String: Any], _ key: String) -> [String] {
|
|
72
|
-
values[key] as? [String] ?? []
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private func optionalStringListValue(_ values: [String: Any], _ key: String) -> [String]? {
|
|
76
|
-
values.keys.contains(key) ? stringListValue(values, key) : nil
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private func dictionaryListValue(_ values: [String: Any], _ key: String) -> [[String: Any]] {
|
|
80
|
-
values[key] as? [[String: Any]] ?? []
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private func optionalDictionaryListValue(_ values: [String: Any], _ key: String) -> [[String: Any]]? {
|
|
84
|
-
values.keys.contains(key) ? dictionaryListValue(values, key) : nil
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
private func putIfNotNil(_ map: inout [String: Any], _ key: String, _ value: Any?) {
|
|
88
|
-
if let value {
|
|
89
|
-
map[key] = value
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
public struct MentraBluetoothSDKConfiguration {
|
|
94
|
-
public static let `default` = MentraBluetoothSDKConfiguration()
|
|
95
|
-
|
|
96
|
-
public init() {}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
public enum DeviceModel: String {
|
|
100
|
-
case g1
|
|
101
|
-
case g2
|
|
102
|
-
case mentraLive
|
|
103
|
-
case mentraNex
|
|
104
|
-
case mach1
|
|
105
|
-
case z100
|
|
106
|
-
case frame
|
|
107
|
-
case simulated
|
|
108
|
-
case r1
|
|
109
|
-
|
|
110
|
-
public var deviceType: String {
|
|
111
|
-
switch self {
|
|
112
|
-
case .g1:
|
|
113
|
-
DeviceTypes.G1
|
|
114
|
-
case .g2:
|
|
115
|
-
DeviceTypes.G2
|
|
116
|
-
case .mentraLive:
|
|
117
|
-
DeviceTypes.LIVE
|
|
118
|
-
case .mentraNex:
|
|
119
|
-
DeviceTypes.NEX
|
|
120
|
-
case .mach1:
|
|
121
|
-
DeviceTypes.MACH1
|
|
122
|
-
case .z100:
|
|
123
|
-
DeviceTypes.Z100
|
|
124
|
-
case .frame:
|
|
125
|
-
DeviceTypes.FRAME
|
|
126
|
-
case .simulated:
|
|
127
|
-
DeviceTypes.SIMULATED
|
|
128
|
-
case .r1:
|
|
129
|
-
ControllerTypes.R1
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
public static func fromDeviceType(_ deviceType: String?) -> DeviceModel {
|
|
134
|
-
switch deviceType {
|
|
135
|
-
case DeviceTypes.G1:
|
|
136
|
-
.g1
|
|
137
|
-
case DeviceTypes.G2:
|
|
138
|
-
.g2
|
|
139
|
-
case DeviceTypes.LIVE:
|
|
140
|
-
.mentraLive
|
|
141
|
-
case DeviceTypes.NEX:
|
|
142
|
-
.mentraNex
|
|
143
|
-
case DeviceTypes.MACH1:
|
|
144
|
-
.mach1
|
|
145
|
-
case DeviceTypes.Z100:
|
|
146
|
-
.z100
|
|
147
|
-
case DeviceTypes.FRAME:
|
|
148
|
-
.frame
|
|
149
|
-
case DeviceTypes.SIMULATED:
|
|
150
|
-
.simulated
|
|
151
|
-
case ControllerTypes.R1:
|
|
152
|
-
.r1
|
|
153
|
-
default:
|
|
154
|
-
.mentraLive
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
public struct Device: Identifiable, Equatable, CustomStringConvertible {
|
|
160
|
-
public let model: DeviceModel
|
|
161
|
-
public let name: String
|
|
162
|
-
/// CoreBluetooth identifier when available.
|
|
163
|
-
public let identifier: String?
|
|
164
|
-
public let rssi: Int?
|
|
165
|
-
/// Stable app-facing scan-result key. Do not parse; use typed fields instead.
|
|
166
|
-
public let id: String
|
|
167
|
-
|
|
168
|
-
public init(
|
|
169
|
-
model: DeviceModel,
|
|
170
|
-
name: String,
|
|
171
|
-
identifier: String? = nil,
|
|
172
|
-
rssi: Int? = nil,
|
|
173
|
-
id: String? = nil
|
|
174
|
-
) {
|
|
175
|
-
self.model = model
|
|
176
|
-
self.name = name
|
|
177
|
-
self.identifier = identifier
|
|
178
|
-
self.rssi = rssi
|
|
179
|
-
self.id = id ?? identifier.flatMap { $0.isEmpty ? nil : $0 } ?? "\(model.deviceType):\(name)"
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
public var description: String {
|
|
183
|
-
"Device(model: \(model), name: \(name))"
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
var dictionary: [String: Any] {
|
|
187
|
-
var values: [String: Any] = [
|
|
188
|
-
"id": id,
|
|
189
|
-
"model": model.deviceType,
|
|
190
|
-
"name": name,
|
|
191
|
-
]
|
|
192
|
-
if let identifier, !identifier.isEmpty {
|
|
193
|
-
values["address"] = identifier
|
|
194
|
-
}
|
|
195
|
-
if let rssi {
|
|
196
|
-
values["rssi"] = rssi
|
|
197
|
-
}
|
|
198
|
-
return values
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
init?(values: [String: Any]) {
|
|
202
|
-
guard let model = stringValue(values, "model") else { return nil }
|
|
203
|
-
guard let name = stringValue(values, "name") else { return nil }
|
|
204
|
-
let identifier = stringValue(values, "address").flatMap { $0.isEmpty ? nil : $0 }
|
|
205
|
-
let rssi = intValue(values["rssi"])
|
|
206
|
-
self.init(
|
|
207
|
-
model: DeviceModel.fromDeviceType(model),
|
|
208
|
-
name: name,
|
|
209
|
-
identifier: identifier,
|
|
210
|
-
rssi: rssi,
|
|
211
|
-
id: stringValue(values, "id")
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
public struct ConnectOptions {
|
|
217
|
-
public let saveAsDefault: Bool
|
|
218
|
-
public let cancelExistingConnectionAttempt: Bool
|
|
219
|
-
|
|
220
|
-
public init(saveAsDefault: Bool = true, cancelExistingConnectionAttempt: Bool = true) {
|
|
221
|
-
self.saveAsDefault = saveAsDefault
|
|
222
|
-
self.cancelExistingConnectionAttempt = cancelExistingConnectionAttempt
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
public struct WifiScanResult: CustomStringConvertible {
|
|
227
|
-
public let ssid: String
|
|
228
|
-
public let requiresPassword: Bool
|
|
229
|
-
public let signalStrength: Int
|
|
230
|
-
public let frequency: Int?
|
|
231
|
-
|
|
232
|
-
public init(ssid: String, requiresPassword: Bool, signalStrength: Int, frequency: Int? = nil) {
|
|
233
|
-
self.ssid = ssid
|
|
234
|
-
self.requiresPassword = requiresPassword
|
|
235
|
-
self.signalStrength = signalStrength
|
|
236
|
-
self.frequency = frequency
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
init(values: [String: Any]) {
|
|
240
|
-
ssid = stringValue(values, "ssid") ?? ""
|
|
241
|
-
requiresPassword = boolValue(values, "requiresPassword") ?? false
|
|
242
|
-
signalStrength = intValue(values["signalStrength"]) ?? -1
|
|
243
|
-
frequency = intValue(values["frequency"])
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
var dictionary: [String: Any] {
|
|
247
|
-
var values: [String: Any] = [
|
|
248
|
-
"ssid": ssid,
|
|
249
|
-
"requiresPassword": requiresPassword,
|
|
250
|
-
"signalStrength": signalStrength,
|
|
251
|
-
]
|
|
252
|
-
if let frequency {
|
|
253
|
-
values["frequency"] = frequency
|
|
254
|
-
}
|
|
255
|
-
return values
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
public var description: String {
|
|
259
|
-
"WifiScanResult(ssid: \(ssid), signalStrength: \(signalStrength))"
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
public enum GlassesConnectionState: String, CustomStringConvertible, Equatable {
|
|
264
|
-
case disconnected = "DISCONNECTED"
|
|
265
|
-
case scanning = "SCANNING"
|
|
266
|
-
case connecting = "CONNECTING"
|
|
267
|
-
case bonding = "BONDING"
|
|
268
|
-
case connected = "CONNECTED"
|
|
269
|
-
|
|
270
|
-
public init(_ value: String?) {
|
|
271
|
-
self = Self.fromValue(value) ?? .disconnected
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
public static func fromValue(_ value: String?) -> GlassesConnectionState? {
|
|
275
|
-
guard let normalized = value?.trimmingCharacters(in: .whitespacesAndNewlines).uppercased(),
|
|
276
|
-
!normalized.isEmpty
|
|
277
|
-
else {
|
|
278
|
-
return nil
|
|
279
|
-
}
|
|
280
|
-
return Self(rawValue: normalized)
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
public var isConnected: Bool {
|
|
284
|
-
self == .connected
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
public var isBusy: Bool {
|
|
288
|
-
self == .scanning || self == .connecting || self == .bonding
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
func statusValues(connected: Bool, fullyBooted: Bool) -> [String: Any] {
|
|
292
|
-
if self == .connected || connected || fullyBooted {
|
|
293
|
-
return ["state": "connected", "fullyBooted": fullyBooted]
|
|
294
|
-
}
|
|
295
|
-
switch self {
|
|
296
|
-
case .scanning:
|
|
297
|
-
return ["state": "scanning"]
|
|
298
|
-
case .connecting:
|
|
299
|
-
return ["state": "connecting"]
|
|
300
|
-
case .bonding:
|
|
301
|
-
return ["state": "bonding"]
|
|
302
|
-
case .connected:
|
|
303
|
-
return ["state": "connected", "fullyBooted": fullyBooted]
|
|
304
|
-
case .disconnected:
|
|
305
|
-
return ["state": "disconnected"]
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
public var description: String {
|
|
310
|
-
rawValue
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
public struct GlassesStatus: CustomStringConvertible {
|
|
315
|
-
let values: [String: Any]
|
|
316
|
-
|
|
317
|
-
init(values: [String: Any]) {
|
|
318
|
-
self.values = values
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
public func applying(_ update: GlassesStatusUpdate) -> GlassesStatus {
|
|
322
|
-
GlassesStatus(values: values.merging(update.values) { _, new in new })
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
public func withBattery(level: Int, charging: Bool) -> GlassesStatus {
|
|
326
|
-
applying(GlassesStatusUpdate(values: ["batteryLevel": level, "charging": charging]))
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
public func withWifi(_ wifi: WifiStatus) -> GlassesStatus {
|
|
330
|
-
applying(GlassesStatusUpdate(values: wifi.storeValues))
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
public func withHotspot(_ hotspot: HotspotStatus) -> GlassesStatus {
|
|
334
|
-
applying(GlassesStatusUpdate(values: hotspot.storeValues))
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
public func disconnected() -> GlassesStatus {
|
|
338
|
-
applying(GlassesStatusUpdate(values: [
|
|
339
|
-
"connected": false,
|
|
340
|
-
"connectionState": "DISCONNECTED",
|
|
341
|
-
"fullyBooted": false,
|
|
342
|
-
"batteryLevel": -1,
|
|
343
|
-
"charging": false,
|
|
344
|
-
"hotspotEnabled": false,
|
|
345
|
-
"hotspotGatewayIp": "",
|
|
346
|
-
"hotspotPassword": "",
|
|
347
|
-
"hotspotSsid": "",
|
|
348
|
-
"wifiConnected": false,
|
|
349
|
-
"wifiSsid": "",
|
|
350
|
-
"wifiLocalIp": "",
|
|
351
|
-
"signalStrength": -1,
|
|
352
|
-
"signalStrengthUpdatedAt": 0,
|
|
353
|
-
]))
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
public var fullyBooted: Bool { boolValue(values, "fullyBooted") ?? false }
|
|
357
|
-
public var connected: Bool { boolValue(values, "connected") ?? false }
|
|
358
|
-
public var micEnabled: Bool { boolValue(values, "micEnabled") ?? false }
|
|
359
|
-
public var connectionState: GlassesConnectionState { GlassesConnectionState(stringValue(values, "connectionState")) }
|
|
360
|
-
public var bluetoothClassicConnected: Bool { boolValue(values, "bluetoothClassicConnected") ?? false }
|
|
361
|
-
public var signalStrength: Int { intValue(values["signalStrength"]) ?? -1 }
|
|
362
|
-
public var signalStrengthUpdatedAt: Int { intValue(values["signalStrengthUpdatedAt"]) ?? 0 }
|
|
363
|
-
public var deviceModel: String { stringValue(values, "deviceModel") ?? "" }
|
|
364
|
-
public var androidVersion: String { stringValue(values, "androidVersion") ?? "" }
|
|
365
|
-
public var firmwareVersion: String { stringValue(values, "firmwareVersion") ?? "" }
|
|
366
|
-
public var besFirmwareVersion: String { stringValue(values, "besFirmwareVersion") ?? "" }
|
|
367
|
-
public var mtkFirmwareVersion: String { stringValue(values, "mtkFirmwareVersion") ?? "" }
|
|
368
|
-
public var bluetoothMacAddress: String { stringValue(values, "bluetoothMacAddress") ?? "" }
|
|
369
|
-
public var leftMacAddress: String { stringValue(values, "leftMacAddress") ?? "" }
|
|
370
|
-
public var rightMacAddress: String { stringValue(values, "rightMacAddress") ?? "" }
|
|
371
|
-
public var macAddress: String { stringValue(values, "macAddress") ?? "" }
|
|
372
|
-
public var buildNumber: String { stringValue(values, "buildNumber") ?? "" }
|
|
373
|
-
public var otaVersionUrl: String { stringValue(values, "otaVersionUrl") ?? "" }
|
|
374
|
-
public var appVersion: String { stringValue(values, "appVersion") ?? "" }
|
|
375
|
-
public var bluetoothName: String { stringValue(values, "bluetoothName") ?? "" }
|
|
376
|
-
public var serialNumber: String { stringValue(values, "serialNumber") ?? "" }
|
|
377
|
-
public var style: String { stringValue(values, "style") ?? "" }
|
|
378
|
-
public var color: String { stringValue(values, "color") ?? "" }
|
|
379
|
-
public var wifi: WifiStatus { WifiStatus.fromStoreValues(values) ?? .disconnected }
|
|
380
|
-
public var hotspot: HotspotStatus { HotspotStatus.fromStoreValues(values) ?? .disabled }
|
|
381
|
-
public var dictionary: [String: Any] { Self.dictionary(from: values) }
|
|
382
|
-
public var batteryLevel: Int { intValue(values["batteryLevel"]) ?? -1 }
|
|
383
|
-
public var charging: Bool { boolValue(values, "charging") ?? false }
|
|
384
|
-
public var caseBatteryLevel: Int { intValue(values["caseBatteryLevel"]) ?? -1 }
|
|
385
|
-
public var caseCharging: Bool { boolValue(values, "caseCharging") ?? false }
|
|
386
|
-
public var caseOpen: Bool { boolValue(values, "caseOpen") ?? true }
|
|
387
|
-
public var caseRemoved: Bool { boolValue(values, "caseRemoved") ?? true }
|
|
388
|
-
public var headUp: Bool { boolValue(values, "headUp") ?? false }
|
|
389
|
-
public var controllerConnected: Bool { boolValue(values, "controllerConnected") ?? false }
|
|
390
|
-
public var controllerFullyBooted: Bool { boolValue(values, "controllerFullyBooted") ?? false }
|
|
391
|
-
public var controllerMacAddress: String { stringValue(values, "controllerMacAddress") ?? "" }
|
|
392
|
-
public var controllerBatteryLevel: Int { intValue(values["controllerBatteryLevel"]) ?? -1 }
|
|
393
|
-
public var controllerSignalStrength: Int { intValue(values["controllerSignalStrength"]) ?? -1 }
|
|
394
|
-
public var ringSignalStrength: Int { intValue(values["ringSignalStrength"]) ?? -1 }
|
|
395
|
-
|
|
396
|
-
public var description: String {
|
|
397
|
-
values.description
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
static func dictionary(from values: [String: Any]) -> [String: Any] {
|
|
401
|
-
var dictionary = values
|
|
402
|
-
dictionary["connection"] = GlassesConnectionState(stringValue(values, "connectionState")).statusValues(
|
|
403
|
-
connected: boolValue(values, "connected") ?? false,
|
|
404
|
-
fullyBooted: boolValue(values, "fullyBooted") ?? false
|
|
405
|
-
)
|
|
406
|
-
dictionary.removeValue(forKey: "connected")
|
|
407
|
-
dictionary.removeValue(forKey: "fullyBooted")
|
|
408
|
-
dictionary.removeValue(forKey: "connectionState")
|
|
409
|
-
dictionary["wifi"] = (WifiStatus.fromStoreValues(values) ?? .disconnected).values
|
|
410
|
-
dictionary["hotspot"] = (HotspotStatus.fromStoreValues(values) ?? .disabled).values
|
|
411
|
-
dictionary.removeValue(forKey: "wifiConnected")
|
|
412
|
-
dictionary.removeValue(forKey: "wifiSsid")
|
|
413
|
-
dictionary.removeValue(forKey: "wifiLocalIp")
|
|
414
|
-
dictionary.removeValue(forKey: "hotspotEnabled")
|
|
415
|
-
dictionary.removeValue(forKey: "hotspotSsid")
|
|
416
|
-
dictionary.removeValue(forKey: "hotspotPassword")
|
|
417
|
-
dictionary.removeValue(forKey: "hotspotGatewayIp")
|
|
418
|
-
return dictionary
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
static func updateDictionary(from values: [String: Any]) -> [String: Any] {
|
|
422
|
-
var dictionary = values
|
|
423
|
-
if hasAnyKey(values, "connection", "connected", "fullyBooted", "connectionState") {
|
|
424
|
-
dictionary["connection"] = (values["connection"] as? [String: Any])
|
|
425
|
-
?? GlassesConnectionState(stringValue(values, "connectionState")).statusValues(
|
|
426
|
-
connected: boolValue(values, "connected") ?? false,
|
|
427
|
-
fullyBooted: boolValue(values, "fullyBooted") ?? false
|
|
428
|
-
)
|
|
429
|
-
dictionary.removeValue(forKey: "connected")
|
|
430
|
-
dictionary.removeValue(forKey: "fullyBooted")
|
|
431
|
-
dictionary.removeValue(forKey: "connectionState")
|
|
432
|
-
}
|
|
433
|
-
if hasAnyKey(values, "wifi", "wifiConnected", "wifiSsid", "wifiLocalIp") {
|
|
434
|
-
let wifi = (values["wifi"] as? [String: Any]).flatMap(WifiStatus.init(values:))
|
|
435
|
-
?? WifiStatus.fromStoreValues(values)
|
|
436
|
-
if let wifi {
|
|
437
|
-
dictionary["wifi"] = wifi.values
|
|
438
|
-
}
|
|
439
|
-
dictionary.removeValue(forKey: "wifiConnected")
|
|
440
|
-
dictionary.removeValue(forKey: "wifiSsid")
|
|
441
|
-
dictionary.removeValue(forKey: "wifiLocalIp")
|
|
442
|
-
}
|
|
443
|
-
if hasAnyKey(values, "hotspot") {
|
|
444
|
-
if let hotspot = (values["hotspot"] as? [String: Any]).flatMap(HotspotStatus.init(values:)) {
|
|
445
|
-
dictionary["hotspot"] = hotspot.values
|
|
446
|
-
}
|
|
447
|
-
} else if hasAnyKey(values, "hotspotEnabled", "hotspotSsid", "hotspotPassword", "hotspotGatewayIp") {
|
|
448
|
-
if let hotspot = HotspotStatus.fromStoreValues(values) {
|
|
449
|
-
dictionary["hotspot"] = hotspot.values
|
|
450
|
-
}
|
|
451
|
-
dictionary.removeValue(forKey: "hotspotEnabled")
|
|
452
|
-
dictionary.removeValue(forKey: "hotspotSsid")
|
|
453
|
-
dictionary.removeValue(forKey: "hotspotPassword")
|
|
454
|
-
dictionary.removeValue(forKey: "hotspotGatewayIp")
|
|
455
|
-
}
|
|
456
|
-
return dictionary
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
public struct BluetoothStatus: CustomStringConvertible {
|
|
461
|
-
let values: [String: Any]
|
|
462
|
-
|
|
463
|
-
init(values: [String: Any]) {
|
|
464
|
-
self.values = Self.normalized(values)
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
private static func normalized(_ values: [String: Any]) -> [String: Any] {
|
|
468
|
-
var normalizedValues = values
|
|
469
|
-
if let searchResults = values["searchResults"] as? [[String: Any]] {
|
|
470
|
-
normalizedValues["searchResults"] = searchResults.compactMap { Device(values: $0)?.dictionary }
|
|
471
|
-
}
|
|
472
|
-
return normalizedValues
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
public func applying(_ update: BluetoothStatusUpdate) -> BluetoothStatus {
|
|
476
|
-
BluetoothStatus(values: values.merging(update.values) { _, new in new })
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
public func withDefaultDevice(_ device: Device?) -> BluetoothStatus {
|
|
480
|
-
applying(BluetoothStatusUpdate(values: [
|
|
481
|
-
"default_wearable": device?.model.deviceType ?? "",
|
|
482
|
-
"device_name": device?.name ?? "",
|
|
483
|
-
"device_address": device?.identifier ?? "",
|
|
484
|
-
]))
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
public var searching: Bool { boolValue(values, "searching") ?? false }
|
|
488
|
-
public var searchingController: Bool { boolValue(values, "searchingController") ?? false }
|
|
489
|
-
public var systemMicUnavailable: Bool { boolValue(values, "systemMicUnavailable") ?? false }
|
|
490
|
-
public var micEnabled: Bool { boolValue(values, "micEnabled") ?? false }
|
|
491
|
-
public var currentMic: String { stringValue(values, "currentMic") ?? "" }
|
|
492
|
-
public var micRanking: [String] { stringListValue(values, "micRanking") }
|
|
493
|
-
/// Nearby glasses in stable discovery order. Existing entries keep their array position as
|
|
494
|
-
/// details refresh; new glasses append at the end, and removals should not reorder remaining entries.
|
|
495
|
-
public var searchResults: [Device] {
|
|
496
|
-
dictionaryListValue(values, "searchResults").compactMap(Device.init(values:))
|
|
497
|
-
}
|
|
498
|
-
public var wifiScanResults: [WifiScanResult] {
|
|
499
|
-
dictionaryListValue(values, "wifiScanResults").map(WifiScanResult.init(values:))
|
|
500
|
-
}
|
|
501
|
-
public var lastLog: [String] { stringListValue(values, "lastLog") }
|
|
502
|
-
public var otherBtConnected: Bool { boolValue(values, "otherBtConnected") ?? false }
|
|
503
|
-
public var defaultWearable: String { stringValue(values, "default_wearable") ?? "" }
|
|
504
|
-
public var pendingWearable: String { stringValue(values, "pending_wearable") ?? "" }
|
|
505
|
-
public var deviceName: String { stringValue(values, "device_name") ?? "" }
|
|
506
|
-
public var deviceAddress: String { stringValue(values, "device_address") ?? "" }
|
|
507
|
-
public var defaultController: String { stringValue(values, "default_controller") ?? "" }
|
|
508
|
-
public var pendingController: String { stringValue(values, "pending_controller") ?? "" }
|
|
509
|
-
public var controllerDeviceName: String { stringValue(values, "controller_device_name") ?? "" }
|
|
510
|
-
public var screenDisabled: Bool { boolValue(values, "screen_disabled") ?? false }
|
|
511
|
-
public var preferredMic: String { stringValue(values, "preferred_mic") ?? "auto" }
|
|
512
|
-
public var sensingEnabled: Bool { boolValue(values, "sensing_enabled") ?? true }
|
|
513
|
-
public var powerSavingMode: Bool { boolValue(values, "power_saving_mode") ?? false }
|
|
514
|
-
public var brightness: Int { intValue(values["brightness"]) ?? 50 }
|
|
515
|
-
public var autoBrightness: Bool { boolValue(values, "auto_brightness") ?? true }
|
|
516
|
-
public var dashboardHeight: Int { intValue(values["dashboard_height"]) ?? 4 }
|
|
517
|
-
public var dashboardDepth: Int { intValue(values["dashboard_depth"]) ?? 2 }
|
|
518
|
-
public var headUpAngle: Int { intValue(values["head_up_angle"]) ?? 30 }
|
|
519
|
-
public var contextualDashboard: Bool { boolValue(values, "contextual_dashboard") ?? true }
|
|
520
|
-
public var galleryModeAuto: Bool { boolValue(values, "galleryModeAuto") ?? true }
|
|
521
|
-
public var buttonPhotoSize: ButtonPhotoSize {
|
|
522
|
-
ButtonPhotoSize(rawValue: stringValue(values, "button_photo_size") ?? "") ?? .medium
|
|
523
|
-
}
|
|
524
|
-
public var buttonCameraLed: Bool { boolValue(values, "button_camera_led") ?? true }
|
|
525
|
-
public var buttonMaxRecordingTime: Int { intValue(values["button_max_recording_time"]) ?? 10 }
|
|
526
|
-
public var buttonVideoWidth: Int { intValue(values["button_video_width"]) ?? 1280 }
|
|
527
|
-
public var buttonVideoHeight: Int { intValue(values["button_video_height"]) ?? 720 }
|
|
528
|
-
public var buttonVideoFrameRate: Int { intValue(values["button_video_fps"]) ?? 30 }
|
|
529
|
-
public var shouldSendPcm: Bool { boolValue(values, "should_send_pcm") ?? false }
|
|
530
|
-
public var shouldSendLc3: Bool { boolValue(values, "should_send_lc3") ?? false }
|
|
531
|
-
public var shouldSendTranscript: Bool { boolValue(values, "should_send_transcript") ?? false }
|
|
532
|
-
public var bypassVad: Bool { boolValue(values, "bypass_vad") ?? true }
|
|
533
|
-
public var offlineCaptionsRunning: Bool { boolValue(values, "offline_captions_running") ?? false }
|
|
534
|
-
public var localSttFallbackActive: Bool { boolValue(values, "local_stt_fallback_active") ?? false }
|
|
535
|
-
public var shouldSendBootingMessage: Bool { boolValue(values, "shouldSendBootingMessage") ?? true }
|
|
536
|
-
|
|
537
|
-
public var defaultDevice: Device? {
|
|
538
|
-
guard !defaultWearable.isEmpty else { return nil }
|
|
539
|
-
return Device(
|
|
540
|
-
model: DeviceModel.fromDeviceType(defaultWearable),
|
|
541
|
-
name: deviceName,
|
|
542
|
-
identifier: deviceAddress.isEmpty ? nil : deviceAddress
|
|
543
|
-
)
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
public var description: String {
|
|
547
|
-
values.description
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
public struct GlassesStatusUpdate: CustomStringConvertible {
|
|
552
|
-
let values: [String: Any]
|
|
553
|
-
|
|
554
|
-
init(values: [String: Any]) {
|
|
555
|
-
self.values = values
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
public var fullyBooted: Bool? { optionalBoolValue(values, "fullyBooted") }
|
|
559
|
-
public var connected: Bool? { optionalBoolValue(values, "connected") }
|
|
560
|
-
public var micEnabled: Bool? { optionalBoolValue(values, "micEnabled") }
|
|
561
|
-
public var connectionState: GlassesConnectionState? {
|
|
562
|
-
GlassesConnectionState.fromValue(optionalStringValue(values, "connectionState"))
|
|
563
|
-
}
|
|
564
|
-
public var bluetoothClassicConnected: Bool? { optionalBoolValue(values, "bluetoothClassicConnected") }
|
|
565
|
-
public var signalStrength: Int? { optionalIntValue(values, "signalStrength") }
|
|
566
|
-
public var signalStrengthUpdatedAt: Int? { optionalIntValue(values, "signalStrengthUpdatedAt") }
|
|
567
|
-
public var deviceModel: String? { optionalStringValue(values, "deviceModel") }
|
|
568
|
-
public var androidVersion: String? { optionalStringValue(values, "androidVersion") }
|
|
569
|
-
public var firmwareVersion: String? { optionalStringValue(values, "firmwareVersion") }
|
|
570
|
-
public var besFirmwareVersion: String? { optionalStringValue(values, "besFirmwareVersion") }
|
|
571
|
-
public var mtkFirmwareVersion: String? { optionalStringValue(values, "mtkFirmwareVersion") }
|
|
572
|
-
public var bluetoothMacAddress: String? { optionalStringValue(values, "bluetoothMacAddress") }
|
|
573
|
-
public var leftMacAddress: String? { optionalStringValue(values, "leftMacAddress") }
|
|
574
|
-
public var rightMacAddress: String? { optionalStringValue(values, "rightMacAddress") }
|
|
575
|
-
public var macAddress: String? { optionalStringValue(values, "macAddress") }
|
|
576
|
-
public var buildNumber: String? { optionalStringValue(values, "buildNumber") }
|
|
577
|
-
public var otaVersionUrl: String? { optionalStringValue(values, "otaVersionUrl") }
|
|
578
|
-
public var appVersion: String? { optionalStringValue(values, "appVersion") }
|
|
579
|
-
public var bluetoothName: String? { optionalStringValue(values, "bluetoothName") }
|
|
580
|
-
public var serialNumber: String? { optionalStringValue(values, "serialNumber") }
|
|
581
|
-
public var style: String? { optionalStringValue(values, "style") }
|
|
582
|
-
public var color: String? { optionalStringValue(values, "color") }
|
|
583
|
-
public var wifi: WifiStatus? {
|
|
584
|
-
if let wifi = values["wifi"] as? [String: Any] {
|
|
585
|
-
return WifiStatus(values: wifi)
|
|
586
|
-
}
|
|
587
|
-
if hasAnyKey(values, "wifiConnected", "wifiSsid", "wifiLocalIp") {
|
|
588
|
-
return WifiStatus.fromStoreValues(values)
|
|
589
|
-
}
|
|
590
|
-
return nil
|
|
591
|
-
}
|
|
592
|
-
public var hotspot: HotspotStatus? {
|
|
593
|
-
if let hotspot = values["hotspot"] as? [String: Any] {
|
|
594
|
-
return HotspotStatus(values: hotspot)
|
|
595
|
-
}
|
|
596
|
-
if hasAnyKey(values, "hotspotEnabled", "hotspotSsid", "hotspotPassword", "hotspotGatewayIp") {
|
|
597
|
-
return HotspotStatus.fromStoreValues(values)
|
|
598
|
-
}
|
|
599
|
-
return nil
|
|
600
|
-
}
|
|
601
|
-
public var dictionary: [String: Any] { GlassesStatus.updateDictionary(from: values) }
|
|
602
|
-
public var batteryLevel: Int? { optionalIntValue(values, "batteryLevel") }
|
|
603
|
-
public var charging: Bool? { optionalBoolValue(values, "charging") }
|
|
604
|
-
public var caseBatteryLevel: Int? { optionalIntValue(values, "caseBatteryLevel") }
|
|
605
|
-
public var caseCharging: Bool? { optionalBoolValue(values, "caseCharging") }
|
|
606
|
-
public var caseOpen: Bool? { optionalBoolValue(values, "caseOpen") }
|
|
607
|
-
public var caseRemoved: Bool? { optionalBoolValue(values, "caseRemoved") }
|
|
608
|
-
public var headUp: Bool? { optionalBoolValue(values, "headUp") }
|
|
609
|
-
public var controllerConnected: Bool? { optionalBoolValue(values, "controllerConnected") }
|
|
610
|
-
public var controllerFullyBooted: Bool? { optionalBoolValue(values, "controllerFullyBooted") }
|
|
611
|
-
public var controllerMacAddress: String? { optionalStringValue(values, "controllerMacAddress") }
|
|
612
|
-
public var controllerBatteryLevel: Int? { optionalIntValue(values, "controllerBatteryLevel") }
|
|
613
|
-
public var controllerSignalStrength: Int? { optionalIntValue(values, "controllerSignalStrength") }
|
|
614
|
-
public var ringSignalStrength: Int? { optionalIntValue(values, "ringSignalStrength") }
|
|
615
|
-
|
|
616
|
-
public var description: String {
|
|
617
|
-
values.description
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
public struct BluetoothStatusUpdate: CustomStringConvertible {
|
|
622
|
-
let values: [String: Any]
|
|
623
|
-
|
|
624
|
-
init(values: [String: Any]) {
|
|
625
|
-
var normalizedValues = values
|
|
626
|
-
if let searchResults = values["searchResults"] as? [[String: Any]] {
|
|
627
|
-
normalizedValues["searchResults"] = searchResults.compactMap { Device(values: $0)?.dictionary }
|
|
628
|
-
}
|
|
629
|
-
self.values = normalizedValues
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
public var searching: Bool? { optionalBoolValue(values, "searching") }
|
|
633
|
-
public var searchingController: Bool? { optionalBoolValue(values, "searchingController") }
|
|
634
|
-
public var systemMicUnavailable: Bool? { optionalBoolValue(values, "systemMicUnavailable") }
|
|
635
|
-
public var micEnabled: Bool? { optionalBoolValue(values, "micEnabled") }
|
|
636
|
-
public var currentMic: String? { optionalStringValue(values, "currentMic") }
|
|
637
|
-
public var micRanking: [String]? { optionalStringListValue(values, "micRanking") }
|
|
638
|
-
/// Nearby glasses in stable discovery order when included in an update. Existing entries keep their
|
|
639
|
-
/// array position as details refresh; new glasses append at the end, and removals should not reorder
|
|
640
|
-
/// remaining entries.
|
|
641
|
-
public var searchResults: [Device]? {
|
|
642
|
-
optionalDictionaryListValue(values, "searchResults")?.compactMap(Device.init(values:))
|
|
643
|
-
}
|
|
644
|
-
public var wifiScanResults: [WifiScanResult]? {
|
|
645
|
-
optionalDictionaryListValue(values, "wifiScanResults")?.map(WifiScanResult.init(values:))
|
|
646
|
-
}
|
|
647
|
-
public var lastLog: [String]? { optionalStringListValue(values, "lastLog") }
|
|
648
|
-
public var otherBtConnected: Bool? { optionalBoolValue(values, "otherBtConnected") }
|
|
649
|
-
public var defaultWearable: String? { optionalStringValue(values, "default_wearable") }
|
|
650
|
-
public var pendingWearable: String? { optionalStringValue(values, "pending_wearable") }
|
|
651
|
-
public var deviceName: String? { optionalStringValue(values, "device_name") }
|
|
652
|
-
public var deviceAddress: String? { optionalStringValue(values, "device_address") }
|
|
653
|
-
public var defaultController: String? { optionalStringValue(values, "default_controller") }
|
|
654
|
-
public var pendingController: String? { optionalStringValue(values, "pending_controller") }
|
|
655
|
-
public var controllerDeviceName: String? { optionalStringValue(values, "controller_device_name") }
|
|
656
|
-
public var screenDisabled: Bool? { optionalBoolValue(values, "screen_disabled") }
|
|
657
|
-
public var preferredMic: String? { optionalStringValue(values, "preferred_mic") }
|
|
658
|
-
public var sensingEnabled: Bool? { optionalBoolValue(values, "sensing_enabled") }
|
|
659
|
-
public var powerSavingMode: Bool? { optionalBoolValue(values, "power_saving_mode") }
|
|
660
|
-
public var brightness: Int? { optionalIntValue(values, "brightness") }
|
|
661
|
-
public var autoBrightness: Bool? { optionalBoolValue(values, "auto_brightness") }
|
|
662
|
-
public var dashboardHeight: Int? { optionalIntValue(values, "dashboard_height") }
|
|
663
|
-
public var dashboardDepth: Int? { optionalIntValue(values, "dashboard_depth") }
|
|
664
|
-
public var headUpAngle: Int? { optionalIntValue(values, "head_up_angle") }
|
|
665
|
-
public var contextualDashboard: Bool? { optionalBoolValue(values, "contextual_dashboard") }
|
|
666
|
-
public var galleryModeAuto: Bool? { optionalBoolValue(values, "galleryModeAuto") }
|
|
667
|
-
public var buttonPhotoSize: ButtonPhotoSize? {
|
|
668
|
-
optionalStringValue(values, "button_photo_size").flatMap(ButtonPhotoSize.init(rawValue:))
|
|
669
|
-
}
|
|
670
|
-
public var buttonCameraLed: Bool? { optionalBoolValue(values, "button_camera_led") }
|
|
671
|
-
public var buttonMaxRecordingTime: Int? { optionalIntValue(values, "button_max_recording_time") }
|
|
672
|
-
public var buttonVideoWidth: Int? { optionalIntValue(values, "button_video_width") }
|
|
673
|
-
public var buttonVideoHeight: Int? { optionalIntValue(values, "button_video_height") }
|
|
674
|
-
public var buttonVideoFrameRate: Int? { optionalIntValue(values, "button_video_fps") }
|
|
675
|
-
public var shouldSendPcm: Bool? { optionalBoolValue(values, "should_send_pcm") }
|
|
676
|
-
public var shouldSendLc3: Bool? { optionalBoolValue(values, "should_send_lc3") }
|
|
677
|
-
public var shouldSendTranscript: Bool? { optionalBoolValue(values, "should_send_transcript") }
|
|
678
|
-
public var bypassVad: Bool? { optionalBoolValue(values, "bypass_vad") }
|
|
679
|
-
public var offlineCaptionsRunning: Bool? { optionalBoolValue(values, "offline_captions_running") }
|
|
680
|
-
public var localSttFallbackActive: Bool? { optionalBoolValue(values, "local_stt_fallback_active") }
|
|
681
|
-
public var shouldSendBootingMessage: Bool? { optionalBoolValue(values, "shouldSendBootingMessage") }
|
|
682
|
-
|
|
683
|
-
public var description: String {
|
|
684
|
-
values.description
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
public struct DisplayTextRequest {
|
|
689
|
-
public let text: String
|
|
690
|
-
public let x: Int
|
|
691
|
-
public let y: Int
|
|
692
|
-
public let size: Int
|
|
693
|
-
|
|
694
|
-
public init(text: String, x: Int = 0, y: Int = 0, size: Int = 24) {
|
|
695
|
-
self.text = text
|
|
696
|
-
self.x = x
|
|
697
|
-
self.y = y
|
|
698
|
-
self.size = size
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
var dictionary: [String: Any] {
|
|
702
|
-
[
|
|
703
|
-
"text": text,
|
|
704
|
-
"x": x,
|
|
705
|
-
"y": y,
|
|
706
|
-
"size": size,
|
|
707
|
-
]
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
struct DisplayEventRequest {
|
|
712
|
-
let values: [String: Any]
|
|
713
|
-
|
|
714
|
-
init(values: [String: Any]) {
|
|
715
|
-
self.values = values
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
public struct DashboardPositionRequest {
|
|
720
|
-
public let height: Int
|
|
721
|
-
public let depth: Int
|
|
722
|
-
|
|
723
|
-
public init(height: Int, depth: Int) {
|
|
724
|
-
self.height = height
|
|
725
|
-
self.depth = depth
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
struct DashboardMenuItem {
|
|
730
|
-
let title: String
|
|
731
|
-
let packageName: String
|
|
732
|
-
let values: [String: Any]
|
|
733
|
-
|
|
734
|
-
init(title: String, packageName: String, values: [String: Any] = [:]) {
|
|
735
|
-
self.title = title
|
|
736
|
-
self.packageName = packageName
|
|
737
|
-
self.values = values
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
var dictionary: [String: Any] {
|
|
741
|
-
values.merging(["title": title, "packageName": packageName]) { _, new in new }
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
public enum GalleryMode {
|
|
746
|
-
case auto
|
|
747
|
-
case manual
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
public enum PhotoSize: String {
|
|
751
|
-
case small
|
|
752
|
-
case medium
|
|
753
|
-
case large
|
|
754
|
-
case full
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
public enum ButtonPhotoSize: String {
|
|
758
|
-
case small
|
|
759
|
-
case medium
|
|
760
|
-
case large
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
public enum PhotoCompression: String {
|
|
764
|
-
case none
|
|
765
|
-
case medium
|
|
766
|
-
case heavy
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
public struct ButtonPhotoSettings {
|
|
770
|
-
public let size: ButtonPhotoSize
|
|
771
|
-
|
|
772
|
-
public init(size: ButtonPhotoSize) {
|
|
773
|
-
self.size = size
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
public struct ButtonVideoRecordingSettings {
|
|
778
|
-
public let width: Int
|
|
779
|
-
public let height: Int
|
|
780
|
-
public let frameRate: Int
|
|
781
|
-
|
|
782
|
-
public init(width: Int, height: Int, frameRate: Int) {
|
|
783
|
-
self.width = width
|
|
784
|
-
self.height = height
|
|
785
|
-
self.frameRate = frameRate
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
public enum CameraFov {
|
|
790
|
-
case standard
|
|
791
|
-
case wide
|
|
792
|
-
|
|
793
|
-
var value: [String: Int] {
|
|
794
|
-
switch self {
|
|
795
|
-
case .standard:
|
|
796
|
-
["fov": 118, "roiPosition": 0]
|
|
797
|
-
case .wide:
|
|
798
|
-
["fov": 118, "roiPosition": 0]
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
public enum MicPreference: String {
|
|
804
|
-
case auto
|
|
805
|
-
case phone
|
|
806
|
-
case glasses
|
|
807
|
-
case bluetooth
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
public struct PhotoRequest {
|
|
811
|
-
public let requestId: String
|
|
812
|
-
public let appId: String
|
|
813
|
-
public let size: PhotoSize
|
|
814
|
-
public let webhookUrl: String?
|
|
815
|
-
public let authToken: String?
|
|
816
|
-
public let compress: PhotoCompression?
|
|
817
|
-
public let sound: Bool
|
|
818
|
-
|
|
819
|
-
public init(
|
|
820
|
-
requestId: String,
|
|
821
|
-
appId: String,
|
|
822
|
-
size: PhotoSize,
|
|
823
|
-
webhookUrl: String? = nil,
|
|
824
|
-
authToken: String? = nil,
|
|
825
|
-
compress: PhotoCompression? = nil,
|
|
826
|
-
sound: Bool
|
|
827
|
-
) {
|
|
828
|
-
self.requestId = requestId
|
|
829
|
-
self.appId = appId
|
|
830
|
-
self.size = size
|
|
831
|
-
self.webhookUrl = webhookUrl
|
|
832
|
-
self.authToken = authToken
|
|
833
|
-
self.compress = compress
|
|
834
|
-
self.sound = sound
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
public struct StreamVideoConfig {
|
|
839
|
-
public let width: Int?
|
|
840
|
-
public let height: Int?
|
|
841
|
-
public let bitrate: Int?
|
|
842
|
-
public let frameRate: Int?
|
|
843
|
-
|
|
844
|
-
public init(
|
|
845
|
-
width: Int? = nil,
|
|
846
|
-
height: Int? = nil,
|
|
847
|
-
bitrate: Int? = nil,
|
|
848
|
-
frameRate: Int? = nil
|
|
849
|
-
) {
|
|
850
|
-
self.width = width
|
|
851
|
-
self.height = height
|
|
852
|
-
self.bitrate = bitrate
|
|
853
|
-
self.frameRate = frameRate
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
var dictionary: [String: Any] {
|
|
857
|
-
var values: [String: Any] = [:]
|
|
858
|
-
if let width { values["width"] = width }
|
|
859
|
-
if let height { values["height"] = height }
|
|
860
|
-
if let bitrate { values["bitrate"] = bitrate }
|
|
861
|
-
if let frameRate { values["frameRate"] = frameRate }
|
|
862
|
-
return values
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
init?(values: [String: Any]?) {
|
|
866
|
-
guard let values else { return nil }
|
|
867
|
-
self.init(
|
|
868
|
-
width: intValue(values["width"]),
|
|
869
|
-
height: intValue(values["height"]),
|
|
870
|
-
bitrate: intValue(values["bitrate"]),
|
|
871
|
-
frameRate: intValue(values["frameRate"])
|
|
872
|
-
)
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
public struct StreamAudioConfig {
|
|
877
|
-
public let bitrate: Int?
|
|
878
|
-
public let sampleRate: Int?
|
|
879
|
-
public let echoCancellation: Bool?
|
|
880
|
-
public let noiseSuppression: Bool?
|
|
881
|
-
|
|
882
|
-
public init(
|
|
883
|
-
bitrate: Int? = nil,
|
|
884
|
-
sampleRate: Int? = nil,
|
|
885
|
-
echoCancellation: Bool? = nil,
|
|
886
|
-
noiseSuppression: Bool? = nil
|
|
887
|
-
) {
|
|
888
|
-
self.bitrate = bitrate
|
|
889
|
-
self.sampleRate = sampleRate
|
|
890
|
-
self.echoCancellation = echoCancellation
|
|
891
|
-
self.noiseSuppression = noiseSuppression
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
var dictionary: [String: Any] {
|
|
895
|
-
var values: [String: Any] = [:]
|
|
896
|
-
if let bitrate { values["bitrate"] = bitrate }
|
|
897
|
-
if let sampleRate { values["sampleRate"] = sampleRate }
|
|
898
|
-
if let echoCancellation { values["echoCancellation"] = echoCancellation }
|
|
899
|
-
if let noiseSuppression { values["noiseSuppression"] = noiseSuppression }
|
|
900
|
-
return values
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
init?(values: [String: Any]?) {
|
|
904
|
-
guard let values else { return nil }
|
|
905
|
-
self.init(
|
|
906
|
-
bitrate: intValue(values["bitrate"]),
|
|
907
|
-
sampleRate: intValue(values["sampleRate"]),
|
|
908
|
-
echoCancellation: values["echoCancellation"] as? Bool,
|
|
909
|
-
noiseSuppression: values["noiseSuppression"] as? Bool
|
|
910
|
-
)
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
public struct StreamRequest {
|
|
915
|
-
public let streamUrl: String
|
|
916
|
-
public let streamId: String
|
|
917
|
-
public let keepAlive: Bool
|
|
918
|
-
public let keepAliveIntervalSeconds: Int
|
|
919
|
-
public let sound: Bool
|
|
920
|
-
public let video: StreamVideoConfig?
|
|
921
|
-
public let audio: StreamAudioConfig?
|
|
922
|
-
public let extraValues: [String: Any]
|
|
923
|
-
|
|
924
|
-
public init(
|
|
925
|
-
streamUrl: String,
|
|
926
|
-
streamId: String = "",
|
|
927
|
-
keepAlive: Bool = true,
|
|
928
|
-
keepAliveIntervalSeconds: Int = 15,
|
|
929
|
-
sound: Bool = true,
|
|
930
|
-
video: StreamVideoConfig? = nil,
|
|
931
|
-
audio: StreamAudioConfig? = nil,
|
|
932
|
-
extraValues: [String: Any] = [:]
|
|
933
|
-
) {
|
|
934
|
-
self.streamUrl = streamUrl
|
|
935
|
-
self.streamId = streamId
|
|
936
|
-
self.keepAlive = keepAlive
|
|
937
|
-
self.keepAliveIntervalSeconds = keepAliveIntervalSeconds
|
|
938
|
-
self.sound = sound
|
|
939
|
-
self.video = video
|
|
940
|
-
self.audio = audio
|
|
941
|
-
self.extraValues = extraValues
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
init(values: [String: Any]) {
|
|
945
|
-
self.init(
|
|
946
|
-
streamUrl: values["streamUrl"] as? String
|
|
947
|
-
?? values["rtmpUrl"] as? String
|
|
948
|
-
?? values["srtUrl"] as? String
|
|
949
|
-
?? values["whipUrl"] as? String
|
|
950
|
-
?? "",
|
|
951
|
-
streamId: values["streamId"] as? String ?? "",
|
|
952
|
-
keepAlive: values["keepAlive"] as? Bool ?? true,
|
|
953
|
-
keepAliveIntervalSeconds: intValue(values["keepAliveIntervalSeconds"]) ?? 15,
|
|
954
|
-
sound: values["sound"] as? Bool ?? true,
|
|
955
|
-
video: StreamVideoConfig(values: values["video"] as? [String: Any]),
|
|
956
|
-
audio: StreamAudioConfig(values: values["audio"] as? [String: Any]),
|
|
957
|
-
extraValues: values
|
|
958
|
-
)
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
public var values: [String: Any] {
|
|
962
|
-
var values = extraValues
|
|
963
|
-
values["type"] = "start_stream"
|
|
964
|
-
values["streamUrl"] = streamUrl
|
|
965
|
-
values["streamId"] = streamId
|
|
966
|
-
values["keepAlive"] = keepAlive
|
|
967
|
-
values["keepAliveIntervalSeconds"] = keepAliveIntervalSeconds
|
|
968
|
-
// The camera light is a privacy indicator and cannot be disabled by SDK callers.
|
|
969
|
-
values["flash"] = true
|
|
970
|
-
values["sound"] = sound
|
|
971
|
-
if let videoValues = video?.dictionary, !videoValues.isEmpty {
|
|
972
|
-
values["video"] = videoValues
|
|
973
|
-
}
|
|
974
|
-
if let audioValues = audio?.dictionary, !audioValues.isEmpty {
|
|
975
|
-
values["audio"] = audioValues
|
|
976
|
-
}
|
|
977
|
-
return values
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
public struct StreamKeepAliveRequest {
|
|
982
|
-
public let streamId: String
|
|
983
|
-
public let ackId: String
|
|
984
|
-
public let extraValues: [String: Any]
|
|
985
|
-
|
|
986
|
-
public init(streamId: String, ackId: String, extraValues: [String: Any] = [:]) {
|
|
987
|
-
self.streamId = streamId
|
|
988
|
-
self.ackId = ackId
|
|
989
|
-
self.extraValues = extraValues
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
init(values: [String: Any]) {
|
|
993
|
-
self.init(
|
|
994
|
-
streamId: values["streamId"] as? String ?? "",
|
|
995
|
-
ackId: values["ackId"] as? String ?? "",
|
|
996
|
-
extraValues: values
|
|
997
|
-
)
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
public var values: [String: Any] {
|
|
1001
|
-
var values = extraValues
|
|
1002
|
-
values["type"] = "keep_stream_alive"
|
|
1003
|
-
values["streamId"] = streamId
|
|
1004
|
-
values["ackId"] = ackId
|
|
1005
|
-
return values
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
public enum RgbLedAction: String {
|
|
1010
|
-
case on
|
|
1011
|
-
case off
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
public enum RgbLedColor: String {
|
|
1015
|
-
case red
|
|
1016
|
-
case green
|
|
1017
|
-
case blue
|
|
1018
|
-
case orange
|
|
1019
|
-
case white
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
public struct RgbLedRequest {
|
|
1023
|
-
public let requestId: String
|
|
1024
|
-
public let packageName: String?
|
|
1025
|
-
public let action: RgbLedAction
|
|
1026
|
-
public let color: RgbLedColor?
|
|
1027
|
-
public let onDurationMs: Int
|
|
1028
|
-
public let offDurationMs: Int
|
|
1029
|
-
public let count: Int
|
|
1030
|
-
|
|
1031
|
-
public init(
|
|
1032
|
-
requestId: String,
|
|
1033
|
-
packageName: String?,
|
|
1034
|
-
action: RgbLedAction,
|
|
1035
|
-
color: RgbLedColor?,
|
|
1036
|
-
onDurationMs: Int,
|
|
1037
|
-
offDurationMs: Int,
|
|
1038
|
-
count: Int
|
|
1039
|
-
) {
|
|
1040
|
-
self.requestId = requestId
|
|
1041
|
-
self.packageName = packageName
|
|
1042
|
-
self.action = action
|
|
1043
|
-
self.color = color
|
|
1044
|
-
self.onDurationMs = onDurationMs
|
|
1045
|
-
self.offDurationMs = offDurationMs
|
|
1046
|
-
self.count = count
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
public struct VideoRecordingRequest {
|
|
1051
|
-
public let requestId: String
|
|
1052
|
-
public let save: Bool
|
|
1053
|
-
public let sound: Bool
|
|
1054
|
-
|
|
1055
|
-
public init(requestId: String, save: Bool, sound: Bool) {
|
|
1056
|
-
self.requestId = requestId
|
|
1057
|
-
self.save = save
|
|
1058
|
-
self.sound = sound
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
public struct ButtonPressEvent: CustomStringConvertible {
|
|
1063
|
-
public let buttonId: String
|
|
1064
|
-
public let pressType: String
|
|
1065
|
-
public let timestamp: Int?
|
|
1066
|
-
|
|
1067
|
-
public init(buttonId: String, pressType: String, timestamp: Int? = nil) {
|
|
1068
|
-
self.buttonId = buttonId
|
|
1069
|
-
self.pressType = pressType
|
|
1070
|
-
self.timestamp = timestamp
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
public var description: String {
|
|
1074
|
-
"ButtonPressEvent(buttonId: \(buttonId), pressType: \(pressType))"
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
public struct TouchEvent: CustomStringConvertible {
|
|
1079
|
-
public let values: [String: Any]
|
|
1080
|
-
|
|
1081
|
-
public init(values: [String: Any]) {
|
|
1082
|
-
self.values = values
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
public var deviceModel: String? {
|
|
1086
|
-
stringValue(values, "deviceModel")
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
public var gestureName: String? {
|
|
1090
|
-
stringValue(values, "gestureName")
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
public var timestamp: Int? {
|
|
1094
|
-
intValue(values["timestamp"])
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
public var isSwipe: Bool {
|
|
1098
|
-
gestureName?.localizedCaseInsensitiveContains("swipe") == true
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
public var description: String {
|
|
1102
|
-
"TouchEvent(gestureName: \(gestureName ?? "unknown"))"
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
public enum WifiStatus: CustomStringConvertible, Equatable {
|
|
1107
|
-
public enum State: String {
|
|
1108
|
-
case disconnected
|
|
1109
|
-
case connected
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
case disconnected
|
|
1113
|
-
case connected(ssid: String, localIp: String?)
|
|
1114
|
-
|
|
1115
|
-
private init?(connected: Bool, ssid: String?, localIp: String?) {
|
|
1116
|
-
if connected {
|
|
1117
|
-
guard
|
|
1118
|
-
let ssid = ssid?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
1119
|
-
!ssid.isEmpty
|
|
1120
|
-
else {
|
|
1121
|
-
return nil
|
|
1122
|
-
}
|
|
1123
|
-
let trimmedLocalIp = localIp?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
1124
|
-
self = .connected(
|
|
1125
|
-
ssid: ssid,
|
|
1126
|
-
localIp: trimmedLocalIp?.isEmpty == false ? trimmedLocalIp : nil
|
|
1127
|
-
)
|
|
1128
|
-
} else {
|
|
1129
|
-
self = .disconnected
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
init?(values: [String: Any]) {
|
|
1134
|
-
if let nested = values["wifi"] as? [String: Any] {
|
|
1135
|
-
guard let wifi = WifiStatus(values: nested) else {
|
|
1136
|
-
return nil
|
|
1137
|
-
}
|
|
1138
|
-
self = wifi
|
|
1139
|
-
return
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
if let state = stringValue(values, "state")?.lowercased() {
|
|
1143
|
-
switch state {
|
|
1144
|
-
case State.connected.rawValue:
|
|
1145
|
-
guard let wifi = WifiStatus(
|
|
1146
|
-
connected: true,
|
|
1147
|
-
ssid: nonEmptyStringValue(values, "ssid"),
|
|
1148
|
-
localIp: nonEmptyStringValue(values, "localIp")
|
|
1149
|
-
) else {
|
|
1150
|
-
return nil
|
|
1151
|
-
}
|
|
1152
|
-
self = wifi
|
|
1153
|
-
case State.disconnected.rawValue:
|
|
1154
|
-
self = .disconnected
|
|
1155
|
-
default:
|
|
1156
|
-
return nil
|
|
1157
|
-
}
|
|
1158
|
-
return
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
return nil
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
static func fromStoreValues(_ values: [String: Any]) -> WifiStatus? {
|
|
1165
|
-
guard let connected = boolValue(values, "wifiConnected") else { return nil }
|
|
1166
|
-
return fromStoreFields(
|
|
1167
|
-
connected: connected,
|
|
1168
|
-
ssid: nonEmptyStringValue(values, "wifiSsid"),
|
|
1169
|
-
localIp: nonEmptyStringValue(values, "wifiLocalIp")
|
|
1170
|
-
)
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
static func fromStoreFields(connected: Bool, ssid: String?, localIp: String?) -> WifiStatus? {
|
|
1174
|
-
WifiStatus(connected: connected, ssid: ssid, localIp: localIp)
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
public var state: State {
|
|
1178
|
-
switch self {
|
|
1179
|
-
case .disconnected:
|
|
1180
|
-
.disconnected
|
|
1181
|
-
case .connected:
|
|
1182
|
-
.connected
|
|
1183
|
-
}
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
public var isConnected: Bool {
|
|
1187
|
-
if case .connected = self {
|
|
1188
|
-
return true
|
|
1189
|
-
}
|
|
1190
|
-
return false
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
public var values: [String: Any] {
|
|
1194
|
-
switch self {
|
|
1195
|
-
case .disconnected:
|
|
1196
|
-
return ["state": State.disconnected.rawValue]
|
|
1197
|
-
case let .connected(ssid, localIp):
|
|
1198
|
-
var values: [String: Any] = [
|
|
1199
|
-
"state": State.connected.rawValue,
|
|
1200
|
-
"ssid": ssid,
|
|
1201
|
-
]
|
|
1202
|
-
if let localIp = localIp {
|
|
1203
|
-
values["localIp"] = localIp
|
|
1204
|
-
}
|
|
1205
|
-
return values
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
var storeValues: [String: Any] {
|
|
1210
|
-
switch self {
|
|
1211
|
-
case .disconnected:
|
|
1212
|
-
[
|
|
1213
|
-
"wifiConnected": false,
|
|
1214
|
-
"wifiSsid": "",
|
|
1215
|
-
"wifiLocalIp": "",
|
|
1216
|
-
]
|
|
1217
|
-
case let .connected(ssid, localIp):
|
|
1218
|
-
[
|
|
1219
|
-
"wifiConnected": true,
|
|
1220
|
-
"wifiSsid": ssid,
|
|
1221
|
-
"wifiLocalIp": localIp ?? "",
|
|
1222
|
-
]
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
public var description: String {
|
|
1227
|
-
switch self {
|
|
1228
|
-
case .disconnected:
|
|
1229
|
-
"WifiStatus(disconnected)"
|
|
1230
|
-
case let .connected(ssid, localIp):
|
|
1231
|
-
"WifiStatus(connected: \(ssid), localIp: \(localIp ?? "unknown"))"
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
public struct WifiStatusEvent: CustomStringConvertible {
|
|
1237
|
-
public let status: WifiStatus
|
|
1238
|
-
|
|
1239
|
-
public init(status: WifiStatus) {
|
|
1240
|
-
self.status = status
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
init(connected: Bool, ssid: String?, localIp: String?) {
|
|
1244
|
-
self.status = WifiStatus.fromStoreFields(connected: connected, ssid: ssid, localIp: localIp) ?? .disconnected
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
init(values: [String: Any]) {
|
|
1248
|
-
self.status = WifiStatus(values: values) ?? .disconnected
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
public var values: [String: Any] {
|
|
1252
|
-
status.values.merging(["type": "wifi_status_change"]) { _, new in new }
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
public var description: String {
|
|
1256
|
-
"WifiStatusEvent(\(status))"
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
public enum HotspotStatus: CustomStringConvertible, Equatable {
|
|
1261
|
-
public enum State: String {
|
|
1262
|
-
case disabled
|
|
1263
|
-
case enabled
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
case disabled
|
|
1267
|
-
case enabled(ssid: String, password: String, localIp: String)
|
|
1268
|
-
|
|
1269
|
-
public init?(enabled: Bool, ssid: String?, password: String?, localIp: String?) {
|
|
1270
|
-
if enabled {
|
|
1271
|
-
guard
|
|
1272
|
-
let ssid = ssid?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
1273
|
-
!ssid.isEmpty,
|
|
1274
|
-
let password = password?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
1275
|
-
!password.isEmpty,
|
|
1276
|
-
let localIp = localIp?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
1277
|
-
!localIp.isEmpty
|
|
1278
|
-
else {
|
|
1279
|
-
return nil
|
|
1280
|
-
}
|
|
1281
|
-
self = .enabled(ssid: ssid, password: password, localIp: localIp)
|
|
1282
|
-
} else {
|
|
1283
|
-
self = .disabled
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
public init?(values: [String: Any]) {
|
|
1288
|
-
if let nested = values["hotspot"] as? [String: Any] {
|
|
1289
|
-
self.init(values: nested)
|
|
1290
|
-
return
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
guard let state = stringValue(values, "state")?.lowercased() else {
|
|
1294
|
-
return nil
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
|
-
switch state {
|
|
1298
|
-
case State.enabled.rawValue:
|
|
1299
|
-
self.init(
|
|
1300
|
-
enabled: true,
|
|
1301
|
-
ssid: nonEmptyStringValue(values, "ssid"),
|
|
1302
|
-
password: nonEmptyStringValue(values, "password"),
|
|
1303
|
-
localIp: nonEmptyStringValue(values, "localIp")
|
|
1304
|
-
)
|
|
1305
|
-
case State.disabled.rawValue:
|
|
1306
|
-
self = .disabled
|
|
1307
|
-
default:
|
|
1308
|
-
return nil
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
static func fromStoreValues(_ values: [String: Any]) -> HotspotStatus? {
|
|
1313
|
-
guard let enabled = boolValue(values, "hotspotEnabled") else {
|
|
1314
|
-
return nil
|
|
1315
|
-
}
|
|
1316
|
-
return fromStoreFields(
|
|
1317
|
-
enabled: enabled,
|
|
1318
|
-
ssid: nonEmptyStringValue(values, "hotspotSsid"),
|
|
1319
|
-
password: nonEmptyStringValue(values, "hotspotPassword"),
|
|
1320
|
-
localIp: nonEmptyStringValue(values, "hotspotGatewayIp")
|
|
1321
|
-
)
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
static func fromStoreFields(enabled: Bool, ssid: String?, password: String?, localIp: String?) -> HotspotStatus? {
|
|
1325
|
-
HotspotStatus(enabled: enabled, ssid: ssid, password: password, localIp: localIp)
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
var storeValues: [String: Any] {
|
|
1329
|
-
switch self {
|
|
1330
|
-
case .disabled:
|
|
1331
|
-
[
|
|
1332
|
-
"hotspotEnabled": false,
|
|
1333
|
-
"hotspotSsid": "",
|
|
1334
|
-
"hotspotPassword": "",
|
|
1335
|
-
"hotspotGatewayIp": "",
|
|
1336
|
-
]
|
|
1337
|
-
case let .enabled(ssid, password, localIp):
|
|
1338
|
-
[
|
|
1339
|
-
"hotspotEnabled": true,
|
|
1340
|
-
"hotspotSsid": ssid,
|
|
1341
|
-
"hotspotPassword": password,
|
|
1342
|
-
"hotspotGatewayIp": localIp,
|
|
1343
|
-
]
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
public var values: [String: Any] {
|
|
1348
|
-
switch self {
|
|
1349
|
-
case .disabled:
|
|
1350
|
-
["state": State.disabled.rawValue]
|
|
1351
|
-
case let .enabled(ssid, password, localIp):
|
|
1352
|
-
[
|
|
1353
|
-
"state": State.enabled.rawValue,
|
|
1354
|
-
"ssid": ssid,
|
|
1355
|
-
"password": password,
|
|
1356
|
-
"localIp": localIp,
|
|
1357
|
-
]
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
public var state: State {
|
|
1362
|
-
switch self {
|
|
1363
|
-
case .disabled:
|
|
1364
|
-
.disabled
|
|
1365
|
-
case .enabled:
|
|
1366
|
-
.enabled
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
public var isEnabled: Bool {
|
|
1371
|
-
if case .enabled = self {
|
|
1372
|
-
return true
|
|
1373
|
-
}
|
|
1374
|
-
return false
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
public var description: String {
|
|
1378
|
-
switch self {
|
|
1379
|
-
case .disabled:
|
|
1380
|
-
"HotspotStatus(disabled)"
|
|
1381
|
-
case let .enabled(ssid, _, localIp):
|
|
1382
|
-
"HotspotStatus(enabled: \(ssid), localIp: \(localIp))"
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
public struct HotspotStatusEvent: CustomStringConvertible {
|
|
1388
|
-
public let status: HotspotStatus
|
|
1389
|
-
|
|
1390
|
-
public init(status: HotspotStatus) {
|
|
1391
|
-
self.status = status
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
init(enabled: Bool, ssid: String?, password: String?, localIp: String?) {
|
|
1395
|
-
self.status = HotspotStatus.fromStoreFields(enabled: enabled, ssid: ssid, password: password, localIp: localIp) ?? .disabled
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
init(values: [String: Any]) {
|
|
1399
|
-
self.status = HotspotStatus(values: values) ?? .disabled
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
public var values: [String: Any] {
|
|
1403
|
-
status.values.merging(["type": "hotspot_status_change"]) { _, new in new }
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
public var description: String {
|
|
1407
|
-
"HotspotStatusEvent(\(status))"
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
public struct HotspotErrorEvent: CustomStringConvertible {
|
|
1412
|
-
public let values: [String: Any]
|
|
1413
|
-
|
|
1414
|
-
public init(values: [String: Any]) {
|
|
1415
|
-
self.values = values
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
public var message: String? {
|
|
1419
|
-
stringValue(values, "errorMessage")
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
public var timestamp: Int? {
|
|
1423
|
-
intValue(values["timestamp"])
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
public var description: String {
|
|
1427
|
-
"HotspotErrorEvent(message: \(message ?? "unknown"))"
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
public enum PhotoResponse: CustomStringConvertible, Equatable {
|
|
1432
|
-
public enum State: String {
|
|
1433
|
-
case success
|
|
1434
|
-
case error
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
case success(requestId: String, uploadUrl: String, timestamp: Int)
|
|
1438
|
-
case error(requestId: String, errorCode: String?, errorMessage: String, timestamp: Int)
|
|
1439
|
-
|
|
1440
|
-
public init(values: [String: Any]) {
|
|
1441
|
-
let requestId = stringValue(values, "requestId") ?? ""
|
|
1442
|
-
let timestamp = intValue(values["timestamp"]) ?? Int(Date().timeIntervalSince1970 * 1000)
|
|
1443
|
-
let state = stringValue(values, "state")?.lowercased()
|
|
1444
|
-
if state == State.success.rawValue {
|
|
1445
|
-
self = .success(
|
|
1446
|
-
requestId: requestId,
|
|
1447
|
-
uploadUrl: stringValue(values, "uploadUrl") ?? "",
|
|
1448
|
-
timestamp: timestamp
|
|
1449
|
-
)
|
|
1450
|
-
} else {
|
|
1451
|
-
self = .error(
|
|
1452
|
-
requestId: requestId,
|
|
1453
|
-
errorCode: stringValue(values, "errorCode"),
|
|
1454
|
-
errorMessage: stringValue(values, "errorMessage") ?? "Unknown photo error",
|
|
1455
|
-
timestamp: timestamp
|
|
1456
|
-
)
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
|
|
1460
|
-
public var state: State {
|
|
1461
|
-
switch self {
|
|
1462
|
-
case .success:
|
|
1463
|
-
.success
|
|
1464
|
-
case .error:
|
|
1465
|
-
.error
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
public var requestId: String {
|
|
1470
|
-
switch self {
|
|
1471
|
-
case let .success(requestId, _, _), let .error(requestId, _, _, _):
|
|
1472
|
-
requestId
|
|
1473
|
-
}
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
|
-
public var timestamp: Int {
|
|
1477
|
-
switch self {
|
|
1478
|
-
case let .success(_, _, timestamp), let .error(_, _, _, timestamp):
|
|
1479
|
-
timestamp
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
public var values: [String: Any] {
|
|
1484
|
-
switch self {
|
|
1485
|
-
case let .success(requestId, uploadUrl, timestamp):
|
|
1486
|
-
return [
|
|
1487
|
-
"state": State.success.rawValue,
|
|
1488
|
-
"requestId": requestId,
|
|
1489
|
-
"uploadUrl": uploadUrl,
|
|
1490
|
-
"timestamp": timestamp,
|
|
1491
|
-
]
|
|
1492
|
-
case let .error(requestId, errorCode, errorMessage, timestamp):
|
|
1493
|
-
var values: [String: Any] = [
|
|
1494
|
-
"state": State.error.rawValue,
|
|
1495
|
-
"requestId": requestId,
|
|
1496
|
-
"errorMessage": errorMessage,
|
|
1497
|
-
"timestamp": timestamp,
|
|
1498
|
-
]
|
|
1499
|
-
if let errorCode, !errorCode.isEmpty {
|
|
1500
|
-
values["errorCode"] = errorCode
|
|
1501
|
-
}
|
|
1502
|
-
return values
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
public var description: String {
|
|
1507
|
-
"PhotoResponse(requestId: \(requestId), state: \(state.rawValue))"
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
public struct PhotoResponseEvent: CustomStringConvertible {
|
|
1512
|
-
public let response: PhotoResponse
|
|
1513
|
-
|
|
1514
|
-
public init(response: PhotoResponse) {
|
|
1515
|
-
self.response = response
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
public init(values: [String: Any]) {
|
|
1519
|
-
self.response = PhotoResponse(values: values)
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
public var requestId: String {
|
|
1523
|
-
response.requestId
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
public var values: [String: Any] {
|
|
1527
|
-
var values = response.values
|
|
1528
|
-
values["type"] = "photo_response"
|
|
1529
|
-
return values
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
public var description: String {
|
|
1533
|
-
"PhotoResponseEvent(requestId: \(requestId), state: \(response.state.rawValue))"
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
|
-
public enum StreamState: String, Equatable {
|
|
1538
|
-
case initializing
|
|
1539
|
-
case streaming
|
|
1540
|
-
case stopping
|
|
1541
|
-
case stopped
|
|
1542
|
-
case reconnecting
|
|
1543
|
-
case reconnected
|
|
1544
|
-
case reconnectFailed = "reconnect_failed"
|
|
1545
|
-
case error
|
|
1546
|
-
|
|
1547
|
-
fileprivate static func from(_ value: String?) -> StreamState? {
|
|
1548
|
-
switch value?.lowercased() {
|
|
1549
|
-
case "initializing", "starting", "connecting":
|
|
1550
|
-
return .initializing
|
|
1551
|
-
case "streaming", "streaming_started", "active":
|
|
1552
|
-
return .streaming
|
|
1553
|
-
case "stopping":
|
|
1554
|
-
return .stopping
|
|
1555
|
-
case "stopped", "not_streaming", "disconnected", "timeout":
|
|
1556
|
-
return .stopped
|
|
1557
|
-
case "reconnecting":
|
|
1558
|
-
return .reconnecting
|
|
1559
|
-
case "reconnected":
|
|
1560
|
-
return .reconnected
|
|
1561
|
-
case "reconnect_failed":
|
|
1562
|
-
return .reconnectFailed
|
|
1563
|
-
case "error", "error_not_streaming":
|
|
1564
|
-
return .error
|
|
1565
|
-
default:
|
|
1566
|
-
return nil
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
public enum StreamStatusKind: String, Equatable {
|
|
1572
|
-
case lifecycle
|
|
1573
|
-
case reconnect
|
|
1574
|
-
case error
|
|
1575
|
-
case snapshot
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
public enum StreamStatus: CustomStringConvertible, Equatable {
|
|
1579
|
-
case lifecycle(state: StreamState, streamId: String?, timestamp: Int?)
|
|
1580
|
-
case reconnecting(streamId: String?, attempt: Int, maxAttempts: Int, reason: String, timestamp: Int?)
|
|
1581
|
-
case reconnected(streamId: String?, attempt: Int, timestamp: Int?)
|
|
1582
|
-
case reconnectFailed(streamId: String?, maxAttempts: Int, timestamp: Int?)
|
|
1583
|
-
case error(streamId: String?, errorDetails: String, timestamp: Int?)
|
|
1584
|
-
case snapshot(state: StreamState, streaming: Bool, reconnecting: Bool, streamId: String?, attempt: Int?, timestamp: Int?)
|
|
1585
|
-
|
|
1586
|
-
public init(values: [String: Any]) {
|
|
1587
|
-
let rawState = stringValue(values, "status")
|
|
1588
|
-
let streamId = stringValue(values, "streamId")
|
|
1589
|
-
let timestamp = intValue(values["timestamp"])
|
|
1590
|
-
let attempt = optionalIntValue(values, "attempt")
|
|
1591
|
-
let maxAttempts = optionalIntValue(values, "maxAttempts") ?? 0
|
|
1592
|
-
|
|
1593
|
-
if hasAnyKey(values, "streaming") || hasAnyKey(values, "reconnecting") {
|
|
1594
|
-
let streaming = boolValue(values, "streaming") == true
|
|
1595
|
-
let reconnecting = boolValue(values, "reconnecting") == true
|
|
1596
|
-
let snapshotState: StreamState = reconnecting ? .reconnecting : (streaming ? .streaming : .stopped)
|
|
1597
|
-
self = .snapshot(
|
|
1598
|
-
state: snapshotState,
|
|
1599
|
-
streaming: streaming,
|
|
1600
|
-
reconnecting: reconnecting,
|
|
1601
|
-
streamId: streamId,
|
|
1602
|
-
attempt: attempt,
|
|
1603
|
-
timestamp: timestamp
|
|
1604
|
-
)
|
|
1605
|
-
return
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
guard let state = StreamState.from(rawState) else {
|
|
1609
|
-
self = .error(
|
|
1610
|
-
streamId: streamId,
|
|
1611
|
-
errorDetails: rawState.map { "Unknown stream status: \($0)" } ?? "Missing stream status",
|
|
1612
|
-
timestamp: timestamp
|
|
1613
|
-
)
|
|
1614
|
-
return
|
|
1615
|
-
}
|
|
1616
|
-
|
|
1617
|
-
switch state {
|
|
1618
|
-
case .reconnecting:
|
|
1619
|
-
self = .reconnecting(
|
|
1620
|
-
streamId: streamId,
|
|
1621
|
-
attempt: attempt ?? 0,
|
|
1622
|
-
maxAttempts: maxAttempts,
|
|
1623
|
-
reason: stringValue(values, "reason") ?? "",
|
|
1624
|
-
timestamp: timestamp
|
|
1625
|
-
)
|
|
1626
|
-
case .reconnected:
|
|
1627
|
-
self = .reconnected(streamId: streamId, attempt: attempt ?? 0, timestamp: timestamp)
|
|
1628
|
-
case .reconnectFailed:
|
|
1629
|
-
self = .reconnectFailed(streamId: streamId, maxAttempts: maxAttempts, timestamp: timestamp)
|
|
1630
|
-
case .error:
|
|
1631
|
-
self = .error(
|
|
1632
|
-
streamId: streamId,
|
|
1633
|
-
errorDetails: stringValue(values, "errorDetails")
|
|
1634
|
-
?? (rawState == "error_not_streaming" ? "not_streaming" : "Unknown stream error"),
|
|
1635
|
-
timestamp: timestamp
|
|
1636
|
-
)
|
|
1637
|
-
default:
|
|
1638
|
-
self = .lifecycle(state: state, streamId: streamId, timestamp: timestamp)
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
|
|
1642
|
-
public var kind: StreamStatusKind {
|
|
1643
|
-
switch self {
|
|
1644
|
-
case .lifecycle:
|
|
1645
|
-
.lifecycle
|
|
1646
|
-
case .reconnecting, .reconnected, .reconnectFailed:
|
|
1647
|
-
.reconnect
|
|
1648
|
-
case .error:
|
|
1649
|
-
.error
|
|
1650
|
-
case .snapshot:
|
|
1651
|
-
.snapshot
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
public var state: StreamState {
|
|
1656
|
-
switch self {
|
|
1657
|
-
case let .lifecycle(state, _, _):
|
|
1658
|
-
state
|
|
1659
|
-
case .reconnecting:
|
|
1660
|
-
.reconnecting
|
|
1661
|
-
case .reconnected:
|
|
1662
|
-
.reconnected
|
|
1663
|
-
case .reconnectFailed:
|
|
1664
|
-
.reconnectFailed
|
|
1665
|
-
case .error:
|
|
1666
|
-
.error
|
|
1667
|
-
case let .snapshot(state, _, _, _, _, _):
|
|
1668
|
-
state
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
|
-
public var streamId: String? {
|
|
1673
|
-
switch self {
|
|
1674
|
-
case let .lifecycle(_, streamId, _),
|
|
1675
|
-
let .reconnecting(streamId, _, _, _, _),
|
|
1676
|
-
let .reconnected(streamId, _, _),
|
|
1677
|
-
let .reconnectFailed(streamId, _, _),
|
|
1678
|
-
let .error(streamId, _, _),
|
|
1679
|
-
let .snapshot(_, _, _, streamId, _, _):
|
|
1680
|
-
streamId
|
|
1681
|
-
}
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
public var timestamp: Int? {
|
|
1685
|
-
switch self {
|
|
1686
|
-
case let .lifecycle(_, _, timestamp),
|
|
1687
|
-
let .reconnecting(_, _, _, _, timestamp),
|
|
1688
|
-
let .reconnected(_, _, timestamp),
|
|
1689
|
-
let .reconnectFailed(_, _, timestamp),
|
|
1690
|
-
let .error(_, _, timestamp),
|
|
1691
|
-
let .snapshot(_, _, _, _, _, timestamp):
|
|
1692
|
-
timestamp
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
|
-
public var values: [String: Any] {
|
|
1697
|
-
var values: [String: Any] = [
|
|
1698
|
-
"kind": kind.rawValue,
|
|
1699
|
-
"status": state.rawValue,
|
|
1700
|
-
]
|
|
1701
|
-
if let streamId, !streamId.isEmpty {
|
|
1702
|
-
values["streamId"] = streamId
|
|
1703
|
-
}
|
|
1704
|
-
if let timestamp {
|
|
1705
|
-
values["timestamp"] = timestamp
|
|
1706
|
-
}
|
|
1707
|
-
|
|
1708
|
-
switch self {
|
|
1709
|
-
case .lifecycle:
|
|
1710
|
-
break
|
|
1711
|
-
case let .reconnecting(_, attempt, maxAttempts, reason, _):
|
|
1712
|
-
values["attempt"] = attempt
|
|
1713
|
-
values["maxAttempts"] = maxAttempts
|
|
1714
|
-
values["reason"] = reason
|
|
1715
|
-
case let .reconnected(_, attempt, _):
|
|
1716
|
-
values["attempt"] = attempt
|
|
1717
|
-
case let .reconnectFailed(_, maxAttempts, _):
|
|
1718
|
-
values["maxAttempts"] = maxAttempts
|
|
1719
|
-
case let .error(_, errorDetails, _):
|
|
1720
|
-
values["errorDetails"] = errorDetails
|
|
1721
|
-
case let .snapshot(_, streaming, reconnecting, _, attempt, _):
|
|
1722
|
-
values["streaming"] = streaming
|
|
1723
|
-
values["reconnecting"] = reconnecting
|
|
1724
|
-
if let attempt {
|
|
1725
|
-
values["attempt"] = attempt
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
return values
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
public var description: String {
|
|
1733
|
-
"StreamStatus(kind: \(kind.rawValue), status: \(state.rawValue), streamId: \(streamId ?? "none"))"
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
public struct StreamStatusEvent: CustomStringConvertible {
|
|
1738
|
-
public let status: StreamStatus
|
|
1739
|
-
|
|
1740
|
-
public init(status: StreamStatus) {
|
|
1741
|
-
self.status = status
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
public init(values: [String: Any]) {
|
|
1745
|
-
self.status = StreamStatus(values: values)
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
public var state: StreamState {
|
|
1749
|
-
status.state
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
public var streamId: String? {
|
|
1753
|
-
status.streamId
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
|
-
public var values: [String: Any] {
|
|
1757
|
-
var values = status.values
|
|
1758
|
-
values["type"] = "stream_status"
|
|
1759
|
-
return values
|
|
1760
|
-
}
|
|
1761
|
-
|
|
1762
|
-
public var description: String {
|
|
1763
|
-
"StreamStatusEvent(kind: \(status.kind.rawValue), status: \(state.rawValue), streamId: \(streamId ?? "none"))"
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
public struct KeepAliveAckEvent: CustomStringConvertible, Equatable {
|
|
1768
|
-
public let streamId: String
|
|
1769
|
-
public let ackId: String
|
|
1770
|
-
public let timestamp: Int?
|
|
1771
|
-
|
|
1772
|
-
public init(streamId: String, ackId: String, timestamp: Int? = nil) {
|
|
1773
|
-
self.streamId = streamId
|
|
1774
|
-
self.ackId = ackId
|
|
1775
|
-
self.timestamp = timestamp
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
public init(values: [String: Any]) {
|
|
1779
|
-
self.streamId = stringValue(values, "streamId") ?? ""
|
|
1780
|
-
self.ackId = stringValue(values, "ackId") ?? ""
|
|
1781
|
-
self.timestamp = intValue(values["timestamp"])
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
|
-
public var values: [String: Any] {
|
|
1785
|
-
var values: [String: Any] = [
|
|
1786
|
-
"type": "keep_alive_ack",
|
|
1787
|
-
"streamId": streamId,
|
|
1788
|
-
"ackId": ackId,
|
|
1789
|
-
]
|
|
1790
|
-
if let timestamp {
|
|
1791
|
-
values["timestamp"] = timestamp
|
|
1792
|
-
}
|
|
1793
|
-
return values
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
public var description: String {
|
|
1797
|
-
"KeepAliveAckEvent(streamId: \(streamId), ackId: \(ackId))"
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
|
|
1801
|
-
public struct BluetoothError: Error, LocalizedError, CustomStringConvertible {
|
|
1802
|
-
public let code: String
|
|
1803
|
-
public let message: String
|
|
1804
|
-
|
|
1805
|
-
public init(code: String, message: String) {
|
|
1806
|
-
self.code = code
|
|
1807
|
-
self.message = message
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
public var errorDescription: String? {
|
|
1811
|
-
message
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
|
-
public var description: String {
|
|
1815
|
-
"\(code): \(message)"
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
private final class BluetoothAvailability: NSObject, CBCentralManagerDelegate {
|
|
1820
|
-
static let shared = BluetoothAvailability()
|
|
1821
|
-
|
|
1822
|
-
private var centralManager: CBCentralManager?
|
|
1823
|
-
private var state: CBManagerState = .unknown
|
|
1824
|
-
|
|
1825
|
-
override private init() {
|
|
1826
|
-
super.init()
|
|
1827
|
-
centralManager = CBCentralManager(
|
|
1828
|
-
delegate: self,
|
|
1829
|
-
queue: .main,
|
|
1830
|
-
options: [CBCentralManagerOptionShowPowerAlertKey: false]
|
|
1831
|
-
)
|
|
1832
|
-
state = centralManager?.state ?? .unknown
|
|
1833
|
-
}
|
|
1834
|
-
|
|
1835
|
-
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
1836
|
-
state = central.state
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
|
-
func requirePoweredOn(operation: String) throws {
|
|
1840
|
-
if let current = centralManager?.state {
|
|
1841
|
-
state = current
|
|
1842
|
-
}
|
|
1843
|
-
switch state {
|
|
1844
|
-
case .poweredOn:
|
|
1845
|
-
return
|
|
1846
|
-
case .poweredOff:
|
|
1847
|
-
throw BluetoothError(
|
|
1848
|
-
code: "bluetooth_powered_off",
|
|
1849
|
-
message: "Turn on phone Bluetooth to \(operation)."
|
|
1850
|
-
)
|
|
1851
|
-
case .unauthorized:
|
|
1852
|
-
throw BluetoothError(
|
|
1853
|
-
code: "bluetooth_unauthorized",
|
|
1854
|
-
message: "Allow Bluetooth access to \(operation)."
|
|
1855
|
-
)
|
|
1856
|
-
case .unsupported:
|
|
1857
|
-
throw BluetoothError(
|
|
1858
|
-
code: "bluetooth_unsupported",
|
|
1859
|
-
message: "This phone does not support Bluetooth."
|
|
1860
|
-
)
|
|
1861
|
-
case .resetting, .unknown:
|
|
1862
|
-
throw BluetoothError(
|
|
1863
|
-
code: "bluetooth_not_ready",
|
|
1864
|
-
message: "Bluetooth is not ready yet. Try again."
|
|
1865
|
-
)
|
|
1866
|
-
@unknown default:
|
|
1867
|
-
throw BluetoothError(
|
|
1868
|
-
code: "bluetooth_unavailable",
|
|
1869
|
-
message: "Bluetooth is unavailable. Try again."
|
|
1870
|
-
)
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
public enum ScanStopReason {
|
|
1876
|
-
case completed
|
|
1877
|
-
case cancelled
|
|
1878
|
-
case error
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
@MainActor
|
|
1882
|
-
public final class ScanSession {
|
|
1883
|
-
private let stopAction: () -> Void
|
|
1884
|
-
private var stopped = false
|
|
1885
|
-
|
|
1886
|
-
init(stopAction: @escaping () -> Void) {
|
|
1887
|
-
self.stopAction = stopAction
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
|
-
public func stop() {
|
|
1891
|
-
guard !stopped else { return }
|
|
1892
|
-
stopped = true
|
|
1893
|
-
stopAction()
|
|
1894
|
-
}
|
|
1895
|
-
|
|
1896
|
-
fileprivate func markStopped() {
|
|
1897
|
-
stopped = true
|
|
1898
|
-
}
|
|
1899
|
-
}
|
|
1900
|
-
|
|
1901
|
-
public struct LocalTranscriptionEvent: CustomStringConvertible {
|
|
1902
|
-
public let text: String
|
|
1903
|
-
public let isFinal: Bool
|
|
1904
|
-
public let values: [String: Any]
|
|
1905
|
-
|
|
1906
|
-
public init(text: String, isFinal: Bool, values: [String: Any]) {
|
|
1907
|
-
self.text = text
|
|
1908
|
-
self.isFinal = isFinal
|
|
1909
|
-
self.values = values
|
|
1910
|
-
}
|
|
1911
|
-
|
|
1912
|
-
public var description: String {
|
|
1913
|
-
"LocalTranscriptionEvent(text: \(text), isFinal: \(isFinal))"
|
|
1914
|
-
}
|
|
1915
|
-
}
|
|
1916
|
-
|
|
1917
|
-
public struct MicPcmEvent: CustomStringConvertible {
|
|
1918
|
-
public static let sampleRate = 16_000
|
|
1919
|
-
public static let bitsPerSample = 16
|
|
1920
|
-
public static let channels = 1
|
|
1921
|
-
public static let encoding = "pcm_s16le"
|
|
1922
|
-
|
|
1923
|
-
public let pcm: Data
|
|
1924
|
-
public let sampleRate: Int
|
|
1925
|
-
public let bitsPerSample: Int
|
|
1926
|
-
public let channels: Int
|
|
1927
|
-
public let encoding: String
|
|
1928
|
-
public let vadGated: Bool
|
|
1929
|
-
public let values: [String: Any]
|
|
1930
|
-
|
|
1931
|
-
public init(values: [String: Any]) {
|
|
1932
|
-
let pcm = values["pcm"] as? Data ?? Data()
|
|
1933
|
-
let sampleRate = intValue(values["sampleRate"]) ?? Self.sampleRate
|
|
1934
|
-
let bitsPerSample = intValue(values["bitsPerSample"]) ?? Self.bitsPerSample
|
|
1935
|
-
let channels = intValue(values["channels"]) ?? Self.channels
|
|
1936
|
-
let encoding = values["encoding"] as? String ?? Self.encoding
|
|
1937
|
-
let vadGated = boolValue(values, "vadGated") ?? false
|
|
1938
|
-
|
|
1939
|
-
var normalized = values
|
|
1940
|
-
normalized["type"] = "mic_pcm"
|
|
1941
|
-
normalized["pcm"] = pcm
|
|
1942
|
-
normalized["sampleRate"] = sampleRate
|
|
1943
|
-
normalized["bitsPerSample"] = bitsPerSample
|
|
1944
|
-
normalized["channels"] = channels
|
|
1945
|
-
normalized["encoding"] = encoding
|
|
1946
|
-
normalized["vadGated"] = vadGated
|
|
1947
|
-
|
|
1948
|
-
self.pcm = pcm
|
|
1949
|
-
self.sampleRate = sampleRate
|
|
1950
|
-
self.bitsPerSample = bitsPerSample
|
|
1951
|
-
self.channels = channels
|
|
1952
|
-
self.encoding = encoding
|
|
1953
|
-
self.vadGated = vadGated
|
|
1954
|
-
self.values = normalized
|
|
1955
|
-
}
|
|
1956
|
-
|
|
1957
|
-
public var description: String {
|
|
1958
|
-
"MicPcmEvent(bytes: \(pcm.count), sampleRate: \(sampleRate), bitsPerSample: \(bitsPerSample), channels: \(channels), encoding: \(encoding), vadGated: \(vadGated))"
|
|
1959
|
-
}
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
public struct MicLc3Event: CustomStringConvertible {
|
|
1963
|
-
public static let sampleRate = 16_000
|
|
1964
|
-
public static let channels = 1
|
|
1965
|
-
public static let encoding = "lc3"
|
|
1966
|
-
public static let frameDurationMs = 10
|
|
1967
|
-
public static let defaultFrameSizeBytes = 60
|
|
1968
|
-
|
|
1969
|
-
public let lc3: Data
|
|
1970
|
-
public let sampleRate: Int
|
|
1971
|
-
public let channels: Int
|
|
1972
|
-
public let encoding: String
|
|
1973
|
-
public let frameDurationMs: Int
|
|
1974
|
-
public let frameSizeBytes: Int
|
|
1975
|
-
public let bitrate: Int
|
|
1976
|
-
public let packetizedFromGlasses: Bool
|
|
1977
|
-
public let vadGated: Bool
|
|
1978
|
-
public let values: [String: Any]
|
|
1979
|
-
|
|
1980
|
-
public init(values: [String: Any]) {
|
|
1981
|
-
let lc3 = values["lc3"] as? Data ?? Data()
|
|
1982
|
-
let sampleRate = intValue(values["sampleRate"]) ?? Self.sampleRate
|
|
1983
|
-
let channels = intValue(values["channels"]) ?? Self.channels
|
|
1984
|
-
let encoding = values["encoding"] as? String ?? Self.encoding
|
|
1985
|
-
let frameDurationMs = intValue(values["frameDurationMs"]) ?? Self.frameDurationMs
|
|
1986
|
-
let frameSizeBytes = intValue(values["frameSizeBytes"]) ?? Self.defaultFrameSizeBytes
|
|
1987
|
-
let bitrate = intValue(values["bitrate"]) ?? frameSizeBytes * 8 * (1000 / frameDurationMs)
|
|
1988
|
-
let packetizedFromGlasses = boolValue(values, "packetizedFromGlasses") ?? false
|
|
1989
|
-
let vadGated = boolValue(values, "vadGated") ?? false
|
|
1990
|
-
|
|
1991
|
-
var normalized = values
|
|
1992
|
-
normalized["type"] = "mic_lc3"
|
|
1993
|
-
normalized["lc3"] = lc3
|
|
1994
|
-
normalized["sampleRate"] = sampleRate
|
|
1995
|
-
normalized["channels"] = channels
|
|
1996
|
-
normalized["encoding"] = encoding
|
|
1997
|
-
normalized["frameDurationMs"] = frameDurationMs
|
|
1998
|
-
normalized["frameSizeBytes"] = frameSizeBytes
|
|
1999
|
-
normalized["bitrate"] = bitrate
|
|
2000
|
-
normalized["packetizedFromGlasses"] = packetizedFromGlasses
|
|
2001
|
-
normalized["vadGated"] = vadGated
|
|
2002
|
-
|
|
2003
|
-
self.lc3 = lc3
|
|
2004
|
-
self.sampleRate = sampleRate
|
|
2005
|
-
self.channels = channels
|
|
2006
|
-
self.encoding = encoding
|
|
2007
|
-
self.frameDurationMs = frameDurationMs
|
|
2008
|
-
self.frameSizeBytes = frameSizeBytes
|
|
2009
|
-
self.bitrate = bitrate
|
|
2010
|
-
self.packetizedFromGlasses = packetizedFromGlasses
|
|
2011
|
-
self.vadGated = vadGated
|
|
2012
|
-
self.values = normalized
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
public var description: String {
|
|
2016
|
-
"MicLc3Event(bytes: \(lc3.count), sampleRate: \(sampleRate), channels: \(channels), frameDurationMs: \(frameDurationMs), frameSizeBytes: \(frameSizeBytes), bitrate: \(bitrate), packetizedFromGlasses: \(packetizedFromGlasses), vadGated: \(vadGated))"
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
public struct GlassesMediaVolumeGetResult: CustomStringConvertible {
|
|
2021
|
-
public let level: Int?
|
|
2022
|
-
public let statusCode: Int?
|
|
2023
|
-
public let values: [String: Any]
|
|
2024
|
-
|
|
2025
|
-
public init(values: [String: Any]) {
|
|
2026
|
-
self.level = intValue(values["level"])
|
|
2027
|
-
self.statusCode = intValue(values["statusCode"])
|
|
2028
|
-
self.values = values
|
|
2029
|
-
}
|
|
2030
|
-
|
|
2031
|
-
public var description: String {
|
|
2032
|
-
let levelText = level.map(String.init) ?? "unknown"
|
|
2033
|
-
let statusCodeText = statusCode.map(String.init) ?? "unknown"
|
|
2034
|
-
return "GlassesMediaVolumeGetResult(level: \(levelText), statusCode: \(statusCodeText))"
|
|
2035
|
-
}
|
|
2036
|
-
}
|
|
2037
|
-
|
|
2038
|
-
public struct GlassesMediaVolumeSetResult: CustomStringConvertible {
|
|
2039
|
-
public let statusCode: Int?
|
|
2040
|
-
public let values: [String: Any]
|
|
2041
|
-
|
|
2042
|
-
public init(values: [String: Any]) {
|
|
2043
|
-
self.statusCode = intValue(values["statusCode"])
|
|
2044
|
-
self.values = values
|
|
2045
|
-
}
|
|
2046
|
-
|
|
2047
|
-
public var description: String {
|
|
2048
|
-
"GlassesMediaVolumeSetResult(statusCode: \(statusCode.map(String.init) ?? "unknown"))"
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2051
|
-
|
|
2052
|
-
public enum BluetoothEvent: CustomStringConvertible {
|
|
2053
|
-
case buttonPress(ButtonPressEvent)
|
|
2054
|
-
case touch(TouchEvent)
|
|
2055
|
-
case wifiStatus(WifiStatusEvent)
|
|
2056
|
-
case hotspotStatus(HotspotStatusEvent)
|
|
2057
|
-
case hotspotError(HotspotErrorEvent)
|
|
2058
|
-
case photoResponse(PhotoResponseEvent)
|
|
2059
|
-
case streamStatus(StreamStatusEvent)
|
|
2060
|
-
case keepAliveAck(KeepAliveAckEvent)
|
|
2061
|
-
case localTranscription(LocalTranscriptionEvent)
|
|
2062
|
-
case raw(name: String, values: [String: Any])
|
|
2063
|
-
|
|
2064
|
-
public var description: String {
|
|
2065
|
-
switch self {
|
|
2066
|
-
case let .buttonPress(event):
|
|
2067
|
-
event.description
|
|
2068
|
-
case let .touch(event):
|
|
2069
|
-
event.description
|
|
2070
|
-
case let .wifiStatus(event):
|
|
2071
|
-
event.description
|
|
2072
|
-
case let .hotspotStatus(event):
|
|
2073
|
-
event.description
|
|
2074
|
-
case let .hotspotError(event):
|
|
2075
|
-
event.description
|
|
2076
|
-
case let .photoResponse(event):
|
|
2077
|
-
event.description
|
|
2078
|
-
case let .streamStatus(event):
|
|
2079
|
-
event.description
|
|
2080
|
-
case let .keepAliveAck(event):
|
|
2081
|
-
event.description
|
|
2082
|
-
case let .localTranscription(event):
|
|
2083
|
-
event.description
|
|
2084
|
-
case let .raw(name, values):
|
|
2085
|
-
"\(name): \(values)"
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
@MainActor
|
|
2091
|
-
public protocol MentraBluetoothSDKDelegate: AnyObject {
|
|
2092
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didUpdateGlassesStatus status: GlassesStatusUpdate)
|
|
2093
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didUpdateBluetoothStatus status: BluetoothStatusUpdate)
|
|
2094
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didDiscover device: Device)
|
|
2095
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didStopScan reason: ScanStopReason)
|
|
2096
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didReceive event: BluetoothEvent)
|
|
2097
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didReceiveMicPcm event: MicPcmEvent)
|
|
2098
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didReceiveMicLc3 event: MicLc3Event)
|
|
2099
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didChangeDefaultDevice device: Device?)
|
|
2100
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didLog message: String)
|
|
2101
|
-
func mentraBluetoothSDK(_ sdk: MentraBluetoothSDK, didFail error: BluetoothError)
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
@MainActor
|
|
2105
|
-
public extension MentraBluetoothSDKDelegate {
|
|
2106
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didUpdateGlassesStatus _: GlassesStatusUpdate) {}
|
|
2107
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didUpdateBluetoothStatus _: BluetoothStatusUpdate) {}
|
|
2108
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didDiscover _: Device) {}
|
|
2109
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didStopScan _: ScanStopReason) {}
|
|
2110
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceive _: BluetoothEvent) {}
|
|
2111
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicPcm _: MicPcmEvent) {}
|
|
2112
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didReceiveMicLc3 _: MicLc3Event) {}
|
|
2113
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didChangeDefaultDevice _: Device?) {}
|
|
2114
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didLog _: String) {}
|
|
2115
|
-
func mentraBluetoothSDK(_: MentraBluetoothSDK, didFail _: BluetoothError) {}
|
|
2116
|
-
}
|
|
2117
|
-
|
|
2118
4
|
@MainActor
|
|
2119
5
|
private final class ActiveScanSession {
|
|
2120
6
|
let model: DeviceModel
|
|
@@ -2163,11 +49,27 @@ public final class MentraBluetoothSDK {
|
|
|
2163
49
|
}
|
|
2164
50
|
}
|
|
2165
51
|
|
|
2166
|
-
public var
|
|
52
|
+
public var state: MentraBluetoothState {
|
|
53
|
+
MentraBluetoothState(glassesStatus: glassesStatus, bluetoothStatus: bluetoothStatus)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public var glasses: GlassesRuntimeState {
|
|
57
|
+
state.glasses
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public var sdkState: PhoneSdkRuntimeState {
|
|
61
|
+
state.sdk
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public var scanState: BluetoothScanState {
|
|
65
|
+
state.scan
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
var glassesStatus: GlassesStatus {
|
|
2167
69
|
GlassesStatus(values: DeviceStore.shared.store.getCategory("glasses"))
|
|
2168
70
|
}
|
|
2169
71
|
|
|
2170
|
-
|
|
72
|
+
var bluetoothStatus: BluetoothStatus {
|
|
2171
73
|
BluetoothStatus(values: DeviceStore.shared.store.getCategory(ObservableStore.bluetoothCategory))
|
|
2172
74
|
}
|
|
2173
75
|
|
|
@@ -2564,9 +466,14 @@ public final class MentraBluetoothSDK {
|
|
|
2564
466
|
private func dispatchStoreUpdate(_ category: String, _ changes: [String: Any]) {
|
|
2565
467
|
switch ObservableStore.normalizeCategory(category) {
|
|
2566
468
|
case "glasses":
|
|
2567
|
-
|
|
469
|
+
let nextState = state
|
|
470
|
+
delegate?.mentraBluetoothSDK(self, didUpdate: nextState)
|
|
471
|
+
delegate?.mentraBluetoothSDK(self, didUpdateGlasses: nextState.glasses)
|
|
2568
472
|
case ObservableStore.bluetoothCategory:
|
|
2569
|
-
|
|
473
|
+
let nextState = state
|
|
474
|
+
delegate?.mentraBluetoothSDK(self, didUpdate: nextState)
|
|
475
|
+
delegate?.mentraBluetoothSDK(self, didUpdateSdkState: nextState.sdk)
|
|
476
|
+
delegate?.mentraBluetoothSDK(self, didUpdateScan: nextState.scan)
|
|
2570
477
|
if !suppressDefaultDeviceEvents && changes.keys.contains(where: { defaultDeviceKeys.contains($0) }) {
|
|
2571
478
|
dispatchDefaultDeviceChanged()
|
|
2572
479
|
}
|
|
@@ -2577,28 +484,6 @@ public final class MentraBluetoothSDK {
|
|
|
2577
484
|
}
|
|
2578
485
|
}
|
|
2579
486
|
|
|
2580
|
-
private func glassesStatusChanges(_ changes: [String: Any]) -> [String: Any] {
|
|
2581
|
-
var merged = changes
|
|
2582
|
-
|
|
2583
|
-
if changes.keys.contains(where: { ["wifiConnected", "wifiSsid", "wifiLocalIp"].contains($0) }) {
|
|
2584
|
-
merged["wifiConnected"] = DeviceStore.shared.get("glasses", "wifiConnected") as? Bool ?? false
|
|
2585
|
-
merged["wifiSsid"] = DeviceStore.shared.get("glasses", "wifiSsid") as? String ?? ""
|
|
2586
|
-
merged["wifiLocalIp"] = DeviceStore.shared.get("glasses", "wifiLocalIp") as? String ?? ""
|
|
2587
|
-
}
|
|
2588
|
-
|
|
2589
|
-
if changes.keys.contains(where: { ["connected", "fullyBooted", "connectionState"].contains($0) }) {
|
|
2590
|
-
merged["connected"] = DeviceStore.shared.get("glasses", "connected") as? Bool ?? false
|
|
2591
|
-
merged["fullyBooted"] = DeviceStore.shared.get("glasses", "fullyBooted") as? Bool ?? false
|
|
2592
|
-
merged["connectionState"] = DeviceStore.shared.get("glasses", "connectionState") as? String ?? "DISCONNECTED"
|
|
2593
|
-
}
|
|
2594
|
-
|
|
2595
|
-
if changes["signalStrengthUpdatedAt"] != nil, changes["signalStrength"] == nil {
|
|
2596
|
-
merged["signalStrength"] = DeviceStore.shared.get("glasses", "signalStrength") as? Int ?? -1
|
|
2597
|
-
}
|
|
2598
|
-
|
|
2599
|
-
return merged
|
|
2600
|
-
}
|
|
2601
|
-
|
|
2602
487
|
private func dispatchDefaultDeviceChanged() {
|
|
2603
488
|
delegate?.mentraBluetoothSDK(self, didChangeDefaultDevice: currentDefaultDevice())
|
|
2604
489
|
}
|