diff --git a/lib/open_earable_flutter.dart b/lib/open_earable_flutter.dart index d6b3387..b215bf2 100644 --- a/lib/open_earable_flutter.dart +++ b/lib/open_earable_flutter.dart @@ -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'; @@ -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 _connectStreamController; late final StreamController _connectingStreamController; @@ -101,8 +104,6 @@ class WearableManager { List _autoConnectDeviceIds = []; StreamSubscription? _autoconnectScanSubscription; - bool? _scanExcludeUnsupported; - final List _wearableFactories = [ OpenEarableFactory(), CosinussOneFactory(), @@ -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 startScan({ - bool excludeUnsupported = false, bool checkAndRequestPermissions = true, }) { - _scanExcludeUnsupported = excludeUnsupported; return _bleManager.startScan( - filterByServices: excludeUnsupported, checkAndRequestPermissions: checkAndRequestPermissions, ); } @@ -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 connectToDevice(DiscoveredDevice device, { Set options = const {}}) async { + Future connectToDevice( + DiscoveredDevice device, { + Set options = const {}, + }) async { if (_connectedIds.contains(device.id)) { logger.w('Device ${device.id} is already connected'); throw Exception('Device is already connected'); @@ -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(() { @@ -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> connectToSystemDevices({List ignoredDeviceIds = const []}) async { - List systemDevices = - await _bleManager.getSystemDevices(filterByServices: false); + Future> connectToSystemDevices({ + List ignoredDeviceIds = const [], + }) async { + List systemDevices = await _bleManager.getSystemDevices(); List 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'); @@ -257,11 +262,16 @@ class WearableManager { } /// Finds valid pairs of stereo devices based on the defined pairing rules. - Future>> findValidPairs(List devices) async { + Future>> findValidPairs( + List devices, + ) async { return await _pairingManager.findValidPairs(devices); } - Future> findValidPairsFor(StereoDevice device, List devices) async { + Future> findValidPairsFor( + StereoDevice device, + List devices, + ) async { return await _pairingManager.findValidPairsFor(device, devices); } @@ -286,11 +296,7 @@ class WearableManager { } }); - if (_scanExcludeUnsupported == null) { - startScan(); - } else { - startScan(excludeUnsupported: _scanExcludeUnsupported!); - } + startScan(); } } diff --git a/lib/src/constants.dart b/lib/src/constants.dart index 1ef7089..e20e21a 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -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"; @@ -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"; @@ -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 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, -]; diff --git a/lib/src/managers/ble_manager.dart b/lib/src/managers/ble_manager.dart index f7443f2..4d9bb4d 100644 --- a/lib/src/managers/ble_manager.dart +++ b/lib/src/managers/ble_manager.dart @@ -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. @@ -111,7 +110,6 @@ class BleManager extends BleGattManager { /// Initiates the BLE device scan to discover nearby Bluetooth devices. Future startScan({ - bool filterByServices = false, bool checkAndRequestPermissions = true, }) async { bool? permGranted; @@ -146,42 +144,29 @@ class BleManager extends BleGattManager { }; if (!kIsWeb) { - List devices = - await getSystemDevices(filterByServices: filterByServices); + List 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> getSystemDevices({ - bool filterByServices = false, - }) async { + Future> 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, diff --git a/lib/src/models/devices/open_earable_v2.dart b/lib/src/models/devices/open_earable_v2.dart index feb6812..da16492 100644 --- a/lib/src/models/devices/open_earable_v2.dart +++ b/lib/src/models/devices/open_earable_v2.dart @@ -36,7 +36,8 @@ const String _audioModeCharacteristicUuid = const String _buttonServiceUuid = "29c10bdc-4773-11ee-be56-0242ac120002"; const String _buttonCharacteristicUuid = "29c10f38-4773-11ee-be56-0242ac120002"; -final VersionConstraint _versionConstraint = VersionConstraint.parse(">=2.1.0 <2.3.0"); +final VersionConstraint _versionConstraint = + VersionConstraint.parse(">=2.1.0 <2.3.0"); // MARK: OpenEarableV2 @@ -91,11 +92,13 @@ class OpenEarableV2 extends Wearable _sensorConfigSubscription?.cancel(); - _sensorConfigSubscription = _bleManager.subscribe( + _sensorConfigSubscription = _bleManager + .subscribe( deviceId: deviceId, serviceId: sensorServiceUuid, characteristicId: sensorConfigStateCharacteristicUuid, - ).listen( + ) + .listen( (data) { controller.add(_parseConfigMap(data)); }, @@ -112,11 +115,13 @@ class OpenEarableV2 extends Wearable controller.onListen = () { // Immediately read the current sensor configuration - _bleManager.read( + _bleManager + .read( deviceId: deviceId, serviceId: sensorServiceUuid, characteristicId: sensorConfigStateCharacteristicUuid, - ).then((data) { + ) + .then((data) { controller.add(_parseConfigMap(data)); }).catchError((error) { logger.e('Error reading initial sensor configuration: $error'); @@ -126,13 +131,15 @@ class OpenEarableV2 extends Wearable return controller.stream; } - Map _parseConfigMap(List data) { + Map _parseConfigMap( + List data, + ) { List sensorConfigs = V2SensorConfig.listFromBytes(Uint8List.fromList(data)); logger.d('Received sensor configuration data: $sensorConfigs'); - + Map sensorConfigMap = {}; - + for (V2SensorConfig sensorConfig in sensorConfigs) { // Find the matching sensor configuration SensorConfiguration? matchingConfig = _sensorConfigurations.where( @@ -143,16 +150,15 @@ class OpenEarableV2 extends Wearable return false; }, ).firstOrNull; - + if (matchingConfig == null) { logger.w( 'No matching sensor configuration found for ID: ${sensorConfig.sensorId}', ); continue; } - - SensorConfigurationValue? sensorConfigValue = - matchingConfig.values.where( + + SensorConfigurationValue? sensorConfigValue = matchingConfig.values.where( (value) { if (value is SensorConfigurationOpenEarableV2Value) { return value.frequencyIndex == sensorConfig.sampleRateIndex && @@ -162,7 +168,7 @@ class OpenEarableV2 extends Wearable return false; }, ).firstOrNull; - + if (sensorConfigValue == null) { logger.w( 'No matching sensor configuration value found for sensor ID: ${sensorConfig.sensorId}', @@ -171,7 +177,7 @@ class OpenEarableV2 extends Wearable } sensorConfigMap[matchingConfig] = sensorConfigValue; } - + return sensorConfigMap; } @@ -198,16 +204,17 @@ class OpenEarableV2 extends Wearable @override Stream get buttonEvents { - StreamController controller = - StreamController(); + StreamController controller = StreamController(); _buttonSubscription?.cancel(); - _buttonSubscription = _bleManager.subscribe( + _buttonSubscription = _bleManager + .subscribe( deviceId: deviceId, serviceId: _buttonServiceUuid, characteristicId: _buttonCharacteristicUuid, - ).listen( + ) + .listen( (data) { if (data.isNotEmpty) { int buttonState = data[0]; @@ -231,11 +238,13 @@ class OpenEarableV2 extends Wearable controller.onListen = () { // Immediately read current button state - _bleManager.read( + _bleManager + .read( deviceId: deviceId, serviceId: _buttonServiceUuid, characteristicId: _buttonCharacteristicUuid, - ).then((data) { + ) + .then((data) { if (data.isNotEmpty) { int buttonState = data[0]; if (buttonState == 0) { @@ -750,15 +759,15 @@ class OpenEarableV2 extends Wearable List positionBytes; try { positionBytes = await _bleManager.read( - deviceId: deviceId, - serviceId: "1410df95-5f68-4ebb-a7c7-5e0fb9ae7557", - characteristicId: "1410df98-5f68-4ebb-a7c7-5e0fb9ae7557", - ); + deviceId: deviceId, + serviceId: "1410df95-5f68-4ebb-a7c7-5e0fb9ae7557", + characteristicId: "1410df98-5f68-4ebb-a7c7-5e0fb9ae7557", + ); } catch (e) { logger.w("Failed to read position characteristic: $e"); return _determinePositionFromName(name); } - + if (positionBytes.length != 1) { logger.e("Expected 1 byte for position, but got ${positionBytes.length}"); return null;