From 95fc9e25f3ce6f25ae5e3e0ef65225b3ff9c033c Mon Sep 17 00:00:00 2001 From: imbrianj Date: Sun, 29 Oct 2017 21:18:11 -0700 Subject: [PATCH] More clear error messaging on missing config file. Parse weather API data and sanitize / properly convert to metric if necessary --- app.js | 2 +- cache/version.txt | 2 +- devices/weather/controller.js | 44 ++++++++++++++++++------ js/combo.min.js | 2 +- tests/unit/devices/weather/controller.js | 36 +++++++++++++++++++ 5 files changed, 73 insertions(+), 13 deletions(-) diff --git a/app.js b/app.js index 0304d0c..f75f36a 100644 --- a/app.js +++ b/app.js @@ -110,7 +110,7 @@ fs.stat(configFile, function (err, data) { cert; if (err) { - console.log('\x1b[31mError\x1b[0m: No controllers found. Is your config file setup correctly?'); + console.log('\x1b[31mError\x1b[0m: Config file could not be read. Ensure you edit the config/config.js or provide the correct path.'); process.exit(1); } diff --git a/cache/version.txt b/cache/version.txt index 7e4d213..9058b9b 100644 --- a/cache/version.txt +++ b/cache/version.txt @@ -1 +1 @@ -1509238927989 \ No newline at end of file +1509336842320 \ No newline at end of file diff --git a/devices/weather/controller.js b/devices/weather/controller.js index a9512f9..2ec315c 100644 --- a/devices/weather/controller.js +++ b/devices/weather/controller.js @@ -29,7 +29,7 @@ module.exports = (function () { * @fileoverview Basic weather information, courtesy of Yahoo. */ return { - version : 20151207, + version : 20171029, readOnly: true, @@ -84,6 +84,29 @@ module.exports = (function () { return Math.round((f - 32) / 1.8); }, + /** + * Accept a raw forecast object and celsius flag. Parse through, sanitizing + * and converting values as necessary. + */ + formatForecast : function (forecast, celsius) { + var util = require(__dirname + '/../../lib/sharedUtil').util, + formatted = [], + i = 0; + + for (i; i < forecast.length; i += 1) { + formatted.push({ + code : util.sanitize(forecast[i].code), + date : util.sanitize(forecast[i].date), + day : util.sanitize(forecast[i].day), + high : util.sanitize(celsius ? this.fToC(forecast[i].high) : parseInt(forecast[i].high, 10)), + low : util.sanitize(celsius ? this.fToC(forecast[i].low) : parseInt(forecast[i].low, 10)), + text : util.sanitize(forecast[i].text) + }); + } + + return formatted; + }, + /** * Accept sunrise and sunset times as deliverd from the API - and determine * what the current sun phase is. Can either be "Day" or "Night". @@ -147,7 +170,8 @@ module.exports = (function () { }); response.once('end', function () { - var weatherData = {}, + var util = require(__dirname + '/../../lib/sharedUtil').util, + weatherData = {}, errMessage = 'err', temp, data, @@ -174,14 +198,14 @@ module.exports = (function () { temp = that.fToC(temp); } - weatherData = { 'city' : city.location.city, - 'temp' : temp, - 'text' : city.item.condition.text, - 'humidity' : city.atmosphere.humidity, - 'sunrise' : city.astronomy.sunrise, - 'sunset' : city.astronomy.sunset, - 'code' : city.item.condition.code, - 'forecast' : city.item.forecast, + weatherData = { 'city' : util.sanitize(city.location.city), + 'temp' : util.sanitize(parseInt(temp, 10)), + 'text' : util.sanitize(city.item.condition.text), + 'humidity' : util.sanitize(parseInt(city.atmosphere.humidity, 10)), + 'sunrise' : util.sanitize(city.astronomy.sunrise), + 'sunset' : util.sanitize(city.astronomy.sunset), + 'code' : util.sanitize(parseInt(city.item.condition.code, 10)), + 'forecast' : that.formatForecast(city.item.forecast, weather.celsius), 'phase' : that.findSunPhase(city.astronomy.sunrise, city.astronomy.sunset) }; } diff --git a/js/combo.min.js b/js/combo.min.js index c2ced41..4cff388 100644 --- a/js/combo.min.js +++ b/js/combo.min.js @@ -1,3 +1,3 @@ -/* 20171028 */ +/* 20171029 */ 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;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),o;o3&&(n=650),i>10&&(n=500),i+=1,setTimeout(E,n))},B=function(e,i){var n,s=d(e);i?(SB.vibrate(),t=SB.getTarget(e).parentNode.parentNode.id,(p=SB.transcribe(S)).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,E()))};"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,d(e)&&(r=!1,c=parseInt(e.changedTouches[0].clientX,10),l=parseInt(e.changedTouches[0].clientY,10),setTimeout(function(){a||(s=!1,B(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,B(e)),u()}),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)&&u()}),SB.event.add(SB.spec.uiComponents.body,"touchcancel",function(e){u()})),SB.event.add(SB.spec.uiComponents.body,"mousedown",function(e){SB.hasClass(SB.getTarget(e).parentNode,"emoji")?B(e,!0):!1===o&&!1===s&&(r=!1,B(e)),s=!1}),SB.event.add(SB.spec.uiComponents.body,"mouseup",function(e){SB.hasClass(SB.getTarget(e).parentNode,"emoji")||u()}),SB.event.add(SB.spec.uiComponents.body,"click",function(e){d(e)&&e.preventDefault()}),SB.event.add(SB.spec.uiComponents.body,"keyup",function(e){13===e.keyCode&&d(e)&&(r=!1,B(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)};return i&&n&&(c=function(e){return"object"==typeof SB&&"object"==typeof SB.util?SB.util.arrayList(e,"activeBuilding",s):require(__dirname+"/../../lib/sharedUtil").util.arrayList(e,"activeBuilding",s)}(n)),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.replace("{{ACTIVEBUILDING_STATE}}",r),t=t.replace("{{ACTIVEBUILDING_ICON}}",a),t=t.replace("{{ACTIVEBUILDING_DYNAMIC}}",o)}}("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");return 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),t=t.replace("{{DEBUG_UPDATE}}",function(e){return"object"==typeof SB&&"object"==typeof SB.util?SB.util.displayTime(e,E):require(__dirname+"/../../lib/sharedUtil").util.displayTime(e,E)}(r)),t=t.replace("{{DEBUG_UPTIME}}",a),t=t.replace("{{DEBUG_RUNTIME}}",c),t=t.replace("{{DEBUG_MEMORY_USED}}",l),t=t.replace("{{DEBUG_SYSTEM_MEMORY}}",p),t=t.replace("{{DEBUG_MEMORY_PERCENT}}",u),t=t.replace("{{DEBUG_CPU}}",S),t=t.replace("{{DEBUG_CLIENT_TEMP}}",f),t=t.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.split("{{DEVICE_POWER}}").join(""),t=t.split("{{DEVICE_Z1_POWER}}").join(""),t=t.split("{{DEVICE_Z1_INPUT}}").join(""),t=t.split("{{DEVICE_Z1_MUTE}}").join(""),t=t.split("{{DEVICE_Z1_MODE}}").join(""),t=t.split("{{DEVICE_Z1_VOLUME}}").join(""),t=t.split("{{DEVICE_Z1_MAXVOLUME}}").join(""),t=t.split("{{DEVICE_Z2_POWER}}").join(""),t=t.split("{{DEVICE_Z2_INPUT}}").join(""),t=t.split("{{DEVICE_Z2_VOLUME}}").join(""),t=t.split("{{DEVICE_Z3_POWER}}").join(""),t=t.split("{{DEVICE_Z3_INPUT}}").join(""),t=t.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,m=s.thumb,T="",C="",_="",g="",v="",I=0,A=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?(T=" device-active",_=h("CAMERA_ARMED")):"off"===n.alarm&&(C=" device-active",_=h("CAMERA_DISARMED")),t=t.split("{{DEVICE_STATE_ON}}").join(T),t=t.split("{{DEVICE_STATE_OFF}}").join(C),t=t.split("{{ARMED_STATUS}}").join(_),t=t.split("{{DISARMED_STATUS}}").join(_),t=t.split("{{SCREENSHOT}}").join(h("SCREENSHOT")),t=t.split("{{THUMBNAIL}}").join(h("THUMBNAIL")),t=t.split("{{VIDEO}}").join(h("VIDEO")),n.photos&&n.photos.length){for(I;I]+)>)/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){var s="",o=0;for(o;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={0:"sun",1:"mon",2:"tue",3:"wed",4:"thur",5:"fri",6:"sat"},c=s.getHours(),l=s.getMinutes(),p=t("am"),u="";switch(c>12&&(c-=12,p=t("pm")),c=0===c?12:c,l<10&&(l="0"+l),r=t(a[r]),i){case"long":u=r+" "+o+" @ "+c+":"+l+p;break;default:u=r+" @ "+c+":"+l+p}return u}}}("undefined"==typeof exports?this.SB:exports); \ No newline at end of file diff --git a/tests/unit/devices/weather/controller.js b/tests/unit/devices/weather/controller.js index da34940..1f0484e 100644 --- a/tests/unit/devices/weather/controller.js +++ b/tests/unit/devices/weather/controller.js @@ -70,6 +70,42 @@ exports.weatherControllerTest = { test.deepEqual(weatherController.fToC(57), 14); + test.done(); + }, + + formatForecast : function (test) { + 'use strict'; + + var weatherController = require(__dirname + '/../../../../devices/weather/controller'), + forecast = [{ + code : '10', + date : '30 Oct 2017', + day : 'Mon', + high : '58', + low : '44', + text : 'Partly Cloudy' + }, + { + code : '11', + date : '31 Oct 2017', + day : 'Tue', + high : '55', + low : '40', + text : 'Spooky' + }], + results = weatherController.formatForecast(forecast, false), + metricResults = weatherController.formatForecast(forecast, true); + + test.strictEqual(results[0].high, 58); + test.strictEqual(results[0].low, 44); + test.strictEqual(results[1].high, 55); + test.strictEqual(results[1].low, 40); + + test.strictEqual(metricResults[0].high, 14); + test.strictEqual(metricResults[0].low, 7); + test.strictEqual(metricResults[1].high, 13); + test.strictEqual(metricResults[1].low, 4); + test.done(); } };