A product image of three Philips Hue bulbs and a Hue Bridge. The bulbs are lined up on an angle receding from the viewer and to the left, towards the bridge in the background. The bulbs are mostly white, lit up in a purple colour. The bridge is white with a lit up blue ring.

Philips Hue Programming (Part 1 of 3)

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.

A product image of three Philips Hue bulbs and a Hue Bridge. The bulbs are lined up on an angle receding from the viewer and to the left, towards the bridge in the background. The bulbs are mostly white, lit up in a purple colour. The bridge is white with a lit up blue ring.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"}}'

4 thoughts on “Philips Hue Programming (Part 1 of 3)

  1. Niraj - August 19, 2024

    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!

  2. Allister Jenks - August 19, 2024

    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.

  3. […] 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. […]

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top