"Balkon" Anlage: Growatt Noah 2000, APsystems EZ1-M und SUNKET 440W TOPCon Panels

Hallo zusammen,
ich möchte euch heute mein PV-Projekt vorstellen, das ich kürzlich realisiert habe. Hier sind die Details:

Systemkomponenten

  • Wechselrichter: Growatt Noah 2000
  • Mikrowechselrichter: APsystems EZ1-M 800W
  • Solarmodule: 4x SUNKET SKT440M10-108D4 (440W TOPCon)
  • Montagesystem: Van der Valk ValkBox 3 (Flachdach-Set für 4 Module)

Aufbau und Leistung

  • Die Panels sind aufgeteilt in 2x Südost (120°) und 2x Südwest (210°)
  • Bei der Installation habe ich die Panels je nach Ausrichtung (SW und SO) parallel geschaltet und am positiven Pol eine Blockdiode installiert
  • Leider gibt es teilweise Verschattung durch Nachbargebäude und das Haus, in dem ich zur Miete wohne
  • Bei wechselhaftem Wetter konnte ich einen Tagesertrag von 4,2 kWh erzielen

Herausforderungen und Besonderheiten

  • Der EZ1-M schaltet bei Lasten über 550W ab, vermutlich weil die Y-Kabel des Growatt nur einen Eingang (P1 oder P2) des EZ1-M ansprechen
  • Installation erfolgte mit Genehmigung des Vermieters
  • Ursprünglich war die Installation auf dem Schuppen geplant, letztendlich wurde das System aber auf dem Garagenplatz montiert
  • Die Mietwohnsituation brachte einige Herausforderungen mit sich, insbesondere bei der Wahl des Installationsortes

Nächste Schritte

  • Integration in Home Assistant (Tibber Pulse, Growatt Noah 2000, EZ1-M bereits eingebunden)
  • Ziel: Realisierung der Nulleinspeisung

Kostenaufstellung

Produkt Preis
APsystems EZ1-M 800W + Kabel Schuko 5m 149,50 €
440W SUNKET TOPCon (4x) 276,00 €
Van der Valk ValkBox 3 - Flachdach - Set 4x 156,00 €
Kabel 6mm2 100m 130,00 €
Y Stecker MC4 T 26,00 €
Stecker MC4 28,05 €
Betonplatten 61,92 €
Kabelführung + Montagematerial 32,98 €
Growatt Noah 2000 619,00 €
Kabel 4mm Verlängerung 26,00 €
Gesamtkosten 1.505,45 €
Ich bin gespannt, wie sich das System weiter entwickeln wird und freue mich darauf, meine Erfahrungen hier mit euch zu teilen.
Sonnige Grüße!

Ich habe mit Claude (AI) folgenden Flow erstellt

  1. Datenerfassung:
    • "Tibber Energie": Erfasst den aktuellen Energieverbrauch
    • "Tibber Stromerzeugung": Misst die aktuelle Energieeinspeisung ins Netz
    • "Solar Maximale Leistung": Liest die aktuelle maximale Leistungseinstellung des Wechselrichters
  2. Datenkombination:
    • "Combine Inputs 3": Fasst die Daten aus den drei Quellen zusammen
  3. Leistungsberechnung:
    • "Berechne neue Solar Leistung": Kernstück des Flows, berechnet die optimale neue Leistungseinstellung für den Wechselrichter

      Die Funktion passt die Solarleistung nach folgenden Regeln an:

      1. Bei geringem Energieverbrauch und Stromerzeugung (beide < 10): Keine Anpassung
      2. Bei hohem Energieverbrauch (>= 10): Erhöhung der Leistung um den Verbrauchswert
      3. Bei Überproduktion (Stromerzeugung >= 10): Reduzierung der Leistung
      4. In allen anderen Fällen: Keine Anpassung
  4. Leistungsanpassung:
    • "Setze Solar Leistung": Setzt die neu berechnete Leistung im Wechselrichter
  5. Debugging:
    • Mehrere Debug-Nodes zur Überwachung der Berechnungen und Ergebnisse
Die zentrale Berechnungsfunktion passt die Wechselrichter-Leistung basierend auf dem Energieverbrauch und der Einspeisung an. Sie rundet die Leistung auf 5er-Schritte und begrenzt sie auf 50 bis 750 Watt. Diese spezifische Obergrenze von 750 Watt (statt 800 Watt) ist notwendig, um die Kompatibilität zwischen dem Noah 2000 von Growatt und dem APsystems Wechselrichter zu gewährleisten und einen stabilen Betrieb sicherzustellen.

Somit habe ich eine Nulleinspeisung!
Alles dank dem Tipps hier im Forum! Danke!


Genau das habe ich auch vor :) super. Ich habe 2 Noah2000 , 4 Panels und den selben WR. Muss noch den Shelly 3M installieren lassen und dann Home Assistant installieren. Bin gespannt. Könnte ich dir bei Fragen schreiben?

@synapse Hey super! Dafür ist das Forum doch da!

Hallo @aspiro . Bist du noch mit deinem Setup zufrieden? Und die NodeRed Berechnung passt auch?

Woher holst Du dir den aktuellen Verbrauch? Mit einem shelly oder mit einem IR Lesekopf direkt vom Stromzähler?

Mir fehlt nur noch der Noah dann hätte ich das gleiche Setup wie du. Könntest du bitte den Node exportieren und zur Verfügung stellen?

Hey @noize,

also ja bin mit dem Setup zufrieden. Auch wenn jetzt gerade die Sonne eine rare Ware ist.

Also ich habe eine Home Assistant Installation. Hier habe ich Red Node installiert.
Über die Tibber API habe ich den Sensordaten, über die Growatt API habe ich die Daten der Batterie und über ApSystems API habe ich den Wechselrichter drinnen.
Hier ist der ganze Flow.

[{"id":"82c8f4f6d6c3e41e","type":"tab","label":"Nulleinspeisung über EZ1-M","disabled":false,"info":"","env":[]},{"id":"50c7c19403d3fb15","type":"api-current-state","z":"82c8f4f6d6c3e41e","name":"Tibber Stromverbrauch","server":"97162e72.b6a46","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","entity_id":"sensor.tibber_pulse_energy","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"topic","propertyType":"msg","value":"tibber_energy","valueType":"str"}],"for":"","forType":"num","x":310,"y":100,"wires":[["b75ae23d311485b3"]]},{"id":"bc76d6b42fad45bf","type":"api-current-state","z":"82c8f4f6d6c3e41e","name":"Tibber Stromerzeugung","server":"97162e72.b6a46","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","entity_id":"sensor.tibber_pulse_production","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"topic","propertyType":"msg","value":"tibber_production","valueType":"str"}],"for":"","forType":"num","x":310,"y":160,"wires":[["b75ae23d311485b3"]]},{"id":"e9ae83fcac900a15","type":"api-current-state","z":"82c8f4f6d6c3e41e","name":"EZ1-M Maximale Leistung","server":"97162e72.b6a46","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","entity_id":"number.solar_max_power","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"topic","propertyType":"msg","value":"solar_max_power","valueType":"str"}],"for":"","forType":"num","x":320,"y":220,"wires":[["b75ae23d311485b3"]]},{"id":"b75ae23d311485b3","type":"join","z":"82c8f4f6d6c3e41e","name":"Zusammenfassen der Payloads","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","useparts":true,"accumulate":false,"timeout":"","count":"3","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":590,"y":160,"wires":[["a71da7b5131747eb"]]},{"id":"a71da7b5131747eb","type":"function","z":"82c8f4f6d6c3e41e","name":"Berechne neue EZ1-M Leistung","func":"function calculateNewSolarPower(msg) {\n    const energy = parseFloat(msg.payload.tibber_energy) || 0;\n    const production = parseFloat(msg.payload.tibber_production) || 0;\n    let currentPower = parseFloat(msg.payload.solar_max_power) || 0;\n\n    let newPower;\n    let reason;\n\n    if (energy > 0 && energy < 20 && production < 15) {\n        newPower = currentPower;\n        reason = \"Keine Anpassung nötig\";\n    } else if (energy > 50) {\n        newPower = currentPower + energy;\n        reason = \"Direkte Anpassung bei hohem Verbrauch\";\n    } else if (currentPower <= 200 && energy >= 25) {\n        newPower = currentPower + 5;\n        reason = \"Kleine Anpassung (+5W)\";\n    } else if (currentPower > 200 && energy >= 25) {\n        newPower = currentPower + energy;\n        reason = \"Direkte Anpassung bei Leistung > 200W\";\n    } else if (production >= 15) {\n        const reduction = Math.min(production, currentPower - 100);\n        newPower = currentPower - reduction;\n        reason = \"Überproduktion\";\n    } else {\n        newPower = currentPower;\n        reason = \"Keine Anpassung nötig\";\n    }\n\n    newPower = Math.round(newPower / 5) * 5;\n    newPower = Math.max(50, Math.min(700, newPower));\n\n    return {\n        payload: newPower,\n        entity_id: 'number.solar_max_power',\n        debugInfo: {\n            energy,\n            production,\n            currentPower,\n            newPower,\n            reason,\n            adjustment: newPower - currentPower\n        }\n    };\n}\n\nreturn calculateNewSolarPower(msg);","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":280,"wires":[["feb536f57104415f","dfac0a66810fe764","b475031ddab95796"]]},{"id":"feb536f57104415f","type":"api-call-service","z":"82c8f4f6d6c3e41e","name":"Setze EZ1-M Leistung","server":"97162e72.b6a46","version":7,"debugenabled":false,"action":"number.set_value","data":"{\"entity_id\":\"{{entity_id}}\",\"value\":\"{{payload}}\"}","dataType":"json","x":900,"y":160,"wires":[["8db61550908ce56c"]]},{"id":"dfac0a66810fe764","type":"debug","z":"82c8f4f6d6c3e41e","name":"Debug Calculation","active":false,"x":890,"y":280,"wires":[]},{"id":"8db61550908ce56c","type":"debug","z":"82c8f4f6d6c3e41e","name":"Debug Response","active":false,"x":890,"y":100,"wires":[]},{"id":"b475031ddab95796","type":"debug","z":"82c8f4f6d6c3e41e","name":"Debug Details","active":false,"x":880,"y":220,"wires":[]},{"id":"a099a2c8924e62ef","type":"inject","z":"82c8f4f6d6c3e41e","name":"6Hz","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"6","x":90,"y":160,"wires":[["50c7c19403d3fb15","bc76d6b42fad45bf","e9ae83fcac900a15"]]},{"id":"97162e72.b6a46","type":"server","name":"Home Assistant","addon":true}]

Dieser Flow regelt die Einspeisung von Solarstrom in dein System und passt die maximale Leistung dynamisch an, abhängig von deinem Stromverbrauch, der Stromerzeugung und weiteren Faktoren. Lass uns den Flow Schritt für Schritt durchgehen:

1. Periodisches Auslösen des Flows

  • Node: Inject Node (6Hz)
    • Dieser Node sendet alle 6 Sekunden ein Signal, um den Flow zu starten.
    • Dadurch wird regelmäßig überprüft, ob eine Anpassung der Einspeiseleistung nötig ist.

2. Abrufen von Sensorwerten

Der Flow fragt drei Sensoren ab:

  • Tibber Stromverbrauch:

    • Node: api-current-state (Tibber Stromverbrauch)
    • Entity ID: sensor.tibber_pulse_energy
    • Liest deinen aktuellen Stromverbrauch aus, den Tibber misst.
  • Tibber Stromerzeugung:

    • Node: api-current-state (Tibber Stromerzeugung)
    • Entity ID: sensor.tibber_pulse_production
    • Liest die aktuelle Solarstromproduktion aus.
  • Maximale Leistung des Einspeiseregulators (EZ1-M):

    • Node: api-current-state (EZ1-M Maximale Leistung)
    • Entity ID: number.solar_max_power
    • Liest die aktuelle maximal eingestellte Leistung des Einspeiseregulators aus.

3. Zusammenführen der Daten

  • Node: Join Node (Zusammenfassen der Payloads)
    • Hier werden die drei abgerufenen Daten (Stromverbrauch, Stromerzeugung und maximale Leistung) in einer einzigen Nachricht (msg.payload) zusammengeführt.
    • Der Flow wartet, bis alle drei Sensorwerte abgerufen sind, bevor er fortfährt.

4. Berechnung der neuen maximalen Leistung

  • Node: Function Node (Berechne neue EZ1-M Leistung)
    • In diesem Node wird eine Funktion verwendet, um die neue maximale Leistung für den Einspeiseregler zu berechnen. Die Berechnung basiert auf verschiedenen Bedingungen:
      • Keine Anpassung: Wenn der Stromverbrauch niedrig (zwischen 0 und 20 W) ist und die Solarproduktion unter 15 W liegt.
      • Erhöhung der Leistung: Wenn der Stromverbrauch über 50 W liegt, wird die maximale Leistung um diesen Wert erhöht.
      • Kleine Anpassung: Wenn die aktuelle Leistung unter 200 W liegt und der Stromverbrauch über 25 W ist, wird die Leistung um 5 W erhöht.
      • Überproduktion: Wenn die Solarproduktion über 15 W liegt, wird die Leistung reduziert, um eine Überproduktion zu vermeiden.
    • Rundung: Die berechnete Leistung wird auf die nächsten 5 W gerundet und in einem Bereich von 50 W bis 700 W begrenzt.

5. Setzen der neuen maximalen Leistung

  • Node: api-call-service (Setze EZ1-M Leistung)
    • Dieser Node setzt die berechnete neue maximale Leistung (number.solar_max_power) über eine Home Assistant Service-API.
    • Dadurch wird der Einspeiseregler entsprechend angepasst.

6. Debugging und Überwachung

  • Nodes: Debug Nodes (Debug Calculation, Debug Response, Debug Details)
    • Diese Nodes dienen zur Überprüfung und Fehlerbehebung. Sie zeigen Details der Berechnung sowie die gesetzten Werte im Debug-Fenster an.
    • Debug Details gibt dir eine detaillierte Übersicht darüber, welche Berechnungen durchgeführt wurden und was die Gründe für die Anpassung waren.

Zusammengefasst:

Der Flow überwacht deinen aktuellen Stromverbrauch und die Solarstromproduktion und passt die maximale Einspeiseleistung automatisch an, um eine optimale Nutzung der Solarenergie zu gewährleisten. Er verhindert sowohl Überproduktion als auch unnötige Einspeisung ins Netz, indem er die Leistung dynamisch reguliert.

Das Ziel des Flows ist es, möglichst effizient mit deinem Strom umzugehen und unnötige Einspeisung zu vermeiden, was dir potenziell Kosten spart und dein System optimal ausnutzt.

Hallo @aspiro.
Besten Dank für deine ausführliche Antwort. Bist der Beste!

Ich habe mal eine Frage zu deinem Wechselrichter EZ1-M und deinen Modulen. Wenn ich es richtig verstanden habe, so sind doch die beiden Eingänge des Wechselrichters auf 500 Watt limitiert. Was passiert denn wenn du volle Karacho Sonne abbekommst und die beiden Module damit an einem Eingang über 500 Watt gehen? Bekommst du dann gar nix mehr bis es wieder unter 500 Watt geht oder geht nur das "darüber" verloren? Wenn gar nix mehr geht, wäre das ja an den sonnigen Tagen sehr ärgerlich.

Du hast den Thread nicht komplett gelesen. Zwischen den Panels und den EZ 1M Ist der Noah 2000 der 2000Wp kann.

ahhh ok das ist mir durch gegangen. sorry. Wenn das aber nicht wäre dann würde der Wechselrichter bei über 500watt an einem Ausgang aussteigen und nichts liefern. Korrekt?

Ich würde eher davon ausgehen, dass der Mikrowechselrichter kaputt geht

Hallo @aspiro,

vielen Dank für die ausführliche Erklärung deines Setups!

Ich habe ein sehr ähnliches Setup und auch das gleiche Problem, dass der EZ1-M sich bei Lasten über 500 W vom Noah 2000 kommend abschaltet.

Hast du das Problem lösen können? So produziert EZ1-M Strom nur solange die maximale Leistung unter 500 W eingestellt ist. Gerade wenn die Batterie voll ist, würde ich gerne den ganzen Strom weiterleiten und den EZ1-M nicht künstlich begrenzen.

Was mich stutzig macht ist, dass die PV-Eingänge vom EZ1-M sehr unterschiedliche Leistungswerte anzeigen, trozt Y-Kabel (z.B. Noah 2000 auf 250 W Ausgabe resultiert in EZ1-M PV1 = 230 W, PV2 = 18 W).

Hello @coherence,

nach dem Update von Noah 2000, ich glaube im November, läuft die Kombination stabil bis zu einer maximalen Leistung von 780W am EZ1-M. Die tatsächliche Leistungsabgabe am EZ1-M beträgt dann 750-760W und der Output am Noah 2000 liegt bei 800W. Ich habe aber auch einige Optimierungen an meinem Flow vorgenommen, sodass der EZ1-M etwas gediegener reagiert.

Über den Tag verteilt teilt der EZ1-M die Leistung zwischen PV1 und PV2 gleichmäßig auf.

Generell habe ich meinen Flow stark angepasst:

Angepasste Leistungsregelung

  • Automatischer Morgenstart zwischen 5-10 Uhr mit 5-Minuten-Prüfung
  • Spezielle Strategie für vollgeladene Batterie (SOC ≥ 98%)
  • Größere Regelschritte bei niedrigerer Ist-Leistung (10W statt 5W)
  • Leistungsfaktor-Korrektur bei Abweichungen

Stabilitätsverbesserungen

  • Stabilitätszähler für längere Aktualisierungsintervalle bei stabilem Betrieb
  • Robustere Wiederaufnahme nach Ausfällen oder unerwarteten Zuständen
  • Differenziertes Regelverhalten in verschiedenen Leistungsbereichen
// Initialisierung des globalen Kontexts zur Speicherung der letzten eingestellten Leistung

if (!global.solarControl) {

global.solarControl = {

lastPower: 0, // Speichert die zuletzt gesetzte Leistung

lastUpdateTime: 0, // Speichert Zeitstempel der letzten Aktualisierung

peakLoadStartTime: 0, // NEU: Zeitstempel des Beginns einer Lastspitze

stabilityCounter: 0 // NEU: Zähler für stabile Lastbedingungen

};

}

function calculateNewSolarPower(msg) {

// Extraktion und Parsing der Sensorwerte aus der Eingangsnachricht

const energie = parseFloat(msg.payload.tibber_energie) || 0; // Netzbezug in Watt

const stromerzeugung = parseFloat(msg.payload.tibber_stromerzeugung) || 0; // Netzeinspeisung in Watt

const soc = parseFloat(msg.payload.batterie_soc) || 0; // Batterieladestand (SOC) in Prozent

const solarPower = parseFloat(msg.payload.solar_power) || 0; // Aktuelle Solarleistung in Watt

let aktuelleLeistung = parseFloat(msg.payload.solar_maximale_leistung) || 0; // Momentane Wechselrichter-Maximalleistung

const tatsaechlicheLeistung = parseFloat(msg.payload.solar_gesamtleistung) || 0; // NEU: Tatsächliche Ausgangsleistung

const strompreis = parseFloat(msg.payload.tibber_strompreis) || 1.0; // Aktueller Strompreis von Tibber

const triggerSource = msg.payload.triggerSource || "unknown"; // Quelle des Triggers

const isEarlyMorning = msg.payload.isEarlyMorning || false; // Flag für frühen Morgen

const isHighLoadSituation = msg.payload.isHighLoadSituation || false; // NEU: Flag für Lastspitze

// Erstmalige Initialisierung bei Erstausführung

if (global.solarControl.lastPower === 0) {

global.solarControl.lastPower = aktuelleLeistung;

}

let neueLeistung = aktuelleLeistung;

let anpassungsgrund = "Keine Änderung";

// Aktuelle Zeit für Zeitstempelvergleiche

const now = Date.now();

// NEU: Leistungsdefizit berechnen (Differenz zwischen Soll und Ist)

const leistungsDefizit = aktuelleLeistung - tatsaechlicheLeistung;

// NEU: Prüfung auf Lastspitze mit Wiederanlauf eines unterbrochenen Wechselrichters

if (tatsaechlicheLeistung < 20 && aktuelleLeistung > 100) {

// Wechselrichter scheint unterbrochen zu sein, Neustart anfordern

neueLeistung = 100; // Auf 100W reduzieren, um Neuanlauf zu vereinfachen

anpassungsgrund = "WR-Neustart: Tatsächliche Leistung zu niedrig, mögliche Unterbrechung erkannt";

}

// NEU: Sofortige Reaktion auf Lastspitzen

else if (isHighLoadSituation) {

// Bei extremem Verbrauch direkt auf Maximum gehen

if (energie > 1000) {

neueLeistung = 780; // Maximale Wechselrichterleistung

anpassungsgrund = `Extreme Lastspitze: ${energie}W Netzbezug, maximale WR-Leistung aktiviert`;

}

// Bei hohem Verbrauch und WR nahe Volllast, noch mehr Leistung anfordern

else if (energie > 100 && tatsaechlicheLeistung > aktuelleLeistung * 0.9) {

// WR arbeitet nahe am Maximum, aber immer noch Netzbezug

neueLeistung = Math.min(aktuelleLeistung + 50, 780);

anpassungsgrund = `Lastspitze: WR am Limit (${tatsaechlicheLeistung}W), erhöhe auf ${neueLeistung}W`;

}

// Lastspitze Zeitstempel erfassen (für statistische Zwecke)

global.solarControl.peakLoadStartTime = now;

}

// Spezialfall: Morgenstart bei verfügbarer Solarleistung und ausgeschaltetem WR

else if (isEarlyMorning && aktuelleLeistung === 0 && solarPower > 50 && soc > 15) {

neueLeistung = 200; // Starte mit niedriger Leistung am Morgen

anpassungsgrund = "Morgenstart: Solarleistung verfügbar, WR aktivieren";

}

// Strategie 2: Bei vollständig geladener Batterie den Eigenverbrauch maximieren

else if (soc >= 98) {

neueLeistung = Math.min(solarPower + energie, 780); // Addiere Energie, begrenze auf 780W

anpassungsgrund = "SOC >= 98%, setze Leistung auf Solar-Input + Netzbezug (max 780W)";

}

// NEU: Strategie für signifikante Abweichung zwischen Soll- und Ist-Leistung

else if (leistungsDefizit > 50 && energie > 25) {

// Wenn WR nicht die gewünschte Leistung liefert, aber Netzbezug besteht

neueLeistung = Math.min(aktuelleLeistung + energie + leistungsDefizit * 0.5, 780);

anpassungsgrund = `WR-Leistungsdefizit: ${leistungsDefizit}W, Erhöhung um ${energie + Math.round(leistungsDefizit * 0.5)}W`;

}

// Strategie 3: Nulleinspeisungs-Logik für alle anderen Zustände (SOC < 98%)

else {

// Prüfen, ob wir uns im Bereich für 5W-Schritte befinden

const isSmallIncrementRange = (aktuelleLeistung >= 50 && aktuelleLeistung <= 275);

// NEU: Berücksichtigung der tatsächlichen Ausgangsleistung

// Wenn tatsächliche Leistung niedriger als die eingestellte ist, stärkere Anpassung

const leistungsFaktor = tatsaechlicheLeistung < aktuelleLeistung * 0.9 ? 1.2 : 1.0;

// Sonderfall: Hoher Netzbezug - Schnelle Reaktion erforderlich

if (energie > 75 && !isSmallIncrementRange) {

// Schnelle Anpassung mit angepasstem Faktor bei starkem Verbrauch

neueLeistung = aktuelleLeistung + Math.round(energie * 0.8 * leistungsFaktor);

anpassungsgrund = `Hoher Netzbezug (${energie}W), schnelle Anpassung mit Faktor ${leistungsFaktor.toFixed(1)}`;

}

// Standardregelung für die Nulleinspeisung

else {

// Keine Anpassung bei sehr geringem Netzbezug ohne Einspeisung

if (energie > 0 && energie < 20 && stromerzeugung < 15) {

neueLeistung = aktuelleLeistung;

anpassungsgrund = "Keine Anpassung nötig (Netzbezug gering, keine Einspeisung)";

}

// Feinabstimmung mit 5W-Schritten im Bereich 50-275W

else if (isSmallIncrementRange) {

if (energie >= 25 || stromerzeugung >= 15) {

// Leistung erhöhen bei Netzbezug

if (energie >= 25) {

// NEU: Bei niedrigerer tatsächlicher Leistung größere Schritte

const schrittGroesse = tatsaechlicheLeistung < aktuelleLeistung * 0.9 ? 10 : 5;

neueLeistung = aktuelleLeistung + schrittGroesse;

anpassungsgrund = `${schrittGroesse}W-Schritt nach oben (Bereich 50-275W, Leistungsfaktor ${leistungsFaktor.toFixed(1)})`;

}

// Leistung reduzieren bei Einspeisung

else if (stromerzeugung >= 15) {

neueLeistung = aktuelleLeistung - 5;

anpassungsgrund = "5W-Schritt nach unten (im Bereich 50-275W)";

}

} else {

neueLeistung = aktuelleLeistung;

anpassungsgrund = "Keine Anpassung nötig (im Bereich 50-275W)";

}

}

// Moderate Leistungsanpassung bei mittlerem Netzbezug (20-50W)

else if (energie >= 20 && energie <= 50) {

neueLeistung = aktuelleLeistung + Math.round(energie * 0.7 * leistungsFaktor);

anpassungsgrund = `Moderater Netzbezug (${energie}W), Anpassung mit Faktor ${leistungsFaktor.toFixed(1)}`;

}

// Direkte Leistungsanpassung bei höherem Netzbezug (>50W)

else if (energie > 50) {

neueLeistung = aktuelleLeistung + Math.round(energie * leistungsFaktor);

anpassungsgrund = `Direkte Anpassung bei Netzbezug (${energie}W) mit Faktor ${leistungsFaktor.toFixed(1)}`;

}

// Direkte Anpassung bei höherer Grundleistung und moderatem Verbrauch

else if (aktuelleLeistung > 275 && energie >= 25) {

neueLeistung = aktuelleLeistung + Math.round(energie * leistungsFaktor);

anpassungsgrund = `Direkte Anpassung bei Netzbezug (${energie}W) mit Faktor ${leistungsFaktor.toFixed(1)}`;

}

// Leistungsreduzierung bei Netzeinspeisung

else if (stromerzeugung >= 15) {

const reduktion = Math.min(stromerzeugung, aktuelleLeistung - 100); // Minimum 100W beibehalten

neueLeistung = aktuelleLeistung - reduktion;

anpassungsgrund = `Reduziere Leistung um ${Math.round(reduktion)}W wegen Netzeinspeisung (${stromerzeugung}W)`;

}

// Keine Änderung im Standardfall

else {

neueLeistung = aktuelleLeistung;

anpassungsgrund = "Keine Anpassung nötig";

}

}

// Rundung auf 5W-Schritte für präzise Steuerung

neueLeistung = Math.round(neueLeistung / 5) * 5;

// Begrenzung der Leistung auf den erlaubten Betriebsbereich (50W bis 780W)

neueLeistung = Math.max(50, Math.min(780, neueLeistung));

}

// Bestimmung, ob ein API-Update erforderlich ist

let sendUpdate = false;

const lastUpdateTime = global.solarControl.lastUpdateTime || 0;

// NEU: Stabilität optimieren - bei Hochlast oder großen Änderungen sofort reagieren,

// sonst nur wenn wirklich nötig

if (neueLeistung !== aktuelleLeistung) {

// Sofortige Reaktion bei Lastspitzen oder großen Änderungen

if (isHighLoadSituation || Math.abs(neueLeistung - aktuelleLeistung) > 50) {

sendUpdate = true;

global.solarControl.stabilityCounter = 0; // Stabilitätszähler zurücksetzen

}

// Bei kleinen Anpassungen zusätzliche Zeitprüfung (mindestens 10 Sekunden zwischen Updates)

else if (now - lastUpdateTime >= 10000) {

// Wenn längere Zeit stabile Bedingungen herrschten, mehr Änderungen zulassen

if (global.solarControl.stabilityCounter > 5) {

// Nach 5 stabilen Intervallen seltener anpassen (alle 30 Sekunden)

if (now - lastUpdateTime >= 30000) {

sendUpdate = true;

global.solarControl.stabilityCounter = 0; // Stabilitätszähler zurücksetzen

}

} else {

sendUpdate = true;

global.solarControl.stabilityCounter++; // Erhöhe Stabilitätszähler

}

}

} else {

// Wenn keine Änderung nötig ist, Stabilitätszähler erhöhen

global.solarControl.stabilityCounter++;

}

// Wenn Update gesendet wird, Status aktualisieren

if (sendUpdate) {

global.solarControl.lastPower = neueLeistung;

global.solarControl.lastUpdateTime = now;

}

// Nach der Berechnung das Update-Flag im Flow-Kontext zurücksetzen

const storage = flow.get('sensorData') || {};

storage.updateRequested = false;

flow.set('sensorData', storage);

// Rückgabe des Ergebnisses mit zusätzlichen Debug-Informationen

return {

payload: neueLeistung, // Neu berechnete Sollleistung für den Wechselrichter

entity_id: 'number.solar_maximale_leistung', // Ziel-Entity für die Steuerung

sendUpdate: sendUpdate, // Flag zur Steuerung des API-Aufrufs

debugInfo: { // Umfangreiche Informationen für Diagnose und Überwachung

energie,

stromerzeugung,

soc,

solarPower,

strompreis,

aktuelleLeistung,

tatsaechlicheLeistung, // NEU: Tatsächliche Ausgangsleistung

leistungsDefizit, // NEU: Leistungsdefizit

neueLeistung,

anpassungsgrund,

anpassungsmenge: neueLeistung - aktuelleLeistung,

updateSent: sendUpdate,

triggerSource,

isEarlyMorning,

isHighLoadSituation, // NEU: Flag für Lastspitze

timeSinceLastUpdate: now - lastUpdateTime,

stabilityCounter: global.solarControl.stabilityCounter, // NEU: Stabilitätszähler

isSmallIncrementRange: (aktuelleLeistung >= 50 && aktuelleLeistung <= 275)

}

};

}

return calculateNewSolarPower(msg);

Hier der gesamte Flow:

[{"id":"7ef2a46dcdbfd821","type":"function","z":"8cc966e1a6747189","name":"Berechne neue EZ1-M Leistung","func":"// Initialisierung des globalen Kontexts zur Speicherung der letzten eingestellten Leistung\nif (!global.solarControl) {\n    global.solarControl = {\n        lastPower: 0,          // Speichert die zuletzt gesetzte Leistung\n        lastUpdateTime: 0,     // Speichert Zeitstempel der letzten Aktualisierung\n        peakLoadStartTime: 0,  // NEU: Zeitstempel des Beginns einer Lastspitze\n        stabilityCounter: 0    // NEU: Zähler für stabile Lastbedingungen\n    };\n}\n\nfunction calculateNewSolarPower(msg) {\n    // Extraktion und Parsing der Sensorwerte aus der Eingangsnachricht\n    const energie = parseFloat(msg.payload.tibber_energie) || 0;       // Netzbezug in Watt\n    const stromerzeugung = parseFloat(msg.payload.tibber_stromerzeugung) || 0; // Netzeinspeisung in Watt\n    const soc = parseFloat(msg.payload.batterie_soc) || 0;          // Batterieladestand (SOC) in Prozent\n    const solarPower = parseFloat(msg.payload.solar_power) || 0;    // Aktuelle Solarleistung in Watt\n    let aktuelleLeistung = parseFloat(msg.payload.solar_maximale_leistung) || 0; // Momentane Wechselrichter-Maximalleistung\n    const tatsaechlicheLeistung = parseFloat(msg.payload.solar_gesamtleistung) || 0; // NEU: Tatsächliche Ausgangsleistung\n    const strompreis = parseFloat(msg.payload.tibber_strompreis) || 1.0;   // Aktueller Strompreis von Tibber\n    const triggerSource = msg.payload.triggerSource || \"unknown\";     // Quelle des Triggers\n    const isEarlyMorning = msg.payload.isEarlyMorning || false;     // Flag für frühen Morgen\n    const isHighLoadSituation = msg.payload.isHighLoadSituation || false; // NEU: Flag für Lastspitze\n    \n    // Erstmalige Initialisierung bei Erstausführung\n    if (global.solarControl.lastPower === 0) {\n        global.solarControl.lastPower = aktuelleLeistung;\n    }\n    \n    let neueLeistung = aktuelleLeistung;\n    let anpassungsgrund = \"Keine Änderung\";\n    \n    // Aktuelle Zeit für Zeitstempelvergleiche\n    const now = Date.now();\n    \n    // NEU: Leistungsdefizit berechnen (Differenz zwischen Soll und Ist)\n    const leistungsDefizit = aktuelleLeistung - tatsaechlicheLeistung;\n    \n    // NEU: Prüfung auf Lastspitze mit Wiederanlauf eines unterbrochenen Wechselrichters\n    if (tatsaechlicheLeistung < 20 && aktuelleLeistung > 100) {\n        // Wechselrichter scheint unterbrochen zu sein, Neustart anfordern\n        neueLeistung = 100; // Auf 100W reduzieren, um Neuanlauf zu vereinfachen\n        anpassungsgrund = \"WR-Neustart: Tatsächliche Leistung zu niedrig, mögliche Unterbrechung erkannt\";\n    }\n    // NEU: Sofortige Reaktion auf Lastspitzen\n    else if (isHighLoadSituation) {\n        // Bei extremem Verbrauch direkt auf Maximum gehen\n        if (energie > 1000) {\n            neueLeistung = 780; // Maximale Wechselrichterleistung\n            anpassungsgrund = `Extreme Lastspitze: ${energie}W Netzbezug, maximale WR-Leistung aktiviert`;\n        }\n        // Bei hohem Verbrauch und WR nahe Volllast, noch mehr Leistung anfordern\n        else if (energie > 100 && tatsaechlicheLeistung > aktuelleLeistung * 0.9) {\n            // WR arbeitet nahe am Maximum, aber immer noch Netzbezug\n            neueLeistung = Math.min(aktuelleLeistung + 50, 780);\n            anpassungsgrund = `Lastspitze: WR am Limit (${tatsaechlicheLeistung}W), erhöhe auf ${neueLeistung}W`;\n        }\n        \n        // Lastspitze Zeitstempel erfassen (für statistische Zwecke)\n        global.solarControl.peakLoadStartTime = now;\n    }\n    // Spezialfall: Morgenstart bei verfügbarer Solarleistung und ausgeschaltetem WR\n    else if (isEarlyMorning && aktuelleLeistung === 0 && solarPower > 50 && soc > 15) {\n        neueLeistung = 200; // Starte mit niedriger Leistung am Morgen\n        anpassungsgrund = \"Morgenstart: Solarleistung verfügbar, WR aktivieren\";\n    }\n    // Strategie 2: Bei vollständig geladener Batterie den Eigenverbrauch maximieren\n    else if (soc >= 98) {\n        neueLeistung = Math.min(solarPower + energie, 780); // Addiere Energie, begrenze auf 780W\n        anpassungsgrund = \"SOC >= 98%, setze Leistung auf Solar-Input + Netzbezug (max 780W)\";\n    }\n    // NEU: Strategie für signifikante Abweichung zwischen Soll- und Ist-Leistung\n    else if (leistungsDefizit > 50 && energie > 25) {\n        // Wenn WR nicht die gewünschte Leistung liefert, aber Netzbezug besteht\n        neueLeistung = Math.min(aktuelleLeistung + energie + leistungsDefizit * 0.5, 780);\n        anpassungsgrund = `WR-Leistungsdefizit: ${leistungsDefizit}W, Erhöhung um ${energie + Math.round(leistungsDefizit * 0.5)}W`;\n    }\n    // Strategie 3: Nulleinspeisungs-Logik für alle anderen Zustände (SOC < 98%)\n    else {\n        // Prüfen, ob wir uns im Bereich für 5W-Schritte befinden\n        const isSmallIncrementRange = (aktuelleLeistung >= 50 && aktuelleLeistung <= 275);\n        \n        // NEU: Berücksichtigung der tatsächlichen Ausgangsleistung\n        // Wenn tatsächliche Leistung niedriger als die eingestellte ist, stärkere Anpassung\n        const leistungsFaktor = tatsaechlicheLeistung < aktuelleLeistung * 0.9 ? 1.2 : 1.0;\n        \n        // Sonderfall: Hoher Netzbezug - Schnelle Reaktion erforderlich\n        if (energie > 75 && !isSmallIncrementRange) {\n            // Schnelle Anpassung mit angepasstem Faktor bei starkem Verbrauch\n            neueLeistung = aktuelleLeistung + Math.round(energie * 0.8 * leistungsFaktor);\n            anpassungsgrund = `Hoher Netzbezug (${energie}W), schnelle Anpassung mit Faktor ${leistungsFaktor.toFixed(1)}`;\n        }\n        // Standardregelung für die Nulleinspeisung\n        else {\n            // Keine Anpassung bei sehr geringem Netzbezug ohne Einspeisung\n            if (energie > 0 && energie < 20 && stromerzeugung < 15) {\n                neueLeistung = aktuelleLeistung;\n                anpassungsgrund = \"Keine Anpassung nötig (Netzbezug gering, keine Einspeisung)\";\n            }\n            // Feinabstimmung mit 5W-Schritten im Bereich 50-275W\n            else if (isSmallIncrementRange) {\n                if (energie >= 25 || stromerzeugung >= 15) {\n                    // Leistung erhöhen bei Netzbezug\n                    if (energie >= 25) {\n                        // NEU: Bei niedrigerer tatsächlicher Leistung größere Schritte\n                        const schrittGroesse = tatsaechlicheLeistung < aktuelleLeistung * 0.9 ? 10 : 5;\n                        neueLeistung = aktuelleLeistung + schrittGroesse;\n                        anpassungsgrund = `${schrittGroesse}W-Schritt nach oben (Bereich 50-275W, Leistungsfaktor ${leistungsFaktor.toFixed(1)})`;\n                    }\n                    // Leistung reduzieren bei Einspeisung\n                    else if (stromerzeugung >= 15) {\n                        neueLeistung = aktuelleLeistung - 5;\n                        anpassungsgrund = \"5W-Schritt nach unten (im Bereich 50-275W)\";\n                    }\n                } else {\n                    neueLeistung = aktuelleLeistung;\n                    anpassungsgrund = \"Keine Anpassung nötig (im Bereich 50-275W)\";\n                }\n            }\n            // Moderate Leistungsanpassung bei mittlerem Netzbezug (20-50W)\n            else if (energie >= 20 && energie <= 50) {\n                neueLeistung = aktuelleLeistung + Math.round(energie * 0.7 * leistungsFaktor);\n                anpassungsgrund = `Moderater Netzbezug (${energie}W), Anpassung mit Faktor ${leistungsFaktor.toFixed(1)}`;\n            }\n            // Direkte Leistungsanpassung bei höherem Netzbezug (>50W)\n            else if (energie > 50) {\n                neueLeistung = aktuelleLeistung + Math.round(energie * leistungsFaktor);\n                anpassungsgrund = `Direkte Anpassung bei Netzbezug (${energie}W) mit Faktor ${leistungsFaktor.toFixed(1)}`;\n            }\n            // Direkte Anpassung bei höherer Grundleistung und moderatem Verbrauch\n            else if (aktuelleLeistung > 275 && energie >= 25) {\n                neueLeistung = aktuelleLeistung + Math.round(energie * leistungsFaktor);\n                anpassungsgrund = `Direkte Anpassung bei Netzbezug (${energie}W) mit Faktor ${leistungsFaktor.toFixed(1)}`;\n            }\n            // Leistungsreduzierung bei Netzeinspeisung\n            else if (stromerzeugung >= 15) {\n                const reduktion = Math.min(stromerzeugung, aktuelleLeistung - 100); // Minimum 100W beibehalten\n                neueLeistung = aktuelleLeistung - reduktion;\n                anpassungsgrund = `Reduziere Leistung um ${Math.round(reduktion)}W wegen Netzeinspeisung (${stromerzeugung}W)`;\n            }\n            // Keine Änderung im Standardfall\n            else {\n                neueLeistung = aktuelleLeistung;\n                anpassungsgrund = \"Keine Anpassung nötig\";\n            }\n        }\n\n        // Rundung auf 5W-Schritte für präzise Steuerung\n        neueLeistung = Math.round(neueLeistung / 5) * 5;\n\n        // Begrenzung der Leistung auf den erlaubten Betriebsbereich (50W bis 780W)\n        neueLeistung = Math.max(50, Math.min(780, neueLeistung));\n    }\n    \n    // Bestimmung, ob ein API-Update erforderlich ist\n    let sendUpdate = false;\n    const lastUpdateTime = global.solarControl.lastUpdateTime || 0;\n    \n    // NEU: Stabilität optimieren - bei Hochlast oder großen Änderungen sofort reagieren,\n    // sonst nur wenn wirklich nötig\n    if (neueLeistung !== aktuelleLeistung) {\n        // Sofortige Reaktion bei Lastspitzen oder großen Änderungen\n        if (isHighLoadSituation || Math.abs(neueLeistung - aktuelleLeistung) > 50) {\n            sendUpdate = true;\n            global.solarControl.stabilityCounter = 0; // Stabilitätszähler zurücksetzen\n        }\n        // Bei kleinen Anpassungen zusätzliche Zeitprüfung (mindestens 10 Sekunden zwischen Updates)\n        else if (now - lastUpdateTime >= 10000) {\n            // Wenn längere Zeit stabile Bedingungen herrschten, mehr Änderungen zulassen\n            if (global.solarControl.stabilityCounter > 5) {\n                // Nach 5 stabilen Intervallen seltener anpassen (alle 30 Sekunden)\n                if (now - lastUpdateTime >= 30000) {\n                    sendUpdate = true;\n                    global.solarControl.stabilityCounter = 0; // Stabilitätszähler zurücksetzen\n                }\n            } else {\n                sendUpdate = true;\n                global.solarControl.stabilityCounter++; // Erhöhe Stabilitätszähler\n            }\n        }\n    } else {\n        // Wenn keine Änderung nötig ist, Stabilitätszähler erhöhen\n        global.solarControl.stabilityCounter++;\n    }\n    \n    // Wenn Update gesendet wird, Status aktualisieren\n    if (sendUpdate) {\n        global.solarControl.lastPower = neueLeistung;\n        global.solarControl.lastUpdateTime = now;\n    }\n    \n    // Nach der Berechnung das Update-Flag im Flow-Kontext zurücksetzen\n    const storage = flow.get('sensorData') || {};\n    storage.updateRequested = false;\n    flow.set('sensorData', storage);\n\n    // Rückgabe des Ergebnisses mit zusätzlichen Debug-Informationen\n    return {\n        payload: neueLeistung,                   // Neu berechnete Sollleistung für den Wechselrichter\n        entity_id: 'number.solar_maximale_leistung', // Ziel-Entity für die Steuerung\n        sendUpdate: sendUpdate,                   // Flag zur Steuerung des API-Aufrufs\n        debugInfo: {                             // Umfangreiche Informationen für Diagnose und Überwachung\n            energie,\n            stromerzeugung,\n            soc,\n            solarPower,\n            strompreis,\n            aktuelleLeistung,\n            tatsaechlicheLeistung,              // NEU: Tatsächliche Ausgangsleistung\n            leistungsDefizit,                    // NEU: Leistungsdefizit\n            neueLeistung,\n            anpassungsgrund,\n            anpassungsmenge: neueLeistung - aktuelleLeistung,\n            updateSent: sendUpdate,\n            triggerSource,\n            isEarlyMorning,\n            isHighLoadSituation,                 // NEU: Flag für Lastspitze\n            timeSinceLastUpdate: now - lastUpdateTime,\n            stabilityCounter: global.solarControl.stabilityCounter, // NEU: Stabilitätszähler\n            isSmallIncrementRange: (aktuelleLeistung >= 50 && aktuelleLeistung <= 275)\n        }\n    };\n}\n\nreturn calculateNewSolarPower(msg);","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":910,"y":200,"wires":[["3e72c8ad9e3157b2","749682f63dbf2565","d58d6cc0de63c09f"]]}]

Der WR nimmt sich genau das, was er maximal kann. Du kannst Module anschließen, die 10.000 W gerade liefern könnten, der WR bedient sich mit 500 W und nicht mehr.

Aber nur so lange die Spannungen den Specs des WR bleibt. Und bei geringer Abnahme würden die Panels zur Heizkraftwerken werden… was ist die Lebensdauer mit hoher Wahrscheinlichkeit ziemlich schaden würde.

Interessant! Gerade das macht er bei mir leider nicht. Ich habe aber nun schon in einigen anderen Foren/Threads gelesen, dass ich wohl mit den Wechselrichter-Einstellungen in der ShinePhone App herumprobieren sollte, da manchmal ein anderer konfigurierter Wechselrichter dann richtig funktioniert. Werde ich heute Nachmittag dann mal machen :slight_smile:

Vielen Dank auch für das Update deines Flows! Da werde ich mir wohl einige Inspirationen her holen für meinen :+1:

Moin.
Ich realisiere gerade ein ähnliches setup, mit vier Leapton 500w bifazialen Modulen, einem AP EZ-1M und einem Noah 2000, kurz das Solakon Paket.

Das ganze kommt auf das Werkstattdach, da es bei mir der einzige Ort ist mit ausreichend (hoffentlich) Sonne, habe hier leider viele Bäume rund um das Haus und Grundstück.
Ausgerichtet wird das ganze Richtung Süd-Südwest.

Bei einem Test, der Akku kommt ja schon vorgeladen mit 50%, funktionierte das ganze mit eingestellten 800w schon mal, aber habe es nach ca. 10 Minuten wieder abgeschaltet, hatte ja noch keine Module dran.

Da ich keine Ahnung von Shelly, Tibber und Co habe, werde ich einfach die Grundlast abschätzen und den Noah versuchen so einzurichten, dass er erst sich selbst lädt, dann den Überschuss einspeist und abends gemächlich die Energie wieder abgibt. Mir geht es auch gar nicht darum, alles zu optimieren bis ins kleinste Detail, sondern um die Stromkosten zu reduzieren, da das Haus ein 3-Kammer-System hat und nicht an die Kanalisation angeschlossen ist, da laufen bis zu drei Pumpen, die die Schose den ganzen Tag lang umwälzen.

Ja, wenn du eine dauerhafte recht hohe Grundlast hast, braucht es keine Regelung der Einspeisung, da kannst du einfach konstant z.B. 200 W vom Noah zu den Zeiten einzuspeisen, wo keine Sonne scheint. Damit bekommst du die ganze Ladung jeden Tag wieder los, die dann auch schon von der Grundlast direkt verbraucht wird.

Update zur Solar-Steuerung: Code-Optimierung und neue Funktionen

Hallo zusammen,

ich habe den Steuerungscode für unseren EZ1-M Wechselrichter komplett überarbeitet und möchte euch die wichtigsten Verbesserungen vorstellen. (Claude Sonnet 3.7)

Übersicht der Hauptverbesserungen

  1. Verbesserte Codestruktur und Wartbarkeit
  • Einführung von CONSTANTS-Objekt für zentrale Konfiguration
  • Bessere Kommentierung und Strukturierung der Entscheidungslogik
  • Klarere Benennung von Variablen und Funktionen
  1. Optimierte Leistungsregelung
  • Gestaffelte Anpassungsschritte je nach Netzbezug und Betriebsbereich
  • Feinere Kontrolle im Grundlastbereich (75-275W)
  • Verbesserte Berücksichtigung des Leistungsdefizits zwischen Soll- und Ist-Werten
  1. Erhöhte Systemstabilität
  • Failsafe-Mechanismus bei fehlenden Sensordaten
  • Optimierte Stabilität durch angepasste Update-Intervalle
  • Zähler für stabile Betriebsbedingungen für konsistentere Regelung
  1. Spezielle Betriebsszenarien
  • Verbesserte Reaktion auf Lastspitzen
  • Optimierte Strategie bei hohem SOC (≥98%)
  • Intelligentere Anpassung bei signifikanten Leistungsdefiziten

Details zur Strategie-Verbesserung

Die Entscheidungslogik wurde komplett neu strukturiert und folgt jetzt einer klaren Prioritätsreihenfolge:

  1. Failsafe bei fehlenden Daten: Setzt eine sichere Grundlast von 175W
  2. Hochlastsituationen: Sofortige aggressive Reaktion bei hohem Netzbezug
  3. Hoher Batterieladezustand: Maximierung des Eigenverbrauchs bei SOC ≥ 98%
  4. Ausgleich bei Leistungsdefizit: Intelligentere Kompensation wenn Soll- und Ist-Leistung stark abweichen
  5. Standardbetrieb mit gestaffelter Regelung:
  • Unterschiedliche Regelstrategien je nach Grundlastbereich
  • Feiner abgestufte Anpassungsschritte (5W, 10W, 30W, 50W) je nach Netzbezug
  • Verbesserte Einspeisung-Reduzierung mit Mindestleistung von 100W
// Systemkonstanten für bessere Wartbarkeit und Lesbarkeit

const CONSTANTS = {

// Leistungsgrenzen

MAX_POWER: 780, // Maximale Wechselrichterleistung in Watt

MIN_POWER: 75, // Minimale Wechselrichterleistung in Watt

// Betriebsbereiche

GRUNDLAST_MIN: 75, // Untere Grenze für Grundlastbereich (W)

GRUNDLAST_MAX: 275, // Obere Grenze für Grundlastbereich (W)

// Schwellwerte

HIGH_SOC: 98, // Schwellwert für hohen SOC (%)

MIN_GRID_EXPORT: 15, // Minimale Einspeisung für Leistungsreduzierung (W)

LOW_GRID_IMPORT: 20, // Schwellwert für niedrigen Netzbezug (W)

MEDIUM_GRID_IMPORT: 50, // Schwellwert für mittleren Netzbezug (W)

HIGH_GRID_IMPORT: 100, // Schwellwert für hohen Netzbezug (W)

VERY_HIGH_GRID_IMPORT: 1000, // Schwellwert für sehr hohen Netzbezug (W)

FAILSAFE_POWER: 175, // Fallback-Leistung bei fehlenden Daten (W)

// Zeitintervalle für Stabilitätssteuerung

LOAD_PEAK_INTERVAL: 3000, // Intervall für Lastspitzen (ms)

NORMAL_UPDATE_INTERVAL: 15000, // Normales Update-Intervall (ms)

STABLE_UPDATE_INTERVAL: 30000, // Verlängertes Intervall bei Stabilität (ms) - auf 30s reduziert

// Stabilitätskonfiguration

STABILITY_THRESHOLD: 8, // Anzahl stabiler Updates für verlängerte Intervalle

LARGE_CHANGE_THRESHOLD: 50 // Schwellwert für große Änderungen (W)

};

// Initialisierung des globalen Kontexts

if (!global.solarControl) {

global.solarControl = {

lastPower: 0, // Zuletzt gesetzte Leistung

lastUpdateTime: 0, // Zeitstempel der letzten Aktualisierung

peakLoadStartTime: 0, // Zeitstempel des Beginns einer Lastspitze

stabilityCounter: 0, // Zähler für stabile Betriebsbedingungen

consecutiveStableUpdates: 0 // Zähler für aufeinanderfolgende stabile Updates

};

}

// Hauptfunktion zur Berechnung der neuen Wechselrichterleistung

function calculateNewSolarPower(msg) {

// Sensorwerte extrahieren und parsen

const energie = parseFloat(msg.payload.tibber_energie) || 0; // Netzbezug (W)

const stromerzeugung = parseFloat(msg.payload.tibber_stromerzeugung) || 0; // Netzeinspeisung (W)

const soc = parseFloat(msg.payload.batterie_soc) || 0; // Batterieladezustand (%)

const solarPower = parseFloat(msg.payload.solar_power) || 0; // Solarleistung (W)

const aktuelleLeistung = parseFloat(msg.payload.solar_maximale_leistung) || 0; // Eingestellte WR-Leistung (W)

const tatsaechlicheLeistung = parseFloat(msg.payload.solar_gesamtleistung) || 0; // Tatsächliche WR-Leistung (W)

const strompreis = parseFloat(msg.payload.tibber_strompreis) || 1.0; // Strompreis (€/kWh)

const isHighLoadSituation = msg.payload.isHighLoadSituation || false; // Lastspitze Flag

// Aktuelle Zeit für Zeitstempelvergleiche

const now = Date.now();

// Erstinitialisierung bei erstem Lauf

if (global.solarControl.lastPower === 0) {

global.solarControl.lastPower = aktuelleLeistung;

}

// Leistungsdefizit berechnen (Differenz zwischen Soll und Ist)

const leistungsDefizit = aktuelleLeistung - tatsaechlicheLeistung;

// Strategieauswahl und Leistungsberechnung

let neueLeistung = aktuelleLeistung;

let anpassungsgrund = "Keine Änderung";

/* ===== ENTSCHEIDUNGSLOGIK IN PRIORITÄTSREIHENFOLGE ===== */

// 1. Failsafe: Prüfung auf fehlende Daten (Tibber API, NEC Noah, EZ1-M)

if (energie === 0 && stromerzeugung === 0 && (solarPower === 0 || tatsaechlicheLeistung === 0)) {

neueLeistung = CONSTANTS.FAILSAFE_POWER; // Failsafe: Sichere Grundlast (standardmäßig 175W)

anpassungsgrund = `Failsafe aktiviert: Fehlende Daten von Sensoren, setze sichere Grundlast (${CONSTANTS.FAILSAFE_POWER}W)`;

}

// 2. Prüfung auf Hochlastsituation (sofortige Reaktion erforderlich)

else if (isHighLoadSituation) {

if (energie > CONSTANTS.VERY_HIGH_GRID_IMPORT) {

// Bei extremem Verbrauch sofort auf Maximum

neueLeistung = CONSTANTS.MAX_POWER;

anpassungsgrund = `Extreme Lastspitze: ${energie}W Netzbezug, maximale WR-Leistung aktiviert`;

} else {

// Bei hohem Verbrauch und WR nahe Volllast, schrittweise erhöhen

neueLeistung = Math.min(aktuelleLeistung + 50, CONSTANTS.MAX_POWER);

anpassungsgrund = `Lastspitze: WR bei ${tatsaechlicheLeistung}W, erhöhe auf ${neueLeistung}W`;

}

global.solarControl.peakLoadStartTime = now;

}

// 3. Strategie bei vollem Akku (SOC ≥ 98%)

else if (soc >= CONSTANTS.HIGH_SOC) {

// Ziel: Batterieladen vermeiden und Eigenverbrauch maximieren

neueLeistung = Math.min(solarPower + energie, CONSTANTS.MAX_POWER);

anpassungsgrund = `SOC ≥ ${CONSTANTS.HIGH_SOC}%, setze Leistung auf Solar-Input + Netzbezug`;

}

// 4. Ausgleich bei signifikantem Leistungsdefizit

else if (leistungsDefizit > 50 && energie > 25) {

// WR liefert nicht die gewünschte Leistung, aber Netzbezug besteht

neueLeistung = Math.min(aktuelleLeistung + energie + leistungsDefizit * 0.5, CONSTANTS.MAX_POWER);

anpassungsgrund = `WR-Leistungsdefizit: ${Math.round(leistungsDefizit)}W, Erhöhung um ${Math.round(energie + leistungsDefizit * 0.5)}W`;

}

// 5. Standardstrategie für normale Betriebsbedingungen

else {

// Prüfen, ob wir uns im Grundlastbereich befinden (75-275W)

const isGrundlastBereich = (aktuelleLeistung >= CONSTANTS.GRUNDLAST_MIN &&

aktuelleLeistung <= CONSTANTS.GRUNDLAST_MAX);

// Berücksichtigung der tatsächlichen Leistung

// Bei signifikant niedrigerer Ist-Leistung stärkere Anpassung vornehmen

const leistungsFaktor = tatsaechlicheLeistung < aktuelleLeistung * 0.9 ? 1.2 : 1.0;

// 5.1 Sonderfall: Hoher Netzbezug außerhalb des Grundlastbereichs

if (energie > CONSTANTS.MEDIUM_GRID_IMPORT && !isGrundlastBereich) {

neueLeistung = aktuelleLeistung + Math.round(energie * 0.8 * leistungsFaktor);

anpassungsgrund = `Hoher Netzbezug (${energie}W), schnelle Anpassung mit Faktor ${leistungsFaktor.toFixed(1)}`;

}

// 5.2 Geringer Netzbezug ohne Einspeisung - stabil, keine Änderung

else if (energie > 0 &&

energie < CONSTANTS.LOW_GRID_IMPORT &&

stromerzeugung < CONSTANTS.MIN_GRID_EXPORT) {

// Keine Änderung, stabiler Zustand

anpassungsgrund = "Stabile Bedingungen, keine Anpassung nötig";

}

// 5.3 Verbesserte Feinabstimmung im Grundlastbereich (75-275W)

else if (isGrundlastBereich) {

if (energie >= 25 || stromerzeugung >= CONSTANTS.MIN_GRID_EXPORT) {

// Netzbezug: Leistung mit gestaffelten Schritten erhöhen

if (energie >= 25) {

let schrittGroesse;

// Deutlich gestaffelte Anpassung je nach Netzbezug

if (energie >= 100) {

// Sehr hoher Netzbezug im Grundlastbereich: Große Schritte

schrittGroesse = Math.min(50, Math.round(energie * 0.5));

anpassungsgrund = `Großer Schritt (${schrittGroesse}W) wegen hohem Netzbezug von ${energie}W`;

} else if (energie >= 50) {

// Mittlerer Netzbezug: Mittlere Schritte

schrittGroesse = Math.min(30, Math.round(energie * 0.4));

anpassungsgrund = `Mittlerer Schritt (${schrittGroesse}W) bei Netzbezug von ${energie}W`;

} else {

// Geringer Netzbezug: Kleine Schritte wie bisher

schrittGroesse = tatsaechlicheLeistung < aktuelleLeistung * 0.9 ? 10 : 5;

anpassungsgrund = `Kleiner Schritt (${schrittGroesse}W) bei geringem Netzbezug`;

}

// Zusätzlicher Faktor für Fälle mit Leistungsdefizit

if (leistungsDefizit < -15) {

// WR liefert mehr als eingestellt - berücksichtigen bei Anpassung

schrittGroesse = Math.round(schrittGroesse * 1.3);

anpassungsgrund += ` (erhöht wegen Leistungsdefizit von ${Math.round(leistungsDefizit)}W)`;

}

neueLeistung = aktuelleLeistung + schrittGroesse;

}

// Einspeisung: Leistung reduzieren (unverändert)

else if (stromerzeugung >= CONSTANTS.MIN_GRID_EXPORT) {

neueLeistung = aktuelleLeistung - 5;

anpassungsgrund = `5W-Schritt nach unten (Netzeinspeisung ${stromerzeugung}W)`;

}

}

}

// 5.4 Moderate Anpassung bei mittlerem Netzbezug

else if (energie >= CONSTANTS.LOW_GRID_IMPORT && energie <= CONSTANTS.MEDIUM_GRID_IMPORT) {

neueLeistung = aktuelleLeistung + Math.round(energie * 0.7 * leistungsFaktor);

anpassungsgrund = `Moderater Netzbezug (${energie}W), angepasste Erhöhung um ${Math.round(energie * 0.7 * leistungsFaktor)}W`;

}

// 5.5 Direkte Anpassung bei höherem Netzbezug

else if (energie > CONSTANTS.MEDIUM_GRID_IMPORT) {

neueLeistung = aktuelleLeistung + Math.round(energie * leistungsFaktor);

anpassungsgrund = `Direkte Anpassung bei Netzbezug (${energie}W) um ${Math.round(energie * leistungsFaktor)}W`;

}

// 5.6 Anpassung bei höherer Grundleistung und moderatem Verbrauch

else if (aktuelleLeistung > CONSTANTS.GRUNDLAST_MAX && energie >= 25) {

neueLeistung = aktuelleLeistung + Math.round(energie * leistungsFaktor);

anpassungsgrund = `Erhöhung bei ${aktuelleLeistung}W Grundleistung und ${energie}W Netzbezug`;

}

// 5.7 Reduzierung bei Netzeinspeisung

else if (stromerzeugung >= CONSTANTS.MIN_GRID_EXPORT) {

// Minimum 100W beibehalten

const reduktion = Math.min(stromerzeugung, aktuelleLeistung - 100);

neueLeistung = Math.max(aktuelleLeistung - reduktion, 100);

anpassungsgrund = `Reduziere Leistung um ${Math.round(reduktion)}W wegen Netzeinspeisung (${stromerzeugung}W)`;

}

}

// IMMER auf 5W-Schritte runden für konsistente Steuerung

neueLeistung = Math.round(neueLeistung / 5) * 5;

// Begrenzung auf erlaubten Betriebsbereich

neueLeistung = Math.max(CONSTANTS.MIN_POWER, Math.min(CONSTANTS.MAX_POWER, neueLeistung));

// Bestimmen, ob ein Update gesendet werden soll (verbesserte Stabilität)

let sendUpdate = false;

const lastUpdateTime = global.solarControl.lastUpdateTime || 0;

const timeSinceLastUpdate = now - lastUpdateTime;

if (neueLeistung !== aktuelleLeistung) {

// 1. Sofortige Reaktion bei Lastspitzen oder großen Änderungen

if (isHighLoadSituation || Math.abs(neueLeistung - aktuelleLeistung) > CONSTANTS.LARGE_CHANGE_THRESHOLD) {

sendUpdate = true;

global.solarControl.stabilityCounter = 0; // Stabilitätszähler zurücksetzen

global.solarControl.consecutiveStableUpdates = 0;

}

// 2. Bei stabilen Bedingungen längere Intervalle zwischen Updates

else if (global.solarControl.stabilityCounter >= CONSTANTS.STABILITY_THRESHOLD) {

// Nach mehreren stabilen Intervallen seltener anpassen

if (timeSinceLastUpdate >= CONSTANTS.STABLE_UPDATE_INTERVAL) {

sendUpdate = true;

global.solarControl.consecutiveStableUpdates++;

// Stabilitätszähler nicht vollständig zurücksetzen bei kleinen Änderungen

global.solarControl.stabilityCounter = Math.max(global.solarControl.stabilityCounter - 2, 0);

}

}

// 3. Normale Aktualisierung nach Mindestwartezeit

else if (timeSinceLastUpdate >= CONSTANTS.NORMAL_UPDATE_INTERVAL) {

sendUpdate = true;

global.solarControl.stabilityCounter++;

global.solarControl.consecutiveStableUpdates = 0;

}

} else {

// Bei "keine Änderung" Stabilitätszähler erhöhen

global.solarControl.stabilityCounter++;

}

// Status aktualisieren, wenn Update gesendet wird

if (sendUpdate) {

global.solarControl.lastPower = neueLeistung;

global.solarControl.lastUpdateTime = now;

}

// Nach der Berechnung das Update-Flag zurücksetzen

const storage = flow.get('sensorData') || {};

storage.updateRequested = false;

flow.set('sensorData', storage);

// Rückgabe des Ergebnisses mit Zusatzinformationen für Debugging

return {

payload: neueLeistung, // Berechnete Sollleistung für den WR

entity_id: 'number.solar_maximale_leistung', // Ziel-Entity für HA

sendUpdate: sendUpdate, // Flag für API-Aufruf

debugInfo: { // Debug-Informationen

energie,

stromerzeugung,

soc,

solarPower,

strompreis,

aktuelleLeistung,

tatsaechlicheLeistung,

leistungsDefizit,

neueLeistung,

anpassungsgrund,

anpassungsmenge: neueLeistung - aktuelleLeistung,

updateSent: sendUpdate,

isHighLoadSituation,

timeSinceLastUpdate,

stabilityCounter: global.solarControl.stabilityCounter,

consecutiveStableUpdates: global.solarControl.consecutiveStableUpdates,

isGrundlastBereich: (aktuelleLeistung >= CONSTANTS.GRUNDLAST_MIN &&

aktuelleLeistung <= CONSTANTS.GRUNDLAST_MAX)

}

};

}

// Hauptfunktion aufrufen und Ergebnis zurückgeben

return calculateNewSolarPower(msg);

Verbesserte Stabilität

Die neue Version reduziert unnötige API-Aufrufe durch:

  • Längere stabile Intervalle (15s normal, 30s bei stabilen Bedingungen)
  • Intelligente Stabilitätserkennung durch Zähler für konsekutive stabile Updates
  • Sofortige Reaktion nur bei signifikanten Änderungen oder Lastspitzen
[{"id":"02638474a170add7","type":"function","z":"638a1d646d26639d","name":"Berechne neue EZ1-M Leistung","func":"// Systemkonstanten für bessere Wartbarkeit und Lesbarkeit\nconst CONSTANTS = {\n    // Leistungsgrenzen\n    MAX_POWER: 780,                // Maximale Wechselrichterleistung in Watt\n    MIN_POWER: 75,                 // Minimale Wechselrichterleistung in Watt\n    \n    // Betriebsbereiche\n    GRUNDLAST_MIN: 75,             // Untere Grenze für Grundlastbereich (W)\n    GRUNDLAST_MAX: 275,            // Obere Grenze für Grundlastbereich (W)\n    \n    // Schwellwerte\n    HIGH_SOC: 98,                  // Schwellwert für hohen SOC (%)\n    MIN_GRID_EXPORT: 15,           // Minimale Einspeisung für Leistungsreduzierung (W)\n    LOW_GRID_IMPORT: 20,           // Schwellwert für niedrigen Netzbezug (W)\n    MEDIUM_GRID_IMPORT: 50,        // Schwellwert für mittleren Netzbezug (W)\n    HIGH_GRID_IMPORT: 100,         // Schwellwert für hohen Netzbezug (W)\n    VERY_HIGH_GRID_IMPORT: 1000,   // Schwellwert für sehr hohen Netzbezug (W)\n    FAILSAFE_POWER: 175,           // Fallback-Leistung bei fehlenden Daten (W)\n    \n    // Zeitintervalle für Stabilitätssteuerung\n    LOAD_PEAK_INTERVAL: 3000,      // Intervall für Lastspitzen (ms)\n    NORMAL_UPDATE_INTERVAL: 15000,  // Normales Update-Intervall (ms)\n    STABLE_UPDATE_INTERVAL: 30000,  // Verlängertes Intervall bei Stabilität (ms) - auf 30s reduziert\n    \n    // Stabilitätskonfiguration\n    STABILITY_THRESHOLD: 8,         // Anzahl stabiler Updates für verlängerte Intervalle\n    LARGE_CHANGE_THRESHOLD: 50      // Schwellwert für große Änderungen (W)\n};\n\n// Initialisierung des globalen Kontexts\nif (!global.solarControl) {\n    global.solarControl = {\n        lastPower: 0,                // Zuletzt gesetzte Leistung\n        lastUpdateTime: 0,           // Zeitstempel der letzten Aktualisierung\n        peakLoadStartTime: 0,        // Zeitstempel des Beginns einer Lastspitze\n        stabilityCounter: 0,         // Zähler für stabile Betriebsbedingungen\n        consecutiveStableUpdates: 0  // Zähler für aufeinanderfolgende stabile Updates\n    };\n}\n\n// Hauptfunktion zur Berechnung der neuen Wechselrichterleistung\nfunction calculateNewSolarPower(msg) {\n    // Sensorwerte extrahieren und parsen\n    const energie = parseFloat(msg.payload.tibber_energie) || 0;           // Netzbezug (W)\n    const stromerzeugung = parseFloat(msg.payload.tibber_stromerzeugung) || 0; // Netzeinspeisung (W)\n    const soc = parseFloat(msg.payload.batterie_soc) || 0;              // Batterieladezustand (%)\n    const solarPower = parseFloat(msg.payload.solar_power) || 0;        // Solarleistung (W)\n    const aktuelleLeistung = parseFloat(msg.payload.solar_maximale_leistung) || 0; // Eingestellte WR-Leistung (W)\n    const tatsaechlicheLeistung = parseFloat(msg.payload.solar_gesamtleistung) || 0; // Tatsächliche WR-Leistung (W)\n    const strompreis = parseFloat(msg.payload.tibber_strompreis) || 1.0;    // Strompreis (€/kWh)\n    const isHighLoadSituation = msg.payload.isHighLoadSituation || false; // Lastspitze Flag\n    \n    // Aktuelle Zeit für Zeitstempelvergleiche\n    const now = Date.now();\n    \n    // Erstinitialisierung bei erstem Lauf\n    if (global.solarControl.lastPower === 0) {\n        global.solarControl.lastPower = aktuelleLeistung;\n    }\n    \n    // Leistungsdefizit berechnen (Differenz zwischen Soll und Ist)\n    const leistungsDefizit = aktuelleLeistung - tatsaechlicheLeistung;\n    \n    // Strategieauswahl und Leistungsberechnung\n    let neueLeistung = aktuelleLeistung;\n    let anpassungsgrund = \"Keine Änderung\";\n    \n    /* ===== ENTSCHEIDUNGSLOGIK IN PRIORITÄTSREIHENFOLGE ===== */\n    \n    // 1. Failsafe: Prüfung auf fehlende Daten (Tibber API, NEC Noah, EZ1-M)\n    if (energie === 0 && stromerzeugung === 0 && (solarPower === 0 || tatsaechlicheLeistung === 0)) {\n        neueLeistung = CONSTANTS.FAILSAFE_POWER; // Failsafe: Sichere Grundlast (standardmäßig 175W)\n        anpassungsgrund = `Failsafe aktiviert: Fehlende Daten von Sensoren, setze sichere Grundlast (${CONSTANTS.FAILSAFE_POWER}W)`;\n    }\n    // 2. Prüfung auf Hochlastsituation (sofortige Reaktion erforderlich)\n    else if (isHighLoadSituation) {\n        if (energie > CONSTANTS.VERY_HIGH_GRID_IMPORT) {\n            // Bei extremem Verbrauch sofort auf Maximum\n            neueLeistung = CONSTANTS.MAX_POWER;\n            anpassungsgrund = `Extreme Lastspitze: ${energie}W Netzbezug, maximale WR-Leistung aktiviert`;\n        } else {\n            // Bei hohem Verbrauch und WR nahe Volllast, schrittweise erhöhen\n            neueLeistung = Math.min(aktuelleLeistung + 50, CONSTANTS.MAX_POWER);\n            anpassungsgrund = `Lastspitze: WR bei ${tatsaechlicheLeistung}W, erhöhe auf ${neueLeistung}W`;\n        }\n        global.solarControl.peakLoadStartTime = now;\n    }\n    // 3. Strategie bei vollem Akku (SOC ≥ 98%)\n    else if (soc >= CONSTANTS.HIGH_SOC) {\n        // Ziel: Batterieladen vermeiden und Eigenverbrauch maximieren\n        neueLeistung = Math.min(solarPower + energie, CONSTANTS.MAX_POWER);\n        anpassungsgrund = `SOC ≥ ${CONSTANTS.HIGH_SOC}%, setze Leistung auf Solar-Input + Netzbezug`;\n    }\n    // 4. Ausgleich bei signifikantem Leistungsdefizit\n    else if (leistungsDefizit > 50 && energie > 25) {\n        // WR liefert nicht die gewünschte Leistung, aber Netzbezug besteht\n        neueLeistung = Math.min(aktuelleLeistung + energie + leistungsDefizit * 0.5, CONSTANTS.MAX_POWER);\n        anpassungsgrund = `WR-Leistungsdefizit: ${Math.round(leistungsDefizit)}W, Erhöhung um ${Math.round(energie + leistungsDefizit * 0.5)}W`;\n    }\n    // 5. Standardstrategie für normale Betriebsbedingungen\n    else {\n        // Prüfen, ob wir uns im Grundlastbereich befinden (75-275W)\n        const isGrundlastBereich = (aktuelleLeistung >= CONSTANTS.GRUNDLAST_MIN && \n                                   aktuelleLeistung <= CONSTANTS.GRUNDLAST_MAX);\n        \n        // Berücksichtigung der tatsächlichen Leistung\n        // Bei signifikant niedrigerer Ist-Leistung stärkere Anpassung vornehmen\n        const leistungsFaktor = tatsaechlicheLeistung < aktuelleLeistung * 0.9 ? 1.2 : 1.0;\n        \n        // 5.1 Sonderfall: Hoher Netzbezug außerhalb des Grundlastbereichs\n        if (energie > CONSTANTS.MEDIUM_GRID_IMPORT && !isGrundlastBereich) {\n            neueLeistung = aktuelleLeistung + Math.round(energie * 0.8 * leistungsFaktor);\n            anpassungsgrund = `Hoher Netzbezug (${energie}W), schnelle Anpassung mit Faktor ${leistungsFaktor.toFixed(1)}`;\n        }\n        // 5.2 Geringer Netzbezug ohne Einspeisung - stabil, keine Änderung\n        else if (energie > 0 && \n                 energie < CONSTANTS.LOW_GRID_IMPORT && \n                 stromerzeugung < CONSTANTS.MIN_GRID_EXPORT) {\n            // Keine Änderung, stabiler Zustand\n            anpassungsgrund = \"Stabile Bedingungen, keine Anpassung nötig\";\n        }\n        // 5.3 Verbesserte Feinabstimmung im Grundlastbereich (75-275W)\n        else if (isGrundlastBereich) {\n            if (energie >= 25 || stromerzeugung >= CONSTANTS.MIN_GRID_EXPORT) {\n                // Netzbezug: Leistung mit gestaffelten Schritten erhöhen\n                if (energie >= 25) {\n                    let schrittGroesse;\n                    \n                    // Deutlich gestaffelte Anpassung je nach Netzbezug\n                    if (energie >= 100) {\n                        // Sehr hoher Netzbezug im Grundlastbereich: Große Schritte\n                        schrittGroesse = Math.min(50, Math.round(energie * 0.5));\n                        anpassungsgrund = `Großer Schritt (${schrittGroesse}W) wegen hohem Netzbezug von ${energie}W`;\n                    } else if (energie >= 50) {\n                        // Mittlerer Netzbezug: Mittlere Schritte\n                        schrittGroesse = Math.min(30, Math.round(energie * 0.4));\n                        anpassungsgrund = `Mittlerer Schritt (${schrittGroesse}W) bei Netzbezug von ${energie}W`;\n                    } else {\n                        // Geringer Netzbezug: Kleine Schritte wie bisher\n                        schrittGroesse = tatsaechlicheLeistung < aktuelleLeistung * 0.9 ? 10 : 5;\n                        anpassungsgrund = `Kleiner Schritt (${schrittGroesse}W) bei geringem Netzbezug`;\n                    }\n                    \n                    // Zusätzlicher Faktor für Fälle mit Leistungsdefizit\n                    if (leistungsDefizit < -15) {\n                        // WR liefert mehr als eingestellt - berücksichtigen bei Anpassung\n                        schrittGroesse = Math.round(schrittGroesse * 1.3);\n                        anpassungsgrund += ` (erhöht wegen Leistungsdefizit von ${Math.round(leistungsDefizit)}W)`;\n                    }\n                    \n                    neueLeistung = aktuelleLeistung + schrittGroesse;\n                }\n                // Einspeisung: Leistung reduzieren (unverändert)\n                else if (stromerzeugung >= CONSTANTS.MIN_GRID_EXPORT) {\n                    neueLeistung = aktuelleLeistung - 5;\n                    anpassungsgrund = `5W-Schritt nach unten (Netzeinspeisung ${stromerzeugung}W)`;\n                }\n            }\n        }\n        // 5.4 Moderate Anpassung bei mittlerem Netzbezug\n        else if (energie >= CONSTANTS.LOW_GRID_IMPORT && energie <= CONSTANTS.MEDIUM_GRID_IMPORT) {\n            neueLeistung = aktuelleLeistung + Math.round(energie * 0.7 * leistungsFaktor);\n            anpassungsgrund = `Moderater Netzbezug (${energie}W), angepasste Erhöhung um ${Math.round(energie * 0.7 * leistungsFaktor)}W`;\n        }\n        // 5.5 Direkte Anpassung bei höherem Netzbezug\n        else if (energie > CONSTANTS.MEDIUM_GRID_IMPORT) {\n            neueLeistung = aktuelleLeistung + Math.round(energie * leistungsFaktor);\n            anpassungsgrund = `Direkte Anpassung bei Netzbezug (${energie}W) um ${Math.round(energie * leistungsFaktor)}W`;\n        }\n        // 5.6 Anpassung bei höherer Grundleistung und moderatem Verbrauch\n        else if (aktuelleLeistung > CONSTANTS.GRUNDLAST_MAX && energie >= 25) {\n            neueLeistung = aktuelleLeistung + Math.round(energie * leistungsFaktor);\n            anpassungsgrund = `Erhöhung bei ${aktuelleLeistung}W Grundleistung und ${energie}W Netzbezug`;\n        }\n        // 5.7 Reduzierung bei Netzeinspeisung\n        else if (stromerzeugung >= CONSTANTS.MIN_GRID_EXPORT) {\n            // Minimum 100W beibehalten\n            const reduktion = Math.min(stromerzeugung, aktuelleLeistung - 100);\n            neueLeistung = Math.max(aktuelleLeistung - reduktion, 100);\n            anpassungsgrund = `Reduziere Leistung um ${Math.round(reduktion)}W wegen Netzeinspeisung (${stromerzeugung}W)`;\n        }\n    }\n\n    // IMMER auf 5W-Schritte runden für konsistente Steuerung\n    neueLeistung = Math.round(neueLeistung / 5) * 5;\n    \n    // Begrenzung auf erlaubten Betriebsbereich\n    neueLeistung = Math.max(CONSTANTS.MIN_POWER, Math.min(CONSTANTS.MAX_POWER, neueLeistung));\n    \n    // Bestimmen, ob ein Update gesendet werden soll (verbesserte Stabilität)\n    let sendUpdate = false;\n    const lastUpdateTime = global.solarControl.lastUpdateTime || 0;\n    const timeSinceLastUpdate = now - lastUpdateTime;\n    \n    if (neueLeistung !== aktuelleLeistung) {\n        // 1. Sofortige Reaktion bei Lastspitzen oder großen Änderungen\n        if (isHighLoadSituation || Math.abs(neueLeistung - aktuelleLeistung) > CONSTANTS.LARGE_CHANGE_THRESHOLD) {\n            sendUpdate = true;\n            global.solarControl.stabilityCounter = 0; // Stabilitätszähler zurücksetzen\n            global.solarControl.consecutiveStableUpdates = 0;\n        }\n        // 2. Bei stabilen Bedingungen längere Intervalle zwischen Updates\n        else if (global.solarControl.stabilityCounter >= CONSTANTS.STABILITY_THRESHOLD) {\n            // Nach mehreren stabilen Intervallen seltener anpassen\n            if (timeSinceLastUpdate >= CONSTANTS.STABLE_UPDATE_INTERVAL) {\n                sendUpdate = true;\n                global.solarControl.consecutiveStableUpdates++;\n                // Stabilitätszähler nicht vollständig zurücksetzen bei kleinen Änderungen\n                global.solarControl.stabilityCounter = Math.max(global.solarControl.stabilityCounter - 2, 0);\n            }\n        }\n        // 3. Normale Aktualisierung nach Mindestwartezeit\n        else if (timeSinceLastUpdate >= CONSTANTS.NORMAL_UPDATE_INTERVAL) {\n            sendUpdate = true;\n            global.solarControl.stabilityCounter++;\n            global.solarControl.consecutiveStableUpdates = 0;\n        }\n    } else {\n        // Bei \"keine Änderung\" Stabilitätszähler erhöhen\n        global.solarControl.stabilityCounter++;\n    }\n    \n    // Status aktualisieren, wenn Update gesendet wird\n    if (sendUpdate) {\n        global.solarControl.lastPower = neueLeistung;\n        global.solarControl.lastUpdateTime = now;\n    }\n    \n    // Nach der Berechnung das Update-Flag zurücksetzen\n    const storage = flow.get('sensorData') || {};\n    storage.updateRequested = false;\n    flow.set('sensorData', storage);\n\n    // Rückgabe des Ergebnisses mit Zusatzinformationen für Debugging\n    return {\n        payload: neueLeistung,                   // Berechnete Sollleistung für den WR\n        entity_id: 'number.solar_maximale_leistung', // Ziel-Entity für HA\n        sendUpdate: sendUpdate,                   // Flag für API-Aufruf\n        debugInfo: {                             // Debug-Informationen\n            energie,\n            stromerzeugung,\n            soc,\n            solarPower,\n            strompreis,\n            aktuelleLeistung,\n            tatsaechlicheLeistung,\n            leistungsDefizit,\n            neueLeistung,\n            anpassungsgrund,\n            anpassungsmenge: neueLeistung - aktuelleLeistung,\n            updateSent: sendUpdate,\n            isHighLoadSituation,\n            timeSinceLastUpdate,\n            stabilityCounter: global.solarControl.stabilityCounter,\n            consecutiveStableUpdates: global.solarControl.consecutiveStableUpdates,\n            isGrundlastBereich: (aktuelleLeistung >= CONSTANTS.GRUNDLAST_MIN && \n                              aktuelleLeistung <= CONSTANTS.GRUNDLAST_MAX)\n        }\n    };\n}\n\n// Hauptfunktion aufrufen und Ergebnis zurückgeben\nreturn calculateNewSolarPower(msg);","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":910,"y":200,"wires":[["d31c244d73a5dd73","a2d8a7b2a494e125","0015b5d7cfce55f6"]]}]```

Hallo aspiro.

Zuerst nochmal vielen Dank für dein Werk und dass du dies teilst.

Das Ganze funktioniert soweit super aber:

Sobald der Speicher voll ist wird nicht die Ausgangsleistung hoch geregelt.
Siehe Screenshot. Zur Zeit ist volle Sonne die nicht genutzt wird.

Oder das geht bei mir nicht richtig weil meine Entitäten von EZ1 anders heißen.

Wo finde ich die Action number.set_value im HA?

1 „Gefällt mir“