Add error in connection updates (#89)

* Add error in connection updates

* implement windows

* Update changelog and Docs

* minor fix in connect()

* Add default timeout in connect

* Document new pair and connect API

* Fix Connect and Pair apis to throw proper errors and Improve logging

* remove import

* Rename PairingException and update Exception doc

* Update changelog

* Update comments

---------

Co-authored-by: Foti Dim <fdimanidis@gmail.com>
This commit is contained in:
Rohit Sangwan
2024-09-27 02:38:25 +05:30
committed by GitHub
parent f41aceaa0e
commit b77d0aaeb4
26 changed files with 851 additions and 698 deletions
+6 -3
View File
@@ -1,8 +1,11 @@
## 0.13.0
* BREAKING CHANGE: `scanFilter` filters are now in OR relation
* BREAKING CHANGE: `manufacturerDataHead` is removed from `BleDevice`
* BREAKING CHANGE: rename `WebConfig` to `WebOptions`
* BREAKING CHANGE: rename `ManufacturerDataFilter.data` to `ManufacturerDataFilter.payload`
* BREAKING CHANGE: `WebConfig` is now `WebOptions`
* BREAKING CHANGE: `ManufacturerDataFilter.data` is now `ManufacturerDataFilter.payload`
* BREAKING CHANGE: `connect()` does not return a boolean anymore. It will throw error on connection failure
* BREAKING CHANGE: `pair()` does not return a boolean anymore. It will throw error on connection failure
* BREAKING CHANGE: `onConnectionChange` returns error as well
* BREAKING CHANGE: rename in-app pairing capabilities
* Deprecation: `manufacturerData` is deprecated in BleDevice and will be removed in the future
* Improve `scanFilter` handling
@@ -11,6 +14,7 @@
* Auto convert all services passed to `getSystemDevices()`
* Return false for receivesAdvertisements on Linux/Web
* Add 1s delay in discoverServices on Linux
* Add `connectionStream` API to get connection updates as stream
## 0.12.0
* BREAKING CHANGE: `unPair` is now `unpair`
@@ -20,7 +24,6 @@
* Add `PlatformConfig` property in `StartScan`
* Add `WebConfig` property in `PlatformConfig`
* Fix notifications for characteristics without cccd on Android
* Add `connectionStream` API to get connection updates as stream
* Promote Linux to stable
## 0.11.1
+2 -2
View File
@@ -153,8 +153,8 @@ UniversalBle.connect(deviceId);
UniversalBle.disconnect(deviceId);
// Get connection/disconnection updates
UniversalBle.onConnectionChange = (String deviceId, bool isConnected) {
debugPrint('OnConnectionChange $deviceId, $isConnected');
UniversalBle.onConnectionChange = (String deviceId, bool isConnected, String? error) {
debugPrint('OnConnectionChange $deviceId, $isConnected Error: $error');
}
// Get current connection state
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v21.1.0), do not edit directly.
// Autogenerated from Pigeon (v22.4.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -53,19 +53,18 @@ data class UniversalBleScanResult (
val name: String? = null,
val isPaired: Boolean? = null,
val rssi: Long? = null,
val manufacturerDataList: List<UniversalManufacturerData?>? = null,
val services: List<String?>? = null
) {
val manufacturerDataList: List<UniversalManufacturerData>? = null,
val services: List<String>? = null
)
{
companion object {
@Suppress("LocalVariableName")
fun fromList(__pigeon_list: List<Any?>): UniversalBleScanResult {
val deviceId = __pigeon_list[0] as String
val name = __pigeon_list[1] as String?
val isPaired = __pigeon_list[2] as Boolean?
val rssi = __pigeon_list[3].let { num -> if (num is Int) num.toLong() else num as Long? }
val manufacturerDataList = __pigeon_list[4] as List<UniversalManufacturerData?>?
val services = __pigeon_list[5] as List<String?>?
fun fromList(pigeonVar_list: List<Any?>): UniversalBleScanResult {
val deviceId = pigeonVar_list[0] as String
val name = pigeonVar_list[1] as String?
val isPaired = pigeonVar_list[2] as Boolean?
val rssi = pigeonVar_list[3] as Long?
val manufacturerDataList = pigeonVar_list[4] as List<UniversalManufacturerData>?
val services = pigeonVar_list[5] as List<String>?
return UniversalBleScanResult(deviceId, name, isPaired, rssi, manufacturerDataList, services)
}
}
@@ -84,14 +83,13 @@ data class UniversalBleScanResult (
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalBleService (
val uuid: String,
val characteristics: List<UniversalBleCharacteristic?>? = null
) {
val characteristics: List<UniversalBleCharacteristic>? = null
)
{
companion object {
@Suppress("LocalVariableName")
fun fromList(__pigeon_list: List<Any?>): UniversalBleService {
val uuid = __pigeon_list[0] as String
val characteristics = __pigeon_list[1] as List<UniversalBleCharacteristic?>?
fun fromList(pigeonVar_list: List<Any?>): UniversalBleService {
val uuid = pigeonVar_list[0] as String
val characteristics = pigeonVar_list[1] as List<UniversalBleCharacteristic>?
return UniversalBleService(uuid, characteristics)
}
}
@@ -106,14 +104,13 @@ data class UniversalBleService (
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalBleCharacteristic (
val uuid: String,
val properties: List<Long?>
) {
val properties: List<Long>
)
{
companion object {
@Suppress("LocalVariableName")
fun fromList(__pigeon_list: List<Any?>): UniversalBleCharacteristic {
val uuid = __pigeon_list[0] as String
val properties = __pigeon_list[1] as List<Long?>
fun fromList(pigeonVar_list: List<Any?>): UniversalBleCharacteristic {
val uuid = pigeonVar_list[0] as String
val properties = pigeonVar_list[1] as List<Long>
return UniversalBleCharacteristic(uuid, properties)
}
}
@@ -131,17 +128,16 @@ data class UniversalBleCharacteristic (
* Generated class from Pigeon that represents data sent in messages.
*/
data class UniversalScanFilter (
val withServices: List<String?>,
val withNamePrefix: List<String?>,
val withManufacturerData: List<UniversalManufacturerDataFilter?>
) {
val withServices: List<String>,
val withNamePrefix: List<String>,
val withManufacturerData: List<UniversalManufacturerDataFilter>
)
{
companion object {
@Suppress("LocalVariableName")
fun fromList(__pigeon_list: List<Any?>): UniversalScanFilter {
val withServices = __pigeon_list[0] as List<String?>
val withNamePrefix = __pigeon_list[1] as List<String?>
val withManufacturerData = __pigeon_list[2] as List<UniversalManufacturerDataFilter?>
fun fromList(pigeonVar_list: List<Any?>): UniversalScanFilter {
val withServices = pigeonVar_list[0] as List<String>
val withNamePrefix = pigeonVar_list[1] as List<String>
val withManufacturerData = pigeonVar_list[2] as List<UniversalManufacturerDataFilter>
return UniversalScanFilter(withServices, withNamePrefix, withManufacturerData)
}
}
@@ -159,14 +155,13 @@ data class UniversalManufacturerDataFilter (
val companyIdentifier: Long,
val data: ByteArray? = null,
val mask: ByteArray? = null
) {
)
{
companion object {
@Suppress("LocalVariableName")
fun fromList(__pigeon_list: List<Any?>): UniversalManufacturerDataFilter {
val companyIdentifier = __pigeon_list[0].let { num -> if (num is Int) num.toLong() else num as Long }
val data = __pigeon_list[1] as ByteArray?
val mask = __pigeon_list[2] as ByteArray?
fun fromList(pigeonVar_list: List<Any?>): UniversalManufacturerDataFilter {
val companyIdentifier = pigeonVar_list[0] as Long
val data = pigeonVar_list[1] as ByteArray?
val mask = pigeonVar_list[2] as ByteArray?
return UniversalManufacturerDataFilter(companyIdentifier, data, mask)
}
}
@@ -183,13 +178,12 @@ data class UniversalManufacturerDataFilter (
data class UniversalManufacturerData (
val companyIdentifier: Long,
val data: ByteArray
) {
)
{
companion object {
@Suppress("LocalVariableName")
fun fromList(__pigeon_list: List<Any?>): UniversalManufacturerData {
val companyIdentifier = __pigeon_list[0].let { num -> if (num is Int) num.toLong() else num as Long }
val data = __pigeon_list[1] as ByteArray
fun fromList(pigeonVar_list: List<Any?>): UniversalManufacturerData {
val companyIdentifier = pigeonVar_list[0] as Long
val data = pigeonVar_list[1] as ByteArray
return UniversalManufacturerData(companyIdentifier, data)
}
}
@@ -200,7 +194,7 @@ data class UniversalManufacturerData (
)
}
}
private object UniversalBlePigeonCodec : StandardMessageCodec() {
private open class UniversalBlePigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
129.toByte() -> {
@@ -294,7 +288,7 @@ interface UniversalBlePlatformChannel {
companion object {
/** The codec used by UniversalBlePlatformChannel. */
val codec: MessageCodec<Any?> by lazy {
UniversalBlePigeonCodec
UniversalBlePigeonCodec()
}
/** Sets up an instance of `UniversalBlePlatformChannel` to handle messages through the `binaryMessenger`. */
@JvmOverloads
@@ -414,7 +408,7 @@ interface UniversalBlePlatformChannel {
val deviceIdArg = args[0] as String
val serviceArg = args[1] as String
val characteristicArg = args[2] as String
val bleInputPropertyArg = args[3].let { num -> if (num is Int) num.toLong() else num as Long }
val bleInputPropertyArg = args[3] as Long
api.setNotifiable(deviceIdArg, serviceArg, characteristicArg, bleInputPropertyArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
@@ -476,7 +470,7 @@ interface UniversalBlePlatformChannel {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val deviceIdArg = args[0] as String
val expectedMtuArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long }
val expectedMtuArg = args[1] as Long
api.requestMtu(deviceIdArg, expectedMtuArg) { result: Result<Long> ->
val error = result.exceptionOrNull()
if (error != null) {
@@ -500,7 +494,7 @@ interface UniversalBlePlatformChannel {
val serviceArg = args[1] as String
val characteristicArg = args[2] as String
val valueArg = args[3] as ByteArray
val bleOutputPropertyArg = args[4].let { num -> if (num is Int) num.toLong() else num as Long }
val bleOutputPropertyArg = args[4] as Long
api.writeValue(deviceIdArg, serviceArg, characteristicArg, valueArg, bleOutputPropertyArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
@@ -621,7 +615,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
companion object {
/** The codec used by UniversalBleCallbackChannel. */
val codec: MessageCodec<Any?> by lazy {
UniversalBlePigeonCodec
UniversalBlePigeonCodec()
}
}
fun onAvailabilityChanged(stateArg: Long, callback: (Result<Unit>) -> Unit)
@@ -692,12 +686,12 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
}
}
}
fun onConnectionChanged(deviceIdArg: String, connectedArg: Boolean, callback: (Result<Unit>) -> Unit)
fun onConnectionChanged(deviceIdArg: String, connectedArg: Boolean, errorArg: String?, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(deviceIdArg, connectedArg)) {
channel.send(listOf(deviceIdArg, connectedArg, errorArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -245,6 +245,82 @@ val DeviceDisconnectedError: FlutterError = FlutterError(
null
)
fun Int.parseHciErrorCode(): String? {
return when (this) {
BluetoothGatt.GATT_SUCCESS -> null
0x01 -> "Unknown HCI Command"
0x02 -> "Unknown Connection Identifier"
0x03 -> "Hardware Failure"
0x04 -> "Page Timeout"
0x05 -> "Authentication Failure"
0x06 -> "PIN or Key Missing"
0x07 -> "Memory Capacity Exceeded"
0x08 -> "Connection Timeout"
0x09 -> "Connection Limit Exceeded"
0x0A -> "Synchronous Connection Limit To A Device Exceeded"
0x0B -> "Connection Already Exists"
0x0C -> "Command Disallowed"
0x0D -> "Connection Rejected due to Limited Resources"
0x0E -> "Connection Rejected Due To Security Reasons"
0x0F -> "Connection Rejected due to Unacceptable BD_ADDR"
0x10 -> "Connection Accept Timeout Exceeded"
0x11 -> "Unsupported Feature or Parameter Value"
0x12 -> "Invalid HCI Command Parameters"
0x13 -> "Remote User Terminated Connection"
0x14 -> "Remote Device Terminated Connection due to Low Resources"
0x15 -> "Remote Device Terminated Connection due to Power Off"
0x16 -> "Connection Terminated By Local Host"
0x17 -> "Repeated Attempts"
0x18 -> "Pairing Not Allowed"
0x19 -> "Unknown LMP PDU"
0x1A -> "Unsupported Remote Feature / Unsupported LMP Feature"
0x1B -> "SCO Offset Rejected"
0x1C -> "SCO Interval Rejected"
0x1D -> "SCO Air Mode Rejected"
0x1E -> "Invalid LMP Parameters / Invalid LL Parameters"
0x1F -> "Unspecified Error"
0x20 -> "Unsupported LMP Parameter Value / Unsupported LL Parameter Value"
0x21 -> "Role Change Not Allowed"
0x22 -> "LMP Response Timeout / LL Response Timeout"
0x23 -> "LMP Error Transaction Collision / LL Procedure Collision"
0x24 -> "LMP PDU Not Allowed"
0x25 -> "Encryption Mode Not Acceptable"
0x26 -> "Link Key cannot be Changed"
0x27 -> "Requested QoS Not Supported"
0x28 -> "Instant Passed"
0x29 -> "Pairing With Unit Key Not Supported"
0x2A -> "Different Transaction Collision"
0x2B -> "Reserved for future use"
0x2C -> "QoS Unacceptable Parameter"
0x2D -> "QoS Rejected"
0x2E -> "Channel Classification Not Supported"
0x2F -> "Insufficient Security"
0x30 -> "Parameter Out Of Mandatory Range"
0x31 -> "Reserved for future use"
0x32 -> "Role Switch Pending"
0x33 -> "Reserved for future use"
0x34 -> "Reserved Slot Violation"
0x35 -> "Role Switch Failed"
0x36 -> "Extended Inquiry Response Too Large"
0x37 -> "Secure Simple Pairing Not Supported By Host"
0x38 -> "Host Busy - Pairing"
0x39 -> "Connection Rejected due to No Suitable Channel Found"
0x3A -> "Controller Busy"
0x3B -> "Unacceptable Connection Parameters"
0x3C -> "Advertising Timeout"
0x3D -> "Connection Terminated due to MIC Failure"
0x3E -> "Connection Failed to be Established / Synchronization Timeout"
0x3F -> "MAC Connection Failed"
0x40 -> "Coarse Clock Adjustment Rejected but Will Try to Adjust Using Clock Dragging"
0x41 -> "Type0 Submap Not Defined"
0x42 -> "Unknown Advertising Identifier"
0x43 -> "Limit Reached"
0x44 -> "Operation Cancelled by Host"
0x45 -> "Packet Too Long"
else -> "Unknown Error $this"
}
}
// Future result classes
class DiscoverServicesFuture(
val deviceId: String,
@@ -162,7 +162,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (currentState == BluetoothGatt.STATE_CONNECTED) {
Log.e(TAG, "$deviceId Already connected")
mainThreadHandler?.post {
callbackChannel?.onConnectionChanged(deviceId, true) {}
callbackChannel?.onConnectionChanged(deviceId, true, null) {}
}
return
} else if (currentState == BluetoothGatt.STATE_CONNECTING) {
@@ -864,19 +864,22 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
devicesStateMap[gatt.device.address] = newState
Log.d(TAG, "onConnectionStateChange-> Status: ${status}, NewState: $newState")
Log.d(
TAG,
"onConnectionStateChange-> Status: $status ${status.parseHciErrorCode()}, NewState: $newState"
)
if (newState == BluetoothGatt.STATE_CONNECTED) {
mainThreadHandler?.post {
callbackChannel?.onConnectionChanged(
gatt.device.address, true
gatt.device.address, true, status.parseHciErrorCode()
) {}
}
} else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
cleanConnection(gatt)
mainThreadHandler?.post {
callbackChannel?.onConnectionChanged(
gatt.device.address, false
gatt.device.address, false, status.parseHciErrorCode()
) {}
}
}
+51 -38
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v21.1.0), do not edit directly.
// Autogenerated from Pigeon (v22.4.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import Foundation
@@ -74,17 +74,19 @@ struct UniversalBleScanResult {
var name: String? = nil
var isPaired: Bool? = nil
var rssi: Int64? = nil
var manufacturerDataList: [UniversalManufacturerData?]? = nil
var services: [String?]? = nil
var manufacturerDataList: [UniversalManufacturerData]? = nil
var services: [String]? = nil
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ __pigeon_list: [Any?]) -> UniversalBleScanResult? {
let deviceId = __pigeon_list[0] as! String
let name: String? = nilOrValue(__pigeon_list[1])
let isPaired: Bool? = nilOrValue(__pigeon_list[2])
let rssi: Int64? = isNullish(__pigeon_list[3]) ? nil : (__pigeon_list[3] is Int64? ? __pigeon_list[3] as! Int64? : Int64(__pigeon_list[3] as! Int32))
let manufacturerDataList: [UniversalManufacturerData?]? = nilOrValue(__pigeon_list[4])
let services: [String?]? = nilOrValue(__pigeon_list[5])
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBleScanResult? {
let deviceId = pigeonVar_list[0] as! String
let name: String? = nilOrValue(pigeonVar_list[1])
let isPaired: Bool? = nilOrValue(pigeonVar_list[2])
let rssi: Int64? = nilOrValue(pigeonVar_list[3])
let manufacturerDataList: [UniversalManufacturerData]? = nilOrValue(pigeonVar_list[4])
let services: [String]? = nilOrValue(pigeonVar_list[5])
return UniversalBleScanResult(
deviceId: deviceId,
@@ -110,12 +112,14 @@ struct UniversalBleScanResult {
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalBleService {
var uuid: String
var characteristics: [UniversalBleCharacteristic?]? = nil
var characteristics: [UniversalBleCharacteristic]? = nil
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ __pigeon_list: [Any?]) -> UniversalBleService? {
let uuid = __pigeon_list[0] as! String
let characteristics: [UniversalBleCharacteristic?]? = nilOrValue(__pigeon_list[1])
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBleService? {
let uuid = pigeonVar_list[0] as! String
let characteristics: [UniversalBleCharacteristic]? = nilOrValue(pigeonVar_list[1])
return UniversalBleService(
uuid: uuid,
@@ -133,12 +137,14 @@ struct UniversalBleService {
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalBleCharacteristic {
var uuid: String
var properties: [Int64?]
var properties: [Int64]
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ __pigeon_list: [Any?]) -> UniversalBleCharacteristic? {
let uuid = __pigeon_list[0] as! String
let properties = __pigeon_list[1] as! [Int64?]
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBleCharacteristic? {
let uuid = pigeonVar_list[0] as! String
let properties = pigeonVar_list[1] as! [Int64]
return UniversalBleCharacteristic(
uuid: uuid,
@@ -157,15 +163,17 @@ struct UniversalBleCharacteristic {
///
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalScanFilter {
var withServices: [String?]
var withNamePrefix: [String?]
var withManufacturerData: [UniversalManufacturerDataFilter?]
var withServices: [String]
var withNamePrefix: [String]
var withManufacturerData: [UniversalManufacturerDataFilter]
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ __pigeon_list: [Any?]) -> UniversalScanFilter? {
let withServices = __pigeon_list[0] as! [String?]
let withNamePrefix = __pigeon_list[1] as! [String?]
let withManufacturerData = __pigeon_list[2] as! [UniversalManufacturerDataFilter?]
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalScanFilter? {
let withServices = pigeonVar_list[0] as! [String]
let withNamePrefix = pigeonVar_list[1] as! [String]
let withManufacturerData = pigeonVar_list[2] as! [UniversalManufacturerDataFilter]
return UniversalScanFilter(
withServices: withServices,
@@ -188,11 +196,13 @@ struct UniversalManufacturerDataFilter {
var data: FlutterStandardTypedData? = nil
var mask: FlutterStandardTypedData? = nil
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ __pigeon_list: [Any?]) -> UniversalManufacturerDataFilter? {
let companyIdentifier = __pigeon_list[0] is Int64 ? __pigeon_list[0] as! Int64 : Int64(__pigeon_list[0] as! Int32)
let data: FlutterStandardTypedData? = nilOrValue(__pigeon_list[1])
let mask: FlutterStandardTypedData? = nilOrValue(__pigeon_list[2])
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalManufacturerDataFilter? {
let companyIdentifier = pigeonVar_list[0] as! Int64
let data: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[1])
let mask: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[2])
return UniversalManufacturerDataFilter(
companyIdentifier: companyIdentifier,
@@ -214,10 +224,12 @@ struct UniversalManufacturerData {
var companyIdentifier: Int64
var data: FlutterStandardTypedData
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ __pigeon_list: [Any?]) -> UniversalManufacturerData? {
let companyIdentifier = __pigeon_list[0] is Int64 ? __pigeon_list[0] as! Int64 : Int64(__pigeon_list[0] as! Int32)
let data = __pigeon_list[1] as! FlutterStandardTypedData
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalManufacturerData? {
let companyIdentifier = pigeonVar_list[0] as! Int64
let data = pigeonVar_list[1] as! FlutterStandardTypedData
return UniversalManufacturerData(
companyIdentifier: companyIdentifier,
@@ -231,6 +243,7 @@ struct UniversalManufacturerData {
]
}
}
private class UniversalBlePigeonCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? {
switch type {
@@ -416,7 +429,7 @@ class UniversalBlePlatformChannelSetup {
let deviceIdArg = args[0] as! String
let serviceArg = args[1] as! String
let characteristicArg = args[2] as! String
let bleInputPropertyArg = args[3] is Int64 ? args[3] as! Int64 : Int64(args[3] as! Int32)
let bleInputPropertyArg = args[3] as! Int64
api.setNotifiable(deviceId: deviceIdArg, service: serviceArg, characteristic: characteristicArg, bleInputProperty: bleInputPropertyArg) { result in
switch result {
case .success:
@@ -470,7 +483,7 @@ class UniversalBlePlatformChannelSetup {
requestMtuChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let deviceIdArg = args[0] as! String
let expectedMtuArg = args[1] is Int64 ? args[1] as! Int64 : Int64(args[1] as! Int32)
let expectedMtuArg = args[1] as! Int64
api.requestMtu(deviceId: deviceIdArg, expectedMtu: expectedMtuArg) { result in
switch result {
case .success(let res):
@@ -491,7 +504,7 @@ class UniversalBlePlatformChannelSetup {
let serviceArg = args[1] as! String
let characteristicArg = args[2] as! String
let valueArg = args[3] as! FlutterStandardTypedData
let bleOutputPropertyArg = args[4] is Int64 ? args[4] as! Int64 : Int64(args[4] as! Int32)
let bleOutputPropertyArg = args[4] as! Int64
api.writeValue(deviceId: deviceIdArg, service: serviceArg, characteristic: characteristicArg, value: valueArg, bleOutputProperty: bleOutputPropertyArg) { result in
switch result {
case .success:
@@ -595,7 +608,7 @@ protocol UniversalBleCallbackChannelProtocol {
func onPairStateChange(deviceId deviceIdArg: String, isPaired isPairedArg: Bool, error errorArg: String?, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onScanResult(result resultArg: UniversalBleScanResult, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onValueChanged(deviceId deviceIdArg: String, characteristicId characteristicIdArg: String, value valueArg: FlutterStandardTypedData, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onConnectionChanged(deviceId deviceIdArg: String, connected connectedArg: Bool, completion: @escaping (Result<Void, PigeonError>) -> Void)
func onConnectionChanged(deviceId deviceIdArg: String, connected connectedArg: Bool, error errorArg: String?, completion: @escaping (Result<Void, PigeonError>) -> Void)
}
class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol {
private let binaryMessenger: FlutterBinaryMessenger
@@ -679,10 +692,10 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol {
}
}
}
func onConnectionChanged(deviceId deviceIdArg: String, connected connectedArg: Bool, completion: @escaping (Result<Void, PigeonError>) -> Void) {
func onConnectionChanged(deviceId deviceIdArg: String, connected connectedArg: Bool, error errorArg: String?, completion: @escaping (Result<Void, PigeonError>) -> Void) {
let channelName: String = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([deviceIdArg, connectedArg] as [Any?]) { response in
channel.sendMessage([deviceIdArg, connectedArg, errorArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
return
+2 -2
View File
@@ -104,11 +104,11 @@ extension CBManagerState {
}
extension Error {
func toFlutterError() -> FlutterError {
func toPigeonError() -> PigeonError {
let nsError = self as NSError
let errorCode: String = .init(nsError.code)
let errorDescription: String = nsError.localizedDescription
return FlutterError(code: errorCode, message: errorDescription, details: nil)
return PigeonError(code: errorCode, message: errorDescription, details: nil)
}
}
+34 -36
View File
@@ -55,7 +55,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
}
func enableBluetooth(completion: @escaping (Result<Bool, Error>) -> Void) {
completion(Result.failure(FlutterError(code: "NotSupported", message: nil, details: nil)))
completion(Result.failure(PigeonError(code: "NotSupported", message: nil, details: nil)))
}
func startScan(filter: UniversalScanFilter?) throws {
@@ -117,7 +117,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
characteristicReadFutures.removeAll { future in
if future.deviceId == deviceId {
future.result(
Result.failure(FlutterError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
Result.failure(PigeonError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
)
return true
}
@@ -126,7 +126,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
characteristicWriteFutures.removeAll { future in
if future.deviceId == deviceId {
future.result(
Result.failure(FlutterError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
Result.failure(PigeonError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
)
return true
}
@@ -135,7 +135,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
characteristicNotifyFutures.removeAll { future in
if future.deviceId == deviceId {
future.result(
Result.failure(FlutterError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
Result.failure(PigeonError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
)
return true
}
@@ -144,7 +144,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
discoverServicesFutures.removeAll { future in
if future.deviceId == deviceId {
future.result(
Result.failure(FlutterError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
Result.failure(PigeonError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
)
return true
}
@@ -156,7 +156,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func discoverServices(deviceId: String, completion: @escaping (Result<[UniversalBleService], Error>) -> Void) {
guard let peripheral = discoveredPeripherals[deviceId] else {
completion(
Result.failure(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil))
Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil))
)
return
}
@@ -193,22 +193,22 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func setNotifiable(deviceId: String, service: String, characteristic: String, bleInputProperty: Int64, completion: @escaping (Result<Void, any Error>) -> Void) {
guard let peripheral = discoveredPeripherals[deviceId] else {
completion(Result.failure(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
return
}
guard let gattCharacteristic = peripheral.getCharacteristic(characteristic, of: service) else {
completion(Result.failure(FlutterError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil)))
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil)))
return
}
if bleInputProperty == BleInputProperty.notification.rawValue && !gattCharacteristic.properties.contains(.notify) {
completion(Result.failure(FlutterError(code: "InvalidAction", message: "Characteristic does not support notify", details: nil)))
completion(Result.failure(PigeonError(code: "InvalidAction", message: "Characteristic does not support notify", details: nil)))
return
}
if bleInputProperty == BleInputProperty.indication.rawValue && !gattCharacteristic.properties.contains(.indicate) {
completion(Result.failure(FlutterError(code: "InvalidAction", message: "Characteristic does not support indicate", details: nil)))
completion(Result.failure(PigeonError(code: "InvalidAction", message: "Characteristic does not support indicate", details: nil)))
return
}
@@ -219,15 +219,15 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func readValue(deviceId: String, service: String, characteristic: String, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
guard let peripheral = discoveredPeripherals[deviceId] else {
completion(Result.failure(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
return
}
guard let gattCharacteristic = peripheral.getCharacteristic(characteristic, of: service) else {
completion(Result.failure(FlutterError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil)))
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil)))
return
}
if !gattCharacteristic.properties.contains(.read) {
completion(Result.failure(FlutterError(code: "InvalidAction", message: "Characteristic does not support read", details: nil)))
completion(Result.failure(PigeonError(code: "InvalidAction", message: "Characteristic does not support read", details: nil)))
return
}
peripheral.readValue(for: gattCharacteristic)
@@ -236,11 +236,11 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func writeValue(deviceId: String, service: String, characteristic: String, value: FlutterStandardTypedData, bleOutputProperty: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
guard let peripheral = discoveredPeripherals[deviceId] else {
completion(Result.failure(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
return
}
guard let gattCharacteristic = peripheral.getCharacteristic(characteristic, of: service) else {
completion(Result.failure(FlutterError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil)))
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil)))
return
}
@@ -248,12 +248,12 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
if type == CBCharacteristicWriteType.withResponse {
if !gattCharacteristic.properties.contains(.write) {
completion(Result.failure(FlutterError(code: "InvalidAction", message: "Characteristic does not support write withResponse", details: nil)))
completion(Result.failure(PigeonError(code: "InvalidAction", message: "Characteristic does not support write withResponse", details: nil)))
return
}
} else if type == CBCharacteristicWriteType.withoutResponse {
if !gattCharacteristic.properties.contains(.writeWithoutResponse) {
completion(Result.failure(FlutterError(code: "InvalidAction", message: "Characteristic does not support write withoutResponse", details: nil)))
completion(Result.failure(PigeonError(code: "InvalidAction", message: "Characteristic does not support write withoutResponse", details: nil)))
return
}
}
@@ -269,7 +269,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func requestMtu(deviceId: String, expectedMtu _: Int64, completion: @escaping (Result<Int64, Error>) -> Void) {
guard let peripheral = discoveredPeripherals[deviceId] else {
completion(Result.failure(FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
return
}
let mtu = peripheral.maximumWriteValueLength(for: CBCharacteristicWriteType.withoutResponse)
@@ -279,15 +279,15 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
}
func isPaired(deviceId _: String, completion: @escaping (Result<Bool, Error>) -> Void) {
completion(Result.failure(FlutterError(code: "NotSupported", message: nil, details: nil)))
completion(Result.failure(PigeonError(code: "NotSupported", message: nil, details: nil)))
}
func pair(deviceId _: String, completion: @escaping (Result<Bool, Error>) -> Void) {
completion(Result.failure(FlutterError(code: "Implemented in Dart", message: nil, details: nil)))
completion(Result.failure(PigeonError(code: "Implemented in Dart", message: nil, details: nil)))
}
func unPair(deviceId _: String) throws {
throw FlutterError(code: "NotSupported", message: nil, details: nil)
throw PigeonError(code: "NotSupported", message: nil, details: nil)
}
func getSystemDevices(withServices: [String], completion: @escaping (Result<[UniversalBleScanResult], Error>) -> Void) {
@@ -345,17 +345,17 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
}
public func centralManager(_: CBCentralManager, didConnect peripheral: CBPeripheral) {
callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, connected: true) { _ in }
callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, connected: true, error: nil) { _ in }
}
public func centralManager(_: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error _: Error?) {
callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, connected: false) { _ in }
// Cleanup on disconnect
callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, connected: false, error: nil) { _ in }
cleanUpConnection(deviceId: peripheral.uuid.uuidString)
}
public func centralManager(_: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("Failed to connect: \(peripheral.uuid.uuidString): \(String(describing: error))")
callbackChannel.onConnectionChanged(deviceId: peripheral.uuid.uuidString, connected: false, error: error?.localizedDescription) { _ in }
cleanUpConnection(deviceId: peripheral.uuid.uuidString)
}
public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices _: Error?) {
@@ -397,8 +397,8 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
characteristicWriteFutures.removeAll { future in
if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr {
if let flutterError = error?.toFlutterError() {
future.result(Result.failure(flutterError))
if let pigeonError = error?.toPigeonError() {
future.result(Result.failure(pigeonError))
} else {
future.result(Result.success({}()))
}
@@ -411,8 +411,8 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
public func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
characteristicNotifyFutures.removeAll { future in
if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr {
if let flutterError = error?.toFlutterError() {
future.result(Result.failure(flutterError))
if let pigeonError = error?.toPigeonError() {
future.result(Result.failure(pigeonError))
} else {
future.result(Result.success({}()))
}
@@ -437,13 +437,13 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
// Update futures for readValue
characteristicReadFutures.removeAll { future in
if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr {
if let flutterError = error?.toFlutterError() {
future.result(Result.failure(flutterError))
if let pigeonError = error?.toPigeonError() {
future.result(Result.failure(pigeonError))
} else {
if let characteristicValue = characteristic.value {
future.result(Result.success(FlutterStandardTypedData(bytes: characteristicValue)))
} else {
future.result(Result.failure(FlutterError(code: "ReadFailed", message: "No value", details: nil)))
future.result(Result.failure(PigeonError(code: "ReadFailed", message: "No value", details: nil)))
}
}
return true
@@ -456,7 +456,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
extension String {
func getPeripheral() throws -> CBPeripheral {
guard let peripheral = discoveredPeripherals[self] else {
throw FlutterError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)
throw PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)
}
return peripheral
}
@@ -466,11 +466,9 @@ extension [String] {
func toCBUUID() throws -> [CBUUID] {
return try compactMap { serviceUUID in
guard UUID(uuidString: serviceUUID.validFullUUID) != nil else {
throw FlutterError(code: "IllegalArgument", message: "Invalid service UUID:\(serviceUUID)", details: nil)
throw PigeonError(code: "IllegalArgument", message: "Invalid service UUID:\(serviceUUID)", details: nil)
}
return CBUUID(string: serviceUUID)
}
}
}
extension FlutterError: Error {}
@@ -59,8 +59,14 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
});
}
void _handleConnectionChange(String deviceId, bool isConnected) {
print('_handleConnectionChange $deviceId, $isConnected');
void _handleConnectionChange(
String deviceId,
bool isConnected,
String? error,
) {
print(
'_handleConnectionChange $deviceId, $isConnected ${error != null ? 'Error: $error' : ''}',
);
setState(() {
if (deviceId == widget.deviceId) {
this.isConnected = isConnected;
@@ -248,12 +254,12 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
enabled: !isConnected,
onPressed: () async {
try {
bool connected = await UniversalBle.connect(
await UniversalBle.connect(
widget.deviceId,
);
_addLog("ConnectionResult", connected);
_addLog("ConnectionResult", true);
} catch (e) {
_addLog('ConnectError', e);
_addLog('ConnectError (${e.runtimeType})', e);
}
},
),
@@ -405,14 +411,18 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
PlatformButton(
enabled: BleCapabilities.supportsAllPairingKinds,
onPressed: () async {
bool? pairingResult = await UniversalBle.pair(
widget.deviceId,
// pairingCommand: BleCommand(
// service: "",
// characteristic: "",
// ),
);
_addLog("Pairing Result", pairingResult);
try {
await UniversalBle.pair(
widget.deviceId,
// pairingCommand: BleCommand(
// service: "",
// characteristic: "",
// ),
);
_addLog("Pairing Result", true);
} catch (e) {
_addLog('PairError (${e.runtimeType})', e);
}
},
text: 'Pair',
),
@@ -0,0 +1,9 @@
class BleConnectionUpdate {
final bool isConnected;
final String? error;
BleConnectionUpdate({
required this.isConnected,
this.error,
});
}
+13
View File
@@ -7,6 +7,11 @@ class BleService {
BleService(String uuid, this.characteristics) {
this.uuid = BleUuidParser.string(uuid);
}
@override
String toString() {
return 'BleService{uuid: $uuid, characteristics: $characteristics}';
}
}
class BleCharacteristic {
@@ -16,6 +21,11 @@ class BleCharacteristic {
BleCharacteristic(String uuid, this.properties) {
this.uuid = BleUuidParser.string(uuid);
}
@override
String toString() {
return 'BleCharacteristic{uuid: $uuid, properties: $properties}';
}
}
enum CharacteristicProperty {
@@ -32,4 +42,7 @@ enum CharacteristicProperty {
factory CharacteristicProperty.parse(int index) =>
CharacteristicProperty.values[index];
@override
String toString() => name;
}
+1
View File
@@ -1,3 +1,4 @@
export 'package:universal_ble/src/models/ble_connection_update.dart';
export 'package:universal_ble/src/models/manufacturer_data.dart';
export 'package:universal_ble/src/models/platform_config.dart';
export 'package:universal_ble/src/models/queue_type.dart';
+114 -75
View File
@@ -5,6 +5,7 @@ import 'package:universal_ble/src/ble_command_queue.dart';
import 'package:universal_ble/src/universal_ble_linux/universal_ble_linux.dart';
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart';
import 'package:universal_ble/src/universal_ble_web/universal_ble_web.dart';
import 'package:universal_ble/src/universal_logger.dart';
import 'package:universal_ble/universal_ble.dart';
class UniversalBle {
@@ -31,7 +32,7 @@ class UniversalBle {
/// [QueueType.none] will execute all commands in parallel.
static set queueType(QueueType queueType) {
_bleCommandQueue.queueType = queueType;
UniversalBlePlatform.logInfo('Queue ${queueType.name}');
UniversalLogger.logInfo('Queue ${queueType.name}');
}
/// Get Bluetooth availability state.
@@ -70,28 +71,37 @@ class UniversalBle {
}
/// Connection stream of a device
Stream<bool> connectionStream(String deviceId) =>
static Stream<BleConnectionUpdate> connectionStream(String deviceId) =>
_platform.connectionStream(deviceId);
/// Connect to a device.
/// It is advised to stop scanning before connecting.
/// It might throw errors if device is not connectable.
/// `connectionTimeout` is supported on Web only.
static Future<bool> connect(
/// It throws error if device connection fails.
/// Default connection timeout is 60 sec.
/// Can throw `ConnectionException` or `PlatformException`.
static Future<void> connect(
String deviceId, {
Duration? connectionTimeout,
}) async {
connectionTimeout ??= const Duration(seconds: 60);
StreamSubscription? connectionSubscription;
try {
Completer<bool> completer = Completer();
connectionSubscription =
_platform.connectionStream(deviceId).listen((bool event) {
connectionSubscription?.cancel();
if (!completer.isCompleted) {
completer.complete(event);
}
});
connectionSubscription = connectionStream(deviceId).listen(
(BleConnectionUpdate event) {
connectionSubscription?.cancel();
if (!completer.isCompleted) {
String? error = event.error;
if (error != null) {
completer.completeError(ConnectionException(error));
} else {
completer.complete(event.isConnected);
}
}
},
);
_platform
.connect(deviceId, connectionTimeout: connectionTimeout)
@@ -99,15 +109,14 @@ class UniversalBle {
(error) {
if (completer.isCompleted == false) {
connectionSubscription?.cancel();
completer.completeError(error);
completer.completeError(ConnectionException(error));
}
},
);
if (connectionTimeout != null) {
return await completer.future.timeout(connectionTimeout);
if (!await completer.future.timeout(connectionTimeout)) {
throw ConnectionException("Failed to connect");
}
return await completer.future;
} finally {
connectionSubscription?.cancel();
}
@@ -211,36 +220,65 @@ class UniversalBle {
static Future<bool?> isPaired(
String deviceId, {
BleCommand? pairingCommand,
Duration? connectionTimeout,
}) async {
if (BleCapabilities.hasSystemPairingApi) {
return _bleCommandQueue.queueCommand(
() => _platform.isPaired(deviceId),
deviceId: deviceId,
);
} else if (pairingCommand != null) {
return _connectAndExecuteBleCommand(deviceId, pairingCommand,
updateCallbackValue: false);
}
return null;
if (pairingCommand == null) {
UniversalLogger.logWarning("PairingCommand required to get result");
return null;
}
try {
await _connectAndExecuteBleCommand(
deviceId,
pairingCommand,
connectionTimeout: connectionTimeout,
updateCallbackValue: false,
);
// Because pairingCommand will be never null, so we wont get Unknown result here
return true;
} catch (e) {
UniversalLogger.logError("ExecuteBleCommandFailed: $e");
return false;
}
}
/// Pair a device.
///
/// It throws error if pairing fails.
///
/// On `Apple` and `Web`, it only works on devices with encrypted characteristics.
/// It returns null if there is no readable characteristic.
/// It is advised to pass a pairingCommand with an encrypted read or write characteristic.
/// When not passing a pairingCommand, you should afterwards use [isPaired] with a pairingCommand
/// to verify the pairing state.
///
/// You can optionally pass a pairingCommand if you know an encrypted read or write characteristic.
/// If you do, it returns true if it can successfully execute the command after pairing.
///
/// On `Web/Windows` and `Web/Linux`, it does not work for devices where `BleCapabilities.triggersConfirmOnlyPairing` is false.
static Future<bool?> pair(
/// On `Web/Windows` and `Web/Linux`, it does not work for devices that use `ConfirmOnly` pairing.
/// Can throw `PairingException`, `ConnectionException` or `PlatformException`.
static Future<void> pair(
String deviceId, {
BleCommand? pairingCommand,
Duration? connectionTimeout,
}) async {
if (BleCapabilities.hasSystemPairingApi) {
return _platform.pair(deviceId);
bool paired = await _platform.pair(deviceId);
if (!paired) throw PairingException();
} else {
if (pairingCommand == null) {
UniversalLogger.logWarning("PairingCommand required to get result");
}
await _connectAndExecuteBleCommand(
deviceId,
pairingCommand,
connectionTimeout: connectionTimeout,
);
}
return _connectAndExecuteBleCommand(deviceId, pairingCommand);
}
/// Unpair a device.
@@ -303,38 +341,32 @@ class UniversalBle {
}
}
static Future<bool?> _connectAndExecuteBleCommand(
static Future<void> _connectAndExecuteBleCommand(
String deviceId,
BleCommand? bleCommand, {
Duration? connectionTimeout,
bool updateCallbackValue = false,
}) async {
try {
if (await getConnectionState(deviceId) != BleConnectionState.connected) {
await connect(deviceId);
}
List<BleService> services = await discoverServices(deviceId);
if (bleCommand == null) {
await _attemptPairingReadingAll(deviceId, services);
return null;
} else {
bool commandResult =
await _executeBleCommand(deviceId, services, bleCommand);
if (updateCallbackValue) {
_platform.updatePairingState(deviceId, commandResult);
}
return commandResult;
}
} catch (e) {
UniversalBlePlatform.logInfo(
"FailedToPerform EncryptedCharOperation: $e",
// Try to connect first
if (await getConnectionState(deviceId) != BleConnectionState.connected) {
UniversalLogger.logInfo("Connecting to $deviceId");
await connect(
deviceId,
connectionTimeout: connectionTimeout,
);
if (updateCallbackValue) {
_platform.updatePairingState(deviceId, false);
}
return false;
}
List<BleService> services = await discoverServices(deviceId);
UniversalLogger.logInfo("Discovered services: ${services.length}");
if (bleCommand == null) {
// Just attempt pairing
await _attemptPairingReadingAll(deviceId, services);
return;
}
await _executeBleCommand(deviceId, services, bleCommand);
if (updateCallbackValue) _platform.updatePairingState(deviceId, true);
}
// Fire and forget, and do not rely on result
@@ -359,12 +391,13 @@ class UniversalBle {
}
}
} catch (_) {}
if (!containsReadCharacteristics) {
throw "No readable characteristic found";
throw PairingException("No readable characteristic found");
}
}
static Future<bool> _executeBleCommand(
static Future<void> _executeBleCommand(
String deviceId,
List<BleService> services,
BleCommand bleCommand,
@@ -384,7 +417,7 @@ class UniversalBle {
}
if (characteristic == null) {
return false;
throw PairingException("BleCommand not found in discovered services");
}
// Check if BleCommand Supports Read or Write
@@ -396,28 +429,34 @@ class UniversalBle {
bleOutputProperty = BleOutputProperty.withoutResponse;
} else if (!characteristic.properties
.contains(CharacteristicProperty.read)) {
return false;
throw PairingException(
"BleCommand does not support read or write operation",
);
}
Uint8List? value = bleCommand.writeValue;
if (value != null && bleOutputProperty != null) {
await writeValue(
deviceId,
bleCommand.service,
bleCommand.characteristic,
value,
bleOutputProperty,
);
} else {
// Fallback to read if supported
await readValue(
deviceId,
bleCommand.service,
bleCommand.characteristic,
timeout: const Duration(seconds: 30),
);
try {
if (value != null && bleOutputProperty != null) {
await writeValue(
deviceId,
bleCommand.service,
bleCommand.characteristic,
value,
bleOutputProperty,
);
} else {
// Fallback to read if supported
await readValue(
deviceId,
bleCommand.service,
bleCommand.characteristic,
timeout: const Duration(seconds: 30),
);
}
} catch (e) {
throw PairingException(e.toString());
}
return true;
}
/// Get updates of remaining items of a queue.
+35
View File
@@ -0,0 +1,35 @@
import 'package:flutter/services.dart';
class ConnectionException implements Exception {
late String message;
ConnectionException([dynamic error]) {
message = _errorParser(error);
}
@override
String toString() => message;
}
class PairingException implements Exception {
late String message;
PairingException([dynamic error]) {
message = _errorParser(error);
}
@override
String toString() => message;
}
String _errorParser(dynamic error) {
if (error == null) {
return "Failed";
} else if (error is PlatformException) {
return error.message ?? error.details ?? error.code;
} else if (error is String) {
return error;
} else {
return error.toString();
}
}
@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
import 'package:universal_ble/src/models/model_exports.dart';
import 'package:universal_ble/src/universal_ble_filter_util.dart';
import 'package:universal_ble/src/universal_ble_platform_interface.dart';
import 'package:universal_ble/src/universal_logger.dart';
class UniversalBleLinux extends UniversalBlePlatform {
UniversalBleLinux._();
@@ -45,10 +46,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
await _activeAdapter?.setPowered(true);
return _activeAdapter?.powered ?? false;
} catch (e) {
UniversalBlePlatform.logInfo(
'Error enabling bluetooth: $e',
isError: true,
);
UniversalLogger.logError('Error enabling bluetooth: $e');
return false;
}
}
@@ -113,10 +111,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
return true;
});
} catch (e) {
UniversalBlePlatform.logInfo(
"stopScan error: $e",
isError: true,
);
UniversalLogger.logError("stopScan error: $e");
}
}
@@ -159,7 +154,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
await device.propertiesChanged.firstWhere((element) {
if (element.contains(BluezProperty.connected)) {
if (!device.connected) {
UniversalBlePlatform.logInfo(
UniversalLogger.logInfo(
"DiscoverServicesFailed: Device disconnected",
);
return true;
@@ -167,7 +162,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
}
return element.contains(BluezProperty.servicesResolved);
}).timeout(const Duration(seconds: 10), onTimeout: () {
UniversalBlePlatform.logInfo(
UniversalLogger.logInfo(
"DiscoverServicesFailed: Timeout",
);
return [];
@@ -238,8 +233,9 @@ class UniversalBleLinux extends UniversalBlePlatform {
);
break;
default:
UniversalBlePlatform.logInfo(
"UnhandledCharValuePropertyChange: $property");
UniversalLogger.logInfo(
"UnhandledCharValuePropertyChange: $property",
);
}
}
});
@@ -351,8 +347,9 @@ class UniversalBleLinux extends UniversalBlePlatform {
.map((e) => e.uuid.toString())
.any((service) => withServices.contains(service));
} else {
UniversalBlePlatform.logInfo(
'Skipping: ${device.address}: Services not resolved yet.');
UniversalLogger.logInfo(
'Skipping: ${device.address}: Services not resolved yet.',
);
return false;
}
}).toList();
@@ -394,7 +391,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
_activeAdapter ??= _client.adapters.first;
UniversalBlePlatform.logInfo(
UniversalLogger.logInfo(
'BleAdapter: ${_activeAdapter?.name} - ${_activeAdapter?.address}',
);
@@ -411,7 +408,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
break;
case BluezProperty.propertyClass:
default:
UniversalBlePlatform.logInfo(
UniversalLogger.logInfo(
"UnhandledPropertyChanged: $property",
);
}
@@ -426,10 +423,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
_initializationCompleter?.complete();
_initializationCompleter = null;
} catch (e) {
UniversalBlePlatform.logInfo(
'Error initializing: $e',
isError: true,
);
UniversalLogger.logError('Error initializing: $e');
_initializationCompleter?.completeError(e);
await _client.close();
rethrow;
@@ -501,7 +495,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
case BluezProperty.manufacturerData:
break;
default:
UniversalBlePlatform.logInfo(
UniversalLogger.logInfo(
"UnhandledDevicePropertyChanged ${device.name} ${device.address}: $property",
);
break;
File diff suppressed because it is too large Load Diff
@@ -119,10 +119,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
) async {
var devices = await _channel.getSystemDevices(withServices ?? []);
return List<BleDevice>.from(
devices
.map((e) => e?.toBleDevice(isSystemDevice: true))
.where((e) => e != null)
.toList(),
devices.map((e) => e.toBleDevice(isSystemDevice: true)).toList(),
);
}
@@ -175,8 +172,8 @@ class _UniversalBleCallbackHandler extends UniversalBleCallbackChannel {
availabilityChange(AvailabilityState.parse(state));
@override
void onConnectionChanged(String deviceId, bool connected) =>
connectionChanged(deviceId, connected);
void onConnectionChanged(String deviceId, bool connected, String? error) =>
connectionChanged(deviceId, connected, error);
@override
void onScanResult(UniversalBleScanResult result) =>
@@ -200,9 +197,9 @@ extension _UniversalBleScanResultExtension on UniversalBleScanResult {
rssi: rssi,
isPaired: isPaired,
isSystemDevice: isSystemDevice,
services: services?.nonNulls.map(BleUuidParser.string).toList() ?? [],
manufacturerDataList: manufacturerDataList?.nonNulls
.map((e) => ManufacturerData(e.companyIdentifier, e.data))
services: services?.map(BleUuidParser.string).toList() ?? [],
manufacturerDataList: manufacturerDataList
?.map((e) => ManufacturerData(e.companyIdentifier, e.data))
.toList() ??
[],
);
+18 -16
View File
@@ -1,10 +1,11 @@
import 'dart:async';
import 'dart:developer';
import 'dart:typed_data';
import 'package:universal_ble/universal_ble.dart';
abstract class UniversalBlePlatform {
StreamController? _connectionStreamController;
StreamController<({String deviceId, bool isConnected, String? error})>?
_connectionStreamController;
final Map<String, bool> _pairStateMap = {};
Future<AvailabilityState> getBluetoothAvailabilityState();
@@ -57,21 +58,27 @@ abstract class UniversalBlePlatform {
bool receivesAdvertisements(String deviceId) => true;
Stream<bool> connectionStream(String deviceId) {
Stream<BleConnectionUpdate> connectionStream(String deviceId) {
_setupConnectionStreamIfRequired();
return _connectionStreamController!.stream
.where((event) => event.deviceId == deviceId)
.map((event) => event.isConnected);
.map((event) => BleConnectionUpdate(
isConnected: event.isConnected,
error: event.error,
));
}
void updateScanResult(BleDevice bleDevice) {
onScanResult?.call(bleDevice);
}
void updateConnection(String deviceId, bool isConnected) {
onConnectionChange?.call(deviceId, isConnected);
_connectionStreamController
?.add((deviceId: deviceId, isConnected: isConnected));
void updateConnection(String deviceId, bool isConnected, [String? error]) {
onConnectionChange?.call(deviceId, isConnected, error);
_connectionStreamController?.add((
deviceId: deviceId,
isConnected: isConnected,
error: error,
));
}
void updateCharacteristicValue(
@@ -97,17 +104,11 @@ abstract class UniversalBlePlatform {
OnAvailabilityChange? onAvailabilityChange;
OnPairingStateChange? onPairingStateChange;
static void logInfo(String message, {bool isError = false}) {
if (isError) message = '\x1B[31m$message\x1B[31m';
log(message, name: 'UniversalBle');
}
/// Creates an auto disposable streamController
void _setupConnectionStreamIfRequired() {
if (_connectionStreamController != null) return;
_connectionStreamController =
StreamController<({String deviceId, bool isConnected})>.broadcast();
_connectionStreamController = StreamController.broadcast();
// Auto dispose if no more subscribers
_connectionStreamController?.onCancel = () {
@@ -119,7 +120,8 @@ abstract class UniversalBlePlatform {
}
// Callback types
typedef OnConnectionChange = void Function(String deviceId, bool isConnected);
typedef OnConnectionChange = void Function(
String deviceId, bool isConnected, String? error);
typedef OnValueChange = void Function(
String deviceId, String characteristicId, Uint8List value);
@@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart';
import 'package:universal_ble/src/models/model_exports.dart';
import 'package:universal_ble/src/universal_ble_platform_interface.dart';
import 'package:universal_ble/src/universal_logger.dart';
class UniversalBleWeb extends UniversalBlePlatform {
static UniversalBleWeb? _instance;
@@ -127,10 +128,7 @@ class UniversalBleWeb extends UniversalBlePlatform {
device.advertisementsUseMemory = true;
await device.watchAdvertisements();
} catch (e) {
UniversalBlePlatform.logInfo(
"WebWatchAdvertisementError: $e",
isError: true,
);
UniversalLogger.logError("WebWatchAdvertisementError: $e");
}
}
@@ -395,9 +393,8 @@ class UniversalBleWeb extends UniversalBlePlatform {
}
if (optionalServices.isEmpty) {
UniversalBlePlatform.logInfo(
UniversalLogger.logError(
"OptionalServices list is empty on web, you have to specify services in the ScanFilter in order to be able to access those after connecting",
isError: true,
);
}
+24
View File
@@ -0,0 +1,24 @@
import 'dart:developer';
class UniversalLogger {
static void logInfo(message) {
log(
message.toString(),
name: 'UniversalBle:INFO',
);
}
static void logError(message) {
log(
'\x1B[31m$message\x1B[31m',
name: 'UniversalBle:ERROR',
);
}
static void logWarning(message) {
log(
'\x1B[33m$message\x1B[33m',
name: 'UniversalBle:WARN',
);
}
}
+1
View File
@@ -1,5 +1,6 @@
library universal_ble;
export 'package:universal_ble/src/universal_ble_exceptions.dart';
export 'package:universal_ble/src/universal_ble_platform_interface.dart';
export 'package:universal_ble/src/universal_ble.dart';
export 'package:universal_ble/src/models/model_exports.dart';
+8 -7
View File
@@ -99,6 +99,7 @@ abstract class UniversalBleCallbackChannel {
void onConnectionChanged(
String deviceId,
bool connected,
String? error,
);
}
@@ -107,8 +108,8 @@ class UniversalBleScanResult {
final String? name;
final bool? isPaired;
final int? rssi;
final List<UniversalManufacturerData?>? manufacturerDataList;
final List<String?>? services;
final List<UniversalManufacturerData>? manufacturerDataList;
final List<String>? services;
UniversalBleScanResult({
required this.name,
@@ -122,21 +123,21 @@ class UniversalBleScanResult {
class UniversalBleService {
String uuid;
List<UniversalBleCharacteristic?>? characteristics;
List<UniversalBleCharacteristic>? characteristics;
UniversalBleService(this.uuid, this.characteristics);
}
class UniversalBleCharacteristic {
String uuid;
List<int?> properties;
List<int> properties;
UniversalBleCharacteristic(this.uuid, this.properties);
}
/// Scan Filters
class UniversalScanFilter {
final List<String?> withServices;
final List<String?> withNamePrefix;
final List<UniversalManufacturerDataFilter?> withManufacturerData;
final List<String> withServices;
final List<String> withNamePrefix;
final List<UniversalManufacturerDataFilter> withManufacturerData;
UniversalScanFilter(
this.withServices,
+1 -1
View File
@@ -30,7 +30,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
pigeon: ^21.1.0
pigeon: ^22.4.0
flutter:
plugin:
+33 -25
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v21.1.0), do not edit directly.
// Autogenerated from Pigeon (v22.4.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#undef _HAS_EXCEPTIONS
@@ -22,10 +22,10 @@ using flutter::EncodableMap;
using flutter::EncodableValue;
FlutterError CreateConnectionError(const std::string channel_name) {
return FlutterError(
"channel-error",
"Unable to establish connection on channel: '" + channel_name + "'.",
EncodableValue(""));
return FlutterError(
"channel-error",
"Unable to establish connection on channel: '" + channel_name + "'.",
EncodableValue(""));
}
// UniversalBleScanResult
@@ -146,7 +146,7 @@ UniversalBleScanResult UniversalBleScanResult::FromEncodableList(const Encodable
}
auto& encodable_rssi = list[3];
if (!encodable_rssi.IsNull()) {
decoded.set_rssi(encodable_rssi.LongValue());
decoded.set_rssi(std::get<int64_t>(encodable_rssi));
}
auto& encodable_manufacturer_data_list = list[4];
if (!encodable_manufacturer_data_list.IsNull()) {
@@ -364,7 +364,7 @@ EncodableList UniversalManufacturerDataFilter::ToEncodableList() const {
UniversalManufacturerDataFilter UniversalManufacturerDataFilter::FromEncodableList(const EncodableList& list) {
UniversalManufacturerDataFilter decoded(
list[0].LongValue());
std::get<int64_t>(list[0]));
auto& encodable_data = list[1];
if (!encodable_data.IsNull()) {
decoded.set_data(std::get<std::vector<uint8_t>>(encodable_data));
@@ -412,36 +412,42 @@ EncodableList UniversalManufacturerData::ToEncodableList() const {
UniversalManufacturerData UniversalManufacturerData::FromEncodableList(const EncodableList& list) {
UniversalManufacturerData decoded(
list[0].LongValue(),
std::get<int64_t>(list[0]),
std::get<std::vector<uint8_t>>(list[1]));
return decoded;
}
PigeonCodecSerializer::PigeonCodecSerializer() {}
PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {}
EncodableValue PigeonCodecSerializer::ReadValueOfType(
EncodableValue PigeonInternalCodecSerializer::ReadValueOfType(
uint8_t type,
flutter::ByteStreamReader* stream) const {
switch (type) {
case 129:
return CustomEncodableValue(UniversalBleScanResult::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
case 130:
return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
case 131:
return CustomEncodableValue(UniversalBleCharacteristic::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
case 132:
return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
case 133:
return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
case 134:
return CustomEncodableValue(UniversalManufacturerData::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
case 129: {
return CustomEncodableValue(UniversalBleScanResult::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
case 130: {
return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
case 131: {
return CustomEncodableValue(UniversalBleCharacteristic::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
case 132: {
return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
case 133: {
return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
case 134: {
return CustomEncodableValue(UniversalManufacturerData::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
default:
return flutter::StandardCodecSerializer::ReadValueOfType(type, stream);
}
}
void PigeonCodecSerializer::WriteValue(
void PigeonInternalCodecSerializer::WriteValue(
const EncodableValue& value,
flutter::ByteStreamWriter* stream) const {
if (const CustomEncodableValue* custom_value = std::get_if<CustomEncodableValue>(&value)) {
@@ -481,7 +487,7 @@ void PigeonCodecSerializer::WriteValue(
/// The codec used by UniversalBlePlatformChannel.
const flutter::StandardMessageCodec& UniversalBlePlatformChannel::GetCodec() {
return flutter::StandardMessageCodec::GetInstance(&PigeonCodecSerializer::GetInstance());
return flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance());
}
// Sets up an instance of `UniversalBlePlatformChannel` to handle messages through the `binary_messenger`.
@@ -1019,7 +1025,7 @@ UniversalBleCallbackChannel::UniversalBleCallbackChannel(
message_channel_suffix_(message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "") {}
const flutter::StandardMessageCodec& UniversalBleCallbackChannel::GetCodec() {
return flutter::StandardMessageCodec::GetInstance(&PigeonCodecSerializer::GetInstance());
return flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance());
}
void UniversalBleCallbackChannel::OnAvailabilityChanged(
@@ -1133,6 +1139,7 @@ void UniversalBleCallbackChannel::OnValueChanged(
void UniversalBleCallbackChannel::OnConnectionChanged(
const std::string& device_id_arg,
bool connected_arg,
const std::string* error_arg,
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error) {
const std::string channel_name = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged" + message_channel_suffix_;
@@ -1140,6 +1147,7 @@ void UniversalBleCallbackChannel::OnConnectionChanged(
EncodableValue encoded_api_arguments = EncodableValue(EncodableList{
EncodableValue(device_id_arg),
EncodableValue(connected_arg),
error_arg ? EncodableValue(*error_arg) : EncodableValue(),
});
channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) {
std::unique_ptr<EncodableValue> response = GetCodec().DecodeMessage(reply, reply_size);
+14 -11
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v21.1.0), do not edit directly.
// Autogenerated from Pigeon (v22.4.0), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#ifndef PIGEON_UNIVERSAL_BLE_G_H_
@@ -57,6 +57,7 @@ template<class T> class ErrorOr {
};
// Generated class from Pigeon that represents data sent in messages.
class UniversalBleScanResult {
public:
@@ -101,7 +102,7 @@ class UniversalBleScanResult {
flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonCodecSerializer;
friend class PigeonInternalCodecSerializer;
std::string device_id_;
std::optional<std::string> name_;
std::optional<bool> is_paired_;
@@ -136,7 +137,7 @@ class UniversalBleService {
flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonCodecSerializer;
friend class PigeonInternalCodecSerializer;
std::string uuid_;
std::optional<flutter::EncodableList> characteristics_;
@@ -163,7 +164,7 @@ class UniversalBleCharacteristic {
flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonCodecSerializer;
friend class PigeonInternalCodecSerializer;
std::string uuid_;
flutter::EncodableList properties_;
@@ -196,7 +197,7 @@ class UniversalScanFilter {
flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonCodecSerializer;
friend class PigeonInternalCodecSerializer;
flutter::EncodableList with_services_;
flutter::EncodableList with_name_prefix_;
flutter::EncodableList with_manufacturer_data_;
@@ -233,7 +234,7 @@ class UniversalManufacturerDataFilter {
flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonCodecSerializer;
friend class PigeonInternalCodecSerializer;
int64_t company_identifier_;
std::optional<std::vector<uint8_t>> data_;
std::optional<std::vector<uint8_t>> mask_;
@@ -261,17 +262,18 @@ class UniversalManufacturerData {
flutter::EncodableList ToEncodableList() const;
friend class UniversalBlePlatformChannel;
friend class UniversalBleCallbackChannel;
friend class PigeonCodecSerializer;
friend class PigeonInternalCodecSerializer;
int64_t company_identifier_;
std::vector<uint8_t> data_;
};
class PigeonCodecSerializer : public flutter::StandardCodecSerializer {
class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer {
public:
PigeonCodecSerializer();
inline static PigeonCodecSerializer& GetInstance() {
static PigeonCodecSerializer sInstance;
PigeonInternalCodecSerializer();
inline static PigeonInternalCodecSerializer& GetInstance() {
static PigeonInternalCodecSerializer sInstance;
return sInstance;
}
@@ -387,6 +389,7 @@ class UniversalBleCallbackChannel {
void OnConnectionChanged(
const std::string& device_id,
bool connected,
const std::string* error,
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error);
+8 -7
View File
@@ -190,7 +190,7 @@ namespace universal_ble
CleanConnection(deviceAddress);
// TODO: send disconnect event only after disconnect is complete
uiThreadHandler_.Post([deviceAddress]
{ callbackChannel->OnConnectionChanged(_mac_address_to_str(deviceAddress), false, SuccessCallback, ErrorCallback); });
{ callbackChannel->OnConnectionChanged(_mac_address_to_str(deviceAddress), false, nullptr, SuccessCallback, ErrorCallback); });
return std::nullopt;
};
@@ -925,7 +925,7 @@ namespace universal_ble
{
std::cout << "ConnectionLog: ConnectionFailed: Failed to get device" << std::endl;
uiThreadHandler_.Post([bluetoothAddress]
{ callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), false, SuccessCallback, ErrorCallback); });
{ callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), false, new std::string("Failed to get device"), SuccessCallback, ErrorCallback); });
co_return;
}
@@ -933,9 +933,10 @@ namespace universal_ble
auto status = servicesResult.Status();
if (status != GattCommunicationStatus::Success)
{
std::cout << "ConnectionFailed: Failed to get services: " << GattCommunicationStatusToString(status) << std::endl;
uiThreadHandler_.Post([bluetoothAddress]
{ callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), false, SuccessCallback, ErrorCallback); });
std::string error = GattCommunicationStatusToString(status);
std::cout << "ConnectionFailed: Failed to get services: " << error << std::endl;
uiThreadHandler_.Post([bluetoothAddress, error]
{ callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), false, &error, SuccessCallback, ErrorCallback); });
co_return;
}
@@ -972,7 +973,7 @@ namespace universal_ble
connectedDevices.insert(std::move(pair));
std::cout << "ConnectionLog: Connected" << std::endl;
uiThreadHandler_.Post([bluetoothAddress]
{ callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), true, SuccessCallback, ErrorCallback); });
{ callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), true, nullptr, SuccessCallback, ErrorCallback); });
}
void UniversalBlePlugin::BluetoothLEDevice_ConnectionStatusChanged(BluetoothLEDevice sender, IInspectable args)
@@ -982,7 +983,7 @@ namespace universal_ble
CleanConnection(sender.BluetoothAddress());
auto bluetoothAddress = sender.BluetoothAddress();
uiThreadHandler_.Post([bluetoothAddress]
{ callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), false, SuccessCallback, ErrorCallback); });
{ callbackChannel->OnConnectionChanged(_mac_address_to_str(bluetoothAddress), false, nullptr, SuccessCallback, ErrorCallback); });
}
}