Dashboard-2 use local stored icon

Hi,

I want to use a local stored icon in an ui-template or standard widget
I use docker and I have setup in settings.js
httpStatic: '/data/static/', //single static source
i can access my icon with "http://localhost:1880/window-shutter-closing.svg" and it shows up in browser

in my template I have

<script>
...
// this.shutter_icon = "mdi-chevron-double-down";   // this works 
this.shutter_icon = "/window-shutter-closing.svg";";  // ??? does not work
...
</script>

howto use this icon in ui-template or standard widget ?

Hi @RedTom

I don't use DB much (let alone DB2)
but aren't the icons MD icons?

therefore css class based?

Hi,
I have not much experiance with html and css,
DB2 uses Material Design Icons, if you mean this with MD icons

do I need something like this

    .window-shutter-closing-icon {
        --icon_src: url("/window-shutter-closing.svg");
        // --button_background:#4289c1;
    }

I think so yes....

The icons are selected by the class, and the css rules apply the result.
I don't know much about how this all happens..

@joepavitt is one of the devs for DB2, so might be worth getting his point of view on it

I know CSS of course, but not how these Icons libs do their thing

thanks,
ok,
we wait for some response from DB2 devs

Don't know if this helps, but I wanted to use a svg file stored locally in a button node, and this worked for me;

Loaded the locally stored icon using a template node set to css (Single Page) with the following -

.my_icon{
    --icon_src: url("/static/button/my_icon.svg");
    --button_background:#f4900c;
}

Then in the button node, or whatever node that you are using, access the CSS to display the icon.

EDIT - Amended flow attached below;

