See your Bluetooth headphone battery on the Windows taskbar. At a glance.
A 25 KB tray app that puts the battery percentage of your Bluetooth headset directly on the Windows 11 taskbar — not buried three clicks deep in Settings, not hidden behind the overflow chevron.
Built for the Anker Soundcore Space Q45, works with most Bluetooth headsets.
This was developed and verified against an Anker Soundcore Space Q45 (A3040) on Windows 11 Pro 26200. That is the only headset it has ever run against.
It reads a generic Windows mechanism, not anything Anker-specific, so it should work with most Bluetooth headsets that report battery to Windows — but nothing else is confirmed. If it works (or doesn't) on yours, a 30-second device report helps the next person.
Windows already knows your headphone battery level. It reads it over the Bluetooth Hands-Free Profile and stores it in a PnP device property. But it only surfaces it in Settings → Bluetooth & devices, which is useless when what you actually want is a glance while you work.
Third-party options exist, but the popular open-source ones don't help most headphone owners: some read only Bluetooth LE (so they see nothing for the many BR/EDR-only headsets), and others read only USB HID gaming headsets.
This app reads the property Windows already populates, and draws it as a number plus a level bar in the notification area — then pins it to the taskbar so it is genuinely at a glance.
- Number + level bar in the tray, readable at real 16px tray size
- Colour-coded — green above 30%, amber below 30%, red at 10% and under
- Pins itself to the taskbar so it isn't hidden behind the
^overflow - Hover tooltip shows the charge and how long ago it was read
- Left click = refresh, with a sweep animation so you can see it worked
- Multi-device — detects every Bluetooth device reporting a battery level and lets you choose which one the icon tracks
- Low battery balloon at 20%
- Optional auto-hide — the icon appears when you connect a headset and disappears when you don't, so it only takes up space when it has something to say
- Theme-aware — adapts to light and dark taskbars
- Bilingual — English and Portuguese, follows your Windows language
- Run at startup toggle, no scheduled tasks or services
- One-command install —
--installsets it up and registers startup;--uninstallremoves the startup entry, settings and files. No admin rights needed - Tiny — 25 KB binary, 1–3 MB of RAM, no dependencies
| OS | Windows 10 or 11 |
| Runtime | None to install — .NET Framework 4.x ships with Windows |
| Privileges | None. Everything stays under your user profile |
| Hardware | A Bluetooth headset that reports battery to Windows over HFP |
Quick way to tell whether your headset qualifies: open Settings → Bluetooth & devices. If Windows shows a battery percentage there, this app will show it on your taskbar. If Windows shows nothing, no app can invent it.
Download BluetoothBatteryTray.exe from the
latest release, then run it once with --install:
.\BluetoothBatteryTray.exe --installThat's the whole installer. It copies itself to
%LOCALAPPDATA%\Programs\BluetoothBatteryTray\, registers to start with
Windows, pins itself to the taskbar, and launches. No admin rights, no
uninstaller entry to rot, nothing outside your user profile.
Or do the download and install in one line:
$u = (irm https://api.github.com/repos/dkamioka/bluetooth-battery-tray/releases/latest).assets[0].browser_download_url
$f = "$env:TEMP\BluetoothBatteryTray.exe"; iwr $u -OutFile $f; & $f --installTo remove it completely — startup entry, settings, files:
.\BluetoothBatteryTray.exe --uninstallSmartScreen will warn you. The binary is unsigned (a code-signing certificate costs more per year than this project will ever justify). Click More info → Run anyway, or build it yourself in one command — it takes about two seconds.
Prefer no install? Download the exe and double-click it. Everything works; it simply won't start with Windows unless you tick Run at startup in the right-click menu.
Pick the file's final location before pinning it to the taskbar — Windows keys the "always show" setting to the executable's path.
No SDK, no NuGet, no downloads — Windows already ships the C# compiler.
git clone https://github.com/dkamioka/bluetooth-battery-tray.git
cd bluetooth-battery-tray
powershell -ExecutionPolicy Bypass -File build.ps1 -RunThat's the whole toolchain. build.ps1 calls csc.exe from
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\, present on every Windows
10/11 install.
If the icon is hidden
Windows 11 drops new tray icons into the overflow flyout by default. The app tries to promote itself automatically, but Explorer sometimes only picks that up on restart. Either:
- drag the icon out of the
^flyout onto the taskbar (instant, persistent), or - right click → Pin to taskbar, then restart Explorer.
| Action | Result |
|---|---|
| Hover | Tooltip: soundcore Space Q45 — 70% / Updated 12m ago |
| Left click | Refresh now, with a ~475 ms sweep animation |
| Right click | Menu (below) |
| Automatic | Silent refresh every 60 seconds |
Right-click menu:
| Item | |
|---|---|
| status line | Device, charge, and when it was last read |
| Refresh now | Same as left click |
| Device ▸ | Pick which device the icon tracks (remembered) |
| Run at startup | Adds/removes an HKCU\...\Run entry |
| Hide icon when disconnected | Icon vanishes while no device is connected |
| Pin to taskbar | Re-applies the always-visible flag |
| Quit | Exits fully — no orphan process |
You can't, reliably — and you don't need to. Windows raises no event when a Bluetooth device reconnects: it logs device installation (first pairing) only, and the Bluetooth operational logs that might help are disabled by default. A Task Scheduler event trigger would therefore fire once, at first pairing, and never again.
Instead, turn on Run at startup plus Hide icon when disconnected. The app sits resident at 1–3 MB, the icon appears on its own within ~15 seconds of connecting a headset, and vanishes when you disconnect. Same result, no scheduled task, nothing to break.
While the icon is hidden there is no menu to right-click. To quit in that state, end
BluetoothBatteryTray.exefrom Task Manager, or connect a device first. The option is off by default for this reason.
Windows stores the HFP battery level in an undocumented PnP property on the device's Hands-Free node — note that the property is empty on the device's main node, which is why most guides fail to find it:
Node: soundcore Space Q45 Hands-Free AG
Property: {104EA319-6EE2-4701-BD47-8DDBF425BBE5}, 2 (DEVPROP_TYPE_BYTE)
Value: 70
The app enumerates the BTHENUM device tree with SetupDiGetClassDevs, reads
that property with SetupDiGetDeviceProperty, and renders the result with GDI+.
A full scan takes about 10 ms.
You can see the same value yourself:
Get-PnpDevice -Class System |
Where-Object FriendlyName -like '*Hands-Free AG*' |
ForEach-Object {
Get-PnpDeviceProperty -InstanceId $_.InstanceId `
-KeyName '{104EA319-6EE2-4701-BD47-8DDBF425BBE5} 2'
} | Select-Object InstanceId, DataReading the property via PowerShell cmdlets takes ~350 ms. Going straight to SetupAPI is ~35× faster, which is why the app is C# rather than a script.
Works with any Bluetooth device that reports a battery level to Windows over HFP — which is most modern wireless headsets and earbuds.
This app was written for, and is only confirmed on, the Anker Soundcore Space Q45 (model A3040). If you own one, this is the tool you're looking for: the Soundcore app is Android/iOS only and has no Windows version, so on a PC your battery level is otherwise stuck in the Settings app.
The rest of the Soundcore line — Space One, Space Q45, Life Q30, Life Q35, Life Q20, Liberty 4, Liberty 4 NC, Space A40 — uses the same Bluetooth profiles and should behave identically, but none has been tested. Reports very welcome.
| Status | Devices |
|---|---|
| ✅ Verified | Anker Soundcore Space Q45 (A3040) · iPhone as an HFP peer — Windows 11 Pro 26200 |
| 🤷 Expected to work, untested | Sony WH-1000XM series · Bose QuietComfort · Jabra Elite · Sennheiser Momentum · JBL Tune/Live · Beats · Samsung Galaxy Buds · AirPods paired to Windows · most other HFP headsets |
| ❌ Won't work | Devices that report battery only over Bluetooth LE's Battery Service, or only over a vendor's proprietary channel, without also using HFP |
If the app shows -- while your headset is connected, see
Troubleshooting.
Tried it on your headset? Tell us — it helps everyone.
Because that is the resolution the protocol carries. This is not a limitation of this app.
Microsoft's own Bluetooth accessory guidelines state:
Accessories that support HFP shall use the HF Indicators and provide a battery level using at least 11 steps (enough to show 0 up to and including 100 by 10s).
Apple's widely-implemented AT+IPHONEACCEV extension is likewise "a digit
between 0 and 9 ... to the nearest 10%".
Finer readings are only possible over Bluetooth LE's Battery Service (BAS,
0x180F), which gives 0–100 in 1% steps — but many headsets, including the one
this was developed against, are BR/EDR-only and expose no LE services at all.
Vendor-proprietary channels are not a shortcut either: OpenSCQ30, which speaks
Soundcore's proprietary RFCOMM protocol, displays battery as a fraction out of
5 — coarser, at 20% steps.
So the colour thresholds are deliberately placed at 10 / 20 / 30 — exactly the steps a device can actually emit, with no wasted resolution.
There is no config file. Everything lives in the registry under HKCU, so
uninstalling is deleting the exe and (optionally) these keys:
| Key | Purpose |
|---|---|
HKCU\Software\BluetoothBatteryTray\Device |
Which device the icon tracks |
HKCU\Software\BluetoothBatteryTray\HideWhenDisconnected |
Auto-hide toggle |
HKCU\Software\Microsoft\Windows\CurrentVersion\Run\BluetoothBatteryTray |
Run at startup |
HKCU\Control Panel\NotifyIconSettings\<hash>\IsPromoted |
Always show on taskbar |
Thresholds and the poll interval are constants at the top of
src/BluetoothBatteryTray.cs — change and rebuild:
public const int RedThreshold = 10; // <= this: red
public const int AmberThreshold = 30; // < this: amber
public const int WarnThreshold = 20; // fires the low battery balloon
const int PollInterval = 60000; // background refresh, msIcon shows -- while the headset is connected.
Your device isn't reporting battery over HFP. Check whether Windows sees it at
all: Settings → Bluetooth & devices. If Windows shows no percentage either,
this app cannot invent one. Note the value disappears if you disable the
"Headset (Hands-Free)" audio endpoint to force stereo-only output.
Icon keeps hiding behind the ^ chevron.
Drag it out of the flyout onto the taskbar. If it returns after moving the exe,
that's expected — the setting is keyed to the executable path.
It shows the wrong device. Right click → Device and pick the right one. Phones connected over HFP show up here too.
Nothing happens when I run it. It's already running — the app is single-instance. Look in the overflow flyout.
| Binary | 25 KB |
| RAM (working set) | 0.8 – 2.9 MB, sawtooth — see below |
| RAM (private working set) | 0.4 – 1.2 MB |
| Battery read | ~10 ms |
| Idle CPU | 0% between 60 s polls |
| Processes | 1 |
| Dependencies | none — .NET Framework 4.x ships with Windows |
The app calls SetProcessWorkingSetSize(-1,-1) after each poll. It is idle
~99.98% of the time, so returning CLR/WinForms pages to the OS between reads
drops the working set from ~29 MB to the range above.
That produces a sawtooth: it falls to ~0.8 MB right after a trim, climbs back toward ~2.9 MB as pages fault in, then trims again. Sampled every 10 s across several poll cycles, the median sits at ~2.8 MB. Whatever Task Manager shows you depends on where in that cycle you look — hence a range rather than a single number.
Commit charge (private bytes) stays around 22 MB regardless. Trimming moves pages to the standby list; it does not reduce what the process has reserved. Physical RAM is what this optimises, and physical RAM is what the table measures.
Credit where due — these solve overlapping problems:
| Project | Approach | Notes |
|---|---|---|
| joric/bluetooth-battery-monitor | BLE GATT 0x180F |
Open source; BLE-only, so it misses BR/EDR-only headsets |
| aarol/headset-battery-indicator | USB HID via HeadsetControl | Open source, Rust, ~2 MB; wired/dongle gaming headsets, not Bluetooth |
| OpenSCQ30 | Soundcore proprietary RFCOMM | Full control app (ANC, EQ); not a tray indicator; battery in 5 steps |
| SoundcoreManager | RFCOMM | Rust GUI for Soundcore devices |
| Bluetooth Battery Monitor | closed source | Paid, US$5.99 |
This project's niche: HFP battery, tray-first, tiny, no dependencies.
Every line of this project — source, build script, documentation, images — was written by Claude, an AI assistant. A human set the goals, tested on real hardware, and decided what to build; they wrote none of the code.
This is disclosed prominently because you deserve to know what you're running. Read AI-DISCLOSURE.md for the full account: what that means for code quality, what was empirically verified versus assumed, and the known limits.
Contributions are very welcome — especially device reports, which are the single most useful thing you can add. See CONTRIBUTING.md.
Quick links: report a bug · report a device · request a feature
MIT — do what you like.


0 comments
log in to comment.