Node.js Twilio API on Intel Edison: Texting Your House

Have you ever been out and began to worry about the state your home was in? Did you leave the door open? Is the oven or lights on? Do your plants need to be watered? 

In days of yore you might call up your neighbor to pop over and check up on things. Now using the Intel Edison, Node.js, Twilio, and a home sensor package you can simply send a text to your house to ask how its doing. 

Over the next few posts I will be going over how to set up your Intel Edison with some basic home sensors and create a server that responds to incoming texts.

Setup Twilio API

The first thing to do is create a Twilio account to recieve your Twilio number, account ID, and authentication token. Go to https://www.twilio.com/, click get started and create your account. Make sure to request a Twilio number. 

Next install the Twilio npm module.

 npm install -g twilio

Send Text

You can now test out the Twilio API by sending out a text from the Edison. 

Run the code below on your Edison either through the XDK or by loading it onto the Edison manually. If you do not know how to do this follow the getting started guide at: https://software.intel.com/en-us/iot/library/edison-getting-started

Make sure to enter all of your Twilio account details as well as the number to send the SMS to before running the code.

// Load the twilio module
var twilio = require('twilio');
 
// Create a new REST API client to make authenticated requests against the
// twilio back end
var TWILIO_ACCOUNT_SID = 'your Twilio Account SID'
var TWILIO_AUTH_TOKEN = 'Your Twilio Authenticaion Token'
var OUTGOING_NUMBER = 'Number to call'
var TWILIO_NUMBER = 'Your Twilio number'

var client = new twilio.RestClient(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
 
// Pass in parameters to the REST API using an object literal notation. The
// REST client will handle authentication and response serialzation for you.
client.sms.messages.create({
    to:OUTGOING_NUMBER,
    from:TWILIO_NUMBER,
    body:'ahoy hoy! Testing Twilio and node.js'
}, function(error, message) {
    // The HTTP request to Twilio will run asynchronously. This callback
    // function will be called when a response is received from Twilio
    // The "error" variable will contain error information, if any.
    // If the request was successful, this value will be "falsy"
    if (!error) {
        // The second argument to the callback will contain the information
        // sent back by Twilio for the request. In this case, it is the
        // information about the text messsage you just sent:
        console.log('Success! The SID for this SMS message is:');
        console.log(message.sid);
 
        console.log('Message sent on:');
        console.log(message.dateCreated);
    } else {
        console.log('error: ' + error.message);
    }
});

If there were no errors you should receive a success message to your console as well as a text 'ahoy hoy! Testing Twilio and node.js' to the number you entered in the code.

If you receive the error: The 'From' number TWILIO_NUMBER is not a valid phone number or shortcode. make sure to activate your Twilio number, not just request it. 

  

Respond to Text

Responding to texts is a little bit more complicated than sending out a text. The Twilio server listens for incoming SMS messages to your Twilio number and then navigates to a public url containing XML instructions on how to respond. In order to do this on the Edison a HTTP server must be set up with the XML directions, and the localhost must be exposed the the public internet in order for Twilio to navigate to it.

Creating a server is easy and can be done with the http node.js module. Exposing the esrver to the public internet is a little more involved and there are a few ways of doing this. I will use localtunnel.me as they have a simple node.js API that creates a tunnel between a localhost and public URL. 

Install the localtunnel.me module on the Edison with:

npm install -g localtunnel

Now we can use the localtunnel API in order to request a public URL tunnel (You can skip to the final code block in this section for complete code):

var localtunnel = require('localtunnel');
var port = 8000;

var tunnel = localtunnel(port, function(err, tunnel) {
    if (err){console.log(err)}
    
    console.log('tunnel open at url: ' +tunnel.url);
    });

    tunnel.on('close', function() {
    // tunnels are closed
});

Now, localtunnel.me will create a random public URL saved to tunnel.url and tunnel it to localhost:8000. We must pass this random URL to Twilio in order for it to know where to navigate for the XML directions. 

To do this we can use the client API:

var client = require('twilio')(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

client.incomingPhoneNumbers(phone_SID).update({
    smsUrl: tunnel.url
}, function(err, number) {
    console.log(number.smsUrl);
});

Next we are going to create the XML directions for Twilio to respond to incoming SMS messages. This can be done by hand, or better yet using the TwML API: 

var resp = new twilio.TwimlResponse();
resp.message('Hello, this is house');

  Next we are going to create a http server using node.js to serve the XML code. 

var http = require('http');

http.createServer(function (req, res) {
  
// what the server is going to serve 

}).listen(port);
 

Finally we can combine all of these code elements into a single script that will create a http server, serve the Twilio XML, create a tunnel to a public URL, and tell Twilio what the public URL is. EXCITING!

NOTE:  Lines 17 - 20 added to actually serve the generated XML

var localtunnel = require('localtunnel');
var twilio = require('twilio');
var http = require('http');
var port = 8000;
var phone_SID = 'PN6e2855accbc67ac79c8fe6ff882fdc1f';
var TWILIO_ACCOUNT_SID = 'ACc96c9f63c4d61f7a8c653f20aec62855'
var TWILIO_AUTH_TOKEN = 'a7086deec5f8703f7284064ac6523ee7'

var client = require('twilio')(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);

http.createServer(function (req, res) {

    var resp = new twilio.TwimlResponse();
    resp.message('Hello, this is still house');
 
    //Render the TwiML XML document using "toString"
    res.writeHead(200, {
        'Content-Type':'text/xml'
    });
    res.end(resp.toString());
    
}).listen(port);

var tunnel = localtunnel(port, function(err, tunnel) {
    if (err){console.log(err)}
    
    console.log('tunnel open at url: ' +tunnel.url);
    
    client.incomingPhoneNumbers(phone_SID).update({
    smsUrl: tunnel.url
}, function(err, number) {
    console.log(number.smsUrl);
});
    
});

    tunnel.on('close', function() {
    // tunnels are closed
});

Ok, so now we have a way of responding to ANY incoming text with a response generated within the code. Tune in next time and we will add a way to parse the incoming request