Echarts options via msg.ui_update not persistant

Hi All, Hi Colin,

now after doing my first experiements I am quite satisfied with what can be achieved triggering the echarts options via msg.ui_update. However, what I experience is that the options once set do not survive a browser refresh (or closing and opening the browser). Which means, each time I open the browser it is required to re-inject the msg.ui_update.


Fig.1 This is what I want (after a msg.ui_update inject)


Fig. 2 This is what happens after a browser refresh

I have attached a nice example flow which demonstrates my issues:

[
    {
        "id": "23448822b52f0ac1",
        "type": "ui-chart",
        "z": "d219a270aa9d8c20",
        "group": "b6dcd3e8ee87dac6",
        "name": "Room XX",
        "label": "Room XX",
        "order": 1,
        "chartType": "line",
        "category": "topic",
        "categoryType": "msg",
        "xAxisLabel": "Uhrzeit",
        "xAxisProperty": "payload.x",
        "xAxisPropertyType": "msg",
        "xAxisType": "time",
        "xAxisFormat": "",
        "xAxisFormatType": "{HH}:{mm}",
        "xmin": "",
        "xmax": "",
        "yAxisLabel": "Temperatur °C",
        "yAxisProperty": "payload.y",
        "yAxisPropertyType": "msg",
        "ymin": "14",
        "ymax": "28",
        "bins": 10,
        "action": "append",
        "stackSeries": false,
        "pointShape": "circle",
        "pointRadius": "5",
        "showLegend": true,
        "removeOlder": "12",
        "removeOlderUnit": "3600",
        "removeOlderPoints": "",
        "colors": [
            "#0095ff",
            "#f50000",
            "#ffdd00",
            "#f2f2f2",
            "#4dff00",
            "#fb00ff",
            "#2ca02c",
            "#00ffd5",
            "#c5b0d5"
        ],
        "textColor": [
            "#ffffff"
        ],
        "textColorDefault": false,
        "gridColor": [
            "#5c5c5c"
        ],
        "gridColorDefault": false,
        "width": 6,
        "height": "6",
        "className": "",
        "interpolation": "linear",
        "x": 2660,
        "y": 3750,
        "wires": [
            []
        ]
    },
    {
        "id": "72dc649413ed09b2",
        "type": "function",
        "z": "d219a270aa9d8c20",
        "name": "msg.ui_update",
        "func": "msg.payload = {} // create an empty object\n\nmsg.ui_update = {\n  \"chartOptions\": {\n    \"series\": [\n      {\n        \"name\": \"ext Sensor\",\n        \"type\": \"line\",\n        \"areaStyle\": null,\n        \"smooth\": false,\n        \"lineStyle\": {\n          \"color\": \"rgb(64, 156, 255)\",\n          \"width\": 3\n        },\n        \"itemStyle\": {\n          \"color\": \"rgb(64, 156, 255)\"\n        }\n      },\n      {\n        \"name\": \"heat\",\n        \"type\": \"line\",\n        \"symbol\": \"none\",\n        \"areaStyle\": {\n          \"color\": \"rgba(255, 99, 110, 0.5)\"\n        },\n        \"smooth\": false,\n        \"lineStyle\": {\n          \"color\": \"rgb(255, 99, 110)\",\n          \"width\": 0\n        },\n        \"itemStyle\": {\n          \"color\": \"rgb(255, 99, 110)\"\n        },\n        \"tooltip\": {\n          \"show\": false\n        }\n      },\n      {\n        \"name\": \"target\",\n        \"type\": \"line\",\n        \"areaStyle\": null,\n        \"smooth\": false,\n        \"lineStyle\": {\n          \"color\": \"rgb(210, 204, 190)\",\n          \"width\": 3\n        },\n        \"itemStyle\": {\n          \"color\": \"rgb(210, 204, 190)\"\n        }\n      }\n    ]\n  }\n};\n\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2450,
        "y": 3850,
        "wires": [
            [
                "23448822b52f0ac1"
            ]
        ]
    },
    {
        "id": "960cbc5dce84c9f9",
        "type": "inject",
        "z": "d219a270aa9d8c20",
        "name": "Clear Data",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "[]",
        "payloadType": "json",
        "x": 2480,
        "y": 3700,
        "wires": [
            [
                "23448822b52f0ac1"
            ]
        ]
    },
    {
        "id": "033f02d33b10315e",
        "type": "function",
        "z": "d219a270aa9d8c20",
        "name": "payloads & topics",
        "func": "\nconst state = Math.random() < 0.4 ? \"idle\" : \"heat\";\nconst target_temp = 21;\nconst ext_temp = Math.floor(Math.random() * (24 - 18 + 1)) + 18; // random between 18 and 24\n\nlet ext_temp_4_idle;\nlet ext_temp_4_heat;\n\nif (state == \"idle\") {\n    ext_temp_4_idle = ext_temp\n    ext_temp_4_heat = \"null\"\n} else if (state == \"heat\") {\n    ext_temp_4_idle = ext_temp\n    ext_temp_4_heat = ext_temp  \n}\n\nlet msg1 = {}\nmsg1.payload = ext_temp_4_idle\nmsg1.topic = \"ext Sensor\"\n\n\nlet msg2 = {}\nmsg2.payload = ext_temp_4_heat\nmsg2.topic = \"heat\"\n\nlet msg3 = {}\nmsg3.payload = target_temp\nmsg3.topic = \"target\"\n\n\nreturn [msg1,msg2,msg3];",
        "outputs": 3,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1920,
        "y": 3750,
        "wires": [
            [
                "1454e8d7c2a6eccd"
            ],
            [
                "c380f1eb2d7f5725"
            ],
            [
                "c10fb20c658e352d"
            ]
        ]
    },
    {
        "id": "1454e8d7c2a6eccd",
        "type": "delay",
        "z": "d219a270aa9d8c20",
        "name": "",
        "pauseType": "delay",
        "timeout": "0",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 2130,
        "y": 3700,
        "wires": [
            [
                "2db0f5ab0ddeb12c"
            ]
        ]
    },
    {
        "id": "c380f1eb2d7f5725",
        "type": "delay",
        "z": "d219a270aa9d8c20",
        "name": "",
        "pauseType": "delay",
        "timeout": "200",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 2130,
        "y": 3750,
        "wires": [
            [
                "2db0f5ab0ddeb12c"
            ]
        ]
    },
    {
        "id": "c10fb20c658e352d",
        "type": "delay",
        "z": "d219a270aa9d8c20",
        "name": "",
        "pauseType": "delay",
        "timeout": "400",
        "timeoutUnits": "milliseconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 2130,
        "y": 3800,
        "wires": [
            [
                "2db0f5ab0ddeb12c"
            ]
        ]
    },
    {
        "id": "2db0f5ab0ddeb12c",
        "type": "function",
        "z": "d219a270aa9d8c20",
        "name": "quantitize time to nearest 5 s",
        "func": "const now = Date.now();\nconst aligned = Math.round(now / 5000) * 5000; // 5.000 ms = 5 s\nmsg.payload = { x: aligned, y: msg.payload };\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 2420,
        "y": 3750,
        "wires": [
            [
                "23448822b52f0ac1"
            ]
        ]
    },
    {
        "id": "c5fdc2d42ca15846",
        "type": "inject",
        "z": "d219a270aa9d8c20",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "5",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 1710,
        "y": 3750,
        "wires": [
            [
                "033f02d33b10315e"
            ]
        ]
    },
    {
        "id": "9d899f13a56ffbe1",
        "type": "inject",
        "z": "d219a270aa9d8c20",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 2260,
        "y": 3850,
        "wires": [
            [
                "72dc649413ed09b2"
            ]
        ]
    },
    {
        "id": "b6dcd3e8ee87dac6",
        "type": "ui-group",
        "name": "msg.ui_update_NEU",
        "page": "a9b32250ebfa989b",
        "width": 6,
        "height": 1,
        "order": 4,
        "showTitle": true,
        "className": "",
        "visible": "true",
        "disabled": "false",
        "groupType": "default"
    },
    {
        "id": "a9b32250ebfa989b",
        "type": "ui-page",
        "name": "Dashboard 2.0 Examples",
        "ui": "14a551a96b61dade",
        "path": "/templates",
        "icon": "view-dashboard",
        "layout": "grid",
        "theme": "cbe751f3fb4f230f",
        "breakpoints": [
            {
                "name": "Default",
                "px": "0",
                "cols": "3"
            },
            {
                "name": "Tablet",
                "px": "576",
                "cols": "6"
            },
            {
                "name": "Small Desktop",
                "px": "768",
                "cols": "9"
            },
            {
                "name": "Desktop",
                "px": "1024",
                "cols": "12"
            }
        ],
        "order": 12,
        "className": "",
        "visible": true,
        "disabled": false
    },
    {
        "id": "14a551a96b61dade",
        "type": "ui-base",
        "name": "My Dashboard 2",
        "path": "/dashboard",
        "appIcon": "",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "headerContent": "page",
        "navigationStyle": "default",
        "titleBarStyle": "default",
        "showReconnectNotification": true,
        "notificationDisplayTime": "1",
        "showDisconnectNotification": true,
        "allowInstall": true
    },
    {
        "id": "cbe751f3fb4f230f",
        "type": "ui-theme",
        "name": "JJ_Dark",
        "colors": {
            "surface": "#2c5168",
            "primary": "#585858",
            "bgPage": "#121212",
            "groupBg": "#2c2b2b",
            "groupOutline": "#c7c2c2"
        },
        "sizes": {
            "density": "compact",
            "pagePadding": "1px",
            "groupGap": "4px",
            "groupBorderRadius": "2px",
            "widgetGap": "6px"
        }
    },
    {
        "id": "348efdce5e5bb4a8",
        "type": "global-config",
        "env": [],
        "modules": {
            "@flowfuse/node-red-dashboard": "1.30.0"
        }
    }
]

