Here it is. Please note that you also need to set a global variable global.vrmSiteId with you VRM access data.
[
{
"id": "1459d4d7551ca112",
"type": "tab",
"label": "SolarVorhersage",
"disabled": false,
"info": "",
"env": []
},
{
"id": "cecfa760836ea314",
"type": "inject",
"z": "1459d4d7551ca112",
"name": "execute_every_30min",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "*/30 6-20 * * *",
"once": true,
"onceDelay": "0.5",
"topic": "",
"payload": "",
"payloadType": "date",
"x": 170,
"y": 100,
"wires": [
[
"83e1731df2cb4a4f",
"4509958d36211e97",
"58bf60b68439ec53"
]
]
},
{
"id": "83e1731df2cb4a4f",
"type": "vrm-api",
"z": "1459d4d7551ca112",
"vrm": "cca5d0a225796a2b",
"name": "forecast_today",
"api_type": "installations",
"idUser": "",
"idSite": "{{global.vrmSiteId}}",
"installations": "stats",
"attribute": "solar_yield_forecast",
"stats_interval": "15mins",
"show_instance": false,
"stats_start": "bod",
"stats_end": "eod",
"use_utc": false,
"gps_start": "",
"gps_end": "",
"widgets": "",
"instance": "",
"store_in_global_context": false,
"verbose": false,
"outputs": 1,
"x": 400,
"y": 40,
"wires": [
[
"19383e3d7c1b8fcf"
]
]
},
{
"id": "53ec77049a4d0973",
"type": "victron-input-battery",
"z": "1459d4d7551ca112",
"service": "com.victronenergy.battery/512",
"path": "/Soc",
"serviceObj": {
"service": "com.victronenergy.battery/512",
"name": "Batterie"
},
"pathObj": {
"path": "/Soc",
"type": "float",
"name": "State of charge (%)"
},
"name": "currentSOC",
"onlyChanges": true,
"roundValues": "0",
"outputs": 1,
"x": 190,
"y": 340,
"wires": [
[
"e2356f404424d7e5"
]
]
},
{
"id": "f2a2c02c8c22031a",
"type": "inject",
"z": "1459d4d7551ca112",
"name": "execute-oncedaily",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "11 00 * * *",
"once": true,
"onceDelay": "0.6",
"topic": "",
"payload": "",
"payloadType": "date",
"x": 210,
"y": 480,
"wires": [
[
"07f9999728e60555"
]
]
},
{
"id": "4509958d36211e97",
"type": "vrm-api",
"z": "1459d4d7551ca112",
"vrm": "cca5d0a225796a2b",
"name": "forecast_tomorrow",
"api_type": "installations",
"idUser": "",
"idSite": "{{global.vrmSiteId}}",
"installations": "stats",
"attribute": "solar_yield_forecast",
"stats_interval": "15mins",
"show_instance": false,
"stats_start": "bot",
"stats_end": "eot",
"use_utc": false,
"gps_start": "",
"gps_end": "",
"widgets": "",
"instance": "",
"store_in_global_context": false,
"verbose": false,
"transform_price_schedule": false,
"outputs": 1,
"x": 410,
"y": 160,
"wires": [
[
"d85c58c4e9cc5238"
]
]
},
{
"id": "07f9999728e60555",
"type": "function",
"z": "1459d4d7551ca112",
"name": "minSOC_saison",
"func": "// Aktuelles Datum abrufen\n// const d = new Date();\n// let cmonth = d.getMonth(); // 0-11\n// Monat um 1 erhöhen, um 1 bis 12 zu erhalten:\n// cmonth++;\n// let currentdate = flow.get('cdatearray');\n// let cmonth = currentdate[0];\n// let cmonth = flow.get('cmonth');\n\nlet d = new Date();\nlet cmonth = 1 + d.getMonth();\nlet cday = d.getDate();\n\n// Berechnung minSOC nach Jahreszeit (Nordhalbkugel)\n// Winter 45%: November (11), Dezember (12), Januar (1), Februar (2)\n// Frühling, Herbst 30%: März (3), April (4), September (9), Oktober (10)\n// Sommer 20%: Mai (5), Juni (6), Juli (6), August (8)\n\nswitch (cmonth) {\n case 1:\n minSOC = 40;\n break;\n case 2:\n minSOC = 40;\n break;\n case 3:\n minSOC = 15;\n break;\n case 4:\n minSOC = 15;\n break;\n case 5:\n minSOC = 15;\n break;\n case 6:\n minSOC = 15;\n break;\n case 7:\n minSOC = 15;\n break;\n case 8:\n minSOC = 15;\n break;\n case 9:\n minSOC = 15;\n break;\n case 10:\n minSOC = 15;\n break;\n case 11:\n minSOC = 40;\n break;\n case 12:\n minSOC = 40;\n break;\n default:\n minSOC = 40;\n}\n\n// als flow-variablen setzen, um sie weiterhin nutzen zu können\nflow.set ('minSOC' , minSOC);\n\n// Nachricht setzen\nmsg.payload = minSOC;\n// Optional: minSOC auch als Payload für Schalter senden\n// msg.minSOC = minSOC; \n\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 420,
"y": 480,
"wires": [
[
"84be15a11f9b4aca"
]
]
},
{
"id": "ce10e89d55ef11a6",
"type": "debug",
"z": "1459d4d7551ca112",
"name": "forecast_today",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "payload",
"statusType": "auto",
"x": 820,
"y": 40,
"wires": []
},
{
"id": "bc15c2dd4db42c81",
"type": "debug",
"z": "1459d4d7551ca112",
"name": "forecast_tomorrow",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "payload",
"statusType": "auto",
"x": 830,
"y": 160,
"wires": []
},
{
"id": "9b375ebef613ee7c",
"type": "victron-output-ess",
"z": "1459d4d7551ca112",
"service": "com.victronenergy.settings",
"path": "/Settings/CGwacs/BatteryLife/MinimumSocLimit",
"serviceObj": {
"service": "com.victronenergy.settings",
"name": "Venus settings"
},
"pathObj": {
"path": "/Settings/CGwacs/BatteryLife/MinimumSocLimit",
"type": "integer",
"name": "Minimum Discharge SOC (%)",
"mode": "both"
},
"initial": 20,
"name": "set-MinSOC",
"onlyChanges": false,
"outputs": 0,
"x": 750,
"y": 480,
"wires": []
},
{
"id": "58bf60b68439ec53",
"type": "vrm-api",
"z": "1459d4d7551ca112",
"vrm": "cca5d0a225796a2b",
"name": "forecast_restofday",
"api_type": "installations",
"idUser": "",
"idSite": "{{global.vrmSiteId}}",
"installations": "stats",
"attribute": "solar_yield_forecast",
"stats_interval": "15mins",
"show_instance": false,
"stats_start": "0",
"stats_end": "eod",
"use_utc": false,
"gps_start": "",
"gps_end": "",
"widgets": "",
"instance": "",
"store_in_global_context": false,
"verbose": false,
"outputs": 1,
"x": 410,
"y": 100,
"wires": [
[
"07b3c351977bce02"
]
]
},
{
"id": "4d003d0b786480b7",
"type": "debug",
"z": "1459d4d7551ca112",
"name": "forecast_restofday",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "payload",
"statusType": "auto",
"x": 830,
"y": 100,
"wires": []
},
{
"id": "2cc09b109a7328c4",
"type": "inject",
"z": "1459d4d7551ca112",
"name": "execute-every10min",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "*/10 6-20 * * *",
"once": true,
"onceDelay": "0.6",
"topic": "",
"payload": "20",
"payloadType": "num",
"x": 200,
"y": 560,
"wires": [
[
"5096be16a5dae81c"
]
]
},
{
"id": "5096be16a5dae81c",
"type": "function",
"z": "1459d4d7551ca112",
"name": "calc_gridsetpoint",
"func": "let forecast_today = flow.get ('forecast_today');\nlet forecast_tomorrow = flow.get ('forecast_tomorrow');\nlet fcrest = flow.get ('forecast_restofday');\nlet currentSOC = flow.get ('currentSOC');\nlet battvoltage = flow.get ('battvoltage');\nlet minSOC = flow.get ('minSOC') || 20;\nlet missingload = flow.get ('missingload') || 0;\nlet solarYield = flow.get ('solarYield') || 0;\n\nlet d = new Date();\nlet cmonth = 1 + d.getMonth();\nlet cday = d.getDate();\nlet chour = d.getHours();\nlet cminute = d.getMinutes();\nlet time = \"0.0\";\nchour = chour.toFixed(0);\nif (cminute < 10) {\n cminute = '0' + cminute.toFixed(0);\n} else {\n cminute = cminute.toFixed(0);\n}\ntime = parseFloat(chour + \".\" + cminute);\n\nlet gridsetpoint = 20;\n\n// SOC wird oft grob falsch angezeigt, deshalb Entladung nur bei \n// Mindest-Batteriespannung von ĂĽber 51,5 V\nif (51.0 < battvoltage) {\nif (cmonth === 5 || cmonth === 6 || cmonth === 7 || cmonth === 8) {\n if (15.30 <= time) { // after 15:30 full charging and resting\n gridsetpoint = -20;\n } else if ((time <= 8.00) && (35 < currentSOC) && (14000 < forecast_today)) {\n gridsetpoint = -500;\n } else if ((8.00 < time) && (time < 13.00) && (25 < currentSOC) && (12000 < fcrest)) {\n gridsetpoint = -2600;\n } else if ((13.00 <= time) && (time < 15.30) && (60 < currentSOC) && (8000 < fcrest)) {\n gridsetpoint = -2800;\n } else {\n gridsetpoint = -20;\n }\n} else if (cmonth === 4 || cmonth === 9) {\n if (15.00 <= time) { // after 15:30 full charging and resting\n gridsetpoint = -20;\n } else if ((time <= 8.00) && (40 < currentSOC) && (14000 < forecast_today)) {\n gridsetpoint = -450;\n } else if ((8.00 < time) && (time < 12.30) && (35 < currentSOC) && (14000 < fcrest)) {\n gridsetpoint = -2400;\n } else if ((12.30 <= time) && (time < 15.00) && (60 < currentSOC) && (8000 < fcrest)) {\n gridsetpoint = -2400;\n } else {\n gridsetpoint = -20;\n }\n} else if (cmonth === 3 && 20 < cday) {\n if ((15.00 <= time) && (time < 19.0)) { // between 14:45 und 20 full charging\n gridsetpoint = -20;\n } else if ((19.0 <= time) && (80 < currentSOC) && (14000 < forecast_tomorrow)) {\n gridsetpoint = -20;\n } else if ((time <= 8.30) && (60 < currentSOC) && (14000 < forecast_today)) {\n gridsetpoint = -250;\n } else if ((8.30 < time) && (time < 10.0) && (50 < currentSOC) && (14000 < fcrest)) {\n gridsetpoint = -2000;\n } else if ((10.0 <= time) && (time < 13.00) && (60 < currentSOC) && (9000 < fcrest)) {\n gridsetpoint = -2600;\n } else if ((13.00 <= time) && (time < 15.00) && (80 < currentSOC) && (7000 < fcrest)) {\n gridsetpoint = -2600;\n } else {\n gridsetpoint = -20;\n }\n} else if ((cmonth === 3 && cday <= 20) || cmonth === 10) {\n if ((14.30 <= time) && (time < 19.0)) { // between 14:45 und 20 full charging\n gridsetpoint = -20;\n } else if ((19.0 <= time) && (80 < currentSOC) && (14000 < forecast_tomorrow)) {\n gridsetpoint = -20;\n } else if ((time <= 8.45) && (70 < currentSOC) && (14000 < forecast_today)) {\n gridsetpoint = -200;\n } else if ((8.45 < time) && (time < 10.0) && (60 < currentSOC) && (14000 < fcrest)) {\n gridsetpoint = -1200;\n } else if ((10.0 <= time) && (time < 12.30) && (70 < currentSOC) && (9000 < fcrest)) {\n gridsetpoint = -2200;\n } else if ((12.30 <= time) && (time < 14.30) && (80 < currentSOC) && (7000 < fcrest)) {\n gridsetpoint = -2200;\n } else {\n gridsetpoint = -20;\n }\n} else if (cmonth === 2) {\n if ((8.45 <= time) && (time < 13.00) && (60 < currentSOC)) {\n if (7000 < fcrest) {\n gridsetpoint = -1500;\n } else if (5000 < fcrest) {\n gridsetpoint = -600;\n } else {\n gridsetpoint = 0;\n }\n } else {\n gridsetpoint = 0;\n }\n} else {\n gridsetpoint = 20;\n}\n} else {\n gridsetpoint = 20;\n}\n\nflow.set ('gridsetpoint' , gridsetpoint);\n\nmsg.payload = gridsetpoint;\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 430,
"y": 600,
"wires": [
[
"1c1dfb11bc4ceb8a"
]
]
},
{
"id": "62cff6b823712e53",
"type": "victron-output-ess",
"z": "1459d4d7551ca112",
"service": "com.victronenergy.settings",
"path": "/Settings/CGwacs/AcPowerSetPoint",
"serviceObj": {
"service": "com.victronenergy.settings",
"name": "Venus settings",
"communityTag": "ess"
},
"pathObj": {
"path": "/Settings/CGwacs/AcPowerSetPoint",
"type": "integer",
"name": "Grid set-point (W)",
"mode": "both"
},
"initial": 20,
"name": "set-GridSetpoint",
"onlyChanges": false,
"outputs": 0,
"conditionalMode": false,
"outputTrue": "",
"outputFalse": "",
"debounce": "",
"x": 760,
"y": 600,
"wires": []
},
{
"id": "e2356f404424d7e5",
"type": "function",
"z": "1459d4d7551ca112",
"name": "flow_currentSOC",
"func": "let currentSOC = msg.payload;\nlet minSOC = flow.get ('minSOC') || 40;\nlet diffSOC = 100 - currentSOC;\nconst fullbattery = 14000;\nlet missingload = diffSOC * fullbattery / 100;\n\nflow.set ('missingload', missingload);\nflow.set ('currentSOC', currentSOC);\n\nmsg.payload = currentSOC;\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 550,
"y": 340,
"wires": [
[]
]
},
{
"id": "2f5522e00a078a17",
"type": "comment",
"z": "1459d4d7551ca112",
"name": "Description",
"info": "Global variable:\n\nvrmSiteId = the individual VRM site id\n I set this global variable in an extra flow\n Never publish your VRM site id!\n\nFlow variables:\n\nUpdated every hour:\nforecast_today = VRM forecast for whole today\nforecast_tomorrow = VRM forecast for whole day tomorrow\nforecast_restofday = VRM forecast from now to end of day\n\nPermanently updated:\ncurrentSOC = the current SOC \nmissingload = load (in Wh) necessary for 100% SOC\n\nUpdated once daily:\nminSOC = minimal SOC for ESS, different for each month of the year\n\nUpdated every hour:\ngridsetpoint = Grid Setpoint, to enable grid-feed-in when more solar yield is expected than inverter power\n\nThe gridsetpoint is modified according to month (seasonal difference), hour (discharging full battery over night etc), current SOC and forecast_restofday\n\nThe calc_gridsetpoint_demo can be used to evaluate different settings according to a different system. The variables cmonth, cday, forecast_today, forecast_tomorrow, forecast_restofday, currentSOC, missingload can be set to the values to be tested. Just click on the inject node.",
"x": 140,
"y": 40,
"wires": []
},
{
"id": "07b3c351977bce02",
"type": "function",
"z": "1459d4d7551ca112",
"name": "adjustment",
"func": "let forecast_restofday = 0.95 * msg.payload.totals.solar_yield_forecast;\nflow.set ('forecast_restofday', forecast_restofday);\nmsg.payload = forecast_restofday;\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 610,
"y": 100,
"wires": [
[
"4d003d0b786480b7"
]
]
},
{
"id": "19383e3d7c1b8fcf",
"type": "function",
"z": "1459d4d7551ca112",
"name": "adjustment",
"func": "let forecast_today = 0.95 * msg.payload.totals.solar_yield_forecast;\nflow.set ('forecast_today', forecast_today);\nmsg.payload = forecast_today;\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 610,
"y": 40,
"wires": [
[
"ce10e89d55ef11a6"
]
]
},
{
"id": "d85c58c4e9cc5238",
"type": "function",
"z": "1459d4d7551ca112",
"name": "adjustment",
"func": "let forecast_tomorrow = 0.95 * msg.payload.totals.solar_yield_forecast;\nflow.set ('forecast_tomorrow', forecast_tomorrow);\nmsg.payload = forecast_tomorrow;\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 610,
"y": 160,
"wires": [
[
"bc15c2dd4db42c81"
]
]
},
{
"id": "1c1dfb11bc4ceb8a",
"type": "rbe",
"z": "1459d4d7551ca112",
"name": "",
"func": "deadband",
"gap": "1",
"start": "",
"inout": "out",
"septopics": false,
"property": "payload",
"topi": "topic",
"x": 600,
"y": 600,
"wires": [
[
"62cff6b823712e53"
]
]
},
{
"id": "84be15a11f9b4aca",
"type": "rbe",
"z": "1459d4d7551ca112",
"name": "",
"func": "deadband",
"gap": "1",
"start": "",
"inout": "out",
"septopics": false,
"property": "payload",
"topi": "topic",
"x": 600,
"y": 480,
"wires": [
[
"9b375ebef613ee7c"
]
]
},
{
"id": "909dfa353bd46276",
"type": "inject",
"z": "1459d4d7551ca112",
"name": "execute-nightly",
"props": [
{
"p": "payload"
}
],
"repeat": "",
"crontab": "0 0-5 * * *",
"once": false,
"onceDelay": "0.6",
"topic": "",
"payload": "20",
"payloadType": "num",
"x": 220,
"y": 640,
"wires": [
[
"5096be16a5dae81c"
]
]
},
{
"id": "a483b3a4cf9ab25e",
"type": "victron-input-system",
"z": "1459d4d7551ca112",
"d": true,
"service": "com.victronenergy.system/0",
"path": "/Dc/Pv/Power",
"serviceObj": {
"service": "com.victronenergy.system/0",
"name": "Venus system",
"communityTag": "system"
},
"pathObj": {
"path": "/Dc/Pv/Power",
"type": "float",
"name": "MPPTs - power (W)"
},
"name": "",
"onlyChanges": false,
"roundValues": "no",
"rateLimit": 0,
"outputs": 1,
"conditionalMode": false,
"outputTrue": "true",
"outputFalse": "false",
"debounce": "2000",
"x": 260,
"y": 280,
"wires": [
[
"2e196c6361b12c20"
]
]
},
{
"id": "2e196c6361b12c20",
"type": "function",
"z": "1459d4d7551ca112",
"d": true,
"name": "flow_solarYield",
"func": "let pv_inverter = global.get ('pv_inverter') || 0.0;\nlet solarYield = msg.payload + pv_inverter;\n\nflow.set ('solarYield', solarYield);\n\nmsg.payload = solarYield;\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 540,
"y": 280,
"wires": [
[
"53e40b28dc0246f9"
]
]
},
{
"id": "53e40b28dc0246f9",
"type": "debug",
"z": "1459d4d7551ca112",
"d": true,
"name": "solarYield",
"active": true,
"tosidebar": false,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "payload",
"statusType": "auto",
"x": 760,
"y": 280,
"wires": []
},
{
"id": "db140c33c65a4bd3",
"type": "victron-input-battery",
"z": "1459d4d7551ca112",
"service": "com.victronenergy.battery/512",
"path": "/Dc/0/Voltage",
"serviceObj": {
"service": "com.victronenergy.battery/512",
"name": "Batterie",
"communityTag": "battery"
},
"pathObj": {
"path": "/Dc/0/Voltage",
"type": "float",
"name": "Battery voltage (V)"
},
"name": "battvoltage",
"onlyChanges": true,
"roundValues": "2",
"outputs": 1,
"conditionalMode": false,
"outputTrue": "",
"outputFalse": "",
"debounce": "",
"x": 180,
"y": 400,
"wires": [
[
"1a3e63a34925729a"
]
]
},
{
"id": "1a3e63a34925729a",
"type": "function",
"z": "1459d4d7551ca112",
"name": "flow_currentSOC",
"func": "let battvoltage = msg.payload;\n\nflow.set ('battvoltage', battvoltage);\n\nmsg.payload = battvoltage;\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 550,
"y": 400,
"wires": [
[]
]
},
{
"id": "cca5d0a225796a2b",
"type": "config-vrm-api",
"name": "SolarVorhersage"
},
{
"id": "97ad0d7a85c50c9e",
"type": "global-config",
"env": [],
"modules": {
"victron-vrm-api": "0.3.11",
"@victronenergy/node-red-contrib-victron": "1.6.63"
}
}
]