Add PTZ Preset Patrol with Generic Gamepad

patrol-onvif-ptz-gamepad
Moe 2024-12-26 01:27:53 -08:00
parent ae070a114c
commit cd8d3d4ab2
8 changed files with 179 additions and 14 deletions

View File

@ -74,11 +74,15 @@
"Failed to Edit Account": "Failed to Edit Account",
"How to Connect": "How to Connect",
"Cycle": "Cycle",
"Auto Placement": "Auto Placement",
"Cycle Interval": "Cycle Interval",
"Cycle Monitor Height": "Cycle Monitor Height",
"Number of Cycle Monitors": "Number of Cycle Monitors",
"Rows and Columns": "Rows and Columns",
"Number of Monitors": "Number of Monitors",
"Number of Rows": "Number of Rows",
"Number of Columns": "Number of Columns",
"Cycle Monitors": "Cycle Monitors",
"Cycle Monitors per row": "Cycle Monitors per row",
"General": "General",
"Login": "Login",
"Room ID": "Room ID",
"Substream": "Substream",
@ -170,6 +174,7 @@
"Open All Monitors": "Open All Monitors",
"Open Wall Display": "Open Wall Display",
"New Wall Display": "New Wall Display",
"Wall Display Settings": "Wall Display Settings",
"openWallViewInfo": "Open Monitors in the top right of this window.",
"Accounts": "Accounts",
"Settings": "Settings",
@ -468,6 +473,7 @@
"ONVIF Port": "ONVIF Port",
"ONVIF Scanner": "ONVIF Scanner",
"ONVIF Events": "ONVIF Events",
"ONVIFNotEnabled": "ONVIF Not Enabled in Monitor Settings",
"ONVIFEventsNotAvailable": "ONVIF Events not Available",
"ONVIFEventsNotAvailableText1": "This service may not be available for this camera or ONVIF has not initialized yet.",
"ONVIFnotCompliantProfileT": "Camera is not ONVIF Profile T Compliant",

View File

@ -9,6 +9,10 @@ module.exports = function(s,config,lang,app,io){
createSnapshot,
addCredentialsToStreamLink,
} = require('../monitor/utils.js')(s,config,lang)
const {
startPatrolPresets,
stopPatrolPresets,
} = require('../onvifDeviceManager/utils.js')(s,config,lang)
const createOnvifDevice = async (onvifAuth) => {
var response = {ok: false}
const monitorConfig = s.group[onvifAuth.ke].rawMonitorConfigurations[onvifAuth.id]
@ -43,6 +47,14 @@ module.exports = function(s,config,lang,app,io){
})
return newOptions
}
const getOnvifDevice = async (groupKey, monitorId) => {
const onvifDevice = s.group[groupKey].activeMonitors[monitorId].onvifConnection;
if(!onvifDevice){
return (await createOnvifDevice(onvifAuth)).device
}else{
return onvifDevice
}
}
const runOnvifMethod = (onvifOptions,callback) => {
return new Promise((resolve) => {
var onvifAuth = onvifOptions.auth
@ -181,6 +193,52 @@ module.exports = function(s,config,lang,app,io){
})
},res,req);
})
/**
* API : ONVIF Start Patrol
*/
app.post(config.webPaths.apiPrefix+':auth/onvifStartPatrol/:ke/:id',function (req,res){
s.auth(req.params, async function(user){
const endData = { ok: true }
try{
const groupKey = req.params.ke;
const monitorId = req.params.id;
const onvifEnabled = s.group[groupKey].rawMonitorConfigurations[monitorId].details.is_onvif === '1';
if(onvifEnabled){
const patrolId = `${groupKey}_${monitorId}`;
const onvifDevice = await getOnvifDevice(groupKey, monitorId);
const startingPresetToken = s.getPostData(req,'startingPresetToken');
const patrolIndexTimeout = s.getPostData(req,'patrolIndexTimeout');
const speed = s.getPostData(req,'speed');
await startPatrolPresets(patrolId, onvifDevice, startingPresetToken, patrolIndexTimeout, speed)
}else{
endData.ok = false;
endData.err = lang.ONVIFNotEnabled;
}
}catch(err){
endData.ok = false;
endData.err = err.toString()
}
s.closeJsonResponse(res,endData)
},res,req);
})
/**
* API : ONVIF Stop Patrol
*/
app.get(config.webPaths.apiPrefix+':auth/onvifStopPatrol/:ke/:id',function (req,res){
s.auth(req.params, async function(user){
const endData = { ok: true }
try{
const groupKey = req.params.ke;
const monitorId = req.params.id;
const patrolId = `${groupKey}_${monitorId}`;
await stopPatrolPresets(patrolId)
}catch(err){
endData.ok = false;
endData.err = err.toString()
}
s.closeJsonResponse(res,endData)
},res,req);
})
s.getSnapshotFromOnvif = getSnapshotFromOnvif
s.createOnvifDevice = createOnvifDevice
s.runOnvifMethod = runOnvifMethod

