Keep MacBook running with lid closed for specified duration

Solution 1:

Github user iccir has made a very handy little menubar app called Fermata that does exactly what you want: keeps a MacBook awake with the lid closed, and allows you to set a timeout duration.

I just tried it on Mojave (10.14.2) and it worked great for me. https://github.com/iccir/Fermata

Solution 2:

It's not ideal, but here's a solution. To prevent the laptop from sleeping when the lid is closed and you're running on battery, run the following commands:

sudo pmset -b sleep 0; sudo pmset -b disablesleep 1

To re-enable laptop sleeping when the lid is closed and you're running on battery, run the following commands:

sudo pmset -b sleep 5; sudo pmset -b disablesleep 0

The "5" in the second set of commands represents the number of minutes before sleeping when on battery; adjust as desired for your laptop.

This is a bit dangerous, since if you forget to re-enable your settings, the laptop will never sleep when on battery. Because of this, I've written a shell script to automatically re-enable the settings:

#!/bin/bash
#***************************************************************************
#*** noz - prevent laptop from sleeping when lid is closed
#***************************************************************************

#***** set some defaults *****
BATTERY_SLEEP=5 # in minutes
DEF_WAKE_LEN=300 # in seconds

#***** determine timeout value *****
timeout_len=${1:-$DEF_WAKE_LEN}

function prevent_sleep() {
    echo
    echo -n "Preventing sleep for $timeout_len seconds; press <enter> to continue..."

    sudo pmset -b disablesleep 1
    sudo pmset -b sleep 0
}

function enable_sleep() {
    # $1: <enter> = 0, timeout = 1, Ctrl-C = undef

    #----- insert a newline for timeout or Ctrl-C -----
    if [[ ${1:-1} -eq 1 ]]; then    echo; fi
    echo "Restoring previous battery sleep setting: $BATTERY_SLEEP"

    sudo pmset -b disablesleep 0
    sudo pmset -b sleep $BATTERY_SLEEP

    #----- sleep on timeout only -----
    if [[ ${1:--1} -eq 1 ]]; then   sudo pmset sleepnow; fi
    exit
}

#***** prevent it from sleeping *****
prevent_sleep

#***** trap Ctrl-C *****
trap enable_sleep INT

#***** wait for an enter *****
read -t $timeout_len
rc=$?

#***** re-enable normal sleep *****
enable_sleep $rc

The shell script will disable sleeping until you hit the Enter key, at which point it will re-enable the sleep settings (alternately, you can hit Ctrl-C and achieve the same thing). It will also set a timeout (defaults to 300 seconds/5 minutes) after which the sleep settings will automatically be re-enabled, and the laptop will be forced to go to sleep. While this would be a pain if you're using your laptop in a meeting, it will be a lifesaver if you forgot and put your laptop in your bag to go home.

Astute readers will note that these commands require sudo; sadly, that's unavoidable AFAIK. What I've done on my system is to make it so that I don't have to enter my password to run pmset as root. To do that, edit the sudoers file (sudo visudo) and add this line:

joe ALL=(ALL) NOPASSWD: /usr/bin/pmset

replacing "joe" with your username. You could probably achieve the same result (i.e. running the script without having to enter your password) by running the shell script SETUID, but I don't like doing that; opening up this one command via sudoers seems less risky to me.

To run the script, stick it in a directory on your PATH and invoke it as such:

$ noz [<timeout in seconds>]

When you get to where you're going, simply hit Enter or Ctrl-C and you're good to go. And if you forget about it, it will automatically reset and sleep.

There's probably a way to achieve all of this via AppleScript, so that you can then assign it a hot key and what not; I'll try that if I even get tired of running this from the command line.