Barely a few days since my previous post on having fun with the Hue API, and the sensor stopped responding to my API calls. There’s no error or anything, the sensor just doesn’t get enabled!
I run the command as before:
|
1 2 3 4 5 6 7 8 9 10 11 |
APIKEY=$(pass apiKeys/hue) BRIDGE_IP="<ip>" SENSOR_ID="<guid>" echo "${GREEN}β¨ Enabling sensor${RESET}" curl --request PUT \ --url https://$BRIDGE_IP/clip/v2/resource/motion/$SENSOR_ID \ --header "content-type: application/json" \ --header "hue-application-key: $APIKEY" \ --data '{ "enabled" : true }' \ --insecure |
The output is a success:
|
1 |
{"data":[{"rid":"77064b38-907d-42ad-a65b-4f22178c3e6d","rtype":"motion"}],"errors":[]} |
And querying the sensor shows its enabled too:
|
1 |
curl --request GET --url https://$BRIDGE_IP/clip/v2/resource/motion/$SENSOR_ID --header "hue-application-key: $APIKEY" --insecure | jq |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
{ "errors": [], "data": [ { "id": "<guid>", "id_v1": "/sensors/xx", "owner": { "rid": "<guid>", "rtype": "device" }, "enabled": true, "motion": { "motion": true, "motion_valid": true, "motion_report": { "changed": "2025-11-04T20:31:09.366Z", "motion": true } }, "sensitivity": { "status": "set", "sensitivity": 4, "sensitivity_max": 4 }, "type": "motion" } ] } |
But it does nothing, and the app shows that it is indeed disabled!
So much for that. π
The lights continue to work though, so I know my API key and things are good. It’s just the sensor and API.
Which got me thinking, maybe I can avoid it? I want the lights to come on when I am working on the Mac. Which means the display is on/ I’ve unlocked the screen. And when the display sleeps/ I’ve locked it, the lights can turn off. So what I really need is a way to trigger my scripts based on the Mac’s state.
Looking for options, I encountered sleepwatcher. (Thanks to ChatGPT actually, it’s way better than Google nowadays for getting search results – unfortunate. And even Gemini, while it suggested sleepwatcher, didn’t suggest any other options like ChatGPT did).
I didn’t use sleepwatcher coz looks like it needs the Rosetta layer on Apple silicon (not a problem but I am trying to keep things Apple silicon only π) plus it seems to be a bit more involved than just downloading and running. I don’t mind things that are a bit more involved, just that the second option ChatGPT suggested was Hammerspoon, which I already use, so I figure I might as well invest more in that.
Turns out there’s a Hammerspoon module called hs.caffeinate.watcher that can watch for system sleep/ wake/ etc. events. Specifically, these ones:
- screensaverDidStart
- screensaverDidStop
- screensaverWillStop
- screensDidLock
- screensDidSleep
- screensDidUnlock
- screensDidWake
- sessionDidBecomeActive
- sessionDidResignActive
- systemDidWake
- systemWillPowerOff
- systemWillSleep
While ChatGPT gave me the code to use to enable this, I wanted to figure out what it does, so what follows is my exploration.
Based on the docs of the module, it looks like I have to create a watcher: hs.caffeinate.watcher.new(fn)
In this fn is the function that will be called. These are Lua functions and they must accept one parameter which is the event type (the ones defined above – systemDidWake and so on). These “event types” are actually constants – i.e. strings – and looks like I must use the full name like hs.caffeinate.watcher.screensDidWake (got from the docs).
So you 1) define a Lua function which does what you need to do (run my script to enable or disable lights) based on the input, 2) create a new watcher instance with the function as input, and 3) start this watcher?
I created a bash script out of my Hue code earlier. This takes an argument and based on it enables or disables the lights.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#!/usr/bin/env bash APIKEY="<apiKey>" BRIDGE_IP="<ip>" SENSOR_ID="<guid>" ALLLIGHTS=(<lightid1> <lightid2>) if [[ $1 == "enable" || $1 == "on" ]]; then echo "Enabling lights" for LIGHT_ID in ${ALLLIGHTS[@]}; do curl --request PUT \ --url https://$BRIDGE_IP/clip/v2/resource/grouped_light/$LIGHT_ID \ --header "content-type: application/json" \ --header "hue-application-key: $APIKEY" \ --data '{ "on": { "on" : true } }' \ --insecure done else echo "Disabling lights" for LIGHT_ID in ${ALLLIGHTS[@]}; do curl --request PUT \ --url https://$BRIDGE_IP/clip/v2/resource/grouped_light/$LIGHT_ID \ --header "content-type: application/json" \ --header "hue-application-key: $APIKEY" \ --data '{ "on": { "on" : false } }' \ --insecure done fi |
(Note: Previously I would read the API key via pass, but I had to remove that because Hammerspoon would hang. I think it’s coz pass tries to get my GPG passphrase and fails).
Next, I created a function and put in my Hammerspoon config file:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function toggleLights(eventType) if (eventType == hs.caffeinate.watcher.screensDidUnlock or eventType == hs.caffeinate.watcher.systemDidWake) then hs.execute("~/desklights.sh enable", false) elseif (eventType == hs.caffeinate.watcher.screensDidLock or eventType == hs.caffeinate.watcher.systemWillSleep or eventType == hs.caffeinate.watcher.systemWillPowerOff or eventType == hs.caffeinate.watcher.screensDidSleep) then hs.execute("~/desklights.sh disable", false) end end |
The function looks for event types that are do with the screen unlocking or the system waking and runs the script with the enable parameter; and for even types that are to do with screen locking or system sleeping or system powering off or screens sleeping it runs the script with the disable parameter. This makes use of hs.execute()Β which takes as input the script & its parameters, and a boolean value that determines if the command should be executed in the user’s login shell or not. In my case I don’t want it to, so I set it to false.
And finally, I created a new watcher and started it.
|
1 2 |
local lightsWatcher = hs.caffeinate.watcher.new(toggleLights) lightsWatcher:start() |
Then I reloaded my Hammerspoon config, locked and unlocked my screen to test, and the lights turned off and on as expected. Awesome! π₯³
Before I end, while Googling on some of the issues I encountered (the bit where Hammerspoon used to hang coz of the pass command in my script) I encountered a blog post and StackOverflow post where others have done pretty much what I did above. That’s also from where ChatGPT gave me its suggestions (esp. the StackOverflow post which gives many more suggestions)… so credit where credit is due! π
Update (a few hours later): See my next post for a follow-up.
