bookmark_borderSpelunking into actual IoT Development – Part 1 – The Lamp in the Corner

So you may have read my previous post about my adventures in controlling my ceiling fan with my phone, computer, watch, etc using HomeKit. I decided my second project would a simple lamp.

For my controller, I chose the ESP-32 originally. It’s cheap, has wifi, and has a good reputation. All of the reliable libraries that implement the HomeKit protocol are proprietary and expensive. I’m on a student budget, so that’s not an option. However, there is a pretty good library built by maximkulkin hosted on Github.

Now, even with that good reputation, I had some difficulties with the ESP-32 that I’ll get to later. Because I am going to be putting this in a lamp which gets its power from mains, I needed a way to get a 5V power supply. So, I took apart one of those USB wall adapters and pulled out the circuitry so that I could use it as my power source. Then, I spliced the wires of the lamp on the inside, and connected them to that circuit. Then I got a relay, and wired the live wire from the wall into the common terminal of the relay and the live wire going to the lamp to the normally open terminal. Then, I wired the 5V from the power supply to the ESP-32’s voltage input GPIO pin and to the VCC pin of the relay.

I found the esp-homekit-demo repository on GitHub from which I modified the LED example to work as a lamp, and U connected that GPIO pin to the input on the relay.

Et voila! I had a lamp controllable from my phone!
Wait. It stopped working exactly two minutes later? Okay, okay.
It turns out that the library I’m using has an issue on the ESP-32 where the MDNS broadcasts don’t work quite right. Since then, I’ve only been using ESP-8266s as they do not share this issue and are actually cheaper. The projects later in this series will all use the ESP-8266.
Thanks for reading!

bookmark_borderDIY Ceiling Fan IoT HomeKit Control

I had seen and sort of dismissed the whole smart home concept long ago because it’s all so expensive, but a few weeks ago, I had an idea. I would try to make my ceiling fan remotely controllable with HomeKit. (HomeKit because I primarily use Apple devices) If I was successful, I would give smart home stuff a shot. Spoiler: I was successful. Soon, I intend write about my adventures making other smart home stuff on the cheap like smart plugs.

How I did it

First, I wanted to get the hardware working without the fuss of working with Apple’s HomeKit protocol. With a multi-meter and testing using some wire I had on hand, I worked out which parts of the button pads I needed to pull low in order to emulate a button press.

Shows which contacts are current sinks or sources on the remote's PCB

Then, I soldered wires to each of the portions of the button pads that source current. (The current sinks are the ones that are connected to ground while the sources are the other ones.)

One thing I haven’t mentioned up to this point is that the remote runs on 12V while the Pi runs on 5V, so if I used the Pi to power the remote PCB, it would not have enough power to send a strong enough signal. I did find that it did sort of work, but wasn’t anything close to reliable, so I cobbled together a small battery holder for the button PCB, and put it’s original battery in it for power. However, in order for the pi to successfully communicate with the remote PCB, they have to share a ground potential. Basically, I had to connect their grounds together. Then I discovered that once connected to the Raspberry Pi, the remote would think that all of the buttons are being perpetually pressed even if the Pi was trying to source current (outputting a 1 on the GPIO pin). So, I soldered some 68k ohm resistors between the Pi and the remote, and…….. Success! I could send button presses to the remote by outputting 0s to the GPIO pin!

Now, I needed to figure a way to control the remote with HomeKit. I found a project online called homebridge. It allows you to write Javascript plugins to control non-homekit devices from HomeKit. I initially tried a couple of plugins designed to control the Pi’s GPIO, but I didn’t end up getting any to work. So, I wrote my own plugin which can be found on Github. I did have some challenges writing the plugin, however. You can order the to a specific state(off, low, medium, high), but the remote has only a single button for the light which makes it difficult to order a specific state. Despite that, after some experimentation, I found that if you hold down the light button for 5 seconds, you can guarantee that the light is on because the dimming function begins after 5 seconds. So, if the light is off, it will turn on on the rising-edge of the button press, and if the falling-edge resides 5 or more seconds later, the light will begin to change its brightness. If the light is already on, it won’t do anything on the rising-edge of the button press, and upon 5 seconds after the rising-edge, the light will begin to dim. You may expect that this would cause the brightness of the light to be erratic, but only after 5 seconds will the brightness begin to change slowly, so it should remain very close to constant, and this only happens when the controller is rebooted.

