Implement Logging (#199)
* Implement Logging * Add timestamp in OnValueChanged * improve web and linux logging * Implement windows and format pigeon * Fix windows build and unity log level * Log commands with timestamp * Fix code doc * Remove unnecessary import * Update docs * Fix crash on windows 11 because of failed pairing * Update changelog * Fix native code formatting
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
## 1.0.0
|
||||
* BREAKING CHANGE: `OnValueChange` callback also includes timestamp
|
||||
* Fix Windows 11 crash on cancelling pairing
|
||||
* Unified error codes for all platforms
|
||||
* Add `isScanning` api
|
||||
* Add `requestPermissions` api and auto ask permission on `startScan`
|
||||
@@ -6,6 +8,7 @@
|
||||
* Improve Windows disconnection event handling and cleanup
|
||||
* Add `withDescriptors` parameter in `discoverServices` API
|
||||
* Add `timestamp` in `BleDevice`
|
||||
* Add `setLogLevel` api for configuring logs
|
||||
|
||||
## 0.21.1
|
||||
* Fix device name resolution on Windows
|
||||
|
||||
@@ -745,6 +745,23 @@ class UniversalBleMock extends UniversalBlePlatform {
|
||||
UniversalBle.setInstance(UniversalBleMock());
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
Configure logging to help debug Ble operations
|
||||
|
||||
### Usage
|
||||
|
||||
Set the log level during app initialization, default level is `none`
|
||||
|
||||
```dart
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
// Enable verbose logging to see all BLE operations
|
||||
await UniversalBle.setLogLevel(BleLogLevel.verbose);
|
||||
runApp(MyApp());
|
||||
}
|
||||
```
|
||||
|
||||
## Low level API
|
||||
|
||||
For more granular control, you can use the [Low-Level API](README.low_level.md). This API is "Device ID"-based, offering greater flexibility by enabling direct calls without the need for object instances.
|
||||
|
||||
@@ -18,13 +18,13 @@ private const val TAG = "UniversalBlePlugin"
|
||||
|
||||
/**
|
||||
* A safe wrapper for Bluetooth LE scanning operations that prevents excessive scanning.
|
||||
*
|
||||
*
|
||||
* This class manages BLE scanning while adhering to Android's scanning frequency limits by:
|
||||
* - Tracking scan start times over a 30-second window
|
||||
* - Limiting to 5 scan operations within this window
|
||||
* - Automatically scheduling delayed scans when frequency limits are exceeded
|
||||
* - Providing safe start/stop scan operations with error handling
|
||||
*
|
||||
*
|
||||
* The scanner will automatically delay new scan requests if the frequency limit is reached,
|
||||
* and will retry once sufficient time has passed. This helps prevent scan failure errors
|
||||
* and ensures compliance with Android's scanning restrictions.
|
||||
|
||||
@@ -81,6 +81,21 @@ class FlutterError (
|
||||
val details: Any? = null
|
||||
) : Throwable()
|
||||
|
||||
enum class UniversalBleLogLevel(val raw: Int) {
|
||||
NONE(0),
|
||||
ERROR(1),
|
||||
WARNING(2),
|
||||
INFO(3),
|
||||
DEBUG(4),
|
||||
VERBOSE(5);
|
||||
|
||||
companion object {
|
||||
fun ofRaw(raw: Int): UniversalBleLogLevel? {
|
||||
return values().firstOrNull { it.raw == raw }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Unified error codes for all platforms */
|
||||
enum class UniversalBleErrorCode(val raw: Int) {
|
||||
UNKNOWN_ERROR(0),
|
||||
@@ -398,40 +413,45 @@ private open class UniversalBlePigeonCodec : StandardMessageCodec() {
|
||||
return when (type) {
|
||||
129.toByte() -> {
|
||||
return (readValue(buffer) as Long?)?.let {
|
||||
UniversalBleErrorCode.ofRaw(it.toInt())
|
||||
UniversalBleLogLevel.ofRaw(it.toInt())
|
||||
}
|
||||
}
|
||||
130.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalBleScanResult.fromList(it)
|
||||
return (readValue(buffer) as Long?)?.let {
|
||||
UniversalBleErrorCode.ofRaw(it.toInt())
|
||||
}
|
||||
}
|
||||
131.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalBleService.fromList(it)
|
||||
UniversalBleScanResult.fromList(it)
|
||||
}
|
||||
}
|
||||
132.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalBleCharacteristic.fromList(it)
|
||||
UniversalBleService.fromList(it)
|
||||
}
|
||||
}
|
||||
133.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalBleDescriptor.fromList(it)
|
||||
UniversalBleCharacteristic.fromList(it)
|
||||
}
|
||||
}
|
||||
134.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalScanFilter.fromList(it)
|
||||
UniversalBleDescriptor.fromList(it)
|
||||
}
|
||||
}
|
||||
135.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalManufacturerDataFilter.fromList(it)
|
||||
UniversalScanFilter.fromList(it)
|
||||
}
|
||||
}
|
||||
136.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalManufacturerDataFilter.fromList(it)
|
||||
}
|
||||
}
|
||||
137.toByte() -> {
|
||||
return (readValue(buffer) as? List<Any?>)?.let {
|
||||
UniversalManufacturerData.fromList(it)
|
||||
}
|
||||
@@ -441,38 +461,42 @@ private open class UniversalBlePigeonCodec : StandardMessageCodec() {
|
||||
}
|
||||
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
|
||||
when (value) {
|
||||
is UniversalBleErrorCode -> {
|
||||
is UniversalBleLogLevel -> {
|
||||
stream.write(129)
|
||||
writeValue(stream, value.raw.toLong())
|
||||
}
|
||||
is UniversalBleScanResult -> {
|
||||
is UniversalBleErrorCode -> {
|
||||
stream.write(130)
|
||||
writeValue(stream, value.toList())
|
||||
writeValue(stream, value.raw.toLong())
|
||||
}
|
||||
is UniversalBleService -> {
|
||||
is UniversalBleScanResult -> {
|
||||
stream.write(131)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalBleCharacteristic -> {
|
||||
is UniversalBleService -> {
|
||||
stream.write(132)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalBleDescriptor -> {
|
||||
is UniversalBleCharacteristic -> {
|
||||
stream.write(133)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalScanFilter -> {
|
||||
is UniversalBleDescriptor -> {
|
||||
stream.write(134)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalManufacturerDataFilter -> {
|
||||
is UniversalScanFilter -> {
|
||||
stream.write(135)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalManufacturerData -> {
|
||||
is UniversalManufacturerDataFilter -> {
|
||||
stream.write(136)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
is UniversalManufacturerData -> {
|
||||
stream.write(137)
|
||||
writeValue(stream, value.toList())
|
||||
}
|
||||
else -> super.writeValue(stream, value)
|
||||
}
|
||||
}
|
||||
@@ -504,6 +528,7 @@ interface UniversalBlePlatformChannel {
|
||||
fun unPair(deviceId: String)
|
||||
fun getSystemDevices(withServices: List<String>, callback: (Result<List<UniversalBleScanResult>>) -> Unit)
|
||||
fun getConnectionState(deviceId: String): Long
|
||||
fun setLogLevel(logLevel: UniversalBleLogLevel)
|
||||
|
||||
companion object {
|
||||
/** The codec used by UniversalBlePlatformChannel. */
|
||||
@@ -876,6 +901,24 @@ interface UniversalBlePlatformChannel {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val logLevelArg = args[0] as UniversalBleLogLevel
|
||||
val wrapped: List<Any?> = try {
|
||||
api.setLogLevel(logLevelArg)
|
||||
listOf(null)
|
||||
} catch (exception: Throwable) {
|
||||
UniversalBlePigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -942,12 +985,12 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
|
||||
}
|
||||
}
|
||||
}
|
||||
fun onValueChanged(deviceIdArg: String, characteristicIdArg: String, valueArg: ByteArray, callback: (Result<Unit>) -> Unit)
|
||||
fun onValueChanged(deviceIdArg: String, characteristicIdArg: String, valueArg: ByteArray, timestampArg: Long?, callback: (Result<Unit>) -> Unit)
|
||||
{
|
||||
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||
val channelName = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged$separatedMessageChannelSuffix"
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
|
||||
channel.send(listOf(deviceIdArg, characteristicIdArg, valueArg)) {
|
||||
channel.send(listOf(deviceIdArg, characteristicIdArg, valueArg, timestampArg)) {
|
||||
if (it is List<*>) {
|
||||
if (it.size > 1) {
|
||||
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.navideck.universal_ble
|
||||
|
||||
import android.util.Log
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
object UniversalBleLogger {
|
||||
private const val TAG = "UniversalBle"
|
||||
private var currentLogLevel: UniversalBleLogLevel = UniversalBleLogLevel.NONE
|
||||
private val timeFormatter = SimpleDateFormat("HH:mm:ss.SSS", Locale.US)
|
||||
|
||||
fun setLogLevel(logLevel: UniversalBleLogLevel) {
|
||||
currentLogLevel = logLevel
|
||||
}
|
||||
|
||||
fun logError(message: String) {
|
||||
if (!allows(UniversalBleLogLevel.ERROR)) return
|
||||
Log.e(TAG, withTimestamp(message))
|
||||
}
|
||||
|
||||
fun logWarning(message: String) {
|
||||
if (!allows(UniversalBleLogLevel.WARNING)) return
|
||||
Log.w(TAG, withTimestamp(message))
|
||||
}
|
||||
|
||||
fun logInfo(message: String) {
|
||||
if (!allows(UniversalBleLogLevel.INFO)) return
|
||||
Log.i(TAG, withTimestamp(message))
|
||||
}
|
||||
|
||||
fun logDebug(message: String) {
|
||||
if (!allows(UniversalBleLogLevel.DEBUG)) return
|
||||
Log.d(TAG, withTimestamp(message))
|
||||
}
|
||||
|
||||
fun logVerbose(message: String) {
|
||||
if (!allows(UniversalBleLogLevel.VERBOSE)) return
|
||||
Log.v(TAG, withTimestamp(message))
|
||||
}
|
||||
|
||||
private fun allows(level: UniversalBleLogLevel): Boolean {
|
||||
return currentLogLevel != UniversalBleLogLevel.NONE && level.ordinal <= currentLogLevel.ordinal
|
||||
}
|
||||
|
||||
private fun withTimestamp(message: String): String {
|
||||
return "[${timeFormatter.format(Date())}] $message"
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import android.content.IntentFilter
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||
@@ -35,8 +34,6 @@ import java.util.concurrent.TimeUnit
|
||||
import androidx.core.content.edit
|
||||
|
||||
|
||||
private const val TAG = "UniversalBlePlugin"
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(), FlutterPlugin,
|
||||
ActivityAware, PluginRegistry.ActivityResultListener,
|
||||
@@ -178,7 +175,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
|
||||
// Set custom scan filter only if required
|
||||
if (usesCustomFilters) {
|
||||
Log.e(TAG, "Using Custom Filters")
|
||||
UniversalBleLogger.logError("Using Custom Filters")
|
||||
universalBleFilterUtil.scanFilter = filter
|
||||
universalBleFilterUtil.serviceFilterUUIDS = filterServices
|
||||
} else {
|
||||
@@ -217,7 +214,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
deviceId.findGatt()?.let {
|
||||
val currentState = bluetoothManager.getConnectionState(it.device, BluetoothProfile.GATT)
|
||||
if (currentState == BluetoothGatt.STATE_CONNECTED) {
|
||||
Log.e(TAG, "$deviceId Already connected")
|
||||
UniversalBleLogger.logError("$deviceId Already connected")
|
||||
mainThreadHandler?.post {
|
||||
callbackChannel?.onConnectionChanged(deviceId, true, null) {}
|
||||
}
|
||||
@@ -266,7 +263,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
connectionState.toBleConnectionState().value
|
||||
} else {
|
||||
// Might be connected with device, but not with app
|
||||
Log.e(TAG, "Device might be connected but not known to this app")
|
||||
UniversalBleLogger.logError("Device might be connected but not known to this app")
|
||||
BleConnectionState.Disconnected.value
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -274,6 +271,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLogLevel(logLevel: UniversalBleLogLevel) {
|
||||
UniversalBleLogger.setLogLevel(logLevel)
|
||||
}
|
||||
|
||||
override fun discoverServices(
|
||||
deviceId: String,
|
||||
withDescriptors: Boolean,
|
||||
@@ -348,6 +349,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
callback: (Result<Unit>) -> Unit,
|
||||
) {
|
||||
try {
|
||||
UniversalBleLogger.logDebug("SET_NOTIFY -> $deviceId $service $characteristic input=$bleInputProperty")
|
||||
val gatt = deviceId.toBluetoothGatt()
|
||||
val gattCharacteristic: BluetoothGattCharacteristic? =
|
||||
gatt.getCharacteristic(service, characteristic)
|
||||
@@ -408,7 +410,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.d("UniversalBle", "CCCD Descriptor not found")
|
||||
UniversalBleLogger.logDebug("CCCD Descriptor not found")
|
||||
}
|
||||
|
||||
if (gatt.setCharacteristicNotification(gattCharacteristic, enable)) {
|
||||
@@ -456,6 +458,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
callback: (Result<ByteArray>) -> Unit,
|
||||
) {
|
||||
try {
|
||||
UniversalBleLogger.logDebug("READ -> $deviceId $service $characteristic")
|
||||
val gatt = deviceId.toBluetoothGatt()
|
||||
val gattCharacteristic = gatt.getCharacteristic(service, characteristic)
|
||||
if (gattCharacteristic == null) {
|
||||
@@ -519,6 +522,9 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
it.result(Result.success(value))
|
||||
} else {
|
||||
UniversalBleLogger.logError(
|
||||
"READ_FAILED <- ${gatt.device.address} ${characteristic.uuid} status=$status"
|
||||
)
|
||||
it.result(
|
||||
Result.failure(
|
||||
createFlutterError(
|
||||
@@ -542,6 +548,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
callback: (Result<Unit>) -> Unit,
|
||||
) {
|
||||
try {
|
||||
UniversalBleLogger.logDebug("WRITE -> $deviceId $service $characteristic len=${value.size} property=$bleOutputProperty")
|
||||
val gatt = deviceId.toBluetoothGatt()
|
||||
val gattCharacteristic = gatt.getCharacteristic(service, characteristic)
|
||||
if (gattCharacteristic == null) {
|
||||
@@ -638,6 +645,9 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
it.result(Result.success(Unit))
|
||||
} else {
|
||||
UniversalBleLogger.logError(
|
||||
"WRITE_FAILED <- ${gatt?.device?.address} ${characteristic.uuid} status=$status"
|
||||
)
|
||||
it.result(
|
||||
Result.failure(
|
||||
createFlutterError(
|
||||
@@ -653,6 +663,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
|
||||
|
||||
override fun requestMtu(deviceId: String, expectedMtu: Long, callback: (Result<Long>) -> Unit) {
|
||||
UniversalBleLogger.logDebug("REQUEST_MTU -> $deviceId expected=$expectedMtu")
|
||||
try {
|
||||
val gatt = deviceId.toBluetoothGatt()
|
||||
gatt.requestMtu(expectedMtu.toInt())
|
||||
@@ -957,13 +968,13 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
|
||||
}
|
||||
if (device == null) {
|
||||
Log.e(TAG, "No device found in ACTION_BOND_STATE_CHANGED intent")
|
||||
UniversalBleLogger.logError("No device found in ACTION_BOND_STATE_CHANGED intent")
|
||||
return
|
||||
}
|
||||
// get pairing failed error
|
||||
when (intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR)) {
|
||||
BluetoothDevice.BOND_BONDING -> {
|
||||
Log.v(TAG, "${device.address} BOND_BONDING")
|
||||
UniversalBleLogger.logVerbose("${device.address} BOND_BONDING")
|
||||
}
|
||||
|
||||
BOND_BONDED -> {
|
||||
@@ -975,7 +986,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
}
|
||||
|
||||
BluetoothDevice.BOND_NONE -> {
|
||||
Log.e(TAG, "${device.address} BOND_NONE")
|
||||
UniversalBleLogger.logError("${device.address} BOND_NONE")
|
||||
onBondStateUpdate(device.address, false)
|
||||
}
|
||||
}
|
||||
@@ -986,12 +997,12 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
|
||||
private val scanCallback = object : ScanCallback() {
|
||||
override fun onScanFailed(errorCode: Int) {
|
||||
Log.e(TAG, "OnScanFailed: ${errorCode.parseScanErrorMessage()}")
|
||||
UniversalBleLogger.logError("OnScanFailed: ${errorCode.parseScanErrorMessage()}")
|
||||
}
|
||||
|
||||
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||
|
||||
// Log.v(TAG, "onScanResult: $result")
|
||||
// UniversalBleLogger.logVerbose("onScanResult: $result")
|
||||
var serviceUuids: Array<UUID> = arrayOf()
|
||||
result.device.uuids?.forEach {
|
||||
serviceUuids += it.uuid
|
||||
@@ -1029,14 +1040,17 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
}
|
||||
|
||||
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
|
||||
Log.v(TAG, "onBatchScanResults: $results")
|
||||
UniversalBleLogger.logVerbose("onBatchScanResults: $results")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
|
||||
Log.d(
|
||||
TAG,
|
||||
override fun onConnectionStateChange(
|
||||
gatt: BluetoothGatt,
|
||||
status: Int,
|
||||
newState: Int,
|
||||
) {
|
||||
UniversalBleLogger.logDebug(
|
||||
"onConnectionStateChange-> Status: $status ${status.parseHciErrorCode()}, NewState: $newState"
|
||||
)
|
||||
|
||||
@@ -1053,7 +1067,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
gatt.device.address, false, status.parseHciErrorCode()
|
||||
) {}
|
||||
}
|
||||
Log.d(TAG, "Closing gatt for ${gatt.device.name}")
|
||||
UniversalBleLogger.logDebug("Closing gatt for ${gatt.device.name}")
|
||||
gatt.close()
|
||||
}
|
||||
}
|
||||
@@ -1063,11 +1077,15 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
||||
characteristic: BluetoothGattCharacteristic,
|
||||
value: ByteArray,
|
||||
) {
|
||||
UniversalBleLogger.logVerbose(
|
||||
"NOTIFY <- ${gatt.device.address} ${characteristic.uuid} len=${value.size}"
|
||||
)
|
||||
mainThreadHandler?.post {
|
||||
callbackChannel?.onValueChanged(
|
||||
deviceIdArg = gatt.device.address,
|
||||
characteristicIdArg = characteristic.uuid.toString(),
|
||||
valueArg = value
|
||||
valueArg = value,
|
||||
timestampArg = System.currentTimeMillis()
|
||||
) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,15 @@ func deepHashUniversalBle(value: Any?, hasher: inout Hasher) {
|
||||
|
||||
|
||||
|
||||
enum UniversalBleLogLevel: Int {
|
||||
case none = 0
|
||||
case error = 1
|
||||
case warning = 2
|
||||
case info = 3
|
||||
case debug = 4
|
||||
case verbose = 5
|
||||
}
|
||||
|
||||
/// Unified error codes for all platforms
|
||||
enum UniversalBleErrorCode: Int {
|
||||
case unknownError = 0
|
||||
@@ -436,22 +445,28 @@ private class UniversalBlePigeonCodecReader: FlutterStandardReader {
|
||||
case 129:
|
||||
let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
|
||||
if let enumResultAsInt = enumResultAsInt {
|
||||
return UniversalBleErrorCode(rawValue: enumResultAsInt)
|
||||
return UniversalBleLogLevel(rawValue: enumResultAsInt)
|
||||
}
|
||||
return nil
|
||||
case 130:
|
||||
return UniversalBleScanResult.fromList(self.readValue() as! [Any?])
|
||||
let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?)
|
||||
if let enumResultAsInt = enumResultAsInt {
|
||||
return UniversalBleErrorCode(rawValue: enumResultAsInt)
|
||||
}
|
||||
return nil
|
||||
case 131:
|
||||
return UniversalBleService.fromList(self.readValue() as! [Any?])
|
||||
return UniversalBleScanResult.fromList(self.readValue() as! [Any?])
|
||||
case 132:
|
||||
return UniversalBleCharacteristic.fromList(self.readValue() as! [Any?])
|
||||
return UniversalBleService.fromList(self.readValue() as! [Any?])
|
||||
case 133:
|
||||
return UniversalBleDescriptor.fromList(self.readValue() as! [Any?])
|
||||
return UniversalBleCharacteristic.fromList(self.readValue() as! [Any?])
|
||||
case 134:
|
||||
return UniversalScanFilter.fromList(self.readValue() as! [Any?])
|
||||
return UniversalBleDescriptor.fromList(self.readValue() as! [Any?])
|
||||
case 135:
|
||||
return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?])
|
||||
return UniversalScanFilter.fromList(self.readValue() as! [Any?])
|
||||
case 136:
|
||||
return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?])
|
||||
case 137:
|
||||
return UniversalManufacturerData.fromList(self.readValue() as! [Any?])
|
||||
default:
|
||||
return super.readValue(ofType: type)
|
||||
@@ -461,30 +476,33 @@ private class UniversalBlePigeonCodecReader: FlutterStandardReader {
|
||||
|
||||
private class UniversalBlePigeonCodecWriter: FlutterStandardWriter {
|
||||
override func writeValue(_ value: Any) {
|
||||
if let value = value as? UniversalBleErrorCode {
|
||||
if let value = value as? UniversalBleLogLevel {
|
||||
super.writeByte(129)
|
||||
super.writeValue(value.rawValue)
|
||||
} else if let value = value as? UniversalBleScanResult {
|
||||
} else if let value = value as? UniversalBleErrorCode {
|
||||
super.writeByte(130)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalBleService {
|
||||
super.writeValue(value.rawValue)
|
||||
} else if let value = value as? UniversalBleScanResult {
|
||||
super.writeByte(131)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalBleCharacteristic {
|
||||
} else if let value = value as? UniversalBleService {
|
||||
super.writeByte(132)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalBleDescriptor {
|
||||
} else if let value = value as? UniversalBleCharacteristic {
|
||||
super.writeByte(133)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalScanFilter {
|
||||
} else if let value = value as? UniversalBleDescriptor {
|
||||
super.writeByte(134)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalManufacturerDataFilter {
|
||||
} else if let value = value as? UniversalScanFilter {
|
||||
super.writeByte(135)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalManufacturerData {
|
||||
} else if let value = value as? UniversalManufacturerDataFilter {
|
||||
super.writeByte(136)
|
||||
super.writeValue(value.toList())
|
||||
} else if let value = value as? UniversalManufacturerData {
|
||||
super.writeByte(137)
|
||||
super.writeValue(value.toList())
|
||||
} else {
|
||||
super.writeValue(value)
|
||||
}
|
||||
@@ -529,6 +547,7 @@ protocol UniversalBlePlatformChannel {
|
||||
func unPair(deviceId: String) throws
|
||||
func getSystemDevices(withServices: [String], completion: @escaping (Result<[UniversalBleScanResult], Error>) -> Void)
|
||||
func getConnectionState(deviceId: String) throws -> Int64
|
||||
func setLogLevel(logLevel: UniversalBleLogLevel) throws
|
||||
}
|
||||
|
||||
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||
@@ -847,6 +866,21 @@ class UniversalBlePlatformChannelSetup {
|
||||
} else {
|
||||
getConnectionStateChannel.setMessageHandler(nil)
|
||||
}
|
||||
let setLogLevelChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||
if let api = api {
|
||||
setLogLevelChannel.setMessageHandler { message, reply in
|
||||
let args = message as! [Any?]
|
||||
let logLevelArg = args[0] as! UniversalBleLogLevel
|
||||
do {
|
||||
try api.setLogLevel(logLevel: logLevelArg)
|
||||
reply(wrapResult(nil))
|
||||
} catch {
|
||||
reply(wrapError(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setLogLevelChannel.setMessageHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Native -> Flutter
|
||||
@@ -856,7 +890,7 @@ protocol UniversalBleCallbackChannelProtocol {
|
||||
func onAvailabilityChanged(state stateArg: Int64, completion: @escaping (Result<Void, PigeonError>) -> Void)
|
||||
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 onValueChanged(deviceId deviceIdArg: String, characteristicId characteristicIdArg: String, value valueArg: FlutterStandardTypedData, timestamp timestampArg: Int64?, 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 {
|
||||
@@ -923,10 +957,10 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol {
|
||||
}
|
||||
}
|
||||
}
|
||||
func onValueChanged(deviceId deviceIdArg: String, characteristicId characteristicIdArg: String, value valueArg: FlutterStandardTypedData, completion: @escaping (Result<Void, PigeonError>) -> Void) {
|
||||
func onValueChanged(deviceId deviceIdArg: String, characteristicId characteristicIdArg: String, value valueArg: FlutterStandardTypedData, timestamp timestampArg: Int64?, completion: @escaping (Result<Void, PigeonError>) -> Void) {
|
||||
let channelName: String = "dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged\(messageChannelSuffix)"
|
||||
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec)
|
||||
channel.sendMessage([deviceIdArg, characteristicIdArg, valueArg] as [Any?]) { response in
|
||||
channel.sendMessage([deviceIdArg, characteristicIdArg, valueArg, timestampArg] as [Any?]) { response in
|
||||
guard let listResponse = response as? [Any?] else {
|
||||
completion(.failure(createConnectionError(withChannelName: channelName)))
|
||||
return
|
||||
|
||||
@@ -30,7 +30,7 @@ class UniversalBleAsyncServiceDiscovery: NSObject {
|
||||
/// Starts the service discovery process
|
||||
func startDiscovery() {
|
||||
guard !isDiscoveryInProgress else {
|
||||
print("Service discovery already in progress for device: \(deviceId)")
|
||||
UniversalBleLogger.shared.logWarning("Service discovery already in progress for device: \(deviceId)")
|
||||
return
|
||||
}
|
||||
isDiscoveryInProgress = true
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import Foundation
|
||||
|
||||
final class UniversalBleLogger {
|
||||
static let shared = UniversalBleLogger()
|
||||
|
||||
private init() {}
|
||||
|
||||
private lazy var dateFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "HH:mm:ss.SSS"
|
||||
return formatter
|
||||
}()
|
||||
|
||||
private var currentLogLevel: UniversalBleLogLevel = .none
|
||||
|
||||
func setLogLevel(_ logLevel: UniversalBleLogLevel) {
|
||||
currentLogLevel = logLevel
|
||||
}
|
||||
|
||||
func logError(_ message: String) {
|
||||
guard allows(.error) else { return }
|
||||
print("UniversalBle:ERROR \(withTimestamp(message))")
|
||||
}
|
||||
|
||||
func logWarning(_ message: String) {
|
||||
guard allows(.warning) else { return }
|
||||
print("UniversalBle:WARN \(withTimestamp(message))")
|
||||
}
|
||||
|
||||
func logInfo(_ message: String) {
|
||||
guard allows(.info) else { return }
|
||||
print("UniversalBle:INFO \(withTimestamp(message))")
|
||||
}
|
||||
|
||||
func logDebug(_ message: String) {
|
||||
guard allows(.debug) else { return }
|
||||
print("UniversalBle:DEBUG \(withTimestamp(message))")
|
||||
}
|
||||
|
||||
func logVerbose(_ message: String) {
|
||||
guard allows(.verbose) else { return }
|
||||
print("UniversalBle:VERBOSE \(withTimestamp(message))")
|
||||
}
|
||||
|
||||
private func allows(_ level: UniversalBleLogLevel) -> Bool {
|
||||
return currentLogLevel != .none && level.rawValue <= currentLogLevel.rawValue
|
||||
}
|
||||
|
||||
private func withTimestamp(_ message: String) -> String {
|
||||
let time = dateFormatter.string(from: Date())
|
||||
return "[\(time)] \(message)"
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
var withServices: [CBUUID] = try filter?.withServices.compactMap { $0 }.toCBUUID() ?? []
|
||||
|
||||
if usesCustomFilters {
|
||||
print("Using Custom Filters")
|
||||
UniversalBleLogger.shared.logInfo("Using Custom Filters")
|
||||
universalBleFilterUtil.scanFilter = filter
|
||||
universalBleFilterUtil.scanFilterServicesUUID = withServices
|
||||
withServices = []
|
||||
@@ -132,6 +132,10 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
return isManageScanning
|
||||
}
|
||||
|
||||
func setLogLevel(logLevel: UniversalBleLogLevel) throws {
|
||||
UniversalBleLogger.shared.setLogLevel(logLevel)
|
||||
}
|
||||
|
||||
func connect(deviceId: String) throws {
|
||||
let peripheral = try deviceId.getPeripheral(manager: manager)
|
||||
peripheral.delegate = self
|
||||
@@ -218,7 +222,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
|
||||
// Check if discovery is already in progress
|
||||
if activeServiceDiscoveries[deviceId] != nil {
|
||||
print("Services discovery already in progress for :\(deviceId), waiting for completion.")
|
||||
UniversalBleLogger.shared.logWarning("Services discovery already in progress for :\(deviceId), waiting for completion.")
|
||||
discoverServicesFutures.append(DiscoverServicesFuture(deviceId: deviceId, result: completion))
|
||||
return
|
||||
}
|
||||
@@ -247,6 +251,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
}
|
||||
|
||||
func setNotifiable(deviceId: String, service: String, characteristic: String, bleInputProperty: Int64, completion: @escaping (Result<Void, any Error>) -> Void) {
|
||||
UniversalBleLogger.shared.logDebug("SET_NOTIFY -> \(deviceId) \(service) \(characteristic) input=\(bleInputProperty)")
|
||||
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
|
||||
completion(Result.failure(createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(deviceId)")))
|
||||
return
|
||||
@@ -273,6 +278,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
}
|
||||
|
||||
func readValue(deviceId: String, service: String, characteristic: String, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
|
||||
UniversalBleLogger.shared.logDebug("READ -> \(deviceId) \(service) \(characteristic)")
|
||||
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
|
||||
completion(Result.failure(createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(self)")))
|
||||
return
|
||||
@@ -290,6 +296,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
}
|
||||
|
||||
func writeValue(deviceId: String, service: String, characteristic: String, value: FlutterStandardTypedData, bleOutputProperty: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
UniversalBleLogger.shared.logDebug("WRITE -> \(deviceId) \(service) \(characteristic) len=\(value.data.count) property=\(bleOutputProperty)")
|
||||
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
|
||||
completion(Result.failure(createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(self)")))
|
||||
return
|
||||
@@ -324,6 +331,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
}
|
||||
|
||||
func requestMtu(deviceId: String, expectedMtu _: Int64, completion: @escaping (Result<Int64, Error>) -> Void) {
|
||||
UniversalBleLogger.shared.logDebug("REQUEST_MTU -> \(deviceId)")
|
||||
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
|
||||
completion(Result.failure(createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(self)")))
|
||||
return
|
||||
@@ -349,7 +357,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
func getSystemDevices(withServices: [String], completion: @escaping (Result<[UniversalBleScanResult], Error>) -> Void) {
|
||||
var servicesFilter = withServices
|
||||
if servicesFilter.isEmpty {
|
||||
print("No services filter was set for getting system connected devices. Using default services...")
|
||||
UniversalBleLogger.shared.logInfo("No services filter was set for getting system connected devices. Using default services...")
|
||||
|
||||
// Add several generic services
|
||||
servicesFilter = ["1800", "1801", "180A", "180D", "1810", "181B", "1808", "181D", "1816", "1814", "181A", "1802", "1803", "1804", "1815", "1805", "1807", "1806", "1848", "185E", "180F", "1812", "180E", "1813"]
|
||||
@@ -461,6 +469,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
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() {
|
||||
UniversalBleLogger.shared.logError("WRITE_FAILED <- \(peripheral.uuid.uuidString) \(characteristic.uuid.uuidStr): \(flutterError.message ?? "")")
|
||||
future.result(Result.failure(flutterError))
|
||||
} else {
|
||||
future.result(Result.success({}()))
|
||||
@@ -475,6 +484,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
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() {
|
||||
UniversalBleLogger.shared.logError("SET_NOTIFY_FAILED <- \(peripheral.uuid.uuidString) \(characteristic.uuid.uuidStr): \(flutterError.message ?? "")")
|
||||
future.result(Result.failure(flutterError))
|
||||
} else {
|
||||
future.result(Result.success({}()))
|
||||
@@ -486,10 +496,24 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
}
|
||||
|
||||
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
|
||||
if let error {
|
||||
UniversalBleLogger.shared.logError("NOTIFY_ERROR <- \(peripheral.uuid.uuidString) \(characteristic.uuid.uuidStr): \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
if characteristic.isNotifying, let characteristicValue = characteristic.value {
|
||||
let preview = characteristicValue.prefix(8).map { String(format: "%02X", $0) }.joined()
|
||||
UniversalBleLogger.shared.logVerbose("NOTIFY <- \(peripheral.uuid.uuidString) \(characteristic.uuid.uuidStr) len=\(characteristicValue.count) data=\(preview)")
|
||||
}
|
||||
|
||||
// Update callbackChannel if notifying
|
||||
if characteristic.isNotifying {
|
||||
if let characteristicValue = characteristic.value {
|
||||
callbackChannel.onValueChanged(deviceId: peripheral.uuid.uuidString, characteristicId: characteristic.uuid.uuidStr, value: FlutterStandardTypedData(bytes: characteristicValue)) { _ in }
|
||||
callbackChannel.onValueChanged(
|
||||
deviceId: peripheral.uuid.uuidString,
|
||||
characteristicId: characteristic.uuid.uuidStr,
|
||||
value: FlutterStandardTypedData(bytes: characteristicValue),
|
||||
timestamp: Int64(Date().timeIntervalSince1970 * 1000)
|
||||
) { _ in }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +525,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
||||
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() {
|
||||
UniversalBleLogger.shared.logError("READ_FAILED <- \(peripheral.uuid.uuidString) \(characteristic.uuid.uuidStr): \(flutterError.message ?? "")")
|
||||
future.result(Result.failure(flutterError))
|
||||
} else {
|
||||
if let characteristicValue = characteristic.value {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
import 'package:universal_ble_example/home/home.dart';
|
||||
|
||||
void main() {
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await UniversalBle.setLogLevel(BleLogLevel.verbose);
|
||||
runApp(
|
||||
MaterialApp(
|
||||
title: 'Universal BLE',
|
||||
|
||||
@@ -81,11 +81,14 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
|
||||
}
|
||||
}
|
||||
|
||||
void _handleValueChange(
|
||||
String deviceId, String characteristicId, Uint8List value) {
|
||||
void _handleValueChange(String deviceId, String characteristicId,
|
||||
Uint8List value, int? timestamp) {
|
||||
String s = String.fromCharCodes(value);
|
||||
String data = '$s\nraw : ${value.toString()}';
|
||||
debugPrint('_handleValueChange $characteristicId, $s');
|
||||
DateTime? timestampDateTime = timestamp != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(timestamp)
|
||||
: null;
|
||||
debugPrint('_handleValueChange ($timestampDateTime) $characteristicId, $s');
|
||||
_addLog("Value", data);
|
||||
}
|
||||
|
||||
|
||||
@@ -214,10 +214,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
version: "1.16.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -315,10 +315,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
version: "0.7.6"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -93,7 +93,7 @@ extension BleDeviceExtension on BleDevice {
|
||||
///
|
||||
/// [service] is the UUID of the service.
|
||||
/// [preferCached] indicates whether to use cached services. If cache is empty, discoverServices() will be called.
|
||||
/// might throw [NotFoundException]
|
||||
/// might throw [UniversalBleException]
|
||||
Future<BleService> getService(
|
||||
String service, {
|
||||
bool preferCached = true,
|
||||
@@ -128,7 +128,7 @@ extension BleDeviceExtension on BleDevice {
|
||||
/// [service] is the UUID of the service.
|
||||
/// [characteristic] is the UUID of the characteristic.
|
||||
/// [preferCached] indicates whether to use cached services. If cache is empty, discoverServices() will be called.
|
||||
/// might throw [NotFoundException]
|
||||
/// might throw [UniversalBleException]
|
||||
Future<BleCharacteristic> getCharacteristic(
|
||||
String characteristic, {
|
||||
required String service,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
enum BleLogLevel {
|
||||
none,
|
||||
error,
|
||||
warning,
|
||||
info,
|
||||
debug,
|
||||
verbose;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export 'package:universal_ble/src/models/ble_log_level.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';
|
||||
|
||||
@@ -24,6 +24,14 @@ class UniversalBle {
|
||||
_bleCommandQueue.timeout = duration;
|
||||
}
|
||||
|
||||
/// Set log level for both Dart and native implementations.
|
||||
/// Only effective in debug builds.
|
||||
static Future<void> setLogLevel(BleLogLevel logLevel) async {
|
||||
if (!kDebugMode) return;
|
||||
UniversalLogger.setLogLevel(logLevel);
|
||||
await _platform.setLogLevel(logLevel);
|
||||
}
|
||||
|
||||
/// Set how commands will be executed. By default, all commands are executed in a global queue (`QueueType.global`),
|
||||
/// with each command waiting for the previous one to finish.
|
||||
///
|
||||
|
||||
@@ -244,6 +244,10 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
@override
|
||||
Future<void> setNotifiable(String deviceId, String service,
|
||||
String characteristic, BleInputProperty bleInputProperty) async {
|
||||
UniversalLogger.logDebug(
|
||||
"SET_NOTIFY -> $deviceId $service $characteristic input=${bleInputProperty.name}",
|
||||
withTimestamp: true,
|
||||
);
|
||||
final char = _getCharacteristic(deviceId, service, characteristic);
|
||||
|
||||
String characteristicKey = "${deviceId}_${service}_$characteristic";
|
||||
@@ -265,10 +269,15 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
for (String property in properties) {
|
||||
switch (property) {
|
||||
case BluezProperty.value:
|
||||
UniversalLogger.logVerbose(
|
||||
"NOTIFY <- $deviceId $service $characteristic len=${char.value.length} data=${char.value}",
|
||||
withTimestamp: true,
|
||||
);
|
||||
updateCharacteristicValue(
|
||||
deviceId,
|
||||
characteristic,
|
||||
Uint8List.fromList(char.value),
|
||||
DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
@@ -293,11 +302,19 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
String characteristic, {
|
||||
final Duration? timeout,
|
||||
}) async {
|
||||
UniversalLogger.logDebug(
|
||||
"READ -> $deviceId $service $characteristic",
|
||||
withTimestamp: true,
|
||||
);
|
||||
try {
|
||||
final c = _getCharacteristic(deviceId, service, characteristic);
|
||||
final data = await c.readValue();
|
||||
return Uint8List.fromList(data);
|
||||
} on BlueZFailedException catch (e) {
|
||||
UniversalLogger.logError(
|
||||
"READ_FAILED <- $deviceId $service $characteristic ${e.message}",
|
||||
withTimestamp: true,
|
||||
);
|
||||
throw e.toUniversalBleException(
|
||||
defaultCode: UniversalBleErrorCode.readFailed,
|
||||
);
|
||||
@@ -311,6 +328,10 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
String characteristic,
|
||||
Uint8List value,
|
||||
BleOutputProperty bleOutputProperty) async {
|
||||
UniversalLogger.logDebug(
|
||||
"WRITE -> $deviceId $service $characteristic len=${value.length} property=${bleOutputProperty.name}",
|
||||
withTimestamp: true,
|
||||
);
|
||||
try {
|
||||
final c = _getCharacteristic(deviceId, service, characteristic);
|
||||
if (bleOutputProperty == BleOutputProperty.withResponse) {
|
||||
@@ -325,6 +346,10 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
);
|
||||
}
|
||||
} on BlueZFailedException catch (e) {
|
||||
UniversalLogger.logError(
|
||||
"WRITE_FAILED <- $deviceId $service $characteristic ${e.message}",
|
||||
withTimestamp: true,
|
||||
);
|
||||
throw e.toUniversalBleException(
|
||||
defaultCode: UniversalBleErrorCode.writeFailed,
|
||||
);
|
||||
|
||||
@@ -41,6 +41,15 @@ bool _deepEquals(Object? a, Object? b) {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
enum UniversalBleLogLevel {
|
||||
none,
|
||||
error,
|
||||
warning,
|
||||
info,
|
||||
debug,
|
||||
verbose,
|
||||
}
|
||||
|
||||
/// Unified error codes for all platforms
|
||||
enum UniversalBleErrorCode {
|
||||
unknownError,
|
||||
@@ -478,30 +487,33 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
if (value is int) {
|
||||
buffer.putUint8(4);
|
||||
buffer.putInt64(value);
|
||||
} else if (value is UniversalBleErrorCode) {
|
||||
} else if (value is UniversalBleLogLevel) {
|
||||
buffer.putUint8(129);
|
||||
writeValue(buffer, value.index);
|
||||
} else if (value is UniversalBleScanResult) {
|
||||
} else if (value is UniversalBleErrorCode) {
|
||||
buffer.putUint8(130);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalBleService) {
|
||||
writeValue(buffer, value.index);
|
||||
} else if (value is UniversalBleScanResult) {
|
||||
buffer.putUint8(131);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalBleCharacteristic) {
|
||||
} else if (value is UniversalBleService) {
|
||||
buffer.putUint8(132);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalBleDescriptor) {
|
||||
} else if (value is UniversalBleCharacteristic) {
|
||||
buffer.putUint8(133);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalScanFilter) {
|
||||
} else if (value is UniversalBleDescriptor) {
|
||||
buffer.putUint8(134);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerDataFilter) {
|
||||
} else if (value is UniversalScanFilter) {
|
||||
buffer.putUint8(135);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerData) {
|
||||
} else if (value is UniversalManufacturerDataFilter) {
|
||||
buffer.putUint8(136);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is UniversalManufacturerData) {
|
||||
buffer.putUint8(137);
|
||||
writeValue(buffer, value.encode());
|
||||
} else {
|
||||
super.writeValue(buffer, value);
|
||||
}
|
||||
@@ -512,20 +524,23 @@ class _PigeonCodec extends StandardMessageCodec {
|
||||
switch (type) {
|
||||
case 129:
|
||||
final value = readValue(buffer) as int?;
|
||||
return value == null ? null : UniversalBleErrorCode.values[value];
|
||||
return value == null ? null : UniversalBleLogLevel.values[value];
|
||||
case 130:
|
||||
return UniversalBleScanResult.decode(readValue(buffer)!);
|
||||
final value = readValue(buffer) as int?;
|
||||
return value == null ? null : UniversalBleErrorCode.values[value];
|
||||
case 131:
|
||||
return UniversalBleService.decode(readValue(buffer)!);
|
||||
return UniversalBleScanResult.decode(readValue(buffer)!);
|
||||
case 132:
|
||||
return UniversalBleCharacteristic.decode(readValue(buffer)!);
|
||||
return UniversalBleService.decode(readValue(buffer)!);
|
||||
case 133:
|
||||
return UniversalBleDescriptor.decode(readValue(buffer)!);
|
||||
return UniversalBleCharacteristic.decode(readValue(buffer)!);
|
||||
case 134:
|
||||
return UniversalScanFilter.decode(readValue(buffer)!);
|
||||
return UniversalBleDescriptor.decode(readValue(buffer)!);
|
||||
case 135:
|
||||
return UniversalManufacturerDataFilter.decode(readValue(buffer)!);
|
||||
return UniversalScanFilter.decode(readValue(buffer)!);
|
||||
case 136:
|
||||
return UniversalManufacturerDataFilter.decode(readValue(buffer)!);
|
||||
case 137:
|
||||
return UniversalManufacturerData.decode(readValue(buffer)!);
|
||||
default:
|
||||
return super.readValueOfType(type, buffer);
|
||||
@@ -1061,6 +1076,30 @@ class UniversalBlePlatformChannel {
|
||||
return (pigeonVar_replyList[0] as int?)!;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setLogLevel(UniversalBleLogLevel logLevel) async {
|
||||
final pigeonVar_channelName =
|
||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel$pigeonVar_messageChannelSuffix';
|
||||
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||
pigeonVar_channelName,
|
||||
pigeonChannelCodec,
|
||||
binaryMessenger: pigeonVar_binaryMessenger,
|
||||
);
|
||||
final Future<Object?> pigeonVar_sendFuture =
|
||||
pigeonVar_channel.send(<Object?>[logLevel]);
|
||||
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||
if (pigeonVar_replyList == null) {
|
||||
throw _createConnectionError(pigeonVar_channelName);
|
||||
} else if (pigeonVar_replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: pigeonVar_replyList[0]! as String,
|
||||
message: pigeonVar_replyList[1] as String?,
|
||||
details: pigeonVar_replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Native -> Flutter
|
||||
@@ -1073,8 +1112,8 @@ abstract class UniversalBleCallbackChannel {
|
||||
|
||||
void onScanResult(UniversalBleScanResult result);
|
||||
|
||||
void onValueChanged(
|
||||
String deviceId, String characteristicId, Uint8List value);
|
||||
void onValueChanged(String deviceId, String characteristicId, Uint8List value,
|
||||
int? timestamp);
|
||||
|
||||
void onConnectionChanged(String deviceId, bool connected, String? error);
|
||||
|
||||
@@ -1192,9 +1231,10 @@ abstract class UniversalBleCallbackChannel {
|
||||
final Uint8List? arg_value = (args[2] as Uint8List?);
|
||||
assert(arg_value != null,
|
||||
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null, expected non-null Uint8List.');
|
||||
final int? arg_timestamp = (args[3] as int?);
|
||||
try {
|
||||
api.onValueChanged(
|
||||
arg_deviceId!, arg_characteristicId!, arg_value!);
|
||||
api.onValueChanged(arg_deviceId!, arg_characteristicId!, arg_value!,
|
||||
arg_timestamp);
|
||||
return wrapResponse(empty: true);
|
||||
} on PlatformException catch (e) {
|
||||
return wrapResponse(error: e);
|
||||
|
||||
@@ -164,6 +164,10 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setLogLevel(BleLogLevel logLevel) => _executeWithErrorHandling(
|
||||
() => _channel.setLogLevel(logLevel.toUniversalBleLogLevel()));
|
||||
|
||||
/// To set listeners
|
||||
void _setupListeners() {
|
||||
UniversalBleCallbackChannel.setUp(_UniversalBleCallbackHandler(
|
||||
@@ -253,8 +257,12 @@ class _UniversalBleCallbackHandler extends UniversalBleCallbackChannel {
|
||||
|
||||
@override
|
||||
void onValueChanged(
|
||||
String deviceId, String characteristicId, Uint8List value) =>
|
||||
valueChanged(deviceId, characteristicId, value);
|
||||
String deviceId,
|
||||
String characteristicId,
|
||||
Uint8List value,
|
||||
int? timestamp,
|
||||
) =>
|
||||
valueChanged(deviceId, characteristicId, value, timestamp);
|
||||
|
||||
@override
|
||||
void onPairStateChange(String deviceId, bool isPaired, String? error) =>
|
||||
@@ -298,3 +306,14 @@ extension _ScanFilterExtension on ScanFilter? {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension _BleLogLevelExtension on BleLogLevel {
|
||||
UniversalBleLogLevel toUniversalBleLogLevel() => switch (this) {
|
||||
BleLogLevel.none => UniversalBleLogLevel.none,
|
||||
BleLogLevel.error => UniversalBleLogLevel.error,
|
||||
BleLogLevel.warning => UniversalBleLogLevel.warning,
|
||||
BleLogLevel.info => UniversalBleLogLevel.info,
|
||||
BleLogLevel.debug => UniversalBleLogLevel.debug,
|
||||
BleLogLevel.verbose => UniversalBleLogLevel.verbose,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'package:universal_ble/src/utils/cache_handler.dart';
|
||||
import 'package:universal_ble/src/utils/universal_ble_stream_controller.dart';
|
||||
import 'package:universal_ble/src/utils/universal_logger.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
abstract class UniversalBlePlatform {
|
||||
@@ -85,6 +86,9 @@ abstract class UniversalBlePlatform {
|
||||
List<String>? withServices,
|
||||
);
|
||||
|
||||
Future<void> setLogLevel(BleLogLevel logLevel) async =>
|
||||
UniversalLogger.setLogLevel(logLevel);
|
||||
|
||||
bool receivesAdvertisements(String deviceId) => true;
|
||||
|
||||
/// Streams
|
||||
@@ -142,6 +146,7 @@ abstract class UniversalBlePlatform {
|
||||
String deviceId,
|
||||
String characteristicId,
|
||||
Uint8List value,
|
||||
int? timestamp,
|
||||
) {
|
||||
characteristicId = BleUuidParser.string(characteristicId);
|
||||
_valueStreamController.add((
|
||||
@@ -150,7 +155,7 @@ abstract class UniversalBlePlatform {
|
||||
value: value,
|
||||
));
|
||||
try {
|
||||
onValueChange?.call(deviceId, characteristicId, value);
|
||||
onValueChange?.call(deviceId, characteristicId, value, timestamp);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
@@ -176,10 +181,17 @@ abstract class UniversalBlePlatform {
|
||||
|
||||
// Callback types
|
||||
typedef OnConnectionChange = void Function(
|
||||
String deviceId, bool isConnected, String? error);
|
||||
String deviceId,
|
||||
bool isConnected,
|
||||
String? error,
|
||||
);
|
||||
|
||||
typedef OnValueChange = void Function(
|
||||
String deviceId, String characteristicId, Uint8List value);
|
||||
String deviceId,
|
||||
String characteristicId,
|
||||
Uint8List value,
|
||||
int? timestamp,
|
||||
);
|
||||
|
||||
typedef OnScanResult = void Function(BleDevice scanResult);
|
||||
|
||||
|
||||
@@ -162,6 +162,10 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
||||
String characteristic,
|
||||
BleInputProperty bleInputProperty,
|
||||
) async {
|
||||
UniversalLogger.logDebug(
|
||||
"SET_NOTIFY -> $deviceId $service $characteristic input=${bleInputProperty.name}",
|
||||
withTimestamp: true,
|
||||
);
|
||||
final bleCharacteristic = await _getBleCharacteristic(
|
||||
deviceId: deviceId,
|
||||
serviceId: service,
|
||||
@@ -185,10 +189,20 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
||||
await bleCharacteristic.startNotifications();
|
||||
_characteristicStreamList[characteristicKey] =
|
||||
bleCharacteristic.value.listen((ByteData event) {
|
||||
final preview = event.buffer
|
||||
.asUint8List()
|
||||
.take(8)
|
||||
.map((e) => e.toRadixString(16).padLeft(2, '0'))
|
||||
.join();
|
||||
UniversalLogger.logVerbose(
|
||||
"NOTIFY <- $deviceId $characteristic len=${event.lengthInBytes} data=$preview",
|
||||
withTimestamp: true,
|
||||
);
|
||||
updateCharacteristicValue(
|
||||
deviceId,
|
||||
characteristic,
|
||||
event.buffer.asUint8List(),
|
||||
DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
@@ -205,6 +219,10 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
||||
Uint8List value,
|
||||
BleOutputProperty bleOutputProperty,
|
||||
) async {
|
||||
UniversalLogger.logDebug(
|
||||
"WRITE -> $deviceId $service $characteristic len=${value.length} property=${bleOutputProperty.name}",
|
||||
withTimestamp: true,
|
||||
);
|
||||
final bleCharacteristic = await _getBleCharacteristic(
|
||||
deviceId: deviceId,
|
||||
serviceId: service,
|
||||
@@ -234,6 +252,10 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
||||
String characteristic, {
|
||||
final Duration? timeout,
|
||||
}) async {
|
||||
UniversalLogger.logDebug(
|
||||
"READ -> $deviceId $service $characteristic",
|
||||
withTimestamp: true,
|
||||
);
|
||||
var bleCharacteristic = await _getBleCharacteristic(
|
||||
deviceId: deviceId,
|
||||
serviceId: service,
|
||||
|
||||
@@ -1,24 +1,80 @@
|
||||
import 'dart:developer';
|
||||
import 'package:universal_ble/src/models/ble_log_level.dart';
|
||||
|
||||
class UniversalLogger {
|
||||
static void logInfo(String message) {
|
||||
static BleLogLevel _currentLogLevel = BleLogLevel.none;
|
||||
|
||||
static BleLogLevel get currentLogLevel => _currentLogLevel;
|
||||
|
||||
static void setLogLevel(BleLogLevel logLevel) {
|
||||
_currentLogLevel = logLevel;
|
||||
}
|
||||
|
||||
static void logError(
|
||||
String message, {
|
||||
bool withTimestamp = false,
|
||||
}) {
|
||||
if (!_allows(BleLogLevel.error)) return;
|
||||
if (withTimestamp) {
|
||||
final ts = DateTime.now().toIso8601String();
|
||||
message = "[$ts] $message";
|
||||
}
|
||||
log(
|
||||
'\x1B[31m$message\x1B[0m',
|
||||
name: 'UniversalBle:ERROR',
|
||||
);
|
||||
}
|
||||
|
||||
static void logWarning(String message, {bool withTimestamp = false}) {
|
||||
if (!_allows(BleLogLevel.warning)) return;
|
||||
if (withTimestamp) {
|
||||
final ts = DateTime.now().toIso8601String();
|
||||
message = "[$ts] $message";
|
||||
}
|
||||
log(
|
||||
'\x1B[33m$message\x1B[0m',
|
||||
name: 'UniversalBle:WARN',
|
||||
);
|
||||
}
|
||||
|
||||
static void logInfo(String message, {bool withTimestamp = false}) {
|
||||
if (!_allows(BleLogLevel.info)) return;
|
||||
if (withTimestamp) {
|
||||
final ts = DateTime.now().toIso8601String();
|
||||
message = "[$ts] $message";
|
||||
}
|
||||
log(
|
||||
message.toString(),
|
||||
name: 'UniversalBle:INFO',
|
||||
);
|
||||
}
|
||||
|
||||
static void logError(String message) {
|
||||
static void logDebug(String message, {bool withTimestamp = false}) {
|
||||
if (!_allows(BleLogLevel.debug)) return;
|
||||
if (withTimestamp) {
|
||||
final ts = DateTime.now().toIso8601String();
|
||||
message = "[$ts] $message";
|
||||
}
|
||||
log(
|
||||
'\x1B[31m$message\x1B[31m',
|
||||
name: 'UniversalBle:ERROR',
|
||||
message.toString(),
|
||||
name: 'UniversalBle:DEBUG',
|
||||
);
|
||||
}
|
||||
|
||||
static void logWarning(String message) {
|
||||
static void logVerbose(String message, {bool withTimestamp = false}) {
|
||||
if (!_allows(BleLogLevel.verbose)) return;
|
||||
if (withTimestamp) {
|
||||
final ts = DateTime.now().toIso8601String();
|
||||
message = "[$ts] $message";
|
||||
}
|
||||
log(
|
||||
'\x1B[33m$message\x1B[33m',
|
||||
name: 'UniversalBle:WARN',
|
||||
message.toString(),
|
||||
name: 'UniversalBle:VERBOSE',
|
||||
);
|
||||
}
|
||||
|
||||
static bool _allows(BleLogLevel level) {
|
||||
return level.index <= _currentLogLevel.index &&
|
||||
_currentLogLevel != BleLogLevel.none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,8 @@ abstract class UniversalBlePlatformChannel {
|
||||
);
|
||||
|
||||
int getConnectionState(String deviceId);
|
||||
|
||||
void setLogLevel(UniversalBleLogLevel logLevel);
|
||||
}
|
||||
|
||||
/// Native -> Flutter
|
||||
@@ -105,6 +107,7 @@ abstract class UniversalBleCallbackChannel {
|
||||
String deviceId,
|
||||
String characteristicId,
|
||||
Uint8List value,
|
||||
int? timestamp,
|
||||
);
|
||||
|
||||
void onConnectionChanged(
|
||||
@@ -134,6 +137,15 @@ class UniversalBleScanResult {
|
||||
});
|
||||
}
|
||||
|
||||
enum UniversalBleLogLevel {
|
||||
none,
|
||||
error,
|
||||
warning,
|
||||
info,
|
||||
debug,
|
||||
verbose,
|
||||
}
|
||||
|
||||
class UniversalBleService {
|
||||
String uuid;
|
||||
List<UniversalBleCharacteristic>? characteristics;
|
||||
|
||||
@@ -106,6 +106,7 @@ class _UniversalBleMock extends UniversalBlePlatformMock {
|
||||
deviceId,
|
||||
characteristic,
|
||||
Uint8List.fromList([1, 2, 3]),
|
||||
DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ list(APPEND PLUGIN_SOURCES
|
||||
"src/universal_ble_filter_util.h"
|
||||
"src/universal_ble_thread_safe.h"
|
||||
"src/enum_parser.h"
|
||||
"src/helper/universal_ble_logger.cpp"
|
||||
"src/helper/universal_ble_logger.h"
|
||||
)
|
||||
|
||||
add_library(${PLUGIN_NAME} SHARED
|
||||
|
||||
@@ -487,27 +487,32 @@ EncodableValue PigeonInternalCodecSerializer::ReadValueOfType(
|
||||
case 129: {
|
||||
const auto& encodable_enum_arg = ReadValue(stream);
|
||||
const int64_t enum_arg_value = encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue();
|
||||
return encodable_enum_arg.IsNull() ? EncodableValue() : CustomEncodableValue(static_cast<UniversalBleErrorCode>(enum_arg_value));
|
||||
return encodable_enum_arg.IsNull() ? EncodableValue() : CustomEncodableValue(static_cast<UniversalBleLogLevel>(enum_arg_value));
|
||||
}
|
||||
case 130: {
|
||||
return CustomEncodableValue(UniversalBleScanResult::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
const auto& encodable_enum_arg = ReadValue(stream);
|
||||
const int64_t enum_arg_value = encodable_enum_arg.IsNull() ? 0 : encodable_enum_arg.LongValue();
|
||||
return encodable_enum_arg.IsNull() ? EncodableValue() : CustomEncodableValue(static_cast<UniversalBleErrorCode>(enum_arg_value));
|
||||
}
|
||||
case 131: {
|
||||
return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
return CustomEncodableValue(UniversalBleScanResult::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 132: {
|
||||
return CustomEncodableValue(UniversalBleCharacteristic::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 133: {
|
||||
return CustomEncodableValue(UniversalBleDescriptor::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
return CustomEncodableValue(UniversalBleCharacteristic::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 134: {
|
||||
return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
return CustomEncodableValue(UniversalBleDescriptor::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 135: {
|
||||
return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 136: {
|
||||
return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
case 137: {
|
||||
return CustomEncodableValue(UniversalManufacturerData::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
|
||||
}
|
||||
default:
|
||||
@@ -519,43 +524,48 @@ void PigeonInternalCodecSerializer::WriteValue(
|
||||
const EncodableValue& value,
|
||||
flutter::ByteStreamWriter* stream) const {
|
||||
if (const CustomEncodableValue* custom_value = std::get_if<CustomEncodableValue>(&value)) {
|
||||
if (custom_value->type() == typeid(UniversalBleErrorCode)) {
|
||||
if (custom_value->type() == typeid(UniversalBleLogLevel)) {
|
||||
stream->WriteByte(129);
|
||||
WriteValue(EncodableValue(static_cast<int>(std::any_cast<UniversalBleLogLevel>(*custom_value))), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleErrorCode)) {
|
||||
stream->WriteByte(130);
|
||||
WriteValue(EncodableValue(static_cast<int>(std::any_cast<UniversalBleErrorCode>(*custom_value))), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleScanResult)) {
|
||||
stream->WriteByte(130);
|
||||
stream->WriteByte(131);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalBleScanResult>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleService)) {
|
||||
stream->WriteByte(131);
|
||||
stream->WriteByte(132);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalBleService>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleCharacteristic)) {
|
||||
stream->WriteByte(132);
|
||||
stream->WriteByte(133);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalBleCharacteristic>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalBleDescriptor)) {
|
||||
stream->WriteByte(133);
|
||||
stream->WriteByte(134);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalBleDescriptor>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalScanFilter)) {
|
||||
stream->WriteByte(134);
|
||||
stream->WriteByte(135);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalScanFilter>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalManufacturerDataFilter)) {
|
||||
stream->WriteByte(135);
|
||||
stream->WriteByte(136);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalManufacturerDataFilter>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
if (custom_value->type() == typeid(UniversalManufacturerData)) {
|
||||
stream->WriteByte(136);
|
||||
stream->WriteByte(137);
|
||||
WriteValue(EncodableValue(std::any_cast<UniversalManufacturerData>(*custom_value).ToEncodableList()), stream);
|
||||
return;
|
||||
}
|
||||
@@ -1151,6 +1161,34 @@ void UniversalBlePlatformChannel::SetUp(
|
||||
channel.SetMessageHandler(nullptr);
|
||||
}
|
||||
}
|
||||
{
|
||||
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setLogLevel" + prepended_suffix, &GetCodec());
|
||||
if (api != nullptr) {
|
||||
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
|
||||
try {
|
||||
const auto& args = std::get<EncodableList>(message);
|
||||
const auto& encodable_log_level_arg = args.at(0);
|
||||
if (encodable_log_level_arg.IsNull()) {
|
||||
reply(WrapError("log_level_arg unexpectedly null."));
|
||||
return;
|
||||
}
|
||||
const auto& log_level_arg = std::any_cast<const UniversalBleLogLevel&>(std::get<CustomEncodableValue>(encodable_log_level_arg));
|
||||
std::optional<FlutterError> output = api->SetLogLevel(log_level_arg);
|
||||
if (output.has_value()) {
|
||||
reply(WrapError(output.value()));
|
||||
return;
|
||||
}
|
||||
EncodableList wrapped;
|
||||
wrapped.push_back(EncodableValue());
|
||||
reply(EncodableValue(std::move(wrapped)));
|
||||
} catch (const std::exception& exception) {
|
||||
reply(WrapError(exception.what()));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
channel.SetMessageHandler(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EncodableValue UniversalBlePlatformChannel::WrapError(std::string_view error_message) {
|
||||
@@ -1267,6 +1305,7 @@ void UniversalBleCallbackChannel::OnValueChanged(
|
||||
const std::string& device_id_arg,
|
||||
const std::string& characteristic_id_arg,
|
||||
const std::vector<uint8_t>& value_arg,
|
||||
const int64_t* timestamp_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.onValueChanged" + message_channel_suffix_;
|
||||
@@ -1275,6 +1314,7 @@ void UniversalBleCallbackChannel::OnValueChanged(
|
||||
EncodableValue(device_id_arg),
|
||||
EncodableValue(characteristic_id_arg),
|
||||
EncodableValue(value_arg),
|
||||
timestamp_arg ? EncodableValue(*timestamp_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);
|
||||
|
||||
@@ -57,6 +57,15 @@ template<class T> class ErrorOr {
|
||||
};
|
||||
|
||||
|
||||
enum class UniversalBleLogLevel {
|
||||
kNone = 0,
|
||||
kError = 1,
|
||||
kWarning = 2,
|
||||
kInfo = 3,
|
||||
kDebug = 4,
|
||||
kVerbose = 5
|
||||
};
|
||||
|
||||
// Unified error codes for all platforms
|
||||
enum class UniversalBleErrorCode {
|
||||
kUnknownError = 0,
|
||||
@@ -425,6 +434,7 @@ class UniversalBlePlatformChannel {
|
||||
const flutter::EncodableList& with_services,
|
||||
std::function<void(ErrorOr<flutter::EncodableList> reply)> result) = 0;
|
||||
virtual ErrorOr<int64_t> GetConnectionState(const std::string& device_id) = 0;
|
||||
virtual std::optional<FlutterError> SetLogLevel(const UniversalBleLogLevel& log_level) = 0;
|
||||
|
||||
// The codec used by UniversalBlePlatformChannel.
|
||||
static const flutter::StandardMessageCodec& GetCodec();
|
||||
@@ -469,6 +479,7 @@ class UniversalBleCallbackChannel {
|
||||
const std::string& device_id,
|
||||
const std::string& characteristic_id,
|
||||
const std::vector<uint8_t>& value,
|
||||
const int64_t* timestamp,
|
||||
std::function<void(void)>&& on_success,
|
||||
std::function<void(const FlutterError&)>&& on_error);
|
||||
void OnConnectionChanged(
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "universal_ble_logger.h"
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
namespace universal_ble {
|
||||
|
||||
UniversalBleLogLevel UniversalBleLogger::current_level_ =
|
||||
UniversalBleLogLevel::kNone;
|
||||
|
||||
static std::string GetCurrentTimestampString() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto time_t = std::chrono::system_clock::to_time_t(now);
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now.time_since_epoch()) %
|
||||
1000;
|
||||
|
||||
std::tm timeinfo;
|
||||
localtime_s(&timeinfo, &time_t);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << std::put_time(&timeinfo, "%H:%M:%S");
|
||||
oss << "." << std::setfill('0') << std::setw(3) << ms.count();
|
||||
return "[" + oss.str() + "]";
|
||||
}
|
||||
|
||||
void UniversalBleLogger::SetLogLevel(UniversalBleLogLevel level) {
|
||||
current_level_ = level;
|
||||
}
|
||||
|
||||
UniversalBleLogLevel UniversalBleLogger::current_log_level() {
|
||||
return current_level_;
|
||||
}
|
||||
|
||||
void UniversalBleLogger::LogError(const std::string &message) {
|
||||
if (!Allows(UniversalBleLogLevel::kError))
|
||||
return;
|
||||
std::cout << "UniversalBle:ERROR " << message << std::endl;
|
||||
}
|
||||
|
||||
void UniversalBleLogger::LogWarning(const std::string &message) {
|
||||
if (!Allows(UniversalBleLogLevel::kWarning))
|
||||
return;
|
||||
std::cout << "UniversalBle:WARN " << message << std::endl;
|
||||
}
|
||||
|
||||
void UniversalBleLogger::LogInfo(const std::string &message) {
|
||||
if (!Allows(UniversalBleLogLevel::kInfo))
|
||||
return;
|
||||
std::cout << "UniversalBle:INFO " << message << std::endl;
|
||||
}
|
||||
|
||||
void UniversalBleLogger::LogDebug(const std::string &message) {
|
||||
if (!Allows(UniversalBleLogLevel::kDebug))
|
||||
return;
|
||||
std::cout << "UniversalBle:DEBUG " << message << std::endl;
|
||||
}
|
||||
|
||||
void UniversalBleLogger::LogVerbose(const std::string &message) {
|
||||
if (!Allows(UniversalBleLogLevel::kVerbose))
|
||||
return;
|
||||
std::cout << "UniversalBle:VERBOSE " << message << std::endl;
|
||||
}
|
||||
|
||||
void UniversalBleLogger::LogDebugWithTimestamp(const std::string &message) {
|
||||
if (!Allows(UniversalBleLogLevel::kDebug))
|
||||
return;
|
||||
std::cout << "UniversalBle:DEBUG " << GetCurrentTimestampString() << " " << message
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void UniversalBleLogger::LogVerboseWithTimestamp(const std::string &message) {
|
||||
if (!Allows(UniversalBleLogLevel::kVerbose))
|
||||
return;
|
||||
std::cout << "UniversalBle:VERBOSE " << GetCurrentTimestampString() << " "
|
||||
<< message << std::endl;
|
||||
}
|
||||
|
||||
bool UniversalBleLogger::Allows(UniversalBleLogLevel level) {
|
||||
return current_level_ != UniversalBleLogLevel::kNone &&
|
||||
static_cast<int>(level) <= static_cast<int>(current_level_);
|
||||
}
|
||||
|
||||
} // namespace universal_ble
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "../generated/universal_ble.g.h"
|
||||
|
||||
namespace universal_ble {
|
||||
|
||||
class UniversalBleLogger {
|
||||
public:
|
||||
static void SetLogLevel(UniversalBleLogLevel level);
|
||||
static UniversalBleLogLevel current_log_level();
|
||||
|
||||
static void LogError(const std::string &message);
|
||||
static void LogWarning(const std::string &message);
|
||||
static void LogInfo(const std::string &message);
|
||||
static void LogDebug(const std::string &message);
|
||||
static void LogVerbose(const std::string &message);
|
||||
static void LogDebugWithTimestamp(const std::string &message);
|
||||
static void LogVerboseWithTimestamp(const std::string &message);
|
||||
|
||||
private:
|
||||
static UniversalBleLogLevel current_level_;
|
||||
static bool Allows(UniversalBleLogLevel level);
|
||||
};
|
||||
|
||||
} // namespace universal_ble
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "enum_parser.h"
|
||||
#include "generated/universal_ble.g.h"
|
||||
#include "helper/universal_ble_logger.h"
|
||||
#include "helper/universal_enum.h"
|
||||
#include "helper/utils.h"
|
||||
#include "pin_entry.h"
|
||||
@@ -165,7 +166,7 @@ UniversalBlePlugin::StartScan(const UniversalScanFilter *filter) {
|
||||
filter->with_name_prefix().size() > 0;
|
||||
|
||||
if (uses_custom_filters) {
|
||||
std::cout << "Using Custom Scan Filter" << std::endl;
|
||||
UniversalBleLogger::LogInfo("Using Custom Scan Filter");
|
||||
setScanFilter(*filter);
|
||||
} else {
|
||||
// Apply Services filter
|
||||
@@ -186,7 +187,7 @@ UniversalBlePlugin::StartScan(const UniversalScanFilter *filter) {
|
||||
bluetooth_le_watcher_.Start();
|
||||
return std::nullopt;
|
||||
} catch (...) {
|
||||
std::cout << "Unknown error StartScan" << std::endl;
|
||||
UniversalBleLogger::LogError("Unknown error StartScan");
|
||||
return create_flutter_error(UniversalBleErrorCode::kUnknownError,
|
||||
"Unknown error");
|
||||
}
|
||||
@@ -205,8 +206,8 @@ std::optional<FlutterError> UniversalBlePlugin::StopScan() {
|
||||
return std::nullopt;
|
||||
} catch (const hresult_error &err) {
|
||||
const int error_code = err.code();
|
||||
std::cout << "StopScanLog: " << to_string(err.message())
|
||||
<< " ErrorCode: " << std::to_string(error_code) << std::endl;
|
||||
UniversalBleLogger::LogError("StopScanLog: " + to_string(err.message()) +
|
||||
" ErrorCode: " + std::to_string(error_code));
|
||||
return create_flutter_error(UniversalBleErrorCode::kFailed,
|
||||
to_string(err.message()),
|
||||
std::to_string(error_code));
|
||||
@@ -248,6 +249,12 @@ UniversalBlePlugin::GetConnectionState(const std::string &device_id) {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<FlutterError>
|
||||
UniversalBlePlugin::SetLogLevel(const UniversalBleLogLevel &log_level) {
|
||||
UniversalBleLogger::SetLogLevel(log_level);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<FlutterError>
|
||||
UniversalBlePlugin::Connect(const std::string &device_id) {
|
||||
ConnectAsync(str_to_mac_address(device_id));
|
||||
@@ -288,6 +295,8 @@ void UniversalBlePlugin::ReadValue(
|
||||
const std::string &device_id, const std::string &service,
|
||||
const std::string &characteristic,
|
||||
std::function<void(ErrorOr<std::vector<uint8_t>> reply)> result) {
|
||||
UniversalBleLogger::LogDebugWithTimestamp("READ -> " + device_id + " " +
|
||||
service + " " + characteristic);
|
||||
try {
|
||||
const auto it = connected_devices_.find(str_to_mac_address(device_id));
|
||||
if (it == connected_devices_.end()) {
|
||||
@@ -317,6 +326,10 @@ void UniversalBlePlugin::ReadValue(
|
||||
const auto read_value_result = sender.GetResults();
|
||||
const auto status = read_value_result.Status();
|
||||
if (status != GattCommunicationStatus::Success) {
|
||||
UniversalBleLogger::LogError(
|
||||
"READ_FAILED <- " + device_id + " " + service + " " +
|
||||
characteristic +
|
||||
" status=" + std::to_string(static_cast<int>(status)));
|
||||
result(create_flutter_error_from_gatt_communication_status(status));
|
||||
} else {
|
||||
result(to_bytevc(read_value_result.Value()));
|
||||
@@ -325,7 +338,7 @@ void UniversalBlePlugin::ReadValue(
|
||||
} catch (const FlutterError &err) {
|
||||
return result(err);
|
||||
} catch (...) {
|
||||
std::cout << "ReadValueLog: Unknown error" << std::endl;
|
||||
UniversalBleLogger::LogError("ReadValueLog: Unknown error");
|
||||
return result(create_flutter_unknown_error());
|
||||
}
|
||||
}
|
||||
@@ -335,6 +348,10 @@ void UniversalBlePlugin::WriteValue(
|
||||
const std::string &characteristic, const std::vector<uint8_t> &value,
|
||||
int64_t ble_output_property,
|
||||
std::function<void(std::optional<FlutterError> reply)> result) {
|
||||
UniversalBleLogger::LogDebugWithTimestamp(
|
||||
"WRITE -> " + device_id + " " + service + " " + characteristic +
|
||||
" len=" + std::to_string(value.size()) +
|
||||
" property=" + std::to_string(ble_output_property));
|
||||
try {
|
||||
const auto it = connected_devices_.find(str_to_mac_address(device_id));
|
||||
if (it == connected_devices_.end()) {
|
||||
@@ -383,6 +400,10 @@ void UniversalBlePlugin::WriteValue(
|
||||
|
||||
const auto status = sender.GetResults();
|
||||
if (status != GattCommunicationStatus::Success) {
|
||||
UniversalBleLogger::LogError(
|
||||
"WRITE_FAILED <- " + device_id + " " + service + " " +
|
||||
characteristic +
|
||||
" status=" + std::to_string(static_cast<int>(status)));
|
||||
result(create_flutter_error_from_gatt_communication_status(status));
|
||||
} else {
|
||||
result(std::nullopt);
|
||||
@@ -391,7 +412,7 @@ void UniversalBlePlugin::WriteValue(
|
||||
} catch (const FlutterError &err) {
|
||||
result(err);
|
||||
} catch (...) {
|
||||
std::cout << "WriteValue: Unknown error" << std::endl;
|
||||
UniversalBleLogger::LogError("WriteValue: Unknown error");
|
||||
result(create_flutter_unknown_error());
|
||||
}
|
||||
}
|
||||
@@ -399,6 +420,9 @@ void UniversalBlePlugin::WriteValue(
|
||||
void UniversalBlePlugin::RequestMtu(
|
||||
const std::string &device_id, int64_t expected_mtu,
|
||||
std::function<void(ErrorOr<int64_t> reply)> result) {
|
||||
UniversalBleLogger::LogDebugWithTimestamp(
|
||||
"REQUEST_MTU -> " + device_id +
|
||||
" expected=" + std::to_string(expected_mtu));
|
||||
try {
|
||||
const auto it = connected_devices_.find(str_to_mac_address(device_id));
|
||||
if (it == connected_devices_.end()) {
|
||||
@@ -495,7 +519,7 @@ fire_and_forget UniversalBlePlugin::InitializeAsync() {
|
||||
}
|
||||
}
|
||||
if (!bluetooth_radio_) {
|
||||
std::cout << "Bluetooth is not available" << std::endl;
|
||||
UniversalBleLogger::LogError("Bluetooth is not available");
|
||||
ui_thread_handler_.Post([] {
|
||||
callback_channel->OnAvailabilityChanged(
|
||||
static_cast<int>(AvailabilityState::unsupported), SuccessCallback,
|
||||
@@ -509,7 +533,7 @@ fire_and_forget UniversalBlePlugin::PairAsync(
|
||||
const std::string &device_id,
|
||||
const std::function<void(ErrorOr<bool> reply)> result) {
|
||||
try {
|
||||
std::cout << "Trying to pair" << std::endl;
|
||||
UniversalBleLogger::LogInfo("Trying to pair");
|
||||
|
||||
const auto device = co_await BluetoothLEDevice::FromBluetoothAddressAsync(
|
||||
str_to_mac_address(device_id));
|
||||
@@ -519,7 +543,7 @@ fire_and_forget UniversalBlePlugin::PairAsync(
|
||||
co_return;
|
||||
}
|
||||
|
||||
std::cout << "Got device" << std::endl;
|
||||
UniversalBleLogger::LogInfo("Got device");
|
||||
|
||||
const auto device_information = device.DeviceInformation();
|
||||
if (device_information.Pairing().IsPaired())
|
||||
@@ -530,25 +554,31 @@ fire_and_forget UniversalBlePlugin::PairAsync(
|
||||
else {
|
||||
const auto pair_result =
|
||||
co_await device_information.Pairing().PairAsync();
|
||||
std::cout << "PairLog: Received pairing status" << std::endl;
|
||||
UniversalBleLogger::LogInfo("PairLog: Received pairing status");
|
||||
bool is_paired =
|
||||
pair_result.Status() == DevicePairingResultStatus::Paired;
|
||||
result(is_paired);
|
||||
|
||||
const std::string *error_msg = nullptr;
|
||||
const auto error_str =
|
||||
device_pairing_result_to_string(pair_result.Status());
|
||||
std::optional<std::string> captured_error;
|
||||
if (error_str.has_value()) {
|
||||
error_msg = &error_str.value();
|
||||
captured_error = error_str.value();
|
||||
}
|
||||
ui_thread_handler_.Post([device_id, is_paired, error_msg] {
|
||||
ui_thread_handler_.Post([device_id, is_paired, captured_error] {
|
||||
const std::string *error_msg = nullptr;
|
||||
std::string error_string;
|
||||
if (captured_error.has_value()) {
|
||||
error_string = captured_error.value();
|
||||
error_msg = &error_string;
|
||||
}
|
||||
callback_channel->OnPairStateChange(device_id, is_paired, error_msg,
|
||||
SuccessCallback, ErrorCallback);
|
||||
});
|
||||
}
|
||||
} catch (...) {
|
||||
result(false);
|
||||
std::cout << "PairLog: Unknown error" << std::endl;
|
||||
UniversalBleLogger::LogError("PairLog: Unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,7 +603,7 @@ fire_and_forget UniversalBlePlugin::CustomPairAsync(
|
||||
const auto custom_pairing = device_information.Pairing().Custom();
|
||||
const event_token token = custom_pairing.PairingRequested(
|
||||
{this, &UniversalBlePlugin::PairingRequestedHandler});
|
||||
std::cout << "PairLog: Trying to pair" << std::endl;
|
||||
UniversalBleLogger::LogInfo("PairLog: Trying to pair");
|
||||
const DevicePairingProtectionLevel protection_level =
|
||||
device_information.Pairing().ProtectionLevel();
|
||||
// DevicePairingKinds => None, ConfirmOnly, DisplayPin, ProvidePin,
|
||||
@@ -581,25 +611,31 @@ fire_and_forget UniversalBlePlugin::CustomPairAsync(
|
||||
const auto pair_result = co_await custom_pairing.PairAsync(
|
||||
DevicePairingKinds::ConfirmOnly | DevicePairingKinds::ProvidePin,
|
||||
protection_level);
|
||||
std::cout << "PairLog: Got Pair Result" << std::endl;
|
||||
UniversalBleLogger::LogInfo("PairLog: Got Pair Result");
|
||||
const DevicePairingResultStatus status = pair_result.Status();
|
||||
custom_pairing.PairingRequested(token);
|
||||
bool is_paired = status == DevicePairingResultStatus::Paired;
|
||||
result(is_paired);
|
||||
|
||||
const std::string *error_msg = nullptr;
|
||||
const auto error_str = device_pairing_result_to_string(status);
|
||||
std::optional<std::string> captured_error;
|
||||
if (error_str.has_value()) {
|
||||
error_msg = &error_str.value();
|
||||
captured_error = error_str.value();
|
||||
}
|
||||
ui_thread_handler_.Post([device_id, is_paired, error_msg] {
|
||||
ui_thread_handler_.Post([device_id, is_paired, captured_error] {
|
||||
const std::string *error_msg = nullptr;
|
||||
std::string error_string;
|
||||
if (captured_error.has_value()) {
|
||||
error_string = captured_error.value();
|
||||
error_msg = &error_string;
|
||||
}
|
||||
callback_channel->OnPairStateChange(device_id, is_paired, error_msg,
|
||||
SuccessCallback, ErrorCallback);
|
||||
});
|
||||
}
|
||||
} catch (...) {
|
||||
result(false);
|
||||
std::cout << "PairLog Error: Pairing Failed" << std::endl;
|
||||
UniversalBleLogger::LogError("PairLog Error: Pairing Failed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,16 +643,16 @@ fire_and_forget UniversalBlePlugin::CustomPairAsync(
|
||||
void UniversalBlePlugin::PairingRequestedHandler(
|
||||
DeviceInformationCustomPairing sender,
|
||||
const DevicePairingRequestedEventArgs &event_args) {
|
||||
std::cout << "PairLog: Got PairingRequest" << std::endl;
|
||||
UniversalBleLogger::LogInfo("PairLog: Got PairingRequest");
|
||||
const DevicePairingKinds kind = event_args.PairingKind();
|
||||
if (kind != DevicePairingKinds::ProvidePin) {
|
||||
event_args.Accept();
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << "PairLog: Trying to get pin from user" << std::endl;
|
||||
UniversalBleLogger::LogInfo("PairLog: Trying to get pin from user");
|
||||
const hstring pin = askForPairingPin();
|
||||
std::wcout << "PairLog: Got Pin: " << pin.c_str() << std::endl;
|
||||
UniversalBleLogger::LogInfo("PairLog: Got Pin: " + to_string(pin));
|
||||
event_args.Accept(pin);
|
||||
}
|
||||
|
||||
@@ -678,10 +714,7 @@ void UniversalBlePlugin::PushUniversalScanResult(
|
||||
|
||||
// Filter final result before sending to Flutter
|
||||
if (is_connectable && filterDevice(scan_result)) {
|
||||
scan_result.set_timestamp(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count());
|
||||
scan_result.set_timestamp(GetCurrentTimestampMillis());
|
||||
ui_thread_handler_.Post([scan_result] {
|
||||
callback_channel->OnScanResult(scan_result, SuccessCallback,
|
||||
ErrorCallback);
|
||||
@@ -762,7 +795,7 @@ void UniversalBlePlugin::SetupDeviceWatcher() {
|
||||
device_watcher_enumeration_completed_token_ =
|
||||
device_watcher_.EnumerationCompleted([this](DeviceWatcher sender,
|
||||
IInspectable args) {
|
||||
std::cout << "DeviceWatcherEvent: EnumerationCompleted" << std::endl;
|
||||
UniversalBleLogger::LogInfo("DeviceWatcherEvent: EnumerationCompleted");
|
||||
DisposeDeviceWatcher();
|
||||
// EnumerationCompleted
|
||||
});
|
||||
@@ -913,7 +946,7 @@ void UniversalBlePlugin::BluetoothLeWatcherReceived(
|
||||
// Filter Device
|
||||
PushUniversalScanResult(universal_scan_result, args.IsConnectable());
|
||||
} catch (...) {
|
||||
std::cout << "ScanResultErrorInParsing" << std::endl;
|
||||
UniversalBleLogger::LogError("ScanResultErrorInParsing");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -936,8 +969,8 @@ fire_and_forget UniversalBlePlugin::ConnectAsync(uint64_t bluetooth_address) {
|
||||
BluetoothLEDevice device =
|
||||
co_await BluetoothLEDevice::FromBluetoothAddressAsync(bluetooth_address);
|
||||
if (!device) {
|
||||
std::cout << "ConnectionLog: ConnectionFailed: Failed to get device"
|
||||
<< std::endl;
|
||||
UniversalBleLogger::LogError(
|
||||
"ConnectionLog: ConnectionFailed: Failed to get device");
|
||||
ui_thread_handler_.Post([bluetooth_address] {
|
||||
callback_channel->OnConnectionChanged(
|
||||
mac_address_to_str(bluetooth_address), false,
|
||||
@@ -947,14 +980,14 @@ fire_and_forget UniversalBlePlugin::ConnectAsync(uint64_t bluetooth_address) {
|
||||
|
||||
co_return;
|
||||
}
|
||||
std::cout << "ConnectionLog: Device found" << std::endl;
|
||||
UniversalBleLogger::LogInfo("ConnectionLog: Device found");
|
||||
auto services_result =
|
||||
co_await device.GetGattServicesAsync((BluetoothCacheMode::Uncached));
|
||||
auto services_result_error =
|
||||
gatt_communication_status_to_error(services_result.Status());
|
||||
if (services_result_error.has_value()) {
|
||||
std::cout << "ConnectionFailed: Failed to get services: "
|
||||
<< services_result_error.value() << std::endl;
|
||||
UniversalBleLogger::LogError("ConnectionFailed: Failed to get services: " +
|
||||
services_result_error.value());
|
||||
ui_thread_handler_.Post([bluetooth_address, services_result_error] {
|
||||
callback_channel->OnConnectionChanged(
|
||||
mac_address_to_str(bluetooth_address), false,
|
||||
@@ -963,7 +996,7 @@ fire_and_forget UniversalBlePlugin::ConnectAsync(uint64_t bluetooth_address) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
std::cout << "ConnectionLog: Services discovered" << std::endl;
|
||||
UniversalBleLogger::LogInfo("ConnectionLog: Services discovered");
|
||||
std::unordered_map<std::string, GattServiceObject> gatt_map;
|
||||
auto gatt_services = services_result.Services();
|
||||
for (GattDeviceService &&service : gatt_services) {
|
||||
@@ -976,9 +1009,9 @@ fire_and_forget UniversalBlePlugin::ConnectAsync(uint64_t bluetooth_address) {
|
||||
gatt_communication_status_to_error(characteristics_result.Status());
|
||||
|
||||
if (characteristics_result_error.has_value()) {
|
||||
std::cout << "Failed to get characteristics for service: " << service_uuid
|
||||
<< ", With Status: " << characteristics_result_error.value()
|
||||
<< std::endl;
|
||||
UniversalBleLogger::LogError(
|
||||
"Failed to get characteristics for service: " + service_uuid +
|
||||
", With Status: " + characteristics_result_error.value());
|
||||
continue;
|
||||
// PostConnectionUpdate(bluetoothAddress, ConnectionState::disconnected);
|
||||
// co_return;
|
||||
@@ -1001,7 +1034,7 @@ fire_and_forget UniversalBlePlugin::ConnectAsync(uint64_t bluetooth_address) {
|
||||
device, connection_status_changed_token, gatt_map);
|
||||
auto pair = std::make_pair(bluetooth_address, std::move(device_agent));
|
||||
connected_devices_.insert(std::move(pair));
|
||||
std::cout << "ConnectionLog: Connected" << std::endl;
|
||||
UniversalBleLogger::LogInfo("ConnectionLog: Connected");
|
||||
ui_thread_handler_.Post([bluetooth_address] {
|
||||
callback_channel->OnConnectionChanged(mac_address_to_str(bluetooth_address),
|
||||
true, nullptr, SuccessCallback,
|
||||
@@ -1089,13 +1122,14 @@ fire_and_forget UniversalBlePlugin::GetSystemDevicesAsync(
|
||||
result(results);
|
||||
} catch (const hresult_error &err) {
|
||||
int error_code = err.code();
|
||||
std::cout << "GetConnectedDeviceLog: " << to_string(err.message())
|
||||
<< " ErrorCode: " << std::to_string(error_code) << std::endl;
|
||||
UniversalBleLogger::LogError(
|
||||
"GetConnectedDeviceLog: " + to_string(err.message()) +
|
||||
" ErrorCode: " + std::to_string(error_code));
|
||||
result(create_flutter_error(UniversalBleErrorCode::kFailed,
|
||||
to_string(err.message()),
|
||||
std::to_string(error_code)));
|
||||
} catch (...) {
|
||||
std::cout << "Unknown error GetSystemDevicesAsyncAsync" << std::endl;
|
||||
UniversalBleLogger::LogError("Unknown error GetSystemDevicesAsyncAsync");
|
||||
result(create_flutter_error(UniversalBleErrorCode::kUnknownError,
|
||||
"Unknown error"));
|
||||
}
|
||||
@@ -1136,9 +1170,8 @@ fire_and_forget UniversalBlePlugin::DiscoverServicesAsync(
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
std::cout << "DiscoverServicesAsync: failed to get descriptors for "
|
||||
"characteristic: "
|
||||
<< std::endl;
|
||||
UniversalBleLogger::LogError("DiscoverServicesAsync: failed to get "
|
||||
"descriptors for characteristic");
|
||||
}
|
||||
}
|
||||
universal_characteristics.push_back(
|
||||
@@ -1180,7 +1213,7 @@ fire_and_forget UniversalBlePlugin::IsPairedAsync(
|
||||
const bool is_paired = device.DeviceInformation().Pairing().IsPaired();
|
||||
result(is_paired);
|
||||
} catch (...) {
|
||||
std::cout << "IsPairedAsync: Error " << std::endl;
|
||||
UniversalBleLogger::LogError("IsPairedAsync: Error");
|
||||
result(create_flutter_error(UniversalBleErrorCode::kUnknownError,
|
||||
"Unknown error"));
|
||||
}
|
||||
@@ -1190,6 +1223,9 @@ fire_and_forget UniversalBlePlugin::SetNotifiableAsync(
|
||||
const std::string &device_id, const std::string &service,
|
||||
const std::string &characteristic, const int64_t ble_input_property,
|
||||
const std::function<void(std::optional<FlutterError> reply)> result) {
|
||||
UniversalBleLogger::LogDebugWithTimestamp(
|
||||
"SET_NOTIFY -> " + device_id + " " + service + " " + characteristic +
|
||||
" input=" + std::to_string(ble_input_property));
|
||||
try {
|
||||
const auto it = connected_devices_.find(str_to_mac_address(device_id));
|
||||
if (it == connected_devices_.end()) {
|
||||
@@ -1236,6 +1272,9 @@ fire_and_forget UniversalBlePlugin::SetNotifiableAsync(
|
||||
.WriteClientCharacteristicConfigurationDescriptorAsync(
|
||||
descriptor_value);
|
||||
if (status != GattCommunicationStatus::Success) {
|
||||
UniversalBleLogger::LogError("SET_NOTIFY_FAILED <- " + device_id + " " +
|
||||
service + " " + characteristic + " status=" +
|
||||
std::to_string(static_cast<int>(status)));
|
||||
result(create_flutter_error_from_gatt_communication_status(status));
|
||||
co_return;
|
||||
}
|
||||
@@ -1246,16 +1285,16 @@ fire_and_forget UniversalBlePlugin::SetNotifiableAsync(
|
||||
if (gatt_char.subscription_token.has_value()) {
|
||||
gatt_characteristic.ValueChanged(gatt_char.subscription_token.value());
|
||||
gatt_char.subscription_token = std::nullopt;
|
||||
std::cout << "Unsubscribed " << to_uuidstr(gatt_characteristic.Uuid())
|
||||
<< std::endl;
|
||||
UniversalBleLogger::LogInfo("Unsubscribed " +
|
||||
to_uuidstr(gatt_characteristic.Uuid()));
|
||||
}
|
||||
} else {
|
||||
// If a notification for the given characteristic is already in progress,
|
||||
// swap the callbacks.
|
||||
if (gatt_char.subscription_token.has_value()) {
|
||||
std::cout << "A notification for the given characteristic is already "
|
||||
"in progress. Swapping callbacks."
|
||||
<< std::endl;
|
||||
UniversalBleLogger::LogWarning(
|
||||
"A notification for the given characteristic is already in "
|
||||
"progress. Swapping callbacks.");
|
||||
gatt_characteristic.ValueChanged(gatt_char.subscription_token.value());
|
||||
gatt_char.subscription_token = std::nullopt;
|
||||
}
|
||||
@@ -1269,7 +1308,7 @@ fire_and_forget UniversalBlePlugin::SetNotifiableAsync(
|
||||
} catch (const FlutterError &err) {
|
||||
result(err);
|
||||
} catch (...) {
|
||||
std::cout << "SetNotifiableLog: Unknown error" << std::endl;
|
||||
UniversalBleLogger::LogError("SetNotifiableLog: Unknown error");
|
||||
result(create_flutter_unknown_error());
|
||||
}
|
||||
}
|
||||
@@ -1278,10 +1317,19 @@ void UniversalBlePlugin::GattCharacteristicValueChanged(
|
||||
const GattCharacteristic &sender, const GattValueChangedEventArgs &args) {
|
||||
auto uuid = to_uuidstr(sender.Uuid());
|
||||
auto bytes = to_bytevc(args.CharacteristicValue());
|
||||
ui_thread_handler_.Post([sender, uuid, bytes] {
|
||||
auto device_id =
|
||||
mac_address_to_str(sender.Service().Device().BluetoothAddress());
|
||||
|
||||
UniversalBleLogger::LogVerboseWithTimestamp(
|
||||
"NOTIFY <- " + device_id + " " + uuid +
|
||||
" len=" + std::to_string(bytes.size()));
|
||||
|
||||
auto timestamp = GetCurrentTimestampMillis();
|
||||
ui_thread_handler_.Post([sender, bytes, timestamp] {
|
||||
auto uuid = to_uuidstr(sender.Uuid());
|
||||
callback_channel->OnValueChanged(
|
||||
mac_address_to_str(sender.Service().Device().BluetoothAddress()), uuid,
|
||||
bytes, SuccessCallback, ErrorCallback);
|
||||
bytes, ×tamp, SuccessCallback, ErrorCallback);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+177
-172
@@ -5,205 +5,210 @@
|
||||
#include <flutter/plugin_registrar_windows.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <winrt/base.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <winrt/Windows.Devices.Enumeration.h>
|
||||
#include <winrt/Windows.Devices.Radios.h>
|
||||
#include <winrt/Windows.Devices.Bluetooth.h>
|
||||
#include <winrt/Windows.Devices.Bluetooth.Advertisement.h>
|
||||
#include <winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h>
|
||||
#include <winrt/Windows.Devices.Bluetooth.h>
|
||||
#include <winrt/Windows.Devices.Enumeration.h>
|
||||
#include <winrt/Windows.Devices.Radios.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <winrt/base.h>
|
||||
|
||||
#include <memory>
|
||||
#include "helper/utils.h"
|
||||
#include "helper/universal_enum.h"
|
||||
#include "helper/universal_ble_base.h"
|
||||
#include "generated/universal_ble.g.h"
|
||||
#include "helper/universal_ble_base.h"
|
||||
#include "helper/universal_enum.h"
|
||||
#include "helper/utils.h"
|
||||
#include "ui_thread_handler.hpp"
|
||||
#include "universal_ble_thread_safe.h"
|
||||
#include <memory>
|
||||
|
||||
namespace universal_ble
|
||||
{
|
||||
struct GattCharacteristicObject
|
||||
{
|
||||
GattCharacteristic obj = nullptr;
|
||||
std::optional<event_token> subscription_token;
|
||||
};
|
||||
namespace universal_ble {
|
||||
struct GattCharacteristicObject {
|
||||
GattCharacteristic obj = nullptr;
|
||||
std::optional<event_token> subscription_token;
|
||||
};
|
||||
|
||||
struct GattServiceObject
|
||||
{
|
||||
GattDeviceService obj = nullptr;
|
||||
std::unordered_map<std::string, GattCharacteristicObject> characteristics;
|
||||
};
|
||||
struct GattServiceObject {
|
||||
GattDeviceService obj = nullptr;
|
||||
std::unordered_map<std::string, GattCharacteristicObject> characteristics;
|
||||
};
|
||||
|
||||
struct BluetoothDeviceAgent
|
||||
{
|
||||
BluetoothLEDevice device;
|
||||
event_token connection_status_changed_token;
|
||||
std::unordered_map<std::string, GattServiceObject> gatt_map;
|
||||
struct BluetoothDeviceAgent {
|
||||
BluetoothLEDevice device;
|
||||
event_token connection_status_changed_token;
|
||||
std::unordered_map<std::string, GattServiceObject> gatt_map;
|
||||
|
||||
BluetoothDeviceAgent(const BluetoothLEDevice &device, const event_token connection_status_changed_token,
|
||||
const std::unordered_map<std::string, GattServiceObject> &gatt_map)
|
||||
: device(device),
|
||||
connection_status_changed_token(connection_status_changed_token),
|
||||
gatt_map(gatt_map)
|
||||
{
|
||||
}
|
||||
BluetoothDeviceAgent(
|
||||
const BluetoothLEDevice &device,
|
||||
const event_token connection_status_changed_token,
|
||||
const std::unordered_map<std::string, GattServiceObject> &gatt_map)
|
||||
: device(device),
|
||||
connection_status_changed_token(connection_status_changed_token),
|
||||
gatt_map(gatt_map) {}
|
||||
|
||||
~BluetoothDeviceAgent()
|
||||
{
|
||||
device = nullptr;
|
||||
}
|
||||
~BluetoothDeviceAgent() { device = nullptr; }
|
||||
|
||||
GattCharacteristicObject &FetchCharacteristic(const std::string &service_uuid,
|
||||
const std::string &characteristic_uuid)
|
||||
{
|
||||
if (gatt_map.count(service_uuid) == 0)
|
||||
{
|
||||
throw create_flutter_error(UniversalBleErrorCode::kServiceNotFound, "Service not found");
|
||||
}
|
||||
if (gatt_map[service_uuid].characteristics.count(characteristic_uuid) == 0)
|
||||
{
|
||||
throw create_flutter_error(UniversalBleErrorCode::kCharacteristicNotFound, "Characteristic not found");
|
||||
}
|
||||
return gatt_map[service_uuid].characteristics.at(characteristic_uuid);
|
||||
}
|
||||
};
|
||||
GattCharacteristicObject &
|
||||
FetchCharacteristic(const std::string &service_uuid,
|
||||
const std::string &characteristic_uuid) {
|
||||
if (gatt_map.count(service_uuid) == 0) {
|
||||
throw create_flutter_error(UniversalBleErrorCode::kServiceNotFound,
|
||||
"Service not found");
|
||||
}
|
||||
if (gatt_map[service_uuid].characteristics.count(characteristic_uuid) ==
|
||||
0) {
|
||||
throw create_flutter_error(UniversalBleErrorCode::kCharacteristicNotFound,
|
||||
"Characteristic not found");
|
||||
}
|
||||
return gatt_map[service_uuid].characteristics.at(characteristic_uuid);
|
||||
}
|
||||
};
|
||||
|
||||
class UniversalBlePlugin : public flutter::Plugin, public UniversalBlePlatformChannel
|
||||
{
|
||||
public:
|
||||
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
|
||||
class UniversalBlePlugin : public flutter::Plugin,
|
||||
public UniversalBlePlatformChannel {
|
||||
public:
|
||||
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);
|
||||
|
||||
UniversalBlePlugin(flutter::PluginRegistrarWindows *registrar);
|
||||
UniversalBlePlugin(flutter::PluginRegistrarWindows *registrar);
|
||||
|
||||
~UniversalBlePlugin();
|
||||
~UniversalBlePlugin();
|
||||
|
||||
// Disallow copy and assign.
|
||||
UniversalBlePlugin(const UniversalBlePlugin&) = delete;
|
||||
UniversalBlePlugin& operator=(const UniversalBlePlugin&) = delete;
|
||||
// Disallow copy and assign.
|
||||
UniversalBlePlugin(const UniversalBlePlugin &) = delete;
|
||||
UniversalBlePlugin &operator=(const UniversalBlePlugin &) = delete;
|
||||
|
||||
private:
|
||||
static void SuccessCallback() {}
|
||||
static void ErrorCallback(const FlutterError &error) {
|
||||
// Ignore ChannelConnection Error, This might occur because of HotReload
|
||||
if (error.code() != "channel-error") {
|
||||
std::cout << "ErrorCode: " << error.code()
|
||||
<< " Message: " << error.message() << std::endl;
|
||||
}
|
||||
}
|
||||
static int64_t GetCurrentTimestampMillis() {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
private:
|
||||
static void SuccessCallback() {}
|
||||
static void ErrorCallback(const FlutterError &error)
|
||||
{
|
||||
// Ignore ChannelConnection Error, This might occur because of HotReload
|
||||
if (error.code() != "channel-error")
|
||||
{
|
||||
std::cout << "ErrorCode: " << error.code() << " Message: " << error.message() << std::endl;
|
||||
}
|
||||
}
|
||||
flutter::PluginRegistrarWindows *registrar_;
|
||||
bool initialized_ = false;
|
||||
|
||||
flutter::PluginRegistrarWindows *registrar_;
|
||||
bool initialized_ = false;
|
||||
UniversalBleUiThreadHandler ui_thread_handler_;
|
||||
Radio bluetooth_radio_{nullptr};
|
||||
RadioState old_radio_state_ = RadioState::Unknown;
|
||||
BluetoothLEAdvertisementWatcher bluetooth_le_watcher_{nullptr};
|
||||
DeviceWatcher device_watcher_{nullptr};
|
||||
|
||||
UniversalBleUiThreadHandler ui_thread_handler_;
|
||||
Radio bluetooth_radio_{nullptr};
|
||||
RadioState old_radio_state_ = RadioState::Unknown;
|
||||
BluetoothLEAdvertisementWatcher bluetooth_le_watcher_{nullptr};
|
||||
DeviceWatcher device_watcher_{nullptr};
|
||||
std::unordered_map<uint64_t, std::unique_ptr<BluetoothDeviceAgent>>
|
||||
connected_devices_{};
|
||||
ThreadSafeMap<std::string, DeviceInformation> device_watcher_devices_{};
|
||||
ThreadSafeMap<std::string, UniversalBleScanResult> scan_results_{};
|
||||
// Maps DeviceInformation.Id() -> MAC address string used as key in
|
||||
// device_watcher_devices_
|
||||
ThreadSafeMap<std::string, std::string> device_watcher_id_to_mac_{};
|
||||
|
||||
std::unordered_map<uint64_t, std::unique_ptr<BluetoothDeviceAgent>> connected_devices_{};
|
||||
ThreadSafeMap<std::string, DeviceInformation> device_watcher_devices_{};
|
||||
ThreadSafeMap<std::string, UniversalBleScanResult> scan_results_{};
|
||||
// Maps DeviceInformation.Id() -> MAC address string used as key in device_watcher_devices_
|
||||
ThreadSafeMap<std::string, std::string> device_watcher_id_to_mac_{};
|
||||
event_token bluetooth_le_watcher_received_token_;
|
||||
event_token device_watcher_added_token_;
|
||||
event_token device_watcher_updated_token_;
|
||||
event_token device_watcher_removed_token_;
|
||||
event_token device_watcher_enumeration_completed_token_;
|
||||
event_token device_watcher_stopped_token_;
|
||||
event_revoker<IRadio> radio_state_changed_revoker_;
|
||||
|
||||
event_token bluetooth_le_watcher_received_token_;
|
||||
event_token device_watcher_added_token_;
|
||||
event_token device_watcher_updated_token_;
|
||||
event_token device_watcher_removed_token_;
|
||||
event_token device_watcher_enumeration_completed_token_;
|
||||
event_token device_watcher_stopped_token_;
|
||||
event_revoker<IRadio> radio_state_changed_revoker_;
|
||||
fire_and_forget InitializeAsync();
|
||||
fire_and_forget ConnectAsync(uint64_t bluetooth_address);
|
||||
fire_and_forget SetNotifiableAsync(
|
||||
const std::string &device_id, const std::string &service,
|
||||
const std::string &characteristic, int64_t ble_input_property,
|
||||
std::function<void(std::optional<FlutterError> reply)> result);
|
||||
fire_and_forget PairAsync(const std::string &device_id,
|
||||
std::function<void(ErrorOr<bool> reply)> result);
|
||||
fire_and_forget
|
||||
CustomPairAsync(const std::string &device_id,
|
||||
std::function<void(ErrorOr<bool> reply)> result);
|
||||
static fire_and_forget GetSystemDevicesAsync(
|
||||
std::vector<std::string> with_services,
|
||||
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);
|
||||
static fire_and_forget
|
||||
IsPairedAsync(const std::string &device_id,
|
||||
std::function<void(ErrorOr<bool> reply)> result);
|
||||
fire_and_forget DiscoverServicesAsync(
|
||||
const std::string &device_id, bool with_descriptors,
|
||||
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);
|
||||
|
||||
void
|
||||
PairingRequestedHandler(DeviceInformationCustomPairing sender,
|
||||
const DevicePairingRequestedEventArgs &event_args);
|
||||
|
||||
fire_and_forget InitializeAsync();
|
||||
fire_and_forget ConnectAsync(uint64_t bluetooth_address);
|
||||
fire_and_forget SetNotifiableAsync(
|
||||
const std::string& device_id,
|
||||
const std::string& service,
|
||||
const std::string& characteristic,
|
||||
int64_t ble_input_property,
|
||||
std::function<void(std::optional<FlutterError> reply)> result);
|
||||
fire_and_forget PairAsync(const std::string& device_id, std::function<void(ErrorOr<bool> reply)> result);
|
||||
fire_and_forget CustomPairAsync(const std::string& device_id, std::function<void(ErrorOr<bool> reply)> result);
|
||||
static fire_and_forget GetSystemDevicesAsync(
|
||||
std::vector<std::string> with_services,
|
||||
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);
|
||||
static fire_and_forget IsPairedAsync(const std::string& device_id, std::function<void(ErrorOr<bool> reply)> result);
|
||||
fire_and_forget DiscoverServicesAsync(const std::string &device_id,
|
||||
bool with_descriptors,
|
||||
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);
|
||||
void RadioStateChanged(const Radio &sender, const IInspectable &);
|
||||
void SetupDeviceWatcher();
|
||||
void DisposeDeviceWatcher();
|
||||
void PushUniversalScanResult(UniversalBleScanResult scan_result,
|
||||
bool is_connectable);
|
||||
void BluetoothLeWatcherReceived(
|
||||
const BluetoothLEAdvertisementWatcher &sender,
|
||||
const BluetoothLEAdvertisementReceivedEventArgs &args);
|
||||
void OnDeviceInfoReceived(const DeviceInformation &device_info);
|
||||
void BluetoothLeDeviceConnectionStatusChanged(const BluetoothLEDevice &sender,
|
||||
const IInspectable &args);
|
||||
void CleanConnection(uint64_t bluetooth_address);
|
||||
void
|
||||
DisposeServices(const std::unique_ptr<BluetoothDeviceAgent> &device_agent);
|
||||
|
||||
void PairingRequestedHandler(DeviceInformationCustomPairing sender, const DevicePairingRequestedEventArgs& event_args);
|
||||
void GattCharacteristicValueChanged(const GattCharacteristic &sender,
|
||||
const GattValueChangedEventArgs &args);
|
||||
|
||||
void RadioStateChanged(const Radio& sender, const IInspectable&);
|
||||
void SetupDeviceWatcher();
|
||||
void DisposeDeviceWatcher();
|
||||
void PushUniversalScanResult(UniversalBleScanResult scan_result, bool is_connectable);
|
||||
void BluetoothLeWatcherReceived(const BluetoothLEAdvertisementWatcher& sender, const
|
||||
BluetoothLEAdvertisementReceivedEventArgs& args);
|
||||
void OnDeviceInfoReceived(const DeviceInformation& device_info);
|
||||
void BluetoothLeDeviceConnectionStatusChanged(const BluetoothLEDevice& sender, const IInspectable& args);
|
||||
void CleanConnection(uint64_t bluetooth_address);
|
||||
void DisposeServices(const std::unique_ptr<BluetoothDeviceAgent> &device_agent);
|
||||
|
||||
|
||||
void GattCharacteristicValueChanged(const GattCharacteristic& sender, const GattValueChangedEventArgs& args);
|
||||
|
||||
// UniversalBlePlatformChannel implementation.
|
||||
void GetBluetoothAvailabilityState(std::function<void(ErrorOr<int64_t> reply)> result) override;
|
||||
void EnableBluetooth(std::function<void(ErrorOr<bool> reply)> result) override;
|
||||
void DisableBluetooth(std::function<void(ErrorOr<bool> reply)> result) override;
|
||||
ErrorOr<int64_t> GetConnectionState(const std::string &device_id) override;
|
||||
std::optional<FlutterError> StartScan(const UniversalScanFilter *filter) override;
|
||||
std::optional<FlutterError> StopScan() override;
|
||||
ErrorOr<bool> IsScanning() override;
|
||||
std::optional<FlutterError> Connect(const std::string &device_id) override;
|
||||
std::optional<FlutterError> Disconnect(const std::string &device_id) override;
|
||||
void RequestPermissions(
|
||||
bool with_android_fine_location,
|
||||
std::function<void(std::optional<FlutterError> reply)> result) override;
|
||||
void DiscoverServices(
|
||||
const std::string &device_id,
|
||||
bool with_descriptors,
|
||||
std::function<void(ErrorOr<flutter::EncodableList> reply)> result) override;
|
||||
void SetNotifiable(
|
||||
const std::string &device_id,
|
||||
const std::string &service,
|
||||
const std::string &characteristic,
|
||||
int64_t ble_input_property,
|
||||
std::function<void(std::optional<FlutterError> reply)> result) override;
|
||||
void ReadValue(
|
||||
const std::string &device_id,
|
||||
const std::string &service,
|
||||
const std::string &characteristic,
|
||||
std::function<void(ErrorOr<std::vector<uint8_t>> reply)> result) override;
|
||||
void WriteValue(
|
||||
const std::string &device_id,
|
||||
const std::string &service,
|
||||
const std::string &characteristic,
|
||||
const std::vector<uint8_t> &value,
|
||||
int64_t ble_output_property,
|
||||
std::function<void(std::optional<FlutterError> reply)> result) override;
|
||||
void RequestMtu(
|
||||
const std::string &device_id,
|
||||
int64_t expected_mtu,
|
||||
std::function<void(ErrorOr<int64_t> reply)> result) override;
|
||||
void IsPaired(
|
||||
const std::string &device_id,
|
||||
// UniversalBlePlatformChannel implementation.
|
||||
void GetBluetoothAvailabilityState(
|
||||
std::function<void(ErrorOr<int64_t> reply)> result) override;
|
||||
void
|
||||
EnableBluetooth(std::function<void(ErrorOr<bool> reply)> result) override;
|
||||
void
|
||||
DisableBluetooth(std::function<void(ErrorOr<bool> reply)> result) override;
|
||||
ErrorOr<int64_t> GetConnectionState(const std::string &device_id) override;
|
||||
std::optional<FlutterError>
|
||||
SetLogLevel(const UniversalBleLogLevel &log_level) override;
|
||||
std::optional<FlutterError>
|
||||
StartScan(const UniversalScanFilter *filter) override;
|
||||
std::optional<FlutterError> StopScan() override;
|
||||
ErrorOr<bool> IsScanning() override;
|
||||
std::optional<FlutterError> Connect(const std::string &device_id) override;
|
||||
std::optional<FlutterError> Disconnect(const std::string &device_id) override;
|
||||
void RequestPermissions(
|
||||
bool with_android_fine_location,
|
||||
std::function<void(std::optional<FlutterError> reply)> result) override;
|
||||
void
|
||||
DiscoverServices(const std::string &device_id, bool with_descriptors,
|
||||
std::function<void(ErrorOr<flutter::EncodableList> reply)>
|
||||
result) override;
|
||||
void SetNotifiable(
|
||||
const std::string &device_id, const std::string &service,
|
||||
const std::string &characteristic, int64_t ble_input_property,
|
||||
std::function<void(std::optional<FlutterError> reply)> result) override;
|
||||
void ReadValue(
|
||||
const std::string &device_id, const std::string &service,
|
||||
const std::string &characteristic,
|
||||
std::function<void(ErrorOr<std::vector<uint8_t>> reply)> result) override;
|
||||
void WriteValue(
|
||||
const std::string &device_id, const std::string &service,
|
||||
const std::string &characteristic, const std::vector<uint8_t> &value,
|
||||
int64_t ble_output_property,
|
||||
std::function<void(std::optional<FlutterError> reply)> result) override;
|
||||
void RequestMtu(const std::string &device_id, int64_t expected_mtu,
|
||||
std::function<void(ErrorOr<int64_t> reply)> result) override;
|
||||
void IsPaired(const std::string &device_id,
|
||||
std::function<void(ErrorOr<bool> reply)> result) override;
|
||||
void Pair(const std::string &device_id,
|
||||
std::function<void(ErrorOr<bool> reply)> result) override;
|
||||
void Pair(
|
||||
const std::string &device_id,
|
||||
std::function<void(ErrorOr<bool> reply)> result) override;
|
||||
std::optional<FlutterError> UnPair(const std::string &device_id) override;
|
||||
void GetSystemDevices(
|
||||
const flutter::EncodableList &with_services,
|
||||
std::function<void(ErrorOr<flutter::EncodableList> reply)> result) override;
|
||||
};
|
||||
std::optional<FlutterError> UnPair(const std::string &device_id) override;
|
||||
void
|
||||
GetSystemDevices(const flutter::EncodableList &with_services,
|
||||
std::function<void(ErrorOr<flutter::EncodableList> reply)>
|
||||
result) override;
|
||||
};
|
||||
|
||||
} // namespace universal_ble
|
||||
|
||||
|
||||
Reference in New Issue
Block a user