Skip to content

Commit 2224620

Browse files
committed
added dynamic displays (for temperature, e.g.)
1 parent 03497ad commit 2224620

File tree

3 files changed

+227
-48
lines changed

3 files changed

+227
-48
lines changed

lib.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ queue : [],
33
changeView : false,
44
loadExpected : false,
55
showKeys : false,
6+
intervals : [],
67

78
key : ( function key( k ) {
89
k = k.toString();
@@ -173,7 +174,16 @@ render : ( function render(resp) {
173174
notice.innerHTML = resp.currentTitle;
174175
var lastFrames = -1;
175176

176-
setInterval(function() { if ( frames == lastFrames ) { notice.innerHTML = "Video Stalled"; frames = 0; lastFrames = -1 } else { lastFrames = frames } } , 10000);
177+
for (let i in lib.intervals) {
178+
clearInterval(lib.intervals[i]);
179+
}
180+
181+
lib.intervals = [];
182+
let inv = setInterval(function() { if ( frames == lastFrames ) { notice.innerHTML = "Video Stalled"; frames = 0; lastFrames = -1 } else { lastFrames = frames } } , 10000);
183+
lib.intervals.push(inv);
184+
inv = setInterval(function() { lib.display('') } , 10000);
185+
lib.display();
186+
lib.intervals.push(inv);
177187

178188
const table = document.getElementById("canvas");
179189
const trs = [];

server.js

Lines changed: 106 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
3838
// also: ffmpeg -re -stream_loop -1 -rtsp_transport tcp -i rtsp://yourscameraorginalstream -c copy -acodec aac -f rtsp rtsp://ipofyourmachine:8554/mystream
3939
// https://marcochiappetta.medium.com/how-to-stream-rtsp-on-the-web-using-web-sockets-and-canvas-d821b8f7171e
4040
// node-rtsp-stream
41+
// https://github.com/forart/HyMPS/blob/main/RTSP.md#--
4142

4243
// To Document:
4344
// -vf:drawtext='fontsize=30:fontcolor=white:x=100:y=100:text=[`c`] %{localtime}'
@@ -75,6 +76,8 @@ let controls = {};
7576
let zoom = false;
7677
let currentDisplay = '';
7778
let htmlText = {};
79+
let pollVars = {};
80+
let intervals = [];
7881

7982
app.use(cors(corsOptions));
8083
// eslint-disable-next-line no-use-before-define
@@ -241,6 +244,10 @@ function processConfig() {
241244
for ( let t of config.text ) {
242245
htmlText[ t.name ] = t.frames;
243246
}
247+
for ( let p of config.poll ) {
248+
pollVars[ p.name ] = p;
249+
}
250+
setUpPolling();
244251
}
245252

246253
function saveConfig() {
@@ -417,6 +424,47 @@ app.get('/log', async (req, res) => {
417424
return res.send('OK');
418425
});
419426

427+
function pollVariable( name, url ) {
428+
console.log('begin poll ');
429+
console.log(name);
430+
console.log(url);
431+
if ( url === undefined ) return;
432+
433+
var xhr = new XMLHttpRequest();
434+
xhr.open('GET', url, true);
435+
xhr.onreadystatechange = function () {
436+
if (xhr.readyState === xhr.DONE) {
437+
if (xhr.status !== 200) {
438+
console.log('There was a problem with the request. [' + xhr.status + ']');
439+
} else {
440+
console.log( name + ' = ' + xhr.responseText );
441+
pollVars[ name ] = xhr.responseText;
442+
}
443+
}
444+
};
445+
xhr.onerror = function (e) {
446+
console.log(xhr.statusText);
447+
};
448+
xhr.send();
449+
return xhr;
450+
}
451+
452+
function setUpPolling() {
453+
for ( let i in intervals ) {
454+
clearInterval( intervals[ i ] );
455+
}
456+
intervals = [];
457+
for ( let p in pollVars ) {
458+
console.log( pollVars[p].name );
459+
console.log( pollVars[p].url );
460+
console.log( pollVars[p].interval );
461+
pollVariable( p, pollVars[p].url );
462+
let url = pollVars[p].url;
463+
inv = setInterval( function() { pollVariable( p, url ) }, pollVars[p].interval * 1000 );
464+
intervals.push(inv);
465+
}
466+
}
467+
420468
function dorequest( url, username, password ) {
421469
var xhr = new XMLHttpRequest();
422470
xhr.withCredentials = true;
@@ -453,65 +501,76 @@ function get_view_list( ) {
453501
}
454502

455503
app.get('/display', async (req, res, next) => {
504+
var key;
456505
try {
457-
switch( req.query.key) {
458-
case 'ChannelList':
459-
case 'c':
460-
key = 'ChannelList';
461-
break;
462-
463-
case 'Info':
464-
case 'i':
465-
key = 'Info';
466-
break;
467-
468-
case 'Menu':
469-
case 'm':
470-
key = 'Menu';
471-
break;
472-
473-
case 'Guide':
474-
case 'g':
475-
key = 'Guide';
476-
break;
477-
478-
case 'E-Manual':
479-
case 'e':
480-
case '?':
481-
case 'h':
482-
key = 'E-Manual';
483-
break;
506+
if ( req.query.key !== undefined ) {
507+
switch( req.query.key) {
508+
case 'ChannelList':
509+
case 'c':
510+
key = 'ChannelList';
511+
break;
512+
513+
case 'Info':
514+
case 'i':
515+
key = 'Info';
516+
break;
517+
518+
case 'Menu':
519+
case 'm':
520+
key = 'Menu';
521+
break;
522+
523+
case 'Guide':
524+
case 'g':
525+
key = 'Guide';
526+
break;
527+
528+
case 'E-Manual':
529+
case 'e':
530+
case '?':
531+
case 'h':
532+
key = 'E-Manual';
533+
break;
534+
}
535+
if ( key !== undefined ) {
536+
if ( key == currentDisplay )
537+
currentFrame += 1;
538+
else
539+
currentFrame = 0;
540+
currentDisplay = key;
541+
}
484542
}
485-
if ( key == currentDisplay )
486-
currentFrame += 1;
487-
else
488-
currentFrame = 0;
489-
currentDisplay = key;
490-
if ( htmlText[ key ] ) {
491-
if ( currentFrame >= htmlText[ key ].length ) {
543+
if ( htmlText[ currentDisplay ] ) {
544+
if ( currentFrame >= htmlText[ currentDisplay ].length ) {
492545
currentFrame = 0;
493546
currentDisplay = '';
494-
return res.send( '' );
547+
return res.send( config.html );
495548
}
496-
if ( htmlText[ key ].length ) {
497-
h = htmlText[ key ][ currentFrame ];
549+
if ( htmlText[ currentDisplay ].length ) {
550+
h = htmlText[ currentDisplay ][ currentFrame ] + config.html;
551+
}
552+
} else
553+
h = config.html;
498554

499-
if ( streams.length > 0 ) {
500-
for ( let v in streams[ 0 ].options.var_map ) {
501-
re = replre( '{{' + v + '}}' );
502-
h = h.replace( re, streams[ 0 ].options.var_map[ v ] );
503-
}
504-
}
505-
re = replre( '{{view-list}}' );
506-
h = h.replace( re, get_view_list() );
507-
return res.send( h );
555+
let re;
556+
if ( streams.length > 0 ) {
557+
for ( let v in streams[ 0 ].options.var_map ) {
558+
re = replre( '{{' + v + '}}' );
559+
h = h.replace( re, streams[ 0 ].options.var_map[ v ] );
508560
}
509561
}
562+
for ( let p in pollVars ) {
563+
re = replre( '{{' + p + '}}' );
564+
h = h.replace( re, pollVars[ p ] );
565+
}
566+
re = replre( '{{view-list}}' );
567+
h = h.replace( re, get_view_list() );
568+
return res.send( h );
510569
} catch ( err ) {
511570
console.log( err );
512571
next( err );
513572
}
514-
return res.send('<B><CENTER><FONT SZ=+4>Define content for ' + key + ' in config</FONT></CENTER></B>');
573+
return res.send('<B><CENTER><FONT SZ=+4>Define content for ' + key + ' in config</FONT></CENTER></B>' + config.html);
515574
});
516575

517576
app.get('/keypress', async (req, res) => {

viewconfig.sample.json

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"poll": [
3+
{ "name" : "back-porch-temperature", "url" : "http://192.168.2.1:1880/endpoint/temp", "interval" : "30" }
4+
],
5+
"html" : "<div style='width: 200px; height: 60px; background-color: black; position: absolute; top: 90%; left: 70%; '><font size=50>{{back-porch-temperature}}</font></div>",
6+
7+
"defaults": {
8+
"rtsp-loglevel" : "quiet",
9+
"ncat-loglevel" : "quiet",
10+
"video-codec" : "mpeg1video",
11+
"format" : "mpegts",
12+
"fps" : "35",
13+
"exit-check" : "ppid=$(ps -o ppid= -p $$$$); if [ $ppid = '1' ]; then exit; fi; ",
14+
"cycle-cmd" : "while true; do {{cmd}} done",
15+
"time-option" : "",
16+
"timeout-cmd" : "",
17+
"audio-option" : "",
18+
"audio-x" : "-c:a mp2 -af asetrate=8000 -q:a 0 -osr 32000 -b:a 32000",
19+
"audio-32k" : "-c:a mp2 -osr 32k -q:a 0 -b:a 32k"
20+
},
21+
"controls" : [
22+
{ "name" : "hi3510-ptz",
23+
"up" : "http://{{host}}/cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=1&-act=up&-speed=255",
24+
"down" : "http://{{host}}/cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=1&-act=down&-speed=255",
25+
"left" : "http://{{host}}/cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=1&-act=left&-speed=255",
26+
"right" : "http://{{host}}/cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=1&-act=right&-speed=255",
27+
"in" : "http://{{host}}/cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=1&-act=zoomin&-speed=255",
28+
"out" : "http://{{host}}/cgi-bin/hi3510/param.cgi?cmd=ptzctrl&-step=1&-act=zoomout&-speed=255"
29+
}
30+
],
31+
"text" : [
32+
{ "name" : "ChannelList", "frames" : ["<div class=menu><font size=20>{{view-list}}</font></div>"] },
33+
{ "name" : "Info", "frames" : ["<div class=menu><font size=20>View {{name}} at {{host}}</font></div>"] },
34+
{ "name" : "Menu", "frames" : ["Menu A", "Menu B" ] },
35+
{ "name" : "Guide", "frames" : ["Guide A", "Guide B" ] },
36+
{ "name" : "E-Manual", "frames" : ["E-Manual A", "E-Manual B", "E-Manual C" ] }
37+
],
38+
"views" : [
39+
{ "name" : "rtsp-audio",
40+
"sources" : [ { "source" : "ffmpeg -rtsp_transport {{proto}} {{time-option}} -i rtsp://{{host}}:{{port}}/{{stream}} -nostats {{audio-option}} -loglevel {{rtsp-loglevel}} -f mp2 pipe:1 " } ],
41+
"proto" : "udp",
42+
"port" : "554",
43+
"stream" : "1"
44+
},
45+
{ "name" : "rtsp-default",
46+
"sources" : [ { "source" : "ffmpeg -rtsp_transport {{proto}} {{time-option}} -i rtsp://{{host}}:{{port}}/{{stream}} -nostats {{audio-option}} -r {{fps}} -loglevel {{rtsp-loglevel}} -vf scale={{hscale}}:{{vscale}} -f {{format}} -codec:v {{video-codec}} -" } ],
47+
"proto" : "udp",
48+
"port" : "554",
49+
"stream" : "1"
50+
},
51+
{ "name" : "rtsp-ptz",
52+
"sources" : [ { "view" : "rtsp-default", "control" : "hi3510-ptz" } ]
53+
},
54+
{ "name" : "ncat-ffmpeg",
55+
"sources" : [ { "source": "ffmpeg {{time-option}} -i <( ( printf '\\x55\\x55\\xaa\\xaa\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x50'; {{timeout-cmd}} cat ) | ncat {{host}} {{port}} -w 10s -i 10s ) -nostats -r {{fps}} -loglevel {{ncat-loglevel}} -vf scale={{scale}} -f {{format}} -codec:v {{video-codec}} -" } ],
56+
"scale" : "{{hscale}}:{{vscale}}",
57+
"port" : "8000"
58+
},
59+
{ "name" : "water-feature", "sources" : [ { "view" : "rtsp-ptz" } ], "host" : "192.168.8.9", "username":"admin", "password":"admin"},
60+
{ "name" : "front-door", "sources" : [ { "view" : "rtsp-ptz" } ], "host" : "192.168.8.11", "username":"admin", "password":"admin"},
61+
{ "name" : "driveway", "sources" : [ { "view" : "rtsp-default" } ], "host" : "192.168.8.10", "username":"admin", "password":"admin"},
62+
{ "name" : "barn", "sources" : [ { "view" : "rtsp-default" } ], "host" : "192.168.8.2", "username":"admin", "password":"admin"},
63+
{ "name" : "garden", "sources" : [ { "view" : "ncat-ffmpeg" } ], "host" : "192.168.8.12", "username":"admin", "password":"111111",
64+
"note" : "app access should be available after July 5th" },
65+
66+
{ "name" : "dual",
67+
"cycle-time" : "10",
68+
"time-option" : "-t {{cycle-time}}",
69+
"timeout-cmd" : "timeout {{cycle-time}}",
70+
"sources" : [ { "view" : "barn", "stream": "2" },
71+
{ "view" : "water-feature", "stream": "2" } ] },
72+
73+
{ "name" : "Quad View 1-4",
74+
"key" : "A",
75+
"sources" : [ { "view" : "front-door", "stream": "2", "audio-option" : "{{audio-32k}}" },
76+
{ "view" : "driveway", "stream": "2" },
77+
{ "view" : "barn", "stream": "2" },
78+
{ "view" : "garden", "stream": "2" } ] },
79+
{ "name" : "Cycle 1-4",
80+
"key" : "B",
81+
"cycle-time" : "10",
82+
"time-option" : "-t {{cycle-time}}",
83+
"timeout-cmd" : "timeout {{cycle-time}}",
84+
"sources" : [ { "view" : "front-door", "stream": "1" },
85+
{ "view" : "driveway", "stream": "1" },
86+
{ "view" : "garden", "stream": "1" },
87+
{ "view" : "barn", "stream": "1" }
88+
] },
89+
{ "name" : "Combo 1-5",
90+
"key" : "C",
91+
"sources" : [ { "view" : "front-door", "stream": "2", "rtsp-loglevel" : "quiet" },
92+
{ "view" : "driveway", "stream": "2" },
93+
{ "view" : "dual", "stream": "2" },
94+
{ "view" : "garden", "stream": "2" } ] },
95+
{ "name" : "Front Door", "key" : "1", "sources" : [ { "view" : "front-door", "stream": "2", "audio-option" : "{{audio-32k}}" } ] },
96+
{ "name" : "Driveway", "key" : "2", "sources" : [ { "view" : "driveway", "stream": "1", "audio-option" : "{{audio-32k}}" } ] },
97+
{ "name" : "Barn", "key" : "3", "sources" : [ { "view" : "barn", "stream": "1", "audio-option" : "{{audio-32k}}" } ] },
98+
{ "name" : "Garden", "key" : "4", "sources" : [ { "view" : "garden", "stream": "1" } ] },
99+
{ "name" : "Water Feature", "key" : "5", "sources" : [ { "view" : "water-feature", "stream": "2", "audio-option" : "{{audio-32k}}" } ] }
100+
],
101+
102+
"users": [
103+
{
104+
"userId": 0,
105+
"username": "admin",
106+
"password": "admin"
107+
}
108+
],
109+
"connectionType": "local"
110+
}

0 commit comments

Comments
 (0)