Future Improvements

I intend to move away from using a Raspberry Pi as an entire operating system is not necessary and quite inefficient to interact with HomeKit. Eventually, I think I’ll transition to using an ESP-8266 as they are super cheap and efficient.

bookmark_borderHow to setup Google OAuth2 login with Express

I was recently working on SubScrubber, and I had to allow users to log into Google and request permissions in order to access their YouTube subscriptions information. The Google documentation for their server-side APIs does not include a code sample or an example for Node.JS at all. They have an example on their Github, but it uses the plain Node.JS standard http library which adds so much boilerplate code that it’s difficult to discern the parts that are Google specific. To fill this void, here is how to setup Google OAuth2 login with Express. Note that this guide assumes you know how to set up a Node.JS project and install dependencies, and have created the project in the Google Developer Console.

If you just want to look at the code, a sample project is available on Github.

1. Set up your Node.JS project, and install the following dependencies:

  • cookie-parser
  • ejs
  • express
  • google-auth-library
  • googleapis
  • jsonwebtoken

2. In the Credentials section of the Google Developer Console, create an OAuth Client ID credential of type Web Application.

3. Create a file named config.js with the following contents,

const port = 3002;
const baseURL = `http://localhost:${port}`;

module.exports = {
  // The secret for the encryption of the jsonwebtoken
  JWTsecret: 'mysecret',

  baseURL: baseURL,
  port: port,

  // The credentials and information for OAuth2
  oauth2Credentials: {
    client_id: "",
    project_id: "", // The name of your project
    auth_uri: "https://accounts.google.com/o/oauth2/auth",
    token_uri: "https://oauth2.googleapis.com/token",
    auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
    client_secret: "",
    redirect_uris: [
      `${baseURL}/auth_callback`
    ],
    scopes: [
      'https://www.googleapis.com/auth/youtube.readonly'
    ]
  }
};

4. Fill in the client_id, project_id, and client_secret properties with the information for your project.

5. Create a main.js. I’ve included all of the includes and boilerplate stuff below as that is outside the scope of this article.

const express = require('express');
const google = require('googleapis').google;
const jwt = require('jsonwebtoken');

// Google's OAuth2 client
const OAuth2 = google.auth.OAuth2;

// Including our config file
const CONFIG = require('./config');

// Creating our express application
const app = express();

// Allowing ourselves to use cookies
const cookieParser = require('cookie-parser');
app.use(cookieParser());

// Setting up EJS Views
app.set('view engine', 'ejs');
app.set('views', __dirname);

// Listen on the port defined in the config file
app.listen(CONFIG.port, function () {
  console.log(`Listening on port ${CONFIG.port}`);
});

Note that I am using EJS for templating, as it is really close to html.

6. Create a GET route for /. This is where we’ll put our link to log in with google.

app.get('/', function (req, res) {

});

6a. The OAuth2 class we included on line 6 of our main.js is from the google-auth-library module. It is just an object for our OAuth2 client. In this route, we want to create an instance of the OAuth2 client so that we can use it to authenticate our requests to the Google API.

// Create an OAuth2 client object from the credentials in our config file
const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);

6b. Now, we want to obtain the link to which we’ll send the user when they click the Login button. To do this, we need to call the generateAuthUrl method on our OAuth2 Client, passing it the access type and what access scopes we need. Access scopes tell google what exactly it needs to ask the user consent for. For example, if we want access to a user’s YouTube data, we would need to ask for the scope https://www.googleapis.com/auth/youtube.readonly, so that Google will ask them if they want to share their YouTube data with us.

// Obtain the google login link to which we'll send our users to give us access
const loginLink = oauth2Client.generateAuthUrl({
  access_type: 'offline', // Indicates that we need to be able to access data continously without the user constantly giving us consent
  scope: CONFIG.oauth2Credentials.scopes // Using the access scopes from our config file
});

6c. Finally, we need to render our index template(we’ll create it in 6d) with the login link.

return res.render("index", { loginLink: loginLink });

At this point, this should be your / route:

