472 lines
23 KiB
JavaScript
472 lines
23 KiB
JavaScript
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": `<i class="fa fa-plus"></i> ${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.')
|
|
}
|
|
}
|
|
}
|