Plotly, data formats

I'm learning a little about plotly for dashboard charts, and found it very easy to load their CDN library via a ui_template node, and display some dummy data.
However I've found using real data is not quite straightforward, as can be seen in the example flow below.

Hardcoding the data in a ui_template node works fine;

  x:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
  y:[28.8, 28.5, 37, 56.8, 69.7, 79.7, 78.5, 77.8, 74.1, 62.6, 45.3, 39.9],

whilst altering the above to;

  x:[{{payload.time}}],
  y:[{{payload.temp}}],

and injecting it like;

var temp = [21.7, 28.5, 37, 56.8, 69.7, 79.7, 78.5, 77.8, 74.1, 62.6, 45.3, 39.9];
var time = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
msg.payload = {"temp":temp,"time":time};
return msg;

Does not render the chart, and I can't work out why not, what am I missing (apart from the obvious answer :confused:)

flow

[{"id":"20f3fd4b.940ca2","type":"ui_template","z":"a444a9ff.e7a408","group":"f93124ac.7c74c8","name":"Load Plotly CDN","order":17,"width":0,"height":0,"format":"<script src=\"https://cdn.plot.ly/plotly-latest.min.js\"></script>\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","x":590,"y":2760,"wires":[[]]},{"id":"d1949ed6.d25ce","type":"function","z":"a444a9ff.e7a408","name":"Chart data","func":"var temp = [21.7, 28.5, 37, 56.8, 69.7, 79.7, 78.5, 77.8, 74.1, 62.6, 45.3, 39.9];\nvar time = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];\nmsg.payload = {\"temp\":temp,\"time\":time};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":410,"y":2800,"wires":[["8e1615b9.9af318"]]},{"id":"a348ec63.82885","type":"inject","z":"a444a9ff.e7a408","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":260,"y":2800,"wires":[["d1949ed6.d25ce"]]},{"id":"8e1615b9.9af318","type":"ui_template","z":"a444a9ff.e7a408","group":"61d2dced.3db8f4","name":"Plotly chart","order":45,"width":0,"height":0,"format":"<body>\n\n  <div id=\"myDiv\"><!-- Plotly chart will be drawn inside this DIV --></div>\n  <script>\n\nvar trace1 = {\n    \n//  This works\n  x:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],\n  y:[28.8, 28.5, 37, 56.8, 69.7, 79.7, 78.5, 77.8, 74.1, 62.6, 45.3, 39.9],\n  \n//  This doesn't work!\n//  x:[{{payload.time}}],\n//  y:[{{payload.temp}}],\n\n    name: 'temperature',\n  //mode: 'lines',\n  connectgaps: true\n};\n\nvar data = [trace1];\n\nvar layout = {\n  title: 'Example plotly chart',\n  plot_bgcolor: '#ffffff',\n  paper_bgcolor: '#ffffff',\n  'yaxis': {\n         title: 'Temperature'\n        },\n\n  //See https://community.plotly.com/t/how-to-make-the-messy-date-ticks-organized/7477\n// 'xaxis': {\n//       'tickformat': '%X'\n//        }\n};\n\nPlotly.newPlot('myDiv', data, layout, {displayModeBar: false}, {displaylogo: false},);\n\n</script>\n</body>\n\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":580,"y":2800,"wires":[[]]},{"id":"f93124ac.7c74c8","type":"ui_group","z":"","name":"Chart","tab":"6cb7ce21.162a1","order":1,"disp":false,"width":"10","collapse":false},{"id":"61d2dced.3db8f4","type":"ui_group","z":0,"name":"flowtest","tab":"3c94630c.13381c","order":1,"disp":true,"width":16,"collapse":false},{"id":"6cb7ce21.162a1","type":"ui_tab","z":"","name":"Realtime Power","icon":"dashboard","order":1,"disabled":false,"hidden":false},{"id":"3c94630c.13381c","type":"ui_tab","z":0,"name":"Test","icon":"dashboard","order":9}]

Your values are already arrays, try without the square brackets...

x:{{payload.time}},
y:{{payload.temp}},

Thanks, but tried that already, still doesn't work unfortunately.

What about the spread operator?

x:[{{...payload.time}}],
y:[{{...payload.temp}}],

Or formatting the values as CSV instead of array?

No, not working either.

