From 1513a53dd46a743f20cecafb79e422a3e0271eae Mon Sep 17 00:00:00 2001 From: Jonas Bark Date: Tue, 16 Dec 2025 10:41:10 +0100 Subject: [PATCH 1/8] update MyWhoosh keymap to use A and D keyboard keys for steering --- lib/utils/keymap/apps/my_whoosh.dart | 32 ++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/utils/keymap/apps/my_whoosh.dart b/lib/utils/keymap/apps/my_whoosh.dart index c01e99b..77dbf99 100644 --- a/lib/utils/keymap/apps/my_whoosh.dart +++ b/lib/utils/keymap/apps/my_whoosh.dart @@ -1,8 +1,8 @@ -import 'package:dartx/dartx.dart'; -import 'package:flutter/services.dart'; import 'package:bike_control/main.dart'; import 'package:bike_control/utils/keymap/apps/supported_app.dart'; import 'package:bike_control/utils/requirements/multi.dart'; +import 'package:dartx/dartx.dart'; +import 'package:flutter/services.dart'; import '../buttons.dart'; import '../keymap.dart'; @@ -44,8 +44,8 @@ class MyWhoosh extends SupportedApp { .map( (b) => KeyPair( buttons: [b], - physicalKey: PhysicalKeyboardKey.arrowRight, - logicalKey: LogicalKeyboardKey.arrowRight, + physicalKey: PhysicalKeyboardKey.keyD, + logicalKey: LogicalKeyboardKey.keyD, touchPosition: Offset(60, 80), isLongPress: true, inGameAction: InGameAction.steerRight, @@ -53,6 +53,18 @@ class MyWhoosh extends SupportedApp { ), ...ControllerButton.values .filter((e) => e.action == InGameAction.steerLeft) + .map( + (b) => KeyPair( + buttons: [b], + physicalKey: PhysicalKeyboardKey.keyA, + logicalKey: LogicalKeyboardKey.keyA, + touchPosition: Offset(32, 80), + isLongPress: true, + inGameAction: InGameAction.steerLeft, + ), + ), + ...ControllerButton.values + .filter((e) => e.action == InGameAction.navigateLeft) .map( (b) => KeyPair( buttons: [b], @@ -63,6 +75,18 @@ class MyWhoosh extends SupportedApp { inGameAction: InGameAction.steerLeft, ), ), + ...ControllerButton.values + .filter((e) => e.action == InGameAction.navigateRight) + .map( + (b) => KeyPair( + buttons: [b], + physicalKey: PhysicalKeyboardKey.arrowRight, + logicalKey: LogicalKeyboardKey.arrowRight, + touchPosition: Offset(32, 80), + isLongPress: true, + inGameAction: InGameAction.steerLeft, + ), + ), ...ControllerButton.values .filter((e) => e.action == InGameAction.toggleUi) .map( From 24212e8e4c18f1d9edf33310fb7cd03d119ffb53 Mon Sep 17 00:00:00 2001 From: Jonas Bark Date: Tue, 16 Dec 2025 10:50:36 +0100 Subject: [PATCH 2/8] update changelog --- CHANGELOG.md | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc97e5b..b06088c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 4.1.0 (unreleased) +### 4.1.0 (16-12-2025) **Features**: - control your trainer manually without requiring a controller - just like a Companion app @@ -6,7 +6,7 @@ **Fixes**: - Gamepads: handle analog values correctly on Windows -- MyWhoosh: updated default keymap to use steering instead of navigating +- MyWhoosh: updated default keymap to use the new A+D keys for steering ### 4.0.0 (07-12-2025) diff --git a/pubspec.yaml b/pubspec.yaml index db96513..4e57924 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: bike_control description: "BikeControl - Control your virtual riding" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 4.1.0+50 +version: 4.1.0+51 environment: sdk: ^3.9.0 From 383055bfe285023960227f75756bf1d4b182071c Mon Sep 17 00:00:00 2001 From: Jonas Bark Date: Tue, 16 Dec 2025 10:57:28 +0100 Subject: [PATCH 3/8] flutter version --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6683c7a..721c264 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ on: env: SHOREBIRD_TOKEN: ${{ secrets.SHOREBIRD_TOKEN }} - FLUTTER_VERSION: 3.38.5 + FLUTTER_VERSION: 3.38.4 jobs: build: From a5b76b43bf635df68c7edaf19688fb52f58ce635 Mon Sep 17 00:00:00 2001 From: Jonas Bark Date: Tue, 16 Dec 2025 11:25:25 +0100 Subject: [PATCH 4/8] build fix --- .github/workflows/build.yml | 4 ++-- pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 721c264..52989e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -347,13 +347,13 @@ jobs: name: Releases path: | build/windows/x64/runner/Release/bike_control.windows.zip - build/windows/x64/runner/Release/bike_control.windows.msix + build/windows/x64/runner/Release/bike_control.msix - name: Update Release uses: ncipollo/release-action@v1 with: allowUpdates: true - artifacts: "build/windows/x64/runner/Release/bike_control.windows.msix" + artifacts: "build/windows/x64/runner/Release/bike_control.msix" prerelease: true tag: v${{ env.VERSION }} token: ${{ secrets.TOKEN }} diff --git a/pubspec.yaml b/pubspec.yaml index 4e57924..f454b44 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: bike_control description: "BikeControl - Control your virtual riding" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 4.1.0+51 +version: 4.1.0+52 environment: sdk: ^3.9.0 From 12ecbf80e1f9ae9907485ab9596e14cae48c5ad1 Mon Sep 17 00:00:00 2001 From: Jonas Bark Date: Tue, 16 Dec 2025 12:19:03 +0100 Subject: [PATCH 5/8] version++ --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index f454b44..6ca915d 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: bike_control description: "BikeControl - Control your virtual riding" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 4.1.0+52 +version: 4.1.1+53 environment: sdk: ^3.9.0 From 59d953bbc47c87bf78e8c33302b4e649c61b612a Mon Sep 17 00:00:00 2001 From: jonasbark Date: Tue, 16 Dec 2025 14:36:30 +0100 Subject: [PATCH 6/8] Update Windows Store version to 4.1.0 --- WINDOWS_STORE_VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WINDOWS_STORE_VERSION.txt b/WINDOWS_STORE_VERSION.txt index fcdb2e1..ee74734 100644 --- a/WINDOWS_STORE_VERSION.txt +++ b/WINDOWS_STORE_VERSION.txt @@ -1 +1 @@ -4.0.0 +4.1.0 From e994eb01dd24c5d82c8c796a0fcfa9691cdb2c33 Mon Sep 17 00:00:00 2001 From: Jonas Bark Date: Tue, 16 Dec 2025 19:53:25 +0100 Subject: [PATCH 7/8] less confusing messages --- lib/bluetooth/devices/base_device.dart | 8 ++++---- lib/bluetooth/devices/mywhoosh/link.dart | 4 ++-- lib/utils/actions/base_actions.dart | 4 ++++ lib/widgets/testbed.dart | 8 ++++---- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/bluetooth/devices/base_device.dart b/lib/bluetooth/devices/base_device.dart index 72b06aa..7318ca1 100644 --- a/lib/bluetooth/devices/base_device.dart +++ b/lib/bluetooth/devices/base_device.dart @@ -1,12 +1,12 @@ import 'dart:async'; -import 'package:dartx/dartx.dart'; -import 'package:flutter/material.dart'; import 'package:bike_control/bluetooth/devices/zwift/constants.dart'; import 'package:bike_control/utils/actions/desktop.dart'; import 'package:bike_control/utils/core.dart'; import 'package:bike_control/utils/keymap/apps/custom_app.dart'; import 'package:bike_control/utils/keymap/manager.dart'; +import 'package:dartx/dartx.dart'; +import 'package:flutter/material.dart'; import '../../utils/keymap/buttons.dart'; import '../messages/notification.dart'; @@ -113,7 +113,7 @@ abstract class BaseDevice { for (final action in buttonsClicked) { // For repeated actions, don't trigger key down/up events (useful for long press) final result = await core.actionHandler.performAction(action, isKeyDown: true, isKeyUp: false); - actionStreamInternal.add(LogNotification(result.message)); + actionStreamInternal.add(ActionNotification(result)); } } @@ -127,7 +127,7 @@ abstract class BaseDevice { Future performRelease(List buttonsReleased) async { for (final action in buttonsReleased) { final result = await core.actionHandler.performAction(action, isKeyDown: false, isKeyUp: true); - actionStreamInternal.add(ActionNotification(result)); + actionStreamInternal.add(LogNotification(result.message)); } } diff --git a/lib/bluetooth/devices/mywhoosh/link.dart b/lib/bluetooth/devices/mywhoosh/link.dart index 740dd2f..e9073cf 100644 --- a/lib/bluetooth/devices/mywhoosh/link.dart +++ b/lib/bluetooth/devices/mywhoosh/link.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'dart:io'; -import 'package:flutter/foundation.dart'; import 'package:bike_control/bluetooth/devices/trainer_connection.dart'; import 'package:bike_control/bluetooth/devices/zwift/protocol/zp.pb.dart'; import 'package:bike_control/bluetooth/messages/notification.dart'; @@ -11,6 +10,7 @@ import 'package:bike_control/utils/core.dart'; import 'package:bike_control/utils/keymap/buttons.dart'; import 'package:bike_control/utils/keymap/keymap.dart'; import 'package:bike_control/utils/requirements/multi.dart'; +import 'package:flutter/foundation.dart'; class WhooshLink extends TrainerConnection { Socket? _socket; @@ -155,7 +155,7 @@ class WhooshLink extends TrainerConnection { InGameAction.steerRight, ]; if (jsonObject != null && !isKeyDown && !supportsIsKeyUpActions.contains(keyPair.inGameAction)) { - return Success('No Action sent on key down for action: ${keyPair.inGameAction}'); + return Ignored('No Action sent on key down for action: ${keyPair.inGameAction}'); } else if (jsonObject != null) { final jsonString = jsonEncode(jsonObject); _socket?.writeln(jsonString); diff --git a/lib/utils/actions/base_actions.dart b/lib/utils/actions/base_actions.dart index 9c9bf25..386a2e3 100644 --- a/lib/utils/actions/base_actions.dart +++ b/lib/utils/actions/base_actions.dart @@ -32,6 +32,10 @@ class NotHandled extends ActionResult { const NotHandled(super.message); } +class Ignored extends ActionResult { + const Ignored(super.message); +} + class Error extends ActionResult { const Error(super.message); } diff --git a/lib/widgets/testbed.dart b/lib/widgets/testbed.dart index 2c06c2c..ba98b38 100644 --- a/lib/widgets/testbed.dart +++ b/lib/widgets/testbed.dart @@ -2,9 +2,6 @@ import 'dart:async'; import 'dart:math' as math; import 'dart:ui'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter/services.dart'; -import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:bike_control/bluetooth/devices/zwift/protocol/zp.pb.dart'; import 'package:bike_control/utils/actions/base_actions.dart' as actions; import 'package:bike_control/utils/core.dart'; @@ -13,6 +10,9 @@ import 'package:bike_control/utils/keymap/apps/custom_app.dart'; import 'package:bike_control/utils/keymap/buttons.dart'; import 'package:bike_control/widgets/ui/button_widget.dart'; import 'package:bike_control/widgets/ui/toast.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; import '../bluetooth/messages/notification.dart'; @@ -125,7 +125,7 @@ class _TestbedState extends State with SingleTickerProviderStateMixin { } } } - } else if (data is ActionNotification) { + } else if (data is ActionNotification && data.result is! actions.Ignored) { buildToast( context, location: ToastLocation.bottomLeft, From 700afb1050d76f5d8fdb21b8cb852402f4830ee7 Mon Sep 17 00:00:00 2001 From: jonasbark Date: Tue, 16 Dec 2025 19:56:56 +0100 Subject: [PATCH 8/8] Document local connection method for Rouvy Added instructions for the local connection method in Rouvy. --- INSTRUCTIONS_ROUVY.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/INSTRUCTIONS_ROUVY.md b/INSTRUCTIONS_ROUVY.md index e69de29..47d20c8 100644 --- a/INSTRUCTIONS_ROUVY.md +++ b/INSTRUCTIONS_ROUVY.md @@ -0,0 +1,4 @@ +## Local Connection method +* +The local connection method (avalable on Android, Windows and macOS) allows BikeControl to directly control Rouvy either using touch or keyboard keys. This way you don't need to select any "Controllers" at all in Rouvy. +Make sure the "Virtual Shifting Controls" are enabled: https://support.rouvy.com/hc/en-us/articles/32452137189393-Virtual-Shifting#h_01K9SWGWYMAVQV108SQ9KWQAKC