My mac just wont sleep and is warm in the morning

I gave my mac a bedtime routine so it can stay awake during the day but idle sleep at night. Great for running agents in the background, but not kill your hardware for 24/7 use.

My mac just wont sleep and is warm in the morning

With all the apps we run now on our Mac's and agents that are doing stuff on our behalf, I'm mindful how long my computer can effectively run before the hardware starts to degrade.

It's not uncommon for me to have all my apps running for weeks on end and I just put the Mac to sleep at night. But I also don't want it to sleep during the day as I'll often have agents doing work on my behalf.

But lately, I've found that overnight my Mac has not been sleeping and something is keeping it awake. WhatApp for desktop, you're the biggest offender. But I've found a few things like a browser, or even my own app has done something to keep the Mac from sleeping.

With Claude I managed to diagnose what the offending apps are. But it make me think.

How do I prevent my mac from sleeping during the day, but idle sleep at night?

So with my trusty agent, we made the following script to run a background daemon to do it. It works by switching the AC idle-sleep timer on a schedule. So at 11pm at night, my Mac will idle sleep after 20 minutes when plugged in. And from 7am the next morning it will keep awake during the day when plugged in.

  • Day (07:00–23:00): AC sleep = 0 → never idle-sleeps.
  • Night (23:00–07:00): AC sleep = 20 → idle-sleeps after 20 min if nothing is holding it awake.

It works by toggling one pmset setting from a small root launchd daemon that fires at your two transition times (and on boot).

Why a root daemon? pmset -c (change settings for the AC/charger power source) requires root. A LaunchDaemon runs as root, so the switch happens with no password prompt each time.

Setup from fresh

1. Create the toggle script

Save this as ~/sleep-toggle.sh:

#!/bin/bash
# Toggle AC idle-sleep by time of day.
# Night (23:00-07:00): allow idle sleep after 20 min.
# Day: never idle-sleep (keeps jobs running with screen off).
H=$(/bin/date +%H)
if [ "$H" -ge 23 ] || [ "$H" -lt 7 ]; then
  MODE="night"; VAL=20
else
  MODE="day"; VAL=0
fi
/usr/bin/pmset -c sleep "$VAL"
echo "$(/bin/date '+%Y-%m-%d %H:%M:%S') switched to $MODE (AC sleep=$VAL)" >> /tmp/sleep-toggle.log

2. Create the LaunchDaemon

Save this as ~/com.personal.sleeptoggle.plist (rename the label/file to your own reverse-DNS prefix if you like):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.personal.sleeptoggle</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/sleep-toggle.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>StartCalendarInterval</key>
    <array>
        <dict>
            <key>Hour</key>
            <integer>7</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
        <dict>
            <key>Hour</key>
            <integer>23</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
    </array>
    <key>StandardOutPath</key>
    <string>/tmp/sleep-toggle.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/sleep-toggle.log</string>
</dict>
</plist>

RunAtLoad sets the correct state immediately when loaded (and on every reboot). StartCalendarInterval fires the script at 07:00 and 23:00 each day.

3. Install and load it

# Copy the script into place (root-owned, executable)
sudo install -m 755 -o root -g wheel ~/sleep-toggle.sh /usr/local/bin/sleep-toggle.sh

# Copy the daemon definition into place
sudo install -m 644 -o root -g wheel ~/com.martin.sleeptoggle.plist /Library/LaunchDaemons/com.martin.sleeptoggle.plist

# Load it (bootout first to clear any previous copy, ignore error if none)
sudo launchctl bootout system /Library/LaunchDaemons/com.martin.sleeptoggle.plist 2>/dev/null || true
sudo launchctl bootstrap system /Library/LaunchDaemons/com.martin.sleeptoggle.plist
Tip: put all of step 3 in a single install.sh and run sudo bash install.sh. Pasting a long multi-line command directly into the terminal can get line-wrapped and split mid-argument, which breaks it.

4. Verify

# current AC sleep value
pmset -g custom | grep -A12 "AC Power" | grep " sleep "

# should show one entry
cat /tmp/sleep-toggle.log                                  

You're done.


1. How do I change the time schedule?

There are two places that define "night", and they must agree:

a) The window logic in /usr/local/bin/sleep-toggle.sh — the if line:

if [ "$H" -ge 23 ] || [ "$H" -lt 7 ]; then   # night = 23:00–07:00

