diff --git a/definitions/base.js b/definitions/base.js index 2183730d..7e0ecb4c 100644 --- a/definitions/base.js +++ b/definitions/base.js @@ -125,6 +125,13 @@ module.exports = function(s,config,lang){ "fieldType": "select", "possible": s.listOfStorage }, + { + "name": "detail=ptz_id", + "field": lang["PTZ Control ID"], + "example": "1", + "form-group-class": "h_c_input h_c_1", + "description": lang["ptzControlIdFieldText"], + }, { "name": "detail=auto_compress_videos", "field": lang['Compress Completed Videos'], diff --git a/languages/en_CA.json b/languages/en_CA.json index b5c436c6..a1217626 100644 --- a/languages/en_CA.json +++ b/languages/en_CA.json @@ -136,6 +136,8 @@ "Google Drive": "Google Drive", "Invert Y-Axis": "Invert Y-Axis", "Get Code": "Get Code", + "PTZ Control ID": "PTZ Control ID", + "ptzControlIdFieldText": "When using a Control Stick or GamePad you can choose an available number to use.", "PTZ Tracking": "PTZ Tracking", "PTZ Tracking Target": "PTZ Tracking Target", "Event Counts": "Event Counts", diff --git a/web/assets/js/bs5.liveGrid.gamepad.js b/web/assets/js/bs5.liveGrid.gamepad.js new file mode 100644 index 00000000..f1698a9f --- /dev/null +++ b/web/assets/js/bs5.liveGrid.gamepad.js @@ -0,0 +1,369 @@ +$(document).ready(function() { + var selectedController = 0; + var keyLegend = { + "0": "b", + "1": "a", + "2": "y", + "3": "x", + "4": "l", + "5": "r", + "6": "zl", + "7": "zr", + "8": "minus", + "9": "plus", + "10": "l_stick", + "11": "r_stick", + "12": "up", + "13": "down", + "14": "left", + "15": "right", + } + var lastState = { + sticks: { + left: {}, + right: {}, + } + } + var lastPtzDirection = {} + var buttonsPressed = {} + var hasGP = false; + var repGP; + var stickBase = 2048 + var stickMax = 4096 + var deadZoneThreshold = 0.35 + var outerDeadZone = 1.01 + var selectedMonitor = dashboardOptions().gamepadMonitorSelection; + var monitorKeys = {}; + var gp = null; + var onMonitorOpenForGamepad = () => {} + window.setGamepadMonitorSelection = (monitorId) => { + dashboardOptions('gamepadMonitorSelection', monitorId); + selectedMonitor = `${monitorId}`; + } + + function canGame() { + return "getGamepads" in navigator; + } + + function convertStickAxisTo2048(value){ + var newVal = parseInt((stickMax - stickBase) * value + stickBase) + return newVal + } + + function getAnalogStickValues(gp, i, callback){ + var label = i === 0 ? 'left' : 'right' + var horizontal = gp.axes[i] * outerDeadZone + var vertical = gp.axes[i + 1] * outerDeadZone + var newH = convertStickAxisTo2048(horizontal > deadZoneThreshold || horizontal < -deadZoneThreshold ? horizontal : 0) + var newV = convertStickAxisTo2048((vertical > deadZoneThreshold || vertical < -deadZoneThreshold ? vertical : 0) * -1) + if( + newH !== lastState.sticks[label].h || + newV !== lastState.sticks[label].v + ){ + callback(label, newH, newV) + } + lastState.sticks[label].h = newH + lastState.sticks[label].v = newV + } + + function getStickValue(gp, i, callback){ + var label = `axis${axis}`; + var axis = gp.axes[i] * outerDeadZone + var newH = convertStickAxisTo2048(axis > deadZoneThreshold || axis < -deadZoneThreshold ? axis : 0) + if(newH !== lastState[label]){ + callback(newH) + } + lastState[label] = newH + } + + function getButtonsPressed(gp, callback, offCallback = () => {}){ + $.each(keyLegend,function(code,key){ + if(gp.buttons[code] && gp.buttons[code].pressed){ + if(!lastState[key]){ + buttonsPressed[code] = true; + callback(code) + } + lastState[key] = true + }else{ + if(lastState[key]){ + buttonsPressed[code] = false; + offCallback(code) + } + lastState[key] = false + } + }) + } + + function setCameraFromButtonCode(buttonCode){ + try{ + const addedOneToButtonCode = parseInt(buttonCode) + 1 + const monitor = loadedMonitors[monitorKeys[addedOneToButtonCode]]; + const isFullscreened = !!document.fullscreenElement; + if(isFullscreened) { + document.exitFullscreen() + closeAllLiveGridPlayers(true) + } + openMonitorInLiveGrid(monitor.mid, function(){ + if(isFullscreened) { + fullScreenLiveGridStreamById(monitor.mid) + } + }) + + }catch(err){ + console.log('No Monitor Associated :', buttonCode) + } + } + + // function setCameraFromButtonNumbers(){ + // const buttons = Object.keys(buttonsPressed).filter(code => buttonsPressed[code]); + // console.log('pressed', buttons) + // if(buttons.length > 1){ + // + // }else if(buttons.length > 0){ + // const monitor = loadedMonitors[monitorKeys[buttons[0]]]; + // console.log(monitorKeys[buttons[0]]) + // openMonitorInLiveGrid(monitor.mid) + // } + // } + + function openMonitorInLiveGrid(monitorId, callback){ + lastPtzDirection = {}; + setGamepadMonitorSelection(monitorId) + mainSocket.f({ + f: 'monitor', + ff: 'watch_on', + id: monitorId + }) + onMonitorOpenForGamepad = (monitorId) => { + setTimeout(() => { + if(monitorId === selectedMonitor){ + onMonitorOpenForGamepad = () => {} + if(callback)callback() + } + }, 200) + } + } + + function sendPtzCommand(direction, doMove){ + runPtzMove(selectedMonitor, direction, doMove) + } + + function sentPtzToHome(){ + runPtzCommand(selectedMonitor, 'center') + } + + function translatePointTiltStick(x, y){ + if(x > stickBase && !lastPtzDirection['right']){ + lastPtzDirection['right'] = true + lastPtzDirection['left'] = false + // sendPtzCommand('left', false) + sendPtzCommand('right', true) + }else if(x < stickBase && !lastPtzDirection['left']){ + lastPtzDirection['left'] = true + lastPtzDirection['right'] = false + // sendPtzCommand('right', false) + sendPtzCommand('left', true) + }else if(x === stickBase){ + if(lastPtzDirection['right'])sendPtzCommand('right', false) + if(lastPtzDirection['left'])sendPtzCommand('left', false) + lastPtzDirection['right'] = false + lastPtzDirection['left'] = false + } + if(y > stickBase && !lastPtzDirection['up']){ + lastPtzDirection['up'] = true + lastPtzDirection['down'] = false + // sendPtzCommand('down', false) + sendPtzCommand('up', true) + }else if(y < stickBase && !lastPtzDirection['down']){ + lastPtzDirection['down'] = true + lastPtzDirection['up'] = false + // sendPtzCommand('up', false) + sendPtzCommand('down', true) + }else if(y === stickBase){ + if(lastPtzDirection['up'])sendPtzCommand('up', false) + if(lastPtzDirection['down'])sendPtzCommand('down', false) + lastPtzDirection['down'] = false + lastPtzDirection['up'] = false + } + console.log(lastPtzDirection) + } + + function translateZoomAxis(value){ + if(value > stickBase && !lastPtzDirection['zoom_in']){ + lastPtzDirection['zoom_in'] = true + lastPtzDirection['zoom_out'] = false + // sendPtzCommand('zoom_out', false) + sendPtzCommand('zoom_in', true) + }else if(value < stickBase && !lastPtzDirection['zoom_out']){ + lastPtzDirection['zoom_out'] = true + lastPtzDirection['zoom_in'] = false + // sendPtzCommand('zoom_in', false) + sendPtzCommand('zoom_out', true) + }else if(value === stickBase){ + if(lastPtzDirection['zoom_in'])sendPtzCommand('zoom_in', false) + if(lastPtzDirection['zoom_out'])sendPtzCommand('zoom_out', false) + lastPtzDirection['zoom_in'] = false + lastPtzDirection['zoom_out'] = false + } + } + + function reportOnXboxGamepad() { + try{ + var gp = navigator.getGamepads()[0]; + getButtonsPressed(gp, function(buttonCode){ + if(buttonCode == 6){ + sendPtzCommand('zoom_out', true) + }else if(buttonCode == 7){ + sendPtzCommand('zoom_in', true) + }else if(buttonCode == 8){ + closeSnapshot() + openSnapshot() + }else if(buttonCode == 9){ + sentPtzToHome() + }else if(buttonCode == 11){ + if (!document.fullscreenElement) { + fullScreenLiveGridStreamById(selectedMonitor) + }else{ + document.exitFullscreen() + } + }else{ + setCameraFromButtonCode(buttonCode) + } + }, function(buttonCode){ + if(buttonCode == 6){ + sendPtzCommand('zoom_out', false) + }else if(buttonCode == 7){ + sendPtzCommand('zoom_in', false) + } + }) + getAnalogStickValues(gp, 0, function(stick, x, y){ + translatePointTiltStick(x, y) + }) + getAnalogStickValues(gp, 2, function(stick, x, y){ + translateZoomAxis(y) + }) + }catch(err){ + console.log(err) + // stopReporting() + } + } + + function reportOnGenericGamepad() { + try{ + const gp = navigator.getGamepads()[0]; + getButtonsPressed(gp, function(buttonCode){ + if(buttonCode == 10){ + closeSnapshot() + }else if(buttonCode == 11){ + closeSnapshot() + openSnapshot() + }else{ + setCameraFromButtonCode(buttonCode) + } + },function(buttonCode){ + + }) + getStickValue(gp, 2,function(value){ + translateZoomAxis(value) + }) + }catch(err){ + console.log(err) + // stopReporting() + } + } + + function openSnapshot(){ + if(!$.confirm.e.is(':visible')){ + getSnapshot(loadedMonitors[selectedMonitor],function(url){ + $.confirm.create({ + title: lang.Snapshot, + body: ``, + clickOptions: { + class: 'btn-primary', + title: lang.Close, + }, + clickCallback: async function(){} + }) + }) + } + } + function closeSnapshot(){ + $.confirm.e.modal('hide') + } + + var reportOnGamepad = reportOnXboxGamepad; + + function startReporting(){ + if(hasGP){ + console.log('Reading Gamepad') + var gp = navigator.getGamepads()[0]; + repGP = window.setInterval(reportOnGamepad,200); + } + } + + function stopReporting(){ + console.log('Stopping Gamepad') + window.clearInterval(repGP) + } + + function generateMonitorKeysFromPtzIds(){ + monitorKeys = [] + Object.values(loadedMonitors) + .filter(item => !!parseInt(item.details.ptz_id)) + .sort((a, b) => parseInt(b.details.ptz_id) - parseInt(a.details.ptz_id)) + .forEach((item) => { + console.log(item.details.ptz_id) + monitorKeys[item.details.ptz_id] = item.mid; + }); + console.log(monitorKeys) + } + + function setControllerType(gamepadId){ + switch(true){ + case gamepadId.includes('Xbox'): + reportOnGamepad = reportOnXboxGamepad + break; + } + } + + if(canGame()) { + $(window).on("gamepadconnected", function(e) { + hasGP = true; + if(tabTree.name === 'liveGrid'){ + startReporting() + } + const gamepadName = e.originalEvent.gamepad.id; + setControllerType(gamepadName) + console.log('Gamepad Connected!', gamepadName) + }) + .on("gamepaddisconnected", function() { + if(!navigator.getGamepads()[0]){ + hasGP = false; + console.log('Gamepad Disconnected!') + } + }) + } + onDashboardReady(function(d){ + generateMonitorKeysFromPtzIds(); + }) + onWebSocketEvent(function(d){ + switch(d.f){ + case'monitor_edit': + generateMonitorKeysFromPtzIds(); + break; + case'monitor_watch_on': + var monitorId = d.mid || d.id; + onMonitorOpenForGamepad(monitorId) + break; + } + }) + addOnTabOpen('liveGrid', function () { + startReporting() + }) + addOnTabReopen('liveGrid', function () { + startReporting() + }) + addOnTabAway('liveGrid', function () { + stopReporting() + }) +}); diff --git a/web/assets/js/bs5.liveGrid.js b/web/assets/js/bs5.liveGrid.js index 63f20cd4..9df0c0ba 100644 --- a/web/assets/js/bs5.liveGrid.js +++ b/web/assets/js/bs5.liveGrid.js @@ -771,6 +771,10 @@ function fullScreenLiveGridStream(monitorItem){ } fullScreenInit(videoElement[0]) } +function fullScreenLiveGridStreamById(monitorId){ + const monitorItem = liveGrid.find(`[data-mid="${monitorId}"]`) + fullScreenLiveGridStream(monitorItem) +} function toggleJpegMode(){ var sendData = { f: 'monitor', @@ -1099,6 +1103,7 @@ $(document).ready(function(e){ .on('click','.toggle-live-grid-monitor-ptz-controls',function(){ var monitorItem = $(this).parents('[data-mid]').attr('data-mid') drawPtzControlsOnLiveGridBlock(monitorItem) + setGamepadMonitorSelection() }) .on('click','.toggle-live-grid-monitor-menu,.mdl-overlay-menu-backdrop',function(){ var monitorItem = $(this).parents('[data-mid]') @@ -1112,6 +1117,7 @@ $(document).ready(function(e){ .on('click','.toggle-live-grid-monitor-fullscreen',function(){ var monitorItem = $(this).parents('[data-mid]') fullScreenLiveGridStream(monitorItem) + setGamepadMonitorSelection() }) .on('click','.run-live-grid-monitor-pop',function(){ var monitorId = $(this).parents('[data-mid]').attr('data-mid') diff --git a/web/pages/blocks/footer.ejs b/web/pages/blocks/footer.ejs index a0be9898..5dfe24b6 100644 --- a/web/pages/blocks/footer.ejs +++ b/web/pages/blocks/footer.ejs @@ -39,6 +39,7 @@ +