Dynamic Data into the HTML Page

Hi,
A user gave me the flow below and it make sense the way it works, but my question now is, what if we replace that change node with a dynamic value or data points coming from, let's say a sensor or device, that has readDP node already built into the node red that's installed on that device? I tried it, and it didn't work. I see values fine in the debug panel but not onto to the HTML page. on to the HTML page it reads: This is the payload: [object Object].

I didn't upload my flow that didn't work because you need to have that same device on your network with node red installed on it to be able to see the read data or write data nodes.

any help would be appreciated so much.

[{"id":"ac8ddd3f.28e1f8","type":"http in","z":"b913f30e.6b6098","name":"","url":"test","method":"get","upload":false,"swaggerDoc":"","x":220,"y":300,"wires":[["57b1ecda.eed20c"]]},{"id":"8b51bdc0.47edc","type":"http response","z":"b913f30e.6b6098","name":"","statusCode":"","headers":{},"x":840,"y":300,"wires":[]},{"id":"3c861534.f6423a","type":"template","z":"b913f30e.6b6098","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div>This is the payload: {{payload}} !</div>","output":"str","x":640,"y":300,"wires":[["8b51bdc0.47edc"]]},{"id":"57b1ecda.eed20c","type":"change","z":"b913f30e.6b6098","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"8","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":300,"wires":[["3c861534.f6423a"]]}]

Hi Ashur .. you could store your values coming from your sensor in Context (more info on the use of Context here)

and when its time to push the values with your http endpoint, use a Function node instead to read them flow.get() and serve them through your template to http out.

Example :

[{"id":"ac8ddd3f.28e1f8","type":"http in","z":"f9d7b0ee.037d2","name":"","url":"test1","method":"get","upload":false,"swaggerDoc":"","x":300,"y":1220,"wires":[["b53e8bc.0cadef8"]]},{"id":"8b51bdc0.47edc","type":"http response","z":"f9d7b0ee.037d2","name":"","statusCode":"","headers":{},"x":920,"y":1220,"wires":[]},{"id":"3c861534.f6423a","type":"template","z":"f9d7b0ee.037d2","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div>This is the Sensor1: {{payload}} !</div>","output":"str","x":720,"y":1220,"wires":[["8b51bdc0.47edc"]]},{"id":"b66885a3.f9c938","type":"inject","z":"f9d7b0ee.037d2","name":"Sensor value","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"10","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$random() * 10\t","payloadType":"jsonata","x":280,"y":1140,"wires":[["3854cf2a.20656"]]},{"id":"3854cf2a.20656","type":"function","z":"f9d7b0ee.037d2","name":"set Context","func":"flow.set(\"Sensor1\", msg.payload)\n","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":1140,"wires":[]},{"id":"b53e8bc.0cadef8","type":"function","z":"f9d7b0ee.037d2","name":"proccess Context","func":"let Sensor1 = flow.get('Sensor1') || 'not set'\n\nmsg.payload = Sensor1.toFixed(2)\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":1220,"wires":[["3c861534.f6423a"]]}]

The payload you are feeding into the template in the flow you posted is a number. You have not shown what you fed in from your sensor, but i suspect it's an object.
Below is an example of feeding a number and a object. You will see the object produces the [object Object] output in the template..

In the last inject i feed an object but i move the value of the object to the global context, not the object.

Have a play, hope it helps.

[{"id":"1cb3fdc4.19e5ba","type":"inject","z":"5a245aa1.510164","name":"simulate http in","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":110,"y":3980,"wires":[["57b1ecda.eed20c"]]},{"id":"57b1ecda.eed20c","type":"change","z":"5a245aa1.510164","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"replace","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":3980,"wires":[["3c861534.f6423a"]]},{"id":"3c861534.f6423a","type":"template","z":"5a245aa1.510164","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div>This is the payload: {{payload}} !</div>","output":"str","x":490,"y":3980,"wires":[["5b5812de.71eeec"]]},{"id":"5b5812de.71eeec","type":"debug","z":"5a245aa1.510164","name":"http response","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":680,"y":3980,"wires":[]},{"id":"fe41594e.17c69","type":"inject","z":"5a245aa1.510164","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"test\":8}","payloadType":"json","x":160,"y":4100,"wires":[["7e6931c8.e12188"]]},{"id":"a2d27855.3db768","type":"inject","z":"5a245aa1.510164","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"8","payloadType":"num","x":150,"y":4060,"wires":[["7e6931c8.e12188"]]},{"id":"587e85ed.13e8ec","type":"inject","z":"5a245aa1.510164","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"test\":8}","payloadType":"json","x":160,"y":4160,"wires":[["51fab315.562834"]]},{"id":"51fab315.562834","type":"change","z":"5a245aa1.510164","name":"","rules":[{"t":"set","p":"replace","pt":"global","to":"payload.test","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":380,"y":4160,"wires":[[]]},{"id":"7e6931c8.e12188","type":"change","z":"5a245aa1.510164","name":"","rules":[{"t":"set","p":"replace","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":4080,"wires":[[]]}]

You could also target the the value of the object in the template.
eg.

[{"id":"1cb3fdc4.19e5ba","type":"inject","z":"5a245aa1.510164","name":"simulate http in","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":110,"y":3980,"wires":[["57b1ecda.eed20c"]]},{"id":"57b1ecda.eed20c","type":"change","z":"5a245aa1.510164","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"replace","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":300,"y":3980,"wires":[["3c861534.f6423a","9099db5.f466ba8"]]},{"id":"9099db5.f466ba8","type":"template","z":"5a245aa1.510164","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div>This is the payload: {{payload.test}} !</div>","output":"str","x":490,"y":4020,"wires":[["1c1cec2.087e614"]]},{"id":"1c1cec2.087e614","type":"debug","z":"5a245aa1.510164","name":"http response target payload.test","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":750,"y":4020,"wires":[]},{"id":"fe41594e.17c69","type":"inject","z":"5a245aa1.510164","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"test\":8}","payloadType":"json","x":160,"y":4100,"wires":[["7e6931c8.e12188"]]},{"id":"7e6931c8.e12188","type":"change","z":"5a245aa1.510164","name":"","rules":[{"t":"set","p":"replace","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":370,"y":4080,"wires":[[]]}]

Awesome UnborN. it's working beautifully now. Please forgive my knowledge, but I have two more questions
1- how I update the page dynamically without refreshing it all the time to see the new values?
2- what if I have multiple different sensors to insert into the page! what would Mustache syntax be? For example, first div would show first sensor values and second div would show other sensor values and so on...

Hi .. but why dont you use the Node-red Dashboard for this ?
it removes all the complexity of having to create your own.

If you still want to experiment with serving your own page then maybe it would be best to use a Websocket connection .. websockets leaves the connection open and you can send and update data without the need to refresh the page .. there is an example of its use in the Flows library.

https://flows.nodered.org/flow/8666510f94ad422e4765

I have modified the base example a bit, to apply for multiple sensors

[{"id":"e3e4522e.1c1bb","type":"inject","z":"f9d7b0ee.037d2","name":"Tick every 5 secs","props":[{"p":"payload","v":"","vt":"date"},{"p":"topic","v":"test","vt":"string"}],"repeat":"5","crontab":"","once":false,"topic":"test","payload":"","payloadType":"date","x":350,"y":1560,"wires":[["45dbf990.ba2408"]]},{"id":"50da04b3.af25fc","type":"websocket out","z":"f9d7b0ee.037d2","name":"","server":"985ecbc7.67a138","client":"","x":770,"y":1560,"wires":[]},{"id":"42a28745.bd5d78","type":"http response","z":"f9d7b0ee.037d2","name":"","x":741,"y":1500,"wires":[]},{"id":"1787be40.e87842","type":"http in","z":"f9d7b0ee.037d2","name":"","url":"/simple","method":"get","swaggerDoc":"","x":372,"y":1500,"wires":[["1857548e.e7a8ab"]]},{"id":"1857548e.e7a8ab","type":"template","z":"f9d7b0ee.037d2","name":"Simple Web Page","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","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            \n            ws.onmessage = function(msg) {\n                console.log('Message received', msg.data);\n                // parse the incoming message data as a JSON object\n                let data = JSON.parse(msg.data)\n                var sensor1 = data.sensor1;\n                var sensor2 = data.sensor2;\n                \n                \n                // replace the messages div with the new \"line\"\n                document.getElementById('sensor1').innerHTML = \"<p>Sensor1 : \" + sensor1 + \"</p>\";\n                document.getElementById('sensor2').innerHTML = \"<p>Sensor2 : \" + sensor2 + \"</p>\";\n               \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 Sensor Display</h1>\n        <div id=\"sensor1\"></div>\n        <div id=\"sensor2\"></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":569,"y":1500,"wires":[["42a28745.bd5d78"]]},{"id":"45dbf990.ba2408","type":"function","z":"f9d7b0ee.037d2","name":"send Sensors Context","func":"msg.payload = flow.get('Sensors');\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":568,"y":1560,"wires":[["50da04b3.af25fc"]]},{"id":"eccc8bc2.133378","type":"websocket in","z":"f9d7b0ee.037d2","name":"","server":"","client":"","x":557,"y":1612,"wires":[["9adfff59.652"]]},{"id":"9adfff59.652","type":"debug","z":"f9d7b0ee.037d2","name":"","active":true,"console":"false","complete":"false","x":747,"y":1612,"wires":[]},{"id":"363a82a7.27e826","type":"inject","z":"f9d7b0ee.037d2","name":"Sensor values","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"10","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":360,"y":1420,"wires":[["e9d1a098.ee2338"]]},{"id":"e9d1a098.ee2338","type":"function","z":"f9d7b0ee.037d2","name":"set Context","func":"let sensor1 = Number((Math.random() * 10).toFixed(2))\nlet sensor2 = Number((Math.random() * 10).toFixed(2))\n\nflow.set(\"Sensors\", {sensor1, sensor2})\n\n","outputs":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":1420,"wires":[]},{"id":"985ecbc7.67a138","type":"websocket-listener","z":"f9d7b0ee.037d2","path":"/ws/simple","wholemsg":"false"}]
1 Like

If you want to do something bespoke but still want to use websockets, uibuilder is the tool to use.

I wanted to use Dashboard, but it has limits when it comes to style. I have to find all the css selectors to customize it. So I thought building my own html page but just have to learn how I get my data values in. and with your example, I see how this needs to be done. I really appreciate your help.

it can be styled .. but yea .. i know what you mean .. me too when i started with node red Dashboards i felt the need to have more control of the javascript and design of the page.

That's when i discovered @TotallyInformation 's node-red-contrib-uibuilder that provides just that.
(Sorry Julian :wink: .. i should have recommended it sooner but i didnt know Ashur's knowledge on html, JS so i posted Dave's simpler websocket example)

2 Likes

uibuilder lets you build your pages however you like. All you need to do is include the uibuilderfe.js front-end library and you then get a bunch of helper functions ready to go. These let you very easily receive and act on data from Node-RED and send data back. Saves you the bother of messing with the nuts and bolts of it all. Though, of course, NR also gives you the tools to do it the hard way too :slight_smile:

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.