Merge branch 'dev' into 'master'

Another Day Another Delivery

Closes #339

See merge request Shinobi-Systems/Shinobi!332
cron-addstorage-fix
Moe 2021-10-08 23:51:19 +00:00
commit cd10e63427
23 changed files with 1758 additions and 929 deletions

View File

@ -94,6 +94,10 @@ docker run -d --name='Shinobi' -p '8080:8080/tcp' -v "/dev/shm/Shinobi/streams":
> For those using `DB_DISABLE_INCLUDED=true` please remember to create a user in your databse first. The Docker image will create the `DB_DATABASE` under the specified connection information. > For those using `DB_DISABLE_INCLUDED=true` please remember to create a user in your databse first. The Docker image will create the `DB_DATABASE` under the specified connection information.
### Power Video Viewer Blank or Not working
This seems to be an issue with using Docker on some linux OS' like Arch Linux. It is uncertain what the specific issue is but for now please use Docker on a consumer or enterprise supported distribution of linux, like Ubuntu 20.04.
### Tips ### Tips
Modifying `conf.json` or Superuser credentials. Modifying `conf.json` or Superuser credentials.

View File

@ -66,6 +66,8 @@ RUN apt install -y software-properties-common \
tar \ tar \
x264 x264
RUN apt install -y zip
RUN apt install -y \ RUN apt install -y \
ffmpeg \ ffmpeg \
git \ git \

View File

