Krolyxon
Commit 090b358

Branches

Refs →

budctlREADME.md

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