Dashboard with SCADA-like HMI mimic using SVG graphics

Howdy. We have been playing around with Node-RED for the past half year and totally love this stuff. Fabulous tools and great forum! So here is our 2 cents back to the community.

Need
We had a need to create a SCADA-like HMI mimic for monitoring engine and electrical signals. One specific request was to be able to draw lines between equipment and put some "breakers" on them. Just like they do in real SCADA mimics. We tried to look for various ready-made tools, but all of those felt a bit too heavy for our very simple needs. We also wanted to maintain the general look and feel of the Dashboard.

Steps taken
After a bit of tinkering and fiddling with the Dashboard node, we ended up with a three layer split screen, where we added in a full-page wide group in the middle for our line art. And then added in there in that group our lines, which we created with simple svg. Creating a breaker on a line is as easy as making an SVG path and passing a payload value to the path.

Breaker code
This is the small bit that makes the "breaker". You see that we use a msg.payload as the value for the breakers upper end to move.

<!-- SG2 breaker -->
<path d="M 1125 70 v -15 l {{msg.payload}} -25" stroke-linejoin="round" style="fill:transparent;stroke:#6e6e6e;stroke-width:4" />

Full code/flow

[{"id":"edf08104.4351e","type":"ui_gauge","z":"2ac8f2df.82ad9e","name":"","group":"90836bed.157498","order":1,"width":"6","height":"4","gtype":"gage","title":"SG 4 (640kW)","label":"kW","format":"{{payload[\"27\"]}}","min":0,"max":"640","colors":["#00b500","#e6e600","#ca3838"],"seg1":"450","seg2":"550","x":1080,"y":1100,"wires":[]},{"id":"96e69974.590068","type":"ui_gauge","z":"2ac8f2df.82ad9e","name":"","group":"1d865536.eb801b","order":2,"width":"6","height":"4","gtype":"gage","title":"SG 3 (1400kW)","label":"kW","format":"{{payload[\"26\"]}}","min":0,"max":"1400","colors":["#00b500","#e6e600","#ca3838"],"seg1":"700","seg2":"1100","x":1260,"y":1100,"wires":[]},{"id":"1ffdd31e.5a990d","type":"ui_gauge","z":"2ac8f2df.82ad9e","name":"","group":"b5d39406.6debf8","order":2,"width":"6","height":"4","gtype":"gage","title":"DG 2 (875kW)","label":"kW","format":"{{payload[\"23\"]}}","min":0,"max":"875","colors":["#00b500","#e6e600","#ca3838"],"seg1":"550","seg2":"700","x":1080,"y":1200,"wires":[]},{"id":"67219259.c8fcdc","type":"ui_gauge","z":"2ac8f2df.82ad9e","name":"","group":"e9ed4336.19b7f","order":2,"width":"6","height":"4","gtype":"gage","title":"MSB Produced Power","label":"kW","format":"{{payload[\"28\"]}}","min":0,"max":"1750","colors":["#00b500","#e6e600","#ca3838"],"seg1":"600","seg2":"1200","x":1360,"y":1200,"wires":[]},{"id":"3aedbc92.8f2d34","type":"ui_gauge","z":"2ac8f2df.82ad9e","name":"","group":"e9ed4336.19b7f","order":5,"width":"6","height":"4","gtype":"gage","title":"DG FO. Flowmeter","label":"kg/h","format":"{{payload[\"3\"]}}","min":0,"max":"300","colors":["#00b500","#e6e600","#ca3838"],"seg1":"120","seg2":"160","x":1370,"y":1260,"wires":[]},{"id":"ae5f7de7.789c","type":"ui_template","z":"2ac8f2df.82ad9e","group":"b5d39406.6debf8","name":"Port side","order":3,"width":"9","height":"5","format":"<style>\n   table {\n   color: #d1d1d1;\n   width: 100%;\n   border-collapse: collapse;\n   border-spacing: 0;\n   font-size: 11px;\n   }\n   td, th {\n   border: 1px solid transparent;\n   /* No more visible border */\n   height: 30px;\n   transition: all 0.3s;\n   /* Simple transition for hover effect */\n   text-align: left;\n   }\n   th {\n   background: #6e6e6e;\n   font-weight: bold;\n   }\n   td {\n   background: #6e6e6e;\n   padding-left: 5px;\n   }\n   /* Cells in even rows (2,4,6...) are one color */\n   tr:nth-child(even) td {\n   background: #4b4b4b;\n   height: 23px;\n   }\n   /* Cells in odd rows (1,3,5...) are another (excludes header cells)  */\n   tr:nth-child(odd) td {\n   background: #3f3f3f;\n   height: 23px;\n   }\n   tr td:hover {\n   background: #8c8c8c;\n   color: #d1d1d1;\n   }\n   .container\n   {\n   /*overflow-y: scroll;*/\n   overflow-x: hidden;\n   }\n   .nr-dashboard-theme .nr-dashboard-template ::-webkit-scrollbar-track {\n   background-color: #1a1a1a;\n   }\n   .value\n   {\n   width: 100px;\n   color: #e6e6e6;\n   font-size: 13px;\n   font-weight:600;\n   text-align: center;\n   }\n   .port\n   {\n   text-align: right;\n   }\n   .unit\n   {\n   text-align: center;\n   }\n</style>\n<div>\n   <div class=\"container\">\n      <table>\n         <tbody>\n            <tr>\n               <td class=\"port\">CHG. Air Temp</td>\n               <td class=\"unit\">°C</td>\n               <td class=\"value\"><span ng-bind=\"msg.payload[108]\"></span></td>\n            </tr>\n            <tr>\n               <td class=\"port\">EXHAUST Temp</td>\n               <td class=\"unit\">°C</td>\n               <td class=\"value\"><span ng-bind=\"msg.payload[107]\"></span></td>\n            </tr>\n            <tr>\n               <td class=\"port\">LO Press</td>\n               <td class=\"unit\">Bar</td>\n               <td class=\"value\"><span ng-bind=\"msg.payload[101]\"></span></td>\n            </tr>            \n            <tr>\n               <td class=\"port\">LT Press</td>\n               <td class=\"unit\">Bar</td>\n               <td class=\"value\"><span ng-bind=\"msg.payload[106]\"></span></td>\n            </tr>      \n            <tr>\n               <td class=\"port\">HT Press</td>\n               <td class=\"unit\">Bar</td>\n               <td class=\"value\"><span ng-bind=\"msg.payload[102]\"></span></td>\n            </tr>   \n            <tr>\n               <td class=\"port\">FO Press</td>\n               <td class=\"unit\">Bar</td>\n               <td class=\"value\"><span ng-bind=\"msg.payload[100]\"></span></td>\n            </tr>   \n            <tr>\n               <td class=\"port\">LO Temp</td>\n               <td class=\"unit\">°C</td>\n               <td class=\"value\"><span ng-bind=\"msg.payload[104]\"></span></td>\n            </tr>\n            <tr>\n               <td class=\"port\">HT Temp</td>\n               <td class=\"unit\">°C</td>\n               <td class=\"value\"><span ng-bind=\"msg.payload[105]\"></span></td>\n            </tr>\n         </tbody>\n      </table>\n   </div>\n</div>\n<div></div>\n</body>\n</html>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","x":1100,"y":1260,"wires":[[]]},{"id":"124ad50e.de713b","type":"ui_template","z":"2ac8f2df.82ad9e","group":"ea4f996c.f87158","name":"Starboard side","order":2,"width":"9","height":"5","format":"<style>\n   table {\n   color: #d1d1d1;\n   width: 100%;\n   border-collapse: collapse;\n   border-spacing: 0;\n   font-size: 11px;\n   }\n   td, th {\n   border: 1px solid transparent;\n   /* No more visible border */\n   height: 30px;\n   transition: all 0.3s;\n   /* Simple transition for hover effect */\n   text-align: left;\n   }\n   th {\n   background: #6e6e6e;\n   font-weight: bold;\n   }\n   td {\n   background: #6e6e6e;\n   padding-left: 5px;\n   }\n   /* Cells in even rows (2,4,6...) are one color */\n   tr:nth-child(even) td {\n   background: #4b4b4b;\n   height: 23px;\n   }\n   /* Cells in odd rows (1,3,5...) are another (excludes header cells)  */\n   tr:nth-child(odd) td {\n   background: #3f3f3f;\n   height: 23px;\n   }\n   tr td:hover {\n   background: #8c8c8c;\n   color: #d1d1d1;\n   }\n   .container\n   {\n   /*overflow-y: scroll;*/\n   overflow-x: hidden;\n   }\n   .nr-dashboard-theme .nr-dashboard-template ::-webkit-scrollbar-track {\n   background-color: #1a1a1a;\n   }\n   .value\n   {\n   width: 100px;\n   color: #e6e6e6;\n   font-size: 13px;\n   font-weight:600;\n   text-align: center;\n   }\n   .port\n   {\n   text-align: right;\n   }\n   .unit\n   {\n   text-align: center;\n   }\n</style>\n<div>\n   <div class=\"container\">\n      <table>\n         <tbody>\n            <tr>\n               <td class=\"value\"><span ng-bind=\"msg.payload[98]\"></span></td>\n               <td class=\"unit\">°C</td>\n               <td>CHG. Air Temp</td>\n            </tr>\n            <tr>\n               <td class=\"value\"><span ng-bind=\"msg.payload[97]\"></span></td>\n               <td class=\"unit\">°C</td>\n               <td>EXHAUST Temp</td>\n            </tr>            \n            <tr>\n               <td class=\"value\"><span ng-bind=\"msg.payload[91]\"></span></td>\n               <td class=\"unit\">Bar</td>\n               <td>LO Press</td>\n            </tr>\n            <tr>\n               <td class=\"value\"><span ng-bind=\"msg.payload[96]\"></span></td>\n               <td class=\"unit\">Bar</td>\n               <td>LT Press</td>\n            </tr>\n            <tr>\n               <td class=\"value\"><span ng-bind=\"msg.payload[92]\"></span></td>\n               <td class=\"unit\">Bar</td>\n               <td>HT Press</td>\n            </tr>\n            <tr>\n               <td class=\"value\"><span ng-bind=\"msg.payload[90]\"></span></td>\n               <td class=\"unit\">Bar</td>\n               <td>FO Press</td>\n            </tr>\n            <tr>\n               <td class=\"value\"><span ng-bind=\"msg.payload[94]\"></span></td>\n               <td class=\"unit\">°C</td>\n               <td>LO Temp</td>\n            </tr>\n            <tr>\n               <td class=\"value\"><span ng-bind=\"msg.payload[95]\"></span></td>\n               <td class=\"unit\">°C</td>\n               <td>HT Temp</td>\n            </tr>\n            </tbody>\n      </table>\n   </div>\n</div>\n<div></div>\n</body>\n</html>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","x":1620,"y":1260,"wires":[[]]},{"id":"8c6f799a.bf7128","type":"ui_gauge","z":"2ac8f2df.82ad9e","name":"","group":"ea4f996c.f87158","order":1,"width":"6","height":"4","gtype":"gage","title":"DG 1 (875kW)","label":"kW","format":"{{payload[\"22\"]}}","min":0,"max":"875","colors":["#00b500","#e6e600","#ca3838"],"seg1":"550","seg2":"700","x":1620,"y":1200,"wires":[]},{"id":"31ade0c9.b1fb9","type":"ui_template","z":"2ac8f2df.82ad9e","group":"c9a2551b.5b9168","name":"Lines","order":2,"width":"30","height":"2","format":"<style>\n   .container\n   {\n   overflow-y: hidden;\n   overflow-x: hidden;\n   }\n</style>\n<div class=\"container\">\n    <svg height=\"100\" width=\"1600\">\n      <!-- to SG4 -->\n      <line x1=\"150\" y1=\"0\" x2=\"150\" y2=\"70\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- vertical PS -->\n      <line x1=\"148\" y1=\"70\" x2=\"702\" y2=\"70\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- to DG2 -->\n      <line x1=\"312\" y1=\"70\" x2=\"312\" y2=\"100\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- to SG3 -->\n      <line x1=\"475\" y1=\"0\" x2=\"475\" y2=\"30\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- SG3 breaker -->\n      <path d=\"M 475 70 v -15 l {{msg.payload}} -25\" stroke-linejoin=\"round\" style=\"fill:transparent;stroke:#6e6e6e;stroke-width:4\" />\n      <!-- vertical PS BT -->\n      <line x1=\"475\" y1=\"15\" x2=\"752\" y2=\"15\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- to PS BT -->\n      <line x1=\"750\" y1=\"0\" x2=\"750\" y2=\"15\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n\n      <!-- to MSB -->\n      <line x1=\"700\" y1=\"70\" x2=\"700\" y2=\"100\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- to MSB -->\n      <line x1=\"900\" y1=\"70\" x2=\"900\" y2=\"100\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- vertical SB -->\n      <line x1=\"898\" y1=\"70\" x2=\"1452\" y2=\"70\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- vertical SB BT -->\n      <line x1=\"848\" y1=\"15\" x2=\"1125\" y2=\"15\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- to SB BT -->\n      <line x1=\"850\" y1=\"0\" x2=\"850\" y2=\"15\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- to SG2 -->\n      <line x1=\"1125\" y1=\"0\" x2=\"1125\" y2=\"30\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- SG2 breaker -->\n      <path d=\"M 1125 70 v -15 l {{msg.payload}} -25\" stroke-linejoin=\"round\" style=\"fill:transparent;stroke:#6e6e6e;stroke-width:4\" />\n      <!-- to DG1 -->\n      <line x1=\"1288\" y1=\"70\" x2=\"1288\" y2=\"100\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n      <!-- to SG1 -->\n      <line x1=\"1450\" y1=\"0\" x2=\"1450\" y2=\"70\" style=\"stroke:#6e6e6e;stroke-width:4\" />\n    </svg>\n</div>\n</body>\n</html>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","x":1350,"y":1060,"wires":[[]]},{"id":"5249259c.8f706c","type":"ui_template","z":"2ac8f2df.82ad9e","group":"65f6f27e.a1f1ec","name":"Bowthruster","order":2,"width":"6","height":"4","format":"<style>\n   .container\n   {\n   overflow-y: hidden;\n   overflow-x: hidden;\n   }\n   .heading\n   {\n   height:100px;\n   text-align:center;\n   }\n   .center\n   {\n   text-align:center;\n   padding-bottom: 10px;\n   }\n\n</style>\n<div class=\"container\">\n    <div class=\"heading\">\n    Bowthrusters\n    </div>\n    <div class=\"center\">\n    AFT No.2&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FWD No.1\n    </div>   \n    <div class=\"center\">\n    <i class=\"material-icons\" style=\"font-size: 60px\">toys</i>\n    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n    <i class=\"material-icons\" style=\"font-size: 60px\">toys</i>\n    </div>   \n</div>\n</body>\n</html>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","x":1370,"y":1140,"wires":[[]]},{"id":"5528b9b1.bbdfb8","type":"ui_gauge","z":"2ac8f2df.82ad9e","name":"","group":"fa92953a.7a1b58","order":1,"width":"6","height":"4","gtype":"gage","title":"SG 1 (640kW)","label":"kW","format":"{{payload[\"24\"]}}","min":0,"max":"640","colors":["#00b500","#e6e600","#ca3838"],"seg1":"450","seg2":"550","x":1620,"y":1100,"wires":[]},{"id":"93701fab.567f","type":"ui_gauge","z":"2ac8f2df.82ad9e","name":"","group":"b238e66b.53c658","order":2,"width":"6","height":"4","gtype":"gage","title":"SG 2 (1400kW)","label":"kW","format":"{{payload[\"25\"]}}","min":0,"max":"1400","colors":["#00b500","#e6e600","#ca3838"],"seg1":"700","seg2":"1100","x":1440,"y":1100,"wires":[]},{"id":"2b4d5c75.692d14","type":"ui_template","z":"2ac8f2df.82ad9e","group":"e9ed4336.19b7f","name":"Description","order":7,"width":"12","height":"1","format":"<style>\n   .center\n   {\n   text-align: center;\n   font-size: 13px;\n   }\n</style>\n<div>\n   <div class=\"center\">\n    DG SFOC = AUX Fuel / DG1+2 Power &nbsp;&nbsp;&nbsp;&nbsp; ME SFOC = ME Fuel / Propulsion power\n   </div>\n</div>\n<div></div>\n</body>\n</html>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","x":1430,"y":1420,"wires":[[]]},{"id":"50c86ca5.8de244","type":"smooth","z":"2ac8f2df.82ad9e","name":"Smooth","property":"payload.sfocAux","action":"mean","count":"5","round":"0","mult":"single","reduce":false,"x":1240,"y":1320,"wires":[["ea6573a6.199f9"]]},{"id":"d7869207.2b2a2","type":"smooth","z":"2ac8f2df.82ad9e","name":"Smooth","property":"payload.sfoc","action":"mean","count":"5","round":"0","mult":"single","reduce":false,"x":1500,"y":1320,"wires":[["e6c691ea.9f50f"]]},{"id":"ea6573a6.199f9","type":"ui_level","z":"2ac8f2df.82ad9e","group":"e9ed4336.19b7f","order":4,"width":"3","height":"4","name":"DG SFOC","label":"DG SFOC","colorHi":"#e60000","colorWarn":"#ff9900","colorNormal":"#00b33c","colorOff":"#595959","min":"100","max":"300","segWarn":"225","segHigh":"270","unit":"g/kWh","layout":"sv","channelA":"","channelB":"","decimals":0,"animations":"off","shape":"1","colorschema":"fixed","textoptions":"default","colorText":"#eeeeee","fontLabel":"","fontValue":"","fontSmall":"","colorFromTheme":true,"textAnimations":false,"hideValue":false,"tickmode":"segments","peakmode":false,"property":"payload.sfocAux","peaktime":3000,"x":1310,"y":1360,"wires":[]},{"id":"e6c691ea.9f50f","type":"ui_level","z":"2ac8f2df.82ad9e","group":"e9ed4336.19b7f","order":6,"width":"3","height":"4","name":"ME SFOC","label":"ME SFOC","colorHi":"#e60000","colorWarn":"#ff9900","colorNormal":"#00b33c","colorOff":"#595959","min":"100","max":"300","segWarn":"225","segHigh":"270","unit":"g/kWh","layout":"sv","channelA":"","channelB":"","decimals":0,"animations":"off","shape":"1","colorschema":"fixed","textoptions":"default","colorText":"#eeeeee","fontLabel":"","fontValue":"","fontSmall":"","colorFromTheme":true,"textAnimations":false,"hideValue":false,"tickmode":"segments","peakmode":false,"property":"payload.sfoc","peaktime":3000,"x":1550,"y":1360,"wires":[]},{"id":"9d0aff26.b57db","type":"inject","z":"2ac8f2df.82ad9e","name":"Close breaker","topic":"","payload":"0","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":1170,"y":1020,"wires":[["31ade0c9.b1fb9"]]},{"id":"412fc689.a556a8","type":"inject","z":"2ac8f2df.82ad9e","name":"Open breaker","topic":"","payload":"15","payloadType":"num","repeat":"","crontab":"","once":true,"onceDelay":0.1,"x":1180,"y":1060,"wires":[["31ade0c9.b1fb9"]]},{"id":"90836bed.157498","type":"ui_group","z":"","name":"SG4","tab":"d0e5d7d6.5b1ae8","order":1,"disp":false,"width":"6","collapse":false},{"id":"1d865536.eb801b","type":"ui_group","z":"","name":"SG3","tab":"d0e5d7d6.5b1ae8","order":2,"disp":false,"width":"6","collapse":false},{"id":"b5d39406.6debf8","type":"ui_group","z":"","name":"DG2","tab":"d0e5d7d6.5b1ae8","order":7,"disp":false,"width":"9","collapse":false},{"id":"e9ed4336.19b7f","type":"ui_group","z":"","name":"MSB","tab":"d0e5d7d6.5b1ae8","order":8,"disp":false,"width":"12","collapse":false},{"id":"ea4f996c.f87158","type":"ui_group","z":"","name":"DG1","tab":"d0e5d7d6.5b1ae8","order":9,"disp":false,"width":"9","collapse":false},{"id":"c9a2551b.5b9168","type":"ui_group","z":"","name":"lines","tab":"d0e5d7d6.5b1ae8","order":6,"disp":false,"width":"30","collapse":false},{"id":"65f6f27e.a1f1ec","type":"ui_group","z":"","name":"Bow thruster","tab":"d0e5d7d6.5b1ae8","order":3,"disp":false,"width":"6","collapse":false},{"id":"fa92953a.7a1b58","type":"ui_group","z":"","name":"SG1","tab":"d0e5d7d6.5b1ae8","order":5,"disp":false,"width":"6","collapse":false},{"id":"b238e66b.53c658","type":"ui_group","z":"","name":"SG2","tab":"d0e5d7d6.5b1ae8","order":4,"disp":false,"width":"6","collapse":false},{"id":"d0e5d7d6.5b1ae8","type":"ui_tab","z":"","name":"nauticAi Elec","icon":"dashboard","order":3,"disabled":false,"hidden":false}]
5 Likes

