You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
306 lines
9.6 KiB
306 lines
9.6 KiB
/**
|
|
*
|
|
* geofency adapter
|
|
* This Adapter is based on the geofency adapter of ccu.io
|
|
*
|
|
*/
|
|
|
|
/* jshint -W097 */// jshint strict:false
|
|
/*jslint node: true */
|
|
"use strict";
|
|
|
|
var utils = require(__dirname + '/lib/utils'); // Get common adapter utils
|
|
|
|
var webServer = null;
|
|
var activate_server = false;
|
|
|
|
var adapter = utils.Adapter({
|
|
name: 'geofency',
|
|
|
|
unload: function (callback) {
|
|
try {
|
|
adapter.log.info("terminating http" + (webServer.settings.secure ? "s" : "") + " server on port " + webServer.settings.port);
|
|
callback();
|
|
} catch (e) {
|
|
callback();
|
|
}
|
|
},
|
|
ready: function () {
|
|
adapter.log.info("Adapter got 'Ready' Signal - initiating Main function...");
|
|
main();
|
|
},
|
|
message: function (msg) {
|
|
processMessage(msg);
|
|
}
|
|
});
|
|
|
|
function main() {
|
|
checkCreateNewObjects();
|
|
if (adapter.config.activate_server !== undefined) activate_server = adapter.config.activate_server;
|
|
else activate_server = true;
|
|
if (activate_server) {
|
|
if (adapter.config.ssl) {
|
|
// subscribe on changes of permissions
|
|
adapter.subscribeForeignObjects('system.group.*');
|
|
adapter.subscribeForeignObjects('system.user.*');
|
|
|
|
if (!adapter.config.certPublic) {
|
|
adapter.config.certPublic = 'defaultPublic';
|
|
}
|
|
if (!adapter.config.certPrivate) {
|
|
adapter.config.certPrivate = 'defaultPrivate';
|
|
}
|
|
|
|
// Load certificates
|
|
adapter.getForeignObject('system.certificates', function (err, obj) {
|
|
if (err || !obj || !obj.native.certificates || !adapter.config.certPublic || !adapter.config.certPrivate || !obj.native.certificates[adapter.config.certPublic] || !obj.native.certificates[adapter.config.certPrivate]
|
|
) {
|
|
adapter.log.error('Cannot enable secure web server, because no certificates found: ' + adapter.config.certPublic + ', ' + adapter.config.certPrivate);
|
|
} else {
|
|
adapter.config.certificates = {
|
|
key: obj.native.certificates[adapter.config.certPrivate],
|
|
cert: obj.native.certificates[adapter.config.certPublic]
|
|
};
|
|
|
|
}
|
|
webServer = initWebServer(adapter.config);
|
|
});
|
|
} else {
|
|
webServer = initWebServer(adapter.config);
|
|
}
|
|
}
|
|
}
|
|
|
|
function initWebServer(settings) {
|
|
|
|
var server = {
|
|
server: null,
|
|
settings: settings
|
|
};
|
|
|
|
if (settings.port) {
|
|
if (settings.ssl) {
|
|
if (!adapter.config.certificates) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if (settings.ssl) {
|
|
server.server = require('https').createServer(adapter.config.certificates, requestProcessor);
|
|
} else {
|
|
server.server = require('http').createServer(requestProcessor);
|
|
}
|
|
|
|
server.server.__server = server;
|
|
} else {
|
|
adapter.log.error('port missing');
|
|
process.exit(1);
|
|
}
|
|
|
|
if (server.server) {
|
|
adapter.getPort(settings.port, function (port) {
|
|
if (port != settings.port && !adapter.config.findNextPort) {
|
|
adapter.log.error('port ' + settings.port + ' already in use');
|
|
process.exit(1);
|
|
}
|
|
server.server.listen(port);
|
|
adapter.log.info('http' + (settings.ssl ? 's' : '') + ' server listening on port ' + port);
|
|
});
|
|
}
|
|
|
|
if (server.server) {
|
|
return server;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function requestProcessor(req, res) {
|
|
var check_user = adapter.config.user;
|
|
var check_pass = adapter.config.pass;
|
|
|
|
// If they pass in a basic auth credential it'll be in a header called "Authorization" (note NodeJS lowercases the names of headers in its request object)
|
|
var auth = req.headers.authorization; // auth is in base64(username:password) so we need to decode the base64
|
|
adapter.log.debug("Authorization Header is: ", auth);
|
|
|
|
var username = '';
|
|
var password = '';
|
|
var request_valid = true;
|
|
if (auth && check_user.length > 0 && check_pass.length > 0) {
|
|
var tmp = auth.split(' '); // Split on a space, the original auth looks like "Basic Y2hhcmxlczoxMjM0NQ==" and we need the 2nd part
|
|
var buf = new Buffer(tmp[1], 'base64'); // create a buffer and tell it the data coming in is base64
|
|
var plain_auth = buf.toString(); // read it back out as a string
|
|
|
|
adapter.log.debug("Decoded Authorization ", plain_auth);
|
|
// At this point plain_auth = "username:password"
|
|
var creds = plain_auth.split(':'); // split on a ':'
|
|
username = creds[0];
|
|
password = creds[1];
|
|
if ((username != check_user) || (password != check_pass)) {
|
|
adapter.log.warn("User credentials invalid");
|
|
request_valid = false;
|
|
}
|
|
}
|
|
/*else {
|
|
adapter.log.warn("Authorization Header missing but user/pass defined");
|
|
request_valid = false;
|
|
}*/
|
|
if (!request_valid) {
|
|
res.statusCode = 403;
|
|
res.end();
|
|
return;
|
|
}
|
|
if (req.method === 'POST') {
|
|
var body = '';
|
|
|
|
adapter.log.debug("request path:" + req.path);
|
|
|
|
req.on('data', function (chunk) {
|
|
body += chunk;
|
|
});
|
|
|
|
req.on('end', function () {
|
|
var user = req.url.slice(1);
|
|
var jbody = JSON.parse(body);
|
|
|
|
handleWebhookRequest(user, jbody);
|
|
|
|
res.writeHead(200);
|
|
res.write("OK");
|
|
res.end();
|
|
});
|
|
}
|
|
else {
|
|
res.writeHead(500);
|
|
res.write("Request error");
|
|
res.end();
|
|
}
|
|
}
|
|
|
|
function handleWebhookRequest(user, jbody) {
|
|
adapter.log.info("adapter geofency received webhook from device " + user + " with values: name: " + jbody.name + ", entry: " + jbody.entry);
|
|
var id = user + '.' + jbody.name.replace(/\s|\./g, '_');
|
|
|
|
// create Objects if not yet available
|
|
adapter.getObject(id, function (err, obj) {
|
|
if (err || !obj) return createObjects(id, jbody);
|
|
setStates(id, jbody);
|
|
setAtHome(user, jbody);
|
|
});
|
|
}
|
|
|
|
var lastStateNames = ["lastLeave", "lastEnter"],
|
|
stateAtHomeCount = "atHomeCount",
|
|
stateAtHome = "atHome";
|
|
|
|
|
|
function setStates(id, jbody) {
|
|
adapter.setState(id + '.entry', {val: ((jbody.entry == "1") ? true : false), ack: true});
|
|
|
|
var ts = adapter.formatDate(new Date(jbody.date), "YYYY-MM-DD hh:mm:ss");
|
|
adapter.setState(id + '.date', {val: ts, ack: true});
|
|
adapter.setState(id + '.' + lastStateNames[(jbody.entry == "1") ? 1 : 0], {val: ts, ack: true});
|
|
}
|
|
|
|
|
|
function createObjects(id, b) {
|
|
// create all Objects
|
|
var children = [];
|
|
|
|
var obj = {
|
|
type: 'state',
|
|
//parent: id,
|
|
common: {name: 'entry', read: true, write: true, type: 'boolean'},
|
|
native: {id: id}
|
|
};
|
|
adapter.setObjectNotExists(id + ".entry", obj);
|
|
children.push(obj);
|
|
obj = {
|
|
type: 'state',
|
|
//parent: id,
|
|
common: {name: 'date', read: true, write: true, type: 'string'},
|
|
native: {id: id}
|
|
};
|
|
adapter.setObjectNotExists(id + ".date", obj);
|
|
children.push(obj);
|
|
|
|
for (var i = 0; i < 2; i++) {
|
|
obj.common.name = lastStateNames[i];
|
|
adapter.setObjectNotExists(id + "." + lastStateNames[i], obj);
|
|
children.push(obj);
|
|
}
|
|
|
|
adapter.setObjectNotExists(id, {
|
|
type: 'device',
|
|
//children: children,
|
|
common: {id: id, name: b.name},
|
|
native: {name: b.name, lat: b.lat, long: b.long, radius: b.radius, device: b.device, beaconUUID: b.beaconUUID, major: b.major, minor: b.minor}
|
|
}, function (err, obj) {
|
|
if (!err && obj) setStates(id, b);
|
|
});
|
|
}
|
|
|
|
|
|
function setAtHome(userName, body) {
|
|
if (body.name.toLowerCase() !== adapter.config.atHome.toLowerCase()) return;
|
|
var atHomeCount, atHome;
|
|
adapter.getState(stateAtHomeCount, function (err, obj) {
|
|
if (err) return;
|
|
atHomeCount = obj ? obj.val : 0;
|
|
adapter.getState(stateAtHome, function (err, obj) {
|
|
if (err) return;
|
|
atHome = obj ? (obj.val ? JSON.parse(obj.val) : []) : [];
|
|
var idx = atHome.indexOf(userName);
|
|
if (body.entry === '1') {
|
|
if (idx < 0) {
|
|
atHome.push(userName);
|
|
adapter.setState(stateAtHome, JSON.stringify(atHome), true);
|
|
}
|
|
} else {
|
|
if (idx >= 0) {
|
|
atHome.splice(idx, 1);
|
|
adapter.setState(stateAtHome, JSON.stringify(atHome), true);
|
|
}
|
|
}
|
|
if (atHomeCount !== atHome.length) adapter.setState(stateAtHomeCount, atHome.length, true);
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
function createAndSetObject(id, obj) {
|
|
adapter.setObjectNotExists(id, obj, function (err) {
|
|
adapter.setState(id, 0, true);
|
|
});
|
|
|
|
}
|
|
|
|
function checkCreateNewObjects() {
|
|
|
|
function doIt() {
|
|
var fs = require('fs'),
|
|
io = fs.readFileSync(__dirname + "/io-package.json"),
|
|
objs = JSON.parse(io);
|
|
|
|
for (var i = 0; i < objs.instanceObjects.length; i++) {
|
|
createAndSetObject(objs.instanceObjects[i]._id, objs.instanceObjects[i]);
|
|
}
|
|
}
|
|
|
|
var timer = setTimeout(doIt, 2000);
|
|
adapter.getState(stateAtHome, function (err, obj) {
|
|
clearTimeout(timer);
|
|
if (!obj) {
|
|
doIt();
|
|
}
|
|
});
|
|
}
|
|
|
|
function processMessage(message) {
|
|
if (!message || !message.message.user || !message.message.data) return;
|
|
|
|
adapter.log.info('Message received = ' + JSON.stringify(message));
|
|
|
|
handleWebhookRequest(message.message.user, message.message.data);
|
|
}
|