NEWS
[Vorlage] Hoymiles Wechselrichter Begrenzung mit openDTU
-
Hi,
ich montiere aktuell eine PV mit 3x Hoymiles HMS-1600 Wechselrichtern.
Um diese zu begrenzen habe ich ein Script zusammengeschustert.
Ich habe es jetzt nur mal manuell getestet, habe aber keine Erfahrungen, wie es sich mit den WR verhält.Der Gedanke: Es wird bei jeder Änderung der Wechselrichterleistung geprüft, ob alle Wechselrichter gemeinsam nicht mehr als die vorgegebene Begrenzung einspeisen. Bei Änderung wird proportional die Begrenzung der WR geändert.
Vielleicht kann dieses Script schon mal jemand testen und gerne Kritik äußern!
Des Weiteren werde ich das Script step-by-step erweitern wollen. Gerade mit dem Thema der Nulleinspeisung, um die maximale Auslastung zu erreichen.
Hierzu wären Tipps und Erfahrungen von Euch wünschenswert.
z.B.: Erst wenn die Einspeisung größer 600/800W beträgt, werden die Wechselrichter begrenzt und/oder werden weitere Verbraucher hinzugeschalten, usw.Die Anzahl der Wechselrichter kann beliebig erweitert oder bis auf einen verkürzt werden.
Ebenfalls ist eine Berechnung für eine Batterieladung integriert.https://github.com/Schmakus/ioBroker-Scripte/blob/main/hoymiles-begrenzung.js
/* * Script zur Begrenzung von Hoymiles Wechselrichter in Verbindung mit openDTU * v0.0.3 2023/12/12 by Schmakus * http://www.github.com/Schmakus/ioBroker-Scripte/ * * Es werden folgende eigene Datenpunkte erstellt: * BegrenzungSoll: Vorgabe der Gesamtbegrenzung * BegrenzungIst: Aktuelle Begrenzung in Watt über alle Wechselrichter * BegrenzungIstInProzent: Aktuelle Begrenzung in Prozent über alle Wechselrichter * * Info: Die Objekt-IDs im Wechselrichter Objekt sind nur zum Test und müssen mit den richtigen Datenpunkten der Wechselrichter, usw. ersetzt werden! */ //Wo sollen die eigenen Datenpunkte angelegt werden? const path = "0_userdata.0.PV"; //** Angaben zu den Wechselrichtern */ const wechselrichter = { 1: { name: "Wechselrichter 1", maxLeistung: 1600, objLeistung: "0_userdata.0.PV.LeistungWR1", // Datenpunkt des Wechselrichters der aktuellen Leistung in Watt objBegrenzung: "0_userdata.0.PV.BegrenzungWR1", // Beispiel: opendtu.0.xxxxxx.power_control.limit_persistent_absolute) aktuelleLeistung: 0, // Nichts ändern! begrenzteLeistung: 0, // Nichts ändern! }, 2: { name: "Wechselrichter 2", maxLeistung: 1600, objLeistung: "0_userdata.0.PV.LeistungWR2", // Datenpunkt des Wechselrichters der aktuellen Leistung in Watt objBegrenzung: "0_userdata.0.PV.BegrenzungWR2", // Beispiel: opendtu.0.xxxxxx.power_control.limit_persistent_absolute) aktuelleLeistung: 0, // Nichts ändern! begrenzteLeistung: 0, // Nichts ändern! }, 3: { name: "Wechselrichter 3", maxLeistung: 1600, objLeistung: "0_userdata.0.PV.LeistungWR3", // Datenpunkt des Wechselrichters der aktuellen Leistung in Watt objBegrenzung: "0_userdata.0.PV.BegrenzungWR3", // Beispiel: opendtu.0.xxxxxx.power_control.limit_persistent_absolute) aktuelleLeistung: 0, // Nichts ändern! begrenzteLeistung: 0, // Nichts ändern! }, }; //Objekt-ID aktuelle Leistungsaufnahme des Hauses const objLeistungHaus = "0_userdata.0.PV.LeistungHaus"; // Hier den Datenpunkt zum Smartmeter angeben (Shelly, IR-Lesekopf, etc...) //Entscheidung ob Batterie geladen werden soll? const batterie = { laden: true, // true = laden, false = nicht laden maxLadeleistung: 600, //maximale Ladeleistung in Watt, unit: "volt", // "volt", "watt" or "percent" objLadungWatt: "0_userdata.0.PV.LadeleistungBatterie", // Hier den Datenpunkt zum Ladegerät angeben objLadungProzent: "0_userdata.0.PV.LadeleistungBatterieInProzent", // Hier den Datenpunkt zum Ladegerät angeben objLadungVolt: "0_userdata.0.PV.LadeleistungBatterieInVolt" // Hier den Datenpunkt zum Ladegerät angeben } //**************************** */ // AB HIER NICHTS MEHR ÄNDERN! // //**************************** */ //Variablen let wechselrichterLeistungen = []; let maximaleLeistungen = []; let entpreller = null; let maxGesamtLeistung = 0; let objBegrenzung = ""; let objBegrenzungIst = ""; let objBegrenzungIstProzent = ""; /** * Diese Funktion wird aufgerufen, wenn das System bereit ist. */ async function onReady() { await setStates(); await getWechselrichterLeistungen(); for (const wr of Object.values(wechselrichter)) { on({ id: wr.objLeistung, change: "ne" }, async (obj) => { wr.aktuelleLeistung = obj.state.val; console.log(`Aktuelle Leistung eines Wechselrichters aktualisiert. Wechselrichter: ${wr.name}, aktuelle Leistung: ${wr.aktuelleLeistung}`); await setWechselrichterLeistungen(); }); } const updateMaxGesamtLeistung = async (obj) => { maxGesamtLeistung = (await getStateAsync(objBegrenzung)).val || 800; await setStateAsync(objBegrenzung, { val: maxGesamtLeistung, ack: true }); console.log(`Aktuelle Begrenzung Soll: ${maxGesamtLeistung}`); await setWechselrichterLeistungen(); }; on({ id: [objLeistungHaus, objBegrenzung], change: "ne" }, updateMaxGesamtLeistung); } /** * Diese Funktion erstellt und initialisiert die eigenen Datenpunkte. */ async function setStates() { // Maximal zulässige Gesamtleistung await createStateAsync(`${path}.BegrenzungSoll`, 800, { read: true, write: true, name: 'Begrenzung Wechselrichter in Summe', type: "number", role: "state", unit: "W", def: 800 }); await createStateAsync(`${path}.BegrenzungIst`, 0, { read: true, write: false, name: 'Ist-Begrenzung Wechselrichter in Summe [Watt]', type: "number", role: "value", unit: "W", def: 0 }); await createStateAsync(`${path}.BegrenzungIstInProzent`, 0, { read: true, write: false, name: 'Ist-Begrenzung Wechselrichter in Summe [Prozent]', type: "number", role: "value", unit: "%", def: 0 }); objBegrenzung = `${path}.BegrenzungSoll`; objBegrenzungIst = `${path}.BegrenzungIst`; objBegrenzungIstProzent = `${path}.BegrenzungIstInProzent`; maxGesamtLeistung = getState(objBegrenzung).val ?? 800; } /** * Diese Funktion ruft die aktuellen Leistungen aller Wechselrichter ab. */ async function getWechselrichterLeistungen() { for (const wr of Object.values(wechselrichter)) { try { const leistung = await getStateAsync(wr.objLeistung); if (leistung) { wr.aktuelleLeistung = leistung.val; } else { console.error( `Could not retrieve state for ${wr.objLeistung}` ); wr.aktuelleLeistung = 0; // Use 0 as a default value } } catch (error) { console.error( `Error retrieving state for ${wr.objLeistung}: ${error}` ); wr.aktuelleLeistung = 0; // Use 0 as a default value } } } /** * Diese Funktion gibt ein Array der maximalen Leistungen aller Wechselrichter zurück. */ async function getMaximaleLeistungen() { return Object.values(wechselrichter).map( (wechselrichter) => wechselrichter.maxLeistung ); } /** * Berechnet die Summe aller aktuellen Leistungen der Wechselrichter. */ async function getAktuelleLeistungSumme() { let summe = 0; // Iteriere durch jedes Wechselrichter-Objekt for (const wr of Object.values(wechselrichter)) { summe = summe + wr.aktuelleLeistung; } return summe; }; /** * Berechnet die Summe aller maxLeistung-Werte im Wechselrichter-Objekt. */ async function getMaxLeistungSumme() { let summe = 0; // Iteriere durch jedes Wechselrichter-Objekt for (const wr of Object.values(wechselrichter)) { summe = summe + wr.maxLeistung; } return summe; }; /** * Berechnet die Summe aller begrenzteLeistung-Werte im Wechselrichter-Objekt. */ async function getMaxBegrenzungSumme() { let summe = 0; // Iteriere durch jedes Wechselrichter-Objekt for (const wr of Object.values(wechselrichter)) { summe = summe + wr.begrenzteLeistung; } return summe; }; /** * Berechnet die maximale Leistung und setzt die Begrenzung der Wechselrichter. */ async function setWechselrichterLeistungen() { const aktuelleLeistungen = await getAktuelleLeistungSumme(); console.log(`Aktuelle Gesamtleistung der Wechselrichter: ${aktuelleLeistungen}`); const faktor = maxGesamtLeistung / aktuelleLeistungen; console.log(`Reduktionsfaktor der Wechselrichter: ${faktor}`); await Promise.all( Object.values(wechselrichter).map(async (wr) => { let begrenzung = Math.floor(wr.aktuelleLeistung * faktor); // Begrenze die einzelnen Wechselrichter auf mindestens 50W begrenzung = Math.max(begrenzung, 50); // Begrenze die einzelnen Wechselrichter auf maxGesamtLeistung begrenzung = Math.min(begrenzung, maxGesamtLeistung); wr.begrenzteLeistung = begrenzung; await setStateAsync(wr.objBegrenzung, { val: wr.begrenzteLeistung, ack: false }); const prozentualeLeistung = (wr.begrenzteLeistung / wr.maxLeistung) * 100; console.log(`${wr.name}: ${wr.begrenzteLeistung}W (${prozentualeLeistung.toFixed(2)}%)`); }) ); const begrenzteLeistungIst = await getMaxBegrenzungSumme(); const begrenzteLeistungIstInProzent = Math.round((begrenzteLeistungIst / await getMaxLeistungSumme()) * 10000) / 100; // Runden auf 2 Nachkommastellen await Promise.all([ setStateAsync(objBegrenzungIst, { val: begrenzteLeistungIst, ack: true }), setStateAsync(objBegrenzungIstProzent, { val: begrenzteLeistungIstInProzent, ack: true }) ]); console.log(`Gesamtbegrenzung nach Anpassung: ${begrenzteLeistungIst}W, ${begrenzteLeistungIstInProzent}%, maxLeistungWR: ${maxGesamtLeistung}W`); //Batterieladung await loadBattery(aktuelleLeistungen); } async function loadBattery(aktuelleLeistungen) { if (batterie.laden) { const leistungHaus = (await getStateAsync(objLeistungHaus)).val; const ueberschuss = Math.max(aktuelleLeistungen - leistungHaus, 0); const ladeLeistung = Math.min(ueberschuss, batterie.maxLadeleistung); const ladeLeistungProzent = Math.round((ueberschuss / batterie.maxLadeleistung) * 10000) / 100; // Runden auf 2 Nachkommastellen const ladeLeistungVolt = Math.round(10 * (ladeLeistungProzent / 100) * 100) / 100; // Runden auf 2 Nachkommastellen switch (batterie.unit) { case "watt": await setStateAsync(batterie.objLadungWatt, { val: ladeLeistung, ack: false }); break; case "percent": await setStateAsync(batterie.objLadungProzent, { val: ladeLeistungProzent, ack: false }); break; case "volt": await setStateAsync(batterie.objLadungVolt, { val: ladeLeistungVolt, ack: false }); break; default: console.warn(`Keine Einheit für die Batterieladung angegeben oder Einheit entspricht nicht 'watt', 'volt' oder 'percent'!`); }; console.log(`Aktueller Überschuss: ${ueberschuss}, Ladeleistung: ${ladeLeistung}W, ${ladeLeistungProzent}%, ${ladeLeistungVolt}`); } } onReady();
Edit 13.12.2023: Code Update
-
@schmakus
Was mir sofort auffällt, ohne tief in das Script eingestiegen zu sein:
Du definierstpath
, aber die einzelnen DP werden trotzdem absolut definiert.
Könnte man beispielhaft auch so machen:const objLeistungHaus = `$(path).LeistungHaus`;
Dann auch gleich oben definieren und konsequent verwenden.
Dann muss man nur diese eine Konstante ändern, wenn man die DP woanders ablegen möchte. -
würde den Wirkungsgrad der WR mit einfließen lassen.
-
@codierknecht sagte in [Vorlage] Hoymiles Wechselrichter Begrenzung mit openDTU:
@schmakus
Was mir sofort auffällt, ohne tief in das Script eingestiegen zu sein:
Du definierstpath
, aber die einzelnen DP werden trotzdem absolut definiert.
Könnte man beispielhaft auch so machen:const objLeistungHaus = `$(path).LeistungHaus`;
Dann auch gleich oben definieren und konsequent verwenden.
Dann muss man nur diese eine Konstante ändern, wenn man die DP woanders ablegen möchte.Hi, schon geändert. Hat jedoch nur einen Datenpunkt betroffen. Die anderen DPs sind meine Test-Datenpunkte und müssen durch die richtigen Datenpunkte der Wechselrichter, usw. ersetzt werden.
-
@ralla66 sagte in [Vorlage] Hoymiles Wechselrichter Begrenzung mit openDTU:
würde den Wirkungsgrad der WR mit einfließen lassen.
Kannst du mir das näher erklären?
-
Beispiel: du hast 2 WR a 1600 Watt
Begrenzung ist 1500 Watt.
Fahrweise 1 -> 2 WR a 750 Watt betreiben, schlechter Wirkungsgrad
oder
Fahrweise 2 -> 1 WR aus, der andere wird mit 1500 Watt betrieben. -
@ralla66 Hi, verstehe ich nicht. Liegt aber vermutlich an meinen noch nicht existierenden Knowhow.
Welche Rolle spielt es, wenn in Summe die Leistung kommt, welche benötigt wird, bzw auf die begrenzt wird? -
@schmakus sagte in [Vorlage] Hoymiles Wechselrichter Begrenzung mit openDTU:
wenn in Summe die Leistung kommt
bezogen auf die eingespeiste Strom Menge aber weniger.
100 Watt -> 92 % Wirkungsgrad macht 92 Watt zum Verbrauch
100 Watt -> 70 % Wirkungsgrad macht 72 Watt zum Verbrauch -
@ralla66 sagte in [Vorlage] Hoymiles Wechselrichter Begrenzung mit openDTU:
@schmakus sagte in [Vorlage] Hoymiles Wechselrichter Begrenzung mit openDTU:
wenn in Summe die Leistung kommt
bezogen auf die eingespeiste Strom Menge aber weniger.
100 Watt -> 92 % Wirkungsgrad macht 92 Watt zum Verbrauch
100 Watt -> 70 % Wirkungsgrad macht 72 Watt zum VerbrauchOk, stehe immer noch etwas auf dem Schlauch.
Bedeutet das, die Begrenzung der WR bezieht sich nicht auf die AC-Leistung, sondern DC-Leistung? -
das hat nichts mit der Begrenzung zu tun, sondern mit reingesteckter Energie zu gewandelter nutzbarer Energie.
Verluste die der WR beim Wandeln selbst verbraucht. -
@ralla66 sagte in [Vorlage] Hoymiles Wechselrichter Begrenzung mit openDTU:
das hat nichts mit der Begrenzung zu tun, sondern mit reingesteckter Energie zu gewandelter nutzbarer Energie.
Verluste die der WR beim Wandeln selbst verbraucht.Verstehe. Es wäre jedoch eh besser, auf die tatsächliche Einspeisung zu begrenzen.
-
Genau, begrenzen auf den aktuellen Verbrauch, Rest in den Speicher.