+- level gauge
A bit of continuation on this project. We also had a need to have a +- balance gauge and failing to understand how to modify the absolutely excellent @hotNipi node-red-contrib-ui-level , we tinkered a bit with SVGs.

Steps taken
Note! This whole schebang can most likely be done a lot simpler, but since the knowledge of js is a bit limited, we go with what works :slight_smile:

Create two new signals to control the gauge:
First we do a relative calculation which compares left side and right side power and calculates a difference and also sets the signals to 0 if the value tilts the other way. The signals also need a bit of adjustment so that the scale is ok for the SVG.

// Power balance left and right
var powerPsRel = (powerSb - powerPs)/1000*200;
var powerSbRel = (powerPs - powerSb)/1000*200;

msg.payload.powerPsRel = powerPsRel;
msg.payload.powerSbRel = powerSbRel;
if (powerPsRel > 0){
    msg.payload.powerPsRel = 0;
}
if (powerPsRel < 0){
    msg.payload.powerPsRel = msg.payload.powerPsRel;
}

if (powerSbRel < 0){
    msg.payload.powerSbRel = msg.payload.powerSbRel * -1;
}
if (powerSbRel > 0){
    msg.payload.powerSbRel = 0;
}

The "left hand" signal only shows negative values and resets to 0 if the value is positive. This way we effectively get something which can tilt our level gauge bar left or right.

