A working, source-available Android alternative to the "Ryze Fit" app that ships with the Ryze Wave smartwatch (Ryze Above, AU; models RZ-WADA/B/C). It talks to the watch directly over Bluetooth LE with no vendor account, no cloud and no telemetry, and it is built around the thing the vendor app does worst: live tracking during exercise — heart rate every second, blood oxygen, and a real GPS track with honest distance and pace, written straight into Android Health Connect so the data is yours.
What it does today: connects and syncs steps, heart rate, SpO2 and sleep; starts, pauses and stops a workout from either the phone or the watch, with spoken cues; streams live heart rate while you run; records the GPS track on the phone and reports distance and pace from it, falling back to a step-and-stride estimate when GPS is lost; recognises all 70 of the watch's sport types; forwards phone notifications to the watch; rings the phone from the watch; and exports everything to Health Connect.
| The watch | Live workout | GPS track and summary | Heart rate and SpO2 | Dashboard |
|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() |
The photo and screenshots are from real use; image metadata is stripped, personal details are pixelated and every GPS coordinate in this repo is deliberately displaced (see docs/privacy_audit_20260906.md).
Build it and put it on your phone: BUILD.md. The app's design and behaviour: docs/APP.md.
Not affiliated with, endorsed by, or supported by Ryze Above or the makers of the Ryze Fit app. "Ryze" and "Ryze Wave" are their trademarks, used here only to say which watch this talks to. Use it at your own risk: it is not a medical device and nothing it records should be treated as medical data.
Licence: copyright (c) 2026 David Buzz, all rights reserved, licensed to you under the PolyForm Noncommercial License 1.0.0. Use it, change it and share it freely for any noncommercial purpose, including personal use, study and hobby projects. Commercial use requires a separate written agreement and may require payment. This is source-available software, not open source. A few files come from elsewhere under permissive licences and are listed in NOTICE.
The rest of this README is the reverse-engineering side: how the watch's protocol was worked out, the tools that did it, and what is still unknown. None of it is needed just to build and run the app.
| Item | Value |
|---|---|
| Watch BT name / MAC | Ryze Wave(ID-91E5) / 78:02:B7:37:91:E5 (OUI: ShenZhen Ultra Easy Technology) |
| Vendor app | Ryze Fit, Android package com.yc.ryzefit (v1.3.8), iOS id 6474378070 |
| Underlying SDK | UTE / Shenzhen Youhong "yc pedometer" SDK (com.yc.pedometer.sdk) — same family as GloryFit, DayDay Band, etc. |
| SoC | Realtek RTL8763-family (app bundles com.realsil.sdk.dfu + libRtkAesJni.so) |
| Radio | Bluetooth 5.2 dual-mode |
| Classic BT (BR/EDR) | HFP / A2DP / AVRCP / PBAP / SPP — audio, calling, answer/hang-up. Standard profiles, nothing to reverse. |
| BLE GATT | Custom services 0x55FF (cmd: 33F1 write / 33F2 notify), 0x56FF (data: 34F1 / 34F2), 0x57FF ("Alipay"/payments: 35F1 / 35F2). All health data, notifications and settings go here. |
| Open-source reference | Gadgetbridge "GloryFit" driver (PR #5063) speaks the same protocol, but did not work with this watch out of the box. The D5 password handshake was the first suspicion and turned out not to be required here (feature bitmap FL1=0x4BA1D4, password bit clear); the real gaps are the missing live-HR and SpO2 commands and the year-byte fix, listed in docs/PLAN.md and docs/gadgetbridge_upstream.md. |
The full command table, packet formats and the pairing handshake are in docs/PROTOCOL.md.
README.md, CLAUDE.md this file / notes for Claude Code sessions
BUILD.md how to build the apps and install them on a phone
docs/PROTOCOL.md BLE protocol reference (living document)
docs/gadgetbridge_upstream.md Protocol corrections worth sending to the Gadgetbridge GloryFit driver
docs/PLAN.md plan for the replacement app (platform, features, distance model, milestones)
docs/wave_application_research.md narrative research notes: everything that mattered, tagged by how we know it
docs/watch_features_research.md the watch's non-exercise features (voice, contacts, music, games, faces) and what we could do with them
docs/privacy_audit_20260906.md what was scrubbed from this repo before publishing, and the rules that now apply
docs/sports_and_step_counting.md all 70 sports: what the wrist sees in each, and whether its count means anything
memory/ Claude Code's notes for this project (one fact per file, index in MEMORY.md)
ryzewave/ our BLE client library + CLI (protocol.py = codec, client.py = bleak I/O)
ryzeapp/ "Buzz's Ryze Wave": the real Android app (Kotlin/Compose/Room/Health Connect), spec in docs/APP.md
android/ RyzeBridge: headless Android app (Java, no Gradle) that relays BLE <-> adb logcat
tools/bridge.py drives RyzeBridge over adb and decodes the traffic (info, sync, hr, spo2, keep, raw)
tools/fastbuild.sh incremental build + install of the app in seconds (see BUILD.md)
tools/make_signed_release.sh signed release APK + source zip + checksums, optionally a GitHub release
tools/app_smoke.sh build + install + launch + screenshot loop for the app (captures/app_smoke_<ts>/)
tools/pull_app_data.sh pull the app's database and settings off a phone into captures/
tools/analyse_workout.py summarise a pulled workout (distance, pace, HR, track)
tools/privacy_blur.py pixelate regions of a screenshot before committing it (see docs/privacy_audit_20260906.md)
tools/app_tap.sh tap a UI element on the phone by its text (uiautomator), e.g. tools/app_tap.sh "Sync now"
tools/app_workout_test.sh scripted workout on the phone: start, wait, stop, capture FD traffic + screenshots
tests/ offline codec tests
tools/probe.py first-contact BLE client: connect, dump GATT, A1/A2
tools/linktest.py 60 s link-stability test with RSSI sampling (run in the background)
tools/logcat_capture.sh record the phone's logcat (Ryze Fit prints every BLE packet in hex)
tools/parse_logcat.py turn that log into a packet timeline
tools/pull_btsnoop.sh pull the Android HCI snoop log via adb
tools/decode_btsnoop.py print GloryFit ATT traffic from a btsnoop log
tools/extract_opcodes.py regenerate captures/sdk_opcode_map.txt from the decompiled SDK
tools/Gadgetbridge/ sparse clone of Gadgetbridge (GloryFit/No1F1/JYou drivers only, git-ignored)
tools/Gadgetbridge-tools/ Gadgetbridge-tools clone, branch jr-gb-dissector (git-ignored)
tools/jadx/ jadx 1.5.6 decompiler (git-ignored)
captures/ btsnoop logs and derived notes (logs are git-ignored)
apk/ Ryze Fit XAPK + jadx output (git-ignored, ~1 GB)
.venv/ python venv (bleak, pytest, pillow)
Building the Android app and putting it on a phone: BUILD.md. The rest of this section is the Python client, which talks to the watch from the laptop.
python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
.venv/bin/python -m pytest tests # offline codec tests
sudo tools/le_only.sh on # Linux/BlueZ: force the LE bearer (see docs/PROTOCOL.md §8); `off` restores dual mode
bluetoothctl trust 78:02:B7:37:91:E5 # once: lets BlueZ cache the (slow) GATT discovery
.venv/bin/python -m ryzewave info # watch must NOT be connected to the phone (adb shell svc bluetooth disable)
.venv/bin/python -m ryzewave sync|hr|spo2|workout|time|find|scan|raw HEX
Pairing state is cached at runtime in captures/devices.json; it is not tracked and is not part of the repo.
The laptop's Intel/BlueZ stack keeps dropping the LE link (supervision timeouts), while the phone's stack is
rock solid with this watch. android/ is a tiny headless app that owns the GATT connection on the phone and is
driven entirely over adb, so all protocol work stays in Python:
android/sdk-install.sh # once: cmdline-tools + platform 34 + build-tools 34 into tools/android-sdk (~300 MB)
android/build.sh # aapt2 + javac + d8 + apksigner, then adb install + pm grant BLUETOOTH_* (details: BUILD.md)
tools/bridge.py info # connect, feature bitmap, version, battery
tools/bridge.py sync|hr 30|spo2|workout 60|keep 120|gatt|bt3 on|spo2auto 10|hrauto on|raw "connect;write a2;until a2 3000"
adb logcat -s RyzeBridge:* # raw view: TX/RX lines in hex
tools/bridge.py force-stops Ryze Fit before each run: Android shares one GATT link between apps, so the vendor app's own traffic would otherwise interleave with ours (we saw its whole init burst arrive on our notifications).
tools/pull_btsnoop.sh — writes captures/btsnoop_hci_<ts>.log..venv/bin/python tools/decode_btsnoop.py captures/btsnoop_hci_<ts>.log
or, for a Wireshark view, copy gloryfit.lua and gb_utils.lua from the tools/Gadgetbridge-tools
submodule into ~/.local/lib/wireshark/plugins/ (they are AGPL-3.0, so we point at them rather than vendor them).Pairing from scratch (unpair in Ryze Fit, forget the watch in Android BT settings, then re-add)
is the single most valuable capture: it shows the D5 handshake and the initial config burst.
The vendor app's distance is wrong, which is the main reason we want our own. Distance and pace are not
produced by the watch: during a workout Ryze Fit computes them on the phone from GPS and pushes them to the
watch once a second (FD 44 …, see the protocol doc). Outside workouts the app estimates distance from step
count and stride. Our app will own both calculations: proper GPS track distance (filtered, haversine) and a
stride model calibrated from GPS walks. Heart rate, SpO2, steps and sleep come from the watch over BLE.
FD 44 push).tools/logcat_capture.sh)captures/reconnect_20260904_185054.md)ryzewave/ (connect, pair, time, battery, fetches, live HR, SpO2 test, workout) — untested on hardwarepython -m ryzewave info and sync work against the watch (2026-09-04): version, battery, steps, HR, SpO2 and sleep history decodedD6 02 then E5 11 streams E5 11 00 <hr> per second (2026-09-04)34 00 FF FF, the result arrives ~1 min latertools/bridge.pycaptures/bluez_linkdrop_notes.mdryzeapp/ (docs/APP.md)adb logcat -s WatchGatt:*), HR mean unified, chart labels clamped (202 unit tests) — independently verified on the phoneEverything project-authored is tracked. Reference material that came from git is a submodule, and downloaded tools / build output / the decompiled vendor APK are ignored:
| Path | Status |
|---|---|
tools/Gadgetbridge | submodule → https://codeberg.org/Freeyourgadget/Gadgetbridge (master; only the gloryfit/no1f1/jyou dirs are needed) |
tools/Gadgetbridge-tools | submodule → https://codeberg.org/Freeyourgadget/Gadgetbridge-tools, branch jr-gb-dissector |
apk/ | ignored: the vendor APK and its jadx output (re-create with the commands in CLAUDE.md) |
tools/jadx/, tools/android-sdk/ | ignored: downloaded tools (android/sdk-install.sh rebuilds the SDK) |
.venv/, ryzeapp/.gradle, */build/, ryzeapp/local.properties | ignored: local environment |
captures/logcat_*.txt, captures/**/logcat.txt | ignored: multi-megabyte raw logcat dumps (decoded notes are tracked) |
Fresh clone bootstrap:
git submodule update --init --depth 1 tools/Gadgetbridge-tools
git -c protocol.version=2 submodule update --init --depth 1 --filter=blob:none tools/Gadgetbridge # then sparse-checkout the three device dirs
python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
android/sdk-install.sh && echo "sdk.dir=$PWD/tools/android-sdk" > ryzeapp/local.properties
Then build the Android app as described in BUILD.md.
jr-gb-dissector, gloryfit/gloryfit.lua): https://codeberg.org/Freeyourgadget/Gadgetbridge-tools77 commits
Kotlin
89.1%
Python
7.4%
Shell
2.2%
Java
1.3%
A working, source-available Android alternative to the "Ryze Fit" app that ships with the Ryze Wave smartwatch (Ryze Above, AU; models RZ-WADA/B/C). It talks to the watch directly over Bluetooth LE with no vendor account, no cloud and no telemetry, and it is built around the thing the vendor app does worst: live tracking during exercise — heart rate every second, blood oxygen, and a real GPS track with honest distance and pace, written straight into Android Health Connect so the data is yours.
What it does today: connects and syncs steps, heart rate, SpO2 and sleep; starts, pauses and stops a workout from either the phone or the watch, with spoken cues; streams live heart rate while you run; records the GPS track on the phone and reports distance and pace from it, falling back to a step-and-stride estimate when GPS is lost; recognises all 70 of the watch's sport types; forwards phone notifications to the watch; rings the phone from the watch; and exports everything to Health Connect.
| The watch | Live workout | GPS track and summary | Heart rate and SpO2 | Dashboard |
|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() |
The photo and screenshots are from real use; image metadata is stripped, personal details are pixelated and every GPS coordinate in this repo is deliberately displaced (see docs/privacy_audit_20260906.md).
Build it and put it on your phone: BUILD.md. The app's design and behaviour: docs/APP.md.
Not affiliated with, endorsed by, or supported by Ryze Above or the makers of the Ryze Fit app. "Ryze" and "Ryze Wave" are their trademarks, used here only to say which watch this talks to. Use it at your own risk: it is not a medical device and nothing it records should be treated as medical data.
Licence: copyright (c) 2026 David Buzz, all rights reserved, licensed to you under the PolyForm Noncommercial License 1.0.0. Use it, change it and share it freely for any noncommercial purpose, including personal use, study and hobby projects. Commercial use requires a separate written agreement and may require payment. This is source-available software, not open source. A few files come from elsewhere under permissive licences and are listed in NOTICE.
The rest of this README is the reverse-engineering side: how the watch's protocol was worked out, the tools that did it, and what is still unknown. None of it is needed just to build and run the app.
| Item | Value |
|---|---|
| Watch BT name / MAC | Ryze Wave(ID-91E5) / 78:02:B7:37:91:E5 (OUI: ShenZhen Ultra Easy Technology) |
| Vendor app | Ryze Fit, Android package com.yc.ryzefit (v1.3.8), iOS id 6474378070 |
| Underlying SDK | UTE / Shenzhen Youhong "yc pedometer" SDK (com.yc.pedometer.sdk) — same family as GloryFit, DayDay Band, etc. |
| SoC | Realtek RTL8763-family (app bundles com.realsil.sdk.dfu + libRtkAesJni.so) |
| Radio | Bluetooth 5.2 dual-mode |
| Classic BT (BR/EDR) | HFP / A2DP / AVRCP / PBAP / SPP — audio, calling, answer/hang-up. Standard profiles, nothing to reverse. |
| BLE GATT | Custom services 0x55FF (cmd: 33F1 write / 33F2 notify), 0x56FF (data: 34F1 / 34F2), 0x57FF ("Alipay"/payments: 35F1 / 35F2). All health data, notifications and settings go here. |
| Open-source reference | Gadgetbridge "GloryFit" driver (PR #5063) speaks the same protocol, but did not work with this watch out of the box. The D5 password handshake was the first suspicion and turned out not to be required here (feature bitmap FL1=0x4BA1D4, password bit clear); the real gaps are the missing live-HR and SpO2 commands and the year-byte fix, listed in docs/PLAN.md and docs/gadgetbridge_upstream.md. |
The full command table, packet formats and the pairing handshake are in docs/PROTOCOL.md.
README.md, CLAUDE.md this file / notes for Claude Code sessions
BUILD.md how to build the apps and install them on a phone
docs/PROTOCOL.md BLE protocol reference (living document)
docs/gadgetbridge_upstream.md Protocol corrections worth sending to the Gadgetbridge GloryFit driver
docs/PLAN.md plan for the replacement app (platform, features, distance model, milestones)
docs/wave_application_research.md narrative research notes: everything that mattered, tagged by how we know it
docs/watch_features_research.md the watch's non-exercise features (voice, contacts, music, games, faces) and what we could do with them
docs/privacy_audit_20260906.md what was scrubbed from this repo before publishing, and the rules that now apply
docs/sports_and_step_counting.md all 70 sports: what the wrist sees in each, and whether its count means anything
memory/ Claude Code's notes for this project (one fact per file, index in MEMORY.md)
ryzewave/ our BLE client library + CLI (protocol.py = codec, client.py = bleak I/O)
ryzeapp/ "Buzz's Ryze Wave": the real Android app (Kotlin/Compose/Room/Health Connect), spec in docs/APP.md
android/ RyzeBridge: headless Android app (Java, no Gradle) that relays BLE <-> adb logcat
tools/bridge.py drives RyzeBridge over adb and decodes the traffic (info, sync, hr, spo2, keep, raw)
tools/fastbuild.sh incremental build + install of the app in seconds (see BUILD.md)
tools/make_signed_release.sh signed release APK + source zip + checksums, optionally a GitHub release
tools/app_smoke.sh build + install + launch + screenshot loop for the app (captures/app_smoke_<ts>/)
tools/pull_app_data.sh pull the app's database and settings off a phone into captures/
tools/analyse_workout.py summarise a pulled workout (distance, pace, HR, track)
tools/privacy_blur.py pixelate regions of a screenshot before committing it (see docs/privacy_audit_20260906.md)
tools/app_tap.sh tap a UI element on the phone by its text (uiautomator), e.g. tools/app_tap.sh "Sync now"
tools/app_workout_test.sh scripted workout on the phone: start, wait, stop, capture FD traffic + screenshots
tests/ offline codec tests
tools/probe.py first-contact BLE client: connect, dump GATT, A1/A2
tools/linktest.py 60 s link-stability test with RSSI sampling (run in the background)
tools/logcat_capture.sh record the phone's logcat (Ryze Fit prints every BLE packet in hex)
tools/parse_logcat.py turn that log into a packet timeline
tools/pull_btsnoop.sh pull the Android HCI snoop log via adb
tools/decode_btsnoop.py print GloryFit ATT traffic from a btsnoop log
tools/extract_opcodes.py regenerate captures/sdk_opcode_map.txt from the decompiled SDK
tools/Gadgetbridge/ sparse clone of Gadgetbridge (GloryFit/No1F1/JYou drivers only, git-ignored)
tools/Gadgetbridge-tools/ Gadgetbridge-tools clone, branch jr-gb-dissector (git-ignored)
tools/jadx/ jadx 1.5.6 decompiler (git-ignored)
captures/ btsnoop logs and derived notes (logs are git-ignored)
apk/ Ryze Fit XAPK + jadx output (git-ignored, ~1 GB)
.venv/ python venv (bleak, pytest, pillow)
Building the Android app and putting it on a phone: BUILD.md. The rest of this section is the Python client, which talks to the watch from the laptop.
python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
.venv/bin/python -m pytest tests # offline codec tests
sudo tools/le_only.sh on # Linux/BlueZ: force the LE bearer (see docs/PROTOCOL.md §8); `off` restores dual mode
bluetoothctl trust 78:02:B7:37:91:E5 # once: lets BlueZ cache the (slow) GATT discovery
.venv/bin/python -m ryzewave info # watch must NOT be connected to the phone (adb shell svc bluetooth disable)
.venv/bin/python -m ryzewave sync|hr|spo2|workout|time|find|scan|raw HEX
Pairing state is cached at runtime in captures/devices.json; it is not tracked and is not part of the repo.
The laptop's Intel/BlueZ stack keeps dropping the LE link (supervision timeouts), while the phone's stack is
rock solid with this watch. android/ is a tiny headless app that owns the GATT connection on the phone and is
driven entirely over adb, so all protocol work stays in Python:
android/sdk-install.sh # once: cmdline-tools + platform 34 + build-tools 34 into tools/android-sdk (~300 MB)
android/build.sh # aapt2 + javac + d8 + apksigner, then adb install + pm grant BLUETOOTH_* (details: BUILD.md)
tools/bridge.py info # connect, feature bitmap, version, battery
tools/bridge.py sync|hr 30|spo2|workout 60|keep 120|gatt|bt3 on|spo2auto 10|hrauto on|raw "connect;write a2;until a2 3000"
adb logcat -s RyzeBridge:* # raw view: TX/RX lines in hex
tools/bridge.py force-stops Ryze Fit before each run: Android shares one GATT link between apps, so the vendor app's own traffic would otherwise interleave with ours (we saw its whole init burst arrive on our notifications).
tools/pull_btsnoop.sh — writes captures/btsnoop_hci_<ts>.log..venv/bin/python tools/decode_btsnoop.py captures/btsnoop_hci_<ts>.log
or, for a Wireshark view, copy gloryfit.lua and gb_utils.lua from the tools/Gadgetbridge-tools
submodule into ~/.local/lib/wireshark/plugins/ (they are AGPL-3.0, so we point at them rather than vendor them).Pairing from scratch (unpair in Ryze Fit, forget the watch in Android BT settings, then re-add)
is the single most valuable capture: it shows the D5 handshake and the initial config burst.
The vendor app's distance is wrong, which is the main reason we want our own. Distance and pace are not
produced by the watch: during a workout Ryze Fit computes them on the phone from GPS and pushes them to the
watch once a second (FD 44 …, see the protocol doc). Outside workouts the app estimates distance from step
count and stride. Our app will own both calculations: proper GPS track distance (filtered, haversine) and a
stride model calibrated from GPS walks. Heart rate, SpO2, steps and sleep come from the watch over BLE.
FD 44 push).tools/logcat_capture.sh)captures/reconnect_20260904_185054.md)ryzewave/ (connect, pair, time, battery, fetches, live HR, SpO2 test, workout) — untested on hardwarepython -m ryzewave info and sync work against the watch (2026-09-04): version, battery, steps, HR, SpO2 and sleep history decodedD6 02 then E5 11 streams E5 11 00 <hr> per second (2026-09-04)34 00 FF FF, the result arrives ~1 min latertools/bridge.pycaptures/bluez_linkdrop_notes.mdryzeapp/ (docs/APP.md)adb logcat -s WatchGatt:*), HR mean unified, chart labels clamped (202 unit tests) — independently verified on the phoneEverything project-authored is tracked. Reference material that came from git is a submodule, and downloaded tools / build output / the decompiled vendor APK are ignored:
| Path | Status |
|---|---|
tools/Gadgetbridge | submodule → https://codeberg.org/Freeyourgadget/Gadgetbridge (master; only the gloryfit/no1f1/jyou dirs are needed) |
tools/Gadgetbridge-tools | submodule → https://codeberg.org/Freeyourgadget/Gadgetbridge-tools, branch jr-gb-dissector |
apk/ | ignored: the vendor APK and its jadx output (re-create with the commands in CLAUDE.md) |
tools/jadx/, tools/android-sdk/ | ignored: downloaded tools (android/sdk-install.sh rebuilds the SDK) |
.venv/, ryzeapp/.gradle, */build/, ryzeapp/local.properties | ignored: local environment |
captures/logcat_*.txt, captures/**/logcat.txt | ignored: multi-megabyte raw logcat dumps (decoded notes are tracked) |
Fresh clone bootstrap:
git submodule update --init --depth 1 tools/Gadgetbridge-tools
git -c protocol.version=2 submodule update --init --depth 1 --filter=blob:none tools/Gadgetbridge # then sparse-checkout the three device dirs
python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
android/sdk-install.sh && echo "sdk.dir=$PWD/tools/android-sdk" > ryzeapp/local.properties
Then build the Android app as described in BUILD.md.
jr-gb-dissector, gloryfit/gloryfit.lua): https://codeberg.org/Freeyourgadget/Gadgetbridge-tools77 commits
Kotlin
89.1%
Python
7.4%
Shell
2.2%
Java
1.3%