Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions lib/open_earable_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ class WearableManager {
return await BleManager.checkPermissions();
}

/// Retrieves Bluetooth devices already known to the operating system.
///
/// When [checkAndRequestPermissions] is `true`, the manager first requests
/// any missing runtime permissions required for system-device discovery.
Future<List<DiscoveredDevice>> getSystemDevices({
bool checkAndRequestPermissions = true,
}) {
return _bleManager.getSystemDevices(
checkAndRequestPermissions: checkAndRequestPermissions,
);
}

/// Adds a wearable factory to the manager.
/// Wearable factories are used to create wearable instances based on the connected devices.
/// This allows the manager to support multiple types of wearables.
Expand Down Expand Up @@ -169,7 +181,8 @@ class WearableManager {
}

/// Returns an unmodifiable list of the currently registered wearable factories.
List<WearableFactory> get wearableFactories => List.unmodifiable(_wearableFactories);
List<WearableFactory> get wearableFactories =>
List.unmodifiable(_wearableFactories);

/// Starts scanning for BLE devices.
/// If `checkAndRequestPermissions` is true, it will check and request the necessary
Expand Down Expand Up @@ -292,10 +305,10 @@ class WearableManager {
ConnectionFailedException _ =>
'Failed to connect to device "$normalizedDeviceName". Please try again.',
_ => _normalizeDeviceNameInMessage(
message: e.toString(),
rawDeviceName: deviceName,
normalizedDeviceName: normalizedDeviceName,
),
message: e.toString(),
rawDeviceName: deviceName,
normalizedDeviceName: normalizedDeviceName,
),
};
}

