Turning the lights off when my MacBook sleeps.

werner
5 min readFeb 11, 2022

A simple Node-Red flow on Home Assistant.

Photo by Jeremy Bezanger on Unsplash

I have an ongoing side-project-passion-obsession-challenge (if that’s a thing) to never have to touch a light switch in my house — instead letting Home Assistant connected sensors and carefully crafted node-red based automations take care of lighting as and when needed.

For example, I have:

  • Select lights throughout the house slowly fading (less) brighter to different thresholds as the sun rises and sets.
  • Some of these are temporarily overridden by motion sensors (like the kitchen counter lights) that go to full brightness when someone is in the vicinity, and fade back down to the sunset mapped level when they leave.
  • The lounge lights fading to a low warm hue if the TV is on and I am sitting on the couch after 19:00 in the evening (and turning it off when the TV turns off any time after 22:00)
  • Bedside lamps that (besides slowly getting brighter as the sun sets), goes to set brightness when I get in bed (when I usually read a book), and ultimately turning off when I fall asleep (according to my health tracker) and turning on again in the morning when it’s time to wake up.

… to name a few.

For this post I will go over how I toggle the light in my home office (many of us have one now — thanks Covid) based on whether my laptop is asleep or not. Here’s a video of it in action:

Disclaimer — I currently have a very unattractive ceiling light.

Using a motion sensor in this case would cause endless irritation as I often sit still for long periods while I work and will have to frantically wave my arms in the air to turn the light on again (many of you will have experienced this with motion activated lights)

So let’s get started…

Requirements:

  • A MacBook laptop running a fairly recent version on macOS (I’m using a 2017 MBP running macOS Monterey here)
  • A “smart” light bulb connected to Home Assistant (I have an IKEA Tradfri mono colour bulb screwed into the roof light socket and connected via ZHA)
  • Node-Red configured to work with Home Assistant.

The Configuration:

MacBook

The ideal approach would be a ping probe (that turns the light off if it does not receive a response on the ICMP packet and vice versa) — but it’s worth noting that MacOS keeps theWi-Fi connected even when asleep whether WoL (Wake-on-LAN) is enabled or not. Thus, using a ping probe from node-red to decide if the laptop is asleep will not work as is.

Realising this, I first tried running a simple HTTP server that node-red can poll. Although this works fairly well, and besides not being able to easily configure socket timeouts, etc. on the request node, I decided I wanted to use a ping probe as it would be more elegant for various reasons.

So the first thing to do is actually turn off the wireless interface when the MacBook sleeps (I do not require any features like Remote Desktop that will be affected by doing this — but it may be different for you, in which case I guess the web server approach — perhaps with a custom node-red function node — will be better for you)

a) Install sleepwatcher:

brew install sleepwatcher

b) Create a script for when the machine sleeps:

echo "networksetup -setairportpower Wi-Fi off" >> ~/.sleep.sh
chmod +x ~/.sleep.sh

c) Create a script for when the machine wakes up:

echo "networksetup -setairportpower Wi-Fi on" >> ~/.wakeup.sh
chmod +x ~/.wakeup.sh

d) You can test if these work by running them directly — for example:

>>> cd ~ 
>>> ./.sleep.sh
Wi-Fi is not a Wi-Fi interface.
Turning off the only airport interface found: en0

Notice how by using Wi-Fi as the interface it defaults to the only airport interface en0 . If you have more than one interface or simply want to be more “correct”, replace Wi-Fi in the scripts above with the correct interface e.g. en0

e) Create a Launch Agent file:

touch ~/Library/LaunchAgents/nl.thewerner.sleepwatcher.plist

f) Populate it with this configuration using your favourite editor:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>nl.thewerner.sleepwatcher</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/sbin/sleepwatcher</string>
<string>-V</string>
<string>-s ~/.sleep.sh</string>
<string>-w ~/.wakeup.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/sleepscript-output.log</string>
<key>StandardErrorPath</key>
<string>/tmp/sleepscript-errors.log</string>
</dict>
</plist>

g) Create the log files (launchd does not create them for you and will not start the daemon if not found)

touch /tmp/sleepscript-output.log
touch /tmp/sleepscript-errors.log

h) Load it:

launchctl load -w ~/Library/LaunchAgents/nl.thewerner.sleepwatcher.plist

i) Check if all good:

>>> launchctl list | grep sleepwatcher
88808 0 nl.thewerner.sleepwatcher

The left hand number is the PID which means the process is running. The second is a status code — if it’s anything but 0, something went wrong. The easiest way to debug is to use the LaunchControl app — brew --cask install LaunchControl

j) Finally, to test, I ping the MacBook from another machine on the network and then let it sleep and wake up:

Nice!

Node-Red

Now that I have my laptop Wi-Fi interface turning off when it goes to sleep, I can create a basic node-red flow with a ping probe that turns the light on if found, and off if not.

Node-Red Flow

a) The Ping node is configured as follows:

b) And the switch node:

The ping node will return the ms (type: number) roundtrip time in the payload if reachable, otherwise false — and so the switch node routes to the correct (Light ON / OFF) service node based on this.

Improvements

  • As is, this flow will send a message to the bulb every 10 seconds whether it’s on or not (same for off) — This can be improved by reading the bulb state first instead of always “spamming” the bulb with desired state.
  • Since I have three computers in my office, I have expanded this concept to include ping probes to the other two — along the lines of “if all asleep, turn off light, else turn on light” — to turn the light on when I work on any of my machines.

Conclusion

And that’s all there is to it. Now my home office ceiling light is in sync with the sleep state of my computers. When I start in the morning, a tap on my keyboard turns on the light, and when I leave for long enough, the laptop goes to sleep and the light turns off.

--

--