For example, to make night 22:00–08:00:

if [ "$H" -ge 22 ] || [ "$H" -lt 8 ]; then

(For a window that does not cross midnight, e.g. 01:00–06:00, use &&if [ "$H" -ge 1 ] && [ "$H" -lt 6 ]; then.)

b) The trigger times in the plist StartCalendarInterval — set the two Hour values to your new boundaries (e.g. 22 and 8). These are when the switch fires; the script logic decides which way.

You can also change how long it waits before sleeping at night by editing VAL=20 (minutes) in the script.

After editing, reinstall and reload:

sudo install -m 755 -o root -g wheel ~/sleep-toggle.sh /usr/local/bin/sleep-toggle.sh
sudo install -m 644 -o root -g wheel ~/com.martin.sleeptoggle.plist /Library/LaunchDaemons/com.martin.sleeptoggle.plist
sudo launchctl bootout system /Library/LaunchDaemons/com.martin.sleeptoggle.plist
sudo launchctl bootstrap system /Library/LaunchDaemons/com.martin.sleeptoggle.plist

2. How do I disable it for a night with a long-running task?

You don't need to touch the schedule. The cleanest way is to hold a wake assertion for the duration of the job — it overrides the night idle-sleep timer and releases automatically when the job ends:

caffeinate -i  <your-command>

# Run claude code with caffeinate
caffeinate -i claude
  • -i prevents idle system sleep while <your-command> runs.
  • When the command finishes, the Mac is free to idle-sleep again — no cleanup needed.
Why this matters: macOS idle-sleep is based on inactivity, not CPU load. A busy AI job that doesn't hold an assertion can still be put to sleep at night. caffeinate is what guarantees it stays up.

Other options:

Temporarily disable the whole schedule for the night (re-enable later — see §3 for the reverse of bootout):

sudo launchctl bootout system /Library/LaunchDaemons/com.personal.sleeptoggle.plist

sudo pmset -c sleep 0    # never idle-sleep until you change it back

Maybe you're already running something, you can force it awake until you manually stop it (run, then Ctrl-C in the morning):

caffeinate -i

Keep the Mac awake for a fixed time (e.g. 8 hours = 28800 s), no command needed:

caffeinate -i -t 28800

If Claude is already running for example, and you want to caffeinate it, then you can attach to each running Claude instance.

for p in $(pgrep -x claude); do caffeinate -i -w "$p" & done

3. How do I uninstall it?

# Unload the daemon
sudo launchctl bootout system /Library/LaunchDaemons/com.martin.sleeptoggle.plist

# Remove the files
sudo rm /Library/LaunchDaemons/com.martin.sleeptoggle.plist
sudo rm /usr/local/bin/sleep-toggle.sh
rm -f /tmp/sleep-toggle.log

Uninstalling leaves AC sleep at whatever value was last set. To restore the macOS default (idle-sleep after 1 min, or pick your own minutes):

sudo pmset -c sleep 1

Or just set it in System Settings → Battery → Options → "Prevent automatic sleeping on power adapter when the display is off".


4. How do I check what the time is set for? (current state & schedule)

What is the Mac doing right now? — check the live AC sleep value:

pmset -g custom | grep -A12 "AC Power" | grep " sleep "
  • sleep 0 → daytime mode (never idle-sleeps).
  • sleep 20 → night mode (idle-sleeps after 20 min).

See the history of switches (when it last flipped, and to which mode):

cat /tmp/sleep-toggle.log
# e.g. 2026-06-20 23:00:01 switched to night (AC sleep=20)
#      2026-06-21 07:00:02 switched to day (AC sleep=0)

Confirm the daemon is loaded and when it last ran:

sudo launchctl print system/com.martin.sleeptoggle | grep -E "state|run count|runs"

See the configured trigger times (the schedule itself):

sudo launchctl print system/com.martin.sleeptoggle | grep -A20 "calendar"
# or just read the source of truth:
cat /Library/LaunchDaemons/com.martin.sleeptoggle.plist

Is anything currently forcing it awake? (e.g. a caffeinate from §2, audio, a video tab) — list all active power assertions:

pmset -g assertions

If you see PreventUserIdleSystemSleep held by an app, that will keep the Mac awake at night even in night mode, which is usually exactly what you want while a job runs.