...Continued from Dashboard 2.0 Pre-Alpha Available
There is also a dashboard-2
tag which you can subscribe to, by going to Topics tagged dashboard-2 and then selecting how you wish to be alerted about new posts.
...Continued from Dashboard 2.0 Pre-Alpha Available
There is also a dashboard-2
tag which you can subscribe to, by going to Topics tagged dashboard-2 and then selecting how you wish to be alerted about new posts.
For sure it is whole new world in terms of layouting. Nothing (specially custom made stuff) can be directly brought over from D1. And even trying to use ready made components takes to understand that world and maybe to learn new tricks.
I think it doesn't take too long when people start to bring over the stuff from D1 and no matter the strategy they choose, there will be some challenges. So without any jokes - it will be somehow topic at it's own for some time.
Dashboard 2 pre-alpha has been available for 4 months.
Is there a road map for actual alpha test stage, beta, production release?
Will it be the official dashboard by the time Node-red 5 comes along?
(Schedules have slipped, but the documentation suggests NR 4 goes into maintenance next May!)
Edit:
Maybe it has already been promoted? https://flows.nodered.org/node/@flowfuse/node-red-dashboard, though it's otherwise a blank page (might be a problem with my internet) says node-red-dashboard 0.10.1, no mention of pre-alpha.
I'm just hoping for some clarity so I can decide at what point to start working with the new dashboard.
We announced Beta about 4-5 weeks ago, but the thread title never changed
It was originally announced as part of https://www.youtube.com/watch?v=7GTHYzRpQ9M&t=1801s
Looks like I never actually published the accompanying article to go with it though! My fault entirely.
In the dropdown it says that it allows dynamic options, using msg.options
. However, I cannot get it to work, what am I doing wrong? (All I get is 'no options available').
msg.options = [ { value: "1", label: "Option 1" }, { value: "2", label: "Option 2" }, { value: "3", label: "Option 3" } ]
Also when settings options in the node, number, string and boolean types can be selected but the msg.options
note specifies string, will this be updated later?
It is working for me, using version 0.10.1
[{"id":"0c31ad9acfb32a07","type":"inject","z":"997da33a0beedade","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":200,"y":940,"wires":[["2dcb529f7d48c393"]]},{"id":"4aef8e568f749630","type":"ui-dropdown","z":"997da33a0beedade","group":"7545fdb22e4a388c","name":"","label":"Select Option:","tooltip":"","order":0,"width":0,"height":0,"passthru":false,"multiple":false,"options":[{"label":"","value":"","type":"str"}],"payload":"","topic":"topic","topicType":"msg","className":"","x":560,"y":940,"wires":[["2dba8a33e051d925"]]},{"id":"2dcb529f7d48c393","type":"function","z":"997da33a0beedade","name":"Set msg.options","func":"msg.options = [ { value: \"1\", label: \"Option 1\" }, { value: \"2\", label: \"Option 2\" }, { value: \"3\", label: \"Option 3\" } ]\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":940,"wires":[["4aef8e568f749630"]]},{"id":"2dba8a33e051d925","type":"debug","z":"997da33a0beedade","name":"debug 2465","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":750,"y":940,"wires":[]},{"id":"7545fdb22e4a388c","type":"ui-group","name":"D2 tests","page":"d7fcdd33578e5d34","width":"6","height":"1","order":-1,"className":""},{"id":"d7fcdd33578e5d34","type":"ui-page","name":"d2","ui":"96e4ba9f6c4dd158","path":"/d2","layout":"flex","theme":"48d09dabddfb21da","order":-1,"className":""},{"id":"96e4ba9f6c4dd158","type":"ui-base","name":"D2","path":"/dashboard"},{"id":"48d09dabddfb21da","type":"ui-theme","name":"Theme 1","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"}}]
That would probably account for it then, I am on the latest release in my Palette which is v0.7.0. I will wait for the update to be published
@Buckskin - Are you using @flowfuse/node-red-dashboard
(note - @flowfuse, not @flowforge)?
@flowforge/node-red-dashboard
is now depreciated (because of the name change from flowforge to flowfuse)
I'm assured that the best way to update to the newer package is;
npm uninstall @flowforge/node-red-dashboard
in the usual manner.npm install @flowfuse/node-red-dashboard
.Doing it that way, should replace the nodes, and not break your flows.
The npm commands have to be run from your .node-red folder (or wherever your flows file is if not .node-red).
In D1 one can use the Font Awsome icons in, for example, a Text node. Is some similar functionality already available in D2?
In a template you can use the mdi icons OOTB
e.g.
<div class="d-flex justify-space-around">
MDI icons
<v-icon icon="mdi-information"></v-icon>
<v-icon icon="mdi-home"></v-icon>
<v-icon icon="mdi-minecraft"></v-icon>
<v-icon icon="mdi-crane"></v-icon>
<v-icon icon="mdi-headphones"></v-icon>
<v-icon icon="mdi-battery-40-bluetooth"></v-icon>
<v-icon icon="mdi-gavel"></v-icon>
</div>
The
v-icon
component provides a large set of glyphs to provide context to various aspects of your application. For a list of all available icons, visit the official Material Design Icons page. To use any of these icons simply use themdi-
prefix followed by the icon name.
Yes, but what about
I think that comes under this issue Paul
I can be done (right now) with (hacky) script magic though.
<script>
export default {
mounted() {
this.applyIcons()
},
methods: {
applyIcons: function() {
// Get all elements with the class '.mdi'
const mdiElements = document.querySelectorAll('div[class*="mdi-icon-"] > div > label')
// Iterate through each element
mdiElements.forEach((element) => {
// Create a new div element
const wrapperDiv = document.createElement('div')
// Extract the 'mdi-xxx' class from the original element's class list
const foundMdiClass = Array.from(element.parentNode.parentNode.classList).find((cls) => cls.startsWith('mdi-icon-'))
const mdiClass = foundMdiClass.replace('mdi-icon-', 'mdi-')
// Create the new icon element with the extracted class
const newIcon = document.createElement('i')
newIcon.className = `mdi ${mdiClass} v-icon mr-2 notranslate v-theme--nrdb v-icon--size-default`
newIcon.setAttribute('aria-hidden', 'true')
// Remove the 'mdi-xxx' class from the parent element
element.parentNode.parentNode.classList.remove(foundMdiClass)
// Clone the original element
const clonedElement = element.cloneNode(true);
// Append the new icon and the CLONED original element to the wrapper div
wrapperDiv.appendChild(newIcon);
wrapperDiv.appendChild(clonedElement);
// Insert the new wrapper div before the original element
element.parentNode.insertBefore(wrapperDiv, element);
// Remove the original element
element.parentNode.removeChild(element);
})
}
}
}
</script>
NOTE This ^ (accessing the DOM directly) ^ is NOT Vue best practice but until there is a means of formatting a label, this might be suitable for some in desperate need.
Demo:
[{"id":"13232e47a6495e4b","type":"ui-text","z":"1ca1817447127d22","group":"34a8586ca5a5880e","order":0,"width":0,"height":0,"name":"","label":"My Label with crane icon","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"Gill Sans,Geneva,sans-serif","fontSize":16,"color":"#000000","className":"mdi-icon-crane","x":2550,"y":100,"wires":[]},{"id":"4e4e5ebcf0b853c5","type":"ui-template","z":"1ca1817447127d22","group":"34a8586ca5a5880e","dashboard":"ID-BASE-1","page":"ID-PAGE-1","name":"Apply icons to Labels with class .mdi-icon-xxxx","order":0,"width":0,"height":0,"head":"","format":"<script>\n\nexport default {\n mounted() {\n this.applyIcons()\n },\n methods: {\n applyIcons: function() {\n\n // Get all elements with the class '.mdi'\n const mdiElements = document.querySelectorAll('div[class*=\"mdi-icon-\"] > div > label')\n \n // Iterate through each element\n mdiElements.forEach((element) => {\n // Create a new div element\n const wrapperDiv = document.createElement('div')\n \n // Extract the 'mdi-xxx' class from the original element's class list\n const foundMdiClass = Array.from(element.parentNode.parentNode.classList).find((cls) => cls.startsWith('mdi-icon-'))\n const mdiClass = foundMdiClass.replace('mdi-icon-', 'mdi-')\n \n // Create the new icon element with the extracted class\n const newIcon = document.createElement('i')\n newIcon.className = `mdi ${mdiClass} v-icon mr-2 notranslate v-theme--nrdb v-icon--size-default`\n newIcon.setAttribute('aria-hidden', 'true')\n \n // Remove the 'mdi-xxx' class from the parent element\n element.parentNode.parentNode.classList.remove(foundMdiClass)\n \n // Clone the original element\n const clonedElement = element.cloneNode(true);\n \n // Append the new icon and the CLONED original element to the wrapper div\n wrapperDiv.appendChild(newIcon);\n wrapperDiv.appendChild(clonedElement);\n \n // Insert the new wrapper div before the original element\n element.parentNode.insertBefore(wrapperDiv, element);\n \n // Remove the original element\n element.parentNode.removeChild(element);\n })\n }\n }\n}\n\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"d-none","x":2620,"y":60,"wires":[[]]},{"id":"05306283049c2598","type":"ui-text","z":"1ca1817447127d22","group":"34a8586ca5a5880e","order":0,"width":0,"height":0,"name":"","label":"My Label with minecraft icon","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"Gill Sans,Geneva,sans-serif","fontSize":16,"color":"#000000","className":"mdi-icon-minecraft","x":2560,"y":140,"wires":[]},{"id":"c97b993296bcdf4c","type":"ui-text","z":"1ca1817447127d22","group":"34a8586ca5a5880e","order":0,"width":0,"height":0,"name":"no text - icon only","label":"","format":"{{msg.payload}}","layout":"row-spread","style":false,"font":"Gill Sans,Geneva,sans-serif","fontSize":16,"color":"#000000","className":"mdi-icon-battery-40-bluetooth","x":2530,"y":180,"wires":[]},{"id":"4a0fdf45a8ef9eaa","type":"inject","z":"1ca1817447127d22","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":2110,"y":140,"wires":[["a7035cd1a3125e8c","1d99b5dba1de131f","e001508b5d471e4e"]]},{"id":"a7035cd1a3125e8c","type":"change","z":"1ca1817447127d22","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"\t\t\t\t(\t $minimum := 50;\t $maximum := 90;\t $round(($random() * ($maximum-$minimum)) + $minimum, 0)\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":2320,"y":100,"wires":[["13232e47a6495e4b"]]},{"id":"1d99b5dba1de131f","type":"change","z":"1ca1817447127d22","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"\t\t\t\t(\t $minimum := 50;\t $maximum := 90;\t $round(($random() * ($maximum-$minimum)) + $minimum, 0)\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":2320,"y":140,"wires":[["05306283049c2598"]]},{"id":"e001508b5d471e4e","type":"change","z":"1ca1817447127d22","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"\t\t\t\t(\t $minimum := 50;\t $maximum := 90;\t $round(($random() * ($maximum-$minimum)) + $minimum, 0)\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":2320,"y":180,"wires":[["c97b993296bcdf4c"]]},{"id":"34a8586ca5a5880e","type":"ui-group","name":"Group 4-1","page":"03b64402237b9653","width":"6","height":"1","order":-1,"showTitle":false,"className":""},{"id":"ID-BASE-1","type":"ui-base","name":"Dashboard","path":"/dashboard"},{"id":"ID-PAGE-1","type":"ui-page","name":"Page 1","ui":"ID-BASE-1","path":"/page1","layout":"notebook","theme":"f9b6670b127dc219"},{"id":"03b64402237b9653","type":"ui-page","name":"Page 4","ui":"ID-BASE-1","path":"/p4","layout":"grid","theme":"4bff2b59c4c518e1","order":-1,"className":""},{"id":"f9b6670b127dc219","type":"ui-theme","name":"FlowForge Theme","colors":{"surface":"#152a47","primary":"#005aff","bgPage":"#ffffff","groupBg":"#ffffff","groupOutline":"#cc3e3e"}},{"id":"4bff2b59c4c518e1","type":"ui-theme","name":"Another Theme","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"}}]
I think that comes under this issue
That appears to be in relation to labels rather than an icon instead of the payload text, but in practice perhaps either would do, as one could manually build the label to be both label and icon.
can be done (right now) with (hacky) script magic
I think maybe a better temporary solution would be to use a template.
In a template you can use the mdi icons
I see I can specify a limited range of colours in v-icon
, can I pass that in to the template in a message? I effectively want to have an LED like output showing on/off.
First, here are the colour names available to use as classes: Material color palette — Vuetify
I see I can specify a limited range of colours in
v-icon
, can I pass that in to the template in a message? I effectively want to have an LED like output showing on/off.
You can achieve this:
with this template:
<template>
<v-icon :color="ledColor" :icon="iconName"></v-icon>
</template>
<script>
export default {
data() {
return {
ledColor: 'grey',
iconName: 'mdi-led-off'
}
},
mounted() {
const component = this
this.$socket.on('msg-input:' + this.id, function(msg) {
console.log(msg)
if (msg.topic === 'on') {
component.iconName = 'mdi-led-on'
component.ledColor = msg.payload
} else if (msg.topic === 'off') {
component.iconName = 'mdi-led-off'
component.ledColor = 'grey'
}
})
}
}
</script>
Flow:
[{"id":"fb5c574dc9088451","type":"ui-template","z":"1ca1817447127d22","group":"34a8586ca5a5880e","dashboard":"ID-BASE-1","page":"ID-PAGE-1","name":"","order":0,"width":0,"height":0,"head":"","format":"<template>\n <v-icon :color=\"ledColor\" :icon=\"iconName\"></v-icon>\n</template>\n\n<script>\n export default {\n data() {\n return {\n ledColor: 'grey',\n iconName: 'mdi-led-off'\n }\n },\n mounted() {\n const component = this\n this.$socket.on('msg-input:' + this.id, function(msg) {\n console.log(msg)\n if (msg.topic === 'on') {\n component.iconName = 'mdi-led-on'\n component.ledColor = msg.payload\n } else if (msg.topic === 'off') {\n component.iconName = 'mdi-led-off'\n component.ledColor = 'grey'\n }\n })\n }\n }\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":2740,"y":320,"wires":[[]]},{"id":"d813d7d4882adf78","type":"function","z":"1ca1817447127d22","name":"generateRandomColorName","func":"const colorNames = [\"red\", \"pink\", \"yellow\", \"green\", \"purple\", \"blue\", \"cyan\", \"lime\", \"orange\"];\n\nfunction generateRandomColorName() {\n const randomIndex = Math.floor(Math.random() * colorNames.length);\n return colorNames[randomIndex];\n}\n\nmsg.payload = generateRandomColorName();\nreturn msg;","outputs":1,"timeout":30,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2680,"y":360,"wires":[["fb5c574dc9088451"]]},{"id":"8330c685d9761104","type":"inject","z":"1ca1817447127d22","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"on","payload":"","payloadType":"str","x":2490,"y":360,"wires":[["d813d7d4882adf78"]]},{"id":"1ebc687c8ca026be","type":"inject","z":"1ca1817447127d22","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"off","payload":"","payloadType":"str","x":2490,"y":320,"wires":[["fb5c574dc9088451"]]},{"id":"34a8586ca5a5880e","type":"ui-group","name":"Group 4-1","page":"03b64402237b9653","width":"6","height":"1","order":-1,"showTitle":false,"className":""},{"id":"ID-BASE-1","type":"ui-base","name":"Dashboard","path":"/dashboard"},{"id":"ID-PAGE-1","type":"ui-page","name":"Page 1","ui":"ID-BASE-1","path":"/page1","layout":"notebook","theme":"f9b6670b127dc219"},{"id":"03b64402237b9653","type":"ui-page","name":"Page 4","ui":"ID-BASE-1","path":"/p4","layout":"grid","theme":"4bff2b59c4c518e1","order":-1,"className":""},{"id":"f9b6670b127dc219","type":"ui-theme","name":"FlowForge Theme","colors":{"surface":"#152a47","primary":"#005aff","bgPage":"#ffffff","groupBg":"#ffffff","groupOutline":"#cc3e3e"}},{"id":"4bff2b59c4c518e1","type":"ui-theme","name":"Another Theme","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"}}]
Magic! Thanks. That should keep me going until D2 catches up.
My flow is littered with workarounds for:
#352 Disabled button re-enables on browser refresh
#342 Form widget should send Number type fields as Numbers not strings
#363 No inputs on form
#210 Not able to select Show state of input in ui-switch
#311 ui-switch malfunctions if on/off payloads are strings
But that is what you get when going with bleeding edge developments. It is inevitable.
Actually, I went too far, too soon Ted!
It is as simple as this:
The template
<v-icon
:color="msg.payload?.state === 'on' ? (msg.payload?.color || 'green') : 'grey'"
:icon="msg.payload?.state === 'on' ? 'mdi-led-on' : 'mdi-led-off'">
</v-icon>
Demo flow
[{"id":"fb5c574dc9088451","type":"ui-template","z":"1ca1817447127d22","group":"34a8586ca5a5880e","dashboard":"ID-BASE-1","page":"ID-PAGE-1","name":"","order":0,"width":0,"height":0,"head":"","format":"\n<v-icon \n :color=\"msg.payload.state === 'on' ? (msg.payload?.color || 'green') : msg.payload.color\"\n :icon=\"msg.payload.state === 'on' ? 'mdi-led-on' : 'mdi-led-off'\">\n</v-icon>\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":2780,"y":320,"wires":[[]]},{"id":"d813d7d4882adf78","type":"function","z":"1ca1817447127d22","name":"generateRandomColorName","func":"const colorNames = [\"red\", \"pink\", \"yellow\", \"green\", \"purple\", \"blue\", \"cyan\", \"lime\", \"orange\"];\n\nfunction generateRandomColorName() {\n const randomIndex = Math.floor(Math.random() * colorNames.length);\n return colorNames[randomIndex];\n}\n\nmsg.payload = {\n state: 'on',\n color: generateRandomColorName()\n}\nreturn msg;","outputs":1,"timeout":30,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2720,"y":360,"wires":[["fb5c574dc9088451"]]},{"id":"8330c685d9761104","type":"inject","z":"1ca1817447127d22","name":"on","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":2490,"y":360,"wires":[["d813d7d4882adf78"]]},{"id":"1ebc687c8ca026be","type":"inject","z":"1ca1817447127d22","name":"off","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"state\": \"off\" }","payloadType":"json","x":2490,"y":320,"wires":[["fb5c574dc9088451"]]},{"id":"34a8586ca5a5880e","type":"ui-group","name":"Group 4-1","page":"03b64402237b9653","width":"6","height":"1","order":-1,"showTitle":false,"className":""},{"id":"ID-BASE-1","type":"ui-base","name":"Dashboard","path":"/dashboard"},{"id":"ID-PAGE-1","type":"ui-page","name":"Page 1","ui":"ID-BASE-1","path":"/page1","layout":"notebook","theme":"f9b6670b127dc219"},{"id":"03b64402237b9653","type":"ui-page","name":"Page 4","ui":"ID-BASE-1","path":"/p4","layout":"grid","theme":"4bff2b59c4c518e1","order":-1,"className":""},{"id":"f9b6670b127dc219","type":"ui-theme","name":"FlowForge Theme","colors":{"surface":"#152a47","primary":"#005aff","bgPage":"#ffffff","groupBg":"#ffffff","groupOutline":"#cc3e3e"}},{"id":"4bff2b59c4c518e1","type":"ui-theme","name":"Another Theme","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"}}]
Just send:
{"state":"on","color":"green-lighten-2"}
or
{"state":"off"}
Thats it
My flow is littered with workarounds for:
I really appreciate the patience Colin - working as fast as I can.
It is as simple as this:
Even more magic. Thanks.
working as fast as I can.
Yes, I am sure that is true, I think it is remarkable how much has been accomplished already.