Automate Home Lights With The Fetch API

Automate Home Lights With The Fetch API

Now that I've settled into being home more-so than usual, I'm looking for ways to improve my decor.

I read that interior lighting can boost your mood, the same way colors on a web page can. So I decided to purchase some LIFX Mini bulbs--no hub required, and can say after a couple days, it's definitely helped keep me sane during these times.

However, after a bit, my developer itch was kicking in. So in this project, we'll use the fetch web API to first list our lights, and from there, we will send a request to a particular light so that it's color changes.

🗒️ It's worth mentioning that if you don't want to take the coding route, the service IFTTT has many 3rd party integrations that you can take advantage of to enhance your lighting experience.


🚨 If you just want to look at the code and fill in the blanks, there is a codeSandbox down below. Just make sure to update the super-secret-token.js file with your keys if wanting to make it work 😉

Getting an API Key

I was pleased to find that LIFX has an API for their bulbs, and upon signing up, you can access your secret token that we'll need later on.

Admittedly, it took me longer in figuring where to find my token than it did to write the code, so to help out, here's the easy path:

  1. Head over to the cloud portal and sign in with the same username and password that you used to sign in with on your mobile app

  2. Once you're signed it, you should be redirected to a page where you can "Generate New Token" (note that I already have a few tokens created).

    Click the "Generate New Token" button and give your token a name.

generate token

That's it 🎉 Now you should see your secret token.

🗒️ As is mentioned on the site, you'll want to copy that somewhere safe because once you navigate away from the page, you won't be able to access the token again and can only generate a new one. In codeSandbox, the easiest way to do this, is to make your project private or an a minimum, unlisted

Alt Text

Reading the Docs

Now that I had an API Key, I was able to checkout the docs and figure out what endpoints were available to me.

In particular the List Lights and the Set State endpoints are what we'll be using.

Authentication

It's worth reading over the Authentication and Rate Limits sections to understand how the API keeps us secure and prevents spamming. Here are the main points:

  • If using Basic auth, as we are, we'll need to provide our token as the username
  • Our requests have to have the Content-Type: application/json header
  • Our token allows us to make 120 requests every 60 seconds. We can use the X-RateLimit-Remaining response header to see how many request we have left.

Setting Up Our Project

As mentioned above, if you have your secret token, feel free to plug that into the super-secret-token.js file's TOKEN area and click the List Light ids button to get your lights. From there, pick and id, plug it into the other secret value, and it should Just Work™️

{% codesandbox 6uqyc %}

For those still with me, let's use the sandbox above and take a tour of the code.

Starting with the index.html file, you'll notice it's fairly boring. It's vanilla JavaScript and we just have a couple of elements with id's assigned so that we can target them in our code. The most appealing part is that we have <input id="color-picker" type="color" />. Giving an input a type attribute of "color" will give us a nice color picker on both desktop and mobile!

Let's get to the star of the show.

Finally Making Our Request With Fetch

In index.js we kick things off by bringing in our tokens, and grabbing a few elements that we created in our index.html file.

Next up we add an event-listener to our button so we can trigger a request whenever a user clicks the List Light ids button.

Now on to our request.

Fetch With Basic Auth

fetch takes in an endpoint as a first argument. Which we are passing as a string

fetch(`https://api.lifx.com/v1/lights/all`

Additionally, fetch can take an object used for configuration as a second argument. This is where we can specify any required headers, as well as set the auth type.

headers: {
  "Content-Type": "application/json",
  Authorization: `Basic ${btoa(TOKEN)}`
}

🔥 Note the use of the btoa function. This is the browser's built-in method for converting a normal string to a base64 encoded string. If wanting to convert the other way around, there's also atob().

From there, we continue as usual:

  1. Fetch returns a promise so we call .then to run code when we get a response from the server
  2. We take the response from the server and parse the data as JSON via res.json()
  3. We update the DOM to show the list of lights in the subsequent .then block.

Fetch With A Put Verb and Hidden Headers

Things get even more interesting with our "change color" button.

After listening for a click event, we grab the value from the input and log it out. What this reveals is that the value is in fact a hex color: #00ffff for example.

This is great because to change a light's color to something more that just a generic "green", we'll have to pass in a hex code value.

Now within our fetch request, we have a new endpoint. This one ends in id:${LIGHT_ID}/state where the LIGHT_ID as you may have guessed, is one of the lights that we got back from our List Light ids button.

However, let's take a moment to dissect the second argument: the configuration object

{
    method: "put",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Basic ${btoa(TOKEN)}`
    },
    body: JSON.stringify({
      color: colorValue
    })

Here, we say the method is put because we are sending data. The headers are the same as before. Next up the body is the data that we are sending. Servers typically expect JSON data to be passed to them, so we use the built-in JSON.stringify() method to convert our object to that format for us.

🎉All done🎉

That alone is enough to get the color to change! If all went well, you should see something like the below tweet

{%twitter 1250285107077771265 %}


🔥Extra Credit🔥

Recall, that the API only allows us to send 120 requests per minute. So the question becomes: How can we make sure we don't go over our limit so that our application doesn't break or we get flagged for spamming?

The answer is in the final lines of our code:

  .then(res => {
      console.log(res.headers.get("x-ratelimit-remaining"));
      return res.json();
    })
    .then(data => console.log(data)); // display to user

Normally, when using fetch, only a small amount of headers are actually available for us to access. So saying res.headers["x-ratelimit-remaining"] is going to give us undefined. However, using the special res.headers.get() function, we can target the header that lets us know how many requests are remaining.

There are actually plenty of other headers to checkout as well! I included an award-winning screenshot of how to find them:

Network Tab

Did you find this article valuable?

Support Michael Liendo by becoming a sponsor. Any amount is appreciated!