NEWS
Hoymiles WR mit 1600W regeln
-
Guten Abend miteinander,
ich habe folgendes Setup:
mME --> Einspeisung WR auf L1 --> ShellyEM 3 --> Verbraucher
Dieser Aufbau ist den Gegebenheiten geschuldet.
Ich schreibe meine Daten mittels des Shelly-Adapter in iobroker und mqtt mit. Die Daten des WR liefert mir eine OpenDTU - diese soll auch die neue Leistungsgrenze schreiben.
Ich habe folgenden JavaScript-Code erarbeitet:
// Datenpunkte const wrPowerDP = 'opendtu.0.1164a00f8949.ac.phase_1.power'; // Wechselrichter-Leistung const housePowerDP = 'shelly.0.SHEM-3#485519C98888#1.Total.InstantPower'; // Hausverbrauch const mqttLimitDP = 'opendtu.0.1164a00f8949.power_control.limit_nonpersistent_absolute'; // MQTT Limit-Datenpunkt const producingDP = 'opendtu.0.1164a00f8949.producing'; // Producing Status // Konfiguration const thresholdWR = 800; // Mindestleistung des Wechselrichters in Watt const thresholdDifferenceMin = 400; // Mindestdifferenz für das Setzen eines Limits const thresholdDifferenceMax = 50; // Maximale Differenz, um das Limit zu erhöhen const maxWRPower = 1600; // Maximale zulässige Leistung des WR const changeThreshold = 10; // Schwellenwert für signifikante Limitänderung (in Watt) // Initialisiere Zähler-Datenpunkte für Prüfungen und Limitänderungen if (!existsState('0_userdata.0.solar.check_counter')) { createState('0_userdata.0.solar.check_counter', 0, { name: 'Anzahl der Prüfungen', type: 'number', unit: 'Prüfungen', read: true, write: true }); } if (!existsState('0_userdata.0.solar.change_counter')) { createState('0_userdata.0.solar.change_counter', 0, { name: 'Anzahl der Prüfungen mit Limitänderung', type: 'number', unit: 'Prüfungen', read: true, write: true }); } if (!existsState('0_userdata.0.solar.no_change_counter')) { createState('0_userdata.0.solar.no_change_counter', 0, { name: 'Anzahl der Prüfungen ohne Limitänderung', type: 'number', unit: 'Prüfungen', read: true, write: true }); } // Differenz-Datenpunkt für Grafana if (!existsState('0_userdata.0.solar.wr_haus_difference')) { createState('0_userdata.0.solar.wr_haus_difference', 0, { name: 'WR - Haus Leistung Differenz', type: 'number', unit: 'Watt', read: true, write: true }); } // Zustand für Low Power Mode if (!existsState('0_userdata.0.solar.low_power_mode')) { createState('0_userdata.0.solar.low_power_mode', false, { name: 'Low Power Mode aktiv', type: 'boolean', read: true, write: true }); } // Funktion zur Berechnung des Limits function calculateLimit(wrPower, housePower) { let limit = wrPower - thresholdDifferenceMin; if (wrPower - housePower < thresholdDifferenceMax || wrPower - housePower > thresholdDifferenceMin) { limit = housePower + thresholdDifferenceMin; } if (limit < 0) { limit = 0; } else if (limit > maxWRPower) { limit = maxWRPower; } return limit; } // Hauptfunktion zur Leistungsprüfung und Limitsteuerung function checkPower() { const wrPower = getState(wrPowerDP).val; const housePower = getState(housePowerDP).val; const powerDifference = parseFloat((wrPower - housePower).toFixed(2)); const isLowPower = getState('0_userdata.0.solar.low_power_mode').val; const currentLimit = getState(mqttLimitDP).val; // Prüfungszähler erhöhen let checkCount = getState('0_userdata.0.solar.check_counter').val + 1; setState('0_userdata.0.solar.check_counter', checkCount); // WR-Leistung unter Schwellwert → Low Power Mode if (wrPower < thresholdWR) { if (!isLowPower) { console.log('WR-Leistung unter ' + thresholdWR + ' W – Low Power Mode aktiviert'); console.log('Limit wird geändert von ' + currentLimit + ' W auf ' + maxWRPower + ' W'); setState(mqttLimitDP, maxWRPower); setState('0_userdata.0.solar.low_power_mode', true); let changeCount = getState('0_userdata.0.solar.change_counter').val + 1; setState('0_userdata.0.solar.change_counter', changeCount); } else { console.log('Low Power Mode aktiv – WR-Leistung weiter unter ' + thresholdWR + ' W'); console.log('Limit bleibt unverändert bei ' + currentLimit + ' W'); let noChangeCount = getState('0_userdata.0.solar.no_change_counter').val + 1; setState('0_userdata.0.solar.no_change_counter', noChangeCount); } } else { if (isLowPower) { console.log('WR-Leistung über ' + thresholdWR + ' W – Low Power Mode deaktiviert'); setState('0_userdata.0.solar.low_power_mode', false); } if (powerDifference >= 0) { const newLimit = calculateLimit(wrPower, housePower); if (Math.abs(currentLimit - newLimit) >= changeThreshold) { console.log('Limit wird geändert von ' + currentLimit + ' W auf ' + newLimit + ' W'); setState(mqttLimitDP, newLimit); let changeCount = getState('0_userdata.0.solar.change_counter').val + 1; setState('0_userdata.0.solar.change_counter', changeCount); } else { console.log('Limit bleibt unverändert bei ' + currentLimit + ' W – Änderung (' + Math.abs(currentLimit - newLimit) + ' W) unterhalb Schwelle (' + changeThreshold + ' W)'); let noChangeCount = getState('0_userdata.0.solar.no_change_counter').val + 1; setState('0_userdata.0.solar.no_change_counter', noChangeCount); } } else { console.log('Keine Regelung – WR-Haus Differenz negativ (' + powerDifference + ' W)'); } } setState('0_userdata.0.solar.wr_haus_difference', powerDifference); } // Sofort prüfen bei Produktionsstart on({ id: producingDP, val: true }, function () { console.log('Produktion gestartet – Starte erste Prüfung der Leistung'); checkPower(); }); // Zyklische Prüfung alle 15 Sekunden bei aktiver Produktion setInterval(function () { if (getState(producingDP).val === true) { checkPower(); } }, 60000);
Ich möchte die Anzahl der Schreibvorgänge auf den WR so gering wie möglich halten, daher die eingebauten Einschränkungen.
Allerdings klappt das schreiben des neuen Limit auf den WR nicht - habe ich irgendwo einen Denkfehler?
Gruß, NobbyNobs
-
Die Logik in "calculateLimit()" ist für mich unklar.
if (wrPower - housePower < thresholdDifferenceMax || wrPower - housePower > thresholdDifferenceMin)
Diese Bedingung ist irgendwie logisch nicht richtig, glaube ich. Sie trifft immer zu, wenn powerDifference außerhalb des Bereichs thresholdDifferenceMax bis thresholdDifferenceMin liegt.
Versuche das mal.
// =========================== // Konfiguration // =========================== const wrPowerDP = 'opendtu.0.1164a00f8949.ac.phase_1.power'; const housePowerDP = 'shelly.0.SHEM-3#485519C98888#1.Total.InstantPower'; const mqttLimitDP = 'opendtu.0.1164a00f8949.power_control.limit_nonpersistent_absolute'; const producingDP = 'opendtu.0.1164a00f8949.producing'; const thresholdWR = 800; const thresholdDifferenceMin = 400; const thresholdDifferenceMax = 50; const maxWRPower = 1600; const changeThreshold = 10; const debug = true; // Setze auf false, um Logs zu deaktivieren // =========================== // Datenpunkt-Initialisierung // =========================== const statesToInit = [ { id: '0_userdata.0.solar.check_counter', default: 0, name: 'Anzahl der Prüfungen', type: 'number', unit: 'Prüfungen' }, { id: '0_userdata.0.solar.change_counter', default: 0, name: 'Anzahl der Prüfungen mit Limitänderung', type: 'number', unit: 'Prüfungen' }, { id: '0_userdata.0.solar.no_change_counter', default: 0, name: 'Anzahl der Prüfungen ohne Limitänderung', type: 'number', unit: 'Prüfungen' }, { id: '0_userdata.0.solar.wr_haus_difference', default: 0, name: 'WR - Haus Leistung Differenz', type: 'number', unit: 'Watt' }, { id: '0_userdata.0.solar.low_power_mode', default: false, name: 'Low Power Mode aktiv', type: 'boolean' } ]; statesToInit.forEach(dp => { if (!existsState(dp.id)) { createState(dp.id, dp.default, { name: dp.name, type: dp.type, unit: dp.unit, read: true, write: true }); } }); // =========================== // Limitberechnung // =========================== function calculateLimit(wrPower, housePower) { let powerDiff = wrPower - housePower; let limit; if (powerDiff >= thresholdDifferenceMax && powerDiff <= thresholdDifferenceMin) { limit = housePower + thresholdDifferenceMin; } else { limit = wrPower - thresholdDifferenceMin; } if (limit < 0) limit = 0; else if (limit > maxWRPower) limit = maxWRPower; return limit; } // =========================== // Hauptfunktion // =========================== function checkPower() { const wrPower = getState(wrPowerDP)?.val ?? 0; const housePower = getState(housePowerDP)?.val ?? 0; const powerDifference = parseFloat((wrPower - housePower).toFixed(2)); const isLowPower = getState('0_userdata.0.solar.low_power_mode')?.val ?? false; const currentLimit = getState(mqttLimitDP)?.val ?? 0; // Prüfungszähler erhöhen let checkCount = getState('0_userdata.0.solar.check_counter')?.val ?? 0; setState('0_userdata.0.solar.check_counter', ++checkCount); if (wrPower < thresholdWR) { if (!isLowPower) { if (debug) { console.log(`WR-Leistung unter ${thresholdWR} W – Low Power Mode aktiviert`); console.log(`Limit wird geändert von ${currentLimit} W auf ${maxWRPower} W`); } setState(mqttLimitDP, maxWRPower); setState('0_userdata.0.solar.low_power_mode', true); let changeCount = getState('0_userdata.0.solar.change_counter')?.val ?? 0; setState('0_userdata.0.solar.change_counter', ++changeCount); } else { if (debug) { console.log(`Low Power Mode aktiv – WR-Leistung weiter unter ${thresholdWR} W`); console.log(`Limit bleibt unverändert bei ${currentLimit} W`); } let noChangeCount = getState('0_userdata.0.solar.no_change_counter')?.val ?? 0; setState('0_userdata.0.solar.no_change_counter', ++noChangeCount); } } else { if (isLowPower) { if (debug) console.log(`WR-Leistung über ${thresholdWR} W – Low Power Mode deaktiviert`); setState('0_userdata.0.solar.low_power_mode', false); } if (powerDifference >= 0) { const newLimit = calculateLimit(wrPower, housePower); const diff = Math.abs(currentLimit - newLimit); if (diff >= changeThreshold) { if (debug) console.log(`Limit wird geändert von ${currentLimit} W auf ${newLimit} W`); setState(mqttLimitDP, newLimit); let changeCount = getState('0_userdata.0.solar.change_counter')?.val ?? 0; setState('0_userdata.0.solar.change_counter', ++changeCount); } else { if (debug) console.log(`Limit bleibt unverändert bei ${currentLimit} W – Änderung (${diff} W) unterhalb Schwelle (${changeThreshold} W)`); let noChangeCount = getState('0_userdata.0.solar.no_change_counter')?.val ?? 0; setState('0_userdata.0.solar.no_change_counter', ++noChangeCount); } } else { if (debug) console.log(`Keine Regelung – WR-Haus Differenz negativ (${powerDifference} W)`); } } setState('0_userdata.0.solar.wr_haus_difference', powerDifference); } // =========================== // Event- und Zeitsteuerung // =========================== on({ id: producingDP, val: true }, function () { if (debug) console.log('Produktion gestartet – Starte erste Prüfung der Leistung'); checkPower(); }); // Zyklische Prüfung alle 60 Sekunden setInterval(() => { if (getState(producingDP)?.val === true) { checkPower(); } }, 60000);
-
Da du sowieso OPENDTU verwendest, verwende OpenDTU on Battery.
Damit kannst du den Wechselrichter nach aktuellem Strombedarf steuern.