Skip to content

Instantly share code, notes, and snippets.

@evokateur
Last active July 23, 2025 07:50
Show Gist options
  • Save evokateur/d9d8978f4d3449b749d71d7e9520dd8e to your computer and use it in GitHub Desktop.
Save evokateur/d9d8978f4d3449b749d71d7e9520dd8e to your computer and use it in GitHub Desktop.
A script to set the bclm to allow full battery charge, report on the charging progress, then reset the bclm when charging stops.

For limiting a MacBook battery's charge to prolong its life I find bclm a lot more straightforward than AlDente.

sudo bclm write 81 keeps the hardware battery percentage at 80% on both my MacBooks (I haven't found a reason to use persist, yet).

I would often set the bclm to 100 some period of time before taking one to a coffee shop. With this script I don't have to remember to reset it.

I currently combine it with telling DropBox to quit, which seems to smooth out issues with the machine orienting itself to the coffee shop's WiFi while waking up.

#!/bin/bash

if [ "$(id -u)" -ne 0 ]; then
    echo 'script must be run by root' >&2
    exit 1
fi

echo "starting full charge cycle.."
bclm-cc || exit 1

echo "telling dropbox to quit.."
osascript -e 'tell application "Dropbox" to quit'

echo "undock!, undock!"

Thanks to a tip from this very informative gist, I now have passwordless sudo set up for this script.

sudo visudo

..opens /etc/sudoers in vim. I added this to the bottom:

my-username ALL=(ALL) NOPASSWD: /Users/my-username/.bin/bclm-cc

Now sudo bclm-cc no longer requires a password.

#!/bin/sh
if [ "$(id -u)" -ne 0 ]; then
echo 'script must be run by root' >&2
exit 1
fi
if ! type bclm >//dev/null 2>&1; then
echo 'bclm command not found, exiting' >&2
exit 1
fi
starting_bclm=$(bclm read)
echo "current bclm is $starting_bclm"
has_battery() {
pmset -g batt | grep -q 'InternalBattery'
}
is_not_plugged() {
pmset -g batt | grep -q 'Battery Power'
}
is_plugged_and_not_charging() {
pmset -g batt | grep -q 'AC Power' && pmset -g batt | grep -Eq 'charged|finishing|not charging'
}
is_plugged_and_charging() {
pmset -g batt | grep -q 'AC Power' && pmset -g batt | grep -q 'charging' && ! pmset -g batt | grep -q 'not charging'
}
get_bat_pct() {
current_capacity=$(ioreg -l | grep -o '"CurrentCapacity" = [0-9]*' | awk '{print $3}')
max_capacity=$(ioreg -l | grep -o '"MaxCapacity" = [0-9]*' | awk '{print $3}')
percentage=$(echo "($current_capacity * 100) / $max_capacity" | bc)
echo "$percentage"
}
get_bat_status() {
if is_not_plugged; then
echo "discharging at $(get_bat_pct)%"
elif is_plugged_and_not_charging; then
echo "not charging at $(get_bat_pct)%"
elif is_plugged_and_charging; then
echo "charging at $(get_bat_pct)%"
else
echo "unknown"
fi
}
reset_bclm() {
if [ -z "$starting_bclm" ]; then
return 1
fi
if [ "$starting_bclm" -eq 100 ]; then
return 1
fi
bclm write "$starting_bclm"
}
wait_for() {
func="$1"
timeout_seconds=10
elapsed_seconds=0
while [ $elapsed_seconds -lt "$timeout_seconds" ]; do
if "$func"; then
return 0
fi
sleep 1
elapsed_seconds=$((elapsed_seconds + 1))
done
return 1
}
reset_bclm_and_report() {
reset_bclm && echo "bclm reset to $starting_bclm"
echo "battery $(get_bat_status)"
}
handle_interrupt() {
printf '\ninterrupted..\n' >&2
reset_bclm
echo "bclm reset to $starting_bclm, waiting for effect.."
wait_for "is_plugged_and_not_charging"
echo "battery $(get_bat_status)"
exit 1
}
if ! has_battery; then
echo 'no battery found, exiting' >&2
exit 1
fi
if is_not_plugged; then
echo 'not plugged in, exiting' >&2
exit 1
fi
if [ "$starting_bclm" -eq 100 ]; then
echo 'nothing to do, exiting' >&2
exit 1
fi
trap handle_interrupt INT TERM
bclm write 100
echo "bclm set to 100, waiting for effect.."
if ! wait_for "is_plugged_and_charging"; then
echo "waiting timed out.."
reset_bclm_and_report
exit 1
fi
while is_plugged_and_charging; do
printf "\rbattery charging at %s.." "$(get_bat_pct)%"
sleep 5
done
echo
if is_not_plugged; then
echo 'battery unplugged..' >&2
reset_bclm_and_report
exit 1
fi
echo "charging is stopped"
reset_bclm_and_report
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment