Help with making a volume gui widget with no html knowledge


  1. i am totally new and learning node-red.
  2. i have no programing exp. nor html exp.
    I'm learning stuff on the fly.
    so with that any help i can get with my project will be appreciated. however i am not asking for someone to do completely for me i need to know how to do myself so i can grow. but what i do need is a nudge or as my wife would say a hard shove off the cliff.
    I own a brewery and right now while not allowed to have indoor customers i started learning node-red.
    to customize my brewing system to be more visual and eyecandy.
    what i have done so far.
    I have a group that turns pumps on and off ,
    group 2 turns on water supply
    group 3 shows the temperatures of my boil kettles
    group 4 shows the temps of fermenters.
    right now its basic and works however this is what i need help with.
    I have been envisioning tanks on my screen and as i am filling them up the gui will show a visual level of the liquid, changing color depending on the actual temp of the liquid(blue cold, red hot).
    also displaying to the right of the tanks the actual volume in gallons and actual temperature.
    eventually as i learn my ultrasonic sensors ill add them to my fermenters as well because they are enclosed.
    i don't know any html coding so can i use a regular html editor to have the basic visual done or do i need to write complete code, meaning i have to learn html first.
    whats the best direction, and or am i way over my head and i should stick to just making the beer? lol

Well if you want to avoid html and all that then you can probably get away with customising the built in level gauge widget.
By default is looks like

but you can add a ui_control property to the message to customise many of the parts including colours etc.. for example

Example flow below...