Thank you

That is certainly not a general problem, I will try your flow.

1 Like

I can replicate the problem.
Until it is sorted you can use a ui-event node to tell you when the page is opened or refreshed and trigger the ui_update then.

1 Like

I have raised an bug report for this. I am working on the fix.

1 Like

Hi Colin,
Thanks. Hope it is nothing too complicated standing behind that behavior. I also found that smaller changes in terms of e.g. chart tiltle etc. remain persistant. However, as soon as data series come in it doesn't work.
Thanks again and best wishes...

This should all be working now in v1.30.1

1 Like

Thank you!! It works perfectly now! And thank you that this has been published on very short notice.
Will start doing sophisticated Charts designs. One question: Is there any command that would reset all settings that have been injected via msg.ui_update ? I can clear the data by injecting a msg.payload = []. However, I also want to reset everything for test purposes. The only working way I found is a NR restart.
Thank you.

Unfortunately no, I don't think that there is a way that can be done. Node red has no way of knowing the default values for all the thousands of eCharts options. If you override one of those then, as far as I know, there is no way to tell eCharts to reset it to the default value.

1 Like

The way I do this is to keep a copy of what I have set as a default and then inject that as a msg.ui_update (so I keep track of all update changes)

The only way to get a true charts default is as Colin says to go through the eChart configuration document and save them.

One thought as I have not tried it, would changing the chart type reset the 'options' Object?

In order to get a "reset" button I'm having to set multiple parameters, many of which are not the default values listed in echarts documentation.
Of course ui-chart passes some overrides. I just wish they were listed somewhere!

I think the best I can do is to point you to node-red-dashboard/ui/src/widgets/ui-chart/UIChart.vue at main · FlowFuse/node-red-dashboard · GitHub. Any overrides should be in there, but it is fairly complex as the values depend on settings in the chart configuration.

I am wondering whether a Reset Options feature would be a good idea, that says discard all previous chartOptions fed in, and recreate the chart object itself with the existing chart data, so that all options will be reset.

It would be very handy while experimenting with options, when there is no available "current set" to view (dev tools just shows "canvas") and if you break it it says broke.

Not much use once you have a working set of options?