My laptop sits on a desk for 90% of its life with the lid open and the AC plugged in. I leave the house with it maybe twice a week. The battery's job, almost all the time, is to do nothing. It is a backup power supply for the rare unplug, and that is fine.
Lithium-ion batteries age with time and use. Temperature, state of charge, chemistry, and charging patterns all matter. Holding a battery at a high charge level can accelerate calendar ageing, but counting every small top-up as equivalent damage is too simple. I chose a lower charge target for a laptop that spends most of its time on AC; that is a usage choice, not a measured lifetime improvement from this tool.
My first attempt was to set charge thresholds: start at 55%, stop at 60%. I noticed warmth and repeated top-ups in that setup. I originally attributed the reported drop to ordinary self-discharge, but a five-percentage-point fall every few hours is not established by the information in this article. System load, firmware behaviour, and the charge gauge can also affect what is reported. This observation does not prove that threshold charging inherently causes rapid cycling.
I wanted a third state above thresholds: stop charging entirely while AC is connected, and leave it uncharged until it falls below a chosen lower threshold.
Linux exposes a charge_behaviour sysfs attribute on supported drivers. The kernel ABI documentation dates the interface to November 2021 and defines auto, inhibit-charge, and force-discharge. Not every device exposes every option. Inhibit mode asks the hardware to stop charging; it does not freeze the battery's charge level or eliminate self-discharge. Check the values exposed by your own battery device, whose name may differ from BAT0.
The feature was available on my machine, and I wanted a small policy layer around it. Other power-management tools also have model-specific battery controls; it would be wrong to imply that they all ignore this interface. TLP's hardware documentation describes support and limitations by driver and model. Avoid letting two tools repeatedly overwrite the same charge settings.
I then wrote inhibit-charge to put a small, persistent layer on top. It is roughly 150 lines of bash. The daemon spends its time waiting on udev events and writing a mode string to sysfs, so there is no hot path that would justify a compiled binary. A short shell script is convenient for me to inspect directly, which matters more to me for a tool that writes to a battery management interface.
The daemon watches udevadm monitor --subsystem-match=power_supply and reacts to plug, unplug, capacity, and CLI signals when they are delivered. The CLI writes the desired state (home mode at 60% by default, or travel mode at 100%) to /var/lib/inhibit-charge/ and signals the daemon over SIGHUP. Events avoid waiting for the fallback polling interval, but handling is not instantaneous or guaranteed by every driver. A slow 5-minute fallback timer handles the case where the cell self-discharges past the recharge band while parked.
The recharge band scales with the target: max(2, target/10). So a target of 60 parks in 54..60, a target of 25 parks in 23..25. As a safety net, the same band is also written to the firmware-level start and end thresholds. If the driver accepted those thresholds, firmware may continue applying them after the daemon stops. That depends on the machine and whether another tool changes the settings. Belt and suspenders.
The interface is two modes, because a laptop has two relevant states: "I am at my desk" and "I am leaving the house." Anything richer (schedules, time-of-day rules, calendar integration) is overengineering for what is, in practice, a binary. inhibit-charge home [target] parks at the target when plugged in. inhibit-charge travel charges to 100% before you leave. inhibit-charge status shows what is happening. journalctl -u inhibit-charged -f shows why. That is the entire interface.
Support depends on the specific laptop, firmware, and kernel driver. I do not have evidence for the earlier estimate that a third of laptop drivers expose this feature, or for a blanket promise about an entire brand. Check whether your battery exposes a writable charge_behaviour with inhibit-charge. The daemon checks for the required interface before proceeding.
If the mode is unavailable, firmware or TLP charge thresholds may still be an option where the hardware supports them.
The battery is no longer warm. It sits around 60% for weeks at a time. When I unplug for a meeting it discharges normally. When I plug back in, the daemon decides what to do based on where the level landed: above 60% it ignores AC and lets the pack idle; between 54% and 60% it also does nothing, holding the charge as it is; only if the level dipped below 54% does it allow a short top-up back to 60%, then stop again. The hysteresis band keeps the charger from flapping on and off near the target. The result is the most boring battery behavior I have ever observed, which is exactly what I want from a battery.
I have not measured the long-term effect on battery life. The tool changes charging policy; it does not establish that parking outperforms properly configured thresholds on every machine. The two approaches can use the same recharge band. Consult the laptop's battery guidance, and judge this tool by whether it applies the behaviour you want on supported hardware.
The kernel had the feature. Userspace just needed to use it.
For the general battery-care advice, Microsoft recommends avoiding sustained high charge and heat. That supports the motivation for charge limits; it does not measure the benefit of this script over a firmware threshold. This live source was added on 15 October 2026.
