Skip to content

Commit

Permalink
Merge pull request juliansteenbakker#77 from Knightro63/master
Browse files Browse the repository at this point in the history
Macos implementation
  • Loading branch information
juliansteenbakker authored Feb 12, 2023
2 parents 2ff98b5 + c07a5a3 commit 4f0264e
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 7 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
[![pub package](https://img.shields.io/pub/v/nordic_dfu.svg)](https://pub.dev/packages/nordic_dfu)
[![mobile_scanner](https://github.com/juliansteenbakker/nordic_dfu/actions/workflows/flutter_format.yml/badge.svg)](https://github.com/juliansteenbakker/nordic_dfu/actions/workflows/flutter_format.yml)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/juliansteenbakker?label=sponsor%20me%20)](https://github.com/sponsors/juliansteenbakker)

## 5.0.0 Breaking changes!
From version 5.0.0, the callbacks are defined as function parameters in NordicDfu().startDfu().
Please see the example app for more information.
## 6.0.0
Version 6.0.0 adds support for macos.

Fork from [flutter_nordic_dfu](https://pub.dev/packages/flutter_nordic_dfu) and updated with latest dependencies.

This library allows you to do a Device Firmware Update (DFU) of your nrf51 or
nrf52 chip from Nordic Semiconductor. It works for Android and iOS.
nrf52 chip from Nordic Semiconductor. It works for Android, iOS, and MacOS.

This is the implementation of the reference "[react-native-nordic-dfu](https://github.com/Pilloxa/react-native-nordic-dfu)"

Expand Down
2 changes: 1 addition & 1 deletion example/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'com.android.tools.build:gradle:7.4.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
182 changes: 182 additions & 0 deletions macos/Classes/SwiftNordicDfuPlugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import FlutterMacOS
import AppKit
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: filePath, 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
}

do{
let firmware = try DFUFirmware(urlToZipFile: URL(fileURLWithPath: filePath))



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)
}
catch{
result(FlutterError(code: "DFU_FIRMWARE_NOT_FOUND", message: "Could not dfu zip file", details: nil))
return
}
}

// 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)")
}
}
25 changes: 25 additions & 0 deletions macos/nordic_dfu.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint hello.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'nordic_dfu'
s.version = '1.0.0'
s.summary = 'MACOS DFU plugin for flutter.'
s.description = <<-DESC
A new Flutter plugin project.
DESC
s.homepage = 'http://www.timeyaa.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Timeyaa' => 'fengqiangboy@timeyaa.com' }

s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.swift_version = '5.4'
s.dependency 'FlutterMacOS'
s.dependency 'iOSDFULibrary', '~> 4.13.0'

s.platform = :osx, '10.14'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
end
4 changes: 3 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: nordic_dfu
description: This library allows you to do a Device Firmware Update (DFU) of your nrf51 or nrf52 chip from Nordic Semiconductor. Fork of flutter-nordic-dfu.
version: 5.2.1
version: 6.0.0
homepage: https://github.com/juliansteenbakker/nordic_dfu

environment:
Expand All @@ -22,3 +22,5 @@ flutter:
pluginClass: NordicDfuPlugin
ios:
pluginClass: NordicDfuPlugin
macos:
pluginClass: NordicDfuPlugin

0 comments on commit 4f0264e

Please sign in to comment.