Krolyxon
Commit d55b50b

Branches

Refs →

budctlREADME.md

226 lines · 11.1 KB · Markdown

1<div align="center">
2 <img src="docs/logo.svg" width="320" alt="budctl">
3 <p>Control your wireless earbuds from Linux and Android, without the vendor app.</p>
4</div>
5
6Noise control, EQ, game mode, touch controls, battery and more. There is a command-line tool,
7`budctl`, a desktop app, `budctl-gui`, and an [Android app](#android-app), all built on the same
8library.
9
10- No telemetry and no account. budctl talks to your buds over Bluetooth, through BlueZ on Linux.
11 The only thing that ever goes online is the Android app's update check, and only if you switch
12 it on.
13- The settings live on the buds, so a change made on one device shows on the others.
14
15**Supported buds:** realme Buds T310. More are added as the author gets them.
16
17<p align="center">
18 <img src="docs/screenshots/overview.png" width="560" alt="budctl-gui's Overview page: battery of each bud, noise control, and switches such as game mode and 3D sound">
19</p>
20
21## Install
22
23You need Linux with BlueZ, and for the desktop app a Wayland desktop. Release builds are for
24x86_64 and aarch64 (glibc 2.35 or newer).
25
26```sh
27curl -fsSL https://github.com/krolyxon/budctl/releases/latest/download/install.sh | sh
28```
29
30This puts `budctl` and `budctl-gui` in `~/.local/bin`, and a launcher entry and its icon in
31`~/.local/share`. The download is checked against its SHA-256 checksum. Options:
32
33```sh
34sh install.sh --version 0.1.1 # a given release instead of the latest
35sh install.sh --prefix /usr/local # elsewhere (uses sudo when it must)
36sh install.sh --uninstall # remove them again; your config and log stay
37```
38
39To build from source instead (Rust 1.88 or newer), run `./install.sh --source` in a checkout.
40
41On Arch, `contrib/arch/PKGBUILD` packages the release builds: run `makepkg -si` in that directory.
42
43Pair the buds and connect them in your Bluetooth settings first.
44
45## Command line
46
47```sh
48budctl list # paired buds, and whether budctl knows them
49budctl status # battery, noise control, EQ, switches
50budctl anc off|on|transparency # noise control
51budctl anc light|medium|deep # noise cancelling, at this strength
52budctl eq # the EQ presets, and the one in use
53budctl eq deep-bass # select a preset
54budctl eq custom-4 50=+3 16000=-2 # change bands of a custom preset (dB)
55budctl eq new Gym # create a custom preset: flat, and put to use
56budctl eq rename custom-4 "Gym mix" # rename a custom preset
57budctl eq delete custom-4 # delete a custom preset
58budctl game on|off # game mode (low latency)
59budctl set bass-boost|spatial|wind|dual on|off
60budctl dual # the phones and computers dual connection keeps
61budctl bass low 2 # bass boost, band by band (low, medium, high): -5 to +5
62budctl touch # what each tap and hold does
63budctl touch left triple-tap previous # give a gesture an action (--dry-run first, if you like)
64budctl touch cycle off anc # the modes a hold steps through
65budctl find start --yes # ring the buds (loud: take them out of your ears)
66budctl find stop
67```
68
69- `--json` prints machine-readable output for scripts and status bars (`"schema": 1`).
70- `--debug` hex-dumps every frame sent and received, on stderr.
71- `--device <MAC>` picks the buds when more than one supported pair is connected. Without it,
72 `"device"` in the config file (below) is used, if set.
73
74Exit codes:
75
76| Code | Meaning |
77|---|---|
78| 0 | success |
79| 1 | usage error |
80| 2 | buds not found or not connected |
81| 3 | control channel busy |
82| 4 | the buds rejected the command or did not answer |
83
84## Desktop app
85
86`budctl-gui` has five pages:
87
88- **Overview:** battery, noise control and its strength, the switches, and bass boost band by band
89 (low, medium and high frequencies).
90- **Equalizer:** the presets; create, rename and delete your own, and change their bands.
91- **Gestures:** what each tap and hold does on each bud, and what a hold steps through.
92- **Earbud settings:** dual connection and the devices it keeps, find my earbuds, firmware, audio codec.
93- **App settings:** where the colors come from, the keys, the version.
94
95<table>
96 <tr>
97 <td valign="top"><img src="docs/screenshots/equalizer.png" alt="Equalizer: the built-in presets, a custom preset with its pencil and bin, and its six bands open below"></td>
98 <td valign="top"><img src="docs/screenshots/gestures.png" alt="Gestures: double tap, triple tap and hold for each bud, and the modes a hold steps through"></td>
99 </tr>
100 <tr>
101 <td valign="top"><img src="docs/screenshots/earbuds.png" alt="Earbud settings: the dual connection switch, the devices the buds keep, find my earbuds, firmware and codec"></td>
102 <td valign="top"><img src="docs/screenshots/keys.png" alt="The keys overlay, shown with ?"></td>
103 </tr>
104</table>
105
106The screenshots show `--demo`'s made-up buds.
107
108The app connects when it needs to, and frees the buds' control channel 10 s after your last
109change, so other apps can use it. A change shows "Saving…" until the buds confirm it.
110
111The mouse works everywhere. Vim-like keys work on top of it (press `?` in the window):
112
113| Keys | What they do |
114|---|---|
115| `j` `k`, arrows, Tab | move the cursor |
116| `gg` `G` | first / last item |
117| `h` `l` | previous / next choice, switch off / on; `l` opens a row's page |
118| Enter, Space | toggle, step through the choices, or open |
119| `J` `K`, Ctrl+Tab, `gt` `gT` | next / previous page |
120| `1` … `5` | go to a page |
121| `r` | read the buds again |
122| Esc | hide the cursor |
123| `q` | quit |
124
125The cursor shows only after you press a key, and the first key only shows it.
126
127**Look:** Nothing OS shapes and type in Material You colors: dot-matrix page names and battery
128figures, LED rings and dotted sliders, pill switches and tracks. The fonts (Doto, Space Grotesk,
129Space Mono) are built into the app.
130
131**Colors:** a built-in palette (near-black, white "on" states, red for live things such as the open
132page's dot and "Saving…"), or a [matugen](https://github.com/InioX/matugen) palette: `--theme
133<file>`, else `~/.config/budctl/theme.json`. The file is JSON with Material 3 roles, `on_x` spelled
134`xFg`, all optional: `primary`, `primaryFg`, `primaryContainer`, `primaryContainerFg`, `tertiary`,
135`tertiaryFg`, `surfaceLowest`, `surfaceLow`, `surfaceHigh`, `surfaceHighest`, `surfaceFg`,
136`surfaceVariantFg`, `outline`, `outlineVariant`, `err`, `errFg` (`bg` and `fg` still work for
137`surfaceLowest` and `surfaceFg`; other keys are ignored). A matugen template only needs lines like
138`"primary": "{{colors.primary.default.hex}}"`. The file is read again whenever the window gets
139focus, so a new wallpaper's colors show up there too. When the file is missing, the app writes the
140built-in colors there: a starting point for a template.
141
142**Log:** each launch writes errors and session events (connected, each change and its answer,
143closed) to `~/.local/state/budctl/gui.log`, replacing the last launch's.
144
145Other options: `--demo` shows made-up buds without Bluetooth; `--debug` and `--device` work as in
146the command-line tool.
147
148## Android app
149
150`android/` holds an Android app (Android 12 or newer) with the desktop app's pages and look, on a
151phone: the same Rust library runs inside it, and only the Bluetooth link and the screen are Kotlin.
152Its colors come from your wallpaper (Material You), or from the desktop app's near-black palette.
153
154<p align="center">
155 <img src="docs/screenshots/android-overview.png" width="240" alt="The Android app's Overview: the battery rings, noise control and its strength, and the switches">
156 <img src="docs/screenshots/android-equalizer.png" width="240" alt="The Android app's Equalizer: the presets, and a custom preset's six bands open below">
157 <img src="docs/screenshots/android-gestures.png" width="240" alt="The Android app's Gestures: what each tap and hold does on each bud, and what a hold steps through">
158</p>
159
160The app talks to the buds only while it is on screen, and frees their control channel 10 s after
161your last change or when you leave it, so your computer can use it. A switch on its App page turns
162on the debug log, which you can copy from there.
163
164To install it, download `budctl.apk` from the
165[latest release](https://github.com/krolyxon/budctl/releases/latest) on your phone and open it
166(Android asks you to allow installs from your browser or file manager). After that, the app can
167update itself: switch on **Check for updates** on its App page, and once a day, when you open it,
168it asks GitHub for budctl's latest release and offers it. **Check now** asks right away, with the
169switch off too. **Update** downloads the APK, checks its SHA-256, and hands it to Android's
170installer, which asks you first (and, the first time, to allow budctl to install apps). Nothing
171about you or your buds is sent, and with the switch off the app never goes online unless you tap
172Check now. Play Protect may offer to scan the new version; *Install without scanning* skips that.
173The APK is signed with budctl's own key. Its certificate's SHA-256 fingerprint is
174`45:6F:9B:93:AA:40:1D:57:AA:6C:75:0D:FF:13:FA:82:5A:7B:28:B6:3D:D0:40:99:80:6F:A9:7C:14:75:23:12`.
175
176To build it yourself instead and put it on your phone (with USB debugging on), you need Android
177Studio or the Android SDK with its NDK, the Rust targets
178(`rustup target add aarch64-linux-android x86_64-linux-android`) and `cargo install cargo-ndk`:
179
180```sh
181cd android
182./gradlew installDebug
183```
184
185A build of your own is signed with your machine's debug key, so Android will not install a release
186over it, or the other way round: uninstall one before installing the other.
187
188How it is put together: [`docs/android.md`](docs/android.md). The screenshots show made-up buds
189(`adb shell am start -n io.github.krolyxon.budctl/.MainActivity --ez demo true`).
190
191## Config
192
193`~/.config/budctl/config.json` is created with the defaults the first time it is needed. Every key
194is optional; a file with a mistake is reported and the defaults are used.
195
196```json
197{
198 "device": null,
199 "gui": {
200 "idle_seconds": 10,
201 "start_page": "overview",
202 "motion": true
203 }
204}
205```
206
207- `device`: the buds to use when `--device` is not given, as `"AA:BB:CC:DD:EE:FF"`.
208- `gui.idle_seconds`: how long the app keeps the control channel open after the last change
209 (1–600).
210- `gui.start_page`: `overview`, `equalizer`, `gestures`, `earbuds` or `settings`.
211- `gui.motion`: `false` turns the app's animations off; every change then shows at once.
212
213## How it works
214
215budctl speaks the buds' own control protocol over an RFCOMM socket. What is known about it, and
216how sure, is written down in [`docs/protocol/`](docs/protocol/): the
217[transport](docs/protocol/transport.md) and the [realme Buds T310](docs/protocol/realme_t310.md).
218
219## License
220
221GPL-3.0-or-later. See [LICENSE](LICENSE).
222
223The desktop and Android apps carry the Doto, Space Grotesk and Space Mono fonts, under the SIL
224Open Font License 1.1 ([`crates/budctl-gui/fonts/OFL.txt`](crates/budctl-gui/fonts/OFL.txt)). The
225Doto in them is a static instance (rounded dots, black weight, Latin only), renamed "Doto Rounded"
226as the license asks of modified fonts.