[Announce] node-red-contrib-ui-svg (final beta) UPDATE V1.2.1

:vulcan_salute:

Hi folks,

Good news for the SVG lovers in the Node-RED community, since we proudly announce our second beta version (1.1.0) :champagne: :partying_face: :champagne: :partying_face:

But to be honest, due to private issues I had very few spare time for this beta release. So most credits go to my partner in crime @Steve-Mcl !!!

A summary of the major changes (beside a series of smaller bugfixes and enhancements):

1 - DrawSvg as preferred input

Joseph Liard (the author of DrawSvg) is currently implementing our feature requests, to integrate DrawSvg even better into Node-RED. Looks very promising, so we have changed the initial mindset of this SVG-node: instead of entering the SVG source manually (and optionally open DrawSvg afterwards), now we advise to draw in DrawSvg (and optionally change the SVG source manually afterwards).

To highlight that DrawSvg is the preferred way to go: we have removed the small DrawSvg button (below the SVG source editor), and instead added a new "Editor" tabsheet containing a large button:

P.S. As you can see in the above demo, a link to the 'Settings' tabsheet is displayed when no editor URL has been specified (as an extra help for the issue that @dynamicdave had registered above).

P.S. Also thanks to @dceejay for assisting us with the CSS issues!!

2 - FontAwesome support in DrawSvg

Joseph Liard has already been so kind to add FontAwesome icon support in his online drawing editor. This allows you e.g. to create very easily a floorplan of your house:

svg_beta2_demo

The above demo shows also how to customize the look-and-feel of the icon (e.g. change the color), but also how to apply an identifier ("cam_kitchen"). That identifier can be used later on to interact dual-way with your icon (i.e. via both input and ouput messages), similar to the interaction with other SVG elements.

3 - Unresponsive flow editor fix

