Linux utilities for Google Pixel Buds — battery monitor and desktop notification widget. Supports Pixel Buds Pro 2, Pixel Buds Pro, and Pixel Buds A-Series on any Linux desktop running dunst.
| Model | Battery monitoring | ANC display |
|---|---|---|
| Pixel Buds Pro 2 (2024) | ✓ | ✓ |
| Pixel Buds Pro (2022) | ✓ | ✓ |
| Pixel Buds A-Series (2021) | ✓ | — |
| Pixel Buds (2020) | untested | untested |
| Pixel Buds (2017, wired) | ✗ | ✗ |
✓ confirmed · — hardware doesn't support this feature · ✗ protocol not supported · untested = reports welcome (see Contributing)
The project is two programs that work together:
pbwatch is a background daemon. It opens an RFCOMM connection to your buds on Bluetooth channel 2 and speaks the Google Fast Pair Message Stream protocol to read battery levels, charging state, bud placement, and ANC mode. Whenever any of that changes, it writes the new state to flat files under $XDG_RUNTIME_DIR/pbwatch/ and runs a configurable command — by default pbwidget.
pbwidget is that command. It reads the state files pbwatch wrote, looks up the device name via BlueZ, renders a PNG using pycairo, and fires a dunst notification. It is entirely stateless: it reads, renders, and exits. You can replace it with any script of your own using pbwatch's --on-update flag.
Pixel Buds ──RFCOMM──▶ pbwatch ──state files──▶ pbwidget ──PNG──▶ dunst
│
└──▶ --on-update / --on-disconnect (any command)
pbwatch runs as a systemd user service and reconnects automatically if the buds disconnect or go back in the case.
pbwatch is configured entirely via CLI flags. There is no config file.
| Flag | Default | Description |
|---|---|---|
--mac MAC |
(required) | Bluetooth MAC address of your buds (e.g. AB:CD:EF:01:23:45) |
--on-update CMD |
pbwidget |
Command run on battery, ANC, or placement change |
--on-disconnect CMD |
pbwidget --disconnected |
Command run when the buds disconnect |
--heal-audio |
off | Wait for WirePlumber to register the BT sink and switch to A2DP on connect |
--debug |
off | Print raw RFCOMM messages to stdout (useful for bug reports) |
State files written to $XDG_RUNTIME_DIR/pbwatch/:
battery— key=value pairs:left,left_charging,right,right_charging,case,case_charging. Values are percentages (0–100) or-1if the component is not present (e.g. a bud is in the case).anc— two lines: the active mode on line 1 (noise_cancellation,off,transparency, oradaptive), comma-separated supported modes on line 2.
Any script that reads these files can serve as a custom --on-update handler.
pbwidget reads pbwatch's state files and renders a dunst notification. It is configured via ~/.pbwidget (copy from pbwidget.conf.example).
| Key | Default | Description |
|---|---|---|
DEVICE_MAC |
(empty) | Bluetooth MAC; used only to look up the device name via BlueZ for the title. Optional — set DEVICE_LABEL instead if you prefer a fixed label. |
DEVICE_LABEL |
(empty) | Fixed label shown at the top of the widget. If empty, uses the device's Bluetooth alias (bluetoothctl set-alias "My Buds" to customise it). |
BG_COLOR |
#1e1e2e |
Background colour |
TEXT_COLOR |
#cdd6f4 |
Text and percentage labels |
ACCENT_COLOR |
#89b4fa |
Highlight colour for the active ANC mode |
RING_COLOR |
#ffffff20 |
Battery gauge track (8-digit hex = with alpha) |
OPACITY |
0.0 |
Background opacity: 0.0 is fully transparent, 1.0 is solid |
AUTO_CLOSE |
5000 |
Milliseconds before the notification dismisses itself (0 = stays until clicked) |
IMAGE_SIZE |
300 |
Width of the notification image in pixels |
ICON_THEME |
hicolor |
Icon theme for battery and ear icons. hicolor works everywhere; richer options: Yaru (Ubuntu), Adwaita (GNOME), breeze (KDE) |
BATTERY_WARN |
50 |
Gauge arc turns yellow below this percentage |
BATTERY_CRITICAL |
10 |
Gauge arc turns red below this percentage |
ANC_ADAPTIVE |
auto |
Whether to show the Adaptive ANC button: auto reads device capabilities, true always shows it, false hides it |
pbwidget can be run at any time, not only from pbwatch — as long as the state files exist the notification will render correctly. Calling pbwidget --disconnected renders a "disconnected" screen instead.
sudo apt install python3-cairo python3-gi gir1.2-rsvg-2.0 python3-pil python3-dbus dunst bluez dbus yaru-theme-icongit clone https://github.com/yom/pbutils.git
cd pbutils
./install.shThe script copies pbwatch and pbwidget to /usr/local/bin/ (requires sudo), generates the sprite sheet, copies the example config to ~/.pbwidget, enables the pbwatch systemd user service, and patches ~/.config/dunst/dunstrc with the required [pbwidget] display rule. If dunstrc doesn't exist yet it prints the block for you to add manually.
After install, set your Pixel Buds MAC address:
# Find your MAC:
bluetoothctl devices
# Edit the service file and set --mac:
$EDITOR ~/.config/systemd/user/pbwatch.service
# Apply the change:
systemctl --user restart pbwatchTo change pbwatch's flags (on-update command, heal-audio, etc.) without editing the installed service file directly, use a systemd override — this survives reinstalls:
systemctl --user edit pbwatchAdd a block like this (the blank ExecStart= clears the original value before setting the new one):
[Service]
ExecStart=
ExecStart=/usr/local/bin/pbwatch --mac AB:CD:EF:01:23:45 --on-update "my-script" --on-disconnect "my-script --disconnected"Audio doesn't switch to the buds when they connect.
Enable --heal-audio. pbwatch will wait for WirePlumber to register the Bluetooth sink, switch the card profile to A2DP, and set it as the default output. If the sink never appears it restarts WirePlumber automatically (you'll see a brief "Audio routing failed" notification). Add it via a systemd override:
systemctl --user edit pbwatch[Service]
ExecStart=
ExecStart=/usr/local/bin/pbwatch --mac AB:CD:EF:01:23:45 --heal-audiopulseaudio-utils and wireplumber must be installed for this to work: sudo apt install pulseaudio-utils wireplumber.
The widget stops updating.
systemctl --user status pbwatch
journalctl --user -u pbwatch -n 30Battery or ear icons are missing.
The hicolor theme is a minimal fallback. Set ICON_THEME in ~/.pbwidget to a theme you have installed — e.g. Yaru on Ubuntu (sudo apt install yaru-theme-icon), Adwaita on GNOME, or breeze on KDE.
Bug reports, compatibility reports, and feature requests are welcome on the GitHub Issues page.
Compatibility reports are especially useful. If you own a Pixel Buds model listed as untested above, please open an issue with the result — even a "works fine" comment helps confirm coverage. To attach useful diagnostic data, run pbwatch with --debug and grab the output:
journalctl --user -u pbwatch -fThen paste the relevant lines (connect event + a few message lines) into your issue.
Protocol specifications, BlueZ D-Bus API docs, and the Rust reference implementation used to build this project are documented in REFERENCES.md.
