@bota.dev/react-native-app-sdk 2.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/BotaDeviceSDK.podspec +55 -0
  2. package/LICENSE +21 -0
  3. package/README.md +101 -0
  4. package/android/build.gradle +76 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDevices.kt +172 -0
  7. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLifecycle.kt +100 -0
  8. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLogs.kt +82 -0
  9. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidModels.kt +476 -0
  10. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidOTA.kt +58 -0
  11. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingUploads.kt +180 -0
  12. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordings.kt +751 -0
  13. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidSecurity.kt +509 -0
  14. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidWiFi.kt +125 -0
  15. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKEncryptedUploadV2Materials.kt +46 -0
  16. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKModule.kt +723 -0
  17. package/android/src/main/java/dev/bota/sdk/reactnative/BotaDeviceSDKPackage.kt +33 -0
  18. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidDevicesTest.kt +211 -0
  19. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLifecycleTest.kt +132 -0
  20. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidLogsTest.kt +57 -0
  21. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidOTATest.kt +75 -0
  22. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingUploadsTest.kt +147 -0
  23. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidRecordingsTest.kt +355 -0
  24. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidSecurityTest.kt +523 -0
  25. package/android/src/test/java/dev/bota/sdk/reactnative/BotaDeviceSDKAndroidWiFiTest.kt +100 -0
  26. package/generated/codegen-contract.json +3593 -0
  27. package/ios/BotaDeviceSDK.mm +1300 -0
  28. package/ios/BotaDeviceSDKAppleBridge.swift +2260 -0
  29. package/ios/BotaDeviceSDKAppleDevices.swift +191 -0
  30. package/ios/BotaDeviceSDKAppleLifecycle.swift +167 -0
  31. package/ios/BotaDeviceSDKAppleLogs.swift +79 -0
  32. package/ios/BotaDeviceSDKAppleOTA.swift +73 -0
  33. package/ios/BotaDeviceSDKAppleRecordingUploads.swift +178 -0
  34. package/ios/BotaDeviceSDKAppleRecordings.swift +670 -0
  35. package/ios/BotaDeviceSDKAppleSecurity.swift +586 -0
  36. package/ios/BotaDeviceSDKAppleWiFi.swift +128 -0
  37. package/ios/BotaDeviceSDKEncryptedUploadV2Materials.swift +99 -0
  38. package/lib/commonjs/BotaClient.js +194 -0
  39. package/lib/commonjs/BotaClient.js.map +1 -0
  40. package/lib/commonjs/ble/constants.js +10 -0
  41. package/lib/commonjs/ble/constants.js.map +1 -0
  42. package/lib/commonjs/ble/deviceLogs.js +50 -0
  43. package/lib/commonjs/ble/deviceLogs.js.map +1 -0
  44. package/lib/commonjs/cache/DeviceStateCache.js +6 -0
  45. package/lib/commonjs/cache/DeviceStateCache.js.map +1 -0
  46. package/lib/commonjs/client.js +746 -0
  47. package/lib/commonjs/client.js.map +1 -0
  48. package/lib/commonjs/compatibility/runtime.js +44 -0
  49. package/lib/commonjs/compatibility/runtime.js.map +1 -0
  50. package/lib/commonjs/index.js +123 -0
  51. package/lib/commonjs/index.js.map +1 -0
  52. package/lib/commonjs/managers/DeviceManager.js +647 -0
  53. package/lib/commonjs/managers/DeviceManager.js.map +1 -0
  54. package/lib/commonjs/managers/OTAManager.js +140 -0
  55. package/lib/commonjs/managers/OTAManager.js.map +1 -0
  56. package/lib/commonjs/managers/RecordingManager.js +444 -0
  57. package/lib/commonjs/managers/RecordingManager.js.map +1 -0
  58. package/lib/commonjs/managers/StreamingSession.js +175 -0
  59. package/lib/commonjs/managers/StreamingSession.js.map +1 -0
  60. package/lib/commonjs/managers/types.js +6 -0
  61. package/lib/commonjs/managers/types.js.map +1 -0
  62. package/lib/commonjs/models/Device.js +2 -0
  63. package/lib/commonjs/models/Device.js.map +1 -0
  64. package/lib/commonjs/models/Recording.js +2 -0
  65. package/lib/commonjs/models/Recording.js.map +1 -0
  66. package/lib/commonjs/models/Status.js +6 -0
  67. package/lib/commonjs/models/Status.js.map +1 -0
  68. package/lib/commonjs/package.json +1 -0
  69. package/lib/commonjs/specs/NativeBotaDeviceSDK.js +9 -0
  70. package/lib/commonjs/specs/NativeBotaDeviceSDK.js.map +1 -0
  71. package/lib/commonjs/sync/syncStatus.js +178 -0
  72. package/lib/commonjs/sync/syncStatus.js.map +1 -0
  73. package/lib/commonjs/utils/errors.js +194 -0
  74. package/lib/commonjs/utils/errors.js.map +1 -0
  75. package/lib/commonjs/utils/logger.js +84 -0
  76. package/lib/commonjs/utils/logger.js.map +1 -0
  77. package/lib/module/BotaClient.js +189 -0
  78. package/lib/module/BotaClient.js.map +1 -0
  79. package/lib/module/ble/constants.js +6 -0
  80. package/lib/module/ble/constants.js.map +1 -0
  81. package/lib/module/ble/deviceLogs.js +45 -0
  82. package/lib/module/ble/deviceLogs.js.map +1 -0
  83. package/lib/module/cache/DeviceStateCache.js +4 -0
  84. package/lib/module/cache/DeviceStateCache.js.map +1 -0
  85. package/lib/module/client.js +740 -0
  86. package/lib/module/client.js.map +1 -0
  87. package/lib/module/compatibility/runtime.js +35 -0
  88. package/lib/module/compatibility/runtime.js.map +1 -0
  89. package/lib/module/index.js +17 -0
  90. package/lib/module/index.js.map +1 -0
  91. package/lib/module/managers/DeviceManager.js +641 -0
  92. package/lib/module/managers/DeviceManager.js.map +1 -0
  93. package/lib/module/managers/OTAManager.js +134 -0
  94. package/lib/module/managers/OTAManager.js.map +1 -0
  95. package/lib/module/managers/RecordingManager.js +438 -0
  96. package/lib/module/managers/RecordingManager.js.map +1 -0
  97. package/lib/module/managers/StreamingSession.js +168 -0
  98. package/lib/module/managers/StreamingSession.js.map +1 -0
  99. package/lib/module/managers/types.js +4 -0
  100. package/lib/module/managers/types.js.map +1 -0
  101. package/lib/module/models/Device.js +2 -0
  102. package/lib/module/models/Device.js.map +1 -0
  103. package/lib/module/models/Recording.js +2 -0
  104. package/lib/module/models/Recording.js.map +1 -0
  105. package/lib/module/models/Status.js +4 -0
  106. package/lib/module/models/Status.js.map +1 -0
  107. package/lib/module/specs/NativeBotaDeviceSDK.js +5 -0
  108. package/lib/module/specs/NativeBotaDeviceSDK.js.map +1 -0
  109. package/lib/module/sync/syncStatus.js +174 -0
  110. package/lib/module/sync/syncStatus.js.map +1 -0
  111. package/lib/module/utils/errors.js +183 -0
  112. package/lib/module/utils/errors.js.map +1 -0
  113. package/lib/module/utils/logger.js +80 -0
  114. package/lib/module/utils/logger.js.map +1 -0
  115. package/lib/typescript/BotaClient.d.ts +40 -0
  116. package/lib/typescript/BotaClient.d.ts.map +1 -0
  117. package/lib/typescript/ble/constants.d.ts +4 -0
  118. package/lib/typescript/ble/constants.d.ts.map +1 -0
  119. package/lib/typescript/ble/deviceLogs.d.ts +10 -0
  120. package/lib/typescript/ble/deviceLogs.d.ts.map +1 -0
  121. package/lib/typescript/cache/DeviceStateCache.d.ts +14 -0
  122. package/lib/typescript/cache/DeviceStateCache.d.ts.map +1 -0
  123. package/lib/typescript/client.d.ts +208 -0
  124. package/lib/typescript/client.d.ts.map +1 -0
  125. package/lib/typescript/compatibility/runtime.d.ts +11 -0
  126. package/lib/typescript/compatibility/runtime.d.ts.map +1 -0
  127. package/lib/typescript/index.d.ts +19 -0
  128. package/lib/typescript/index.d.ts.map +1 -0
  129. package/lib/typescript/managers/DeviceManager.d.ts +117 -0
  130. package/lib/typescript/managers/DeviceManager.d.ts.map +1 -0
  131. package/lib/typescript/managers/OTAManager.d.ts +25 -0
  132. package/lib/typescript/managers/OTAManager.d.ts.map +1 -0
  133. package/lib/typescript/managers/RecordingManager.d.ts +54 -0
  134. package/lib/typescript/managers/RecordingManager.d.ts.map +1 -0
  135. package/lib/typescript/managers/StreamingSession.d.ts +35 -0
  136. package/lib/typescript/managers/StreamingSession.d.ts.map +1 -0
  137. package/lib/typescript/managers/types.d.ts +19 -0
  138. package/lib/typescript/managers/types.d.ts.map +1 -0
  139. package/lib/typescript/models/Device.d.ts +392 -0
  140. package/lib/typescript/models/Device.d.ts.map +1 -0
  141. package/lib/typescript/models/Recording.d.ts +259 -0
  142. package/lib/typescript/models/Recording.d.ts.map +1 -0
  143. package/lib/typescript/models/Status.d.ts +106 -0
  144. package/lib/typescript/models/Status.d.ts.map +1 -0
  145. package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts +393 -0
  146. package/lib/typescript/specs/NativeBotaDeviceSDK.d.ts.map +1 -0
  147. package/lib/typescript/sync/syncStatus.d.ts +123 -0
  148. package/lib/typescript/sync/syncStatus.d.ts.map +1 -0
  149. package/lib/typescript/utils/errors.d.ts +83 -0
  150. package/lib/typescript/utils/errors.d.ts.map +1 -0
  151. package/lib/typescript/utils/logger.d.ts +36 -0
  152. package/lib/typescript/utils/logger.d.ts.map +1 -0
  153. package/package.json +100 -0
  154. package/scripts/bota_device_sdk_spm_workaround.rb +53 -0
  155. package/src/BotaClient.ts +231 -0
  156. package/src/ble/constants.ts +3 -0
  157. package/src/ble/deviceLogs.ts +61 -0
  158. package/src/cache/DeviceStateCache.ts +20 -0
  159. package/src/client.ts +1410 -0
  160. package/src/compatibility/runtime.ts +55 -0
  161. package/src/index.ts +146 -0
  162. package/src/managers/DeviceManager.ts +927 -0
  163. package/src/managers/OTAManager.ts +195 -0
  164. package/src/managers/RecordingManager.ts +578 -0
  165. package/src/managers/StreamingSession.ts +212 -0
  166. package/src/managers/types.ts +36 -0
  167. package/src/models/Device.ts +484 -0
  168. package/src/models/Recording.ts +281 -0
  169. package/src/models/Status.ts +128 -0
  170. package/src/specs/NativeBotaDeviceSDK.ts +542 -0
  171. package/src/sync/syncStatus.ts +243 -0
  172. package/src/utils/errors.ts +327 -0
  173. package/src/utils/logger.ts +122 -0
