Wolfden Gatekeeper

The story so far

At Wolfpack Digital, we are very proud of our office building, located in the heart of Cluj-Napoca, and we worked really hard to make it a cool working space for our pack. We proudly call it the Wolfden, and since last year in the fall, more and more of our colleagues came back to the office to work from here.

the office

We have some parking spaces in the backyard, and every morning, some of us would come by car to the office. The main gate, which allows car access to the backyard, is controlled by a remote system, that requires you to have a gate remote to open and close it.

the gate

Pretty soon, we found out that providing remotes to the gate will be difficult, especially since we already gave away all of the remotes that we had in stock. So a quick solution that came naturally was to write on the main Slack channel if someone was at the office and could activate the gate through a remote that we had inside the office.

A couple of months passed, and it became a regular practice in the morning, to have messages arrive on Slack with requests to open the gate. At that moment, this practice became a "low-hanging fruit" of an operation that could be efficiently delegated towards automation.

Here is where the Wolfden GateKeeper enters the scene

On a bleak day in February, I met up with some colleagues for dinner and drinks, and among the various topics that we discussed, was the "eternal" topic of opening the gate. I was inspired to ask: "Couldn't we automate the damn thing?!". We debated strategies and came up with a plan to make it a reality.

A research phase came afterward, reading articles on the Internet about how others managed to do this, and after some time I stumbled upon this article. It was a great inspiration for what we planned afterward.

Raspberry, Dataplicity, Slack API, and some wires

We quickly assembled a shopping list, consisting of a Raspberry PI 3 (the only one in stock, version 4 is quite sought after), an SD Card, a 5V module relay to send the right voltage to the remote, some cables, and the power brick. Additionally, we also added a camera module, but more on that later.

the shopping list

The plan was simple:

  • connect the remote (somehow, I had no electronics experience beforehand) to the Raspberry
  • create a Slack App that can use a Slash Command to call an endpoint
  • write a software program that will handle the request
  • use Dataplicity to forward the endpoint and make it accessible to the Slack command
  • make sure it's secure

The plan was good. Let's put it into action.

I found a free Saturday in which I could work on this, and have the office to myself, and also a friend that could help me with the electronics part. Decided to write the program in JavaScript, just because I do that all day 😅. And off we went down this rabbit hole.

Began by setting up the Raspberry PI with Raspberry OS, and connected it through the relay module to a remote.

Afterward, I installed Dataplicity on Raspberry and started managing it through their app. They have a great free plan, on which you can connect one device and have total access to it. The Wormhole is remote access through a specific address that they provide and it can expose an HTTP server that will handle requests that come through.

I created a Slack App and a Slash Command from the Admin panel of Slack, called /opendoor, and the cool thing about this is that the payload comes with security headers consisting of a signature and a timestamp, that you can later verify against the App Client ID and Secret.

Then came the code. I wrote a simple Node App that spawns an HTTP server and exposes an endpoint that can handle a Slack Slash Command request. I wanted to make sure that it was secure, so I created multiple checks for the request to make sure that it comes from Slack and that it respects the proper credentials, and only allow it if it came from a particular Slack Channel, so you can trigger the command only from there.

function slackSignatureVerification(req) {
  const signingSecret = 'THE_SECRET';

  const requestSignature = req.headers["x-slack-signature"];
  const requestTimestamp = req.headers["x-slack-request-timestamp"];

  // verify the existance of the X-Slack-Signature and X-Slack-Request-Timestamp headers
  if (!requestSignature || !requestTimestamp) {
    return false;
  }

  // verify if the Timestamp is recent
  if (Math.abs(Math.floor(new Date().getTime() / 1000) - +requestTimestamp) > 300) {
    return false;
  }

  const baseString = `v0:${requestTimestamp}:${req.body}`;
  const cryptoHmacString = crypto.createHmac("sha256", signingSecret).update(baseString, "utf8").digest("hex");
  const expectedSignature = `v0=${cryptoHmacString}`;

  return expectedSignature === requestSignature;
}

The way to verify is described in the Slack API Docs and this article.

I also created a logging system that outputs all of the requests that came through, successful or not, and create a notice when the request parameters were not in order.

Finally, I did a timestamp verification to allow the system to operate only during business hours, and restrict it from operating during the weekend.

const timeRestriction = {
  restrictedTime: {
    lower: 7,
    greater: 19,
  },
  restrictedWeekend: true,
};

const isTimeRestricted = () => {
  const now = new Date();
  const nowHours = now.getHours();
  if (nowHours < timeRestriction.restrictedTime.lower || nowHours > timeRestriction.restrictedTime.greater) {
    return true;
  }

  const nowDay = now.getDay();
  if (timeRestriction.restrictedWeekend && (nowDay === 0 || nowDay === 6)) {
    return true;
  }
  return false;
};

If all the checks are passing, we will open the relay module for one second with the help of the onoff library.

const PINX = new Gpio("PIN_NUMBER", "out");
let relayOpen = false;

PINX.writeSync(0);
setTimeout(() => {
  PINX.writeSync(1);
  relayOpen = false;
}, doorTimeout);

Tying everything together, it was time to test the newly born Wolfden Gatekeeper. And hold and behold, it's ALIVEEE!

0:00
/
the first test

Final thoughts

This was a great learning opportunity for me, both by learning how to use a Raspberry and some new info about electronics, and it was also a neat side-project on which I got to write some code.

I am already thinking of future improvements, and one of them will involve a camera module that will take a picture of the gate, and use a simple background subtraction algorithm to detect if the gate is opened or closed, to be able to close it after working hours, in case it remained open.

If you got this far, thank you for reading and I'll see you in the next one.

over and out.