Introduction: Get Public IP Change Notification With RPi and Node.JS

With a Raspberry PI at home and a real need to, sometimes, access it remotely, whether by ssh or any other service I wanted a simple way to always have by hand my home dynamic public IP address. Also a requirement, no need to subscribe a third party service for that. Having some knowledge in Node.js, I started looking into it. Later I found that my IP doesn't change so often as I supposed... Good, i don't feel bad for having opt for e-mail notifications! :)

Step 1: Requirements

  1. A RaspberryPI (any version) or any other computer (Linux), and physical access to it or accessible by "ssh".
  2. Node.js Installed (I won't talk about that, because there's lots of tutorials on the internet and that's not the point of this instructable)
  3. A router connected to the internet (so that you have a public IP)
  4. Access to a SMTP Mail Server. Not hard if you have an ISP account.

Step 2: Preparation

Whether directly or remotely by ssh, you should open a terminal window on your RPI and gain access to the command line.

  • DIG:

'dig' is a linux command you can use to query DNS name servers on some tasks. For this project dig is the "magic" that obtains the public IP. Is part of a package that you can install with the following command :

> sudo apt-get install dnsutils

After installing you can test it issuing the following command at command prompt. (Later it will be used in our NodeJS app):

> dig +short myip.opendns.com @resolver1.opendns.com

If all went good, this should return your public ip

  • NODEMAILER

I've chosen mail to receive the change notifications. For that I used an easy to use Node module available at :

https://github.com/nodemailer/nodemailer

To install it just go to the terminal window and type (if you're not root, precede it with 'sudo'):

> npm install nodemailer
...
> npm install nodemailer-smtp-transport

The second command installs a second Node.js module needed to create a SMTP transport for the nodemailer.

Step 3: The Node.js Application (config.js)

First I created a config file (config.js) with the following content:

module.exports = {	
	// number of minutes between calls to get the public IP
	getServerInterval: function(){
		return 180;  
	},

	// SMTP server options
	getServerOptions: function(){ 
		return {
			host		: 'smtp.your_isp.com',
			port		: 465, 
			secure		: true,
			ignoreTLS	: true,
			debug		: true,
			auth: {
				user : 'youra_account@your_isp.com',
				pass : 'your_password'
			}
		}
	},
	getMailOptions: function(){
		return {
			from: '????@????.com',
    			to: '????@???.com',  // the email address where you want to receive the notification
    			
			// the next parameters can be specified later
			subject: 'IP has changed',
    			text: 'Hello world',
			html: '<strong>Hello world</strong>' // html body
		}
	}
}

The main application will import this config file in the following lines:

var intervalMinutes = require('./config.js').getServerInterval();

var serverOptions = require('./config.js').getServerOptions();
var mailOptions = require('./config.js').getMailOptions();

Step 4: The Node.js Application (app.js)

The main application file will be:

Start by the requires (nodemailer, nodemailer-smtp-transport, fs and the already made config.js )

// Variable declaration and requires

var exec = require('child_process').exec;
var nodeMailer = require('nodemailer');

var smtpTransport = require('nodemailer-smtp-transport');

var serverOptions = require('./config.js').getServerOptions();

var mailOptions = require('./config.js').getMailOptions();

var intervalMinutes = require('./config.js').getServerInterval();

var fs = require('fs');

var sendServer = nodeMailer.createTransport(smtpTransport(serverOptions));

var logStream = fs.createWriteStream("log.txt", {'flags': 'a'});

var lastIP = "";

// The function responsible for sending the mail

var sendMail = function(ipAddress){ var msg= "---------------------------\n";

msg += "SendMail : " + ipAddress ;

mailOptions.text = ipAddress

mailOptions.html = "" + ipAddress + "";

sendServer.sendMail(mailOptions, function(error, info){

if (error){

msg += "Err : " + error;

logStream.write(" SendMail.Err : " + error);

console.log(error);

}else{

console.log('Message sent: ' + info.response); msg += info.response ;

}

msg += "\n---------------------------\n";

logStream.write(msg);

})

};

// Getting the external IP address

var getExternalIp = function(errorCallback,successCallback){

var child = exec("dig +short myip.opendns.com @resolver1.opendns.com", function (error, stdout, stderr) {

if (error !== null) {

errorCallback(error);

}else{

var dt = new Date();

var dtSt = dt.getFullYear() + '-' + ("0" + (dt.getMonth() + 1)).slice(-2) + '-' + ("0" + (dt.getDate() + 1)).slice(-2) + ' ' + ("0" + dt.getHours()).slice(-2) + ':' + ("0" + dt.getMinutes()).slice(-2) + ':' + ("0" + dt.getSeconds()).slice(-2)

logStream.write(dt + " : " + stdout) ;

if (stdout != lastIP){

var lastStream = fs.createWriteStream("last.txt");

lastStream.write(stdout);

lastIP = stdout

successCallback(stdout);

}

}

});

}

// Error callback to be called on next two

var errorFunction = function(err){
logStream.write("ERR : " + err);

}

// Main Timer to poll IP and send email if it changed

function startTimer(){
setInterval(function(){

getExternalIp(errorFunction, sendMail);

}, 60000 * intervalMinutes);

}

// Each launch of the application, reads the last obtained IP from a manual created file (last.txt)

fs.readFile('./last.txt', 'utf8' , function(err, data){
if (err) throw err;

else{

var dt = new Date();

var dtSt = dt.getFullYear() + '-' + ("0" + (dt.getMonth() + 1)).slice(-2) + '-' + ("0" + (dt.getDate() )).slice(-2) + ' ' + ("0" + dt.getHours()).slice(-2) + ':' + ("0" + dt.getMinutes()).slice(-2) + ':' + ("0" + dt.getSeconds()).slice(-2)

lastIP = data;

logStream.write("\n");

logStream.write("\n");

logStream.write("*****************************************\n");

logStream.write(dtSt) ;

logStream.write("\n");

console.log("startServer:lastIP : " + lastIP);

logStream.write("START_SERVER : lastIP : " + lastIP + "\n");

getExternalIp(errorFunction, sendMail);

setTimeout(startTimer, 10000);

}

})

Step 5: Running It

In order to test it, issue:

$ node app.js

Watch the log.txt and the last.txt files.

If you want (I think you should) have it persistently running, no matter what happens (reboots, ...)

see my other Instructable:

Node.js app as a RPI Service (boot at startup)

Cheers...