Battery care from the system tray — charge limits, a live battery gauge, and smart standby control. ChargeKeeper runs on Lenovo ThinkPads today (it requires the Lenovo Power Management Driver) and is built to support more hardware over time.
Formerly published as Lenovo Power Tray. See Upgrading from Lenovo Power Tray.
Two power features, without opening the slow Lenovo Vantage app:
- Smart Charge — battery charge threshold, via the Lenovo Power Manager local-RPC interface
(the same one Lenovo Vantage uses), through a small native bridge (
LenPower.dll) - Smart Standby — Modern Standby scheduling, via the
LenovoSmartStandbyWindows service
Left-click the tray icon for a battery dashboard (arc gauge with live % and charge-rate, threshold tick markers, adjustable start/stop sliders); right-click for quick toggles, presets, and Settings… (opens the full configuration window). The tray icon itself shows a live battery-level arc, colour-coded green/orange/red — or, optionally, the battery percentage as a number.
- Threshold presets — named charging profiles (Daily 60–80%, Travel 80–100%), added/ renamed/deleted from the Settings window's Smart Charge section, quick-applied from the tray right-click Presets submenu.
- Charge to 100% once (travel override) — a tray menu item that temporarily lifts the charge threshold for one cycle, then auto-restores it once the battery reaches full. Survives an app restart mid-charge.
- Network-aware presets — automatically apply a preset when the detected network location (adapter MAC + IP subnet — never Wi-Fi network name) changes, e.g. a different preset at the office dock vs. on the road. Configured in the Settings window's Network section.
- Custom low-battery warning — a toast at a user-set battery % while discharging (Settings window → Notifications).
- Time remaining / time to full — the dashboard shows estimated time-to-full (charging) or time-remaining (discharging).
- Battery % history graph — a persistent graph of recent battery level in the dashboard, with selectable time scales.
- Configurable startup delay — wait N seconds before the app initialises at sign-in (Settings window → General).
- Numeric % tray icon — show the battery percentage as a number instead of the arc gauge (toggle in the tray menu, dashboard, and Settings window).
- Settings window — a proper titled window (tray icon → Settings…) with a General/Smart
Charge/Notifications/Network/Home Assistant/Appearance sidebar, replacing the old nested tray
submenus. Settings are stored as human-readable JSON at
%AppData%\ChargeKeeper\settings.json(tray icon → Open settings folder); an out-of-band edit to that file can be picked up without restarting via tray icon → Reload settings from file. Settings are portable across machines by copying this file; automatic cloud sync is not yet implemented.
This project was written entirely by an AI assistant ("vibe coded") through natural-language prompting — no line was hand-authored by a human. Treat it accordingly: it works on the author's machine, but it drives Lenovo's power-management RPC interface and a Windows service and comes with no warranty. Read the code before running it elevated on your own hardware.
winget (recommended):
winget install 0z00z0.ChargeKeeper
winget upgrade 0z00z0.ChargeKeeper # update laterOr grab ChargeKeeper-Setup.exe from the latest release
and run it. The installer is per-user — no admin needed to install — and offers two options:
- Run at startup — auto-starts the app at sign-in. Ticking it asks for elevation once, to register a prompt-free elevated logon task.
- Auto update in background — a logon task that silently runs
winget upgradeafter each sign-in, so the app keeps itself current. Needs no elevation, and only works once the package is available from a winget source (see installer/README.md).
The app itself shows a UAC prompt when it launches, since changing the charge threshold / standby service requires administrator rights.
ChargeKeeper is the same app under a new name. Upgrading is safe and mostly automatic:
- Settings and battery history migrate automatically. On first launch, ChargeKeeper moves your
old
%AppData%\LenovoPowerTrayfolder to%AppData%\ChargeKeeper— settings, presets, and the battery history graph all carry over. - The installer cleans up after the old version. Running the ChargeKeeper installer over an existing Lenovo Power Tray install closes the old app, removes its old binaries and scheduled tasks, and upgrades in place.
- winget users: install the new ID once. The package identity changed, so the old package will
not auto-upgrade across the rename. Run:
and Windows' Apps list will show ChargeKeeper replacing the old entry.
winget install 0z00z0.ChargeKeeper
- Windows 10 (1809+) / Windows 11
- Currently a Lenovo ThinkPad (built and tested on an X1 Yoga Gen 7) — the charge-limit feature talks to Lenovo's power-management driver; support for other vendors is planned
- Administrator rights — both features require elevation (the app manifest declares
requireAdministrator)
Smart Charge talks directly to the Lenovo Power Management Driver (Windows service PWMGR,
"Lenovo Power and Battery") — the same service Lenovo Vantage uses under the hood.
You do not need Lenovo Vantage. You do need the driver.
It ships as part of the ThinkPad hardware driver package. If your laptop originally shipped with Windows (or has had a full driver installation) it is almost certainly already present.
To check — run in an elevated PowerShell:
Get-Service -Name PWMGR -ErrorAction SilentlyContinueIf the service appears (Running or Stopped), Smart Charge will work. If nothing is returned,
install the driver:
- Go to your model's driver page on Lenovo Support (search your model name / serial number, or use PC Support Auto-Detect).
- Select Drivers & Software → category Power Management.
- Download and run "Power Management Driver for Windows 10 and 11 (64-bit)".
If the driver is absent, Smart Charge shows as Unavailable in the tray and dashboard — the rest of the app (Smart Standby, auto-start, battery gauge) works fine without it.
- .NET 10 SDK
- A C++ toolset (Visual Studio / Build Tools with "Desktop development with C++") to build the
native Smart Charge bridge (
native/) — only needed once - A sibling clone of 0z0-shared (shared ZeroZero Software components — see Shared components below)
# 0. Clone this repo AND the shared components library as siblings (the csproj references
# ..\0z0-shared\src\ZeroZero.Brand.WinUI by relative path).
git clone https://github.com/0z00z0/ChargeKeeper.git
git clone https://github.com/0z00z0/0z0-shared.git
cd ChargeKeeper
# 1. Build the native Smart Charge bridge (LenPower.dll). One-time, needs the C++ toolset.
# build.cmd locates MSVC + MIDL automatically (incl. VS 2026 Insiders).
cd native
.\build.cmd
cd ..
# 2. Build the app (Release output is Authenticode-signed if a cert is set up — see USAGE.md).
# The csproj copies native\LenPower.dll next to the executable.
dotnet build -c Release
# 3. Run elevated
dotnet run
# or right-click the compiled ChargeKeeper.exe → "Run as administrator"Smart Standby, the dashboard, and auto-start work without the native bridge. If
LenPower.dllis missing, Smart Charge simply shows as Unavailable rather than breaking the app.
A UAC prompt appears on first launch. Enable Launch at startup from the right-click menu to auto-start prompt-free on subsequent boots (via a Task Scheduler logon task).
See USAGE.md for full usage, code-signing, troubleshooting, and architecture notes.
The per-user installer is built with Inno Setup and distributed via winget. See installer/README.md for the full release workflow:
winget install JRSoftware.InnoSetup # one-time
cd installer
.\build-installer.ps1 # auto-bumps patch (or pass -Version 1.2.0 explicitly)The app targets the Microsoft stack (.NET, Windows App SDK / WinUI 3, System.* runtime
packages). The only non-Microsoft dependencies are:
| Library | Author | Purpose | License |
|---|---|---|---|
| H.NotifyIcon.WinUI | HavenDV | System-tray icon + native context menu for WinUI 3 | MIT |
| TaskScheduler | David Hall | Managed wrapper over the Windows Task Scheduler API (auto-start) | MIT |
| CommunityToolkit.WinUI.Controls.RangeSelector | .NET Foundation | Dual-handle range slider (Smart Charge start/stop threshold) | MIT |
| CommunityToolkit.WinUI.Controls.SettingsControls | .NET Foundation | SettingsCard/SettingsExpander rows (Settings window) | MIT |
| WinUIEx | Morten Nielsen | WinUI 3 window helper extensions (Settings window placement) | MIT |
| MQTTnet | The MQTTnet Project | MQTT client for the Home Assistant integration | MIT |
| NLog | Jarek Kowalski, Kim Christensen, Julian Verdurmen | Event log with size/age-based rotation (app.log) | BSD-3-Clause |
ChargeKeeper can publish to Home Assistant over MQTT using HA's auto-discovery — no YAML on the HA side. When enabled it connects to your broker and creates a single ChargeKeeper device with entities for battery %, charge power (W), on-AC, the Smart Charge start/stop thresholds, and the adapter rating; an availability topic (with a Last-Will) marks it offline if the app stops.
It is off by default and never touches the network until you both enable it and set a broker host. Configure it from the tray icon → Settings… → Home Assistant — the enabled toggle applies immediately, and the broker host/port/username/password/TLS/discovery-prefix fields commit as a batch behind an Apply button (so the MQTT connection reconnects once per Apply, not per keystroke). The password is never logged or shown in any toast.
Settings can also be edited directly in %AppData%\ChargeKeeper\settings.json (tray icon → Open
settings folder), then picked up without restarting via tray icon → Reload settings from file:
The password lives only in your own local settings file.
The About window comes from 0z0-shared
(ZeroZero.Brand.WinUI.BrandAboutWindow, MIT) — the shared components library used across ZeroZero
Software apps, referenced as a sibling-folder ProjectReference (no NuGet package yet). How the
integration works:
- The app supplies data only (
BrandAboutOptions→AboutInfo: name, version, description, repo URL, external-library credits); the library owns all chrome, layout, and the brand typeface. OnCheckForUpdates(Func<Task<bool>>) plugs the app's own update flow into the window's Check for Updates button. ChargeKeeper always returnsfalse— it downloads the installer on a background task and exits itself when ready, so the window never needs to drive the exit (OnBeforeExitis therefore left unset).- CI clones the library pinned to an exact commit (see the
ZEROZEROBRAND_REFstep in.github/workflows/ci.yml); bump the pin deliberately after validating a new library version locally.
Smart Charge is the hard part: ThinkPad firmware does not expose the battery charge threshold through WMI, so it has to be driven over the Lenovo Power Manager's local-RPC interface — the same one Lenovo Vantage uses.
- LenPwrCtl by alandau (MIT) — the Power Manager RPC
interface in
native/pwrmgr.idl(endpoint, context handles, and theLpcGetChargeThreshold/LpcSetChargeThresholdprocedure layout) was reverse-engineered by this project and is reused here under its MIT license. This app would not be possible without it. Huge thanks. 🙏
The native/ bridge (lenpower.c) is a thin wrapper that exposes two flat exports over that
interface for the managed app to P/Invoke; the interface definition itself is alandau's work.
Tooling: the installer is built with Inno Setup by Jordan Russell & Martijn Laan (free, with attribution under its license), and distributed via winget (Microsoft, MIT).
MIT © ZeroZero Software (0z0.xyz) — you are free to use, modify, fork, and redistribute, including commercially, provided you keep the copyright and license notice. See LICENSE for the full text.
{ "HomeAssistantEnabled": true, "MqttBrokerHost": "homeassistant.local", // or the broker IP "MqttBrokerPort": 1883, "MqttUsername": "your-mqtt-user", "MqttPassword": "your-mqtt-password", // stored locally, same as any MQTT client "MqttUseTls": false, "MqttDiscoveryPrefix": "homeassistant" // must match HA's MQTT discovery prefix }