I would prefer to use arrays if possible, because I did find it difficult to construct data in csv format (probably because I'm more used to using arrays??)

and injecting it like;

var temp = [21.7, 28.5, 37, 56.8, 69.7, 79.7, 78.5, 77.8, 74.1, 62.6, 45.3, 39.9];
var time = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
msg.payload = {"temp":temp,"time":time};
return msg;

Does not render the chart, and I can't work out why not, what am I missing

It works for me ?

Have you checked the comments in the 'Plotly Chart' ui_template node, and maybe loading the hardcoded data instead?

ah, checking... :wink:

I don't have it working yet, but the key is in the initialisation, initially the there is no data, that will throw an error from plotly - first render it with some 0 values, then use the scope;

(function(scope) {
  
  scope.$watch('msg', function(msg) {
    if (msg) {
   }
})

and assign msg.payload.time and temp to the data values and use the update from plotly. I will play around some more.

1 Like

This should work, not sure how the update/redraw methods work, but rendering a new plot works.

<div id="plot"></div>
<script>
var temparr = [0]
var timearr = [0]
var data = [{

    x: temparr,
    y:timearr,
	name: 'temperature',
    connectgaps: true
}];

var layout = {
    title: 'Example plotly chart',
    plot_bgcolor: '#ffffff',
    paper_bgcolor: '#ffffff',
    'yaxis': {
        title: 'Temperature'
    }

};

Plotly.newPlot('plot', data, layout, { displayModeBar: false }, { displaylogo: false }, );

(function(scope) {
  scope.$watch('msg', function(msg) {
    if (msg) {
       
     var data = [{
        x: msg.payload.time,
        y:msg.payload.temp,
        name: 'temperature',
        connectgaps: true
     }];
      console.log(data,layout)
     Plotly.newPlot('plot', data, layout, { displayModeBar: false }, { displaylogo: false },)
    }
  });
})(scope);
</script>

Yes, that is working here, thanks.
However, just looking at the Plotly forum posts, I think you are correct that plotly update may be the way in which the chart data is refreshed.
I'll do some reading of the docs (again!) tonight and see if I can find out how - unless someone already knows.

It would be good to develop an example 'plotly' flow, as the chart offers lots more options than the chart.js chart, such as 2 scaled y-axis, plus all of the modebar functions.

Well not a lot further I'm afraid. It appears that Plotly.update updates the data array faster than redrawing the whole plot again with Plotly.newPlot so it's probably a better option.
However, I can't seem to find an working example that I can use in node-RED, so maybe for now, the only option is to re-draw the chart again on update.

@afelix - did you have has some success with Plotly?

If Plotly.newPlot works (which it does), I would have thought that Plotly.update would also work...

(function(scope) {
  scope.$watch('msg', function(msg) {
    if (msg) {

     var new_data = [{
        x: msg.payload.time,
        y: msg.payload.temp,
        name: 'temperature',
        connectgaps: true
     }];
     console.log(new_data,layout);
     Plotly.update('plot', new_data, layout, { displayModeBar: false }, { displaylogo: false },);
    }
  });
})(scope);

Using the restyle method, apperently the "update" method was deprecated for the react method, restyle should be similar in speed.

<div id="plot"></div>
<script>
var temparr = [0]
var timearr = [0]
var data = [{

    x: temparr,
    y:timearr,
	name: 'temperature',
    connectgaps: true
}];

var layout = {
    title: 'Example plotly chart',
    plot_bgcolor: '#ffffff',
    paper_bgcolor: '#ffffff',
    'yaxis': {
        title: 'Temperature'
    }

};

Plotly.newPlot('plot', [data], layout, { displayModeBar: false }, { displaylogo: false }, );

(function(scope) {
  scope.$watch('msg', function(msg) {
    if (msg) {
     temparr.push(msg.payload.temp)
     timearr.push(msg.payload.time)
     data = {'x':[timearr],'y':[temparr]};
     console.log(data,layout)
     Plotly.restyle('plot', data);  
    }
  });
})(scope);
</script>

Flow

[{"id":"f96f3dcd.45ac7","type":"ui_template","z":"52944ca6.d1ae74","group":"76bd7324.6affbc","name":"Load Plotly CDN","order":17,"width":0,"height":0,"format":"<script src=\"https://cdn.plot.ly/plotly-latest.min.js\"></script>\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","x":594,"y":264,"wires":[[]]},{"id":"4143aaaf.9085cc","type":"function","z":"52944ca6.d1ae74","name":"Chart data","func":"function randomBetween(min, max) {\n    if (min < 0) {\n        return min + Math.random() * (Math.abs(min)+max);\n    }else {\n        return min + Math.random() * max;\n    }\n}\n\nt = context.get('time')+1 || 0\n\ncontext.set('time',t)\n\nvar temp = randomBetween(2,40)\nvar time =  t\nmsg.payload = {\"temp\":temp,\"time\":time};\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":390,"y":328,"wires":[["e2deccc7.ed202","789026e7.d60f7"]]},{"id":"c495532.be0163","type":"inject","z":"52944ca6.d1ae74","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"5","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":240,"y":328,"wires":[["4143aaaf.9085cc"]]},{"id":"789026e7.d60f7","type":"ui_template","z":"52944ca6.d1ae74","group":"73ae1e1d.1e6e8","name":"Plotly chart","order":45,"width":0,"height":0,"format":"<div id=\"plot\"></div>\n<script>\nvar temparr = [0]\nvar timearr = [0]\nvar data = [{\n\n    x: temparr,\n    y:timearr,\n\tname: 'temperature',\n    connectgaps: true\n}];\n\nvar layout = {\n    title: 'Example plotly chart',\n    plot_bgcolor: '#ffffff',\n    paper_bgcolor: '#ffffff',\n    'yaxis': {\n        title: 'Temperature'\n    }\n\n};\n\nPlotly.newPlot('plot', [data], layout, { displayModeBar: false }, { displaylogo: false }, );\n\n(function(scope) {\n  scope.$watch('msg', function(msg) {\n    if (msg) {\n     temparr.push(msg.payload.temp)\n     timearr.push(msg.payload.time)\n     data = {'x':[timearr],'y':[temparr]};\n      console.log(data,layout)\n    Plotly.restyle('plot', data);  \n    }\n  });\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":574,"y":312,"wires":[[]]},{"id":"e2deccc7.ed202","type":"debug","z":"52944ca6.d1ae74","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":554,"y":360,"wires":[]},{"id":"76bd7324.6affbc","type":"ui_group","z":"","name":"Chart","tab":"8c125800.17827","order":1,"disp":false,"width":"10","collapse":false},{"id":"73ae1e1d.1e6e8","type":"ui_group","z":0,"name":"flowtest","tab":"18e14d4.a4f85b3","order":1,"disp":true,"width":16,"collapse":false},{"id":"8c125800.17827","type":"ui_tab","z":"","name":"Realtime Power","icon":"dashboard","order":1,"disabled":false,"hidden":false},{"id":"18e14d4.a4f85b3","type":"ui_tab","z":0,"name":"Test","icon":"dashboard","order":9}]
1 Like

@bakman2 I think that now I understand both of the examples that you have kindly posted, the Plotly.newplot & Plotly.restore and when each case should be used.

I'll try and knock up some examples, 2 y-axis etc, and share them in the NR library.

Thank you :+1:

2 Likes

Finally had time to work on a NR/Plotly weather forecast chart...

1 Like

Post that there flow Paul!

You just need to add you coordinates & Climacell api key to the change node.

In my flow, I save the Climacell format data to context every 10 minutes, which is then retrieved and injected into the Climacell chart flow when a ui-control node detects a browser connection.

[{"id":"f9eb44a9.6564b8","type":"function","z":"a444a9ff.e7a408","name":"Format data","func":"let x    = [];\nlet rain = [];\nlet temp = [];\nlet time = [];\nlet wind = [];\nlet gust = [];\n\n// function to add leading zeros to date/times\nfunction LZ(n){\n     if(n <= 9){return \"0\" + n;}\n     return n}\n \nfor (var i in msg.payload) {\n        // Fix to display UTC timestamp in Local Timezone\n        x[i] = new Date(msg.payload[i].observation_time.value);\n    time[i] = x[i].getFullYear() + \"-\" + LZ(x[i].getMonth() + 1) + \"-\" + LZ(x[i].getDate()) + \" \" + LZ(x[i].getHours()) + \":\" + LZ(x[i].getMinutes()) + \":\" + LZ(x[i].getSeconds());\n    rain[i] = (msg.payload[i].precipitation.value).toFixed(1);\n    temp[i] = (msg.payload[i].temp.value).toFixed(1);\n    wind[i] = (msg.payload[i].wind_speed.value).toFixed(1);\n    gust[i] = (msg.payload[i].wind_gust.value).toFixed(1);\n}\n\nmsg.payload = {\"temp\":temp,\"rain\":rain,\"wind\":wind,\"gust\":gust,\"time\":time};\nreturn msg;\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":560,"y":2960,"wires":[["6334618a.42a4e"]]},{"id":"5e0d9a67.116c84","type":"ui_template","z":"a444a9ff.e7a408","group":"2e16cb22.999174","name":"Load Plotly CDN","order":17,"width":0,"height":0,"format":"<script src=\"https://cdn.plot.ly/plotly-latest.min.js\"></script>\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","x":750,"y":2960,"wires":[[]]},{"id":"6334618a.42a4e","type":"ui_template","z":"a444a9ff.e7a408","group":"61d2dced.3db8f4","name":"Climacel Plotly chart","order":1,"width":0,"height":0,"format":"<div id=\"plot\"></div>\n<script>\n\n// Setup chart framework\nvar temparr = [0]\nvar rainarr = [0]\nvar timearr = [0]\nvar windarr = [0]\nvar gustarr = [0]\n\nvar trace1 = {\n    x: temparr,\n    y:timearr,\n};\nvar trace2 = {\n    x: rainarr,\n    y:timearr,\n};\nvar trace3 = {\n    x: windarr,\n    y:timearr,\n};\nvar trace4 = {\n    x: gustarr,\n    y:timearr,\n};\n\nvar data = [trace1,trace2,trace3,trace4];\nPlotly.newPlot('plot', data,);\n\n// Fill chart with payload data\n(function(scope) {\n  scope.$watch('msg', function(msg) {\n    if (msg) {\n        \n    var tempdata = {\n        x: msg.payload.time,\n        y:msg.payload.temp,\n        name: 'temperature',\n        mode: 'lines',\n        line: {\n              color: '#ff0000'\n            },\n        connectgaps: true\n     };\n     \n    var winddata = {\n        x: msg.payload.time,\n        y:msg.payload.wind,\n        name: 'wind-speed',\n        mode: 'lines',\n        line: {\n              dash: 'dashdot',\n              color: '#6E6E6DFF'\n            },\n        connectgaps: true\n     };\n     \n    var gustdata = {\n        x: msg.payload.time,\n        y:msg.payload.gust,\n        name: 'wind-gust',\n        mode: 'lines',\n        line: {\n              dash: 'dashdot',\n              color: '#603F83FF'\n            },\n        connectgaps: true\n     };\n     \n    var raindata = {\n        x: msg.payload.time,\n        y:msg.payload.rain,\n        name: 'precipitation',\n        mode: 'lines',\n        line: {\n            color: '#0000ff'\n            },\n        yaxis: 'y2',\n        connectgaps: true\n     };\n\nvar data = [tempdata,winddata,gustdata,raindata];\n\n    var layout = {\n    title: 'Climacell 6hr Weather Forecast',\n    showlegend: false,\n    plot_bgcolor: '#ffffff',\n    paper_bgcolor: '#ffffff',\n    'yaxis': {\n        title: 'Temp °C / Wind mph',\n        range: [0, 30],\n        color: '#ff0000'\n        },\n    'yaxis2': {\n        title: 'Precipitation ml/hr',\n        autorange: true,\n        color: '#0000ff',\n        overlaying: 'y',\n        side: 'right'\n        },\n    'xaxis': {\n       'tickformat': '%X'\n        }\n    };\n\n      console.log(data,layout)\n      Plotly.newPlot('plot', data, layout, { modeBarButtonsToRemove: ['toImage','toggleSpikelines'], displaylogo: false})\n     }\n  });\n})(scope);\n</script>\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","x":770,"y":3000,"wires":[[]]},{"id":"b008306f.70169","type":"http request","z":"a444a9ff.e7a408","name":"Climacell","method":"GET","ret":"obj","paytoqs":true,"url":"","tls":"","persist":false,"proxy":"","authType":"","x":410,"y":2960,"wires":[["f9eb44a9.6564b8"]]},{"id":"d071f468.86f868","type":"change","z":"a444a9ff.e7a408","name":"API Format","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"apikey\":\"nyxjtJ3XXXXXXXXXXXXXXXt0c0ec6ib\",\"lat\":53.473547,\"lon\":-1.05813,\"start_time\":\"now\",\"fields\":[\"precipitation\",\"temp\",\"wind_speed:mph\",\"wind_gust:mph\"]}","tot":"json"},{"t":"set","p":"url","pt":"msg","to":"https://api.climacell.co/v3/weather/nowcast","tot":"str"},{"t":"set","p":"headers","pt":"msg","to":"{\"'content-type\":\"application/json\",\"Accept\":\"application/json\",\"User-Agent\":\"node-red\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":250,"y":2960,"wires":[["b008306f.70169"]],"info":"https://developer.climacell.co/v3/reference#get-realtime\n\nhttps://developer.climacell.co/v3/docs/present\n\nhttps://developer.climacell.co/v3/widgets"},{"id":"fa713334.171d4","type":"inject","z":"a444a9ff.e7a408","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":135,"y":2960,"wires":[["d071f468.86f868"]],"l":false},{"id":"2e16cb22.999174","type":"ui_group","z":"","name":"Chart","tab":"567d8fae.98148","order":1,"disp":false,"width":"10","collapse":false},{"id":"61d2dced.3db8f4","type":"ui_group","z":0,"name":"flowtest","tab":"3c94630c.13381c","order":1,"disp":true,"width":16,"collapse":false},{"id":"567d8fae.98148","type":"ui_tab","z":"","name":"Realtime Power","icon":"dashboard","order":1,"disabled":false,"hidden":false},{"id":"3c94630c.13381c","type":"ui_tab","z":0,"name":"Test","icon":"dashboard","order":9}]
1 Like

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