”2016-08-10”

This commit is contained in:
Martin Donnelly 2016-08-10 13:51:42 +01:00
parent fac5f7cd45
commit 7a0f3327a6
487 changed files with 98627 additions and 0 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -0,0 +1,32 @@
; http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
[*.txt]
insert_final_newline = false
trim_trailing_whitespace = false
[*.py]
indent_size = 4
[*.m]
indent_size = 4
[Makefile]
indent_style = tab
indent_size = 8
[*.{js,json}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false

180
mdot/mdot_mqtt/mdot_mqtt/.gitignore vendored Normal file
View File

@ -0,0 +1,180 @@
# Created by .ignore support plugin (hsz.mobi)
### Archives template
# It's better to unpack these files and commit the raw source because
# git has its own built in compression methods.
*.7z
*.jar
*.rar
*.zip
*.gz
*.bzip
*.bz2
*.xz
*.lzma
*.cab
#packing-only formats
*.iso
*.tar
#package management formats
*.dmg
*.xpi
*.gem
*.egg
*.deb
*.rpm
*.msi
*.msm
*.msp
### Windows template
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### OSX template
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Node template
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules
bower_components
### VisualStudioCode template
.settings
### Xcode template
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
*.moved-aside
*.xcuserstate
dist

View File

@ -0,0 +1,46 @@
{
"disallowKeywords": ["with"],
"disallowKeywordsOnNewLine": ["else"],
"disallowMixedSpacesAndTabs": true,
"disallowMultipleVarDecl": "exceptUndefined",
"disallowNewlineBeforeBlockStatements": true,
"disallowQuotedKeysInObjects": true,
"disallowSpaceAfterObjectKeys": true,
"disallowSpaceAfterPrefixUnaryOperators": true,
"disallowSpacesInFunction": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInsideParentheses": true,
"disallowTrailingWhitespace": true,
"maximumLineLength": 160,
"requireCamelCaseOrUpperCaseIdentifiers": false,
"requireCapitalizedComments": true,
"requireCapitalizedConstructors": true,
"requireCurlyBraces": true,
"requireSpaceAfterKeywords": [
"if",
"else",
"for",
"while",
"do",
"switch",
"case",
"return",
"try",
"catch",
"typeof"
],
"requireSpaceAfterLineComment": true,
"requireSpaceAfterBinaryOperators": true,
"requireSpaceBeforeBinaryOperators": true,
"requireSpaceBeforeBlockStatements": true,
"requireSpaceBeforeObjectValues": true,
"requireSpacesInFunction": {
"beforeOpeningCurlyBrace": true
},
"requireTrailingComma": false,
"requireEarlyReturn": false,
"validateIndentation": 2,
"validateLineBreaks": "LF",
"validateQuoteMarks": "'"
}

View File

@ -0,0 +1,37 @@
{
"predef": [
"Promise",
"$"
],
"globals": {
"$": false,
"MicroEvent": false
},
"node":true,
"browser": true,
"boss": true,
"curly": true,
"debug": false,
"devel": true,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": false,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": false,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": true,
"strict": false,
"white": false,
"eqnull": true,
"esnext": true,
"unused": true,
"supernew":true
}

View File

@ -0,0 +1,262 @@
/**
* Created by Martin on 08/02/2016.
*/
'use strict';
var express = require('express');
var path = require('path');
var http = require('http');
var ejs = require('ejs');
var morgan = require('morgan');
var cookieparser = require('cookie-parser');
var session = require('express-session');
var methodoverride = require('method-override');
var bodyparser = require('body-parser');
var errorhandler = require('errorhandler');
var mqttConnect = require('./lib/mqtt/mqttConnect');
var log4js = require('log4js');
var logger = log4js.getLogger();
var WebSocketServer = require('websocket').server;
var EventEmitter = require('events');
var busEmitter = new EventEmitter();
var apn = require('apn');
require('sugar-date');
var calendar = require('./lib/office/officeController.js');
var cal = new calendar.officeController(busEmitter);
var lighting_v1 = require('./routes/lighting_v1');
var heating_v1 = require('./routes/heating_v1');
var projector_v1 = require('./routes/projector_v1');
var isProduction = false;
function restartTimer() {
// Ibm / mqtt things go a bit weird if left on for too long so restart the server
// at 7:30 ever morning before anyone is using it.
var tom = Date.create('tomorrow at 7:30am');
var ms = tom.getTime() - Date.create('now').getTime();
console.log('Restarting in: ', ms);
setTimeout(function() {process.exit(1);}, ms);
}
mqttConnect.setEmitter(busEmitter);
mqttConnect.doConnection();
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
if (process.env.NODE_ENV === 'production') {
isProduction = true;
}
logger.debug('isProduction:', isProduction);
var app = express();
app.set('port', process.env.PORT || 4545);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(morgan('combined'));
app.use(cookieparser('your secret here'));
app.use(session({
secret: '1234567890QWERTY', resave: false, saveUninitialized: false
}));
/* 'default', 'short', 'tiny', 'dev' */
app.use(methodoverride());
app.use(bodyparser.urlencoded({extended: false}));
// Parse application/json
app.use(bodyparser.json());
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'X-Requested-With');
next();
});
// Run npm start --production to use dist
var staticDir = isProduction ? 'dist' : 'app';
app.use(express.static(path.join(__dirname, staticDir)));
app.use(errorhandler({dumpExceptions: true, showStack: true}));
lighting_v1.use(mqttConnect);
heating_v1.use(mqttConnect);
projector_v1.use(mqttConnect);
// Calendar handler
busEmitter.on('lightingOn', lighting_v1.doLightsOn);
busEmitter.on('lightingOff', lighting_v1.doLightsOff);
busEmitter.on('heatingOn', mqttConnect.heatingOn);
busEmitter.on('heatingOff', mqttConnect.heatingOff);
busEmitter.on('projectorOn', projector_v1.doProjectorOn);
busEmitter.on('projectorOff', projector_v1.doProjectorOff);
busEmitter.on('connectWS', mqttConnect.connectWS);
busEmitter.on('restartMQTTSocket', mqttConnect.restartMQTTSocket);
mqttConnect.setupPing();
cal.startController(busEmitter);
app.get('/stop', function(request, response) {
cal.stopController();
response.sendStatus(200);
});
app.get('/start', function(request, response) {
cal.startController();
response.sendStatus(200);
});
app.get('/api/calendar', function(req, res) {
var calJson = cal.returnCalendar();
logger.info(calJson);
res.json(calJson);
});
app.post('/api/calendar/extend', function(req, res) {
res.json({});
});
// Events and sockets
busEmitter.on('clientConnected', (socketSet) => {
logger.info('-=-=-=-=-');
logger.info(socketSet.getClientStatus());
heating_v1.setsocket(socketSet).subscribe();
lighting_v1.setsocket(socketSet).subscribe();
projector_v1.setsocket(socketSet).subscribe();
});
busEmitter.on('clientStatusUpdated', (v) => {
logger.info(v);
});
logger.info('Configuring WebSocket Listener...');
var server = http.createServer(function(request, response) {
logger.info((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
});
server.listen(3001, function() {
logger.info((new Date()) + ' Server is listening on port 3001');
});
var wsServer = new WebSocketServer({
httpServer: server, // You should not use autoAcceptConnections for production
// applications, as it defeats all standard cross-origin protection
// facilities built into the protocol and the browser. You should
// *always* verify the connection's origin and decide whether or not
// to accept it.
autoAcceptConnections: false
});
function originIsAllowed(origin) {
// Put logic here to detect whether the specified origin is allowed.
return true;
}
wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
// Make sure we only accept requests from an allowed origin
request.reject();
logger.info((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
return;
}
var connection = request.accept('stream', request.origin);
logger.info((new Date()) + ' Connection accepted.');
var sendSocketHandler = (obj) => {
try {
connection.sendUTF(JSON.stringify(obj));
}
catch (err) {
logger.error(err);
logger.warn('Offending object: ', obj);
}
};
busEmitter.on('sendSocket', sendSocketHandler);
connection.on('message', function(message) {
if (message.type === 'utf8') {
logger.info('Received Message: ' + message.utf8Data);
connection.sendUTF(message.utf8Data);
} else if (message.type === 'binary') {
logger.info('Received Binary Message of ' + message.binaryData.length + ' bytes');
connection.sendBytes(message.binaryData);
}
});
connection.on('close', function(reasonCode, description) {
logger.info((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
busEmitter.removeListener('sendSocket', sendSocketHandler);
});
});
mqttConnect.connectWS(function() {
logger.info('Ready to plug in sockets...');
});
app.post('/api/v1/lighting/off', lighting_v1.turnoff);
app.post('/api/v1/lighting/on', lighting_v1.turnon);
app.post('/api/v1/lighting/cmd', lighting_v1.command);
app.post('/api/v1/heating/off', heating_v1.turnoff);
app.post('/api/v1/heating/on', heating_v1.turnon);
app.post('/api/v1/projector/off', projector_v1.turnoff);
app.post('/api/v1/projector/on', projector_v1.turnon);
app.post('/api/v1/projector/cmd', projector_v1.command);
app.post('/api/v1/register/ios', function(req, res) {
var body = req.body, registrationId;
logger.debug(body);
if (body.hasOwnProperty('registrationId')) {
registrationId = body['registrationId'];
logger.debug(registrationId);
cal.registeriOSToken(registrationId);
}
});
app.post('/api/v1/extend', function(req, res) {
var body = req.body, data;
if (body.hasOwnProperty('extendBy') && body.hasOwnProperty('uid')) {
data = req.body;
cal.extendMeeting(data);
res.sendStatus(200);
}
});
app.listen(3000, function() {
logger.info('Express listening on 3000');
restartTimer();
});

View File

@ -0,0 +1,24 @@
{
"name": "sodashserver",
"description": "Smart Office Dashboard Server",
"main": "index.js",
"authors": [
"Martin Donnelly"
],
"license": "ISC",
"homepage": "",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"chroma-js": "^1.1.1",
"jquery": "^2.2.3",
"mui": "^0.5.3",
"sugarjs-date": "^1.5.1"
}
}

View File

@ -0,0 +1,117 @@
'use strict';
var gulp = require('gulp');
var autoprefixer = require('gulp-autoprefixer');
var cssnano = require('gulp-cssnano');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');
var jsmin = require('gulp-jsmin');
var rename = require('gulp-rename');
var concat = require('gulp-concat');
var notify = require('gulp-notify');
var cache = require('gulp-cache');
var livereload = require('gulp-livereload');
var htmlmin = require('gulp-htmlmin');
var inject = require('gulp-inject');
var del = require('del');
var htmlreplace = require('gulp-html-replace');
var googleWebFonts = require('gulp-google-webfonts');
var stripDebug = require('gulp-strip-debug');
var size = require('gulp-size');
var debug = require('gulp-debug');
var filePath = {
build_dir: './dist'
};
gulp.task('scripts', function() {
return gulp.src(['app/js/sowebsocket.js','app/js/colours.js','app/js/appv2.js'])
.pipe(jshint('.jshintrc'))
.pipe(jshint.reporter('default'))
.pipe(concat('app.js'))
// .pipe(stripDebug())
.pipe(jsmin())
.pipe(size({title: 'Scripts'}))
.pipe(gulp.dest('dist/js'));
});
gulp.task('vendor', function() {
return gulp.src(['bower_components/jquery/dist/jquery.min.js',
'bower_components/mui/packages/cdn/js/mui.min.js',
'bower_components/chroma-js/chroma.min.js',
'bower_components/sugar/release/sugar-full.min.js',
'app/lib/skycons.js',
'app/lib/microevent.js'])
.pipe(concat('vendor.js'))
.pipe(uglify({mangle: false}))
.pipe(size({title: 'Vendor'}))
.pipe(gulp.dest('dist/js'));
});
gulp.task('styles', function() {
return gulp.src(['app/css/mui.custom.css','app/css/material-icons.css','app/css/app.css'])
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
.pipe(cssnano())
.pipe(concat('app.css'))
.pipe(size({title: 'Styles'}))
.pipe(gulp.dest('dist/css'));
});
gulp.task('partials', function() {
// Gulp.src(['app/partials/**/*']).pipe(gulp.dest('dist/partials'));
// gulp.src(['app/libs/ejs_production.js']).pipe(gulp.dest('dist/libs'));
// gulp.src(['app/libs/microevent.js']).pipe(gulp.dest('dist/libs'));
gulp.src(['app/fav/**/*']).pipe(size({title: 'Partials'})).pipe(gulp.dest('dist/fav'));
gulp.src(['app/gfx/censis_logo_white.png']).pipe(gulp.dest('dist/gfx'));
});
gulp.task('migrate', function() {
return gulp.src(['dist/**/*'])
.pipe(debug({title: 'migrate:'}))
.pipe(size({title: 'Migrate'}))
.pipe(gulp.dest('/Users/martin/newdev/SODashApp/www'));
});
gulp.task('index', function() {
var sources = gulp.src(['js/apps.js', 'css/app.css'], {read: false});
return gulp.src(['app/index.html'])
.pipe(htmlreplace({
css: 'css/app.css',
js: 'js/app.js',
vendor: 'js/vendor.js',
fonts: 'fonts/fonts.css'
}))
.pipe(htmlmin({removeComments: true, collapseWhitespace: true, keepClosingSlash: true}))
.pipe(size({title: 'Index'}))
.pipe(gulp.dest('dist/'));
});
var options = { };
gulp.task('fonts', function() {
return gulp.src('./fonts.list')
.pipe(googleWebFonts(options))
.pipe(size({title: 'Fonts'}))
.pipe(gulp.dest('dist/fonts'))
;
});
gulp.task('clean', function() {
return del(['dist']);
});
gulp.task('default', ['clean'], function() {
gulp.start('styles', 'scripts', 'vendor', 'fonts', 'partials', 'index');
});
gulp.task('Cordova', ['clean'], function() {
gulp.start('styles', 'scripts', 'vendor', 'fonts', 'index');
});

View File

@ -0,0 +1,28 @@
var log4js = require('log4js');
var logger = log4js.getLogger();
var WebSocketServer = require('websocket').server;
var EventEmitter = require('events');
var busEmitter = new EventEmitter();
var mqttClient = require('./lib/mqtt/mqttClient');
//var mqttSocket = require('./lib/mqtt/mqttSocket');
//busEmitter.on('startWSConnection', startWSConnection);
var mqtt = new mqttClient.mqttClient(busEmitter);
/*
mqttConnect.setEmitter(busEmitter);
mqttConnect.doConnection();
mqttConnect.connectWS();
*/

View File

@ -0,0 +1,130 @@

var IoTFconnector = function (orgId, api_key, auth_token, $rootScope) {
//this.connected = '';
this.clientId = "a:" + orgId + ":" + Date.now();
console.log("clientId: " + this.clientId);
this.hostname = orgId + ".messaging.internetofthings.ibmcloud.com";
this.client = '';
this.initialize = function () {
client = new Messaging.Client(this.hostname, 8883, this.clientId);
client.onMessageArrived = function (msg) {
console.log("Message from :" + msg.destinationName);
};
var connectOptions = new Object();
connectOptions.keepAliveInterval = 3600;
connectOptions.useSSL = true;
connectOptions.userName = api_key;
connectOptions.password = auth_token;
connectOptions.onSuccess = function () {
IoTFconnector.prototype.clientStatus.connected = true;
// $rootScope.$broadcast("clientStatusUpdated", clientStatus);
console.log("MQTT connected to host: " + client.host + " port : " + client.port + " at " + Date.now());
}
connectOptions.onFailure = function (e) {
console.log("MQTT connection failed at " + Date.now() + "\nerror: " + e.errorCode + " : " + e.errorMessage);
}
console.log("about to connect to " + client.host);
client.connect(connectOptions);
//client = new Messaging.Client(this.hostname, 8883, this.clientId);
//// Initialize the Realtime Graph
////var rtGraph = new RealtimeGraph();
//client.onMessageArrived = function(msg) {
// //var topic = msg.destinationName;
// //var payload = JSON.parse(msg.payloadString);
// ////First message, instantiate the graph
// //if (firstMessage) {
// // $('#chart').empty();
// // firstMessage = false;
// // rtGraph.displayChart(null, payload);
// //} else {
// // rtGraph.graphData(payload);
// //}
// console.log("Message from :" + msg.destinationName);
//};
//client.onConnectionLost = function(e) {
// console.log("Connection Lost at " + Date.now() + " : " + e.errorCode + " : " + e.errorMessage);
// this.connect(connectOptions);
//}
//var connectOptions = new Object();
//connectOptions.keepAliveInterval = 3600;
//connectOptions.useSSL = true;
//connectOptions.userName = api_key;
//connectOptions.password = auth_token;
//connectOptions.onSuccess = function() {
// IoTFconnector.prototype.connected = true;
// console.log("MQTT connected to host: " + client.host + " port : " + client.port + " at " + Date.now());
//}
//connectOptions.onFailure = function(e) {
// console.log("MQTT connection failed at " + Date.now() + "\nerror: " + e.errorCode + " : " + e.errorMessage);
//}
//console.log("about to connect to " + client.host);
//client.connect(connectOptions);
}
this.initialize();
//var imageHTML = '<div class="iotdashboardtext">The selected device is not currently sending events to the Internet of Things Foundation</div><br><div class="iotdashboardtext">Select to view historical data or select a different device.</div> <img class="iotimagesMiddle" align="middle" alt="Chart" src="../../images/IOT_Icons_Thing02.svg">';
};
IoTFconnector.prototype.clientStatus = { connected: false, subscribeTopic: '' };;
IoTFconnector.prototype.disconnect = function disconnect() {
client.disconnect();
console.log("Connection closed");
}
IoTFconnector.prototype.connect = function connect() {
// this.initialize();
}
IoTFconnector.prototype.subscribe = function subscribe(deviceId) {
var subscribeOptions = {
qos: 0,
onSuccess: function () {
console.log("subscribed to " + IoTFconnector.prototype.clientStatus.subscribeTopic);
},
onFailure: function () {
console.log("Failed to subscribe to " + IoTFconnector.prototype.clientStatus.subscribeTopic);
console.log("As messages are not available, visualization is not possible");
}
};
if (IoTFconnector.prototype.clientStatus.subscribeTopic != "") {
console.log("Unsubscribing to " + IoTFconnector.prototype.clientStatus.subscribeTopic);
client.unsubscribe(IoTFconnector.prototype.clientStatus.subscribeTopic);
};
IoTFconnector.prototype.clientStatus.subscribeTopic = "iot-2/type/iotsample-ti-cc3200/id/" + deviceId + "/evt/+/fmt/json";
client.subscribe(IoTFconnector.prototype.clientStatus.subscribeTopic, subscribeOptions);
}

View File

@ -0,0 +1,157 @@
var mqtt = require('mqtt');
var request = require('request');
var util = require('util');
var logger = require('log4js').getLogger();
var EventEmitter = require('events');
//var nano = require('nano')('http://martind2000:1V3D4m526i@localhost:5984');
var nano = require('nano')('http://localhost:5984');
var busEmitter = new EventEmitter();
var db_name = 'mdot';
var dbCouch = nano.use(db_name);
var db = require('../server/db-connector').dbConnection;
var dbSave = require('../server/db-save')(db);
function dataBuilder(obj) {
'use strict';
var now = new Date();
var newObj = {device_type: 'mDot', evt_type: 'update', timestamp: {}, evt: {}};
newObj.device_id = obj.topic.split('/')[4];
newObj.evt = obj;
newObj.timestamp['$date'] = now.getTime();
return newObj;
}
function insertEntry(obj) {
var newObj = dataBuilder(obj);
logger.debug('Inserting into couch...');
logger.info(util.inspect(newObj));
dbCouch.insert(newObj, function(err, body, header) {
if (err) {
logger.error('Error inserting into couch');
return;
}
});
logger.debug('Insert done..');
}
var doInsertEntry = (obj) => {
// Logger.info('sendSocket: ' + JSON.stringify(obj));
var _rawObj = obj;
_rawObj.type = 1;
insertEntry(_rawObj);
dbSave.addNewEvent(obj)
.then(function(d) {
'use strict';
console.log('Finished - Raw',d);
})
.catch(function(e) {
'use strict';
console.error(e);
});
dbSave.addProcessedEvent(obj)
.then(function(d) {
'use strict';
console.log('Finished - Processed',d);
})
.catch(function(e) {
'use strict';
console.error(e);
});
};
var mqttClient = function() {
var subscribeTopic;
var orgId = 'qz0da4';
var userName = 'a-qz0da4-dfwwdkmkzr';
var address = '.messaging.internetofthings.ibmcloud.com';
var appKey = '9txJEf3Cjy7hkSOvkv';
var prefix = 'iot-2/type/mDot/id/';
var deviceId = ['CENSIS-LoRa-1','CENSIS-LoRa-2','CENSIS-LoRa-3','CENSIS-LoRa-4','HIE-mobile-1','HIE-demo','HIE-mobile-2','HIE-smart-campus-1','HIE-smart-campus-2','HIE-smart-campus-3','HIE-smart-campus-4','HIE-smart-campus-5','HIE-smart-campus-6','HIE-smart-campus-7','HIE-mDot-1'];
//Var subscribeTopic = prefix + deviceId + '/evt/+/fmt/json';
this.connected = false;
var options = {
keepalive: 3600,
clientId: 'a:' + orgId + ':' + Date.now(),
username: userName,
password: new Buffer(appKey)
};
this.client = mqtt.connect('mqtt://' + orgId + address, options);
this.client.on('connect', function() {
connected = true;
logger.info('Connected to ', address);
}.bind(this));
for (var t = 0;t < deviceId.length;t++) {
subscribeTopic = prefix + deviceId[t] + '/evt/+/fmt/json';
logger.info('Subscribing:', subscribeTopic);
this.client.subscribe(subscribeTopic);
}
this.client.on('message', function(topic, message) {
console.log('onMessage',topic);
var json = JSON.parse(message.toString());
json.topic = topic;
logger.debug(JSON.stringify(json));
busEmitter.emit('saveData', json);
}.bind(this));
this.isConnected = function() {
return this.connected;
};
this.client.on('connected', function() {
logger.debug('mqttConnect - doConnection - Connected');
});
this.client.on('disconnect', function(e) {
logger.error('mqttConnect - doConnection - disconnect');
logger.error(e);
});
this.client.on('error', function(e) {
logger.error('mqttConnect - doConnection - disconnect');
logger.error(e);
});
busEmitter.on('saveData', doInsertEntry);
};
module.exports.mqttClient = mqttClient;

View File

@ -0,0 +1,353 @@
/**
* Created by Martin on 08/02/2016.
*/
'use strict';
var mqtt = require('mqtt');
var Messaging = require('mqtt_over_websockets');
var log4js = require('log4js');
var logger = log4js.getLogger();
var mqttConfig = {
orgId: 'qz0da4',
userName: 'martind2000',
appKey: 'MPReoa43',
prefix: 'iot-2/type/Ti-CC3200/id/'
};
function randomString(length) {
return Math.round((Math.pow(36, length + 1) - Math.random() * Math.pow(36, length))).toString(36).slice(1);
}
/*
Projector: 'ProjectorISP15',
lighting: 'LightingISP15',
heating: 'HeatingISP15',
*/
var live = true;
logger.warn('!!! Live? ', live);
module.exports = {
restartTimer : 0,
pingTimer: 0,
statuses: {},
sockets: null,
wsClient: null,
watches: {},
client: null,
projector: live ? 'Projector' : 'ProjectorISP15',
lighting: live ? 'Lighting' : 'LightingISP15',
heating: live ? 'Heating' : 'HeatingISP15',
myID: 0,
connected: false,
options: {
// Keepalive: 3600,
keepalive: 60,
clientId: 'a:' + mqttConfig.orgId + ':' + Date.now(),
username: mqttConfig.userName,
password: new Buffer(mqttConfig.appKey)
},
lightList: {o: 'frontLight', f: 'frontLight',n: 'backLight', g: 'backLight'},
updateStatus: function(id, packet) {
this.statuses[id] = packet;
logger.info('Statuses:', this.statuses);
},
setEmitter: function(newEmitter) {
this.emitter = newEmitter;
},
doConnection: function(cb) {
console.log('Do connection');
var self = this;
if (this.client === null) {
this.client = mqtt.connect('mqtt://' + 'silvrtree.co.uk', this.options);
this.client.on('connect', function() {
logger.info('Connected to SilvrBroker');
self.connected = this.connected;
});
this.client.on('connected', function() {
logger.debug('mqttConnect - doConnection - Connected');
});
this.client.on('disconnect', function(e) {
logger.error('mqttConnect - doConnection - disconnect');
logger.error(e);
});
this.client.on('error', function(e) {
logger.error('mqttConnect - doConnection - disconnect');
logger.error(e);
});
}
return this;
},
isConnected: function() {
return this.connected;
},
sendCommand: function(deviceID, command) {
var payload = {
id: 'd',
text: command
};
},
projectorOn: function(callback) {
var packet;
console.log('projectorOn:');
var _callback = callback || {};
if (!this.client) {
return -1;
}
packet = {id: this.projector, status: true};
var destinationName = mqttConfig.prefix + this.projector + '/cmd/' + 'ON' + '/fmt/json';
this.client.publish(destinationName, 'ON', _callback);
this.emitter.emit('sendSocket',packet);
logger.debug('Storing status...');
this.updateStatus('projector', packet);
},
projectorOff: function(callback) {
var packet;
var _callback = callback || {};
if (!this.client) {
return -1;
}
packet = {id: this.projector, status: false};
var destinationName = mqttConfig.prefix + this.projector + '/cmd/' + 'OFF' + '/fmt/json';
this.client.publish(destinationName, 'OFF', _callback);
this.emitter.emit('sendSocket',packet);
console.log('Storing status...');
this.updateStatus('projector', packet);
},
projectorCmd: function(id, callback) {
var packet;
var _callback = callback || {};
if (!this.client) {
return -1;
}
var destinationName = mqttConfig.prefix + this.projector + '/cmd/' + id + '/fmt/json';
this.client.publish(destinationName, 'cmd', _callback);
},
heatingOn: function(callback) {
console.log('Turn heating on...');
var _callback = callback || null;
if (!this.client) {
return -1;
}
var destinationName = mqttConfig.prefix + this.heating + '/cmd/' + 'on' + '/fmt/json';
this.client.publish(destinationName, 'ON', _callback);
this.emitter.emit('sendSocket',{id: this.heating, status: true});
},
heatingOff: function(callback) {
var _callback = callback || {};
if (!this.client) {
return -1;
}
var destinationName = mqttConfig.prefix + this.heating + '/cmd/' + 'off' + '/fmt/json';
this.client.publish(destinationName, 'OFF', _callback);
this.emitter.emit('sendSocket',{id: this.heating, status: false});
},
lightingOn: function(id, callback) {
var packet;
console.log('lightingOn:' + id);
var _callback = callback || null;
if (!this.client) {
return -1;
}
var destinationName = mqttConfig.prefix + this.lighting + '/cmd/' + id + '/fmt/json';
this.client.publish(destinationName, 'ON', _callback);
packet = {id: this.lighting, device: id, status: true};
this.emitter.emit('sendSocket',packet);
logger.debug('Storing status...');
this.updateStatus(this.lightList[id], packet);
},
lightingOff: function(id, callback) {
var packet;
var _callback = callback || null;
if (!this.client) {
return -1;
}
var destinationName = mqttConfig.prefix + this.lighting + '/cmd/' + id + '/fmt/json';
this.client.publish(destinationName, 'OFF', _callback);
packet = {id: this.lighting, device: id, status: false};
this.emitter.emit('sendSocket', packet);
this.updateStatus(this.lightList[id], packet);
},
lightingCommand: function(id, callback) {
var packet;
var _callback = callback || null;
if (!this.client) {
return -1;
}
var destinationName = mqttConfig.prefix + this.lighting + '/cmd/' + id + '/fmt/json';
this.client.publish(destinationName, 'cmd', _callback);
},
setupEvents: function() {
this.emitter.on('lightingOn', this.lightingOn);
this.emitter.on('lightingOff', this.lightingOff);
this.emitter.on('heatingOn', this.heatingOn);
this.emitter.on('heatingOff', this.heatingOff);
this.emitter.on('projectorOn', this.projectorOn);
this.emitter.on('projectorOff', this.projectorOff);
},
setupPing: function() {
logger.warn('Starting ping timer...');
this.pingTimer = setTimeout(function() {this.ping();}.bind(this), 10000);
},
ping: function() {
//Logger.error('Ping!');
this.sendRefresh();
var now = new Date;
var mod = 10000 - (now.getTime() % 10000);
setTimeout(function() {this.ping();}.bind(this),mod + 10);
},
sendRefresh: function() {
// logger.debug('+ Send refresh', this.statuses);
for (var item in this.statuses) {
if (this.statuses.hasOwnProperty(item)) {
console.log(this.statuses[item]);
this.emitter.emit('sendSocket', this.statuses[item]);
}
}
// logger.debug('+ Send refresh');
},
preRestartConnection: function() {
logger.debug('Restart connection...');
//this.emitter.emit('restartMQTTSocket', this);
//logger.debug(this);
this.restartMQTTSocket();
},
startMQTTSocket:function() {
logger.warn('Restarting socket?');
this.connectWS();
},
restartMQTTSocket: function() {
this.wsClient.disconnect();
// setTimeout(this.startMQTTSocket.bind(this), 15000);
},
connectWS: function(connectCB) {
logger.debug('Going to connect WS');
var self = this;
var hostname = 'silvrtree.co.uk';
var clientId = 'a:' + 'qz0da4' + ':' + Date.now();
var api_key = 'martind2000';
var auth_token = 'MPReoa43';
this.wsClient =new Messaging.Client(hostname, 8883, clientId);
//var wsClient = new Messaging.Client(hostname, 8883, clientId);
var wsClient = this.wsClient;
var clientStatus = {connected: false, subscribed: false, deviceConnected: false};
var sensorData = {};
this.restartTimer = setTimeout(this.preRestartConnection.bind(this), 60000);
wsClient.onMessageArrived = function(msg) {
//logger.info("Message from :" + msg.destinationName);
clearTimeout(self.restartTimer);
self.restartTimer = setTimeout(self.preRestartConnection.bind(self), 60000);
clientStatus.deviceConnected = true;
sensorData = JSON.parse(msg.payloadString);
// Logger.debug(sensorData);
var temp = msg.destinationName.split('/');
if (self.watches.hasOwnProperty(temp[4])) {
// Logger.info('Emit: ' + JSON.stringify({id:self.watches[temp[4]],sensorData:sensorData}));
self.emitter.emit('sendSocket',{id: self.watches[temp[4]],sensorData: sensorData});
}
};
wsClient.onConnectionLost = function(e) {
logger.error('+ wsClient.onConnectionLost');
logger.error(e);
logger.warn('Going to force a restart of the Socket.. Hold on.');
setTimeout(self.startMQTTSocket.bind(self), 15000);
//self.emitter.emit('sendSocket',{id: 'deviceLost',data: e});
//var wsReconnectTimer = setTimeout(function() {logger.debug('TRYING TO RECONNECT TO MQTT');self.emitter.emit('connectWS');}.bind(this),30000);
logger.error('- wsClient.onConnectionLost');
};
var connectOptions = {};
connectOptions.keepAliveInterval = 3600;
connectOptions.useSSL = true;
connectOptions.userName = api_key;
connectOptions.password = auth_token;
connectOptions.onSuccess = function() {
clientStatus.connected = true;
logger.info('MQTT connected to host: ' + wsClient.host + ' port : ' + wsClient.port + ' at ' + Date.now());
self.emitter.emit('clientStatusUpdated', clientStatus);
self.emitter.emit('clientConnected', self.sockets);
};
connectOptions.onFailure = function(e) {
self.emitter.emit('sendSocket',{id: 'deviceNotConnecting'});
logger.error('wsClient onFailure', e);
logger.error('MQTT connection failed at ' + Date.now() + '\nerror: ' + e.errorCode + ' : ' + e.errorMessage);
//var wsReconnectTimer = setTimeout(function() {logger.debug('TRYING TO RECONNECT TO MQTT');self.emitter.emit('connectWS');}.bind(this),30000);
};
logger.debug('about to connect to ' + wsClient.host);
wsClient.connect(connectOptions);
this.sockets = {
getClientStatus: function() {
return clientStatus;
},
subscribe: function(deviceId, emitterId) {
logger.debug('trying to subscribe');
var subscribeTopic = 'iot-2/type/Ti-CC3200/id/' + deviceId + '/evt/+/fmt/json';
logger.debug(subscribeTopic);
var subscribeOptions = {
qos: 0,
onSuccess: function() {
logger.info('subscribed to ' + subscribeTopic);
clientStatus.subscribed = true;
// $rootScope.$broadcast("clientStatusUpdated", clientStatus);
self.emitter.emit('clientStatusUpdated', clientStatus);
self.watches[deviceId] = emitterId;
},
onFailure: function() {
logger.error('Failed to subscribe to ' + subscribeTopic);
logger.error('As messages are not available, visualization is not possible');
}
};
/*If (IoTFconnector.prototype.clientStatus.subscribeTopic != "") {
console.log("Unsubscribing to " + subscribeTopic);
wsClient.unsubscribe(IoTFconnector.prototype.clientStatus.subscribeTopic);
};*/
wsClient.subscribe(subscribeTopic, subscribeOptions);
}
};
return this.sockets;
}
};

View File

@ -0,0 +1,7 @@
/**
*
* User: Martin Donnelly
* Date: 2016-07-28
* Time: 14:48
*
*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
'uses strict';
/**
*
* User: Martin Donnelly
* Date: 2016-03-11
* Time: 10:22
*
*/
var pgp = require('pg-promise')();
var cn = {
host: 'localhost',
port: 5432,
database: 'mdot',
user: 'postgres',
password: ''
};
exports.dbConnection = pgp(cn);

View File

@ -0,0 +1,128 @@
'use strict';
var newId = require('uuid-pure').newId;
var atob = require('atob');
module.exports = function(db) {
var module = {};
module.deviceIds = ['CENSIS-LoRa-1','CENSIS-LoRa-2','CENSIS-LoRa-3','CENSIS-LoRa-4','HIE-mobile-1','HIE-demo','HIE-mobile-2','HIE-smart-campus-1','HIE-smart-campus-2','HIE-smart-campus-3','HIE-smart-campus-4','HIE-smart-campus-5','HIE-smart-campus-6','HIE-smart-campus-7','HIE-mDot-1'];
module.sqlInsertRawEvent = function(data) {
let _data = data;
return new Promise(function(resolve, reject) {
db.func('insert_raw',
[_data.timestamp, _data.event])
.then(()=> {
return resolve('ok');
})
.catch((err)=> {
return reject(err);
});
});
};
module.sqlInsertDecoded = function(data) {
let _data = data;
console.log('sqlInsertDecoded', data);
return new Promise(function(resolve, reject) {
db.func('insert_decoded',
[_data.deviceid, _data.timestamp, _data.lux, _data.co2, _data.temp, _data.humidity, _data.sound])
.then(()=> {
return resolve('ok');
})
.catch((err)=> {
return reject(err);
});
});
};
module.addNewEvent = function(data) {
console.log('addNewEvent');
var self = this;
return new Promise((resolve, reject) => {
let _data = {};
_data.timestamp = new Date();
_data.event = data;
self.sqlInsertRawEvent(_data)
.then((d)=> {
console.log('Postgres returns', d);
return resolve({reply: 'raw event inserted'});
})
.catch((err)=> {
return reject(err);
});
});
};
module.addProcessedEvent = function(data) {
console.log('addProcessedEvent');
var self = this;
return new Promise((resolve, reject) => {
let _data = self.rawBreaker(data);
self.sqlInsertDecoded(_data)
.then((d)=> {
console.log('Postgres returns', d);
return resolve({reply: 'Processed event inserted'});
})
.catch((err)=> {
return reject(err);
});
});
};
module.decoder = function(data) {
var _obj = {};
var _data = atob(data).split('');
var bytes = _data.map(i => i.charCodeAt());
_obj.light = parseInt('0x' + ('0' + bytes[0]).substr(-2) + ('0' + bytes[1]).substr(-2));
_obj.co2 = parseInt(_data[2] + _data[3] + _data[4] + _data[5] + _data[6], 10);
_obj.temp = (parseInt(_data[7] + _data[8] + _data[9] + _data[10] + _data[11], 10) - 1000) / 10;
_obj.humid = (parseInt(_data[12] + _data[13] + _data[14] + _data[15] + _data[16], 10) / 10);
_obj.noise = parseInt('0x' + ('0' + bytes[17]).substr(-2) + ('0' + bytes[18]).substr(-2));
_obj.binData = bytes;
return _obj;
};
module.rawBreaker = function(data) {
var self = this;
var workObj = {};
var device_name = data.topic.split('/')[4];
console.log('Device_name', device_name);
workObj.deviceid = self.deviceIds.indexOf(device_name);
if (data.hasOwnProperty('data')) {
var _data = self.decoder(data.data);
workObj.lux = _data.light;
workObj.co2 = _data.co2;
workObj.temp = _data.temp;
workObj.humidity = _data.humid;
workObj.sound = _data.noise;
workObj.timestamp = new Date();
return workObj;
} else {
console.error('Data does not have base64 data');
return null;
}
};
return module;
};

View File

@ -0,0 +1,20 @@
/**
*
* User: Martin Donnelly
* Date: 2016-06-28
* Time: 14:50
*
*/
var nano = require('nano')('http://localhost:5984');
var db_name = 'mqtt';
// Clean up the database we created previously
nano.db.destroy(db_name, function() {
// Create a new database
nano.db.create(db_name, function() {
// Specify the database we are going to use
var newDB = nano.use(db_name);
});
});

View File

@ -0,0 +1,75 @@
{
"name": "mdot_mqtt",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"atob": "^2.0.3",
"body-parser": "^1.15.1",
"cookie-parser": "*",
"ejs": "*",
"errorhandler": "*",
"express": "^4.13.4",
"express-session": "*",
"htmlparser": "^1.7.7",
"method-override": "*",
"minibus": "^3.1.0",
"morgan": "*",
"mqtt": "^1.10.0",
"mqtt_over_websockets": "0.0.1",
"node-localstorage": "^1.1.2",
"pg-promise": "^5.2.7",
"request": "^2.72.0",
"websocket": "^1.0.22"
},
"devDependencies": {
"after": "^0.8.1",
"apn": "^1.7.8",
"apns": "^0.1.0",
"basic-authentication": "^1.6.2",
"chai": "^3.5.0",
"cheerio": "^0.20.0",
"clone": "^1.0.2",
"del": "^2.2.0",
"elapsed": "0.0.7",
"events": "^1.1.1",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^3.1.0",
"gulp-cache": "^0.4.5",
"gulp-concat": "^2.6.0",
"gulp-cssmin": "^0.1.7",
"gulp-cssnano": "^2.1.2",
"gulp-debug": "^2.1.2",
"gulp-google-webfonts": "0.0.13",
"gulp-html-replace": "^1.5.5",
"gulp-htmlmin": "^2.0.0",
"gulp-inject": "^4.0.0",
"gulp-jshint": "^2.0.1",
"gulp-jsmin": "^0.1.5",
"gulp-livereload": "^3.8.1",
"gulp-notify": "^2.2.0",
"gulp-rename": "^1.2.2",
"gulp-size": "^2.1.0",
"gulp-strip-debug": "^1.1.0",
"gulp-uglify": "^2.0.0",
"jshint": "^2.9.2",
"jsonfile": "^2.3.1",
"log4js": "^0.6.36",
"mocha": "^3.0.2",
"mqtt-ws": "^0.2.0",
"nano": "^6.2.0",
"node-cron": "^1.1.1",
"require-dir": "^0.3.0",
"should": "^10.0.0",
"string": "^3.3.1",
"sugar": "^2.0.1",
"sugar-date": "^2.0.0",
"superagent": "^2.1.0",
"supertest": "^2.0.0"
},
"author": "Martin Donnelly <martind2000@gmail.com>",
"license": "ISC"
}

View File

@ -0,0 +1,20 @@
{
"apps": [
{
"name": "SODashServer",
"script": "app.js",
"cwd": "/home/azureuser/live",
"watch": true,
"instances": 1,
"exec_mode": "cluster",
"combine_logs": true,
"max_memory_restart": "150M",
"restart_delay": 5000,
"ignore_watch": [
"[\\/\\\\]\\./",
"node_modules",
"server/static"
]
}
]
}

View File

@ -0,0 +1,32 @@
; http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2
[*.txt]
insert_final_newline = false
trim_trailing_whitespace = false
[*.py]
indent_size = 4
[*.m]
indent_size = 4
[Makefile]
indent_style = tab
indent_size = 8
[*.{js,json}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false

180
mdot/mdot_server/mdot_server/.gitignore vendored Normal file
View File

@ -0,0 +1,180 @@
# Created by .ignore support plugin (hsz.mobi)
### Archives template
# It's better to unpack these files and commit the raw source because
# git has its own built in compression methods.
*.7z
*.jar
*.rar
*.zip
*.gz
*.bzip
*.bz2
*.xz
*.lzma
*.cab
#packing-only formats
*.iso
*.tar
#package management formats
*.dmg
*.xpi
*.gem
*.egg
*.deb
*.rpm
*.msi
*.msm
*.msp
### Windows template
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### OSX template
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Node template
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules
bower_components
### VisualStudioCode template
.settings
### Xcode template
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
*.moved-aside
*.xcuserstate
dist

View File

@ -0,0 +1,46 @@
{
"disallowKeywords": ["with"],
"disallowKeywordsOnNewLine": ["else"],
"disallowMixedSpacesAndTabs": true,
"disallowMultipleVarDecl": "exceptUndefined",
"disallowNewlineBeforeBlockStatements": true,
"disallowQuotedKeysInObjects": true,
"disallowSpaceAfterObjectKeys": true,
"disallowSpaceAfterPrefixUnaryOperators": true,
"disallowSpacesInFunction": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInsideParentheses": true,
"disallowTrailingWhitespace": true,
"maximumLineLength": 160,
"requireCamelCaseOrUpperCaseIdentifiers": false,
"requireCapitalizedComments": true,
"requireCapitalizedConstructors": true,
"requireCurlyBraces": true,
"requireSpaceAfterKeywords": [
"if",
"else",
"for",
"while",
"do",
"switch",
"case",
"return",
"try",
"catch",
"typeof"
],
"requireSpaceAfterLineComment": true,
"requireSpaceAfterBinaryOperators": true,
"requireSpaceBeforeBinaryOperators": true,
"requireSpaceBeforeBlockStatements": true,
"requireSpaceBeforeObjectValues": true,
"requireSpacesInFunction": {
"beforeOpeningCurlyBrace": true
},
"requireTrailingComma": false,
"requireEarlyReturn": false,
"validateIndentation": 2,
"validateLineBreaks": "LF",
"validateQuoteMarks": "'"
}

View File

@ -0,0 +1,37 @@
{
"predef": [
"Promise",
"$"
],
"globals": {
"$": false,
"MicroEvent": false
},
"node":true,
"browser": true,
"boss": true,
"curly": true,
"debug": false,
"devel": true,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": false,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": false,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": true,
"strict": false,
"white": false,
"eqnull": true,
"esnext": true,
"unused": true,
"supernew":true
}

View File

@ -0,0 +1,88 @@
/**
* Created by Martin on 08/02/2016.
*/
'use strict';
var express = require('express');
var path = require('path');
var http = require('http');
var ejs = require('ejs');
var morgan = require('morgan');
var cookieparser = require('cookie-parser');
var session = require('express-session');
var methodoverride = require('method-override');
var bodyparser = require('body-parser');
var errorhandler = require('errorhandler');
var log4js = require('log4js');
var logger = log4js.getLogger();
var WebSocketServer = require('websocket').server;
var EventEmitter = require('events');
var busEmitter = new EventEmitter();
var apn = require('apn');
require('sugar-date');
var isProduction = false;
var mdotApi = require('./lib/mdot/api.js');
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
if (process.env.NODE_ENV === 'production') {
isProduction = true;
}
logger.warn('isProduction:', isProduction);
var app = express();
app.set('port', process.env.PORT || 4545);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(morgan('combined'));
app.use(cookieparser('your secret here'));
app.use(session({
secret: '1234567890QWERTY', resave: false, saveUninitialized: false
}));
/* 'default', 'short', 'tiny', 'dev' */
app.use(methodoverride());
app.use(bodyparser.urlencoded({extended: false}));
// Parse application/json
app.use(bodyparser.json());
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'X-Requested-With');
next();
});
// Run npm start --production to use dist
var staticDir = isProduction ? 'dist' : 'app';
app.use(express.static(path.join(__dirname, staticDir)));
app.use(errorhandler({dumpExceptions: true, showStack: true}));
// Events and sockets
function originIsAllowed(origin) {
// Put logic here to detect whether the specified origin is allowed.
return true;
}
// glue routes
mdotApi(app);
//app.get('/api/mdot/:id', mDot.getData);
app.listen(3010, function() {
logger.info('Express listening on 3010');
});

