Build imaginary monitoring
site using Node-RED template
node after reading the following examples.
- Web Form with Node-RED, CSS/JS/HTML
- 5-minute Signup Forms with Node-RED and Compose
- Simple Web page with live data updates via websocket
- HTTP GET
/monitor
node - (
function
node) msg.payload.apikey for Google Map API Key - (
template
node) msg.payload.script for Javascript - (
template
node) msg.payload.style for CSS style - (
template
node) msg.payload = HTML template + CDN links (jquery, bootstrap, google map, etc) + msg.payload.apikey + msg.payload.script + msg.payload.style - msg.payload => (HTTP Response node)
[{"id":"fe81076e.42bdc8","type":"http in","z":"934da042.4fbe4","name":"","url":"/monitor","method":"get","upload":false,"swaggerDoc":"","x":148,"y":167.0000286102295,"wires":[["fc4091d2.2f8b9"]]},{"id":"fc4091d2.2f8b9","type":"function","z":"934da042.4fbe4","name":"msg.apikey = \"<map api key>\";","func":"//Assign API Key\nmsg.payload.apikey = \"abcdefghijklmnop\";\nreturn msg;","outputs":1,"noerr":0,"x":260.00008392333984,"y":228.00005531311035,"wires":[["144cb1cf.a8969e"]]},{"id":"d5348f9d.0263c","type":"template","z":"934da042.4fbe4","name":"CSS","field":"payload.style","fieldType":"msg","format":"html","syntax":"mustache","template":"body{\n font-family: 'Droid Sans', 'Helvetica', Arial, sans-serif;\n margin:5px;\n}\n#map{\n display: block;\n width: 95%;\n height: 500px;\n margin: 0 auto;\n -moz-box-shadow: 0px 5px 20px #ccc;\n -webkit-box-shadow: 0px 5px 20px #ccc;\n box-shadow: 0px 5px 20px #ccc;\n}\n#map.large{\n height:600px;\n}\n\n.overlay{\n display:block;\n text-align:center;\n color:#fff;\n font-size:60px;\n line-height:80px;\n opacity:0.8;\n background:#4477aa;\n border:solid 3px #336699;\n border-radius:4px;\n box-shadow:2px 2px 10px #333;\n text-shadow:1px 1px 1px #666;\n padding:0 4px;\n}\n\n.overlay_arrow{\n left:50%;\n margin-left:-16px;\n width:0;\n height:0;\n position:absolute;\n}\n.overlay_arrow.above{\n bottom:-15px;\n border-left:16px solid transparent;\n border-right:16px solid transparent;\n border-top:16px solid #336699;\n}\n.overlay_arrow.below{\n top:-15px;\n border-left:16px solid transparent;\n border-right:16px solid transparent;\n border-bottom:16px solid #336699;\n}\n\n#toast-container > .toast {\n background-image: none !important;\n}\n\n#toast-container > .toast:before {\n position: fixed;\n font-family: FontAwesome;\n font-size: 24px;\n line-height: 18px;\n float: left;\n color: #FFF;\n padding-right: 0.5em;\n margin: auto 0.5em auto -1.5em;\n} \n#toast-container > .toast-warning:before {\n content: \"\\f119\";\n}\n#toast-container > .toast-error:before {\n content: \"\\f119\";\n}\n#toast-container > .toast-info:before {\n content: \"\\f005\";\n}\n#toast-container > .toast-success:before {\n content: \"\\f002\";\n}","x":523.0000038146973,"y":227.0000286102295,"wires":[["8817e51.cef5118"]]},{"id":"144cb1cf.a8969e","type":"template","z":"934da042.4fbe4","name":"JavaScript","field":"payload.script","fieldType":"msg","format":"javascript","syntax":"plain","template":"\n/////////////////////////////////\n// Global \n\nvar loc = window.location;\nvar ws;\nvar wsUri = \"ws:\";\nvar map;\nvar markers = [];\n\nif (loc.protocol === \"https:\") { wsUri = \"wss:\"; }\n// This needs to point to the web socket in the Node-RED flow\n// ... in this case it's ws/simple\nwsUri += \"//\" + loc.host + loc.pathname.replace(\"monitor\",\"ws/alert\");\n\n//////////////////////////////////////////////\n// google map\n\nfunction initMap() {\n map = new google.maps.Map(document.getElementById('map'), {\n zoom: 13,\n center: {lat: 40.489497, lng: -74.448254}\n });\n}\n\nfunction dropMarker (type, addr, la, lo) {\n \n clearMarker(addr);\n var marker;\n if ( type === 'Water') {\n marker = new google.maps.Marker({\n position: {lat: la, lng: lo},\n title : addr,\n map: map,\n label : 'W',\n animation: google.maps.Animation.DROP\n });\n } else {\n marker = new google.maps.Marker({\n position: {lat: la, lng: lo},\n title : addr,\n map: map,\n label : 'G',\n animation: google.maps.Animation.DROP\n }); \n }\n \n markers.push(marker);\n}\n\n\nfunction clearMarkers() {\n for ( var i = 0; i < markers.length; i++) {\n markers[i].setMap(null);\n }\n}\n\nfunction clearMarker(addr) {\n for ( var i = 0; i < markers.length; i++) {\n if ( markers[i].title == addr ) markers[i].setMap(null);\n }\n}\n\n////////////////////////////////////////////////\n// Toastr\n\nfunction showToast(type, addr, la, lo) { \n toastr.options.positionClass = 'toast-bottom-full-width'; toastr.options.extendedTimeOut = 0; //1000;\n toastr.options.timeOut = 1000;\n toastr.options.fadeOut = 250;\n toastr.options.fadeIn = 250;\n var msg = ' Address : ' + addr;\n msg = msg + ', Latitude : '+ la;\n msg = msg + ', Longitude : '+ lo;\n \n if ( type === 'Water') {\n toastr.warning('<b>Water Leak</b> '+msg);\n } else {\n toastr.error('<b>Gas Leak</b> '+msg);\n }\n}\n\n///////////////////////////////////////////////\n// WebSocket \n\nfunction wsConnect() {\n \n console.log(\"connect\",wsUri);\n ws = new WebSocket(wsUri);\n\n ws.onopen = function() {\n console.log(\"connected\");\n }\n ws.onclose = function() {\n setTimeout(wsConnect,5000);\n }\n \n ws.onmessage = function(msg) {\n var payload = JSON.parse(msg.data);\n //console.log(payload);\n showToast(payload.type, payload.addr, payload.la, payload.lo);\n //insert Marker\n dropMarker(payload.type, payload.addr, payload.la, payload.lo);\n }\n}\n\nfunction action(m) {\n // Nothing has been defined yet\n if (ws) { ws.send(m); }\n}\n \n","x":424.0000534057617,"y":165.0000286102295,"wires":[["d5348f9d.0263c"]]},{"id":"422d2000.76f7d","type":"http response","z":"934da042.4fbe4","name":"","statusCode":"","headers":{},"x":742.0000610351562,"y":223.00005531311035,"wires":[]},{"id":"8817e51.cef5118","type":"template","z":"934da042.4fbe4","name":"HTML","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!doctype html>\n<html>\n<head>\n <!-- Required meta tags -->\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\n <!-- Bootstrap CSS -->\n <!-- Latest compiled and minified CSS -->\n <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css\" integrity=\"sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u\" crossorigin=\"anonymous\">\n <link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.1.0/css/all.css\" integrity=\"sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt\" crossorigin=\"anonymous\">\n <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.css\" crossorigin=\"anonymous\">\n <style>{{{payload.style}}}</style>\n \n <!-- jQuery first, then Bootstrap JS -->\n <script src=\"https://code.jquery.com/jquery-2.2.4.js\" integrity=\"sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=\" crossorigin=\"anonymous\"></script> <!-- Latest compiled and minified JavaScript -->\n <script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js\" integrity=\"sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa\" crossorigin=\"anonymous\"></script>\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.js\" crossorigin=\"anonymous\"></script> \n \n <script>{{{payload.script}}}</script>\n</head>\n<body onload=\"wsConnect();\" onunload=\"ws.disconnect();\">\n\n<div class=\"container\">\n <div class=\"page-header\">\n <h1>Alert Monitoring System <small> Water/Gas Leaks</small></h1>\n </div>\n</div>\n<div class=\"container\">\n <div class=\"row justify-content-center\">\n <div class=\"col-12 col-lg-8 col-md-12\"> \n <div id=\"map\"></div>\n </div>\n </div>\n <br>\n <br>\n</div>\n<script async defer\n src=\"https://maps.googleapis.com/maps/api/js?key={{{payload.apikey}}}&callback=initMap\">\n</script>\n \n</body>\n</html>","x":638.000057220459,"y":169.0000286102295,"wires":[["422d2000.76f7d"]]},{"id":"ef2d665c.ae6b98","type":"comment","z":"934da042.4fbe4","name":"Inject msg object properties (API Key)","info":"","x":253.00003051757812,"y":263.0000286102295,"wires":[]},{"id":"f757b3ad.2c45","type":"comment","z":"934da042.4fbe4","name":"http://localhost:1880/monitor","info":"","x":201,"y":127.00000095367432,"wires":[]},{"id":"c8427880.258498","type":"comment","z":"934da042.4fbe4","name":"Alert Message to Browser","info":"","x":666.0000076293945,"y":349.00000190734863,"wires":[]},{"id":"4293d71c.08c428","type":"comment","z":"934da042.4fbe4","name":"New Brunswick Leak Monitoring Site","info":"","x":419,"y":62,"wires":[]},{"id":"b5ef4df0.46d12","type":"comment","z":"934da042.4fbe4","name":"IoT Event Listener ","info":"","x":176.16665649414062,"y":343.11116790771484,"wires":[]},{"id":"85ee3376.744af","type":"inject","z":"934da042.4fbe4","name":"","topic":"","payload":"","payloadType":"date","repeat":"5","crontab":"","once":false,"onceDelay":0.1,"x":192.16665267944336,"y":420.8888921737671,"wires":[["bb248168.03a0a"]]},{"id":"bb248168.03a0a","type":"function","z":"934da042.4fbe4","name":"Alert Generator","func":"var msg ={};\n\nvar count = Math.floor(Math.random() * Math.floor(4));\n\nswitch ( count ) {\ncase 0 : \n msg.payload = {\n addr : '1 RichMond St',\n la : 40.494181,\n lo : -74.440144,\n type : 'Water',\n severity : 3\n };\n break;\ncase 1 :\n msg.payload = {\n addr : '1-21 Oxford St',\n la : 40.492229,\n lo : -74.457641,\n type : 'Water',\n severity : 1\n };\n break;\ncase 2 :\n msg.payload = {\n addr : '104-70 Guilden St',\n la : 40.500203,\n lo : -74.456846,\n type : 'Gas',\n severity : 3\n };\n break;\ncase 3 :\n msg.payload = {\n addr : '9-165 Edgeworth Pl',\n la : 40.483208,\n lo : -74.454688,\n type : 'Gas',\n severity : 3\n };\n break;\ndefault :\n msg.payload = {\n addr : '300 Somerset St',\n la : 40.492735,\n lo : -74.456407,\n type : 'Water',\n severity : 2\n };\n break;\n\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":421.1666679382324,"y":421.33333587646484,"wires":[["8c59d981.361258","a0978def.ec19"]]},{"id":"7c9b20fd.7d9be","type":"debug","z":"934da042.4fbe4","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":423.1666717529297,"y":536.7777824401855,"wires":[]},{"id":"8c59d981.361258","type":"debug","z":"934da042.4fbe4","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":669.1666717529297,"y":458.7778072357178,"wires":[]},{"id":"a0978def.ec19","type":"websocket out","z":"934da042.4fbe4","name":"","server":"5e53441f.11d6ac","client":"","x":666.1666717529297,"y":388.6667232513428,"wires":[]},{"id":"1dbde980.69ccf7","type":"websocket in","z":"934da042.4fbe4","name":"","server":"5e53441f.11d6ac","client":"","x":163.16665649414062,"y":537.1112232208252,"wires":[["7c9b20fd.7d9be"]]},{"id":"b5c0a096.911a8","type":"comment","z":"934da042.4fbe4","name":"Coomunicate with Browser","info":"","x":197.16665649414062,"y":496.3333377838135,"wires":[]},{"id":"13758f8c.9d682","type":"comment","z":"934da042.4fbe4","name":"Example : MQTT message from MQTT Broker","info":"","x":255.16665649414062,"y":378.3194465637207,"wires":[]},{"id":"5e53441f.11d6ac","type":"websocket-listener","z":"","path":"/ws/alert","wholemsg":"false"}]
New pricing changes go into effect starting July 16, 2018. Get-API-Key
Once Google API-Key is available,
In Function
node,
Assign msg.payload.apikey
<- Google Map API Key
In HTML
template,
<script async defer src="https://maps.googleapis.com/maps/api/js?key={{{payload.apikey}}}&callback=initMap" type="text/javascript"></script>
For example,
- Set
httpStatic
insettings.js
to$HOME/.node_red/dist
. - Place
picture.jpg' in
$HOME/.node_red/dist/images` <img src="/images/picture.jpg">
inindex.html
If CDN options are not desirable, use httpStatic
and adjust src
attribute.