diff --git a/example/lib/home/home.dart b/example/lib/home/home.dart index 6d25158..562730a 100644 --- a/example/lib/home/home.dart +++ b/example/lib/home/home.dart @@ -62,7 +62,7 @@ class _MyAppState extends State { }; UniversalBle.onScanResult = (result) { - debugPrint("${result.name} ${result.manufacturerData}"); + // debugPrint("${result.name} ${result.manufacturerData}"); int index = _scanResults.indexWhere((e) => e.deviceId == result.deviceId); if (index == -1) { _scanResults.add(result); diff --git a/example/pubspec.lock b/example/pubspec.lock index 580ae8c..ce7ee5d 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -386,7 +386,7 @@ packages: path: ".." relative: true source: path - version: "0.9.1" + version: "0.9.2" vector_math: dependency: transitive description: diff --git a/lib/src/universal_ble_linux/universal_ble_linux.dart b/lib/src/universal_ble_linux/universal_ble_linux.dart index 38819a6..ceed032 100644 --- a/lib/src/universal_ble_linux/universal_ble_linux.dart +++ b/lib/src/universal_ble_linux/universal_ble_linux.dart @@ -3,6 +3,7 @@ import 'dart:typed_data'; import 'package:bluez/bluez.dart'; import 'package:collection/collection.dart'; +import 'package:flutter/services.dart'; import 'package:universal_ble/src/models/model_exports.dart'; import 'package:universal_ble/src/universal_ble_platform_interface.dart'; @@ -179,9 +180,16 @@ class UniversalBleLinux extends UniversalBlePlatform { @override Future readValue( String deviceId, String service, String characteristic) async { - var c = _getCharacteristic(deviceId, service, characteristic); - var data = await c.readValue(); - return Uint8List.fromList(data); + try { + var c = _getCharacteristic(deviceId, service, characteristic); + var data = await c.readValue(); + return Uint8List.fromList(data); + } on BlueZFailedException catch (e) { + throw PlatformException( + code: e.errorCode ?? "ReadFailed", + message: e.message, + ); + } } @override @@ -191,16 +199,23 @@ class UniversalBleLinux extends UniversalBlePlatform { String characteristic, Uint8List value, BleOutputProperty bleOutputProperty) async { - var c = _getCharacteristic(deviceId, service, characteristic); - if (bleOutputProperty == BleOutputProperty.withResponse) { - await c.writeValue( - value, - type: BlueZGattCharacteristicWriteType.request, - ); - } else { - await c.writeValue( - value, - type: BlueZGattCharacteristicWriteType.command, + try { + var c = _getCharacteristic(deviceId, service, characteristic); + if (bleOutputProperty == BleOutputProperty.withResponse) { + await c.writeValue( + value, + type: BlueZGattCharacteristicWriteType.request, + ); + } else { + await c.writeValue( + value, + type: BlueZGattCharacteristicWriteType.command, + ); + } + } on BlueZFailedException catch (e) { + throw PlatformException( + code: e.errorCode ?? "WriteFailed", + message: e.message, ); } } @@ -486,3 +501,23 @@ extension on BlueZGattCharacteristicFlag { }; } } + +extension on BlueZFailedException { + /// Extract error code from message and parse into decimal + /// example: 'Operation failed with ATT error: 0x90' => 144 + String? get errorCode { + try { + RegExp regExp = RegExp(r'0x\w+'); + Match? match = regExp.firstMatch(message); + String? code = match?.group(0); + if (code == null) return null; + int? decimalValue = int.tryParse( + code.replaceFirst('0x', ''), + radix: 16, + ); + return decimalValue?.toString() ?? code; + } catch (e) { + return null; + } + } +} diff --git a/windows/src/universal_ble_plugin.cpp b/windows/src/universal_ble_plugin.cpp index 71556b5..d586b96 100644 --- a/windows/src/universal_ble_plugin.cpp +++ b/windows/src/universal_ble_plugin.cpp @@ -322,28 +322,8 @@ namespace universal_ble return; } } - auto async_c = gatt_characteristic_holder.obj.WriteValueAsync(from_bytevc(value), writeOption); - async_c.Completed([&, result](IAsyncOperation const &sender, AsyncStatus const args) - { - auto writeResult = sender.GetResults(); - switch (writeResult) - { - case GenericAttributeProfile::GattCommunicationStatus::Success: - result(std::nullopt); - return; - case GenericAttributeProfile::GattCommunicationStatus::Unreachable: - result(FlutterError("Unreachable", "Failed to write value")); - return; - case GenericAttributeProfile::GattCommunicationStatus::ProtocolError: - result(FlutterError("ProtocolError", "Failed to write value")); - return; - case GenericAttributeProfile::GattCommunicationStatus::AccessDenied: - result(FlutterError("AccessDenied", "Failed to write value")); - return; - default: - result(FlutterError("Failed", "Failed to write value")); - return; - } }); + + WriteAsync(gatt_characteristic_holder.obj, writeOption, value, result); } catch (const FlutterError &err) { @@ -774,6 +754,47 @@ namespace universal_ble } } + winrt::fire_and_forget UniversalBlePlugin::WriteAsync(GattCharacteristic characteristic, GattWriteOption writeOption, + const std::vector &value, std::function reply)> result) + { + try + { + auto writeResult = co_await characteristic.WriteValueAsync(from_bytevc(value), writeOption); + switch (writeResult) + { + case GenericAttributeProfile::GattCommunicationStatus::Success: + result(std::nullopt); + co_return; + case GenericAttributeProfile::GattCommunicationStatus::Unreachable: + result(FlutterError("Unreachable", "Failed to write value")); + co_return; + case GenericAttributeProfile::GattCommunicationStatus::ProtocolError: + result(FlutterError("ProtocolError", "Failed to write value")); + co_return; + case GenericAttributeProfile::GattCommunicationStatus::AccessDenied: + result(FlutterError("AccessDenied", "Failed to write value")); + co_return; + default: + result(FlutterError("Failed", "Failed to write value")); + co_return; + } + } + catch (const winrt::hresult_error &err) + { + int errorCode = err.code(); + std::cout << "WriteValueLog: " << winrt::to_string(err.message()) << " ErrorCode: " << std::to_string(errorCode) << std::endl; + result(FlutterError(std::to_string(errorCode), winrt::to_string(err.message()))); + } + catch (const std::exception &err) + { + result(FlutterError("WriteFailed", err.what())); + } + catch (...) + { + result(FlutterError("WriteFailed", "Unknown error")); + } + } + winrt::fire_and_forget UniversalBlePlugin::ConnectAsync(uint64_t bluetoothAddress) { BluetoothLEDevice device = co_await BluetoothLEDevice::FromBluetoothAddressAsync(bluetoothAddress); @@ -883,37 +904,48 @@ namespace universal_ble flutter::EncodableList results = flutter::EncodableList(); for (auto &&deviceInfo : devices) { - BluetoothLEDevice device = co_await BluetoothLEDevice::FromIdAsync(deviceInfo.Id()); - auto deviceId = _mac_address_to_str(device.BluetoothAddress()); - // Filter by services - if (!with_services.empty()) - { - auto serviceResult = co_await device.GetGattServicesAsync(BluetoothCacheMode::Cached); - if (serviceResult.Status() == GattCommunicationStatus::Success) + try{ + BluetoothLEDevice device = co_await BluetoothLEDevice::FromIdAsync(deviceInfo.Id()); + auto deviceId = _mac_address_to_str(device.BluetoothAddress()); + // Filter by services + if (!with_services.empty()) { - bool hasService = false; - for (auto service : serviceResult.Services()) + auto serviceResult = co_await device.GetGattServicesAsync(BluetoothCacheMode::Cached); + if (serviceResult.Status() == GattCommunicationStatus::Success) { - std::string serviceUUID = to_uuidstr(service.Uuid()); - if (std::find(with_services.begin(), with_services.end(), serviceUUID) != with_services.end()) + bool hasService = false; + for (auto service : serviceResult.Services()) { - hasService = true; - break; + std::string serviceUUID = to_uuidstr(service.Uuid()); + if (std::find(with_services.begin(), with_services.end(), serviceUUID) != with_services.end()) + { + hasService = true; + break; + } } + if (!hasService) + continue; } - if (!hasService) - continue; } - } - // Add to results, if pass all filters - auto universalScanResult = UniversalBleScanResult(deviceId); - universalScanResult.set_name(winrt::to_string(deviceInfo.Name())); - universalScanResult.set_is_paired(deviceInfo.Pairing().IsPaired()); - results.push_back(flutter::CustomEncodableValue(universalScanResult)); - // deviceConnectableStatus[_str_to_mac_address(deviceId)] = true; + // Add to results, if pass all filters + auto universalScanResult = UniversalBleScanResult(deviceId); + universalScanResult.set_name(winrt::to_string(deviceInfo.Name())); + universalScanResult.set_is_paired(deviceInfo.Pairing().IsPaired()); + results.push_back(flutter::CustomEncodableValue(universalScanResult)); + }catch(...){ } } result(results); } + catch (const winrt::hresult_error &err) + { + int errorCode = err.code(); + std::cout << "GetConnectedDeviceLog: " << winrt::to_string(err.message()) << " ErrorCode: " << std::to_string(errorCode) << std::endl; + result(FlutterError(std::to_string(errorCode), winrt::to_string(err.message()))); + } + catch (const std::exception &err) + { + result(FlutterError(err.what())); + } catch (...) { std::cout << "Unknown error GetConnectedDevicesAsync" << std::endl; diff --git a/windows/src/universal_ble_plugin.h b/windows/src/universal_ble_plugin.h index 37a1515..201558f 100644 --- a/windows/src/universal_ble_plugin.h +++ b/windows/src/universal_ble_plugin.h @@ -120,6 +120,9 @@ namespace universal_ble winrt::fire_and_forget GetConnectedDevicesAsync(std::vector with_services, std::function reply)> result); winrt::fire_and_forget IsPairedAsync(std::string device_id, std::function reply)> result); + winrt::fire_and_forget WriteAsync(GattCharacteristic characteristic, GattWriteOption writeOption, + const std::vector &value, + std::function reply)> result); // UniversalBlePlatformChannel implementation. void GetBluetoothAvailabilityState(std::function reply)> result) override;