From 01509eaae9685142d7d55ed4e72fd104f673363d Mon Sep 17 00:00:00 2001 From: Jonas Bark Date: Mon, 13 Oct 2025 11:09:18 +0200 Subject: [PATCH] refactor device handling to support more devices #2 --- lib/bluetooth/connection.dart | 11 ++++++----- lib/bluetooth/devices/base_device.dart | 16 +++++++++++----- .../devices/{ => zwift}/zwift_click.dart | 2 +- .../devices/{ => zwift}/zwift_clickv2.dart | 6 +++--- .../devices/{ => zwift}/zwift_play.dart | 2 +- .../devices/{ => zwift}/zwift_ride.dart | 10 +++++----- lib/pages/device.dart | 2 +- lib/utils/actions/remote.dart | 2 +- test/zwift_ride_analog_test.dart | 3 ++- 9 files changed, 31 insertions(+), 23 deletions(-) rename lib/bluetooth/devices/{ => zwift}/zwift_click.dart (94%) rename lib/bluetooth/devices/{ => zwift}/zwift_clickv2.dart (93%) rename lib/bluetooth/devices/{ => zwift}/zwift_play.dart (98%) rename lib/bluetooth/devices/{ => zwift}/zwift_ride.dart (97%) diff --git a/lib/bluetooth/connection.dart b/lib/bluetooth/connection.dart index 33a97e8..9cecc60 100644 --- a/lib/bluetooth/connection.dart +++ b/lib/bluetooth/connection.dart @@ -43,8 +43,9 @@ class Connection { _addDevices([scanResult]); } else { final manufacturerData = result.manufacturerDataList; - final data = - manufacturerData.firstOrNullWhere((e) => e.companyId == Constants.ZWIFT_MANUFACTURER_ID)?.payload; + final data = manufacturerData + .firstOrNullWhere((e) => e.companyId == Constants.ZWIFT_MANUFACTURER_ID) + ?.payload; _actionStreams.add(LogNotification('Found unknown device with identifier: ${data?.firstOrNull}')); } } @@ -69,7 +70,7 @@ class Connection { // does not work on web, may not work on Windows if (!kIsWeb && !Platform.isWindows) { UniversalBle.getSystemDevices( - withServices: [BleUuid.ZWIFT_CUSTOM_SERVICE_UUID, BleUuid.ZWIFT_RIDE_CUSTOM_SERVICE_UUID], + withServices: BaseDevice.servicesToScan, ).then((devices) async { final baseDevices = devices.mapNotNull(BaseDevice.fromScanResult).toList(); if (baseDevices.isNotEmpty) { @@ -79,8 +80,8 @@ class Connection { } await UniversalBle.startScan( - scanFilter: ScanFilter(withServices: [BleUuid.ZWIFT_CUSTOM_SERVICE_UUID, BleUuid.ZWIFT_RIDE_CUSTOM_SERVICE_UUID]), - platformConfig: PlatformConfig(web: WebOptions(optionalServices: [BleUuid.ZWIFT_CUSTOM_SERVICE_UUID])), + scanFilter: ScanFilter(withServices: BaseDevice.servicesToScan), + platformConfig: PlatformConfig(web: WebOptions(optionalServices: BaseDevice.servicesToScan)), ); Future.delayed(Duration(seconds: 30)).then((_) { if (isScanning.value) { diff --git a/lib/bluetooth/devices/base_device.dart b/lib/bluetooth/devices/base_device.dart index fc4cc5e..6e38906 100644 --- a/lib/bluetooth/devices/base_device.dart +++ b/lib/bluetooth/devices/base_device.dart @@ -3,10 +3,10 @@ import 'dart:async'; import 'package:dartx/dartx.dart'; import 'package:flutter/foundation.dart'; import 'package:swift_control/bluetooth/ble.dart'; -import 'package:swift_control/bluetooth/devices/zwift_click.dart'; -import 'package:swift_control/bluetooth/devices/zwift_clickv2.dart'; -import 'package:swift_control/bluetooth/devices/zwift_play.dart'; -import 'package:swift_control/bluetooth/devices/zwift_ride.dart'; +import 'package:swift_control/bluetooth/devices/zwift/zwift_click.dart'; +import 'package:swift_control/bluetooth/devices/zwift/zwift_clickv2.dart'; +import 'package:swift_control/bluetooth/devices/zwift/zwift_play.dart'; +import 'package:swift_control/bluetooth/devices/zwift/zwift_ride.dart'; import 'package:swift_control/main.dart'; import 'package:swift_control/utils/actions/desktop.dart'; import 'package:universal_ble/universal_ble.dart'; @@ -16,9 +16,10 @@ import '../messages/notification.dart'; abstract class BaseDevice { final BleDevice scanResult; + final bool isBeta; final List availableButtons; - BaseDevice(this.scanResult, {required this.availableButtons}); + BaseDevice(this.scanResult, {required this.availableButtons, this.isBeta = false}); bool isConnected = false; int? batteryLevel; @@ -27,6 +28,11 @@ abstract class BaseDevice { Timer? _longPressTimer; Set _previouslyPressedButtons = {}; + static List servicesToScan = [ + BleUuid.ZWIFT_CUSTOM_SERVICE_UUID, + BleUuid.ZWIFT_RIDE_CUSTOM_SERVICE_UUID, + ]; + static BaseDevice? fromScanResult(BleDevice scanResult) { // Use the name first as the "System Devices" and Web (android sometimes Windows) don't have manufacturer data final device = kIsWeb diff --git a/lib/bluetooth/devices/zwift_click.dart b/lib/bluetooth/devices/zwift/zwift_click.dart similarity index 94% rename from lib/bluetooth/devices/zwift_click.dart rename to lib/bluetooth/devices/zwift/zwift_click.dart index 206f9a3..4459273 100644 --- a/lib/bluetooth/devices/zwift_click.dart +++ b/lib/bluetooth/devices/zwift/zwift_click.dart @@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:swift_control/bluetooth/devices/zwift/zwift_device.dart'; import 'package:swift_control/utils/keymap/buttons.dart'; -import '../messages/click_notification.dart'; +import '../../messages/click_notification.dart'; class ZwiftClick extends ZwiftDevice { ZwiftClick(super.scanResult) diff --git a/lib/bluetooth/devices/zwift_clickv2.dart b/lib/bluetooth/devices/zwift/zwift_clickv2.dart similarity index 93% rename from lib/bluetooth/devices/zwift_clickv2.dart rename to lib/bluetooth/devices/zwift/zwift_clickv2.dart index cf1f427..89ee330 100644 --- a/lib/bluetooth/devices/zwift_clickv2.dart +++ b/lib/bluetooth/devices/zwift/zwift_clickv2.dart @@ -1,9 +1,9 @@ import 'dart:typed_data'; -import 'package:swift_control/bluetooth/devices/zwift_ride.dart'; +import 'package:swift_control/bluetooth/devices/zwift/zwift_ride.dart'; -import '../ble.dart'; -import '../protocol/zp.pbenum.dart'; +import '../../ble.dart'; +import '../../protocol/zp.pbenum.dart'; class ZwiftClickV2 extends ZwiftRide { ZwiftClickV2(super.scanResult); diff --git a/lib/bluetooth/devices/zwift_play.dart b/lib/bluetooth/devices/zwift/zwift_play.dart similarity index 98% rename from lib/bluetooth/devices/zwift_play.dart rename to lib/bluetooth/devices/zwift/zwift_play.dart index 71670c7..85af421 100644 --- a/lib/bluetooth/devices/zwift_play.dart +++ b/lib/bluetooth/devices/zwift/zwift_play.dart @@ -5,7 +5,7 @@ import 'package:swift_control/bluetooth/devices/zwift/zwift_device.dart'; import 'package:swift_control/bluetooth/messages/play_notification.dart'; import 'package:swift_control/utils/keymap/buttons.dart'; -import '../ble.dart'; +import '../../ble.dart'; class ZwiftPlay extends ZwiftDevice { ZwiftPlay(super.scanResult) diff --git a/lib/bluetooth/devices/zwift_ride.dart b/lib/bluetooth/devices/zwift/zwift_ride.dart similarity index 97% rename from lib/bluetooth/devices/zwift_ride.dart rename to lib/bluetooth/devices/zwift/zwift_ride.dart index 258840d..4d2b9ab 100644 --- a/lib/bluetooth/devices/zwift_ride.dart +++ b/lib/bluetooth/devices/zwift/zwift_ride.dart @@ -1,17 +1,17 @@ import 'package:dartx/dartx.dart'; import 'package:flutter/foundation.dart'; import 'package:protobuf/protobuf.dart' as $pb; +import 'package:swift_control/bluetooth/devices/zwift/zwift_clickv2.dart'; import 'package:swift_control/bluetooth/devices/zwift/zwift_device.dart'; -import 'package:swift_control/bluetooth/devices/zwift_clickv2.dart'; import 'package:swift_control/bluetooth/messages/ride_notification.dart'; import 'package:swift_control/bluetooth/protocol/zp_vendor.pb.dart'; import 'package:swift_control/utils/keymap/buttons.dart'; import 'package:universal_ble/universal_ble.dart'; -import '../../main.dart'; -import '../ble.dart'; -import '../messages/notification.dart'; -import '../protocol/zp.pb.dart'; +import '../../../main.dart'; +import '../../ble.dart'; +import '../../messages/notification.dart'; +import '../../protocol/zp.pb.dart'; class ZwiftRide extends ZwiftDevice { /// Minimum absolute analog value (0-100) required to trigger paddle button press. diff --git a/lib/pages/device.dart b/lib/pages/device.dart index 665d119..c710cb7 100644 --- a/lib/pages/device.dart +++ b/lib/pages/device.dart @@ -5,7 +5,7 @@ import 'package:dartx/dartx.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:swift_control/bluetooth/devices/zwift_clickv2.dart'; +import 'package:swift_control/bluetooth/devices/zwift/zwift_clickv2.dart'; import 'package:swift_control/bluetooth/protocol/zp.pb.dart'; import 'package:swift_control/main.dart'; import 'package:swift_control/pages/markdown.dart'; diff --git a/lib/utils/actions/remote.dart b/lib/utils/actions/remote.dart index 0857e86..b38a4fe 100644 --- a/lib/utils/actions/remote.dart +++ b/lib/utils/actions/remote.dart @@ -3,7 +3,7 @@ import 'dart:ui'; import 'package:accessibility/accessibility.dart'; import 'package:bluetooth_low_energy/bluetooth_low_energy.dart'; import 'package:flutter/foundation.dart'; -import 'package:swift_control/bluetooth/devices/zwift_click.dart'; +import 'package:swift_control/bluetooth/devices/zwift/zwift_click.dart'; import 'package:swift_control/main.dart'; import 'package:swift_control/utils/actions/base_actions.dart'; import 'package:swift_control/utils/keymap/buttons.dart'; diff --git a/test/zwift_ride_analog_test.dart b/test/zwift_ride_analog_test.dart index 113279c..dcf45df 100644 --- a/test/zwift_ride_analog_test.dart +++ b/test/zwift_ride_analog_test.dart @@ -1,6 +1,7 @@ import 'dart:typed_data'; + import 'package:flutter_test/flutter_test.dart'; -import 'package:swift_control/bluetooth/devices/zwift_ride.dart'; +import 'package:swift_control/bluetooth/devices/zwift/zwift_ride.dart'; void main() { group('Zwift Ride Analog Paddle - ZigZag Encoding Tests', () {