@@ -0,0 +1,2260 @@
1
+ import BotaAppSDK
2
+ import Foundation
3
+
4
+ private enum BotaDeviceSDKAppleBridgeInputError: LocalizedError {
5
+ case invalidConnectedDevice
6
+ case invalidConnectionSettings
7
+ case invalidEnvironment
8
+ case invalidHexadecimal
9
+ case invalidSHA256
10
+ case invalidTimeout
11
+ case invalidUnsignedInteger
12
+
13
+ var errorDescription: String? {
14
+ switch self {
15
+ case .invalidConnectedDevice: "connected device contains an unsupported value"
16
+ case .invalidConnectionSettings: "connection settings contain an unsupported value"
17
+ case .invalidEnvironment: "API environment is unsupported"
18
+ case .invalidHexadecimal: "public key must be hexadecimal"
19
+ case .invalidSHA256: "SHA-256 value must contain exactly 32 bytes"
20
+ case .invalidTimeout: "timeout must be a finite non-negative number"
21
+ case .invalidUnsignedInteger: "value must be a finite non-negative integer"
22
+ }
23
+ }
24
+ }
25
+
26
+ @objc(BotaDeviceSDKAppleBridge)
27
+ public final class BotaDeviceSDKAppleBridge: NSObject, @unchecked Sendable {
28
+ @objc public static let shared = BotaDeviceSDKAppleBridge()
29
+
30
+ private let lifecycle: BotaDeviceSDKAppleLifecycle
31
+ private let devices: BotaDeviceSDKAppleDevices
32
+ private let logs: BotaDeviceSDKAppleLogs
33
+ private let ota: BotaDeviceSDKAppleOTA
34
+ private let recordings: BotaDeviceSDKAppleRecordings
35
+ private let recordingUploads: BotaDeviceSDKAppleRecordingUploads
36
+ private let security: BotaDeviceSDKAppleSecurity
37
+ private let wifi: BotaDeviceSDKAppleWiFi
38
+
39
+ override private init() {
40
+ lifecycle = BotaDeviceSDKAppleLifecycle()
41
+ devices = BotaDeviceSDKAppleDevices()
42
+ logs = BotaDeviceSDKAppleLogs()
43
+ ota = BotaDeviceSDKAppleOTA()
44
+ recordings = BotaDeviceSDKAppleRecordings()
45
+ recordingUploads = BotaDeviceSDKAppleRecordingUploads()
46
+ security = BotaDeviceSDKAppleSecurity()
47
+ wifi = BotaDeviceSDKAppleWiFi()
48
+ super.init()
49
+ }
50
+
51
+ @objc(configureWithApplicationSupportDirectory:logLevel:completion:)
52
+ public func configure(
53
+ applicationSupportDirectory: String?,
54
+ logLevel _: String,
55
+ completion: @escaping @Sendable (NSError?) -> Void
56
+ ) {
57
+ let directory = applicationSupportDirectory.map {
58
+ URL(fileURLWithPath: $0, isDirectory: true)
59
+ }
60
+ Task {
61
+ do {
62
+ try await lifecycle.configure(applicationSupportDirectory: directory)
63
+ await recordingUploads.configure(applicationSupportDirectory: directory)
64
+ completion(nil)
65
+ } catch {
66
+ completion(error as NSError)
67
+ }
68
+ }
69
+ }
70
+
71
+ @objc(destroyWithCompletion:)
72
+ public func destroy(completion: @escaping @Sendable () -> Void) {
73
+ Task {
74
+ await security.cancelAll()
75
+ await wifi.cancelAll()
76
+ await logs.stop()
77
+ await ota.cancelAll()
78
+ await recordings.cancelAll()
79
+ await recordingUploads.cancelAll()
80
+ await devices.stopAll()
81
+ await lifecycle.destroy()
82
+ completion()
83
+ }
84
+ }
85
+
86
+ @objc(startScanWithTimeoutMilliseconds:allowDuplicates:onDevice:onError:completion:)
87
+ public func startScan(
88
+ timeoutMilliseconds: Double,
89
+ allowDuplicates: Bool,
90
+ onDevice: @escaping @Sendable ([String: Any]) -> Void,
91
+ onError: @escaping @Sendable (NSError) -> Void,
92
+ completion: @escaping @Sendable (NSError?) -> Void
93
+ ) {
94
+ Task {
95
+ do {
96
+ try await devices.startScan(
97
+ timeoutMilliseconds: try Self.timeoutMilliseconds(timeoutMilliseconds),
98
+ allowDuplicates: allowDuplicates,
99
+ onDevice: { onDevice(Self.discoveredDevice($0)) },
100
+ onError: { onError($0 as NSError) }
101
+ )
102
+ completion(nil)
103
+ } catch {
104
+ completion(error as NSError)
105
+ }
106
+ }
107
+ }
108
+
109
+ @objc(stopScanWithCompletion:)
110
+ public func stopScan(completion: @escaping @Sendable () -> Void) {
111
+ Task {
112
+ await devices.stopScan()
113
+ completion()
114
+ }
115
+ }
116
+
117
+ @objc(connectSelectedWithID:name:deviceType:firmwareVersion:macAddress:pairingState:rssi:discoveredAtMilliseconds:completion:)
118
+ public func connectSelected(
119
+ id: String,
120
+ name: String?,
121
+ deviceType: String?,
122
+ firmwareVersion: String?,
123
+ macAddress: String?,
124
+ pairingState: String?,
125
+ rssi: Double,
126
+ discoveredAtMilliseconds: Double,
127
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
128
+ ) {
129
+ let selected = DiscoveredDevice(
130
+ id: id,
131
+ name: name,
132
+ deviceType: Self.deviceType(deviceType),
133
+ firmwareVersion: firmwareVersion,
134
+ macAddress: macAddress,
135
+ pairingState: Self.pairingState(pairingState),
136
+ rssi: Int(rssi),
137
+ discoveredAt: Date(timeIntervalSince1970: discoveredAtMilliseconds / 1_000)
138
+ )
139
+ Task {
140
+ do {
141
+ completion(Self.connectedDevice(try await devices.connect(selected)), nil)
142
+ } catch {
143
+ completion(nil, error as NSError)
144
+ }
145
+ }
146
+ }
147
+
148
+ @objc(reconnectWithSerialNumber:scanTimeoutMilliseconds:connectionTimeoutMilliseconds:completion:)
149
+ public func reconnect(
150
+ serialNumber: String,
151
+ scanTimeoutMilliseconds: Double,
152
+ connectionTimeoutMilliseconds: Double,
153
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
154
+ ) {
155
+ Task {
156
+ do {
157
+ let hint = try DeviceReconnectHint(
158
+ scanTimeoutMilliseconds: Self.timeoutMilliseconds(scanTimeoutMilliseconds),
159
+ connectionTimeoutMilliseconds: Self.timeoutMilliseconds(
160
+ connectionTimeoutMilliseconds
161
+ )
162
+ )
163
+ completion(
164
+ Self.connectedDevice(
165
+ try await devices.reconnect(serialNumber: serialNumber, hint: hint)
166
+ ),
167
+ nil
168
+ )
169
+ } catch {
170
+ completion(nil, error as NSError)
171
+ }
172
+ }
173
+ }
174
+
175
+ @objc(disconnectWithCompletion:)
176
+ public func disconnect(completion: @escaping @Sendable (NSError?) -> Void) {
177
+ Task {
178
+ do {
179
+ try await devices.disconnect()
180
+ completion(nil)
181
+ } catch {
182
+ completion(error as NSError)
183
+ }
184
+ }
185
+ }
186
+
187
+ @objc(isProvisionedWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
188
+ public func isProvisioned(
189
+ id: String,
190
+ serialNumber: String,
191
+ deviceType: String,
192
+ firmwareVersion: String,
193
+ hardwareRevision: String?,
194
+ isProvisioned: Bool,
195
+ connectionState: String,
196
+ mtu: Double,
197
+ completion: @escaping @Sendable (Bool, NSError?) -> Void
198
+ ) {
199
+ Task {
200
+ do {
201
+ completion(try await security.isProvisioned(Self.connectedDevice(
202
+ id: id,
203
+ serialNumber: serialNumber,
204
+ deviceType: deviceType,
205
+ firmwareVersion: firmwareVersion,
206
+ hardwareRevision: hardwareRevision,
207
+ isProvisioned: isProvisioned,
208
+ connectionState: connectionState,
209
+ mtu: mtu
210
+ )), nil)
211
+ } catch {
212
+ completion(false, error as NSError)
213
+ }
214
+ }
215
+ }
216
+
217
+ @objc(readPublicKeyWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
218
+ public func readPublicKey(
219
+ id: String,
220
+ serialNumber: String,
221
+ deviceType: String,
222
+ firmwareVersion: String,
223
+ hardwareRevision: String?,
224
+ isProvisioned: Bool,
225
+ connectionState: String,
226
+ mtu: Double,
227
+ completion: @escaping @Sendable (String?, NSError?) -> Void
228
+ ) {
229
+ Task {
230
+ do {
231
+ completion(try await security.readPublicKey(from: Self.connectedDevice(
232
+ id: id,
233
+ serialNumber: serialNumber,
234
+ deviceType: deviceType,
235
+ firmwareVersion: firmwareVersion,
236
+ hardwareRevision: hardwareRevision,
237
+ isProvisioned: isProvisioned,
238
+ connectionState: connectionState,
239
+ mtu: mtu
240
+ )), nil)
241
+ } catch {
242
+ completion(nil, error as NSError)
243
+ }
244
+ }
245
+ }
246
+
247
+ @objc(readAuthNonceWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
248
+ public func readAuthNonce(
249
+ id: String,
250
+ serialNumber: String,
251
+ deviceType: String,
252
+ firmwareVersion: String,
253
+ hardwareRevision: String?,
254
+ isProvisioned: Bool,
255
+ connectionState: String,
256
+ mtu: Double,
257
+ completion: @escaping @Sendable (String?, NSError?) -> Void
258
+ ) {
259
+ Task {
260
+ do {
261
+ completion(try await security.readAuthNonce(from: Self.connectedDevice(
262
+ id: id,
263
+ serialNumber: serialNumber,
264
+ deviceType: deviceType,
265
+ firmwareVersion: firmwareVersion,
266
+ hardwareRevision: hardwareRevision,
267
+ isProvisioned: isProvisioned,
268
+ connectionState: connectionState,
269
+ mtu: mtu
270
+ )), nil)
271
+ } catch {
272
+ completion(nil, error as NSError)
273
+ }
274
+ }
275
+ }
276
+
277
+ @objc(setApiEndpointWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:environment:completion:)
278
+ public func setAPIEndpoint(
279
+ id: String,
280
+ serialNumber: String,
281
+ deviceType: String,
282
+ firmwareVersion: String,
283
+ hardwareRevision: String?,
284
+ isProvisioned: Bool,
285
+ connectionState: String,
286
+ mtu: Double,
287
+ environment: String,
288
+ completion: @escaping @Sendable (NSError?) -> Void
289
+ ) {
290
+ Task {
291
+ do {
292
+ try await security.setAPIEndpoint(
293
+ try Self.environment(environment),
294
+ on: Self.connectedDevice(
295
+ id: id,
296
+ serialNumber: serialNumber,
297
+ deviceType: deviceType,
298
+ firmwareVersion: firmwareVersion,
299
+ hardwareRevision: hardwareRevision,
300
+ isProvisioned: isProvisioned,
301
+ connectionState: connectionState,
302
+ mtu: mtu
303
+ )
304
+ )
305
+ completion(nil)
306
+ } catch {
307
+ completion(error as NSError)
308
+ }
309
+ }
310
+ }
311
+
312
+ @objc(deliverCertificateWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:certificatePem:privateKeyPem:completion:)
313
+ public func deliverCertificate(
314
+ id: String,
315
+ serialNumber: String,
316
+ deviceType: String,
317
+ firmwareVersion: String,
318
+ hardwareRevision: String?,
319
+ isProvisioned: Bool,
320
+ connectionState: String,
321
+ mtu: Double,
322
+ certificatePEM: String,
323
+ privateKeyPEM: String,
324
+ completion: @escaping @Sendable (NSError?) -> Void
325
+ ) {
326
+ Task {
327
+ do {
328
+ try await security.deliverCertificate(
329
+ certificatePEM,
330
+ privateKeyPEM: privateKeyPEM,
331
+ to: Self.connectedDevice(
332
+ id: id,
333
+ serialNumber: serialNumber,
334
+ deviceType: deviceType,
335
+ firmwareVersion: firmwareVersion,
336
+ hardwareRevision: hardwareRevision,
337
+ isProvisioned: isProvisioned,
338
+ connectionState: connectionState,
339
+ mtu: mtu
340
+ )
341
+ )
342
+ completion(nil)
343
+ } catch {
344
+ completion(error as NSError)
345
+ }
346
+ }
347
+ }
348
+
349
+ @objc(deliverBackendPublicKeyWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:publicKeyHex:completion:)
350
+ public func deliverBackendPublicKey(
351
+ id: String,
352
+ serialNumber: String,
353
+ deviceType: String,
354
+ firmwareVersion: String,
355
+ hardwareRevision: String?,
356
+ isProvisioned: Bool,
357
+ connectionState: String,
358
+ mtu: Double,
359
+ publicKeyHex: String,
360
+ completion: @escaping @Sendable (NSError?) -> Void
361
+ ) {
362
+ Task {
363
+ do {
364
+ try await security.deliverBackendPublicKey(
365
+ try Self.hexData(publicKeyHex),
366
+ to: Self.connectedDevice(
367
+ id: id,
368
+ serialNumber: serialNumber,
369
+ deviceType: deviceType,
370
+ firmwareVersion: firmwareVersion,
371
+ hardwareRevision: hardwareRevision,
372
+ isProvisioned: isProvisioned,
373
+ connectionState: connectionState,
374
+ mtu: mtu
375
+ )
376
+ )
377
+ completion(nil)
378
+ } catch {
379
+ completion(error as NSError)
380
+ }
381
+ }
382
+ }
383
+
384
+ @objc(writeGrantWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:grantBlob:completion:)
385
+ public func writeGrant(
386
+ id: String,
387
+ serialNumber: String,
388
+ deviceType: String,
389
+ firmwareVersion: String,
390
+ hardwareRevision: String?,
391
+ isProvisioned: Bool,
392
+ connectionState: String,
393
+ mtu: Double,
394
+ grantBlob: String,
395
+ completion: @escaping @Sendable (NSError?) -> Void
396
+ ) {
397
+ Task {
398
+ do {
399
+ try await security.writeGrant(
400
+ grantBlob,
401
+ to: Self.connectedDevice(
402
+ id: id,
403
+ serialNumber: serialNumber,
404
+ deviceType: deviceType,
405
+ firmwareVersion: firmwareVersion,
406
+ hardwareRevision: hardwareRevision,
407
+ isProvisioned: isProvisioned,
408
+ connectionState: connectionState,
409
+ mtu: mtu
410
+ )
411
+ )
412
+ completion(nil)
413
+ } catch {
414
+ completion(error as NSError)
415
+ }
416
+ }
417
+ }
418
+
419
+ @objc(syncTimeWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
420
+ public func syncTime(
421
+ id: String,
422
+ serialNumber: String,
423
+ deviceType: String,
424
+ firmwareVersion: String,
425
+ hardwareRevision: String?,
426
+ isProvisioned: Bool,
427
+ connectionState: String,
428
+ mtu: Double,
429
+ completion: @escaping @Sendable (NSError?) -> Void
430
+ ) {
431
+ Task {
432
+ do {
433
+ try await security.syncTime(Self.connectedDevice(
434
+ id: id,
435
+ serialNumber: serialNumber,
436
+ deviceType: deviceType,
437
+ firmwareVersion: firmwareVersion,
438
+ hardwareRevision: hardwareRevision,
439
+ isProvisioned: isProvisioned,
440
+ connectionState: connectionState,
441
+ mtu: mtu
442
+ ))
443
+ completion(nil)
444
+ } catch {
445
+ completion(error as NSError)
446
+ }
447
+ }
448
+ }
449
+
450
+ @objc(requestStartRecordingWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:grantBlob:completion:)
451
+ public func requestStartRecording(
452
+ id: String,
453
+ serialNumber: String,
454
+ deviceType: String,
455
+ firmwareVersion: String,
456
+ hardwareRevision: String?,
457
+ isProvisioned: Bool,
458
+ connectionState: String,
459
+ mtu: Double,
460
+ grantBlob: String,
461
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
462
+ ) {
463
+ Task {
464
+ do {
465
+ let result = try await security.requestStartRecording(
466
+ Self.connectedDevice(
467
+ id: id,
468
+ serialNumber: serialNumber,
469
+ deviceType: deviceType,
470
+ firmwareVersion: firmwareVersion,
471
+ hardwareRevision: hardwareRevision,
472
+ isProvisioned: isProvisioned,
473
+ connectionState: connectionState,
474
+ mtu: mtu
475
+ ),
476
+ grantBlob: grantBlob
477
+ )
478
+ completion(Self.recordingControlResult(result), nil)
479
+ } catch {
480
+ completion(nil, error as NSError)
481
+ }
482
+ }
483
+ }
484
+
485
+ @objc(requestStopRecordingWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:grantBlob:completion:)
486
+ public func requestStopRecording(
487
+ id: String,
488
+ serialNumber: String,
489
+ deviceType: String,
490
+ firmwareVersion: String,
491
+ hardwareRevision: String?,
492
+ isProvisioned: Bool,
493
+ connectionState: String,
494
+ mtu: Double,
495
+ grantBlob: String,
496
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
497
+ ) {
498
+ Task {
499
+ do {
500
+ let result = try await security.requestStopRecording(
501
+ Self.connectedDevice(
502
+ id: id,
503
+ serialNumber: serialNumber,
504
+ deviceType: deviceType,
505
+ firmwareVersion: firmwareVersion,
506
+ hardwareRevision: hardwareRevision,
507
+ isProvisioned: isProvisioned,
508
+ connectionState: connectionState,
509
+ mtu: mtu
510
+ ),
511
+ grantBlob: grantBlob
512
+ )
513
+ completion(Self.recordingControlResult(result), nil)
514
+ } catch {
515
+ completion(nil, error as NSError)
516
+ }
517
+ }
518
+ }
519
+
520
+ @objc(readRecordingStateWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
521
+ public func readRecordingState(
522
+ id: String,
523
+ serialNumber: String,
524
+ deviceType: String,
525
+ firmwareVersion: String,
526
+ hardwareRevision: String?,
527
+ isProvisioned: Bool,
528
+ connectionState: String,
529
+ mtu: Double,
530
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
531
+ ) {
532
+ Task {
533
+ do {
534
+ let state = try await security.readRecordingState(from: Self.connectedDevice(
535
+ id: id,
536
+ serialNumber: serialNumber,
537
+ deviceType: deviceType,
538
+ firmwareVersion: firmwareVersion,
539
+ hardwareRevision: hardwareRevision,
540
+ isProvisioned: isProvisioned,
541
+ connectionState: connectionState,
542
+ mtu: mtu
543
+ ))
544
+ completion(Self.recordingState(state), nil)
545
+ } catch {
546
+ completion(nil, error as NSError)
547
+ }
548
+ }
549
+ }
550
+
551
+ @objc(startRecordingStateUpdatesWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:onState:onError:completion:)
552
+ public func startRecordingStateUpdates(
553
+ id: String,
554
+ serialNumber: String,
555
+ deviceType: String,
556
+ firmwareVersion: String,
557
+ hardwareRevision: String?,
558
+ isProvisioned: Bool,
559
+ connectionState: String,
560
+ mtu: Double,
561
+ onState: @escaping @Sendable ([String: Any]) -> Void,
562
+ onError: @escaping @Sendable (NSError) -> Void,
563
+ completion: @escaping @Sendable (NSError?) -> Void
564
+ ) {
565
+ Task {
566
+ do {
567
+ try await security.startRecordingStateUpdates(
568
+ Self.connectedDevice(
569
+ id: id,
570
+ serialNumber: serialNumber,
571
+ deviceType: deviceType,
572
+ firmwareVersion: firmwareVersion,
573
+ hardwareRevision: hardwareRevision,
574
+ isProvisioned: isProvisioned,
575
+ connectionState: connectionState,
576
+ mtu: mtu
577
+ ),
578
+ onState: { onState(Self.recordingState($0)) },
579
+ onError: { onError($0 as NSError) }
580
+ )
581
+ completion(nil)
582
+ } catch {
583
+ completion(error as NSError)
584
+ }
585
+ }
586
+ }
587
+
588
+ @objc(stopRecordingStateUpdatesWithCompletion:)
589
+ public func stopRecordingStateUpdates(completion: @escaping @Sendable () -> Void) {
590
+ Task {
591
+ await security.stopRecordingStateUpdates()
592
+ completion()
593
+ }
594
+ }
595
+
596
+ @objc(configureWiFiWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:ssid:password:grantBlob:completion:)
597
+ public func configureWiFi(
598
+ id: String,
599
+ serialNumber: String,
600
+ deviceType: String,
601
+ firmwareVersion: String,
602
+ hardwareRevision: String?,
603
+ isProvisioned: Bool,
604
+ connectionState: String,
605
+ mtu: Double,
606
+ ssid: String,
607
+ password: String,
608
+ grantBlob: String,
609
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
610
+ ) {
611
+ Task {
612
+ do {
613
+ let result = try await wifi.configure(
614
+ Self.connectedDevice(
615
+ id: id,
616
+ serialNumber: serialNumber,
617
+ deviceType: deviceType,
618
+ firmwareVersion: firmwareVersion,
619
+ hardwareRevision: hardwareRevision,
620
+ isProvisioned: isProvisioned,
621
+ connectionState: connectionState,
622
+ mtu: mtu
623
+ ),
624
+ ssid: ssid,
625
+ password: password,
626
+ grantBlob: grantBlob
627
+ )
628
+ completion(Self.wifiConfigResult(result), nil)
629
+ } catch {
630
+ completion(nil, error as NSError)
631
+ }
632
+ }
633
+ }
634
+
635
+ @objc(disconnectWiFiWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
636
+ public func disconnectWiFi(
637
+ id: String,
638
+ serialNumber: String,
639
+ deviceType: String,
640
+ firmwareVersion: String,
641
+ hardwareRevision: String?,
642
+ isProvisioned: Bool,
643
+ connectionState: String,
644
+ mtu: Double,
645
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
646
+ ) {
647
+ Task {
648
+ do {
649
+ let result = try await wifi.disconnect(Self.connectedDevice(
650
+ id: id,
651
+ serialNumber: serialNumber,
652
+ deviceType: deviceType,
653
+ firmwareVersion: firmwareVersion,
654
+ hardwareRevision: hardwareRevision,
655
+ isProvisioned: isProvisioned,
656
+ connectionState: connectionState,
657
+ mtu: mtu
658
+ ))
659
+ completion(Self.wifiConfigResult(result), nil)
660
+ } catch {
661
+ completion(nil, error as NSError)
662
+ }
663
+ }
664
+ }
665
+
666
+ @objc(readWiFiStatusWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
667
+ public func readWiFiStatus(
668
+ id: String,
669
+ serialNumber: String,
670
+ deviceType: String,
671
+ firmwareVersion: String,
672
+ hardwareRevision: String?,
673
+ isProvisioned: Bool,
674
+ connectionState: String,
675
+ mtu: Double,
676
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
677
+ ) {
678
+ Task {
679
+ do {
680
+ let status = try await wifi.readStatus(Self.connectedDevice(
681
+ id: id,
682
+ serialNumber: serialNumber,
683
+ deviceType: deviceType,
684
+ firmwareVersion: firmwareVersion,
685
+ hardwareRevision: hardwareRevision,
686
+ isProvisioned: isProvisioned,
687
+ connectionState: connectionState,
688
+ mtu: mtu
689
+ ))
690
+ completion(Self.wifiStatusInfo(status), nil)
691
+ } catch {
692
+ completion(nil, error as NSError)
693
+ }
694
+ }
695
+ }
696
+
697
+ @objc(startWiFiStatusUpdatesWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:onStatus:onError:completion:)
698
+ public func startWiFiStatusUpdates(
699
+ id: String,
700
+ serialNumber: String,
701
+ deviceType: String,
702
+ firmwareVersion: String,
703
+ hardwareRevision: String?,
704
+ isProvisioned: Bool,
705
+ connectionState: String,
706
+ mtu: Double,
707
+ onStatus: @escaping @Sendable ([String: Any]) -> Void,
708
+ onError: @escaping @Sendable (NSError) -> Void,
709
+ completion: @escaping @Sendable (NSError?) -> Void
710
+ ) {
711
+ Task {
712
+ do {
713
+ try await wifi.startStatusUpdates(
714
+ Self.connectedDevice(
715
+ id: id,
716
+ serialNumber: serialNumber,
717
+ deviceType: deviceType,
718
+ firmwareVersion: firmwareVersion,
719
+ hardwareRevision: hardwareRevision,
720
+ isProvisioned: isProvisioned,
721
+ connectionState: connectionState,
722
+ mtu: mtu
723
+ ),
724
+ onStatus: { onStatus(Self.wifiStatusInfo($0)) },
725
+ onError: { onError($0 as NSError) }
726
+ )
727
+ completion(nil)
728
+ } catch {
729
+ completion(error as NSError)
730
+ }
731
+ }
732
+ }
733
+
734
+ @objc(stopWiFiStatusUpdatesWithCompletion:)
735
+ public func stopWiFiStatusUpdates(completion: @escaping @Sendable () -> Void) {
736
+ Task {
737
+ await wifi.stopStatusUpdates()
738
+ completion()
739
+ }
740
+ }
741
+
742
+ @objc(scanWiFiNetworksWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
743
+ public func scanWiFiNetworks(
744
+ id: String,
745
+ serialNumber: String,
746
+ deviceType: String,
747
+ firmwareVersion: String,
748
+ hardwareRevision: String?,
749
+ isProvisioned: Bool,
750
+ connectionState: String,
751
+ mtu: Double,
752
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
753
+ ) {
754
+ Task {
755
+ do {
756
+ let result = try await wifi.scanNetworks(Self.connectedDevice(
757
+ id: id,
758
+ serialNumber: serialNumber,
759
+ deviceType: deviceType,
760
+ firmwareVersion: firmwareVersion,
761
+ hardwareRevision: hardwareRevision,
762
+ isProvisioned: isProvisioned,
763
+ connectionState: connectionState,
764
+ mtu: mtu
765
+ ))
766
+ completion(Self.wifiScanResult(result), nil)
767
+ } catch {
768
+ completion(nil, error as NSError)
769
+ }
770
+ }
771
+ }
772
+
773
+ @objc(listRecordingsWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
774
+ public func listRecordings(
775
+ id: String,
776
+ serialNumber: String,
777
+ deviceType: String,
778
+ firmwareVersion: String,
779
+ hardwareRevision: String?,
780
+ isProvisioned: Bool,
781
+ connectionState: String,
782
+ mtu: Double,
783
+ completion: @escaping @Sendable ([[String: Any]]?, NSError?) -> Void
784
+ ) {
785
+ Task {
786
+ do {
787
+ let values = try await recordings.listRecordings(Self.connectedDevice(
788
+ id: id,
789
+ serialNumber: serialNumber,
790
+ deviceType: deviceType,
791
+ firmwareVersion: firmwareVersion,
792
+ hardwareRevision: hardwareRevision,
793
+ isProvisioned: isProvisioned,
794
+ connectionState: connectionState,
795
+ mtu: mtu
796
+ ))
797
+ completion(values.map(Self.recording), nil)
798
+ } catch {
799
+ completion(nil, error as NSError)
800
+ }
801
+ }
802
+ }
803
+
804
+ @objc(syncRecordingWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:recordingUUID:startedAtMilliseconds:durationMilliseconds:fileSize:codec:isEncrypted:sinkID:onProgress:completion:)
805
+ public func syncRecording(
806
+ id: String,
807
+ serialNumber: String,
808
+ deviceType: String,
809
+ firmwareVersion: String,
810
+ hardwareRevision: String?,
811
+ isProvisioned: Bool,
812
+ connectionState: String,
813
+ mtu: Double,
814
+ recordingUUID: String,
815
+ startedAtMilliseconds: Double,
816
+ durationMilliseconds: Double,
817
+ fileSize: Double,
818
+ codec: String,
819
+ isEncrypted: Bool,
820
+ sinkID: String,
821
+ onProgress: @escaping @Sendable ([String: Any]) -> Void,
822
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
823
+ ) {
824
+ Task {
825
+ do {
826
+ let result = try await recordings.syncRecording(
827
+ Self.connectedDevice(
828
+ id: id,
829
+ serialNumber: serialNumber,
830
+ deviceType: deviceType,
831
+ firmwareVersion: firmwareVersion,
832
+ hardwareRevision: hardwareRevision,
833
+ isProvisioned: isProvisioned,
834
+ connectionState: connectionState,
835
+ mtu: mtu
836
+ ),
837
+ recording: DeviceRecording(
838
+ uuid: recordingUUID,
839
+ startedAt: Date(
840
+ timeIntervalSince1970:
841
+ Double(try Self.unsignedInteger(startedAtMilliseconds)) / 1_000
842
+ ),
843
+ durationMs: try Self.unsignedInteger(durationMilliseconds),
844
+ fileSizeBytes: try Self.unsignedInteger(fileSize),
845
+ codec: .known(Self.audioCodec(codec)),
846
+ isEncrypted: isEncrypted
847
+ ),
848
+ sinkID: sinkID
849
+ ) { progress in
850
+ onProgress([
851
+ "completedUnits": progress.completedBytes,
852
+ "totalUnits": progress.totalBytes,
853
+ ])
854
+ }
855
+ var value: [String: Any] = [
856
+ "localPath": result.localPath,
857
+ "e2eEncrypted": result.isE2EEncrypted,
858
+ ]
859
+ if let contentSHA256Hex = result.contentSHA256Hex {
860
+ value["contentSha256"] = contentSHA256Hex
861
+ }
862
+ completion(value, nil)
863
+ } catch {
864
+ completion(nil, error as NSError)
865
+ }
866
+ }
867
+ }
868
+
869
+ @objc(syncEncryptedRecordingV2WithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:recordingUUID:generation:ciphertextLength:ciphertextSHA256:operationID:onProfileRequest:onProgress:completion:)
870
+ public func syncEncryptedRecordingV2(
871
+ id: String,
872
+ serialNumber: String,
873
+ deviceType: String,
874
+ firmwareVersion: String,
875
+ hardwareRevision: String?,
876
+ isProvisioned: Bool,
877
+ connectionState: String,
878
+ mtu: Double,
879
+ recordingUUID: String,
880
+ generation: Double,
881
+ ciphertextLength: String,
882
+ ciphertextSHA256: String,
883
+ operationID: String,
884
+ onProfileRequest: @escaping @Sendable ([String: Any]) -> Void,
885
+ onProgress: @escaping @Sendable ([String: Any]) -> Void,
886
+ completion: @escaping @Sendable (NSError?) -> Void
887
+ ) {
888
+ Task {
889
+ do {
890
+ guard let generationValue = UInt32(
891
+ exactly: try Self.unsignedInteger(generation)
892
+ ),
893
+ let length = UInt64(ciphertextLength)
894
+ else {
895
+ throw BotaDeviceSDKAppleBridgeInputError.invalidUnsignedInteger
896
+ }
897
+ let digest = try Self.sha256Data(ciphertextSHA256)
898
+ try await recordings.syncEncryptedRecordingV2(
899
+ Self.connectedDevice(
900
+ id: id,
901
+ serialNumber: serialNumber,
902
+ deviceType: deviceType,
903
+ firmwareVersion: firmwareVersion,
904
+ hardwareRevision: hardwareRevision,
905
+ isProvisioned: isProvisioned,
906
+ connectionState: connectionState,
907
+ mtu: mtu
908
+ ),
909
+ recording: .init(
910
+ uuid: recordingUUID,
911
+ generation: generationValue,
912
+ ciphertextLength: length,
913
+ ciphertextSHA256: digest
914
+ ),
915
+ operationID: operationID,
916
+ onProfileRequest: onProfileRequest,
917
+ onProgress: onProgress
918
+ )
919
+ completion(nil)
920
+ } catch {
921
+ completion(error as NSError)
922
+ }
923
+ }
924
+ }
925
+
926
+ @objc(resolveEncryptedUploadV2ProfileWithRequestID:profile:uploadSessionID:ownerRevision:securityPolicy:materialRegistrationID:completion:)
927
+ public func resolveEncryptedUploadV2Profile(
928
+ requestID: String,
929
+ profile: String,
930
+ uploadSessionID: String,
931
+ ownerRevision: Double,
932
+ securityPolicy: String,
933
+ materialRegistrationID: String,
934
+ completion: @escaping @Sendable (NSError?) -> Void
935
+ ) {
936
+ Task {
937
+ do {
938
+ guard let revision = UInt32(
939
+ exactly: try Self.unsignedInteger(ownerRevision)
940
+ ) else {
941
+ throw BotaDeviceSDKAppleBridgeInputError.invalidUnsignedInteger
942
+ }
943
+ try await recordings.resolveEncryptedUploadV2Profile(
944
+ requestID: requestID,
945
+ profile: profile,
946
+ uploadSessionID: uploadSessionID,
947
+ ownerRevision: revision,
948
+ securityPolicy: securityPolicy,
949
+ materialRegistrationID: materialRegistrationID
950
+ )
951
+ completion(nil)
952
+ } catch {
953
+ completion(error as NSError)
954
+ }
955
+ }
956
+ }
957
+
958
+ @objc(rejectEncryptedUploadV2ProfileWithRequestID:errorCode:completion:)
959
+ public func rejectEncryptedUploadV2Profile(
960
+ requestID: String,
961
+ errorCode: String,
962
+ completion: @escaping @Sendable (NSError?) -> Void
963
+ ) {
964
+ Task {
965
+ do {
966
+ try await recordings.rejectEncryptedUploadV2Profile(
967
+ requestID: requestID,
968
+ errorCode: errorCode
969
+ )
970
+ completion(nil)
971
+ } catch {
972
+ completion(error as NSError)
973
+ }
974
+ }
975
+ }
976
+
977
+ @objc(startStreamingWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:sessionID:recordingUUID:recordingID:chunkSizeBytes:flushIntervalMilliseconds:onProgress:onDestinationRequest:onFinalizeRequest:completion:)
978
+ public func startStreaming(
979
+ id: String,
980
+ serialNumber: String,
981
+ deviceType: String,
982
+ firmwareVersion: String,
983
+ hardwareRevision: String?,
984
+ isProvisioned: Bool,
985
+ connectionState: String,
986
+ mtu: Double,
987
+ sessionID: String,
988
+ recordingUUID: String,
989
+ recordingID _: String,
990
+ chunkSizeBytes: Double,
991
+ flushIntervalMilliseconds: Double,
992
+ onProgress: @escaping @Sendable ([String: Any]) -> Void,
993
+ onDestinationRequest: @escaping @Sendable ([String: Any]) -> Void,
994
+ onFinalizeRequest: @escaping @Sendable ([String: Any]) -> Void,
995
+ completion: @escaping @Sendable (Double, NSError?) -> Void
996
+ ) {
997
+ Task {
998
+ do {
999
+ let totalBytes = try await recordings.streamRecording(
1000
+ Self.connectedDevice(
1001
+ id: id,
1002
+ serialNumber: serialNumber,
1003
+ deviceType: deviceType,
1004
+ firmwareVersion: firmwareVersion,
1005
+ hardwareRevision: hardwareRevision,
1006
+ isProvisioned: isProvisioned,
1007
+ connectionState: connectionState,
1008
+ mtu: mtu
1009
+ ),
1010
+ recordingUUID: recordingUUID,
1011
+ sessionID: sessionID,
1012
+ chunkSizeBytes: Int(try Self.unsignedInteger(chunkSizeBytes)),
1013
+ flushIntervalMilliseconds: try Self.unsignedInteger(
1014
+ flushIntervalMilliseconds
1015
+ ),
1016
+ onProgress: onProgress,
1017
+ onDestinationRequest: onDestinationRequest,
1018
+ onFinalizeRequest: onFinalizeRequest
1019
+ )
1020
+ completion(Double(totalBytes), nil)
1021
+ } catch {
1022
+ completion(0, error as NSError)
1023
+ }
1024
+ }
1025
+ }
1026
+
1027
+ @objc(abortStreamingWithSessionID:completion:)
1028
+ public func abortStreaming(
1029
+ sessionID: String,
1030
+ completion: @escaping @Sendable () -> Void
1031
+ ) {
1032
+ Task {
1033
+ await recordings.abortStreaming(sessionID: sessionID)
1034
+ completion()
1035
+ }
1036
+ }
1037
+
1038
+ @objc(resolveStreamingDestinationWithRequestID:url:method:contentType:bearerToken:completion:)
1039
+ public func resolveStreamingDestination(
1040
+ requestID: String,
1041
+ url: String,
1042
+ method: String,
1043
+ contentType: String,
1044
+ bearerToken: String?,
1045
+ completion: @escaping @Sendable () -> Void
1046
+ ) {
1047
+ Task {
1048
+ await recordings.resolveStreamingDestination(
1049
+ requestID: requestID,
1050
+ url: url,
1051
+ method: method,
1052
+ contentType: contentType,
1053
+ bearerToken: bearerToken
1054
+ )
1055
+ completion()
1056
+ }
1057
+ }
1058
+
1059
+ @objc(rejectStreamingDestinationWithRequestID:message:completion:)
1060
+ public func rejectStreamingDestination(
1061
+ requestID: String,
1062
+ message: String,
1063
+ completion: @escaping @Sendable () -> Void
1064
+ ) {
1065
+ Task {
1066
+ await recordings.rejectStreamingDestination(requestID: requestID, message: message)
1067
+ completion()
1068
+ }
1069
+ }
1070
+
1071
+ @objc(resolveStreamingFinalizeWithRequestID:completion:)
1072
+ public func resolveStreamingFinalize(
1073
+ requestID: String,
1074
+ completion: @escaping @Sendable () -> Void
1075
+ ) {
1076
+ Task {
1077
+ await recordings.resolveStreamingFinalize(requestID: requestID)
1078
+ completion()
1079
+ }
1080
+ }
1081
+
1082
+ @objc(rejectStreamingFinalizeWithRequestID:message:completion:)
1083
+ public func rejectStreamingFinalize(
1084
+ requestID: String,
1085
+ message: String,
1086
+ completion: @escaping @Sendable () -> Void
1087
+ ) {
1088
+ Task {
1089
+ await recordings.rejectStreamingFinalize(requestID: requestID, message: message)
1090
+ completion()
1091
+ }
1092
+ }
1093
+
1094
+ @objc(confirmRecordingWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:recordingUUID:completion:)
1095
+ public func confirmRecording(
1096
+ id: String,
1097
+ serialNumber: String,
1098
+ deviceType: String,
1099
+ firmwareVersion: String,
1100
+ hardwareRevision: String?,
1101
+ isProvisioned: Bool,
1102
+ connectionState: String,
1103
+ mtu: Double,
1104
+ recordingUUID: String,
1105
+ completion: @escaping @Sendable (NSError?) -> Void
1106
+ ) {
1107
+ Task {
1108
+ do {
1109
+ try await recordings.confirmRecording(
1110
+ Self.connectedDevice(
1111
+ id: id,
1112
+ serialNumber: serialNumber,
1113
+ deviceType: deviceType,
1114
+ firmwareVersion: firmwareVersion,
1115
+ hardwareRevision: hardwareRevision,
1116
+ isProvisioned: isProvisioned,
1117
+ connectionState: connectionState,
1118
+ mtu: mtu
1119
+ ),
1120
+ recordingUUID: recordingUUID
1121
+ )
1122
+ completion(nil)
1123
+ } catch {
1124
+ completion(error as NSError)
1125
+ }
1126
+ }
1127
+ }
1128
+
1129
+ @objc(uploadRecordingFileWithTaskID:recordingID:localPath:uploadURL:uploadToken:completeURL:contentType:contentSHA256:relayURL:relayBearerToken:onProgress:completion:)
1130
+ public func uploadRecordingFile(
1131
+ taskID: String,
1132
+ recordingID: String,
1133
+ localPath: String,
1134
+ uploadURL: String,
1135
+ uploadToken: String?,
1136
+ completeURL: String?,
1137
+ contentType: String?,
1138
+ contentSHA256: String?,
1139
+ relayURL: String?,
1140
+ relayBearerToken: String?,
1141
+ onProgress: @escaping @Sendable ([String: Any]) -> Void,
1142
+ completion: @escaping @Sendable (NSError?) -> Void
1143
+ ) {
1144
+ Task {
1145
+ do {
1146
+ try await recordingUploads.upload(.init(
1147
+ taskID: taskID,
1148
+ recordingID: recordingID,
1149
+ localPath: localPath,
1150
+ uploadURL: uploadURL,
1151
+ uploadToken: uploadToken,
1152
+ completeURL: completeURL,
1153
+ contentType: contentType,
1154
+ contentSHA256: contentSHA256,
1155
+ relayURL: relayURL,
1156
+ relayBearerToken: relayBearerToken
1157
+ )) { progress in
1158
+ onProgress([
1159
+ "taskId": progress.taskID,
1160
+ "completedBytes": progress.completedBytes,
1161
+ "totalBytes": progress.totalBytes,
1162
+ ])
1163
+ }
1164
+ completion(nil)
1165
+ } catch {
1166
+ completion(error as NSError)
1167
+ }
1168
+ }
1169
+ }
1170
+
1171
+ @objc(cancelRecordingUploadWithTaskID:completion:)
1172
+ public func cancelRecordingUpload(
1173
+ taskID: String,
1174
+ completion: @escaping @Sendable () -> Void
1175
+ ) {
1176
+ Task {
1177
+ await recordingUploads.cancel(taskID: taskID)
1178
+ completion()
1179
+ }
1180
+ }
1181
+
1182
+ @objc(loadCompatibilityUploadQueueWithCompletion:)
1183
+ public func loadCompatibilityUploadQueue(
1184
+ completion: @escaping @Sendable (String?, NSError?) -> Void
1185
+ ) {
1186
+ Task {
1187
+ do {
1188
+ completion(try await recordingUploads.loadQueue(), nil)
1189
+ } catch {
1190
+ completion(nil, error as NSError)
1191
+ }
1192
+ }
1193
+ }
1194
+
1195
+ @objc(saveCompatibilityUploadQueueWithSerializedTasks:completion:)
1196
+ public func saveCompatibilityUploadQueue(
1197
+ serializedTasks: String,
1198
+ completion: @escaping @Sendable (NSError?) -> Void
1199
+ ) {
1200
+ Task {
1201
+ do {
1202
+ try await recordingUploads.saveQueue(serializedTasks)
1203
+ completion(nil)
1204
+ } catch {
1205
+ completion(error as NSError)
1206
+ }
1207
+ }
1208
+ }
1209
+
1210
+ @objc(stopAllRecordingOperationsWithCompletion:)
1211
+ public func stopAllRecordingOperations(completion: @escaping @Sendable () -> Void) {
1212
+ Task {
1213
+ await recordingUploads.cancelAll()
1214
+ await recordings.cancelAll()
1215
+ completion()
1216
+ }
1217
+ }
1218
+
1219
+ @objc(observeUploadOwnershipWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:recordingUUID:uploadID:destinationID:onProgress:completion:)
1220
+ public func observeUploadOwnership(
1221
+ id: String,
1222
+ serialNumber: String,
1223
+ deviceType: String,
1224
+ firmwareVersion: String,
1225
+ hardwareRevision: String?,
1226
+ isProvisioned: Bool,
1227
+ connectionState: String,
1228
+ mtu: Double,
1229
+ recordingUUID: String,
1230
+ uploadID: String,
1231
+ destinationID: String,
1232
+ onProgress: @escaping @Sendable ([String: Any]) -> Void,
1233
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
1234
+ ) {
1235
+ Task {
1236
+ do {
1237
+ let result = try await recordings.observeUploadOwnership(
1238
+ Self.connectedDevice(
1239
+ id: id,
1240
+ serialNumber: serialNumber,
1241
+ deviceType: deviceType,
1242
+ firmwareVersion: firmwareVersion,
1243
+ hardwareRevision: hardwareRevision,
1244
+ isProvisioned: isProvisioned,
1245
+ connectionState: connectionState,
1246
+ mtu: mtu
1247
+ ),
1248
+ recordingUUID: recordingUUID,
1249
+ uploadID: uploadID,
1250
+ destinationID: destinationID
1251
+ ) { progress in
1252
+ onProgress([
1253
+ "completedUnits": progress.completedBytes,
1254
+ "totalUnits": progress.totalBytes,
1255
+ ])
1256
+ }
1257
+ completion(Self.uploadOwnershipResult(result), nil)
1258
+ } catch {
1259
+ completion(nil, error as NSError)
1260
+ }
1261
+ }
1262
+ }
1263
+
1264
+ @objc(updateFirmwareWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:version:sizeUnits:crc32:url:onProgress:completion:)
1265
+ public func updateFirmware(
1266
+ id: String,
1267
+ serialNumber: String,
1268
+ deviceType: String,
1269
+ firmwareVersion: String,
1270
+ hardwareRevision: String?,
1271
+ isProvisioned: Bool,
1272
+ connectionState: String,
1273
+ mtu: Double,
1274
+ version: String,
1275
+ sizeUnits: Double,
1276
+ crc32: Double,
1277
+ url: String,
1278
+ onProgress: @escaping @Sendable ([String: Any]) -> Void,
1279
+ completion: @escaping @Sendable (NSError?) -> Void
1280
+ ) {
1281
+ Task {
1282
+ do {
1283
+ try await ota.updateFirmware(
1284
+ Self.connectedDevice(
1285
+ id: id,
1286
+ serialNumber: serialNumber,
1287
+ deviceType: deviceType,
1288
+ firmwareVersion: firmwareVersion,
1289
+ hardwareRevision: hardwareRevision,
1290
+ isProvisioned: isProvisioned,
1291
+ connectionState: connectionState,
1292
+ mtu: mtu
1293
+ ),
1294
+ version: version,
1295
+ sizeBytes: try Self.unsigned32(sizeUnits),
1296
+ crc32: try Self.unsigned32(crc32),
1297
+ url: url
1298
+ ) { progress in
1299
+ onProgress([
1300
+ "phase": Self.firmwarePhase(progress.phase),
1301
+ "completedUnits": progress.completedBytes,
1302
+ "totalUnits": progress.totalBytes,
1303
+ ])
1304
+ }
1305
+ completion(nil)
1306
+ } catch {
1307
+ completion(error as NSError)
1308
+ }
1309
+ }
1310
+ }
1311
+
1312
+ @objc(cancelFirmwareUpdateWithCompletion:)
1313
+ public func cancelFirmwareUpdate(completion: @escaping @Sendable () -> Void) {
1314
+ Task {
1315
+ await ota.cancelAll()
1316
+ completion()
1317
+ }
1318
+ }
1319
+
1320
+ @objc(startDeviceLogsWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:onLine:onError:completion:)
1321
+ public func startDeviceLogs(
1322
+ id: String,
1323
+ serialNumber: String,
1324
+ deviceType: String,
1325
+ firmwareVersion: String,
1326
+ hardwareRevision: String?,
1327
+ isProvisioned: Bool,
1328
+ connectionState: String,
1329
+ mtu: Double,
1330
+ onLine: @escaping @Sendable ([String: Any]) -> Void,
1331
+ onError: @escaping @Sendable (NSError) -> Void,
1332
+ completion: @escaping @Sendable (NSError?) -> Void
1333
+ ) {
1334
+ Task {
1335
+ do {
1336
+ try await logs.start(
1337
+ Self.connectedDevice(
1338
+ id: id,
1339
+ serialNumber: serialNumber,
1340
+ deviceType: deviceType,
1341
+ firmwareVersion: firmwareVersion,
1342
+ hardwareRevision: hardwareRevision,
1343
+ isProvisioned: isProvisioned,
1344
+ connectionState: connectionState,
1345
+ mtu: mtu
1346
+ ),
1347
+ onLine: {
1348
+ onLine([
1349
+ "message": $0.message,
1350
+ "isBacklog": $0.isBacklog,
1351
+ ])
1352
+ },
1353
+ onError: { onError($0 as NSError) }
1354
+ )
1355
+ completion(nil)
1356
+ } catch {
1357
+ completion(error as NSError)
1358
+ }
1359
+ }
1360
+ }
1361
+
1362
+ @objc(stopDeviceLogsWithCompletion:)
1363
+ public func stopDeviceLogs(completion: @escaping @Sendable () -> Void) {
1364
+ Task {
1365
+ await logs.stop()
1366
+ completion()
1367
+ }
1368
+ }
1369
+
1370
+ @objc(readStatusWithCompletion:)
1371
+ public func readStatus(
1372
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
1373
+ ) {
1374
+ Task {
1375
+ do {
1376
+ completion(Self.deviceStatus(try await devices.readStatus()), nil)
1377
+ } catch {
1378
+ completion(nil, error as NSError)
1379
+ }
1380
+ }
1381
+ }
1382
+
1383
+ @objc(startStatusUpdatesWithOnStatus:onError:completion:)
1384
+ public func startStatusUpdates(
1385
+ onStatus: @escaping @Sendable ([String: Any]) -> Void,
1386
+ onError: @escaping @Sendable (NSError) -> Void,
1387
+ completion: @escaping @Sendable (NSError?) -> Void
1388
+ ) {
1389
+ Task {
1390
+ do {
1391
+ try await devices.startStatusUpdates(
1392
+ onStatus: { onStatus(Self.deviceStatus($0)) },
1393
+ onError: { onError($0 as NSError) }
1394
+ )
1395
+ completion(nil)
1396
+ } catch {
1397
+ completion(error as NSError)
1398
+ }
1399
+ }
1400
+ }
1401
+
1402
+ @objc(stopStatusUpdatesWithCompletion:)
1403
+ public func stopStatusUpdates(completion: @escaping @Sendable () -> Void) {
1404
+ Task {
1405
+ await devices.stopStatusUpdates()
1406
+ completion()
1407
+ }
1408
+ }
1409
+
1410
+ @objc(provisionWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:onMaterialRequest:completion:)
1411
+ public func provision(
1412
+ id: String,
1413
+ serialNumber: String,
1414
+ deviceType: String,
1415
+ firmwareVersion: String,
1416
+ hardwareRevision: String?,
1417
+ isProvisioned: Bool,
1418
+ connectionState: String,
1419
+ mtu: Double,
1420
+ onMaterialRequest: @escaping @Sendable ([String: Any]) -> Void,
1421
+ completion: @escaping @Sendable (NSError?) -> Void
1422
+ ) {
1423
+ Task {
1424
+ do {
1425
+ let device = try Self.connectedDevice(
1426
+ id: id,
1427
+ serialNumber: serialNumber,
1428
+ deviceType: deviceType,
1429
+ firmwareVersion: firmwareVersion,
1430
+ hardwareRevision: hardwareRevision,
1431
+ isProvisioned: isProvisioned,
1432
+ connectionState: connectionState,
1433
+ mtu: mtu
1434
+ )
1435
+ try await security.provision(device) { request in
1436
+ onMaterialRequest([
1437
+ "requestId": request.requestID,
1438
+ "serialNumber": request.serialNumber,
1439
+ "nonce": request.nonce,
1440
+ "devicePublicKey": request.devicePublicKey,
1441
+ ])
1442
+ }
1443
+ completion(nil)
1444
+ } catch {
1445
+ completion(error as NSError)
1446
+ }
1447
+ }
1448
+ }
1449
+
1450
+ @objc(deprovisionWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:grantBlob:completion:)
1451
+ public func deprovision(
1452
+ id: String,
1453
+ serialNumber: String,
1454
+ deviceType: String,
1455
+ firmwareVersion: String,
1456
+ hardwareRevision: String?,
1457
+ isProvisioned: Bool,
1458
+ connectionState: String,
1459
+ mtu: Double,
1460
+ grantBlob: String,
1461
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
1462
+ ) {
1463
+ Task {
1464
+ do {
1465
+ let result = try await security.deprovision(Self.connectedDevice(
1466
+ id: id,
1467
+ serialNumber: serialNumber,
1468
+ deviceType: deviceType,
1469
+ firmwareVersion: firmwareVersion,
1470
+ hardwareRevision: hardwareRevision,
1471
+ isProvisioned: isProvisioned,
1472
+ connectionState: connectionState,
1473
+ mtu: mtu
1474
+ ), grantBlob: grantBlob)
1475
+ completion([
1476
+ "success": result.success,
1477
+ "error": result.error?.rawValue as Any,
1478
+ ], nil)
1479
+ } catch {
1480
+ completion(nil, error as NSError)
1481
+ }
1482
+ }
1483
+ }
1484
+
1485
+ @objc(writeConnectionSettingsWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:enabledWifi:enabledCellular:heartbeatWifi:heartbeatCellular:uploadNetworkPreference:wifiIdleTimeoutSeconds:cellularIdleTimeoutSeconds:streamingEnabled:streamingFlushIntervalSeconds:completion:)
1486
+ public func writeConnectionSettings(
1487
+ id: String,
1488
+ serialNumber: String,
1489
+ deviceType: String,
1490
+ firmwareVersion: String,
1491
+ hardwareRevision: String?,
1492
+ isProvisioned: Bool,
1493
+ connectionState: String,
1494
+ mtu: Double,
1495
+ enabledWifi: Bool,
1496
+ enabledCellular: Bool,
1497
+ heartbeatWifi: Bool,
1498
+ heartbeatCellular: Bool,
1499
+ uploadNetworkPreference: [String],
1500
+ wifiIdleTimeoutSeconds: Double,
1501
+ cellularIdleTimeoutSeconds: Double,
1502
+ streamingEnabled: Bool,
1503
+ streamingFlushIntervalSeconds: Double,
1504
+ completion: @escaping @Sendable (NSError?) -> Void
1505
+ ) {
1506
+ Task {
1507
+ do {
1508
+ let preference = try uploadNetworkPreference.map {
1509
+ guard let value = Self.connectionType($0) else {
1510
+ throw BotaDeviceSDKAppleBridgeInputError.invalidConnectionSettings
1511
+ }
1512
+ return value
1513
+ }
1514
+ let settings = DeviceConnectionSettings(
1515
+ enabledConnections: .init(
1516
+ wifi: enabledWifi,
1517
+ cellular: enabledCellular
1518
+ ),
1519
+ heartbeatEnabledConnections: .init(
1520
+ wifi: heartbeatWifi,
1521
+ cellular: heartbeatCellular
1522
+ ),
1523
+ uploadNetworkPreference: preference,
1524
+ powerManagement: .init(
1525
+ wifiIdleTimeoutSeconds: try Self.signedInteger(
1526
+ wifiIdleTimeoutSeconds
1527
+ ),
1528
+ cellularIdleTimeoutSeconds: try Self.signedInteger(
1529
+ cellularIdleTimeoutSeconds
1530
+ )
1531
+ ),
1532
+ streamingEnabled: streamingEnabled,
1533
+ streamingFlushIntervalSeconds: try Self.signedInteger(
1534
+ streamingFlushIntervalSeconds
1535
+ )
1536
+ )
1537
+ try await security.writeConnectionSettings(
1538
+ settings,
1539
+ to: Self.connectedDevice(
1540
+ id: id,
1541
+ serialNumber: serialNumber,
1542
+ deviceType: deviceType,
1543
+ firmwareVersion: firmwareVersion,
1544
+ hardwareRevision: hardwareRevision,
1545
+ isProvisioned: isProvisioned,
1546
+ connectionState: connectionState,
1547
+ mtu: mtu
1548
+ )
1549
+ )
1550
+ completion(nil)
1551
+ } catch {
1552
+ completion(error as NSError)
1553
+ }
1554
+ }
1555
+ }
1556
+
1557
+ @objc(readConnectionSettingsWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:completion:)
1558
+ public func readConnectionSettings(
1559
+ id: String,
1560
+ serialNumber: String,
1561
+ deviceType: String,
1562
+ firmwareVersion: String,
1563
+ hardwareRevision: String?,
1564
+ isProvisioned: Bool,
1565
+ connectionState: String,
1566
+ mtu: Double,
1567
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
1568
+ ) {
1569
+ Task {
1570
+ do {
1571
+ let settings = try await security.readConnectionSettings(
1572
+ from: Self.connectedDevice(
1573
+ id: id,
1574
+ serialNumber: serialNumber,
1575
+ deviceType: deviceType,
1576
+ firmwareVersion: firmwareVersion,
1577
+ hardwareRevision: hardwareRevision,
1578
+ isProvisioned: isProvisioned,
1579
+ connectionState: connectionState,
1580
+ mtu: mtu
1581
+ )
1582
+ )
1583
+ completion(Self.connectionSettings(settings), nil)
1584
+ } catch {
1585
+ completion(nil, error as NSError)
1586
+ }
1587
+ }
1588
+ }
1589
+
1590
+ @objc(factoryResetWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:commandID:bindingGeneration:requiresApplicationPersistence:onGrantRequest:onPersistenceRequest:completion:)
1591
+ public func factoryReset(
1592
+ id: String,
1593
+ serialNumber: String,
1594
+ deviceType: String,
1595
+ firmwareVersion: String,
1596
+ hardwareRevision: String?,
1597
+ isProvisioned: Bool,
1598
+ connectionState: String,
1599
+ mtu: Double,
1600
+ commandID: String,
1601
+ bindingGeneration: Double,
1602
+ requiresApplicationPersistence: Bool,
1603
+ onGrantRequest: @escaping @Sendable ([String: Any]) -> Void,
1604
+ onPersistenceRequest: @escaping @Sendable ([String: Any]) -> Void,
1605
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
1606
+ ) {
1607
+ Task {
1608
+ do {
1609
+ let persistenceHandler: (@Sendable (
1610
+ BotaDeviceSDKAppleFactoryResetPersistenceRequest
1611
+ ) -> Void)?
1612
+ if requiresApplicationPersistence {
1613
+ persistenceHandler = { request in
1614
+ onPersistenceRequest([
1615
+ "requestId": request.requestID,
1616
+ "localRecordingsDeleted": request.localRecordingsDeleted,
1617
+ ])
1618
+ }
1619
+ } else {
1620
+ persistenceHandler = nil
1621
+ }
1622
+ let result = try await security.factoryReset(
1623
+ Self.connectedDevice(
1624
+ id: id,
1625
+ serialNumber: serialNumber,
1626
+ deviceType: deviceType,
1627
+ firmwareVersion: firmwareVersion,
1628
+ hardwareRevision: hardwareRevision,
1629
+ isProvisioned: isProvisioned,
1630
+ connectionState: connectionState,
1631
+ mtu: mtu
1632
+ ),
1633
+ commandID: commandID,
1634
+ bindingGeneration: try Self.unsignedInteger(bindingGeneration),
1635
+ onGrantRequest: { request in
1636
+ onGrantRequest([
1637
+ "requestId": request.requestID,
1638
+ "serialNumber": request.serialNumber,
1639
+ "nonce": request.nonce,
1640
+ "commandId": request.commandID,
1641
+ "bindingGeneration": request.bindingGeneration,
1642
+ ])
1643
+ },
1644
+ onPersistenceRequest: persistenceHandler
1645
+ )
1646
+ completion(Self.factoryResetCompletion(result), nil)
1647
+ } catch {
1648
+ completion(nil, error as NSError)
1649
+ }
1650
+ }
1651
+ }
1652
+
1653
+ @objc(resumePendingFactoryResetWithID:serialNumber:deviceType:firmwareVersion:hardwareRevision:isProvisioned:connectionState:mtu:currentBindingGeneration:requiresApplicationPersistence:onPersistenceRequest:completion:)
1654
+ public func resumePendingFactoryReset(
1655
+ id: String,
1656
+ serialNumber: String,
1657
+ deviceType: String,
1658
+ firmwareVersion: String,
1659
+ hardwareRevision: String?,
1660
+ isProvisioned: Bool,
1661
+ connectionState: String,
1662
+ mtu: Double,
1663
+ currentBindingGeneration: Double,
1664
+ requiresApplicationPersistence: Bool,
1665
+ onPersistenceRequest: @escaping @Sendable ([String: Any]) -> Void,
1666
+ completion: @escaping @Sendable ([String: Any]?, NSError?) -> Void
1667
+ ) {
1668
+ Task {
1669
+ do {
1670
+ let persistenceHandler: (@Sendable (
1671
+ BotaDeviceSDKAppleFactoryResetPersistenceRequest
1672
+ ) -> Void)?
1673
+ if requiresApplicationPersistence {
1674
+ persistenceHandler = { request in
1675
+ onPersistenceRequest([
1676
+ "requestId": request.requestID,
1677
+ "localRecordingsDeleted": request.localRecordingsDeleted,
1678
+ ])
1679
+ }
1680
+ } else {
1681
+ persistenceHandler = nil
1682
+ }
1683
+ let result = try await security.resumePendingFactoryReset(
1684
+ Self.connectedDevice(
1685
+ id: id,
1686
+ serialNumber: serialNumber,
1687
+ deviceType: deviceType,
1688
+ firmwareVersion: firmwareVersion,
1689
+ hardwareRevision: hardwareRevision,
1690
+ isProvisioned: isProvisioned,
1691
+ connectionState: connectionState,
1692
+ mtu: mtu
1693
+ ),
1694
+ currentBindingGeneration: try Self.unsignedInteger(currentBindingGeneration),
1695
+ onPersistenceRequest: persistenceHandler
1696
+ )
1697
+ completion(result.map(Self.factoryResetCompletion), nil)
1698
+ } catch {
1699
+ completion(nil, error as NSError)
1700
+ }
1701
+ }
1702
+ }
1703
+
1704
+ @objc(resolveProvisioningMaterialWithRequestID:apiEndpoint:deviceToken:mtu:completion:)
1705
+ public func resolveProvisioningMaterial(
1706
+ requestID: String,
1707
+ apiEndpoint: String,
1708
+ deviceToken: String,
1709
+ mtu: Double,
1710
+ completion: @escaping @Sendable (NSError?) -> Void
1711
+ ) {
1712
+ Task {
1713
+ do {
1714
+ try await security.resolveProvisioningMaterial(
1715
+ requestID: requestID,
1716
+ apiEndpoint: apiEndpoint,
1717
+ deviceToken: deviceToken,
1718
+ mtu: try Self.unsignedInteger(mtu)
1719
+ )
1720
+ completion(nil)
1721
+ } catch {
1722
+ completion(error as NSError)
1723
+ }
1724
+ }
1725
+ }
1726
+
1727
+ @objc(resolveFactoryResetGrantWithRequestID:grantBlob:completion:)
1728
+ public func resolveFactoryResetGrant(
1729
+ requestID: String,
1730
+ grantBlob: String,
1731
+ completion: @escaping @Sendable (NSError?) -> Void
1732
+ ) {
1733
+ Task {
1734
+ do {
1735
+ try await security.resolveFactoryResetGrant(
1736
+ requestID: requestID,
1737
+ grantBlob: grantBlob
1738
+ )
1739
+ completion(nil)
1740
+ } catch {
1741
+ completion(error as NSError)
1742
+ }
1743
+ }
1744
+ }
1745
+
1746
+ @objc(resolveFactoryResetResultPersistenceWithRequestID:completion:)
1747
+ public func resolveFactoryResetResultPersistence(
1748
+ requestID: String,
1749
+ completion: @escaping @Sendable (NSError?) -> Void
1750
+ ) {
1751
+ Task {
1752
+ do {
1753
+ try await security.resolveFactoryResetResultPersistence(requestID: requestID)
1754
+ completion(nil)
1755
+ } catch {
1756
+ completion(error as NSError)
1757
+ }
1758
+ }
1759
+ }
1760
+
1761
+ @objc(rejectApplicationMaterialWithRequestID:message:completion:)
1762
+ public func rejectApplicationMaterial(
1763
+ requestID: String,
1764
+ message: String,
1765
+ completion: @escaping @Sendable (NSError?) -> Void
1766
+ ) {
1767
+ Task {
1768
+ do {
1769
+ try await security.rejectApplicationMaterial(
1770
+ requestID: requestID,
1771
+ message: message
1772
+ )
1773
+ completion(nil)
1774
+ } catch {
1775
+ completion(error as NSError)
1776
+ }
1777
+ }
1778
+ }
1779
+
1780
+ @objc(stateWithCompletion:)
1781
+ public func state(completion: @escaping @Sendable (String) -> Void) {
1782
+ Task {
1783
+ completion(await lifecycle.state())
1784
+ }
1785
+ }
1786
+
1787
+ @objc public func capabilities() -> [String: Any] {
1788
+ let capabilities = BotaDeviceSDKAppleCapabilities.current
1789
+ return [
1790
+ "backgroundReconnect": capabilities.backgroundReconnect,
1791
+ "backgroundScan": capabilities.backgroundScan,
1792
+ "bluetooth": capabilities.bluetooth,
1793
+ "nativeFileTransfer": capabilities.nativeFileTransfer,
1794
+ "platform": capabilities.platform,
1795
+ ]
1796
+ }
1797
+
1798
+ private static func discoveredDevice(_ device: DiscoveredDevice) -> [String: Any] {
1799
+ var value: [String: Any] = [
1800
+ "id": device.id,
1801
+ "rssi": device.rssi,
1802
+ "discoveredAtMs": device.discoveredAt.timeIntervalSince1970 * 1_000,
1803
+ ]
1804
+ if let name = device.name { value["name"] = name }
1805
+ if let type = device.deviceType { value["deviceType"] = deviceType(type) }
1806
+ if let version = device.firmwareVersion { value["firmwareVersion"] = version }
1807
+ if let address = device.macAddress { value["macAddress"] = address }
1808
+ if let state = device.pairingState { value["pairingState"] = pairingState(state) }
1809
+ return value
1810
+ }
1811
+
1812
+ private static func connectedDevice(_ device: ConnectedDevice) -> [String: Any] {
1813
+ var value: [String: Any] = [
1814
+ "id": device.id,
1815
+ "serialNumber": device.serialNumber,
1816
+ "deviceType": deviceType(device.deviceType),
1817
+ "firmwareVersion": device.firmwareVersion,
1818
+ "isProvisioned": device.isProvisioned,
1819
+ "connectionState": connectionState(device.connectionState),
1820
+ "mtu": device.mtu,
1821
+ ]
1822
+ if let revision = device.hardwareRevision { value["hardwareRevision"] = revision }
1823
+ return value
1824
+ }
1825
+
1826
+ private static func recording(_ recording: DeviceRecording) -> [String: Any] {
1827
+ [
1828
+ "uuid": recording.uuid,
1829
+ "startedAtMs": recording.startedAt.timeIntervalSince1970 * 1_000,
1830
+ "durationMs": recording.durationMs,
1831
+ "fileSize": recording.fileSizeBytes,
1832
+ "codec": audioCodec(recording.codec),
1833
+ "isEncrypted": recording.isEncrypted,
1834
+ ]
1835
+ }
1836
+
1837
+ private static func recordingControlResult(_ result: RecordingControlResult) -> [String: Any] {
1838
+ var value: [String: Any] = ["success": result.success]
1839
+ if let error = result.error { value["error"] = error.rawValue }
1840
+ return value
1841
+ }
1842
+
1843
+ private static func recordingState(_ state: RecordingState) -> [String: Any] {
1844
+ var value: [String: Any] = [
1845
+ "active": state.active,
1846
+ "initiatedBy": state.initiatedBy == .remote ? "remote" : "local",
1847
+ ]
1848
+ if let recordingID = state.recordingID { value["recordingId"] = recordingID }
1849
+ return value
1850
+ }
1851
+
1852
+ private static func uploadOwnershipResult(_ result: UploadOwnershipResult) -> [String: Any] {
1853
+ switch result {
1854
+ case .deviceUploadCompleted:
1855
+ ["kind": "device_upload_completed"]
1856
+ case let .deviceUploadPreserved(uploadID):
1857
+ [
1858
+ "kind": "device_upload_preserved",
1859
+ "uploadId": uploadID,
1860
+ ]
1861
+ case let .bluetoothFallback(recordingUUID, uploadID, destinationID):
1862
+ [
1863
+ "kind": "bluetooth_fallback",
1864
+ "recordingUuid": recordingUUID,
1865
+ "uploadId": uploadID,
1866
+ "destinationId": destinationID,
1867
+ ]
1868
+ }
1869
+ }
1870
+
1871
+ private static func firmwarePhase(_ phase: FirmwareUpdatePhase) -> String {
1872
+ switch phase {
1873
+ case .downloading: "downloading"
1874
+ case .awaitingDevice: "awaiting_device"
1875
+ case .transferring: "transferring"
1876
+ case .verifying: "verifying"
1877
+ case .rebooting: "rebooting"
1878
+ case .reconnecting: "reconnecting"
1879
+ case .complete: "complete"
1880
+ }
1881
+ }
1882
+
1883
+ private static func audioCodec(_ codec: WireValue<AudioCodec>) -> String {
1884
+ switch codec {
1885
+ case let .known(value): audioCodec(value)
1886
+ case .unknown: "opus_16k"
1887
+ }
1888
+ }
1889
+
1890
+ private static func audioCodec(_ value: AudioCodec) -> String {
1891
+ switch value {
1892
+ case .pcm16k: "pcm_16k"
1893
+ case .pcm8k: "pcm_8k"
1894
+ case .opus16k: "opus_16k"
1895
+ case .opus8k: "opus_8k"
1896
+ }
1897
+ }
1898
+
1899
+ private static func audioCodec(_ value: String) -> AudioCodec {
1900
+ switch value {
1901
+ case "pcm_16k": .pcm16k
1902
+ case "pcm_8k": .pcm8k
1903
+ case "opus_8k": .opus8k
1904
+ default: .opus16k
1905
+ }
1906
+ }
1907
+
1908
+ private static func connectionType(_ value: String) -> ConnectionType? {
1909
+ switch value {
1910
+ case "wifi": .wifi
1911
+ case "ble": .ble
1912
+ case "cellular": .cellular
1913
+ default: nil
1914
+ }
1915
+ }
1916
+
1917
+ private static func connectionTypeValue(_ value: ConnectionType) -> String? {
1918
+ switch value {
1919
+ case .wifi: "wifi"
1920
+ case .ble: "ble"
1921
+ case .cellular: "cellular"
1922
+ case .unknown: nil
1923
+ }
1924
+ }
1925
+
1926
+ private static func connectionSettings(_ settings: DeviceConnectionSettings) -> [String: Any] {
1927
+ [
1928
+ "enabledConnections": [
1929
+ "wifi": settings.enabledConnections.wifi,
1930
+ "cellular": settings.enabledConnections.cellular,
1931
+ ],
1932
+ "heartbeatEnabledConnections": [
1933
+ "wifi": settings.heartbeatEnabledConnections.wifi,
1934
+ "cellular": settings.heartbeatEnabledConnections.cellular,
1935
+ ],
1936
+ "uploadNetworkPreference": settings.uploadNetworkPreference.compactMap(connectionTypeValue),
1937
+ "powerManagement": [
1938
+ "wifiIdleTimeoutSeconds": settings.powerManagement.wifiIdleTimeoutSeconds,
1939
+ "cellularIdleTimeoutSeconds": settings.powerManagement.cellularIdleTimeoutSeconds,
1940
+ ],
1941
+ "streamingEnabled": settings.streamingEnabled,
1942
+ "streamingFlushIntervalSeconds": settings.streamingFlushIntervalSeconds,
1943
+ ]
1944
+ }
1945
+
1946
+ private static func wifiConfigResult(_ result: WiFiConfigResult) -> [String: Any] {
1947
+ switch result {
1948
+ case .success:
1949
+ ["success": true]
1950
+ case .invalidGrant:
1951
+ ["success": false, "error": "invalid_grant"]
1952
+ case .grantExpired:
1953
+ ["success": false, "error": "grant_expired"]
1954
+ case .decryptionError:
1955
+ ["success": false, "error": "decryption_error"]
1956
+ case .storageError:
1957
+ ["success": false, "error": "storage_error"]
1958
+ case .unknown:
1959
+ ["success": false, "error": "unknown"]
1960
+ }
1961
+ }
1962
+
1963
+ private static func wifiStatusInfo(_ status: WiFiStatusInfo) -> [String: Any] {
1964
+ var value: [String: Any] = ["status": wifiConnectionStatus(status.status)]
1965
+ if let strength = status.signalStrength { value["signalStrength"] = strength }
1966
+ if let ssid = status.ssid { value["ssid"] = ssid }
1967
+ if let error = status.lastError { value["lastError"] = error }
1968
+ return value
1969
+ }
1970
+
1971
+ private static func wifiConnectionStatus(_ status: WiFiConnectionStatus) -> String {
1972
+ switch status {
1973
+ case .idle: "idle"
1974
+ case .connecting: "connecting"
1975
+ case .connected: "connected"
1976
+ case .failed: "failed"
1977
+ case .disconnected: "disconnected"
1978
+ case .unknown: "idle"
1979
+ }
1980
+ }
1981
+
1982
+ private static func wifiScanResult(_ result: DeviceWiFiScanResult) -> [String: Any] {
1983
+ var value: [String: Any] = [
1984
+ "networks": result.networks.map { network in
1985
+ [
1986
+ "ssid": network.ssid,
1987
+ "quality": network.quality,
1988
+ "isCurrent": network.isCurrent,
1989
+ "isOpen": network.isOpen,
1990
+ ] as [String: Any]
1991
+ },
1992
+ ]
1993
+ if let currentSSID = result.currentSSID { value["currentSsid"] = currentSSID }
1994
+ return value
1995
+ }
1996
+
1997
+ private static func factoryResetCompletion(
1998
+ _ completion: FactoryResetCompletion
1999
+ ) -> [String: Any] {
2000
+ [
2001
+ "commandId": completion.commandID,
2002
+ "bindingGeneration": completion.bindingGeneration,
2003
+ ]
2004
+ }
2005
+
2006
+ private static func connectedDevice(
2007
+ id: String,
2008
+ serialNumber: String,
2009
+ deviceType: String,
2010
+ firmwareVersion: String,
2011
+ hardwareRevision: String?,
2012
+ isProvisioned: Bool,
2013
+ connectionState: String,
2014
+ mtu: Double
2015
+ ) throws -> ConnectedDevice {
2016
+ guard let type = self.deviceType(deviceType),
2017
+ let state = self.connectionState(connectionState),
2018
+ mtu.isFinite,
2019
+ mtu.rounded(.towardZero) == mtu,
2020
+ let mtuValue = Int(exactly: mtu)
2021
+ else {
2022
+ throw BotaDeviceSDKAppleBridgeInputError.invalidConnectedDevice
2023
+ }
2024
+ return ConnectedDevice(
2025
+ id: id,
2026
+ serialNumber: serialNumber,
2027
+ deviceType: type,
2028
+ firmwareVersion: firmwareVersion,
2029
+ hardwareRevision: hardwareRevision,
2030
+ isProvisioned: isProvisioned,
2031
+ connectionState: state,
2032
+ mtu: mtuValue
2033
+ )
2034
+ }
2035
+
2036
+ private static func signedInteger(_ value: Double) throws -> Int {
2037
+ guard value.isFinite,
2038
+ value.rounded(.towardZero) == value,
2039
+ let result = Int(exactly: value)
2040
+ else {
2041
+ throw BotaDeviceSDKAppleBridgeInputError.invalidConnectionSettings
2042
+ }
2043
+ return result
2044
+ }
2045
+
2046
+ private static func environment(_ value: String) throws -> DeviceAPIEnvironment {
2047
+ switch value {
2048
+ case "development": .development
2049
+ case "gamma": .gamma
2050
+ case "production": .production
2051
+ default: throw BotaDeviceSDKAppleBridgeInputError.invalidEnvironment
2052
+ }
2053
+ }
2054
+
2055
+ private static func hexData(_ value: String) throws -> Data {
2056
+ guard value.count.isMultiple(of: 2) else {
2057
+ throw BotaDeviceSDKAppleBridgeInputError.invalidHexadecimal
2058
+ }
2059
+ var data = Data(capacity: value.count / 2)
2060
+ var index = value.startIndex
2061
+ while index < value.endIndex {
2062
+ let end = value.index(index, offsetBy: 2)
2063
+ guard let byte = UInt8(value[index..<end], radix: 16) else {
2064
+ throw BotaDeviceSDKAppleBridgeInputError.invalidHexadecimal
2065
+ }
2066
+ data.append(byte)
2067
+ index = end
2068
+ }
2069
+ return data
2070
+ }
2071
+
2072
+ private static func sha256Data(_ value: String) throws -> Data {
2073
+ guard value.count == 64,
2074
+ let data = try? hexData(value)
2075
+ else {
2076
+ throw BotaDeviceSDKAppleBridgeInputError.invalidSHA256
2077
+ }
2078
+ return data
2079
+ }
2080
+
2081
+ private static func deviceStatus(_ status: DeviceStatus) -> [String: Any] {
2082
+ var value: [String: Any] = [
2083
+ "batteryLevel": status.batteryLevel,
2084
+ "storageTotalMb": status.storageTotalMb,
2085
+ "storageUsedMb": status.storageUsedMb,
2086
+ "state": deviceState(status.state),
2087
+ "pendingRecordings": status.pendingRecordings,
2088
+ "signalStrength": status.signalStrength,
2089
+ "flags": deviceFlags(status.flags),
2090
+ "timestamp": status.timestamp,
2091
+ "lteStatus": lteStatus(status.lteStatus),
2092
+ ]
2093
+ if let batteryMv = status.batteryMv { value["batteryMv"] = batteryMv }
2094
+ if let syncedAt = status.lastTimeSyncAt {
2095
+ value["lastTimeSyncAtMs"] = syncedAt.timeIntervalSince1970 * 1_000
2096
+ }
2097
+ if let quality = status.lteSignalQuality { value["lteSignalQuality"] = quality }
2098
+ if let wifi = status.wifiStatus { value["wifiStatus"] = wifiStatus(wifi) }
2099
+ if let modem = status.modemInfo { value["modemInfo"] = modemInfo(modem) }
2100
+ return value
2101
+ }
2102
+
2103
+ private static func deviceFlags(_ flags: DeviceFlags) -> [String: Any] {
2104
+ [
2105
+ "charging": flags.charging,
2106
+ "lowBattery": flags.lowBattery,
2107
+ "storageFull": flags.storageFull,
2108
+ "wifiConnected": flags.wifiConnected,
2109
+ "lteConnected": flags.lteConnected,
2110
+ "syncActive": flags.syncActive,
2111
+ ]
2112
+ }
2113
+
2114
+ private static func modemInfo(_ modem: ModemInfo) -> [String: Any] {
2115
+ var value: [String: Any] = [:]
2116
+ if let imei = modem.imei { value["imei"] = imei }
2117
+ if let iccid = modem.iccid { value["iccid"] = iccid }
2118
+ if let carrier = modem.operator { value["operator"] = carrier }
2119
+ if let rat = modem.rat { value["rat"] = rat }
2120
+ if let band = modem.band { value["band"] = band }
2121
+ if let apn = modem.apn { value["apn"] = apn }
2122
+ if let simStatus = modem.simStatus { value["simStatus"] = simStatus }
2123
+ if let csq = modem.csq { value["csq"] = csq }
2124
+ if let address = modem.ipAddress { value["ipAddress"] = address }
2125
+ if let voltage = modem.modemVoltage { value["modemVoltage"] = voltage }
2126
+ if let firmware = modem.modemFirmware { value["modemFirmware"] = firmware }
2127
+ if let roaming = modem.roaming { value["roaming"] = roaming }
2128
+ return value
2129
+ }
2130
+
2131
+ private static func deviceType(_ value: DeviceType) -> String {
2132
+ switch value {
2133
+ case .botaPin: "bota_pin"
2134
+ case .botaPin4G: "bota_pin_4g"
2135
+ case .botaNote: "bota_note"
2136
+ case .unknown: "bota_pin"
2137
+ }
2138
+ }
2139
+
2140
+ private static func deviceType(_ value: String?) -> DeviceType? {
2141
+ switch value {
2142
+ case "bota_pin": .botaPin
2143
+ case "bota_pin_4g": .botaPin4G
2144
+ case "bota_note": .botaNote
2145
+ default: nil
2146
+ }
2147
+ }
2148
+
2149
+ static func pairingState(_ value: PairingState) -> String {
2150
+ switch value {
2151
+ case .unpaired: "unpaired"
2152
+ case .pairing: "pairing"
2153
+ case .paired: "paired"
2154
+ case .error: "error"
2155
+ case .unknown: "unpaired"
2156
+ }
2157
+ }
2158
+
2159
+ private static func pairingState(_ value: String?) -> PairingState? {
2160
+ switch value {
2161
+ case "unpaired": .unpaired
2162
+ case "pairing": .pairing
2163
+ case "paired": .paired
2164
+ case "error": .error
2165
+ default: nil
2166
+ }
2167
+ }
2168
+
2169
+ private static func connectionState(_ value: ConnectionState) -> String {
2170
+ switch value {
2171
+ case .disconnected: "disconnected"
2172
+ case .connecting: "connecting"
2173
+ case .bonding: "bonding"
2174
+ case .discovering: "discovering"
2175
+ case .connected: "connected"
2176
+ case .disconnecting: "disconnecting"
2177
+ }
2178
+ }
2179
+
2180
+ private static func connectionState(_ value: String) -> ConnectionState? {
2181
+ switch value {
2182
+ case "disconnected": .disconnected
2183
+ case "connecting": .connecting
2184
+ case "bonding": .bonding
2185
+ case "discovering": .discovering
2186
+ case "connected": .connected
2187
+ case "disconnecting": .disconnecting
2188
+ default: nil
2189
+ }
2190
+ }
2191
+
2192
+ private static func deviceState(_ value: WireValue<DeviceState>) -> String {
2193
+ guard case let .known(state) = value else { return "idle" }
2194
+ return switch state {
2195
+ case .idle: "idle"
2196
+ case .recording: "recording"
2197
+ case .syncing: "syncing"
2198
+ case .uploading: "uploading"
2199
+ case .charging: "charging"
2200
+ case .lowBattery: "lowBattery"
2201
+ case .storageFull: "storageFull"
2202
+ case .error: "error"
2203
+ }
2204
+ }
2205
+
2206
+ private static func lteStatus(_ value: WireValue<LteStatus>) -> String {
2207
+ guard case let .known(status) = value else { return "off" }
2208
+ return switch status {
2209
+ case .off: "off"
2210
+ case .searching: "searching"
2211
+ case .registered: "registered"
2212
+ case .connected: "connected"
2213
+ case .denied: "denied"
2214
+ case .noSim: "noSim"
2215
+ case .error: "error"
2216
+ case .lowVoltage: "lowVoltage"
2217
+ case .disabled: "disabled"
2218
+ }
2219
+ }
2220
+
2221
+ private static func wifiStatus(_ value: WireValue<WifiRadioStatus>) -> String {
2222
+ guard case let .known(status) = value else { return "off" }
2223
+ return switch status {
2224
+ case .off: "off"
2225
+ case .scanning: "scanning"
2226
+ case .connecting: "connecting"
2227
+ case .connected: "connected"
2228
+ case .connectFailed: "connectFailed"
2229
+ case .noCredentials: "noCredentials"
2230
+ case .disabled: "disabled"
2231
+ case .error: "error"
2232
+ }
2233
+ }
2234
+
2235
+ static func timeoutMilliseconds(_ value: Double) throws -> UInt64 {
2236
+ guard value.isFinite, value >= 0, value <= Double(Int64.max) else {
2237
+ throw BotaDeviceSDKAppleBridgeInputError.invalidTimeout
2238
+ }
2239
+ return UInt64(value)
2240
+ }
2241
+
2242
+ private static func unsignedInteger(_ value: Double) throws -> UInt64 {
2243
+ guard value.isFinite,
2244
+ value >= 0,
2245
+ value <= 9_007_199_254_740_991,
2246
+ value.rounded(.towardZero) == value
2247
+ else {
2248
+ throw BotaDeviceSDKAppleBridgeInputError.invalidUnsignedInteger
2249
+ }
2250
+ return UInt64(value)
2251
+ }
2252
+
2253
+ private static func unsigned32(_ value: Double) throws -> UInt32 {
2254
+ let integer = try unsignedInteger(value)
2255
+ guard let result = UInt32(exactly: integer) else {
2256
+ throw BotaDeviceSDKAppleBridgeInputError.invalidUnsignedInteger
2257
+ }
2258
+ return result
2259
+ }
2260
+ }