[{"id":"ce809e.d3996f6","type":"inject","z":"cca8d493.5a61e8","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"50","payloadType":"num","x":180,"y":570,"wires":[["68085e5a.c0c4"]]},{"id":"68085e5a.c0c4","type":"function","z":"cca8d493.5a61e8","name":"","func":"msg.ui_control = {\n    options: {\n        circleColor:\"#FF7777\", \n        textColor:\"#FF4444\", \n        waveTextColor:\"#FFAAAA\", \n        waveColor:\"#FFDDDD\", \n        circleThickness:0.1, \n        textVertPosition:0.25, \n        waveHeight:0.05, \n        waveCount:8\n    }\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":375,"y":570,"wires":[["a71c000b.e3c62"]]},{"id":"a71c000b.e3c62","type":"ui_gauge","z":"cca8d493.5a61e8","name":"","group":"d16ec6fc.4da508","order":7,"width":0,"height":0,"gtype":"wave","title":"IPA","label":" %","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":560,"y":570,"wires":[]},{"id":"d16ec6fc.4da508","type":"ui_group","name":"Group 1","tab":"20934cee.3e6c14","order":1,"disp":true,"width":8},{"id":"20934cee.3e6c14","type":"ui_tab","name":"Tab 1","icon":"dashboard","order":1,"disabled":false,"hidden":false}]

And of course as you could change those colours dynamically if you wish. And there are many more options that could be set - D3 Liquid Fill Gauge -

I like the circle however whats easiest way to convert this or merge the 2 together.14EE2ECCEBAC49029C5E864061F369FB.png

It's good to be ambitious... but yeah ... you would have to crack open the html and javascript and probably svg books for that.

So here is a couple of ideas I was thinking

So I may at this point really need to have someone help with the widget. Or I may be a few months out so I can learn more of html. And js.

Sorry today is brew day so I am scatter brained.

I actually can’t find the built in level gauge widget what is its actual node name?


OK so the beauty of Node Red is that you can build something in stages.

Start with the basic guages and build on them, get all your logic and reporting of data correct (hint grafana and influx DB will be fun for you in terms of graphs etc)

Then slowly start learning how to put together the graphics etc.

There is a lot to unpack in those graphics and how to get them exactly how you want (and i am not a graphics guys so can not help much there)

There are lots of guys on here who are very very good with SVG etc - so ask them pointed questions as you proceed and build it a little at a time


ok, i now have the level sensor working correctly how ever now how do i take the info and convert it to Volume.
ive been reading so many things im confused on the direction i need to do.
i have it working in node red now but it only tells me how far the level is from the sensor
not actually what i have in my tank.

Do you mean how to calculate the actual volume of liquid (in litres or whatever)? If so then you need to tell us the shape and dimensions of the tank and where the sensor is.

If you don't mean that you are trying to calculate the volume, then what are you trying to calculate?

Fairly straight forward

Measure the height of your tank - lets say for example it is 100cm

Lets say the capacity of the tank is 1000L

Therefore every cm = 10 litres

So if the sensors says the water is 20cm away from the sensor i.e the tank has 80cm of water - then it is 800 litres left

DOes not matter re the shape etc - as long as the top is flat.

Also remember if you have the Waterproof Ultrasonic - that is has a dead spot of about 20cm where it can not measure.


yes , i understand on the water proof sensor that it has a dead spot.
i also understand the logic as to how many gallons per centermeter.
the question how to i use that logic so the gage shows actual gallons instead of a percent. or a measurement from the sensor.
i assume i would have to know gallons per cm. and i need to know how many gallons actually is and i would have to know the actual distance from full line to sensor.
thanks for the input .

Here is a small portion of one of my flows that you can see the logic with

[{"id":"f81fa250.1486e","type":"mqtt in","z":"10b98a0a.175376","name":"Water Level in Tank","topic":"Tasmota/TankLevel/Distance/SENSOR","qos":"2","datatype":"json","broker":"e2ed99fb.aa45d8","x":890,"y":121,"wires":[["7fbc282b.c01c28"]]},{"id":"c4d66267.4e6b","type":"smooth","z":"10b98a0a.175376","name":"Average the Level of the tank readings","property":"payload","action":"mean","count":"20","round":"","mult":"single","reduce":false,"x":950,"y":180,"wires":[["8a33396e.6b9ad8","89b54359.9f211"]]},{"id":"8a33396e.6b9ad8","type":"function","z":"10b98a0a.175376","name":"Calculate Tank Percentage","func":"//Total Tank depth (TTD) = 153 CM\nvar OffSet = 0; //Deadspot in sensor with readings less than 20cm need to resolve by raising sensor\n//Sensor returns units in CM\n//Calculation needs to be \n//(TTD - Payload)/TTD\nvar SensorReading = msg.payload;\nvar TTD = 153; \nvar TankRemainingCapacity = (TTD - (SensorReading - OffSet))/TTD; //\nmsg.payload = Math.round(TankRemainingCapacity * 100);\nflow.set(\"TankRemainingCapacity\", msg.payload);\nnode.status({text:\"Sensor Reading \" + SensorReading});\nreturn msg;","outputs":1,"noerr":0,"x":920,"y":240,"wires":[["72920a79.7c5a34","4627097d.c66538","1db66ed6.4b9941"]]},{"id":"72920a79.7c5a34","type":"debug","z":"10b98a0a.175376","name":"Percentage Remaining","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","x":1200,"y":240,"wires":[]},{"id":"4627097d.c66538","type":"ui_gauge","z":"10b98a0a.175376","name":"Water Tank Level","group":"9252424c.768bd","order":0,"width":0,"height":0,"gtype":"wave","title":"Water Tank Level","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":1190,"y":300,"wires":[]},{"id":"dbe1dbde.cda348","type":"comment","z":"10b98a0a.175376","name":"Get the Water Tank Level from the Ultrasonic Sensor","info":"","x":990,"y":80,"wires":[]},{"id":"1db66ed6.4b9941","type":"link out","z":"10b98a0a.175376","name":"LInk to re-enable Automatic scheduling","links":["b0dc343d.457298"],"x":1015,"y":300,"wires":[]},{"id":"89b54359.9f211","type":"debug","z":"10b98a0a.175376","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1230,"y":180,"wires":[]},{"id":"7fbc282b.c01c28","type":"change","z":"10b98a0a.175376","name":"","rules":[{"t":"move","p":"payload.SR04.Distance","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1160,"y":120,"wires":[["c4d66267.4e6b"]]},{"id":"e2ed99fb.aa45d8","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"9252424c.768bd","type":"ui_group","z":"","name":"Water Tank Level","tab":"7ded32a1.cf8ccc","order":2,"disp":true,"width":"6","collapse":false},{"id":"7ded32a1.cf8ccc","type":"ui_tab","z":"","name":"Water Tank","icon":"","order":2}]


Given that there is a linear relationship of distance to volume (assuming a vertical sided tank) - then the range node can easily rescale the values for you.

Interesting on this example .

Now confused how does this flow know where to get the data from. This is what I have set up.

[{"id":"104d8a1d.65e8c6","type":"rpi-srf","z":"9de3e533.933c58","name":"hlt","topic":"SRF","pulse":"0.5","pins":"16,18","x":200,"y":100,"wires":[["270a70af.9f079","de971801.db2438","e787ee07.5a614"]]},{"id":"270a70af.9f079","type":"debug","z":"9de3e533.933c58","name":"level debug","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":470,"y":200,"wires":},{"id":"de971801.db2438","type":"ui_level","z":"9de3e533.933c58","group":"ac2830aa.1d317","order":6,"width":0,"height":0,"name":"guagetest","label":"","colorHi":"#e60000","colorWarn":"#ff9900","colorNormal":"#00b33c","colorOff":"#595959","min":"0","max":"150","segWarn":"","segHigh":"","unit":"","layout":"sh","channelA":"","channelB":"","decimals":0,"animations":"soft","shape":2,"colorschema":"fixed","textoptions":"default","colorText":"#eeeeee","fontLabel":"","fontValue":"","fontSmall":"","colorFromTheme":true,"textAnimations":true,"hideValue":false,"tickmode":"off","peakmode":false,"property":"payload","peaktime":3000,"x":460,"y":300,"wires":},{"id":"e787ee07.5a614","type":"ui_gauge","z":"9de3e533.933c58","name":"test","group":"ac2830aa.1d317","order":7,"width":0,"height":0,"gtype":"wave","title":"gauge","label":"units","format":"{{value}}","min":"150","max":"0","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":650,"y":100,"wires":},{"id":"ac2830aa.1d317","type":"ui_group","name":"BREWHOUSE","tab":"a74ffe3d.4eb73","order":3,"disp":true,"width":"8","collapse":false},{"id":"a74ffe3d.4eb73","type":"ui_tab","name":"Brewhouse","icon":"dashboard","order":3,"disabled":false,"hidden":false}]

FYI I am really new to node, I just figured out this week how to import and export.

I have no programing or html knowledge other then I used adobe go live to make pages 20 years ago.


Ok, I think I understand looking into it

I don’t really need mqtt my stuff is all local.

So this is what I have changed to your flow example to work with my set up.

I am doing this on a prototype raspberry pi instead of the one I am actually am using for the brewing system.

I removed the mqtt inject and the function node changing the message to message payload

And then I added the rpi-srf node to supply the data to the rest of your conversion.

[{"id":"c4d66267.4e6b","type":"smooth","z":"11c84233.90ee4e","name":"Average the Level of the tank readings","property":"payload","action":"mean","count":"20","round":"","mult":"single","reduce":false,"x":350,"y":159,"wires":[["8a33396e.6b9ad8","89b54359.9f211"]]},{"id":"8a33396e.6b9ad8","type":"function","z":"11c84233.90ee4e","name":"Calculate Tank Percentage","func":"//Total Tank depth (TTD) = 153 CM\nvar OffSet = 0; //Deadspot in sensor with readings less than 20cm need to resolve by raising sensor\n//Sensor returns units in CM\n//Calculation needs to be \n//(TTD - Payload)/TTD\nvar SensorReading = msg.payload;\nvar TTD = 153; \nvar TankRemainingCapacity = (TTD - (SensorReading - OffSet))/TTD; //\nmsg.payload = Math.round(TankRemainingCapacity * 100);\nflow.set("TankRemainingCapacity", msg.payload);\nnode.status({text:"Sensor Reading " + SensorReading});\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":320,"y":219,"wires":[["72920a79.7c5a34","4627097d.c66538","1db66ed6.4b9941"]]},{"id":"72920a79.7c5a34","type":"debug","z":"11c84233.90ee4e","name":"Percentage Remaining","active":true,"tosidebar":false,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":600,"y":219,"wires":},{"id":"4627097d.c66538","type":"ui_gauge","z":"11c84233.90ee4e","name":"Water Tank Level","group":"9252424c.768bd","order":0,"width":0,"height":0,"gtype":"wave","title":"Water Tank Level","label":"%","format":"{{value}}","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":590,"y":279,"wires":},{"id":"dbe1dbde.cda348","type":"comment","z":"11c84233.90ee4e","name":"Get the Water Tank Level from the Ultrasonic Sensor","info":"","x":390,"y":59,"wires":},{"id":"1db66ed6.4b9941","type":"link out","z":"11c84233.90ee4e","name":"LInk to re-enable Automatic scheduling","links":["b0dc343d.457298"],"x":415,"y":279,"wires":},{"id":"89b54359.9f211","type":"debug","z":"11c84233.90ee4e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":630,"y":159,"wires":},{"id":"c627d7dd.a84fa8","type":"rpi-srf","z":"11c84233.90ee4e","name":"hlttest","topic":"SRF","pulse":"0.5","pins":"16,18","x":100,"y":100,"wires":[["c4d66267.4e6b"]]},{"id":"9252424c.768bd","type":"ui_group","name":"Water Tank Level","tab":"7ded32a1.cf8ccc","order":2,"disp":true,"width":"6","collapse":false},{"id":"7ded32a1.cf8ccc","type":"ui_tab","name":"Water Tank","icon":"","order":2}]

Now I have to read the conversion you had written and then modify to fit my specific size ad application.

The coding has me lost but I am really picking up what you had done.

I am obviously going to have to learn python, html, css, and svg to really dial in the eyecandy I am invesioning. Once I have this dialed in. I have to figure out how to change the graphic ui to the way im thinking.

Cheers, and thanks again.

So using logic.

“msg.payload = Math.round(TankRemainingCapacity * 100);”

This line actually computes logic into a percent number

However if I switch it to say

msg.payload = Math.round(TankRemainingCapacity * 204);

204 is actual gallons of this tank.

That will intern give me actuall gallons?

Am I thinking this strait?

Node-RED gives standard set of very cool nodes to make such calculations without deep analyse needed.

Let say you measure the distance to liquid surface in tank.
No matter the units, you have:
minimum distance - meaning that tank is full
maximum distance - meaning that tank is empty
And you know the capacity of tank (no matter the units)

Must also say that this is accurate only if the tank shape is cylinder (vertical sided tank).

In this example I use arbitrary amounts:
minimum distance 20
maximum distance 147
capacity of tank 240

Examine the flow:

[{"id":"f8ffb2bb.503ce","type":"ui_slider","z":"dee586f8.76a168","name":"","label":"Distance to surface","tooltip":"","group":"ad9b95d4.4d2718","order":0,"width":"6","height":"1","passthru":true,"outs":"end","topic":"","min":"20","max":"147","step":1,"x":350,"y":900,"wires":[["3ebc0146.ad5dee"]]},{"id":"1ccfec6f.f46774","type":"ui_gauge","z":"dee586f8.76a168","name":"","group":"ad9b95d4.4d2718","order":1,"width":0,"height":0,"gtype":"gage","title":"Tank","label":"gal","format":"{{value | number:1}}","min":0,"max":"240","colors":["#00b500","#e6e600","#ca3838"],"seg1":"","seg2":"","x":670,"y":900,"wires":[]},{"id":"3ebc0146.ad5dee","type":"range","z":"dee586f8.76a168","minin":"20","maxin":"147","minout":"240","maxout":"0","action":"scale","round":false,"property":"payload","name":"","x":530,"y":900,"wires":[["1ccfec6f.f46774"]]},{"id":"ad9b95d4.4d2718","type":"ui_group","name":"Default","tab":"c38fb46f.de97f8","order":4,"disp":true,"width":"6","collapse":false},{"id":"c38fb46f.de97f8","type":"ui_tab","name":"Controls","icon":"dashboard","disabled":false,"hidden":false}]
1 Like

Here is an update,

All I did was change the formula to multiply the .”” number by actual gallons of tank and it gives me exact gallons,

It is a little off I may need to rethink and use an Arduino for the sensor. And get a more accurate reading. And then bring info into pi for the node red ui. What’s you alls thoughts.