You can find this documented in many places on the internet. This note is just a backup for the future me.
Looking at /sys/class/power_supply/BAT0/capacity_level, I see that the capacity is stored in 0-100 percentage values. This makes things easier.
What we need to modify are the following:
- Set when to stop charging:
/sys/class/power_supply/BAT0/charge_stop_threshold
- Set until when the battery should start charging when plugged in:
/sys/class/power_supply/BAT0/charge_start_threshold
The charge_start_threshold used to confuse me. So a table would help.
Assume that charge_start_threshold is 50, and charge_stop_threshold is 100.
| Current Battery Level | Current Battery Status | New Battery Status |
| 45 | Discharging | Charging |
| 45 | Charging | Charging |
| 55 | Discharging | Not Charging |
| 55 | Charging | Charging |
The key one is the last two rows, if the battery level is above the charge_stop_threshold and is unplugged, it would not charge the battery. But If the batter was already charging and hit the charge_start_threshold, it would continue to charge until charge_stop_threshold is met.
I also noticed that when the charge_start_threshold needs to be changed, the kernel would not allow setting it above charge_stop_threshold. The reverse is also true.
Keeping it all in mind, I will set three battery profiles.
| Profile | Charge start | Charge stop |
| Home | 45 | 50 |
| Away | 75 | 80 |
| Full | 95 | 100 |
Like all my other utility scripts, this will also use fzf.
Selecting profile
profile=$(printf 'Home\t45%%\t50%%\nAway\t75%%\t80%%\nFull\t95%%\t100%%' | fzf --height=6 --header="$(printf 'Type\tStart\tMax')" --layout=reverse --prompt='Charge level > ' | cut -f1)
This gives me a tabular view of the profiles and charge level in case I forget the values.
Functions to set charge levels
set_start() {
echo "${1}" | sudo tee /sys/class/power_supply/BAT0/charge_start_threshold >/dev/null
}
set_stop() {
echo "${1}" | sudo tee /sys/class/power_supply/BAT0/charge_stop_threshold >/dev/null
}
Now that I am looking at this, I probably should remove sudo dependency from the script. But I use it excessively in my daily life, feel free to remove it or replace it with other tools.
Set charge level based on selected profile
if [ "${profile}" = "Home" ]; then
set_start 45
set_stop 50
elif [ "${profile}" = "Away" ]; then
start=$(head -n 1 /sys/class/power_supply/BAT0/charge_start_threshold)
# Handle charge level conflict
if [ "${start}" -gt 75 ]; then
set_stop 80
set_start 75
else
set_start 75
set_stop 80
fi
elif [ "${profile}" = "Full" ]; then
set_stop 100
set_start 95
else
exit 137
fi
The complete script
Combine them all, and ensure POSIX compatibility.
#!/usr/bin/env sh
profile=$(printf 'Home\t45%%\t50%%\nAway\t75%%\t80%%\nFull\t95%%\t100%%' | fzf --height=6 --header="$(printf 'Type\tStart\tMax')" --layout=reverse --prompt='Charge level > ' | cut -f1)
set_start() {
echo "${1}" | sudo tee /sys/class/power_supply/BAT0/charge_start_threshold >/dev/null
}
set_stop() {
echo "${1}" | sudo tee /sys/class/power_supply/BAT0/charge_stop_threshold >/dev/null
}
if [ "${profile}" = "Home" ]; then
set_start 45
set_stop 50
elif [ "${profile}" = "Away" ]; then
start=$(head -n 1 /sys/class/power_supply/BAT0/charge_start_threshold)
if [ "${start}" -gt 75 ]; then
set_stop 80
set_start 75
else
set_start 75
set_stop 80
fi
elif [ "${profile}" = "Full" ]; then
set_stop 100
set_start 95
else
exit 137
fi