Twice in the past month, I came back to my (company-issued) MacBook after leaving it with the lid open overnight to find it refusing to start because its battery was completely drained, worrying me a little each time. I wondered why it wasn't going to sleep due to inactivity.
Finding the culprit with the
Recently, I've been exploring various macOS command-line configuration tools such as
defaults (see my previous post),
osascript etc. I finally discovered why my Mac wasn't sleeping when expected after seeing the output of the
$ pmset -g System-wide power settings: Currently in use: standbydelaylow 10800 standby 1 womp 1 halfdim 1 hibernatefile /var/vm/sleepimage proximitywake 1 powernap 1 gpuswitch 2 networkoversleep 0 disksleep 10 standbydelayhigh 86400 sleep 1 (sleep prevented by Intel(R) Power Gadget) hibernatemode 3 ttyskeepawake 1 displaysleep 10 tcpkeepalive 1 highstandbythreshold 50 acwake 0 lidwake 1
According to the man page,
pmset is a command which manipulates power management settings. The
-g flag (with no argument) will display the settings currently in use.
pmset will also reveal when there are processes interfering with any usual settings:
Whenever processes override any system power settings, pmset will list those processes and their power assertions in -g and -g assertions.
From the output above, I could see that the Intel(R) Power Gadget utility which I had been running to monitor my Mac's processor load and temperature was also preventing my system from sleeping! I hastily disabled it and hoped that my Mac would no longer drain its battery overnight again.
It's not over yet
This morning, I came back to an even bigger surprise: my MacBook was still awake, and this time, even the screen was still on! I immediately checked
pmset -g again:
$ pmset -g System-wide power settings: Currently in use: standbydelaylow 10800 standby 1 womp 1 halfdim 1 hibernatefile /var/vm/sleepimage proximitywake 1 powernap 1 gpuswitch 2 networkoversleep 0 disksleep 10 standbydelayhigh 86400 sleep 1 (sleep prevented by screencaptureui) hibernatemode 3 ttyskeepawake 1 displaysleep 10 (display sleep prevented by screencaptureui) tcpkeepalive 1 highstandbythreshold 50 acwake 0 lidwake 1
This time, there was a process called
screencaptureui which was preventing not just sleep, but also display sleep!
screencaptureui is the new screenshot tool you can activate with Cmd-Shift-5 introduced in macOS 10.14 Mojave. (Thinking back, I did have an issue the previous day when I was taking a screen recording but ran into a bit of trouble finding the stop button in the menu bar afterwards.)
screencaptureui was a short-term fix, but the underlying issue was that it was hard to tell if there was any process preventing sleep (or display sleep) without manually checking
A cron job to the rescue
I wrote a short script which checks the output of
pmset -g for any processes preventing sleep, then triggers a Notification Centre notification if there are any:
#!/bin/sh - sleep_blocker=$(pmset -g | grep -m1 "sleep prevented by" | sed -E 's/.+sleep prevented by (.+)\)$/\1/') if [ ! -z "$sleep_blocker" ]; then osascript -e "display notification \"$sleep_blocker\" with title \"Sleep prevention warning\" subtitle \"The following processes are preventing sleep:\"" fi
For example, if Intel(R) Power Gadget is running, the following notification will be shown:
The script above can also be found at https://gist.github.com/yi-jiayu/f1a8f32d70a6a4c3b7dbd81589c40f0d.
Adding it to cron
crontab -e opens your per-user crontab file in your preferred editor. I added the following line to it:
*/30 * * * * /Users/yijiayu/sleep_prevention_warning.sh >/dev/null 2>&1
This registers my script to run every half an hour, using the cron expression I got from here, and I tested it to work by leaving my Intel(R) Power Gadget open until the cron job triggered.
While writing this, I noticed that Chrome also prevents sleep quite often, although its never caused me any issues.
I'm mostly worried about additional noise from yet another source of notifications, but at this point in time it's too early to say. Ideally most of the time the script should be a no-op, since there shouldn't be any processes preventing sleep normally (right?).
Triggering notifications with
osacript is pretty rudimentary, and doesn't let you register anything to happen when you click it, such as bringing up more detailed information from
pmset -g assertions. Perhaps it would be an interesting project to write a full Cocoa menu bar application to monitor processes preventing the system from sleeping? (It would be ironic if such an application ended up preventing the system from sleeping though.)