Skip to content

Repository files navigation

Keychron Q6 Max Battery Widget

A Windows system tray widget that gives you an actual sense of your Keychron Q6 Max's battery level — something Keychron's own Launcher app and firmware simply don't provide over the 2.4GHz dongle.

Why this exists

Keychron makes genuinely excellent keyboards. Their software is a different story: connect a Q6 Max over its 2.4GHz "Keychron Link" dongle and there is no battery indicator anywhere — not in Launcher, not in Windows, nothing. The only feedback is a blinking red LED under the spacebar once the battery is already critically low, with no way to tell if you're at 80% or 15% before that point.

This project reverse-engineers what's actually possible and ships a practical answer:

  • On 2.4G (your daily driver, presumably — lower latency than Bluetooth): a background-estimated battery percentage, modeled from how long the keyboard has been active vs. idle/asleep and Keychron's published battery-life figures.
  • On Bluetooth (switch to it for a moment): an exact reading, pulled from the same battery data Windows already uses for its own Bluetooth device battery display.

The full investigation — including why the "obvious" approach (the documented Keychron mouse battery protocol, adapted for this keyboard) does not work — is in research/. Short version: this keyboard's current firmware doesn't answer a battery query over the 2.4G dongle, confirmed by protocol testing, by reading Keychron Launcher's own source, and by Launcher's UI showing no battery indicator even wired via USB-C. Bluetooth's standard GATT Battery Service works immediately.

How it works

  • 2.4G estimate: polls Windows' system-wide idle timer every 30s and buckets elapsed time into three draw-rate tiers (active/RGB-on, backlight-off-but-awake, fully asleep), based on your keyboard's actual configured timeouts and Keychron's published ~80h (RGB on) / ~180h (RGB off) battery-life figures for a 4000mAh Q6 Max. This is a heuristic, not a real reading — it's clearly labeled as an estimate in the tray tooltip.
  • Bluetooth exact reading: rather than fight BLE GATT directly (flaky on Windows for already-paired devices), this reads the standard Windows PnP device property (DEVPKEY_Device_BatteryLevel) that Windows itself populates once your keyboard is paired over Bluetooth — the exact same number Settings → Bluetooth & devices shows you.
  • Self-recalibration: every time you do a real Bluetooth check, the heuristic estimate resets from that real value, so estimation drift from the 2.4G model doesn't compound indefinitely.

Features

  • Tray icon that visually fills/color-codes by battery level (green → amber → red), with a marker distinguishing an estimate from a real reading
  • Right-click menu: current estimate, on-demand exact Bluetooth check, "mark as fully charged," Windows autostart toggle
  • Persists state across restarts (%LOCALAPPDATA%\Keychron_Q6_Max_BatteryWidget)
  • No third-party services, no telemetry, everything local

Installation

Option A — prebuilt exe

Grab the latest .exe from Releases and run it. No installation, no Python required.

Note on antivirus false positives: this is an unsigned, self-contained PyInstaller executable, and Windows Defender's behavioral heuristics will likely flag it — specifically Behavior:Win32/Persistence.A!ml, triggered by the app writing itself into the Windows startup registry key when you enable "Start with Windows." This is a well-known false-positive pattern for small unsigned tray/autostart utilities and not a sign of anything malicious — see Troubleshooting below. If you'd rather not deal with it at all, build from source (Option B) and you'll see exactly what's running.

Option B — run/build from source

git clone <this-repo>
cd Keychron_Q6_Max_BatteryWidget
pip install -r requirements.txt
python main.py

To build your own standalone exe:

pip install pyinstaller
pyinstaller --noconsole --onefile --windowed --icon=assets\app.ico main.py -n "Keychron Q6 Max Battery Widget"

The exe will be in dist/.

Configuration

Battery-life figures and your keyboard's sleep/backlight timeouts live in config.py:

