Add error in connection updates (#89)
* Add error in connection updates * implement windows * Update changelog and Docs * minor fix in connect() * Add default timeout in connect * Document new pair and connect API * Fix Connect and Pair apis to throw proper errors and Improve logging * remove import * Rename PairingException and update Exception doc * Update changelog * Update comments --------- Co-authored-by: Foti Dim <fdimanidis@gmail.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
class BleConnectionUpdate {
|
||||
final bool isConnected;
|
||||
final String? error;
|
||||
|
||||
BleConnectionUpdate({
|
||||
required this.isConnected,
|
||||
this.error,
|
||||
});
|
||||
}
|
||||
@@ -7,6 +7,11 @@ class BleService {
|
||||
BleService(String uuid, this.characteristics) {
|
||||
this.uuid = BleUuidParser.string(uuid);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BleService{uuid: $uuid, characteristics: $characteristics}';
|
||||
}
|
||||
}
|
||||
|
||||
class BleCharacteristic {
|
||||
@@ -16,6 +21,11 @@ class BleCharacteristic {
|
||||
BleCharacteristic(String uuid, this.properties) {
|
||||
this.uuid = BleUuidParser.string(uuid);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'BleCharacteristic{uuid: $uuid, properties: $properties}';
|
||||
}
|
||||
}
|
||||
|
||||
enum CharacteristicProperty {
|
||||
@@ -32,4 +42,7 @@ enum CharacteristicProperty {
|
||||
|
||||
factory CharacteristicProperty.parse(int index) =>
|
||||
CharacteristicProperty.values[index];
|
||||
|
||||
@override
|
||||
String toString() => name;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export 'package:universal_ble/src/models/ble_connection_update.dart';
|
||||
export 'package:universal_ble/src/models/manufacturer_data.dart';
|
||||
export 'package:universal_ble/src/models/platform_config.dart';
|
||||
export 'package:universal_ble/src/models/queue_type.dart';
|
||||
|
||||
+114
-75
@@ -5,6 +5,7 @@ import 'package:universal_ble/src/ble_command_queue.dart';
|
||||
import 'package:universal_ble/src/universal_ble_linux/universal_ble_linux.dart';
|
||||
import 'package:universal_ble/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart';
|
||||
import 'package:universal_ble/src/universal_ble_web/universal_ble_web.dart';
|
||||
import 'package:universal_ble/src/universal_logger.dart';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
class UniversalBle {
|
||||
@@ -31,7 +32,7 @@ class UniversalBle {
|
||||
/// [QueueType.none] will execute all commands in parallel.
|
||||
static set queueType(QueueType queueType) {
|
||||
_bleCommandQueue.queueType = queueType;
|
||||
UniversalBlePlatform.logInfo('Queue ${queueType.name}');
|
||||
UniversalLogger.logInfo('Queue ${queueType.name}');
|
||||
}
|
||||
|
||||
/// Get Bluetooth availability state.
|
||||
@@ -70,28 +71,37 @@ class UniversalBle {
|
||||
}
|
||||
|
||||
/// Connection stream of a device
|
||||
Stream<bool> connectionStream(String deviceId) =>
|
||||
static Stream<BleConnectionUpdate> connectionStream(String deviceId) =>
|
||||
_platform.connectionStream(deviceId);
|
||||
|
||||
/// Connect to a device.
|
||||
/// It is advised to stop scanning before connecting.
|
||||
/// It might throw errors if device is not connectable.
|
||||
/// `connectionTimeout` is supported on Web only.
|
||||
static Future<bool> connect(
|
||||
/// It throws error if device connection fails.
|
||||
/// Default connection timeout is 60 sec.
|
||||
/// Can throw `ConnectionException` or `PlatformException`.
|
||||
static Future<void> connect(
|
||||
String deviceId, {
|
||||
Duration? connectionTimeout,
|
||||
}) async {
|
||||
connectionTimeout ??= const Duration(seconds: 60);
|
||||
StreamSubscription? connectionSubscription;
|
||||
|
||||
try {
|
||||
Completer<bool> completer = Completer();
|
||||
|
||||
connectionSubscription =
|
||||
_platform.connectionStream(deviceId).listen((bool event) {
|
||||
connectionSubscription?.cancel();
|
||||
if (!completer.isCompleted) {
|
||||
completer.complete(event);
|
||||
}
|
||||
});
|
||||
connectionSubscription = connectionStream(deviceId).listen(
|
||||
(BleConnectionUpdate event) {
|
||||
connectionSubscription?.cancel();
|
||||
if (!completer.isCompleted) {
|
||||
String? error = event.error;
|
||||
if (error != null) {
|
||||
completer.completeError(ConnectionException(error));
|
||||
} else {
|
||||
completer.complete(event.isConnected);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
_platform
|
||||
.connect(deviceId, connectionTimeout: connectionTimeout)
|
||||
@@ -99,15 +109,14 @@ class UniversalBle {
|
||||
(error) {
|
||||
if (completer.isCompleted == false) {
|
||||
connectionSubscription?.cancel();
|
||||
completer.completeError(error);
|
||||
completer.completeError(ConnectionException(error));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (connectionTimeout != null) {
|
||||
return await completer.future.timeout(connectionTimeout);
|
||||
if (!await completer.future.timeout(connectionTimeout)) {
|
||||
throw ConnectionException("Failed to connect");
|
||||
}
|
||||
return await completer.future;
|
||||
} finally {
|
||||
connectionSubscription?.cancel();
|
||||
}
|
||||
@@ -211,36 +220,65 @@ class UniversalBle {
|
||||
static Future<bool?> isPaired(
|
||||
String deviceId, {
|
||||
BleCommand? pairingCommand,
|
||||
Duration? connectionTimeout,
|
||||
}) async {
|
||||
if (BleCapabilities.hasSystemPairingApi) {
|
||||
return _bleCommandQueue.queueCommand(
|
||||
() => _platform.isPaired(deviceId),
|
||||
deviceId: deviceId,
|
||||
);
|
||||
} else if (pairingCommand != null) {
|
||||
return _connectAndExecuteBleCommand(deviceId, pairingCommand,
|
||||
updateCallbackValue: false);
|
||||
}
|
||||
return null;
|
||||
|
||||
if (pairingCommand == null) {
|
||||
UniversalLogger.logWarning("PairingCommand required to get result");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
await _connectAndExecuteBleCommand(
|
||||
deviceId,
|
||||
pairingCommand,
|
||||
connectionTimeout: connectionTimeout,
|
||||
updateCallbackValue: false,
|
||||
);
|
||||
|
||||
// Because pairingCommand will be never null, so we wont get Unknown result here
|
||||
return true;
|
||||
} catch (e) {
|
||||
UniversalLogger.logError("ExecuteBleCommandFailed: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Pair a device.
|
||||
///
|
||||
/// It throws error if pairing fails.
|
||||
///
|
||||
/// On `Apple` and `Web`, it only works on devices with encrypted characteristics.
|
||||
/// It returns null if there is no readable characteristic.
|
||||
/// It is advised to pass a pairingCommand with an encrypted read or write characteristic.
|
||||
/// When not passing a pairingCommand, you should afterwards use [isPaired] with a pairingCommand
|
||||
/// to verify the pairing state.
|
||||
///
|
||||
/// You can optionally pass a pairingCommand if you know an encrypted read or write characteristic.
|
||||
/// If you do, it returns true if it can successfully execute the command after pairing.
|
||||
///
|
||||
/// On `Web/Windows` and `Web/Linux`, it does not work for devices where `BleCapabilities.triggersConfirmOnlyPairing` is false.
|
||||
static Future<bool?> pair(
|
||||
/// On `Web/Windows` and `Web/Linux`, it does not work for devices that use `ConfirmOnly` pairing.
|
||||
/// Can throw `PairingException`, `ConnectionException` or `PlatformException`.
|
||||
static Future<void> pair(
|
||||
String deviceId, {
|
||||
BleCommand? pairingCommand,
|
||||
Duration? connectionTimeout,
|
||||
}) async {
|
||||
if (BleCapabilities.hasSystemPairingApi) {
|
||||
return _platform.pair(deviceId);
|
||||
bool paired = await _platform.pair(deviceId);
|
||||
if (!paired) throw PairingException();
|
||||
} else {
|
||||
if (pairingCommand == null) {
|
||||
UniversalLogger.logWarning("PairingCommand required to get result");
|
||||
}
|
||||
await _connectAndExecuteBleCommand(
|
||||
deviceId,
|
||||
pairingCommand,
|
||||
connectionTimeout: connectionTimeout,
|
||||
);
|
||||
}
|
||||
return _connectAndExecuteBleCommand(deviceId, pairingCommand);
|
||||
}
|
||||
|
||||
/// Unpair a device.
|
||||
@@ -303,38 +341,32 @@ class UniversalBle {
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool?> _connectAndExecuteBleCommand(
|
||||
static Future<void> _connectAndExecuteBleCommand(
|
||||
String deviceId,
|
||||
BleCommand? bleCommand, {
|
||||
Duration? connectionTimeout,
|
||||
bool updateCallbackValue = false,
|
||||
}) async {
|
||||
try {
|
||||
if (await getConnectionState(deviceId) != BleConnectionState.connected) {
|
||||
await connect(deviceId);
|
||||
}
|
||||
|
||||
List<BleService> services = await discoverServices(deviceId);
|
||||
|
||||
if (bleCommand == null) {
|
||||
await _attemptPairingReadingAll(deviceId, services);
|
||||
return null;
|
||||
} else {
|
||||
bool commandResult =
|
||||
await _executeBleCommand(deviceId, services, bleCommand);
|
||||
if (updateCallbackValue) {
|
||||
_platform.updatePairingState(deviceId, commandResult);
|
||||
}
|
||||
return commandResult;
|
||||
}
|
||||
} catch (e) {
|
||||
UniversalBlePlatform.logInfo(
|
||||
"FailedToPerform EncryptedCharOperation: $e",
|
||||
// Try to connect first
|
||||
if (await getConnectionState(deviceId) != BleConnectionState.connected) {
|
||||
UniversalLogger.logInfo("Connecting to $deviceId");
|
||||
await connect(
|
||||
deviceId,
|
||||
connectionTimeout: connectionTimeout,
|
||||
);
|
||||
if (updateCallbackValue) {
|
||||
_platform.updatePairingState(deviceId, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
List<BleService> services = await discoverServices(deviceId);
|
||||
UniversalLogger.logInfo("Discovered services: ${services.length}");
|
||||
|
||||
if (bleCommand == null) {
|
||||
// Just attempt pairing
|
||||
await _attemptPairingReadingAll(deviceId, services);
|
||||
return;
|
||||
}
|
||||
|
||||
await _executeBleCommand(deviceId, services, bleCommand);
|
||||
if (updateCallbackValue) _platform.updatePairingState(deviceId, true);
|
||||
}
|
||||
|
||||
// Fire and forget, and do not rely on result
|
||||
@@ -359,12 +391,13 @@ class UniversalBle {
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
if (!containsReadCharacteristics) {
|
||||
throw "No readable characteristic found";
|
||||
throw PairingException("No readable characteristic found");
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> _executeBleCommand(
|
||||
static Future<void> _executeBleCommand(
|
||||
String deviceId,
|
||||
List<BleService> services,
|
||||
BleCommand bleCommand,
|
||||
@@ -384,7 +417,7 @@ class UniversalBle {
|
||||
}
|
||||
|
||||
if (characteristic == null) {
|
||||
return false;
|
||||
throw PairingException("BleCommand not found in discovered services");
|
||||
}
|
||||
|
||||
// Check if BleCommand Supports Read or Write
|
||||
@@ -396,28 +429,34 @@ class UniversalBle {
|
||||
bleOutputProperty = BleOutputProperty.withoutResponse;
|
||||
} else if (!characteristic.properties
|
||||
.contains(CharacteristicProperty.read)) {
|
||||
return false;
|
||||
throw PairingException(
|
||||
"BleCommand does not support read or write operation",
|
||||
);
|
||||
}
|
||||
|
||||
Uint8List? value = bleCommand.writeValue;
|
||||
if (value != null && bleOutputProperty != null) {
|
||||
await writeValue(
|
||||
deviceId,
|
||||
bleCommand.service,
|
||||
bleCommand.characteristic,
|
||||
value,
|
||||
bleOutputProperty,
|
||||
);
|
||||
} else {
|
||||
// Fallback to read if supported
|
||||
await readValue(
|
||||
deviceId,
|
||||
bleCommand.service,
|
||||
bleCommand.characteristic,
|
||||
timeout: const Duration(seconds: 30),
|
||||
);
|
||||
|
||||
try {
|
||||
if (value != null && bleOutputProperty != null) {
|
||||
await writeValue(
|
||||
deviceId,
|
||||
bleCommand.service,
|
||||
bleCommand.characteristic,
|
||||
value,
|
||||
bleOutputProperty,
|
||||
);
|
||||
} else {
|
||||
// Fallback to read if supported
|
||||
await readValue(
|
||||
deviceId,
|
||||
bleCommand.service,
|
||||
bleCommand.characteristic,
|
||||
timeout: const Duration(seconds: 30),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
throw PairingException(e.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Get updates of remaining items of a queue.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class ConnectionException implements Exception {
|
||||
late String message;
|
||||
|
||||
ConnectionException([dynamic error]) {
|
||||
message = _errorParser(error);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
||||
|
||||
class PairingException implements Exception {
|
||||
late String message;
|
||||
|
||||
PairingException([dynamic error]) {
|
||||
message = _errorParser(error);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => message;
|
||||
}
|
||||
|
||||
String _errorParser(dynamic error) {
|
||||
if (error == null) {
|
||||
return "Failed";
|
||||
} else if (error is PlatformException) {
|
||||
return error.message ?? error.details ?? error.code;
|
||||
} else if (error is String) {
|
||||
return error;
|
||||
} else {
|
||||
return error.toString();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:universal_ble/src/models/model_exports.dart';
|
||||
import 'package:universal_ble/src/universal_ble_filter_util.dart';
|
||||
import 'package:universal_ble/src/universal_ble_platform_interface.dart';
|
||||
import 'package:universal_ble/src/universal_logger.dart';
|
||||
|
||||
class UniversalBleLinux extends UniversalBlePlatform {
|
||||
UniversalBleLinux._();
|
||||
@@ -45,10 +46,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
await _activeAdapter?.setPowered(true);
|
||||
return _activeAdapter?.powered ?? false;
|
||||
} catch (e) {
|
||||
UniversalBlePlatform.logInfo(
|
||||
'Error enabling bluetooth: $e',
|
||||
isError: true,
|
||||
);
|
||||
UniversalLogger.logError('Error enabling bluetooth: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -113,10 +111,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
return true;
|
||||
});
|
||||
} catch (e) {
|
||||
UniversalBlePlatform.logInfo(
|
||||
"stopScan error: $e",
|
||||
isError: true,
|
||||
);
|
||||
UniversalLogger.logError("stopScan error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +154,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
await device.propertiesChanged.firstWhere((element) {
|
||||
if (element.contains(BluezProperty.connected)) {
|
||||
if (!device.connected) {
|
||||
UniversalBlePlatform.logInfo(
|
||||
UniversalLogger.logInfo(
|
||||
"DiscoverServicesFailed: Device disconnected",
|
||||
);
|
||||
return true;
|
||||
@@ -167,7 +162,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
}
|
||||
return element.contains(BluezProperty.servicesResolved);
|
||||
}).timeout(const Duration(seconds: 10), onTimeout: () {
|
||||
UniversalBlePlatform.logInfo(
|
||||
UniversalLogger.logInfo(
|
||||
"DiscoverServicesFailed: Timeout",
|
||||
);
|
||||
return [];
|
||||
@@ -238,8 +233,9 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
);
|
||||
break;
|
||||
default:
|
||||
UniversalBlePlatform.logInfo(
|
||||
"UnhandledCharValuePropertyChange: $property");
|
||||
UniversalLogger.logInfo(
|
||||
"UnhandledCharValuePropertyChange: $property",
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -351,8 +347,9 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
.map((e) => e.uuid.toString())
|
||||
.any((service) => withServices.contains(service));
|
||||
} else {
|
||||
UniversalBlePlatform.logInfo(
|
||||
'Skipping: ${device.address}: Services not resolved yet.');
|
||||
UniversalLogger.logInfo(
|
||||
'Skipping: ${device.address}: Services not resolved yet.',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}).toList();
|
||||
@@ -394,7 +391,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
|
||||
_activeAdapter ??= _client.adapters.first;
|
||||
|
||||
UniversalBlePlatform.logInfo(
|
||||
UniversalLogger.logInfo(
|
||||
'BleAdapter: ${_activeAdapter?.name} - ${_activeAdapter?.address}',
|
||||
);
|
||||
|
||||
@@ -411,7 +408,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
break;
|
||||
case BluezProperty.propertyClass:
|
||||
default:
|
||||
UniversalBlePlatform.logInfo(
|
||||
UniversalLogger.logInfo(
|
||||
"UnhandledPropertyChanged: $property",
|
||||
);
|
||||
}
|
||||
@@ -426,10 +423,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
_initializationCompleter?.complete();
|
||||
_initializationCompleter = null;
|
||||
} catch (e) {
|
||||
UniversalBlePlatform.logInfo(
|
||||
'Error initializing: $e',
|
||||
isError: true,
|
||||
);
|
||||
UniversalLogger.logError('Error initializing: $e');
|
||||
_initializationCompleter?.completeError(e);
|
||||
await _client.close();
|
||||
rethrow;
|
||||
@@ -501,7 +495,7 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
||||
case BluezProperty.manufacturerData:
|
||||
break;
|
||||
default:
|
||||
UniversalBlePlatform.logInfo(
|
||||
UniversalLogger.logInfo(
|
||||
"UnhandledDevicePropertyChanged ${device.name} ${device.address}: $property",
|
||||
);
|
||||
break;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -119,10 +119,7 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
|
||||
) async {
|
||||
var devices = await _channel.getSystemDevices(withServices ?? []);
|
||||
return List<BleDevice>.from(
|
||||
devices
|
||||
.map((e) => e?.toBleDevice(isSystemDevice: true))
|
||||
.where((e) => e != null)
|
||||
.toList(),
|
||||
devices.map((e) => e.toBleDevice(isSystemDevice: true)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -175,8 +172,8 @@ class _UniversalBleCallbackHandler extends UniversalBleCallbackChannel {
|
||||
availabilityChange(AvailabilityState.parse(state));
|
||||
|
||||
@override
|
||||
void onConnectionChanged(String deviceId, bool connected) =>
|
||||
connectionChanged(deviceId, connected);
|
||||
void onConnectionChanged(String deviceId, bool connected, String? error) =>
|
||||
connectionChanged(deviceId, connected, error);
|
||||
|
||||
@override
|
||||
void onScanResult(UniversalBleScanResult result) =>
|
||||
@@ -200,9 +197,9 @@ extension _UniversalBleScanResultExtension on UniversalBleScanResult {
|
||||
rssi: rssi,
|
||||
isPaired: isPaired,
|
||||
isSystemDevice: isSystemDevice,
|
||||
services: services?.nonNulls.map(BleUuidParser.string).toList() ?? [],
|
||||
manufacturerDataList: manufacturerDataList?.nonNulls
|
||||
.map((e) => ManufacturerData(e.companyIdentifier, e.data))
|
||||
services: services?.map(BleUuidParser.string).toList() ?? [],
|
||||
manufacturerDataList: manufacturerDataList
|
||||
?.map((e) => ManufacturerData(e.companyIdentifier, e.data))
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:typed_data';
|
||||
import 'package:universal_ble/universal_ble.dart';
|
||||
|
||||
abstract class UniversalBlePlatform {
|
||||
StreamController? _connectionStreamController;
|
||||
StreamController<({String deviceId, bool isConnected, String? error})>?
|
||||
_connectionStreamController;
|
||||
|
||||
final Map<String, bool> _pairStateMap = {};
|
||||
|
||||
Future<AvailabilityState> getBluetoothAvailabilityState();
|
||||
@@ -57,21 +58,27 @@ abstract class UniversalBlePlatform {
|
||||
|
||||
bool receivesAdvertisements(String deviceId) => true;
|
||||
|
||||
Stream<bool> connectionStream(String deviceId) {
|
||||
Stream<BleConnectionUpdate> connectionStream(String deviceId) {
|
||||
_setupConnectionStreamIfRequired();
|
||||
return _connectionStreamController!.stream
|
||||
.where((event) => event.deviceId == deviceId)
|
||||
.map((event) => event.isConnected);
|
||||
.map((event) => BleConnectionUpdate(
|
||||
isConnected: event.isConnected,
|
||||
error: event.error,
|
||||
));
|
||||
}
|
||||
|
||||
void updateScanResult(BleDevice bleDevice) {
|
||||
onScanResult?.call(bleDevice);
|
||||
}
|
||||
|
||||
void updateConnection(String deviceId, bool isConnected) {
|
||||
onConnectionChange?.call(deviceId, isConnected);
|
||||
_connectionStreamController
|
||||
?.add((deviceId: deviceId, isConnected: isConnected));
|
||||
void updateConnection(String deviceId, bool isConnected, [String? error]) {
|
||||
onConnectionChange?.call(deviceId, isConnected, error);
|
||||
_connectionStreamController?.add((
|
||||
deviceId: deviceId,
|
||||
isConnected: isConnected,
|
||||
error: error,
|
||||
));
|
||||
}
|
||||
|
||||
void updateCharacteristicValue(
|
||||
@@ -97,17 +104,11 @@ abstract class UniversalBlePlatform {
|
||||
OnAvailabilityChange? onAvailabilityChange;
|
||||
OnPairingStateChange? onPairingStateChange;
|
||||
|
||||
static void logInfo(String message, {bool isError = false}) {
|
||||
if (isError) message = '\x1B[31m$message\x1B[31m';
|
||||
log(message, name: 'UniversalBle');
|
||||
}
|
||||
|
||||
/// Creates an auto disposable streamController
|
||||
void _setupConnectionStreamIfRequired() {
|
||||
if (_connectionStreamController != null) return;
|
||||
|
||||
_connectionStreamController =
|
||||
StreamController<({String deviceId, bool isConnected})>.broadcast();
|
||||
_connectionStreamController = StreamController.broadcast();
|
||||
|
||||
// Auto dispose if no more subscribers
|
||||
_connectionStreamController?.onCancel = () {
|
||||
@@ -119,7 +120,8 @@ abstract class UniversalBlePlatform {
|
||||
}
|
||||
|
||||
// Callback types
|
||||
typedef OnConnectionChange = void Function(String deviceId, bool isConnected);
|
||||
typedef OnConnectionChange = void Function(
|
||||
String deviceId, bool isConnected, String? error);
|
||||
|
||||
typedef OnValueChange = void Function(
|
||||
String deviceId, String characteristicId, Uint8List value);
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_web_bluetooth/flutter_web_bluetooth.dart';
|
||||
import 'package:universal_ble/src/models/model_exports.dart';
|
||||
import 'package:universal_ble/src/universal_ble_platform_interface.dart';
|
||||
import 'package:universal_ble/src/universal_logger.dart';
|
||||
|
||||
class UniversalBleWeb extends UniversalBlePlatform {
|
||||
static UniversalBleWeb? _instance;
|
||||
@@ -127,10 +128,7 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
||||
device.advertisementsUseMemory = true;
|
||||
await device.watchAdvertisements();
|
||||
} catch (e) {
|
||||
UniversalBlePlatform.logInfo(
|
||||
"WebWatchAdvertisementError: $e",
|
||||
isError: true,
|
||||
);
|
||||
UniversalLogger.logError("WebWatchAdvertisementError: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,9 +393,8 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
||||
}
|
||||
|
||||
if (optionalServices.isEmpty) {
|
||||
UniversalBlePlatform.logInfo(
|
||||
UniversalLogger.logError(
|
||||
"OptionalServices list is empty on web, you have to specify services in the ScanFilter in order to be able to access those after connecting",
|
||||
isError: true,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import 'dart:developer';
|
||||
|
||||
class UniversalLogger {
|
||||
static void logInfo(message) {
|
||||
log(
|
||||
message.toString(),
|
||||
name: 'UniversalBle:INFO',
|
||||
);
|
||||
}
|
||||
|
||||
static void logError(message) {
|
||||
log(
|
||||
'\x1B[31m$message\x1B[31m',
|
||||
name: 'UniversalBle:ERROR',
|
||||
);
|
||||
}
|
||||
|
||||
static void logWarning(message) {
|
||||
log(
|
||||
'\x1B[33m$message\x1B[33m',
|
||||
name: 'UniversalBle:WARN',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
library universal_ble;
|
||||
|
||||
export 'package:universal_ble/src/universal_ble_exceptions.dart';
|
||||
export 'package:universal_ble/src/universal_ble_platform_interface.dart';
|
||||
export 'package:universal_ble/src/universal_ble.dart';
|
||||
export 'package:universal_ble/src/models/model_exports.dart';
|
||||
|
||||
Reference in New Issue
Block a user