Add in an HTML template node with an SVG, which is layered like this:

  • Background striped rectangle using svg <pattern> and <rect>
    -- the left and right hand signals as svg <path> (we tried using <rect> also here, but since a <path> is much easier to control with a msg.payload, we went with that)
    --- and lastly a foreground "reversed" striped svg <rect> to mask the red and green paths
    This gives the striped look of the signals.
<style>
</style>
<div style="text-align:center;font-size:11px;">
    Power balance PS / SB
    <svg height="20" width="300">
	  <defs>
		<pattern id="bgstripes" patternUnits="userSpaceOnUse" width="3" height="3" patternTransform="rotate(0)">
			<line x1="0" y="0" x2="0" y2="3" stroke="#4A4A4A" stroke-width="4" />
		</pattern>
		<pattern id="fgstripes" patternUnits="userSpaceOnUse" width="4.5" height="4.5" patternTransform="rotate(0)">
			<line x1="0" y="0" x2="0" y2="4.5" stroke="#1a1a1a" stroke-width="1" />
		</pattern>
	  </defs>
      <!-- background -->
      <rect height="15" width="300" fill="url(#bgstripes)" :opacity="1" />
      <!-- power to PS -->
      <path d="M150 7.5 h {{msg.payload.powerPsRel}}" style="fill:transparent;stroke:#E60000;stroke-width:15" />
      <!-- power to SB -->
      <path d="M150 7.5 h {{msg.payload.powerSbRel}}" style="fill:transparent;stroke:#02a93a;stroke-width:15" />
      <!-- foreground -->
      <rect height="15" width="300" fill="url(#fgstripes)" :opacity="1" />
    </svg>
</div>
</body>
</html>
2 Likes

There is of course the artless gauge with differential mode fully supported.

image

3 Likes

Indeed there is! Hadn't noticed that yet. Many thanks for the tip :+1:

Next up for us would be two more things:

  1. a variation of the UI-level vertical gauge, so that you can flip the gauge/values right/left. This would improve the look and feel - make it more symmetrical - if you use these for left hand and right hand signals.
  2. and another flip-variation to have a vertical gauge that extends down (so that 0 is at the top and the value goes down from that

Due the really bad design and too many features I have created for ui-level, it takes quite of work and testing time to achieve what you asked. My schedule is too tight to promise anything...
But PR always welcome :slight_smile:

Yes, we sort of noticed that already when looking at the code :slight_smile: Not entirely straightforward to create flipped or inverted layouts. But the ui-level is very nice for what it is designed to do.

Keep you posted on what we can figure out.