grupetto is an app that creates a system-wide overlay with live statistics about your ride:
You can use it to watch media from sources like Netflix and Youtube while viewing your current power output, cadence, resistance, and speed.
Note: This project is wholly unaffiliated with Peloton. Please do not approach them for support with this app. It relies on undocumented interfaces that are subject to change with any update.
For developers: When forking this project, be sure to update the ReleaseChecker to point to your repository
grupetto must be side-loaded onto the Peloton, follow this guide to learn how to sideload an APK.
After following those steps, the APK for grupetto can be found on the Releases tab above.
Note: Unfortunately, sideloading functionality was recently locked behind a valid subscription. At the time of writing, once the overlay has been installed its continued function is not tied to a subscription. (this is subject to change)
- When first run, grupetto will ask for permission to draw over other apps. This permission is required for the app to function.
-
By default the overlay is shown in its expanded form.
Pressing and holding on power graph will shrink it, and the overlay be dragged around the screen to position it:
- Clicking once on either edge of the overlay enters minimized mode. In this mode the overlay can no longer be relocated, but it continues to show updated values. Click on it again to re-enter the exapnded mode
- The timer can only be controlled in expanded mode. Click once on the timer to start it. Click again to pause, and hold down for 3 seconds to restart it. There is a configuration option on the main screen to show or hide the timer when minimized
This project was greatly helped by the work done by @ihaque on the Pelomon project: https://ihaque.org/tag/pelomon.html
My initial attempt was to directly interface with the serial port via the SerialPort API (which would rely on reflection). But the OS is locked down quite extensively, and there was no clear way for a non-system app to access the serial port directly. Additionally there is no (known) way to get a system application onto the bike, which heavily limits our options, as many permissions used internally are marked as system level permissions.
I explored trying to leverage the built-in updater services, but it seems unlikely that there is no verification of installed packages, and they are also likely behind similar system level permissions to the serial port.
After establishing the direct-access method was a non-starter, there I pulled APKs from my production Generation 2 bike and decompiled them via JADX. They were heavily obfuscated, but using the Pelomon project as a reference, I was able to track down an internal system service that provides communication with a Generation 2 bike.
By leveraging that system service, we're able to "leapfrog" the restriction on direct access, since technically the internal system service is accessing the Serial Port, not our application.
This also provides an ergonomic interface to build against (all the serial port resource handling is done for us), but drastically increases the odds of this being broken by an update.
I also explored a backup approach by using reflection to instantiate internal classes and manipulate their APIs, but it's a much tricker approach since we're running any code found under the current app's UID (meaning anything that was locked off to system-apps
Grupetto is an example of implementing a system service with a rich UI. Jetpack Compose was leveraged for rapid iteration.
A service acts as a LifecycleOwner
to provide the scaffolding Compose expects outside of a normal
activity (see LifecycleEnabledService.kt)
Due to the nature of this project relying on an undocumented interface that may break without notice, effort that has been put into organization of the composables themselves is somewhat limited, and I would not use them as a reference for what a well-built Compose app looks like
However, the patterns used to provide them with data are generally sound, and could be used as a reference for how a Service can provide data for a complex UI
The app is intentionally designed to not necessarily rely on data from the Peloton. The app could be expanded to support other tablets and sensor sources, such as ANT+ power meters.
Please do not approach Peloton with issues related to this. They have no duty to support a reverse engineered service.
Please use the Github issue tab, and mention which version of the OS you're running if possible ( this is displayed at the bottom of screen before the overlay is started)
I've limited this project's scope due to the fact it could easily be broken by an update.
Features I would consider out-of-scope are mostly related to workout session tracking, and interfacing directly with other apps.