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
62 changes: 34 additions & 28 deletions lib/open_earable_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export 'src/models/devices/polar.dart';

export 'src/managers/wearable_disconnect_notifier.dart';

export 'src/models/capabilities/device_firmware_version.dart' hide DeviceFirmwareVersionNumberExt;
export 'src/models/capabilities/device_firmware_version.dart'
hide DeviceFirmwareVersionNumberExt;
export 'src/models/capabilities/device_hardware_version.dart';
export 'src/models/capabilities/device_identifier.dart';
export 'src/models/capabilities/battery_level.dart';
Expand Down Expand Up @@ -89,9 +90,11 @@ class WearableManager {
static final WearableManager _instance = WearableManager._internal();

late final BleManager _bleManager;
final PairingManager _pairingManager = PairingManager(rules: [
OpenEarableV2PairingRule(),
],);
final PairingManager _pairingManager = PairingManager(
rules: [
OpenEarableV2PairingRule(),
],
);

late final StreamController<Wearable> _connectStreamController;
late final StreamController<DiscoveredDevice> _connectingStreamController;
Expand All @@ -101,8 +104,6 @@ class WearableManager {
List<String> _autoConnectDeviceIds = [];
StreamSubscription<DiscoveredDevice>? _autoconnectScanSubscription;

bool? _scanExcludeUnsupported;

final List<WearableFactory> _wearableFactories = [
OpenEarableFactory(),
CosinussOneFactory(),
Expand Down Expand Up @@ -148,25 +149,20 @@ class WearableManager {
}

/// Starts scanning for BLE devices.
/// If `excludeUnsupported` is true, it will filter out devices that do not support
/// the required services.
/// If `checkAndRequestPermissions` is true, it will check and request the necessary
/// permissions before starting the scan.
/// Returns a Future that completes when the scan starts.
///
///
/// The discovered devices can be listened to via the [scanStream].
///
///
/// Example usage:
/// ```dart
/// await WearableManager().startScan(excludeUnsupported: true);
/// await WearableManager().startScan();
/// ```
Future<void> startScan({
bool excludeUnsupported = false,
bool checkAndRequestPermissions = true,
}) {
_scanExcludeUnsupported = excludeUnsupported;
return _bleManager.startScan(
filterByServices: excludeUnsupported,
checkAndRequestPermissions: checkAndRequestPermissions,
);
}
Expand All @@ -190,7 +186,10 @@ class WearableManager {
/// connected wearables list.
/// If the device is not supported by any factory, it throws an exception.
/// If the connection fails, it also throws an exception.
Future<Wearable> connectToDevice(DiscoveredDevice device, { Set<ConnectionOption> options = const {}}) async {
Future<Wearable> connectToDevice(
DiscoveredDevice device, {
Set<ConnectionOption> options = const {},
}) async {
if (_connectedIds.contains(device.id)) {
logger.w('Device ${device.id} is already connected');
throw Exception('Device is already connected');
Expand All @@ -210,7 +209,8 @@ class WearableManager {
wearableFactory.disconnectNotifier = disconnectNotifier;
logger.t("checking factory: $wearableFactory");
if (await wearableFactory.matches(device, connectionResult.$2)) {
Wearable wearable = await wearableFactory.createFromDevice(device, options: options);
Wearable wearable =
await wearableFactory.createFromDevice(device, options: options);

_connectedIds.add(device.id);
wearable.addDisconnectListener(() {
Expand All @@ -234,16 +234,21 @@ class WearableManager {
/// Connects to all wearables that are currently discovered in the system.
/// It retrieves the system devices and attempts to connect to each one.
/// Returns a list of successfully connected wearables.
Future<List<Wearable>> connectToSystemDevices({List<String> ignoredDeviceIds = const []}) async {
List<DiscoveredDevice> systemDevices =
await _bleManager.getSystemDevices(filterByServices: false);
Future<List<Wearable>> connectToSystemDevices({
List<String> ignoredDeviceIds = const [],
}) async {
List<DiscoveredDevice> systemDevices = await _bleManager.getSystemDevices();
List<Wearable> connectedWearables = [];
for (DiscoveredDevice device in systemDevices) {
if (_connectedIds.contains(device.id) || ignoredDeviceIds.contains(device.id)) {
if (_connectedIds.contains(device.id) ||
ignoredDeviceIds.contains(device.id)) {
continue;
}
try {
Wearable wearable = await connectToDevice(device, options: {const ConnectedViaSystem()});
Wearable wearable = await connectToDevice(
device,
options: {const ConnectedViaSystem()},
);
connectedWearables.add(wearable);
} catch (e) {
logger.e('Failed to connect to system device ${device.id}: $e');
Expand All @@ -257,11 +262,16 @@ class WearableManager {
}

/// Finds valid pairs of stereo devices based on the defined pairing rules.
Future<Map<StereoDevice, List<StereoDevice>>> findValidPairs(List<StereoDevice> devices) async {
Future<Map<StereoDevice, List<StereoDevice>>> findValidPairs(
List<StereoDevice> devices,
) async {
return await _pairingManager.findValidPairs(devices);
}

Future<List<StereoDevice>> findValidPairsFor(StereoDevice device, List<StereoDevice> devices) async {
Future<List<StereoDevice>> findValidPairsFor(
StereoDevice device,
List<StereoDevice> devices,
) async {
return await _pairingManager.findValidPairsFor(device, devices);
}

Expand All @@ -286,11 +296,7 @@ class WearableManager {
}
});

if (_scanExcludeUnsupported == null) {
startScan();
} else {
startScan(excludeUnsupported: _scanExcludeUnsupported!);
}
startScan();
}
}

Expand Down
32 changes: 4 additions & 28 deletions lib/src/constants.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import 'models/devices/open_earable_v2.dart';
import 'models/devices/cosinuss_one.dart';
import 'models/devices/open_earable_v1.dart';
import 'models/devices/polar.dart';

const String sensorServiceUuid = "34c2e3bb-34aa-11eb-adc1-0242ac120002";
const String sensorConfigurationCharacteristicUuid =
"34c2e3bd-34aa-11eb-adc1-0242ac120002";
Expand All @@ -25,10 +20,12 @@ const String deviceHardwareVersionCharacteristicUuid =

const String parseInfoServiceUuid = "caa25cb7-7e1b-44f2-adc9-e8c06c9ced43";
const String schemeCharacteristicUuid = "caa25cb8-7e1b-44f2-adc9-e8c06c9ced43";
const String sensorListCharacteristicUuid = "caa25cb9-7e1b-44f2-adc9-e8c06c9ced43";
const String sensorListCharacteristicUuid =
"caa25cb9-7e1b-44f2-adc9-e8c06c9ced43";
const String requestSensorSchemeCharacteristicUuid =
"caa25cba-7e1b-44f2-adc9-e8c06c9ced43";
const String sensorSchemeCharacteristicUuid = "caa25cbb-7e1b-44f2-adc9-e8c06c9ced43";
const String sensorSchemeCharacteristicUuid =
"caa25cbb-7e1b-44f2-adc9-e8c06c9ced43";

const String audioPlayerServiceUuid = "5669146e-476d-11ee-be56-0242ac120002";
const String audioSourceCharacteristic = "566916a8-476d-11ee-be56-0242ac120002";
Expand All @@ -43,24 +40,3 @@ const String buttonStateCharacteristicUuid =

const String ledServiceUuid = "81040a2e-4819-11ee-be56-0242ac120002";
const String ledSetStateCharacteristic = "81040e7a-4819-11ee-be56-0242ac120002";

// All UUIDs in a list for filters
List<String> allServiceUuids = [
OpenEarableV1.ledServiceUuid,
OpenEarableV1.deviceInfoServiceUuid,
OpenEarableV1.audioPlayerServiceUuid,
OpenEarableV1.sensorServiceUuid,
OpenEarableV1.parseInfoServiceUuid,
OpenEarableV1.buttonServiceUuid,
OpenEarableV1.batteryServiceUuid,

OpenEarableV2.deviceInfoServiceUuid,
OpenEarableV2.batteryServiceUuid,
OpenEarableV2.ledServiceUuid,

CosinussOne.ppgAndAccServiceUuid,
CosinussOne.temperatureServiceUuid,
CosinussOne.heartRateServiceUuid,
Polar.disServiceUuid,
Polar.heartRateServiceUuid,
];
23 changes: 4 additions & 19 deletions lib/src/managers/ble_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:universal_ble/universal_ble.dart';

import '../../open_earable_flutter.dart';
import '../constants.dart';

/// A class that establishes and manages Bluetooth Low Energy (BLE)
/// communication with OpenEarable devices.
Expand Down Expand Up @@ -111,7 +110,6 @@ class BleManager extends BleGattManager {

/// Initiates the BLE device scan to discover nearby Bluetooth devices.
Future<void> startScan({
bool filterByServices = false,
bool checkAndRequestPermissions = true,
}) async {
bool? permGranted;
Expand Down Expand Up @@ -146,42 +144,29 @@ class BleManager extends BleGattManager {
};

if (!kIsWeb) {
List<DiscoveredDevice> devices =
await getSystemDevices(filterByServices: filterByServices);
List<DiscoveredDevice> devices = await getSystemDevices();
for (var device in devices) {
_scanStreamController?.add(device);
}
}

await UniversalBle.startScan(
scanFilter: ScanFilter(
// Needs to be passed for web, can be empty for the rest
withServices: (kIsWeb || filterByServices) ? allServiceUuids : [],
),
);
await UniversalBle.startScan();
}
_firstScan = false;
}
}

/// Retrieves a list of system devices.
/// If `filterByServices` is true, it filters devices by the predefined service UUIDs.
/// Returns a list of `BleDevice` objects.
/// 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({
bool filterByServices = false,
}) async {
Future<List<DiscoveredDevice>> getSystemDevices() async {
if (!await checkAndRequestPermissions()) {
throw Exception("Permissions not granted");
}
if (kIsWeb) {
throw Exception("getSystemDevices is not supported on web");
}
return UniversalBle.getSystemDevices(
withServices: filterByServices ? allServiceUuids : [],
).then((devices) {
return UniversalBle.getSystemDevices().then((devices) {
return devices.map((device) {
return DiscoveredDevice(
id: device.deviceId,
Expand Down
Loading