app.get('/', function (req, res) {
  // Create an OAuth2 client object from the credentials in our config file
  const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);

  // Obtain the google login link to which we'll send our users to give us access
  const loginLink = oauth2Client.generateAuthUrl({
    access_type: 'offline', // Indicates that we need to be able to access data continously without the user constantly giving us consent
    scope: CONFIG.oauth2Credentials.scopes // Using the access scopes from our config file
  });
  return res.render("index", { loginLink: loginLink });
});

6d. Create a base html(ish) file named index.ejs with a login link to the page we passed to the file.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Express Google OAuth2 Tutorial by Aidan Lovelace</title>
</head>
<body>
  <a href="<%= loginLink %>">Login</a>
</body>
</html>

7. At this point, you can run node main.js and visit http://localhost:3002/ and see a little Login button that links to a Google error. This error is due to the fact that we didn’t tell Google that we want it to redirect the user back to us at http://localhost:3002/auth_callback, so now we need to do that. In the Google Developer Console, click on the Web Application credential you created for this project, and add http://localhost:3002/auth_callback to the list of Authorized Redirect URLs. Now, you should be able to login with your Google Account and get redirected to a 404 error.

8. This 404 error is due to us not having implemented the auth_callback route, so we should probably do that now. When Google redirects the user, it’s either going to redirect with a code that we can use to obtain permanent credentials or an error if the user decided not to give us access. This data will be included in the GET parameters. We need an OAuth2 client here, so add that first thing. we also need to check for the error parameter. If there is one, let’s redirect the user to the homepage. Otherwise, we need to get the permanent user credentials and store them in a cookie so we can use them later. The code below redirects the user to /get_some_data, a page we have not yet created but will show some data about the user.

app.get('/auth_callback', function (req, res) {
  // Create an OAuth2 client object from the credentials in our config file
  const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);

  if (req.query.error) {
    // The user did not give us permission.
    return res.redirect('/');
  } else {
    oauth2Client.getToken(req.query.code, function(err, token) {
      if (err)
        return res.redirect('/');
      
      // Store the credentials given by google into a jsonwebtoken in a cookie called 'jwt'
      res.cookie('jwt', jwt.sign(token, CONFIG.JWTsecret));
      return res.redirect('/get_some_data');
    });
  }
});

9. Let’s create the/get_some_data page. In my example, it’ll display 5 channels to which the user is subscribed. It’ll need to create an OAuth2 client and add the user’s credentials to it in order to access anything. Then, it’ll get the subscriptions and send them to the template.

app.get('/get_some_data', function (req, res) {
  if (!req.cookies.jwt) {
    // We haven't logged in
    return res.redirect('/');
  }

  // Create an OAuth2 client object from the credentials in our config file
  const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);

  // Add this specific user's credentials to our OAuth2 client
  oauth2Client.credentials = jwt.verify(req.cookies.jwt, CONFIG.JWTsecret);

  // Get the youtube service
  const service = google.youtube('v3');

  // Get five of the user's subscriptions (the channels they're subscribed to)
  service.subscriptions.list({
    auth: oauth2Client,
    mine: true,
    part: 'snippet,contentDetails',
    maxResults: 5
  }).then(response => {
    // Render the data view, passing the subscriptions to it
    return res.render('data', { subscriptions: response.data.items });
  });
});

Lastly, we need to create the data.ejs template in order to display the data.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Express Google OAuth2 Tutorial by Aidan Lovelace</title>
</head>
<body>
  <ul>
    <% subscriptions.forEach(function (subscription) { %>
      <li><%= subscription.snippet.title %></li>
    <% }) %>
  </ul>
</body>
</html>

Thanks for reading! If I made any mistakes, please don’t hesitate to let me know in the comments.

bookmark_borderHow to use ES6+ with Node.JS

I have seriously come to really like the new features that come with ES6+ Javascript such as async/await, the new class syntax, let and const (screw you hoisting!), arrow functions, destructuring, the new import syntax, and many more. These features are seriously useful. I think that the import syntax in particular makes Node.JS feel so much more like it was made for Javascript.

Ok. Ok. I’ll get on with it—How can I use ES6+ with Node.JS? It’s super simple.

1. cd to your project in your Terminal

2. Run npm install --save-dev esm

3. Run your main Javascript file with node -r esm {THE FILE}

If you are using nodemon, you can just run nodemon -r esm {THE FILE}.