Update 2022-09-24:
Auto-poweroff before the battery is no longer recoverable by the phone has been implemented in several distros recently. Check with your distro to see if you need this solution.
I created a lowpoweroff
script for the Pinephone and Pinephone Pro to help prevent over-discharge by forcing a power-off below a certain capacity.
It loops through all power sources the kernel is aware of and enforces a minimum capacity rule that powers off the system if a battery is below a certain threshold (default: 5%) and not charging.
Install the lowpoweroff script
# Create /usr/local/sbin if it doesn't exist
# and set privileges if it's been newly created
sudo mkdir /usr/local/sbin && sudo chmod 755 /usr/local/sbin
# Install lowpoweroff
sudo touch /usr/local/sbin/lowpoweroff
sudo chmod 755 /usr/local/sbin/lowpoweroff
sudo vi /usr/local/sbin/lowpoweroff
# Paste the following:
#!/usr/bin/env bash
# License: BSD 0-Clause, Ulfnic
Err(){
printf '%s\n' "$2" 1>&2
[ $1 -gt 0 ] && exit $1
}
while [ "$1" ]; do
case $1 in
--test|-t)
Test=1 ;;
--min-capacity|-m)
MinCapacity=$2
shift ;;
*)
Err 1 "Unrecognized option: $1" ;;
esac
shift
done
# Apply default to minimum battery capacity and validate
: ${MinCapacity:=5}
Re='^[0-9][0-9]?0?$'
[[ $MinCapacity =~ $Re ]] || Err 1 "Unrecognized minimum battery capacity: $MinCapacity"
BatteryPathCount=0
for BatteryPath in /sys/class/power_supply/*; do
[[ $Test == 1 ]] && printf '%s\n%s\n' "=== $((BatteryPathCount++)) ===" "Battery: $BatteryPath"
if [[ ! -f "$BatteryPath/type" ]]; then
[[ $Test == 1 ]] && printf '%s\n' "Skipping, battery type file not found at: $BatteryPath/type"
continue
fi
BatteryType=$(cat "$BatteryPath/type")
[[ $? > 0 ]] && continue
[[ $Test == 1 ]] && printf '%s\n' "Type: $BatteryType"
if [[ $BatteryType == 'Battery' ]]; then
# Fetch and act on battery status
if [[ ! -f "$BatteryPath/status" ]]; then
[[ $Test == 1 ]] && printf '%s\n' "Skipping, battery status file not found at: $BatteryPath/status"
continue
fi
BatteryStatus=$(cat "$BatteryPath/status")
[[ $? > 0 ]] && continue
[[ $Test == 1 ]] && printf '%s\n' "Status: $BatteryStatus"
if [[ $BatteryStatus == 'Discharging' ]] || [[ $Test == 1 ]]; then
# Obtain battery capacity and poweroff if it's below minimum threshold
if [[ ! -f "$BatteryPath/capacity" ]]; then
[[ $Test == 1 ]] && printf '%s\n' "Skipping, battery status file not found at: $BatteryPath/capacity"
continue
fi
BatteryCapacity=$(cat "$BatteryPath/capacity")
[[ $? > 0 ]] && continue
[[ $Test == 1 ]] && printf '%s\n%s\n' "Capacity: $BatteryCapacity" "Minimum Capacity: $MinCapacity"
# Validate battery capacity format
Re='^[0-9][0-9]?0?$'
if [[ ! $BatteryCapacity =~ $Re ]]; then
[[ $Test == 1 ]] && printf '%s\n' "Skipping, battery capacity format is incompatible: $BatteryCapacity"
continue
fi
if [[ $BatteryCapacity < $MinCapacity ]]; then
if [[ $Test == 1 ]]; then printf '%s\n' "Battery below minimum capacity, this would trigger poweroff"
elif [ -f '/usr/sbin/poweroff' ]; then /usr/sbin/poweroff
elif [ -f '/sbin/poweroff' ]; then /sbin/poweroff
else Err 1 'poweroff not found'
fi
fi
fi
fi
done
Test lowpoweroff script
Normally lowpoweroff
is quiet unless there’s an error. Adding --test performs a dry-run and displays a readout of every power supply value it’s reading. If the script is working and USB is plugged out, this test should say “Battery below minimum capacity, this would trigger poweroff”.
# Plug in USB power
sudo /usr/local/sbin/lowpoweroff --test --min-capacity 100
# Unplug USB power
sudo /usr/local/sbin/lowpoweroff --test --min-capacity 100
Make it a daemon
lowpoweroff
needs something to run it once every minute. Choose from Option 1 or 2 below this post for the method most suitable to your operating system.
If you’d like to set it up differently, here’s some example commands you might want to use:
# Auto-poweroff at the default 5% capacity
/usr/local/sbin/lowpoweroff
# Auto-poweroff at 3% capacity
/usr/local/sbin/lowpoweroff --min-capacity 3