Can CSV being used as a profile in a graph

I was thinking you could generate the current profile setpoint in node-red and pass it down to the ESP as the current setpoint, rather than pass the whole profile down. You may have good reasons for not wanting to do that though.
So for charting the profile in real time you could start with something like this

[{"id":"29202d62.2414ea","type":"inject","z":"cfa39195.99bef","name":"Get file","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":546,"wires":[["1d7d4e90.1985f1"]]},{"id":"1d7d4e90.1985f1","type":"file in","z":"cfa39195.99bef","name":"","filename":"/path/to/profile.csv","format":"utf8","chunk":false,"sendError":false,"x":259.5,"y":545,"wires":[["5e84b499.467634"]]},{"id":"5e84b499.467634","type":"csv","z":"cfa39195.99bef","name":"","sep":",","hdrin":"","hdrout":"","multi":"mult","ret":"\\n","temp":"","skip":"0","x":272.5,"y":589,"wires":[["31e94c8a.bc689c"]]},{"id":"454e9bde.2b681c","type":"debug","z":"cfa39195.99bef","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":705,"y":574,"wires":[]},{"id":"31e94c8a.bc689c","type":"change","z":"cfa39195.99bef","name":"topic: csv","rules":[{"t":"set","p":"topic","pt":"msg","to":"csv","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":410.5,"y":589,"wires":[["837fa64c.1e87f"]]},{"id":"837fa64c.1e87f","type":"join","z":"cfa39195.99bef","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":552.5,"y":636,"wires":[["454e9bde.2b681c","f5828d81.87dcc"]]},{"id":"ced9bf3c.b771d8","type":"inject","z":"cfa39195.99bef","name":"Tick","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":89,"y":648,"wires":[["7e40e90d.0305c8"]]},{"id":"f5828d81.87dcc","type":"function","z":"cfa39195.99bef","name":"Calculate value","func":"if (msg.topic === \"csv\") {\n    // this is a message containing the csv data\n    context.set(\"profile\", msg.payload.csv)\n    msg = null\n} else {\n    // otherwise it is a tick message\n    // get the csv array\n    var csv = context.get(\"profile\")\n    // timestamp is in msg.payload.tick\n    // do the stuff\n    msg.payload = \"some calculated value\"\n}\nreturn msg;","outputs":1,"noerr":0,"x":728.5,"y":636,"wires":[[]]},{"id":"7e40e90d.0305c8","type":"change","z":"cfa39195.99bef","name":"topic: tick","rules":[{"t":"set","p":"topic","pt":"msg","to":"tick","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":406,"y":647,"wires":[["837fa64c.1e87f"]]}]

You may have to tweak the csv node to match your data. I have assumed comma separated. The join won't output anything till it has received both the file and a tick input, then it passes the data on to the function node each time there is a tick.