Skip to content

Instantly share code, notes, and snippets.

@evokateur
Last active March 20, 2026 00:37
Show Gist options
  • Select an option

  • Save evokateur/d9d8978f4d3449b749d71d7e9520dd8e to your computer and use it in GitHub Desktop.

Select an option

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 for a period of time to get a full charge 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 open waking up, as well as connecting the VPN ahead of time.

#!/bin/bash

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
}

vpn_is_connected() {
    mullvad status | grep "Connected" >/dev/null
}

tell() {
    local some_app="$1"
    local what="$2"

    is_running() {
        pgrep -x "$1" >/dev/null
    }

    case "$what" in
    quit)
        is_running "$some_app" || return 0
        ;;
    activate)
        ! is_running "$some_app" || return 0
        ;;
    esac

    echo "telling $some_app to $what.."
    osascript -e "tell application \"$some_app\" to $what" || return 1
}

tell "Dropbox" "activate" || exit 1

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

tell "Docker Desktop" "quit" || exit 1
tell "Dropbox" "quit" || exit 1

if ! vpn_is_connected; then
    # tell_app "Mullvad VPN" "activate"
    echo "telling mullvad to connect.."
    mullvad connect || exit 1
fi

echo "checking vpn connection.."
if wait_for "vpn_is_connected"; then
    echo "vpn connected; undock!, undock!"
else
    echo "vpn failed to connect" >&2
    exit 1
fi

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

sudo visudo

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

[my username] ALL=(ALL) NOPASSWD: /usr/local/bin/bclm

Now sudo bclm no longer requires a password.

#!/bin/sh
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
sudo 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'
reset_bclm
echo "bclm reset to $starting_bclm, waiting for effect.."
wait_for "is_plugged_and_not_charging"
echo "battery $(get_bat_status)"
exit 0
}
if ! type bclm >//dev/null 2>&1; then
echo 'bclm command not found, exiting' >&2
exit 1
fi
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
starting_bclm=$(bclm read)
echo "current bclm is $starting_bclm"
if [ "$starting_bclm" -eq 100 ]; then
echo 'nothing to do, exiting'
exit 0
fi
trap handle_interrupt INT TERM
sudo bclm write 100
echo "bclm set to 100, waiting for effect.."
if ! wait_for "is_plugged_and_charging"; then
echo "charging did not start"
reset_bclm_and_report
exit 0
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