View File

@ -1,4 +1,5 @@
// relies on https://gitlab.com/Shinobi-Systems/shinobi-onvif
const currentlyPatrolling = {};
const {
mergeDeep
} = require('../common.js')
@ -498,6 +499,65 @@ const getUIFieldValues = async (onvifDevice) => {
all: true,
})
}
const getPresets = async (onvifDevice, asObject = false, profileToken = "__CURRENT_TOKEN") => {
const presets = (await runOnvifMethod({
device: onvifDevice,
action: 'getPresets',
service: 'ptz',
options: { "ProfileToken": profileToken }
})).responseFromDevice.GetPresetsResponse.Preset.map((item) => {
return { token: item.$.token, name: item.Name }
});
if(asObject){
const theObject = {};
for(preset of presets){
theObject[preset.token] = preset
}
return theObject
}
return presets
}
const goToPreset = async (onvifDevice, presetToken, speed = 1) => {
const response = (await runOnvifMethod({
device: onvifDevice,
action: 'getPresets',
service: 'ptz',
options: {
"ProfileToken":"__CURRENT_TOKEN",
"PresetToken": presetToken,
"Speed": { x: speed, y: speed, z: speed }
}
})).responseFromDevice;
return response
}
const getNextPresetToken = (presetsAsObject, presetToken) => {
const nextToken = parseInt(presetToken) + 1;
const chosenToken = presetsAsObject[nextToken] ? `${nextToken}` : Object.values(presets)[0].token
return chosenToken
}
const startPatrolPresets = async (patrolId, onvifDevice, startingPresetToken, patrolIndexTimeout = 5000, speed = 1) => {
await stopPatrolPresets(patrolId)
const presets = await getPresets(onvifDevice, true);
await goToPreset(onvifDevice, startingPresetToken, speed)
const nextFromStartToken = getNextPresetToken(presets, startingPresetToken)
const moveToPresetOnTimeout = (presetToken) => {
currentlyPatrolling[patrolId] = setTimeout(async () => {
await goToPreset(onvifDevice, presetToken, speed)
const nextToken = getNextPresetToken(presets, presetToken)
moveToPresetOnTimeout(nextToken)
}, patrolIndexTimeout)
}
moveToPresetOnTimeout(nextFromStartToken)
}
const stopPatrolPresets = (patrolId) => {
return new Promise((resolve) => {
clearTimeout(currentlyPatrolling[patrolId])
setTimeout(() => {
clearTimeout(currentlyPatrolling[patrolId])
resolve()
},1000)
})
}
module.exports = {
getDeviceInformation: getDeviceInformation,
setHostname: setHostname,
@ -514,4 +574,9 @@ module.exports = {
setDiscoveryMode: setDiscoveryMode,
setNetworkInterface: setNetworkInterface,
getUIFieldValues: getUIFieldValues,
getPresets,
goToPreset,
getNextPresetToken,
startPatrolPresets,
stopPatrolPresets,
}

View File

@ -160,6 +160,14 @@ $(document).ready(function() {
runPtzCommand(selectedMonitor, 'center')
}
function startPatrol(){
return onvifStartPatrol(selectedMonitor)
}
function stopPatrol(){
return onvifStopPatrol(selectedMonitor)
}
function translatePointTiltStick(x, y){
if(x > stickBase && !lastPtzDirection['right']){
lastPtzDirection['right'] = true
@ -264,10 +272,9 @@ $(document).ready(function() {
const gp = navigator.getGamepads()[0];
getButtonsPressed(gp, function(buttonCode){
if(buttonCode == 10){
closeSnapshot()
openSnapshot()
startPatrol()
}else if(buttonCode == 11){
closeSnapshot()
stopPatrol()
}else{
buttonPressAction(buttonCode)
}

View File

@ -0,0 +1,18 @@
function onvifStartPatrol(monitorId, startingPresetToken = '1', patrolIndexTimeout = 5000, speed = 1){
return new Promise((resolve) => {
$.post(getApiPrefix('onvifStartPatrol') + '/' + monitorId, {
startingPresetToken,
patrolIndexTimeout,
speed,
},function(response){
resolve(response)
})
})
}
function onvifStopPatrol(monitorId){
return new Promise((resolve) => {
$.get(getApiPrefix('onvifStopPatrol') + '/' + monitorId,function(response){
resolve(response)
})
})
}

View File

@ -134,14 +134,16 @@ $(document).ready(function() {
}
}
function setCameraFromButtonCode(buttonCode = 0, preAdded){
function getMonitorFromButtonCode(buttonCode = 0, preAdded){
const addedOneToButtonCode = preAdded ? buttonCode : parseInt(buttonCode) + 1
const monitor = loadedMonitors[monitorKeys[addedOneToButtonCode]];
const monitorId = monitor.mid;
return { monitor, monitorId }
}
function setCameraFromButtonCode(buttonCode = 0, preAdded){
try{
console.log('addedOneToButtonCode', addedOneToButtonCode)
console.log('monitorKeys', monitorKeys)
const monitor = loadedMonitors[monitorKeys[addedOneToButtonCode]];
console.log('monitor', monitor)
const monitorId = monitor.mid;
const { monitor, monitorId } = getMonitorFromButtonCode(buttonCode, preAdded)
const isFullscreened = !!document.fullscreenElement;
if(isFullscreened) {
document.exitFullscreen()
@ -180,6 +182,14 @@ $(document).ready(function() {
runPtzCommand(selectedMonitor, 'center')
}
function startPatrol(){
return onvifStartPatrol(selectedMonitor)
}
function stopPatrol(){
return onvifStopPatrol(selectedMonitor)
}
function translatePointTiltStick(x, y){
if(x > stickBase && !lastPtzDirection['right']){
lastPtzDirection['right'] = true
@ -286,10 +296,9 @@ $(document).ready(function() {
const gp = navigator.getGamepads()[0];
getButtonsPressed(gp, function(buttonCode){
if(buttonCode == 10){
// closeSnapshot()
startPatrol()
}else if(buttonCode == 11){
// closeSnapshot()
// openSnapshot()
stopPatrol()
}else{
buttonPressAction(buttonCode)
}

View File

@ -37,6 +37,7 @@
<script src="<%-window.libURL%>assets/js/bs5.monitorsUtils.js"></script>
<script src="<%-window.libURL%>assets/js/bs5.monitorStates.js"></script>
<script src="<%-window.libURL%>assets/js/bs5.schedules.js"></script>
<script src="<%-window.libURL%>assets/js/bs5.onvif.js"></script>
<script src="<%-window.libURL%>assets/js/bs5.liveGrid.js"></script>
<script src="<%-window.libURL%>assets/js/bs5.liveGrid.cycle.js"></script>
<script src="<%-window.libURL%>assets/js/bs5.liveGrid.gamepad.js"></script>

View File

@ -92,5 +92,6 @@
<script src="<%- urlPrefix %>assets/js/bs5.extenders.js"></script>
<script src="<%- urlPrefix %>assets/js/bs5.websocket.js"></script>
<script src="<%- urlPrefix %>assets/js/bs5.wallview.js"></script>
<script src="<%- urlPrefix %>assets/js/bs5.onvif.js"></script>
<script src="<%- urlPrefix %>assets/js/bs5.wallview.gamepad.js"></script>
<script src="<%- urlPrefix %>assets/js/bs5.wallview.cycle.js"></script>