From b3f56ba5c445c541dc652600814504905cb63ced Mon Sep 17 00:00:00 2001 From: imbrianj Date: Wed, 10 Jan 2018 18:56:06 -0800 Subject: [PATCH] Cleanup and bug fixes to machine learning code. Have http and https servers run in parallel (still sorting some stuff here). Add values to generic removePunctuation and consolidate on its use. --- app.js | 49 ++++++++++++++++++++++--------- apps/announceTrendingNews.js | 7 +++-- apps/gerty/ai.js | 25 +++++++++++----- cache/version.txt | 2 +- config/config.js | 18 +++++++----- devices/nest/controller.js | 4 ++- devices/smartthings/controller.js | 3 +- js/combo.min.js | 2 +- lib/ai.js | 37 ++++++++++++++++++----- lib/deviceState.js | 16 ++++++---- lib/staticAssets.js | 5 ++-- lib/translate.js | 2 +- 12 files changed, 117 insertions(+), 53 deletions(-) diff --git a/app.js b/app.js index f75f36a..8338467 100644 --- a/app.js +++ b/app.js @@ -39,14 +39,17 @@ var fs = require('fs'), db = require(__dirname + '/lib/db'), webSockets = require(__dirname + '/lib/webSockets'), controllers, + sslServer, server, wsServer, + sslWsServer, settings, configFile = __dirname + '/config/config.js', arg, options, connection, - startup; + startup, + sslStartup; for (arg in process.argv) { if (process.argv.hasOwnProperty(arg)) { @@ -103,6 +106,12 @@ startup = function () { } }; +sslStartup = function () { + 'use strict'; + + console.log('\x1b[36mListening on port ' + settings.config.config.ssl.serverPort + ' for SSL\x1b[0m'); +}; + fs.stat(configFile, function (err, data) { 'use strict'; @@ -146,27 +155,39 @@ fs.stat(configFile, function (err, data) { else if (process.platform === 'linux') { staticAssets.buildCerts(settings.config.config.ssl); } - } - if (options) { - console.log('\x1b[36mSSL\x1b[0m: Enabled'); - server = https.createServer(options, connection).listen(settings.config.config.serverPort, startup); + if (options) { + console.log('\x1b[36mSSL\x1b[0m: Enabled'); + sslServer = https.createServer(options, connection).listen(settings.config.config.ssl.serverPort, sslStartup); + + // SSL will need it's own Web Sockets server. + if (sslServer) { + sslWsServer = new webSocketServer({ httpServer : sslServer }, 'echo-protocol'); + + sslWsServer.on('request', function (request) { + webSockets.newConnection(request, controllers); + }); + } + } } - else { - console.log('\x1b[31mSSL\x1b[0m: Disabled'); + if (settings.config.config.serverPort) { server = http.createServer(connection).listen(settings.config.config.serverPort, startup); + + // Or you're connecting with Web Sockets + wsServer = new webSocketServer({ httpServer : server }, 'echo-protocol'); + + wsServer.on('request', function (request) { + webSockets.newConnection(request, controllers); + }); + } + + if ((!sslServer) && (!server)) { + console.log('\x1b[31mError\x1b[0m: No server started. Do you have a serverPort configured?'); } ai.processFiles(); db.readDb(); - - // Or you're connecting with Web Sockets - wsServer = new webSocketServer({ httpServer : server }, 'echo-protocol'); - - wsServer.on('request', function (request) { - webSockets.newConnection(request, controllers); - }); } else { diff --git a/apps/announceTrendingNews.js b/apps/announceTrendingNews.js index c6a6a2e..5629ddb 100644 --- a/apps/announceTrendingNews.js +++ b/apps/announceTrendingNews.js @@ -32,7 +32,7 @@ module.exports = (function () { var CooldownWords = {}; return { - version : 20171127, + version : 20180110, translate : function (token, lang) { var translate = require(__dirname + '/../lib/translate'); @@ -41,12 +41,13 @@ module.exports = (function () { }, findProperNouns : function (articleParts, blacklist) { - var i = 0, + var translate = require(__dirname + '/../lib/translate'), + i = 0, word, properNouns = []; for (i; i < articleParts.length; i += 1) { - word = articleParts[i].replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, ''); + word = translate.stripPunctuation(articleParts[i]); word = word.replace(/\s{2,}/g, ' '); if ((word) && (this.isWordCapitalized(word)) && (blacklist.indexOf(word.toLowerCase()) === -1)) { diff --git a/apps/gerty/ai.js b/apps/gerty/ai.js index a7c582c..5854b42 100644 --- a/apps/gerty/ai.js +++ b/apps/gerty/ai.js @@ -30,14 +30,17 @@ module.exports = (function () { 'use strict'; return { - version : 20170920, + version : 20180109, /** - * Take in a typeClass and return the type of device it's categorized as. + * Take in a deviceId, command and set of controllers. Determines if + * there's reasonable intent for a given action and will notify or act on + * that implied command. */ findIntent : function (deviceId, command, controllers) { - var config = controllers.config, - delay = config.ai.executeDelaySeconds || 1; + var config = controllers.config, + trainingWheels = config.ai.trainingWheels || false, + delay = config.ai.executeDelaySeconds || 1; if (!config.ai.disable) { setTimeout(function() { @@ -47,12 +50,14 @@ module.exports = (function () { translate, tempDevice, utterance, - i = 0; + i = 0, + j = 0; if (intent.length) { + runCommand = require(__dirname + '/../../lib/runCommand'); + for (tempDevice in controllers) { if ((tempDevice !== 'config') && (controllers[tempDevice].config.typeClass === 'gerty')) { - runCommand = require(__dirname + '/../../lib/runCommand'); translate = require(__dirname + '/../../lib/translate'); for (i; i < intent.length; i += 1) { @@ -60,11 +65,15 @@ module.exports = (function () { runCommand.runCommand(tempDevice, 'text-' + utterance); } - // Eventually fired intent should note that this was an AI command, - // so the intended event doesn't cascade to others (?) break; } } + + if (!trainingWheels) { + for (j; j < intent.length; j += 1) { + runCommand.runCommand(intent[j].device, 'subdevice-' + intent[j].subdevice + '-' + intent[j].command); + } + } } }, delay * 1000); } diff --git a/cache/version.txt b/cache/version.txt index 1e32a96..6b44958 100644 --- a/cache/version.txt +++ b/cache/version.txt @@ -1 +1 @@ -1515546199655 \ No newline at end of file +1515639239743 \ No newline at end of file diff --git a/config/config.js b/config/config.js index debe0d6..9f26643 100644 --- a/config/config.js +++ b/config/config.js @@ -26,12 +26,13 @@ exports.config = { // in all (or any) browsers. Also note that SmartThings cannot contact // SwitchBoard for real-time updates. ssl : { - country : 'US', - state : 'Washington', - city : 'Seattle', - org : 'Switchboard', - name : 'Switchboard', - disabled : true + country : 'US', + state : 'Washington', + city : 'Seattle', + org : 'SwitchBoard', + name : 'SwitchBoard', + serverPort : 8181, + disabled : false }, ai : { eventLogging : false, @@ -40,6 +41,9 @@ exports.config = { eventCooldownMinutes : 120, minimumThreshold : 100, confidence : 90, + // With trainingWheels on, Gerty will notify you of intent. Set this to + // false and he will take action. + trainingWheels : true, disable : false } }, @@ -750,7 +754,7 @@ exports.config = { 'I\'m Back' : 'smartthings=subdevice-mode-Home;nest=Home', 'Welcome Home' : 'smartthings=subdevice-mode-Home;nest=Home' }, controllerIds : ['samsung', 'roku', 'ps3', 'panasonic', 'lg', 'pioneer', 'denon', 'speech', 'stocks', 'weather', 'foscam', 'mp3', 'sms', 'pushover', 'smartthings', 'powerView', 'nest', 'switchBoardCI', 'xbmc', 'raspberryRemote', 'wemo', 'activeBuilding', 'clientMp3', 'clientNotify', 'clientSpeech'] } }, - disabled : true + disabled : false }, /* diff --git a/devices/nest/controller.js b/devices/nest/controller.js index 3cd07c8..f727502 100644 --- a/devices/nest/controller.js +++ b/devices/nest/controller.js @@ -29,7 +29,7 @@ module.exports = (function () { * @requires querystring, fs, https */ return { - version : 20171127, + version : 20180109, inputs : ['command', 'text', 'list', 'subdevice'], @@ -262,6 +262,7 @@ module.exports = (function () { label : that.findLabel(response.topaz[i].where_id, config.language), type : 'protect', lastOn : matched.lastOn, + lastOff : matched.lastOff, duration : matched.duration, readOnly : true }); @@ -288,6 +289,7 @@ module.exports = (function () { label : that.findLabel(response.device[i].where_id), type : 'thermostat', lastOn : matched.lastOn, + lastOff : matched.lastOff, duration : matched.duration, celsius : celsius }); diff --git a/devices/smartthings/controller.js b/devices/smartthings/controller.js index dcaa6fe..e49483a 100644 --- a/devices/smartthings/controller.js +++ b/devices/smartthings/controller.js @@ -29,7 +29,7 @@ module.exports = (function () { * @fileoverview Basic control of SmartThings endpoint. */ return { - version : 20171108, + version : 20180109, inputs : ['list', 'subdevice'], @@ -213,6 +213,7 @@ module.exports = (function () { id : util.sanitize(device.id), label : util.sanitize(device.label), lastOn : util.sanitize(matched.lastOn), + lastOff : util.sanitize(matched.lastOff), duration : util.sanitize(matched.duration) }; diff --git a/js/combo.min.js b/js/combo.min.js index bd77c22..c4cb13b 100644 --- a/js/combo.min.js +++ b/js/combo.min.js @@ -1,3 +1,3 @@ -/* 20180109 */ +/* 20180110 */ SB=function(){"use strict";return{version:20170320,isChildOf:function(e,t){if(t===e)return!1;for(;e&&e!==t&&e!==document.body;)e=e.parentNode;return e===t},event:{list:[],add:function(e,t,i,n){n=n||!1,e.addEventListener?e.addEventListener(t,i,n):e.attachEvent?e.attachEvent("on"+t,i):e["on"+t]=i,SB.event.list.push([e,t,i])},remove:function(e,t,i,n){n=n||!1;var s=0;for(e.removeEventListener?e.removeEventListener(t,i,n):e.detachEvent?e.detachEvent("on"+t,i):e["on"+t]=null;s=0;t-=1)SB.event.list[t]&&(SB.event.list[t][0]!==e&&e!==document||SB.event.remove(SB.event.list[t][0],SB.event.list[t][1],SB.event.list[t][2]))}},getTarget:function(e){return(e=e||window.event).target?e.target:e.srcElement},hasAttribute:function(e,t,i){if(e[t])return!!e[t].match(new RegExp("(\\s|^)"+i+"(\\s|$)"))},hasClass:function(e,t){var i=!1;return e&&e.className&&(i=!!SB.hasAttribute(e,"className",t)),i},addClass:function(e,t){e&&t&&(SB.hasClass(e,t)||(e.className=SB.trim(e.className+" "+t)))},removeClass:function(e,t){SB.hasClass(e,t)&&(e.className=e.className.replace(new RegExp("(\\s|^)"+t+"(\\s|$)")," "),e.className=SB.trim(e.className))},toggleClass:function(e,t){SB.hasClass(e,t)?SB.removeClass(e,t):SB.addClass(e,t)},setFocus:function(e){"function"==typeof e.setActive?e.setActive():"function"==typeof e.focus&&e.focus()},get:function(e){return document.getElementById(e)},getByTag:function(e,t){return(t=t||document).getElementsByTagName(e)},getByClass:function(e,t,i){var n=[],s=[],o=0,r=0;if(t=t||document,"*"===(i=i.toLowerCase()||"*")&&document.getElementsByClassName)return t.getElementsByClassName(e);if(t.getElementsByClassName)if(s=t.getElementsByClassName(e),i&&s.length)for(o in s)s[o].tagName&&s[o].tagName.toLowerCase()===i&&(n[r]=s[o],r+=1);else n=s;else{s=SB.getByTag(i,t);for(o in s)SB.hasClass(s[o],e)&&(n[r]=s[o],r+=1)}return n},getText:function(e){return e.textContent?e.textContent:e.innerText?e.innerText:e.text?e.text:e.innerHTML},putText:function(e,t){e.textContent?e.textContent=t:e.innerText?e.innerText=t:e.text?e.text=t:e.innerHTML=t},trim:function(e){return(e=e||"").toString().replace(/^\s\s*/,"").replace(/\s\s*$/,"")},decode:function(e){var t="";return"object"==typeof JSON&&(t=JSON.parse(e)),t},encode:function(e){var t="";return"object"==typeof JSON&&(t=JSON.stringify(e)),t},log:function(e,t,i){var n=new Date,s="color: white";if("object"==typeof console&&"function"==typeof console.log)if(t&&"object"!=typeof e){switch(e="%c"+t+"%c: "+e+" ("+n.getHours()+":"+(n.getMinutes()<10?"0":"")+n.getMinutes()+")",i){case"success":s="color: green";break;case"info":s="color: aqua";break;case"error":s="color: red"}console.log(e,"background: black; "+s,"background: black; color: white")}else console.log(e)},vibrate:function(e){e=e||20,window.navigator&&window.navigator.vibrate?window.navigator.vibrate(e):SB.log("Not supported","Vibrate","error")},notify:function(e,t,i){var n,s;return"function"==typeof Notification&&("granted"===Notification.permission?(n=new Notification(e,t),setTimeout(function(){n.close(),SB.event.remove(n,"click",s)},1e4),s=function(e){window.focus(),i(e),SB.event.remove(n,"click",s)},SB.event.add(n,"click",s)):SB.notifyAsk()),n},notifyAsk:function(){"function"==typeof Notification&&"denied"!==Notification.permission&&Notification.requestPermission(function(e){Notification.permission!==e&&(Notification.permission=e)})},sound:{sounds:{},play:function(e){"function"==typeof Audio&&(SB.sound.sounds[e]||(SB.sound.sounds[e]=new Audio(e)),SB.sound.sounds[e].play())}},speak:function(e,t,i){var n,s;window.speechSynthesis?(n=new SpeechSynthesisUtterance,s=window.speechSynthesis.getVoices(),n.text=e,n.lang=t||"en-US",s&&s[10]&&"Alex"===s[10].name&&(n.voice=s[10],"female"===i&&(n.voice=s[30])),window.speechSynthesis.speak(n)):SB.log("Not supported","Speak","error")},transcribe:function(e){var t,i,n=window.SpeechRecognition||window.webkitSpeechRecognition||window.mozSpeechRecognition||window.msSpeechRecognition||null;return n?(SB.log("Supported","Transcribe","info"),t=new n,i=function(n){e(n.results[0][0].transcript,n.results[0][0].confidence),SB.event.remove(t,"result",i)},SB.event.add(t,"result",i)):SB.log("Not supported","Transcribe","error"),t},storage:function(e,t){var i;return e&&("undefined"!=typeof localStorage?(void 0!==t&&("object"==typeof t&&(t=SB.encode(t)),localStorage.setItem(e,t)),i=localStorage.getItem(e)):SB.log("Not supported","Local Storage","error")),i},ajax:{request:function(e){e.method=e.method||"GET",e.onStart=e.onStart||function(){},e.onComplete=e.onComplete||function(){};var t,i,n="?";if(i=function(){switch(e.onStart(),typeof e.onComplete){case"object":e.onComplete.value?e.onComplete.value=e.response:e.onComplete.childNodes[0]&&SB.putText(e.onComplete,e.response);break;case"function":e.onComplete()}},window.XMLHttpRequest)t=new XMLHttpRequest;else{if(!window.ActiveXObject)return!1;t=new ActiveXObject("Microsoft.XMLHTTP")}"GET"===e.method&&(-1===e.path.indexOf("?")&&-1===e.param.indexOf("?")||(n="&"),e.path=e.path+n+e.param,e.param=""),t.open(e.method.toUpperCase(),e.path,!0),"POST"===e.method&&t.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),t.setRequestHeader("rest","true"),SB.event.add(t,"readystatechange",function(){if(4===t.readyState){if(200!==t.status)return!1;e.response=t.responseText,i()}}),t.send(e.param)}},init:function(){SB.spec&&SB.spec.init&&SB.spec.init(),SB.addClass(document.body,"rich")}}}(),document.addEventListener&&document.addEventListener("DOMContentLoaded",SB.init,!1),SB.event.add(window,"load",function(){"use strict";document.addEventListener||SB.init()}),SB.event.add(window,"unload",function(){"use strict";SB.event.removeAll()}),"object"==typeof module&&(module.exports=SB),SB.spec=function(){"use strict";return{version:20170414,state:{},parsers:{},templates:{},strings:{},socket:{},uiComponents:{header:{},body:{},indicator:{},templates:[]},ariaDevice:function(){var e=SB.getByClass("selected",SB.spec.uiComponents.header,"li")[0],t=SB.getByTag("h2",SB.spec.uiComponents.header)[0],i=SB.spec.strings.CURRENT.split("{{DEVICE}}").join(SB.getText(e));SB.putText(t,i),i=null},navChange:function(e){var t=SB.getByClass(e,SB.spec.uiComponents.header,"li")[0],i=SB.get(e),n=SB.getByClass("selected",SB.spec.uiComponents.header,"li")[0],s=SB.getByClass("selected",SB.spec.uiComponents.body,"section")[0];t&&!SB.hasClass(t,"selected")&&(SB.removeClass(n,"selected"),SB.removeClass(s,"selected"),SB.spec.lazyLoad(e),SB.storage("selected",e),SB.addClass(t,"selected"),SB.addClass(i,"selected"),SB.spec.lazyUnLoad(s),SB.spec.ariaDevice())},updateTemplate:function(e){var t,i,n,s,o,r=SB.get(e.deviceId),a=SB.spec.parsers[e.typeClass],c=e.value,l=e.state,p=document.createElement("div"),u=!1;return SB.spec.state[e.deviceId]=e,SB.log("Updated",e.deviceId,"success"),r&&(n=SB.spec.uiComponents.templates[e.typeClass].markup,i=SB.hasClass(r,"selected")?" selected":"",s=r.cloneNode(!0),(t=SB.getByTag("h1",s)[0]).parentNode.removeChild(t),o=s.innerHTML,a&&(n=a(e.deviceId,n,l,c,SB.spec.uiComponents.templates[e.typeClass].fragments)),"ok"===l?(n=n.split("{{DEVICE_ACTIVE}}").join(SB.spec.strings.ACTIVE),SB.hasClass(r,"device-off")&&(SB.removeClass(r,"device-off"),SB.addClass(r,"device-on"),SB.putText(SB.getByTag("em",SB.getByTag("h1",r)[0])[0],SB.spec.strings.ACTIVE))):(n=n.split("{{DEVICE_ACTIVE}}").join(SB.spec.strings.INACTIVE),SB.hasClass(r,"device-on")&&(SB.removeClass(r,"device-on"),SB.addClass(r,"device-off"),SB.putText(SB.getByTag("em",SB.getByTag("h1",r)[0])[0],SB.spec.strings.INACTIVE))),r&&n&&e&&(SB.storage("state",SB.spec.state),n=(n=(n=n.split("{{DEVICE_ID}}").join(e.deviceId)).split("{{DEVICE_TYPE}}").join(e.typeClass)).split("{{DEVICE_SELECTED}}").join(i),n="ok"===e.state?n.split("{{DEVICE_STATE}}").join(" device-on"):n.split("{{DEVICE_STATE}}").join(" device-off"),n=i?n.split("{{LAZY_LOAD_IMAGE}}").join("src"):n.split("{{LAZY_LOAD_IMAGE}}").join("data-src")),n&&(p.innerHTML=n,p=SB.getByTag("section",p)[0],(t=SB.getByTag("h1",p)[0]).parentNode.removeChild(t),p.innerHTML!==o&&(r.outerHTML=n,u=!0))),r=null,a=null,c=null,l=null,t=null,i=null,n=null,p=null,s=null,o=null,u},buildIndicator:function(){var e;SB.get("indicator")||((e=document.createElement("span")).id="indicator",SB.addClass(e,"connecting"),SB.putText(e,SB.spec.strings.CONNECTING),SB.spec.uiComponents.indicator=e,SB.spec.uiComponents.header.appendChild(e))},checkConnection:function(){var e=SB.spec.socket.readyState<=1;return e||(SB.spec.socketConnect(0),e=SB.spec.socket.readyState<=1),e},socketConnect:function(e){var t,i,n,s,o,r="https:"===window.location.protocol?"wss":"ws";SB.spec.socket.readyState&&3!==SB.spec.socket.readyState||(SB.log("Connecting","WebSocket","info"),SB.spec.socket=new WebSocket(r+"://"+window.location.host,"echo-protocol"),e+=1,t=function(){var t=Math.round(Math.min(Math.max(e*(15*Math.random()),10),60));SB.log("Retrying in "+t+"s","WebSocket","info"),SB.spec.uiComponents.indicator.className="disconnected",SB.putText(SB.spec.uiComponents.indicator,SB.spec.strings.DISCONNECTED),setTimeout(function(){SB.spec.socketConnect(e)},1e3*t)}),i=function(){var e="disconnected"===SB.spec.uiComponents.indicator.className;SB.spec.uiComponents.indicator.className="connected",SB.putText(SB.spec.uiComponents.indicator,SB.spec.strings.CONNECTED),SB.log("Connected","WebSocket","success"),e&&(SB.spec.socket.send("fetch state"),SB.log("Reconnected","WebSocket","success"))},n=function(e){var t,i=SB.decode(e.data),n={};if("string"==typeof i.speech)i.speech&&(SB.log(i.speech,"Speech","success"),SB.speak(i.speech,i.language,i.voice));else if("string"==typeof i.sound)SB.log(i.sound,"Sound","success"),SB.sound.play("/mp3/"+i.sound+".mp3");else if("string"==typeof i.vibrate)SB.log(i.vibrate,"Vibrate","success"),SB.vibrate(100*i.vibrate);else if("string"==typeof i.title)t=function(){i.deviceId&&SB.spec.navChange(i.deviceId)},SB.notify(i.title,i.options,t);else if("string"==typeof i.deviceId)SB.spec.updateTemplate(i);else if("object"==typeof i){for(n in i)if(i.hasOwnProperty(n))break;if(i[n]&&i[n].deviceId){SB.log("Received","State","success");for(n in i)i.hasOwnProperty(n)&&SB.spec.updateTemplate(i[n])}else i[n]&&i[n].markup&&(SB.spec.uiComponents.templates=i,SB.storage("templates",SB.spec.uiComponents.templates))}},s=function(){SB.event.remove(SB.spec.socket,"close",s),SB.log("Disconnected","WebSocket","error"),t()},o=function(){SB.event.remove(SB.spec.socket,"open",i),SB.event.remove(SB.spec.socket,"message",n),SB.event.remove(SB.spec.socket,"close",s),SB.event.remove(SB.spec.socket,"error",o)},SB.event.add(SB.spec.socket,"open",i),SB.event.add(SB.spec.socket,"message",n),SB.event.add(SB.spec.socket,"close",s),SB.event.add(SB.spec.socket,"error",o)},statePoller:function(){var e;SB.log("not supported - using polling","WebSockets","error"),e={path:"/templates/",param:"ts="+(new Date).getTime(),method:"GET",onComplete:function(){SB.spec.uiComponents.templates=SB.decode(e.response),SB.storage("templates",SB.spec.uiComponents.templates)}},SB.ajax.request(e),setInterval(function(){var e={path:"/state/",param:"ts="+(new Date).getTime(),method:"GET",onComplete:function(){var t,i=SB.decode(e.response);if(i){SB.spec.uiComponents.indicator.className="connected",SB.putText(SB.spec.uiComponents.indicator,SB.spec.strings.CONNECTED),setTimeout(function(){SB.spec.uiComponents.indicator.className="connecting",SB.putText(SB.spec.uiComponents.indicator,SB.spec.strings.CONNECTIG)},1e3);for(t in i)i.hasOwnProperty(t)&&SB.spec.updateTemplate(i[t])}else SB.spec.uiComponents.indicator.className="disconnected",SB.putText(SB.spec.uiComponents.indicator,SB.spec.strings.DISCONNECTED)}};SB.ajax.request(e)},1e4)},lazyLoad:function(e){var t,i,n,s,o=0;if(SB.get(e))for(t=SB.get(e),i=Array.prototype.slice.call(SB.getByTag("img",t)),n=Array.prototype.slice.call(SB.getByTag("iframe",t)),s=i.concat(n);o3&&(n=650),i>10&&(n=500),i+=1,setTimeout(d,n))},E=function(e,i){var n,s=S(e);i?(SB.vibrate(),t=SB.getTarget(e).parentNode.parentNode.id,SB.transcribe(u).start()):s&&!r&&("external"===s.rel?((n=window.open()).opener=null,n.location=s.href):SB.hasClass(s,"modal")?SB.spec.openModal(s):SB.hasClass(s,"close-modal")||SB.hasClass(s,"curtain")?SB.spec.closeModal(s):(t=s.href,d()))};"ontouchstart"in document.documentElement&&(SB.log("Enabled","Touch Events","info"),SB.event.add(SB.spec.uiComponents.body,"touchstart",function(e){s=!0,o=!0,a=!1,S(e)&&(r=!1,c=parseInt(e.changedTouches[0].clientX,10),l=parseInt(e.changedTouches[0].clientY,10),setTimeout(function(){a||(s=!1,E(e))},450))}),SB.event.add(SB.spec.uiComponents.body,"contextmenu",function(e){e.preventDefault()}),SB.event.add(SB.spec.uiComponents.body,"touchend",function(e){!SB.hasClass(SB.getTarget(e).parentNode,"emoji")&&s&&(a=!0,E(e)),p()}),SB.event.add(SB.spec.uiComponents.body,"touchmove",function(e){o&&(Math.abs(parseInt(e.changedTouches[0].clientX,10)-c)>5||Math.abs(parseInt(e.changedTouches[0].clientY,10)-l)>5)&&p()}),SB.event.add(SB.spec.uiComponents.body,"touchcancel",function(e){p()})),SB.event.add(SB.spec.uiComponents.body,"mousedown",function(e){SB.hasClass(SB.getTarget(e).parentNode,"emoji")?E(e,!0):!1===o&&!1===s&&(r=!1,E(e)),s=!1}),SB.event.add(SB.spec.uiComponents.body,"mouseup",function(e){SB.hasClass(SB.getTarget(e).parentNode,"emoji")||p()}),SB.event.add(SB.spec.uiComponents.body,"click",function(e){S(e)&&e.preventDefault()}),SB.event.add(SB.spec.uiComponents.body,"keyup",function(e){13===e.keyCode&&S(e)&&(r=!1,E(e),setTimeout(function(){r=!0},n/2))})},sendInput:function(e){var t=SB.getByTag("input",e,"input")[0],i=t.value,n=t.name,s=SB.getByClass("input-type",e,"input")[0].value;"text"===t.type&&(t.value=""),SB.spec.sendTextInput(i,n,s)},sendTextInput:function(e,t,i){var n;i=i||"text",SB.spec.socket?SB.spec.checkConnection()&&(SB.log("Issued","Text Command","success"),SB.spec.socket.send("/?"+t+"="+i+"-"+e)):(n={path:"/",param:t+"="+i+"-"+e+"&ts="+(new Date).getTime(),method:"POST",onComplete:function(){SB.log(n.response)}},SB.ajax.request(n))},formInput:function(e){SB.event.add(SB.spec.uiComponents.body,"submit",function(t){var i=SB.getTarget(t);t.preventDefault(),e?SB.log("Issued","Demo Text Command","success"):SB.spec.sendInput(i)}),SB.event.add(SB.spec.uiComponents.body,"change",function(t){var i,n,s=SB.getTarget(t),o=s.type;("range"===o||"number"===o&&void 0!==s.max&&void 0!==s.min)&&("range"===o?"number"===(n=s.previousElementSibling).type&&(n.value=s.value):"number"===o&&"range"===(i=s.nextElementSibling).type&&(i.value=s.value),e?SB.log("Issued","Demo Form Command","success"):SB.spec.sendInput(s.form))})},nav:function(){SB.event.add(SB.spec.uiComponents.header,"click",function(e){var t=SB.getTarget(e).parentNode;"li"===t.tagName.toLowerCase()?(e.preventDefault(),SB.spec.navChange(t.className),SB.vibrate(),SB.notifyAsk()):"indicator"===SB.getTarget(e).id&&SB.hasClass(SB.getTarget(e),"disconnected")&&SB.spec.socketConnect(0)})},init:function(){var e,t,i,n=SB.storage("selected"),s=SB.storage("templates"),o=SB.storage("state");if(SB.spec.uiComponents.header=SB.getByTag("header")[0],SB.spec.uiComponents.body=SB.getByTag("main")[0],SB.spec.buildIndicator(),t=SB.spec.uiComponents.header.dataset,i=SB.spec.uiComponents.body.dataset,SB.spec.strings={CURRENT:t.stringCurrent,CONNECTED:t.stringConnected,CONNECTING:t.stringConnecting,DISCONNECTED:t.stringDisconnected,ACTIVE:i.stringActive,INACTIVE:i.stringInactive,ON:i.stringOn,OFF:i.stringOff,CLOSE:i.stringClose,AM:i.stringAm,PM:i.stringPm,SUN:i.stringSun,MON:i.stringMon,TUE:i.stringTue,WED:i.stringWed,THUR:i.stringThur,FRI:i.stringFri,SAT:i.stringSat},n&&SB.spec.navChange(n),s&&o){SB.spec.uiComponents.templates=SB.decode(s),o=SB.decode(o);for(e in o)o.hasOwnProperty(e)&&SB.spec.updateTemplate(o[e])}"function"==typeof WebSocket||"object"==typeof WebSocket?SB.spec.socketConnect(0):SB.spec.statePoller(),SB.spec.lazyLoad(n||document.body.className),SB.spec.command(!1),SB.spec.formInput(!1),SB.spec.nav(),SB.spec.ariaDevice()}}}(),function(e){"use strict";e.activeBuilding=function(e,t,i,n,s){var o="",r="",a="",c="",l=function(e){return e="object"==typeof SB&&"object"==typeof SB.util?SB.util.translate(e,"activeBuilding"):require(__dirname+"/../../lib/sharedUtil").util.translate(e,"activeBuilding",s)};i&&n&&(p=n,c="object"==typeof SB&&"object"==typeof SB.util?SB.util.arrayList(p,"activeBuilding",s):require(__dirname+"/../../lib/sharedUtil").util.arrayList(p,"activeBuilding",s));var p;return n&&0!==n.length?1===n.length?(r="ok",a="tag",o=l("SINGLE_PACKAGE")):n.length>1&&(r="ok",a="tags",o=l("PLURAL_PACKAGES")):(r="err",a="times",o=l("NO_PACKAGES")),o=o.split("{{SENDERS}}").join(c),t=(t=(t=t.replace("{{ACTIVEBUILDING_STATE}}",r)).replace("{{ACTIVEBUILDING_ICON}}",a)).replace("{{ACTIVEBUILDING_DYNAMIC}}",o)}}("undefined"==typeof exports?this.SB.spec.parsers:exports),function(e){"use strict";e.airQuality=function(e,t,i,n,s){var o,r=s.report,a=s.graph,c=0,l="",p="",u="";if(n){l=n.location;for(c in n.report)n.report.hasOwnProperty(c)&&(u="",(o=n.report[c].max)&&(u=(u=a.split("{{MAX_VALUE}}").join(o)).split("{{PERCENT_QUALITY}}").join(n.report[c].value/o*100)),p=(p=(p=(p+=r.split("{{AIR_QUALITY_GRAPH}}").join(u)).split("{{AIR_QUALITY_TYPE}}").join(n.report[c].type)).split("{{AIR_QUALITY_VALUE}}").join(n.report[c].value)).split("{{AIR_QUALITY_UNITS}}").join(n.report[c].units))}return(t=t.replace("{{AIR_QUALITY_LOCATION}}",l)).replace("{{AIR_QUALITY_DYNAMIC}}",p)}}("undefined"==typeof exports?this.SB.spec.parsers:exports),function(e){"use strict";e.debug=function(e,t,i,n,s,o){var r=(new Date).getTime(),a="",c="",l=0,p=0,u=0,S=0,d=0,E=function(e){return e="object"==typeof SB&&"object"==typeof SB.util?SB.util.translate(e,"debug"):require(__dirname+"/../../lib/sharedUtil").util.translate(e,"debug",o)},B=function(e){return"object"==typeof SB&&"object"==typeof SB.util?SB.util.displayRelativeTime(e):require(__dirname+"/../../lib/sharedUtil").util.displayRelativeTime(e)},f=E("NA");n&&(a=B(n.uptime),c=B((r-n.startup)/1e3),l=n.memoryUsed,p=n.totalMemory,u=n.percentMemory,S=Math.round(100*n.cpuLoad[0]),d=n.clientCount,f=n.temperature?n.temperature:f);var T;return t=(t=(t=(t=(t=(t=(t=(t=(t=t.replace("{{DEBUG_UPDATE}}",(T=r,"object"==typeof SB&&"object"==typeof SB.util?SB.util.displayTime(T,E):require(__dirname+"/../../lib/sharedUtil").util.displayTime(T,E)))).replace("{{DEBUG_UPTIME}}",a)).replace("{{DEBUG_RUNTIME}}",c)).replace("{{DEBUG_MEMORY_USED}}",l)).replace("{{DEBUG_SYSTEM_MEMORY}}",p)).replace("{{DEBUG_MEMORY_PERCENT}}",u)).replace("{{DEBUG_CPU}}",S)).replace("{{DEBUG_CLIENT_TEMP}}",f)).replace("{{DEBUG_CLIENT_COUNT}}",d)}}("undefined"==typeof exports?this.SB.spec.parsers:exports),function(e){"use strict";e.denon=function(e,t,i,n){return n&&(t=t.split("{{DEVICE_POWER}}").join(n.power),n.ZONE1&&(t=(t=(t=(t=(t=(t=t.split("{{DEVICE_Z1_INPUT}}").join(n.ZONE1.input)).split("{{DEVICE_Z1_POWER}}").join(n.ZONE1.power)).split("{{DEVICE_Z1_MUTE}}").join(n.ZONE1.mute)).split("{{DEVICE_Z1_MODE}}").join(n.ZONE1.mode)).split("{{DEVICE_Z1_VOLUME}}").join(n.ZONE1.volume)).split("{{DEVICE_Z1_MAXVOLUME}}").join(n.ZONE1.maxvolume)),n.ZONE2&&(t=(t=(t=t.split("{{DEVICE_Z2_POWER}}").join(n.ZONE2.power)).split("{{DEVICE_Z2_INPUT}}").join(n.ZONE2.input)).split("{{DEVICE_Z2_VOLUME}}").join(n.ZONE2.volume)),n.ZONE3&&(t=(t=(t=t.split("{{DEVICE_Z3_POWER}}").join(n.ZONE3.power)).split("{{DEVICE_Z3_INPUT}}").join(n.ZONE3.input)).split("{{DEVICE_Z3_VOLUME}}").join(n.ZONE3.volume))),t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=t.split("{{DEVICE_POWER}}").join("")).split("{{DEVICE_Z1_POWER}}").join("")).split("{{DEVICE_Z1_INPUT}}").join("")).split("{{DEVICE_Z1_MUTE}}").join("")).split("{{DEVICE_Z1_MODE}}").join("")).split("{{DEVICE_Z1_VOLUME}}").join("")).split("{{DEVICE_Z1_MAXVOLUME}}").join("")).split("{{DEVICE_Z2_POWER}}").join("")).split("{{DEVICE_Z2_INPUT}}").join("")).split("{{DEVICE_Z2_VOLUME}}").join("")).split("{{DEVICE_Z3_POWER}}").join("")).split("{{DEVICE_Z3_INPUT}}").join("")).split("{{DEVICE_Z3_VOLUME}}").join("")}}("undefined"==typeof exports?this.SB.spec.parsers:exports),function(e){"use strict";e.foscam=function(e,t,i,n,s,o){var r,a,c,l,p,u,S,d=s.photo,E=s.photos,B=s.video,f=s.videos,T=s.thumb,m="",C="",_="",g="",v="",A=0,I=0,h=function(e){return e="object"==typeof SB&&"object"==typeof SB.util?SB.util.translate(e,"foscam"):require(__dirname+"/../../lib/sharedUtil").util.translate(e,"foscam",o)};if("on"===(n=n||{}).alarm?(m=" device-active",_=h("CAMERA_ARMED")):"off"===n.alarm&&(C=" device-active",_=h("CAMERA_DISARMED")),t=(t=(t=(t=(t=(t=(t=t.split("{{DEVICE_STATE_ON}}").join(m)).split("{{DEVICE_STATE_OFF}}").join(C)).split("{{ARMED_STATUS}}").join(_)).split("{{DISARMED_STATUS}}").join(_)).split("{{SCREENSHOT}}").join(h("SCREENSHOT"))).split("{{THUMBNAIL}}").join(h("THUMBNAIL"))).split("{{VIDEO}}").join(h("VIDEO")),n.photos&&n.photos.length){for(;A]+)>)/gi,""):e},stripControl:function(e){return"string"==typeof e?e.replace(new RegExp("[\0-]+","g"),""):e},sanitize:function(t){return e.util.stripTags(e.util.stripControl(t))},encodeName:function(e){return e&&"string"==typeof e&&(e=e.replace(/[\s!@#$%^&*()"'\\<>,;.:/+]/g,"_").toLowerCase()),e},translate:function(e,t,i){var n,s="";return e&&"string"==typeof e?(e=e.toUpperCase(),"object"==typeof SB?(i=document.documentElement.getAttribute("lang"),n=function(e,t){var i=SB.getByClass(t,SB.getByTag("main")[0],"section")[0];return e=e.replace("{{i18n_","").replace("}}",""),i.dataset&&i.dataset["string"+e.charAt(0)+e.substr(1).toLowerCase()]?e=i.dataset["string"+e.charAt(0)+e.substr(1).toLowerCase()]:SB.spec.strings[e]&&(e=SB.spec.strings[e]),e}):n=require(__dirname+"/translate").translate,s=n("{{i18n_"+e+"}}",t,i)):s=e,s},arrayList:function(t,i,n){for(var s="",o=0;o=i[t]&&(n[t]=Math.floor(e/i[t]),n[t]<10&&(n[t]="0"+n[t]),e-=n[t]*i[t]);return n.day+" "+n.hour+":"+n.minute+":"+n.second},displayTime:function(e,t,i,n){var s=n||new Date(e),o=s.getDate(),r=s.getDay(),a=s.getHours(),c=s.getMinutes(),l=t("am"),p="";switch(a>12&&(a-=12,l=t("pm")),a=0===a?12:a,c<10&&(c="0"+c),r=t({0:"sun",1:"mon",2:"tue",3:"wed",4:"thur",5:"fri",6:"sat"}[r]),i){case"long":p=r+" "+o+" @ "+a+":"+c+l;break;default:p=r+" @ "+a+":"+c+l}return p},fToC:function(e){return Math.round((e-32)/1.8*10)/10},cToF:function(e){return Math.round(10*(1.8*e+32))/10}}}("undefined"==typeof exports?this.SB:exports); \ No newline at end of file diff --git a/lib/ai.js b/lib/ai.js index 33a8ccd..d1ffca4 100644 --- a/lib/ai.js +++ b/lib/ai.js @@ -35,7 +35,7 @@ module.exports = (function () { var PROCESSED = null; return { - version : 20171007, + version : 20180110, /** * Find all unprocessed, raw DB files that need to be processed. @@ -257,9 +257,16 @@ module.exports = (function () { if (subdeviceId === currentState.value.devices[i].label) { now = new Date().getTime(); - if ((currentState.value.devices[i].lastOn + (eventCooldownMinutes * 60000) > now) && - (!currentState.value.devices[i].readOnly)) { - returnState = currentState.value.devices[i]; + if (!currentState.value.devices[i].readOnly) { + if (((!currentState.value.devices[i].lastOn) && (!currentState.value.devices[i].lastOff)) || + ((currentState.value.devices[i].lastOn) && (now > currentState.value.devices[i].lastOn + (eventCooldownMinutes * 60000))) || + ((currentState.value.devices[i].lastOff) && (now > currentState.value.devices[i].lastOff + (eventCooldownMinutes * 60000)))) { + returnState = currentState.value.devices[i]; + } + + else { + console.log('\x1b[35mAI\x1b[0m: Ignoring ' + currentState.value.devices[i].label + ' since it recently changed state.'); + } } break; @@ -315,17 +322,27 @@ module.exports = (function () { if (values[valueType]) { confidence = parseInt(((values[valueType] / total) * 100), 10); - if (confidence > config.ai.confidence) { - // Only indicate intent if it differs from the current - // state or is not read only. - intendedDevice = this.findDevice(deviceId, subdeviceId, controllers, eventCooldownMinutes); + // Only indicate intent if it differs from the current + // state or is not read only. + intendedDevice = this.findDevice(deviceId, subdeviceId, controllers, eventCooldownMinutes); + if (confidence > config.ai.confidence) { if ((intendedDevice) && (intendedDevice.state) && (intendedDevice.state !== valueType)) { + console.log('\x1b[35mAI\x1b[0m: Intent available with ' + confidence + '% confidence that ' + subdeviceId + ' should be ' + valueType + '.'); + intent.push({ device : device, subdevice : subdeviceId, command : valueType, confidence : confidence }); } + + else if ((intendedDevice) && (intendedDevice.state)) { + console.log('\x1b[35mAI\x1b[0m: Already set, but ' + confidence + '% confident ' + subdeviceId + ' should be ' + valueType + '.'); + } + } + + else if (intendedDevice) { + console.log('\x1b[35mAI\x1b[0m: Only ' + confidence + '% confident ' + subdeviceId + ' should be ' + valueType + '.'); } } } @@ -335,6 +352,10 @@ module.exports = (function () { } } } + + else { + console.log('\x1b[35mAI\x1b[0m: Only ' + summary.count + ' events for ' + deviceId + ' ' + command + '.'); + } } return intent; diff --git a/lib/deviceState.js b/lib/deviceState.js index dca454c..fa16d39 100644 --- a/lib/deviceState.js +++ b/lib/deviceState.js @@ -47,13 +47,14 @@ module.exports = (function () { }, findDeviceOnTime : function (deviceId, newState, oldState) { - var now = Math.round(new Date().getTime() / 1000), + var now = new Date().getTime(), i = 0, device; if (oldState.state !== newState.state) { if (newState.state === 'ok') { - State[deviceId].lastOn = now; + State[deviceId].lastOn = now; + State[deviceId].lastOff = null; } else if (newState.state === 'err') { @@ -62,7 +63,8 @@ module.exports = (function () { State[deviceId].duration += (now - State[deviceId].lastOn); } - State[deviceId].lastOn = null; + State[deviceId].lastOn = null; + State[deviceId].lastOff = now; } } @@ -72,7 +74,8 @@ module.exports = (function () { if ((oldState.value.devices[i]) && (oldState.value.devices[i].state !== device.state)) { if (device.state === 'on') { - State[deviceId].value.devices[i].lastOn = now; + State[deviceId].value.devices[i].lastOn = now; + State[deviceId].value.devices[i].lastOff = null; } else if (device.state === 'off') { @@ -81,7 +84,8 @@ module.exports = (function () { State[deviceId].value.devices[i].duration += (now - State[deviceId].value.devices[i].lastOn); } - State[deviceId].value.devices[i].lastOn = null; + State[deviceId].value.devices[i].lastOff = now; + State[deviceId].value.devices[i].lastOn = null; } } } @@ -98,7 +102,7 @@ module.exports = (function () { */ updateState : function (deviceId, typeClass, config) { var webSockets = require(__dirname + '/webSockets'), - now = Math.round(new Date().getTime() / 1000), + now = new Date().getTime(), oldState; if (deviceId) { diff --git a/lib/staticAssets.js b/lib/staticAssets.js index 27290c1..659fffe 100644 --- a/lib/staticAssets.js +++ b/lib/staticAssets.js @@ -31,7 +31,7 @@ module.exports = (function () { 'use strict'; return { - version : 20161027, + version : 20180109, mimeTypes : { '.html' : 'text/html', '.css' : 'text/css', @@ -232,10 +232,11 @@ module.exports = (function () { params.push('req'); params.push('-x509'); params.push('-nodes'); + params.push('-sha256'); params.push('-days'); params.push('9990'); params.push('-newkey'); - params.push('rsa:2048'); + params.push('rsa:4096'); params.push('-keyout'); params.push('cache/key.pem'); params.push('-out'); diff --git a/lib/translate.js b/lib/translate.js index e8e4393..52e30b2 100644 --- a/lib/translate.js +++ b/lib/translate.js @@ -36,7 +36,7 @@ module.exports = (function () { * Accept a string and return that string without punctation. */ stripPunctuation : function (input) { - return input.replace(/[:;.,!?]/g, ''); + return input.replace(/[:;.,!?\/#$%\^&\*{}=\-_`~()]/g, ''); }, /**