From d545d449c685f3c8582fcef5a0b88846450904e2 Mon Sep 17 00:00:00 2001 From: josch Date: Wed, 13 Jun 2012 23:20:20 +0200 Subject: [PATCH] initial commit --- Makefile | 8 + avahi.service | 10 + collector.js | 376 +++++++++++++++++++++++++++++ debian/changelog | 5 + debian/compat | 1 + debian/control | 8 + debian/rules | 4 + debian/wattsapp-collector.init | 60 +++++ debian/wattsapp-collector.postinst | 42 ++++ 9 files changed, 514 insertions(+) create mode 100644 Makefile create mode 100644 avahi.service create mode 100755 collector.js create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100755 debian/rules create mode 100644 debian/wattsapp-collector.init create mode 100644 debian/wattsapp-collector.postinst diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..67ef1bb --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +install: + env | grep DESTDIR + env | grep PREFIX + mkdir -p ${DESTDIR}/usr/bin + cp collector.js ${DESTDIR}/usr/bin/wattsapp-collector + chmod +x ${DESTDIR}/usr/bin/wattsapp-collector + mkdir -p ${DESTDIR}/etc/avahi/services/ + cp avahi.service ${DESTDIR}/etc/avahi/services/wattsapp-collector.service diff --git a/avahi.service b/avahi.service new file mode 100644 index 0000000..1a2781e --- /dev/null +++ b/avahi.service @@ -0,0 +1,10 @@ + + + + collector + + _https._tcp + 8443 + ns=wattsapp;ip=2001:638:709:5::5 + + diff --git a/collector.js b/collector.js new file mode 100755 index 0000000..7809a58 --- /dev/null +++ b/collector.js @@ -0,0 +1,376 @@ +#!/usr/bin/env node + +const crypto = require('crypto'), + http = require('http'), + https = require('https'), + fs = require('fs'), + util = require('util'), + url = require('url'), + exec = require('child_process').exec, +// mdns = require('mdns'), + sqlite3 = require('sqlite3').verbose(); + db = new sqlite3.Database('/var/lib/wattsapp/collector.sqlite'); + +function handle_list(res, query) { + result = []; + + db.each('SELECT * FROM sensors', function(err, row) { + if (err) throw err; + result.push({ + 'id': row['id'], + 'name': row['name'], + 'status': row['status'] == 0 ? "inactive" : "active", + 'type': row['type'], + 'unit': row['unit'], + 'location': row['lat']+','+row['lon'] + }); + }, + function() { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify(result)); + }); +} + +function handle_details(res, query) { + function foreach_value_row(err, row) { + if (err) throw err; + result[row['id']]['values'].push({ + 'begin': row['begin'], + 'end': row['end'], + 'value': row['value'], + }); + }; + + function foreach_value_complete() { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify(result)); + } + + function foreach_sensors_row(err, row) { + if (err) throw err; + result[row['id']] = { + 'name': row['name'], + 'status': row['status'] == 0 ? 'inactive' : 'active', + 'type': row['type'], + 'unit': row['unit'], + 'location': row['lat']+","+row['lon'], + 'values': [] + } + } + + function foreach_sensors_complete() { + var values_query = "SELECT * FROM value"; + var values_param = []; + var begin_time = null; + var end_time = null; + + if (query['meters'] || query['time']) { + values_query += " WHERE"; + } + + if (query['meters']) { + var sensors = query['meters'].split(','); + values_query += " id IN ("; + values_query += sensors.map(function(e) { return "?" }).join(','); + values_query += ")"; + values_param = values_param.concat(sensors); + } + + if (query['meters'] && query['time']) { + values_query += " AND"; + } + + if (query['time']) { + var time = query['time'].split(':') + time_begin = time[0]; + time_end = time[1]; + + if (time_begin > 0 && time_end > 0) { + values_query += " begin > ? AND end < ?"; + values_param = values_param.concat([time_begin, time_end]); + } else if (time_begin > 0) { + values_query += " begin > ?"; + values_param = values_param.concat([time_begin]); + } else if (time_end > 0) { + values_query += " end < ?"; + values_param = values_param.concat([time_end]); + } + } + + db.each(values_query, values_param, foreach_value_row, foreach_value_complete); + } + + result = {}; + + var sensors_query = "SELECT * FROM sensors"; + var sensors = []; + + if (query['meters']) { + var sensors = query['meters'].split(','); + sensors_query += " WHERE id IN ("; + sensors_query += sensors.map(function(e) { return "?" }).join(','); + sensors_query += ")" + } + + db.each(sensors_query, sensors, foreach_sensors_row, foreach_sensors_complete); +} + +function handle_sum(res, query) { + function foreach_value_row(err, row) { + if (err) throw err; + result[row['id']]['values'].push({ + 'begin': row['begin'], + 'end': row['end'], + 'value': row['value'], + }); + }; + + function foreach_value_complete() { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify(result)); + } + + function foreach_sensors_row(err, row) { + if (err) throw err; + result[row['id']] = { + 'name': row['name'], + 'status': row['status'] == 0 ? 'inactive' : 'active', + 'type': row['type'], + 'unit': row['unit'], + 'location': row['lat']+","+row['lon'], + 'values': [] + } + } + + function foreach_sensors_complete() { + var values_query = "SELECT id, MIN(begin) AS begin, MAX(end) AS end, SUM(value) AS value FROM value"; + var values_param = []; + var begin_time = null; + var end_time = null; + + if (query['meters'] || query['time']) { + values_query += " WHERE"; + } + + if (query['meters']) { + var sensors = query['meters'].split(','); + values_query += " id IN ("; + values_query += sensors.map(function(e) { return "?" }).join(','); + values_query += ")"; + values_param = values_param.concat(sensors); + } + + if (query['meters'] && query['time']) { + values_query += " AND"; + } + + if (query['time']) { + var time = query['time'].split(':') + time_begin = time[0]; + time_end = time[1]; + + if (time_begin > 0 && time_end > 0) { + values_query += " begin > ? AND end < ?"; + values_param = values_param.concat([time_begin, time_end]); + } else if (time_begin > 0) { + values_query += " begin > ?"; + values_param = values_param.concat([time_begin]); + } else if (time_end > 0) { + values_query += " end < ?"; + values_param = values_param.concat([time_end]); + } + } + + values_query += " GROUP BY id"; + + db.each(values_query, values_param, foreach_value_row, foreach_value_complete); + } + + result = {}; + + var sensors_query = "SELECT * FROM sensors"; + var sensors = []; + + if (query['meters']) { + var sensors = query['meters'].split(','); + sensors_query += " WHERE id IN ("; + sensors_query += sensors.map(function(e) { return "?" }).join(','); + sensors_query += ")" + } + + db.each(sensors_query, sensors, foreach_sensors_row, foreach_sensors_complete); +} + +function handle_blacklist(res, query) { + var blacklist_query = "UPDATE sensors SET status=0 WHERE id=?"; + + if (!query['sensor']) { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify([])); + return; + } + + db.run(blacklist_query, query['sensor'], function(err) { + if (err) throw err; + + var result_query = "SELECT * FROM sensors WHERE id=?"; + + db.get(result_query, query['sensor'], function(err, row) { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify(row)); + }); + }); +} + +function handle_unblacklist(res, query) { + var blacklist_query = "UPDATE sensors SET status=1 WHERE id=?"; + + if (!query['sensor']) { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify([])); + return; + } + + db.run(blacklist_query, query['sensor'], function(err) { + if (err) throw err; + + var result_query = "SELECT * FROM sensors WHERE id=?"; + + db.get(result_query, query['sensor'], function(err, row) { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify(row)); + }); + }); +} + +function handle_rename(res, query) { + var rename_query = "UPDATE sensors SET name=? WHERE id=?"; + + if (!query['sensor'] || !query['name']) { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify([])); + return; + } + + db.run(rename_query, query['name'], query['sensor'], function(err) { + if (err) throw err; + + var result_query = "SELECT * FROM sensors WHERE id=?"; + + db.get(result_query, query['sensor'], function(err, row) { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify(row)); + }); + }); +} + +function handle_relocate(res, query) { + var rename_query = "UPDATE sensors SET lat=?, lon=? WHERE id=?"; + + if (!query['sensor'] || !query['location']) { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify([])); + return; + } + + var loc = query['location'].split(','); + + if (!loc[0] || !loc[1]) { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify([])); + return; + } + + db.run(rename_query, loc[0], loc[1], query['sensor'], function(err) { + if (err) throw err; + + var result_query = "SELECT * FROM sensors WHERE id=?"; + + db.get(result_query, query['sensor'], function(err, row) { + res.writeHead(200, {'Content-Type': 'application/json'}); + res.end(JSON.stringify(row)); + }); + }); +} + +function request_handler(req, res) { + if (!req.connection.authorized) { + res.writeHead(403, {'Content-Type': 'text/plain'}); + res.end("invalid ssl client certificate"); + return; + } + + parsed = url.parse(req.url, true); + + methods = { + '/list': handle_list, + '/details': handle_details, + '/sum': handle_sum, + '/blacklist': handle_blacklist, + '/unblacklist': handle_unblacklist, + '/rename': handle_rename, + '/relocate': handle_relocate + } + + console.log(req.url); + + if (parsed['pathname'] in methods) { + methods[parsed['pathname']](res, parsed['query']) + } else { + res.writeHead(501, {'Content-Type': 'application/json'}); + res.end("") + } +}; + +/* +function poll_sensors() { + fs.readFile('sensors.json', 'utf8', function (err, data) { + data = JSON.parse(data); + + for (hosts in data) { + var options = { + host: hosts['host'], + port: hosts['port'], + path: '/details', + method: 'GET' + } + + var request = http.request(options); + + request.on('response', function (res) { + console.log("success"); + }); + + request.on('error', function(e) { + console.log(e.message); + }); + + request.end(); + } + }); +} + +setInterval(poll_sensors, 1000*3600); // once per hour +*/ + +/* +mdns.createAdvertisement('https', 8443).start(); +*/ + +/* +child = exec("avahi-publish-service collector _https._tcp 8443 \"ns=wattsapp;ip=2001:638:709:5::5\"", function(error, stdout, stderr) {}); +*/ + +var options = { + key: fs.readFileSync('/var/lib/wattsapp/collector.key'), + cert: fs.readFileSync('/var/lib/wattsapp/collector.crt'), + ca: fs.readdirSync('/var/lib/wattsapp/collector_clients').map(function(x) { + return fs.readFileSync('/var/lib/wattsapp/collector_clients/'+x); + }), + requestCert: true, +// rejectUnauthorized: true +}; + +var server = https.createServer(options); +server.addListener("request", request_handler); +server.listen(8443); diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..ec735d7 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +wattsapp-collector (0.1-1) oneiric; urgency=low + + * initial version + + -- Johannes Schauer Thu, 07 Jul 2011 12:33:58 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..a35a1ca --- /dev/null +++ b/debian/control @@ -0,0 +1,8 @@ +Source: wattsapp-collector +Maintainer: Johannes Schauer +Build-Depends: debhelper + +Package: wattsapp-collector +Architecture: all +Depends: openssl, node-sqlite3, sqlite3, nodejs, avahi-daemon +Description: wattsapp-collector diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..2d33f6a --- /dev/null +++ b/debian/rules @@ -0,0 +1,4 @@ +#!/usr/bin/make -f + +%: + dh $@ diff --git a/debian/wattsapp-collector.init b/debian/wattsapp-collector.init new file mode 100644 index 0000000..569ac9c --- /dev/null +++ b/debian/wattsapp-collector.init @@ -0,0 +1,60 @@ +#! /bin/sh + +### BEGIN INIT INFO +# Provides: wattsapp-collector +# Required-Start: $syslog +# Required-Stop: $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: wattsapp collector +### END INIT INFO + +set -e + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/bin/wattsapp-collector +NAME=wattsapp-collector +DESC="wattsapp collector" + +test -x $DAEMON || exit 0 + +. /lib/lsb/init-functions + +do_start() +{ + start-stop-daemon --start --quiet --oknodo --make-pidfile --pidfile /var/run/wattsapp-collector.pid --background --exec $DAEMON -- $DAEMON_OPTS +} + +do_stop() +{ + start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/wattsapp-collector.pid + rm -f /var/run/wattsapp-collector.pid +} + +case "$1" in + start) + log_daemon_msg "Starting $DESC" "$NAME" + do_start + log_end_msg $? + ;; + stop) + log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + log_end_msg $? + ;; + + restart|force-reload) + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + sleep 1 + do_start + log_end_msg $? + ;; + + *) + log_success_msg "Usage: $0 {start|stop|restart|force-reload}" + exit 1 + ;; +esac + +exit 0 diff --git a/debian/wattsapp-collector.postinst b/debian/wattsapp-collector.postinst new file mode 100644 index 0000000..c146c21 --- /dev/null +++ b/debian/wattsapp-collector.postinst @@ -0,0 +1,42 @@ +#!/bin/sh + +set -e + +case "$1" in + configure) + mkdir -p /var/lib/wattsapp + mkdir -p /var/lib/wattsapp/collector_clients + if [ ! -f /var/lib/wattsapp/collector.key ]; then + openssl genrsa -out /var/lib/wattsapp/collector.key 1024 + echo "created privaate key in /var/lib/wattsapp/collector.key" + openssl req -new -key /var/lib/wattsapp/collector.key -out /tmp/certrequest.csr + openssl x509 -req -in /tmp/certrequest.csr -signkey /var/lib/wattsapp/collector.key -out /var/lib/wattsapp/collector.crt + echo "created self-signed certificate in /var/lib/wattsapp/collector.crt" + echo "distribute it to the cloud" + rm /tmp/certrequest.csr + fi + if [ ! -f /var/lib/wattsapp/collector.sqlite ]; then + cat | sqlite3 /var/lib/wattsapp/collector.sqlite << END +PRAGMA foreign_keys=OFF; +BEGIN TRANSACTION; +CREATE TABLE sensors ( + id INTEGER, + name TEXT, + status INTEGER, + type TEXT, + unit TEXT, + lat REAL, + lon REAL +, uuid TEXT); +CREATE TABLE value ( + id INTEGER, + begin INTEGER, + end INTEGER, + value REAL +); +COMMIT; +END + fi +esac + +#DEBHELPER#