This guide provides a workaround for macOS battery drain issues during sleep by automatically disabling Bluetooth and WiFi when the system sleeps and re-enabling them on wake.
macOS currently has issues with battery draining during sleep when Bluetooth and WiFi remain active.
Use sleepwatcher to run scripts that:
- Disable Bluetooth and WiFi when the system goes to sleep
- Re-enable Bluetooth and WiFi when the system wakes up
- Install Homebrew (if not already installed):
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"- Install sleepwatcher:
brew install sleepwatcher- Install blueutil (for Bluetooth control):
brew install blueutilCreate ~/.sleep file:
cat > ~/.sleep << 'EOF'
#!/bin/bash
exec >> ~/lid-events.log 2>&1
echo "Lid closed or system sleeping at $(date)"
# Print battery level
echo "Battery level $(pmset -g batt | grep -Eo '\d+%' | cut -d% -f1)%"
# Run commands in background with nohup to avoid blocking sleep
nohup sh -c '/opt/homebrew/bin/blueutil --power 0 2>/dev/null; networksetup -setairportpower en0 off 2>/dev/null' &>/dev/null &
# Exit immediately to let system sleep
exit 0
EOFCreate ~/.wakeup file:
cat > ~/.wakeup << 'EOF'
#!/bin/bash
exec >> ~/lid-events.log 2>&1
echo "Lid opened or system woke up at $(date)"
# Print battery level
echo "Battery level $(pmset -g batt | grep -Eo '\d+%' | cut -d% -f1)%"
/opt/homebrew/bin/blueutil --power 1; networksetup -setairportpower en0 on
EOFMake scripts executable:
chmod +x ~/.sleep ~/.wakeupStart and enable the service to run at login:
brew services start sleepwatcherCheck that sleepwatcher is active:
brew services list | grep sleepwatcherYou should see:
sleepwatcher started [username] ~/Library/LaunchAgents/homebrew.mxcl.sleepwatcher.plist
-
Terminal/iTerm Access: You may need to grant Terminal or your terminal app permission to control system settings
- Go to System Settings → Privacy & Security → Accessibility
- Add and enable your terminal application
-
Network Configuration Access: The
networksetupcommand may trigger a permission dialog- Click "Allow" when prompted
-
Automation Permissions: You might need to approve automation access
- Go to System Settings → Privacy & Security → Automation
- Enable necessary permissions for Terminal
Check the log file to verify it's working:
tail -f ~/lid-events.logYou should see entries like:
Lid closed or system sleeping at [timestamp]
Lid opened or system woke up at [timestamp]
After sleep/wake, verify the radios are being controlled:
# Check Bluetooth status
blueutil --power
# Check WiFi status
networksetup -getairportpower en0- Check sleepwatcher is running:
brew services list | grep sleepwatcher- Restart the service:
brew services restart sleepwatcher- Check for errors in system log:
log show --predicate 'process == "sleepwatcher"' --last 1hThis setup uses nohup and background execution to prevent immediate wake. If you still experience issues:
- Verify the
.sleepscript has the correct content with background execution - Ensure you're using the exact script format provided above
- Check that
blueutilis installed at/opt/homebrew/bin/blueutil
If you want to stop using this workaround:
brew services stop sleepwatcherTo completely uninstall:
brew services stop sleepwatcher
brew uninstall sleepwatcher
rm ~/.sleep ~/.wakeup- sleepwatcher: Monitors system sleep/wake events and triggers scripts
- Background execution: Using
nohupand&prevents the commands from blocking the sleep process - Immediate exit: The script exits quickly, allowing the system to complete the sleep transition
- Logging: All events are logged to
~/lid-events.logfor troubleshooting
This is a temporary workaround until Apple fixes the underlying battery drain issue in macOS. The solution has been tested and confirmed to work without causing immediate wake issues.
Thanks for putting this together.
Instead of
/opt/homebrew/bin/blueutil --power 1 && networksetup -setairportpower en0 on, it could be/opt/homebrew/bin/blueutil --power 1; networksetup -setairportpower en0 onso that if there is any issue turning on the Bluetooth, the next step goes on, like how the turn-off events run.