From ebce1b20311dd3e7ab9d4d5dfe26841e1c009290 Mon Sep 17 00:00:00 2001 From: imbrianj Date: Mon, 19 Feb 2018 16:18:45 -0800 Subject: [PATCH] If alarm system goes off, send photo along with the message (desktop notification + pushover). Still bug in pushover, image corrupted - but getting close --- apps/homeWatch.js | 50 ++++++++++--- cache/version.txt | 2 +- config/config.js | 2 +- devices/clientNotify/controller.js | 4 +- devices/foscam/controller.js | 95 +++++++++++++++---------- js/combo.min.js | 2 +- lib/notify.js | 12 +++- tests/unit/devices/foscam/controller.js | 1 + 8 files changed, 114 insertions(+), 54 deletions(-) diff --git a/apps/homeWatch.js b/apps/homeWatch.js index 2cf14bd..fae9685 100644 --- a/apps/homeWatch.js +++ b/apps/homeWatch.js @@ -32,7 +32,7 @@ module.exports = (function () { 'use strict'; return { - version : 20161101, + version : 20180219, homeWatch : function (deviceId, command, controllers, values, config) { var notify = require(__dirname + '/../lib/notify'), @@ -54,33 +54,65 @@ module.exports = (function () { return found; }, - isSecure = function () { + isArmed = function () { var deviceState = require(__dirname + '/../lib/deviceState'), smartThingsState = deviceState.getDeviceState(deviceId), - isSecure = false; + isArmed = false; if ((smartThingsState) && (smartThingsState.value) && (smartThingsState.value.mode)) { if (secureModes.indexOf(smartThingsState.value.mode) !== -1) { - isSecure = true; + isArmed = true; } } - return isSecure; + return isArmed; + }, + findCamera = function() { + var device, + camera; + + for (device in controllers) { + if ((controllers[device]) && (controllers[device].config) && (controllers[device].config.typeClass === 'foscam')) { + camera = device; + break; + } + } + + return camera; }; trigger = isIn(command, config.motion, 'subdevice-state-motion-') || isIn(command, config.contact, 'subdevice-state-contact-'); - if (trigger && isSecure()) { + if (trigger && isArmed()) { // We use a delay here of a few seconds, as Presence sensors may take a // few seconds to register and update the mode. We want to avoid false // positives as much as possible. setTimeout(function () { - if(isSecure()) { - console.log('\x1b[35m' + controllers[deviceId].config.title + '\x1b[0m: Home Watch found something suspicious'); + var camera = findCamera(controllers), + runCommand, + callback; + if(isArmed()) { + console.log('\x1b[35m' + controllers[deviceId].config.title + '\x1b[0m: Home Watch found something suspicious'); message = translate.translate('{{i18n_HOME_WATCH}}', 'smartthings', controllers.config.language).split('{{LABEL}}').join(trigger); - notify.notify(message, controllers, deviceId); + + if (camera) { + runCommand = require(__dirname + '/../lib/runCommand'); + + callback = function(err, dataReply) { + var rawImage = dataReply.rawImage, + fileName = dataReply.fileName; + + notify.notify(message, controllers, camera, fileName, rawImage); + }; + + runCommand.runCommand(camera, 'TAKE', deviceId, null, null, callback); + } + + else { + notify.notify(message, controllers, deviceId); + } } }, delay); } diff --git a/cache/version.txt b/cache/version.txt index 9e38565..80abbd5 100644 --- a/cache/version.txt +++ b/cache/version.txt @@ -1 +1 @@ -1518939337696 \ No newline at end of file +1519085681168 \ No newline at end of file diff --git a/config/config.js b/config/config.js index 0d178fd..ebf0b84 100644 --- a/config/config.js +++ b/config/config.js @@ -483,7 +483,7 @@ exports.config = { motion : ['Living Room Motion'], secureModes : ['Away'], delay : 15, - controllerIds : ['pushover', 'sms', 'clientSpeech', 'clientNotify', 'gerty'] } + controllerIds : ['foscam', 'pushover', 'sms', 'clientSpeech', 'clientNotify', 'gerty'] } }, */ className : { Goblin : 'fa-female' }, diff --git a/devices/clientNotify/controller.js b/devices/clientNotify/controller.js index a26b1e7..2a6cce4 100644 --- a/devices/clientNotify/controller.js +++ b/devices/clientNotify/controller.js @@ -29,7 +29,7 @@ module.exports = (function () { * clients. */ return { - version : 20151007, + version : 20180219, inputs : ['text'], @@ -58,7 +58,7 @@ module.exports = (function () { clientNotify = {}; clientNotify.options = {}; - clientNotify.options.icon = config.device.image || '/images/icons/apple-touch-icon.png'; + clientNotify.options.icon = config.payload || '/images/icons/apple-touch-icon.png'; clientNotify.options.body = config.text || ''; clientNotify.title = config.device.title || 'SwitchBoard'; clientNotify.deviceId = config.source || ''; diff --git a/devices/foscam/controller.js b/devices/foscam/controller.js index 8921eb5..43837b3 100644 --- a/devices/foscam/controller.js +++ b/devices/foscam/controller.js @@ -29,7 +29,7 @@ module.exports = (function () { * @fileoverview Basic control of Foscam IP camera. */ return { - version : 20180214, + version : 20180219, inputs : ['command', 'list'], @@ -106,6 +106,32 @@ module.exports = (function () { return clean; }, + getRawPhoto : function (foscam, fileName) { + var title = foscam.title, + http = require('http'), + postData = this.postPrepare(foscam), + imageUrl = 'http://' + postData.host + ':' + postData.port + postData.path, + request, + dataReply = '', + path = '/images/foscam/photos/'; + + request = http.request(imageUrl).on('response', function (response) { + response.setEncoding('binary'); + + response.on('data', function (response) { + dataReply += response; + }); + + response.once('end', function () { + console.log('\x1b[35m' + title + '\x1b[0m: Read raw image'); + + foscam.callback(null, { rawImage : dataReply, fileName : path + fileName }, true); + }); + }); + + request.end(); + }, + /** * Grab all thumbnail path and assume corresponding screenshot and videos. */ @@ -162,7 +188,7 @@ module.exports = (function () { getStoredPhotos : function (foscam) { var fs = require('fs'), deviceState = require(__dirname + '/../../lib/deviceState'), - path = 'images/foscam/photos', + path = '/images/foscam/photos/', that = this, photos = [], filename; @@ -182,7 +208,7 @@ module.exports = (function () { if (filename.indexOf(foscam.deviceId + '-') === 0) { photos.push({ name : filename, - photo : path + '/' + filename + '.jpg' + photo : path + filename + '.jpg' }); } } @@ -243,6 +269,10 @@ module.exports = (function () { var http = require('http'), fs = require('fs'), that = this, + foscam = {}, + dataReply = '', + request, + title = config.device.title, now = new Date(), year = now.getFullYear(), month = ('0' + (now.getMonth() + 1)).slice(-2), @@ -251,14 +281,11 @@ module.exports = (function () { minute = ('0' + (now.getMinutes())).slice(-2), second = ('0' + (now.getSeconds())).slice(-2), localPath = __dirname + '/../../images/foscam/photos/', - fileName = localPath + '/' + config.device.deviceId + '-' + year + '-' + month + '-' + day + '-' + hour + '-' + minute + '-' + second + '.jpg', - imageUrl = '', - foscam = {}, - dataReply = '', - request, - title = config.device.title; + fileName = config.device.deviceId + '-' + year + '-' + month + '-' + day + '-' + hour + '-' + minute + '-' + second + '.jpg', + callback = config.callback || function () {}; foscam.deviceId = config.device.deviceId; + foscam.title = config.device.title; foscam.deviceIp = config.device.deviceIp; foscam.timeout = config.device.localTimeout || config.config.localTimeout; foscam.username = config.device.username; @@ -267,40 +294,34 @@ module.exports = (function () { foscam.command = config.command || ''; foscam.list = config.list || ''; foscam.devicePort = config.device.devicePort || 80; - foscam.callback = config.callback || function () {}; + foscam.callback = callback; if (foscam.list) { this.getStoredVideos(foscam); } - else if (foscam.command === 'TAKE') { - fs.stat(fileName, function(err, data) { - var http, - postData; - + if (foscam.command === 'TAKE') { + fs.stat(localPath + fileName, function(err, data) { if (err) { - http = require('http'); - postData = that.postPrepare(foscam); - imageUrl = 'http://' + postData.host + ':' + postData.port + postData.path; - request = http.request(imageUrl).on('response', function (response) { - response.setEncoding('binary'); - - response.on('data', function (response) { - dataReply += response; - }); - - response.once('end', function () { - console.log('\x1b[35m' + title + '\x1b[0m: Saved image for ' + fileName); - - fs.writeFile(fileName, dataReply, 'binary', function(err) { - if (err) { - console.log('\x1b[31m' + title + '\x1b[0m: Unable to save ' + fileName); - } - }); - }); - }); - - request.end(); + foscam.callback = function(err, dataReply) { + var rawImage = dataReply.rawImage; + + if (rawImage) { + fs.writeFile(localPath + fileName, rawImage, 'binary', function(err) { + if (err) { + console.log('\x1b[31m' + title + '\x1b[0m: Unable to save ' + fileName); + } + + else { + console.log('\x1b[35m' + title + '\x1b[0m: Image saved as ' + fileName); + } + }); + } + + callback(err, dataReply); + }; + + that.getRawPhoto(foscam, fileName); } else if (data) { diff --git a/js/combo.min.js b/js/combo.min.js index f811491..053259d 100644 --- a/js/combo.min.js +++ b/js/combo.min.js @@ -1,3 +1,3 @@ -/* 20180216 */ +/* 20180219 */ 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 for(o in s=SB.getByTag(i,t))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)for(n in SB.log("Received","State","success"),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)for(t in 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),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)for(e in SB.spec.uiComponents.templates=SB.decode(s),o=SB.decode(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="",p=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&&(o=n,l="object"==typeof SB&&"object"==typeof SB.util?SB.util.arrayList(o,"activeBuilding",s):require(__dirname+"/../../lib/sharedUtil").util.arrayList(o,"activeBuilding",s)),n&&0!==n.length?1===n.length?(a="ok",c="tag",r=p("SINGLE_PACKAGE")):n.length>1&&(a="ok",c="tags",r=p("PLURAL_PACKAGES")):(a="err",c="times",r=p("NO_PACKAGES")),r=r.split("{{SENDERS}}").join(l),t=(t=(t=t.replace("{{ACTIVEBUILDING_STATE}}",a)).replace("{{ACTIVEBUILDING_ICON}}",c)).replace("{{ACTIVEBUILDING_DYNAMIC}}",r)}}("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)for(c in l=n.location,n.report)n.report.hasOwnProperty(c)&&(u="",(o=n.report[c].max)&&(u=(u=a.split("{{MAX_VALUE}}").join(o)).split("{{PERCENT_QUALITY}}").join(Math.round(n.report[c].value/o*100))),p=(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)).split("{{AIR_QUALITY_HIGH}}").join(n.report[c].high));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,a=(new Date).getTime(),c="",l="",p=0,u=0,S=0,d=0,E=0,B=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)},f=function(e){return"object"==typeof SB&&"object"==typeof SB.util?SB.util.displayRelativeTime(e):require(__dirname+"/../../lib/sharedUtil").util.displayRelativeTime(e)},T=B("NA");return n&&(c=f(n.uptime),l=f((a-n.startup)/1e3),p=n.memoryUsed,u=n.totalMemory,S=n.percentMemory,d=Math.round(100*n.cpuLoad[0]),E=n.clientCount,T=n.temperature?n.temperature:T),t=(t=(t=(t=(t=(t=(t=(t=(t=t.replace("{{DEBUG_UPDATE}}",(r=a,"object"==typeof SB&&"object"==typeof SB.util?SB.util.displayTime(r,B):require(__dirname+"/../../lib/sharedUtil").util.displayTime(r,B)))).replace("{{DEBUG_UPTIME}}",c)).replace("{{DEBUG_RUNTIME}}",l)).replace("{{DEBUG_MEMORY_USED}}",p)).replace("{{DEBUG_SYSTEM_MEMORY}}",u)).replace("{{DEBUG_MEMORY_PERCENT}}",S)).replace("{{DEBUG_CPU}}",d)).replace("{{DEBUG_CLIENT_TEMP}}",T)).replace("{{DEBUG_CLIENT_COUNT}}",E)}}("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="",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?(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(;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){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/notify.js b/lib/notify.js index 2978dd6..3d022f8 100644 --- a/lib/notify.js +++ b/lib/notify.js @@ -30,14 +30,14 @@ module.exports = (function () { 'use strict'; return { - version : 20160314, + version : 20180218, /** * Accepts a message and object comprised of device controllers. If any of * the controllers are conidered a notification means, the message is sent * to them. */ - notify : function (message, controllers, source) { + notify : function (message, controllers, source, fileName, payload) { var runCommand = require(__dirname + '/../lib/runCommand'), deviceId; @@ -45,14 +45,20 @@ module.exports = (function () { if ((controllers[deviceId].config) && (deviceId !== source)) { switch (controllers[deviceId].config.typeClass) { case 'clientNotify' : + runCommand.runCommand(deviceId, 'text-' + message, source, null, null, null, null, fileName); + break; + case 'clientSpeech' : - case 'pushover' : case 'sms' : case 'speech' : case 'gerty' : runCommand.runCommand(deviceId, 'text-' + message, source); break; + case 'pushover' : + runCommand.runCommand(deviceId, 'text-' + message, source, null, null, null, null, payload); + break; + case 'clientVibrate' : runCommand.runCommand(deviceId, 'text-5', source); break; diff --git a/tests/unit/devices/foscam/controller.js b/tests/unit/devices/foscam/controller.js index 1309724..b43cb71 100644 --- a/tests/unit/devices/foscam/controller.js +++ b/tests/unit/devices/foscam/controller.js @@ -97,6 +97,7 @@ exports.foscamControllerTest = { controller : { inputs : ['state'], send : function (request) { return request; } }, config : { deviceId : 'FOO', + title : 'Foscam', deviceIp : '127.0.0.1', username : 'USERNAME', password : 'PASSWORD' } } };