View File

@ -0,0 +1,276 @@
body {
font-family: Ubuntu, "Helvetica Neue", Helvetica, arial, sans-serif;
background-color: #004c6d;
}
html, body {
height: 100%;
overflow: hidden;
}
#weatherIcon {
margin-left:25%;
margin-right:25%;
height:70px;
width:70px;
}
#lightR, #projR { color: red !important; }
#lightG, #projG { color: green !important; }
#lightB, #projB { color: blue !important; }
#lightW, #projW { background-color: #aabbcc; }
.lightBG, .heatingBG, .projectorBG {
float: right;
}
/*.lightBG {
background-color: rgba(255, 255, 0, 0.3);
}
.heatingBG {
background-color: rgba(255, 0, 255, 0.3);
}
.projectorBG {
background-color: rgba(0, 255, 255, 0.3);
}*/
.mui-panel {
background-color: #015579;
}
.h105 {
height: 100px;
}
.mdHeading {
overflow: hidden;
}
.mui--text-title {
color: #ffffff;
}
.item_content {
height: 100px;
/* border: 1px solid grey;*/
min-height: 100px;
overflow: hidden;
}
.item_content a.title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #ffffff;
}
.item_content div.body, .item_content div.site, .item_content div.tags {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #313131;
}
.time, .date, .temp {
font-family: 'Ubuntu Condensed', sans-serif;
font-size: 80px;
color: #bad649;
}
.time span.hour:after {
content: ":";
}
.date {
font-size: 35px;
line-height: 1;
}
.temp::after {
content: "°c";
}
.item_content div.tags {
color: blue;
}
.noConnection {
color: rgb(244, 150, 26);
}
#caltext {
color: #fff;
}
/* Smartphones (portrait and landscape) ----------- */
@media only screen and (min-device-width: 320px) and (max-device-width: 480px) {
/* Styles */
.time, .date, .temp {
font-family: 'Ubuntu Condensed', sans-serif;
font-size: 33px;
/*color: #ff0000;*/
}
.time {
font-size: 50px;
line-height: 1;
}
.time span.hour:after {
content: "\a";
white-space: pre;
}
.temp {
font-size: 70px;
}
.temp::after {
content: "°";
}
.wd-we {
font-size: 75%;
}
.mo {
font-size: 85%;
}
.mo.mo-1, .mo.mo-10 {
font-size: 70%;
}
.mo.mo-2 {
font-size: 65%;
}
.mo.mo-8 {
font-size: 80%;
}
.mo.mo-9 {
font-size: 55%;
}
.mo.mo-11, .mo.mo-12 {
font-size: 60%;
}
}
/* Smartphones (landscape) ----------- */
@media only screen and (min-width: 321px) {
/* Styles */
}
/* Smartphones (portrait) ----------- */
@media only screen and (max-width: 320px) {
/* Styles */
}
.spinner {
margin: 25px auto 0;
width: 70px;
text-align: center;
}
.spinner > div {
width: 18px;
height: 18px;
background-color: rgb(244, 150, 26);
border-radius: 100%;
display: inline-block;
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
}
.spinner .bounce1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
.spinner .bounce2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
@-webkit-keyframes sk-bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0) }
40% { -webkit-transform: scale(1.0) }
}
@keyframes sk-bouncedelay {
0%, 80%, 100% {
-webkit-transform: scale(0);
transform: scale(0);
}
40% {
-webkit-transform: scale(1.0);
transform: scale(1.0);
}
}
.material-icons {
color: #e5f7fd;
}
.material-icons.md-18 { font-size: 18px; }
.material-icons.md-24 { font-size: 24px; }
.material-icons.md-36 { font-size: 36px; }
.material-icons.md-48 { font-size: 48px; }
.material-icons.md-100 { font-size: 100px; }
/* Rules for using icons as black on a light background. */
.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); }
.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
/* Rules for using icons as white on a dark background. */
.material-icons.md-light { color: rgba(255, 255, 255, 1); }
.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
.material-icons.md-bulb {
content: "&#xE90F;"
}
/*
fan : toys
<i class="material-icons">&#xE332;</i>
bulb : lightbulb_outline
<i class="material-icons">&#xE90F;</i>
calendar: event_note
<i class="material-icons">&#xE616;</i>
projector: cast
<i class="material-icons">&#xE307;</i>
*/
.md-display {
opacity: 1;
transition: opacity 0.3s, visibility 0.3s;
}
.lostConnection {
opacity: 0.5;
transition: opacity 0.3s, visibility 0.3s;
}

