Skip to content

Instantly share code, notes, and snippets.

@NightMachinery
Last active December 6, 2024 21:21
Show Gist options
  • Save NightMachinery/402863ccf37515a804b2f5fa15672375 to your computer and use it in GitHub Desktop.
Save NightMachinery/402863ccf37515a804b2f5fa15672375 to your computer and use it in GitHub Desktop.
A guide on limiting macOS battery charging to at most 80%.

Why?

See BU-808: How to Prolong Lithium-based Batteries.

Related Tools

Several of the following are also compatible with Intel machines, although their functionality may be limited to when the laptop lid is open. For detailed information, please consult their respective documentation.

Limiting Apple Silicon macOS charging to 80%:

1. Install smc:

To install smc on your macOS, follow these steps:

# Install smcfancontrol using Homebrew
brew install --cask smcfancontrol

# Copy the 'smc' binary to /usr/local/bin/ for global access
sudo cp '/Applications/smcFanControl.app/Contents/Resources/smc' /usr/local/bin/

2. Update sudo permissions:

To avoid entering your password every time you use the smc command with sudo, you can update the sudoers file.

  1. Open the sudoers file using the visudo command:
sudo visudo

Or if you don't know vi/vim:

sudo VISUAL=nano visudo
  1. Scroll to the bottom of the file and add the following line. Replace username with your actual macOS username:
username ALL=(ALL) NOPASSWD: /usr/local/bin/smc
  1. Save and exit the editor.

3. Using the Zsh Functions:

Make sure you have added the provided Zsh functions in battery_limit.zsh to ~/.zshenv or another Zsh configuration file.

  • To enable the battery charge limit:

    battery-charge-limit-enable
  • To disable the battery charge limit:

    battery-charge-limit-disable
  • To check the status of the battery charge limit:

    battery-charge-limit-status

With these functions in place and smc installed, you can easily manage the battery charge limit of your macOS device from the terminal.

Automate Charge Limiting on Startup:

To ensure that the battery charge limit is applied automatically after each system restart, you can use cron, a time-based job scheduler, to execute the necessary command upon startup. Here's how to set it up:

  • Ensure that the Zsh helper functions (battery-charge-limit-enable, battery-charge-limit-disable, and battery-charge-limit-status) are defined in ~/.zshenv since this file is sourced by all instances of Zsh, including non-interactive shells that run during the startup process. If you had previously placed the functions in ~/.zshrc, please move them to ~/.zshenv.

  • Open the terminal and type the following command to find out where zsh is installed:

which zsh

The command will output the path to zsh, which usually is /bin/zsh. Note this path as you will need it for the crontab entry.

  • Open the terminal and access the crontab configuration file by typing the following command:
crontab -e
  • If you're not familiar with vi or vim, the default editors in the terminal, you might want to switch to a more user-friendly editor temporarily. To use nano as the editor for crontab, for instance, use this instead:
VISUAL=nano crontab -e
  • In the crontab editor, set the SHELL variable to ensure that zsh is used. Then, add a new line to specify the job that should run at reboot:
SHELL="/bin/zsh"  # Replace "/bin/zsh" with the actual path if different

@reboot battery-charge-limit-enable
  • Save and exit the editor. If you're using nano, you can do so by pressing CTRL+X, then Y to confirm the changes, and Enter to write to the file.

Compatibility

This needs an updated firmware on an Apple Silicon machine.

# Apple Silicon laptops with firmware > 13.0 have a native charge threshold that does not required any userspace daemon running.
# This native limit works even when the laptop is sleeping or powered off therefore it is preferable to the userspace daemon.
# Nonetheless, it only works with fixed thresholds (80% as upper limit and 70% as lower limit).
# CHWA key is the one used to enable/disable the native limit. 01 = 80% limit, 00 = no limit
##
typeset -g smc_command="/usr/local/bin/smc"
typeset -g smc_charge_limit_key="CHWA"
typeset -g smc_charge_limit_status_on="01"
typeset -g smc_charge_limit_status_off="00"
function battery-charge-limit-enable {
if [[ "$(uname)" == "Darwin" ]]; then
if [[ ! -e "${smc_command}" ]]; then
echo 'SMC command not found!'
return 1
fi
sudo "${smc_command}" -k "${smc_charge_limit_key}" -w "${smc_charge_limit_status_on}"
else
echo "Not a Darwin system."
fi
}
function battery-charge-limit-disable {
if [[ "$(uname)" == "Darwin" ]]; then
if [[ ! -e "${smc_command}" ]]; then
echo 'SMC command not found!'
return 1
fi
sudo "${smc_command}" -k "${smc_charge_limit_key}" -w "${smc_charge_limit_status_off}"
else
echo "Not a Darwin system."
fi
}
function battery-charge-limit-status {
if [[ "$(uname)" == "Darwin" ]]; then
if [[ ! -e "${smc_command}" ]]; then
echo 'SMC command not found!'
return 1
fi
local status_raw="$(sudo "${smc_command}" -k "${smc_charge_limit_key}" -r)"
# Extract the bytes using regex
if [[ "$status_raw" =~ 'bytes ([0-9]+)' ]]; then
status_raw=$match[1]
fi
case "$status_raw" in
"${smc_charge_limit_status_on}")
echo "on"
;;
"${smc_charge_limit_status_off}")
echo "off"
;;
*)
echo "Unknown ${smc_charge_limit_key} status: $status_raw"
;;
esac
else
echo "Not a Darwin system."
fi
}
@fangar
Copy link

fangar commented Dec 6, 2024

This has worked fine for me until today after an update to Sonoma 14.7.1.

battery-charge-limit-status gives this response:
Unknown CHWA status: CHWA [ ] no data

battery-charge-limit-enable gives this response:
Error: SMCWriteKey() = e00002bc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment