From 244ca39609ede42cca358d2642333b8fef3c1a94 Mon Sep 17 00:00:00 2001 From: Robin Bolscher Date: Wed, 7 Oct 2015 16:41:43 +0200 Subject: [PATCH] added network scanning functionality If a new new instance is instantiated without a host defined, it will start scanning the network for devices listening on port 4242. --- .gitignore | 1 + lib/neo.js | 221 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 155 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index e16e6a0..107b271 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ results npm-debug.log /node_modules/ test/mocha.opts +.idea/* diff --git a/lib/neo.js b/lib/neo.js index 09f77f5..813a1b2 100644 --- a/lib/neo.js +++ b/lib/neo.js @@ -1,125 +1,212 @@ "use strict"; -var net = require('net'); -var util = require("util"); -var events = require('events'); +var net = require( 'net' ); +var util = require( "util" ); +var events = require( 'events' ); +var ip = require( 'ip' ); +var Socket = require( 'net' ).Socket; + +function Neo ( host, port ) { + this.port = (!port) ? 4242 : port; + var self = this; + if ( !host ) { + scanNetwork( this.port, function ( port, host ) { + self.host = host; + events.EventEmitter.call( self ); + + } ); + } + else { + this.host = host; + events.EventEmitter.call( this ); + } -function Neo(host, port) { - this.host = host; - this.port = (typeof port === "undefined") ? 4242 : port; - events.EventEmitter.call(this); } +function scanNetwork ( port, callback ) { + // Create LAN address + var LAN = ip.address().substr( 0, ip.address().lastIndexOf( '.' ) ) || ip.address(); + + // Scan over a range of IP addresses and execute a function each time the PORT is shown to be open. + for ( var i = 0; i <= 255; i++ ) { -util.inherits(Neo, events.EventEmitter); + // Scan local network + createConnection( port, LAN + '.' + i, function ( err, result ) { + if ( !err && result ) { + callback( port, result ); + } + } ); + } +} +/** + * This method creates a socket connection to host@port and returns + * whether this device is listening on port or not. + * @param port + * @param host + * @param checkPort + * @param callback + */ +var createConnection = function ( port, host, callback ) { + var socket = new Socket(), status = null, error = null; + // Socket connection established, port is open + socket.on( 'connect', function () { + callback( error, host ); + socket.end(); + } ); + + // If no response, assume port is not listening + socket.setTimeout( 1500 ); + + // When connection timeout destroy and return + socket.on( 'timeout', function () { + status = 'closed'; + error = true; + socket.destroy(); + } ); + + // On error, set status to closed + socket.on( 'error', function () { + status = 'closed'; + } ); + + // When a socket is closed + socket.on( 'close', function () { + //var neo = new heatmiser.Neo(ip); + // + //neo.on('success', function(data) { + // //console.log(data); + // //callback(neo); + //}); + //neo.on('error', function(data) { + // //console.log(data); + // //callback(null); + //}); + // + //neo.info(); + //neo.statistics(); + //neo.setAway(true, ["living","kitchen"]); + } ); + // Connect socket to host@port + socket.connect( port, host ); +} + +util.inherits( Neo, events.EventEmitter ); var ENCODING = 'ascii'; -var FLOAT_FIELDS = ["CURRENT_FLOOR_TEMPERATURE","CURRENT_SET_TEMPERATURE","CURRENT_TEMPERATURE","MAX_TEMPERATURE","MIN_TEMPERATURE"]; +var FLOAT_FIELDS = [ "CURRENT_FLOOR_TEMPERATURE", "CURRENT_SET_TEMPERATURE", "CURRENT_TEMPERATURE", "MAX_TEMPERATURE", "MIN_TEMPERATURE" ]; // Construct an arbitrary thermostat command -Neo.prototype.command = function(data, callback) { +Neo.prototype.command = function ( data, callback ) { var self = this; - var client = net.connect({host: this.host, port: this.port}, function() { //'connect' listener - client.write(JSON.stringify(data), ENCODING); - client.write(new Buffer([0])); // null terminate the string - }); + var client = net.connect( { host: this.host, port: this.port }, function () { //'connect' listener + client.write( JSON.stringify( data ), ENCODING ); + client.write( new Buffer( [ 0 ] ) ); // null terminate the string + } ); - client.setTimeout(3000); + client.setTimeout( 3000 ); var buffer; - client.on('data', function(data) { - if (buffer == null) { + client.on( 'data', function ( data ) { + if ( buffer == null ) { buffer = data; - } else { - buffer = Buffer.concat([buffer, data]); + } + else { + buffer = Buffer.concat( [ buffer, data ] ); } client.end(); - }); - client.on('timeout', function(e){ + } ); + client.on( 'timeout', function ( e ) { client.end(); - self.emit('error', (typeof e === 'undefined') ? new Error("Timed out") : e); - }); - client.on('error', function(e){ + self.emit( 'error', (typeof e === 'undefined') ? new Error( "Timed out" ) : e ); + } ); + client.on( 'error', function ( e ) { client.end(); - self.emit('error', e); - }); + self.emit( 'error', e ); + } ); - client.on('end', function() { + client.on( 'end', function () { // strip null byte at the end and parse json - var data = JSON.parse(buffer.toString(ENCODING,0,buffer.length-1)); + var data = JSON.parse( buffer.toString( ENCODING, 0, buffer.length - 1 ) ); // some json floats are set as strings, fix - if (data.devices != null) { - for (var i=0; i