@ -392,8 +392,6 @@ module.exports = function(s,config,lang){
"default": "10", "default": "10",
"example": "", "example": "",
"possible": "", "possible": "",
"form-group-class": "h_t_input h_t_h264 h_t_hls h_t_mp4 h_t_jpeg h_t_mjpeg h_t_local",
"form-group-class-pre-layer": "h_auto_host_input h_auto_host_0 auto_host_fill",
}, },
{ {
"name": "detail=skip_ping", "name": "detail=skip_ping",

File diff suppressed because it is too large Load Diff

View File

@ -1,420 +1,30 @@
var fs = require('fs')
var execSync = require('child_process').execSync
var P2P = require('pipe2pam')
var PamDiff = require('pam-diff')
module.exports = function(jsonData,pamDiffResponder){ module.exports = function(jsonData,pamDiffResponder){
var noiseFilterArray = {}; const {
const groupKey = jsonData.rawMonitorConfig.ke // see libs/detectorUtils.js for more parameters and functions
const monitorId = jsonData.rawMonitorConfig.mid //
const triggerTimer = {} config,
var pamDiff groupKey,
var p2p monitorId,
var regionJson monitorDetails,
try{ completeMonitorConfig,
regionJson = JSON.parse(jsonData.rawMonitorConfig.details.cords) pamDetectorIsEnabled,
}catch(err){ //
regionJson = jsonData.rawMonitorConfig.details.cords attachPamPipeDrivers,
} //
var width, getAcceptedTriggers,
height, getRegionsWithMinimumChange,
globalSensitivity, getRegionsBelowMaximumChange,
globalColorThreshold, getRegionsWithThresholdMet,
fullFrame = false filterTheNoise,
if(jsonData.rawMonitorConfig.details.detector_scale_x===''||jsonData.rawMonitorConfig.details.detector_scale_y===''){ filterTheNoiseFromMultipleRegions,
width = jsonData.rawMonitorConfig.details.detector_scale_x; //
height = jsonData.rawMonitorConfig.details.detector_scale_y; buildDetectorObject,
} buildTriggerEvent,
else{ sendDetectedData,
width = jsonData.rawMonitorConfig.width } = require('./libs/detectorUtils.js')(jsonData,pamDiffResponder)
height = jsonData.rawMonitorConfig.height return function(cameraProcess){
} if(pamDetectorIsEnabled){
if(jsonData.rawMonitorConfig.details.detector_sensitivity===''){ attachPamPipeDrivers(cameraProcess)
globalSensitivity = 10
}else{
globalSensitivity = parseInt(jsonData.rawMonitorConfig.details.detector_sensitivity)
}
if(jsonData.rawMonitorConfig.details.detector_color_threshold===''){
globalColorThreshold = 9
}else{
globalColorThreshold = parseInt(jsonData.rawMonitorConfig.details.detector_color_threshold)
}
globalThreshold = parseInt(jsonData.rawMonitorConfig.details.detector_threshold) || 0
var regionConfidenceMinimums = {}
Object.values(regionJson).forEach(function(region){
// writeToStderr(JSON.stringify(region,null,3))
regionConfidenceMinimums[region.name] = region.sensitivity;
})
var writeToStderr = function(text){
fs.appendFileSync('/home/Shinobi/test.log',text + '\n','utf8')
}
// writeToStderr(JSON.stringify({
// regionConfidenceMinimums: regionConfidenceMinimums
// },null,3))
if(typeof pamDiffResponder === 'function'){
var sendDetectedData = function(detectorObject){
pamDiffResponder(detectorObject)
}
}else{
var sendDetectedData = function(detectorObject){
pamDiffResponder.write(Buffer.from(JSON.stringify(detectorObject)))
}
}
function checkMinimumChange(confidence,minimumChangeRequired){
const amountChanged = confidence
const minimumChange = !isNaN(minimumChangeRequired) ? parseInt(minimumChangeRequired) : 10
if(!isNaN(amountChanged)){
if(amountChanged < minimumChange){
return false
}
}
return true
}
function getRegionsWithMinimumChange(data){
try{
var acceptedTriggers = []
data.trigger.forEach((trigger) => {
if(checkMinimumChange(trigger.percent,regionConfidenceMinimums[trigger.name] || globalSensitivity)){
acceptedTriggers.push(trigger)
}
})
return acceptedTriggers
}catch(err){
// writeToStderr(err.stack)
}
}
function createPamDiffEngine(){
const regionsAreMasks = jsonData.rawMonitorConfig.details.detector_frame !== '1' && jsonData.rawMonitorConfig.details.inverse_trigger === '1';
if(Object.keys(regionJson).length === 0 || jsonData.rawMonitorConfig.details.detector_frame === '1'){
fullFrame = {
name:'FULL_FRAME',
sensitivity: globalSensitivity,
color_threshold: globalColorThreshold,
points:[
[0,0],
[0,height],
[width,height],
[width,0]
]
}
}
const mask = {
max_sensitivity : globalSensitivity,
threshold : globalThreshold,
}
var regions = createPamDiffRegionArray(regionJson,globalColorThreshold,globalSensitivity,fullFrame)
var pamDiffOptions = {
mask: regionsAreMasks,
grayscale: 'luminosity',
regions : regions.forPam,
percent : globalSensitivity,
difference : globalColorThreshold,
response: "bounds"
}
pamDiff = new PamDiff(pamDiffOptions)
p2p = new P2P()
var regionArray = Object.values(regionJson)
if(jsonData.globalInfo.config.detectorMergePamRegionTriggers === true){
// merge pam triggers for performance boost
var buildTriggerEvent = function(trigger){
var detectorObject = {
f:'trigger',
id:monitorId,
ke:groupKey,
name:trigger.name,
details:{
plug:'built-in',
name:trigger.name,
reason:'motion',
confidence:trigger.percent,
matrices: trigger.matrices,
imgHeight:jsonData.rawMonitorConfig.details.detector_scale_y,
imgWidth:jsonData.rawMonitorConfig.details.detector_scale_x
},
}
if(trigger.merged){
if(trigger.matrices)detectorObject.details.matrices = trigger.matrices
var filteredCount = 0
var filteredCountSuccess = 0
trigger.merged.forEach(function(triggerPiece){
var region = regionsAreMasks ? mask : regionArray.find(x => x.name == triggerPiece.name)
checkMaximumSensitivity(region, detectorObject, function(err1) {
checkTriggerThreshold(region, detectorObject, function(err2) {
++filteredCount
if(!err1 && !err2)++filteredCountSuccess
if(filteredCount === trigger.merged.length && filteredCountSuccess > 0){
detectorObject.doObjectDetection = (jsonData.globalInfo.isAtleatOneDetectorPluginConnected && jsonData.rawMonitorConfig.details.detector_use_detect_object === '1')
sendDetectedData(detectorObject)
}
})
})
})
}else{
if(trigger.matrix)detectorObject.details.matrices = [trigger.matrix]
var region = regionsAreMasks ? mask : regionArray.find(x => x.name == detectorObject.name)
checkMaximumSensitivity(region, detectorObject, function(err1) {
checkTriggerThreshold(region, detectorObject, function(err2) {
if(!err1 && !err2){
detectorObject.doObjectDetection = (jsonData.globalInfo.isAtleatOneDetectorPluginConnected && jsonData.rawMonitorConfig.details.detector_use_detect_object === '1')
sendDetectedData(detectorObject)
}
})
})
}
}
if(jsonData.rawMonitorConfig.details.detector_noise_filter==='1'){
Object.keys(regions.notForPam).forEach(function(name){
if(!noiseFilterArray[name])noiseFilterArray[name]=[];
})
pamDiff.on('diff', (data) => {
var filteredCount = 0
var filteredCountSuccess = 0
var acceptedTriggers = getRegionsWithMinimumChange(data)
acceptedTriggers.forEach(function(trigger){
filterTheNoise(noiseFilterArray,regions,trigger,function(err){
++filteredCount
if(!err)++filteredCountSuccess
if(filteredCount === data.trigger.length && filteredCountSuccess > 0){
buildTriggerEvent(mergePamTriggers({trigger: acceptedTriggers}))
}
})
})
})
}else{
pamDiff.on('diff', (data) => {
buildTriggerEvent(mergePamTriggers({trigger: getRegionsWithMinimumChange(data)}))
})
}
}else{
//config.detectorMergePamRegionTriggers NOT true
//original behaviour, all regions have their own event.
var buildTriggerEvent = function(trigger){
var detectorObject = {
f:'trigger',
id: monitorId,
ke: groupKey,
name:trigger.name,
details:{
plug:'built-in',
name:trigger.name,
reason:'motion',
confidence:trigger.percent,
matrices: trigger.matrices,
imgHeight:jsonData.rawMonitorConfig.details.detector_scale_y,
imgWidth:jsonData.rawMonitorConfig.details.detector_scale_x
},
plates:[],
}
if(trigger.matrix)detectorObject.details.matrices = [trigger.matrix]
var region = regionsAreMasks ? mask : Object.values(regionJson).find(x => x.name == detectorObject.name)
checkMaximumSensitivity(region, detectorObject, function(err1) {
checkTriggerThreshold(region, detectorObject, function(err2) {
if(!err1 && ! err2){
detectorObject.doObjectDetection = (jsonData.globalInfo.isAtleatOneDetectorPluginConnected && jsonData.rawMonitorConfig.details.detector_use_detect_object === '1')
sendDetectedData(detectorObject)
}
})
})
}
if(jsonData.rawMonitorConfig.details.detector_noise_filter==='1'){
Object.keys(regions.notForPam).forEach(function(name){
if(!noiseFilterArray[name])noiseFilterArray[name]=[];
})
pamDiff.on('diff', (data) => {
getRegionsWithMinimumChange(data).forEach(function(trigger){
filterTheNoise(noiseFilterArray,regions,trigger,function(){
createMatrixFromPamTrigger(trigger)
buildTriggerEvent(trigger)
})
})
})
}else{
pamDiff.on('diff', (data) => {
getRegionsWithMinimumChange(data).forEach(function(trigger){
createMatrixFromPamTrigger(trigger)
buildTriggerEvent(trigger)
})
})
} }
} }
} }
createPamDiffRegionArray = function(regions,globalColorThreshold,globalSensitivity,fullFrame){
var pamDiffCompliantArray = [],
arrayForOtherStuff = [],
json
try{
json = JSON.parse(regions)
}catch(err){
json = regions
}
if(fullFrame){
json[fullFrame.name]=fullFrame;
}
Object.values(json).forEach(function(region){
if(!region)return false;
region.polygon = [];
region.points.forEach(function(points){
var x = parseFloat(points[0]);
var y = parseFloat(points[1]);
if(x < 0)x = 0;
if(y < 0)y = 0;
region.polygon.push({
x: x,
y: y
})
})
if(region.sensitivity===''){
region.sensitivity = globalSensitivity
}else{
region.sensitivity = parseInt(region.sensitivity)
}
if(region.color_threshold===''){
region.color_threshold = globalColorThreshold
}else{
region.color_threshold = parseInt(region.color_threshold)
}
pamDiffCompliantArray.push({name: region.name, difference: region.color_threshold, percent: region.sensitivity, polygon:region.polygon})
arrayForOtherStuff[region.name] = region;
})
if(pamDiffCompliantArray.length===0){pamDiffCompliantArray = null}
return {forPam:pamDiffCompliantArray,notForPam:arrayForOtherStuff};
}
filterTheNoise = function(noiseFilterArray,regions,trigger,callback){
if(noiseFilterArray[trigger.name].length > 2){
var thePreviousTriggerPercent = noiseFilterArray[trigger.name][noiseFilterArray[trigger.name].length - 1];
var triggerDifference = trigger.percent - thePreviousTriggerPercent;
var noiseRange = jsonData.rawMonitorConfig.details.detector_noise_filter_range
if(!noiseRange || noiseRange === ''){
noiseRange = 6
}
noiseRange = parseFloat(noiseRange)
if(((trigger.percent - thePreviousTriggerPercent) < noiseRange)||(thePreviousTriggerPercent - trigger.percent) > -noiseRange){
noiseFilterArray[trigger.name].push(trigger.percent);
}
}else{
noiseFilterArray[trigger.name].push(trigger.percent);
}
if(noiseFilterArray[trigger.name].length > 10){
noiseFilterArray[trigger.name] = noiseFilterArray[trigger.name].splice(1,10)
}
var theNoise = 0;
noiseFilterArray[trigger.name].forEach(function(v,n){
theNoise += v;
})
theNoise = theNoise / noiseFilterArray[trigger.name].length;
var triggerPercentWithoutNoise = trigger.percent - theNoise;
if(triggerPercentWithoutNoise > regions.notForPam[trigger.name].sensitivity){
callback(null,trigger)
}else{
callback(true)
}
}
checkMaximumSensitivity = function(region, detectorObject, callback) {
var logName = detectorObject.id + ':' + detectorObject.name
var globalMaxSensitivity = parseInt(jsonData.rawMonitorConfig.details.detector_max_sensitivity) || undefined
var maxSensitivity = parseInt(region.max_sensitivity) || globalMaxSensitivity
if (maxSensitivity === undefined || detectorObject.details.confidence <= maxSensitivity) {
callback(null)
} else {
callback(true)
if (triggerTimer[detectorObject.name] !== undefined) {
clearTimeout(triggerTimer[detectorObject.name].timeout)
triggerTimer[detectorObject.name] = undefined
}
}
}
checkTriggerThreshold = function(region, detectorObject, callback){
var threshold = parseInt(region.threshold) || globalThreshold
if (threshold <= 1) {
callback(null)
} else {
if (triggerTimer[detectorObject.name] === undefined) {
triggerTimer[detectorObject.name] = {
count : threshold,
timeout : null
}
}
if (--triggerTimer[detectorObject.name].count == 0) {
callback(null)
clearTimeout(triggerTimer[detectorObject.name].timeout)
triggerTimer[detectorObject.name] = undefined
} else {
callback(true)
var fps = parseFloat(jsonData.rawMonitorConfig.details.detector_fps) || 2
if (triggerTimer[detectorObject.name].timeout !== null)
clearTimeout(triggerTimer[detectorObject.name].timeout)
triggerTimer[detectorObject.name].timeout = setTimeout(function() {
triggerTimer[detectorObject.name] = undefined
}, ((threshold+0.5) * 1000) / fps)
}
}
}
mergePamTriggers = function(data){
if(data.trigger.length > 1){
var n = 0
var sum = 0
var name = []
var matrices = []
data.trigger.forEach(function(trigger){
name.push(trigger.name + ' ('+trigger.percent+'%)')
++n
sum += trigger.percent
createMatrixFromPamTrigger(trigger)
if(trigger.matrix)matrices.push(trigger.matrix)
})
var average = sum / n
name = name.join(', ')
if(matrices.length === 0)matrices = null
var trigger = {
name: name,
percent: parseInt(average),
matrices: matrices,
merged: data.trigger
}
}else{
var trigger = data.trigger[0]
createMatrixFromPamTrigger(trigger)
trigger.matrices = [trigger.matrix]
}
return trigger
}
createMatrixFromPamTrigger = function(trigger){
if(
trigger.minX ||
trigger.maxX ||
trigger.minY ||
trigger.maxY
){
var coordinates = [
{"x" : trigger.minX, "y" : trigger.minY},
{"x" : trigger.maxX, "y" : trigger.minY},
{"x" : trigger.maxX, "y" : trigger.maxY}
]
var width = Math.sqrt( Math.pow(coordinates[1].x - coordinates[0].x, 2) + Math.pow(coordinates[1].y - coordinates[0].y, 2));
var height = Math.sqrt( Math.pow(coordinates[2].x - coordinates[1].x, 2) + Math.pow(coordinates[2].y - coordinates[1].y, 2))
trigger.matrix = {
x: coordinates[0].x,
y: coordinates[0].y,
width: width,
height: height,
tag: trigger.name
}
}
return trigger
}
return function(cameraProcess,fallback){
if(jsonData.rawMonitorConfig.details.detector_pam === '1'){
createPamDiffEngine()
cameraProcess.stdio[3].pipe(p2p).pipe(pamDiff)
}
};
}

View File

@ -0,0 +1,394 @@
const P2P = require('pipe2pam')
const PamDiff = require('pam-diff')
module.exports = function(jsonData,pamDiffResponder){
const noiseFilterArray = {};
const config = jsonData.globalInfo.config
const completeMonitorConfig = jsonData.rawMonitorConfig
const groupKey = completeMonitorConfig.ke
const monitorId = completeMonitorConfig.mid
const monitorDetails = completeMonitorConfig.details
const triggerTimer = {}
let regionJson
try{
regionJson = JSON.parse(monitorDetails.cords)
}catch(err){
regionJson = monitorDetails.cords
}
let fullFrame = null
const pamDetectorIsEnabled = monitorDetails.detector_pam === '1'
const width = parseInt(monitorDetails.detector_scale_x) || 640
const height = parseInt(monitorDetails.detector_scale_y) || 480
const globalSensitivity = parseInt(monitorDetails.detector_sensitivity) || 10
const globalMaxSensitivity = parseInt(monitorDetails.detector_max_sensitivity) || 100
const globalColorThreshold = parseInt(monitorDetails.detector_color_threshold) || 9
const globalThreshold = parseInt(monitorDetails.detector_threshold) || 1
const detectorFrameRate = parseInt(monitorDetails.detector_fps) || 2
const regionsAreMasks = monitorDetails.detector_frame !== '1' && monitorDetails.inverse_trigger === '1';
const regionConfidenceMinimums = {}
const regionConfidenceMaximums = {}
const regionTriggerThresholds = {}
const mergeTriggers = config.detectorMergePamRegionTriggers
if(Object.keys(regionJson).length === 0 || monitorDetails.detector_frame === '1'){
fullFrame = {
name:'FULL_FRAME',
sensitivity: globalSensitivity,
color_threshold: globalColorThreshold,
points:[
[0,0],
[0,height],
[width,height],
[width,0]
]
}
}
const mask = {
name: 'theMask',
max_sensitivity : globalSensitivity,
threshold : globalThreshold,
}
const regions = createPamDiffRegionArray(regionJson,globalColorThreshold,globalSensitivity,fullFrame)
const pamDiffOptions = {
mask: regionsAreMasks,
grayscale: 'luminosity',
regions : regions.forPam,
percent : config.sendDetectionDataWithoutTrigger ? 1 : globalSensitivity,
difference : globalColorThreshold,
response: "blobs",
}
const pamDiff = new PamDiff(pamDiffOptions)
const p2p = new P2P()
Object.values(regionJson).forEach(function(region){
regionConfidenceMinimums[region.name] = parseInt(region.sensitivity) || globalSensitivity;
regionConfidenceMaximums[region.name] = parseInt(region.max_sensitivity) || globalMaxSensitivity;
regionTriggerThresholds[region.name] = parseInt(region.threshold) || globalThreshold;
})
Object.keys(regions.notForPam).forEach(function(name){
if(!noiseFilterArray[name])noiseFilterArray[name] = []
})
if(typeof pamDiffResponder === 'function'){
var sendDetectedData = function(detectorObject){
pamDiffResponder(detectorObject)
}
}else{
var sendDetectedData = function(detectorObject){
pamDiffResponder.write(Buffer.from(JSON.stringify(detectorObject)))
}
}
function logData(...args){
process.logData(JSON.stringify(args))
}
function getRegionsWithMinimumChange(data){
try{
var acceptedTriggers = []
data.forEach((trigger) => {
if(trigger.percent > regionConfidenceMinimums[trigger.name]){
acceptedTriggers.push(trigger)
}
})
return acceptedTriggers
}catch(err){
return []
// process.logData(err.stack)
}
}
function getRegionsBelowMaximumChange(data){
try{
var acceptedTriggers = []
data.forEach((trigger) => {
if(trigger.percent < regionConfidenceMaximums[trigger.name]){
acceptedTriggers.push(trigger)
}
})
return acceptedTriggers
}catch(err){
return []
// process.logData(err.stack)
}
}
function getRegionsWithThresholdMet(data){
try{
var acceptedTriggers = []
data.forEach((trigger) => {
if(checkTriggerThreshold(trigger.name)){
acceptedTriggers.push(trigger)
}
})
return acceptedTriggers
}catch(err){
return []
// process.logData(err.stack)
}
}
function buildDetectorObject(trigger){
return {
f: 'trigger',
id: monitorId,
ke: groupKey,
name: trigger.name,
details: {
plug:'built-in',
name: trigger.name,
reason: 'motion',
confidence:trigger.percent,
matrices: trigger.matrices.filter(matrix => !!matrix),
imgHeight: monitorDetails.detector_scale_y,
imgWidth: monitorDetails.detector_scale_x
}
}
}
function filterTheNoise(trigger,callback){
if(noiseFilterArray[trigger.name].length > 2){
var thePreviousTriggerPercent = noiseFilterArray[trigger.name][noiseFilterArray[trigger.name].length - 1];
var triggerDifference = trigger.percent - thePreviousTriggerPercent;
var noiseRange = monitorDetails.detector_noise_filter_range
if(!noiseRange || noiseRange === ''){
noiseRange = 6
}
noiseRange = parseFloat(noiseRange)
if(((trigger.percent - thePreviousTriggerPercent) < noiseRange)||(thePreviousTriggerPercent - trigger.percent) > -noiseRange){
noiseFilterArray[trigger.name].push(trigger.percent);
}
}else{
noiseFilterArray[trigger.name].push(trigger.percent);
}
if(noiseFilterArray[trigger.name].length > 10){
noiseFilterArray[trigger.name] = noiseFilterArray[trigger.name].splice(1,10)
}
var theNoise = 0;
noiseFilterArray[trigger.name].forEach(function(v,n){
theNoise += v;
})
theNoise = theNoise / noiseFilterArray[trigger.name].length;
var triggerPercentWithoutNoise = trigger.percent - theNoise;
if(triggerPercentWithoutNoise > regions.notForPam[trigger.name].sensitivity){
callback(null,trigger)
}else{
callback(true)
}
}
function filterTheNoiseFromMultipleRegions(acceptedTriggers){
return new Promise((resolve,reject) => {
let filteredCount = 0
let filteredCountSuccess = 0
acceptedTriggers.forEach(function(trigger,n){
filterTheNoise(trigger,function(err){
++filteredCount
if(!err)++filteredCountSuccess
if(filteredCount === acceptedTriggers.length && filteredCountSuccess > 0){
resolve(true)
}else if(acceptedTriggers.length === n + 1){
resolve(false)
}
})
})
})
}
function getAcceptedTriggers(data){
return getRegionsWithThresholdMet(
getRegionsBelowMaximumChange(
getRegionsWithMinimumChange(data)
)
)
}
function buildTriggerEvent(data){
const detectorObject = buildDetectorObject(data)
sendDetectedData(detectorObject)
}
function attachPamPipeDrivers(cameraProcess,onEvent){
let pamAnalyzer = function(){}
if(typeof onEvent === 'function'){
pamAnalyzer = onEvent
}else{
if(mergeTriggers === true){
// merge pam triggers for performance boost
if(monitorDetails.detector_noise_filter === '1'){
pamAnalyzer = async (data) => {
const acceptedTriggers = getAcceptedTriggers(data.trigger)
const passedFilter = await filterTheNoiseFromMultipleRegions(acceptedTriggers)
if(passedFilter)buildTriggerEvent(mergePamTriggers(acceptedTriggers))
}
}else{
pamAnalyzer = (data) => {
const acceptedTriggers = getAcceptedTriggers(data.trigger)
// logData(acceptedTriggers)
buildTriggerEvent(mergePamTriggers(acceptedTriggers))
}
}
}else{
//config.detectorMergePamRegionTriggers NOT true
//original behaviour, all regions have their own event.
if(monitorDetails.detector_noise_filter === '1'){
pamAnalyzer = (data) => {
getAcceptedTriggers(data.trigger).forEach(function(trigger){
filterTheNoise(trigger,function(){
createMatricesFromBlobs(trigger)
buildTriggerEvent(trigger)
})
})
}
}else{
pamAnalyzer = (data) => {
getAcceptedTriggers(data.trigger).forEach(function(trigger){
createMatricesFromBlobs(trigger)
buildTriggerEvent(trigger)
})
}
}
}
}
pamDiff.on('diff',pamAnalyzer)
cameraProcess.stdio[3].pipe(p2p).pipe(pamDiff)
}
function createPamDiffRegionArray(regions,globalColorThreshold,globalSensitivity,fullFrame){
var pamDiffCompliantArray = [],
arrayForOtherStuff = [],
json
try{
json = JSON.parse(regions)
}catch(err){
json = regions
}
if(fullFrame){
json[fullFrame.name] = fullFrame
}
Object.values(json).forEach(function(region){
if(!region)return false;
region.polygon = [];
region.points.forEach(function(points){
var x = parseFloat(points[0]);
var y = parseFloat(points[1]);
if(x < 0)x = 0;
if(y < 0)y = 0;
region.polygon.push({
x: x,
y: y
})
})
if(region.sensitivity===''){
region.sensitivity = globalSensitivity
}else{
region.sensitivity = parseInt(region.sensitivity)
}
if(region.color_threshold===''){
region.color_threshold = globalColorThreshold
}else{
region.color_threshold = parseInt(region.color_threshold)
}
pamDiffCompliantArray.push({name: region.name, difference: region.color_threshold, percent: region.sensitivity, polygon:region.polygon})
arrayForOtherStuff[region.name] = region;
})
if(pamDiffCompliantArray.length === 0)pamDiffCompliantArray = null;
return {forPam:pamDiffCompliantArray,notForPam:arrayForOtherStuff};
}
function checkTriggerThreshold(triggerLabel){
const threshold = regionTriggerThresholds[triggerLabel] || globalThreshold
if (threshold <= 1) {
return true
} else {
if (triggerTimer[triggerLabel] === undefined) {
triggerTimer[triggerLabel] = {
count : threshold,
timeout : null
}
}
const theTriggerTimerInfo = triggerTimer[triggerLabel]
if (--theTriggerTimerInfo.count == 0) {
clearTimeout(theTriggerTimerInfo.timeout)
theTriggerTimerInfo = undefined
return true
} else {
if (theTriggerTimerInfo.timeout !== null){
clearTimeout(theTriggerTimerInfo.timeout)
}
theTriggerTimerInfo.timeout = setTimeout(function() {
triggerTimer[triggerLabel] = undefined
}, ((threshold+0.5) * 1000) / detectorFrameRate)
return false
}
}
}
function mergePamTriggers(acceptedTriggers){
var n = 0
var sum = 0
var matrices = []
acceptedTriggers.forEach(function(trigger){
++n
sum += trigger.percent
createMatricesFromBlobs(trigger)
if(trigger.matrices)matrices.push(...trigger.matrices)
})
var average = sum / n
if(matrices === null)matrices = []
var trigger = {
name: `multipleRegions`,
percent: parseInt(average),
matrices: matrices,
acceptedTriggers: acceptedTriggers
}
return trigger
}
function getPropertiesFromBlob(data){
const coordinates = [
{"x" : data.minX, "y" : data.minY},
{"x" : data.maxX, "y" : data.minY},
{"x" : data.maxX, "y" : data.maxY}
]
return {
confidence: data.percent,
width: Math.sqrt( Math.pow(coordinates[1].x - coordinates[0].x, 2) + Math.pow(coordinates[1].y - coordinates[0].y, 2)),
height: Math.sqrt( Math.pow(coordinates[2].x - coordinates[1].x, 2) + Math.pow(coordinates[2].y - coordinates[1].y, 2)),
x: coordinates[0].x,
y: coordinates[0].y,
}
}
function createMatricesFromBlobs(trigger){
trigger.matrices = []
trigger.blobs.forEach(function(blob){
const blobProperties = getPropertiesFromBlob(blob)
blobProperties.tag = trigger.name
trigger.matrices.push(blobProperties)
})
return trigger
}
return {
//functions
getRegionsWithMinimumChange,
getRegionsBelowMaximumChange,
getRegionsWithThresholdMet,
buildDetectorObject,
filterTheNoise,
filterTheNoiseFromMultipleRegions,
getAcceptedTriggers,
sendDetectedData,
buildTriggerEvent,
attachPamPipeDrivers,
createPamDiffRegionArray,
checkTriggerThreshold,
mergePamTriggers,
getPropertiesFromBlob,
createMatricesFromBlobs,
logData,
// parameters
pamDetectorIsEnabled,
noiseFilterArray,
config,
completeMonitorConfig,
groupKey,
monitorId,
monitorDetails,
triggerTimer,
regionJson,
width,
height,
globalSensitivity,
globalMaxSensitivity,
globalColorThreshold,
globalThreshold,
detectorFrameRate,
regionsAreMasks,
regionConfidenceMinimums,
regionConfidenceMaximums,
regionTriggerThresholds,
mergeTriggers,
}
}

View File

@ -6,6 +6,7 @@ const isWindows = (process.platform === 'win32' || process.platform === 'win64')
process.send = process.send || function () {}; process.send = process.send || function () {};
var jsonData = JSON.parse(fs.readFileSync(process.argv[3],'utf8')) var jsonData = JSON.parse(fs.readFileSync(process.argv[3],'utf8'))
const config = jsonData.globalInfo.config
const ffmpegAbsolutePath = process.argv[2].trim() const ffmpegAbsolutePath = process.argv[2].trim()
const ffmpegCommandString = jsonData.cmd const ffmpegCommandString = jsonData.cmd
const rawMonitorConfig = jsonData.rawMonitorConfig const rawMonitorConfig = jsonData.rawMonitorConfig
@ -21,6 +22,7 @@ var writeToStderr = function(text){
} }
// fs.appendFileSync('/home/ubuntu/cdn-site/tools/compilers/diycam/Shinobi/test.log',text + '\n','utf8') // fs.appendFileSync('/home/ubuntu/cdn-site/tools/compilers/diycam/Shinobi/test.log',text + '\n','utf8')
} }
process.logData = writeToStderr
if(!process.argv[2] || !process.argv[3]){ if(!process.argv[2] || !process.argv[3]){
return writeToStderr('Missing FFMPEG Command String or no command operator') return writeToStderr('Missing FFMPEG Command String or no command operator')
} }
@ -109,10 +111,8 @@ writeToStderr('Thread Opening')
if(rawMonitorConfig.details.detector === '1' && rawMonitorConfig.details.detector_pam === '1'){ if(rawMonitorConfig.details.detector === '1' && rawMonitorConfig.details.detector_pam === '1'){
try{ try{
const attachPamDetector = require(__dirname + '/detector.js')(jsonData,stdioWriters[3]) const attachPamDetector = require(config.monitorDetectorDaemonPath ? config.monitorDetectorDaemonPath : __dirname + '/detector.js')(jsonData,stdioWriters[3])
attachPamDetector(cameraProcess,(err)=>{ attachPamDetector(cameraProcess)
writeToStderr(err)
})
}catch(err){ }catch(err){
writeToStderr(err.stack) writeToStderr(err.stack)
} }

View File

@ -29,7 +29,7 @@ module.exports = function(s,config,lang,app,io){
Object.keys(options).forEach((key) => { Object.keys(options).forEach((key) => {
const value = options[key] const value = options[key]
if(typeof value === 'string'){ if(typeof value === 'string'){
newOptions[key] = value.replace(/__CURRENT_TOKEN/g,Camera.current_profile.token) newOptions[key] = value.replace(/__CURRENT_TOKEN/g,Camera.current_profile ? Camera.current_profile.token : 'NOTOKEN')
}else if(value !== undefined && value !== null){ }else if(value !== undefined && value !== null){
newOptions[key] = value newOptions[key] = value
} }

View File

@ -371,6 +371,7 @@ module.exports = function(s,config,lang){
} }
const moveCameraPtzToMatrix = function(event,trackingTarget){ const moveCameraPtzToMatrix = function(event,trackingTarget){
if(moveLock[event.ke + event.id])return; if(moveLock[event.ke + event.id])return;
trackingTarget = trackingTarget || 'person'
const imgHeight = event.details.imgHeight const imgHeight = event.details.imgHeight
const imgWidth = event.details.imgWidth const imgWidth = event.details.imgWidth
const thresholdX = imgWidth * 0.125 const thresholdX = imgWidth * 0.125
@ -378,7 +379,7 @@ module.exports = function(s,config,lang){
const imageCenterX = imgWidth / 2 const imageCenterX = imgWidth / 2
const imageCenterY = imgHeight / 2 const imageCenterY = imgHeight / 2
const matrices = event.details.matrices || [] const matrices = event.details.matrices || []
const largestMatrix = getLargestMatrix(matrices.filter(matrix => matrix.tag === (trackingTarget || 'person'))) const largestMatrix = getLargestMatrix(matrices.filter(matrix => trackingTarget.indexOf(matrix.tag) > -1))
// console.log(matrices.find(matrix => matrix.tag === 'person')) // console.log(matrices.find(matrix => matrix.tag === 'person'))
if(!largestMatrix)return; if(!largestMatrix)return;
const monitorConfig = s.group[event.ke].rawMonitorConfigurations[event.id] const monitorConfig = s.group[event.ke].rawMonitorConfigurations[event.id]

View File

@ -221,6 +221,7 @@ module.exports = async (s,config,lang,app,io) => {
case'blocks': case'blocks':
fs.readdir(thirdLevelName,function(err,webFolderContents){ fs.readdir(thirdLevelName,function(err,webFolderContents){
webFolderContents.forEach(function(filename){ webFolderContents.forEach(function(filename){
if(!filename)return;
var fullPath = thirdLevelName + '/' + filename var fullPath = thirdLevelName + '/' + filename
var blockPrefix = '' var blockPrefix = ''
switch(true){ switch(true){

View File

@ -84,7 +84,6 @@ module.exports = function(s,config,lang,app,io){
plug: "dropInEvent", plug: "dropInEvent",
reason: reason reason: reason
}, },
doObjectDetection: (s.isAtleatOneDetectorPluginConnected && s.group[ke].rawMonitorConfigurations[mid].details.detector_use_detect_object === '1')
},config.dropInEventForceSaveEvent) },config.dropInEventForceSaveEvent)
} }
if(search(filename,'.txt')){ if(search(filename,'.txt')){
@ -310,7 +309,6 @@ module.exports = function(s,config,lang,app,io){
plug: "dropInEvent", plug: "dropInEvent",
reason: reasonTag reason: reasonTag
}, },
doObjectDetection: (s.isAtleatOneDetectorPluginConnected && details.detector_use_detect_object === '1')
},config.dropInEventForceSaveEvent) },config.dropInEventForceSaveEvent)
callback() callback()
}) })

View File

@ -347,6 +347,9 @@ module.exports = (s,config,lang,app,io) => {
const runEventExecutions = async (eventTime,monitorConfig,eventDetails,forceSave,filter,d, triggerEvent) => { const runEventExecutions = async (eventTime,monitorConfig,eventDetails,forceSave,filter,d, triggerEvent) => {
const monitorDetails = monitorConfig.details const monitorDetails = monitorConfig.details
const detailString = JSON.stringify(eventDetails) const detailString = JSON.stringify(eventDetails)
if(monitorDetails.detector_ptz_follow === '1'){
moveCameraPtzToMatrix(d,monitorDetails.detector_ptz_follow_target)
}
if(monitorDetails.det_multi_trig === '1'){ if(monitorDetails.det_multi_trig === '1'){
runMultiTrigger(monitorConfig,eventDetails, d, triggerEvent) runMultiTrigger(monitorConfig,eventDetails, d, triggerEvent)
} }
@ -642,9 +645,6 @@ module.exports = (s,config,lang,app,io) => {
didCountingAlready = true didCountingAlready = true
countObjects(d) countObjects(d)
} }
if(monitorDetails.detector_ptz_follow === '1'){
moveCameraPtzToMatrix(d,monitorDetails.detector_ptz_follow_target)
}
if(filter.useLock){ if(filter.useLock){
const passedMotionLock = checkMotionLock(d,monitorDetails) const passedMotionLock = checkMotionLock(d,monitorDetails)
if(!passedMotionLock)return if(!passedMotionLock)return
@ -653,6 +653,12 @@ module.exports = (s,config,lang,app,io) => {
if(!passedObjectInRegionCheck)return if(!passedObjectInRegionCheck)return
// //
d.doObjectDetection = (
eventDetails.reason !== 'object' &&
s.isAtleatOneDetectorPluginConnected &&
monitorDetails.detector_use_detect_object === '1' &&
monitorDetails.detector_use_motion === '1'
);
if(d.doObjectDetection === true){ if(d.doObjectDetection === true){
sendFramesFromSecondaryOutput(d.ke,d.id) sendFramesFromSecondaryOutput(d.ke,d.id)
} }

View File

@ -5,6 +5,11 @@ module.exports = function(s,config){
s.onSocketAuthenticationExtensions.push(callback) s.onSocketAuthenticationExtensions.push(callback)
} }
// //
s.onUserLogExtensions = []
s.onUserLog = function(callback){
s.onUserLogExtensions.push(callback)
}
//
s.loadGroupExtensions = [] s.loadGroupExtensions = []
s.loadGroupExtender = function(callback){ s.loadGroupExtender = function(callback){
s.loadGroupExtensions.push(callback) s.loadGroupExtensions.push(callback)

View File

@ -63,11 +63,18 @@ module.exports = async (s,config,lang,onFinish) => {
} }
},null,3),'utf8') },null,3),'utf8')
var cameraCommandParams = [ var cameraCommandParams = [
__dirname + '/cameraThread/singleCamera.js', config.monitorDaemonPath ? config.monitorDaemonPath : __dirname + '/cameraThread/singleCamera.js',
config.ffmpegDir, config.ffmpegDir,
e.sdir + 'cmd.txt' e.sdir + 'cmd.txt'
] ]
return spawn('node',cameraCommandParams,{detached: true,stdio: stdioPipes}) const cameraProcess = spawn('node',cameraCommandParams,{detached: true,stdio: stdioPipes})
if(config.debugLog === true){
cameraProcess.stderr.on('data',(data) => {
console.log(`${e.ke} ${e.mid}`)
console.log(data.toString())
})
}
return cameraProcess
}catch(err){ }catch(err){
s.systemLog(err) s.systemLog(err)
return null return null

View File

@ -682,7 +682,7 @@ module.exports = (s,config,lang) => {
const videoFps = !isNaN(parseFloat(e.details.stream_fps)) && e.details.stream_fps !== '0' ? parseFloat(e.details.stream_fps) : null const videoFps = !isNaN(parseFloat(e.details.stream_fps)) && e.details.stream_fps !== '0' ? parseFloat(e.details.stream_fps) : null
const inputMap = buildInputMap(e,e.details.input_map_choices.detector_sip_buffer) const inputMap = buildInputMap(e,e.details.input_map_choices.detector_sip_buffer)
const { videoWidth, videoHeight } = validateDimensions(e.details.event_record_scale_x,e.details.event_record_scale_y) const { videoWidth, videoHeight } = validateDimensions(e.details.event_record_scale_x,e.details.event_record_scale_y)
const hlsTime = !isNaN(parseInt(e.details.detector_buffer_hls_list_time)) ? `${parseInt(e.details.detector_buffer_hls_list_time)}` : '2' const hlsTime = !isNaN(parseInt(e.details.detector_buffer_hls_time)) ? `${parseInt(e.details.detector_buffer_hls_time)}` : '2'
const hlsListSize = !isNaN(parseInt(e.details.detector_buffer_hls_list_size)) ? `${parseInt(e.details.detector_buffer_hls_list_size)}` : '4' const hlsListSize = !isNaN(parseInt(e.details.detector_buffer_hls_list_size)) ? `${parseInt(e.details.detector_buffer_hls_list_size)}` : '4'
if(inputMap)outputFlags.push(inputMap) if(inputMap)outputFlags.push(inputMap)
if(e.details.cust_sip_record)outputFlags.push(e.details.cust_sip_record) if(e.details.cust_sip_record)outputFlags.push(e.details.cust_sip_record)

View File

@ -904,15 +904,28 @@ module.exports = function(s,config,lang){
if(e.details.detector_pam === '1'){ if(e.details.detector_pam === '1'){
// s.group[e.ke].activeMonitors[e.id].spawn.stdio[3].pipe(s.group[e.ke].activeMonitors[e.id].p2p).pipe(s.group[e.ke].activeMonitors[e.id].pamDiff) // s.group[e.ke].activeMonitors[e.id].spawn.stdio[3].pipe(s.group[e.ke].activeMonitors[e.id].p2p).pipe(s.group[e.ke].activeMonitors[e.id].pamDiff)
s.group[e.ke].activeMonitors[e.id].spawn.stdio[3].on('data',function(buf){ s.group[e.ke].activeMonitors[e.id].spawn.stdio[3].on('data',function(buf){
let theJson
try{ try{
buf.toString().split('}{').forEach((object,n)=>{ buf.toString().split('}{').forEach((object,n)=>{
var theJson = object theJson = object
if(object.substr(object.length - 1) !== '}')theJson += '}' if(object.substr(object.length - 1) !== '}')theJson += '}'
if(object.substr(0,1) !== '{')theJson = '{' + theJson if(object.substr(0,1) !== '{')theJson = '{' + theJson
try{
var data = JSON.parse(theJson) var data = JSON.parse(theJson)
}catch(err){
var data = JSON.parse(theJson + '}')
}
switch(data.f){
case'trigger':
triggerEvent(data) triggerEvent(data)
break;
case's.tx':
s.tx(data.data,data.to)
break;
}
}) })
}catch(err){ }catch(err){
console.log(theJson)
console.log('There was an error parsing a detector event') console.log('There was an error parsing a detector event')
console.log(err) console.log(err)
} }

View File

@ -13,4 +13,5 @@ module.exports = function(s,config,lang){
require('./notifications/email.js')(s,config,lang,getSnapshot) require('./notifications/email.js')(s,config,lang,getSnapshot)
require('./notifications/discordBot.js')(s,config,lang,getSnapshot) require('./notifications/discordBot.js')(s,config,lang,getSnapshot)
require('./notifications/telegram.js')(s,config,lang,getSnapshot) require('./notifications/telegram.js')(s,config,lang,getSnapshot)
require('./notifications/pushover.js')(s,config,lang,getSnapshot)
} }

View File

@ -0,0 +1,349 @@
var fs = require('fs');
module.exports = function (s, config, lang, getSnapshot) {
const { getEventBasedRecordingUponCompletion } =
require('../events/utils.js')(s, config, lang);
if (config.pushover === true) {
const Pushover = require('pushover-notifications');
try {
const sendMessage = async function (sendBody, files, groupKey) {
var pushover = s.group[groupKey].pushover;
if (!pushover) {
s.userLog(
{ ke: groupKey, mid: '$USER' },
{
type: lang.NotifyErrorText,
msg: lang.DiscordNotEnabledText,
}
);
return;
}
if (pushover && pushover.send) {
try {
var msg = {
// These values correspond to the parameters detailed on https://pushover.net/api
// 'message' is required. All other values are optional.
message: sendBody.description, // required
title: 'Shinobi: ' + sendBody.title,
sound: 'siren',
// we do not support devices here. use group identifiers instead.
// device: 'devicename',
priority: 1,
};
if (files.length > 0) {
// sadly pushover allows only ONE single attachment
msg.file = {
name: files[0].name,
data: files[0].attachment,
};
}
pushover.send(msg, function (err, result) {
if (err) {
throw err;
}
console.log(result);
});
} catch (err) {
s.userLog(
{ ke: groupKey, mid: '$USER' },
{ type: lang.NotifyErrorText, msg: err }
);
}
} else {
s.userLog(
{
ke: groupKey,
mid: '$USER',
},
{
type: lang.NotifyErrorText,
msg: lang['Check the Recipient ID'],
}
);
}
};
const loadPushoverForUser = function (user) {
const userDetails = s.parseJSON(user.details);
if (
!s.group[user.ke].pushover &&
config.pushover === true &&
userDetails.pushover === '1' &&
userDetails.pushover_token !== '' &&
userDetails.pushover_recipient_identifier !== ''
) {
s.group[user.ke].pushover = new Pushover({
user: userDetails.pushover_recipient_identifier,
token: userDetails.pushover_token,
});
}
};
const unloadPushoverForUser = function (user) {
if (
s.group[user.ke].pushover &&
s.group[user.ke].pushover.destroy
) {
s.group[user.ke].pushover.destroy();
}
delete s.group[user.ke].pushover;
};
const onTwoFactorAuthCodeNotificationForPushover = function (r) {
// r = user
if (r.details.factor_pushover === '1') {
sendMessage(
{
title: r.lang['Enter this code to proceed'],
description:
'**' +
s.factorAuth[r.ke][r.uid].key +
'** ' +
r.lang.FactorAuthText1,
},
[],
r.ke
);
}
};
const onEventTriggerForPushover = async (d, filter) => {
const monitorConfig =
s.group[d.ke].rawMonitorConfigurations[d.id];
// d = event object
if (
filter.pushover &&
s.group[d.ke].pushover &&
monitorConfig.details.notify_pushover === '1' &&
!s.group[d.ke].activeMonitors[d.id].detector_pushover
) {
var detector_pushover_timeout;
if (
!monitorConfig.details.detector_pushover_timeout ||
monitorConfig.details.detector_pushover_timeout === ''
) {
detector_pushover_timeout = 1000 * 60 * 10;
} else {
detector_pushover_timeout =
parseFloat(
monitorConfig.details.detector_pushover_timeout
) *
1000 *
60;
}
s.group[d.ke].activeMonitors[d.id].detector_pushover =
setTimeout(function () {
clearTimeout(
s.group[d.ke].activeMonitors[d.id]
.detector_pushover
);
s.group[d.ke].activeMonitors[
d.id
].detector_pushover = null;
}, detector_pushover_timeout);
await getSnapshot(d, monitorConfig);
if (d.screenshotBuffer) {
sendMessage(
{
title: lang.Event + ' - ' + d.screenshotName,
description:
lang.EventText1 + ' ' + d.currentTimestamp,
},
[
{
type: 'photo',
attachment: d.screenshotBuffer,
name: d.screenshotName + '.jpg',
},
],
d.ke
);
}
}
};
const onEventTriggerBeforeFilterForPushover = function (d, filter) {
filter.pushover = true;
};
const onDetectorNoTriggerTimeoutForPushover = function (e) {
//e = monitor object
var currentTime = new Date();
if (e.details.detector_notrigger_pushover === '1') {
var html =
'*' +
lang.NoMotionEmailText2 +
' ' +
(e.details.detector_notrigger_timeout || 10) +
' ' +
lang.minutes +
'.*\n';
html +=
'**' + lang['Monitor Name'] + '** : ' + e.name + '\n';
html += '**' + lang['Monitor ID'] + '** : ' + e.id + '\n';
html += currentTime;
sendMessage(
{
title: lang['"No Motion" Detector'],
description: html,
},
[],
e.ke
);
}
};
const onMonitorUnexpectedExitForPushover = (monitorConfig) => {
if (
monitorConfig.details.notify_pushover === '1' &&
monitorConfig.details.notify_onUnexpectedExit === '1'
) {
const ffmpegCommand =
s.group[monitorConfig.ke].activeMonitors[
monitorConfig.mid
].ffmpeg;
const description =
lang['Process Crashed for Monitor'] +
'\n' +
ffmpegCommand;
const currentTime = new Date();
sendMessage(
{
title:
lang['Process Unexpected Exit'] +
' : ' +
monitorConfig.name,
description: description,
},
[],
monitorConfig.ke
);
}
};
s.loadGroupAppExtender(loadPushoverForUser);
s.unloadGroupAppExtender(unloadPushoverForUser);
s.onTwoFactorAuthCodeNotification(
onTwoFactorAuthCodeNotificationForPushover
);
s.onEventTrigger(onEventTriggerForPushover);
s.onEventTriggerBeforeFilter(onEventTriggerBeforeFilterForPushover);
s.onDetectorNoTriggerTimeout(onDetectorNoTriggerTimeoutForPushover);
s.onMonitorUnexpectedExit(onMonitorUnexpectedExitForPushover);
s.definitions['Monitor Settings'].blocks[
'Notifications'
].info[0].info.push({
name: 'detail=notify_pushover',
field: 'Pushover',
description: '',
default: '0',
example: '',
selector: 'h_det_pushover',
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_pushover !== '0'",
isFormGroupGroup: true,
name: 'Pushover',
color: 'blue',
'section-class': 'h_det_pushover_input h_det_pushover_1',
info: [
{
name: 'detail=detector_pushover_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_pushover',
field: 'Pushover',
default: '1',
example: '',
fieldType: 'select',
possible: [
{
name: lang.No,
value: '0',
},
{
name: lang.Yes,
value: '1',
},
],
});
s.definitions['Account Settings'].blocks['Pushover'] = {
evaluation: "$user.details.use_pushover !== '0'",
name: 'Pushover',
color: 'blue',
info: [
{
name: 'detail=pushover',
selector: 'u_pushover',
field: lang.Enabled,
default: '0',
example: '',
fieldType: 'select',
possible: [
{
name: lang.No,
value: '0',
},
{
name: lang.Yes,
value: '1',
},
],
},
{
hidden: true,
name: 'detail=pushover_token',
fieldType: 'password',
placeholder: 'azGDORePK8gMaC0QOYAMyEEuzJnyUi',
field: lang.Token,
'form-group-class': 'u_pushover_input u_pushover_1',
description: '',
default: '',
example: '',
possible: '',
},
{
hidden: true,
name: 'detail=pushover_recipient_identifier',
placeholder: 'uQiRzpo4DXghDmr9QzzfQu27cmVRsG',
field: lang['Recipient ID'],
'form-group-class': 'u_pushover_input u_pushover_1',
description: '',
default: '',
example: '',
possible: '',
},
],
};
} catch (err) {
console.log(err);
console.log(
'Could not start pushover notifications, please run "npm install pushover-notifications" inside the Shinobi folder.'
);
}
}
};

View File

@ -105,7 +105,17 @@ module.exports = function(s,config,lang){
} }
}) })
} }
s.tx({f:'log',ke:e.ke,mid:e.mid,log:x,time:s.timeObject()},'GRPLOG_'+e.ke); const logEvent = {
f: 'log',
ke: e.ke,
mid: e.mid,
log: x,
time: s.timeObject()
}
s.tx(logEvent,'GRPLOG_'+e.ke);
s.onUserLogExtensions.forEach(function(extender){
extender(logEvent)
})
} }
s.loadGroup = function(e){ s.loadGroup = function(e){
s.loadGroupExtensions.forEach(function(extender){ s.loadGroupExtensions.forEach(function(extender){

View File

@ -1354,7 +1354,6 @@ module.exports = function(s,config,lang,app,io){
} }
break; break;
} }
d.doObjectDetection = (!d.details.matrices || d.details.matrices.length === 0) && (s.isAtleatOneDetectorPluginConnected && details.detector_use_detect_object === '1')
triggerEvent(d) triggerEvent(d)
s.closeJsonResponse(res,{ s.closeJsonResponse(res,{
ok: true, ok: true,

View File

@ -38,6 +38,7 @@
"node-ssh": "^11.1.1", "node-ssh": "^11.1.1",
"node-telegram-bot-api": "^0.52.0", "node-telegram-bot-api": "^0.52.0",
"nodemailer": "^6.4.11", "nodemailer": "^6.4.11",
"node-pushover": "^1.0.0",
"pam-diff": "^1.0.0", "pam-diff": "^1.0.0",
"path": "^0.12.7", "path": "^0.12.7",
"pipe2pam": "^0.6.2", "pipe2pam": "^0.6.2",

View File

@ -18,154 +18,28 @@ wget -O $DIR/package.json https://cdn.shinobi.video/binaries/tensorflow/2.3.0/pa
echo "Removing existing Tensorflow Node.js modules..." echo "Removing existing Tensorflow Node.js modules..."
rm -rf $DIR/node_modules rm -rf $DIR/node_modules
npm install yarn -g --unsafe-perm --force npm install yarn -g --unsafe-perm --force
npm install dotenv
installJetsonFlag=false
installArmFlag=false
installGpuFlag=false
dontCreateKeyFlag=false
while [ ! $# -eq 0 ] npm install @tensorflow/tfjs-backend-cpu@2.3.0 @tensorflow/tfjs-backend-webgl@2.3.0 @tensorflow/tfjs-converter@2.3.0 @tensorflow/tfjs-core@2.3.0 @tensorflow/tfjs-layers@2.3.0 @tensorflow/tfjs-node@2.3.0 --unsafe-perm --force --legacy-peer-deps
do
case "$1" in
--jetson)
installJetsonFlag=true
exit
;;
--arm)
installArmFlag=true
exit
;;
--gpu)
installGpuFlag=true
exit
;;
--dont-create-key)
dontCreateKeyFlag=true
exit
;;
esac
shift
done
if [ "$installJetsonFlag" = true ] && [ "$installArmFlag" = true ]; then
echo "--jetson and --arm cannot both be set. Exiting..."
exit -1
fi
if ([ "$installJetsonFlag" = true ] || [ "$installArmFlag" = true ]) && [ "$installGpuFlag" = true ]; then
echo "--gpu flag cannot be set with --jetson or --arm. Exiting..."
exit -2
fi
nonInteractiveFlag=false
if [ "$installJetsonFlag" = true ] || [ "$installArmFlag" = true ] || [ "$installGpuFlag" = true ]; then
nonInteractiveFlag=true
fi
manualInstallRequirements() {
npm install --unsafe-perm
npm install @tensorflow/tfjs-backend-cpu@2.3.0 @tensorflow/tfjs-backend-webgl@2.3.0 @tensorflow/tfjs-converter@2.3.0 @tensorflow/tfjs-core@2.3.0 @tensorflow/tfjs-layers@2.3.0 @tensorflow/tfjs-node@2.3.0 --unsafe-perm --force
}
runRebuildCpu() {
npm rebuild @tensorflow/tfjs-node --build-addon-from-source --unsafe-perm
}
runRebuildGpu() {
npm rebuild @tensorflow/tfjs-node-gpu --build-addon-from-source --unsafe-perm
}
installJetson() {
installGpuFlag=true
npm install @tensorflow/tfjs-node-gpu@2.3.0 --unsafe-perm npm install @tensorflow/tfjs-node-gpu@2.3.0 --unsafe-perm
customBinaryLocation="node_modules/@tensorflow/tfjs-node-gpu/scripts/custom-binary.json" customBinaryLocation="node_modules/@tensorflow/tfjs-node-gpu/scripts/custom-binary.json"
case cudaCompute in
"33" ) # Nano and TX1
echo '{"tf-lib": "https://cdn.shinobi.video/binaries/tensorflow/2.3.0/libtensorflow.tar.gz"}' > "$customBinaryLocation" echo '{"tf-lib": "https://cdn.shinobi.video/binaries/tensorflow/2.3.0/libtensorflow.tar.gz"}' > "$customBinaryLocation"
;; npm rebuild @tensorflow/tfjs-node-gpu --build-addon-from-source --unsafe-perm
"25" ) # Xavier NX and AGX Xavier
echo '{"tf-lib": "https://cdn.shinobi.video/binaries/tensorflow/2.3.0-xavier/libtensorflow.tar.gz"}' > "$customBinaryLocation"
;;
* ) # default
echo '{"tf-lib": "https://cdn.shinobi.video/binaries/tensorflow/2.3.0/libtensorflow.tar.gz"}' > "$customBinaryLocation"
;;
esac
manualInstallRequirements
chmod -R 777 .
runRebuildGpu
}
installGpuRoute() {
installGpuFlag=true
manualInstallRequirements
npm install @tensorflow/tfjs-node-gpu@2.3.0 --unsafe-perm --force
}
installNonGpuRoute() {
manualInstallRequirements
npm install @tensorflow/tfjs-node@2.3.0 --unsafe-perm --force
runRebuildCpu
}
if [ "$nonInteractiveFlag" = false ]; then
echo "Shinobi - Are you installing on Jetson Nano or Xavier?"
echo "You must be on JetPack 4.4 for this plugin to install!"
echo "(y)es or (N)o"
read armCpu
if [ "$armCpu" = "y" ] || [ "$armCpu" = "Y" ]; then
# echo "Shinobi - Is it a Jetson Nano?"
# echo "You must be on JetPack 4.4 for this plugin to install!"
# echo "(y)es or (N)o"
# read isItJetsonNano
# echo "Shinobi - You may see Unsupported Errors, please wait while patches are applied."
# if [ "$isItJetsonNano" = "y" ] || [ "$isItJetsonNano" = "Y" ]; then
installJetson
# else
# installArm
# fi
else
echo "Shinobi - Do you want to install TensorFlow.js with GPU support? "
echo "You can run this installer again to change it."
echo "(y)es or (N)o"
read nodejsinstall
if [ "$nodejsinstall" = "y" ] || [ "$nodejsinstall" = "Y" ]; then
installGpuRoute
else
installNonGpuRoute
fi
fi
else
if [ "$installJetsonFlag" = true ]; then
installJetson
fi
#
# if [ "$installArmFlag" = true ]; then
# installArm
# fi
if [ "$installGpuFlag" = true ]; then
installGpuRoute
else
installNonGpuRoute
fi
fi
# # npm audit fix --force # # npm audit fix --force
if [ ! -e "$DIR/conf.json" ]; then if [ ! -e "$DIR/conf.json" ]; then
dontCreateKeyFlag=false
echo "Creating conf.json" echo "Creating conf.json"
sudo cp $DIR/conf.sample.json $DIR/conf.json sudo cp $DIR/conf.sample.json $DIR/conf.json
else else
echo "conf.json already exists..." echo "conf.json already exists..."
fi fi
if [ "$dontCreateKeyFlag" = false ]; then
tfjsBuildVal="cpu"
if [ "$installGpuFlag" = true ]; then
tfjsBuildVal="gpu" tfjsBuildVal="gpu"
fi
echo "Adding Random Plugin Key to Main Configuration" echo "Adding Random Plugin Key to Main Configuration"
node $DIR/../../tools/modifyConfigurationForPlugin.js tensorflow key=$(head -c 64 < /dev/urandom | sha256sum | awk '{print substr($1,1,60)}') tfjsBuild=$tfjsBuildVal node $DIR/../../tools/modifyConfigurationForPlugin.js tensorflow key=$(head -c 64 < /dev/urandom | sha256sum | awk '{print substr($1,1,60)}') tfjsBuild=$tfjsBuildVal
fi
echo "TF_FORCE_GPU_ALLOW_GROWTH=true" > "$DIR/.env" echo "TF_FORCE_GPU_ALLOW_GROWTH=true" > "$DIR/.env"
echo "#CUDA_VISIBLE_DEVICES=0,2" >> "$DIR/.env" echo "#CUDA_VISIBLE_DEVICES=0,2" >> "$DIR/.env"

View File

@ -6,7 +6,14 @@ $(document).ready(function(e){
var onvifScannerWindow = $('#onvif_probe') var onvifScannerWindow = $('#onvif_probe')
var scanForm = onvifScannerWindow.find('form'); var scanForm = onvifScannerWindow.find('form');
var outputBlock = onvifScannerWindow.find('.onvif_result'); var outputBlock = onvifScannerWindow.find('.onvif_result');
var checkTimeout var checkTimeout;
function addCredentialsToUri(uri,username,password){
let newUri = `${uri}`
const uriParts = newUri.split('://')
uriParts[1] = `${username}:${password}@${uriParts[1]}`
newUri = uriParts.join('://')
return newUri
}
var setAsLoading = function(appearance){ var setAsLoading = function(appearance){
if(appearance){ if(appearance){
onvifScannerWindow.find('._loading').show() onvifScannerWindow.find('._loading').show()
@ -49,7 +56,7 @@ $(document).ready(function(e){
path: pathLocation.pathname, path: pathLocation.pathname,
protocol: theLocation.protocol, protocol: theLocation.protocol,
details: { details: {
auto_host: streamUrl, auto_host: addCredentialsToUri(streamUrl,currentUsername,currentPassword),
muser: currentUsername, muser: currentUsername,
mpass: currentPassword, mpass: currentPassword,
is_onvif: '1', is_onvif: '1',