Simple Web page with live data updates via websocket

This flow creates a simple web page at 'http://{your-server-ip:1880/simple'

The web page then connects back to the server using a websocket and receives time updates every 5 seconds and updates the web page with that information.

There is also a simple button to push a message back to the server - which will appear in the debug.

\n \n \n \n

Simple Live Display

\n
\n \n
\n
unknown
\n
\n \n\n","x":338,"y":260,"wires":[["42a28745.bd5d78"]]},{"id":"45dbf990.ba2408","type":"function","z":"9e538f88.61ac7","name":"format time nicely","func":"msg.payload = Date(msg.payload).toString();\nreturn msg;","outputs":1,"noerr":0,"x":327,"y":320,"wires":[["50da04b3.af25fc"]]},{"id":"eccc8bc2.133378","type":"websocket in","z":"9e538f88.61ac7","name":"","server":"985ecbc7.67a138","client":"","x":326,"y":372,"wires":[["9adfff59.652"]]},{"id":"9adfff59.652","type":"debug","z":"9e538f88.61ac7","name":"","active":true,"console":"false","complete":"false","x":516,"y":372,"wires":[]}] /** * Parse the flow.json to get the details on the tabs/subflows present */ const processFlow = function (flow) { const tabids = [] const tabs = [] flow.forEach((d) => { if (d.type === 'subflow') { tabids.push(d.id) tabs.push({ id: d.id, label: d.name, type: 'subslow' }) } else if (d.z) { if (tabids.indexOf(d.z) === -1) { tabids.push(d.z) tabs.push({ id: d.z, label: d.z, type: 'tab', }) } } }) return tabs } function addPanZoom () { var svgs = d3.selectAll('.flowviewer svg'); svgs.each(function() { var svg = d3.select(this); svg.html('' + svg.html() + ''); var inner = svg.select('g'); var zoom = d3.zoom() .translateExtent([[0, 0], [3000, 3000]]) .scaleExtent([0.5, 1]) .on("start", function () { svg.classed("dragging", true); }) .on('zoom', function(event) { inner.attr('transform', event.transform); }) .on("end", function () { svg.classed("dragging", false); }); svg.call(zoom); }); } const clearFlow = function () { $('.flowviewer svg .flowGridlines').empty() $('.flowviewer svg .containerGroup').empty() $('.flowviewer svg .flowGroups').empty() $('.flowviewer svg .flowWires').empty() $('.flowviewer svg .flowNodes').empty() } // get the tabs/subflows for our provided flow const tabs = processFlow(flow) // sort them such that tabs render first tabs.sort((a, b) => { return a.type > b.type ? -1 : (a.type < b.type) ? 1 : 0 }) function openTab (id) { // clear any existing flow clearFlow() // draw the new flow - uses function from `public/js/flowviewer.js` renderFlow(id, flow, $('.flowviewer svg')); addPanZoom() } function addTab (tab, index) { const classes = 'flowviewer-tab flowviewer-' + tab.type const name = tab.type === 'tab' ? 'Flow ' + (index + 1) : tab.label const tabDOM = $('.flowviewer-tabs') .append(`
${name}
`) .on('click', `#flowviewer-tab-${index}`, function () { // add on click to each new tab $('.flowviewer-tab').removeClass('active') $(this).addClass('active') openTab(tab.id) }) } tabs.forEach((tab, index) => { addTab(tab, index) }) clearFlow(); renderFlow(tabs[0].id, flow, $('.flowviewer svg')); addPanZoom(); $('#flowviewer-tab-0').addClass('active') $('#copy-flow').on('click', function() { navigator.clipboard.writeText(JSON.stringify(flow)); }) })()
[{"id":"985ecbc7.67a138","type":"websocket-listener","z":"9e538f88.61ac7","path":"/ws/simple","wholemsg":"false"},{"id":"e3e4522e.1c1bb","type":"inject","z":"9e538f88.61ac7","name":"Tick every 5 secs","topic":"test","payload":"","payloadType":"date","repeat":"5","crontab":"","once":false,"x":139,"y":320,"wires":[["45dbf990.ba2408"]]},{"id":"50da04b3.af25fc","type":"websocket out","z":"9e538f88.61ac7","name":"","server":"985ecbc7.67a138","client":"","x":539,"y":320,"wires":[]},{"id":"42a28745.bd5d78","type":"http response","z":"9e538f88.61ac7","name":"","x":510,"y":260,"wires":[]},{"id":"1787be40.e87842","type":"http in","z":"9e538f88.61ac7","name":"","url":"/simple","method":"get","swaggerDoc":"","x":141,"y":260,"wires":[["1857548e.e7a8ab"]]},{"id":"1857548e.e7a8ab","type":"template","z":"9e538f88.61ac7","name":"Simple Web Page","field":"payload","format":"html","template":"<!DOCTYPE HTML>\n<html>\n    <head>\n    <title>Simple Live Display</title>\n    <script type=\"text/javascript\">\n        var ws;\n        var wsUri = \"ws:\";\n        var loc = window.location;\n        console.log(loc);\n        if (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\n        wsUri += \"//\" + loc.host + loc.pathname.replace(\"simple\",\"ws/simple\");\n\n        function wsConnect() {\n            console.log(\"connect\",wsUri);\n            ws = new WebSocket(wsUri);\n            //var line = \"\";    // either uncomment this for a building list of messages\n            ws.onmessage = function(msg) {\n                var line = \"\";  // or uncomment this to overwrite the existing message\n                // parse the incoming message as a JSON object\n                var data = msg.data;\n                //console.log(data);\n                // build the output from the topic and payload parts of the object\n                line += \"<p>\"+data+\"</p>\";\n                // replace the messages div with the new \"line\"\n                document.getElementById('messages').innerHTML = line;\n                //ws.send(JSON.stringify({data:data}));\n            }\n            ws.onopen = function() {\n                // update the status div with the connection status\n                document.getElementById('status').innerHTML = \"connected\";\n                //ws.send(\"Open for data\");\n                console.log(\"connected\");\n            }\n            ws.onclose = function() {\n                // update the status div with the connection status\n                document.getElementById('status').innerHTML = \"not connected\";\n                // in case of lost connection tries to reconnect every 3 secs\n                setTimeout(wsConnect,3000);\n            }\n        }\n        \n        function doit(m) {\n            if (ws) { ws.send(m); }\n        }\n    </script>\n    </head>\n    <body onload=\"wsConnect();\" onunload=\"ws.disconnect();\">\n        <font face=\"Arial\">\n        <h1>Simple Live Display</h1>\n        <div id=\"messages\"></div>\n        <button type=\"button\" onclick='doit(\"click\");'>Click to send message</button>\n        <hr/>\n        <div id=\"status\">unknown</div>\n        </font>\n    </body>\n</html>\n","x":338,"y":260,"wires":[["42a28745.bd5d78"]]},{"id":"45dbf990.ba2408","type":"function","z":"9e538f88.61ac7","name":"format time nicely","func":"msg.payload = Date(msg.payload).toString();\nreturn msg;","outputs":1,"noerr":0,"x":327,"y":320,"wires":[["50da04b3.af25fc"]]},{"id":"eccc8bc2.133378","type":"websocket in","z":"9e538f88.61ac7","name":"","server":"985ecbc7.67a138","client":"","x":326,"y":372,"wires":[["9adfff59.652"]]},{"id":"9adfff59.652","type":"debug","z":"9e538f88.61ac7","name":"","active":true,"console":"false","complete":"false","x":516,"y":372,"wires":[]}]

Flow Info

Created 8 years, 7 months ago
Updated 3 years, 7 months ago
Rating: 5 3

Owner

Actions

Rate:

Node Types

Core
  • debug (x1)
  • function (x1)
  • http in (x1)
  • http response (x1)
  • inject (x1)
  • template (x1)
  • websocket in (x1)
  • websocket out (x1)
  • websocket-listener (x1)

Tags

  • web
  • page
  • websocket
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option