BATTERY_CAPACITY_MAH = 4000
HOURS_RGB_ON = 80.0
HOURS_RGB_OFF = 180.0
BACKLIGHT_OFF_TIMEOUT_MINUTES = 10   # match your keyboard's actual setting
AUTO_SLEEP_TIMEOUT_MINUTES = 30      # match your keyboard's actual setting

These are tuned for a stock Q6 Max. If you've changed your keyboard's sleep timers in Launcher (Advance Mode, requires a wired connection), update these to match — the estimate is only as good as these numbers. Other 4000mAh/similar Keychron Max-series boards will likely be close enough to use as-is; check your model's own published battery-life figures if you want a more accurate estimate.

Troubleshooting

Windows Defender quarantined the exe / flagged it as Behavior:Win32/Persistence.A!ml. This is a false positive — Defender's ML heuristics flag any unsigned exe that writes a Windows startup registry entry, since that's structurally how malware persists too. It's triggered by the "Start with Windows" feature specifically. To fix:

  1. Windows Security → Virus & threat protection → Protection history → find the detection → Actions → Restore.
  2. Add an exclusion for the folder you're running it from: Windows Security → Virus & threat protection → Manage settings → Exclusions → Add an exclusion → Folder.

If you're not comfortable with either, build from source instead (see above) — nothing about the source is doing anything you can't read for yourself in autostart.py (it's ~30 lines).

"Check real battery now" says the keyboard wasn't found / no battery level reported. The keyboard needs to be paired over Bluetooth at least once via Windows Settings first (the app doesn't pair devices, only reads already-paired ones), and its physical mode switch needs to be set to BT at the moment you check — 2.4G and Bluetooth are mutually exclusive radio modes on this keyboard, selected by a physical switch software can't override.

The 2.4G estimate seems off. It's a heuristic based on idle time, not a real reading — see Configuration. Do a real Bluetooth check occasionally (switch to BT, click "Check real battery now") to recalibrate it; accuracy between recalibrations depends on how well config.py's timeouts and hours-per-charge figures match your actual usage and RGB settings.

Two PowerShell windows flash when checking battery. Should already be fixed (ble_reader.py uses CREATE_NO_WINDOW) — if you still see this, you're likely running an older build; pull the latest source and rebuild.

Limitations

  • 2.4G mode does not expose battery. This is a hard limitation of the keyboard's current firmware / Keychron's dongle protocol, not something this app can fix in software — see research/ for the full investigation. If Keychron ever ships firmware that answers a battery query over 2.4G, this app could switch to a real live reading; contributions welcome if you get there first.
  • Heuristic accuracy depends on config.py matching your actual keyboard settings and how closely your usage matches Keychron's published best-case battery-life figures.
  • Windows only (uses winreg, ctypes.windll, and Windows PnP device properties throughout).

Contributing

This started as a personal fix for one very specific annoyance, but the underlying problem — Keychron's wireless boards giving you no real battery visibility — likely affects a lot of people.

If you'd like to contribute to this, please do! And if you'd like to chat about this project with me, or other ideas, drop by my twitch when I'm live or send me a message on here if you can!

https://www.twitch.tv/sasirawr

PRs, forks, and issues are welcome, especially if you:

  • Have a different Keychron 2.4G board and want to confirm/extend the protocol findings in research/
  • Want to build proper low-battery notifications, multi-device support, or a config UI instead of hand-editing config.py
  • Have real interest in the bigger goal this points toward: a genuinely open, cross-platform alternative to Keychron Launcher for on-device configuration (remapping, macros, lighting) that also isn't crippled over 2.4G. That's a much bigger project than this widget, but this repo is as good a place as any to start pulling that thread.

Credits / prior art

Built on protocol facts documented by:

The actual working protocol details for this keyboard were found by reading Keychron Launcher's own shipped JavaScript, not any of the above.

License

MIT — see LICENSE.

About

Tray widget that estimates/reads your Keychron Q6 Max's battery level on Windows - because Keychron's own software won't tell you.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages