Letzten Endes bin ich doch auch, wie viele, bei NodeRed gelandet. Statt einer PID-Regelung Habe ich für den Moment eine P-Regelung, Die ich noch fine tunen muss. Regeln tut sie schon mal schön.
Im Folgenden habe ich eine Erklärung des Flows mit AI erzeugt. Quellcode für den Flow zum direkten Import und Screenshot kommt am Ende.
Node-RED Flow für Proportionale PV-Nulleinspeisung (ca. 10W Ziel)
Hallo zusammen,
ich möchte euch einen einfachen Node-RED Flow vorstellen, der eure überschüssige PV-Leistung so regelt, dass idealerweise nur ein minimaler Netzbezug von etwa 10W verbleibt. Das Prinzip basiert auf einer proportionalen Regelung.
Wie funktioniert es?
** * “Netzbezug geändert” (trigger-state): Dieser Knoten überwacht den aktuellen Netzbezug (oder die Netzeinspeisung) von eurem Sensor (sensor.bitshake_aktueller_verbrauch). Sobald sich dieser Wert ändert, wird der Flow ausgelöst.**
** * “Rate Limit (5s)” (delay): Um zu verhindern, dass zu viele Anfragen an Home Assistant gesendet werden und das System unnötig belasten, begrenzt dieser Knoten die Ausführung des Flows auf maximal alle 5 Sekunden.**
** * “PV Schalter aktiv?” (api-current-state): Hier wird geprüft, ob ein von euch definierter Schalter (input_boolean.nulleinspeisung_hauptschalter) aktiviert ist. Nur wenn dieser Schalter “on” ist, wird die Regelung aktiv.**
** * “grid_power speichern” (change): Der aktuelle Wert des Netzbezugs/der Einspeisung (der im payload ankommt) wird in der msg.grid_power gespeichert. So können wir später darauf zugreifen.**
** * “Akt. Solar Limit holen” (api-current-state): Dieser Knoten liest den aktuellen Zielwert für die maximale Solarleistung aus einer number-Entität (number.solar_maximale_leistung). Diesen Wert wird der Flow später anpassen.**
** * “Proportionale Regelung” (function): Das Herzstück der Regelung! Dieser Function-Node enthält den JavaScript-Code, der die Anpassung der Solarleistung berechnet:**
** * Er nimmt den aktuellen Netzbezug (msg.grid_power) und das aktuelle Solarleistungs-Limit (msg.payload) entgegen.**
** * Er berechnet die Differenz (error) zwischen dem aktuellen Netzbezug und dem Zielwert von 10W.**
** * Anschließend wird mit einem Proportionalitätsfaktor (Kp) eine Anpassung der Solarleistung (adjustment) berechnet. Wichtig: Der Wert von Kp (aktuell 0.3) bestimmt, wie stark die Regelung auf Änderungen des Netzbezugs reagiert. Ein kleinerer Wert führt zu sanfteren, aber langsameren Anpassungen, während ein größerer Wert schnellere Reaktionen, aber auch ein höheres Risiko von Überschwingen mit sich bringt. Hier müsst ihr ggf. experimentieren, um den optimalen Wert für eure Anlage zu finden.**
** * Das neue Solarleistungs-Limit wird berechnet und auf konfigurierte Minimal- und Maximalwerte (min_solar_limit, max_solar_limit) begrenzt. Diese Werte solltet ihr an eure Wechselrichter-Spezifikationen anpassen.**
** * Um unnötige API-Aufrufe zu vermeiden, wird das neue Limit nur dann weitergegeben, wenn es sich um mindestens 1W vom vorherigen Wert unterscheidet.**
** * “Solar Limit setzen” (api-call-service): Wenn der Function-Node ein neues Solarleistungs-Limit ausgibt, wird dieser Knoten verwendet, um den Wert der number-Entität (number.solar_maximale_leistung) in Home Assistant entsprechend zu setzen. Dies sollte idealerweise euren Wechselrichter oder eine andere Steuerungseinheit ansprechen, die die Solarleistung dynamisch anpassen kann.**
** * “Neues Solar Limit” (debug): Dieser optionale Debug-Knoten gibt das neu berechnete Solarleistungs-Limit im Node-RED Debug-Fenster aus. Das kann bei der Fehlersuche und Optimierung hilfreich sein.**
Wichtige Hinweise:
** * Kp-Faktor: Der Wert des Kp-Faktors im Function-Node “Proportionale Regelung” ist entscheidend für das Regelverhalten. Beginnt am besten mit einem kleinen Wert und erhöht ihn schrittweise, falls die Regelung zu träge ist. Beobachtet dabei, ob es zu starken Schwankungen im Netzbezug kommt.**
** * Min/Max Solarleistung: Stellt sicher, dass die min_solar_limit und max_solar_limit im Function-Node mit den minimal und maximal möglichen Leistungseinstellungen eures Wechselrichters übereinstimmen.**
** * Rate-Limit: Das eingestellte Rate-Limit von 5 Sekunden ist ein guter Kompromiss. Ihr könnt diesen Wert anpassen, aber ein zu niedriges Limit könnte die Systemlast erhöhen.**
** * Voraussetzungen: Dieser Flow setzt voraus, dass ihr einen Sensor habt, der den aktuellen Netzbezug zuverlässig erfasst, und eine Möglichkeit, die maximale Solarleistung eures Wechselrichters über Home Assistant (z.B. über eine number-Entität) zu steuern.**
[
{
"id": "7583219a0506bf8d",
"type": "tab",
"label": "PV Nulleinspeisung Proportional (10W Ziel)",
"disabled": false,
"info": "Regelt die maximale Solarleistung, um einen Netzbezug von ca. 10W zu erreichen.\n\nKp-Faktor im Function-Node \"Proportionale Regelung\" muss ggf. angepasst werden!\nMin/Max Solarleistung ebenfalls dort prüfen.\nRate-Limit aktuell auf 5 Sekunden.",
"env": []
},
{
"id": "bd65966074bb3675",
"type": "trigger-state",
"z": "7583219a0506bf8d",
"name": "Netzbezug geändert",
"server": "homeassistant-server",
"version": 5,
"inputs": 0,
"outputs": 1,
"exposeAsEntityConfig": "",
"entities": {
"entity": [
"sensor.bitshake_aktueller_verbrauch"
],
"substring": [],
"regex": []
},
"debugEnabled": false,
"constraints": [],
"customOutputs": [],
"outputInitially": false,
"stateType": "num",
"enableInput": false,
"x": 150,
"y": 160,
"wires": [
[
"2e0f4306eaaef93b"
]
]
},
{
"id": "2e0f4306eaaef93b",
"type": "delay",
"z": "7583219a0506bf8d",
"name": "Rate Limit (5s)",
"pauseType": "rate",
"timeout": "5",
"timeoutUnits": "seconds",
"rate": "1",
"nbRateUnits": "5",
"rateUnits": "second",
"randomFirst": "1",
"randomLast": "5",
"randomUnits": "seconds",
"drop": true,
"allowrate": false,
"outputs": 1,
"x": 350,
"y": 160,
"wires": [
[
"7a332b19643ce197"
]
]
},
{
"id": "7a332b19643ce197",
"type": "api-current-state",
"z": "7583219a0506bf8d",
"name": "PV Schalter aktiv?",
"server": "homeassistant-server",
"version": 3,
"outputs": 2,
"halt_if": "on",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "input_boolean.nulleinspeisung_hauptschalter",
"state_type": "str",
"blockInputOverrides": false,
"outputProperties": [
{
"property": "payload_original_trigger",
"propertyType": "msg",
"value": "",
"valueType": "triggerId"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "none",
"entity_location": "data",
"override_data": "none",
"x": 560,
"y": 160,
"wires": [
[
"4528fbf90313a230"
],
[]
]
},
{
"id": "4528fbf90313a230",
"type": "change",
"z": "7583219a0506bf8d",
"name": "grid_power speichern",
"rules": [
{
"t": "move",
"p": "payload",
"pt": "msg",
"to": "grid_power",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 200,
"y": 240,
"wires": [
[
"ff910a48a86854ad"
]
]
},
{
"id": "ff910a48a86854ad",
"type": "api-current-state",
"z": "7583219a0506bf8d",
"name": "Akt. Solar Limit holen",
"server": "homeassistant-server",
"version": 3,
"outputs": 1,
"halt_if": "",
"halt_if_type": "str",
"halt_if_compare": "is",
"entity_id": "number.solar_maximale_leistung",
"state_type": "num",
"blockInputOverrides": false,
"outputProperties": [
{
"property": "payload",
"propertyType": "msg",
"value": "",
"valueType": "entityState"
},
{
"property": "data",
"propertyType": "msg",
"value": "",
"valueType": "entity"
}
],
"for": "0",
"forType": "num",
"forUnits": "minutes",
"override_topic": false,
"state_location": "payload",
"override_payload": "msg",
"entity_location": "data",
"override_data": "msg",
"x": 430,
"y": 240,
"wires": [
[
"29c076b1b69c1f18"
]
]
},
{
"id": "29c076b1b69c1f18",
"type": "function",
"z": "7583219a0506bf8d",
"name": "Proportionale Regelung",
"func": "// Eingehende Nachrichten:\n// msg.grid_power: Aktueller Netzbezug/Einspeisung (Zahl, z.B. 50 für Bezug, -200 für Einspeisung)\n// msg.payload: Aktueller eingestellter Wert von number.solar_maximale_leistung (Zahl)\n\nlet grid_power = msg.grid_power;\n// Sicherstellen, dass current_solar_limit eine Zahl ist, falls der vorherige Node mal was anderes liefert\nlet current_solar_limit = parseFloat(msg.payload);\n\nif (isNaN(grid_power) || isNaN(current_solar_limit)) {\n node.error(\"Ungültige Eingabewerte für Regelung! grid_power: \" + msg.grid_power + \", current_solar_limit: \" + msg.payload);\n return null;\n}\n\n// --- Konfiguration der Regelung ---\nconst target_grid_power = 10.0; // Ziel: 10W Netzbezug\nconst Kp = 0.5; // Proportionalitätsfaktor. WICHTIG: Dieser Wert muss ggf. angepasst werden!\n // Kleinere Werte (z.B. 0.1-0.2) = sanfter, langsamer.\n // Größere Werte (z.B. 0.5-0.8) = schneller, aber Gefahr von Überschwingen.\nconst min_solar_limit = 30.0; // Minimale Leistungseinstellung für den Wechselrichter\nconst max_solar_limit = 800.0; // Maximale Leistungseinstellung für den Wechselrichter\n// --- Ende Konfiguration ---\n\n// Fehler: Differenz zwischen Ist-Netzleistung und Soll-Netzleistung\n// error > 0: Netzbezug ist zu hoch (oder Einspeisung zu gering) -> Solarleistung erhöhen\n// error < 0: Einspeisung ist zu hoch (oder Netzbezug zu gering/negativ) -> Solarleistung senken\nlet error = grid_power - target_grid_power;\n\n// Berechnung der nötigen Anpassung der Solarleistung\nlet adjustment = error * Kp;\n\nlet new_solar_limit = current_solar_limit + adjustment;\n\n// Begrenzung auf Min/Max Werte\nif (new_solar_limit > max_solar_limit) {\n new_solar_limit = max_solar_limit;\n} else if (new_solar_limit < min_solar_limit) {\n new_solar_limit = min_solar_limit;\n}\n\n// Runden auf Ganzzahlen\new_solar_limit = Math.round(new_solar_limit);\n\n// Nur senden, wenn sich der Wert tatsächlich ändert (mind. 1W Unterschied),\n// um unnötige API-Calls und Systemlast zu vermeiden.\nif (Math.abs(new_solar_limit - Math.round(current_solar_limit)) < 1) {\n node.status({fill:\"blue\", shape:\"ring\", text:\"Keine Änderung: \" + new_solar_limit + \"W\"});\n return null; // Stoppt den Flow hier, wenn keine signifikante Änderung\n}\n\nmsg.payload = new_solar_limit;\nnode.status({fill:\"green\", shape:\"dot\", text:\"Setze: \" + new_solar_limit + \"W\"});\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 670,
"y": 240,
"wires": [
[
"1df797cfc92adc6d",
"a8f7aa73c0dc1ef3"
]
]
},
{
"id": "1df797cfc92adc6d",
"type": "api-call-service",
"z": "7583219a0506bf8d",
"name": "Solar Limit setzen",
"server": "homeassistant-server",
"version": 7,
"debugenabled": false,
"action": "number.set_value",
"floorId": [],
"areaId": [],
"deviceId": [],
"entityId": [
"number.solar_maximale_leistung"
],
"labelId": [],
"data": "{\t \"value\":msg.payload\t}",
"dataType": "jsonata",
"mergeContext": "",
"mustacheAltTags": false,
"outputProperties": [],
"queue": "none",
"x": 900,
"y": 240,
"wires": [
[]
]
},
{
"id": "a8f7aa73c0dc1ef3",
"type": "debug",
"z": "7583219a0506bf8d",
"name": "Neues Solar Limit",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 900,
"y": 300,
"wires": []
},
{
"id": "homeassistant-server",
"type": "server",
"name": "Home Assistant",
"addon": true
}
]