diff --git a/.jshintrc b/.jshintrc index d7fb754..375085b 100644 --- a/.jshintrc +++ b/.jshintrc @@ -3,6 +3,10 @@ "Promise", "$" ], + "globals": { + "$": false, + "MicroEvent": false + }, "node":true, "browser": true, "boss": true, diff --git a/app.js b/app.js index 5539ecb..841c69b 100644 --- a/app.js +++ b/app.js @@ -27,11 +27,21 @@ 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; 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); @@ -55,8 +65,11 @@ app.use(function(req, res, next) { res.header('Access-Control-Allow-Headers', 'X-Requested-With'); next(); }); -// App.use(app.router); -app.use(express.static(path.join(__dirname, 'app'))); + +// 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); @@ -74,7 +87,7 @@ busEmitter.on('projectorOn', mqttConnect.projectorOn); busEmitter.on('projectorOff', mqttConnect.projectorOff); - +mqttConnect.setupPing(); cal.startController(busEmitter); app.get('/stop', function(request, response) { @@ -153,8 +166,14 @@ wsServer.on('request', function(request) { console.log((new Date()) + ' Connection accepted.'); var sendSocketHandler = (obj) => { - // Logger.info('sendSocket: ' , JSON.stringify(obj)); - connection.sendUTF(JSON.stringify(obj)); + //logger.info('sendSocket: ' , JSON.stringify(obj)); + try { + connection.sendUTF(JSON.stringify(obj)); + } + catch (err) { + logger.error(err); + } + }; busEmitter.on('sendSocket', sendSocketHandler); diff --git a/app/index.html b/app/index.html index fedd386..73e60c7 100644 --- a/app/index.html +++ b/app/index.html @@ -206,6 +206,10 @@ - + + + + + diff --git a/app/js/app.js b/app/js/app.js index c69c259..111a60e 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -605,6 +605,7 @@ var show_weather = function(position) { +/* (function() { path = 'http://localhost:3000/'; @@ -619,6 +620,7 @@ var show_weather = function(position) { get_weather(); var soWebSocket = new SOWEBSOCKET(); })(); +*/ document.addEventListener('deviceready', onDeviceReady, false); diff --git a/app/js/appv2.js b/app/js/appv2.js new file mode 100644 index 0000000..506c53f --- /dev/null +++ b/app/js/appv2.js @@ -0,0 +1,347 @@ +/* + SO App v2 + */ + +var skycons = new Skycons({color: '#e5f7fd'}); + +var SOController = (function() { + 'use strict'; + + var path; + var wsUrl; + var local = true; + var bus = {}; + var prevDate; + var prevTime; + var weatherTimer = 0; + var weatherStore = null; + var lastWeatherRequest = 0; + var soWebSocket; + + var lightsList = { + off: ['frontLightOff', 'backLightOff'], on: ['frontLightOn', 'backLightOn'] + }; + + if (local) { + path = 'http://localhost:3000/'; + wsUrl = 'ws://localhost:3001'; + + } else { + wsUrl = 'ws://ec2-52-50-147-81.eu-west-1.compute.amazonaws.com:8080'; + path = 'http://ec2-52-50-147-81.eu-west-1.compute.amazonaws.com/'; + } + + MicroEvent.mixin(bus); + + var iOS = ['iPad', 'iPhone', 'iPod'].indexOf(navigator.platform) >= 0; + if (iOS) { + $('#iosTaskbar').show(); + path = 'http://localhost:3000/'; + } + + var module = {lights: lightsList}; + var storage = {}; + + storage.supportsLocalStorage = function() { + try { + return !localStorage ? false : true; + + } + catch (e) { + return false; + } + }; + + storage._localStorage = storage.supportsLocalStorage(); + if (storage._localStorage) { + storage.LocalStorage = { + save: function(i, v) { + localStorage[i] = v; + }, load: function(i) { + return localStorage[i]; + } + }; + } else { + storage.LocalStorage = { + save: function(i, v) { + document.cookie = (i) + '=' + encodeURIComponent(v); + }, load: function(i) { + var s; + var p; + s = '; ' + document.cookie + ';'; + p = s.indexOf('; ' + i + '='); + if (p < 0) { + return ''; + } + p = p + i.length + 3; + var p2 = s.indexOf(';', p + 1); + return decodeURIComponent(s.substring(p, p2)); + } + }; + } + + function updateCalendar(d) { + var $calendar = $('#calendar'); + + if (d.current !== null && d.current.hasOwnProperty('dtstart')) { + var calString = [ + '
', d.current.summary, + '
' + ].join(''); + $('#caltext').html(calString); + $calendar.slideDown(); + } else { + $calendar.slideUp(); + } + } + + function updateDateTime() { + var now = Date.create(new Date()); + + var $date = $('#date'); + var $time = $('#time'); + var curTime = now.format('{24hr}{mm}'); + var curDate = now.format('{yyyy}-{MM}-{dd}'); + if (prevTime !== curTime) { + $time.html(curTime); + // UpdateCalendar(); + prevTime = curTime; + } + + if (prevDate !== curDate) { + $date.html(now.format( + '{Weekday}
{Month} {dd}
{yyyy}')); + prevDate = curDate; + } + } + + function turnOnLights(id) { + console.log(id); + $.post(path + 'api/v1/lighting/on', {light: id}, function() {}); + } + + function turnOffLights(id) { + $.post(path + 'api/v1/lighting/off', {light: id}, function() {}); + } + + function turnOnHeating() { + $.post(path + 'api/v1/heating/on', {}, function() {}); + } + + function turnOffHeating() { + $.post(path + 'api/v1/heating/off', {}, function() {}); + } + + function turnOnProjector() { + $.post(path + 'api/v1/projector/on', {}, function() {}); + } + + function turnOffProjector() { + $.post(path + 'api/v1/projector/off', {}, function() {}); + } + + function attachClicks() { + $('#projectorOn').on('click', function() { + turnOnProjector(); + }); + + $('#projectorOff').on('click', function() { + turnOffProjector(); + }); + + $('#heatingOn').on('click', function() { + turnOnHeating(); + }); + + $('#heatingOff').on('click', function() { + turnOffHeating(); + }); + + $('#frontLightOn').on('click', function() { + // 1 for board lights + turnOnLights('o'); + }); + + $('#middleLightOn').on('click', function() { + turnOnLights(2); + }); + + $('#backLightOn').on('click', function() { + // 3 for board lights + turnOnLights('n'); + }); + + $('#frontLightOff').on('click', function() { + // A for board lights + turnOffLights('f'); + }); + + $('#middleLightOff').on('click', function() { + turnOffLights('b'); + }); + + $('#backLightOff').on('click', function() { + turnOffLights('g'); + }); + + } + + function clock() { + updateDateTime(); + var now = new Date; + var mod = 60000 - (now.getTime() % 60000); + + setTimeout(function() {clock();}, mod + 10); + } + + var get_weather = function() { + navigator.geolocation.getCurrentPosition(show_weather, + function(e) {alert(e.code + ' / ' + e.message);}); + }; + + bus.bind('displayWeather', function(data) { + console.log(data.currently); + $('#weatherText').html(parseInt(data.currently.temperature) + '°c '); + skycons.add('icon1', data.currently.icon); + }); + + var weatherClock = function() { + get_weather(); + var now = new Date; + var mod = 1800000 - (now.getTime() % 1800000); + + weatherTimer = setTimeout(function() {weatherClock();}, mod + 10); + }; + + var show_weather = function(position) { + var latitude = position.coords.latitude; + var longitude = position.coords.longitude; + // Show cached first before doing a long request. + // forecast.io is a bit slow. + var doRefresh = false; + if (weatherStore === null) { + var w = storage.LocalStorage.load('weather'); + if (typeof w !== 'undefined') { + w = JSON.parse(w); + weatherStore = w; + bus.trigger('displayWeather', w); + } else { + doRefresh = true; + } + } else { + doRefresh = true; + } + + // Let's show a map or do something interesting! + if (doRefresh) { + var now = new Date(); + if (now.getTime() - lastWeatherRequest < 10000) { + // Sometimes when the app loses focus, and then returns the weather requests queue up + // this ensures that we don't do multiple requests within a ten second window + return -1; + } + + lastWeatherRequest = now.getTime(); + + $.ajax({ + type: 'GET', + url: 'https://api.forecast.io/forecast/0657dc0d81c037cbc89ca88e383b6bbf/' + latitude.toString() + ',' + longitude.toString() + '?units=uk2', + data: '', + dataType: 'jsonp', + timeout: 10000, + context: $('body'), + contentType: ('application/json'), + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'PUT, GET, POST, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type' + }, + success: function(data) { + storage.LocalStorage.save('weather', JSON.stringify(data)); + weatherStore = data; + console.log('fresh..'); + bus.trigger('displayWeather', data); + }, + error: function(xhr, type) { + console.error('ajax error'); + console.error(xhr); + console.error(type); + } + }); + } + }; + + var browserNotify = function(d) { + console.log('BrowserNotify:', d); + if (Notification.permission !== 'granted') + Notification.requestPermission(); + else { + var notification = new Notification('SmartOffice Console', { + icon: '/fav/favicon-96x96.png', + body: d.msg + }); + + notification.onclick = function() { + //window.open("http://stackoverflow.com/a/13328397/1269037"); + console.log('click'); + }; + + } + + }; + + var notifyConfirm = function(r) { + console.log(r); + }; + + var cordovaNotify = function(d) { + navigator.notification.confirm( + d.msg, // message + notifyConfirm, // callback to invoke with index of button pressed + 'Smartoffice Console', // title + ['Extend','Ignore'] // buttonLabels + ); + }; + + // + + module.init = function() { + attachClicks(); + clock(); + weatherClock(); + soWebSocket = new SOWEBSOCKET(this); + }; + + module.updateCalendar = function(data) { + updateCalendar(data); + }; + + module.webSocketURL = function() { + return wsUrl; + }; + + module.notify = function(d) { + if (window.cordova && !(window.cordova instanceof HTMLElement)) { + document.addEventListener('deviceready', onDeviceReady, false); + } else { + browserNotify(d); + } + + }; + + + return module; +})(); + +function onDeviceReady() { + SOController.init(); +} + +;(function() { + if (window.cordova && !(window.cordova instanceof HTMLElement)) { + document.addEventListener('deviceready', onDeviceReady, false); + } else { + onDeviceReady(); + Notification.requestPermission(); + } +})(); diff --git a/app/js/colours.js b/app/js/colours.js new file mode 100644 index 0000000..388ed80 --- /dev/null +++ b/app/js/colours.js @@ -0,0 +1,200 @@ +var tempColours = [ + { + t: 0, + r: 80, + g: 181, + b: 221 + }, + { + t: 1, + r: 80, + g: 181, + b: 221 + }, + { + t: 2, + r: 80, + g: 181, + b: 221 + }, + { + t: 3, + r: 80, + g: 181, + b: 221 + }, + { + t: 4, + r: 80, + g: 181, + b: 221 + }, + { + t: 5, + r: 80, + g: 181, + b: 221 + }, + { + t: 6, + r: 78, + g: 178, + b: 206 + }, + { + t: 7, + r: 76, + g: 176, + b: 190 + }, + { + t: 8, + r: 73, + g: 173, + b: 175 + }, + { + t: 9, + r: 72, + g: 171, + b: 159 + }, + { + t: 10, + r: 70, + g: 168, + b: 142 + }, + { + t: 11, + r: 68, + g: 166, + b: 125 + }, + { + t: 12, + r: 66, + g: 164, + b: 108 + }, + { + t: 13, + r: 102, + g: 173, + b: 94 + }, + { + t: 14, + r: 135, + g: 190, + b: 64 + }, + { + t: 15, + r: 179, + g: 204, + b: 26 + }, + { + t: 16, + r: 214, + g: 213, + b: 28 + }, + { + t: 17, + r: 249, + g: 202, + b: 3 + }, + { + t: 18, + r: 246, + g: 181, + b: 3 + }, + { + t: 19, + r: 244, + g: 150, + b: 26 + }, + { + t: 20, + r: 236, + g: 110, + b: 5 + }, + { + t: 21, + r: 234, + g: 90, + b: 36 + }, + { + t: 22, + r: 228, + g: 87, + b: 43 + }, + { + t: 23, + r: 225, + g: 74, + b: 41 + }, + { + t: 24, + r: 224, + g: 65, + b: 39 + }, + { + t: 25, + r: 217, + g: 55, + b: 43 + }, + { + t: 26, + r: 214, + g: 49, + b: 41 + }, + { + t: 27, + r: 209, + g: 43, + b: 43 + }, + { + t: 28, + r: 205, + g: 40, + b: 47 + }, + { + t: 29, + r: 200, + g: 36, + b: 50 + }, + { + t: 30, + r: 195, + g: 35, + b: 52 + }, + { + t: 31, + r: 190, + g: 33, + b: 56 + }, + { + t: 32, + r: 185, + g: 32, + b: 59 + } +]; diff --git a/app/js/sowebsocket.js b/app/js/sowebsocket.js new file mode 100644 index 0000000..5f22539 --- /dev/null +++ b/app/js/sowebsocket.js @@ -0,0 +1,231 @@ +/** + * + * User: Martin Donnelly + * Date: 2016-04-25 + * Time: 12:03 + * + */ +'use strict'; +var SOWEBSOCKET = function(newController) { + var controller = newController; + this.socket = null; + this.retry = 0; + this.timer = 0; + this.previousTemp = 0; + + function rgb(cv) { + return ['rgb(', cv.r, ',', cv.g, ',', cv.b, ')'].join(''); + } + + this.startWebSocket = function() { + + console.log('Starting socket?'); + + var url = controller.webSocketURL(); + + var wsCtor = window['MozWebSocket'] ? MozWebSocket : WebSocket; + this.socket = new wsCtor(url, 'stream'); + + this.socket.onopen = this.handleWebsocketOnOpen.bind(this); + this.socket.onmessage = this.handleWebsocketMessage.bind(this); + this.socket.onclose = this.handleWebsocketClose.bind(this); + this.socket.onerror = function(e) { + console.error('!!WebSocket Error'); + console.error(e); + }; + + }; + + this.updateHeating = function(obj) { + var $curTemp = $('#curTemp'); + if (this.previousTemp !== obj.temp) { + $curTemp.text(obj.temp); + $curTemp.css('color', rgb(tempColours[parseInt(obj.temp)])); + this.previousTemp = obj.temp; + } + }; + + this.toggleLighting = function(id) { + var _id; + var _off = ['f', 'g']; + var _on = ['o', 'n']; + var $show; + var $hide; + + _id = _off.indexOf(id); + + // console.log(id,_id); + if (_id > -1) { + // Lights are being turnd off + + $hide = ['#', controller.lights.off[_id]].join(''); + $show = ['#', controller.lights.on[_id]].join(''); + } else { + _id = _on.indexOf(id); + $show = ['#', controller.lights.off[_id]].join(''); + $hide = ['#', controller.lights.on[_id]].join(''); + } + $($show).show(); + $($hide).hide(); + + }; + + this.toggleHeating = function(status) { + var $show, $hide; + if (status) { + $hide = $('#heatingOn'); + $show = $('#heatingOff'); + } else { + $show = $('#heatingOn'); + $hide = $('#heatingOff'); + } + $($show).show(); + $($hide).hide(); + + }; + + this.toggleProjector = function(status) { + var $show, $hide; + if (status) { + $hide = $('#projectorOn'); + $show = $('#projectorOff'); + } else { + $show = $('#projectorOn'); + $hide = $('#projectorOff'); + + } + $($show).show(); + $($hide).hide(); + + }; + + this.updateLighting = function(obj) { + //Console.log(obj); + $('#lightR').text(obj.Red); + $('#lightG').text(obj.Green); + $('#lightB').text(obj.Blue); + + var r = parseInt(obj.Red); + var g = parseInt(obj.Green); + var b = parseInt(obj.Blue); + + var newR = Math.floor(((r / 65535.0) * 100) * 255); + var newG = Math.floor(((g / 65535.0) * 100) * 255); + var newB = Math.floor(((b / 65535.0) * 100) * 255); + + var w = '#' + ('0' + parseInt(newR).toString(16)).substr(-2, + 2) + ('0' + parseInt(newG).toString(16)).substr(-2, + 2) + ('0' + parseInt(newB).toString(16)).substr(-2, 2); + + $('#lightW').text(w).css('background-color', w); + }; + + this.updateProj = function(obj) { + //Console.log(obj); + var y; + var r = parseInt(obj.Red); + var g = parseInt(obj.Green); + var b = parseInt(obj.Blue); + + var newR = Math.floor(((r / 65535.0) * 100) * 255); + var newG = Math.floor(((g / 65535.0) * 100) * 255); + var newB = Math.floor(((b / 65535.0) * 100) * 255); + + $('#projR').text(newR); + $('#projG').text(newG); + $('#projB').text(newB); + + y = Math.floor((0.2126 * newR + 0.7152 * newG + 0.0722 * newB)); + + var w = '#' + ('0' + parseInt(y).toString(16)).substr(-2, + 2) + ('0' + parseInt(y).toString(16)).substr(-2, 2) + ('0' + parseInt( + y).toString(16)).substr(-2, 2); + + $('#projW').text(w).css('background-color', w); + }; + + this.handleData = function(d) { + console.log(d.id); + switch (d.id) { + case 'LightingDataReceived': + + // This.updateLighting(d.sensorData.d); + break; + case 'ProjectorDataReceived': + // This.updateProj(d.sensorData.d); + break; + case 'HeatingDataReceived': + this.updateHeating(d.sensorData.d); + break; + case 'calendar': + controller.updateCalendar(d); + break; + case 'Lighting': + this.toggleLighting(d.device); + break; + case 'Heating': + this.toggleHeating(d.status); + break; + case 'Projector': + this.toggleProjector(d.status); + break; + + case 'announce': + controller.notify(d); + break; + default: + console.log(d); + break; + } + }; + + this.handleWebsocketOnOpen = function() { + 'use strict'; + this.retry = 0; + $('#longWait').hide(); + $('#noSocket').slideUp(); + }; + + this.handleWebsocketMessage = function(message) { + + var command; + try { + command = JSON.parse(message.data); + this.updateDateTime(); + } + catch (e) { /* Do nothing */ + } + + if (command) { + this.handleData.call(this,command); + } + }; + + this.handleWebsocketClose = function() { + console.error('WebSocket Connection Closed.', this.retry); + var self = this, delay; + + if (this.retry > 0) { + $('#noSocket').slideDown(); + } + + if (this.retry > 12) { + this.retry = 1; + } + + if (this.retry === 3) { + $('#longWait').fadeIn(); + } + + if (this.retry > 0) { + delay = 5000 * this.retry; + } else { + delay = 1500; + } + console.log('Waiting ', delay); + this.timer = setTimeout(function() {self.startWebSocket();},delay); + this.retry++; + }; + + this.startWebSocket(); +}; diff --git a/gulpfile.js b/gulpfile.js index 44fadde..34c5ac4 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -24,7 +24,7 @@ var filePath = { }; gulp.task('scripts', function() { - return gulp.src('app/js/**/*.js') + 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')) @@ -32,12 +32,6 @@ gulp.task('scripts', function() { .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', diff --git a/lib/mqtt/mqttConnect.js b/lib/mqtt/mqttConnect.js index 5aea07e..6184131 100644 --- a/lib/mqtt/mqttConnect.js +++ b/lib/mqtt/mqttConnect.js @@ -26,6 +26,8 @@ function randomString(length) { */ module.exports = { + pingTimer: 0, + statuses: {}, sockets: null, watches: {}, client: null, @@ -41,6 +43,12 @@ module.exports = { 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; }, @@ -119,6 +127,8 @@ module.exports = { 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) { @@ -126,18 +136,26 @@ module.exports = { } var destinationName = mqttConfig.prefix + this.lighting + '/cmd/' + id + '/fmt/json'; this.client.publish(destinationName, 'ON', _callback); - this.emitter.emit('sendSocket',{id: this.lighting, device: id, status: true}); + 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); - this.emitter.emit('sendSocket',{id: this.lighting, device: id, status: false}); + packet = {id: this.lighting, device: id, status: false}; + this.emitter.emit('sendSocket', packet); + this.updateStatus(this.lightList[id], packet); }, + setupEvents: function() { + this.emitter.on('lightingOn', this.lightingOn); this.emitter.on('lightingOff', this.lightingOff); this.emitter.on('heatingOn', this.heatingOn); @@ -145,7 +163,31 @@ module.exports = { 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'); + }, connectWS: function(connectCB) { logger.debug('Going to connect WS'); @@ -165,7 +207,7 @@ module.exports = { //Logger.info("Message from :" + msg.destinationName); clientStatus.deviceConnected = true; sensorData = JSON.parse(msg.payloadString); - logger.debug(sensorData); + // logger.debug(sensorData); var temp = msg.destinationName.split('/'); if (self.watches.hasOwnProperty(temp[4])) { @@ -179,7 +221,8 @@ module.exports = { logger.error('+ onConnectionLost'); logger.error(e); logger.error('- onConnectionLost'); - } + }; + var connectOptions = {}; connectOptions.keepAliveInterval = 3600; @@ -241,6 +284,7 @@ module.exports = { return this.sockets; } + }; diff --git a/lib/office/calendarInterface.js b/lib/office/calendarInterface.js index 389c700..22ce5e6 100644 --- a/lib/office/calendarInterface.js +++ b/lib/office/calendarInterface.js @@ -53,6 +53,7 @@ function processICAL(ical) { description: '', timeStart: null, timeEnd: null, + actualEnd: null, duration: 0, combined: '', uid: '' @@ -71,6 +72,7 @@ function processICAL(ical) { if (blockStep.indexOf(segments.meetingEndID) >= 0) { ws = STRING(block[step].split(segments.meetingEndID)[1]).collapseWhitespace().s; workBlock.dtend = Date.create(ws); + workBlock.actualEnd = workBlock.dtend; } if (blockStep.indexOf(segments.meetingStartAlt) >= 0) { ws = STRING(block[step].split(segments.meetingStartAlt)[1]).collapseWhitespace().s; @@ -98,16 +100,23 @@ function processICAL(ical) { } if (workBlock.dtstart !== null) { - workBlock.timeStart = workBlock.dtstart.format('{12hr}:{mm}:{ss} {tt}'); + workBlock.timeStart = workBlock.dtstart.format('{24hr}:{mm}:{ss} {tt}'); workBlock.cronStart = workBlock.dtstart.format('{m} {H} * * *'); // WorkBlock.combined = '' + workBlock.timeStart + ' - '; workBlock.combined = workBlock.timeStart + ' - '; } workBlock.combined = workBlock.combined + workBlock.summary; + if (workBlock.dtend !== null) { - workBlock.timeEnd = workBlock.dtend.format('{12hr}:{mm}:{ss} {tt}'); - workBlock.cronStop = workBlock.dtend.format('{m} {H} * * *'); + var fiveMins; + workBlock.timeEnd = workBlock.actualEnd.format('{24hr}:{mm}:{ss} {tt}'); + workBlock.cronStop = workBlock.actualEnd.format('{m} {H} * * *'); + //fiveMins = workBlock.actualEnd.rewind({minutes: 5}); + + fiveMins = Date.create(new Date()).addMinutes('5'); + workBlock.cronAlert = fiveMins.format('{m} {H} * * *'); } + if (workBlock.dtstart !== null && workBlock.dtend !== null) { var elapsedTime = new Elapsed(workBlock.dtstart, workBlock.dtend); workBlock.duration = elapsedTime.optimal; diff --git a/lib/office/officeController.js b/lib/office/officeController.js index 9517a49..621be07 100644 --- a/lib/office/officeController.js +++ b/lib/office/officeController.js @@ -78,8 +78,24 @@ var officeController = function(neweventbus) { logger.debug('+ => cleanUp'); this.cleanUpMeeting(id); }); + + eventBus.on('announce', (meeting) => { + logger.debug('+ => announce'); + this.announceMeetingEnd(meeting); + + }); }; +officeController.prototype.announceMeetingEnd = function(meeting) { + logger.debug('announceMeetingEnd',meeting); + var msg = ['The meeting "', meeting.summary, '" is due to end in five minutes. Would you like to extend it by another five minutes?'].join(''); + + var packet = {id: 'announce', msg: msg, uid: meeting.uid}; + + eventBus.emit('sendSocket', packet); +}; + + officeController.prototype.cleanUpMeeting = function(id) { logger.debug('deleting scheduled item: ' + id); logger.info('Pre delete Schedule count: ' + Object.keys(this.schedule).length); @@ -92,25 +108,32 @@ officeController.prototype.createMeeting = function(obj,delayStart) { // Logger.debug(util.inspect(this)); var newMeeting = obj, _delayStart = delayStart || false, cronStartStr; + logger.debug('obj', obj); + logger.debug('Delay start?', delayStart); var now = new Date.create(); var self = this; // Logger.debug(newMeeting); newMeeting.cronJOBS = {start: null, stop: null}; if (_delayStart) { - cronStartStr = now.addSeconds(15).format('{s} {m} {H} * * *'); + + cronStartStr = now.addSeconds(5).format('{s} {m} {H} * * *'); } else { cronStartStr = newMeeting.cronStart; } + newMeeting.cronJOBS.start = cron.schedule(cronStartStr, function() { var n = new Date(); logger.debug('MEETING STARTED!!!!' , n); var key = Object.keys(self.schedule)[0]; + var curMeeting = {}; self.cloneObject(self.schedule[key], curMeeting); curMeeting.cronJOBS = null; + // We want to kick of the pre -end timer here. + // Lets turn on some lights! eventBus.emit('lightingOn','o'); eventBus.emit('lightingOn','n'); @@ -118,8 +141,22 @@ officeController.prototype.createMeeting = function(obj,delayStart) { eventBus.emit('sendSocket', {id: 'calendar',current: curMeeting}); + }, true); + + newMeeting.cronJOBS.alert = cron.schedule(newMeeting.cronAlert, function() { + var n = new Date(); + logger.error('MEETING ends in 5 minutes'); + logger.debug(this); + + // Announce there are five minutes remaining.. + + eventBus.emit('announce', newMeeting); + + }.bind(this), true); + + newMeeting.cronJOBS.stop = cron.schedule(newMeeting.cronStop, function() { var n = new Date(); logger.debug('MEETING ENDED' + n); diff --git a/package.json b/package.json index a028e0b..9a08aa4 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,8 @@ "supertest": "^1.1.0" }, "scripts": { - "test": "mocha --recursive --reporter spec --bail --check-leaks --timeout 3000" + "test": "mocha --recursive --reporter spec --bail --check-leaks --timeout 3000", + "start": "node app" }, "author": "Martin Donnelly", "license": "ISC", diff --git a/routes/lighting_v1.js b/routes/lighting_v1.js index 869811b..5354e2a 100644 --- a/routes/lighting_v1.js +++ b/routes/lighting_v1.js @@ -9,13 +9,13 @@ mqttConnect.doConnection();*/ var mqttConnect; -function doLightsOn(id) { + +function doLightsOn(id) { mqttConnect.doConnection().lightingOn(id); } function doLightsOff(id) { - mqttConnect.doConnection().lightingOff(id); } @@ -74,7 +74,7 @@ module.exports = { doLightsOn: function(id) { doLightsOn(id); }, - doLightsOff: function(id) { - doLightsOff(id); - } + doLightsOff: function(id) { + doLightsOff(id); + } };