BikeControl turns a Zwift Click into a controller for any trainer app
Do Virtual Gear Shifting (and more) in any rider app with your Zwift Play, Zwift Click, Zwift Ride or any other controller out there!
At a glance
- What is it?
- BikeControl is a Flutter application that relays a physical controller such as a Zwift Click, Zwift Ride, Zwift Play or Shimano Di2 into your indoor cycling app, adding virtual shifting where the app lacks it. It suits riders whose trainer software has no support for their buttons, and its source is only partly open, since the trainer and controller emulator lives in a private submodule.
- Who is it for?
- Adopt BikeControl if your indoor trainer app has no support for the controller you own and you want virtual shifting, gestures, steering or intensity control without waiting for the vendor to add it, since the app bridges the gap across MyWhoosh, Zwift, Rouvy, Tacx and others.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Dart, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the controller actually does once it is bridged
BikeControl, formerly SwiftControl, lets you control a favourite trainer app using a Zwift Click, Zwift Ride, Zwift Play, Shimano Di2 or similar device. The feature list is broader than shifting.
Virtual shifting is the headline: you can add it to trainer apps that do not support it natively, and adjust it to your liking by changing the number of gears, adjusting the gear ratios, and more. Then there is steering and navigation, workout intensity adjustment, music control on your device, and the ability to create screenshots or launch any command or shortcut. Gestures are configurable, so a single click, double click or long press can each do something different, and the summary is that if you can do it via keyboard, mouse or touch, you can do it with BikeControl.
Connecting the smart trainer directly to BikeControl rather than to the app opens a second set of options: virtual shifting where the app lacks it, adjusted gear counts and ratios, direct gear, intensity and mode changes through the controller, starting a mini workout, and proxying to WiFi.
That last item is a practical detail that matters in a room with a laptop and a phone. The trainer can reach the network through the machine running BikeControl instead of being on the same WiFi itself.
The supported device list is long, and the betas are labelled
The device support is the strongest argument for the project, and the README is careful about which entries are stable.
Fully listed are the Zwift Click, the Zwift Ride and the Zwift Play, Shimano Di2, SRAM AXS and eTap, the Wahoo Kickr Bike in Shift, Pro, V1 and V2 versions, and gamepads and keyboard input. The Zwift Click v2 is listed as mostly supported with a pointer to issue 68. The Stages SB20 Smart Bike, the CYCPLUS BC2 Virtual Shifter, the Thinkrider VS200 Virtual Shifter and the Elite Square Smart Frame are marked beta, as are cheap Bluetooth buttons and the Wahoo KICKR HEADWIND fan accessory.
Two configuration notes matter more than the list itself. For Shimano Di2 you configure the levers to use D-Fly channels with the Shimano E-Tube app. For SRAM AXS, BikeControl handles the configuration of the buttons so they no longer perform actual shifting on your derailleurs, which is the only way the virtual shifting can take over cleanly.
There are also two non-hardware inputs that most people overlook. Your phone works: mount it on the handlebar and it can detect steering. And a keyboard acts like a companion app, which is useful when remapping keys from another device with something like AutoHotkey. The README also notes that some trainers did not support keyboard input for all functions and now do, which is a fix for a specific class of frustration.
Four connection methods, chosen by app and operating system
The How does it work section is the heart of the documentation, because the app connects to your controller devices automatically and then uses different methods of connecting to the trainer app depending on the trainer app and the operating system.
Connecting to the trainer app on the same device or on another device using Network is available on Android, iOS, iPadOS, macOS and Windows, and is supported by apps such as MyWhoosh, Rouvy and Zwift. Connecting to the trainer app on another device by simulating a Bluetooth device covers the same set of operating systems, and is supported by Rouvy and Zwift.
Directly controlling the trainer app via Accessibility features, simulating touch and keyboard input, is available on Android, macOS and Windows, and is supported by all trainer apps. And connecting using the OpenBikeControl protocol is available on Android, iOS, iPadOS, macOS and Windows.
The distribution of support across methods tells you something practical. Network and Bluetooth simulation are the paths that work when the app has an actual communication channel. Accessibility is the fallback that works everywhere but is limited to Android, macOS and Windows, since iOS restricts synthetic input. The OpenBikeControl protocol route is the one that a compatible app supports natively.
Supported apps are listed as MyWhoosh, Zwift, TrainingPeaks Virtual, Biketerra.com, Rouvy, Strappo, Tacx Training, FulGaz, OpenBikeControl compatible apps, and any other, with custom mapping and adjustable touch points or keyboard shortcuts.
Building from source, and the part that is not open
The build instructions are short, and they contain the most important fact about the project:
git clone https://github.com/OpenBikeControl/bikecontrol.git
cd bikecontrol
flutter pub get
# The localization accessors under lib/gen/ are generated, not committed.
flutter pub global activate intl_utils
flutter pub global run intl_utils:generate
flutter build apk --debug # or: flutter runThe prop submodule section explains the situation. The .gitmodules file references a private prop repository, and the README says you do not need it and that git submodule update --init is expected to fail, so leave it uninitialised. That repository holds the trainer and controller emulator implementations, which are not open source, and the author offers to discuss access on request.
What the repository does ship is prop_public/, an API-compatible stub of the same package, and pubspec.yaml points at it, so a clone builds, analyzes and runs against the stub with no extra setup.
The practical meaning is spelled out. The app launches, and the UI, settings, localization and non-emulator code are all fully buildable and hackable, but the stubbed calls are inert, so trainer emulation and virtual shifting do not actually do anything in a build made that way.
So this is an open source application with a closed core, which makes sense for a project whose value is a set of protocol implementations, and it also means you should not evaluate this project by reading its source alone. The GitHub Releases APKs are signed with a key that is not in the repository, and when android/keystore.properties is absent the release build falls back to the debug key.
A Flutter codebase with platform directories for every target
The repository layout shows the scale of the work behind a project that presents itself as a small utility.
There is a lib/ directory for the Dart code, an android/ directory, an ios/ directory, macos/, linux/ and windows/ for the desktop targets, a web/ directory and a packages/ directory. There are also platform-specific helper modules: accessibility/, keypress_simulator/ and media_key_detector/.
The instruction documents in the root are a good map of the reverse engineering effort. There is INSTRUCTIONS_ZWIFT.md, INSTRUCTIONS_ROUVY.md, INSTRUCTIONS_MYWHOOSH_LINK.md, INSTRUCTIONS_REMOTE_CONTROL.md, INSTRUCTIONS_IOS.md and INSTRUCTIONS_LOCAL.md, alongside TROUBLESHOOTING.md, SCRIPTING.md, STORE_METADATA.md and a WINDOWS_STORE_VERSION.txt. A separate per-instruction document for each supported app is what you would expect from a project that has to reverse engineer how each one exposes input to a controller.
The tooling is Flutter and Dart, with dart_test.yaml, a test/ directory, an integration_test/ directory, analysis_options.yaml and pubspec.yaml plus a lock file. There is a shorebird.yaml, which points to Shorebird for distributing patches to the published apps, a Gemfile for Ruby-side tooling, and a windows_iap/ directory for in-app purchase handling on the Microsoft Store build.
The distribution model is app stores rather than a package manager. The README links the Google Play listing, the iPhone and Mac App Store listings, the Microsoft Store listing, and a direct Windows zip download.
A testing period, a renamed app and an open hardware initiative
Two things in the README describe how the project is positioned rather than how it works.
The first is the testing period. The download section says the best route is the landing page and the Get Started button, which explains on which platform you want to run BikeControl, and that a testing period is available allowing you to try out the full functionality. The stores are linked from the same section, and the app is also described as tryable in a browser if it supports Bluetooth connections, although the README is explicit that no controlling is possible there.
The second is the renaming. The project is now BikeControl, formerly SwiftControl, and the store listings still carry the old package identifier, de.jonasbark.swiftcontrol on Google Play and the swiftcontrol App Store entry. That mismatch is worth knowing if you follow a link or install from a store, and it also explains the swiftcontrol topic tag sitting next to bikecontrol in the repository topics.
The hardware direction is the third. The README says the team is working on creating an affordable alternative based on an open standard, supported by all major trainer apps, with an interest registration link on the openbikecontrol.org site. The OpenBikeControl protocol is already usable as a connection method with compatible apps, so the open standard is not only a future idea, it is the interface the project is steering third-party hardware toward.
Editorial conclusion
Adopt BikeControl if your indoor trainer app has no support for the controller you own and you want virtual shifting, gestures, steering or intensity control without waiting for the vendor to add it, since the app bridges the gap across MyWhoosh, Zwift, Rouvy, Tacx and others. Do not adopt it as a fully open source product, because the trainer and controller emulator implementations sit in a private prop submodule, so a clone builds against an inert stub and the emulator calls do nothing. Before you commit, read the prop and prop_public section of the README, confirm the platforms and apps you need are on the supported list, and note the release record, where version 6.3 came out on 2026-07-10 with the last push on 2026-10-19.
Frequently asked questions
What does BikeControl do and which controllers does it support?
BikeControl, formerly SwiftControl, lets you control a trainer app with a Zwift Click, Zwift Ride, Zwift Play, Shimano Di2, SRAM AXS, Wahoo Kickr Bike, gamepads, keyboard input or your phone. It adds virtual shifting to apps that lack it, and supports gestures, steering, intensity adjustment and command shortcuts.
Which indoor cycling apps does BikeControl support?
The supported list is MyWhoosh, Zwift, TrainingPeaks Virtual, Biketerra.com, Rouvy, Strappo, Tacx Training, FulGaz and OpenBikeControl compatible apps. The README says you can add custom mapping and adjust touch points or keyboard shortcuts to your liking for any other app.
How does BikeControl connect to the trainer app?
It uses different methods depending on the app and the operating system: connecting over the network to the app on the same or another device, simulating a Bluetooth device to reach the app on another device, controlling the app directly via Accessibility features for touch and keyboard input, or connecting through the OpenBikeControl protocol.
Can I build BikeControl from source?
The build is a Flutter project: clone the repository, run flutter pub get, generate the localization accessors with intl_utils, and then flutter build apk --debug or flutter run. The README notes that the localization accessors under lib/gen/ are generated and not committed.
Is the trainer emulation part of BikeControl open source?
No. The .gitmodules file references a private prop repository holding the trainer and controller emulator implementations, which are not open source, and the README says git submodule update --init is expected to fail. The repository ships prop_public/, an API-compatible stub, so a clone builds and runs, but the stubbed calls are inert and trainer emulation and virtual shifting do not work in such a build.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/openbikecontrol-bikecontrol)Was this useful?