Users like @mtoko had reported that the flow editor became unresponsive (i.e. you couldn't click on any other node), after leaving our config screen. This problem should be solved now ...

--- WHAT IS NEXT ---
The major required functionality is now available in this current beta version, so we will try to publish a final version on NPM soon. Perhaps we might fix one or more smaller bugs next week, but we don't expect huge changes anymore. So it would be nice if this beta could be tested as much as possible!

Have fun with drawing in Node-RED!
Bart & Steve

6 Likes

I can use exactly this for an upcoming project. Where can I find the demo flow? I looked on the NR forum and Git. I didn't see this one.

edit: It is the data driven HMI demo

Hi Joe (@jmorris644),

You can find the nice demo from @Steve-Mcl in the "import" menu.
For Node-RED version 1.0.x it can be accessed like this:

svg_example_flow

1 Like

Thanks Bart! I didn't even know the demo area of import existed.

The layout that @Steve-Mcl created is exactly what I will be needing. But the flow was not quite what I had imagined by being data driven.

I need to monitor about 100 remote IOT devices with both architectural health information but internal user information as well. Also the number of devices may change upon implementation so I do not wish to hard-code the boxes in the node.

At a high level, if I needed to create the boxes with the data and the ability to drill down just like @Steve-Mcl did, but totally from outside of the SVG control, what would be the best process for doing that?

I hope there is a little clarity to my question.

Joe

For a demo, demo data is faked. What did you expect? If you want real data to drive it then replace the demo data :+1:

You want something to draw boxes in a 4x2 format that drills down but you don't want to hard code them? How do you imagine such a program could guess what you want? Please elaborate.

I am struggling to understand what you mean? From outside? Our control is for simplifying the creation and manipulation of SVG graphics - not for controlling things outside of itself?

1 Like

Sorry, total confusion.

So lets say that I want the boxes lie you have them with certain names and certain data and allow drilldown to display more data within a box. Each box could represent a device in the real world that is being monitored. Based on the state of the real-world device the box would change color.

Now, lets say that when we implement the flow solution that contains the SVG node that we do not know how many real-world devices exist or the names of those devices.

Is there a way to dynamically build the SVG source without any of it being hardcoded in the node itself?

I see that the node input allows me to set attributes of elements, but can I dynamically create the elements themselves?

I hope this is more understandable.

Thanks

Joe

In Win10/Chrome the behaviours are not working for me (title bar not going to full screen, and station 10 click just flashes up page 2 then drops back to page 1).

@Bobo yeah, I had some difficulties with this & I am considering adding something into this contrib node that better handles it.

I dont fully remember the problems I had (something like full screen would only work first time after refresh or stop working after switching dashboard tabs).

For now, it is flaky - but if you manage to get a reliable demo working - please let us know.

Cheers.

Regardless of the demo, it is a great piece of work. :+1:

Ah ha now we are getting to it :smiley:

in a sense, yes, there is the ability to "update_text" (see built in info) which under the hood updates the content of a tag. If you referenced an element e.g. future_pages <g id="future_pages"> </g> you could execute an update_text function and add as much as you want.

e.g.
2019-10-17_11-41-56

Flow...

[{"id":"97bf246.d5ca6d8","type":"inject","z":"3391e156.d9d19e","name":"","topic":"","payload":"","payloadType":"date","repeat":"5","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":1080,"wires":[["e2e37baf.9531b8"]]},{"id":"e2e37baf.9531b8","type":"function","z":"3391e156.d9d19e","name":"random mode generator","func":"let r1 = Math.floor(Math.random() * 5000) + 1000;  \n\nfunction randomItem(arr){\n    var len = arr.length;\n    var rand = Math.floor(Math.random() * len);\n    return arr[rand]\n}\n\nlet station = randomItem([\"STN10\",\"STN20\",\"STN30\",\"STN40\",\"STN50\",\"STN60\",\"STN70\",\"STN80\"]);\nlet mode = randomItem([\"auto\",\"manual\",\"fault\",\"auto\",\"other\",\"auto\",\"fault\",\"auto\"]);\nlet fault = randomItem([\"sensor fault\",\"coolant empty\",\"CP tripped\",\"over speed\",\"E-Stop\",\"Safety Mat\"]);\nlet other = randomItem([\"full\",\"waiting\",\"over cycle\"]);\n\nlet statusText = mode == \"other\" ? other : (mode == \"fault\" ? fault : mode);\n\nlet state = flow.get(\"state\") || {}\nif(!state[station]){\n    state[station] = {mode:\"manual\", status:\"manual\", count:0}    \n}\nstate[station].mode = mode;\nstate[station].status = statusText;\nflow.set(\"state\",state)\n\n//$(\"#STN50 > .background\").attr(\"fill\",\"url('#fault')\")\n//$(\"#STN50 > .status\").text(\"hi\")\n\nlet newmsg = {payload:[\n        {\n            \"command\": \"update_attribute\",\n            \"selector\": \"#\" + station + \" > .background\",\n            \"attributeName\": \"fill\",\n            \"attributeValue\": \"url('#\" + mode + \"')\"\n        },\n        {\n            \"command\": \"update_text\",\n            selector: \"#\" + station + \" > .status\",\n            textContent: statusText\n        },\n        {\n            \"command\": \"update_text\",\n            selector: \"#\" + station + \" > .count\",\n            textContent: state[station].count.toString()\n        }\n    ]\n}\n\n\nsetTimeout(function () {\n    node.send(newmsg)\n}, r1 - 100);\n\n    \n//return msg;","outputs":1,"noerr":0,"x":350,"y":1080,"wires":[["474cfd5a.60b584"]]},{"id":"474cfd5a.60b584","type":"ui_svg_graphics","z":"3391e156.d9d19e","group":"17b0dd6e.e23ec3","order":0,"width":"12","height":"9","svgString":"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" id=\"svghmi\" viewBox=\"0 -0.006988120265305042 320 180.01397705078125\" width=\"80%\" height=\"80%\">\n  <defs id=\"svgEditorDefs\">\n    <symbol xmlns=\"http://www.w3.org/2000/svg\" id=\"f015\" preserveAspectRatio=\"xMidYMid meet\" viewBox=\"0 0 576 512\">\n      <path d=\"M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z\"/>\n    </symbol>\n    <polygon id=\"svgEditorIconDefs\" style=\"fill:rosybrown;\"/>\n  </defs>\n  <rect id=\"svgEditorBackground\" x=\"0\" y=\"0\" width=\"320\" height=\"180\" style=\"fill:none; stroke: none;\"/>\n  <defs>\n    <linearGradient spreadMethod=\"repeat\" y1=\"0\" x1=\"0.5\" y2=\"1\" x2=\"0.5\" id=\"auto\">\n      <stop offset=\"0\" stop-color=\"#00aa00\"/>\n      <stop offset=\"0.5\" stop-color=\"#00ff00\"/>\n      <stop offset=\"1\" stop-color=\"#00aa00\"/>\n    </linearGradient>\n    <linearGradient spreadMethod=\"repeat\" y1=\"0\" x1=\"0.5\" y2=\"1\" x2=\"0.5\" id=\"fault\">\n      <stop offset=\"0\" stop-color=\"#aa0000\"/>\n      <stop offset=\"0.5\" stop-color=\"#ff0000\"/>\n      <stop offset=\"1\" stop-color=\"#aa0000\"/>\n    </linearGradient>\n    <linearGradient spreadMethod=\"repeat\" y1=\"0\" x1=\"0.5\" y2=\"1\" x2=\"0.5\" id=\"other\">\n      <stop offset=\"0\" stop-color=\"#aaaa00\"/>\n      <stop offset=\"0.5\" stop-color=\"#ffff00\"/>\n      <stop offset=\"1\" stop-color=\"#aaaa00\"/>\n    </linearGradient>\n    <linearGradient spreadMethod=\"repeat\" y1=\"0\" x1=\"0.5\" y2=\"1\" x2=\"0.5\" id=\"manual\">\n      <stop offset=\"0\" stop-color=\"#0000aa\"/>\n      <stop offset=\"0.5\" stop-color=\"#0000ff\"/>\n      <stop offset=\"1\" stop-color=\"#0000aa\"/>\n    </linearGradient>\n  </defs>\n  <g display=\"inline\">\n    <title>background</title>\n    <rect x=\"0.000017590256192079323\" fill=\"#000000\" stroke-dasharray=\"null\" stroke-linejoin=\"null\" stroke-linecap=\"null\" y=\"0.7031467887925658\" width=\"320\" height=\"180\" id=\"svg_5\" stroke=\"#000000\"/>\n  <g id=\"home_button\" class=\"home_button\">\n      <rect x=\"243.175\" y=\"168.165\" style=\"fill:khaki;stroke:black;stroke-width:1px;\" id=\"e2_rectangle\" width=\"71.7319\" height=\"11.9569\" transform=\"matrix(1.04259 0 0 1.04259 -11.5347 -7.65778)\"/>\n      <use xlink:href=\"#f015\" x=\"243.513\" y=\"169.051\" width=\"12.2989\" height=\"9.93866\" id=\"e2_icon\" style=\"fill:rosybrown;\"/><text style=\"fill:black;font-family:Arial;font-size:10px;\" x=\"250.00698852539062\" y=\"171.73304748535156\" id=\"e4_texte\"/><text style=\"fill:black;font-family:Arial Black;font-size:12px;\" x=\"275.0726318359375\" y=\"175.89859008789062\" id=\"e5_texte\" transform=\"matrix(0.623201 0 0 0.623201 94.2814 67.0708)\" textLength=\"52.16404582508221\">Home</text>\n  </g>\n  </g>\n  <g display=\"inline\" id=\"page1\" class=\"page\">\n    <title>Main Overview</title>\n    <g id=\"STN10\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect class=\"background\" fill=\"url(#manual)\" x=\"3.20313\" y=\"28.83594\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\" id=\"box_1\"/>\n      <text class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"-39.92712\" y=\"62.62216\" id=\"svg_2\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Station 10</text>\n      <text class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"-39.37879\" y=\"175.82813\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\" id=\"Status_STN10\">Manual</text>\n      <text class=\"count\" xml:space=\"preserve\" text-anchor=\"middle\" font-family=\"Sans-serif\" font-size=\"24\" id=\"count_1\" y=\"72.78907\" x=\"41.82814\" stroke-linecap=\"null\" stroke-linejoin=\"null\" stroke-dasharray=\"null\" stroke-width=\"0\" stroke=\"#000000\" fill=\"#000000\">0</text>\n    </g>\n    <g id=\"STN20\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect id=\"svg_24\" class=\"background\" fill=\"url(#manual)\" x=\"82.65625\" y=\"28.83594\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n      <text id=\"svg_25\" class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"150.36079\" y=\"62.62215\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Station 20</text>\n      <text id=\"svg_26\" class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"150.90912\" y=\"175.82812\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Manual</text>\n      <text id=\"svg_27\" class=\"count\" xml:space=\"preserve\" text-anchor=\"middle\" font-family=\"Sans-serif\" font-size=\"24\" y=\"72.78907\" x=\"121.28127\" stroke-linecap=\"null\" stroke-linejoin=\"null\" stroke-dasharray=\"null\" stroke-width=\"0\" stroke=\"#000000\" fill=\"#000000\">0</text>\n    </g>\n    <g id=\"STN30\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect id=\"svg_29\" class=\"background\" fill=\"url(#manual)\" x=\"161.54688\" y=\"28.83594\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n      <text id=\"svg_30\" class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"339.30151\" y=\"62.62216\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Station 30</text>\n      <text id=\"svg_31\" class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"339.84985\" y=\"175.82813\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Manual</text>\n      <text id=\"svg_32\" class=\"count\" xml:space=\"preserve\" text-anchor=\"middle\" font-family=\"Sans-serif\" font-size=\"24\" y=\"72.78907\" x=\"200.17189\" stroke-linecap=\"null\" stroke-linejoin=\"null\" stroke-dasharray=\"null\" stroke-width=\"0\" stroke=\"#000000\" fill=\"#000000\">0</text>\n    </g>\n    <g id=\"STN40\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect id=\"svg_3\" class=\"background\" fill=\"url(#manual)\" x=\"240.43751\" y=\"28.83594\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n      <text id=\"svg_4\" class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"528.24226\" y=\"62.62216\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Station 40</text>\n      <text id=\"svg_6\" class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"528.7906\" y=\"175.82813\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Manual</text>\n      <text id=\"svg_7\" class=\"count\" xml:space=\"preserve\" text-anchor=\"middle\" font-family=\"Sans-serif\" font-size=\"24\" y=\"72.78907\" x=\"279.06252\" stroke-linecap=\"null\" stroke-linejoin=\"null\" stroke-dasharray=\"null\" stroke-width=\"0\" stroke=\"#000000\" fill=\"#000000\">0</text>\n    </g>\n    <g id=\"STN50\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect id=\"svg_57\" class=\"background\" fill=\"url(#manual)\" x=\"3.20313\" y=\"102.24219\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n      <text id=\"svg_58\" class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"-39.92712\" y=\"222.78125\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Station 50</text>\n      <text id=\"svg_59\" class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"-39.37879\" y=\"335.98722\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Manual</text>\n      <text id=\"svg_60\" class=\"count\" xml:space=\"preserve\" text-anchor=\"middle\" font-family=\"Sans-serif\" font-size=\"24\" y=\"146.19532\" x=\"41.82814\" stroke-linecap=\"null\" stroke-linejoin=\"null\" stroke-dasharray=\"null\" stroke-width=\"0\" stroke=\"#000000\" fill=\"#000000\">0</text>\n    </g>\n    <g id=\"STN60\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect id=\"svg_62\" class=\"background\" fill=\"url(#manual)\" x=\"82.65625\" y=\"102.24219\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n      <text id=\"svg_63\" class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"150.36079\" y=\"222.78124\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Station 60</text>\n      <text id=\"svg_64\" class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"150.90912\" y=\"335.98721\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Manual</text>\n      <text id=\"svg_65\" class=\"count\" xml:space=\"preserve\" text-anchor=\"middle\" font-family=\"Sans-serif\" font-size=\"24\" y=\"146.19532\" x=\"121.28127\" stroke-linecap=\"null\" stroke-linejoin=\"null\" stroke-dasharray=\"null\" stroke-width=\"0\" stroke=\"#000000\" fill=\"#000000\">0</text>\n    </g>\n    <g id=\"STN70\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect id=\"svg_67\" class=\"background\" fill=\"url(#manual)\" x=\"161.54688\" y=\"102.24219\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n      <text id=\"svg_68\" class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"339.30151\" y=\"222.78124\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Station 70</text>\n      <text id=\"svg_69\" class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"339.84985\" y=\"335.98721\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Manual</text>\n      <text id=\"svg_70\" class=\"count\" xml:space=\"preserve\" text-anchor=\"middle\" font-family=\"Sans-serif\" font-size=\"24\" y=\"146.19532\" x=\"200.17189\" stroke-linecap=\"null\" stroke-linejoin=\"null\" stroke-dasharray=\"null\" stroke-width=\"0\" stroke=\"#000000\" fill=\"#000000\">0</text>\n    </g>\n    <g id=\"STN80\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect id=\"svg_72\" class=\"background\" fill=\"url(#manual)\" x=\"240.43751\" y=\"102.24219\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n      <text id=\"svg_73\" class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"528.24226\" y=\"222.78124\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Station 80</text>\n      <text id=\"svg_74\" class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"528.7906\" y=\"335.98721\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Manual</text>\n      <text id=\"svg_75\" class=\"count\" xml:space=\"preserve\" text-anchor=\"middle\" font-family=\"Sans-serif\" font-size=\"24\" y=\"146.19532\" x=\"279.06252\" stroke-linecap=\"null\" stroke-linejoin=\"null\" stroke-dasharray=\"null\" stroke-width=\"0\" stroke=\"#000000\" fill=\"#000000\">0</text>\n    </g>\n    <g id=\"MAIN_HEADER\" class=\"header_bar\">\n      <rect stroke=\"#000000\" class=\"background\" fill=\"url(#manual)\" x=\"0\" y=\"0\" width=\"320\" height=\"22\" id=\"svg_14\"/>\n      <text class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"237.32815\" y=\"5.23012\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\" id=\"svg_15\">Main Overview</text>\n    </g>\n  </g>\n  <g id=\"page2\" display=\"none\" class=\"page\">\n    <title>Station 10 Overview</title>\n    <g id=\"STN10_HEADER\" class=\"header_bar\">\n      <rect stroke=\"#000000\" class=\"background\" fill=\"url(#manual)\" x=\"0\" y=\"0\" width=\"320\" height=\"22\"/>\n      <text class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"236.24292\" y=\"6.73012\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Station 10</text>\n    </g>\n    <g id=\"STN10_Air\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect class=\"background\" fill=\"url(#auto)\" x=\"119.51562\" y=\"61.65625\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n      <text class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"236.15508\" y=\"186.68609\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Air Supply</text>\n    </g>\n    <g id=\"STN10_Water\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect class=\"background\" fill=\"url(#auto)\" x=\"208.51562\" y=\"61.65625\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n      <text class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"449.30747\" y=\"186.68609\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Water Supply</text>\n    </g>\n    <g id=\"STN10_ControlPower\" transform=\"matrix(1 0 0 1 0 -5.36688)\">\n      <rect class=\"background\" fill=\"url(#fault)\" x=\"32.51562\" y=\"61.65625\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n      <text class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"27.79263\" y=\"186.68609\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Control Power</text>\n    </g>\n  </g>\n  <g display=\"none\" id=\"page3\" class=\"page\">\n  </g>\n\n</svg>","clickableShapes":[{"targetId":"#STN10","action":"click","payload":"page2","payloadType":"str","topic":"change_page"},{"targetId":".home_button","action":"click","payload":"page1","payloadType":"str","topic":"change_page"},{"targetId":"#STN20","action":"click","payload":"page3","payloadType":"str","topic":"change_page"}],"smilAnimations":[],"bindings":[],"showCoordinates":false,"autoFormatAfterEdit":false,"outputField":"payload","editorUrl":"http://drawsvg.org/drawsvg.html","directory":"","name":"","x":620,"y":1080,"wires":[["33a3bd1d.079462","725b90d9.b91ce"]]},{"id":"9b284c7d.74084","type":"function","z":"3391e156.d9d19e","name":"random count generator","func":"function randomInc(station, delayms){\n\n    setTimeout(function () {\n        var state = flow.get(\"state\") || {};\n        if(!state[station]){\n            state[station] = {count: 0}    \n        }\n        if(state[station].mode == \"auto\"){\n            state[station].count += 1;\n            flow.set(\"state\",state)\n            var newmsg = {\n                payload:{\n                    \"command\": \"update_text\",\n                    selector: \"#\" + station + \" > .count\",\n                    textContent: state[station].count.toString()\n                }\n            }\n            node.send(newmsg);\n        }\n        \n    }, delayms-5);\n}\n\nvar stns = [\"STN10\",\"STN20\",\"STN30\",\"STN40\",\"STN50\",\"STN60\",\"STN70\",\"STN80\"]\nvar i = 0;\nfor(i=0; i<stns.length; i++){\n    randomInc(stns[i], Math.floor(Math.random() * 1000) )\n}\n\n    \n//return msg;","outputs":1,"noerr":0,"x":350,"y":1120,"wires":[["474cfd5a.60b584"]]},{"id":"6f583b6.1bc37c4","type":"inject","z":"3391e156.d9d19e","name":"","topic":"","payload":"","payloadType":"date","repeat":"5","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":1120,"wires":[["9b284c7d.74084"]]},{"id":"33a3bd1d.079462","type":"debug","z":"3391e156.d9d19e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":790,"y":1080,"wires":[]},{"id":"4da7998b.5191a8","type":"function","z":"3391e156.d9d19e","name":"init  mode generator","func":"let stations = [\"STN10\",\"STN20\",\"STN30\",\"STN40\",\"STN50\",\"STN60\",\"STN70\",\"STN80\"];\nlet state = flow.get(\"state\") || {}\n\nfunction buildUpdates(station, state){\n    if(isNaN(state.count)){\n        state.count = 0;\n    }\n    msgData.push({\n                \"command\": \"update_attribute\",\n                \"selector\": \"#\" + station + \" > .background\",\n                \"attributeName\": \"fill\",\n                \"attributeValue\": \"url('#\" + (state.mode || \"manual\") + \"')\"\n            })\n    msgData.push({\n                \"command\": \"update_text\",\n                selector: \"#\" + station + \" > .status\",\n                textContent: state.status || \"manual\"\n            })\n    msgData.push({\n                \"command\": \"update_text\",\n                selector: \"#\" + station + \" > .count\",\n                textContent: state.count || \"0\"\n            })\n}\nlet msgData = [];\n\n[\"STN10\",\"STN20\",\"STN30\",\"STN40\",\"STN50\",\"STN60\",\"STN70\",\"STN80\"].forEach(function(station){\n    if(!state[station]){\n        state[station] = {mode:\"manual\", status:\"manual\", count:0}    \n    }\n    buildUpdates(station, state[station]);\n})\n\nflow.set(\"state\",state)\nmsg.payload = msgData;\nreturn msg;","outputs":1,"noerr":0,"x":330,"y":1040,"wires":[["474cfd5a.60b584","df202829.299b18"]]},{"id":"860389f0.892158","type":"inject","z":"3391e156.d9d19e","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":true,"onceDelay":"0.5","x":150,"y":1040,"wires":[["4da7998b.5191a8"]]},{"id":"df202829.299b18","type":"debug","z":"3391e156.d9d19e","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":570,"y":1040,"wires":[]},{"id":"d1b28cc1.1c166","type":"comment","z":"3391e156.d9d19e","name":"HMI Demo with fake data. Click Title bar to full screen, click Station 10 for page 2","info":"","x":340,"y":1000,"wires":[]},{"id":"9b8bfcfb.37f47","type":"inject","z":"3391e156.d9d19e","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":220,"y":1200,"wires":[["7a445b20.f4fce4"]]},{"id":"7a445b20.f4fce4","type":"function","z":"3391e156.d9d19e","name":"create page3","func":"\nvar inner = `\n<title>Station 20 Overview</title>\n  <g id=\"STN20_HEADER\" class=\"header_bar\">\n   <rect stroke=\"#000000\" class=\"background\" fill=\"url(#manual)\" x=\"0\" y=\"0\" width=\"320\" height=\"22\"/>\n   <text class=\"name\" fill=\"#ffffff\" stroke-width=\"0\" x=\"236.24292\" y=\"6.73012\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Station 20</text>\n  </g>\n  <g id=\"STN20_Air\">\n   <rect class=\"background\" fill=\"url(#fault)\" x=\"119.51562\" y=\"61.65625\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n   <text class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"236.15508\" y=\"186.68609\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Air Supply</text>\n  </g>\n  <g id=\"STN20_Water\">\n   <rect class=\"background\" fill=\"url(#auto)\" x=\"208.51562\" y=\"61.65625\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n   <text class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"449.30747\" y=\"186.68609\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Water Supply</text>\n  </g>\n  <g id=\"STN20_ControlPower\">\n   <rect class=\"background\" fill=\"url(#auto)\" x=\"32.51562\" y=\"61.65625\" width=\"77.25\" height=\"70.625\" rx=\"10\" ry=\"10\" stroke=\"#000000\"/>\n   <text class=\"status\" fill=\"#ffffff\" stroke-width=\"0\" x=\"27.79263\" y=\"186.68609\" font-size=\"24\" font-family=\"Sans-serif\" text-anchor=\"middle\" xml:space=\"preserve\" font-weight=\"bold\" transform=\"matrix(0.41754164704216323,0,0,0.4583333432674408,58.471179716401295,13.575520584359765) \" stroke=\"#ffffff\">Control Power</text>\n  </g>\n\n`\n\nmsg.payload = [\n {\n    \"command\": \"update_text\",\n    \"selector\": \"#page3\",\n    \"textContent\": inner\n }\n]\n\nreturn msg;","outputs":1,"noerr":0,"x":370,"y":1200,"wires":[["474cfd5a.60b584"]]},{"id":"725b90d9.b91ce","type":"switch","z":"3391e156.d9d19e","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"change_page","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":550,"y":1260,"wires":[["5d6a998.7c63568"]]},{"id":"5d6a998.7c63568","type":"function","z":"3391e156.d9d19e","name":"change page by payload","func":"\nmsg.payload = [\n {  //1st, hide all pages\n    \"command\": \"update_attribute\",\n    \"selector\": \".page\",\n    \"attributeName\": \"display\",\n    \"attributeValue\": \"none\"\n },\n {   //2nd, show desired page\n    \"command\": \"update_attribute\",\n    \"selector\": \"#\" + msg.payload,\n    \"attributeName\": \"display\",\n    \"attributeValue\": \"inline\"\n }\n]\n\nreturn msg;","outputs":1,"noerr":0,"x":750,"y":1260,"wires":[["474cfd5a.60b584"]]},{"id":"74c0ab99.b6b7f4","type":"ui_button","z":"3391e156.d9d19e","name":"","group":"17b0dd6e.e23ec3","order":4,"width":"2","height":"1","passthru":false,"label":"Add page","tooltip":"","color":"","bgcolor":"","icon":"","payload":"true","payloadType":"bool","topic":"","x":210,"y":1260,"wires":[["7a445b20.f4fce4"]]},{"id":"17b0dd6e.e23ec3","type":"ui_group","z":"","name":"HMI","tab":"3c2499dc.db9136","disp":true,"width":"12","collapse":false},{"id":"3c2499dc.db9136","type":"ui_tab","z":"","name":"Home","icon":"dashboard"}]

Caveats

  • As click events and some other dynamic parts are created when the node is initialised, you cannot use the "Clickable" items inside the node for dynamically generated items (events are not attached) thats why in the demo i put an ugly home button in the SVG Source
2 Likes

Hmmm, I see the "add page" in the flow, and I see it in the dashboard outline that that config looks good too but it does not show up on the dashboard.

I also tried adding additional dashboard nodes. Even adding them on different tabs. None of them are showing up.

I also updated the dashboard to the newest version 2.17.1 and I am running NR v1.0.2. I will have to do some more playing as I might have stumbled upon a bug?

Do you see errors in the browse console log?

Just in the middle of my testing. It works great on a rPi but failing on Windows10. Using the same Chrome browser for both.

I will take a look at the logs.

Joe

Yes. Here it is:

WebSocket connection to 'ws://localhost:1880/ui/socket.io/?EIO=3&transport=websocket&sid=t5c3VErzo8WHVYSpAAAF' failed: Error during WebSocket handshake: Unexpected response code: 400
r.doOpen @ index.js:83

Joe,
Does anything else on your dashboard work? Because the websocket is internal dashboard stuff to communicate all data to the browser. We have no control about that...
Is this the Node-RED log (server side) or the console log of the browser (where the dashboard is displayed). Because both are relevant!

Nothing else on the dashboard other than the SVG itself is displaying. What i posted was form the Chrome console.

I typically use NR on rPis. I am not as familiar with running it on windows. I have searched but am unable to find how to view the NR log in Windows. Where is it?

Also, on running the exact same flow on rPi I can see the the "add page" button but when pressed it does nothing. No errors on the browser console or the NR log.

Joe

UPDATE - V1.2.0 V1.2.1 - final beta before publishing to NPM/Pallete (we hope)

  • update_attribute (and set_attribute) commands now support removing of attributes by sending empty attributeValue
  • new command type update_style / set_style ...
    • Named style attribute change: { "command": "update_style", "selector": ".camera", "attributeName": "fill", "attributeValue": "purple" }
    • Style object attribute(s) change: { "command": "update_style", "selector": ".camera", "style": { "fill": "blue", "transform": "rotate(5deg)" } }
    • of course this update_style / set_style also supports removing any styles that are forcing the color e.g. {"command":"update_style", "selector":".camera", "style":{"fill":"","transform":""}}

demo.
Watch the oddness (svg+attr+css priorities) where color/fill/attribute/style are initially set in SVG source. However this new functionality (and with this flow as a demo) we can demonstrate workarounds and capabilities...

Flow

[{"id":"ce0b967b.2280e8","type":"debug","z":"116cddab.f8a6f2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":430,"y":200,"wires":[]},{"id":"854d7903.b244a8","type":"inject","z":"116cddab.f8a6f2","name":"set_attribute fill red","topic":"","payload":"{\"command\":\"set_attribute\",\"selector\":\".camera\",\"attributeName\":\"fill\",\"attributeValue\":\"red\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":160,"wires":[["cadd2431.a59568"]]},{"id":"dd18275c.ed0918","type":"inject","z":"116cddab.f8a6f2","name":"update_style fill purple","topic":"","payload":"{\"command\":\"update_style\",\"selector\":\".camera\",\"attributeName\":\"fill\",\"attributeValue\":\"purple\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":220,"wires":[["cadd2431.a59568"]]},{"id":"55189bc0.d79c34","type":"inject","z":"116cddab.f8a6f2","name":"update_style fill blue AND rotate 5","topic":"","payload":"{\"command\":\"update_style\",\"selector\":\".camera\",\"style\":{\"fill\":\"blue\",\"transform\":\"rotate(5deg)\"}}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":180,"y":280,"wires":[["cadd2431.a59568"]]},{"id":"ea3e1c6b.f248c","type":"inject","z":"116cddab.f8a6f2","name":"update_style multiple - remove color & rotate","topic":"","payload":"{\"command\":\"update_style\",\"selector\":\".camera\",\"style\":{\"fill\":\"\",\"transform\":\"\"}}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":210,"y":340,"wires":[["cadd2431.a59568"]]},{"id":"f7a9d42d.993a98","type":"inject","z":"116cddab.f8a6f2","name":"remove attributes & style - color, fill, rotate","topic":"","payload":"[{\"command\":\"set_attribute\",\"selector\":\".camera\",\"attributeName\":\"fill\",\"attributeValue\":\"\"},{\"command\":\"set_attribute\",\"selector\":\".camera\",\"attributeName\":\"color\",\"attributeValue\":\"\"},{\"command\":\"set_style\",\"selector\":\".camera\",\"attributeName\":\"fill\",\"attributeValue\":\"\"},{\"command\":\"set_style\",\"selector\":\".camera\",\"attributeName\":\"rotate\",\"attributeValue\":\"\"},{\"command\":\"set_style\",\"selector\":\".camera\",\"attributeName\":\"color\",\"attributeValue\":\"\"}]","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":200,"y":400,"wires":[["cadd2431.a59568"]]},{"id":"f6d4c7c5.3281f8","type":"inject","z":"116cddab.f8a6f2","name":"set_style color orange","topic":"","payload":"{\"command\":\"set_style\",\"selector\":\".camera\",\"attributeName\":\"color\",\"attributeValue\":\"orange\"}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":100,"wires":[["cadd2431.a59568"]]},{"id":"cadd2431.a59568","type":"ui_svg_graphics","z":"116cddab.f8a6f2","group":"ebcbf9c5.11fbf8","order":1,"width":0,"height":0,"svgString":"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0\" y=\"0\" height=\"100\" viewBox=\"0 0 200 100\" width=\"200\" preserveAspectRatio=\"xMidYMid meet\">\n  <rect id=\"svgEditorBackground\" x=\"0\" y=\"0\" width=\"100\" height=\"100\" style=\"fill:none; stroke: none;\" />\n  <defs id=\"svgEditorDefs\">\n    <symbol xmlns=\"http://www.w3.org/2000/svg\" id=\"f03d\" preserveAspectRatio=\"xMidYMid meet\" viewBox=\"0 0 576 512\">\n      <path d=\"M336.2 64H47.8C21.4 64 0 85.4 0 111.8v288.4C0 426.6 21.4 448 47.8 448h288.4c26.4 0 47.8-21.4 47.8-47.8V111.8c0-26.4-21.4-47.8-47.8-47.8zm189.4 37.7L416 177.3v157.4l109.6 75.5c21.2 14.6 50.4-.3 50.4-25.8V127.5c0-25.4-29.1-40.4-50.4-25.8z\" />\n    </symbol>\n    <polygon id=\"svgEditorIconDefs\" style=\"fill:rosybrown;\" />\n  </defs>\n  <use class=\"camera\" style=\"colour:silver;\" xlink:href=\"#f03d\" x=\"12.595355033874512\" y=\"5.487990379333496\" width=\"21.7916\" height=\"22.1596\" id=\"mijn_icoon\" transform=\"matrix(1.20715 0 0 1.20715 -5.5721 -2.67466)\" />\n  <use class=\"camera\" style=\"fill:silver;\" xlink:href=\"#f03d\" x=\"12.776228904724121\" y=\"26.766639709472656\" width=\"21.7916\" height=\"22.1596\" id=\"mijn_icoon\" transform=\"matrix(1.20715 0 0 1.20715 -5.5721 -2.67466)\" />\n  <use class=\"camera\" fill=\"silver\" xlink:href=\"#f03d\" x=\"12.916482925415039\" y=\"45.98122787475586\" width=\"21.7916\" height=\"22.1596\" id=\"cam2\" transform=\"matrix(1.20715 0 0 1.20715 -5.5721 -2.67466)\" />\n  <text style=\"fill:var(--nr-dashboard-widgetTextColor);font-family:Arial;font-size:20px;\" x=\"45.168331146240234\" y=\"44.669822692871094\" id=\"e1_texte\" transform=\"matrix(1.20715 0 0 1.20715 -5.5721 -2.67466)\">style fill</text>\n  <text style=\"fill:var(--nr-dashboard-widgetTextColor);font-family:Arial;font-size:20px;\" x=\"44.11644744873047\" y=\"62.55198287963867\" id=\"e2_texte\" transform=\"matrix(1.20715 0 0 1.20715 -5.5721 -2.67466)\">attribute fill</text>\n  <text style=\"fill:var(--nr-dashboard-widgetTextColor);font-family:Arial;font-size:20px;\" x=\"45.543025970458984\" y=\"23.158767700195312\" id=\"e3_texte\" transform=\"matrix(1.20715 0 0 1.20715 -5.5721 -2.67466)\">style color</text>\n</svg>","clickableShapes":[{"targetId":"#mijn_icoon","action":"click","payload":"my payload","payloadType":"str","topic":"my topic"}],"smilAnimations":[],"bindings":[],"showCoordinates":false,"autoFormatAfterEdit":false,"outputField":"mycustomfield","editorUrl":"http://drawsvg.org/drawsvg.html","directory":"","name":"","x":400,"y":160,"wires":[["ce0b967b.2280e8"]]},{"id":"ebcbf9c5.11fbf8","type":"ui_group","z":"","name":"Hikvision","tab":"39ae8d86.d9fa72","disp":true,"width":"6","collapse":false},{"id":"39ae8d86.d9fa72","type":"ui_tab","z":"","name":"Camera","icon":"dashboard","disabled":false,"hidden":false}]
edit...
V1.2.1 pushed to github because someone forgot to update doumentation
4 Likes

This is the end of our beta testing phase. We have now published the SVG node on NPM, so please add your feedback on our new discussion.

1 Like