View File

@ -0,0 +1,15 @@
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,136 @@
/*!
* Notification.js
*
* A well designed, highly customizable and lightweigth notification library.
*
* @author Dominique Müller <dominique.m.mueller@gmail.com>
* @copyright Dominique Müller 2015
* @license MIT <http://opensource.org/licenses/MIT>
* @link Github <https://github.com/dominique-mueller/notification-js>
* @version 1.0.0
*/
/* Notification container */
.notification {
position: fixed;
z-index: 9999;
overflow: hidden;
box-shadow: 0 1px 2px rgba(0,0,0,0);
-moz-box-sizing: border-box;
box-sizing: border-box;
opacity: 0;
}
.notification.is-visible {
box-shadow: 0 1px 2px rgba(0,0,0,.15);
opacity: 1;
}
.notification-background {
position: absolute;
top: 50%;
z-index: -1;
width: 200%;
padding-bottom: 200%;
border-radius: 50%;
-webkit-transform: translateY(-50%) scale(0);
transform: translateY(-50%) scale(0);
}
.notification.is-visible > .notification-background {
-webkit-transform: translateY(-50%) scale(1);
transform: translateY(-50%) scale(1);
}
/* Notification symbol */
.notification-symbol {
position: absolute;
top: 50%;
opacity: 0;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.notification.is-visible > .notification-symbol {
opacity: 1;
}
/* Notification default success symbol */
.notification-symbol-success > polyline {
transition: stroke-dashoffset 0s linear 2s;
stroke-dashoffset: 23;
}
.notification.is-visible > .notification-symbol-success > polyline {
transition: stroke-dashoffset .8s ease;
stroke-dashoffset: 0;
}
/* Notification default error symbol */
.notification-symbol-error > line {
transition: stroke-dashoffset 0s linear 2s;
stroke-dashoffset: 17;
}
.notification.is-visible > .notification-symbol-error > line:nth-child(1) {
transition: stroke-dashoffset .5s ease;
stroke-dashoffset: 0;
}
.notification.is-visible > .notification-symbol-error > line:nth-child(2) {
transition: stroke-dashoffset .5s ease .4s;
stroke-dashoffset: 0;
}
/* Notification message */
.notification-message {
margin-top: 0;
margin-bottom: 0;
opacity: 0;
-webkit-transform: translateX(-22px);
transform: translateX(-22px);
}
.notification.is-visible > .notification-message {
opacity: 1;
-webkit-transform: translateX(0);
transform: translateX(0);
}
/* Notification button */
.notification-btn {
position: absolute;
top: 50%;
right: 10px;
cursor: pointer;
background-color: transparent;
border: none;
opacity: 0;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
}
.notification-btn:hover,
.notification-btn:focus,
.notification-btn:active {
padding: inherit;
}
.notification-btn::-moz-focus-inner {
border: 0;
}
.notification.is-visible > .notification-btn {
opacity: .5;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="/fav/mstile-70x70.png"/>
<square150x150logo src="/fav/mstile-150x150.png"/>
<square310x310logo src="/fav/mstile-310x310.png"/>
<wide310x150logo src="/fav/mstile-310x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,43 @@
{
"name": "Censis Smart Office ",
"icons": [
{
"src": "\/fav\/android-chrome-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": 0.75
},
{
"src": "\/fav\/android-chrome-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": 1
},
{
"src": "\/fav\/android-chrome-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": 1.5
},
{
"src": "\/fav\/android-chrome-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": 2
},
{
"src": "\/fav\/android-chrome-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": 3
},
{
"src": "\/fav\/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": 4
}
],
"display": "standalone",
"orientation": "portrait"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,46 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="991.000000pt" height="991.000000pt" viewBox="0 0 991.000000 991.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,991.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M7465 9503 c-480 -60 -867 -422 -967 -903 -28 -132 -21 -345 16 -517
40 -184 56 -315 56 -460 0 -146 -15 -244 -54 -347 -36 -96 -38 -98 -89 -80
-71 25 -267 73 -372 90 -219 35 -551 33 -774 -5 -973 -169 -1752 -917 -1945
-1871 -14 -67 -27 -124 -30 -127 -9 -9 -162 19 -235 43 -168 56 -304 137 -577
345 -209 157 -328 222 -489 266 -93 25 -114 27 -290 27 -207 0 -253 -8 -420
-73 -270 -104 -514 -347 -624 -623 -113 -281 -105 -620 20 -887 113 -241 291
-426 524 -541 311 -154 673 -157 985 -9 107 51 172 95 328 226 311 258 558
392 752 405 l44 3 17 -70 c115 -492 361 -914 725 -1247 354 -324 795 -530
1294 -604 80 -12 177 -17 320 -17 222 0 346 14 545 58 254 57 407 0 493 -183
25 -52 27 -68 27 -177 -1 -119 -1 -121 -49 -245 -66 -173 -86 -263 -93 -410
-21 -494 279 -940 743 -1106 352 -126 754 -67 1054 156 84 62 205 187 259 269
62 91 126 231 157 341 38 140 45 372 14 520 -96 469 -473 820 -950 886 -179
25 -223 35 -297 69 -85 39 -159 105 -199 177 -27 49 -29 61 -29 158 0 95 3
110 27 156 15 28 70 102 123 165 239 281 410 611 500 963 57 223 70 332 70
596 0 264 -13 373 -70 595 -115 448 -379 885 -712 1179 -57 50 -103 95 -103
100 0 19 92 136 150 189 119 110 264 194 490 282 80 31 176 69 215 86 333 142
568 418 656 770 29 120 36 325 15 448 -84 482 -453 849 -931 926 -82 14 -245
18 -320 8z m-879 -2369 c179 -72 362 -175 526 -296 l67 -49 -23 -52 c-78 -176
-73 -421 13 -642 17 -45 63 -137 103 -205 210 -367 298 -727 268 -1103 -53
-664 -433 -1233 -1019 -1527 -295 -148 -564 -207 -891 -197 -233 7 -398 39
-600 114 -449 168 -823 510 -1029 943 -42 89 -123 311 -116 318 2 2 44 -11 95
-28 199 -66 403 -188 797 -477 211 -155 338 -226 497 -278 766 -251 1582 242
1721 1041 20 113 19 336 0 450 -95 545 -532 983 -1079 1081 -125 22 -354 22
-470 -1 -279 -54 -482 -162 -716 -377 -330 -304 -539 -447 -753 -514 -117 -37
-118 -37 -101 33 82 320 239 603 467 839 161 167 310 274 522 379 232 113 430
166 690 184 357 24 460 45 614 125 111 57 194 125 255 209 26 36 54 66 60 66
7 0 52 -16 102 -36z m-701 -1448 c90 -24 232 -98 307 -160 122 -101 217 -251
259 -411 28 -105 30 -279 4 -380 -91 -359 -405 -608 -770 -608 -123 0 -231 25
-350 83 -165 80 -284 199 -364 363 -54 109 -72 180 -78 308 -11 224 60 417
215 581 109 116 254 198 407 232 87 19 283 15 370 -8z m-2044 -463 c-10 -56
-15 -144 -14 -313 0 -211 3 -246 25 -348 l24 -114 -30 6 c-106 20 -209 26
-353 20 l-162 -6 -6 24 c-31 139 -44 479 -25 664 6 65 13 120 13 120 1 1 114
5 252 9 137 3 252 8 254 11 2 2 12 4 21 4 15 0 15 -6 1 -77z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1015 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show More