Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve windows and linux error handling #12

Merged
merged 4 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/lib/home/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class _MyAppState extends State<MyApp> {
};

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);
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.9.1"
version: "0.9.2"
vector_math:
dependency: transitive
description:
Expand Down
61 changes: 48 additions & 13 deletions lib/src/universal_ble_linux/universal_ble_linux.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -179,9 +180,16 @@ class UniversalBleLinux extends UniversalBlePlatform {
@override
Future<Uint8List> 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
Expand All @@ -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,
);
}
}
Expand Down Expand Up @@ -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;
}
}
}
120 changes: 76 additions & 44 deletions windows/src/universal_ble_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<GattCommunicationStatus> 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)
{
Expand Down Expand Up @@ -774,6 +754,47 @@ namespace universal_ble
}
}

winrt::fire_and_forget UniversalBlePlugin::WriteAsync(GattCharacteristic characteristic, GattWriteOption writeOption,
const std::vector<uint8_t> &value, std::function<void(std::optional<FlutterError> 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);
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions windows/src/universal_ble_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ namespace universal_ble
winrt::fire_and_forget GetConnectedDevicesAsync(std::vector<std::string> with_services,
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);
winrt::fire_and_forget IsPairedAsync(std::string device_id, std::function<void(ErrorOr<bool> reply)> result);
winrt::fire_and_forget WriteAsync(GattCharacteristic characteristic, GattWriteOption writeOption,
const std::vector<uint8_t> &value,
std::function<void(std::optional<FlutterError> reply)> result);

// UniversalBlePlatformChannel implementation.
void GetBluetoothAvailabilityState(std::function<void(ErrorOr<int64_t> reply)> result) override;
Expand Down