Spark JS SDK + Node.js + Twilio Secure Door Buzzer

Hi Sparkers! I wanted to share my project I just recently finished up this weekend: An intelligent door buzzer for my apartment, built with Node.js + Express.js, the Spark JavaScript SDK, and Twilio. The idea is relatively simple: Trigger my front door buzzer via text message in a secure way. Check out the demo video:

When a SMS is sent to my Twilio phone number, it checks two things for security purposes:

  • The phone number that sent the text is on the list of “allowed people” to whom I’ve given access to my apartment. Be nice to me and maybe I’ll add you to the list!
  • The body of the SMS matches the password I’ve set to trigger the opening of the door. Double secure, whammy!

The security logic occurs in a Node.js + Express web app triggered by the Twilio SMS. If those two requirements are met, the Spark JS SDK is used to log the user in and switch pin D0 to HIGH, then LOW after a few seconds. On the hardware side, an NPN transistor + a simple relay completes the circuit of the open door terminals in my in-unit intercom system. The below photos show the setup, thanks to @BDub for the help on the circuit!

The code of my app.js file is below for you to check out:

var twilio = require('twilio'),
    express = require('express'),
    bodyParser = require('body-parser'),
    spark = require('spark')

// Create express app with middleware to parse POST body
var app = express()
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());


// Create a route to respond to a call
app.post('/openDoor', function(req, res) {
    // Options
    var magicWord = 'Open Sesame'
    var allowed_people = ['+1xxxxxxxxxx', '+1xxxxxxxxxx'];
    var twilioToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxx';
    var sparkEmail = 'xxx@xxx.com';
    var sparkPassword = 'xxxxxxxxxxxxxxx';
    var pin = 'D0';
    var buzzerTime = 4000;
    //Validate that this request really came from Twilio...
    if (twilio.validateExpressRequest(req, twilioToken)) {
        var twiml = new twilio.TwimlResponse();
        var fromPhone = req.body.From;
        var textBody = req.body.Body;
        if (allowed_people.indexOf(fromPhone) > -1  ) {
          if(textBody.toUpperCase() === magicWord.toUpperCase()) {
            twiml.message('Door opening... ');
            // LOG IN TO SPARK
            var promise = spark.login({username: sparkEmail, password: sparkPassword});
            promise.then(
              function(token){
                var devicesPr = spark.listDevices();
                devicesPr.then(
                  // We get an array with devices back and we list them
                  function(devices){
                    var core = devices[0];
                    core.callFunction('digitalwrite', pin+':HIGH');
                    setTimeout(function() {core.callFunction('digitalwrite', 'D0:LOW');},buzzerTime);
                  },
                  function(err) {
                    console.log('API call List Devices completed on promise fail: ', err);
                  }
                );
              },
              function(err) {
                console.log('API call completed on promise fail: ', err);
              }
            );
          } else {
            twiml.message('Wrong password, try again dude!')
          }
        } else{
          twiml.message('Sorry! Jeff did not give you access to his humble abode.');
        }

        res.type('text/xml');
        res.send(twiml.toString());
    }
    else {
        res.send('you are not twilio.  Buzz off.');
    }
});

app.listen(process.env.PORT || 3000);

Feedback/Comments welcome!

10 Likes

I would love to do this but I’m not well versed (at all) in circuits, electronics, etc. I have a similar 4-wire unit in my apartment (a Lee Dan/TekTone IR-204 box), I think it’s 3.3v. Will I be able to do this with a relay shield - and if so, would I still need a resistor? I saw a similar project using a RasPi that uses an Optocoupler instead of a relay.

Hoping @jeiden or @avidan can help or point me at the right kind of documentation (I swear I’m not a total idiot). Help (and thanks)

@flatline Hi! Glad you are interested in the project, it was a really fun one to do. Admittedly, I’m not a EE expert either. @BDub helped me out with the circuit I needed to make it work. I believe a relay shield would do the trick, as all that’s really happening is moving a high voltage pin from LOW to HIGH.

@BDub should be able to confirm that. To get started, you can always just use an LED, or the on-board D7 pin to digitalWrite to simulate the door buzzer, and just focus on the software.

Does this help?

2 Likes

Thanks @jeiden - I’m looking through all the documentation and it’s not 100% clear to me the best/easiest way to do this. I can write local code that controls the relay shield, but I’m confused about how to link the Core to Twilio. Here is where I’m stumped:

  • What is the URL that I’m pointing Twilio at when a text comes in (now that I mention it, is it a URL or a TwiML app)?
  • Do I need to be running my own webserver for this, can the Express app reside on the Core, or can I just host it in the Particle cloud somehow?

I apologize for my continued ignorance, I’ve tried looking through the tutorials and walkthroughs but it hasn’t clicked.

@flatline

No problem! Happy to help more here.

  • You point Twilio at the open door endpoint that you expose in your Express app. For the code example, that endpoint was /openDoor. When a text is sent to that Twilio number, Twilio will automatically send a POST to /openDoor with a hunk of JSON that you can use to interact with the Core. You add the URL to hit in your Twilio dashboard.

  • You do need to run your own webserver for this currently. The Core’s firmware is not the place to run an Express app. I just ran the Express app generator to get a simple skeleton app, and edited the app.js file with the above code. For hosting, I used Heroku to keep things uber simple for deployment.

2 Likes

Thanks @jeiden

I ended up signing up for an AWS free-tier account and standing up a t2.micro LAMP server, because I understood how to get @avidan’s PHP solution off the ground better.

On the Core, I have this basic relay shield code flashed.

Twilio is pointed to my_aws_instance_dns.com/door.php

On that server, /var/www/html/door.php looks like this:

<?php

        $people = array(
                "+1234567890”=>"Me”,
                "+1xxxxxxxxx3"=>”GF”,
                "+10000000000”=>”Cleaning Lady, etc”
        );

        // if the sender is known, then greet them by name
        // otherwise, consider them just another guest
        if(!$name = $people[$_REQUEST['From']]) {
                $name = "Guest";
        }

        header("content-type: text/xml");
        echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";

$key = ‘<MYACCESSKEY>’;

if($_REQUEST)
{
$pin = "r1";
openDoor($key, $pin);
}

function openDoor($key, $pin)
{
$result = hitTheCore($key, $pin, "HIGH");
sleep(4);  //keep the buzzer pressed for 4 seconds
$result = hitTheCore($key, $pin, "LOW");
}

function hitTheCore($key, $pin, $pin_level)
{
$post_data = "access_token=$key&params=$pin,$pin_level";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.particle.io/v1/devices/MYDEVICEID/relay');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$info = curl_getinfo($ch);
$result = curl_exec($ch);
return $result;
}

?>
<Response>
        <Message>Welcome <?php echo "to our home " . $name . "! The door will open for 4 seconds."; ?></Message>
</Response>

This is almost working - Twilio definitely hits the right file when it receives a text, but I don’t think the URL is getting formed correctly - not sure if I have the pin wrong, or what. Need to get an LED I can test with. I would like to do it the way you did, since that gives me more granular control over verifying the message actually came from Twilio, but this seems close for a first try.

So I created an Express app on my AWS box using that generator, and replaced it with the code you developed, subbing in my details. How do I test this? Should Twilio be pointed at aws_dns_entry.com/openDoor:3000 or will it work over 80/HTTP?

Got this working, it turned out that I have a new(er) version of the Relay Shield that seems, for some reason, to use a different set of pins, so I had to update the code running on my Core to this instead. Works fine now.

Great to hear @flatline!!!