Generate chart images in Node-Red for email or chat messages
Dynamically generate a chart as an image so it can be sent in email, or as a picture message in chat.
This flow uses the node-red-contrib-chart-image node and contains a few examples how to generate vertical bar, line and pie charts with one or multiple series. I live Telegram, so all these examples the output is sent as a Telegram message. But I also included and example how to add the image to an email as attachment.
If you have issues installing node-red-contrib-chart-image, this discussion may help: https://discourse.nodered.org/t/solved-using-node-red-contrib-chart-image/35300/6
More details on this flow here: https://youtu.be/bF-Q-LW5HsY
[{"id":"dce34fa7.56d7d","type":"function","z":"daafb700.230458","name":"Vertical Bar Chart","func":"function getGetOrdinal(n) {\n var s=[\"th\",\"st\",\"nd\",\"rd\"],\n v=n%100;\n return n+(s[(v-20)%10]||s[v]||s[0]);\n }\n\nvar dL = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nvar dS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nvar mL = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\nvar mS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];\n\nlet now = new Date();\n\n// Create the chart object\nlet m = {\n type: 'bar',\n options: {\n title: {\n display:true,\n text:'Vertical bar example'\n },\n legend: {\n display:false\n },\n chartArea: {\n backgroundColor: 'white'\n },\n plugins: {\n datalabels: {\n display:true,\n backgroundColor:'whitesmoke',\n borderRadius:1,\n padding:1,\n align: 'right',\n anchor: function(context) {\n //node.send({debug:{dataindex:context.dataIndex}});\n if (context.dataIndex == context.dataset.data.length - 1) {\n return 'center';\n } else {\n return 'end';\n }\n },\n offset:8,\n formatter:function(value) {\n return value > 0 ? value.toLocaleString() : '';\n }\n }\n }\n },\n data: {\n labels:[],\n datasets: [\n {\n label:\"Sample data\",\n backgroundColor:'rgba(57,97,184,0.8)',\n data:[]\n }\n ]\n }\n}\n\nlet l = Math.floor(Math.random()*50);\n\nfor (let i=0; i<10; i++) {\n l = l + Math.floor(Math.random()*6)-3;\n m.data.datasets[0].data.push(l);\n var d = new Date();\n d.setTime(now.getTime()-1000*60*60*24*(10-i));\n m.data.labels.push(dL[d.getDay()]+\", \"+getGetOrdinal(d.getDate()));\n}\n\nmsg.payload = m;\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1130,"y":120,"wires":[["12d010c4.fc8e3f"]]},{"id":"12d010c4.fc8e3f","type":"chart-image","z":"daafb700.230458","name":"","width":500,"height":"500","x":1330,"y":120,"wires":[["62271758.c63728"]]},{"id":"62271758.c63728","type":"file","z":"daafb700.230458","name":"Image dump","filename":"/home/pi/charts/test.png","appendNewline":true,"createDir":false,"overwriteFile":"true","encoding":"none","x":1590,"y":120,"wires":[["18d162d1.33e8ed"]]},{"id":"18d162d1.33e8ed","type":"function","z":"daafb700.230458","name":"Telegram message","func":"msg.payload = {chatId: \"xxxxx\", type:\"photo\", content:\"/home/pi/charts/test.png\", caption:\"Chart caption\"};\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1790,"y":120,"wires":[["f7ea37e0.ae85f8"]]},{"id":"68d737f4.5047f8","type":"inject","z":"daafb700.230458","name":"Vertical Bar","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":930,"y":120,"wires":[["dce34fa7.56d7d"]]},{"id":"f7ea37e0.ae85f8","type":"telegram sender","z":"daafb700.230458","name":"","bot":"","x":2030,"y":120,"wires":[[]]},{"id":"5f624f25.98f38","type":"function","z":"daafb700.230458","name":"Vertical Bar Chart 2 series","func":"function getGetOrdinal(n) {\n var s=[\"th\",\"st\",\"nd\",\"rd\"],\n v=n%100;\n return n+(s[(v-20)%10]||s[v]||s[0]);\n }\n\nvar dL = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nvar dS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nvar mL = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\nvar mS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];\n\nlet now = new Date();\n\n// Create the chart object\nlet m = {\n type: 'bar',\n options: {\n title: {\n display:true,\n text:'Vertical bar with 2 data series'\n },\n legend: {\n display:true\n },\n chartArea: {\n backgroundColor: 'white'\n },\n plugins: {\n datalabels: {\n display:true,\n backgroundColor:'whitesmoke',\n borderRadius:1,\n padding:1,\n align: 'right',\n anchor: function(context) {\n //node.send({debug:{dataindex:context.dataIndex}});\n if (context.dataIndex == context.dataset.data.length - 1) {\n return 'center';\n } else {\n return 'end';\n }\n },\n offset:8,\n formatter:function(value) {\n return value > 0 ? value.toLocaleString() : '';\n }\n }\n }\n },\n data: {\n labels:[],\n datasets: [\n {\n label:\"Series 1\",\n backgroundColor:'rgba(57,97,184,0.8)',\n data:[]\n },\n {\n label:\"Series 2\",\n backgroundColor:'rgba(127, 184, 57,0.8)',\n data:[]\n }\n ]\n }\n}\n\nlet l = Math.floor(Math.random()*50);\nlet k = Math.floor(Math.random()*50);\n\nfor (let i=0; i<10; i++) {\n l = l + Math.floor(Math.random()*6)-3;\n m.data.datasets[0].data.push(l);\n k = k + Math.floor(Math.random()*6)-3;\n m.data.datasets[1].data.push(k);\n var d = new Date();\n d.setTime(now.getTime()-1000*60*60*24*(10-i));\n let month = \"\" + (d.getMonth() + 1);\n let day = \"\" + d.getDate();\n let year = d.getFullYear();\n\n if (month.length < 2) month = '0' + month;\n if (day.length < 2) day = '0' + day;\n m.data.labels.push(day + \".\"+month+\".\"+year);\n}\n\nmsg.payload = m;\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1150,"y":180,"wires":[["89dfcf39.990ca"]]},{"id":"89dfcf39.990ca","type":"chart-image","z":"daafb700.230458","name":"","width":500,"height":"500","x":1370,"y":180,"wires":[["62271758.c63728"]]},{"id":"d658d601.f421e8","type":"inject","z":"daafb700.230458","name":"Vertical Bar","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":930,"y":180,"wires":[["5f624f25.98f38"]]},{"id":"e284de4.fc6a12","type":"function","z":"daafb700.230458","name":"Line Chart","func":"function getGetOrdinal(n) {\n var s=[\"th\",\"st\",\"nd\",\"rd\"],\n v=n%100;\n return n+(s[(v-20)%10]||s[v]||s[0]);\n }\n\nvar dL = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nvar dS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nvar mL = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\nvar mS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];\n\nlet now = new Date();\n\n// Create the chart object\nlet m = {\n type: 'line',\n options: {\n title: {\n display:true,\n text:'Line chart example'\n },\n legend: {\n display:false\n },\n chartArea: {\n backgroundColor: 'white'\n },\n plugins: {\n datalabels: {\n display:true,\n backgroundColor:'whitesmoke',\n borderRadius:1,\n padding:1,\n align: 'right',\n anchor: function(context) {\n //node.send({debug:{dataindex:context.dataIndex}});\n if (context.dataIndex == context.dataset.data.length - 1) {\n return 'center';\n } else {\n return 'end';\n }\n },\n offset:8,\n formatter:function(value) {\n return value > 0 ? value.toLocaleString() : '';\n }\n }\n }\n },\n data: {\n labels:[],\n datasets: [\n {\n label:\"Sample data\",\n borderColor:'rgba(57,97,184,0.8)',\n fill:false,\n data:[]\n }\n ]\n }\n}\n\nlet l = Math.floor(Math.random()*50);\n\nfor (let i=0; i<10; i++) {\n l = l + Math.floor(Math.random()*6)-3;\n m.data.datasets[0].data.push(l);\n var d = new Date();\n d.setTime(now.getTime()-1000*60*60*24*(10-i));\n m.data.labels.push(dL[d.getDay()]+\", \"+getGetOrdinal(d.getDate()));\n}\n\nmsg.payload = m;\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1110,"y":280,"wires":[["f7e11042.f96d9"]]},{"id":"f7e11042.f96d9","type":"chart-image","z":"daafb700.230458","name":"","width":500,"height":"500","x":1330,"y":280,"wires":[["62271758.c63728","fac75646.ed0358"]]},{"id":"f9531538.ab1988","type":"inject","z":"daafb700.230458","name":"Line Chart","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":920,"y":280,"wires":[["e284de4.fc6a12"]]},{"id":"4bfe6190.6619c","type":"function","z":"daafb700.230458","name":"Line Chart 3 series","func":"function getGetOrdinal(n) {\n var s=[\"th\",\"st\",\"nd\",\"rd\"],\n v=n%100;\n return n+(s[(v-20)%10]||s[v]||s[0]);\n }\n\nvar dL = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nvar dS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nvar mL = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\nvar mS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];\n\nlet now = new Date();\n\n// Create the chart object\nlet m = {\n type: 'line',\n options: {\n title: {\n display:true,\n text:'Line chart example'\n },\n legend: {\n display:false\n },\n chartArea: {\n backgroundColor: 'white'\n },\n plugins: {\n datalabels: {\n display:true,\n backgroundColor:'whitesmoke',\n borderRadius:1,\n padding:1,\n align: 'right',\n anchor: function(context) {\n //node.send({debug:{dataindex:context.dataIndex}});\n if (context.dataIndex == context.dataset.data.length - 1) {\n return 'center';\n } else {\n return 'end';\n }\n },\n offset:8,\n formatter:function(value) {\n return value > 0 ? value.toLocaleString() : '';\n }\n }\n }\n },\n data: {\n labels:[],\n datasets: [\n {\n label:\"Series 1\",\n borderColor:'rgba(184, 163, 57,0.8)',\n backgroundColor:'rgba(184, 163, 57,0.1)',\n fill:true,\n data:[]\n },\n {\n label:\"Series 2\",\n borderColor:'rgba(57,97,184,0.8)',\n fill:false,\n data:[]\n },\n {\n label:\"Series 3\",\n borderColor:'rgba(184, 57, 93,0.8)',\n fill:false,\n data:[]\n }\n ]\n }\n}\n\nlet l = Math.floor(Math.random()*50);\nlet k = l;\nlet j = l;\n\nfor (let i=0; i<10; i++) {\n l = l + Math.floor(Math.random()*6)-3;\n m.data.datasets[0].data.push(l);\n k = k + Math.floor(Math.random()*6)-3;\n m.data.datasets[1].data.push(k);\n j = j + Math.floor(Math.random()*6)-3;\n m.data.datasets[2].data.push(j);\n var d = new Date();\n d.setTime(now.getTime()-1000*60*60*24*(10-i));\n m.data.labels.push(dL[d.getDay()]+\", \"+getGetOrdinal(d.getDate()));\n}\n\nmsg.payload = m;\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1130,"y":360,"wires":[["c86b69d5.68d2d8"]]},{"id":"c86b69d5.68d2d8","type":"chart-image","z":"daafb700.230458","name":"","width":500,"height":"500","x":1330,"y":360,"wires":[["62271758.c63728"]]},{"id":"13c25f21.9a6aa1","type":"inject","z":"daafb700.230458","name":"Line Chart","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":920,"y":360,"wires":[["4bfe6190.6619c"]]},{"id":"d1ca5ba9.da1338","type":"function","z":"daafb700.230458","name":"Doughnut example","func":"function getGetOrdinal(n) {\n var s=[\"th\",\"st\",\"nd\",\"rd\"],\n v=n%100;\n return n+(s[(v-20)%10]||s[v]||s[0]);\n }\n\nvar dL = [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"];\nvar dS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\nvar mL = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\nvar mS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];\n\nlet now = new Date();\n\n// Create the chart object\nlet m = {\n type: 'pie',\n options: {\n cutoutPercentage: 50,\n title: {\n display:true,\n text:'Doughnut chart example'\n },\n legend: {\n display:false\n },\n chartArea: {\n backgroundColor: 'white'\n },\n plugins: {\n datalabels: {\n display:true,\n backgroundColor:'whitesmoke',\n borderRadius:1,\n padding:1,\n align: 'right',\n anchor: function(context) {\n //node.send({debug:{dataindex:context.dataIndex}});\n if (context.dataIndex == context.dataset.data.length - 1) {\n return 'center';\n } else {\n return 'end';\n }\n },\n offset:8,\n formatter:function(value, context) {\n return value > 0 ? context.chart.data.labels[context.dataIndex]+\": \"+value.toLocaleString() : '';\n }\n }\n }\n },\n data: {\n labels:[],\n datasets: [\n {\n backgroundColor:[],\n borderColor:\"white\",\n borderWidth:2,\n data:[]\n }\n ]\n }\n}\n\n\n\nfor (let i=0; i<12; i++) {\n m.data.datasets[0].data.push(Math.floor(Math.random()*50));\n m.data.datasets[0].backgroundColor.push(\"hsl(\"+i*360/12+\",100%,50%)\");\n m.data.labels.push(mL[i]);\n}\n\nmsg.payload = m;\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1130,"y":440,"wires":[["4af5b6dc.e60d28"]]},{"id":"4af5b6dc.e60d28","type":"chart-image","z":"daafb700.230458","name":"","width":500,"height":"500","x":1330,"y":440,"wires":[["62271758.c63728"]]},{"id":"493ec7a4.b98fe8","type":"inject","z":"daafb700.230458","name":"Line Chart","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":920,"y":440,"wires":[["d1ca5ba9.da1338"]]},{"id":"fac75646.ed0358","type":"change","z":"daafb700.230458","name":"Set up the email","rules":[{"t":"set","p":"attachments","pt":"msg","to":"{}","tot":"msg"},{"t":"set","p":"attachments.content","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"attachments.filename","pt":"msg","to":"chart.jpg","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"Chart example","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"This is the auto generated chart","tot":"str"},{"t":"set","p":"to","pt":"msg","to":"<to_email_address>","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1640,"y":280,"wires":[[]]}]