🚀 [FlexDash] alpha release - a dashboard for Node-RED

I like to use the dark theme, but if the browser is refreshed, or opened in a different browser, then it reverts back to the light theme.
At some stage (at the bottom of the todo list), could the user's choice be persisted?
(I'm aware about adding a ?theme=dark query string to the url, but that's not ideal.)

1 Like

Definitely! Right now there are no cookies (nor any login) and remembering requires cookies, so there's some work involved... Also, I'm not sure where to persist that stuff: in memory seems bad but on disk prob requires at least tying into a persistent context store (which is something NR doesn't make that simple 'cause a node can't discover which context stores are available AFAIK).

1 Like

I believe it can, though it might not be straight-forwards. They are defined in settings.js and you have access to the settings in the node.

But the point is true anyway. This is why uibuilder uses its own filing structure.

Hmmm, wait, the change node has flow as one of the content options and that displays a list of context stores. I should be able to use that!

At client side, you can list available context stores to present to the user via RED.settings.context.stores.forEach(callback)

1 Like

Is the Dropdown select widget in a state that it could be released even though it needs further tidying up? That would allow me to make progress in building my whole UI using FD.

1 Like

Yikes! I wouldn't want to be impeding that! You need to update node-red-flexdash and node-red-corewidgets:

26 Aug 10:29:55 - [info] Node-RED FlexDash plugin version 0.4.94                                    
26 Aug 10:29:57 - [info] Node-RED FlexDash version 0.4.94                                           
26 Aug 10:29:57 - [info] Node-RED FD Core Widgets version 0.4.41                                    
26 Aug 10:29:59 - [info] FlexDash UI version 0.4.51                                                 
1 Like

I'm passing the data array into the time-plot node using msg.payload, and it works great, but...
looking at the description in the config, it suggests using msg.data (which doesn't work).
Is it a typo?
data

It's not a typo. I did notice some issues still with typedInput and null values. :cry: I need to do another round of fixing it seems.

2 Likes

Hi, I would like to replace the old dashboard with FlexDash but I am not able to customize the buttons: I would like to change their size (including icon and font sizes) with a result similar to the attached image. It can be done? Thank you

Screenshot 2022-08-28 01.07.45

1 Like

What have you tried?

I would create the three widgets, then place them into a Panel (config node), make the panel 2x2 in dimension, then make the 'sala' widget 2x2, the 'cucina' widget 2x3 (WxH), and the 'tv' widget 2x1.

Here's a starting point:

[{"id":"fab6b7c44654ddb3","type":"fd-stat","z":"0e6b577fa39b8084","fd_container":"a85c2b65747b8fa9","fd_cols":"2","fd_rows":"2","fd_array":false,"fd_array_max":10,"name":"sala","title":"sala","popup_info":"","unit":"°C","value":23,"color":"","low_color":"blue","high_color":"pink","low_threshold":null,"high_threshold":null,"low_regexp":"","high_regexp":"","chip":false,"iso_prefix":true,"zoom":1,"x":190,"y":400,"wires":[]},{"id":"1b994b3c1404a987","type":"fd-stat","z":"0e6b577fa39b8084","fd_container":"a85c2b65747b8fa9","fd_cols":"2","fd_rows":"3","fd_array":false,"fd_array_max":10,"name":"cucina","title":"cucina","popup_info":"","unit":"°C","value":24.8,"color":"","low_color":"blue","high_color":"pink","low_threshold":null,"high_threshold":null,"low_regexp":"","high_regexp":"","chip":false,"iso_prefix":true,"zoom":1,"x":190,"y":440,"wires":[]},{"id":"93a7dce8c1cada97","type":"fd-push-button","z":"0e6b577fa39b8084","fd_container":"a85c2b65747b8fa9","fd_cols":"2","fd_rows":1,"fd_array":false,"fd_array_max":10,"fd_output_topic":"","name":"tv","title":"TV","popup_info":"","enabled":true,"color":"","output_value":25,"icon":null,"x":190,"y":480,"wires":[[]]},{"id":"a85c2b65747b8fa9","type":"flexdash container","name":"demo panel","kind":"Panel","fd_children":",fab6b7c44654ddb3,1b994b3c1404a987,93a7dce8c1cada97","title":"","tab":"","min_cols":"1","max_cols":"20","parent":"f54be6d9a8b8053d","solid":false,"cols":"2","rows":"2"},{"id":"f54be6d9a8b8053d","type":"flexdash container","name":"demo grid","kind":"StdGrid","fd_children":",a85c2b65747b8fa9","title":"","tab":"060e75cb0ea9e3e1","min_cols":"1","max_cols":"20","parent":"","solid":false,"cols":"1","rows":"1"},{"id":"060e75cb0ea9e3e1","type":"flexdash tab","name":"demo tab","icon":"mdi-rocket","title":"","fd_children":",f54be6d9a8b8053d","fd":"e8f5aea52ab49500"}]

image

I don't have an icon widget to put in there, oops, one more for the to-do list!

I'm not managing to reproduce this issue. Would it be possible for you to post a sample flow that shows it?

Does that needs to be a dedicated widget? Isn't this simply html content (containing some icon identifier) inside the existing widget?

P.S. this is not an argument against a new widget (which is probably a more user friendly solution), but just a question...

2 Likes

Well, the button widget has an icon prop and that places an icon into the button in front of the text (if any). I could do something similar with the stat widget, and the gauge widget too? But then you also get into questions about having the icon to the left, to the right, above, below?

There is a label widget which allows you to place text, change its justification, alignment, size and color. So you can combine a label and another widget in a panel to get whatever arrangement you want, more or less. An icon widget would allow you to do something similar.

At some point you just have to create your own custom widget, which will be easy someday, but not yet...

3 Likes

OK, 2 identical flows attached, one uses msg.data to feed in the data array, as described in the config - Array with unix timestamp followed by a value per series. Change using msg[data]

And a second flow using msg.payload instead of msg.data.

[{"id":"bfbe90dadb982ce6","type":"function","z":"1543d308b342690a","name":"Use msg.data","func":"if (msg.hasOwnProperty(\"reset\")) {\n    context.set('Flexchart', [])\n    return\n}\n\nlet data = context.get('Flexchart') || []\nconst newdata = msg.payload\nconst timestamp = Date.now() / 1000\n\nnewdata.unshift(timestamp)\ndata.push(newdata)\nwhile (data.length > 50) data.shift()\ncontext.set('Flexchart', data)\nmsg.data = data\nmsg.labels = [\"dataA\",\"dataB\"]\nmsg.widths = [1,1]\nmsg.colors = ['blue','red']\nreturn msg\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":490,"y":185,"wires":[["e381de90de996890"]]},{"id":"9b370a9a1cdb26aa","type":"function","z":"1543d308b342690a","name":"Example Data","func":"/*\n\n*/\nconst now = Date.now()\nconst a = Math.sin(now / 6000)\nconst b = 1.2 * Math.cos(now / 4000)\nmsg.payload = [a,b]\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":265,"y":185,"wires":[["bfbe90dadb982ce6"]]},{"id":"7b830c4b6928a022","type":"inject","z":"1543d308b342690a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":110,"y":185,"wires":[["9b370a9a1cdb26aa"]]},{"id":"ec11c1750a0e181e","type":"inject","z":"1543d308b342690a","name":"Clear Chart","props":[{"p":"reset","v":"","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":275,"y":225,"wires":[["bfbe90dadb982ce6"]]},{"id":"e381de90de996890","type":"fd-time-plot","z":"1543d308b342690a","fd_container":"69c2e3f5798c3475","fd_cols":"7","fd_rows":"3","fd_array":false,"fd_array_max":10,"fd_output_topic":"","name":"Test chart","title":"Using msg.data","popup_info":"","data":"\"null\"","labels":"\"[]\"","colors":"\"[]\"","axes":"\"[]\"","widths":"\"[]\"","span_gaps":"\"[]\"","left_unit":"","right_unit":"","left_min":null,"left_max":null,"left_decimals":1,"right_min":null,"right_max":null,"right_decimals":1,"reverse_legend":false,"x":680,"y":185,"wires":[[]]},{"id":"bd24b9df38fd9b97","type":"function","z":"1543d308b342690a","name":"Use msg.payload","func":"if (msg.hasOwnProperty(\"reset\")) {\n    context.set('Flexchart', [])\n    return\n}\n\nlet data = context.get('Flexchart') || []\nconst newdata = msg.payload\nconst timestamp = Date.now() / 1000\n\nnewdata.unshift(timestamp)\ndata.push(newdata)\nwhile (data.length > 50) data.shift()\ncontext.set('Flexchart', data)\nmsg.payload = data\nmsg.labels = [\"dataA\",\"dataB\"]\nmsg.widths = [1,1]\nmsg.colors = ['blue','red']\nreturn msg\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":275,"wires":[["92068bc37c49da87"]]},{"id":"ca257a34e62fa351","type":"function","z":"1543d308b342690a","name":"Example Data","func":"/*\n\n*/\nconst now = Date.now()\nconst a = Math.sin(now / 6000)\nconst b = 1.2 * Math.cos(now / 4000)\nmsg.payload = [a,b]\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":265,"y":275,"wires":[["bd24b9df38fd9b97"]]},{"id":"d101acc060767af7","type":"inject","z":"1543d308b342690a","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":110,"y":275,"wires":[["ca257a34e62fa351"]]},{"id":"7cf85c40db3d0ba7","type":"inject","z":"1543d308b342690a","name":"Clear Chart","props":[{"p":"reset","v":"","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":275,"y":315,"wires":[["bd24b9df38fd9b97"]]},{"id":"92068bc37c49da87","type":"fd-time-plot","z":"1543d308b342690a","fd_container":"69c2e3f5798c3475","fd_cols":"7","fd_rows":"3","fd_array":false,"fd_array_max":10,"fd_output_topic":"","name":"Test chart","title":"Using msg.payload","popup_info":"","data":"\"null\"","labels":"\"[]\"","colors":"\"[]\"","axes":"\"[]\"","widths":"\"[]\"","span_gaps":"\"[]\"","left_unit":"","right_unit":"","left_min":null,"left_max":null,"left_decimals":1,"right_min":null,"right_max":null,"right_decimals":1,"reverse_legend":false,"x":680,"y":275,"wires":[[]]},{"id":"69c2e3f5798c3475","type":"flexdash container","name":"Demo grid","kind":"StdGrid","fd_children":",ecf5adc800160eeb,e381de90de996890,92068bc37c49da87","title":"","tab":"fef6c0f6d48841d6","min_cols":"8","max_cols":"10","parent":"","solid":false,"cols":"1","rows":"1"},{"id":"fef6c0f6d48841d6","type":"flexdash tab","name":"Demo","icon":"mdi-view-dashboard","fd_children":",69c2e3f5798c3475","fd":"e8f5aea52ab49500"}]
1 Like

The Dropdown Select is working well, thanks.
One minor issue, in the config, under Value it says "Value of selection to show in the box. Change using msg[value]". I think that should say msg[payload]. It works correctly, it is just the text shown in the config. As a matter of interest, why do you use msg[xxx] rather than msg.xxx?

A couple of questions on the Text View widget. With that widget I have to pass the text to show via msg.text, whereas msg.payload would be more conventional for the primary input value. Is that intentional or just the way it has come out?
Also on Text View I see it has an output, and I had hoped that this was intended to be the widget that might eventually become something comparable to the ui_text dashboard widget which displays and allows input of text, numbers etc. But the fd node operates in a different way, requiring the user to go into edit mode, edit, and then save it, which is inconvenient if just using it as a user input node. Could you share your thoughts on this?
I have just noticed that there is also the Value Sequence node, which is also comes into this area.

1 Like

Thanks but using "stat" instead of "button" I cannot perform action by pressing that widget (in my previous example "sala", "cucina" and "tv" turn on their respective lights in addition to displaying the room temperature through an html code inserted in the label of the button as in the example below).

[{"id":"12d68747b1a5db6e","type":"ui_button","z":"14153030d1f012bd","name":"Luce Sala","group":"8ecdbfcddfca1903","order":1,"width":3,"height":2,"passthru":false,"label":"{{label}}","tooltip":"","color":"{{color}}","bgcolor":"DimGray","className":"","icon":"{{icon}}","payload":"","payloadType":"str","topic":"","topicType":"str","x":500,"y":320,"wires":[["290e3a27bbf6a3aa"]]},{"id":"6bdc7783322545cf","type":"mqtt in","z":"14153030d1f012bd","name":"Stato Luce Sala","topic":"shellies/shelly1-XXXXXXXXXX/relay/0","qos":"2","datatype":"auto","broker":"babea684.f913c","nl":false,"rap":false,"inputs":0,"x":120,"y":320,"wires":[["4dd6a544f936ca97"]]},{"id":"4862ef6044bea485","type":"link out","z":"14153030d1f012bd","name":"MQTT out","links":["4353bbe6.c81db4","7672bd27.3024c4"],"x":930,"y":320,"wires":[],"l":true},{"id":"4dd6a544f936ca97","type":"function","z":"14153030d1f012bd","name":"Desktop","func":"var icon = \"iconify-mdi:sofa 48px\";\nvar object = \"Sala\";\nglobal.set(\"luce_sala\", msg.payload);\nconst object1 = global.get(\"temperatura_sala\");\nconst object2 = Number(object1).toFixed(1);\nvar text = \"<br/><font size=4 color=white>\" + object + \"</font>\";\nvar temp = \"<br/><font size=3 color=white>\" + object2 + \"°</font>\";\nvar label = text + temp;\nvar iconcolor;\n\nif (msg.payload == \"on\") {\n    iconcolor = \"DarkOrange\";\n} else {\n    iconcolor = \"White\";\n}\n\nmsg = { color:iconcolor, label:label, icon: icon};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":320,"wires":[["12d68747b1a5db6e"]]},{"id":"0f5633a18075df97","type":"mqtt in","z":"14153030d1f012bd","name":"Temperatura Sala","topic":"rtl_433/Nexus-TH-119/temperature_C","qos":"2","datatype":"auto","broker":"babea684.f913c","nl":false,"rap":false,"inputs":0,"x":130,"y":380,"wires":[["d0e12fba75150c0e"]]},{"id":"4d6a4d409ee10d64","type":"change","z":"14153030d1f012bd","name":"Stato Temperatura Sala","rules":[{"t":"set","p":"temperatura_sala","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":570,"y":380,"wires":[[]]},{"id":"d0e12fba75150c0e","type":"delay","z":"14153030d1f012bd","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":340,"y":380,"wires":[["4d6a4d409ee10d64"]]},{"id":"290e3a27bbf6a3aa","type":"function","z":"14153030d1f012bd","name":"Object State","func":"var object3 = global.get(\"luce_sala\");\nvar topic = \"shellies/shelly1-XXXXXXXXXX/relay/0/command\";\n\nif (object3 === \"on\") {\n    msg.payload = \"off\";\n    \n} else if (object3 === \"off\") {\n    msg.payload = \"on\";\n}\n\nmsg = { topic:topic, payload:msg.payload};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":710,"y":320,"wires":[["4862ef6044bea485"]]},{"id":"8ecdbfcddfca1903","type":"ui_group","name":"Piano Terra","tab":"a404e75d.99def8","order":2,"disp":false,"width":"15","collapse":false,"className":""},{"id":"babea684.f913c","type":"mqtt-broker","name":"Mosquitto","broker":"10.0.0.6","port":"1883","clientid":"nodered","autoConnect":true,"usetls":false,"compatmode":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"sessionExpiry":""},{"id":"a404e75d.99def8","type":"ui_tab","name":"Home","icon":"dashboard","order":1,"disabled":false,"hidden":false}]
1 Like

@tve congrats . this is great effort. I used your ealier older version a while ago without the integration to node-red . yet to try this one.
just one quick thing , in regards to

Editing a FlexDash node in the flow editor uses typed input input fields which have various issues with defaults for boolean values, null as object or string values, not supporting the input of multi-line strings, not supporting a color picker, and more. An input field better suited for FlexDash is badly needed`

I can possibly help with TypedInput. I played so long with it that I managed to create custom input fields that are embedded within the TypedInput itself using the custom function handlers exposed by the TypedInput Widget and jQuery. Looks good and works as expected but needs a bit of code to handle different events, etc. I used it in a personal project as I really like to use standard fields such as TypedInput and it gives loads of flexibility to users. Anyway , please try put together details of what you trying to achieve (type of input like ColorPicker, etc. ) and I can create the TypedInput for it :slight_smile:
cheers

4 Likes

I think the example flow was to show you how do the layout. If you want a push button then use that instead of a stat.

1 Like

None of the widgets actually have a payload prop, it's the node generator that maps msg.payload to one of the props using a small hard-coded list (looking for a value or data prop at the moment). I need to enhance the generator so it can take some config to fix this as well as provide the ability to specify an icon for each generated node. I added text to the list for now...

You're right about the msg[value] vs msg.value, brain fart on my end :roll_eyes:, would have to be msg['value'] to be correct, actually... I just changed it, thanks!

You are asking for the TextField widget, which I haven't yet written :stuck_out_tongue_winking_eye:. For text fields I've used the PropsTable widget, which allows you to input multiple fields at once and then submit them. To get a TextField widget, if you post the spec (help text and props) I'll write the rest :smiley:.

The intent for the Text widget is to display unformatted text, for example lines of log output.

You can change the stat widgets to buttons. To display the temperature, I would use two widgets: one for the button and one for the temperature. If you set the title of a widget to the empty string you won't get a title, so you don't end up with two "sala" titles. You will have to play around a little to get the layout you like... :building_construction:

That would be very appreciated, thank you for offering!!! I've made progress over the last week and some of the issues are fixed. I believe what I'm missing is:

  • add a selection for "unset" or "null" so one can clearly unset a field (same effect as string + empty box or json "null")
  • add '...' button to string input to bring up a multi-line text editor so one can input some text
  • possibly integrate the color picker into TypedInput

For reference, the code is at node-red-flexdash/plugin/flexdash-plugin.html at main · flexdash/node-red-flexdash · GitHub and gets called from the oneditprepare and oneditsave from node-red-flexdash/templates/widget-node.html at main · flexdash/node-red-flexdash · GitHub (which is a template for the node generator). You can see it in action in any of the core widgets. Maybe we need to hack up a core widget and embed the load_props_edit and save_props_edit functions to serve as a test-bed?

1 Like