@blueid/access-capacitor 0.102.0 → 0.105.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.
- package/BlueidAccessCapacitor.podspec +2 -1
- package/dist/esm/BlueCore_pb.d.ts +8 -0
- package/dist/esm/BlueCore_pb.js +10 -0
- package/dist/esm/BlueCore_pb.js.map +1 -1
- package/dist/esm/BlueSDK_pb.d.ts +48 -0
- package/dist/esm/BlueSDK_pb.js +12 -0
- package/dist/esm/BlueSDK_pb.js.map +1 -1
- package/dist/plugin.cjs.js +22 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +22 -0
- package/dist/plugin.js.map +1 -1
- package/ios/CBlueIDAccess.xcframework/ios-arm64/Headers/core/BlueCore.pb.h +2 -0
- package/ios/CBlueIDAccess.xcframework/ios-arm64/libCBlueIDAccess.a +0 -0
- package/ios/CBlueIDAccess.xcframework/ios-arm64_x86_64-simulator/Headers/core/BlueCore.pb.h +2 -0
- package/ios/CBlueIDAccess.xcframework/ios-arm64_x86_64-simulator/libCBlueIDAccess.a +0 -0
- package/ios/CBlueIDAccess.xcframework/macos-arm64_x86_64/Headers/core/BlueCore.pb.h +2 -0
- package/ios/CBlueIDAccess.xcframework/macos-arm64_x86_64/libCBlueIDAccess.a +0 -0
- package/ios/Plugin/BlueIDAccessSDK/BlueAPI.swift +7 -0
- package/ios/Plugin/BlueIDAccessSDK/BlueAPIProtocol.swift +13 -0
- package/ios/Plugin/BlueIDAccessSDK/BlueAccess.swift +12 -24
- package/ios/Plugin/BlueIDAccessSDK/BlueCommands.swift +3 -0
- package/ios/Plugin/BlueIDAccessSDK/BlueCore.pb.swift +8 -0
- package/ios/Plugin/BlueIDAccessSDK/BlueDFU/BlueDFUPeripheralService.swift +73 -0
- package/ios/Plugin/BlueIDAccessSDK/BlueDFU/BlueUpdateAccessDeviceFirmwareCommand.swift +226 -0
- package/ios/Plugin/BlueIDAccessSDK/BlueDevices.swift +24 -0
- package/ios/Plugin/BlueIDAccessSDK/BlueError.swift +1 -1
- package/ios/Plugin/BlueIDAccessSDK/BlueFetch.swift +10 -2
- package/ios/Plugin/BlueIDAccessSDK/BlueModal/BlueModal.swift +30 -5
- package/ios/Plugin/BlueIDAccessSDK/BlueModal/{BlueSynchronizeAccessDeviceModalSession.swift → BlueStepProgressModalSession.swift} +4 -4
- package/ios/Plugin/BlueIDAccessSDK/BlueModal/{BlueSynchronizeAccessDeviceModalView.swift → BlueStepProgressModalView.swift} +98 -66
- package/ios/Plugin/BlueIDAccessSDK/BlueSDK.pb.swift +216 -0
- package/ios/Plugin/BlueIDAccessSDK/BlueTaskRunner.swift +34 -3
- package/ios/Plugin/BlueIDAccessSDK/BlueZip.swift +30 -0
- package/package.json +1 -1
|
@@ -110,6 +110,8 @@ typedef enum BlueReturnCode {
|
|
|
110
110
|
BlueReturnCode_SdkGetBlacklistEntriesFailed = -419,
|
|
111
111
|
BlueReturnCode_SdkGetSystemStatusFailed = -420,
|
|
112
112
|
BlueReturnCode_SdkWaitDeviceToRestartFailed = -421,
|
|
113
|
+
BlueReturnCode_SdkUnzipError = -422,
|
|
114
|
+
BlueReturnCode_SdkInvalidFirmwareURL = -423,
|
|
113
115
|
BlueReturnCode_OssMAReturnCodeStart = -1000,
|
|
114
116
|
BlueReturnCode_OssMAReturnCodeEnd = -1100
|
|
115
117
|
} BlueReturnCode_t;
|
|
Binary file
|
|
@@ -110,6 +110,8 @@ typedef enum BlueReturnCode {
|
|
|
110
110
|
BlueReturnCode_SdkGetBlacklistEntriesFailed = -419,
|
|
111
111
|
BlueReturnCode_SdkGetSystemStatusFailed = -420,
|
|
112
112
|
BlueReturnCode_SdkWaitDeviceToRestartFailed = -421,
|
|
113
|
+
BlueReturnCode_SdkUnzipError = -422,
|
|
114
|
+
BlueReturnCode_SdkInvalidFirmwareURL = -423,
|
|
113
115
|
BlueReturnCode_OssMAReturnCodeStart = -1000,
|
|
114
116
|
BlueReturnCode_OssMAReturnCodeEnd = -1100
|
|
115
117
|
} BlueReturnCode_t;
|
|
Binary file
|
|
@@ -110,6 +110,8 @@ typedef enum BlueReturnCode {
|
|
|
110
110
|
BlueReturnCode_SdkGetBlacklistEntriesFailed = -419,
|
|
111
111
|
BlueReturnCode_SdkGetSystemStatusFailed = -420,
|
|
112
112
|
BlueReturnCode_SdkWaitDeviceToRestartFailed = -421,
|
|
113
|
+
BlueReturnCode_SdkUnzipError = -422,
|
|
114
|
+
BlueReturnCode_SdkInvalidFirmwareURL = -423,
|
|
113
115
|
BlueReturnCode_OssMAReturnCodeStart = -1000,
|
|
114
116
|
BlueReturnCode_OssMAReturnCodeEnd = -1100
|
|
115
117
|
} BlueReturnCode_t;
|
|
Binary file
|
|
@@ -100,6 +100,13 @@ class BlueAPI: BlueAPIProtocol {
|
|
|
100
100
|
)
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
func getLatestFirmware(deviceID: String, with tokenAuthentication: BlueTokenAuthentication) async throws -> BlueFetchResponse<BlueGetLatestFirmwareResult> {
|
|
104
|
+
return try await post(
|
|
105
|
+
endpoint: .AccessGetLatestFirmware,
|
|
106
|
+
request: BlueGetLatestFirmwareRequest(deviceId: deviceID, tokenAuthentication: tokenAuthentication)
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
103
110
|
private func post<T>(endpoint: BlueAPIEndpoints, request: Encodable) async throws -> BlueFetchResponse<T> {
|
|
104
111
|
guard #available(macOS 12.0, *) else {
|
|
105
112
|
throw BlueError(.sdkUnsupportedPlatform)
|
|
@@ -18,6 +18,7 @@ public enum BlueAPIEndpoints: String {
|
|
|
18
18
|
case AccessCredentials = "/access/credentials"
|
|
19
19
|
case AccessSynchronizeOfflineAccess = "/access/synchronizeOfflineAccess"
|
|
20
20
|
case AccessClaimCredential = "/access/cc"
|
|
21
|
+
case AccessGetLatestFirmware = "/access/getLatestFirmware"
|
|
21
22
|
|
|
22
23
|
var url: URL {
|
|
23
24
|
guard let url = URL(string: baseURL) else {
|
|
@@ -236,6 +237,17 @@ internal struct BlueAccessToken: Codable {
|
|
|
236
237
|
var expiresAt: Int
|
|
237
238
|
}
|
|
238
239
|
|
|
240
|
+
/// [POST] /access/getLatestFirmware request
|
|
241
|
+
internal struct BlueGetLatestFirmwareRequest: Encodable {
|
|
242
|
+
var deviceId: String
|
|
243
|
+
var tokenAuthentication: BlueTokenAuthentication
|
|
244
|
+
}
|
|
245
|
+
/// [POST] /access/getLatestFirmware response
|
|
246
|
+
internal struct BlueGetLatestFirmwareResult: Decodable {
|
|
247
|
+
var version: Int
|
|
248
|
+
var url: String
|
|
249
|
+
}
|
|
250
|
+
|
|
239
251
|
protocol BlueAPIProtocol {
|
|
240
252
|
func getAccessToken(credentialId: String) async throws -> BlueFetchResponse<BlueAccessToken>
|
|
241
253
|
func synchronizeMobileAccess(with tokenAuthentication: BlueTokenAuthentication, forceRefresh: Bool?) async throws -> BlueFetchResponse<BlueMobileAccessSynchronizationResult>
|
|
@@ -250,4 +262,5 @@ protocol BlueAPIProtocol {
|
|
|
250
262
|
func claimDevice(deviceID: String, objectID: String, with tokenAuthentication: BlueTokenAuthentication) async throws -> BlueFetchResponse<BlueClaimDeviceResult>
|
|
251
263
|
func claimAccessCredential(activationToken: String) async throws -> BlueFetchResponse<BlueClaimAccessCredentialResult>
|
|
252
264
|
func getAccessCredentials(with tokenAuthentication: BlueTokenAuthentication) async throws -> BlueFetchResponse<BlueGetAccessCredentialsResult>
|
|
265
|
+
func getLatestFirmware(deviceID: String, with tokenAuthentication: BlueTokenAuthentication) async throws -> BlueFetchResponse<BlueGetLatestFirmwareResult>
|
|
253
266
|
}
|
|
@@ -162,14 +162,14 @@ public class BlueSynchronizeAccessDeviceCommand: BlueSdkAsyncCommand {
|
|
|
162
162
|
BlueTask(
|
|
163
163
|
id: BlueSynchronizeAccessTaskId.getAuthenticationToken,
|
|
164
164
|
label: blueI18n.syncDeviceGetAuthenticationTokenTaskLabel
|
|
165
|
-
) { _ in
|
|
165
|
+
) { _, _ in
|
|
166
166
|
.result(try await self.getAuthenticationToken(credential))
|
|
167
167
|
},
|
|
168
168
|
|
|
169
169
|
BlueTask(
|
|
170
170
|
id: BlueSynchronizeAccessTaskId.getDeviceConfig,
|
|
171
171
|
label: blueI18n.syncDeviceRetrieveDeviceConfigurationTaskLabel
|
|
172
|
-
) { runner in
|
|
172
|
+
) { _, runner in
|
|
173
173
|
let tokenAuthentication: BlueTokenAuthentication = try runner.getResult(BlueSynchronizeAccessTaskId.getAuthenticationToken)
|
|
174
174
|
|
|
175
175
|
return .result(try await self.getBlueSystemConfig(deviceID: deviceID, with: tokenAuthentication))
|
|
@@ -178,7 +178,7 @@ public class BlueSynchronizeAccessDeviceCommand: BlueSdkAsyncCommand {
|
|
|
178
178
|
BlueTask(
|
|
179
179
|
id: BlueSynchronizeAccessTaskId.updateDeviceConfig,
|
|
180
180
|
label: blueI18n.syncDeviceUpdateDeviceConfigurationTaskLabel
|
|
181
|
-
) { runner in
|
|
181
|
+
) { _, runner in
|
|
182
182
|
let config: BlueSystemConfig? = try runner.getResult(BlueSynchronizeAccessTaskId.getDeviceConfig)
|
|
183
183
|
|
|
184
184
|
let status: BlueSystemStatus = try await self.updateDevice(deviceID, config)
|
|
@@ -191,7 +191,7 @@ public class BlueSynchronizeAccessDeviceCommand: BlueSdkAsyncCommand {
|
|
|
191
191
|
BlueTask(
|
|
192
192
|
id: BlueSynchronizeAccessTaskId.updateDeviceTime,
|
|
193
193
|
label: blueI18n.syncDeviceUpdateDeviceTimeTaskLabel
|
|
194
|
-
) { runner in
|
|
194
|
+
) { _, runner in
|
|
195
195
|
let status: BlueSystemStatus = try runner.getResult(BlueSynchronizeAccessTaskId.updateDeviceConfig)
|
|
196
196
|
|
|
197
197
|
return .resultWithStatus(nil, status.settings.timeWasSet == true ? .succeeded : .skipped)
|
|
@@ -200,7 +200,7 @@ public class BlueSynchronizeAccessDeviceCommand: BlueSdkAsyncCommand {
|
|
|
200
200
|
BlueTask(
|
|
201
201
|
id: BlueSynchronizeAccessTaskId.waitForRestart,
|
|
202
202
|
label: blueI18n.syncDeviceWaitForDeviceToRestartTaskLabel
|
|
203
|
-
) { _ in
|
|
203
|
+
) { _, _ in
|
|
204
204
|
.result(try await self.waitUntilDeviceHasBeenRestarted(deviceID))
|
|
205
205
|
},
|
|
206
206
|
|
|
@@ -208,7 +208,7 @@ public class BlueSynchronizeAccessDeviceCommand: BlueSdkAsyncCommand {
|
|
|
208
208
|
id: BlueSynchronizeAccessTaskId.pushEventLogs,
|
|
209
209
|
label: blueI18n.syncDevicePushEventLogsTaskLabel,
|
|
210
210
|
failable: true
|
|
211
|
-
) { runner in
|
|
211
|
+
) { _, runner in
|
|
212
212
|
let tokenAuthentication: BlueTokenAuthentication = try runner.getResult(BlueSynchronizeAccessTaskId.getAuthenticationToken)
|
|
213
213
|
let status: BlueSystemStatus = try runner.getResult(BlueSynchronizeAccessTaskId.updateDeviceConfig)
|
|
214
214
|
|
|
@@ -219,7 +219,7 @@ public class BlueSynchronizeAccessDeviceCommand: BlueSdkAsyncCommand {
|
|
|
219
219
|
id: BlueSynchronizeAccessTaskId.pushSystemLogs,
|
|
220
220
|
label: blueI18n.syncDevicePushSystemLogsTaskLabel,
|
|
221
221
|
failable: true
|
|
222
|
-
) { runner in
|
|
222
|
+
) { _, runner in
|
|
223
223
|
let tokenAuthentication: BlueTokenAuthentication = try runner.getResult(BlueSynchronizeAccessTaskId.getAuthenticationToken)
|
|
224
224
|
let status: BlueSystemStatus = try runner.getResult(BlueSynchronizeAccessTaskId.updateDeviceConfig)
|
|
225
225
|
|
|
@@ -230,7 +230,7 @@ public class BlueSynchronizeAccessDeviceCommand: BlueSdkAsyncCommand {
|
|
|
230
230
|
id: BlueSynchronizeAccessTaskId.getBlacklistEntries,
|
|
231
231
|
label: blueI18n.syncDeviceRetrieveBlacklistEntriesTaskLabel,
|
|
232
232
|
failable: true
|
|
233
|
-
) { runner in
|
|
233
|
+
) { _, runner in
|
|
234
234
|
let tokenAuthentication: BlueTokenAuthentication = try runner.getResult(BlueSynchronizeAccessTaskId.getAuthenticationToken)
|
|
235
235
|
|
|
236
236
|
return .result(try await self.getBlacklistEntries(deviceID, tokenAuthentication))
|
|
@@ -240,7 +240,7 @@ public class BlueSynchronizeAccessDeviceCommand: BlueSdkAsyncCommand {
|
|
|
240
240
|
id: BlueSynchronizeAccessTaskId.deployBlacklistEntries,
|
|
241
241
|
label: blueI18n.syncDeviceDeployBlacklistEntriesTaskLabel,
|
|
242
242
|
failable: true
|
|
243
|
-
) { runner in
|
|
243
|
+
) { _, runner in
|
|
244
244
|
let entries: BlueBlacklistEntries? = try runner.getResult(BlueSynchronizeAccessTaskId.getBlacklistEntries)
|
|
245
245
|
|
|
246
246
|
guard let entries = entries else {
|
|
@@ -253,14 +253,14 @@ public class BlueSynchronizeAccessDeviceCommand: BlueSdkAsyncCommand {
|
|
|
253
253
|
BlueTask(
|
|
254
254
|
id: BlueSynchronizeAccessTaskId.getSystemStatus,
|
|
255
255
|
label: blueI18n.syncDeviceRetrieveSystemStatusTaskLabel
|
|
256
|
-
) { _ in
|
|
256
|
+
) { _, _ in
|
|
257
257
|
.result(try await self.getSystemStatus(deviceID))
|
|
258
258
|
},
|
|
259
259
|
|
|
260
260
|
BlueTask(
|
|
261
261
|
id: BlueSynchronizeAccessTaskId.pushSystemStatus,
|
|
262
262
|
label: blueI18n.syncDevicePushSystemStatusTaskLabel
|
|
263
|
-
) { runner in
|
|
263
|
+
) { _, runner in
|
|
264
264
|
let tokenAuthentication: BlueTokenAuthentication = try runner.getResult(BlueSynchronizeAccessTaskId.getAuthenticationToken)
|
|
265
265
|
let status: BlueSystemStatus = try runner.getResult(BlueSynchronizeAccessTaskId.getSystemStatus)
|
|
266
266
|
|
|
@@ -321,19 +321,7 @@ public class BlueSynchronizeAccessDeviceCommand: BlueSdkAsyncCommand {
|
|
|
321
321
|
|
|
322
322
|
private func waitUntilDeviceHasBeenRestarted(_ deviceID: String) async throws {
|
|
323
323
|
do {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
while attempts <= 2 {
|
|
327
|
-
try? await Task.sleep(nanoseconds: UInt64(blueSecondsToNanoseconds(10)))
|
|
328
|
-
|
|
329
|
-
if blueGetDevice(deviceID) != nil {
|
|
330
|
-
return
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
attempts += 1
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
throw BlueError(.sdkDeviceNotFound)
|
|
324
|
+
try await waitForDeviceAvailability(deviceID)
|
|
337
325
|
} catch {
|
|
338
326
|
throw BlueError(.sdkWaitDeviceToRestartFailed, cause: error)
|
|
339
327
|
}
|
|
@@ -93,6 +93,9 @@ public struct BlueCommands {
|
|
|
93
93
|
|
|
94
94
|
public let openAppSettings = BlueOpenAppSettingsCommand()
|
|
95
95
|
|
|
96
|
+
// MARK: - DFU commands
|
|
97
|
+
public let updateAccessDeviceFirmware = BlueUpdateAccessDeviceFirmwareCommand(defaultSdkService)
|
|
98
|
+
|
|
96
99
|
// TODO: Make it available only for dev environment
|
|
97
100
|
public let UNSAFE_clearData = BlueClearDataCommand()
|
|
98
101
|
|
|
@@ -129,6 +129,8 @@ public enum BlueReturnCode: SwiftProtobuf.Enum {
|
|
|
129
129
|
case sdkGetBlacklistEntriesFailed // = -419
|
|
130
130
|
case sdkGetSystemStatusFailed // = -420
|
|
131
131
|
case sdkWaitDeviceToRestartFailed // = -421
|
|
132
|
+
case sdkUnzipError // = -422
|
|
133
|
+
case sdkInvalidFirmwareURL // = -423
|
|
132
134
|
case ossMareturnCodeStart // = -1000
|
|
133
135
|
case ossMareturnCodeEnd // = -1100
|
|
134
136
|
|
|
@@ -140,6 +142,8 @@ public enum BlueReturnCode: SwiftProtobuf.Enum {
|
|
|
140
142
|
switch rawValue {
|
|
141
143
|
case -1100: self = .ossMareturnCodeEnd
|
|
142
144
|
case -1000: self = .ossMareturnCodeStart
|
|
145
|
+
case -423: self = .sdkInvalidFirmwareURL
|
|
146
|
+
case -422: self = .sdkUnzipError
|
|
143
147
|
case -421: self = .sdkWaitDeviceToRestartFailed
|
|
144
148
|
case -420: self = .sdkGetSystemStatusFailed
|
|
145
149
|
case -419: self = .sdkGetBlacklistEntriesFailed
|
|
@@ -243,6 +247,8 @@ public enum BlueReturnCode: SwiftProtobuf.Enum {
|
|
|
243
247
|
switch self {
|
|
244
248
|
case .ossMareturnCodeEnd: return -1100
|
|
245
249
|
case .ossMareturnCodeStart: return -1000
|
|
250
|
+
case .sdkInvalidFirmwareURL: return -423
|
|
251
|
+
case .sdkUnzipError: return -422
|
|
246
252
|
case .sdkWaitDeviceToRestartFailed: return -421
|
|
247
253
|
case .sdkGetSystemStatusFailed: return -420
|
|
248
254
|
case .sdkGetBlacklistEntriesFailed: return -419
|
|
@@ -3643,6 +3649,8 @@ extension BlueReturnCode: SwiftProtobuf._ProtoNameProviding {
|
|
|
3643
3649
|
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
|
3644
3650
|
-1100: .same(proto: "OssMAReturnCodeEnd"),
|
|
3645
3651
|
-1000: .same(proto: "OssMAReturnCodeStart"),
|
|
3652
|
+
-423: .same(proto: "SdkInvalidFirmwareURL"),
|
|
3653
|
+
-422: .same(proto: "SdkUnzipError"),
|
|
3646
3654
|
-421: .same(proto: "SdkWaitDeviceToRestartFailed"),
|
|
3647
3655
|
-420: .same(proto: "SdkGetSystemStatusFailed"),
|
|
3648
3656
|
-419: .same(proto: "SdkGetBlacklistEntriesFailed"),
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import CoreBluetooth
|
|
2
|
+
import Foundation
|
|
3
|
+
import NordicDFU
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @class BlueDFUPeripheralService
|
|
7
|
+
* A utility class for looking up peripherals that are advertising DFU service.
|
|
8
|
+
*/
|
|
9
|
+
internal class BlueDFUPeripheralService: NSObject, CBCentralManagerDelegate {
|
|
10
|
+
private var centralManager: CBCentralManager? = nil
|
|
11
|
+
private var discoveredPeripheral: CBPeripheral?
|
|
12
|
+
private var continuation: CheckedContinuation<CBPeripheral, any Error>?
|
|
13
|
+
|
|
14
|
+
/// Finds a peripheral that is advertising DFU service asynchronously within the specified timeout period.
|
|
15
|
+
///
|
|
16
|
+
/// - parameter timeout: The duration in seconds to wait for the peripheral to be discovered. Default value is 10.0 seconds.
|
|
17
|
+
/// - returns: A `CBPeripheral` object representing the DFU peripheral if found.
|
|
18
|
+
/// - throws: An error of type `BlueError` if the timeout period elapses without finding the peripheral.
|
|
19
|
+
func find(_ timeout: TimeInterval = 10.0) async throws -> CBPeripheral {
|
|
20
|
+
if self.centralManager != nil {
|
|
21
|
+
throw BlueError(.invalidState)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
self.centralManager = CBCentralManager(delegate: self, queue: nil)
|
|
25
|
+
|
|
26
|
+
return try await withCheckedThrowingContinuation { continuation in
|
|
27
|
+
self.continuation = continuation
|
|
28
|
+
|
|
29
|
+
self.startScan()
|
|
30
|
+
|
|
31
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + timeout) {
|
|
32
|
+
if self.discoveredPeripheral == nil {
|
|
33
|
+
continuation.resume(throwing: BlueError(.sdkTimeout))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Destroys the BlueDFUPeripheralService instance, releasing any resources it holds.
|
|
40
|
+
func destroy() {
|
|
41
|
+
if let centralManager = centralManager {
|
|
42
|
+
if centralManager.isScanning {
|
|
43
|
+
centralManager.stopScan()
|
|
44
|
+
}
|
|
45
|
+
centralManager.delegate = nil
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/// Starts scanning for peripherals that are that are advertising DFU service.
|
|
50
|
+
private func startScan() {
|
|
51
|
+
if let centralManager = centralManager {
|
|
52
|
+
if (centralManager.isScanning) {
|
|
53
|
+
centralManager.stopScan()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if centralManager.state == .poweredOn {
|
|
57
|
+
centralManager.scanForPeripherals(withServices: [DFUUuidHelper().secureDFUService], options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// MARK: - CBCentralManagerDelegate API
|
|
63
|
+
|
|
64
|
+
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
|
|
65
|
+
self.discoveredPeripheral = peripheral
|
|
66
|
+
self.continuation?.resume(returning: peripheral)
|
|
67
|
+
self.destroy()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
71
|
+
self.startScan()
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import CoreBluetooth
|
|
2
|
+
import Foundation
|
|
3
|
+
import NordicDFU
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @class BlueUpdateAccessDeviceFirmwareCommand
|
|
7
|
+
* A SDK command for updating firmware of nRF51 and nRF52 devices over Bluetooth LE.
|
|
8
|
+
*/
|
|
9
|
+
public class BlueUpdateAccessDeviceFirmwareCommand: BlueSdkAsyncCommand {
|
|
10
|
+
override func runAsync(arg0: Any?, arg1: Any?, arg2: Any?) async throws -> Any? {
|
|
11
|
+
return try await runAsync(
|
|
12
|
+
credentialID: blueCastArg(String.self, arg0),
|
|
13
|
+
deviceID: blueCastArg(String.self, arg1)
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public func runAsync(credentialID: String, deviceID: String) async throws {
|
|
18
|
+
guard let credential = blueGetAccessCredential(credentialID: credentialID) else {
|
|
19
|
+
throw BlueError(.sdkCredentialNotFound)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
guard blueGetDevice(deviceID) != nil else {
|
|
23
|
+
throw BlueError(.sdkDeviceNotFound)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try await BlueUpdateAccessDeviceFirmware(sdkService)
|
|
27
|
+
.update(credential, deviceID)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
internal class BlueUpdateAccessDeviceFirmware: LoggerDelegate, DFUServiceDelegate, DFUProgressDelegate {
|
|
32
|
+
public enum BlueUpdateAccessDeviceFirmwareTaskId {
|
|
33
|
+
case getAuthenticationToken
|
|
34
|
+
case checkLatestFirmware
|
|
35
|
+
case downloadLatestFirmware
|
|
36
|
+
case prepareUpdate
|
|
37
|
+
case startBootloader
|
|
38
|
+
case findDFUPeripheral
|
|
39
|
+
case updateFirmware
|
|
40
|
+
case waitRestart
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private let sdkService: BlueSdkService
|
|
44
|
+
|
|
45
|
+
private var semaphore: DispatchSemaphore?
|
|
46
|
+
private var controller: DFUServiceController?
|
|
47
|
+
private var task: BlueTask?
|
|
48
|
+
private var error: BlueError? = nil
|
|
49
|
+
|
|
50
|
+
init(_ sdkService: BlueSdkService) {
|
|
51
|
+
self.sdkService = sdkService
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public func update(_ credential: BlueAccessCredential, _ deviceID: String) async throws {
|
|
55
|
+
|
|
56
|
+
let tasks = [
|
|
57
|
+
BlueTask(
|
|
58
|
+
id: BlueUpdateAccessDeviceFirmwareTaskId.getAuthenticationToken,
|
|
59
|
+
label: blueI18n.dfuGetAuthenticationTokenTaskLabel
|
|
60
|
+
) { _, _ in
|
|
61
|
+
.result(try await self.sdkService.authenticationTokenService.getTokenAuthentication(credential: credential))
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
BlueTask(
|
|
65
|
+
id: BlueUpdateAccessDeviceFirmwareTaskId.checkLatestFirmware,
|
|
66
|
+
label: blueI18n.dfuCheckLatestFwlabel
|
|
67
|
+
) { _, runner in
|
|
68
|
+
let tokenAuthentication: BlueTokenAuthentication = try runner.getResult(BlueUpdateAccessDeviceFirmwareTaskId.getAuthenticationToken)
|
|
69
|
+
|
|
70
|
+
return .result(try await self.sdkService.apiService.getLatestFirmware(deviceID: deviceID, with: tokenAuthentication).getData())
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
BlueTask(
|
|
74
|
+
id: BlueUpdateAccessDeviceFirmwareTaskId.downloadLatestFirmware,
|
|
75
|
+
label: blueI18n.dfuDownloadLatestFwlabel
|
|
76
|
+
) { _, runner in
|
|
77
|
+
let latestFW: BlueGetLatestFirmwareResult = try runner.getResult(BlueUpdateAccessDeviceFirmwareTaskId.checkLatestFirmware)
|
|
78
|
+
|
|
79
|
+
return .result(try await self.downloadLatestFirmware(url: latestFW.url))
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
BlueTask(
|
|
83
|
+
id: BlueUpdateAccessDeviceFirmwareTaskId.prepareUpdate,
|
|
84
|
+
label: blueI18n.dfuPrepareUpdateLabel
|
|
85
|
+
) { _, runner in
|
|
86
|
+
let zip: Data = try runner.getResult(BlueUpdateAccessDeviceFirmwareTaskId.downloadLatestFirmware)
|
|
87
|
+
|
|
88
|
+
return .result(try self.prepareUpdate(zip))
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
BlueTask(
|
|
92
|
+
id: BlueUpdateAccessDeviceFirmwareTaskId.startBootloader,
|
|
93
|
+
label: blueI18n.dfuStartBootloaderLabel
|
|
94
|
+
) { _, _ in
|
|
95
|
+
return .result(try await self.startBootloader(deviceID))
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
BlueTask(
|
|
99
|
+
id: BlueUpdateAccessDeviceFirmwareTaskId.findDFUPeripheral,
|
|
100
|
+
label: blueI18n.dfuFindDfuperipheralLabel
|
|
101
|
+
) { _, _ in
|
|
102
|
+
return .result(try await self.findPeripheral())
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
BlueTask(
|
|
106
|
+
id: BlueUpdateAccessDeviceFirmwareTaskId.updateFirmware,
|
|
107
|
+
label: blueI18n.dfuUpdateFwlabel,
|
|
108
|
+
progress: 0,
|
|
109
|
+
cancelHandler: {
|
|
110
|
+
_ = self.controller?.abort()
|
|
111
|
+
},
|
|
112
|
+
handler: { task, runner in
|
|
113
|
+
let urlToZipFile: URL = try runner.getResult(BlueUpdateAccessDeviceFirmwareTaskId.prepareUpdate)
|
|
114
|
+
let peripheral: CBPeripheral = try runner.getResult(BlueUpdateAccessDeviceFirmwareTaskId.findDFUPeripheral)
|
|
115
|
+
|
|
116
|
+
self.task = task
|
|
117
|
+
|
|
118
|
+
return .result(try self.update(peripheral, urlToZipFile))
|
|
119
|
+
}
|
|
120
|
+
),
|
|
121
|
+
|
|
122
|
+
BlueTask(
|
|
123
|
+
id: BlueUpdateAccessDeviceFirmwareTaskId.waitRestart,
|
|
124
|
+
label: blueI18n.dfuWaitForDeviceToRestartTaskLabel
|
|
125
|
+
) { _, _ in
|
|
126
|
+
return .result(try await waitForDeviceAvailability(deviceID, timeout: 5, maxRetries: 6))
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
let runner = BlueSerialTaskRunner(tasks)
|
|
131
|
+
|
|
132
|
+
#if os(iOS) || os(watchOS)
|
|
133
|
+
try await blueShowUpdateAccessDeviceFirmwareModal(runner)
|
|
134
|
+
#else
|
|
135
|
+
try await runner.execute(true)
|
|
136
|
+
#endif
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private func downloadLatestFirmware(url string: String) async throws -> Data {
|
|
140
|
+
guard let url = URL(string: string) else {
|
|
141
|
+
throw BlueError(.sdkInvalidFirmwareURL)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return try await BlueFetch.get(url: url).getData()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private func prepareUpdate(_ zip: Data) throws -> URL {
|
|
148
|
+
let extractedURL = try BlueZip.extract(data: zip)
|
|
149
|
+
|
|
150
|
+
return extractedURL.appendingPathComponent("dfu_application.zip")
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private func startBootloader(_ deviceID: String) async throws {
|
|
154
|
+
try await blueTerminalRun(deviceID: deviceID, action: "BOOTLD")
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private func findPeripheral() async throws -> CBPeripheral {
|
|
158
|
+
let service = BlueDFUPeripheralService()
|
|
159
|
+
|
|
160
|
+
defer {
|
|
161
|
+
service.destroy()
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return try await service.find()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private func update(_ peripheral: CBPeripheral, _ urlToZipFile: URL) throws {
|
|
168
|
+
self.semaphore = DispatchSemaphore(value: 0)
|
|
169
|
+
|
|
170
|
+
let firmware = try DFUFirmware(urlToZipFile: urlToZipFile)
|
|
171
|
+
|
|
172
|
+
let initiator = DFUServiceInitiator()
|
|
173
|
+
initiator.logger = self
|
|
174
|
+
initiator.delegate = self
|
|
175
|
+
initiator.progressDelegate = self
|
|
176
|
+
|
|
177
|
+
self.controller = initiator
|
|
178
|
+
.with(firmware: firmware)
|
|
179
|
+
.start(targetWithIdentifier: peripheral.identifier)
|
|
180
|
+
|
|
181
|
+
self.semaphore?.wait()
|
|
182
|
+
|
|
183
|
+
if let error = error {
|
|
184
|
+
throw error
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// MARK: - LoggerDelegate API
|
|
189
|
+
|
|
190
|
+
public func logWith(_ level: NordicDFU.LogLevel, message: String) {
|
|
191
|
+
switch (level) {
|
|
192
|
+
case .debug, .application, .verbose:
|
|
193
|
+
blueLogDebug(message)
|
|
194
|
+
break
|
|
195
|
+
case .info:
|
|
196
|
+
blueLogInfo(message)
|
|
197
|
+
break
|
|
198
|
+
case .warning:
|
|
199
|
+
blueLogWarn(message)
|
|
200
|
+
break
|
|
201
|
+
case .error:
|
|
202
|
+
blueLogError(message)
|
|
203
|
+
break
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// MARK: - DFUServiceDelegate API
|
|
208
|
+
|
|
209
|
+
public func dfuStateDidChange(to state: NordicDFU.DFUState) {
|
|
210
|
+
if state == .completed || state == .aborted {
|
|
211
|
+
semaphore?.signal()
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public func dfuError(_ error: NordicDFU.DFUError, didOccurWithMessage message: String) {
|
|
216
|
+
self.error = BlueError(.error, detail: message)
|
|
217
|
+
|
|
218
|
+
semaphore?.signal()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// MARK: - DFUProgressDelegate API
|
|
222
|
+
|
|
223
|
+
public func dfuProgressDidChange(for part: Int, outOf totalParts: Int, to progress: Int, currentSpeedBytesPerSecond: Double, avgSpeedBytesPerSecond: Double) {
|
|
224
|
+
self.task?.updateProgress(Float(progress))
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -10,6 +10,30 @@ internal func blueSetMaxDeviceAgeSeconds(_ newMaxDeviceAgeSeconds: Double) {
|
|
|
10
10
|
maxDeviceAgeSeconds = max(newMaxDeviceAgeSeconds, 1)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/// Waits asynchronously for a device with the specified ID to become available.
|
|
14
|
+
/// This function waits for a device with the given ID to be discovered within a predefined timeout period, with a maximum number of retries.
|
|
15
|
+
/// - parameters:
|
|
16
|
+
/// - deviceID: The ID of the device to wait for.
|
|
17
|
+
/// - timeout: The duration in seconds to wait for the device to be discovered each time. Default value is 10 seconds.
|
|
18
|
+
/// - maxRetries: The maximum number of retries before giving up. Default value is 3.
|
|
19
|
+
/// - throws: An error of type `BlueError` if the device is not found within the specified timeout period and maximum retries.
|
|
20
|
+
|
|
21
|
+
internal func waitForDeviceAvailability(_ deviceID: String, timeout: Int = 10, maxRetries: Int = 3) async throws {
|
|
22
|
+
var attempts = 0
|
|
23
|
+
|
|
24
|
+
while attempts < maxRetries {
|
|
25
|
+
try? await Task.sleep(nanoseconds: UInt64(blueSecondsToNanoseconds(timeout)))
|
|
26
|
+
|
|
27
|
+
if blueGetDevice(deviceID) != nil {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
attempts += 1
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
throw BlueError(.sdkDeviceNotFound)
|
|
35
|
+
}
|
|
36
|
+
|
|
13
37
|
internal func blueGetDevice(_ deviceID: String) -> BlueDevice? {
|
|
14
38
|
for device in blueDevices {
|
|
15
39
|
if (device.info.deviceID == deviceID) {
|
|
@@ -45,7 +45,7 @@ public final class BlueError: Error, LocalizedError, Equatable {
|
|
|
45
45
|
self.detail = nil
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
public init(_ returnCode: BlueReturnCode, cause: Error, detail: String? = nil) {
|
|
48
|
+
public init(_ returnCode: BlueReturnCode, cause: Error? = nil, detail: String? = nil) {
|
|
49
49
|
self.returnCode = returnCode
|
|
50
50
|
self.cause = cause
|
|
51
51
|
self.detail = detail
|
|
@@ -15,6 +15,10 @@ struct BlueFetchResponse<T> where T: Decodable {
|
|
|
15
15
|
|
|
16
16
|
func getData() throws -> T {
|
|
17
17
|
guard let data = data else {
|
|
18
|
+
if let rawData = rawData as? T {
|
|
19
|
+
return rawData
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
let status: String = statusCode?.description ?? "Unknown"
|
|
19
23
|
var description = ""
|
|
20
24
|
|
|
@@ -40,7 +44,6 @@ struct BlueFetchResponse<T> where T: Decodable {
|
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
|
|
43
|
-
@available(macOS 12.0, *)
|
|
44
47
|
class BlueFetch {
|
|
45
48
|
|
|
46
49
|
static func post<T>(url: URL, data: Data?, config: BlueFetchConfig? = nil) async throws -> BlueFetchResponse<T> where T: Decodable {
|
|
@@ -65,18 +68,23 @@ class BlueFetch {
|
|
|
65
68
|
|
|
66
69
|
var statusCode: Int?
|
|
67
70
|
var decodedData: T?
|
|
71
|
+
var contentType: String?
|
|
68
72
|
|
|
69
73
|
do {
|
|
70
74
|
let (data, response) = try await URLSession.shared.data(for: request)
|
|
71
75
|
|
|
72
76
|
if let httpResponse = response as? HTTPURLResponse {
|
|
73
77
|
statusCode = httpResponse.statusCode
|
|
78
|
+
contentType = httpResponse.value(forHTTPHeaderField: "content-type")
|
|
74
79
|
|
|
75
80
|
blueLogDebug("Status code: \(httpResponse.statusCode)")
|
|
81
|
+
blueLogDebug("Content-Type: \(String(describing: contentType))")
|
|
76
82
|
blueLogDebug("Data: \(String(describing: String(data: data, encoding: .utf8)))")
|
|
77
83
|
}
|
|
78
84
|
|
|
79
|
-
|
|
85
|
+
let isJSON = contentType == "application/json"
|
|
86
|
+
|
|
87
|
+
if (statusCode == 200 && isJSON) {
|
|
80
88
|
do {
|
|
81
89
|
decodedData = try JSONDecoder().decode(T.self, from: data)
|
|
82
90
|
} catch {
|