[{"id":"8fb5da02a6d1981d","type":"ui-button","z":"be4568a984cb4685","group":"48fc19d1161f11e6","name":"","label":"My Icon button","order":3,"width":"3","height":"1","emulateClick":false,"tooltip":"","color":"","bgcolor":"","className":"icon_button my_icon","icon":"","iconPosition":"left","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":340,"y":1840,"wires":[[]]},{"id":"fa864cf3c7b14005","type":"ui-template","z":"be4568a984cb4685","group":"","page":"19eb6d108e9275e2","ui":"","name":"","order":0,"width":0,"height":0,"head":"","format":"/*for all icons define icon url and matching button \nbackground color as css variables.*/\n.my_icon{\n    --icon_src: url(\"/static/button/my_icon.svg\");\n    --button_background:#f4900c;\n}\n\n\n/*change the color of the button to match color of icon. optional*/\n.icon_button button{\n    background-color: var(--button_background);\n}\n/*make the text to support multiline, change the layout to grid with 2 columns*/\n.icon_button .v-btn__content{    \n    white-space: normal;\n    text-wrap:pretty;\n    width:100%;\n    gap:0.25em;\n    display: grid;\n    grid-template-columns: calc(var(--v-btn-height)) 1fr;\n}\n/*add the icon to the button using pseudoelement :: before*/\n.icon_button .v-btn__content::before{\n    content:\"\";\n    background-image: var(--icon_src);\n    min-height:calc(var(--v-btn-height));\n    min-width:calc(var(--v-btn-height));\n}","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"page:style","className":"","x":330,"y":1800,"wires":[[]]},{"id":"48fc19d1161f11e6","type":"ui-group","name":"Test","page":"19eb6d108e9275e2","width":"20","height":"13","order":-1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"19eb6d108e9275e2","type":"ui-page","name":"Examples","ui":"ae3d4aeb3f977a90","path":"/examples","icon":"","layout":"notebook","theme":"a965ccfef139317a","order":3,"className":"","visible":"true","disabled":"false"},{"id":"ae3d4aeb3f977a90","type":"ui-base","name":"Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"temporary"},{"id":"a965ccfef139317a","type":"ui-theme","name":"Default","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]
2 Likes

Hi,
thanks but still got it not working

my NodeRed start from log

2024-05-28 22:54:45 Welcome to Node-RED
2024-05-28 22:54:45 ===================
2024-05-28 22:54:45
2024-05-28 22:54:45 28 May 20:54:45 - [info] Node-RED version: v4.0.0-beta.3-1
2024-05-28 22:54:45 28 May 20:54:45 - [info] Node.js version: v20.13.1
2024-05-28 22:54:45 28 May 20:54:45 - [info] Linux 5.10.16.3-microsoft-standard-WSL2 x64 LE
2024-05-28 22:54:45 28 May 20:54:45 - [info] Loading palette nodes
2024-05-28 22:54:49 28 May 20:54:49 - [info] Settings file : /data/settings.js
2024-05-28 22:54:49 28 May 20:54:49 - [info] HTTP Static : /data/static > /
2024-05-28 22:54:49 28 May 20:54:49 - [info] Context store : 'default' [module=memory]
2024-05-28 22:54:49 28 May 20:54:49 - [info] User directory : /data
2024-05-28 22:54:49 28 May 20:54:49 - [warn] Projects disabled : editorTheme.projects.enabled=false
2024-05-28 22:54:49 28 May 20:54:49 - [info] Flows file : /data/flows.json
2024-05-28 22:54:49 28 May 20:54:49 - [info] Server now running at http://127.0.0.1:1880/

I set template node to CSS single page with

.my_icon {
/*    --icon_src: url("/static/button/my_icon.svg");*/
/*    --icon_src: url("window-shutter-closing.svg");*/
/*    --icon_src: url("/window-shutter-closing.svg"); */
    --icon_src: url("/static/window-shutter-closing.svg"); 
    --button_background:#f4900c;
}

and button to

tried different url's but none worked

what is the additional "icon_button" before the class name ?

button and css class

[
    {
        "id": "8cace3cc9f8eacd3",
        "type": "ui-button",
        "z": "be85a1a7964e5e68",
        "group": "0789716f7ee3c76d",
        "name": "",
        "label": "buttonUserIcon",
        "order": 7,
        "width": "2",
        "height": "1",
        "emulateClick": false,
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "className": "icon_button my_icon",
        "icon": "",
        "iconPosition": "left",
        "payload": "",
        "payloadType": "str",
        "topic": "topic",
        "topicType": "msg",
        "x": 340,
        "y": 1500,
        "wires": [
            []
        ]
    },
    {
        "id": "62dd1b2512a5262b",
        "type": "ui-template",
        "z": "be85a1a7964e5e68",
        "page": "be7dc60180b9862f",
        "ui": "91ac914334bc2f74",
        "name": "icon_button",
        "order": 0,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "\n   \n.my_icon {\n/*    --icon_src: url(\"/static/button/my_icon.svg\");*/\n/*    --icon_src: url(\"window-shutter-closing.svg\");*/\n/*    --icon_src: url(\"/window-shutter-closing.svg\"); */\n    --icon_src: url(\"/static/window-shutter-closing.svg\"); \n    --button_background:#f4900c;\n}",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "page:style",
        "className": "",
        "x": 330,
        "y": 1540,
        "wires": [
            []
        ]
    },
    {
        "id": "0789716f7ee3c76d",
        "type": "ui-group",
        "name": "Lights",
        "page": "be7dc60180b9862f",
        "width": "12",
        "height": "1",
        "order": 7,
        "showTitle": true,
        "className": "",
        "visible": "false",
        "disabled": "false"
    },
    {
        "id": "be7dc60180b9862f",
        "type": "ui-page",
        "name": "Start",
        "ui": "91ac914334bc2f74",
        "path": "/pageStart",
        "icon": "home",
        "layout": "grid",
        "theme": "713e504734925435",
        "order": 1,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "91ac914334bc2f74",
        "type": "ui-base",
        "name": "My Dashboard",
        "path": "/dashboard",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "navigationStyle": "default"
    },
    {
        "id": "713e504734925435",
        "type": "ui-theme",
        "name": "HN Theme",
        "colors": {
            "surface": "#5c5c5c",
            "primary": "#0094ce",
            "bgPage": "#383838",
            "groupBg": "#4f4f4f",
            "groupOutline": "#858585"
        },
        "sizes": {
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px"
        }
    }
]

Sorry, copy/paste error - my bad :astonished:
Try -

[{"id":"8fb5da02a6d1981d","type":"ui-button","z":"be4568a984cb4685","group":"48fc19d1161f11e6","name":"","label":"My Icon button","order":3,"width":"3","height":"1","emulateClick":false,"tooltip":"","color":"","bgcolor":"","className":"icon_button my_icon","icon":"","iconPosition":"left","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":340,"y":1840,"wires":[[]]},{"id":"fa864cf3c7b14005","type":"ui-template","z":"be4568a984cb4685","group":"","page":"19eb6d108e9275e2","ui":"","name":"","order":0,"width":0,"height":0,"head":"","format":"/*for all icons define icon url and matching button \nbackground color as css variables.*/\n.my_icon{\n    --icon_src: url(\"/static/button/my_icon.svg\");\n    --button_background:#f4900c;\n}\n\n\n/*change the color of the button to match color of icon. optional*/\n.icon_button button{\n    background-color: var(--button_background);\n}\n/*make the text to support multiline, change the layout to grid with 2 columns*/\n.icon_button .v-btn__content{    \n    white-space: normal;\n    text-wrap:pretty;\n    width:100%;\n    gap:0.25em;\n    display: grid;\n    grid-template-columns: calc(var(--v-btn-height)) 1fr;\n}\n/*add the icon to the button using pseudoelement :: before*/\n.icon_button .v-btn__content::before{\n    content:\"\";\n    background-image: var(--icon_src);\n    min-height:calc(var(--v-btn-height));\n    min-width:calc(var(--v-btn-height));\n}","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"page:style","className":"","x":330,"y":1800,"wires":[[]]},{"id":"48fc19d1161f11e6","type":"ui-group","name":"Test","page":"19eb6d108e9275e2","width":"20","height":"13","order":-1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"19eb6d108e9275e2","type":"ui-page","name":"Examples","ui":"ae3d4aeb3f977a90","path":"/examples","icon":"","layout":"notebook","theme":"a965ccfef139317a","order":3,"className":"","visible":"true","disabled":"false"},{"id":"ae3d4aeb3f977a90","type":"ui-base","name":"Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"temporary"},{"id":"a965ccfef139317a","type":"ui-theme","name":"Default","colors":{"surface":"#ffffff","primary":"#0094ce","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]

Thanks,

i replaced the icon name with my icon and it works.
but I must say, i do not understand much how this works :roll_eyes:

i also tried it with a switch node, but I then get either one or two icons ?

and in the ui-template ???

Within a ui-template, we have access to all Vuetify components, which includes a v-icon (docs) - easy option here is to just use on of the included mdi- icons from the MDI Library.

If you're set on your own icon, then @Paul-Reed's approach is best as you can utilise CSS for any widget here.

Hi,

thanks, but sorry need some more help,

I made a template window shutter control in which I want to replace some of the the mdi-icons with local stored modified icons.
The icons change dynamically with the state of the control.

this are the nodes which I use to test the control (includes simulation of the shutter responses)

[
    {
        "id": "d703d7c03729cecd",
        "type": "ui-template",
        "z": "be85a1a7964e5e68",
        "group": "1b676f206101966b",
        "name": "shuttercontrol room1",
        "order": 0,
        "width": "1",
        "height": "1",
        "head": "",
        "format": "<template>\n    <v-btn class=\"button_Shutter\" ref=\"button\" stacked @click=\"clicked\">\n        <div class=\"title_Shutter\">{{labellevel}}</div>\n        <v-icon class=\"icon_Shutter\" :color=\"iconcolor\" ref=\"icon\">{{shutter_icon}}</v-icon>\n    </v-btn>\n</template>\n\n\n<script>\n    export default {\n        data() {\n            return {\n                value: \"OFF\",\n                label: \"Room1\",\n                labellevel: \"Room1\",\n                shutter_icon: \"mdi-window-shutter-open\",\n                iconcolor: \"yellow\",\n                state: \"open\",\n                operationState: \"STOPPED\",\n                level: 0,       // level=0=closed, level=1=open\n                shutterstate: \"closed\",\n                timerID: 0,\n                clicked_mem: false,\n            };\n        },\n        methods: {\n            singleclick: function ()  {\n                this.send({ \"log\": \"singleclick\"});\n            },\n            doubleclick: function ()  {\n                this.send({ \"log\": \"doubleclick\"});\n            },\n            tofunction: function ()  {\n                this.close_shutter();\n                this.clicked_mem=false;\n            },\n           \n            \n            clicked: function () \n            {\n                if(this.operationState==\"MOVING\")\n                {\n                    this.stop_shutter();\n                }\n                else\n                {\n                    switch(this.shutterstate)\n                    {\n                    case \"open\":\n                        this.close_shutter();\n                        break;\n                    case \"closed\":\n                        this.open_shutter();\n                        break;\n                    case \"between\":\n                        if(this.clicked_mem==false)    \n                        {   \n                            this.timerID=setTimeout(this.tofunction, 1000);                \n                            this.clicked_mem=true;\n                        }\n                        else {\n                            clearTimeout(this.timerID);\n                            this.clicked_mem=false;\n                            this.open_shutter();\n                        }\n                    break;\n                    }\n                }\n                \n            },\n            close_shutter: function () {\n                this.send({ payload: 'close' });\n            },\n            open_shutter: function () {\n                this.send({ payload: 'open' });\n            },\n            stop_shutter: function () {\n                this.send({ payload: 'stop' });\n            },\n        },\n        watch: {\n            operationState: function()\n            {\n                switch(this.operationState)\n                {\n                    case \"STOPPED\":\n                        if(this.level==1) {\n                            this.iconcolor=\"yellow\";\n                            this.shutter_icon = \"mdi-window-shutter-open\";\n                            this.labellevel=this.label;\n                            this.shutterstate=\"open\";\n\n                        }\n                        else if(this.level==0) {\n                            this.iconcolor=\"grey\";\n                            this.shutter_icon = \"mdi-window-shutter\";\n                            this.labellevel=this.label;\n                            this.shutterstate=\"closed\";\n                        }\n                        else {\n                            this.iconcolor=\"blue\";\n                            this.shutter_icon = \"mdi-window-shutter-open\";\n                            this.labellevel=this.label+\" \"+this.level*100+\"%\";\n                            this.shutterstate=\"between\";\n                        }\n                        this.dirup=false;\n                        break;\n                    case \"MOVING\":\n                        if(this.level==0)   // closing\n                        {\n                            this.shutter_icon = \"mdi-chevron-double-down\";\n                            //this.shutter_icon = \"my_icon_2\";\n                            this.iconcolor=\"orange\";          \n                        }\n                        else \n                        if(this.level==1)   // opening\n                        {\n                           this.shutter_icon = \"mdi-chevron-double-up\";\n                           this.iconcolor=\"orange\";          \n                        }\n                        \n\n                        break;\n                }\n            },\n            msg: function(){\n                if(this.msg.payload != undefined){\n                    if(this.msg.payload.state != undefined){\n                        if(this.msg.payload.state.operationState != undefined){\n                            this.operationState=this.msg.payload.state.operationState;\n                        }\n                        if(this.msg.payload.state.level != undefined){\n                            this.level=this.msg.payload.state.level;\n                          \n                        }\n                    }\n                }\n                if(this.msg.label!= undefined)\n                {\n                    this.label=this.msg.label;\n                    this.labellevel=this.label;\n                }\n                \n                if(this.msg.color!= undefined)\n                {\n                    this.iconcolor=this.msg.color;\n                }\n            }\n        },\n    }\n</script>\n\n<style>\n    .button_Shutter {\n        display: grid;\n        margin: auto;\n        height: 75px;\n        width: 75px;\n        background-color: #4F4F4F;\n        color: #000000;\n        border: 1px solid #000000;\n        font-size: 14px;\n        border-radius: 12px;\n    }\n\n    .title_Shutter {\n        color: white;\n    }\n\n/*\n    .icon_Shutter {\n        font-size: 40px;\n    }\n*/\n\n\n\n    /*for all icons define icon url and matching button \nbackground color as css variables.*/\n.my_icon_2{\n    /*--icon_src: url(\"/static/button/my_icon.svg\");*/\n    --icon_src: url(\"/window-shutter-closing.svg\");\n    --button_background:#f4900c;\n}\n\n\n/*change the color of the button to match color of icon. optional*/\n.icon_button button{\n    background-color: var(--button_background);\n}\n/*make the text to support multiline, change the layout to grid with 2 columns*/\n/*\n.icon_button .v-btn__content{    \n    white-space: normal;\n    text-wrap:pretty;\n    width:100%;\n    gap:0.25em;\n    display: grid;\n    grid-template-columns: calc(var(--v-btn-height)) 1fr;\n}\n*/\n\n/*add the icon to the button using pseudoelement :: before*/\n/*\n.icon_button .v-btn__content::before{\n    content:\"\";\n    background-image: var(--icon_src);\n    min-height:calc(var(--v-btn-height));\n    min-width:calc(var(--v-btn-height));\n}\n*/\n.icon_Shutter .v-icon__content::before{\n    content:\"\";\n    background-image: var(--icon_src);\n    min-height:calc(var(--v-icon-height));\n    min-width:calc(var(--v-icon-height));\n}\n\n</style>",
        "storeOutMessages": true,
        "passthru": false,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 620,
        "y": 1180,
        "wires": [
            [
                "7018959cbe43ea14",
                "d3a73654b65867f2",
                "8d23580f59ae66a7"
            ]
        ]
    },
    {
        "id": "7018959cbe43ea14",
        "type": "debug",
        "z": "be85a1a7964e5e68",
        "name": "debug 27",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 860,
        "y": 1140,
        "wires": []
    },
    {
        "id": "d3a73654b65867f2",
        "type": "function",
        "z": "be85a1a7964e5e68",
        "name": "simulate end of movement",
        "func": "/*\n{\n    \"path\": \"/devices/hdm:ZigBee:6c5cb1fffe7540eb/services/ShutterControl\",\n        \"operations\": [\"decrementOpenLevel\", \"incrementOpenLevel\"],\n        \"@type\": \"DeviceServiceData\",\n        \"id\": \"ShutterControl\",\n        \"state\": {\n            \"delayCompensationTime\": 0.2, \n            \"referenceMovingTimes\": { \"movingTimeBottomToTopInMillis\": 19300, \"movingTimeTopToBottomInMillis\": 19300 },\n            \"endPositionAutoDetect\": true, \n            \"delayCompensationSupported\": true, \n            \"level\": 0,\n            \"@type\":\"shutterControlState\",\n            \"operationState\": \"MOVING\",\n            \"endPositionSupported\": true, \n            \"automaticDelayCompensation\": true,\n            \"calibrated\": true\n        }, \n    \"deviceId\": \"hdm:ZigBee:6c5cb1fffe7540eb\"\n}\n*/\n\nif (this.msg.payload == undefined) {\n    return;\n}\n\n\n\nvar newMsg = { \"payload\": null };\n//newMsg.payload=\"test\";\n//return newMsg\n\nif (msg.payload == \"stop\") {\n    newMsg.reset = true;\n}\nelse {\n\n\n    newMsg.payload =\n    {\n        \"path\": \"/devices/hdm:ZigBee:6c5cb1fffe7540eb/services/ShutterControl\",\n        \"operations\": [\"decrementOpenLevel\", \"incrementOpenLevel\"],\n        \"@type\": \"DeviceServiceData\",\n        \"id\": \"ShutterControl\",\n        \"state\": {\n            \"delayCompensationTime\": 0.2,\n            \"referenceMovingTimes\": { \"movingTimeBottomToTopInMillis\": 19300, \"movingTimeTopToBottomInMillis\": 19300 },\n            \"endPositionAutoDetect\": true,\n            \"delayCompensationSupported\": true,\n            \"level\": 0,\n            \"@type\": \"shutterControlState\",\n            \"operationState\": \"STOPPED\",\n            \"endPositionSupported\": true,\n            \"automaticDelayCompensation\": true,\n            \"calibrated\": true\n        },\n        \"deviceId\": \"hdm:ZigBee:6c5cb1fffe7540eb\"\n    };\n\n\n    if (msg.payload == \"close\") {\n        newMsg.payload.state.operationState = \"STOPPED\"\n        newMsg.payload.state.level = 0;\n    }\n    if (msg.payload == \"open\") {\n        newMsg.payload.state.operationState = \"STOPPED\"\n        newMsg.payload.state.level = 1;\n    }\n\n\n}\nreturn newMsg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 920,
        "y": 1180,
        "wires": [
            [
                "c2ee16235c4fd8f4"
            ]
        ]
    },
    {
        "id": "5305e43c8cd9a32c",
        "type": "debug",
        "z": "be85a1a7964e5e68",
        "name": "debug 28",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1520,
        "y": 1180,
        "wires": []
    },
    {
        "id": "8d23580f59ae66a7",
        "type": "function",
        "z": "be85a1a7964e5e68",
        "name": "simulate start of movement",
        "func": "/*\n{\n    \"path\": \"/devices/hdm:ZigBee:6c5cb1fffe7540eb/services/ShutterControl\",\n        \"operations\": [\"decrementOpenLevel\", \"incrementOpenLevel\"],\n        \"@type\": \"DeviceServiceData\",\n        \"id\": \"ShutterControl\",\n        \"state\": {\n            \"delayCompensationTime\": 0.2, \n            \"referenceMovingTimes\": { \"movingTimeBottomToTopInMillis\": 19300, \"movingTimeTopToBottomInMillis\": 19300 },\n            \"endPositionAutoDetect\": true, \n            \"delayCompensationSupported\": true, \n            \"level\": 0,\n            \"@type\":\"shutterControlState\",\n            \"operationState\": \"MOVING\",\n            \"endPositionSupported\": true, \n            \"automaticDelayCompensation\": true,\n            \"calibrated\": true\n        }, \n    \"deviceId\": \"hdm:ZigBee:6c5cb1fffe7540eb\"\n}\n*/\n\n\n\nif(this.msg.payload == undefined){\n    return;\n}\n\nvar newMsg={\"payload\":null};\n\nnewMsg.payload=\n{\n    \"path\": \"/devices/hdm:ZigBee:6c5cb1fffe7540eb/services/ShutterControl\",\n    \"operations\": [\"decrementOpenLevel\", \"incrementOpenLevel\"],\n    \"@type\": \"DeviceServiceData\",\n    \"id\": \"ShutterControl\",\n    \"state\": {\n        \"delayCompensationTime\": 0.2,\n        \"referenceMovingTimes\": { \"movingTimeBottomToTopInMillis\": 19300, \"movingTimeTopToBottomInMillis\": 19300 },\n        \"endPositionAutoDetect\": true,\n        \"delayCompensationSupported\": true,\n        \"level\": 0,\n        \"@type\": \"shutterControlState\",\n        \"operationState\": \"MOVING\",\n        \"endPositionSupported\": true,\n        \"automaticDelayCompensation\": true,\n        \"calibrated\": true\n    },\n    \"deviceId\": \"hdm:ZigBee:6c5cb1fffe7540eb\"\n};\n\nif (msg.payload == \"close\")\n{\n    newMsg.payload.state.operationState = \"MOVING\"\n    newMsg.payload.state.level=0\n}\nif (msg.payload == \"open\")\n{\n    newMsg.payload.state.operationState = \"MOVING\"\n    newMsg.payload.state.level=1\n}\nif (msg.payload == \"stop\") {\n    newMsg.payload.state.operationState = \"STOPPED\"\n    newMsg.payload.state.level=0.5\n}\nreturn newMsg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 920,
        "y": 1260,
        "wires": [
            [
                "d703d7c03729cecd",
                "1119d61bc3d6ac3e"
            ]
        ]
    },
    {
        "id": "c2ee16235c4fd8f4",
        "type": "delay",
        "z": "be85a1a7964e5e68",
        "name": "",
        "pauseType": "delay",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 1140,
        "y": 1180,
        "wires": [
            [
                "d703d7c03729cecd",
                "1139a83f5c6cd6b3"
            ]
        ]
    },
    {
        "id": "bfa7290d568fe058",
        "type": "comment",
        "z": "be85a1a7964e5e68",
        "name": "level=0=closed, level=1=open",
        "info": "",
        "x": 580,
        "y": 1140,
        "wires": []
    },
    {
        "id": "1139a83f5c6cd6b3",
        "type": "function",
        "z": "be85a1a7964e5e68",
        "name": "function 137",
        "func": "\nvar newMsg = {\n    \"payload\":\n    {\n        \"operationState\": msg.payload.state.operationState,\n        \"level\": msg.payload.state.level\n    }\n}\n\nreturn newMsg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1350,
        "y": 1180,
        "wires": [
            [
                "5305e43c8cd9a32c"
            ]
        ]
    },
    {
        "id": "1119d61bc3d6ac3e",
        "type": "function",
        "z": "be85a1a7964e5e68",
        "name": "function 138",
        "func": "\nvar newMsg = {\n    \"payload\":\n    {\n        \"operationState\": msg.payload.state.operationState,\n        \"level\": msg.payload.state.level\n    }\n}\n\nreturn newMsg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1350,
        "y": 1260,
        "wires": [
            [
                "e2ddb08a302c4dc2"
            ]
        ]
    },
    {
        "id": "e2ddb08a302c4dc2",
        "type": "debug",
        "z": "be85a1a7964e5e68",
        "name": "debug 31",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 1520,
        "y": 1260,
        "wires": []
    },
    {
        "id": "1b676f206101966b",
        "type": "ui-group",
        "name": "testgroup2",
        "page": "be7dc60180b9862f",
        "width": "6",
        "height": "1",
        "order": 3,
        "showTitle": false,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "be7dc60180b9862f",
        "type": "ui-page",
        "name": "Start",
        "ui": "91ac914334bc2f74",
        "path": "/pageStart",
        "icon": "home",
        "layout": "grid",
        "theme": "713e504734925435",
        "order": 2,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "91ac914334bc2f74",
        "type": "ui-base",
        "name": "My Dashboard",
        "path": "/dashboard",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "navigationStyle": "default"
    },
    {
        "id": "713e504734925435",
        "type": "ui-theme",
        "name": "HN Theme",
        "colors": {
            "surface": "#5c5c5c",
            "primary": "#0094ce",
            "bgPage": "#383838",
            "groupBg": "#4f4f4f",
            "groupOutline": "#858585"
        },
        "sizes": {
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px"
        }
    }
]

but to make it easier, I made an example toggle button, which changes the appended icon with its state.
I added the classes to the button and get a static local stored icon prepended to the button,
but I want to change the appended icons dynamically with local stored icons.

Screenshot-button-icon

[
    {
        "id": "8fb5da02a6d1981d",
        "type": "ui-button",
        "z": "675c06697bd9d35c",
        "group": "48fc19d1161f11e6",
        "name": "",
        "label": "My Icon button (Paul-Reed)",
        "order": 3,
        "width": "4",
        "height": "1",
        "emulateClick": false,
        "tooltip": "",
        "color": "",
        "bgcolor": "",
        "className": "icon_button my_icon",
        "icon": "",
        "iconPosition": "left",
        "payload": "",
        "payloadType": "str",
        "topic": "topic",
        "topicType": "msg",
        "x": 220,
        "y": 160,
        "wires": [
            []
        ]
    },
    {
        "id": "fa864cf3c7b14005",
        "type": "ui-template",
        "z": "675c06697bd9d35c",
        "group": "",
        "page": "19eb6d108e9275e2",
        "ui": "",
        "name": "",
        "order": 0,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "/*for all icons define icon url and matching button \nbackground color as css variables.*/\n.my_icon{\n    /*--icon_src: url(\"/static/button/my_icon.svg\");*/\n    --icon_src: url(\"/window-shutter-closing.svg\");\n    --button_background:#f4900c;\n}\n\n\n/*change the color of the button to match color of icon. optional*/\n.icon_button button{\n    background-color: var(--button_background);\n}\n/*make the text to support multiline, change the layout to grid with 2 columns*/\n.icon_button .v-btn__content{    \n    white-space: normal;\n    text-wrap:pretty;\n    width:100%;\n    gap:0.25em;\n    display: grid;\n    grid-template-columns: calc(var(--v-btn-height)) 1fr;\n}\n/*add the icon to the button using pseudoelement :: before*/\n.icon_button .v-btn__content::before{\n    content:\"\";\n    background-image: var(--icon_src);\n    min-height:calc(var(--v-btn-height));\n    min-width:calc(var(--v-btn-height));\n}",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "page:style",
        "className": "",
        "x": 170,
        "y": 200,
        "wires": [
            []
        ]
    },
    {
        "id": "459d628e7e182030",
        "type": "ui-template",
        "z": "675c06697bd9d35c",
        "group": "48fc19d1161f11e6",
        "name": "my ui-template Button",
        "order": 0,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "<template>\n    <div>\n        <v-btn class=\"icon_button my_icon\" :append-icon=\"shutter_icon\" @click=\"onClick()\">my ui-template button</v-btn>\n    </div>\n</template>\n\n\n<script>\n//          <v-btn class=\"icon_button my_icon\" :append-icon=\"shutter_icon\" @click=\"onClick()\">IconButton</v-btn>\n    export default {\n        data() {\n            // define variables available component-wide\n            // (in <template> and component functions)\n            return {\n                count: 0,\n                state: false,\n                shutter_icon: \"mdi-window-shutter-open\"\n            }\n        },\n        methods: {\n            // expose a method to our <template> and Vue Application\n            onClick: function () {\n                this.count++\n                if(this.state==false)\n                {\n                    this.shutter_icon=\"mdi-window-shutter-open\";\n                    //this.shutter_icon=\"my_icon\";  \n                    this.state=true;    \n                    \n                }\n                else\n                {\n                    this.shutter_icon=\"mdi-window-shutter\";\n                    this.state=false;    \n                }\n                this.send({payload: this.state});\n            }\n        },\n    }\n</script>\n<style>\n\n</style>",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 500,
        "y": 160,
        "wires": [
            [
                "b089902018dfe292"
            ]
        ]
    },
    {
        "id": "b089902018dfe292",
        "type": "debug",
        "z": "675c06697bd9d35c",
        "name": "debug 34",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 720,
        "y": 160,
        "wires": []
    },
    {
        "id": "48fc19d1161f11e6",
        "type": "ui-group",
        "name": "Test",
        "page": "19eb6d108e9275e2",
        "width": "20",
        "height": "13",
        "order": -1,
        "showTitle": true,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "19eb6d108e9275e2",
        "type": "ui-page",
        "name": "Examples",
        "ui": "91ac914334bc2f74",
        "path": "/examples",
        "icon": "",
        "layout": "notebook",
        "theme": "a965ccfef139317a",
        "order": 5,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "91ac914334bc2f74",
        "type": "ui-base",
        "name": "My Dashboard",
        "path": "/dashboard",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "navigationStyle": "default"
    },
    {
        "id": "a965ccfef139317a",
        "type": "ui-theme",
        "name": "Default",
        "colors": {
            "surface": "#ffffff",
            "primary": "#0094ce",
            "bgPage": "#eeeeee",
            "groupBg": "#ffffff",
            "groupOutline": "#cccccc"
        },
        "sizes": {
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px"
        }
    }
]

Hi,

I got by button working with local stored icons, changing dynamically.
I don't know if it's well programmed.

Screenshot-buttons

flows(21).json (4.3 KB)

[{"id":"ab5afb9fac61b28b","type":"ui-template","z":"be4568a984cb4685","group":"8c1ad7fbb760fac2","name":"my ui-template Button 1","order":1,"width":"1","height":"1","head":"","format":"<template>\n    <div class=\"container_shutter\">\n        <div class=\"title_shutter\">{{labellevel}}</div>\n        <v-btn class=\"button_shutter\" size=\"small\" :color=\"icon_color\" @click=\"onClick()\" icon>\n            <v-img :src=\"shutter_icon\" height=32 width=32>  \n        </v-btn>\n    </div>\n</template>\n\n\n<script>\n    export default {\n        data() {\n            // define variables available component-wide\n            // (in <template> and component functions)\n            return {\n                state: \"open\",\n                shutter_icon: \"/window-shutter-closing.svg\",\n                labellevel: \"Room 1\",\n                icon_color: \"yellow\"\n\n            }\n        },\n        methods: {\n            // expose a method to our <template> and Vue Application\n            onClick: function () {\n                switch(this.state)\n                {\n                    case \"open\":\n                        this.state=\"closing\";\n                        this.shutter_icon=\"/window-shutter-closing.svg\";  \n                        this.icon_color=\"blue\"\n                        break;\n                    case \"closing\":\n                        this.state=\"closed\";\n                        this.shutter_icon=\"/window-shutter.svg\";  \n                        this.icon_color=\"grey\";\n                        break;\n                    case \"closed\":\n                        this.state=\"openup\";\n                        this.shutter_icon=\"/window-shutter-openup.svg\";  \n                        this.icon_color=\"orange\";\n                        break;\n                    case \"openup\":\n                        this.state=\"open\";\n                        this.shutter_icon=\"/window-shutter-open.svg\";  \n                        this.icon_color=\"yellow\";\n                        break;\n\n                }\n                 this.send({payload: this.state});\n            }\n        },\n    }\n</script>\n\n\n<style>\n    .container_shutter {\n    display: grid;\n    border: 1px solid #000000;\n    }\n    \n    .title_shutter {\n    margin: auto;\n    color: white;\n    }\n    \n    .button_shutter {\n    display: grid;\n    margin: auto;\n    border: 1px solid #000000;\n    border-radius: 12px;\n    font-size: 14px;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":270,"y":1820,"wires":[[]]},{"id":"8c1ad7fbb760fac2","type":"ui-group","name":"group1","page":"2f76f5e68d2a1098","width":"6","height":"1","order":-1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"2f76f5e68d2a1098","type":"ui-page","name":"test","ui":"ae3d4aeb3f977a90","path":"/page4","icon":"home","layout":"grid","theme":"713e504734925435","order":5,"className":"","visible":true,"disabled":"false"},{"id":"ae3d4aeb3f977a90","type":"ui-base","name":"Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"temporary"},{"id":"713e504734925435","type":"ui-theme","name":"HN Theme","colors":{"surface":"#5c5c5c","primary":"#0094ce","bgPage":"#383838","groupBg":"#4f4f4f","groupOutline":"#858585"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]
1 Like

Good to see that you are getting somewhere with static icons :smiley:
I've just tried your flow, but noticed that;

  1. If the browser is re-opened or refreshed, the button's state is not preserved, and is out of sync with it's latest output msg.
  2. I'm not seeing the label "Room 1".

@RedTom - A better way of posting flows in the forum is to paste your flow as compact json, and surround your flow with three backticks (also known as a left quote or backquote ```)
It enables users to look at, or import your flow without having to download it, open it in a viewer, and then copy the flow text.

``` 
   flow goes here 
```

I've added the flow to your post above in that format so that you can see the difference.

See this post for more details - How to share code or flow json

Hi,

@Paul-Reed

I used backticks before, but I always get a big code block with scrollbars.
How do you get this code block with one line ?
OK, nevertheless I will use backticks again :slightly_smiling_face:.

The label "Room 1" is in the stored in the variable "labellevel".
I want to show here additional information when the shutter is not closed fully, e.g. "Room 1 25%"

I have seen that others also have the problem, that the state of information is not kept after refresh.
There are some posts about it, but I do not yet understand much ("replay of messages ???") :roll_eyes:.
Even if you use the default example template, after refresh the count is 0.

Maybe this is at the moment not a problem for me, because when I navigate to the page, i detect with "event" a pageview message, then I get the shutter states from hardware, which initialize the shutter controls to proper state.

When you export the flow, ensure that you select compact - as circled in red below.

clip

Ah, OK. It was just an observation, It's good that you have found a solution for your problem :+1:

Hi,

@Paul-Reed , thanks

after getting a little bit more experience what the browser does on refresh,
and how the template works, I changed the template button code.
It now keeps the different states after refresh.

Screenshot-button

[{"id":"459d628e7e182030","type":"ui-template","z":"675c06697bd9d35c","group":"574599296b7cf11f","name":"my ui-template Button 1","order":1,"width":"1","height":"1","head":"","format":"<template>\n    <div class=\"container_shutter\">\n        <div class=\"title_shutter\">{{labellevel}}</div>\n        <v-btn class=\"button_shutter\" size=\"small\" :color=\"icon_color\" @click=\"onClick()\" icon>\n            <v-img :src=\"shutter_icon\" height=32 width=32>  \n        </v-btn>\n    </div>\n</template>\n\n\n<script>\n    export default {\n        data() {\n            // define variables available component-wide\n            // (in <template> and component functions)\n            return {\n                state: \"\",      \n                //state: \"open\",   // !? it does not keep state open,does not enter watch state       \n                shutter_icon: \"/window-shutter-closing.svg\",\n                labellevel: \"Room 1\",\n                icon_color: \"yellow\",\n            }\n        },\n        watch: {\n            // watch for message\n            msg: function () {\n                console.log(`!!! watch msg: Message received: ${JSON.stringify(this.msg)}`)\n                if(this.msg.payload != undefined) {\n                    this.state=this.msg.payload;\n                }\n            },\n            // watch for any change of state\n            state: function () {\n                console.log(`!!! watch state: state: ${JSON.stringify(this.state)}`);\n                switch(this.state)\n                {\n                case \"open\":\n                    this.shutter_icon=\"/window-shutter-open.svg\";\n                    this.icon_color=\"yellow\"\n                    break;\n                case \"closing\":\n                    this.shutter_icon=\"/window-shutter-closing.svg\";\n                    this.icon_color=\"blue\";\n                    break;\n                case \"closed\":\n                    this.shutter_icon=\"/window-shutter.svg\";\n                    this.icon_color=\"grey\";\n                    break;\n                case \"openup\":\n                    this.shutter_icon=\"/window-shutter-openup.svg\";\n                    this.icon_color=\"orange\";\n                    break;\n                }\n            }\n        },  \n        methods: {\n            // expose a method to our <template> and Vue Application\n            onClick: function () {\n                console.log(`!!! onClick: Message received: ${JSON.stringify(this.msg)}`)\n                switch(this.state)\n                {\n                    case \"open\":\n                        this.state=\"closing\";\n                        break;\n                    case \"closing\":\n                        this.state=\"closed\";\n                        break;\n                    case \"closed\":\n                        this.state=\"openup\";\n                        break;\n                    case \"openup\":\n                        this.state=\"open\";\n                        break;\n                    default:  \n                        this.state=\"open\";\n                        break;\n\n                }\n                this.send({payload: this.state});\n            }\n        },\n    }\n</script>\n\n\n<style>\n    .container_shutter {\n    display: grid;\n    border: 1px solid #000000;\n    }\n    \n    .title_shutter {\n    margin: auto;\n    color: white;\n    }\n    \n    .button_shutter {\n    display: grid;\n    margin: auto;\n    border: 1px solid #000000;\n    border-radius: 12px;\n    font-size: 14px;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":630,"y":280,"wires":[["b089902018dfe292"]]},{"id":"b089902018dfe292","type":"debug","z":"675c06697bd9d35c","name":"debug 34","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":840,"y":280,"wires":[]},{"id":"79c6d05699c37e32","type":"inject","z":"675c06697bd9d35c","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"open","payloadType":"str","x":350,"y":280,"wires":[["459d628e7e182030"]]},{"id":"0f3509c2933094e6","type":"inject","z":"675c06697bd9d35c","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"closed","payloadType":"str","x":350,"y":320,"wires":[["459d628e7e182030"]]},{"id":"513f8efc8d26b70f","type":"inject","z":"675c06697bd9d35c","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"openup","payloadType":"str","x":350,"y":360,"wires":[["459d628e7e182030"]]},{"id":"da050a532a07c74c","type":"inject","z":"675c06697bd9d35c","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"closing","payloadType":"str","x":350,"y":400,"wires":[["459d628e7e182030"]]},{"id":"34e7046ccd59493d","type":"ui-template","z":"675c06697bd9d35c","group":"574599296b7cf11f","name":"my ui-template Button 2","order":2,"width":"1","height":"1","head":"","format":"<template>\n    <div class=\"container_shutter\">\n        <div class=\"title_shutter\">{{labellevel}}</div>\n        <v-btn class=\"button_shutter\" size=\"small\" :color=\"icon_color\" @click=\"onClick()\" icon>\n            <v-img :src=\"shutter_icon\" height=32 width=32>  \n        </v-btn>\n    </div>\n</template>\n\n\n<script>\n    export default {\n        data() {\n            // define variables available component-wide\n            // (in <template> and component functions)\n            return {\n                state: \"\",      \n                //state: \"open\",   // !? it does not keep state open,does not enter watch state       \n                shutter_icon: \"/window-shutter-closing.svg\",\n                labellevel: \"Room 2\",\n                icon_color: \"yellow\",\n            }\n        },\n        watch: {\n            // watch for message\n            msg: function () {\n                console.log(`!!! watch msg: Message received: ${JSON.stringify(this.msg)}`)\n                if(this.msg.payload != undefined) {\n                    this.state=this.msg.payload;\n                }\n            },\n            // watch for any change of state\n            state: function () {\n                console.log(`!!! watch state: state: ${JSON.stringify(this.state)}`);\n                switch(this.state)\n                {\n                case \"open\":\n                    this.shutter_icon=\"/window-shutter-open.svg\";\n                    this.icon_color=\"yellow\"\n                    break;\n                case \"closing\":\n                    this.shutter_icon=\"/window-shutter-closing.svg\";\n                    this.icon_color=\"blue\";\n                    break;\n                case \"closed\":\n                    this.shutter_icon=\"/window-shutter.svg\";\n                    this.icon_color=\"grey\";\n                    break;\n                case \"openup\":\n                    this.shutter_icon=\"/window-shutter-openup.svg\";\n                    this.icon_color=\"orange\";\n                    break;\n                }\n            }\n        },  \n        methods: {\n            // expose a method to our <template> and Vue Application\n            onClick: function () {\n                console.log(`!!! onClick: Message received: ${JSON.stringify(this.msg)}`)\n                switch(this.state)\n                {\n                    case \"open\":\n                        this.state=\"closing\";\n                        break;\n                    case \"closing\":\n                        this.state=\"closed\";\n                        break;\n                    case \"closed\":\n                        this.state=\"openup\";\n                        break;\n                    case \"openup\":\n                        this.state=\"open\";\n                        break;\n                    default:  \n                        this.state=\"open\";\n                        break;\n\n                }\n                this.send({payload: this.state});\n            }\n        },\n    }\n</script>\n\n\n<style>\n    .container_shutter {\n    display: grid;\n    border: 1px solid #000000;\n    }\n    \n    .title_shutter {\n    margin: auto;\n    color: white;\n    }\n    \n    .button_shutter {\n    display: grid;\n    margin: auto;\n    border: 1px solid #000000;\n    border-radius: 12px;\n    font-size: 14px;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":630,"y":320,"wires":[[]]},{"id":"2c7ea21ca52dfe73","type":"ui-template","z":"675c06697bd9d35c","group":"574599296b7cf11f","name":"my ui-template Button 3","order":3,"width":"1","height":"1","head":"","format":"<template>\n    <div class=\"container_shutter\">\n        <div class=\"title_shutter\">{{labellevel}}</div>\n        <v-btn class=\"button_shutter\" size=\"small\" :color=\"icon_color\" @click=\"onClick()\" icon>\n            <v-img :src=\"shutter_icon\" height=32 width=32>  \n        </v-btn>\n    </div>\n</template>\n\n\n<script>\n    export default {\n        data() {\n            // define variables available component-wide\n            // (in <template> and component functions)\n            return {\n                state: \"\",      \n                //state: \"open\",   // !? it does not keep state open,does not enter watch state       \n                shutter_icon: \"/window-shutter-closing.svg\",\n                labellevel: \"Room 3\",\n                icon_color: \"yellow\",\n            }\n        },\n        watch: {\n            // watch for message\n            msg: function () {\n                console.log(`!!! watch msg: Message received: ${JSON.stringify(this.msg)}`)\n                if(this.msg.payload != undefined) {\n                    this.state=this.msg.payload;\n                }\n            },\n            // watch for any change of state\n            state: function () {\n                console.log(`!!! watch state: state: ${JSON.stringify(this.state)}`);\n                switch(this.state)\n                {\n                case \"open\":\n                    this.shutter_icon=\"/window-shutter-open.svg\";\n                    this.icon_color=\"yellow\"\n                    break;\n                case \"closing\":\n                    this.shutter_icon=\"/window-shutter-closing.svg\";\n                    this.icon_color=\"blue\";\n                    break;\n                case \"closed\":\n                    this.shutter_icon=\"/window-shutter.svg\";\n                    this.icon_color=\"grey\";\n                    break;\n                case \"openup\":\n                    this.shutter_icon=\"/window-shutter-openup.svg\";\n                    this.icon_color=\"orange\";\n                    break;\n                }\n            }\n        },  \n        methods: {\n            // expose a method to our <template> and Vue Application\n            onClick: function () {\n                console.log(`!!! onClick: Message received: ${JSON.stringify(this.msg)}`)\n                switch(this.state)\n                {\n                    case \"open\":\n                        this.state=\"closing\";\n                        break;\n                    case \"closing\":\n                        this.state=\"closed\";\n                        break;\n                    case \"closed\":\n                        this.state=\"openup\";\n                        break;\n                    case \"openup\":\n                        this.state=\"open\";\n                        break;\n                    default:  \n                        this.state=\"open\";\n                        break;\n\n                }\n                this.send({payload: this.state});\n            }\n        },\n    }\n</script>\n\n\n<style>\n    .container_shutter {\n    display: grid;\n    border: 1px solid #000000;\n    }\n    \n    .title_shutter {\n    margin: auto;\n    color: white;\n    }\n    \n    .button_shutter {\n    display: grid;\n    margin: auto;\n    border: 1px solid #000000;\n    border-radius: 12px;\n    font-size: 14px;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":630,"y":360,"wires":[[]]},{"id":"a1777c194605532c","type":"ui-template","z":"675c06697bd9d35c","group":"574599296b7cf11f","name":"my ui-template Button 4","order":4,"width":"1","height":"1","head":"","format":"<template>\n    <div class=\"container_shutter\">\n        <div class=\"title_shutter\">{{labellevel}}</div>\n        <v-btn class=\"button_shutter\" size=\"small\" :color=\"icon_color\" @click=\"onClick()\" icon>\n            <v-img :src=\"shutter_icon\" height=32 width=32>  \n        </v-btn>\n    </div>\n</template>\n\n\n<script>\n    export default {\n        data() {\n            // define variables available component-wide\n            // (in <template> and component functions)\n            return {\n                state: \"\",      \n                //state: \"open\",   // !? it does not keep state open,does not enter watch state       \n                shutter_icon: \"/window-shutter-closing.svg\",\n                labellevel: \"Room 4\",\n                icon_color: \"yellow\",\n            }\n        },\n        watch: {\n            // watch for message\n            msg: function () {\n                console.log(`!!! watch msg: Message received: ${JSON.stringify(this.msg)}`)\n                if(this.msg.payload != undefined) {\n                    this.state=this.msg.payload;\n                }\n            },\n            // watch for any change of state\n            state: function () {\n                console.log(`!!! watch state: state: ${JSON.stringify(this.state)}`);\n                switch(this.state)\n                {\n                case \"open\":\n                    this.shutter_icon=\"/window-shutter-open.svg\";\n                    this.icon_color=\"yellow\"\n                    break;\n                case \"closing\":\n                    this.shutter_icon=\"/window-shutter-closing.svg\";\n                    this.icon_color=\"blue\";\n                    break;\n                case \"closed\":\n                    this.shutter_icon=\"/window-shutter.svg\";\n                    this.icon_color=\"grey\";\n                    break;\n                case \"openup\":\n                    this.shutter_icon=\"/window-shutter-openup.svg\";\n                    this.icon_color=\"orange\";\n                    break;\n                }\n            }\n        },  \n        methods: {\n            // expose a method to our <template> and Vue Application\n            onClick: function () {\n                console.log(`!!! onClick: Message received: ${JSON.stringify(this.msg)}`)\n                switch(this.state)\n                {\n                    case \"open\":\n                        this.state=\"closing\";\n                        break;\n                    case \"closing\":\n                        this.state=\"closed\";\n                        break;\n                    case \"closed\":\n                        this.state=\"openup\";\n                        break;\n                    case \"openup\":\n                        this.state=\"open\";\n                        break;\n                    default:  \n                        this.state=\"open\";\n                        break;\n\n                }\n                this.send({payload: this.state});\n            }\n        },\n    }\n</script>\n\n\n<style>\n    .container_shutter {\n    display: grid;\n    border: 1px solid #000000;\n    }\n    \n    .title_shutter {\n    margin: auto;\n    color: white;\n    }\n    \n    .button_shutter {\n    display: grid;\n    margin: auto;\n    border: 1px solid #000000;\n    border-radius: 12px;\n    font-size: 14px;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":630,"y":400,"wires":[[]]},{"id":"574599296b7cf11f","type":"ui-group","name":"group1","page":"2f76f5e68d2a1098","width":"6","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"2f76f5e68d2a1098","type":"ui-page","name":"test","ui":"91ac914334bc2f74","path":"/page4","icon":"home","layout":"grid","theme":"713e504734925435","order":4,"className":"","visible":true,"disabled":"false"},{"id":"91ac914334bc2f74","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"default","titleBarStyle":"default"},{"id":"713e504734925435","type":"ui-theme","name":"HN Theme","colors":{"surface":"#5c5c5c","primary":"#0094ce","bgPage":"#383838","groupBg":"#4f4f4f","groupOutline":"#858585"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]

same control but with mdi icons

[{"id":"a1777c194605532c","type":"ui-template","z":"675c06697bd9d35c","group":"574599296b7cf11f","name":"my ui-template Button 4","order":4,"width":"1","height":"1","head":"","format":"<template>\n    <div class=\"container_shutter\">\n        <div class=\"title_shutter\">{{labellevel}}</div>\n<!--        <v-btn class=\"button_shutter\" size=\"small\" :color=\"icon_color\" @click=\"onClick()\" icon>\n            <v-img :src=\"shutter_icon\" height=32 width=32>  \n        </v-btn>\n-->        \n         <v-btn class=\"button_shutter\" size=\"small\" :color=\"icon_color\" @click=\"onClick()\" :icon=\"shutter_icon\" height=32 width=32>\n         </v-btn>\n    </div>\n</template>\n\n\n<script>\n    export default {\n        data() {\n            // define variables available component-wide\n            // (in <template> and component functions)\n            return {\n                state: \"\",      \n                //state: \"open\",   // !? it does not keep state open,does not enter watch state       \n                shutter_icon: \"/window-shutter-closing.svg\",\n                labellevel: \"Room 4\",\n                icon_color: \"yellow\",\n            }\n        },\n        watch: {\n            // watch for message\n            msg: function () {\n                console.log(`!!! watch msg: Message received: ${JSON.stringify(this.msg)}`)\n                if(this.msg.payload != undefined) {\n                    this.state=this.msg.payload;\n                }\n            },\n            // watch for any change of state\n            state: function () {\n                console.log(`!!! watch state: state: ${JSON.stringify(this.state)}`);\n                switch(this.state)\n                {\n                case \"open\":\n                    //this.shutter_icon=\"/window-shutter-open.svg\";\n                    this.shutter_icon=\"mdi-window-shutter-open\";\n                    this.icon_color=\"yellow\"\n                    break;\n                case \"closing\":\n                    //this.shutter_icon=\"/window-shutter-closing.svg\";\n                    this.shutter_icon=\"mdi-chevron-double-down\";\n                    this.icon_color=\"blue\";\n                    break;\n                case \"closed\":\n                    //this.shutter_icon=\"/window-shutter.svg\";\n                    this.shutter_icon=\"mdi-window-shutter\";\n                    this.icon_color=\"grey\";\n                    break;\n                case \"openup\":\n                    //this.shutter_icon=\"/window-shutter-openup.svg\";\n                    this.shutter_icon=\"mdi-chevron-double-up\";\n                    this.icon_color=\"orange\";\n                    break;\n                }\n            }\n        },  \n        methods: {\n            // expose a method to our <template> and Vue Application\n            onClick: function () {\n                console.log(`!!! onClick: Message received: ${JSON.stringify(this.msg)}`)\n                switch(this.state)\n                {\n                    case \"open\":\n                        this.state=\"closing\";\n                        break;\n                    case \"closing\":\n                        this.state=\"closed\";\n                        break;\n                    case \"closed\":\n                        this.state=\"openup\";\n                        break;\n                    case \"openup\":\n                        this.state=\"open\";\n                        break;\n                    default:  \n                        this.state=\"open\";\n                        break;\n\n                }\n                this.send({payload: this.state});\n            }\n        },\n    }\n</script>\n\n\n<style>\n    .container_shutter {\n    display: grid;\n    border: 1px solid #000000;\n    }\n    \n    .title_shutter {\n    margin: auto;\n    color: white;\n    }\n    \n    .button_shutter {\n    display: grid;\n    margin: auto;\n    border: 1px solid #000000;\n    border-radius: 12px;\n    font-size: 14px;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":630,"y":400,"wires":[[]]},{"id":"574599296b7cf11f","type":"ui-group","name":"group1","page":"2f76f5e68d2a1098","width":"6","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"2f76f5e68d2a1098","type":"ui-page","name":"test","ui":"91ac914334bc2f74","path":"/page4","icon":"home","layout":"grid","theme":"713e504734925435","order":4,"className":"","visible":true,"disabled":"false"},{"id":"91ac914334bc2f74","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"navigationStyle":"default","titleBarStyle":"default"},{"id":"713e504734925435","type":"ui-theme","name":"HN Theme","colors":{"surface":"#5c5c5c","primary":"#0094ce","bgPage":"#383838","groupBg":"#4f4f4f","groupOutline":"#858585"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.