Expand Down
72 changes: 36 additions & 36 deletions lib/src/managers/ble_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class BleManager extends BleGattManager {
int _mtu = _desiredMtu; // Largest Byte package sent is 42 bytes for IMU
int get mtu => _mtu;

final Map<String, List<StreamController<List<int>>>> _streamControllers = {};
final Map<String, StreamController<List<int>>> _streamControllers = {};

/// A stream of discovered devices during scanning.
StreamController<DiscoveredDevice>? _scanStreamController;
Expand Down Expand Up @@ -43,18 +43,12 @@ class BleManager extends BleGattManager {

void _closeAndRemoveStreamsForDevice(String deviceId) {
final prefix = "$deviceId||";
final keys = _streamControllers.keys
.where((key) => key.startsWith(prefix))
.toList();
final keys =
_streamControllers.keys.where((key) => key.startsWith(prefix)).toList();

for (final key in keys) {
final controllers = _streamControllers.remove(key);
if (controllers == null) {
continue;
}
for (final controller in controllers) {
controller.close();
}
logger.d("Closing stream for $key due to device disconnection");
_streamControllers.remove(key)?.close();
}
}

Expand Down Expand Up @@ -89,9 +83,11 @@ class BleManager extends BleGattManager {
if (!_streamControllers.containsKey(streamIdentifier)) {
return;
}
for (var e in _streamControllers[streamIdentifier]!) {
e.add(value);
if (_streamControllers[streamIdentifier] == null) {
logger.w("Stream controller for $streamIdentifier is null");
return;
}
_streamControllers[streamIdentifier]!.add(value);
};
}

Expand Down Expand Up @@ -177,8 +173,11 @@ class BleManager extends BleGattManager {
/// Throws an exception if called on web.
/// If no devices are found, returns an empty list.
/// If the platform is not web, it uses `UniversalBle.getSystemDevices`.
Future<List<DiscoveredDevice>> getSystemDevices() async {
if (!await checkAndRequestPermissions()) {
Future<List<DiscoveredDevice>> getSystemDevices({
bool checkAndRequestPermissions = true,
}) async {
if (checkAndRequestPermissions &&
!await BleManager.checkAndRequestPermissions()) {
throw Exception("Permissions not granted");
}
if (kIsWeb) {
Expand Down Expand Up @@ -244,8 +243,6 @@ class BleManager extends BleGattManager {
required String deviceId,
required String serviceId,
}) async {


if (!isConnected(deviceId)) {
throw Exception("Device is not connected");
}
Expand Down Expand Up @@ -273,7 +270,8 @@ class BleManager extends BleGattManager {
for (final service in services) {
if (service.uuid.toLowerCase() == serviceId.toLowerCase()) {
for (final characteristic in service.characteristics) {
if (characteristic.uuid.toLowerCase() == characteristicId.toLowerCase()) {
if (characteristic.uuid.toLowerCase() ==
characteristicId.toLowerCase()) {
return true;
}
}
Expand Down Expand Up @@ -308,30 +306,34 @@ class BleManager extends BleGattManager {
required String serviceId,
required String characteristicId,
}) {
final streamController = StreamController<List<int>>();
String streamIdentifier = _getCharacteristicKey(deviceId, characteristicId);
logger.d(
"Subscribing to $deviceId, service $serviceId, characteristic $characteristicId",
);
String streamIdentifier = _getCharacteristicKey(
deviceId,
characteristicId,
);
StreamController<List<int>>? streamController =
_streamControllers[streamIdentifier];
streamController ??= StreamController<List<int>>.broadcast();
if (!_streamControllers.containsKey(streamIdentifier)) {
UniversalBle.subscribeNotifications(
deviceId,
serviceId,
characteristicId,
);
_streamControllers[streamIdentifier] = [streamController];
} else {
_streamControllers[streamIdentifier]!.add(streamController);
_streamControllers[streamIdentifier] = streamController;
}

streamController.onCancel = () {
if (_streamControllers.containsKey(streamIdentifier)) {
_streamControllers[streamIdentifier]!.remove(streamController);
if (_streamControllers[streamIdentifier]!.isEmpty) {
UniversalBle.unsubscribe(
deviceId,
serviceId,
characteristicId,
);
_streamControllers.remove(streamIdentifier);
}
_streamControllers.remove(streamIdentifier)?.close();
UniversalBle.unsubscribe(
deviceId,
serviceId,
characteristicId,
);
_streamControllers.remove(streamIdentifier);
}
};

Expand Down Expand Up @@ -373,10 +375,8 @@ class BleManager extends BleGattManager {
UniversalBle.onScanResult = (_) {};
_scanStreamController?.close();

for (var list in _streamControllers.values) {
for (var e in list) {
e.close();
}
for (var controller in _streamControllers.values) {
controller.close();
}
}
}
26 changes: 16 additions & 10 deletions lib/src/utils/sensor_scheme_parser/v2_sensor_scheme_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';

import 'package:open_earable_flutter/open_earable_flutter.dart' show logger;
import 'package:open_earable_flutter/src/constants.dart';

import '../../managers/ble_gatt_manager.dart';
Expand Down Expand Up @@ -48,36 +49,41 @@ class V2SensorSchemeReader extends SensorSchemeReader {
}

// Listen to the notification of the characteristic
Stream stream = _bleManager.subscribe(
final Stream<List<int>> stream = _bleManager.subscribe(
deviceId: _deviceId,
serviceId: parseInfoServiceUuid,
characteristicId: sensorSchemeCharacteristicUuid,
);

// Request sensor value
final Future<List<int>> responseFuture = stream
.cast<List<int>>()
.first
.timeout(const Duration(seconds: 5));

// Request sensor value only after the listener/future is set up
await _bleManager.write(
deviceId: _deviceId,
serviceId: parseInfoServiceUuid,
characteristicId: requestSensorSchemeCharacteristicUuid,
byteData: [sensorId],
);

// Wait for the notification
try {
await for (List<int> value in stream.timeout(const Duration(seconds: 5))) {
SensorScheme scheme = _parseSensorScheme(value);
final value = await responseFuture;
logger.d("Received notification for sensor scheme of sensor $sensorId: $value");

final scheme = _parseSensorScheme(value);
if (scheme.sensorId != sensorId) {
throw Exception("Sensor id mismatch. Expected: $sensorId, got: ${scheme.sensorId}");
throw Exception(
"Sensor id mismatch. Expected: $sensorId, got: ${scheme.sensorId}",
);
}

_sensorSchemes[sensorId] = scheme;
return scheme;
}
} on TimeoutException catch (e) {
throw Exception("Timeout while waiting for sensor scheme: $e");
throw TimeoutException("Timeout while waiting for sensor scheme: $e");
}

throw Exception("Unknown error while waiting for sensor scheme.");
}

@override
Expand Down
Loading