Automate dashboard contents

Hi all!

I need to automate how i'm using the dashboard. Actually i'm using the dashboard for documentation purposes. Now i do all of it manually, i create groups, create the tabs, create the nodes, then i configure the nodes one by one. All of it takes me a lot of time and maybe it can be automated.

Any ideas?.

Thanks a lot!

You will need to explain further what you are actually doing.

Have you looked at uibuilder which, amongst other things, lets you use simple configuration data to generate a UI so it might be useful for what you are trying to do.

Ok, i can share and example.

flows.json (52.5 KB)

That example is what i have so far. The main goal is to automate those nodes so anyone can fill up the information without have to do them manually.

Thanks for the help!

Sorry, but I'm not going to load such a large unknown flow just to help you with this. I can see it has multiple sub-flows, Dashboard themes and more. An explanation should be sufficient.

Do you mean that you want to dynamically create the dashboard based on something that defines what should be on the dashboard?

What are you documenting using the dashboard?

Untitled 1

I imported it but I'm definitely not going to deploy and then delete 103 useless config nodes one by one.

Hi!

Sorry my bad, this is the updated flow.

[
    {
        "id": "5c42798006c66cf9",
        "type": "tab",
        "label": "docs",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "af895c52e12cee21",
        "type": "group",
        "z": "5c42798006c66cf9",
        "name": "client 2",
        "style": {
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "52b79c05fc2d13b9",
            "edd35247be478004",
            "33da1404a4f86a06",
            "f14340a37e866581",
            "b59c2d5703d7af40"
        ],
        "x": 374,
        "y": 79,
        "w": 272,
        "h": 242
    },
    {
        "id": "27a29473dcb0799c",
        "type": "group",
        "z": "5c42798006c66cf9",
        "name": "client 1",
        "style": {
            "label": true,
            "color": "#000000"
        },
        "nodes": [
            "f8e6f4e03ec6f437",
            "df3b09823a0be235",
            "dac69f93724f9be2",
            "8e6687ef2a55ccf0"
        ],
        "x": 74,
        "y": 79,
        "w": 272,
        "h": 202
    },
    {
        "id": "f8e6f4e03ec6f437",
        "type": "ui_text",
        "z": "5c42798006c66cf9",
        "g": "27a29473dcb0799c",
        "group": "5f373ce19a1aa290",
        "order": 1,
        "width": 0,
        "height": 0,
        "name": "nombre bot",
        "label": "bot name:",
        "format": "lalala",
        "layout": "row-left",
        "className": "main-title-content no-gap",
        "x": 170,
        "y": 120,
        "wires": []
    },
    {
        "id": "df3b09823a0be235",
        "type": "ui_text",
        "z": "5c42798006c66cf9",
        "g": "27a29473dcb0799c",
        "group": "5f373ce19a1aa290",
        "order": 2,
        "width": 0,
        "height": 0,
        "name": "",
        "label": "Query sql",
        "format": "example query",
        "layout": "col-center",
        "className": "main-title-content no-gap",
        "x": 160,
        "y": 240,
        "wires": []
    },
    {
        "id": "dac69f93724f9be2",
        "type": "ui_text",
        "z": "5c42798006c66cf9",
        "g": "27a29473dcb0799c",
        "group": "5f373ce19a1aa290",
        "order": 5,
        "width": 0,
        "height": 0,
        "name": "accion de la integracion",
        "label": "Accion de la integracion:  ",
        "format": "some info",
        "layout": "col-center",
        "className": "main-title-content no-gap text-formatting accion",
        "x": 210,
        "y": 200,
        "wires": []
    },
    {
        "id": "8e6687ef2a55ccf0",
        "type": "ui_text",
        "z": "5c42798006c66cf9",
        "g": "27a29473dcb0799c",
        "group": "5f373ce19a1aa290",
        "order": 3,
        "width": 0,
        "height": 0,
        "name": "service",
        "label": "asadad",
        "format": "Publico",
        "layout": "row-left",
        "className": "main-title-content no-gap",
        "x": 160,
        "y": 160,
        "wires": []
    },
    {
        "id": "52b79c05fc2d13b9",
        "type": "ui_text",
        "z": "5c42798006c66cf9",
        "g": "af895c52e12cee21",
        "group": "25bfd2e62c3836cb",
        "order": 3,
        "width": 0,
        "height": 0,
        "name": "query mysql",
        "label": "query mysql",
        "format": "asd",
        "layout": "col-center",
        "className": "main-title-content no-gap",
        "x": 470,
        "y": 280,
        "wires": []
    },
    {
        "id": "edd35247be478004",
        "type": "ui_text",
        "z": "5c42798006c66cf9",
        "g": "af895c52e12cee21",
        "group": "25bfd2e62c3836cb",
        "order": 1,
        "width": 0,
        "height": 0,
        "name": "plataforma",
        "label": "platform",
        "format": "http://www.google.com",
        "layout": "row-left",
        "className": "main-title-content no-gap",
        "x": 470,
        "y": 240,
        "wires": []
    },
    {
        "id": "33da1404a4f86a06",
        "type": "ui_text",
        "z": "5c42798006c66cf9",
        "g": "af895c52e12cee21",
        "group": "25bfd2e62c3836cb",
        "order": 5,
        "width": 0,
        "height": 0,
        "name": "accion de la integracion",
        "label": "Accion de la integracion:  ",
        "format": "some information",
        "layout": "col-center",
        "className": "main-title-content no-gap text-formatting accion",
        "x": 510,
        "y": 200,
        "wires": []
    },
    {
        "id": "f14340a37e866581",
        "type": "ui_text",
        "z": "5c42798006c66cf9",
        "g": "af895c52e12cee21",
        "group": "25bfd2e62c3836cb",
        "order": 4,
        "width": 0,
        "height": 0,
        "name": "salida ws",
        "label": "Salida al servicio:",
        "format": "Publico",
        "layout": "row-left",
        "className": "main-title-content no-gap",
        "x": 460,
        "y": 160,
        "wires": []
    },
    {
        "id": "b59c2d5703d7af40",
        "type": "ui_text",
        "z": "5c42798006c66cf9",
        "g": "af895c52e12cee21",
        "group": "25bfd2e62c3836cb",
        "order": 2,
        "width": 0,
        "height": 0,
        "name": "nombre bot",
        "label": "bot name:",
        "format": "lalalal2",
        "layout": "row-left",
        "className": "main-title-content no-gap",
        "x": 470,
        "y": 120,
        "wires": []
    },
    {
        "id": "e8a24a141a3f5bf4",
        "type": "ui_template",
        "z": "5c42798006c66cf9",
        "group": "",
        "name": "button collapse_expand groups CSS",
        "order": 1,
        "width": 0,
        "height": 0,
        "format": "<style>\nbody {\n    background: -webkit-linear-gradient(\n    55deg,\n    #518b8a 50%,\n    #245aa5 100%,\n    #b800e9 0%\n    );\n    -webkit-touch-callout: none !important;\n}\n\nbody.nr-dashboard-theme {\n    font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif;\n}\nbody.nr-dashboard-theme md-toolbar,\nbody.nr-dashboard-theme md-content md-card {\n    background-color: transparent !important;\n    color: #FFFFFF;\n}\n\nui-card-panel {\n    background-color: rgba(255,255,255,0.1) !important;\n    border-radius: 10px !important;\n}\n    \n.md-card.md-default-theme, md-card {\n    border-radius: 10px;\n}\n\n.nr-dashboard-switch.ng-scope {\n    background-color: rgba(255,255,255,.2) !important;\n}\n\n.nr-dashboard-switch.ng-scope:hover {\n    background-color: rgba(255,255,255,.5) !important;\n}\n\n.nr-dashboard-theme .nr-dashboard-button .md-button {\n    background-color: rgba(255,255,255,.2);\n}\n\n.md-button {\n    border-radius: 10px;\n}\n.collapse_groups_btn:hover {\n    background: rgba(255,255,255,1);\n    color: #64bb5d;\n}\n.collapse_groups_btn {\n    transition: background 0.2s, color 0.2s;\n    background-color: rgba(255,255,255,0.1);\n    position: relative;\n    color: whitesmoke;\n    border: solid;\n    border-radius: 10px;\n    right: 5px;\n    font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif;\n}\n.nr-dashboard-cardpanel layout-column {\n    background-color: rgba(255,255,255,0.2) !important;\n    background-color: transparent !important;\n    border-radius: 10px !important;\n}\n\n.nr-dashboard-theme ui-card-panel {\n    border: solid;\n    border-color: cadetblue;\n}\n\n.nr-dashboard-template {\n    overflow-y: visible;\n}\n.nr-dashboard-theme md-content md-card {\n    background-color: transparent !important;\n}\n\n.nr-dashboard-theme ui-card-panel p.nr-dashboard-cardtitle {\n    color: #a1e7e3;\n    font-weight: bold;   \n}\n\n.no-gap{\n    top: 0px !important;\n}\n.group-integracion{\n    height: initial !important;\n}\n.main-title-content{\n    align-items: flex-start !important;\n    height: auto !important;\n    position: relative !important;\n    text-align: justify;\n}\n.nr-dashboard-cardcontainer {\n    height: fit-content !important;\n    width: auto !important;\n}\n.nr-dashboard-cardpanel p{\n    min-height: 32px !important;\n    position: relative;\n    \n}\n.nr-dashboard-text .value{\n    font-weight: normal !important;\n}\n.nr-dashboard-text p {\n    margin-top: 0.5em;\n    margin-right: 0.3em;\n}\n.group-integracion{\n    font-weight: bold !important;\n}\n.ui-card-panel div div[style]{\n    height: initial !important;\n}\n.body.nr-dashboard-theme md-sidenav{\n    background-color: rgb(24 46 40 / 80%);\n}\n.md-sidenav-left.md-closed, md-sidenav.md-closed {\n    transform: translate3d(-100%,0,0);\n}\n.md-toolbar-tools h1, .md-toolbar-tools h2, .md-toolbar-tools h3 {\n    font-size: 22px;\n    font-weight: bold;\n    position: relative;\n    left: 15px;\n}\n\n</style>\n\n<script>\ndocument.ontouchmove = function (e) {\n    e.preventDefault();\n}\n</script>",
        "storeOutMessages": true,
        "fwdInMessages": true,
        "resendOnRefresh": true,
        "templateScope": "global",
        "className": "",
        "x": 830,
        "y": 80,
        "wires": [
            []
        ]
    },
    {
        "id": "3df796318f8bc20b",
        "type": "ui_template",
        "z": "5c42798006c66cf9",
        "group": "",
        "name": "btn agrupar todo",
        "order": 0,
        "width": 0,
        "height": 0,
        "format": "<script>\n    let observer = new MutationObserver(function (mutations) {\n        for (let mutation of mutations) {\n            if (mutation.type === 'childList') {\n                if (mutation.target.nodeName === 'MD-CONTENT') {\n                    if (mutation.addedNodes.length > 0) {\n                        for (let i = 0; i < mutation.addedNodes.length; i++) {\n                            if (mutation.addedNodes[i].nodeName === 'MD-TOOLBAR') {\n                                const toolbar = angular.element('#nr-dashboard-toolbar');\n                                let div = $('<div />');\n                                let button = $('<button type=\"button\" class= \"collapse_groups_btn\" id=\"toolbar-toggle-button\" onclick=\"toggleGroups()\">Agrupar Todo</button>');\n                                div.append(button);\n                                div[0].style.margin = '5px 5px 5px auto';\n                                toolbar.append(div);\n                                break;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    }),\n        targetNode;\n\n    function toggleGroups() {\n        let toggleButtons = angular.element('.nr-dashboard-cardcarat');\n        for (let i = 0; i < toggleButtons.length; i++) {\n            if (toggleButtons[i].firstElementChild.className == 'fa fa-caret-up') {\n                toggleButtons[i].click();\n            };\n        }\n    }\n\n    // Listen to all dashboard changes\n    angular.element(async function () {\n        try {\n            targetNode = angular.element('#nr-dashboard')[0];\n            let toolbar = angular.element('#nr-dashbard-toolbar');\n            if (toolbar.length == 0) {\n                observer.observe(targetNode, {\n                    attributes: false,\n                    childList: true,\n                    characterData: false,\n                    subtree: true\n                });\n            } else {\n                let div = $('<div />');\n                let button = $('<button type=\"button\" class= \"collapse_groups_btn\" id=\"toolbar-toggle-button\" onclick=\"toggleGroups()\">Agrupar Todo</button>');\n                div.append(button);\n                div[0].style.margin = '5px 5px 5px auto';\n                toolbar.append(div);\n            }\n        } catch (err) {\n            console.error(err);\n        }\n    });\n\n</script>",
        "storeOutMessages": true,
        "fwdInMessages": true,
        "resendOnRefresh": true,
        "templateScope": "global",
        "className": "",
        "x": 760,
        "y": 120,
        "wires": [
            []
        ]
    },
    {
        "id": "5f373ce19a1aa290",
        "type": "ui_group",
        "name": "integration 1",
        "tab": "4a9479ab3f4555f0",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": true,
        "className": "group-integracion"
    },
    {
        "id": "25bfd2e62c3836cb",
        "type": "ui_group",
        "name": "Validacion",
        "tab": "b70b0fa3501be886",
        "order": 1,
        "disp": true,
        "width": "6",
        "collapse": true,
        "className": "group-integracion"
    },
    {
        "id": "4a9479ab3f4555f0",
        "type": "ui_tab",
        "name": "client 1",
        "icon": "launch",
        "order": 8,
        "disabled": false,
        "hidden": false
    },
    {
        "id": "b70b0fa3501be886",
        "type": "ui_tab",
        "name": "client 2",
        "icon": "launch",
        "order": 10,
        "disabled": false,
        "hidden": false
    }
]

delete 103 useless config nodes one by one.

FYI you can delete multiple at the same time.

Or rather than importing just start with a separate flow file. Eg node-red testflow.json. Then bin it later

OK, I didn't know that. I don't know how.

Never done that either, but it sounds like a good plan.

Thanks both, I consider myself reprimanded :upside_down_face:

Thanks a lot for that tip!.

Please, if someone can help me about my question i will be pleased and i know that functionality will be useful to someone else.

Regards!

How do you know what nodes need to be generated?

I need to fill up all of it. Every nodes has information and that info is the one that i want to automate. I mean, i don´t want to do it manually node by node and maybe there is a way to pass the information via another way (csv, txt, etc...).

image

Please let me know if it is understood.

There is no simple way that I know of of doing that using node-red-dashboard.
You could write code that edited the flows file, or you could use the node red admin api, but it would not be trivial.

uhm i see..

I should try to do it with only one node and then replicate it among the others.

Do you mean using the node-red api?

I mean the node red admin api Admin API : Node-RED

Ok, any clue on how i can start with the API?.

What about using a template node instead ? (or ui-table)

Sorry how it can be done using template node?. No clue about it.

You are already using template nodes for the css.

Something like:

which produces:

And this can be fully generated, not the tabs however.