A few years ago, I decided I needed to get some smart light bulbs. I need to be able to set very dim light in my study for the nights I am on call for my job. After trying some cheap no-name bulbs, I discovered the Nanoleaf Essentials range. These were affordable and also came with HomeKit compatibility.
Recently I had one fail and it was the last straw, at least for use in my study. It was, if I recall correctly, the fourth such failure in a few years.
I put out the call in the Podfeet slack and in a couple of other places for a reliable brand of bulbs. There was an overwhelming majority in favour of the Philips Hue bulbs. I had originally complained these were expensive and I didn’t like the idea of needing a hub. The Nanoleaf bulbs do not need one.
Allison explained the hub actually improved the reliability and speed of operation so I tentatively moved that aspect into the plus column. But they are still expensive, especially given the inclusion of a hub. Events conspired to get me over that hurdle, too, as I managed to score a starter kit at well less than half price.
In my study I have four bulb sockets — three spotlights which got the starter pack bulbs, plus a dome light near the door. Because of the tiny, dark room, I also had a couple of Nanoleaf light strips — one along the back of my desk as a backlight, and one at the top of the wall behind my chair, as a fill light. As I got to know the features of the Hue app, I began to crave that ALL of the lights in my room would be controllable by it.
I’ve now bought an additional bulb for the doorway, and have just replaced the backlight strip with a Hue “White and colour ambience” light strip. While playing around with the very cool music sync feature — seriously, I have an Apple Music playlist called “Light Show” now — I got to wondering what bespoke automations I might be able to create with Hue lights.
Back in January of 2023, I contributed a three part series to the podcast, called “Stream Deck — Down the Rabbit Hole”. In that, I mentioned I had looked for ways to directly control my Nanoleaf lights from automations. I had settled on an app called HomeControl which provides an interface to HomeKit via a URL scheme. This has worked OK for me, but does seem to fall foul of HomeKit reliability problems. I wondered whether Philips Hue gave me any new opportunities.
Oh, boy, howdy, does it what!!
It took me a little digging around to find out what was needed to start controlling my Hue lights but, I am happy to report anyone can do it without needing to sign up to anything. It does help to create a free developer account with Philips, but that is just about getting access to detailed information. With just what I will talk about, you can control your lights to suit most automation needs.
Everything I cover will be done from a terminal. I’ve been using the standard macOS terminal with zsh but anywhere you can access the curl command and be on the same network as your Hue Bridge, you should be OK.
Before we move on, that is one pre-requisite. While Hue bulbs can work without a Bridge, the Hue API is an API to the Bridge.
The first thing we need to do is find out what IP address the Bridge is on. One way to do this is from the Hue app for iPhone or Android. In the settings, tap My Hue System, then the info button for the Hue Bridge you wish to control. The IP address will be listed in the top section.
Alternatively, you may be able to look up the address from your router’s GUI. If you go this way, you might like to reserve an IP address for the bridge so you can always use the same address for your automations.
Every action we do will be running a curl
command to talk to the bridge. We will either ask for information, or provide information, or sometimes both. The first command we need is to set up a user account on the Bridge.
Knowing your bridge IP address, let’s enter the first command, which will set up a user profile to use going forward.
curl --request POST --data '{"devicetype":"app_name#instance_name", "generateclientkey":true}' https://192.168.1.15/api
You’ll get an error back which tells you the extra step you missed.
[{"error":{"type":101,"address":"","description":"link button not pressed"}}]
Press the button on your Bridge, then run the command again and you’ll get a more useful response.
[{"success":{"username":"Qn74cB7YlKursSzMYyPL4pr5oLWxayBqhKyjFD10","clientkey":"8B249DD79EF93F004595E2AC2DFEC942"}}]
Record both the user name and client key in your password manager. For what I will cover, you’ll only use the username.
Now let’s list out all the devices your bridge knows about.
curl --request GET --header "hue-application-key: Qn74cB7YlKursSzMYyPL4pr5oLWxayBqhKyjFD10" https://192.168.1.15/clip/v2/resource/device
But hang on, curl
returns an error.
curl: (60) SSL certificate problem: unable to get local issuer certificate
If we were writing an app that many people would use with their own Bridges, we’d need to deal with validating the certificate. Because we’re just playing with our own Bridge, we need to tell curl
not to worry about that, by adding an extra flag to the command: -k
curl -k --request GET --header "hue-application-key: Qn74cB7YlKursSzMYyPL4pr5oLWxayBqhKyjFD10" https://192.168.1.15/clip/v2/resource/device
Now we get a huge glop of data back. It’s JSON, so if you have jq
installed, you can format it. To learn about jq
, check out Programming by Stealth episode 155 onwards.
curl -k --request GET --header "hue-application-key: Qn74cB7YlKursSzMYyPL4pr5oLWxayBqhKyjFD10" https://192.168.1.15/clip/v2/resource/device | jq
The formatted JSON takes many more lines but you can probably make sense of it now. It contains an error array, hopefully empty, and a data array. Here’s one example entry from my data array.
{
"id": "194ab81c-46d8-1e4a-7c66-751137cdc984",
"id_v1": "/lights/4",
"product_data": {
"model_id": "LCA006",
"manufacturer_name": "Signify Netherlands B.V.",
"product_name": "Hue color lamp",
"product_archetype": "sultan_bulb",
"certified": true,
"software_version": "1.116.3",
"hardware_platform_type": "100b-114"
},
"metadata": {
"name": "Study A1",
"archetype": "ceiling_round"
},
"identify": {},
"services": [
{
"rid": "a4edc74c-5ac1-4d82-bbc1-3066fda691fb",
"rtype": "zigbee_connectivity"
},
{
"rid": "f355d81f-7b92-4113-90ad-f92626fc5dd9",
"rtype": "light"
},
{
"rid": "4c019efe-0915-4d34-bc4b-d2fa3b8270fc",
"rtype": "entertainment"
},
{
"rid": "6e9993c3-0684-4bff-9c62-323ddd8164e3",
"rtype": "taurus_7455"
},
{
"rid": "f1f8214a-68ff-4295-ad79-b801d10132bf",
"rtype": "device_software_update"
}
],
"type": "device"
},
There are several interesting things to see here.
The id
key specifies the overall id of the device, though we can get more specific.
The product_data
object is comprised of lots of information about what the device actually is, along with its software version.
The metadata
object contains the name you have given the device in the Hue app, as well as the archetype
. In this case, I have described my light as being a round ceiling light.
Lastly, the services
object gives us information about each of the roles the device can play. It’s one of the rid
keys in here that we’re interested in. The one that relates to an rtype
of light
. We’re going to use that to check on and control the light, so note it down.
Next, let’s get some information about the light service for this device. Once again, we will pass the output to jq
for formatting.
curl -k --request GET --header "hue-application-key: Qn74cB7YlKursSzMYyPL4pr5oLWxayBqhKyjFD10" https://192.168.1.15/clip/v2/resource/light/f355d81f-7b92-4113-90ad-f92626fc5dd9 | jq
At the top, we get back some of the same information we already had, but then it gets interesting.
"on": {
"on": true
},
"dimming": {
"brightness": 100.0,
"min_dim_level": 0.20000000298023225
},
"dimming_delta": {},
"color_temperature": {
"mirek": 156,
"mirek_valid": true,
"mirek_schema": {
"mirek_minimum": 153,
"mirek_maximum": 500
}
},
"color_temperature_delta": {},
"color": {
"xy": {
"x": 0.3143,
"y": 0.3301
},
"gamut": {
"red": {
"x": 0.6915,
"y": 0.3083
},
"green": {
"x": 0.17,
"y": 0.7
},
"blue": {
"x": 0.1532,
"y": 0.0475
}
},
"gamut_type": "C"
},
There is plenty more after the section I have shown, which mostly relates to advanced functions, but here we have the basics of this colour-capable bulb.
The on
object details whether the bulb is currently on or not.
The dimming
object shows the current dimming level.
The color_temperature
object shows the current white temperature. More on this in a bit.
The color
object seems to contain a lot of information to define a colour! More on this later, too.
Let’s see how we can turn the light on or off. From here on in, we’ll use a fairly standard curl
command with differences in just one of the values.
curl -k --request PUT --header "hue-application-key: Qn74cB7YlKursSzMYyPL4pr5oLWxayBqhKyjFD10" --data '{"on":{"on":false}}' https://192.168.1.15/clip/v2/resource/light/f355d81f-7b92-4113-90ad-f92626fc5dd9
This is very similar to the previous command we used with two important differences. The request type is now PUT
, and we are supplying some --data
.
Note that the --data
value is single quoted to allow for the double quotes in the value. That value is a snippet of JSON which should look quite familiar.
When we queried the light’s current state, it returned its power state, amongst other data, as {"on":{"on":false}}
and that is exactly the form of value we supplied to change it.
After running the command, you will get a small snippet of JSON in response. It contains the id of the service we targeted and, once again, an errors array which is hopefully empty. And, of course, the light should have acted on your instruction.
{"data":[{"rid":"f366d87e-78d2-4f13-93fd-f2862d5c5879","rtype":"light"}],"errors":[]}
You can probably imagine that we can set some of the other values the same way, and you’d be right. Let’s try colour temperature.
From here on in, I’m just going to show the --data
values, as the rest of the command is always the same, at least for this light.
--data '{"color_temperature":{"mirek":500}}'
Wait… what’s a mirek? More commonly called “mired”, it’s short for reciprocal megakelvin. The mirek value is based on a colour temperature scale you’re possibly more familiar with — degrees Kelvin. Let’s say you want to set the colour temperature to 6500ºK. To calculate the equivalent mirek value, you divide a million degrees Kelvin by the number of degrees Kelvin you want. So 1,000,000 divided by 6,500 is about 153.85.
If you were paying attention earlier, you may have seen the number 153. When we queried the light service, as well as the current mirek value, we were also given mirek_minimum
and mirek_maximum
values — 153 and 500, respectively. If we divide 1,000,000 by 500, we of course get 2,000. So the light has a temperature range of 2,000 – 6,500 degrees Kelvin.
With my previous command, I set my bulb to a very warm colour temperature — basically orange. I can set it back to its brightest white by changing the mirek
value to 153.
The next thing we can try is setting multiple attributes at once. Let’s set the colour temperature and the brightness.
--data '{"color_temperature":{"mirek":312},"dimming":{"brightness":50.0}}'
Brightness is an easy one — it’s just a number from 0.1 to 100.0 from dimmest to brightest. Specifying multiple attributes is as simple as putting one after another, separated by a comma.
What about setting a colour, for colour-capable bulbs? It’s easily executed, though figuring out what numbers you need for a specific colour can be a challenge.
You might be used to specifying colour with three components — red, green, and blue, or “RGB”. It’s not going to be that easy for your Hue lights. Instead we supply only two numbers, which are X and Y in a CIE colour space. Basically, they are coordinates on a chromaticity diagram.
If, like me, you can think in an RGB colour space, then there is a way to convert between that and the CIE colour space, but it’s not straightforward at all. Instead, I recommend a resource like this page from “Dumb Switches” I found, which gives you values for a whole bunch of colours. There is also a link to a Python library that can do the calculations if you’re keen.
Anyway, here are the data values to pass for each of red, green, and blue.
--data '{"color":{"xy":{"x":0.675,"y":0.322}}}'
--data '{"color":{"xy":{"x":0.4091,"y":0.518}}}'
--data '{"color":{"xy":{"x":0.167,"y":0.04}}}'
There are many, many more things you can do using the Hue API, like grouping lights, reading sensors, streaming “entertainment” data to lights, and more, but I’ll leave it here for now. If you wish to explore more, sign up for a free developer account at developers.meethue.com.
In a future segment, I’ll tell you how I worked around needing to look up those giant light IDs with a little bit of command line magic.
But just before I go, here’s a --data
value to try that’s pretty nifty.
--data '{"effects":{"effect":"prism"}}'
[…] Philips Hue Programming (Part 1 of 3) […]
You’re a natural teacher, Allister!
I will read the rest of the series since we do have a few Philips lights that are used every night.
My goal is with another company, Lutron and their light switches.
We have 3 rows of lights in the kitchen:
– two rows above the dining table
– one row above the counter
At times we either want only light from the two Dining Table rows, or a softer light just from the Counter row.
For now, I’ve got HomeKit programmed to either ‘Flip the lights’ or ‘Reverse the lights’. Which would turn off and on the pre-programmed rows of lights.
As you can imagine, we forget which phrase is associated with the to-be-enabled rows.
I will look into Lutron and see if they have an API that allows querying the current state, and then simply toggling the values.
Thanks for sparking a curiosity, Allister!
Thanks for your kind words, Niraj. For your use case, I would suggest considering the target state rather than the act of switching. In HomeKit you can create scenes that not only turn lights on but also off. So you could create two scenes with the relevant lights on and off or off and on, as it were, and name them for the purpose. E.g. “Dinner lights” and “Mood lights”. I’ve only ever used Siri to directly run scenes (“Hey Siri, set the daylight scene.”) but you could use Shortcuts to provide an interface with a different phrase I think. However, my goal with the Hue lights was to allow control from any automation interface. I have actions programmed into my Stream Deck that use the simple “run script” action which runs my (final – see Part 3 next week) Hue script, in some cases more than once with intervening delays.
[…] In my previous instalment, I explained how to use the curl command to control Philips Hue lights. Part of the exercise involved dumping out a huge glop of JSON data and finding a long, gobbledegook of random alphanumeric characters that were the ID of a light we could control. […]