NEWS
[Script] PV-Überschuss mit Speicher vorhersagen
-
Hallo!
Hoffe, ich bin in diesem Bereich richtig. Sonst gerne verschieben
@PatrickWalther hat unter https://forum.iobroker.net/topic/45315/test-pv-forecast-adapter ja einen Test-Adapter zur Abfrage der PV-Ertragsprognose veröffentlicht.
Ich habe nun ein Skript "drumherum" geschrieben, welches diese Daten verwendet und zusätzlich noch einen Batteriespeicher und einen geschätzten Grundverbrauch einrechnet. Im Ergebnis erhält man dann einen Graph (für JSON-Chart der Material Design Widgets) und Datenpunkte für die prognostizierte Netzeinspeisung und den Netzbezug. Beides will man ja eigentlich vermeiden...
Das Skript ist noch ganz frisch und wird auch sicherlich noch erweitert. Wollte es aber schonmal teilen:
v0.3 20.02.2022
// PV-Ertragsprognose // Ermittelt anhand der PV-Ertragsprognose des "PV Forecast Adapters", // der Kapazität des PV-Speichers und des geschätzen Grundverbrauchs // den Überschuss (= Strom der eingespeist wird) // und den Netzbezug (= Strom der vom Netzbetreiber bezogen wird). // // Ziel soll es sein, den Eigenverbrauch zu erhöhen indem variable Großverbraucher // gezielt eingeschaltet werden können. // // --- Anleitung --- // 1. PV Forecast Adapter von https://forum.iobroker.net/topic/45315/test-pv-forecast-adapter installieren // und richtig konfigurieren // 2. Untenstehende Datenpunkte ggf. anpassen (nicht existierende werden automatisch angelegt) // 3. Skript starten // 4. Für Visualisierung das JSON-Chart Widget der Material Design Widgets zur Visualisierung hinzufügen // (https://github.com/Scrounger/ioBroker.vis-materialdesign) // 5. Quelle des JSON-Chart Widgets auf den untenstehenden DP dpPVPrognoseGraph (Default: "0_userdata.0.PV.ErtragsPrognoseGraph") setzen // // // // v0.1 19.02.2022 (oxident) // erste Version // v0.2 19.02.2022 (oxident) // JSON-Graph Korrekturen // v0.3 20.02.2022 (oxident) // bekannter Akkustand geändert (wird ignoriert wenn Wert < 0) // konfigurierbare Einstellungen: const dpPVPrognoseStunde = "pvforecast.0.summary.everyhour_kw"; // DP vom PV-Forecast Adapter const dpPVPrognoseLastUpdate = "pvforecast.0.summary.lastUpdated_data"; // DP vom PV-Forecast Adapter const dpPVPrognoseAkkustandJetzt = "0_userdata.0.PV.AkkuLadestandJetzt"; // DP mit aktuellem Ladestand des Speichers // falls Wert >= 0, dann wird der Wert zu der Stunde // der Aktualisierung verwendet // falls Wert < 0, dann wird der Wert ignoriert const dpPVPrognoseAkkustandMax = "0_userdata.0.PV.AkkuLadestandMax"; // DP mit maximalem Ladestand des Speichers // (beides in kWh) // die Werte dann unbedingt auf die richtigen Werte // einstellen bzw. vom Speicher abfragen const dpPVPrognoseVerbrauch = "0_userdata.0.PV.VerbrauchStunde" // Verzeichnis mit Datenpunkten des Verbrauchs pro Stunde const dpPVPrognoseGraph = "0_userdata.0.PV.ErtragsPrognoseGraph"; // hier wird das JSON für das JSON Chart Widget geschrieben const dpPVPrognoseEinspeisung = "0_userdata.0.PV.PrognoseEinspeisung"; // hier wird die Einspeisung für den gesamten Tag geschrieben const dpPVPrognoseBezug = "0_userdata.0.PV.PrognoseBezug"; // hier wird der Netzbezug für den gesamten Tag geschrieben const fAkkustandBeginn = 0.0; // angenommener Akkustand (kWh) zu Tagesbeginn // dies könnte in Zukunft ggf. auf den tatsächlichen // Wert um Mitternacht gesetzt werden const fGrundverbrauch = 1.0; // angenommener Grundverbrauch (kWh) pro Stunde // könnte in Zukunft auch noch feiner eingestellt werden // wenn z. B. bekannt ist, zu welcher Zeit der Verbrauch // höher oder niedriger ist // oder man zieht historische Daten hinzu. // ENDE Einstellungen const fErtragPrognose = []; // PV-Ertrag in kWh pro Stunde var fGrundverbrauchPrognose = []; // geschätzter Grundverbauch in kWh pro Stunde const fAkkustandPrognose = []; // geschätzter Akkustand in kWh pro Stunde const fNetzbezugPrognose = []; // geschätzter Netzbezug in kWh pro Stunde const fUeberschussPrognose = []; // geschätzer Überschuss in kWh pro Stunde (voller Akku) function createDP() { if(!existsState(dpPVPrognoseGraph)) { createState(dpPVPrognoseGraph,"",{name: "PV Prognose JSON-Graph", type: 'string', role: 'value', write: false}, function() {}); } for(var i=0;i<24;i++) { if(!existsState(dpPVPrognoseVerbrauch + "." + pad(i,2) + ":00:00")) { createState(dpPVPrognoseVerbrauch + "." + pad(i,2) + ":00:00",0,{name: "PV Prognose Verbrauch " + pad(i,2) + ":00:00", type: 'number', role: 'value', write: true, unit: 'kWh'}, function () { setState(dpPVPrognoseVerbrauch + "." + pad(i,2) + ":00:00", fGrundverbrauch); }); } } if(!existsState(dpPVPrognoseBezug)) { createState(dpPVPrognoseBezug,0,{name: "PV Prognose Netzbezug", type: 'number', role: 'value', write: false, unit: 'kWh'}, function () {}); } if(!existsState(dpPVPrognoseEinspeisung)) { createState(dpPVPrognoseEinspeisung,0,{name: "PV Prognose Einspeisung", type: 'number', role: 'value', write: false, unit: 'kWh'}, function () {}); } if(!existsState(dpPVPrognoseAkkustandJetzt)) { createState(dpPVPrognoseAkkustandJetzt,-1,{name: "Aktueller Akkustand", type: 'number', role: 'value', write: true, unit: 'kWh'}, function () {}); } if(!existsState(dpPVPrognoseAkkustandMax)) { createState(dpPVPrognoseAkkustandMax,10,{name: "Maximaler Akkustand", type: 'number', role: 'value', write: true, unit: 'kWh'}, function () {}); } } function updateGraph() { var iAkkuUpdateHour = 0; if(getState(dpPVPrognoseAkkustandJetzt).val>=0) iAkkuUpdateHour = new Date(getState(dpPVPrognoseAkkustandJetzt).ts).getHours(); const fAkkustandMax = getState(dpPVPrognoseAkkustandMax).val; // maximale Akkuladung in kWh var iEinspeisungGesamt = 0.0; // Netzeinspeisung Tageswert in kWh var iNetzbezugGesamt = 0.0; // Netzbezug Tageswert in kWh fGrundverbrauchPrognose = []; for(var i=0;i<24;i++) { fGrundverbrauchPrognose[i] = getState(dpPVPrognoseVerbrauch + "." + pad(i,2) + ":00:00").val; } for(var i=0;i<24;i++) { if(existsState(dpPVPrognoseStunde + "." + pad(i, 2) + ":00:00")) { fErtragPrognose[i] = getState(dpPVPrognoseStunde + "." + pad(i, 2) + ":00:00").val; } else { fErtragPrognose[i] = 0.0; } //fErtragPrognose[i] = Math.round((fErtragPrognose[i] + Number.EPSILON) * 100) / 100; } fAkkustandPrognose[0] = 0.0; fUeberschussPrognose[0] = 0.0; fNetzbezugPrognose[0] = fGrundverbrauchPrognose[0]; for(var i=1;i<24;i++) { if(i==iAkkuUpdateHour) { fAkkustandPrognose[i] = getState(dpPVPrognoseAkkustandJetzt).val; } else { fAkkustandPrognose[i] = fAkkustandPrognose[i-1]; } if((fAkkustandPrognose[i] + (fErtragPrognose[i] - fGrundverbrauchPrognose[i])) > 0.0) { // Akku wird ausreichen if((fAkkustandPrognose[i] + (fErtragPrognose[i] - fGrundverbrauchPrognose[i])) > fAkkustandMax) { // Akku erreicht maximale Ladung fUeberschussPrognose[i] = (fErtragPrognose[i] - fGrundverbrauchPrognose[i]) - (fAkkustandMax - fAkkustandPrognose[i]); fAkkustandPrognose[i] = fAkkustandMax; fNetzbezugPrognose[i] = 0.0; iEinspeisungGesamt += fUeberschussPrognose[i]; } else { // Akku bleibt unterhalb maximaler Ladung fAkkustandPrognose[i] += (fErtragPrognose[i] - fGrundverbrauchPrognose[i]); fNetzbezugPrognose[i] = 0.0; fUeberschussPrognose[i] = 0.0; } } else { // Akku wird nicht ausreichen (Netzbezug) fNetzbezugPrognose[i] = (fGrundverbrauchPrognose[i] - fErtragPrognose[i]) - fAkkustandPrognose[i]; fAkkustandPrognose[i] = 0.0; fUeberschussPrognose[i] = 0.0; iNetzbezugGesamt += fNetzbezugPrognose[i]; } } var objJsonGraph = {}; objJsonGraph.axisLabels = []; for(var i=0; i<24; i++) { objJsonGraph.axisLabels.push(pad(i, 2) + ":00"); } objJsonGraph.graphs = []; // Ertragsprognose objJsonGraph.graphs[0] = {}; objJsonGraph.graphs[0]['type'] = "line"; objJsonGraph.graphs[0]['legendText'] = "Ertragsprognose"; objJsonGraph.graphs[0]['datalabel_show'] = false; objJsonGraph.graphs[0]['yAxis_show'] = true; objJsonGraph.graphs[0]['yAxis_min'] = 0; objJsonGraph.graphs[0]['yAxis_max'] = 15; objJsonGraph.graphs[0]['yAxis_id'] = 1; objJsonGraph.graphs[0]['tooltip_AppendText'] = "kWh"; objJsonGraph.graphs[0]['color'] = "yellow"; objJsonGraph.graphs[0]['line_pointSize'] = 2; objJsonGraph.graphs[0]['line_Tension'] = 0.3; objJsonGraph.graphs[0]['line_pointSizeHover'] = 5; objJsonGraph.graphs[0]['line_UseFillColor'] = true; objJsonGraph.graphs[0]['displayOrder'] = 3; objJsonGraph.graphs[0]['data'] = []; for(var i=0; i<24; i++) { objJsonGraph.graphs[0]['data'].push(fErtragPrognose[i]); } // Netzbezug (extern) objJsonGraph.graphs[1] = {}; objJsonGraph.graphs[1]['type'] = "line"; objJsonGraph.graphs[1]['data'] = []; for(var i=0; i<24; i++) { objJsonGraph.graphs[1]['data'].push(fNetzbezugPrognose[i]); } objJsonGraph.graphs[1]['legendText'] = "Netzbezug " + roundTwoDigits(iNetzbezugGesamt) + "kWh"; objJsonGraph.graphs[1]['datalabel_show'] = false; objJsonGraph.graphs[1]['yAxis_show'] = false; objJsonGraph.graphs[1]['yAxis_min'] = 0; objJsonGraph.graphs[1]['yAxis_max'] = 15; objJsonGraph.graphs[1]['yAxis_id'] = 1; objJsonGraph.graphs[1]['tooltip_AppendText'] = "kWh"; objJsonGraph.graphs[1]['color'] = "red"; objJsonGraph.graphs[1]['line_pointSize'] = 2; objJsonGraph.graphs[1]['line_Tension'] = 0.3; objJsonGraph.graphs[1]['line_pointSizeHover'] = 5; objJsonGraph.graphs[1]['line_UseFillColor'] = true; objJsonGraph.graphs[1]['displayOrder'] = 1; // Akkustand objJsonGraph.graphs[2] = {}; objJsonGraph.graphs[2]['type'] = "line"; objJsonGraph.graphs[2]['data'] = []; for(var i=0; i<24; i++) { objJsonGraph.graphs[2]['data'].push(fAkkustandPrognose[i]); } objJsonGraph.graphs[2]['legendText'] = "Akkustand"; objJsonGraph.graphs[2]['datalabel_show'] = false; objJsonGraph.graphs[2]['yAxis_show'] = false; objJsonGraph.graphs[2]['yAxis_min'] = 0; objJsonGraph.graphs[2]['yAxis_max'] = 15; objJsonGraph.graphs[2]['yAxis_id'] = 1; objJsonGraph.graphs[2]['tooltip_AppendText'] = "kWh"; objJsonGraph.graphs[2]['color'] = "green"; objJsonGraph.graphs[2]['line_pointSize'] = 2; objJsonGraph.graphs[2]['line_Tension'] = 0.3; objJsonGraph.graphs[2]['line_pointSizeHover'] = 5; objJsonGraph.graphs[2]['line_UseFillColor'] = true; objJsonGraph.graphs[2]['displayOrder'] = 2; // Netzeinspeisung (extern) objJsonGraph.graphs[3] = {}; objJsonGraph.graphs[3]['type'] = "line"; objJsonGraph.graphs[3]['data'] = []; for(var i=0; i<24; i++) { objJsonGraph.graphs[3]['data'].push(fUeberschussPrognose[i]); } objJsonGraph.graphs[3]['legendText'] = "Netzeinspeisung " + roundTwoDigits(iEinspeisungGesamt) + "kWh"; objJsonGraph.graphs[3]['datalabel_show'] = false; objJsonGraph.graphs[3]['yAxis_show'] = false; objJsonGraph.graphs[3]['yAxis_min'] = 0; objJsonGraph.graphs[3]['yAxis_max'] = 15; objJsonGraph.graphs[3]['yAxis_id'] = 1; objJsonGraph.graphs[3]['tooltip_AppendText'] = "kWh"; objJsonGraph.graphs[3]['color'] = "blue"; objJsonGraph.graphs[3]['line_pointSize'] = 2; objJsonGraph.graphs[3]['line_Tension'] = 0.3; objJsonGraph.graphs[3]['line_pointSizeHover'] = 5; objJsonGraph.graphs[3]['line_UseFillColor'] = true; objJsonGraph.graphs[3]['displayOrder'] = 0; // Verbrauch (geplant) objJsonGraph.graphs[4] = {}; objJsonGraph.graphs[4]['type'] = "line"; objJsonGraph.graphs[4]['data'] = []; for(var i=0; i<24; i++) { objJsonGraph.graphs[4]['data'].push(fGrundverbrauchPrognose[i]); } objJsonGraph.graphs[4]['legendText'] = "Verbrauch"; objJsonGraph.graphs[4]['datalabel_show'] = false; objJsonGraph.graphs[4]['yAxis_show'] = false; objJsonGraph.graphs[4]['yAxis_min'] = 0; objJsonGraph.graphs[4]['yAxis_max'] = 15; objJsonGraph.graphs[4]['yAxis_id'] = 1; objJsonGraph.graphs[4]['tooltip_AppendText'] = "kWh"; objJsonGraph.graphs[4]['color'] = "gray"; objJsonGraph.graphs[4]['line_pointSize'] = 2; objJsonGraph.graphs[4]['line_Tension'] = 0.3; objJsonGraph.graphs[4]['line_pointSizeHover'] = 5; objJsonGraph.graphs[4]['line_UseFillColor'] = true; objJsonGraph.graphs[4]['displayOrder'] = 4; setStateAsync(dpPVPrognoseGraph, JSON.stringify(objJsonGraph), true); setStateAsync(dpPVPrognoseBezug, roundTwoDigits(iNetzbezugGesamt), true); setStateAsync(dpPVPrognoseEinspeisung, roundTwoDigits(iEinspeisungGesamt), true); //console.log("Netzbezug: " + roundTwoDigits(iNetzbezugGesamt) + " Einspeisung: " + roundTwoDigits(iEinspeisungGesamt)); } function pad(num, size) { num = num.toString(); while (num.length < size) num = "0" + num; return num; } function roundTwoDigits(num) { return Math.round((num + Number.EPSILON) * 100) / 100; } createDP(); updateGraph(); on({id: dpPVPrognoseLastUpdate, change: 'ne'}, function(obj) { updateGraph(); }); on({id: dpPVPrognoseAkkustandJetzt, change: 'ne'}, function(obj) { updateGraph(); }); on({id: dpPVPrognoseAkkustandMax, change: 'ne'}, function(obj) { updateGraph(); }); var aVerbrauchsDP = []; for(var i=0;i<24;i++) { aVerbrauchsDP.push(dpPVPrognoseVerbrauch + "." + pad(i,2) + ":00:00"); } on({id: aVerbrauchsDP, change: 'ne'}, function(obj) { updateGraph(); });