# Dashboard-2 use local stored icon

**URL:** <https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324>\
**Category:** Dashboard\
**Tags:** docker, dashboard-2\
**Created:** [28 May 2024 16:36 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324 "2024-05-28T16:36:03Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![RedTom](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/redtom/32/88014_2.png) [@RedTom](https://discourse.nodered.org/u/RedTom)\
**Post date:** [28 May 2024 16:36 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/1 "2024-05-28T16:36:03Z")

</div>

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](http://localhost:1880/window-shutter-closing.svg)" and it shows up in browser

in my template I have

```auto
<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 ?

---

<div class="post-metadata">

**Author:** ![marcus-j-davies](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/marcus-j-davies/32/103435_2.png) [@marcus-j-davies](https://discourse.nodered.org/u/marcus-j-davies)\
**Post date:** [28 May 2024 16:46 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/2 "2024-05-28T16:46:03Z")

</div>

Hi @RedTom

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

therefore css class based?

---

<div class="post-metadata">

**Author:** ![RedTom](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/redtom/32/88014_2.png) [@RedTom](https://discourse.nodered.org/u/RedTom)\
**Post date:** [28 May 2024 16:54 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/3 "2024-05-28T16:54:08Z")

</div>

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

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

```

---

<div class="post-metadata">

**Author:** ![marcus-j-davies](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/marcus-j-davies/32/103435_2.png) [@marcus-j-davies](https://discourse.nodered.org/u/marcus-j-davies)\
**Post date:** [28 May 2024 17:00 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/4 "2024-05-28T17:00:40Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![RedTom](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/redtom/32/88014_2.png) [@RedTom](https://discourse.nodered.org/u/RedTom)\
**Post date:** [28 May 2024 17:12 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/5 "2024-05-28T17:12:56Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Paul-Reed](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/paul-reed/32/66906_2.png) [@Paul-Reed](https://discourse.nodered.org/u/Paul-Reed)\
**Post date:** [28 May 2024 20:08 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/6 "2024-05-28T20:08:40Z")

</div>

> [@RedTom](#):
>
> howto use this icon in ui-template or standard widget ?

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 -

```auto
.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;

```auto
[{"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"}}]

```

---

<div class="post-metadata">

**Author:** ![RedTom](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/redtom/32/88014_2.png) [@RedTom](https://discourse.nodered.org/u/RedTom)\
**Post date:** [28 May 2024 21:45 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/7 "2024-05-28T21:45:44Z")

</div>

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/](http://127.0.0.1:1880/)

I set template node to CSS single page with

```auto
.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

 ![Screenshot-buttonUserIcon](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/f/9/f92ad8e572e719d425eb1193ec48b1dac2cffce5.jpeg)

tried different url's but none worked

what is the additional "icon\_button" before the class name ?

button and css class

```auto
[
    {
        "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"
        }
    }
]

```

---

<div class="post-metadata">

**Author:** ![Paul-Reed](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/paul-reed/32/66906_2.png) [@Paul-Reed](https://discourse.nodered.org/u/Paul-Reed)\
**Post date:** [28 May 2024 22:22 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/8 "2024-05-28T22:22:30Z")

</div>

Sorry, copy/paste error - my bad 😲  
Try -

```auto
[{"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"}}]

```

---

<div class="post-metadata">

**Author:** ![RedTom](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/redtom/32/88014_2.png) [@RedTom](https://discourse.nodered.org/u/RedTom)\
**Post date:** [28 May 2024 22:47 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/9 "2024-05-28T22:47:47Z")

</div>

Thanks,

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

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

and in the ui-template ???

---

<div class="post-metadata">

**Author:** ![joepavitt](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/joepavitt/32/59722_2.png) [@joepavitt](https://discourse.nodered.org/u/joepavitt)\
**Post date:** [29 May 2024 07:57 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/10 "2024-05-29T07:57:43Z")

</div>

Within a `ui-template`, we have access to all Vuetify components, which includes a `v-icon` ([docs](https://vuetifyjs.com/en/components/icons/#usage)) - easy option here is to just use on of the included `mdi-` icons from the [MDI Library](https://pictogrammers.com/library/mdi/).

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

---

<div class="post-metadata">

**Author:** ![RedTom](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/redtom/32/88014_2.png) [@RedTom](https://discourse.nodered.org/u/RedTom)\
**Post date:** [29 May 2024 10:34 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/11 "2024-05-29T10:34:02Z")

</div>

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)

```auto
[
    {
        "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](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/9/2/92b77c7f8f528bbd5360fe44bded0c5dc31267bc.jpeg)

```auto
[
    {
        "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"
        }
    }
]

```

---

<div class="post-metadata">

**Author:** ![RedTom](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/redtom/32/88014_2.png) [@RedTom](https://discourse.nodered.org/u/RedTom)\
**Post date:** [31 May 2024 08:59 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/12 "2024-05-31T08:59:23Z")

</div>

Hi,

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

![Screenshot-buttons](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/4/3/43eb62981543f67b5933dc9fe069317f964fd121.jpeg)

[flows(21).json](https://discourse.nodered.org/uploads/short-url/pkKQwGevtuIEA3nyIETDcvtgJSC.json) (4.3 KB)

```auto
[{"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"}}]

```

---

<div class="post-metadata">

**Author:** ![Paul-Reed](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/paul-reed/32/66906_2.png) [@Paul-Reed](https://discourse.nodered.org/u/Paul-Reed)\
**Post date:** [31 May 2024 18:01 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/13 "2024-05-31T18:01:39Z")

</div>

Good to see that you are getting somewhere with static icons 😃  
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".

---

<div class="post-metadata">

**Author:** ![Paul-Reed](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/paul-reed/32/66906_2.png) [@Paul-Reed](https://discourse.nodered.org/u/Paul-Reed)\
**Post date:** [31 May 2024 18:10 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/14 "2024-05-31T18:10:08Z")

</div>

@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.

````auto
``` 
   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](https://discourse.nodered.org/t/how-to-share-code-or-flow-json/506)

---

<div class="post-metadata">

**Author:** ![RedTom](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/redtom/32/88014_2.png) [@RedTom](https://discourse.nodered.org/u/RedTom)\
**Post date:** [31 May 2024 19:51 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/15 "2024-05-31T19:51:20Z")

</div>

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 🙂.

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 ???") 🙄.  
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.

---

<div class="post-metadata">

**Author:** ![Paul-Reed](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/paul-reed/32/66906_2.png) [@Paul-Reed](https://discourse.nodered.org/u/Paul-Reed)\
**Post date:** [31 May 2024 20:04 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/16 "2024-05-31T20:04:13Z")

</div>

> [@RedTom](#):
>
> I used backticks before, but I always get a big code block with scrollbars.  
> How do you get this code block with one line ?

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

![clip](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/3/9/392fee135503f5a08aaeaa19fa2ade1d42105e66.jpeg)

> [@RedTom](#):
>
> 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

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

---

<div class="post-metadata">

**Author:** ![RedTom](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/redtom/32/88014_2.png) [@RedTom](https://discourse.nodered.org/u/RedTom)\
**Post date:** [1 June 2024 09:23 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/17 "2024-06-01T09:23:46Z")

</div>

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](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/8/6/867658ed31ee21b8931fe5a78df0ad67eeccc3c9.jpeg)

```auto
[{"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

```auto
[{"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"}}]

```

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [1 July 2024 09:24 UTC](https://discourse.nodered.org/t/dashboard-2-use-local-stored-icon/88324/18 "2024-07-01T09:24:10Z")

</div>

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