123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- import Flutter
- import UIKit
- import iOSDFULibrary
- import CoreBluetooth
- public class SwiftNordicDfuPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, DFUServiceDelegate, DFUProgressDelegate, LoggerDelegate {
-
- let registrar: FlutterPluginRegistrar
- var sink: FlutterEventSink!
- var pendingResult: FlutterResult?
- var deviceAddress: String?
- private var dfuController : DFUServiceController!
-
- public static func register(with registrar: FlutterPluginRegistrar) {
- let instance = SwiftNordicDfuPlugin(registrar)
-
- let method = FlutterMethodChannel(name: "dev.steenbakker.nordic_dfu/method", binaryMessenger: registrar.messenger())
-
- let event = FlutterEventChannel(name:
- "dev.steenbakker.nordic_dfu/event", binaryMessenger: registrar.messenger())
- registrar.addMethodCallDelegate(instance, channel: method)
- event.setStreamHandler(instance)
- }
-
- init(_ registrar: FlutterPluginRegistrar) {
- self.registrar = registrar
- super.init()
- }
- public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
- switch call.method {
- case "startDfu": initializeDfu(call, result)
- case "abortDfu" : abortDfu()
- default: result(FlutterMethodNotImplemented)
- }
- }
-
- public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
- sink = events
- return nil
- }
-
- public func onCancel(withArguments arguments: Any?) -> FlutterError? {
- sink = nil
- return nil
- }
-
- private func abortDfu() {
- _ = dfuController?.abort()
- dfuController = nil
- }
-
- private func initializeDfu(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
- guard let arguments = call.arguments as? Dictionary<String, AnyObject> else {
- result(FlutterError(code: "ABNORMAL_PARAMETER", message: "no parameters", details: nil))
- return
- }
- let name = arguments["name"] as? String
- guard let address = arguments["address"] as? String,
- var filePath = arguments["filePath"] as? String else {
- result(FlutterError(code: "ABNORMAL_PARAMETER", message: "address and filePath are required", details: nil))
- return
- }
-
- let forceDfu = arguments["forceDfu"] as? Bool
-
- let enableUnsafeExperimentalButtonlessServiceInSecureDfu = arguments["enableUnsafeExperimentalButtonlessServiceInSecureDfu"] as? Bool
-
- let fileInAsset = (arguments["fileInAsset"] as? Bool) ?? false
-
- if (fileInAsset) {
- let key = registrar.lookupKey(forAsset: filePath)
- guard let pathInAsset = Bundle.main.path(forResource: key, ofType: nil) else {
- result(FlutterError(code: "ABNORMAL_PARAMETER", message: "file in asset not found \(filePath)", details: nil))
- return
- }
-
- filePath = pathInAsset
- }
-
- let alternativeAdvertisingNameEnabled = arguments["alternativeAdvertisingNameEnabled"] as? Bool
-
- startDfu(address,
- name: name,
- filePath: filePath,
- forceDfu: forceDfu,
- enableUnsafeExperimentalButtonlessServiceInSecureDfu: enableUnsafeExperimentalButtonlessServiceInSecureDfu,
- alternativeAdvertisingNameEnabled: alternativeAdvertisingNameEnabled,
- result: result)
- }
-
- private func startDfu(
- _ address: String,
- name: String?,
- filePath: String,
- forceDfu: Bool?,
- enableUnsafeExperimentalButtonlessServiceInSecureDfu: Bool?,
- alternativeAdvertisingNameEnabled: Bool?,
- result: @escaping FlutterResult) {
- guard let uuid = UUID(uuidString: address) else {
- result(FlutterError(code: "DEVICE_ADDRESS_ERROR", message: "Device address conver to uuid failed", details: "Device uuid \(address) convert to uuid failed"))
- return
- }
-
- guard let firmware = DFUFirmware(urlToZipFile: URL(fileURLWithPath: filePath)) else {
- result(FlutterError(code: "DFU_FIRMWARE_NOT_FOUND", message: "Could not dfu zip file", details: nil))
- return
- }
-
- let dfuInitiator = DFUServiceInitiator(queue: nil)
- .with(firmware: firmware);
- dfuInitiator.delegate = self
- dfuInitiator.progressDelegate = self
- dfuInitiator.logger = self
-
- if let enableUnsafeExperimentalButtonlessServiceInSecureDfu = enableUnsafeExperimentalButtonlessServiceInSecureDfu {
- dfuInitiator.enableUnsafeExperimentalButtonlessServiceInSecureDfu = enableUnsafeExperimentalButtonlessServiceInSecureDfu
- }
-
- if let forceDfu = forceDfu {
- dfuInitiator.forceDfu = forceDfu
- }
-
- if let alternativeAdvertisingNameEnabled = alternativeAdvertisingNameEnabled {
- dfuInitiator.alternativeAdvertisingNameEnabled = alternativeAdvertisingNameEnabled
- }
-
- pendingResult = result
- deviceAddress = address
-
- dfuController = dfuInitiator.start(targetWithIdentifier: uuid)
- }
-
- // MARK: DFUServiceDelegate
- public func dfuStateDidChange(to state: DFUState) {
- switch state {
- case .completed:
- sink?(["onDfuCompleted":deviceAddress])
- pendingResult?(deviceAddress)
- pendingResult = nil
- dfuController = nil
- case .disconnecting:
- sink?(["onDeviceDisconnecting":deviceAddress])
- case .aborted:
- sink?(["onDfuAborted": deviceAddress])
- pendingResult?(FlutterError(code: "DFU_ABORTED", message: "DFU ABORTED by user", details: "device address: \(deviceAddress!)"))
- pendingResult = nil
- case .connecting:
- sink?(["onDeviceConnecting":deviceAddress])
- case .starting:
- sink?(["onDfuProcessStarting":deviceAddress])
- case .enablingDfuMode:
- sink?(["onEnablingDfuMode":deviceAddress])
- case .validating:
- sink?(["onFirmwareValidating":deviceAddress])
- case .uploading:
- sink?(["onFirmwareUploading":deviceAddress])
- }
- }
-
- public func dfuError(_ error: DFUError, didOccurWithMessage message: String) {
- sink?(["onError":["deviceAddress": deviceAddress!, "error": error.rawValue, "errorType":error.rawValue, "message": message]])
- pendingResult?(FlutterError(code: "\(error.rawValue)", message: "DFU FAILED: \(message)", details: "Address: \(deviceAddress!), Error type \(error.rawValue)"))
- pendingResult = nil
- }
-
- //MARK: DFUProgressDelegate
- public func dfuProgressDidChange(for part: Int, outOf totalParts: Int, to progress: Int, currentSpeedBytesPerSecond: Double, avgSpeedBytesPerSecond: Double) {
- sink?(["onProgressChanged":["deviceAddress": deviceAddress!, "percent": progress, "speed":currentSpeedBytesPerSecond, "avgSpeed": avgSpeedBytesPerSecond, "currentPart": part, "partsTotal": totalParts]])
- }
-
- //MARK: - LoggerDelegate
- public func logWith(_ level: LogLevel, message: String) {
- //print("\(level.name()): \(message)")
- }
- }
|