A Linear Gauge For Node-Red


My first ui node! There is also some good reading for making your own ui nodes in this thread.

Please use & modify as you wish and I hope it helps with your project.


A Node-Red ui node that creates a linear gauge with high/low limits and animated sliding pointer.

This is very useful for quickly checking many different kinds of processes and how they are performing.

Ideally, the pointer should be in the center of the gauge. This would indicate that the process is at it's setpoint.

This gauge has three different zones. One in the center is your acceptable value window. The top is the high limit zone and the bottom is the low limit zone. This way you can easily see how close the process is to it's high or low limit.

The node can be injected with:

High Limit: msg.highlimit

Setpoint: msg.setpoint

Low Limit: msg.lowlimit

Using msg.payload as the value to display and position the pointer.


Node-Red v19.4 or greater

Node-Red-dashboard v2.13.0 or greater


Run the following command in your Node-RED user directory - typically ~/.node-red

npm i node-red-contrib-ui-lineargauge


[{"id":"aa4a0304.04105","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"b2013812.ab6398","type":"random","z":"aa4a0304.04105","name":"","low":"20","high":"45","inte":"true","property":"payload","x":506,"y":287,"wires":[["d0a03537.0e7ab8"]]},{"id":"a23cb944.a1a308","type":"inject","z":"aa4a0304.04105","name":"","topic":"","payload":"","payloadType":"date","repeat":"1","crontab":"","once":true,"onceDelay":0.1,"x":288,"y":391,"wires":[["b2013812.ab6398","b7652c59.847ad","158fbf06.93ce51","7e9a5d4e.d34b44","a32bbccb.66fb5","da938b76.73b4c8"]]},{"id":"e082d1b8.5d663","type":"linear-gauge","z":"aa4a0304.04105","group":"8b6fa991.18ccd8","order":0,"width":"1","height":"5","name":" Tank #2","colorLowArea":"#ffffc0","colorMidArea":"#99ff99","colorHighArea":"#ff8080","unit":"°","x":842,"y":286,"wires":[[]]},{"id":"d0a03537.0e7ab8","type":"change","z":"aa4a0304.04105","name":"","rules":[{"t":"set","p":"highlimit","pt":"msg","to":"100","tot":"num"},{"t":"set","p":"lowlimit","pt":"msg","to":"0","tot":"num"},{"t":"set","p":"setpoint","pt":"msg","to":"50","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":680,"y":287,"wires":[["e082d1b8.5d663"]]},{"id":"78ce759c.c7957c","type":"linear-gauge","z":"aa4a0304.04105","group":"8b6fa991.18ccd8","order":0,"width":"1","height":"5","name":"Tank #3","colorLowArea":"#e6d7e8","colorMidArea":"#8000ff","colorHighArea":"#ff80ff","unit":"°","x":842,"y":330,"wires":[[]]},{"id":"e336e407.b2de58","type":"change","z":"aa4a0304.04105","name":"","rules":[{"t":"set","p":"highlimit","pt":"msg","to":"100","tot":"num"},{"t":"set","p":"lowlimit","pt":"msg","to":"0","tot":"num"},{"t":"set","p":"setpoint","pt":"msg","to":"50","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":679,"y":330,"wires":[["78ce759c.c7957c"]]},{"id":"b7652c59.847ad","type":"random","z":"aa4a0304.04105","name":"","low":"20","high":"45","inte":"true","property":"payload","x":507,"y":330,"wires":[["e336e407.b2de58"]]},{"id":"8395fca1.0f666","type":"linear-gauge","z":"aa4a0304.04105","group":"8b6fa991.18ccd8","order":0,"width":"1","height":"5","name":"Tank #4","colorLowArea":"#80ffff","colorMidArea":"#ff8000","colorHighArea":"#ff00ff","unit":"°","x":843,"y":372,"wires":[[]]},{"id":"14cea708.cebda9","type":"change","z":"aa4a0304.04105","name":"","rules":[{"t":"set","p":"highlimit","pt":"msg","to":"100","tot":"num"},{"t":"set","p":"lowlimit","pt":"msg","to":"0","tot":"num"},{"t":"set","p":"setpoint","pt":"msg","to":"50","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":681,"y":373,"wires":[["8395fca1.0f666"]]},{"id":"158fbf06.93ce51","type":"random","z":"aa4a0304.04105","name":"","low":"20","high":"45","inte":"true","property":"payload","x":509,"y":374,"wires":[["14cea708.cebda9"]]},{"id":"7e9a5d4e.d34b44","type":"random","z":"aa4a0304.04105","name":"","low":"20","high":"45","inte":"true","property":"payload","x":509,"y":413,"wires":[["6f99e65c.d07868"]]},{"id":"3f2392eb.ba84ee","type":"linear-gauge","z":"aa4a0304.04105","group":"8b6fa991.18ccd8","order":0,"width":"1","height":"5","name":" Tank #5","colorLowArea":"#c0c0c0","colorMidArea":"#99ff99","colorHighArea":"#808080","unit":"°","x":845,"y":412,"wires":[[]]},{"id":"6f99e65c.d07868","type":"change","z":"aa4a0304.04105","name":"","rules":[{"t":"set","p":"highlimit","pt":"msg","to":"100","tot":"num"},{"t":"set","p":"lowlimit","pt":"msg","to":"0","tot":"num"},{"t":"set","p":"setpoint","pt":"msg","to":"50","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":683,"y":413,"wires":[["3f2392eb.ba84ee"]]},{"id":"f551014b.e16a7","type":"linear-gauge","z":"aa4a0304.04105","group":"8b6fa991.18ccd8","order":0,"width":"1","height":"5","name":"Tank #6","colorLowArea":"#c0c0c0","colorMidArea":"#ffff00","colorHighArea":"#c0c0c0","unit":"°","x":845,"y":456,"wires":[[]]},{"id":"33ffcd53.3ace62","type":"change","z":"aa4a0304.04105","name":"","rules":[{"t":"set","p":"highlimit","pt":"msg","to":"100","tot":"num"},{"t":"set","p":"lowlimit","pt":"msg","to":"0","tot":"num"},{"t":"set","p":"setpoint","pt":"msg","to":"50","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":682,"y":456,"wires":[["f551014b.e16a7"]]},{"id":"a32bbccb.66fb5","type":"random","z":"aa4a0304.04105","name":"","low":"20","high":"45","inte":"true","property":"payload","x":510,"y":456,"wires":[["33ffcd53.3ace62"]]},{"id":"8d110c3b.fd5ab","type":"linear-gauge","z":"aa4a0304.04105","group":"8b6fa991.18ccd8","order":0,"width":"1","height":"5","name":"Tank #7","colorLowArea":"#00ff00","colorMidArea":"#ff8080","colorHighArea":"#00ff00","unit":"°","x":846,"y":498,"wires":[[]]},{"id":"eca59385.51c3","type":"change","z":"aa4a0304.04105","name":"","rules":[{"t":"set","p":"highlimit","pt":"msg","to":"100","tot":"num"},{"t":"set","p":"lowlimit","pt":"msg","to":"0","tot":"num"},{"t":"set","p":"setpoint","pt":"msg","to":"50","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":684,"y":499,"wires":[["8d110c3b.fd5ab"]]},{"id":"da938b76.73b4c8","type":"random","z":"aa4a0304.04105","name":"","low":"20","high":"45","inte":"true","property":"payload","x":512,"y":500,"wires":[["eca59385.51c3"]]},{"id":"8b6fa991.18ccd8","type":"ui_group","z":"","name":"Linear Gauges","tab":"c067cc3b.a45ea","disp":true,"width":"6","collapse":false},{"id":"c067cc3b.a45ea","type":"ui_tab","z":"","name":"Home","icon":"dashboard"}]


Thank you Bart for turning this into a working node-red node.

Merry Christmas!



As used in most glass cockpits these days. Nice work. Thank you.

1 Like


Thanks for sharing, can you also add it flows.nodered.org, as a flow, so people searching there can also find it?

1 Like


Hi @seth350, this would be indeed a nice candidate for a ui widget. Unfortunately there aren't many examples yet from which you can cheek, otherwise I would have tried to support you ... Merry Christmas for you too!

1 Like


Thank you. Ah yes, I need to add it to the flows collection. I will do that today.



Added to the flow section now with working example.


1 Like


Hi @seth350,
I have imported your example flow, but I get an error in my Chrome console log:


But the widget looks normal to me:



I see the same thing.

I see the problem. The initial value of the transform isnt correct.

I will fix it and edit the flow.



Hey @seth350,
Creating your own custom ui widget has a lot of advantages compared to pasting your code in a template node:

  • It can easily be installed via the palette
  • It has it's own config screen, where you can allow the user to setup your client-side widget
  • ...

But as mentioned before there are few ui widget examples at the moment. So it would be nice if some ui widgets could be created, so other developers have something to start from.

Therefore I have created a basic node-red-contrib-ui-linear-gauge node. To demonstrate how you can let the user customize your widget, I have added as an example 3 colour selectors. Here is a short demo (note that the animated gif becomes yellow at the end for some reason ...):

If you like it, you can have it (since you did most of the work!!). Just copy it to your Github account, and then I will remove the repository on my account. Note that you will have to change some things to start with:

  • The URL's in the package.json file refer to my Github account
  • Don't know if you want the Apache license?
  • The bug (that I reported a couple of hours ago) is not fixed here
  • ...

If you have any questions about it, please let me know !!!



Nice teamwork guys!

1 Like


Wow Bart! Thank you so much! I knew it would be so much better as an actual ui node but I didn’t/don’t know how to make it into one.

I think I should be able to figure it out since you did it.
Once I get back to my computer, I will copy it over.

Three cheers for Bart!!

On a side note, I have no preference or any idea what license I need/want. I just picked the MIT because that is what most everyone is using on npm. If Apache is “better” then I will go with it.



Ok Bart,
I think I have yours cloned now and on my repo.

Still figuring out Git and Github!



Me neither :woozy_face: Don't even know why the hell I have chosen to use Apache two years ago. Have been reading at the time being about the differences, but have completely forgotten it ... We can only hope that some unreliable lawyer is reading this, and gives us some reliable advice :joy:

Well let's admit that a large part of the Node-RED community is a great team... For this one I also like to thank @dceejay and @nisiyama for their patience with us :wink:



Thanks Bart. Its always great to see the amazing things you folk come up with, and how far you manage to push it (and us :slight_smile:

@seth350 -The core project is Apache 2 licensed, but you are free to create nodes under whatever you wish, and MIT perfectly valid if you prefer that .



When you are happy with the functionality, licence etc, you may wish to consider publishing it on npm, so that it will appear in the node-red library for the rest of the node-red community to use.



Will do Paul.

I still need to do some more editing. I would like to add some more fields to the configuration so that a user has the option to manually enter a high/low limit.

Also add option to change pointer color.

I do not understand how the configuration gets transferred into the actual ui widget?

For example, this is from the HTML file for the node, specifically for one of the color areas.

 <div class="form-row">
        <label for="node-input-colorArea1"><i class="fa fa-paint-brush"></i> Color 1</span></label>
        <input type="color" id="node-input-colorArea1"/>
                .scaleArea1 {
                    fill: ` + config.colorArea1 + `;
                    stroke: #000;
                    stroke-width: 1px;

Where is config derived from in the CSS and in the HTML?




I just installed the node to my node-red and I am getting some errors. The node shows up under the dashboard nodes but it does not add itself to the dashboard group.

I get this error in the log.



@seth350: You will have to implement a couple of things to transfer data from you config screen to the dashboard widget:

  • Create a html widget to visualise the data in the config screen (flow editor):
    <div class="form-row">
         <label for="node-input-colorArea1"><i class="fa fa-paint-brush"></i> Color 1</span></label>
         <input type="color" id="node-input-colorArea1"/>
  • Then the data needs to be listed inside the 'default' values section of 'this' JavaScript object:
    defaults: {
             colorArea1: {value: '#ffffc0'}
    You specify here the default value (in case the user hasn't been able to change it yet), but you can also e.g. specify whether the fields is required or not ...
  • The the colorArea1 fields becomes available in the config input parameter:
    function HTML(config) {
  • And then you can use it inside the html code that will be generated (in the flow on the server side which will be send to the dashboard on the client side):
    .scaleArea1 {
         fill: ` + config.colorArea1 + `;
         stroke: #000;
         stroke-width: 1px;

Note that this works all fine when you drag a new gauge-node on your flow editor. But it will become more difficult when you have to add new fields afterwards, and users have already been using your previous version in their flows. Then you will need to write some extra Javascript code to make sure that existing nodes (which don't have your new field yet), will get the new field with the correct default data. But that is not relevant for you at this moment (since you haven't published your first version on npm yet): just remove the existing gauge-nodes from your flow, and drag a new one from your pallette to your flow and the new field with the default value will become automatically available. In case you will ever need it, here is how you can accomplish it ...

Have you installed it in your node-RED directory? Don't know about this ... Is there anybody else that knows what could be causing this problem ???

1 Like


Yes, I copied it to the node_modules directory and then npm install node-red-contrib-ui-lineargauge

I also added it to NPM last night and it is now available in the pallet manager. Installing from there gives the same problem. Tried npm uninstall node-red-contrib-ui-lineargauge and then reinstall, restart node-red. Same problem.

The problem did not resolve itself until I upgraded to dashboard v2.13. I was on v2.9.8.



Next problem,

How to individualize each widget?
As it is, if more than one gauge is added, the first will continue working and the second one will not update its pointer. The colors are also being applied globally to all gauges.
I believe this is caused by hard coding the id into the gauge.
<div class="linearGauge">

I see that the other widgets assign their own class/id so that they can each be customized or display their own value. IE., gauge_249, gauge_250
It doesnt seem to assign the id in the node's .js or .html file.
Does the id get assigned when the dashboard is loaded?

<div id="gauge_249" style="text-align:center;;width:288px;
    padding-top:0px;" class="nr-dashboard-gauge">

Currently, this is what I get.
The payload and name are unique, but that is all.