diff --git a/definitions/base.js b/definitions/base.js
index 8e2f1e41..ac661d6e 100644
--- a/definitions/base.js
+++ b/definitions/base.js
@@ -3151,12 +3151,30 @@ module.exports = function(s,config,lang){
}
]
},
+ {
+ hidden: true,
+ "id": "detectorsSelected",
+ "name": "detail=detectors_selected",
+ "field": lang["Detectors Selected"],
+ "description": lang.fieldTextDetectorsSelected,
+ "default": "all",
+ "attribute": "multiple",
+ "fieldType": "select",
+ "form-group-class": "h_casc_input h_casc_1",
+ "possible": [
+ {
+ "name": `${lang.All} (${lang.Default})`,
+ "value": "all"
+ }
+ ]
+ },
{
"name": "detail=detector_object_ignore_not_move",
"field": lang["Ignore Non-Moving"],
"default": "0",
"fieldType": "select",
"selector": "h_obj_ignore_move",
+ "form-group-class": "h_casc_input h_casc_1",
"possible": [
{
"name": lang.No,
@@ -3183,6 +3201,7 @@ module.exports = function(s,config,lang){
"description": lang["fieldTextDetectorSendFramesObject"],
"default": "1",
"fieldType": "select",
+ "form-group-class": "h_casc_input h_casc_1",
"possible": [
{
"name": lang.No,
@@ -3222,6 +3241,7 @@ module.exports = function(s,config,lang){
"default": "1",
"example": "",
"fieldType": "select",
+ "form-group-class": "h_casc_input h_casc_1",
"possible": [
{
"name": lang.No,
@@ -3242,6 +3262,7 @@ module.exports = function(s,config,lang){
"example": "",
"selector": "h_det_mot_fir",
"fieldType": "select",
+ "form-group-class": "h_casc_input h_casc_1",
"possible": [
{
"name": lang.No,
@@ -3290,7 +3311,6 @@ module.exports = function(s,config,lang){
]
},
{
- isAdvanced: true,
hidden: true,
"name": lang['Event-Based Recording'],
"input-mapping": "detector_sip_buffer",
diff --git a/languages/en_CA.json b/languages/en_CA.json
index 58583bb6..7eb947c6 100644
--- a/languages/en_CA.json
+++ b/languages/en_CA.json
@@ -544,6 +544,7 @@
"Time-lapse Tool": "Time-lapse Tool",
"total": "total",
"MB": "MB",
+ "All": "All",
"Calendar": "Calendar",
"Leave blank for random.": "Leave blank for random.",
"Currently viewing": "Currently viewing",
@@ -697,6 +698,7 @@
"Minutes": "Minutes",
"Custom": "Custom",
"Detector": "Detector",
+ "Detectors Selected": "Detectors Selected",
"Audio Detector": "Audio Detector",
"Audio Detection": "Audio Detection",
"Minimum dB": "Minimum dB",
@@ -1218,6 +1220,7 @@
"Preview": "Preview",
"Websocket Connected": "Websocket Connected",
"Websocket Disconnected": "Websocket Disconnected",
+ "Disconnected": "Disconnected",
"Videos Merge": "Videos Merge",
"Channel ID": "Channel ID",
"Recipient ID": "Recipient ID",
@@ -1588,6 +1591,7 @@
"MQTT Client": "MQTT Client",
"Buffer Time from Event": "Buffer Time from Event",
"detected": "detected",
+ "fieldTextDetectorsSelected": "Select which detectors to send frames to.",
"fieldTextEventFilters": "Enable to have all Events honor your Event Filter rules.",
"fieldTextBufferTimeFromEvent": "The amount of seconds to record before the trigger happened. If this is consistently inaccurate you will need to look at the optimization guide or force encoding on the server.",
"fieldTextMode": "This is the primary task of the monitor.",
@@ -1932,6 +1936,8 @@
"HowToConnectDes1": "This feature is available to Mobile License subscribers. To get an API Key please login to your ShinobiShop account and create a key associated to any active Subscription ID. Learn More.",
"HowToConnectDes2": "If you would like to get access to a private (dedicated) P2P server please create an account at the ShinobiShop and contact us via the Live Chat widget",
"User": "User",
+ "Save Unknown Faces": "Save Unknown Faces",
+ "saveUnknownFacesFieldText": "Save Unknown faces to the Face Manager. Manual sorting may still be required.",
"Current Version": "Current Version",
"Default is Global value": "Default is Global value",
"rejectUnauth": "Ignore server certificate"
diff --git a/libs/events/utils.js b/libs/events/utils.js
index 629c5c87..ba86a0a6 100644
--- a/libs/events/utils.js
+++ b/libs/events/utils.js
@@ -40,7 +40,7 @@ module.exports = (s,config,lang) => {
const monitorId = options.mid || options.id
const groupKey = options.ke
//if(!frameBuffer || imageSaveEventLock[groupKey + monitorId])return;
- if(!frameBuffer || frameBuffer.length === 0 || imageSaveEventLock[groupKey + monitorId]) return;
+ if(!frameBuffer || frameBuffer.length === 0 || imageSaveEventLock[groupKey + monitorId]) return;
const eventTime = options.time
const objectsFound = options.matrices
const monitorConfig = Object.assign({id: monitorId},s.group[groupKey].rawMonitorConfigurations[monitorId])
@@ -678,17 +678,37 @@ module.exports = (s,config,lang) => {
const activeMonitor = s.group[groupKey].activeMonitors[monitorId]
const theEmitter = activeMonitor.secondaryDetectorOutput
if(!activeMonitor.sendingFromSecondaryDetectorOuput){
- s.debugLog('start sending object frames',groupKey,monitorId)
- theEmitter.on('data',activeMonitor.secondaryDetectorOuputContentWriter = (data) => {
+ const monitorConfig = s.group[groupKey].rawMonitorConfigurations[monitorId]
+ const monitorDetails = monitorConfig.details;
+ let chosenDetector = monitorDetails.detectors_selected;
+ if(chosenDetector instanceof Array)chosenDetector = chosenDetector.join(',');
+ let sendToDetector = (data) => {
s.ocvTx({
f : 'frame',
- mon : s.group[groupKey].rawMonitorConfigurations[monitorId].details,
+ mon : monitorDetails,
ke : groupKey,
id : monitorId,
time : s.formattedTime(),
frame : data
})
- })
+ }
+ if(chosenDetector && !(chosenDetector.includes('all'))){
+ const pluginsGettingIt = chosenDetector.split(',').map(item => item.trim()).filter(item => !!item);
+ sendToDetector = (data) => {
+ for(pluginName of pluginsGettingIt){
+ s.sendToDetector(pluginName, {
+ f : 'frame',
+ mon : monitorDetails,
+ ke : groupKey,
+ id : monitorId,
+ time : s.formattedTime(),
+ frame : data
+ })
+ }
+ }
+ }
+ s.debugLog('start sending object frames',groupKey,monitorId)
+ theEmitter.on('data', activeMonitor.secondaryDetectorOuputContentWriter = sendToDetector)
}
clearTimeout(activeMonitor.sendingFromSecondaryDetectorOuput)
activeMonitor.sendingFromSecondaryDetectorOuput = setTimeout(() => {
diff --git a/libs/extenders.js b/libs/extenders.js
index 0f8fc0fd..9359de4d 100644
--- a/libs/extenders.js
+++ b/libs/extenders.js
@@ -87,6 +87,8 @@ module.exports = function(s,config){
createExtension(`onSubscriptionCheck`)
createExtension(`onDataPortMessage`)
createExtension(`onHttpRequestUpgrade`,null,true)
+ createExtension(`onPluginConnected`)
+ createExtension(`onPluginDisconnected`)
/////// CRON ////////
createExtension(`onCronGroupProcessed`)
createExtension(`onCronGroupProcessedAwaited`)
diff --git a/libs/monitor/utils.js b/libs/monitor/utils.js
index 8f182dcc..8134ecec 100644
--- a/libs/monitor/utils.js
+++ b/libs/monitor/utils.js
@@ -1186,7 +1186,6 @@ module.exports = (s,config,lang) => {
})
}
if(e.details.detector === '1'){
- s.ocvTx({f:'init_monitor',id:monitorId,ke:groupKey})
//frames from motion detect
if(e.details.detector_pam === '1'){
// activeMonitor.spawn.stdio[3].pipe(activeMonitor.p2p).pipe(activeMonitor.pamDiff)
diff --git a/libs/plugins.js b/libs/plugins.js
index 9cd51d87..fe0cb4b3 100644
--- a/libs/plugins.js
+++ b/libs/plugins.js
@@ -43,18 +43,7 @@ module.exports = function(s,config,lang,app,io){
s.detectorPluginArray = []
s.isAtleatOneDetectorPluginConnected = false
s.addDetectorPlugin = function(name,d){
- if(config.useOldPluginConnectionMethod === true){
- s.ocv = {
- started: s.timeObject(),
- id: d.id,
- plug: d.plug,
- notice: d.notice,
- isClientPlugin: d.isClientPlugin,
- isHostPlugin: d.isHostPlugin,
- connectionType: d.connectionType
- }
- }
- s.connectedDetectorPlugins[d.plug] = {
+ const newDetector = {
started: s.timeObject(),
id: d.id,
plug: d.plug,
@@ -62,15 +51,22 @@ module.exports = function(s,config,lang,app,io){
isClientPlugin: d.isClientPlugin,
isHostPlugin: d.isHostPlugin,
connectionType: d.connectionType
+ };
+ if(config.useOldPluginConnectionMethod === true){
+ s.ocv = newDetector
}
+ s.connectedDetectorPlugins[d.plug] = newDetector
s.resetDetectorPluginArray()
+ s.runExtensionsForArray('onPluginConnected', null, [d.plug, newDetector])
}
s.removeDetectorPlugin = function(name){
+ const theDetector = Object.assign({}, s.connectedDetectorPlugins[name])
if(config.oldPluginConnectionMethod === true && s.ocv && s.ocv.plug === name){
delete(s.ocv)
}
delete(s.connectedDetectorPlugins[name])
s.resetDetectorPluginArray(name)
+ s.runExtensionsForArray('onPluginDisconnected', null, [name, theDetector])
}
s.resetDetectorPluginArray = function(){
pluginArray = []
@@ -164,6 +160,10 @@ module.exports = function(s,config,lang,app,io){
})
}
}
+ s.sendToDetector = function(pluginName, data){
+ const detector = s.connectedPlugins[pluginName];
+ if(detector)detector.tx(data);
+ }
s.sendDetectorInfoToClient = function(data,txFunction){
s.detectorPluginArray.forEach(function(name){
var detectorData = Object.assign(data,{
@@ -363,7 +363,33 @@ module.exports = function(s,config,lang,app,io){
}
}
}
+ function onMonitorUpdate(monitorConfig){
+ // console.log('Sending Monitor Info to Plugin', monitorConfig.mid)
+ s.sendToAllDetectors({ f: 'monitorUpdate', monitorConfig });
+ }
+ function sendCopyOfAllMonitorConfigs(){
+ const groupKeys = Object.keys(s.group);
+ for(groupKey of groupKeys){
+ const monitorConfigs = Object.values(s.group[groupKey].rawMonitorConfigurations);
+ for(monitorConfig of monitorConfigs){
+ onMonitorUpdate(monitorConfig)
+ }
+ }
+ }
+ /**
+ * API : Get List of Connected Plugins
+ */
+ app.get(config.webPaths.apiPrefix+':auth/plugins/list', async (req,res) => {
+ s.auth(req.params, async (resp) => {
+ s.closeJsonResponse(res,{
+ ok: true,
+ plugins: s.connectedDetectorPlugins
+ })
+ },res,req)
+ })
s.onSocketAuthentication(onSocketAuthentication)
s.onWebSocketDisconnection(onWebSocketDisconnection)
s.onWebSocketConnection(onWebSocketConnection)
+ s.onMonitorStart(onMonitorUpdate)
+ s.onPluginConnected(sendCopyOfAllMonitorConfigs)
}
diff --git a/plugins/pluginBase.js b/plugins/pluginBase.js
index b027529c..dbaca12a 100644
--- a/plugins/pluginBase.js
+++ b/plugins/pluginBase.js
@@ -56,8 +56,11 @@ module.exports = function(__dirname, config){
if(!config.hostPort){config.hostPort = 8082}
if(config.systemLog === undefined){config.systemLog = true}
if(config.connectionType === undefined)config.connectionType = 'websocket'
+ const imageBuffers = {}
s = {
group: {},
+ monitors: {},
+ monitorInfo: {},
dir: {},
isWin: (process.platform === 'win32'),
s: (json) => {
@@ -217,16 +220,26 @@ module.exports = function(__dirname, config){
cn.emit('init',{ok:true,plug:config.plug,notice:config.notice,type:config.type})
}
break;
- case'init_monitor':
- retryConnection = 0
- if(s.group[d.ke] && s.group[d.ke][d.id]){
- s.group[d.ke][d.id].numberOfTriggers = 0
- delete(s.group[d.ke][d.id].cords)
- delete(s.group[d.ke][d.id].buffer)
- s.onCameraInitExtensions.forEach((extender) => {
- extender(d,cn,tx)
- })
+ case'monitorUpdate':
+ var monitorConfig = d.monitorConfig;
+ var groupKey = monitorConfig.ke;
+ var monitorId = monitorConfig.mid;
+ var monitorDetails = monitorConfig.details;
+ var monitorKey = `${groupKey}${monitorId}`
+ if(!s.monitors[monitorKey])s.monitors[monitorKey] = Object.assign({}, monitorConfig);
+ var isObjectDetectionSeparate = monitorDetails.detector_use_detect_object === '1'
+ var width = parseFloat(isObjectDetectionSeparate && monitorDetails.detector_scale_x_object ? monitorDetails.detector_scale_x_object : monitorDetails.detector_scale_x)
+ var height = parseFloat(isObjectDetectionSeparate && monitorDetails.detector_scale_y_object ? monitorDetails.detector_scale_y_object : monitorDetails.detector_scale_y)
+ s.monitorInfo[monitorKey] = {
+ isObjectDetectionSeparate,
+ width,
+ height,
}
+ delete(imageBuffers[monitorKey])
+ for(extender of s.onCameraInitExtensions){
+ extender(monitorConfig, cn, tx)
+ }
+ // console.log(monitorId, 'registered', s.monitorInfo[monitorKey])
break;
case'frameFromRam':
if(!s.group[d.ke]){
@@ -239,29 +252,22 @@ module.exports = function(__dirname, config){
break;
case'frame':
try{
- if(!s.group[d.ke]){
- s.group[d.ke]={}
- }
- if(!s.group[d.ke][d.id]){
- s.group[d.ke][d.id] = {}
- s.onCameraInitExtensions.forEach((extender) => {
- extender(d,cn,tx)
- })
- }
- if(!s.group[d.ke][d.id].buffer){
- s.group[d.ke][d.id].buffer = [d.frame];
+ const monitorKey = `${d.id}${d.ke}`;
+ imageBuffers[monitorKey]
+ if(!imageBuffers[monitorKey]){
+ imageBuffers[monitorKey] = [d.frame];
}else{
- s.group[d.ke][d.id].buffer.push(d.frame)
+ imageBuffers[monitorKey].push(d.frame)
}
if(d.frame[d.frame.length-2] === 0xFF && d.frame[d.frame.length-1] === 0xD9){
- var buffer = Buffer.concat(s.group[d.ke][d.id].buffer);
+ var buffer = Buffer.concat(imageBuffers[monitorKey]);
processImage(buffer,d,tx)
- s.group[d.ke][d.id].buffer = null
+ imageBuffers[monitorKey] = null
}
}catch(err){
if(err){
s.systemLog(err)
- delete(s.group[d.ke][d.id].buffer)
+ delete(imageBuffers[monitorKey])
}
}
break;
diff --git a/plugins/pluginWorkerBase.js b/plugins/pluginWorkerBase.js
index 69ae375a..4a14f55d 100644
--- a/plugins/pluginWorkerBase.js
+++ b/plugins/pluginWorkerBase.js
@@ -32,8 +32,11 @@ module.exports = function(__dirname, config){
if(!config.hostPort){config.hostPort = 8082}
if(config.systemLog === undefined){config.systemLog = true}
if(config.connectionType === undefined)config.connectionType = 'websocket'
+ const imageBuffers = {}
s = {
group: {},
+ monitors: {},
+ monitorInfo: {},
dir: {},
isWin: (process.platform === 'win32'),
s: (json) => {
@@ -192,16 +195,26 @@ module.exports = function(__dirname, config){
cn.emit('init',{ok:true,plug:config.plug,notice:config.notice,type:config.type})
}
break;
- case'init_monitor':
- retryConnection = 0
- if(s.group[d.ke] && s.group[d.ke][d.id]){
- s.group[d.ke][d.id].numberOfTriggers = 0
- delete(s.group[d.ke][d.id].cords)
- delete(s.group[d.ke][d.id].buffer)
- s.onCameraInitExtensions.forEach((extender) => {
- extender(d,cn,tx)
- })
+ case'monitorUpdate':
+ var monitorConfig = d.monitorConfig;
+ var groupKey = monitorConfig.ke;
+ var monitorId = monitorConfig.mid;
+ var monitorDetails = monitorConfig.details;
+ var monitorKey = `${groupKey}${monitorId}`
+ if(!s.monitors[monitorKey])s.monitors[monitorKey] = Object.assign({}, monitorConfig);
+ var isObjectDetectionSeparate = monitorDetails.detector_use_detect_object === '1'
+ var width = parseFloat(isObjectDetectionSeparate && monitorDetails.detector_scale_x_object ? monitorDetails.detector_scale_x_object : monitorDetails.detector_scale_x)
+ var height = parseFloat(isObjectDetectionSeparate && monitorDetails.detector_scale_y_object ? monitorDetails.detector_scale_y_object : monitorDetails.detector_scale_y)
+ s.monitorInfo[monitorKey] = {
+ isObjectDetectionSeparate,
+ width,
+ height,
}
+ delete(imageBuffers[monitorKey])
+ for(extender of s.onCameraInitExtensions){
+ extender(monitorConfig, cn, tx)
+ }
+ // console.log(monitorId, 'registered', s.monitorInfo[monitorKey])
break;
case'frameFromRam':
if(!s.group[d.ke]){
@@ -214,29 +227,22 @@ module.exports = function(__dirname, config){
break;
case'frame':
try{
- if(!s.group[d.ke]){
- s.group[d.ke]={}
- }
- if(!s.group[d.ke][d.id]){
- s.group[d.ke][d.id] = {}
- s.onCameraInitExtensions.forEach((extender) => {
- extender(d,cn,tx)
- })
- }
- if(!s.group[d.ke][d.id].buffer){
- s.group[d.ke][d.id].buffer = [d.frame];
+ const monitorKey = `${d.id}${d.ke}`;
+ imageBuffers[monitorKey]
+ if(!imageBuffers[monitorKey]){
+ imageBuffers[monitorKey] = [d.frame];
}else{
- s.group[d.ke][d.id].buffer.push(d.frame)
+ imageBuffers[monitorKey].push(d.frame)
}
if(d.frame[d.frame.length-2] === 0xFF && d.frame[d.frame.length-1] === 0xD9){
- var buffer = Buffer.concat(s.group[d.ke][d.id].buffer);
+ var buffer = Buffer.concat(imageBuffers[monitorKey]);
processImage(buffer,d,tx)
- s.group[d.ke][d.id].buffer = null
+ imageBuffers[monitorKey] = null
}
}catch(err){
if(err){
s.systemLog(err)
- delete(s.group[d.ke][d.id].buffer)
+ delete(imageBuffers[monitorKey])
}
}
break;
diff --git a/web/assets/js/bs5.monitorSettings.js b/web/assets/js/bs5.monitorSettings.js
index 31d220d8..c82101a1 100644
--- a/web/assets/js/bs5.monitorSettings.js
+++ b/web/assets/js/bs5.monitorSettings.js
@@ -15,6 +15,7 @@ var monSectionPresets = $('#monSectionPresets')
var copySettingsSelector = $('#copy_settings')
var monitorPresetsSelection = $('#monitorPresetsSelection')
var monitorPresetsNameField = $('#monitorPresetsName')
+var detectorsSelected = $('#detectorsSelected')
var monitorsList = monitorEditorWindow.find('.monitors_list')
var editorForm = monitorEditorWindow.find('form')
var tagsInput = monitorEditorWindow.find('[name="tags"]')
@@ -566,7 +567,37 @@ function drawInputMapSelectorHtml(options,parent){
`
parent.prepend(html)
}
-function importIntoMonitorEditor(options){
+function getPluginsList(monitorConfig){
+ return new Promise((resolve) => {
+ const chosenDetectors = safeJsonParse(monitorConfig.details).detectors_selected || [];
+ $.get(getApiPrefix() + '/plugins/list',function(data){
+ var plugins = data.plugins || {};
+ var pluginNames = Object.keys(plugins)
+ var disconnectedPlugins = chosenDetectors.filter(item => !pluginNames.includes(item));
+ var html = createOptionHtml({
+ value: 'all',
+ label: `${lang.All} (${lang.Default})`
+ });
+ $.each(plugins, function(name, pluginInfo){
+ html += createOptionHtml({
+ value: name,
+ label: name,
+ selected: chosenDetectors.includes(name),
+ })
+ });
+ $.each(disconnectedPlugins, function(n, name){
+ html += createOptionHtml({
+ value: name,
+ label: `${name} (${lang.Disconnected})`,
+ selected: true,
+ })
+ });
+ detectorsSelected.html(html)
+ resolve(plugins)
+ })
+ })
+}
+async function importIntoMonitorEditor(options){
var monitorConfig = options.values || options
var monitorId = monitorConfig.mid
var monitorDetails = safeJsonParse(monitorConfig.details);
@@ -686,6 +717,9 @@ function importIntoMonitorEditor(options){
}
}
});
+ //
+ await getPluginsList(monitorConfig)
+ //
copySettingsSelector.val('0').change()
var tmp = '';
$.each(loadedMonitors,function(n,monitor){
@@ -1319,9 +1353,11 @@ editorForm.find('[name="type"]').change(function(e){
break;
case'detector_plugged':
addDetectorPlugin(d.plug,d)
+ if(monitorEditorSelectedMonitor)getPluginsList(monitorEditorSelectedMonitor);
break;
case'detector_unplugged':
removeDetectorPlugin(d.plug)
+ if(monitorEditorSelectedMonitor)getPluginsList(monitorEditorSelectedMonitor);
break;
}
})
@@ -1335,6 +1371,7 @@ editorForm.find('[name="type"]').change(function(e){
drawMonitorListToSelector(monitorsList.find('optgroup'),false,'host')
monitorsList.val(theSelected)
checkToOpenSideMenu()
+ if(monitorEditorSelectedMonitor)getPluginsList(monitorEditorSelectedMonitor)
}
addOnTabAway('monitorSettings', function(){
if(isSideBarMenuCollapsed()){
diff --git a/web/assets/js/super.pluginManager.js b/web/assets/js/super.pluginManager.js
index 06b15a87..7d7eb694 100644
--- a/web/assets/js/super.pluginManager.js
+++ b/web/assets/js/super.pluginManager.js
@@ -147,7 +147,11 @@ $(document).ready(function(){
$.post(superApiPrefix + $user.sessionKey + '/plugins/download',{
downloadUrl: url,
packageRoot: packageRoot,
- },callback)
+ },function(data){
+ setTimeout(function(){
+ callback(data)
+ },3000)
+ })
}
})
}