Implement unified and type safe error messages (#186)

* Implement unified and type safe error messages

* Fix Apple error handling

* improve linux exception handling

* improve web exception handling

* fix window imports

* update pigeon generated files

* Fix compilaton issue

* Fix windows error parsing

* Fix windows compilation

* Improve error handling on dart side

* rename _wrapFuture to _executeWithErrorHandling

* Add pigeon generation script and format generated dart file

* Update changelog

* Update readme

---------

Co-authored-by: Foti Dim <foti@navideck.com>
This commit is contained in:
Rohit Sangwan
2025-11-12 00:04:29 +05:30
committed by GitHub
parent b52ca9ee84
commit acfa3200ca
32 changed files with 3399 additions and 1721 deletions
+3
View File
@@ -1,3 +1,6 @@
## 1.0.0
* Unified error codes for all platforms
## 0.21.1
* Fix device name resolution on Windows
* Add `exclusionFilters` to filter out devices from scan results
+51
View File
@@ -17,6 +17,7 @@ A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE
- [Requesting MTU](#requesting-mtu)
- [Command Queue](#command-queue)
- [Timeout](#timeout)
- [Error Handling](#error-handling)
- [UUID Format Agnostic](#uuid-format-agnostic)
## API Support
@@ -456,6 +457,56 @@ UniversalBle.timeout = null;
You can also specify the `timeout` parameter when sending a command. This will override the global timeout.
## Error Handling
Universal BLE provides a unified and type-safe error handling system across all platforms. All errors are represented using the `UniversalBleException` base class with typed error codes from the `UniversalBleErrorCode` enum.
### Exception Types
- **`UniversalBleException`**: Base exception class for all BLE errors
- **`ConnectionException`**: Thrown for connection-related errors
- **`PairingException`**: Thrown for pairing-related errors
- **`WebBluetoothGloballyDisabled`**: Thrown when Web Bluetooth is globally disabled
### Error Codes
All errors are categorized using the `UniversalBleErrorCode` enum, which includes codes for:
- Connection errors (timeout, failed, rejected, etc.)
- Pairing errors (failed, cancelled, not allowed, etc.)
- Operation errors (not supported, timeout, cancelled, etc.)
- Permission errors (not allowed, unauthorized, access denied, etc.)
- Device errors (not found, disconnected, etc.)
- Service/Characteristic errors (not found, invalid UUID, etc.)
- And many more...
### Usage
```dart
try {
await bleDevice.connect();
} on ConnectionException catch (e) {
// Handle connection-specific errors
switch (e.code) {
case UniversalBleErrorCode.connectionTimeout:
// Handle timeout
break;
case UniversalBleErrorCode.connectionFailed:
// Handle connection failure
break;
case UniversalBleErrorCode.deviceDisconnected:
// Handle disconnection
break;
default:
// Handle other connection errors
}
} on UniversalBleException catch (e) {
// Handle other BLE errors
print('Error code: ${e.code}, Message: ${e.message}');
}
```
The error parser automatically converts platform-specific error formats (strings, numeric codes, PlatformExceptions) into the unified `UniversalBleErrorCode` enum, ensuring consistent error handling across all platforms.
## UUID Format Agnostic
Universal BLE is agnostic to the UUID format of services and characteristics regardless of the platform the app runs on. When passing a UUID, you can pass it in any format (long/short) or character case (upper/lower case) you want. Universal BLE will take care of necessary conversions, across all platforms, so that you don't need to worry about underlying platform differences.
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v26.0.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -7,16 +7,22 @@ package com.navideck.universal_ble
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMethodCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private object UniversalBlePigeonUtils {
private fun wrapResult(result: Any?): List<Any?> {
fun createConnectionError(channelName: String): FlutterError {
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") }
fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
}
}
private fun wrapError(exception: Throwable): List<Any?> {
fun wrapError(exception: Throwable): List<Any?> {
return if (exception is FlutterError) {
listOf(
exception.code,
@@ -30,10 +36,38 @@ private fun wrapError(exception: Throwable): List<Any?> {
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
)
}
}
}
fun deepEquals(a: Any?, b: Any?): Boolean {
if (a is ByteArray && b is ByteArray) {
return a.contentEquals(b)
}
if (a is IntArray && b is IntArray) {
return a.contentEquals(b)
}
if (a is LongArray && b is LongArray) {
return a.contentEquals(b)
}
if (a is DoubleArray && b is DoubleArray) {
return a.contentEquals(b)
}
if (a is Array<*> && b is Array<*>) {
return a.size == b.size &&
a.indices.all{ deepEquals(a[it], b[it]) }
}
if (a is List<*> && b is List<*>) {
return a.size == b.size &&
a.indices.all{ deepEquals(a[it], b[it]) }
}
if (a is Map<*, *> && b is Map<*, *>) {
return a.size == b.size && a.all {
(b as Map<Any?, Any?>).contains(it.key) &&
deepEquals(it.value, b[it.key])
}
}
return a == b
}
private fun createConnectionError(channelName: String): FlutterError {
return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "")}
}
/**
* Error class for passing custom error details to Flutter via a thrown PlatformException.
@@ -47,6 +81,77 @@ class FlutterError (
val details: Any? = null
) : Throwable()
/** Unified error codes for all platforms */
enum class UniversalBleErrorCode(val raw: Int) {
UNKNOWN_ERROR(0),
FAILED(1),
NOT_SUPPORTED(2),
NOT_IMPLEMENTED(3),
CHANNEL_ERROR(4),
BLUETOOTH_NOT_AVAILABLE(5),
BLUETOOTH_NOT_ENABLED(6),
BLUETOOTH_NOT_ALLOWED(7),
BLUETOOTH_UNAUTHORIZED(8),
DEVICE_DISCONNECTED(9),
CONNECTION_TIMEOUT(10),
CONNECTION_FAILED(11),
CONNECTION_REJECTED(12),
CONNECTION_LIMIT_EXCEEDED(13),
CONNECTION_ALREADY_EXISTS(14),
CONNECTION_TERMINATED(15),
CONNECTION_IN_PROGRESS(16),
ILLEGAL_ARGUMENT(17),
DEVICE_NOT_FOUND(18),
SERVICE_NOT_FOUND(19),
CHARACTERISTIC_NOT_FOUND(20),
INVALID_SERVICE_UUID(21),
INVALID_CHARACTERISTIC_UUID(22),
INVALID_OFFSET(23),
INVALID_ATTRIBUTE_LENGTH(24),
INVALID_PDU(25),
INVALID_HANDLE(26),
READ_FAILED(27),
READ_NOT_PERMITTED(28),
WRITE_FAILED(29),
WRITE_NOT_PERMITTED(30),
WRITE_REQUEST_BUSY(31),
INVALID_ACTION(32),
OPERATION_NOT_SUPPORTED(33),
OPERATION_TIMEOUT(34),
OPERATION_CANCELLED(35),
OPERATION_IN_PROGRESS(36),
CHARACTERISTIC_DOES_NOT_SUPPORT_READ(37),
CHARACTERISTIC_DOES_NOT_SUPPORT_WRITE(38),
CHARACTERISTIC_DOES_NOT_SUPPORT_WRITE_WITHOUT_RESPONSE(39),
CHARACTERISTIC_DOES_NOT_SUPPORT_NOTIFY(40),
CHARACTERISTIC_DOES_NOT_SUPPORT_INDICATE(41),
NOT_PAIRED(42),
NOT_PAIRABLE(43),
ALREADY_PAIRED(44),
PAIRING_FAILED(45),
PAIRING_CANCELLED(46),
PAIRING_TIMEOUT(47),
PAIRING_NOT_ALLOWED(48),
AUTHENTICATION_FAILURE(49),
INSUFFICIENT_AUTHENTICATION(50),
INSUFFICIENT_AUTHORIZATION(51),
INSUFFICIENT_ENCRYPTION(52),
INSUFFICIENT_KEY_SIZE(53),
PROTECTION_LEVEL_NOT_MET(54),
ACCESS_DENIED(55),
UNPAIRING_FAILED(56),
ALREADY_UNPAIRED(57),
SCAN_FAILED(58),
STOPPING_SCAN_IN_PROGRESS(59),
WEB_BLUETOOTH_GLOBALLY_DISABLED(60);
companion object {
fun ofRaw(raw: Int): UniversalBleErrorCode? {
return values().firstOrNull { it.raw == raw }
}
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class UniversalBleScanResult (
val deviceId: String,
@@ -78,6 +183,16 @@ data class UniversalBleScanResult (
services,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalBleScanResult) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -99,6 +214,16 @@ data class UniversalBleService (
characteristics,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalBleService) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -120,6 +245,16 @@ data class UniversalBleCharacteristic (
properties,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalBleCharacteristic) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/**
@@ -148,6 +283,16 @@ data class UniversalScanFilter (
withManufacturerData,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalScanFilter) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -172,6 +317,16 @@ data class UniversalManufacturerDataFilter (
mask,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalManufacturerDataFilter) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -193,36 +348,51 @@ data class UniversalManufacturerData (
data,
)
}
override fun equals(other: Any?): Boolean {
if (other !is UniversalManufacturerData) {
return false
}
if (this === other) {
return true
}
return UniversalBlePigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
private open class UniversalBlePigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalBleScanResult.fromList(it)
return (readValue(buffer) as Long?)?.let {
UniversalBleErrorCode.ofRaw(it.toInt())
}
}
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalBleService.fromList(it)
UniversalBleScanResult.fromList(it)
}
}
131.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalBleCharacteristic.fromList(it)
UniversalBleService.fromList(it)
}
}
132.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalScanFilter.fromList(it)
UniversalBleCharacteristic.fromList(it)
}
}
133.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalManufacturerDataFilter.fromList(it)
UniversalScanFilter.fromList(it)
}
}
134.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalManufacturerDataFilter.fromList(it)
}
}
135.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
UniversalManufacturerData.fromList(it)
}
@@ -232,30 +402,34 @@ private open class UniversalBlePigeonCodec : StandardMessageCodec() {
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
when (value) {
is UniversalBleScanResult -> {
is UniversalBleErrorCode -> {
stream.write(129)
writeValue(stream, value.toList())
writeValue(stream, value.raw.toLong())
}
is UniversalBleService -> {
is UniversalBleScanResult -> {
stream.write(130)
writeValue(stream, value.toList())
}
is UniversalBleCharacteristic -> {
is UniversalBleService -> {
stream.write(131)
writeValue(stream, value.toList())
}
is UniversalScanFilter -> {
is UniversalBleCharacteristic -> {
stream.write(132)
writeValue(stream, value.toList())
}
is UniversalManufacturerDataFilter -> {
is UniversalScanFilter -> {
stream.write(133)
writeValue(stream, value.toList())
}
is UniversalManufacturerData -> {
is UniversalManufacturerDataFilter -> {
stream.write(134)
writeValue(stream, value.toList())
}
is UniversalManufacturerData -> {
stream.write(135)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@@ -302,10 +476,10 @@ interface UniversalBlePlatformChannel {
api.getBluetoothAvailabilityState{ result: Result<Long> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -320,10 +494,10 @@ interface UniversalBlePlatformChannel {
api.enableBluetooth{ result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -338,10 +512,10 @@ interface UniversalBlePlatformChannel {
api.disableBluetooth{ result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -359,7 +533,7 @@ interface UniversalBlePlatformChannel {
api.startScan(filterArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -375,7 +549,7 @@ interface UniversalBlePlatformChannel {
api.stopScan()
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -393,7 +567,7 @@ interface UniversalBlePlatformChannel {
api.connect(deviceIdArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -411,7 +585,7 @@ interface UniversalBlePlatformChannel {
api.disconnect(deviceIdArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -431,9 +605,9 @@ interface UniversalBlePlatformChannel {
api.setNotifiable(deviceIdArg, serviceArg, characteristicArg, bleInputPropertyArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
reply.reply(wrapResult(null))
reply.reply(UniversalBlePigeonUtils.wrapResult(null))
}
}
}
@@ -450,10 +624,10 @@ interface UniversalBlePlatformChannel {
api.discoverServices(deviceIdArg) { result: Result<List<UniversalBleService>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -472,10 +646,10 @@ interface UniversalBlePlatformChannel {
api.readValue(deviceIdArg, serviceArg, characteristicArg) { result: Result<ByteArray> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -493,10 +667,10 @@ interface UniversalBlePlatformChannel {
api.requestMtu(deviceIdArg, expectedMtuArg) { result: Result<Long> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -517,9 +691,9 @@ interface UniversalBlePlatformChannel {
api.writeValue(deviceIdArg, serviceArg, characteristicArg, valueArg, bleOutputPropertyArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
reply.reply(wrapResult(null))
reply.reply(UniversalBlePigeonUtils.wrapResult(null))
}
}
}
@@ -536,10 +710,10 @@ interface UniversalBlePlatformChannel {
api.isPaired(deviceIdArg) { result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -556,10 +730,10 @@ interface UniversalBlePlatformChannel {
api.pair(deviceIdArg) { result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -577,7 +751,7 @@ interface UniversalBlePlatformChannel {
api.unPair(deviceIdArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -594,10 +768,10 @@ interface UniversalBlePlatformChannel {
api.getSystemDevices(withServicesArg) { result: Result<List<UniversalBleScanResult>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
reply.reply(UniversalBlePigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
reply.reply(UniversalBlePigeonUtils.wrapResult(data))
}
}
}
@@ -614,7 +788,7 @@ interface UniversalBlePlatformChannel {
val wrapped: List<Any?> = try {
listOf(api.getConnectionState(deviceIdArg))
} catch (exception: Throwable) {
wrapError(exception)
UniversalBlePigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
@@ -650,7 +824,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
@@ -667,7 +841,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
@@ -684,7 +858,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
@@ -701,7 +875,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
@@ -718,7 +892,7 @@ class UniversalBleCallbackChannel(private val binaryMessenger: BinaryMessenger,
callback(Result.success(Unit))
}
} else {
callback(Result.failure(createConnectionError(channelName)))
callback(Result.failure(UniversalBlePigeonUtils.createConnectionError(channelName)))
}
}
}
@@ -2,11 +2,8 @@ package com.navideck.universal_ble
import android.annotation.SuppressLint
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.os.ParcelUuid
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.UUID
import kotlin.experimental.and
@@ -46,7 +43,7 @@ class UniversalBleFilterUtil {
}
private fun isNameMatchingFilters(scanFilter: UniversalScanFilter, name: String?): Boolean {
val namePrefixFilter = scanFilter.withNamePrefix.filterNotNull()
val namePrefixFilter = scanFilter.withNamePrefix
if (namePrefixFilter.isEmpty()) {
return true
}
@@ -74,7 +71,7 @@ class UniversalBleFilterUtil {
scanFilter: UniversalScanFilter,
manufacturerDataList: List<UniversalManufacturerData>,
): Boolean {
val filters = scanFilter.withManufacturerData.filterNotNull()
val filters = scanFilter.withManufacturerData
if (filters.isEmpty()) return true
if (manufacturerDataList.isEmpty()) return false
return manufacturerDataList.any { msd ->
@@ -122,8 +119,8 @@ fun UniversalScanFilter.toScanFilters(serviceUuids: List<UUID>): List<ScanFilter
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
throw FlutterError(
"illegalIllegalArgument",
throw createFlutterError(
UniversalBleErrorCode.INVALID_SERVICE_UUID,
"Invalid serviceId: $service",
e.toString()
)
@@ -133,7 +130,7 @@ fun UniversalScanFilter.toScanFilters(serviceUuids: List<UUID>): List<ScanFilter
// Add ManufacturerData Filter
for (manufacturerData in this.withManufacturerData) {
try {
manufacturerData?.companyIdentifier?.let {
manufacturerData.companyIdentifier.let {
val data: ByteArray = manufacturerData.data ?: ByteArray(0)
val mask: ByteArray? = manufacturerData.mask
if (mask == null) {
@@ -152,9 +149,9 @@ fun UniversalScanFilter.toScanFilters(serviceUuids: List<UUID>): List<ScanFilter
}
} catch (e: Exception) {
Log.e(TAG, e.toString())
throw FlutterError(
"illegalIllegalArgument",
"Invalid manufacturerData: ${manufacturerData?.companyIdentifier} ${manufacturerData?.data} ${manufacturerData?.mask}",
throw createFlutterError(
UniversalBleErrorCode.FAILED,
"Invalid manufacturerData: ${manufacturerData.companyIdentifier} ${manufacturerData.data} ${manufacturerData.mask}",
e.toString()
)
}
@@ -17,6 +17,7 @@ import android.util.SparseArray
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.UUID
import androidx.core.util.size
private const val TAG = "UniversalBlePlugin"
@@ -46,8 +47,8 @@ enum class BleInputProperty(val value: Long) {
}
enum class BleOutputProperty(val value: Long) {
withResponse(0),
withoutResponse(1);
WithResponse(0),
WithoutResponse(1);
}
@@ -80,7 +81,10 @@ fun List<String>.toUUIDList(): List<UUID> {
fun String.toBluetoothGatt(): BluetoothGatt {
return this.findGatt()
?: throw FlutterError("IllegalArgument", "Unknown deviceId: $this", null)
?: throw createFlutterError(
UniversalBleErrorCode.DEVICE_NOT_FOUND,
"Unknown deviceId: $this",
)
}
fun String.isKnownGatt(): Boolean {
@@ -122,53 +126,6 @@ fun Int.parseScanErrorMessage(): String {
}
}
fun Int.parseBluetoothStatusCodeError(): String? {
if (this == BluetoothStatusCodes.SUCCESS) return null
return when (this) {
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED -> "ERROR_BLUETOOTH_NOT_ENABLED"
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED -> "ERROR_BLUETOOTH_NOT_ALLOWED"
BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED -> "ERROR_DEVICE_NOT_BONDED"
BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED -> "ERROR_GATT_WRITE_NOT_ALLOWED"
BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY -> "ERROR_GATT_WRITE_REQUEST_BUSY"
BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION -> "ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION"
BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND -> "ERROR_PROFILE_SERVICE_NOT_BOUND"
BluetoothStatusCodes.ERROR_UNKNOWN -> "ERROR_UNKNOWN"
BluetoothStatusCodes.FEATURE_NOT_CONFIGURED -> "FEATURE_NOT_CONFIGURED"
BluetoothStatusCodes.FEATURE_NOT_SUPPORTED -> "FEATURE_NOT_SUPPORTED"
BluetoothStatusCodes.FEATURE_SUPPORTED -> "FEATURE_SUPPORTED"
else -> "ErrorCode: $this"
}
}
fun Int.parseGattErrorCode(): String? {
return when (this) {
BluetoothGatt.GATT_SUCCESS -> null
BluetoothGatt.GATT_READ_NOT_PERMITTED -> "GATT_READ_NOT_PERMITTED"
BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> "GATT_WRITE_NOT_PERMITTED"
BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION -> "GATT_INSUFFICIENT_AUTHENTICATION"
BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED -> "GATT_REQUEST_NOT_SUPPORTED"
BluetoothGatt.GATT_INVALID_OFFSET -> "GATT_INVALID_OFFSET"
BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION -> "GATT_INSUFFICIENT_AUTHORIZATION"
BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH -> "GATT_INVALID_ATTRIBUTE_LENGTH"
BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION -> "GATT_INSUFFICIENT_ENCRYPTION"
BluetoothGatt.GATT_CONNECTION_CONGESTED -> "GATT_CONNECTION_CONGESTED"
BluetoothGatt.GATT_FAILURE -> "GATT_FAILURE"
BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED -> "ERROR_GATT_WRITE_NOT_ALLOWED"
BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY -> "ERROR_GATT_WRITE_REQUEST_BUSY"
BluetoothStatusCodes.FEATURE_NOT_CONFIGURED -> "FEATURE_NOT_CONFIGURED"
0x01 -> "GATT_INVALID_HANDLE"
0x04 -> "GATT_INVALID_PDU"
0x09 -> "GATT_PREPARE_QUEUE_FULL"
0x0a -> "GATT_ATTR_NOT_FOUND"
0x0b -> "GATT_ATTR_NOT_LONG"
0x0c -> "GATT_INSUFFICIENT_KEY_SIZE"
0x0e -> "GATT_UNLIKELY"
0x10 -> "GATT_UNSUPPORTED_GROUP"
0x11 -> "GATT_INSUFFICIENT_RESOURCES"
else -> "Unknown Error: $this"
}
}
val ScanResult.manufacturerDataList: List<UniversalManufacturerData>
get() {
return scanRecord?.manufacturerSpecificData?.toList()?.map { (key, value) ->
@@ -177,7 +134,7 @@ val ScanResult.manufacturerDataList: List<UniversalManufacturerData>
}
fun <T> SparseArray<T>.toList(): List<Pair<Int, T>> {
return (0 until size()).map { index ->
return (0 until size).map { index ->
keyAt(index) to valueAt(index)
}
}
@@ -189,16 +146,6 @@ fun BluetoothGatt.getCharacteristic(
return getService(UUID.fromString(service))?.getCharacteristic(UUID.fromString(characteristic))
}
fun subscriptionFailedError(error: String? = null): Result<Unit> {
return Result.failure(
FlutterError(
"Failed",
"Failed to update subscription state",
error
)
)
}
fun BluetoothDevice.removeBond() {
try {
javaClass.getMethod("removeBond").invoke(this)
@@ -207,7 +154,6 @@ fun BluetoothDevice.removeBond() {
}
}
fun BluetoothGattCharacteristic.getPropertiesList(): ArrayList<Long> {
val propertiesList = arrayListOf<Long>()
if (properties and BluetoothGattCharacteristic.PROPERTY_BROADCAST > 0) {
@@ -241,16 +187,54 @@ fun BluetoothGattCharacteristic.getPropertiesList(): ArrayList<Long> {
fun Short.toByteArray(byteOrder: ByteOrder = ByteOrder.LITTLE_ENDIAN): ByteArray =
ByteBuffer.allocate(2 /*Short.SIZE_BYTES*/).order(byteOrder).putShort(this).array()
// Errors
fun unknownCharacteristicError(char: String) =
FlutterError("IllegalArgument", "Unknown error", null)
fun createFlutterError(
code: UniversalBleErrorCode,
message: String? = null,
details: String? = null,
) = FlutterError(code.raw.toString(), message, details ?: code.toString())
val DeviceDisconnectedError: FlutterError = FlutterError(
"DeviceDisconnected",
"Device Disconnected",
null
)
fun gattStatusToUniversalBleErrorCode(code: Int): UniversalBleErrorCode {
return when (code) {
BluetoothGatt.GATT_READ_NOT_PERMITTED -> UniversalBleErrorCode.READ_NOT_PERMITTED
BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> UniversalBleErrorCode.WRITE_NOT_PERMITTED
BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION -> UniversalBleErrorCode.INSUFFICIENT_AUTHENTICATION
BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION -> UniversalBleErrorCode.INSUFFICIENT_AUTHORIZATION
BluetoothGatt.GATT_INSUFFICIENT_ENCRYPTION -> UniversalBleErrorCode.INSUFFICIENT_ENCRYPTION
BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED -> UniversalBleErrorCode.OPERATION_NOT_SUPPORTED
BluetoothGatt.GATT_INVALID_OFFSET -> UniversalBleErrorCode.INVALID_OFFSET
BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH -> UniversalBleErrorCode.INVALID_ATTRIBUTE_LENGTH
BluetoothGatt.GATT_CONNECTION_CONGESTED -> UniversalBleErrorCode.CONNECTION_FAILED
BluetoothGatt.GATT_FAILURE -> UniversalBleErrorCode.FAILED
0x01 -> UniversalBleErrorCode.INVALID_HANDLE
0x04 -> UniversalBleErrorCode.INVALID_PDU
0x09 -> UniversalBleErrorCode.OPERATION_IN_PROGRESS
0x0a -> UniversalBleErrorCode.SERVICE_NOT_FOUND
0x0b -> UniversalBleErrorCode.INVALID_ATTRIBUTE_LENGTH
0x0c -> UniversalBleErrorCode.INSUFFICIENT_KEY_SIZE
0x0e -> UniversalBleErrorCode.FAILED
0x10 -> UniversalBleErrorCode.OPERATION_NOT_SUPPORTED
0x11 -> UniversalBleErrorCode.FAILED
else -> UniversalBleErrorCode.UNKNOWN_ERROR
}
}
fun Int.parseBluetoothStatusCodeError(): UniversalBleErrorCode? {
if (this == BluetoothStatusCodes.SUCCESS) return null
return when (this) {
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED -> UniversalBleErrorCode.BLUETOOTH_NOT_ENABLED
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED -> UniversalBleErrorCode.BLUETOOTH_NOT_ALLOWED
BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED -> UniversalBleErrorCode.NOT_PAIRED
BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED -> UniversalBleErrorCode.WRITE_NOT_PERMITTED
BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY -> UniversalBleErrorCode.WRITE_REQUEST_BUSY
BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION -> UniversalBleErrorCode.CONNECTION_FAILED
BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND -> UniversalBleErrorCode.SERVICE_NOT_FOUND
BluetoothStatusCodes.ERROR_UNKNOWN -> UniversalBleErrorCode.UNKNOWN_ERROR
BluetoothStatusCodes.FEATURE_NOT_CONFIGURED -> UniversalBleErrorCode.NOT_IMPLEMENTED
BluetoothStatusCodes.FEATURE_NOT_SUPPORTED -> UniversalBleErrorCode.NOT_SUPPORTED
else -> null
}
}
fun Int.parseHciErrorCode(): String? {
return when (this) {
@@ -32,6 +32,7 @@ import io.flutter.plugin.common.PluginRegistry
import java.util.UUID
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import androidx.core.content.edit
private const val TAG = "UniversalBlePlugin"
@@ -102,7 +103,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (bluetoothEnableRequestFuture != null) {
callback(
Result.failure(
FlutterError("Failed", "Bluetooth enable request in progress", null)
createFlutterError(
UniversalBleErrorCode.OPERATION_IN_PROGRESS,
"Bluetooth enable request in progress"
)
)
)
return
@@ -120,7 +124,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (bluetoothDisableRequestFuture != null) {
callback(
Result.failure(
FlutterError("Failed", "Bluetooth disable request in progress", null)
createFlutterError(
UniversalBleErrorCode.OPERATION_IN_PROGRESS,
"Bluetooth disable request in progress"
)
)
)
return
@@ -131,9 +138,9 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
override fun startScan(filter: UniversalScanFilter?) {
if (!isBluetoothAvailable()) throw FlutterError(
"BluetoothNotEnabled",
"Bluetooth not enabled",
if (!isBluetoothAvailable()) throw createFlutterError(
UniversalBleErrorCode.BLUETOOTH_NOT_ENABLED,
"Bluetooth not enabled"
)
val builder = ScanSettings.Builder()
@@ -143,10 +150,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
val settings = builder.build()
val usesCustomFilters = filter?.usesCustomFilters() ?: false;
val usesCustomFilters = filter?.usesCustomFilters() ?: false
try {
val filterServices = filter?.withServices?.filterNotNull()?.toUUIDList() ?: emptyList()
val filterServices = filter?.withServices?.toUUIDList() ?: emptyList()
var scanFilters = emptyList<ScanFilter>()
// Set custom scan filter only if required
@@ -156,25 +163,25 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
universalBleFilterUtil.serviceFilterUUIDS = filterServices
} else {
universalBleFilterUtil.scanFilter = null
scanFilters = filter?.toScanFilters(filterServices) ?: emptyList<ScanFilter>()
scanFilters = filter?.toScanFilters(filterServices) ?: emptyList()
}
safeScanner.startScan(
scanFilters, settings, scanCallback
)
} catch (e: Exception) {
throw FlutterError(
"illegalIllegalArgument",
throw createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to start Scan",
e.toString()
details = e.toString()
)
}
}
override fun stopScan() {
if (!isBluetoothAvailable()) throw FlutterError(
"BluetoothNotEnabled",
"Bluetooth not enabled",
if (!isBluetoothAvailable()) throw createFlutterError(
UniversalBleErrorCode.BLUETOOTH_NOT_ENABLED,
"Bluetooth not enabled"
)
// check if already scanning
safeScanner.stopScan(scanCallback)
@@ -192,7 +199,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
return
} else if (currentState == BluetoothGatt.STATE_CONNECTING) {
throw FlutterError("Connecting", "Connection already in progress", null)
throw createFlutterError(
UniversalBleErrorCode.CONNECTION_IN_PROGRESS,
"Connection already in progress"
)
}
}
@@ -240,7 +250,12 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
discoverServicesFutureList.add(DiscoverServicesFuture(deviceId, callback))
} else {
callback(
Result.failure(FlutterError("Failed", "Failed to discover services", null))
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to discover services"
)
)
)
}
} catch (e: FlutterError) {
@@ -253,7 +268,12 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
discoverServicesFutureList.filter { it.deviceId == gatt.device.address }.forEach {
discoverServicesFutureList.remove(it)
it.result(
Result.failure(FlutterError("Failed", "Failed to discover services", null))
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to discover services"
)
)
)
}
return
@@ -290,7 +310,14 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
gatt.getCharacteristic(service, characteristic)
if (gattCharacteristic == null) {
callback(subscriptionFailedError("characteristic not found"))
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_NOT_FOUND,
"characteristic not found"
)
)
)
return
}
@@ -298,7 +325,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
gattCharacteristic.getDescriptor(ccdCharacteristic)
val bleInputPropertyEnum: BleInputProperty =
BleInputProperty.values().first { it.value == bleInputProperty }
BleInputProperty.entries.first { it.value == bleInputProperty }
val (value, enable) = when (bleInputPropertyEnum) {
BleInputProperty.Notification -> BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE to true
@@ -310,14 +337,30 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
// Some devices do not need CCCD to update
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val status = gatt.writeDescriptor(descriptor, value)
if (gatt.writeDescriptor(descriptor, value) != BluetoothStatusCodes.SUCCESS) {
callback(subscriptionFailedError("Failed to update descriptor"))
callback(
Result.failure(
createFlutterError(
status.parseBluetoothStatusCodeError()
?: UniversalBleErrorCode.FAILED,
"Failed to update descriptor"
)
)
)
return
}
} else {
descriptor.value = value
if (!gatt.writeDescriptor(descriptor)) {
callback(subscriptionFailedError("Failed to update descriptor"))
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to update descriptor"
)
)
)
return
}
}
@@ -339,12 +382,27 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
callback(Result.success(Unit))
}
} else {
callback(subscriptionFailedError())
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to update subscription state"
)
)
)
}
} catch (e: FlutterError) {
callback(Result.failure(e))
} catch (e: Exception) {
callback(subscriptionFailedError(e.toString()))
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to update subscription state",
e.toString()
)
)
)
}
}
@@ -359,13 +417,23 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
val gattCharacteristic = gatt.getCharacteristic(service, characteristic)
if (gattCharacteristic == null) {
callback(
Result.failure(FlutterError("IllegalArgument", "Unknown characteristic", null))
Result.failure(
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_NOT_FOUND,
"Unknown characteristic"
)
)
)
return
}
if (!gatt.readCharacteristic(gattCharacteristic)) {
callback(
Result.failure(unknownCharacteristicError(characteristic))
Result.failure(
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_NOT_FOUND,
"$characteristic not found",
)
)
)
return
}
@@ -383,8 +451,8 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
} catch (e: Exception) {
callback(
Result.failure(
FlutterError(
"Failed",
createFlutterError(
UniversalBleErrorCode.READ_FAILED,
"Failed to read value",
e.toString()
)
@@ -410,10 +478,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
} else {
it.result(
Result.failure(
FlutterError(
status.toString(),
"Failed to read: (${status.parseGattErrorCode()})",
null,
createFlutterError(
gattStatusToUniversalBleErrorCode(status),
"Failed to read",
status.toString()
)
)
)
@@ -434,33 +502,38 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
val gatt = deviceId.toBluetoothGatt()
val gattCharacteristic = gatt.getCharacteristic(service, characteristic)
if (gattCharacteristic == null) {
callback(Result.failure(unknownCharacteristicError(characteristic)))
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_NOT_FOUND,
"$characteristic not found",
)
)
)
return
}
var writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
if (bleOutputProperty == BleOutputProperty.withResponse.value) {
if (bleOutputProperty == BleOutputProperty.WithResponse.value) {
if (gattCharacteristic.properties and BluetoothGattCharacteristic.PROPERTY_WRITE == 0) {
callback(
Result.failure(
FlutterError(
"IllegalArgument",
"Characteristic does not support write withResponse",
null
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_DOES_NOT_SUPPORT_WRITE,
"Characteristic does not support write withResponse"
)
)
)
return
}
writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
} else if (bleOutputProperty == BleOutputProperty.withoutResponse.value) {
} else if (bleOutputProperty == BleOutputProperty.WithoutResponse.value) {
if (gattCharacteristic.properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE == 0) {
callback(
Result.failure(
FlutterError(
"IllegalArgument",
"Characteristic does not support write withoutResponse",
null
createFlutterError(
UniversalBleErrorCode.CHARACTERISTIC_DOES_NOT_SUPPORT_WRITE_WITHOUT_RESPONSE,
"Characteristic does not support write withoutResponse"
)
)
)
@@ -495,10 +568,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
writeResultFutureList.remove(writeFuture)
callback(
Result.failure(
FlutterError(
"WriteError",
"Failed to write: ${result.parseGattErrorCode()}",
null
createFlutterError(
gattStatusToUniversalBleErrorCode(result),
"Failed to write",
result.toString()
)
)
)
@@ -524,10 +597,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
} else {
it.result(
Result.failure(
FlutterError(
status.toString(),
"Failed to write: (${status.parseGattErrorCode()})",
null,
createFlutterError(
gattStatusToUniversalBleErrorCode(status),
"Failed to write",
status.toString()
)
)
)
@@ -553,7 +626,14 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (status == BluetoothGatt.GATT_SUCCESS) {
it.result(Result.success(mtu.toLong()))
} else {
it.result(Result.failure(FlutterError("Failed to change MTU", null, null)))
it.result(
Result.failure(
createFlutterError(
UniversalBleErrorCode.FAILED,
"Failed to change MTU"
)
)
)
}
}
}
@@ -580,10 +660,9 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (pendingFuture != null) {
callback(
Result.failure(
FlutterError(
"InProgress",
"Pairing already in progress",
null
createFlutterError(
UniversalBleErrorCode.OPERATION_IN_PROGRESS,
"Pairing already in progress"
)
)
)
@@ -594,12 +673,19 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (remoteDevice.createBond()) {
pairResultFutures[deviceId] = callback
} else {
callback(Result.failure(FlutterError("Failed", "Failed to pair", null)))
callback(
Result.failure(
createFlutterError(
UniversalBleErrorCode.PAIRING_FAILED,
"Failed to pair"
)
)
)
}
} catch (e: Exception) {
callback(
Result.failure(
FlutterError("Failed", e.toString(), null)
createFlutterError(UniversalBleErrorCode.FAILED, e.toString())
)
)
}
@@ -664,7 +750,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
try {
val timeout = (devices.size * 2).toLong()
latch.await(timeout, TimeUnit.SECONDS)
} catch (e: InterruptedException) {
} catch (_: InterruptedException) {
Thread.currentThread().interrupt()
}
@@ -749,10 +835,13 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
private fun cleanConnection(gatt: BluetoothGatt) {
gatt.removeCache()
gatt.disconnect()
val deviceDisconnectedError: FlutterError = createFlutterError(
UniversalBleErrorCode.DEVICE_DISCONNECTED,
"Device Disconnected",
)
readResultFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(DeviceDisconnectedError))
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
@@ -760,7 +849,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
writeResultFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(DeviceDisconnectedError))
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
@@ -768,7 +857,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
subscriptionResultFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(DeviceDisconnectedError))
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
@@ -776,7 +865,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
mtuResultFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(DeviceDisconnectedError))
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
@@ -784,7 +873,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
}
discoverServicesFutureList.removeAll {
if (it.deviceId == gatt.device.address) {
it.result(Result.failure(DeviceDisconnectedError))
it.result(Result.failure(deviceDisconnectedError))
true
} else {
false
@@ -830,7 +919,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
Log.v(TAG, "${device.address} BOND_BONDING")
}
BluetoothDevice.BOND_BONDED -> {
BOND_BONDED -> {
onBondStateUpdate(device.address, true)
}
@@ -944,7 +1033,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
if (descriptor?.uuid.toString() == ccdCharacteristic.toString()) {
val char: String? = descriptor?.characteristic?.uuid?.toString()
val service: String? = descriptor?.characteristic?.service?.uuid?.toString()
val deviceId: String? = gatt?.device?.address;
val deviceId: String? = gatt?.device?.address
if (deviceId != null && char != null && service != null) {
updateSubscriptionState(deviceId, char, service, status)
}
@@ -963,14 +1052,13 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
it.serviceId == service
}.forEach {
subscriptionResultFutureList.remove(it)
val error: String? = status.parseGattErrorCode()
if (error != null) {
if (status != BluetoothGatt.GATT_SUCCESS) {
it.result(
Result.failure(
FlutterError(
status.toString(),
"Failed to update subscription state: $error",
null,
createFlutterError(
gattStatusToUniversalBleErrorCode(status),
"Failed to update subscription state",
status.toString()
)
)
)
@@ -989,7 +1077,7 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
"com.navideck.universal_ble.services",
Context.MODE_PRIVATE
)
cachedServicesSharedPref.edit().putStringSet(deviceId, services.toSet()).apply()
cachedServicesSharedPref.edit { putStringSet(deviceId, services.toSet()) }
cachedServicesMap[deviceId] = services
}
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
echo "Building pigeon..."
dart run pigeon --input pigeon/universal_ble.dart
echo "Pigeon built successfully"
echo "Formatting generated files..."
dart format lib/src/universal_ble_pigeon/universal_ble.g.dart
echo "Generated files formatted successfully"
echo "Done"
+194 -32
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v26.0.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import Foundation
@@ -15,9 +15,9 @@ import Foundation
final class PigeonError: Error {
let code: String
let message: String?
let details: Any?
let details: Sendable?
init(code: String, message: String?, details: Any?) {
init(code: String, message: String?, details: Sendable?) {
self.code = code
self.message = message
self.details = details
@@ -68,8 +68,137 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
return value as! T?
}
func deepEqualsUniversalBle(_ lhs: Any?, _ rhs: Any?) -> Bool {
let cleanLhs = nilOrValue(lhs) as Any?
let cleanRhs = nilOrValue(rhs) as Any?
switch (cleanLhs, cleanRhs) {
case (nil, nil):
return true
case (nil, _), (_, nil):
return false
case is (Void, Void):
return true
case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable):
return cleanLhsHashable == cleanRhsHashable
case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]):
guard cleanLhsArray.count == cleanRhsArray.count else { return false }
for (index, element) in cleanLhsArray.enumerated() {
if !deepEqualsUniversalBle(element, cleanRhsArray[index]) {
return false
}
}
return true
case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false }
for (key, cleanLhsValue) in cleanLhsDictionary {
guard cleanRhsDictionary.index(forKey: key) != nil else { return false }
if !deepEqualsUniversalBle(cleanLhsValue, cleanRhsDictionary[key]!) {
return false
}
}
return true
default:
// Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue.
return false
}
}
func deepHashUniversalBle(value: Any?, hasher: inout Hasher) {
if let valueList = value as? [AnyHashable] {
for item in valueList { deepHashUniversalBle(value: item, hasher: &hasher) }
return
}
if let valueDict = value as? [AnyHashable: AnyHashable] {
for key in valueDict.keys {
hasher.combine(key)
deepHashUniversalBle(value: valueDict[key]!, hasher: &hasher)
}
return
}
if let hashableValue = value as? AnyHashable {
hasher.combine(hashableValue.hashValue)
}
return hasher.combine(String(describing: value))
}
/// Unified error codes for all platforms
enum UniversalBleErrorCode: Int {
case unknownError = 0
case failed = 1
case notSupported = 2
case notImplemented = 3
case channelError = 4
case bluetoothNotAvailable = 5
case bluetoothNotEnabled = 6
case bluetoothNotAllowed = 7
case bluetoothUnauthorized = 8
case deviceDisconnected = 9
case connectionTimeout = 10
case connectionFailed = 11
case connectionRejected = 12
case connectionLimitExceeded = 13
case connectionAlreadyExists = 14
case connectionTerminated = 15
case connectionInProgress = 16
case illegalArgument = 17
case deviceNotFound = 18
case serviceNotFound = 19
case characteristicNotFound = 20
case invalidServiceUuid = 21
case invalidCharacteristicUuid = 22
case invalidOffset = 23
case invalidAttributeLength = 24
case invalidPdu = 25
case invalidHandle = 26
case readFailed = 27
case readNotPermitted = 28
case writeFailed = 29
case writeNotPermitted = 30
case writeRequestBusy = 31
case invalidAction = 32
case operationNotSupported = 33
case operationTimeout = 34
case operationCancelled = 35
case operationInProgress = 36
case characteristicDoesNotSupportRead = 37
case characteristicDoesNotSupportWrite = 38
case characteristicDoesNotSupportWriteWithoutResponse = 39
case characteristicDoesNotSupportNotify = 40
case characteristicDoesNotSupportIndicate = 41
case notPaired = 42
case notPairable = 43
case alreadyPaired = 44
case pairingFailed = 45
case pairingCancelled = 46
case pairingTimeout = 47
case pairingNotAllowed = 48
case authenticationFailure = 49
case insufficientAuthentication = 50
case insufficientAuthorization = 51
case insufficientEncryption = 52
case insufficientKeySize = 53
case protectionLevelNotMet = 54
case accessDenied = 55
case unpairingFailed = 56
case alreadyUnpaired = 57
case scanFailed = 58
case stoppingScanInProgress = 59
case webBluetoothGloballyDisabled = 60
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalBleScanResult {
struct UniversalBleScanResult: Hashable {
var deviceId: String
var name: String? = nil
var isPaired: Bool? = nil
@@ -78,7 +207,6 @@ struct UniversalBleScanResult {
var services: [String]? = nil
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBleScanResult? {
let deviceId = pigeonVar_list[0] as! String
@@ -107,15 +235,19 @@ struct UniversalBleScanResult {
services,
]
}
static func == (lhs: UniversalBleScanResult, rhs: UniversalBleScanResult) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalBleService {
struct UniversalBleService: Hashable {
var uuid: String
var characteristics: [UniversalBleCharacteristic]? = nil
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBleService? {
let uuid = pigeonVar_list[0] as! String
@@ -132,15 +264,19 @@ struct UniversalBleService {
characteristics,
]
}
static func == (lhs: UniversalBleService, rhs: UniversalBleService) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalBleCharacteristic {
struct UniversalBleCharacteristic: Hashable {
var uuid: String
var properties: [Int64]
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalBleCharacteristic? {
let uuid = pigeonVar_list[0] as! String
@@ -157,18 +293,22 @@ struct UniversalBleCharacteristic {
properties,
]
}
static func == (lhs: UniversalBleCharacteristic, rhs: UniversalBleCharacteristic) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
}
}
/// Scan Filters
///
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalScanFilter {
struct UniversalScanFilter: Hashable {
var withServices: [String]
var withNamePrefix: [String]
var withManufacturerData: [UniversalManufacturerDataFilter]
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalScanFilter? {
let withServices = pigeonVar_list[0] as! [String]
@@ -188,16 +328,20 @@ struct UniversalScanFilter {
withManufacturerData,
]
}
static func == (lhs: UniversalScanFilter, rhs: UniversalScanFilter) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalManufacturerDataFilter {
struct UniversalManufacturerDataFilter: Hashable {
var companyIdentifier: Int64
var data: FlutterStandardTypedData? = nil
var mask: FlutterStandardTypedData? = nil
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalManufacturerDataFilter? {
let companyIdentifier = pigeonVar_list[0] as! Int64
@@ -217,15 +361,19 @@ struct UniversalManufacturerDataFilter {
mask,
]
}
static func == (lhs: UniversalManufacturerDataFilter, rhs: UniversalManufacturerDataFilter) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
}
}
/// Generated class from Pigeon that represents data sent in messages.
struct UniversalManufacturerData {
struct UniversalManufacturerData: Hashable {
var companyIdentifier: Int64
var data: FlutterStandardTypedData
// swift-format-ignore: AlwaysUseLowerCamelCase
static func fromList(_ pigeonVar_list: [Any?]) -> UniversalManufacturerData? {
let companyIdentifier = pigeonVar_list[0] as! Int64
@@ -242,22 +390,33 @@ struct UniversalManufacturerData {
data,
]
}
static func == (lhs: UniversalManufacturerData, rhs: UniversalManufacturerData) -> Bool {
return deepEqualsUniversalBle(lhs.toList(), rhs.toList()) }
func hash(into hasher: inout Hasher) {
deepHashUniversalBle(value: toList(), hasher: &hasher)
}
}
private class UniversalBlePigeonCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? {
switch type {
case 129:
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 130:
return UniversalBleService.fromList(self.readValue() as! [Any?])
return UniversalBleScanResult.fromList(self.readValue() as! [Any?])
case 131:
return UniversalBleCharacteristic.fromList(self.readValue() as! [Any?])
return UniversalBleService.fromList(self.readValue() as! [Any?])
case 132:
return UniversalScanFilter.fromList(self.readValue() as! [Any?])
return UniversalBleCharacteristic.fromList(self.readValue() as! [Any?])
case 133:
return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?])
return UniversalScanFilter.fromList(self.readValue() as! [Any?])
case 134:
return UniversalManufacturerDataFilter.fromList(self.readValue() as! [Any?])
case 135:
return UniversalManufacturerData.fromList(self.readValue() as! [Any?])
default:
return super.readValue(ofType: type)
@@ -267,24 +426,27 @@ private class UniversalBlePigeonCodecReader: FlutterStandardReader {
private class UniversalBlePigeonCodecWriter: FlutterStandardWriter {
override func writeValue(_ value: Any) {
if let value = value as? UniversalBleScanResult {
if let value = value as? UniversalBleErrorCode {
super.writeByte(129)
super.writeValue(value.toList())
} else if let value = value as? UniversalBleService {
super.writeValue(value.rawValue)
} else if let value = value as? UniversalBleScanResult {
super.writeByte(130)
super.writeValue(value.toList())
} else if let value = value as? UniversalBleCharacteristic {
} else if let value = value as? UniversalBleService {
super.writeByte(131)
super.writeValue(value.toList())
} else if let value = value as? UniversalScanFilter {
} else if let value = value as? UniversalBleCharacteristic {
super.writeByte(132)
super.writeValue(value.toList())
} else if let value = value as? UniversalManufacturerDataFilter {
} else if let value = value as? UniversalScanFilter {
super.writeByte(133)
super.writeValue(value.toList())
} else if let value = value as? UniversalManufacturerData {
} else if let value = value as? UniversalManufacturerDataFilter {
super.writeByte(134)
super.writeValue(value.toList())
} else if let value = value as? UniversalManufacturerData {
super.writeByte(135)
super.writeValue(value.toList())
} else {
super.writeValue(value)
}
@@ -650,7 +812,7 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol {
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(Void()))
completion(.success(()))
}
}
}
@@ -668,7 +830,7 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol {
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(Void()))
completion(.success(()))
}
}
}
@@ -686,7 +848,7 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol {
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(Void()))
completion(.success(()))
}
}
}
@@ -704,7 +866,7 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol {
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(Void()))
completion(.success(()))
}
}
}
@@ -722,7 +884,7 @@ class UniversalBleCallbackChannel: UniversalBleCallbackChannelProtocol {
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else {
completion(.success(Void()))
completion(.success(()))
}
}
}
+59 -2
View File
@@ -103,12 +103,69 @@ extension CBManagerState {
}
}
/// Maps string error codes to UniversalBleErrorCode enum
func mapErrorCodeToEnum(_ code: String) -> UniversalBleErrorCode {
switch code.lowercased() {
case "notsupported", "not_supported":
return .notSupported
case "notimplemented", "not_implemented":
return .notImplemented
case "channel-error", "channelerror":
return .channelError
case "failed":
return .failed
case "devicedisconnected", "device_disconnected":
return .deviceDisconnected
case "illegalargument", "illegal_argument":
return .illegalArgument
case "invalidaction", "invalid_action":
return .invalidAction
case "readfailed", "read_failed":
return .readFailed
case "devicenotfound", "device_not_found":
return .deviceNotFound
case "servicenotfound", "service_not_found":
return .serviceNotFound
case "characteristicnotfound", "characteristic_not_found":
return .characteristicNotFound
case "invalidserviceuuid", "invalid_service_uuid":
return .invalidServiceUuid
case "characteristicdoesnotsupportread":
return .characteristicDoesNotSupportRead
case "characteristicdoesnotsupportwrite":
return .characteristicDoesNotSupportWrite
case "characteristicdoesnotsupportwritewithoutresponse":
return .characteristicDoesNotSupportWriteWithoutResponse
case "characteristicdoesnotsupportnotify":
return .characteristicDoesNotSupportNotify
case "characteristicdoesnotsupportindicate":
return .characteristicDoesNotSupportIndicate
default:
return .unknownError
}
}
/// Creates a PigeonError with the error code enum in details
func createFlutterError(
code: UniversalBleErrorCode,
message: String? = nil,
details: String? = nil
) -> PigeonError {
// Pass the enum's rawValue (Int) in code as string, and enum name in details
return PigeonError(
code: code.rawValue.description,
message: message,
details: details ?? code.rawValue
)
}
extension Error {
func toPigeonError() -> PigeonError {
func toFlutterError() -> PigeonError {
let nsError = self as NSError
let errorCode: String = .init(nsError.code)
let errorDescription: String = nsError.localizedDescription
return PigeonError(code: errorCode, message: errorDescription, details: nil)
let mappedCode = mapErrorCodeToEnum(errorCode)
return createFlutterError(code: mappedCode, message: errorDescription, details: errorCode)
}
}
+33 -33
View File
@@ -55,11 +55,11 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
}
func enableBluetooth(completion: @escaping (Result<Bool, Error>) -> Void) {
completion(Result.failure(PigeonError(code: "NotSupported", message: nil, details: nil)))
completion(Result.failure(createFlutterError(code: .notSupported)))
}
func disableBluetooth(completion: @escaping (Result<Bool, any Error>) -> Void) {
completion(Result.failure(PigeonError(code: "NotSupported", message: nil, details: nil)))
completion(Result.failure(createFlutterError(code: .notSupported)))
}
func startScan(filter: UniversalScanFilter?) throws {
@@ -122,7 +122,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
characteristicReadFutures.removeAll { future in
if future.deviceId == deviceId {
future.result(
Result.failure(PigeonError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
Result.failure(createFlutterError(code: .deviceDisconnected, message: "Device Disconnected"))
)
return true
}
@@ -131,7 +131,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
characteristicWriteFutures.removeAll { future in
if future.deviceId == deviceId {
future.result(
Result.failure(PigeonError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
Result.failure(createFlutterError(code: .deviceDisconnected, message: "Device Disconnected"))
)
return true
}
@@ -140,7 +140,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
characteristicNotifyFutures.removeAll { future in
if future.deviceId == deviceId {
future.result(
Result.failure(PigeonError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
Result.failure(createFlutterError(code: .deviceDisconnected, message: "Device Disconnected"))
)
return true
}
@@ -149,7 +149,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
discoverServicesFutures.removeAll { future in
if future.deviceId == deviceId {
future.result(
Result.failure(PigeonError(code: "DeviceDisconnected", message: "Device Disconnected", details: nil))
Result.failure(createFlutterError(code: .deviceDisconnected, message: "Device Disconnected"))
)
return true
}
@@ -161,7 +161,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func discoverServices(deviceId: String, completion: @escaping (Result<[UniversalBleService], Error>) -> Void) {
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
completion(
Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil))
Result.failure(createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(self)"))
)
return
}
@@ -198,22 +198,22 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func setNotifiable(deviceId: String, service: String, characteristic: String, bleInputProperty: Int64, completion: @escaping (Result<Void, any Error>) -> Void) {
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
completion(Result.failure(createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(deviceId)")))
return
}
guard let gattCharacteristic = peripheral.getCharacteristic(characteristic, of: service) else {
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil)))
completion(Result.failure(createFlutterError(code: .characteristicNotFound, message: "Unknown characteristic:\(characteristic)")))
return
}
if bleInputProperty == BleInputProperty.notification.rawValue && !gattCharacteristic.properties.contains(.notify) {
completion(Result.failure(PigeonError(code: "InvalidAction", message: "Characteristic does not support notify", details: nil)))
completion(Result.failure(createFlutterError(code: .characteristicDoesNotSupportNotify, message: "Characteristic does not support notify")))
return
}
if bleInputProperty == BleInputProperty.indication.rawValue && !gattCharacteristic.properties.contains(.indicate) {
completion(Result.failure(PigeonError(code: "InvalidAction", message: "Characteristic does not support indicate", details: nil)))
completion(Result.failure(createFlutterError(code: .characteristicDoesNotSupportIndicate, message: "Characteristic does not support indicate")))
return
}
@@ -224,15 +224,15 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func readValue(deviceId: String, service: String, characteristic: String, completion: @escaping (Result<FlutterStandardTypedData, Error>) -> Void) {
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
completion(Result.failure(createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(self)")))
return
}
guard let gattCharacteristic = peripheral.getCharacteristic(characteristic, of: service) else {
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil)))
completion(Result.failure(createFlutterError(code: .characteristicNotFound, message: "Unknown characteristic:\(characteristic)")))
return
}
if !gattCharacteristic.properties.contains(.read) {
completion(Result.failure(PigeonError(code: "InvalidAction", message: "Characteristic does not support read", details: nil)))
completion(Result.failure(createFlutterError(code: .characteristicDoesNotSupportRead, message: "Characteristic does not support read")))
return
}
peripheral.readValue(for: gattCharacteristic)
@@ -241,11 +241,11 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func writeValue(deviceId: String, service: String, characteristic: String, value: FlutterStandardTypedData, bleOutputProperty: Int64, completion: @escaping (Result<Void, Error>) -> Void) {
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
completion(Result.failure(createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(self)")))
return
}
guard let gattCharacteristic = peripheral.getCharacteristic(characteristic, of: service) else {
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown characteristic:\(characteristic)", details: nil)))
completion(Result.failure(createFlutterError(code: .characteristicNotFound, message: "Unknown characteristic:\(characteristic)")))
return
}
@@ -253,12 +253,12 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
if type == CBCharacteristicWriteType.withResponse {
if !gattCharacteristic.properties.contains(.write) {
completion(Result.failure(PigeonError(code: "InvalidAction", message: "Characteristic does not support write withResponse", details: nil)))
completion(Result.failure(createFlutterError(code: .characteristicDoesNotSupportWrite, message: "Characteristic does not support write withResponse")))
return
}
} else if type == CBCharacteristicWriteType.withoutResponse {
if !gattCharacteristic.properties.contains(.writeWithoutResponse) {
completion(Result.failure(PigeonError(code: "InvalidAction", message: "Characteristic does not support write withoutResponse", details: nil)))
completion(Result.failure(createFlutterError(code: .characteristicDoesNotSupportWriteWithoutResponse, message: "Characteristic does not support write withoutResponse")))
return
}
}
@@ -275,7 +275,7 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
func requestMtu(deviceId: String, expectedMtu _: Int64, completion: @escaping (Result<Int64, Error>) -> Void) {
guard let peripheral = deviceId.findPeripheral(manager: manager) else {
completion(Result.failure(PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)))
completion(Result.failure(createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(self)")))
return
}
let mtu = peripheral.maximumWriteValueLength(for: CBCharacteristicWriteType.withoutResponse)
@@ -285,15 +285,15 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
}
func isPaired(deviceId _: String, completion: @escaping (Result<Bool, Error>) -> Void) {
completion(Result.failure(PigeonError(code: "NotSupported", message: nil, details: nil)))
completion(Result.failure(createFlutterError(code: .notSupported)))
}
func pair(deviceId _: String, completion: @escaping (Result<Bool, Error>) -> Void) {
completion(Result.failure(PigeonError(code: "Implemented in Dart", message: nil, details: nil)))
completion(Result.failure(createFlutterError(code: .notImplemented)))
}
func unPair(deviceId _: String) throws {
throw PigeonError(code: "NotSupported", message: nil, details: nil)
throw createFlutterError(code: .notSupported)
}
func getSystemDevices(withServices: [String], completion: @escaping (Result<[UniversalBleScanResult], Error>) -> Void) {
@@ -427,8 +427,8 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
characteristicWriteFutures.removeAll { future in
if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr {
if let pigeonError = error?.toPigeonError() {
future.result(Result.failure(pigeonError))
if let flutterError = error?.toFlutterError() {
future.result(Result.failure(flutterError))
} else {
future.result(Result.success({}()))
}
@@ -441,8 +441,8 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
public func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
characteristicNotifyFutures.removeAll { future in
if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr {
if let pigeonError = error?.toPigeonError() {
future.result(Result.failure(pigeonError))
if let flutterError = error?.toFlutterError() {
future.result(Result.failure(flutterError))
} else {
future.result(Result.success({}()))
}
@@ -467,13 +467,13 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
// Update futures for readValue
characteristicReadFutures.removeAll { future in
if future.deviceId == peripheral.uuid.uuidString && future.characteristicId == characteristic.uuid.uuidStr && future.serviceId == characteristic.service?.uuid.uuidStr {
if let pigeonError = error?.toPigeonError() {
future.result(Result.failure(pigeonError))
if let flutterError = error?.toFlutterError() {
future.result(Result.failure(flutterError))
} else {
if let characteristicValue = characteristic.value {
future.result(Result.success(FlutterStandardTypedData(bytes: characteristicValue)))
} else {
future.result(Result.failure(PigeonError(code: "ReadFailed", message: "No value", details: nil)))
future.result(Result.failure(createFlutterError(code: .readFailed, message: "No value")))
}
}
return true
@@ -484,15 +484,15 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
}
extension CBPeripheral {
func saveCache(){
discoveredPeripherals[self.uuid.uuidString] = self
func saveCache() {
discoveredPeripherals[uuid.uuidString] = self
}
}
extension String {
func getPeripheral(manager: CBCentralManager) throws -> CBPeripheral {
guard let peripheral = findPeripheral(manager: manager) else {
throw PigeonError(code: "IllegalArgument", message: "Unknown deviceId:\(self)", details: nil)
throw createFlutterError(code: .deviceNotFound, message: "Unknown deviceId:\(self)")
}
return peripheral
}
@@ -515,7 +515,7 @@ extension [String] {
func toCBUUID() throws -> [CBUUID] {
return try compactMap { serviceUUID in
guard UUID(uuidString: serviceUUID) != nil else {
throw PigeonError(code: "IllegalArgument", message: "Invalid service UUID:\(serviceUUID)", details: nil)
throw createFlutterError(code: .invalidServiceUuid, message: "Invalid service UUID:\(serviceUUID)")
}
return CBUUID(string: serviceUUID)
}
+1 -1
View File
@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
<string>13.0</string>
</dict>
</plist>
+1 -1
View File
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'
# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+3 -3
View File
@@ -31,11 +31,11 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
PODFILE CHECKSUM: 4f1c12611da7338d21589c0b2ecd6bd20b109694
COCOAPODS: 1.15.2
COCOAPODS: 1.16.2
+3 -3
View File
@@ -470,7 +470,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -598,7 +598,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -647,7 +647,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
@@ -54,6 +55,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
+8 -3
View File
@@ -1,3 +1,4 @@
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
import 'package:universal_ble/src/utils/cache_handler.dart';
import 'package:universal_ble/universal_ble.dart';
@@ -105,13 +106,17 @@ extension BleDeviceExtension on BleDevice {
}
if (discoveredServices.isEmpty) {
throw ServiceNotFoundException('No services found');
throw UniversalBleException(
code: UniversalBleErrorCode.serviceNotFound,
message: 'No services found',
);
}
return discoveredServices.firstWhere(
(s) => BleUuidParser.compareStrings(s.uuid, service),
orElse: () => throw ServiceNotFoundException(
'Service "$service" not available',
orElse: () => throw UniversalBleException(
code: UniversalBleErrorCode.serviceNotFound,
message: 'Service "$service" not available',
),
);
}
@@ -1,3 +1,4 @@
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
import 'package:universal_ble/universal_ble.dart';
/// Extension methods for [BleService] objects.
@@ -8,12 +9,16 @@ extension BleServiceExtension on BleService {
/// with the given UUID is not available.
BleCharacteristic getCharacteristic(String characteristicId) {
if (characteristics.isEmpty) {
throw CharacteristicNotFoundException('No characteristics found');
throw UniversalBleException(
code: UniversalBleErrorCode.characteristicNotFound,
message: 'No characteristics found',
);
}
return characteristics.firstWhere(
(c) => BleUuidParser.compareStrings(c.uuid, characteristicId),
orElse: () => throw CharacteristicNotFoundException(
'Characteristic "$characteristicId" not available',
orElse: () => throw UniversalBleException(
code: UniversalBleErrorCode.characteristicNotFound,
message: 'Characteristic "$characteristicId" not available',
),
);
}
+63 -36
View File
@@ -1,58 +1,85 @@
import 'package:flutter/services.dart';
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
import 'package:universal_ble/src/utils/universal_ble_error_parser.dart';
class ConnectionException implements Exception {
late String message;
/// Base exception class for all BLE errors with typed error codes
class UniversalBleException implements Exception {
final UniversalBleErrorCode code;
final String message;
final dynamic details;
ConnectionException([dynamic error]) {
message = _errorParser(error);
UniversalBleException({
required this.code,
required this.message,
this.details,
});
@override
String toString() => message;
factory UniversalBleException.fromError(dynamic error) {
String message = error.toString();
dynamic details = error;
if (error is PlatformException) {
message = error.message ?? error.details?.toString() ?? error.code;
details = error.details;
}
@override
String toString() => message;
}
class PairingException implements Exception {
late String message;
PairingException([dynamic error]) {
message = _errorParser(error);
return UniversalBleException(
code: UniversalBleErrorParser.getCode(error),
message: message,
details: details,
);
}
@override
String toString() => message;
}
class WebBluetoothGloballyDisabled implements Exception {
String message;
WebBluetoothGloballyDisabled(this.message);
/// Exception thrown when connection-related errors occur
class ConnectionException extends UniversalBleException {
ConnectionException._({
required super.code,
required super.message,
super.details,
});
@override
String toString() => message;
ConnectionException([dynamic error])
: this._(
code: UniversalBleErrorParser.getCode(error),
message: _errorParser(error),
details: error,
);
}
class NotFoundException implements Exception {}
/// Exception thrown when pairing-related errors occur
class PairingException extends UniversalBleException {
PairingException._({
required super.code,
required super.message,
super.details,
});
class ServiceNotFoundException implements NotFoundException {
String message;
ServiceNotFoundException(this.message);
@override
String toString() => message;
/// Legacy constructor for backward compatibility
PairingException([dynamic error])
: this._(
code: UniversalBleErrorParser.getCode(error),
message: _errorParser(error),
details: error,
);
}
class CharacteristicNotFoundException implements NotFoundException {
String message;
CharacteristicNotFoundException(this.message);
@override
String toString() => message;
/// Exception thrown when Web Bluetooth API is globally disabled
class WebBluetoothGloballyDisabled extends UniversalBleException {
WebBluetoothGloballyDisabled({
super.code = UniversalBleErrorCode.webBluetoothGloballyDisabled,
required super.message,
super.details,
});
}
/// Legacy error parser for backward compatibility
String _errorParser(dynamic error) {
if (error == null) {
return "Failed";
} else if (error is PlatformException) {
return error.message ?? error.details ?? error.code;
return error.message ?? error.details ?? error.code ?? 'Unknown error';
} else if (error is String) {
return error;
} else {
@@ -3,9 +3,12 @@ import 'dart:async';
import 'package:bluez/bluez.dart';
import 'package:flutter/services.dart';
import 'package:universal_ble/src/models/model_exports.dart';
import 'package:universal_ble/src/utils/universal_ble_error_parser.dart';
import 'package:universal_ble/src/utils/universal_ble_filter_util.dart';
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
import 'package:universal_ble/src/universal_ble_platform_interface.dart';
import 'package:universal_ble/src/utils/universal_logger.dart';
import 'package:universal_ble/src/universal_ble_exceptions.dart';
class UniversalBleLinux extends UniversalBlePlatform {
UniversalBleLinux._();
@@ -180,7 +183,10 @@ class UniversalBleLinux extends UniversalBlePlatform {
await Future.delayed(const Duration(seconds: 1));
if (device.gattServices.isEmpty && !device.servicesResolved) {
throw "Failed to resolve services";
throw UniversalBleException(
code: UniversalBleErrorCode.failed,
message: "Failed to resolve services",
);
}
List<BleService> services = [];
@@ -217,7 +223,10 @@ class UniversalBleLinux extends UniversalBlePlatform {
orElse: () => null);
if (c == null) {
throw Exception('Unknown characteristic:$characteristic');
throw UniversalBleException(
code: UniversalBleErrorCode.characteristicNotFound,
message: 'Unknown characteristic:$characteristic',
);
}
return c;
}
@@ -279,9 +288,8 @@ class UniversalBleLinux extends UniversalBlePlatform {
final data = await c.readValue();
return Uint8List.fromList(data);
} on BlueZFailedException catch (e) {
throw PlatformException(
code: e.errorCode ?? "ReadFailed",
message: e.message,
throw e.toUniversalBleException(
defaultCode: UniversalBleErrorCode.readFailed,
);
}
}
@@ -307,9 +315,8 @@ class UniversalBleLinux extends UniversalBlePlatform {
);
}
} on BlueZFailedException catch (e) {
throw PlatformException(
code: e.errorCode ?? "WriteFailed",
message: e.message,
throw e.toUniversalBleException(
defaultCode: UniversalBleErrorCode.writeFailed,
);
}
}
@@ -317,7 +324,12 @@ class UniversalBleLinux extends UniversalBlePlatform {
@override
Future<int> requestMtu(String deviceId, int expectedMtu) async {
final device = _findDeviceById(deviceId);
if (!device.connected) throw Exception('Device not connected');
if (!device.connected) {
throw UniversalBleException(
code: UniversalBleErrorCode.deviceDisconnected,
message: 'Device not connected',
);
}
for (BlueZGattService service in device.gattServices) {
for (BlueZGattCharacteristic characteristic in service.characteristics) {
int? mtu = characteristic.mtu;
@@ -325,7 +337,10 @@ class UniversalBleLinux extends UniversalBlePlatform {
if (mtu != null) return mtu - 3;
}
}
throw Exception('MTU not available');
throw UniversalBleException(
code: UniversalBleErrorCode.operationNotSupported,
message: 'MTU not available',
);
}
@override
@@ -393,7 +408,10 @@ class UniversalBleLinux extends UniversalBlePlatform {
(device) => device?.address == deviceId,
orElse: () => null);
if (device == null) {
throw Exception('Unknown deviceId:$deviceId');
throw UniversalBleException(
code: UniversalBleErrorCode.deviceNotFound,
message: 'Unknown deviceId:$deviceId',
);
}
return device;
}
@@ -459,7 +477,10 @@ class UniversalBleLinux extends UniversalBlePlatform {
}
if (client.adapters.isEmpty) {
throw Exception('Bluetooth adapter unavailable');
throw UniversalBleException(
code: UniversalBleErrorCode.bluetoothNotAvailable,
message: 'Bluetooth adapter unavailable',
);
}
}
@@ -584,6 +605,21 @@ extension on BlueZGattCharacteristicFlag {
}
extension on BlueZFailedException {
UniversalBleException toUniversalBleException({
required UniversalBleErrorCode defaultCode,
}) {
// Map BlueZ error code to UniversalBleErrorCode
UniversalBleErrorCode code = UniversalBleErrorParser.getCode(errorCode);
if (code == UniversalBleErrorCode.unknownError) {
code = defaultCode;
}
throw UniversalBleException(
code: code,
message: message,
details: errorCode,
);
}
/// Extract error code from message and parse into decimal
/// example: 'Operation failed with ATT error: 0x90' => 144
String? get errorCode {
+415 -103
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v26.0.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
@@ -15,7 +15,8 @@ PlatformException _createConnectionError(String channelName) {
);
}
List<Object?> wrapResponse({Object? result, PlatformException? error, bool empty = false}) {
List<Object?> wrapResponse(
{Object? result, PlatformException? error, bool empty = false}) {
if (empty) {
return <Object?>[];
}
@@ -25,6 +26,86 @@ List<Object?> wrapResponse({Object? result, PlatformException? error, bool empty
return <Object?>[error.code, error.message, error.details];
}
bool _deepEquals(Object? a, Object? b) {
if (a is List && b is List) {
return a.length == b.length &&
a.indexed
.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
}
if (a is Map && b is Map) {
return a.length == b.length &&
a.entries.every((MapEntry<Object?, Object?> entry) =>
(b as Map<Object?, Object?>).containsKey(entry.key) &&
_deepEquals(entry.value, b[entry.key]));
}
return a == b;
}
/// Unified error codes for all platforms
enum UniversalBleErrorCode {
unknownError,
failed,
notSupported,
notImplemented,
channelError,
bluetoothNotAvailable,
bluetoothNotEnabled,
bluetoothNotAllowed,
bluetoothUnauthorized,
deviceDisconnected,
connectionTimeout,
connectionFailed,
connectionRejected,
connectionLimitExceeded,
connectionAlreadyExists,
connectionTerminated,
connectionInProgress,
illegalArgument,
deviceNotFound,
serviceNotFound,
characteristicNotFound,
invalidServiceUuid,
invalidCharacteristicUuid,
invalidOffset,
invalidAttributeLength,
invalidPdu,
invalidHandle,
readFailed,
readNotPermitted,
writeFailed,
writeNotPermitted,
writeRequestBusy,
invalidAction,
operationNotSupported,
operationTimeout,
operationCancelled,
operationInProgress,
characteristicDoesNotSupportRead,
characteristicDoesNotSupportWrite,
characteristicDoesNotSupportWriteWithoutResponse,
characteristicDoesNotSupportNotify,
characteristicDoesNotSupportIndicate,
notPaired,
notPairable,
alreadyPaired,
pairingFailed,
pairingCancelled,
pairingTimeout,
pairingNotAllowed,
authenticationFailure,
insufficientAuthentication,
insufficientAuthorization,
insufficientEncryption,
insufficientKeySize,
protectionLevelNotMet,
accessDenied,
unpairingFailed,
alreadyUnpaired,
scanFailed,
stoppingScanInProgress,
webBluetoothGloballyDisabled,
}
class UniversalBleScanResult {
UniversalBleScanResult({
required this.deviceId,
@@ -47,7 +128,7 @@ class UniversalBleScanResult {
List<String>? services;
Object encode() {
List<Object?> _toList() {
return <Object?>[
deviceId,
name,
@@ -58,6 +139,10 @@ class UniversalBleScanResult {
];
}
Object encode() {
return _toList();
}
static UniversalBleScanResult decode(Object result) {
result as List<Object?>;
return UniversalBleScanResult(
@@ -65,10 +150,27 @@ class UniversalBleScanResult {
name: result[1] as String?,
isPaired: result[2] as bool?,
rssi: result[3] as int?,
manufacturerDataList: (result[4] as List<Object?>?)?.cast<UniversalManufacturerData>(),
manufacturerDataList:
(result[4] as List<Object?>?)?.cast<UniversalManufacturerData>(),
services: (result[5] as List<Object?>?)?.cast<String>(),
);
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (other is! UniversalBleScanResult || other.runtimeType != runtimeType) {
return false;
}
if (identical(this, other)) {
return true;
}
return _deepEquals(encode(), other.encode());
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hashAll(_toList());
}
class UniversalBleService {
@@ -81,20 +183,41 @@ class UniversalBleService {
List<UniversalBleCharacteristic>? characteristics;
Object encode() {
List<Object?> _toList() {
return <Object?>[
uuid,
characteristics,
];
}
Object encode() {
return _toList();
}
static UniversalBleService decode(Object result) {
result as List<Object?>;
return UniversalBleService(
uuid: result[0]! as String,
characteristics: (result[1] as List<Object?>?)?.cast<UniversalBleCharacteristic>(),
characteristics:
(result[1] as List<Object?>?)?.cast<UniversalBleCharacteristic>(),
);
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (other is! UniversalBleService || other.runtimeType != runtimeType) {
return false;
}
if (identical(this, other)) {
return true;
}
return _deepEquals(encode(), other.encode());
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hashAll(_toList());
}
class UniversalBleCharacteristic {
@@ -107,13 +230,17 @@ class UniversalBleCharacteristic {
List<int> properties;
Object encode() {
List<Object?> _toList() {
return <Object?>[
uuid,
properties,
];
}
Object encode() {
return _toList();
}
static UniversalBleCharacteristic decode(Object result) {
result as List<Object?>;
return UniversalBleCharacteristic(
@@ -121,6 +248,23 @@ class UniversalBleCharacteristic {
properties: (result[1] as List<Object?>?)!.cast<int>(),
);
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (other is! UniversalBleCharacteristic ||
other.runtimeType != runtimeType) {
return false;
}
if (identical(this, other)) {
return true;
}
return _deepEquals(encode(), other.encode());
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hashAll(_toList());
}
/// Scan Filters
@@ -137,7 +281,7 @@ class UniversalScanFilter {
List<UniversalManufacturerDataFilter> withManufacturerData;
Object encode() {
List<Object?> _toList() {
return <Object?>[
withServices,
withNamePrefix,
@@ -145,14 +289,35 @@ class UniversalScanFilter {
];
}
Object encode() {
return _toList();
}
static UniversalScanFilter decode(Object result) {
result as List<Object?>;
return UniversalScanFilter(
withServices: (result[0] as List<Object?>?)!.cast<String>(),
withNamePrefix: (result[1] as List<Object?>?)!.cast<String>(),
withManufacturerData: (result[2] as List<Object?>?)!.cast<UniversalManufacturerDataFilter>(),
withManufacturerData: (result[2] as List<Object?>?)!
.cast<UniversalManufacturerDataFilter>(),
);
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (other is! UniversalScanFilter || other.runtimeType != runtimeType) {
return false;
}
if (identical(this, other)) {
return true;
}
return _deepEquals(encode(), other.encode());
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hashAll(_toList());
}
class UniversalManufacturerDataFilter {
@@ -168,7 +333,7 @@ class UniversalManufacturerDataFilter {
Uint8List? mask;
Object encode() {
List<Object?> _toList() {
return <Object?>[
companyIdentifier,
data,
@@ -176,6 +341,10 @@ class UniversalManufacturerDataFilter {
];
}
Object encode() {
return _toList();
}
static UniversalManufacturerDataFilter decode(Object result) {
result as List<Object?>;
return UniversalManufacturerDataFilter(
@@ -184,6 +353,23 @@ class UniversalManufacturerDataFilter {
mask: result[2] as Uint8List?,
);
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (other is! UniversalManufacturerDataFilter ||
other.runtimeType != runtimeType) {
return false;
}
if (identical(this, other)) {
return true;
}
return _deepEquals(encode(), other.encode());
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hashAll(_toList());
}
class UniversalManufacturerData {
@@ -196,13 +382,17 @@ class UniversalManufacturerData {
Uint8List data;
Object encode() {
List<Object?> _toList() {
return <Object?>[
companyIdentifier,
data,
];
}
Object encode() {
return _toList();
}
static UniversalManufacturerData decode(Object result) {
result as List<Object?>;
return UniversalManufacturerData(
@@ -210,8 +400,24 @@ class UniversalManufacturerData {
data: result[1]! as Uint8List,
);
}
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (other is! UniversalManufacturerData ||
other.runtimeType != runtimeType) {
return false;
}
if (identical(this, other)) {
return true;
}
return _deepEquals(encode(), other.encode());
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hashAll(_toList());
}
class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec();
@@ -220,24 +426,27 @@ class _PigeonCodec extends StandardMessageCodec {
if (value is int) {
buffer.putUint8(4);
buffer.putInt64(value);
} else if (value is UniversalBleScanResult) {
} else if (value is UniversalBleErrorCode) {
buffer.putUint8(129);
writeValue(buffer, value.encode());
} else if (value is UniversalBleService) {
writeValue(buffer, value.index);
} else if (value is UniversalBleScanResult) {
buffer.putUint8(130);
writeValue(buffer, value.encode());
} else if (value is UniversalBleCharacteristic) {
} else if (value is UniversalBleService) {
buffer.putUint8(131);
writeValue(buffer, value.encode());
} else if (value is UniversalScanFilter) {
} else if (value is UniversalBleCharacteristic) {
buffer.putUint8(132);
writeValue(buffer, value.encode());
} else if (value is UniversalManufacturerDataFilter) {
} else if (value is UniversalScanFilter) {
buffer.putUint8(133);
writeValue(buffer, value.encode());
} else if (value is UniversalManufacturerData) {
} else if (value is UniversalManufacturerDataFilter) {
buffer.putUint8(134);
writeValue(buffer, value.encode());
} else if (value is UniversalManufacturerData) {
buffer.putUint8(135);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
@@ -247,16 +456,19 @@ class _PigeonCodec extends StandardMessageCodec {
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 129:
return UniversalBleScanResult.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : UniversalBleErrorCode.values[value];
case 130:
return UniversalBleService.decode(readValue(buffer)!);
return UniversalBleScanResult.decode(readValue(buffer)!);
case 131:
return UniversalBleCharacteristic.decode(readValue(buffer)!);
return UniversalBleService.decode(readValue(buffer)!);
case 132:
return UniversalScanFilter.decode(readValue(buffer)!);
return UniversalBleCharacteristic.decode(readValue(buffer)!);
case 133:
return UniversalManufacturerDataFilter.decode(readValue(buffer)!);
return UniversalScanFilter.decode(readValue(buffer)!);
case 134:
return UniversalManufacturerDataFilter.decode(readValue(buffer)!);
case 135:
return UniversalManufacturerData.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
@@ -269,9 +481,11 @@ class UniversalBlePlatformChannel {
/// Constructor for [UniversalBlePlatformChannel]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
UniversalBlePlatformChannel({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
UniversalBlePlatformChannel(
{BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
: pigeonVar_binaryMessenger = binaryMessenger,
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
pigeonVar_messageChannelSuffix =
messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
final BinaryMessenger? pigeonVar_binaryMessenger;
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
@@ -279,14 +493,17 @@ class UniversalBlePlatformChannel {
final String pigeonVar_messageChannelSuffix;
Future<int> getBluetoothAvailabilityState() async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getBluetoothAvailabilityState$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(null) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -306,14 +523,17 @@ class UniversalBlePlatformChannel {
}
Future<bool> enableBluetooth() async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.enableBluetooth$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(null) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -333,14 +553,17 @@ class UniversalBlePlatformChannel {
}
Future<bool> disableBluetooth() async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disableBluetooth$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disableBluetooth$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(null) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -360,14 +583,18 @@ class UniversalBlePlatformChannel {
}
Future<void> startScan(UniversalScanFilter? filter) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.startScan$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[filter]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[filter]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -382,14 +609,17 @@ class UniversalBlePlatformChannel {
}
Future<void> stopScan() async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopScan$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.stopScan$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(null) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -404,14 +634,18 @@ class UniversalBlePlatformChannel {
}
Future<void> connect(String deviceId) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.connect$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.connect$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[deviceId]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -426,14 +660,18 @@ class UniversalBlePlatformChannel {
}
Future<void> disconnect(String deviceId) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disconnect$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.disconnect$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[deviceId]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -447,15 +685,20 @@ class UniversalBlePlatformChannel {
}
}
Future<void> setNotifiable(String deviceId, String service, String characteristic, int bleInputProperty) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setNotifiable$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
Future<void> setNotifiable(String deviceId, String service,
String characteristic, int bleInputProperty) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.setNotifiable$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel
.send(<Object?>[deviceId, service, characteristic, bleInputProperty]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId, service, characteristic, bleInputProperty]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -470,14 +713,18 @@ class UniversalBlePlatformChannel {
}
Future<List<UniversalBleService>> discoverServices(String deviceId) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.discoverServices$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[deviceId]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -492,19 +739,25 @@ class UniversalBlePlatformChannel {
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<UniversalBleService>();
return (pigeonVar_replyList[0] as List<Object?>?)!
.cast<UniversalBleService>();
}
}
Future<Uint8List> readValue(String deviceId, String service, String characteristic) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readValue$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
Future<Uint8List> readValue(
String deviceId, String service, String characteristic) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.readValue$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[deviceId, service, characteristic]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId, service, characteristic]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -524,14 +777,18 @@ class UniversalBlePlatformChannel {
}
Future<int> requestMtu(String deviceId, int expectedMtu) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestMtu$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestMtu$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[deviceId, expectedMtu]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId, expectedMtu]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -550,15 +807,20 @@ class UniversalBlePlatformChannel {
}
}
Future<void> writeValue(String deviceId, String service, String characteristic, Uint8List value, int bleOutputProperty) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.writeValue$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
Future<void> writeValue(String deviceId, String service,
String characteristic, Uint8List value, int bleOutputProperty) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.writeValue$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(
<Object?>[deviceId, service, characteristic, value, bleOutputProperty]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId, service, characteristic, value, bleOutputProperty]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -573,14 +835,18 @@ class UniversalBlePlatformChannel {
}
Future<bool> isPaired(String deviceId) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPaired$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.isPaired$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[deviceId]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -600,14 +866,18 @@ class UniversalBlePlatformChannel {
}
Future<bool> pair(String deviceId) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.pair$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[deviceId]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -627,14 +897,18 @@ class UniversalBlePlatformChannel {
}
Future<void> unPair(String deviceId) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.unPair$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.unPair$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[deviceId]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -648,15 +922,20 @@ class UniversalBlePlatformChannel {
}
}
Future<List<UniversalBleScanResult>> getSystemDevices(List<String> withServices) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getSystemDevices$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
Future<List<UniversalBleScanResult>> getSystemDevices(
List<String> withServices) async {
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getSystemDevices$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[withServices]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[withServices]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -671,19 +950,24 @@ class UniversalBlePlatformChannel {
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<UniversalBleScanResult>();
return (pigeonVar_replyList[0] as List<Object?>?)!
.cast<UniversalBleScanResult>();
}
}
Future<int> getConnectionState(String deviceId) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectionState$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
final String pigeonVar_channelName =
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.getConnectionState$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel =
BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture =
pigeonVar_channel.send(<Object?>[deviceId]);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[deviceId]) as List<Object?>?;
await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -713,15 +997,24 @@ abstract class UniversalBleCallbackChannel {
void onScanResult(UniversalBleScanResult result);
void onValueChanged(String deviceId, String characteristicId, Uint8List value);
void onValueChanged(
String deviceId, String characteristicId, Uint8List value);
void onConnectionChanged(String deviceId, bool connected, String? error);
static void setUp(UniversalBleCallbackChannel? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) {
messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
static void setUp(
UniversalBleCallbackChannel? api, {
BinaryMessenger? binaryMessenger,
String messageChannelSuffix = '',
}) {
messageChannelSuffix =
messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged$messageChannelSuffix', pigeonChannelCodec,
final BasicMessageChannel<
Object?> pigeonVar_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onAvailabilityChanged$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
@@ -739,14 +1032,18 @@ abstract class UniversalBleCallbackChannel {
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange$messageChannelSuffix', pigeonChannelCodec,
final BasicMessageChannel<
Object?> pigeonVar_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onPairStateChange$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
@@ -768,14 +1065,18 @@ abstract class UniversalBleCallbackChannel {
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult$messageChannelSuffix', pigeonChannelCodec,
final BasicMessageChannel<
Object?> pigeonVar_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
@@ -784,7 +1085,8 @@ abstract class UniversalBleCallbackChannel {
assert(message != null,
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null.');
final List<Object?> args = (message as List<Object?>?)!;
final UniversalBleScanResult? arg_result = (args[0] as UniversalBleScanResult?);
final UniversalBleScanResult? arg_result =
(args[0] as UniversalBleScanResult?);
assert(arg_result != null,
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onScanResult was null, expected non-null UniversalBleScanResult.');
try {
@@ -793,14 +1095,18 @@ abstract class UniversalBleCallbackChannel {
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged$messageChannelSuffix', pigeonChannelCodec,
final BasicMessageChannel<
Object?> pigeonVar_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
@@ -819,19 +1125,24 @@ abstract class UniversalBleCallbackChannel {
assert(arg_value != null,
'Argument for dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onValueChanged was null, expected non-null Uint8List.');
try {
api.onValueChanged(arg_deviceId!, arg_characteristicId!, arg_value!);
api.onValueChanged(
arg_deviceId!, arg_characteristicId!, arg_value!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged$messageChannelSuffix', pigeonChannelCodec,
final BasicMessageChannel<
Object?> pigeonVar_channel = BasicMessageChannel<
Object?>(
'dev.flutter.pigeon.universal_ble.UniversalBleCallbackChannel.onConnectionChanged$messageChannelSuffix',
pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
@@ -853,7 +1164,8 @@ abstract class UniversalBleCallbackChannel {
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
return wrapResponse(
error: PlatformException(code: 'error', message: e.toString()));
}
});
}
@@ -1,5 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
import 'package:universal_ble/src/utils/universal_ble_filter_util.dart';
import 'package:universal_ble/universal_ble.dart';
@@ -18,7 +17,9 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
@override
Future<AvailabilityState> getBluetoothAvailabilityState() async {
int state = await _channel.getBluetoothAvailabilityState();
int state = await _executeWithErrorHandling(
() => _channel.getBluetoothAvailabilityState(),
);
return AvailabilityState.parse(state);
}
@@ -27,7 +28,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
if (!BleCapabilities.supportsBluetoothEnableApi) {
throw UnsupportedError("Not supported");
}
return _channel.enableBluetooth();
return _executeWithErrorHandling(() => _channel.enableBluetooth());
}
@override
@@ -35,7 +36,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
if (!BleCapabilities.supportsBluetoothEnableApi) {
throw UnsupportedError("Not supported");
}
return _channel.disableBluetooth();
return _executeWithErrorHandling(() => _channel.disableBluetooth());
}
@override
@@ -45,31 +46,37 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
}) async {
await _ensureInitialized();
_bleFilter.scanFilter = scanFilter;
await _channel.startScan(
await _executeWithErrorHandling(
() => _channel.startScan(
scanFilter.toUniversalScanFilter(),
),
);
}
@override
Future<void> stopScan() => _channel.stopScan();
Future<void> stopScan() =>
_executeWithErrorHandling(() => _channel.stopScan());
@override
Future<BleConnectionState> getConnectionState(String deviceId) async {
int state = await _channel.getConnectionState(deviceId);
int state = await _executeWithErrorHandling(
() => _channel.getConnectionState(deviceId));
return BleConnectionState.parse(state);
}
@override
Future<void> connect(String deviceId, {Duration? connectionTimeout}) =>
_channel.connect(deviceId);
_executeWithErrorHandling(() => _channel.connect(deviceId));
@override
Future<void> disconnect(String deviceId) => _channel.disconnect(deviceId);
Future<void> disconnect(String deviceId) =>
_executeWithErrorHandling(() => _channel.disconnect(deviceId));
@override
Future<List<BleService>> discoverServices(String deviceId) async {
List<UniversalBleService?> universalBleServices =
await _channel.discoverServices(deviceId);
await _executeWithErrorHandling(
() => _channel.discoverServices(deviceId));
return List<BleService>.from(universalBleServices
.where((e) => e != null)
.map((e) => e!.toBleService(deviceId))
@@ -79,12 +86,12 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
@override
Future<void> setNotifiable(String deviceId, String service,
String characteristic, BleInputProperty bleInputProperty) {
return _channel.setNotifiable(
return _executeWithErrorHandling(() => _channel.setNotifiable(
deviceId,
service,
characteristic,
bleInputProperty.index,
);
));
}
@override
@@ -94,7 +101,8 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
String characteristic, {
final Duration? timeout,
}) {
return _channel.readValue(deviceId, service, characteristic);
return _executeWithErrorHandling(
() => _channel.readValue(deviceId, service, characteristic));
}
@override
@@ -104,33 +112,38 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
String characteristic,
Uint8List value,
BleOutputProperty bleOutputProperty) {
return _channel.writeValue(
return _executeWithErrorHandling(() => _channel.writeValue(
deviceId,
service,
characteristic,
value,
bleOutputProperty.index,
);
));
}
@override
Future<int> requestMtu(String deviceId, int expectedMtu) =>
_channel.requestMtu(deviceId, expectedMtu);
_executeWithErrorHandling(
() => _channel.requestMtu(deviceId, expectedMtu));
@override
Future<bool> isPaired(String deviceId) => _channel.isPaired(deviceId);
Future<bool> isPaired(String deviceId) =>
_executeWithErrorHandling(() => _channel.isPaired(deviceId));
@override
Future<bool> pair(String deviceId) => _channel.pair(deviceId);
Future<bool> pair(String deviceId) =>
_executeWithErrorHandling(() => _channel.pair(deviceId));
@override
Future<void> unpair(String deviceId) => _channel.unPair(deviceId);
Future<void> unpair(String deviceId) =>
_executeWithErrorHandling(() => _channel.unPair(deviceId));
@override
Future<List<BleDevice>> getSystemDevices(
List<String>? withServices,
) async {
var devices = await _channel.getSystemDevices(withServices ?? []);
var devices = await _executeWithErrorHandling(
() => _channel.getSystemDevices(withServices ?? []));
return List<BleDevice>.from(
devices.map((e) => e.toBleDevice(isSystemDevice: true)).toList(),
);
@@ -152,6 +165,16 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
));
}
/// Executes a platform call with error handling
/// Converts any errors to UniversalBleException
Future<T> _executeWithErrorHandling<T>(Future<T> Function() future) async {
try {
return await future();
} catch (error) {
throw UniversalBleException.fromError(error);
}
}
Future<void> _ensureInitialized() async {
// Check bluetooth availability on Apple
// so that it will ask permission only when required, and throw error on failed
@@ -160,13 +183,13 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
AvailabilityState state = await getBluetoothAvailabilityState();
switch (state) {
case AvailabilityState.unauthorized:
throw PlatformException(
code: "Unauthorized",
throw UniversalBleException(
code: UniversalBleErrorCode.bluetoothUnauthorized,
message: "Not authorized to access Bluetooth",
);
case AvailabilityState.unsupported:
throw PlatformException(
code: "Unsupported",
throw UniversalBleException(
code: UniversalBleErrorCode.notSupported,
message: "Bluetooth is not supported",
);
default:
@@ -4,6 +4,7 @@ import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart';
import 'package:universal_ble/src/utils/universal_logger.dart';
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
import 'package:universal_ble/universal_ble.dart';
class UniversalBleWeb extends UniversalBlePlatform {
@@ -35,7 +36,12 @@ class UniversalBleWeb extends UniversalBlePlatform {
Duration? connectionTimeout = const Duration(seconds: 10),
}) async {
var device = _getDeviceById(deviceId);
if (device == null) throw "$deviceId Not Found";
if (device == null) {
throw UniversalBleException(
code: UniversalBleErrorCode.deviceNotFound,
message: "$deviceId Not Found",
);
}
await device.connect(timeout: connectionTimeout);
// Subscribe to Connection Stream
@@ -92,7 +98,7 @@ class UniversalBleWeb extends UniversalBlePlatform {
} catch (e) {
String error = e.toString().replaceAll("DeviceNotFoundError:", "").trim();
if (error.toLowerCase().contains("api globally disabled")) {
throw WebBluetoothGloballyDisabled(error);
throw WebBluetoothGloballyDisabled(message: error);
}
rethrow;
}
@@ -154,7 +160,9 @@ class UniversalBleWeb extends UniversalBlePlatform {
);
if (bleCharacteristic == null) {
throw Exception(
throw UniversalBleException(
code: UniversalBleErrorCode.characteristicNotFound,
message:
'Characteristic $characteristic for service $service not found',
);
}
@@ -195,7 +203,9 @@ class UniversalBleWeb extends UniversalBlePlatform {
);
if (bleCharacteristic == null) {
throw Exception(
throw UniversalBleException(
code: UniversalBleErrorCode.characteristicNotFound,
message:
'Characteristic $characteristic for service $service not found',
);
}
@@ -221,8 +231,11 @@ class UniversalBleWeb extends UniversalBlePlatform {
characteristicId: characteristic,
);
if (bleCharacteristic == null) {
throw Exception(
'Characteristic $characteristic for service $service not found');
throw UniversalBleException(
code: UniversalBleErrorCode.characteristicNotFound,
message:
'Characteristic $characteristic for service $service not found',
);
}
var data = timeout != null
? bleCharacteristic.readValue(timeout: timeout)
@@ -233,29 +246,44 @@ class UniversalBleWeb extends UniversalBlePlatform {
/// `Unimplemented`
@override
Future<int> requestMtu(String deviceId, int expectedMtu) {
throw UnimplementedError();
throw UniversalBleException(
code: UniversalBleErrorCode.notImplemented,
message: "requestMtu is not implemented on Web platform",
);
}
@override
Future<bool> isPaired(String deviceId) {
throw UnimplementedError();
throw UniversalBleException(
code: UniversalBleErrorCode.notImplemented,
message: "isPaired is not implemented on Web platform",
);
}
@override
Future<bool> pair(String deviceId) {
throw UnimplementedError();
throw UniversalBleException(
code: UniversalBleErrorCode.notImplemented,
message: "pair is not implemented on Web platform",
);
}
@override
Future<void> unpair(String deviceId) {
throw UnimplementedError();
throw UniversalBleException(
code: UniversalBleErrorCode.notImplemented,
message: "unpair is not implemented on Web platform",
);
}
@override
Future<List<BleDevice>> getSystemDevices(
List<String>? withServices,
) {
throw UnimplementedError();
throw UniversalBleException(
code: UniversalBleErrorCode.notImplemented,
message: "getSystemDevices is not implemented on Web platform",
);
}
/// Helpers
@@ -335,12 +363,18 @@ class UniversalBleWeb extends UniversalBlePlatform {
@override
Future<bool> enableBluetooth() {
throw UnimplementedError();
throw UniversalBleException(
code: UniversalBleErrorCode.notImplemented,
message: "enableBluetooth is not implemented on Web platform",
);
}
@override
Future<bool> disableBluetooth() {
throw UnimplementedError();
throw UniversalBleException(
code: UniversalBleErrorCode.notImplemented,
message: "disableBluetooth is not implemented on Web platform",
);
}
RequestOptionsBuilder _getRequestOptionBuilder(
@@ -0,0 +1,417 @@
import 'package:flutter/services.dart';
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble.g.dart';
/// Utility class to parse error codes from dynamic errors
class UniversalBleErrorParser {
static UniversalBleErrorCode getCode(dynamic error) {
if (error is UniversalBleErrorCode) return error;
if (error is PlatformException) {
int? errorCodeInt = int.tryParse((error).code);
if (errorCodeInt != null) {
return UniversalBleErrorCode.values[errorCodeInt];
}
}
if (error is num) {
return _parseNumericErrorCode((error).toInt());
}
if (error is String) {
int? errorCodeInt = int.tryParse(error);
if (errorCodeInt != null) {
return _parseNumericErrorCode(errorCodeInt);
} else {
return _parseStringErrorCode(error) ??
UniversalBleErrorCode.unknownError;
}
}
return UniversalBleErrorCode.unknownError;
}
static UniversalBleErrorCode? _parseStringErrorCode(String code) {
switch (code.toLowerCase()) {
case 'notsupported':
case 'not_supported':
return UniversalBleErrorCode.notSupported;
case 'notimplemented':
case 'not_implemented':
return UniversalBleErrorCode.notImplemented;
case 'channel-error':
case 'channelerror':
return UniversalBleErrorCode.channelError;
case 'failed':
return UniversalBleErrorCode.failed;
case 'bluetoothnotavailable':
case 'bluetooth_not_available':
return UniversalBleErrorCode.bluetoothNotAvailable;
case 'bluetoothnotenabled':
case 'bluetooth_not_enabled':
return UniversalBleErrorCode.bluetoothNotEnabled;
case 'bluetoothnotallowed':
case 'bluetooth_not_allowed':
return UniversalBleErrorCode.bluetoothNotAllowed;
case 'bluetoothunauthorized':
case 'bluetooth_unauthorized':
return UniversalBleErrorCode.bluetoothUnauthorized;
case 'devicedisconnected':
case 'device_disconnected':
return UniversalBleErrorCode.deviceDisconnected;
case 'connectiontimeout':
case 'connection_timeout':
return UniversalBleErrorCode.connectionTimeout;
case 'connectionfailed':
case 'connection_failed':
return UniversalBleErrorCode.connectionFailed;
case 'connectionrejected':
case 'connection_rejected':
return UniversalBleErrorCode.connectionRejected;
case 'connecting':
case 'connectioninprogress':
case 'connection_in_progress':
return UniversalBleErrorCode.connectionInProgress;
case 'connectionterminated':
case 'connection_terminated':
return UniversalBleErrorCode.connectionTerminated;
case 'illegalargument':
case 'illegal_argument':
return UniversalBleErrorCode.illegalArgument;
case 'devicenotfound':
case 'device_not_found':
return UniversalBleErrorCode.deviceNotFound;
case 'servicenotfound':
case 'service_not_found':
return UniversalBleErrorCode.serviceNotFound;
case 'characteristicnotfound':
case 'characteristic_not_found':
return UniversalBleErrorCode.characteristicNotFound;
case 'invalidserviceuuid':
case 'invalid_service_uuid':
return UniversalBleErrorCode.invalidServiceUuid;
case 'invalidcharacteristicuuid':
case 'invalid_characteristic_uuid':
return UniversalBleErrorCode.invalidCharacteristicUuid;
case 'readfailed':
case 'read_failed':
return UniversalBleErrorCode.readFailed;
case 'writefailed':
case 'write_failed':
case 'writeerror':
case 'write_error':
return UniversalBleErrorCode.writeFailed;
case 'invalidaction':
case 'invalid_action':
return UniversalBleErrorCode.invalidAction;
case 'operationnotsupported':
case 'operation_not_supported':
return UniversalBleErrorCode.operationNotSupported;
case 'operationtimeout':
case 'operation_timeout':
return UniversalBleErrorCode.operationTimeout;
case 'operationcancelled':
case 'operation_cancelled':
return UniversalBleErrorCode.operationCancelled;
case 'inprogress':
case 'alreadyinprogress':
case 'already_in_progress':
return UniversalBleErrorCode.operationInProgress;
case 'notpaired':
case 'not_paired':
return UniversalBleErrorCode.notPaired;
case 'notpairable':
case 'not_pairable':
return UniversalBleErrorCode.notPairable;
case 'alreadypaired':
case 'already_paired':
return UniversalBleErrorCode.alreadyPaired;
case 'pairingfailed':
case 'pairing_failed':
return UniversalBleErrorCode.pairingFailed;
case 'pairingcancelled':
case 'pairing_cancelled':
return UniversalBleErrorCode.pairingCancelled;
case 'pairingtimeout':
case 'pairing_timeout':
return UniversalBleErrorCode.pairingTimeout;
case 'pairingnotallowed':
case 'pairing_not_allowed':
return UniversalBleErrorCode.pairingNotAllowed;
case 'authenticationfailure':
case 'authentication_failure':
return UniversalBleErrorCode.authenticationFailure;
case 'authenticationtimeout':
case 'authentication_timeout':
case 'authenticationnotallowed':
case 'authentication_not_allowed':
return UniversalBleErrorCode.authenticationFailure;
case 'hardwarefailure':
case 'hardware_failure':
case 'toomanyconnections':
case 'too_many_connections':
case 'notreadytopair':
case 'not_ready_to_pair':
case 'nonsupportedprofiles':
case 'no_supported_profiles':
case 'invalidceremonydata':
case 'invalid_ceremony_data':
case 'requiredhandlernotregistered':
case 'required_handler_not_registered':
case 'rejectedbyhandler':
case 'rejected_by_handler':
case 'remotedevicehasassociation':
case 'remote_device_has_association':
return UniversalBleErrorCode.pairingFailed;
case 'protectionlevelcouldnotbemet':
case 'protection_level_could_not_be_met':
return UniversalBleErrorCode.protectionLevelNotMet;
case 'unpairingfailed':
case 'unpairing_failed':
return UniversalBleErrorCode.unpairingFailed;
case 'alreadyunpaired':
case 'already_unpaired':
return UniversalBleErrorCode.alreadyUnpaired;
case 'accessdenied':
case 'access_denied':
return UniversalBleErrorCode.accessDenied;
case 'scan_failed_already_started':
case 'scanfailedalreadystarted':
case 'scan_failed_application_registration_failed':
case 'scanfailedapplicationregistrationfailed':
case 'scan_failed_feature_unsupported':
case 'scanfailedfeatureunsupported':
case 'scan_failed_internal_error':
case 'scanfailedinternalerror':
case 'scan_failed_out_of_hardware_resources':
case 'scanfailedoutofhardwareresources':
case 'scan_failed_scanning_too_frequently':
case 'scanfailedscanningtoofrequently':
return UniversalBleErrorCode.scanFailed;
case 'stoppingscaninprogress':
case 'stopping_scan_in_progress':
return UniversalBleErrorCode.stoppingScanInProgress;
case 'gatt_failure':
case 'gattfailure':
case 'unreachable':
case 'protocolerror':
case 'protocol_error':
case 'gattunreachable':
case 'gatt_protocol_error':
case 'gatt_unlikely':
case 'gattunlikely':
case 'gatt_insufficient_resources':
case 'gattinsufficientresources':
return UniversalBleErrorCode.failed;
case 'gatt_read_not_permitted':
case 'gattreadnotpermitted':
return UniversalBleErrorCode.readNotPermitted;
case 'gatt_write_not_permitted':
case 'gattwritenotpermitted':
return UniversalBleErrorCode.writeNotPermitted;
case 'gatt_insufficient_authentication':
case 'gattinsufficientauthentication':
return UniversalBleErrorCode.insufficientAuthentication;
case 'gatt_insufficient_authorization':
case 'gattinsufficientauthorization':
return UniversalBleErrorCode.insufficientAuthorization;
case 'gatt_insufficient_encryption':
case 'gattinsufficientencryption':
return UniversalBleErrorCode.insufficientEncryption;
case 'gatt_insufficient_key_size':
case 'gattinsufficientkeysize':
return UniversalBleErrorCode.insufficientKeySize;
case 'gatt_request_not_supported':
case 'gattrequestnotsupported':
return UniversalBleErrorCode.operationNotSupported;
case 'gatt_invalid_offset':
case 'gattinvalidoffset':
return UniversalBleErrorCode.invalidOffset;
case 'gatt_invalid_attribute_length':
case 'gattinvalidattributelength':
return UniversalBleErrorCode.invalidAttributeLength;
case 'gatt_connection_congested':
case 'gattconnectioncongested':
return UniversalBleErrorCode.connectionFailed;
case 'gatt_invalid_handle':
case 'gattinvalidhandle':
return UniversalBleErrorCode.invalidHandle;
case 'gatt_invalid_pdu':
case 'gattinvalidpdu':
return UniversalBleErrorCode.invalidPdu;
case 'gatt_prepare_queue_full':
case 'gattpreparequeuefull':
return UniversalBleErrorCode.operationInProgress;
case 'gatt_attr_not_found':
case 'gattattrnotfound':
return UniversalBleErrorCode.serviceNotFound;
case 'gatt_attr_not_long':
case 'gattattrnotlong':
return UniversalBleErrorCode.invalidAttributeLength;
case 'gatt_unsupported_group':
case 'gattunsupportedgroup':
return UniversalBleErrorCode.operationNotSupported;
case 'feature_not_configured':
case 'featurenotconfigured':
case 'feature_not_supported':
case 'featurenotsupported':
case 'feature_supported':
case 'featuresupported':
return UniversalBleErrorCode.notSupported;
case 'error_profile_service_not_bound':
case 'errorprofileservicenotbound':
return UniversalBleErrorCode.failed;
case 'error_missing_bluetooth_connect_permission':
case 'errormissingbluetoothconnectpermission':
return UniversalBleErrorCode.bluetoothNotAllowed;
case 'error_device_not_bonded':
case 'errordevicenotbonded':
return UniversalBleErrorCode.notPaired;
case 'error_gatt_write_not_allowed':
case 'errorgattwritenotallowed':
return UniversalBleErrorCode.writeNotPermitted;
case 'error_gatt_write_request_busy':
case 'errorgattwriterequestbusy':
return UniversalBleErrorCode.writeRequestBusy;
case 'error_unknown':
case 'errorunknown':
return UniversalBleErrorCode.unknownError;
case 'webbluetoothgloballydisabled':
case 'web_bluetooth_globally_disabled':
return UniversalBleErrorCode.webBluetoothGloballyDisabled;
default:
return null;
}
}
static UniversalBleErrorCode _parseNumericErrorCode(int code) {
switch (code) {
case 0x00:
return UniversalBleErrorCode.unknownError;
case 0x01:
return UniversalBleErrorCode.invalidHandle;
case 0x02:
return UniversalBleErrorCode.readNotPermitted;
case 0x03:
return UniversalBleErrorCode.writeNotPermitted;
case 0x04:
return UniversalBleErrorCode.invalidPdu;
case 0x05:
return UniversalBleErrorCode.insufficientAuthentication;
// Consolidated: gattRequestNotSupported -> operationNotSupported
case 0x06:
return UniversalBleErrorCode.operationNotSupported;
case 0x07:
return UniversalBleErrorCode.invalidOffset;
case 0x08:
return UniversalBleErrorCode.insufficientAuthorization;
// Consolidated: gattPrepareQueueFull -> operationInProgress
case 0x09:
return UniversalBleErrorCode.operationInProgress;
// Consolidated: gattAttrNotFound -> serviceNotFound
case 0x0A:
return UniversalBleErrorCode.serviceNotFound;
// Consolidated: gattAttrNotLong -> invalidAttributeLength
case 0x0B:
return UniversalBleErrorCode.invalidAttributeLength;
case 0x0C:
return UniversalBleErrorCode.insufficientKeySize;
case 0x0D:
return UniversalBleErrorCode.invalidAttributeLength;
// Consolidated: gattUnlikely -> failed
case 0x0E:
return UniversalBleErrorCode.failed;
case 0x0F:
return UniversalBleErrorCode.insufficientEncryption;
// Consolidated: gattUnsupportedGroup -> operationNotSupported
case 0x10:
return UniversalBleErrorCode.operationNotSupported;
// Consolidated: gattInsufficientResources -> failed
case 0x11:
return UniversalBleErrorCode.failed;
// Consolidated: gattConnectionCongested -> connectionFailed
case 0x85:
return UniversalBleErrorCode.connectionFailed;
// Consolidated: gattFailure -> failed
case 0x101:
return UniversalBleErrorCode.failed;
}
// HCI error codes - consolidated to higher-level errors
switch (code) {
// Connection errors
case 0x08: // Connection Timeout
case 0x10: // Connection Accept Timeout Exceeded
return UniversalBleErrorCode.connectionTimeout;
case 0x09: // Connection Limit Exceeded
case 0x0A: // Synchronous Connection Limit To A Device Exceeded
return UniversalBleErrorCode.connectionLimitExceeded;
case 0x0B: // Connection Already Exists
return UniversalBleErrorCode.connectionAlreadyExists;
case 0x0D: // Connection Rejected due to Limited Resources
case 0x0F: // Connection Rejected due to Unacceptable BD_ADDR
case 0x39: // Connection Rejected due to No Suitable Channel Found
return UniversalBleErrorCode.connectionRejected;
case 0x0E: // Connection Rejected Due To Security Reasons
return UniversalBleErrorCode.connectionRejected;
case 0x13: // Remote User Terminated Connection
case 0x14: // Remote Device Terminated Connection due to Low Resources
case 0x15: // Remote Device Terminated Connection due to Power Off
case 0x16: // Connection Terminated By Local Host
case 0x3D: // Connection Terminated due to MIC Failure
return UniversalBleErrorCode.connectionTerminated;
case 0x3E: // Connection Failed to be Established
case 0x3F: // MAC Connection Failed
return UniversalBleErrorCode.connectionFailed;
// Pairing/Authentication errors
case 0x05: // Authentication Failure
return UniversalBleErrorCode.authenticationFailure;
case 0x18: // Pairing Not Allowed
return UniversalBleErrorCode.pairingNotAllowed;
case 0x03: // Hardware Failure
case 0x29: // Pairing With Unit Key Not Supported
case 0x37: // Secure Simple Pairing Not Supported By Host
case 0x38: // Host Busy - Pairing
return UniversalBleErrorCode.pairingFailed;
case 0x2F: // Insufficient Security
return UniversalBleErrorCode.insufficientEncryption;
// Operation errors
case 0x0C: // Command Disallowed
case 0x11: // Unsupported Feature or Parameter Value
case 0x12: // Invalid HCI Command Parameters
case 0x1A: // Unsupported Remote Feature
case 0x1E: // Invalid LMP Parameters
case 0x20: // Unsupported LMP Parameter Value
return UniversalBleErrorCode.operationNotSupported;
case 0x22: // LMP Response Timeout
return UniversalBleErrorCode.operationTimeout;
// General errors
case 0x01: // Unknown HCI Command
case 0x02: // Unknown Connection Identifier
return UniversalBleErrorCode.unknownError;
case 0x04: // Page Timeout
return UniversalBleErrorCode.connectionTimeout;
case 0x06: // PIN or Key Missing
return UniversalBleErrorCode.authenticationFailure;
case 0x07: // Memory Capacity Exceeded
return UniversalBleErrorCode.failed;
case 0x17: // Repeated Attempts
return UniversalBleErrorCode.failed;
case 0x25: // Encryption Mode Not Acceptable
return UniversalBleErrorCode.insufficientEncryption;
case 0x26: // Link Key cannot be Changed
case 0x27: // Requested QoS Not Supported
case 0x2C: // QoS Unacceptable Parameter
case 0x2D: // QoS Rejected
case 0x2E: // Channel Classification Not Supported
case 0x30: // Parameter Out Of Mandatory Range
case 0x32: // Role Switch Pending
case 0x35: // Role Switch Failed
case 0x36: // Extended Inquiry Response Too Large
case 0x3A: // Controller Busy
case 0x3B: // Unacceptable Connection Parameters
case 0x3C: // Advertising Timeout
return UniversalBleErrorCode.failed;
}
// Default to unknown error for unmapped codes
return UniversalBleErrorCode.unknownError;
}
}
+84
View File
@@ -169,3 +169,87 @@ class UniversalManufacturerData {
required this.data,
});
}
/// Unified error codes for all platforms
enum UniversalBleErrorCode {
// General errors
unknownError,
failed,
notSupported,
notImplemented,
channelError,
// Bluetooth availability errors
bluetoothNotAvailable,
bluetoothNotEnabled,
bluetoothNotAllowed,
bluetoothUnauthorized,
// Connection errors
deviceDisconnected,
connectionTimeout,
connectionFailed,
connectionRejected,
connectionLimitExceeded,
connectionAlreadyExists,
connectionTerminated,
connectionInProgress,
// Device/Service/Characteristic errors
illegalArgument,
deviceNotFound,
serviceNotFound,
characteristicNotFound,
invalidServiceUuid,
invalidCharacteristicUuid,
invalidOffset,
invalidAttributeLength,
invalidPdu,
invalidHandle,
// Operation errors
readFailed,
readNotPermitted,
writeFailed,
writeNotPermitted,
writeRequestBusy,
invalidAction,
operationNotSupported,
operationTimeout,
operationCancelled,
operationInProgress,
// Characteristic property errors
characteristicDoesNotSupportRead,
characteristicDoesNotSupportWrite,
characteristicDoesNotSupportWriteWithoutResponse,
characteristicDoesNotSupportNotify,
characteristicDoesNotSupportIndicate,
// Pairing errors
notPaired,
notPairable,
alreadyPaired,
pairingFailed,
pairingCancelled,
pairingTimeout,
pairingNotAllowed,
authenticationFailure,
insufficientAuthentication,
insufficientAuthorization,
insufficientEncryption,
insufficientKeySize,
protectionLevelNotMet,
accessDenied,
// Unpairing errors
unpairingFailed,
alreadyUnpaired,
// Scan errors
scanFailed,
stoppingScanInProgress,
// Web-specific errors
webBluetoothGloballyDisabled,
}
+2 -2
View File
@@ -1,6 +1,6 @@
name: universal_ble
description: A cross-platform (Android/iOS/macOS/Windows/Linux/Web) Bluetooth Low Energy (BLE) plugin for Flutter
version: 0.21.1
version: 1.0.0
homepage: https://navideck.com
repository: https://github.com/Navideck/universal_ble
issue_tracker: https://github.com/Navideck/universal_ble/issues
@@ -38,7 +38,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
pigeon: ^22.4.0
pigeon: ^26.0.5
flutter:
plugin:
+6 -2
View File
@@ -1,6 +1,10 @@
#pragma once
#include <string>
#include <optional>
#include "helper/universal_enum.h"
#include "helper/universal_ble_base.h"
#include "generated/universal_ble.g.h"
namespace universal_ble
{
@@ -46,9 +50,9 @@ namespace universal_ble
return std::nullopt;
}
inline std::optional<std::string> parse_pairing_fail_error(const DevicePairingResult& result)
inline std::optional<std::string> device_pairing_result_to_string(const DevicePairingResultStatus result)
{
switch (result.Status())
switch (result)
{
case DevicePairingResultStatus::Paired: return std::nullopt;
case DevicePairingResultStatus::AlreadyPaired: return "AlreadyPaired";
+22 -12
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v26.0.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#undef _HAS_EXCEPTIONS
@@ -425,21 +425,26 @@ EncodableValue PigeonInternalCodecSerializer::ReadValueOfType(
flutter::ByteStreamReader* stream) const {
switch (type) {
case 129: {
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 130: {
return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
return CustomEncodableValue(UniversalBleScanResult::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
case 131: {
return CustomEncodableValue(UniversalBleCharacteristic::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
return CustomEncodableValue(UniversalBleService::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
case 132: {
return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
return CustomEncodableValue(UniversalBleCharacteristic::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
case 133: {
return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
return CustomEncodableValue(UniversalScanFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
case 134: {
return CustomEncodableValue(UniversalManufacturerDataFilter::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
case 135: {
return CustomEncodableValue(UniversalManufacturerData::FromEncodableList(std::get<EncodableList>(ReadValue(stream))));
}
default:
@@ -451,33 +456,38 @@ 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(UniversalBleScanResult)) {
if (custom_value->type() == typeid(UniversalBleErrorCode)) {
stream->WriteByte(129);
WriteValue(EncodableValue(static_cast<int>(std::any_cast<UniversalBleErrorCode>(*custom_value))), stream);
return;
}
if (custom_value->type() == typeid(UniversalBleScanResult)) {
stream->WriteByte(130);
WriteValue(EncodableValue(std::any_cast<UniversalBleScanResult>(*custom_value).ToEncodableList()), stream);
return;
}
if (custom_value->type() == typeid(UniversalBleService)) {
stream->WriteByte(130);
stream->WriteByte(131);
WriteValue(EncodableValue(std::any_cast<UniversalBleService>(*custom_value).ToEncodableList()), stream);
return;
}
if (custom_value->type() == typeid(UniversalBleCharacteristic)) {
stream->WriteByte(131);
stream->WriteByte(132);
WriteValue(EncodableValue(std::any_cast<UniversalBleCharacteristic>(*custom_value).ToEncodableList()), stream);
return;
}
if (custom_value->type() == typeid(UniversalScanFilter)) {
stream->WriteByte(132);
stream->WriteByte(133);
WriteValue(EncodableValue(std::any_cast<UniversalScanFilter>(*custom_value).ToEncodableList()), stream);
return;
}
if (custom_value->type() == typeid(UniversalManufacturerDataFilter)) {
stream->WriteByte(133);
stream->WriteByte(134);
WriteValue(EncodableValue(std::any_cast<UniversalManufacturerDataFilter>(*custom_value).ToEncodableList()), stream);
return;
}
if (custom_value->type() == typeid(UniversalManufacturerData)) {
stream->WriteByte(134);
stream->WriteByte(135);
WriteValue(EncodableValue(std::any_cast<UniversalManufacturerData>(*custom_value).ToEncodableList()), stream);
return;
}
+66 -18
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.6.1), do not edit directly.
// Autogenerated from Pigeon (v26.0.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
#ifndef PIGEON_UNIVERSAL_BLE_G_H_
@@ -57,6 +57,71 @@ template<class T> class ErrorOr {
};
// Unified error codes for all platforms
enum class UniversalBleErrorCode {
kUnknownError = 0,
kFailed = 1,
kNotSupported = 2,
kNotImplemented = 3,
kChannelError = 4,
kBluetoothNotAvailable = 5,
kBluetoothNotEnabled = 6,
kBluetoothNotAllowed = 7,
kBluetoothUnauthorized = 8,
kDeviceDisconnected = 9,
kConnectionTimeout = 10,
kConnectionFailed = 11,
kConnectionRejected = 12,
kConnectionLimitExceeded = 13,
kConnectionAlreadyExists = 14,
kConnectionTerminated = 15,
kConnectionInProgress = 16,
kIllegalArgument = 17,
kDeviceNotFound = 18,
kServiceNotFound = 19,
kCharacteristicNotFound = 20,
kInvalidServiceUuid = 21,
kInvalidCharacteristicUuid = 22,
kInvalidOffset = 23,
kInvalidAttributeLength = 24,
kInvalidPdu = 25,
kInvalidHandle = 26,
kReadFailed = 27,
kReadNotPermitted = 28,
kWriteFailed = 29,
kWriteNotPermitted = 30,
kWriteRequestBusy = 31,
kInvalidAction = 32,
kOperationNotSupported = 33,
kOperationTimeout = 34,
kOperationCancelled = 35,
kOperationInProgress = 36,
kCharacteristicDoesNotSupportRead = 37,
kCharacteristicDoesNotSupportWrite = 38,
kCharacteristicDoesNotSupportWriteWithoutResponse = 39,
kCharacteristicDoesNotSupportNotify = 40,
kCharacteristicDoesNotSupportIndicate = 41,
kNotPaired = 42,
kNotPairable = 43,
kAlreadyPaired = 44,
kPairingFailed = 45,
kPairingCancelled = 46,
kPairingTimeout = 47,
kPairingNotAllowed = 48,
kAuthenticationFailure = 49,
kInsufficientAuthentication = 50,
kInsufficientAuthorization = 51,
kInsufficientEncryption = 52,
kInsufficientKeySize = 53,
kProtectionLevelNotMet = 54,
kAccessDenied = 55,
kUnpairingFailed = 56,
kAlreadyUnpaired = 57,
kScanFailed = 58,
kStoppingScanInProgress = 59,
kWebBluetoothGloballyDisabled = 60
};
// Generated class from Pigeon that represents data sent in messages.
class UniversalBleScanResult {
@@ -96,7 +161,6 @@ class UniversalBleScanResult {
void set_services(const flutter::EncodableList* value_arg);
void set_services(const flutter::EncodableList& value_arg);
private:
static UniversalBleScanResult FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
@@ -109,7 +173,6 @@ class UniversalBleScanResult {
std::optional<int64_t> rssi_;
std::optional<flutter::EncodableList> manufacturer_data_list_;
std::optional<flutter::EncodableList> services_;
};
@@ -131,7 +194,6 @@ class UniversalBleService {
void set_characteristics(const flutter::EncodableList* value_arg);
void set_characteristics(const flutter::EncodableList& value_arg);
private:
static UniversalBleService FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
@@ -140,7 +202,6 @@ class UniversalBleService {
friend class PigeonInternalCodecSerializer;
std::string uuid_;
std::optional<flutter::EncodableList> characteristics_;
};
@@ -158,7 +219,6 @@ class UniversalBleCharacteristic {
const flutter::EncodableList& properties() const;
void set_properties(const flutter::EncodableList& value_arg);
private:
static UniversalBleCharacteristic FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
@@ -167,7 +227,6 @@ class UniversalBleCharacteristic {
friend class PigeonInternalCodecSerializer;
std::string uuid_;
flutter::EncodableList properties_;
};
@@ -191,7 +250,6 @@ class UniversalScanFilter {
const flutter::EncodableList& with_manufacturer_data() const;
void set_with_manufacturer_data(const flutter::EncodableList& value_arg);
private:
static UniversalScanFilter FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
@@ -201,7 +259,6 @@ class UniversalScanFilter {
flutter::EncodableList with_services_;
flutter::EncodableList with_name_prefix_;
flutter::EncodableList with_manufacturer_data_;
};
@@ -228,7 +285,6 @@ class UniversalManufacturerDataFilter {
void set_mask(const std::vector<uint8_t>* value_arg);
void set_mask(const std::vector<uint8_t>& value_arg);
private:
static UniversalManufacturerDataFilter FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
@@ -238,7 +294,6 @@ class UniversalManufacturerDataFilter {
int64_t company_identifier_;
std::optional<std::vector<uint8_t>> data_;
std::optional<std::vector<uint8_t>> mask_;
};
@@ -256,7 +311,6 @@ class UniversalManufacturerData {
const std::vector<uint8_t>& data() const;
void set_data(const std::vector<uint8_t>& value_arg);
private:
static UniversalManufacturerData FromEncodableList(const flutter::EncodableList& list);
flutter::EncodableList ToEncodableList() const;
@@ -265,7 +319,6 @@ class UniversalManufacturerData {
friend class PigeonInternalCodecSerializer;
int64_t company_identifier_;
std::vector<uint8_t> data_;
};
@@ -280,12 +333,10 @@ class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer {
void WriteValue(
const flutter::EncodableValue& value,
flutter::ByteStreamWriter* stream) const override;
protected:
flutter::EncodableValue ReadValueOfType(
uint8_t type,
flutter::ByteStreamReader* stream) const override;
};
// Flutter -> Native
@@ -352,10 +403,8 @@ class UniversalBlePlatformChannel {
const std::string& message_channel_suffix);
static flutter::EncodableValue WrapError(std::string_view error_message);
static flutter::EncodableValue WrapError(const FlutterError& error);
protected:
UniversalBlePlatformChannel() = default;
};
// Native -> Flutter
//
@@ -393,7 +442,6 @@ class UniversalBleCallbackChannel {
const std::string* error,
std::function<void(void)>&& on_success,
std::function<void(const FlutterError&)>&& on_error);
private:
flutter::BinaryMessenger* binary_messenger_;
std::string message_channel_suffix_;
+142 -1
View File
@@ -1,8 +1,11 @@
#include "Utils.h"
#include "utils.h"
#include "../generated/universal_ble.g.h"
#include "../enum_parser.h"
#include <iomanip>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <windows.h>
#include <stdio.h>
#include <sdkddkver.h>
@@ -158,4 +161,142 @@ namespace universal_ble
return rove.dwMajorVersion == 10 && rove.dwBuildNumber >= 22000;
}
FlutterError create_flutter_error(
UniversalBleErrorCode code,
const std::string& message,
const std::string& details
)
{
// Pass the enum's underlying integer value as string in code, and enum name or details in details
std::string code_str = std::to_string(static_cast<int>(code));
std::string details_str = details.empty() ? std::to_string(static_cast<int>(code)) : details;
return FlutterError(code_str, message, details_str);
}
FlutterError create_flutter_error_from_gatt_communication_status(
GattCommunicationStatus status,
const std::string& message
)
{
if (status == GattCommunicationStatus::Success) {
// Success case - shouldn't normally create an error, but if called, return unknown
return create_flutter_error(UniversalBleErrorCode::kUnknownError, message);
}
UniversalBleErrorCode error_code;
switch (status)
{
case GattCommunicationStatus::Unreachable:
case GattCommunicationStatus::ProtocolError:
case GattCommunicationStatus::AccessDenied:
default:
error_code = UniversalBleErrorCode::kFailed;
break;
}
auto error_message = gatt_communication_status_to_error(status);
std::string final_message = message.empty() && error_message.has_value()
? error_message.value()
: message;
return create_flutter_error(error_code, final_message);
}
FlutterError create_flutter_error_from_pairing_status(
DevicePairingResultStatus status,
const std::string& message
)
{
if (status == DevicePairingResultStatus::Paired) {
// Success case - shouldn't normally create an error, but if called, return unknown
return create_flutter_error(UniversalBleErrorCode::kUnknownError, message);
}
UniversalBleErrorCode error_code;
switch (status)
{
case DevicePairingResultStatus::AlreadyPaired:
error_code = UniversalBleErrorCode::kAlreadyPaired;
break;
case DevicePairingResultStatus::ConnectionRejected:
error_code = UniversalBleErrorCode::kConnectionRejected;
break;
case DevicePairingResultStatus::NotPaired:
error_code = UniversalBleErrorCode::kNotPaired;
break;
case DevicePairingResultStatus::NotReadyToPair:
case DevicePairingResultStatus::TooManyConnections:
case DevicePairingResultStatus::HardwareFailure:
case DevicePairingResultStatus::NoSupportedProfiles:
case DevicePairingResultStatus::InvalidCeremonyData:
case DevicePairingResultStatus::RequiredHandlerNotRegistered:
case DevicePairingResultStatus::RejectedByHandler:
case DevicePairingResultStatus::RemoteDeviceHasAssociation:
error_code = UniversalBleErrorCode::kPairingFailed;
break;
case DevicePairingResultStatus::AuthenticationTimeout:
case DevicePairingResultStatus::AuthenticationNotAllowed:
case DevicePairingResultStatus::AuthenticationFailure:
error_code = UniversalBleErrorCode::kAuthenticationFailure;
break;
case DevicePairingResultStatus::ProtectionLevelCouldNotBeMet:
error_code = UniversalBleErrorCode::kProtectionLevelNotMet;
break;
case DevicePairingResultStatus::AccessDenied:
error_code = UniversalBleErrorCode::kAccessDenied;
break;
case DevicePairingResultStatus::PairingCanceled:
error_code = UniversalBleErrorCode::kPairingCancelled;
break;
case DevicePairingResultStatus::OperationAlreadyInProgress:
error_code = UniversalBleErrorCode::kOperationInProgress;
break;
default:
error_code = UniversalBleErrorCode::kPairingFailed;
break;
}
auto error_message = device_pairing_result_to_string(status);
std::string final_message = message.empty() && error_message.has_value()
? error_message.value()
: message;
return create_flutter_error(error_code, final_message);
}
FlutterError create_flutter_error_from_unpairing_status(
DeviceUnpairingResultStatus status,
const std::string& message
)
{
if (status == DeviceUnpairingResultStatus::Unpaired) {
// Success case - shouldn't normally create an error, but if called, return unknown
return create_flutter_error(UniversalBleErrorCode::kUnknownError, message);
}
UniversalBleErrorCode error_code;
switch (status)
{
case DeviceUnpairingResultStatus::Failed:
error_code = UniversalBleErrorCode::kUnpairingFailed;
break;
case DeviceUnpairingResultStatus::AlreadyUnpaired:
error_code = UniversalBleErrorCode::kAlreadyUnpaired;
break;
case DeviceUnpairingResultStatus::AccessDenied:
error_code = UniversalBleErrorCode::kAccessDenied;
break;
case DeviceUnpairingResultStatus::OperationAlreadyInProgress:
error_code = UniversalBleErrorCode::kOperationInProgress;
break;
default:
error_code = UniversalBleErrorCode::kUnpairingFailed;
break;
}
auto error_message = device_unpairing_result_to_string(status);
std::string final_message = message.empty() && error_message.has_value()
? error_message.value()
: message;
return create_flutter_error(error_code, final_message);
}
} // namespace universal_ble
+38 -2
View File
@@ -6,8 +6,10 @@
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.Storage.Streams.h"
#include "winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h"
#include "winrt/base.h"
#include "universal_ble_base.h"
#include "../generated/universal_ble.g.h"
constexpr uint32_t TEN_SECONDS_IN_MSECS = 10000;
@@ -28,6 +30,40 @@ namespace universal_ble
bool is_little_endian();
bool is_windows11_or_greater();
/// Creates a FlutterError with the error code enum in details
FlutterError create_flutter_error(
UniversalBleErrorCode code,
const std::string& message = "",
const std::string& details = ""
);
// Error creation functions
/// Creates a FlutterError from GATT communication status
FlutterError create_flutter_error_from_gatt_communication_status(
GattCommunicationStatus status,
const std::string& message = ""
);
/// Creates a FlutterError from device pairing result status
FlutterError create_flutter_error_from_pairing_status(
DevicePairingResultStatus status,
const std::string& message = ""
);
/// Creates a FlutterError from device unpairing result status
FlutterError create_flutter_error_from_unpairing_status(
DeviceUnpairingResultStatus status,
const std::string& message = ""
);
// Create Flutter unknown error
inline FlutterError create_flutter_unknown_error(
const std::string& message = "Unknown error"
) {
return create_flutter_error(UniversalBleErrorCode::kUnknownError, message);
}
/// To call async functions synchronously
template <typename AsyncT>
static auto async_get(AsyncT const &async)
@@ -42,11 +78,11 @@ namespace universal_ble
}
catch (const hresult_error &err)
{
throw FlutterError("Failed", to_string(err.message()));
throw create_flutter_error(UniversalBleErrorCode::kFailed, to_string(err.message()));
}
catch (...)
{
throw FlutterError("Failed", "Unknown error");
throw create_flutter_unknown_error();
}
}
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -61,11 +61,11 @@ namespace universal_ble
{
if (gatt_map.count(service_uuid) == 0)
{
throw FlutterError("IllegalArgument", "Service not found");
throw create_flutter_error(UniversalBleErrorCode::kServiceNotFound, "Service not found");
}
if (gatt_map[service_uuid].characteristics.count(characteristic_uuid) == 0)
{
throw FlutterError("IllegalArgument", "Characteristic not found");
throw create_flutter_error(UniversalBleErrorCode::kCharacteristicNotFound, "Characteristic not found");
}
return gatt_map[service_uuid].characteristics.at(characteristic_uuid);
}