I just squashed the data collection into one function, looks neater, but works the same.
Send the debug to an influxdb batch node, I have set the retention to "month" for the 1/2 hour data, so create a retention for that, or remove from the code.
EDIT: added more checks for partial data download.
[{"id":"5fa27756ada8a4f4","type":"function","z":"712086ff.3e3668","name":"Process Data","func":"// Check for status code errors\nif (msg.statusCode !== 200) {\n const text = `error - statusCode: ${msg.statusCode} - ${msg.payload.message}`;\n node.error(text);\n node.status({ fill: \"red\", shape: \"dot\", text: text });\n return;\n}\n\n// Function to process data for different topics\nfunction processData(mode) {\n let msgData = [];\n\n // Initialize date and index based on mode\n let date = new Date();\n let index;\n\n if (mode === \"monthly\") {\n index = date.getMonth() > 0 ? date.getMonth() : 12;\n } else {\n index = date.getDate() - 1; // array starts at 0\n }\n\n // Filter data based on mode \n const gas = mode === \"monthly\"\n ? (msg.payload?.gas?.data?.filter(el => el.month === index) || [])\n : (msg.payload?.gas?.data || []);\n\n const elec = mode === \"monthly\"\n ? (msg.payload?.electricity?.data?.filter(el => el.month === index) || [])\n : (msg.payload?.electricity?.data || []);\n\n // Function to check data availability\n function checkDataAvailability(mode, data, index) {\n if (mode === \"daily\" || mode === \"monthly\") { return data && data[index] !== undefined; } // Daily & Monthly mode check\n if (mode === \"half-hourly\") { return data && data.length === 48; } // Half-hourly mode check\n return false; // If mode is not recognized, return false\n }\n\n // Check data availability for both gas and elec data\n const isGasAvailable = checkDataAvailability(mode, gas, index);\n const isElecAvailable = checkDataAvailability(mode, elec, index);\n\n // If either gas or elec data is not available, handle the error\n if (!isGasAvailable || !isElecAvailable) {\n node.warn(`Missing ${mode} data for ${msg.targetDate}`);\n node.status({ fill: \"yellow\", shape: \"dot\", text: `Missing ${mode} data for ${msg.targetDate}` });\n return;\n } else {\n node.send([null, { reset: true }]);\n node.status({ fill: \"green\", shape: \"dot\", text: `Success ${mode} data for ${msg.targetDate}` });\n }\n\n // Process and create `msgData` based on the mode\n if (mode === \"monthly\") {\n msgData.push({\n measurement: \"meters_monthly\",\n fields: {\n kWh: gas[0].consumption,\n cost: parseFloat(gas[0].cost.amount)\n },\n tags: { type: \"gas\" },\n timestamp: new Date(`${gas[0].year}-${gas[0].month}-28`).getTime()\n });\n\n msgData.push({\n measurement: \"meters_monthly\",\n fields: {\n kWh: elec[0].consumption,\n cost: parseFloat(elec[0].cost.amount)\n },\n tags: { type: \"electric\" },\n timestamp: new Date(`${elec[0].year}-${elec[0].month}-28`).getTime()\n });\n } else if (mode === \"daily\") {\n msgData.push({\n measurement: \"meters_daily\",\n fields: {\n kWh: gas[index].consumption,\n cost: parseFloat(gas[index].cost.amount),\n rate: gas[index].rates.anytime,\n standing: gas[index].rates.standing\n },\n tags: { type: \"gas\" },\n timestamp: new Date(gas[index].interval.end).getTime()\n });\n\n msgData.push({\n measurement: \"meters_daily\",\n fields: {\n kWh: elec[index].consumption,\n cost: parseFloat(elec[index].cost.amount),\n rate: elec[index].rates.anytime,\n standing: elec[index].rates.standing\n },\n tags: { type: \"electric\" },\n timestamp: new Date(elec[index].interval.end).getTime()\n });\n } else if (mode === \"half-hourly\") {\n for (let index = 0; index < gas.length; index++) {\n\n msgData.push({\n measurement: \"meters\",\n fields: {\n kWh: gas[index].consumption\n },\n tags: { type: \"gas\" },\n timestamp: new Date(gas[index].interval.end).getTime()\n })\n\n msgData.push({\n measurement: \"meters\",\n fields: {\n kWh: elec[index].consumption\n },\n tags: { type: \"electric\" },\n timestamp: new Date(elec[index].interval.end).getTime()\n })\n }\n msg.retentionPolicy = \"month\"\n }\n\n return msgData;\n\n}\n\nmsg.payload = processData(msg.topic);\n\nif (!msg.payload) { return ([null, msg]); } // start trigger node, to re-request data\n\nreturn msg\n\n\n","outputs":2,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1935,"y":1425,"wires":[["fddc7b6a3a060484"],["bcc293757e5627df"]],"info":"// Check for status code errors\r\nif (msg.statusCode !== 200) {\r\n const text = `error - statusCode: ${msg.statusCode} - ${msg.payload.message}`;\r\n node.error(text);\r\n node.status({ fill: \"red\", shape: \"dot\", text: text });\r\n return;\r\n}\r\n\r\n// Function to process data for different topics\r\nfunction processData(mode) {\r\n let gas, elec, msgData = [];\r\n\r\n // Initialize date and index based on mode\r\n let date = new Date();\r\n let index;\r\n\r\n if (mode === \"monthly\") {\r\n index = date.getMonth() > 0 ? date.getMonth() : 12;\r\n } else {\r\n index = date.getDate() - 3; // start at 0\r\n }\r\n\r\n // Filter data based on mode\r\n gas = msg.payload.gas.data.filter(el => {\r\n if (mode === \"monthly\") return el.month === index;\r\n if (mode === \"daily\") return el.date === index;\r\n if (mode === \"half-hourly\") return el.interval.end === index; \r\n });\r\n\r\n elec = msg.payload.electricity.data.filter(el => {\r\n if (mode === \"monthly\") return el.month === index;\r\n if (mode === \"daily\") return el.date === index;\r\n if (mode === \"half-hourly\") return el.interval.end === index; \r\n });\r\n\r\n // Check if data is available\r\n if (gas.length === 0 || elec.length === 0) {\r\n node.warn(\"No Data\");\r\n node.status({ fill: \"yellow\", shape: \"dot\", text: \"No Data \" + msg.date });\r\n return [null, msg];\r\n } else {\r\n node.send([null, { reset: true }]);\r\n node.status({ fill: \"green\", shape: \"dot\", text: \"Success \" + msg.date });\r\n }\r\n\r\n // Process and create `msgData` based on the mode\r\n if (mode === \"monthly\") {\r\n msgData.push({\r\n measurement: \"meters_monthly\",\r\n fields: {\r\n kWh: gas[0].consumption,\r\n cost: parseFloat(gas[0].cost.amount)\r\n },\r\n tags: { type: \"gas\" },\r\n timestamp: new Date(`${gas[0].year}-${gas[0].month}-28`).getTime()\r\n });\r\n\r\n msgData.push({\r\n measurement: \"meters_monthly\",\r\n fields: {\r\n kWh: elec[0].consumption,\r\n cost: parseFloat(elec[0].cost.amount)\r\n },\r\n tags: { type: \"electric\" },\r\n timestamp: new Date(`${elec[0].year}-${elec[0].month}-28`).getTime()\r\n });\r\n } else if (mode === \"daily\") {\r\n msgData.push({\r\n measurement: \"meters_daily\",\r\n fields: {\r\n kWh: gas[index].consumption,\r\n cost: parseFloat(gas[index].cost.amount),\r\n rate: gas[index].rates.anytime,\r\n standing: gas[index].rates.standing\r\n },\r\n tags: { type: \"gas\" },\r\n timestamp: new Date(gas[index].interval.end).getTime()\r\n });\r\n\r\n msgData.push({\r\n measurement: \"meters_daily\",\r\n fields: {\r\n kWh: elec[index].consumption,\r\n cost: parseFloat(elec[index].cost.amount),\r\n rate: elec[index].rates.anytime,\r\n standing: elec[index].rates.standing\r\n },\r\n tags: { type: \"electric\" },\r\n timestamp: new Date(elec[index].interval.end).getTime()\r\n });\r\n } else if (mode === \"half-hourly\") {\r\n for (let index = 0; index < gas.length; index++) {\r\n\r\n msgData.push({\r\n measurement: \"meters\",\r\n retentionPolicy: \"month\",\r\n fields: {\r\n kWh: gas[index].consumption\r\n },\r\n tags: { type: \"gas\" },\r\n timestamp: new Date(gas[index].interval.end).getTime()\r\n })\r\n\r\n msgData.push({\r\n measurement: \"meters\",\r\n retentionPolicy: \"month\",\r\n fields: {\r\n kWh: elec[index].consumption\r\n },\r\n tags: { type: \"electric\" },\r\n timestamp: new Date(elec[index].interval.end).getTime()\r\n })\r\n }\r\n\r\n msg.retentionPolicy = \"month\"\r\n }\r\n\r\n msg.payload = msgData;\r\n return msg;\r\n}\r\n\r\n// Execute function based on msg.topic\r\nif (msg.topic === \"monthly\") {\r\n return processData(\"monthly\");\r\n} else if (msg.topic === \"daily\") {\r\n return processData(\"daily\");\r\n} else if (msg.topic === \"half-hourly\") {\r\n return processData(\"half-hourly\");\r\n} else {\r\n node.warn(\"Unknown topic: \" + msg.topic);\r\n node.status({ fill: \"red\", shape: \"dot\", text: \"Unknown topic: \" + msg.topic });\r\n return;\r\n}\r\n\r\n"},{"id":"f9e7bba66a8679d4","type":"inject","z":"712086ff.3e3668","name":"08:00 half-hourly","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"00 08 * * *","once":false,"onceDelay":0.1,"topic":"half-hourly","payload":"","payloadType":"date","x":1095,"y":1380,"wires":[["e377d8cd85113afb"]]},{"id":"a32901e8082ed7f3","type":"inject","z":"712086ff.3e3668","name":"08:01 daily","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"01 08 * * *","once":false,"onceDelay":0.1,"topic":"daily","payload":"","payloadType":"date","x":1075,"y":1425,"wires":[["e377d8cd85113afb"]]},{"id":"fddc7b6a3a060484","type":"debug","z":"712086ff.3e3668","name":"database","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":2145,"y":1395,"wires":[]},{"id":"f7b10d0530b5aa34","type":"function","z":"712086ff.3e3668","name":"set date","func":"// Check for valid topic\nif (![\"monthly\", \"daily\", \"half-hourly\"].includes(msg.topic) || !msg.topic) {\n node.error(\"Unknown topic: \" + msg.topic);\n node.status({ fill: \"red\", shape: \"dot\", text: \"Unknown topic: \" + msg.topic });\n return;\n}\n\nlet date = new Date();\ndate.setDate(date.getDate() - 1); // Set date to yesterday\n\nswitch (msg.topic) {\n\n case \"daily\":\n msg.date = date.toISOString().slice(0, 7); // eg 2024-02\n msg.targetDate = date.toISOString().split('T')[0]; // eg 2024-02-29\n break;\n\n case \"monthly\":\n msg.date = date.getFullYear(); // eg 2024\n msg.targetDate = date.getMonth()\n break;\n\n case \"half-hourly\":\n msg.date = date.toISOString().split('T')[0]; // eg 2024-02-29\n msg.targetDate = msg.date\n break;\n\n}\n\nmsg.cookies = { \"refresh_token\": msg.responseCookies?.refresh_token };\n\nnode.status({ fill: \"green\", shape: \"dot\", text: `${msg.topic} ${msg.date}` });\n\nreturn msg;\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":1650,"y":1425,"wires":[["28a3c26921bd6172"]],"info":"let date = new Date()\r\n\r\ndate.setDate(date.getDate() - 1); //yesterday\r\n\r\nvar year = date.toLocaleString(\"default\", { year: \"numeric\" });\r\nvar month = date.toLocaleString(\"default\", { month: \"2-digit\" });\r\nvar day = date.toLocaleString(\"default\", { day: \"2-digit\" });\r\n\r\nmsg.date = `${year}-${month}-${day}`;\r\n\r\nmsg.cookies = { \"refresh_token\": msg.responseCookies.refresh_token }\r\n\r\nreturn msg;"},{"id":"503a23925ef301f4","type":"http request","z":"712086ff.3e3668","name":"get token","method":"POST","ret":"obj","paytoqs":"ignore","url":"https://my.ovoenergy.com/api/v2/auth/login","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":1515,"y":1425,"wires":[["f7b10d0530b5aa34"]]},{"id":"e377d8cd85113afb","type":"change","z":"712086ff.3e3668","name":"get login","rules":[{"t":"set","p":"payload","pt":"msg","to":"login","tot":"flow"},{"t":"set","p":"account","pt":"msg","to":"account","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":1380,"y":1425,"wires":[["503a23925ef301f4"]]},{"id":"28a3c26921bd6172","type":"http request","z":"712086ff.3e3668","name":"get data ","method":"GET","ret":"obj","paytoqs":"ignore","url":"https://smartpaymapi.ovoenergy.com/usage/api/{{topic}}/{{account}}?date={{date}}","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":1785,"y":1425,"wires":[["5fa27756ada8a4f4"]]},{"id":"bcc293757e5627df","type":"trigger","z":"712086ff.3e3668","name":"request again","op1":"","op2":"0","op1type":"nul","op2type":"str","duration":"1","extend":false,"overrideDelay":false,"units":"hr","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":2170,"y":1425,"wires":[["e377d8cd85113afb"]]},{"id":"41724b812e4d13ca","type":"function","z":"712086ff.3e3668","name":"1st of Month","func":"if (msg.payload && typeof msg.payload === 'number') {\n let date = new Date(msg.payload);\n if (date.getDate() === 1) { return msg };\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1250,"y":1470,"wires":[["e377d8cd85113afb"]]},{"id":"9e9b76ac22fb9c94","type":"inject","z":"712086ff.3e3668","name":"08:03 monthly","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"03 08 * * *","once":false,"onceDelay":0.1,"topic":"monthly","payload":"","payloadType":"date","x":1085,"y":1470,"wires":[["41724b812e4d13ca"]]}]