A small Linux daemon that monitors the Sony INZONE H5 (WH-G500) wireless
headset over its 2.4 GHz USB dongle (054C:0EBF) and surfaces battery,
charger, volume, mic-mute and game/chat-balance state in the system tray.
- One static Rust binary, ~4 MB, no
hidapi/libusb/appindicatorlinkage. - Talks to the dongle directly via
/dev/hidrawN. - Tray icon via
ksni(StatusNotifierItem — KDE/XFCE/etc). - Desktop notifications via
org.freedesktop.Notifications. - Read-only: no
SETcommands in this release.
Pure-Rust dependency tree, stable toolchain:
cargo build --release
For a fully-static binary that runs on any glibc/musl distro with a session D-Bus:
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl
install -Dm755 target/release/sony-h5-tray ~/.local/bin/sony-h5-tray
sudo install -Dm644 packaging/udev/99-sony-inzone-h5.rules /etc/udev/rules.d/
install -Dm644 packaging/autostart/sony-h5-tray.desktop ~/.config/autostart/
sudo udevadm control --reload-rules && sudo udevadm trigger
The udev rule sets TAG+="uaccess" on the dongle's hidraw nodes, which
hands ownership to the seat's logged-in user — no group fiddling needed.
The autostart file is XDG-compliant (XFCE Settings → Session and Startup → Application Autostart picks it up).
sony-h5-tray [OPTIONS]
--log-level <info|debug|warn|error> Default: info
--notify-mic Emit notifications on mic mute toggle
--no-tray Run headless; useful for systemd
Environment: RUST_LOG (e.g. RUST_LOG=sony_h5_tray=debug) overrides
--log-level.
The icon reflects connection + battery + charging state. Right-click for a menu that lists the live values and offers Refresh (re-query the dongle) and Quit.
XFCE caveat: xfce4-panel's StatusNotifier Plugin is separate from the older Notification Area (XEmbed) plugin. If the icon doesn't appear, add the StatusNotifier plugin to your panel.
Emitted (with per-kind throttling):
| Kind | Trigger | Urgency |
|---|---|---|
| Headset connected/disconnected | _2GHZ_CONNECT_STATUS transitions |
normal |
| Battery low / critical | crosses 20 % / 10 % while not charging | normal / critical |
| Charger connected/disconnected | charger transitions | low |
| Mic muted / live (opt-in) | with --notify-mic |
low |
cargo test
Covers the HCI frame parser against captured wire bytes (charger, mic, volume, balance, power-off frames; bad-checksum and wrong-destination rejections; GET-battery wire match).
+---------------------+
| main thread | blocks on controller join
+---------+-----------+
| shutdown (Ctrl-C)
v
+-----------+ frames +-----------------+ notify req +----------+
| HID reader|----------->| Controller |------------->| Notifier |
| thread |<-----------| thread | | thread |
+-----------+ writes +-----------------+ +----------+
/dev/hidrawN state owner D-Bus calls
poll(2) + read enumeration throttling
nix::libc errnos dispatch
owns ksni Handle <----+
|
+------+ |
| ksni |-------+ runs its own thread
+------+ (spawned by .spawn())
In scope: monitoring (read-only).
Out of scope this release:
- SET commands (volume/balance/mic-mute control). The wire format slots into the controller without architectural changes.
- PS5-mode operation (PID/descriptor differ).
- Firmware update path (Airoha DFU on HID Usage Page
0xFF13). Explicitly avoided. - Multiple-dongle support — first match wins.
MIT.