Comparing version 1.0.1 to 1.0.2
@@ -8,4 +8,4 @@ var ghoma = require('./ghoma.js'); | ||
ghoma.onNew = function(plug) { | ||
console.log('Registerd ' + plug.remoteAddress+" "+plug.fullMac.toString('hex')); | ||
// Switch the plug on | ||
console.log('Registerd ' + plug.remoteAddress+" "+plug.id); | ||
// For this example switch the plug on in the moment it is registered. | ||
plug.on(); | ||
@@ -21,3 +21,3 @@ } | ||
ghoma.onClose = function(plug) { | ||
console.log('Closed ' + plug.remoteAddress); | ||
console.log('Closed ' + plug.remoteAddress); | ||
} | ||
@@ -24,0 +24,0 @@ |
/** | ||
* Usage example for the ghoma control server library together with a minimal express.js application. | ||
* | ||
* You have to 'npm install express' before starting this example by 'node express_example.js'. | ||
* You have to run 'npm install express' before starting this example by 'node express_example.js'. | ||
* A ghoma control server is started on port 4196 and a http server is started on port 3000. | ||
@@ -21,4 +21,7 @@ * | ||
// Uncomment this line to get a detailed log output | ||
// ghoma.log=console.log; | ||
//ghoma.log=console.log; | ||
var httpPort = 3000; // Express http listening port | ||
var ghomaPort = 4196; // G-Homa default port | ||
/** | ||
@@ -28,12 +31,5 @@ * List all registered plugs. | ||
app.get('/list', function (req, res) { | ||
res.setHeader('Content-Type', 'application/json'); | ||
var plugs = []; | ||
ghoma.forEach(function(plug,idx) { | ||
plugs.push( { | ||
id : plug.id, | ||
state : plug.state, | ||
triggered : plug.triggered, | ||
remoteAddress : plug.remoteAddress | ||
}); | ||
}); | ||
res.setHeader('Content-Type', 'application/json'); | ||
ghoma.forEach(function(plug) { plugs.push(plug); }); | ||
res.send(JSON.stringify(plugs)); | ||
@@ -103,7 +99,7 @@ }); | ||
// Start the ghoma control server listening server on this port | ||
ghoma.startServer(4196); | ||
ghoma.startServer(ghomaPort); | ||
// Start the express http server listening on port 3000 | ||
app.listen(3000, function () { | ||
console.log('ghoma express example app start listening on port 3000 for http requests.'); | ||
// Start the express http server listening | ||
app.listen(httpPort, function () { | ||
console.log('ghoma express example app start listening on port '+httpPort+' for http requests.'); | ||
}); |
87
ghoma.js
/** | ||
* Node.js implementation of a G-Homa Control Server | ||
* See : http://www.fhemwiki.de/wiki/G-Homa | ||
* Tested with GAO G-Homa EMW302WF | ||
* | ||
* Tested with GAO G-Homa EMW302WF (IP20) and G-Homa EMW302WFO (IP44). | ||
*/ | ||
var net = require('net'); | ||
// Prefix bytes for each data block | ||
var PREFIX = new Buffer([0x5A,0xA5]); | ||
// Postfix bytes for each data block | ||
var POSTFIX = new Buffer([0x5B,0xB5]); | ||
@@ -24,4 +27,9 @@ | ||
// Registry for nodes | ||
var gHomaRegistry = []; | ||
// Timeout to kick the nodes that have not send a heartbeat in some time | ||
var timeout = 60000*5; // 5 Minutes | ||
// The interval timer | ||
var server = net.createServer(function(socket) { | ||
@@ -43,4 +51,6 @@ log('','CONNECTED'); | ||
initalized : new Date(), | ||
lastheartbeat : new Date(), | ||
on : switchOn, | ||
off : switchOff | ||
off : switchOff, | ||
socket : socket | ||
} | ||
@@ -64,2 +74,4 @@ | ||
ghoma.id = ghoma.shortMac.toString('hex'); | ||
// Remember the trigger code - used for on/off. | ||
ghoma.triggercode = msg.payload.slice(4,6); | ||
send('INIT2', BuildMsg(INIT2)); | ||
@@ -161,3 +173,5 @@ } else if( msg.command.equals( CMD_INIT2REPLY ) ) { | ||
Buffer.concat([ | ||
new Buffer([0x10,0x01,0x01,0x0a,0xe0,0x32,0x23]), | ||
//new Buffer([0x10,0x01,0x01,0x0a,0xe0,0x32,0x23]), | ||
new Buffer([0x10,0x01,0x01,0x0a,0xe0]), | ||
ghoma.triggercode, | ||
ghoma.shortMac, | ||
@@ -174,3 +188,5 @@ new Buffer([0xff,0xfe,0x00,0x00,0x10,0x11,0x00,0x00,0x01,0x00,0x00,0x00,0xff]) | ||
Buffer.concat([ | ||
new Buffer([0x10,0x01,0x01,0x0a,0xe0,0x32,0x23]), | ||
//new Buffer([0x10,0x01,0x01,0x0a,0xe0,0x32,0x23]), | ||
new Buffer([0x10,0x01,0x01,0x0a,0xe0]), | ||
ghoma.triggercode, | ||
ghoma.shortMac, | ||
@@ -241,6 +257,17 @@ new Buffer([0xff,0xfe,0x00,0x00,0x10,0x11,0x00,0x00,0x01,0x00,0x00,0x00,0x00]) | ||
function pad(pad, msg) { return (pad + msg).slice(-pad.length); } | ||
}); | ||
// | ||
// Local helper functions | ||
// | ||
pad = function (pad, msg) { return (pad + msg).slice(-pad.length); } | ||
log = function(action, msg) { | ||
if( exports.log ) { | ||
exports.log(pad(action_padding,'GHOMA')+' ['+pad(msg_padding,action)+'] '+msg); | ||
} | ||
} | ||
// Get plug by id | ||
indexOfById = function(id) { | ||
@@ -256,2 +283,19 @@ var fid = -1; | ||
// Return not the complete plug object | ||
filterPlug = function(plug) { | ||
return { | ||
id : plug.id, | ||
state : plug.state, | ||
statechanged: plug.statechanged, | ||
tiggered: plug.triggered, | ||
prevstate : plug.prevstate, | ||
initalized : plug.initalized, | ||
remoteAddress : plug.remoteAddress, | ||
remotePort : plug.remotePort, | ||
on : plug.on, | ||
off : plug.off, | ||
heartbeat: plug.lastheartbeat | ||
} | ||
} | ||
/** | ||
@@ -263,3 +307,5 @@ * Iterator method, to go to each registered plug. | ||
exports.forEach = function(callback) { | ||
gHomaRegistry.forEach(callback); | ||
gHomaRegistry.forEach(function(plug) { | ||
callback(filterPlug(plug)); | ||
}); | ||
} | ||
@@ -274,3 +320,3 @@ | ||
var idx = indexOfById(id); | ||
return (idx!=-1 ? gHomaRegistry[idx] : null); | ||
return (idx!=-1 ? filterPlug(gHomaRegistry[idx]) : null); | ||
} | ||
@@ -285,3 +331,20 @@ | ||
exports.startServer=function(port, address) { | ||
log('START','Control server start at '+new Date()); | ||
// Create server socket | ||
server.listen(port, address); | ||
// Start the check timeer | ||
checkTimer = setInterval( function() { | ||
var now = new Date(); | ||
log('CHECK',now); | ||
gHomaRegistry.forEach( function(plug) { | ||
var diff = now.getTime() - plug.lastheartbeat.getTime(); | ||
if( diff > timeout ) { | ||
log('TIMEOUT', 'No hearbeat from ['+plug.id+'] in '+diff+' ms. Last hearbeat was ['+plug.lastheartbeat+'].'); | ||
plug.socket.destroy(); // This should trigger close | ||
} | ||
}); | ||
}, timeout ); | ||
} | ||
@@ -293,3 +356,11 @@ | ||
exports.shutdown=function() { | ||
log('SHUTDOWN','Control server shutdown at '+new Date()); | ||
// Destroy each socket | ||
gHomaRegistry.forEach(function(plug) { | ||
plug.socket.destroy(); | ||
}); | ||
// Close server socket | ||
server.close(); | ||
// Remove check timer | ||
clearInterval(checkTimer); | ||
} |
{ | ||
"name": "ghoma", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"description": "A control server for g-homa wifi plugs.", | ||
@@ -5,0 +5,0 @@ "main": "ghoma.js", |
@@ -1,2 +0,2 @@ | ||
A control server for G-Homa Wifi plugs written in [node](http://nodejs.org). | ||
A control server for G-Homa wifi plugs written in [node](http://nodejs.org). | ||
@@ -20,29 +20,28 @@ This library enables the use of the wifi plugs without the need to allow them to call home. | ||
```js | ||
var ghoma = require('ghoma'); | ||
var ghoma = require('./ghoma.js'); | ||
// Uncomment this line to get a detailed console log output | ||
// Uncomment this line to get a detailed log output | ||
// ghoma.log=console.log; | ||
// A new plug was registered. | ||
ghoma.onNew = function(ghoma) { | ||
console.log('Registerd ' + ghoma.remoteAddress+" "+ghoma.id); | ||
// Switch the plug on | ||
ghoma.on(); | ||
// Register a listener for new plugs | ||
ghoma.onNew = function(plug) { | ||
console.log('Registerd ' + plug.remoteAddress+" "+plug.id); | ||
// For this example switch the plug on in the moment it is registered. | ||
plug.on(); | ||
} | ||
// Called when the plug switches on or off | ||
ghoma.onStatusChange = function(ghoma) { | ||
console.log('New state of ' + ghoma.remoteAddress+' is '+ghoma.state+' triggered '+ghoma.triggered); | ||
ghoma.onStatusChange = function(plug) { | ||
console.log('New state of ' + plug.remoteAddress+' is '+plug.state+' triggered '+plug.triggered); | ||
} | ||
// Called when the plug connection to the server was lost | ||
ghoma.onClose = function(ghoma) { | ||
console.log('Closed ' + ghoma.remoteAddress); | ||
ghoma.onClose = function(plug) { | ||
console.log('Closed ' + plug.remoteAddress); | ||
} | ||
/* Enable to listen for heartbeats of the plugs | ||
ghoma.onHeartbeat = function(ghoma) { | ||
console.log('Heartbeat ' + ghoma.remoteAddress); | ||
// Listen for heartbeats of the plugs | ||
ghoma.onHeartbeat = function(plug) { | ||
console.log('Heartbeat ' + plug.remoteAddress); | ||
} | ||
*/ | ||
@@ -53,2 +52,3 @@ // Start a listening server on this port | ||
A more comprehensive example shows the node-ghoma library in combination with the express framework. See [express_example.js](https://github.com/rodney42/node-ghoma/blob/master/express_example.js) in the git repository. | ||
@@ -63,12 +63,12 @@ | ||
#### onNew(ghoma) | ||
#### onNew(plug) | ||
Called when a new plug is registered with the control server. | ||
#### onStatusChange(ghoma) | ||
#### onStatusChange(plug) | ||
Called if the plug was switched on or off. | ||
#### onClose(ghoma) | ||
#### onClose(plug) | ||
Called if the connection to the plug was lost. | ||
#### onHeartbeat(ghoma) | ||
#### onHeartbeat(plug) | ||
Called when a heartbeat event from the plug occurred. | ||
@@ -81,33 +81,30 @@ | ||
#### forEach(callback) | ||
Iterate through each registered ghome plug object. | ||
Iterate through each registered plug object. The callback will receive the plug object. | ||
Example: | ||
```js | ||
ghoma.forEach(function(plug) { | ||
console.log('Plug with id '+plug.id+' at '+plug.remoteAddress+' has state '+plug.state); | ||
}); | ||
``` | ||
#### get(id) | ||
Helper method to get a plug by id. The id is the hex representation of the short mac of the plug. It can be used to unique identify the plug. | ||
### The ghoma object | ||
### The ghoma plug object | ||
The ghoma object is used as callback argument for each notification and as a result of method calls. | ||
The plug object is used as callback argument for each notification and as a result of method calls. | ||
```js | ||
{ | ||
"state": "on", // 'on' or 'off' | ||
"triggered": "local", // 'local' or 'remote'. 'local' if the plug button was used. | ||
"remoteAddress": "::ffff:192.168.12.136", | ||
"remotePort": 12635, | ||
"initalized": "2016-07-24T20:57:00.525Z", // First seen time | ||
"shortMac": { | ||
"type": "Buffer", | ||
"data": [ | ||
... | ||
] | ||
}, | ||
"id": "62ad10", // id from the short mac | ||
"fullMac": { | ||
"type": "Buffer", | ||
"data": [ | ||
... | ||
] | ||
}, | ||
"prevstate": "unknown", // The previous state is unknown if the plug was yet registered. 'on' or 'off' otherwise | ||
"statechanged": "2016-07-26T20:57:02.975Z" // / Last state change | ||
"id": "53ae11", // Unique ID of the plug. These are the last 3 bytes from the MAC address. | ||
"state": "on", // Current state. May be 'on','off' or 'unknown' | ||
"statechanged": "2017-01-22T21:00:19.733Z", // Time of the last state change | ||
"tiggered": "local", // local: Triggered by switch on the plug. remote: Triggered by control server | ||
"prevstate": "off", // The previous state. May be 'unkown', if the plug has no state send until now | ||
"initalized": "2017-01-22T20:59:23.565Z", // Time the plug has registered with the control server | ||
"remoteAddress": "::ffff:192.168.1.136", // The address of the plug | ||
"remotePort": 14283, // The remote port | ||
"heartbeat": "2017-01-22T21:00:20.893Z" // Time of the last heartbeat call from the plug | ||
} | ||
@@ -114,0 +111,0 @@ ``` |
20179
434
125