var fs = require("fs") module.exports = function(s,config,lang,getSnapshot){ if(config.mqttClient === true){ console.log('Loading MQTT Outbound Connectivity...') const mqtt = require('mqtt') const { getEventBasedRecordingUponCompletion, } = require('../events/utils.js')(s,config,lang) try{ function createMqttSubscription(options){ let mqttEndpoint = options.host const username = options.username || '' const password = options.password || '' const clientId = options.clientId || `shinobi_${Math.random().toString(16).substr(2, 8)}` const subKey = options.subKey const pubKey = options.pubKey const groupKey = options.ke const onData = options.onData || function(){} function mqttUserLog(type,data){ s.userLog({ ke: groupKey, mid: '$USER' },{ type: type, msg: data }) } if(mqttEndpoint.indexOf('://') === -1){ mqttEndpoint = `mqtt://${mqttEndpoint}` } mqttUserLog('Connecting... ' + mqttEndpoint) const client = mqtt.connect(mqttEndpoint,{ clean: true, username: username, password: password, clientId: clientId, reconnectPeriod: 10000, // 10 seconds }); client.on('reconnect', (e) => mqttUserLog(`MQTT Reconnected`)) client.on('disconnect', (e) => mqttUserLog(`MQTT Disconnected`)) client.on('offline', (e) => mqttUserLog(`MQTT Offline`)) client.on('error', (e) => mqttUserLog(`MQTT Error`,e)) client.on('connect', function () { mqttUserLog('Connected! ' + mqttEndpoint) client.subscribe(pubKey, function (err) { if (err) { s.debugLog(err) s.userLog({ ke: groupKey, mid: '$USER' },{ type: lang['MQTT Error'], msg: err }) }else{ client.on('message', function (topic, message) { const data = s.parseJSON(message.toString()) onData(data) }) } }) }) return client } function sendToMqttConnections(groupKey,eventName,addedArgs,checkMonitors){ try{ (s.group[groupKey].mqttOutbounderKeys || []).forEach(function(key){ const outBounder = s.group[groupKey].mqttOutbounders[key] const theAction = outBounder.eventHandlers[eventName] if(!theAction)return; if(checkMonitors){ const monitorsToRead = outBounder.monitorsToRead const firstArg = addedArgs[0] const monitorId = firstArg.mid || firstArg.id if(monitorsToRead.indexOf(monitorId) > -1 || monitorsToRead.indexOf('$all') > -1)theAction(...addedArgs); }else{ theAction(...addedArgs) } }) }catch(err){ s.debugLog(err) } } const sendMessage = async function(options,data){ const sendBody = s.stringJSON(data) const groupKey = options.ke const subId = options.subId const publishTo = options.to try{ s.group[groupKey].mqttOutbounders[subId].client.publish(publishTo,sendBody) }catch(err){ s.debugLog('MQTT Error',err) s.userLog({ke:groupKey,mid:'$USER'},{type:lang['MQTT Error'],msg:err}) } } const onEventTriggerBeforeFilter = function(d,filter){ filter.mqttout = false } const onDetectorNoTriggerTimeout = function(e){ if(e.details.detector_notrigger_mqttout === '1'){ const groupKey = e.ke sendToMqttConnections(groupKey,'onDetectorNoTriggerTimeout',[e],true) } } const onEventTrigger = async (d,filter) => { const monitorConfig = s.group[d.ke].rawMonitorConfigurations[d.id] if((filter.mqttout || monitorConfig.details.notify_mqttout === '1') && !s.group[d.ke].activeMonitors[d.id].detector_mqttout){ var detector_mqttout_timeout if(!monitorConfig.details.detector_mqttout_timeout||monitorConfig.details.detector_mqttout_timeout===''){ detector_mqttout_timeout = 1000 * 60 * 10; }else{ detector_mqttout_timeout = parseFloat(monitorConfig.details.detector_mqttout_timeout) * 1000 * 60; } s.group[d.ke].activeMonitors[d.id].detector_mqttout = setTimeout(function(){ clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_mqttout); s.group[d.ke].activeMonitors[d.id].detector_mqttout = null },detector_mqttout_timeout) // const groupKey = d.ke await getSnapshot(d,monitorConfig) sendToMqttConnections(groupKey,'onEventTrigger',[Object.assign({},d,{ screenshotBuffer: d.screenshotBuffer.toString('base64') }),filter],true) } } const onMonitorSave = (monitorConfig) => { const groupKey = monitorConfig.ke sendToMqttConnections(groupKey,'onMonitorSave',[monitorConfig],true) } const onMonitorStart = (monitorConfig) => { const groupKey = monitorConfig.ke sendToMqttConnections(groupKey,'onMonitorStart',[monitorConfig],true) } const onMonitorStop = (monitorConfig) => { const groupKey = monitorConfig.ke sendToMqttConnections(groupKey,'onMonitorStop',[monitorConfig],true) } const onMonitorDied = (monitorConfig) => { const groupKey = monitorConfig.ke sendToMqttConnections(groupKey,'onMonitorDied',[monitorConfig],true) } const onEventBasedRecordingComplete = (response,monitorConfig) => { const groupKey = monitorConfig.ke sendToMqttConnections(groupKey,'onEventBasedRecordingComplete',[monitorConfig],true) } const insertCompletedVideoExtender = (activeMonitor,temp,insertQuery,response) => { const groupKey = insertQuery.ke const monitorId = insertQuery.mid const monitorConfig = s.group[groupKey].rawMonitorConfigurations[monitorId] sendToMqttConnections(groupKey,'insertCompletedVideoExtender',[monitorConfig],true) } const onAccountSave = (activeGroup,userDetails,user) => { const groupKey = user.ke sendToMqttConnections(groupKey,'onAccountSave',[activeGroup,userDetails,user]) } const onUserLog = (logEvent) => { const groupKey = logEvent.ke if(groupKey.indexOf('$') === -1){ sendToMqttConnections(groupKey,'onUserLog',[logEvent]) }else{ s.debugLog(`Failed sendToMqttConnections onUserLog : ${groupKey}`) } } const onTwoFactorAuthCodeNotification = function(user){ const groupKey = user.ke if(user.details.factor_mqttout === '1'){ sendToMqttConnections(groupKey,'onTwoFactorAuthCodeNotification',[user],true) } } const loadMqttListBotForUser = function(user){ const groupKey = user.ke const userDetails = s.parseJSON(user.details); if(!s.group[groupKey].mqttOutbounders)s.group[groupKey].mqttOutbounders = {}; const mqttSubs = s.group[groupKey].mqttOutbounders if(userDetails.mqttout === '1' && Object.keys(mqttSubs).length === 0){ const mqttClientList = userDetails.mqttout_list || [] mqttClientList.forEach(function(row,n){ try{ const mqttSubId = `${row.host} ${row.pubKey}` const message = row.type || [] const eventsToAttachTo = row.msgFor || [] const monitorsToRead = row.monitors || [] mqttSubs[mqttSubId] = { eventHandlers: {} }; mqttSubs[mqttSubId].client = createMqttSubscription({ username: row.username, password: row.password, clientId: row.clientId, host: row.host, pubKey: row.pubKey, ke: groupKey, }); const msgOptions = { ke: groupKey, subId: mqttSubId, to: row.pubKey, } const titleLegend = { onMonitorSave: lang['Monitor Edit'], onMonitorStart: lang['Monitor Start'], onMonitorStop: lang['Monitor Stop'], onMonitorDied: lang['Monitor Died'], onEventTrigger: lang['Event'], insertCompletedVideoExtender: lang['Recording Complete'], onEventBasedRecordingComplete: lang['Event-Based Recording'], onDetectorNoTriggerTimeout: lang['"No Motion" Detector'], onAccountSave: lang['Account Save'], onUserLog: lang['User Log'], onTwoFactorAuthCodeNotification: lang['2-Factor Authentication'], } eventsToAttachTo.forEach(function(eventName){ let theAction = function(){} switch(eventName){ case'insertCompletedVideoExtender': theAction = function(activeMonitor,temp,insertQuery,response){ sendMessage(msgOptions,{ title: titleLegend[eventName], name: eventName, data: insertQuery, time: new Date(), }) } break; case'onEventBasedRecordingComplete': theAction = function(response,monitorConfig){ sendMessage(msgOptions,{ title: titleLegend[eventName], name: eventName, data: { ke: monitorConfig.ke, mid: monitorConfig.mid, response: response, }, time: new Date(), }) } break; case'onEventTrigger': theAction = function(d,filter){ const eventObject = Object.assign({},d) delete(eventObject.frame); sendMessage(msgOptions,{ title: titleLegend[eventName], name: eventName, data: eventObject, time: new Date(), }) } break; case'onAccountSave': theAction = function(activeGroup,userDetails,user){ sendMessage(msgOptions,{ title: titleLegend[eventName], name: eventName, data: { mail: user.mail, ke: user.ke, }, time: new Date(), }) } break; case'userLog': theAction = function(logEvent){ sendMessage(msgOptions,{ title: titleLegend[eventName], name: eventName, data: logEvent, time: new Date(), }) } break; case'onTwoFactorAuthCodeNotification': theAction = function(user){ sendMessage(msgOptions,{ title: titleLegend[eventName], name: eventName, data: { code: s.factorAuth[user.ke][user.uid].key }, time: new Date(), }) } break; case'onMonitorSave': case'onMonitorStart': case'onMonitorStop': case'onMonitorDied': case'onDetectorNoTriggerTimeout': theAction = function(monitorConfig){ //e = monitor object sendMessage(msgOptions,{ title: titleLegend[eventName], name: eventName, data: { name: monitorConfig.name, monitorId: monitorConfig.mid || monitorConfig.id, }, time: new Date(), }) } break; } mqttSubs[mqttSubId].eventHandlers[eventName] = theAction }) mqttSubs[mqttSubId].monitorsToRead = monitorsToRead; }catch(err){ s.debugLog(err) // s.systemLog(err) } }) s.group[groupKey].mqttOutbounderKeys = Object.keys(s.group[groupKey].mqttOutbounders) }else{ s.group[groupKey].mqttOutbounderKeys = [] } } const unloadMqttListBotForUser = function(user){ const groupKey = user.ke const mqttSubs = s.group[groupKey].mqttOutbounders || {} Object.keys(mqttSubs).forEach(function(mqttSubId){ try{ mqttSubs[mqttSubId].client.end() }catch(err){ s.debugLog(err) // s.userLog({ // ke: groupKey, // mid: '$USER' // },{ // type: lang['MQTT Error'], // msg: err // }) } delete(mqttSubs[mqttSubId]) }) } const onBeforeAccountSave = function(data){ data.d.mqttout_list = [] } s.loadGroupAppExtender(loadMqttListBotForUser) s.unloadGroupAppExtender(unloadMqttListBotForUser) s.beforeAccountSave(onBeforeAccountSave) s.onTwoFactorAuthCodeNotification(onTwoFactorAuthCodeNotification) s.onEventTrigger(onEventTrigger) s.onEventTriggerBeforeFilter(onEventTriggerBeforeFilter) s.onDetectorNoTriggerTimeout(onDetectorNoTriggerTimeout) s.onMonitorSave(onMonitorSave) s.onMonitorStart(onMonitorStart) s.onMonitorStop(onMonitorStop) s.onMonitorDied(onMonitorDied) s.insertCompletedVideoExtender(insertCompletedVideoExtender) s.onEventBasedRecordingComplete(onEventBasedRecordingComplete) s.onUserLog(onUserLog) s.definitions["Monitor Settings"].blocks["Notifications"].info[0].info.push( { "name": "detail=notify_mqttout", "field": lang['MQTT Outbound'], "description": "", "default": "0", "example": "", "selector": "h_det_mqttout", "fieldType": "select", "possible": [ { "name": lang.No, "value": "0" }, { "name": lang.Yes, "value": "1" } ] }, ) s.definitions["Monitor Settings"].blocks["Notifications"].info.push({ "evaluation": "$user.details.use_mqttout !== '0'", isFormGroupGroup: true, "name": lang['MQTT Outbound'], "color": "blue", "section-class": "h_det_mqttout_input h_det_mqttout_1", "info": [ { "name": "detail=detector_mqttout_timeout", "field": lang['Allow Next Alert'] + ` (${lang['on Event']})`, "description": "", "default": "10", "example": "", "possible": "" }, ] }) s.definitions["Account Settings"].blocks["2-Factor Authentication"].info.push({ "name": "detail=factor_mqttout", "field": lang['MQTT Outbound'], "default": "1", "example": "", "fieldType": "select", "possible": [ { "name": lang.No, "value": "0" }, { "name": lang.Yes, "value": "1" } ] }) s.definitions["Account Settings"].blocks["MQTT Outbound"] = { "evaluation": "$user.details.use_mqttout !== '0'", "name": lang['MQTT Outbound'], "color": "blue", "info": [ { "name": "detail=mqttout", "selector":"u_mqttout", "field": lang.Enabled, "default": "0", "example": "", "fieldType": "select", "possible": [ { "name": lang.No, "value": "0" }, { "name": lang.Yes, "value": "1" } ] }, { "fieldType": "btn", "class": `btn-success mqtt-out-add-row`, "btnContent": `   ${lang['Add']}`, }, { "id": "mqttout_list", "fieldType": "div", }, { "fieldType": "script", "src": "assets/js/bs5.mqttOut.js", } ] } s.definitions["Event Filters"].blocks["Action for Selected"].info.push({ "name": "actions=mqttout", "field": lang['MQTT Outbound'], "fieldType": "select", "form-group-class": "actions-row", "default": "", "example": "1", "possible": [ { "name": lang['Original Choice'], "value": "", "selected": true }, { "name": lang.Yes, "value": "1", } ] }) }catch(err){ console.error(err) console.log('Could not start MQTT Outbound Handling.') } } }