Audio Detector, Detect Object in Region, and API Methods for Scheduling System
- Audio Detector measures decibels (dB). Section can be found under Motion Detection. - Regions can now be used with Object detection alone, find the option the Object Detection section. - Scheduling System will allow automatically activating Monitor States based on time. GUI will be posted in the next commit. + Update Installers + Update framework.sql + `npm test` will run the test + Minor Bug Fixesmerge-requests/49/head
parent
91f530c461
commit
afdc0cf568
|
@ -143,21 +143,14 @@ if [ "$mysqlDefaultData" = "y" ] || [ "$mysqlDefaultData" = "Y" ]; then
|
|||
echo "====================================="
|
||||
echo "====================================="
|
||||
fi
|
||||
if [ ! "$sqliteormariadb" = "M" ] && [ ! "$sqliteormariadb" = "m" ]; then
|
||||
echo "====================================="
|
||||
echo "||===== Install Completed =====||"
|
||||
echo "====================================="
|
||||
echo "|| Login with the Superuser and create a new user!!"
|
||||
echo "||==================================="
|
||||
echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser."
|
||||
echo "||==================================="
|
||||
echo "|| Default Superuser : admin@shinobi.video"
|
||||
echo "|| Default Password : admin"
|
||||
echo "====================================="
|
||||
echo "====================================="
|
||||
else
|
||||
echo "+=================================+"
|
||||
echo "||===== Install Completed =====||"
|
||||
echo "|| Access the main Shinobi panel at http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080 in your web browser."
|
||||
echo "+=================================+"
|
||||
fi
|
||||
echo "====================================="
|
||||
echo "||===== Install Completed =====||"
|
||||
echo "====================================="
|
||||
echo "|| Login with the Superuser and create a new user!!"
|
||||
echo "||==================================="
|
||||
echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser."
|
||||
echo "||==================================="
|
||||
echo "|| Default Superuser : admin@shinobi.video"
|
||||
echo "|| Default Password : admin"
|
||||
echo "====================================="
|
||||
echo "====================================="
|
||||
|
|
|
@ -115,15 +115,6 @@ sudo chmod -R 755 .
|
|||
touch INSTALL/installed.txt
|
||||
dos2unix /home/Shinobi/INSTALL/shinobi
|
||||
ln -s /home/Shinobi/INSTALL/shinobi /usr/bin/shinobi
|
||||
if [ "$mysqlDefaultData" = "y" ] || [ "$mysqlDefaultData" = "Y" ]; then
|
||||
echo "=====================================" > INSTALL/installed.txt
|
||||
echo "======= Login Credentials =======" >> INSTALL/installed.txt
|
||||
echo "|| Username : $userEmail" >> INSTALL/installed.txt
|
||||
echo "|| Password : $userPasswordPlain" >> INSTALL/installed.txt
|
||||
echo "|| API Key : $apiKey" >> INSTALL/installed.txt
|
||||
echo "=====================================" >> INSTALL/installed.txt
|
||||
echo "=====================================" >> INSTALL/installed.txt
|
||||
fi
|
||||
echo "Shinobi - Start Shinobi and set to start on boot?"
|
||||
echo "(y)es or (N)o"
|
||||
read startShinobi
|
||||
|
@ -134,31 +125,14 @@ if [ "$startShinobi" = "y" ] || [ "$startShinobi" = "Y" ]; then
|
|||
sudo pm2 save
|
||||
sudo pm2 list
|
||||
fi
|
||||
if [ "$mysqlDefaultData" = "y" ] || [ "$mysqlDefaultData" = "Y" ]; then
|
||||
echo "details written to INSTALL/installed.txt"
|
||||
echo "====================================="
|
||||
echo "======= Login Credentials ======="
|
||||
echo "|| Username : $userEmail"
|
||||
echo "|| Password : $userPasswordPlain"
|
||||
echo "|| API Key : $apiKey"
|
||||
echo "====================================="
|
||||
echo "====================================="
|
||||
fi
|
||||
if [ ! "$sqliteormariadb" = "M" ] && [ ! "$sqliteormariadb" = "m" ]; then
|
||||
echo "====================================="
|
||||
echo "||===== Install Completed =====||"
|
||||
echo "====================================="
|
||||
echo "|| Login with the Superuser and create a new user!!"
|
||||
echo "||==================================="
|
||||
echo "|| Open http://$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1):8080/super in your web browser."
|
||||
echo "||==================================="
|
||||
echo "|| Default Superuser : admin@shinobi.video"
|
||||
echo "|| Default Password : admin"
|
||||
echo "====================================="
|
||||
echo "====================================="
|
||||
else
|
||||
echo "+=================================+"
|
||||
echo "||===== Install Completed =====||"
|
||||
echo "|| Access the main Shinobi panel at http://$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1):8080 in your web browser."
|
||||
echo "+=================================+"
|
||||
fi
|
||||
echo "====================================="
|
||||
echo "||===== Install Completed =====||"
|
||||
echo "====================================="
|
||||
echo "|| Login with the Superuser and create a new user!!"
|
||||
echo "||==================================="
|
||||
echo "|| Open http://$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1):8080/super in your web browser."
|
||||
echo "||==================================="
|
||||
echo "|| Default Superuser : admin@shinobi.video"
|
||||
echo "|| Default Password : admin"
|
||||
echo "====================================="
|
||||
echo "====================================="
|
||||
|
|
|
@ -30,12 +30,17 @@ if [ ! -e "./super.json" ]; then
|
|||
echo "* You can edit these settings in \"super.json\" located in the Shinobi directory."
|
||||
sudo cp super.sample.json super.json
|
||||
fi
|
||||
if ! [ -x "$(command -v ifconfig)" ]; then
|
||||
echo "============="
|
||||
echo "Shinobi - Installing Net-Tools"
|
||||
sudo apt install net-tools -y
|
||||
fi
|
||||
if ! [ -x "$(command -v node)" ]; then
|
||||
echo "============="
|
||||
echo "Shinobi - Installing Node.js"
|
||||
wget https://deb.nodesource.com/setup_9.x
|
||||
chmod +x setup_9.x
|
||||
./setup_9.x
|
||||
wget https://deb.nodesource.com/setup_8.x
|
||||
chmod +x setup_8.x
|
||||
./setup_8.x
|
||||
sudo apt install nodejs -y
|
||||
else
|
||||
echo "Node.js Found..."
|
||||
|
@ -132,21 +137,14 @@ if [ "$startShinobi" = "y" ] || [ "$startShinobi" = "y" ]; then
|
|||
sudo pm2 save
|
||||
sudo pm2 list
|
||||
fi
|
||||
if [ ! "$sqliteormariadb" = "M" ] && [ ! "$sqliteormariadb" = "m" ]; then
|
||||
echo "====================================="
|
||||
echo "||===== Install Completed =====||"
|
||||
echo "====================================="
|
||||
echo "|| Login with the Superuser and create a new user!!"
|
||||
echo "||==================================="
|
||||
echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser."
|
||||
echo "||==================================="
|
||||
echo "|| Default Superuser : admin@shinobi.video"
|
||||
echo "|| Default Password : admin"
|
||||
echo "====================================="
|
||||
echo "====================================="
|
||||
else
|
||||
echo "+=================================+"
|
||||
echo "||===== Install Completed =====||"
|
||||
echo "|| Access the main Shinobi panel at http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080 in your web browser."
|
||||
echo "+=================================+"
|
||||
fi
|
||||
echo "====================================="
|
||||
echo "||===== Install Completed =====||"
|
||||
echo "====================================="
|
||||
echo "|| Login with the Superuser and create a new user!!"
|
||||
echo "||==================================="
|
||||
echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser."
|
||||
echo "||==================================="
|
||||
echo "|| Default Superuser : admin@shinobi.video"
|
||||
echo "|| Default Password : admin"
|
||||
echo "====================================="
|
||||
echo "====================================="
|
||||
|
|
|
@ -67,6 +67,8 @@ loadLib('ffmpeg')(s,config,function(){
|
|||
loadLib('notification')(s,config,lang)
|
||||
//custom module loader
|
||||
loadLib('customAutoLoad')(s,config,lang,app,io)
|
||||
//scheduling engine
|
||||
loadLib('scheduler')(s,config,lang,app,io)
|
||||
//on-start actions, daemon(s) starter
|
||||
loadLib('startup')(s,config,lang)
|
||||
})
|
||||
|
|
|
@ -329,6 +329,10 @@
|
|||
"Filter for Objects only": "Filter for Objects only",
|
||||
"Custom": "Custom",
|
||||
"Detector": "Detector",
|
||||
"Audio Detector": "Audio Detector",
|
||||
"Audio Detection": "Audio Detection",
|
||||
"Minimum dB": "Minimum dB",
|
||||
"Maximum dB": "Maximum dB",
|
||||
"Connected": "Connected",
|
||||
"Not Saved": "Not Saved",
|
||||
"Not Connected": "Not Connected",
|
||||
|
@ -666,6 +670,10 @@
|
|||
"Inserted State Configuration": "Inserted State Configuration",
|
||||
"Edited State Configuration": "Edited State Configuration",
|
||||
"Deleted State Configuration": "Deleted State Configuration",
|
||||
"Schedule Configuration Not Found": "Schedule Configuration Not Found",
|
||||
"Inserted Schedule Configuration": "Inserted Schedule Configuration",
|
||||
"Edited Schedule Configuration": "Edited Schedule Configuration",
|
||||
"Deleted Schedule Configuration": "Deleted Schedule Configuration",
|
||||
"Dashboard Language": "Dashboard Language",
|
||||
"Form Data Not Found": "Form Data Not Found",
|
||||
"File Not Found": "File Not Found",
|
||||
|
@ -792,6 +800,7 @@
|
|||
"TV Channel ID":"TV Channel ID",
|
||||
"TV Channel Group":"TV Channel Group",
|
||||
"Emotion Average":"Emotion Average",
|
||||
"Require Object to be in Region":"Require Object to be in Region",
|
||||
"Show Regions of Interest":"Show Regions of Interest",
|
||||
"Confidence of Detection":"Confidence of Detection",
|
||||
"Edit Selected":"Edit Selected",
|
||||
|
|
|
@ -33,7 +33,7 @@ module.exports = function(s,config){
|
|||
s.parseJSON = function(string){
|
||||
var parsed
|
||||
try{
|
||||
string = JSON.parse(string)
|
||||
parsed = JSON.parse(string)
|
||||
}catch(err){
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
var P2P = require('pipe2pam');
|
||||
// Matrix In Region Libs >
|
||||
var SAT = require('sat')
|
||||
var V = SAT.Vector;
|
||||
var P = SAT.Polygon;
|
||||
// Matrix In Region Libs />
|
||||
var P2P = require('pipe2pam')
|
||||
// pamDiff is based on https://www.npmjs.com/package/pam-diff
|
||||
var PamDiff = require('./detectorPamDiff.js');
|
||||
var PamDiff = require('pam-diff')
|
||||
module.exports = function(s,config){
|
||||
s.createPamDiffEngine = function(e){
|
||||
var width,
|
||||
|
@ -46,7 +51,7 @@ module.exports = function(s,config){
|
|||
[width,height],
|
||||
[width,0]
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
e.triggerTimer = {}
|
||||
|
@ -58,7 +63,7 @@ module.exports = function(s,config){
|
|||
regions : regions.forPam,
|
||||
drawMatrix : e.details.detector_show_matrix
|
||||
});
|
||||
s.group[e.ke].mon[e.id].p2p = new P2P();
|
||||
s.group[e.ke].mon[e.id].p2p = new P2P()
|
||||
var regionArray = Object.values(regionJson)
|
||||
if(config.detectorMergePamRegionTriggers === true){
|
||||
// merge pam triggers for performance boost
|
||||
|
@ -317,4 +322,39 @@ module.exports = function(s,config){
|
|||
}
|
||||
return trigger
|
||||
}
|
||||
s.isAtleastOneMatrixInRegion = function(regions,matrices,callback){
|
||||
var regionPolys = []
|
||||
var matrixPoints = []
|
||||
regions.forEach(function(region,n){
|
||||
var polyPoints = []
|
||||
region.points.forEach(function(point){
|
||||
polyPoints.push(new V(parseInt(point[0]),parseInt(point[1])))
|
||||
})
|
||||
regionPolys[n] = new P(new V(0,0), polyPoints)
|
||||
})
|
||||
var collisions = []
|
||||
var foundInRegion = false
|
||||
matrices.forEach(function(matrix){
|
||||
var matrixPoints = [
|
||||
new V(matrix.x,matrix.y),
|
||||
new V(matrix.width,matrix.y),
|
||||
new V(matrix.width,matrix.height),
|
||||
new V(matrix.x,matrix.height)
|
||||
]
|
||||
var matrixPoly = new P(new V(0,0), matrixPoints)
|
||||
regionPolys.forEach(function(region,n){
|
||||
var response = new SAT.Response()
|
||||
var collided = SAT.testPolygonPolygon(matrixPoly, region, response)
|
||||
if(collided === true){
|
||||
collisions.push({
|
||||
matrix: matrix,
|
||||
region: regions[n]
|
||||
})
|
||||
foundInRegion = true
|
||||
}
|
||||
})
|
||||
})
|
||||
if(callback)callback(foundInRegion,collisions)
|
||||
return foundInRegion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
d.mon=s.group[d.ke].mon_conf[d.id];
|
||||
var currentConfig = s.group[d.ke].mon[d.id].details
|
||||
var hasMatrices = (d.details.matrices && d.details.matrices.length > 0)
|
||||
//read filters
|
||||
if(
|
||||
currentConfig.use_detector_filters === '1' &&
|
||||
|
@ -160,7 +161,7 @@ module.exports = function(s,config,lang){
|
|||
})
|
||||
if(d.details.matrices && d.details.matrices.length === 0 || filter.halt === true){
|
||||
return
|
||||
}else if(d.details.matrices && d.details.matrices.length > 0){
|
||||
}else if(hasMatrices){
|
||||
var reviewedMatrix = []
|
||||
d.details.matrices.forEach(function(matrix){
|
||||
if(matrix)reviewedMatrix.push(matrix)
|
||||
|
@ -193,6 +194,16 @@ module.exports = function(s,config,lang){
|
|||
return
|
||||
}
|
||||
}
|
||||
// check if object should be in region
|
||||
if(hasMatrices && currentConfig.detector_obj_region === '1'){
|
||||
var regions = s.group[d.ke].mon[d.id].parsedObjects.cords
|
||||
var isMatrixInRegions = s.isAtleastOneMatrixInRegion(regions,d.details.matrices)
|
||||
if(isMatrixInRegions){
|
||||
s.debugLog('Matrix in region!')
|
||||
}else{
|
||||
return
|
||||
}
|
||||
}
|
||||
// check modified indifference
|
||||
if(filter.indifference !== false && d.details.confidence < parseFloat(filter.indifference)){
|
||||
// fails indifference check for modified indifference
|
||||
|
|
|
@ -735,6 +735,17 @@ module.exports = function(s,config,onFinish){
|
|||
x.record_string+=x.vcodec+x.record_fps+x.record_video_filters+x.record_dimensions+x.segment;
|
||||
}
|
||||
}
|
||||
ffmpeg.buildAudioDetector = function(e,x){
|
||||
if(e.details.detector_audio === '1'){
|
||||
if(e.details.input_map_choices&&e.details.input_map_choices.detector_audio){
|
||||
//add input feed map
|
||||
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector_audio)
|
||||
}else{
|
||||
x.pipe += ' -map 0:a'
|
||||
}
|
||||
x.pipe += ' -acodec pcm_s16le -f s16le -ac 1 -ar 16000 pipe:6'
|
||||
}
|
||||
}
|
||||
ffmpeg.buildMainDetector = function(e,x){
|
||||
//e = monitor object
|
||||
//x = temporary values
|
||||
|
@ -761,10 +772,10 @@ module.exports = function(s,config,onFinish){
|
|||
if(e.details.detector_use_detect_object === '1'){
|
||||
//for object detection
|
||||
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector)
|
||||
x.pipe += ' -f singlejpeg '+x.detector_vf+x.cust_detect+x.dratio+' pipe:4';
|
||||
x.pipe += ' -an -f singlejpeg '+x.detector_vf+x.cust_detect+x.dratio+' pipe:4';
|
||||
}
|
||||
}else{
|
||||
x.pipe+=' -f image2pipe '+x.detector_vf+x.cust_detect+x.dratio+' pipe:3';
|
||||
x.pipe+=' -an -f image2pipe '+x.detector_vf+x.cust_detect+x.dratio+' pipe:3';
|
||||
}
|
||||
}
|
||||
//Traditional Recording Buffer
|
||||
|
@ -886,6 +897,7 @@ module.exports = function(s,config,onFinish){
|
|||
ffmpeg.buildMainInput(e,x)
|
||||
ffmpeg.buildMainStream(e,x)
|
||||
ffmpeg.buildMainRecording(e,x)
|
||||
ffmpeg.buildAudioDetector(e,x)
|
||||
ffmpeg.buildMainDetector(e,x)
|
||||
s.onFfmpegCameraStringCreationExtensions.forEach(function(extender){
|
||||
extender(e,x)
|
||||
|
|
106
libs/monitor.js
106
libs/monitor.js
|
@ -6,6 +6,7 @@ var Mp4Frag = require('mp4frag');
|
|||
var onvif = require('node-onvif');
|
||||
var request = require('request');
|
||||
var connectionTester = require('connection-tester')
|
||||
var SoundDetection = require('shinobi-sound-detection')
|
||||
var URL = require('url')
|
||||
module.exports = function(s,config,lang){
|
||||
s.initiateMonitorObject = function(e){
|
||||
|
@ -21,6 +22,7 @@ module.exports = function(s,config,lang){
|
|||
if(!s.group[e.ke].mon[e.mid].eventBasedRecording){s.group[e.ke].mon[e.mid].eventBasedRecording={}};
|
||||
if(!s.group[e.ke].mon[e.mid].watch){s.group[e.ke].mon[e.mid].watch={}};
|
||||
if(!s.group[e.ke].mon[e.mid].fixingVideos){s.group[e.ke].mon[e.mid].fixingVideos={}};
|
||||
if(!s.group[e.ke].mon[e.mid].parsedObjects){s.group[e.ke].mon[e.mid].parsedObjects={}};
|
||||
if(!s.group[e.ke].mon[e.mid].isStarted){s.group[e.ke].mon[e.mid].isStarted = false};
|
||||
if(s.group[e.ke].mon[e.mid].delete){clearTimeout(s.group[e.ke].mon[e.mid].delete)}
|
||||
if(!s.group[e.ke].mon_conf){s.group[e.ke].mon_conf={}}
|
||||
|
@ -282,12 +284,20 @@ module.exports = function(s,config,lang){
|
|||
s.cameraCheckObjectsInDetails = function(e){
|
||||
//parse Objects
|
||||
(['detector_cascades','cords','detector_filters','input_map_choices']).forEach(function(v){
|
||||
if(e.details&&e.details[v]&&(e.details[v] instanceof Object)===false){
|
||||
if(e.details && e.details[v]){
|
||||
try{
|
||||
if(e.details[v] === '') e.details[v] = '{}'
|
||||
e.details[v]=JSON.parse(e.details[v]);
|
||||
if(!e.details[v])e.details[v]={};
|
||||
s.group[e.ke].mon[e.id].details = e.details;
|
||||
if(!e.details[v] || e.details[v] === '')e.details[v] = '{}'
|
||||
e.details[v] = s.parseJSON(e.details[v])
|
||||
if(!e.details[v])e.details[v] = {}
|
||||
s.group[e.ke].mon[e.id].details = e.details
|
||||
switch(v){
|
||||
case'cords':
|
||||
s.group[e.ke].mon[e.id].parsedObjects[v] = Object.values(s.parseJSON(e.details[v]))
|
||||
break;
|
||||
default:
|
||||
s.group[e.ke].mon[e.id].parsedObjects[v] = s.parseJSON(e.details[v])
|
||||
break;
|
||||
}
|
||||
}catch(err){
|
||||
|
||||
}
|
||||
|
@ -776,6 +786,46 @@ module.exports = function(s,config,lang){
|
|||
if(e.type==='jpeg'){
|
||||
s.cameraPullJpegStream(e)
|
||||
}
|
||||
if(e.details.detector_audio === '1'){
|
||||
var triggerLevel
|
||||
var triggerLevelMax
|
||||
if(e.details.detector_audio_min_db && e.details.detector_audio_min_db !== ''){
|
||||
triggerLevel = parseInt(e.details.detector_audio_min_db)
|
||||
}else{
|
||||
triggerLevel = 5
|
||||
}
|
||||
if(e.details.detector_audio_max_db && e.details.detector_audio_max_db !== ''){
|
||||
triggerLevelMax = parseInt(e.details.detector_audio_max_db)
|
||||
}
|
||||
var audioDetector = new SoundDetection({
|
||||
format: {
|
||||
bitDepth: 16,
|
||||
numberOfChannels: 1,
|
||||
signed: true
|
||||
},
|
||||
triggerLevel: triggerLevel,
|
||||
triggerLevelMax: triggerLevelMax
|
||||
},function(dB) {
|
||||
s.triggerEvent({
|
||||
f:'trigger',
|
||||
id:e.id,
|
||||
ke:e.ke,
|
||||
name: 'db',
|
||||
details:{
|
||||
plug:'audio',
|
||||
name:'db',
|
||||
reason:'soundChange',
|
||||
confidence:dB
|
||||
},
|
||||
plates:[],
|
||||
imgHeight:e.details.detector_scale_y,
|
||||
imgWidth:e.details.detector_scale_x
|
||||
})
|
||||
})
|
||||
s.group[e.ke].mon[e.id].audioDetector = audioDetector
|
||||
audioDetector.start()
|
||||
s.group[e.ke].mon[e.id].spawn.stdio[6].pipe(audioDetector.streamDecoder)
|
||||
}
|
||||
if(e.details.detector === '1'){
|
||||
s.ocvTx({f:'init_monitor',id:e.id,ke:e.ke})
|
||||
//frames from motion detect
|
||||
|
@ -1435,7 +1485,7 @@ module.exports = function(s,config,lang){
|
|||
s.group[e.ke].mon[e.mid].isRecording = false
|
||||
}
|
||||
//set up fatal error handler
|
||||
if(e.details.fatal_max===''){
|
||||
if(e.details.fatal_max === ''){
|
||||
e.details.fatal_max = 10
|
||||
}else{
|
||||
e.details.fatal_max = parseFloat(e.details.fatal_max)
|
||||
|
@ -1454,4 +1504,48 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
if(typeof cn === 'function'){setTimeout(function(){cn()},1000)}
|
||||
}
|
||||
//
|
||||
s.activateMonitorStates = function(groupKey,stateName,user,callback){
|
||||
var endData = {
|
||||
ok: false
|
||||
}
|
||||
s.findPreset([groupKey,'monitorStates',stateName],function(notFound,preset){
|
||||
if(notFound === false){
|
||||
var sqlQuery = 'SELECT * FROM Monitors WHERE ke=? AND '
|
||||
var monitorQuery = []
|
||||
var sqlQueryValues = [groupKey]
|
||||
var monitorPresets = {}
|
||||
preset.details.monitors.forEach(function(monitor){
|
||||
monitorQuery.push('mid=?')
|
||||
sqlQueryValues.push(monitor.mid)
|
||||
monitorPresets[monitor.mid] = monitor
|
||||
})
|
||||
sqlQuery += '('+monitorQuery.join(' OR ')+')'
|
||||
s.sqlQuery(sqlQuery,sqlQueryValues,function(err,monitors){
|
||||
if(monitors && monitors[0]){
|
||||
monitors.forEach(function(monitor){
|
||||
s.checkDetails(monitor)
|
||||
s.checkDetails(monitorPresets[monitor.mid])
|
||||
var monitorPreset = monitorPresets[monitor.mid]
|
||||
monitorPreset.details = Object.assign(monitor.details,monitorPreset.details)
|
||||
monitor = s.cleanMonitorObjectForDatabase(Object.assign(monitor,monitorPreset))
|
||||
monitor.details = JSON.stringify(monitor.details)
|
||||
s.addOrEditMonitor(Object.assign(monitor,{}),function(err,endData){
|
||||
|
||||
},user)
|
||||
})
|
||||
endData.ok = true
|
||||
s.tx({f:'change_group_state',ke:groupKey,name:stateName},'GRP_'+groupKey)
|
||||
callback(endData)
|
||||
}else{
|
||||
endData.msg = user.lang['State Configuration has no monitors associated']
|
||||
callback(endData)
|
||||
}
|
||||
})
|
||||
}else{
|
||||
endData.msg = user.lang['State Configuration Not Found']
|
||||
callback(endData)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,257 @@
|
|||
module.exports = function(s,config,lang,app,io){
|
||||
s.schedules = {}
|
||||
//Get all Schedules
|
||||
s.getAllSchedules = function(callback){
|
||||
s.schedules = {}
|
||||
s.sqlQuery('SELECT * FROM Schedules',function(err,rows){
|
||||
rows.forEach(function(schedule){
|
||||
s.updateSchedule(schedule)
|
||||
})
|
||||
if(callback)callback()
|
||||
})
|
||||
}
|
||||
//update schedule
|
||||
s.updateSchedule = function(row){
|
||||
var schedule = Object.assign(row,{})
|
||||
if(!s.schedules[schedule.ke])s.schedules[schedule.ke] = {}
|
||||
s.checkDetails(schedule)
|
||||
if(!s.schedules[schedule.ke][schedule.name]){
|
||||
s.schedules[schedule.ke][schedule.name] = schedule
|
||||
}else{
|
||||
s.schedules[schedule.ke][schedule.name] = Object.assign(s.schedules[schedule.ke][schedule.name],schedule)
|
||||
}
|
||||
}
|
||||
//check time in schedule
|
||||
s.checkTimeAgainstSchedule = function(start,end,callback){
|
||||
try{
|
||||
if(
|
||||
start
|
||||
){
|
||||
var checkStartTime = new Date()
|
||||
var startSplit = start.split(':')
|
||||
var startHour = parseInt(startSplit[0])
|
||||
var startMin = parseInt(startSplit[1])
|
||||
checkStartTime.setHours(startHour)
|
||||
checkStartTime.setMinutes(startMin)
|
||||
if(end){
|
||||
var checkEndTime = new Date()
|
||||
var endSplit = end.split(':')
|
||||
var endHour = parseInt(endSplit[0])
|
||||
var endMin = parseInt(endSplit[1])
|
||||
checkEndTime.setHours(endHour)
|
||||
checkEndTime.setMinutes(endMin)
|
||||
}
|
||||
var currentDate = new Date()
|
||||
if(
|
||||
(
|
||||
currentDate >= checkStartTime &&
|
||||
currentDate <= checkEndTime
|
||||
) ||
|
||||
currentDate >= checkStartTime && !end
|
||||
){
|
||||
callback()
|
||||
}else{
|
||||
callback({
|
||||
currentDate : currentDate,
|
||||
startTime : checkStartTime,
|
||||
endTime : checkEndTime
|
||||
})
|
||||
}
|
||||
}else{
|
||||
callback()
|
||||
}
|
||||
}catch(err){
|
||||
console.log(err)
|
||||
callback()
|
||||
}
|
||||
}
|
||||
//check all Schedules
|
||||
s.checkSchedules = function(v,callback){
|
||||
var groupKeys = Object.keys(s.schedules)
|
||||
groupKeys.forEach(function(key){
|
||||
var scheduleNames = Object.keys(s.schedules[key])
|
||||
scheduleNames.forEach(function(name){
|
||||
var schedule = s.schedules[key][name]
|
||||
if(!schedule.active && schedule.enabled === 1 && schedule.start && schedule.details.monitorStates){
|
||||
s.checkTimeAgainstSchedule(schedule.start,schedule.end,function(err){
|
||||
if(!err){
|
||||
schedule.active = true
|
||||
var monitorStates = schedule.details.monitorStates
|
||||
monitorStates.forEach(function(stateName){
|
||||
s.activateMonitorStates(key,stateName,{
|
||||
ke: key,
|
||||
uid: 'System',
|
||||
details: {},
|
||||
permissions: {},
|
||||
lang: lang
|
||||
},function(endData){
|
||||
// console.log(endData)
|
||||
})
|
||||
})
|
||||
}else{
|
||||
schedule.active = false
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
//
|
||||
s.findSchedule = function(groupKey,name,callback){
|
||||
//presetQueryVals = [ke, type, name]
|
||||
s.sqlQuery("SELECT * FROM Schedules WHERE ke=? AND name=? LIMIT 1",[groupKey,name],function(err,schedules){
|
||||
var schedule
|
||||
var notFound = false
|
||||
if(schedules && schedules[0]){
|
||||
schedule = schedules[0]
|
||||
s.checkDetails(schedule)
|
||||
}else{
|
||||
notFound = true
|
||||
}
|
||||
callback(notFound,schedule)
|
||||
})
|
||||
}
|
||||
//
|
||||
var onProcessReady = function(){
|
||||
s.getAllSchedules(function(){
|
||||
s.checkSchedules()
|
||||
})
|
||||
setInterval(function(){
|
||||
s.checkSchedules()
|
||||
},1000 * 60 * 5)
|
||||
}
|
||||
/**
|
||||
* WebServerPath : API : Update Schedule
|
||||
*/
|
||||
app.all([
|
||||
config.webPaths.apiPrefix+':auth/schedule/:ke',
|
||||
config.webPaths.adminApiPrefix+':auth/schedule/:ke',
|
||||
config.webPaths.apiPrefix+':auth/schedule/:ke/:name',
|
||||
config.webPaths.adminApiPrefix+':auth/schedule/:ke/:name',
|
||||
],function (req,res){
|
||||
s.auth(req.params,function(user){
|
||||
var endData = {
|
||||
ok : false
|
||||
}
|
||||
if(user.details.sub){
|
||||
endData.msg = user.lang['Not Permitted']
|
||||
s.closeJsonResponse(res,endData)
|
||||
return
|
||||
}
|
||||
var theQuery = "SELECT * FROM Schedules WHERE ke=?"
|
||||
var theQueryValues = [req.params.ke]
|
||||
if(req.params.name){
|
||||
theQuery += ' AND name=?'
|
||||
theQueryValues.push(req.params.name)
|
||||
}
|
||||
s.sqlQuery(theQuery,theQueryValues,function(err,schedules){
|
||||
if(schedules && schedules[0]){
|
||||
endData.ok = true
|
||||
schedules.forEach(function(schedule){
|
||||
s.checkDetails(schedule)
|
||||
})
|
||||
endData.schedules = schedules
|
||||
}else{
|
||||
endData.msg = user.lang['Not Found']
|
||||
}
|
||||
s.closeJsonResponse(res,endData)
|
||||
})
|
||||
})
|
||||
})
|
||||
/**
|
||||
* WebServerPath : API : Update Schedule
|
||||
*/
|
||||
app.all([
|
||||
config.webPaths.apiPrefix+':auth/schedule/:ke/:name/:action',
|
||||
config.webPaths.adminApiPrefix+':auth/schedule/:ke/:name/:action'
|
||||
],function (req,res){
|
||||
s.auth(req.params,function(user){
|
||||
var endData = {
|
||||
ok : false
|
||||
}
|
||||
if(user.details.sub){
|
||||
endData.msg = user.lang['Not Permitted']
|
||||
s.closeJsonResponse(res,endData)
|
||||
return
|
||||
}
|
||||
switch(req.params.action){
|
||||
case'insert':case'edit':
|
||||
var form = s.getPostData(req)
|
||||
s.checkDetails(form)
|
||||
if(!form || !form.details){
|
||||
endData.msg = user.lang['Form Data Not Found']
|
||||
s.closeJsonResponse(res,endData)
|
||||
return
|
||||
}
|
||||
form.enabled = parseInt(form.enabled) || 1;
|
||||
s.findSchedule(req.params.ke,req.params.name,function(notFound,preset){
|
||||
if(notFound === true){
|
||||
endData.msg = lang["Inserted Schedule Configuration"]
|
||||
var insertData = {
|
||||
ke: req.params.ke,
|
||||
name: req.params.name,
|
||||
details: s.stringJSON(form.details),
|
||||
start: form.start,
|
||||
end: form.end,
|
||||
enabled: form.enabled
|
||||
}
|
||||
s.sqlQuery('INSERT INTO Schedules ('+Object.keys(insertData).join(',')+') VALUES (?,?,?,?,?,?)',Object.values(insertData))
|
||||
s.tx({
|
||||
f: 'add_schedule',
|
||||
insertData: insertData,
|
||||
ke: req.params.ke,
|
||||
name: req.params.name
|
||||
},'GRP_'+req.params.ke)
|
||||
}else{
|
||||
endData.msg = lang["Edited Schedule Configuration"]
|
||||
var insertData = {
|
||||
details: s.stringJSON(form.details),
|
||||
start: form.start,
|
||||
end: form.end,
|
||||
enabled: form.enabled,
|
||||
ke: req.params.ke,
|
||||
name: req.params.name
|
||||
}
|
||||
s.sqlQuery('UPDATE Schedules SET details=?,start=?,end=?,enabled=? WHERE ke=? AND name=?',Object.values(insertData))
|
||||
s.tx({
|
||||
f: 'edit_schedule',
|
||||
insertData: insertData,
|
||||
ke: req.params.ke,
|
||||
name: req.params.name
|
||||
},'GRP_'+req.params.ke)
|
||||
}
|
||||
s.updateSchedule({
|
||||
ke: req.params.ke,
|
||||
name: req.params.name,
|
||||
details: s.stringJSON(form.details),
|
||||
start: form.start,
|
||||
end: form.end,
|
||||
enabled: form.enabled
|
||||
})
|
||||
endData.ok = true
|
||||
s.closeJsonResponse(res,endData)
|
||||
})
|
||||
break;
|
||||
case'delete':
|
||||
s.findSchedule(req.params.ke,req.params.name,function(notFound,schedule){
|
||||
if(notFound === true){
|
||||
endData.msg = user.lang['Schedule Configuration Not Found']
|
||||
s.closeJsonResponse(res,endData)
|
||||
}else{
|
||||
s.sqlQuery('DELETE FROM Schedules WHERE ke=? AND name=?',[req.params.ke,req.params.name],function(err){
|
||||
if(!err){
|
||||
endData.msg = lang["Deleted Schedule Configuration"]
|
||||
endData.ok = true
|
||||
if(s.schedules[schedule.ke])delete(s.schedules[schedule.ke][schedule.name])
|
||||
}
|
||||
s.closeJsonResponse(res,endData)
|
||||
})
|
||||
}
|
||||
})
|
||||
break;
|
||||
}
|
||||
})
|
||||
})
|
||||
//bind events
|
||||
s.onProcessReady(onProcessReady)
|
||||
}
|
|
@ -1365,7 +1365,7 @@ module.exports = function(s,config,lang,io){
|
|||
if(cn.cron){
|
||||
delete(s.cron);
|
||||
}
|
||||
if(cn.ocv){
|
||||
if(cn.ocv && s.ocv){
|
||||
s.tx({f:'detector_unplugged',plug:s.ocv.plug},'CPU')
|
||||
delete(s.ocv);
|
||||
delete(s.api[cn.id])
|
||||
|
|
10
libs/sql.js
10
libs/sql.js
|
@ -10,6 +10,12 @@ module.exports = function(s,config){
|
|||
if(s.databaseOptions.client.indexOf('sqlite')>-1){
|
||||
s.databaseOptions.client = 'sqlite3';
|
||||
s.databaseOptions.useNullAsDefault = true;
|
||||
try{
|
||||
require('sqlite3')
|
||||
}catch(err){
|
||||
console.log('Installing SQlite3 Module...')
|
||||
require('child_process').execSync('npm install sqlite3 --unsafe-perm')
|
||||
}
|
||||
}
|
||||
if(s.databaseOptions.client === 'sqlite3' && s.databaseOptions.connection.filename === undefined){
|
||||
s.databaseOptions.connection.filename = s.mainDirectory+"/shinobi.sqlite"
|
||||
|
@ -99,6 +105,10 @@ module.exports = function(s,config){
|
|||
},true)
|
||||
}
|
||||
},true)
|
||||
//add Schedules table, will remove in future
|
||||
s.sqlQuery("CREATE TABLE IF NOT EXISTS `Schedules` (`ke` varchar(50) DEFAULT NULL,`name` text,`details` text,`start` varchar(10) DEFAULT NULL,`end` varchar(10) DEFAULT NULL,`enabled` int(1) NOT NULL DEFAULT '1')" + mySQLtail + ';',[],function(err){
|
||||
if(err)console.error(err)
|
||||
},true)
|
||||
//add Cloud Videos table, will remove in future
|
||||
s.sqlQuery('CREATE TABLE IF NOT EXISTS `Cloud Videos` (`mid` varchar(50) NOT NULL,`ke` varchar(50) DEFAULT NULL,`href` text NOT NULL,`size` float DEFAULT NULL,`time` timestamp NULL DEFAULT NULL,`end` timestamp NULL DEFAULT NULL,`status` int(1) DEFAULT \'0\',`details` text)' + mySQLtail + ';',[],function(err){
|
||||
if(err)console.error(err)
|
||||
|
|
|
@ -4,16 +4,15 @@ var moment = require('moment');
|
|||
var crypto = require('crypto');
|
||||
var exec = require('child_process').exec;
|
||||
var execSync = require('child_process').execSync;
|
||||
module.exports = function(s,config,lang,io,processReady){
|
||||
module.exports = function(s,config,lang,io,){
|
||||
console.log('FFmpeg version : '+s.ffmpegVersion)
|
||||
console.log('Node.js version : '+execSync("node -v"))
|
||||
s.processReady = function(){
|
||||
s.systemLog(lang.startUpText5)
|
||||
process.send('ready')
|
||||
s.onProcessReadyExtensions.forEach(function(extender){
|
||||
extender(true)
|
||||
})
|
||||
if(processReady)processReady()
|
||||
process.send('ready')
|
||||
}
|
||||
var loadedAccounts = []
|
||||
var loadMonitors = function(callback){
|
||||
|
|
14
libs/user.js
14
libs/user.js
|
@ -308,4 +308,18 @@ module.exports = function(s,config){
|
|||
}
|
||||
})
|
||||
}
|
||||
s.findPreset = function(presetQueryVals,callback){
|
||||
//presetQueryVals = [ke, type, name]
|
||||
s.sqlQuery("SELECT * FROM Presets WHERE ke=? AND type=? AND name=? LIMIT 1",presetQueryVals,function(err,presets){
|
||||
var preset
|
||||
var notFound = false
|
||||
if(presets && presets[0]){
|
||||
preset = presets[0]
|
||||
s.checkDetails(preset)
|
||||
}else{
|
||||
notFound = true
|
||||
}
|
||||
callback(notFound,preset)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -419,19 +419,7 @@ module.exports = function(s,config,lang,app){
|
|||
s.closeJsonResponse(res,endData)
|
||||
return
|
||||
}
|
||||
var findPreset = function(callback){
|
||||
s.sqlQuery("SELECT * FROM Presets WHERE ke=? AND type=? AND name=? LIMIT 1",[req.params.ke,'monitorStates',req.params.stateName],function(err,presets){
|
||||
var preset
|
||||
var notFound = false
|
||||
if(presets && presets[0]){
|
||||
preset = presets[0]
|
||||
s.checkDetails(preset)
|
||||
}else{
|
||||
notFound = true
|
||||
}
|
||||
callback(notFound,preset)
|
||||
})
|
||||
}
|
||||
var presetQueryVals = [req.params.ke,'monitorStates',req.params.stateName]
|
||||
switch(req.params.action){
|
||||
case'insert':case'edit':
|
||||
var form = s.getPostData(req)
|
||||
|
@ -441,7 +429,7 @@ module.exports = function(s,config,lang,app){
|
|||
s.closeJsonResponse(res,endData)
|
||||
return
|
||||
}
|
||||
findPreset(function(notFound,preset){
|
||||
s.findPreset(presetQueryVals,function(notFound,preset){
|
||||
if(notFound === true){
|
||||
endData.msg = lang["Inserted State Configuration"]
|
||||
var details = {
|
||||
|
@ -478,7 +466,7 @@ module.exports = function(s,config,lang,app){
|
|||
})
|
||||
break;
|
||||
case'delete':
|
||||
findPreset(function(notFound,preset){
|
||||
s.findPreset(presetQueryVals,function(notFound,preset){
|
||||
if(notFound === true){
|
||||
endData.msg = user.lang['State Configuration Not Found']
|
||||
s.closeJsonResponse(res,endData)
|
||||
|
@ -494,43 +482,8 @@ module.exports = function(s,config,lang,app){
|
|||
})
|
||||
break;
|
||||
default://change monitors according to state
|
||||
findPreset(function(notFound,preset){
|
||||
if(notFound === false){
|
||||
var sqlQuery = 'SELECT * FROM Monitors WHERE ke=? AND '
|
||||
var monitorQuery = []
|
||||
var sqlQueryValues = [req.params.ke]
|
||||
var monitorPresets = {}
|
||||
preset.details.monitors.forEach(function(monitor){
|
||||
monitorQuery.push('mid=?')
|
||||
sqlQueryValues.push(monitor.mid)
|
||||
monitorPresets[monitor.mid] = monitor
|
||||
})
|
||||
sqlQuery += '('+monitorQuery.join(' OR ')+')'
|
||||
s.sqlQuery(sqlQuery,sqlQueryValues,function(err,monitors){
|
||||
if(monitors && monitors[0]){
|
||||
monitors.forEach(function(monitor){
|
||||
s.checkDetails(monitor)
|
||||
s.checkDetails(monitorPresets[monitor.mid])
|
||||
var monitorPreset = monitorPresets[monitor.mid]
|
||||
monitorPreset.details = Object.assign(monitor.details,monitorPreset.details)
|
||||
monitor = s.cleanMonitorObjectForDatabase(Object.assign(monitor,monitorPreset))
|
||||
monitor.details = JSON.stringify(monitor.details)
|
||||
s.addOrEditMonitor(Object.assign(monitor,{}),function(err,endData){
|
||||
|
||||
},user)
|
||||
})
|
||||
endData.ok = true
|
||||
s.tx({f:'change_group_state',ke:req.params.ke,name:req.params.stateName},'GRP_'+req.params.ke)
|
||||
s.closeJsonResponse(res,endData)
|
||||
}else{
|
||||
endData.msg = user.lang['State Configuration has no monitors associated']
|
||||
s.closeJsonResponse(res,endData)
|
||||
}
|
||||
})
|
||||
}else{
|
||||
endData.msg = user.lang['State Configuration Not Found']
|
||||
s.closeJsonResponse(res,endData)
|
||||
}
|
||||
s.activateMonitorStates(req.params.ke,req.params.stateName,user,function(endData){
|
||||
s.closeJsonResponse(res,endData)
|
||||
})
|
||||
break;
|
||||
}
|
||||
|
|
29
package.json
29
package.json
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "shinobi",
|
||||
"productName": "Shinobi",
|
||||
"version": "1.0.38",
|
||||
"version": "2.0.0",
|
||||
"description": "CCTV and NVR in Node.js",
|
||||
"main": "camera.js",
|
||||
"bin": "camera.js",
|
||||
"scripts": {
|
||||
"test": "node test.js",
|
||||
"test": "node camera.js test",
|
||||
"start": "chmod +x INSTALL/start.sh && INSTALL/start.sh"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -19,30 +19,33 @@
|
|||
},
|
||||
"homepage": "https://gitlab.com/Shinobi-Systems/Shinobi#readme",
|
||||
"dependencies": {
|
||||
"aws-sdk": "^2.279.1",
|
||||
"backblaze-b2": "^1.0.4",
|
||||
"body-parser": "^1.15.2",
|
||||
"circular-json": "0.3.1",
|
||||
"connection-tester": "^0.1.1",
|
||||
"mp4frag": "^0.0.22",
|
||||
"discord.js": "^11.3.2",
|
||||
"ejs": "^2.5.5",
|
||||
"express": "^4.14.0",
|
||||
"http-proxy": "^1.17.0",
|
||||
"jsonfile": "^3.0.1",
|
||||
"moment": "^2.17.0",
|
||||
"mysql": "^2.12.0",
|
||||
"knex": "^0.14.2",
|
||||
"aws-sdk": "^2.279.1",
|
||||
"pam-diff": "^0.10.2",
|
||||
"pipe2pam": "^0.6.2",
|
||||
"nodemailer": "^4.0.1",
|
||||
"ldapauth-fork": "^4.0.2",
|
||||
"moment": "^2.17.0",
|
||||
"mp4frag": "^0.0.22",
|
||||
"mysql": "^2.12.0",
|
||||
"node-onvif": "^0.1.4",
|
||||
"nodemailer": "^4.0.1",
|
||||
"pam-diff": "^0.10.2",
|
||||
"path": "^0.12.7",
|
||||
"pipe2pam": "^0.6.2",
|
||||
"request": "^2.79.0",
|
||||
"socket.io": "^1.7.1",
|
||||
"socket.io-client": "^1.7.2",
|
||||
"http-proxy": "^1.17.0",
|
||||
"sqlite3": "^4.0.4",
|
||||
"webdav-fs": "^1.11.0",
|
||||
"discord.js": "^11.3.2",
|
||||
"backblaze-b2": "^1.0.4",
|
||||
"ldapauth-fork": "^4.0.2"
|
||||
"sat": "^0.7.1",
|
||||
"shinobi-sound-detection": "^0.1.7"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
|
|
@ -125,6 +125,18 @@ CREATE TABLE IF NOT EXISTS `Files` (
|
|||
`status` int(1) NOT NULL DEFAULT '0'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
ALTER TABLE `Files` ADD COLUMN `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
-- Dumping structure for table ccio.Schedules
|
||||
CREATE TABLE IF NOT EXISTS `Schedules` (
|
||||
`ke` varchar(50) DEFAULT NULL,
|
||||
`name` text,
|
||||
`details` text,
|
||||
`start` varchar(10) DEFAULT NULL,
|
||||
`end` varchar(10) DEFAULT NULL,
|
||||
`enabled` int(1) NOT NULL DEFAULT '1'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
|
||||
|
|
|
@ -12,7 +12,7 @@ processArgv.forEach(function(val) {
|
|||
var theSplit = val.split('=');
|
||||
var index = theSplit[0];
|
||||
var value = theSplit[1];
|
||||
if(index === 'addToConfig'){
|
||||
if(index.indexOf('addToConfig') > -1){
|
||||
try{
|
||||
value = JSON.parse(value)
|
||||
config = Object.assign(config,value)
|
||||
|
@ -36,4 +36,4 @@ processArgv.forEach(function(val) {
|
|||
jsonfile.writeFile(configLocation,config,{spaces: 2},function(){
|
||||
console.log('Changes Complete. Here is what it is now.')
|
||||
console.log(JSON.stringify(config,null,2))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1065,14 +1065,6 @@
|
|||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group h_det_pam_input h_det_pam_1">
|
||||
<label><div><span><%-lang['Show Matrices']%></span></div>
|
||||
<div><select class="form-control" detail="detector_show_matrix">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Indifference']%></span></div>
|
||||
<div><input class="form-control" detail="detector_sensitivity" placeholder="0.5"></div>
|
||||
|
@ -1159,6 +1151,14 @@
|
|||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Require Object to be in Region']%></span></div>
|
||||
<div><select class="form-control" detail="detector_obj_region">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Check for Motion First']%></span></div>
|
||||
<div><select class="form-control" detail="detector_use_motion" selector="h_det_mot_fir">
|
||||
|
@ -1294,6 +1294,29 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Start of Audio Detection -->
|
||||
<div class="form-group-group orange" section id="monSectionAudioDetector">
|
||||
<h4><%-lang['Audio Detector']%></h4>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Enabled']%></span></div>
|
||||
<div><select class="form-control" detail="detector_audio">
|
||||
<option value="0" selected><%-lang.No%></option>
|
||||
<option value="1"><%-lang.Yes%></option>
|
||||
</select></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Minimum dB']%></span></div>
|
||||
<div><input class="form-control" detail="detector_audio_min_db" placeholder="5"></div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label><div><span><%-lang['Maximum dB']%></span></div>
|
||||
<div><input class="form-control" detail="detector_audio_max_db" placeholder=""></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END of Audio Detection --/>
|
||||
<!-- END of Motion -->
|
||||
<div class="form-group-group blue" section id="monSectionControl">
|
||||
<h4><%-lang['Control']%></h4>
|
||||
|
|
Loading…
Reference in New Issue