Zonneplan in the Netherlands uses a Solar bonus between sunrise and sunset of 10 percent.
The flow I created in nodered adjusts the dess scheduled sell formula according tus sunrise and sunset every day at 1:00.
[
{
"id": "d7b06853644b688b",
"type": "group",
"z": "28b7874e5f70d147",
"name": "Set Victron Dess Sell Price Schedule based on sunrise sunset - for Zonneplan Zonnebonus",
"style": {
"label": true
},
"nodes": [
"426f5b74668d1a6b",
"be370ca5931e2729",
"37c9a6df5ed98310",
"e0b6c70677569798",
"1d4ac16e0acc5e6a"
],
"x": 94,
"y": 99,
"w": 952,
"h": 282
},
{
"id": "426f5b74668d1a6b",
"type": "inject",
"z": "28b7874e5f70d147",
"g": "d7b06853644b688b",
"name": "Daily at 1:00 Your Timezone",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "00 01 * * *",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 260,
"y": 140,
"wires": [
[
"37c9a6df5ed98310"
]
]
},
{
"id": "be370ca5931e2729",
"type": "debug",
"z": "28b7874e5f70d147",
"g": "d7b06853644b688b",
"name": "All object debug",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 920,
"y": 340,
"wires": []
},
{
"id": "37c9a6df5ed98310",
"type": "suncron",
"z": "28b7874e5f70d147",
"g": "d7b06853644b688b",
"name": "Center of The Netherlands",
"lat": "52.1007",
"lon": "5.1775",
"replay": false,
"ejectScheduleOnUpdate": false,
"sunrisePayload": "1",
"sunrisePayloadType": "num",
"sunriseTopic": "sunrise",
"sunriseOffsetType": 1,
"sunriseOffsetHours": 0,
"sunriseOffsetMinutes": 0,
"sunriseOffset": 0,
"sunriseEndPayload": "",
"sunriseEndPayloadType": "str",
"sunriseEndTopic": "",
"sunriseEndOffsetType": 1,
"sunriseEndOffsetHours": 0,
"sunriseEndOffsetMinutes": 0,
"sunriseEndOffset": 0,
"goldenHourEndPayload": "",
"goldenHourEndPayloadType": "str",
"goldenHourEndTopic": "",
"goldenHourEndOffsetType": 1,
"goldenHourEndOffsetHours": 0,
"goldenHourEndOffsetMinutes": 0,
"goldenHourEndOffset": 0,
"solarNoonPayload": "",
"solarNoonPayloadType": "str",
"solarNoonTopic": "",
"solarNoonOffsetType": 1,
"solarNoonOffsetHours": 0,
"solarNoonOffsetMinutes": 0,
"solarNoonOffset": 0,
"goldenHourPayload": "",
"goldenHourPayloadType": "str",
"goldenHourTopic": "",
"goldenHourOffsetType": 1,
"goldenHourOffsetHours": 0,
"goldenHourOffsetMinutes": 0,
"goldenHourOffset": 0,
"sunsetStartPayload": "",
"sunsetStartPayloadType": "str",
"sunsetStartTopic": "",
"sunsetStartOffsetType": 1,
"sunsetStartOffsetHours": 0,
"sunsetStartOffsetMinutes": 0,
"sunsetStartOffset": 0,
"sunsetPayload": "1",
"sunsetPayloadType": "num",
"sunsetTopic": "sunset",
"sunsetOffsetType": 1,
"sunsetOffsetHours": 0,
"sunsetOffsetMinutes": 0,
"sunsetOffset": 0,
"duskPayload": "",
"duskPayloadType": "str",
"duskTopic": "",
"duskOffsetType": 1,
"duskOffsetHours": 0,
"duskOffsetMinutes": 0,
"duskOffset": 0,
"nauticalDuskPayload": "",
"nauticalDuskPayloadType": "str",
"nauticalDuskTopic": "",
"nauticalDuskOffsetType": 1,
"nauticalDuskOffsetHours": 0,
"nauticalDuskOffsetMinutes": 0,
"nauticalDuskOffset": 0,
"nightPayload": "",
"nightPayloadType": "str",
"nightTopic": "",
"nightOffsetType": 1,
"nightOffsetHours": 0,
"nightOffsetMinutes": 0,
"nightOffset": 0,
"nadirPayload": "",
"nadirPayloadType": "str",
"nadirTopic": "",
"nadirOffsetType": 1,
"nadirOffsetHours": 0,
"nadirOffsetMinutes": 0,
"nadirOffset": 0,
"nightEndPayload": "",
"nightEndPayloadType": "str",
"nightEndTopic": "",
"nightEndOffsetType": 1,
"nightEndOffsetHours": 0,
"nightEndOffsetMinutes": 0,
"nightEndOffset": 0,
"nauticalDawnPayload": "",
"nauticalDawnPayloadType": "str",
"nauticalDawnTopic": "",
"nauticalDawnOffsetType": 1,
"nauticalDawnOffsetHours": 0,
"nauticalDawnOffsetMinutes": 0,
"nauticalDawnOffset": 0,
"dawnPayload": "",
"dawnPayloadType": "str",
"dawnTopic": "",
"dawnOffsetType": 1,
"dawnOffsetHours": 0,
"dawnOffsetMinutes": 0,
"dawnOffset": 0,
"x": 280,
"y": 240,
"wires": [
[
"e0b6c70677569798"
]
]
},
{
"id": "e0b6c70677569798",
"type": "function",
"z": "28b7874e5f70d147",
"g": "d7b06853644b688b",
"name": "Set sellPriceFormulaSchedule based on sunrise sunset",
"func": "const suncronData = msg;\nlet returnmsg = {};\n\nif (suncronData && suncronData.schedule && suncronData.schedule.sunrise && suncronData.schedule.sunset) {\n const sunriseTime = new Date(suncronData.schedule.sunrise.sunEventTime);\n const sunsetTime = new Date(suncronData.schedule.sunset.sunEventTime);\n\n // Helper function to round a date to the nearest 15-minute interval\n const floorToQuarter = (date) => {\n const minutes = date.getMinutes();\n const floorMinutes = Math.round(minutes / 15) * 15;\n\n let floorDate = new Date(date);\n floorDate.setMinutes(floorMinutes);\n floorDate.setSeconds(0);\n floorDate.setMilliseconds(0);\n \n return floorDate;\n };\n \n const ceilToQuarter = (date) => {\n let ceilDate = floorToQuarter(date);\n ceilDate.setTime(ceilDate.getTime() + (15 * 60 * 1000));\n\n return ceilDate;\n }\n\n // Helper for START times (Exactly on the quarter hour: e.g., 06:45)\n const formatStartTime = (date) => {\n return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;\n };\n\n // Generate the formatted start and end times\n // Acccording to https://jeroen.nl/dynamische-energie/aanbieders/zonneplan\n // the start of sunbonus is at the beginning of the quarter when the sun is partly visible\n // and the end of sunbonus is at the end of the quarter when the sun disappears completely\n const sunrise = formatStartTime(floorToQuarter(sunriseTime)); // e.g., \"06:45\"\n const sunset = formatStartTime(ceilToQuarter(sunsetTime)); // e.g., \"20:15\"\n\n const schedule = [\n {\n to: sunrise,\n from: \"00:00\",\n formula: \"p+0.01652\"\n },\n {\n to: sunset,\n from: sunrise,\n formula: \"p*1.1+0.01652\"\n },\n {\n to: \"00:00\",\n from: sunset,\n formula: \"p+0.01652\"\n }\n ];\n\n const completeSchedule = [\n {\n days: [0, 1, 2, 3, 4, 5, 6],\n schedule: schedule\n }\n ];\n\n // Map the stringified schedules to the required VRM API parameters\n returnmsg.payload = {\n \"sellPriceFormulaSchedule\" : JSON.stringify(completeSchedule)\n };\n\n return returnmsg;\n} else {\n node.warn(\"Invalid or unavailable suncron data, check 'schedule', 'sunrise', and 'sunset'.\");\n return null;\n}\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 360,
"y": 340,
"wires": [
[
"be370ca5931e2729",
"1d4ac16e0acc5e6a"
]
]
},
{
"id": "1d4ac16e0acc5e6a",
"type": "vrm-api",
"z": "28b7874e5f70d147",
"g": "d7b06853644b688b",
"vrm": "930860d071785de1",
"name": "Update Dynamic ESS configuration",
"api_type": "installations",
"idUser": "",
"idSite": "000000",
"installations": "patch-dynamic-ess-settings",
"attribute": "",
"stats_interval": "",
"show_instance": false,
"stats_start": "",
"stats_end": "",
"use_utc": false,
"gps_start": "",
"gps_end": "",
"widgets": "",
"instance": "",
"store_in_global_context": false,
"verbose": true,
"transform_price_schedule": false,
"outputs": 1,
"x": 700,
"y": 240,
"wires": [
[
"be370ca5931e2729"
]
]
},
{
"id": "930860d071785de1",
"type": "config-vrm-api",
"name": ""
},
{
"id": "2b5bd863cffce7c7",
"type": "global-config",
"env": [],
"modules": {
"node-red-contrib-suncron": "1.6.9",
"victron-vrm-api": "0.4.3"
}
}
]
Also check out the github page for this where I will push future changes:

