esp-who/components/modules/web/www/monitor.html

1013 lines
111 KiB
HTML
Raw Permalink Normal View History

2021-09-08 20:04:59 +08:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style type="text/css">
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;-moz-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]: after,a[href^="javascript:"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.label{border:1px solid #000}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}[role=button]{cursor:pointer}h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1,h2,h3{margin-top:20px;margin-bottom:10px}h4,h5,h6{margin-top:10px;margin-bottom:10px}p{margin:0 0 10px}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{
html {
font-size: 16px;
overflow-y: auto;
}
body {
color: #cacaca;
background-color: #242424;
user-select: text;
}
h3,
h4 {
color: #cacaca;
font-weight: 300;
font-size: 18px;
}
h3 {
margin-bottom: 20px;
font-size: 1.5rem;
}
h4 { margin-top: 4px; }
h4 a {
color: #cacaca;
/*margin-left: 4px;*/
}
h4 a.btn {
margin-top: -6px;
float:right;
}
h4 a:focus, h4 a:hover {
color: #cacaca;
text-decoration: none;
}
/* Overwrites */
.text-warning {
color: #de8938;
}
.well {
background-color: #242424;
border: 1px solid #525252;
}
.form-control:focus, .btn:focus {
outline: none;
box-shadow: none;
}
.form-control {
display: inline-block;
}
/* Buttons */
.btn {
letter-spacing: 0.025rem;
border-radius: 4px;
line-height: 18px;
margin-top: -1px;
margin-bottom: 1px;
}
.btn-default {
background-color: #333333;
color: #cacaca;
border-color: #525252;
}
.btn-default:active, .btn-default:focus, .btn-default:hover, .btn-default:active:hover {
color: #cacaca;
background-color: #626262;
background-image: none;
border-color: #525252;
}
.disable {
pointer-events: none;
opacity: 0.4;
}
.collapsed {
display: none;
}
.glyphicon {
margin-top: -4px;
}
/* Selects */
select.form-control {
height:30px;
background-color: #626262;
color: #cacaca;
border: 1px solid #525252;
-webkit-appearance:none;
line-height: 14px;
cursor:pointer;
}
/* Console */
#console {
min-height: 201px;
max-height: 201px;
padding: 0 8px;
overflow-y: auto;
overflow-x: hidden;
font-family: monospace, monospace;
font-size: 0.875rem;
margin-bottom: 0px;
}
#console p { margin: 0 0 4px; }
#console .comment { color: #2C9040; }
#console pre {
color: #aaaaaa;
background-color: #626262;
border: 1px solid #525252;
border-radius: 4px;
}
#console-holder {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
.panel-action {
position: absolute;
right: 6px;
top: 8px;
}
#controls-holder {
position: absolute;
left: 0;
top: 0;
right: 0;
height: 42px;
}
#view-holder {
position: absolute;
left: 0;
top: 47px;
right: 0;
bottom: 251px;
}
#cam-holder {
width: 342px;
left: 0px;
overflow-x: hidden;
overflow-y: auto;
}
#stream-holder {
left: 342px;
right: 0px;
}
#cam-holder, #stream-holder {
padding: 0px;
height: 100%;
position: absolute;
top: 0px;
bottom: 0px;
}
#cam-holder .row, #stream-holder .row {
margin: 5px;
padding: 5px;
border: 1px solid #525252;
border-radius: 4px;
background-color: #333333;
}
.preview-tbar {
background-color: #000000;
}
.cam-selected {
background-color: #aa0000;
}
#cam-holder img {
cursor: pointer;
}
#stream {
max-width: 100%;
max-height: 100%;
height: 100%;
width:100%;
object-fit: contain;
}
#stream-win {
width: 100%;
height: 100%;
text-align: center;
background-color: black;
}
#resolution {
width:170px;
}
#refresh-mdns-interval {
width:100px;
}
#switch-cams-interval, #refresh-thumbs-interval {
width:54px;
}
#xclk {
width:auto;
height:30px;
background-color: #626262;
color: #cacaca;
border: 1px solid #525252;
}
.sitem, .mitem, .fitem {
position: relative;
border-radius: 0;
}
.sitem {
z-index: 2;
margin-right: -2px;
/*margin-top: -1px;*/
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.fitem {
z-index: 2;
margin-left: -2px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
</style>
<script type="text/javascript">
/*
* Console
*/
function isArray(a){
return (typeof a === "object" && a instanceof Array);
}
function isObject(a){
return (typeof a === "object" && (a instanceof Array) == false);
}
function isFunction(a){
return (typeof a === "function");
}
function isString(a){
return (typeof a === "string" || (typeof a === 'undefined' && a instanceof String));
}
String.prototype.contains = function(it) {
return this.indexOf(it) != -1;
};
function addToConsole(msg, color){
document.getElementById("gCodeLog").innerHTML += '<p class="text-'+color+'">' + msg + '</p>';
let con = document.getElementById("console");
con.scrollTop = con.scrollHeight;
}
function gDebug(msg){ addToConsole(msg, "muted"); }
function gLog(msg){ addToConsole(msg, "info"); }
function gInfo(msg){ addToConsole(msg, "success"); }
function gWarn(msg){ addToConsole(msg, "warning"); }
function gError(msg){ addToConsole(msg, "danger"); }
function gException(e){
var msg = "Error: '"+e+"'";
if(e instanceof Error){
// var stack = e.stack;
// while (stack.indexOf('\n') >= 0) {
// stack = stack.replace('\n', '<br/>');
// }
msg = e.name+": '"+e.message+"', line: "+e.line+", stack: <br/><pre>"+e.stack+"</pre>";
}
gError(msg);
return msg;
}
/*
* Variables
*/
var resolutions = {
// <!-- 5MP -->
"21":"QSXGA(2560x1920)",
"20":"P FHD(1080x1920)",
"19":"WQXGA(2560x1600)",
"18":"QHD(2560x1440)",
// <!-- 3MP -->
"17":"QXGA(2048x1564)",
"16":"P 3MP(864x1564)",
"15":"P HD(720x1280)",
"14":"FHD(1920x1080)",
// <!-- 2MP -->
"13":"UXGA(1600x1200)",
"12":"SXGA(1280x1024)",
"11":"HD(1280x720)",
"10":"XGA(1024x768)",
"9":"SVGA(800x600)",
// <!-- VGA -->
"8":"VGA(640x480)",
"7":"HVGA(480x320)",
"6":"CIF(400x296)",
"5":"QVGA(320x240)",
"4":"240x240",
"3":"HQVGA(240x176)",
"2":"QCIF(176x144)",
"1":"QQVGA(160x120)",
"0":"96x96"
};
var pixformats = [
"RGB565", // 2BPP/RGB565
"YUV422", // 2BPP/YUV422
"GRAYSCALE", // 1BPP/GRAYSCALE
"JPEG", // JPEG/COMPRESSED
"RGB888", // 3BPP/RGB888
"RAW", // RAW
"RGB444", // 3BP2P/RGB444
"RGB555", // 3BP2P/RGB555
];
var cameras = {};
var selectedCamera = null;
var playing = false;
/*
* Camera URLs
*/
function getCamURL(id){
let host = `http://${cameras[id].ip}`;
let port = cameras[id].port;
if(port !== 80){
return `${host}:${port}`;
}
return host;
}
function getCamStreamURL(id){
let host = `http://${cameras[id].ip}`;
let port = cameras[id].txt.stream_port?parseInt(cameras[id].txt.stream_port):cameras[id].port;
if(port !== 80){
return `${host}:${port}/stream`;
}
return `${host}/stream`;
}
/*
* AJAX
*/
function fetchTimeout(url, options, timeout = 2000) {
return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), timeout)
)
]);
}
function fetchUrl(url, type, cb){
function ecb(code, data){
if(data instanceof TypeError && data.message == "cancelled"){
if(cb)cb(200, null);
} else {
gError("Fetch '"+url+"' Failed!");
if(cb)cb(code, gException(data));
}
}
fetchTimeout(url)
.then(function (response) {
if (response.status !== 200) {
ecb(response.status, response.statusText);
} else {
var promis = null;
switch(type){
case "arrayBuffer": promis = response.arrayBuffer(); break;
case "blob": promis = response.blob(); break;
case "formData": promis = response.formData(); break;
case "json": promis = response.json(); break;
default: promis = response.text(); break;
}
if(promis !== null){
promis.then(function(data){
if(cb)cb(200, data, response.headers);
}).catch(function(err) {
ecb(-1, err);
});
} else {
ecb(-1, "Unknown type: "+type);
}
}
})
.catch(function(err) {
ecb(-1, err);
});
}
/*
* Camera Thumbnails
*/
function getCamURL(id){
let host = `http://${cameras[id].ip}`;
let port = cameras[id].port;
if(port !== 80){
return `${host}:${port}`;
}
return host;
}
function getCamStreamURL(id){
let host = `http://${cameras[id].ip}`;
let port = cameras[id].txt.stream_port?parseInt(cameras[id].txt.stream_port):cameras[id].port;
if(port !== 80){
return `${host}:${port}/stream`;
}
return `${host}/stream`;
}
function loadCameraThumbnail(id){
function getCb(id){
return function(){
loadCameraThumbnail(id);
}
}
function copyThumbnailFromPlayer(id) {
let view = document.getElementById('stream');
var canvas = document.createElement("canvas");
canvas.width = view.naturalWidth;
canvas.height = view.naturalHeight;
document.body.appendChild(canvas);
var context = canvas.getContext('2d');
context.drawImage(view,0,0);
try {
canvas.toBlob(function(data){
if(data && cameras.hasOwnProperty(id)){
document.getElementById(id).src = window.URL.createObjectURL(data);
}
}, 'image/jpeg', 0.80);
//document.getElementById(id).src = canvas.toDataURL('image/jpeg');
} catch (e) {
gException(e);
}
canvas.parentNode.removeChild(canvas);
}
function loadRemoteThumbnail(id){
if(cameras.hasOwnProperty(id)){
fetchUrl(getCamURL(id)+'/capture', "blob", function(code, data, headers){
if(code < 0){
removeCamera(id);
} else if(code === 200 && cameras.hasOwnProperty(id)){
if(data){
document.getElementById(id).src = window.URL.createObjectURL(data);
if(selectedCamera == id && !playing){
//copy thumbnail to player
document.getElementById("stream").src = document.getElementById(id).src;
}
}
var to = parseInt(document.getElementById("refresh-thumbs-interval").value);
if(to){
cameras[id].timeout = setTimeout(getCb(id), to * 1000);
}
}
});
}
}
if(cameras[id].timeout !== null){
clearTimeout(cameras[id].timeout);
cameras[id].timeout = null;
}
if(selectedCamera == id && playing){
copyThumbnailFromPlayer(id);
var to = parseInt(document.getElementById("refresh-thumbs-interval").value);
if(to){
cameras[id].timeout = setTimeout(getCb(id), to * 1000);
}
} else {
loadRemoteThumbnail(id);
}
}
/*
* Thumbnail Refresh
*/
function refreshThumbnails(){
for(var cam in cameras){
loadCameraThumbnail(cam);
}
}
function initThumbnailRefresh(){
var button = document.getElementById("refresh-thumbs");
button.onclick = function(e){
refreshThumbnails();
};
document.getElementById("refresh-thumbs-interval").onchange = function(e){
let value = parseInt(e.target.value);
window.stop();
//reastart playback
if(selectedCamera !== null && playing){
document.getElementById("stream").src = getCamStreamURL(selectedCamera);
}
//clear all timeouts
for(var cam in cameras){
if(cameras[cam].timeout != null){
clearTimeout(cameras[cam].timeout);
cameras[cam].timeout = null;
}
}
if(value > 0){
button.classList.add('disable');
refreshThumbnails();
} else {
button.classList.remove('disable');
}
};
}
/*
* Camera Boxes
*/
function removeCamera(id){
if(!cameras.hasOwnProperty(id)){
return;
}
let item = document.getElementById('item-'+id);
if(item){
gWarn("Remove Camera["+id+"]: "+cameras[id].instance);
item.parentElement.removeChild(item);
}
if(cameras[id].timeout != null){
clearTimeout(cameras[id].timeout);
cameras[id].timeout = null;
}
delete cameras[id];
if(selectedCamera == id){
selectedCamera = null;
if(playing){
onCameraButtonStop(id);
}
document.getElementById("controls-holder").classList.add('disable');
playButtonClear();
}
}
function updateCamera(id){
document.getElementById("title-"+id).innerHTML = cameras[id].instance;
let anchor = document.createElement("a");
anchor.href = getCamURL(id);
anchor.target = "_blank";
anchor.classList.add('btn');
anchor.classList.add('btn-sm');
anchor.classList.add('btn-default');
anchor.classList.add('glyphicon');
anchor.classList.add('glyphicon-cog');
document.getElementById("title-"+id).appendChild(anchor);
document.getElementById("tbar-"+id).innerHTML = cameras[id].txt.board+" "+cameras[id].txt.model+" "+pixformats[parseInt(cameras[id].txt.pixformat)]+" "+resolutions[cameras[id].txt.framesize];
}
function addCamera(id){
gLog("Add Camera["+id+"]: "+cameras[id].instance);
let holder = document.getElementById("cam-holder");
let item = document.createElement("div");
let title = document.createElement("h4");
let anchor = document.createElement("a");
let img = document.createElement("img");
let tbar = document.createElement("div");
item.id = "item-"+id;
item.className = "row";
title.id = "title-"+id;
title.className = "text-center";
title.innerHTML = cameras[id].instance;
item.appendChild(title);
anchor.href = getCamURL(id);
anchor.target = "_blank";
anchor.classList.add('btn');
anchor.classList.add('btn-sm');
anchor.classList.add('btn-default');
anchor.classList.add('glyphicon');
anchor.classList.add('glyphicon-cog');
title.appendChild(anchor);
tbar.id = "tbar-"+id;
tbar.className = "text-center preview-tbar";
tbar.innerHTML = cameras[id].txt.board+" "+cameras[id].txt.model+" "+pixformats[parseInt(cameras[id].txt.pixformat)]+" "+resolutions[cameras[id].txt.framesize];
item.appendChild(tbar);
img.id = id;
img.crossorigin = "";
img.setAttribute("width", "100%");
item.appendChild(img);
holder.appendChild(item);
img.onclick = function(e){
document.getElementById("switch-cams-interval").value = 0;
cancelCamSwitcher();
selectCamera(id);
};
}
function handleCamera(cam){
let isNew = document.getElementById(cam.id) === null;
cam.timeout = null;
cameras[cam.id] = cam;
if(isNew){
//new camera
addCamera(cam.id);
} else {
updateCamera(cam.id);
}
loadCameraThumbnail(cam.id);
}
/*
* Cam Switcher
*/
function switchCam(){
let keys = Object.keys(cameras);
let numCams = keys.length;
if(numCams === 0 || (numCams === 1 && selectedCamera !== null)){
return;
}
if(selectedCamera === null){
selectCamera(cameras[keys[0]].id);
return;
}
//more than one cams and one is selected!
for (var i = 0; i < numCams; i++) {
if(keys[i] === selectedCamera){
if(i === (numCams - 1)){
selectCamera(cameras[keys[0]].id);
} else {
selectCamera(cameras[keys[i+1]].id);
}
return;
}
}
}
var camSwitcherTimer = null;
function cancelCamSwitcher(){
if(camSwitcherTimer !== null){
clearInterval(camSwitcherTimer);
camSwitcherTimer = null;
}
}
function initCamSwitcher(){
document.getElementById("switch-cams").onclick = function(e){
switchCam();
};
function handleChange(){
let value = parseInt(document.getElementById("switch-cams-interval").value);
if(value > 0){
cancelCamSwitcher();
camSwitcherTimer = setInterval(switchCam, value * 1000);
} else {
cancelCamSwitcher();
}
}
document.getElementById("switch-cams-interval").onchange = handleChange;
handleChange();
}
/*
* Camera Player Controls
*/
function fillResolutionSelect(model){
let s = document.getElementById("resolution");
s.innerHTML = "";
var m = 8;
switch(model){
case "OV2640": m = 13; break;
case "OV3660": m = 17; break;
case "OV5640": m = 21; break;
default: break;
}
for(var i=0; i<=m; i++){
let option = document.createElement("option");
option.value = i;
option.innerHTML = resolutions[''+i];
s.appendChild(option);
}
}
function playButtonClear(){
let playButton = document.getElementById("play-stop");
playButton.classList.remove('btn-danger');
playButton.classList.remove('glyphicon-stop');
playButton.classList.add('btn-success');
playButton.classList.add('glyphicon-play');
playButton.onclick = function(){};
playing = false;
}
function onCameraButtonStop(id){
let stream = document.getElementById("stream");
let playButton = document.getElementById("play-stop");
playButton.classList.remove('btn-danger');
playButton.classList.remove('glyphicon-stop');
playButton.classList.add('btn-success');
playButton.classList.add('glyphicon-play');
window.stop();
playing = false;
if(parseInt(document.getElementById("refresh-thumbs-interval").value) > 0){
refreshThumbnails();
} else if(cameras.hasOwnProperty(id)){
loadCameraThumbnail(id);
}
playButton.onclick = function(){
if(cameras.hasOwnProperty(id)){
onCameraButtonPlay(id);
}
}
}
function onCameraButtonPlay(id){
document.getElementById("switch-cams-interval").value = 0;
cancelCamSwitcher();
let stream = document.getElementById("stream");
let playButton = document.getElementById("play-stop");
playButton.classList.remove('btn-success');
playButton.classList.remove('glyphicon-play');
playButton.classList.add('btn-danger');
playButton.classList.add('glyphicon-stop');
stream.src = getCamStreamURL(id);
playing = true;
playButton.onclick = function(){
onCameraButtonStop(id);
}
}
function selectCamera(id){
if(selectedCamera != id){
if(selectedCamera !== null){
//mark as unselected?
document.getElementById("tbar-"+selectedCamera).classList.remove('cam-selected');
if(playing){
onCameraButtonStop(selectedCamera);
}
}
let stream = document.getElementById("stream");
let holder = document.getElementById("controls-holder");
let playButton = document.getElementById("play-stop");
selectedCamera = null;
holder.classList.add('disable');
playButtonClear();
stream.src = document.getElementById(id).src;
fillResolutionSelect(cameras[id].txt.model);
document.getElementById("resolution").value = cameras[id].txt.framesize;
selectedCamera = id;
fetchUrl(getCamURL(id)+'/status', "json", function(code, data, headers){
if(code === 200){
if(!stream.src){
stream.src = document.getElementById(id).src;
}
//mark selected
document.getElementById("tbar-"+selectedCamera).classList.add('cam-selected');
document.getElementById("resolution").value = data.framesize;
document.getElementById("xclk").value = data.xclk || 20;
holder.classList.remove('disable');
playButton.onclick = function(){
onCameraButtonPlay(id);
};
} else if(selectedCamera != null){
removeCamera(selectedCamera);
}
});
}
}
function initCameraPlayerControls(){
//Resolution
document.getElementById("resolution").onchange = function(e){
if(selectedCamera === null){
return;
}
let url = getCamURL(selectedCamera);
fetchUrl(`${url}/control?var=framesize&val=${e.target.value}`, 'text', function(code){
if(code < 0 && selectedCamera != null){
removeCamera(selectedCamera);
}
});
}
//XCLK
document.getElementById("xclk").onchange = function(e){
if(selectedCamera === null){
return;
}
let value = parseInt(e.target.value);
if(value < 5 || value > 20){
gError("Invalid XCLK Value: "+value);
return;
}
let url = getCamURL(selectedCamera);
fetchUrl(`${url}/xclk?xclk=${value}`, 'text', function(code){
if(code < 0 && selectedCamera != null){
removeCamera(selectedCamera);
}
});
}
//Save Image
const saveButton = document.getElementById('save-still');
saveButton.onclick = (e) => {
let view = document.getElementById('stream');
var canvas = document.createElement("canvas");
canvas.width = view.naturalWidth;
canvas.height = view.naturalHeight;
document.body.appendChild(canvas);
var context = canvas.getContext('2d');
context.drawImage(view,0,0);
try {
var dataURL = canvas.toDataURL('image/png');
saveButton.href = dataURL;
var d = new Date();
saveButton.download = d.getFullYear() + ("0"+(d.getMonth()+1)).slice(-2) + ("0" + d.getDate()).slice(-2) + ("0" + d.getHours()).slice(-2) + ("0" + d.getMinutes()).slice(-2) + ("0" + d.getSeconds()).slice(-2) + ".png";
} catch (e) {
gException(e);
}
canvas.parentNode.removeChild(canvas);
}
}
/*
* MDNS
*/
//const baseHost = 'http://192.168.254.81';
const baseHost = document.location.origin;
function scanMdns(){
fetchUrl(baseHost+'/mdns', "json", function(code, data, headers){
if(code === 200){
if(isArray(data)){
for(var i = 0; i < data.length; i++) {
handleCamera(data[i]);
}
if(data.length > 0 && selectedCamera == null){
selectCamera(data[0].id);
}
} else {
gError("Data is not Array: "+data);
}
} else if(code < 0){
gError("Failed to fetch MDNS");
}
});
}
function initMdns(){
var mdnsRefreshTimer = null;
scanMdns();
document.getElementById("refresh-mdns").onclick = function(e){
scanMdns();
};
function handleChange(){
if(mdnsRefreshTimer !== null){
clearInterval(mdnsRefreshTimer);
mdnsRefreshTimer = null;
}
let value = parseInt(document.getElementById("refresh-mdns-interval").value);
if(value > 0){
document.getElementById("refresh-mdns").classList.add('disable');
mdnsRefreshTimer = setInterval(scanMdns, value * 60 * 1000);
} else {
document.getElementById("refresh-mdns").classList.remove('disable');
}
}
document.getElementById("refresh-mdns-interval").onchange = handleChange;
handleChange();
}
//
// Startup
//
document.addEventListener("DOMContentLoaded", function(event) {
var consoleVisible = true;
document.getElementById("hide-console").onclick = function(e){
consoleVisible = !consoleVisible;
if(consoleVisible){
e.target.classList.remove('glyphicon-chevron-up');
e.target.classList.add('glyphicon-chevron-down');
document.getElementById("console").classList.remove('collapsed');
document.getElementById("console-holder").style.paddingBottom = "";
document.getElementById("view-holder").style.bottom = "";
} else {
e.target.classList.remove('glyphicon-chevron-down');
e.target.classList.add('glyphicon-chevron-up');
document.getElementById("console").classList.add('collapsed');
document.getElementById("console-holder").style.paddingBottom = 0;
document.getElementById("view-holder").style.bottom = "46px";
}
};
initMdns();
initThumbnailRefresh();
initCameraPlayerControls();
initCamSwitcher();
});
</script>
<title>ESP-EYE Monitor</title>
</head>
<body>
<div id="page">
<div class="row">
<div id="cam-holder">
<div class="row" id="mdns-holder">
<select class="form-control sitem" id="refresh-mdns-interval" placeholder="Search Interval Minutes">
<option value="0" selected="selected">Manual</option>
<option value="1">1 Minute</option>
<option value="2">2 Minutes</option>
<option value="5">5 Minutes</option>
<option value="10">10 Minutes</option>
<option value="15">15 Minutes</option>
<option value="30">30 Minutes</option>
<option value="60">1 Hour</option>
</select>
<button type="button" class="btn btn-sm glyphicon btn-success glyphicon-search fitem disable" id="refresh-mdns" title="Search"></button>
<select class="form-control sitem" id="refresh-thumbs-interval">
<option value="0">OFF</option>
<option value="1">1s</option>
<option value="2" selected="selected">2s</option>
<option value="5">5s</option>
<option value="10">10s</option>
<option value="30">30s</option>
<option value="60">60s</option>
</select>
<button type="button" class="btn btn-sm glyphicon btn-success glyphicon-camera fitem disable" id="refresh-thumbs" title="Refresh"></button>
<select class="form-control sitem" id="switch-cams-interval">
<option value="0">OFF</option>
<option value="1">1s</option>
<option value="2">2s</option>
<option value="5" selected="selected">5s</option>
<option value="10">10s</option>
<option value="30">30s</option>
<option value="60">60s</option>
</select>
<button type="button" class="btn btn-sm glyphicon btn-success glyphicon-step-forward fitem" id="switch-cams" title="Switch Cams"></button>
</div>
</div>
<div id="stream-holder">
<div class="row disable" id="controls-holder">
<a id="save-still" href="#" class="btn btn-sm glyphicon btn-danger glyphicon-picture" download="capture.jpg"></a>
<button type="button" class="btn btn-sm glyphicon btn-success glyphicon-play" id="play-stop" title="Play/Stop"></button>
<select class="form-control" id="resolution" placeholder="Resolution"></select>
<select class="form-control" id="xclk" placeholder="XCLK MHz">
<option value="20" selected="selected">20 MHz</option>
<option value="10">10 MHz</option>
<option value="5">5 MHz</option>
</select>
</div>
<div class="row" id="view-holder">
<div id="stream-win">
<img id="stream" crossorigin>
</div>
</div>
<div class="row" id="console-holder">
<h4 class="text-center">Console<button type="button" class="btn btn-sm glyphicon btn-default glyphicon-chevron-down panel-action" id="hide-console" title="Hide/Show Console"></button></h4>
<div class="well" id="console" title="Console"><div id="gCodeLog"></div></div>
</div>
</div>
</div>
</div>
</body>
</html>