Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. SMA Forecast Charging mit APG möglich ?

NEWS

  • Jahresrückblick 2025 – unser neuer Blogbeitrag ist online! ✨
    BluefoxB
    Bluefox
    16
    1
    1.6k

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    13
    1
    858

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    25
    1
    2.1k

SMA Forecast Charging mit APG möglich ?

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
14 Beiträge 2 Kommentatoren 635 Aufrufe 2 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • Q Offline
    Q Offline
    Qlink
    schrieb am zuletzt editiert von
    #1

    Hi Leute,

    ich habe folgende PV Anlage bei mir:

    1x SMA Sunny Tripower 10 SE
    1x SMA Sunny Tripower 10
    1x SMA Sunny Home Manager 2.0
    1x BYD HVM 22.1

    Ich würde damit gerne die SMA Forecast Charging Lösung von @arteck hier: https://github.com/arteck/SMA_forecast_charging nutzen.
    Da ich allerdings in Österreich wohne und meine dynamischen Stromtarifdaten per APG Adapter hier: https://github.com/HGlab01/ioBroker.apg-info in iobroker reinbekomme, stellt sich für mich nun folgende Frage:

    Ist es möglich die Forecast Charging Lösung auch anstatt der Tibber Daten mit den APG Daten zu nutzen ?
    Falls ja, was müsste ich dazu anpassen, oder könnte jemand die Scripte entsprechend erweitern ?
    So würde die Lösung einem noch größeren Publikum von Nutzen sein.

    Hier noch ein Screenshot wie die Daten aus dem APG Adapter verfügbar sind:
    3073a9ee-60d4-4af1-a509-cc68d627e30a-image.png

    Beste Grüße

    arteckA 1 Antwort Letzte Antwort
    0
    • Q Qlink

      Hi Leute,

      ich habe folgende PV Anlage bei mir:

      1x SMA Sunny Tripower 10 SE
      1x SMA Sunny Tripower 10
      1x SMA Sunny Home Manager 2.0
      1x BYD HVM 22.1

      Ich würde damit gerne die SMA Forecast Charging Lösung von @arteck hier: https://github.com/arteck/SMA_forecast_charging nutzen.
      Da ich allerdings in Österreich wohne und meine dynamischen Stromtarifdaten per APG Adapter hier: https://github.com/HGlab01/ioBroker.apg-info in iobroker reinbekomme, stellt sich für mich nun folgende Frage:

      Ist es möglich die Forecast Charging Lösung auch anstatt der Tibber Daten mit den APG Daten zu nutzen ?
      Falls ja, was müsste ich dazu anpassen, oder könnte jemand die Scripte entsprechend erweitern ?
      So würde die Lösung einem noch größeren Publikum von Nutzen sein.

      Hier noch ein Screenshot wie die Daten aus dem APG Adapter verfügbar sind:
      3073a9ee-60d4-4af1-a509-cc68d627e30a-image.png

      Beste Grüße

      arteckA Offline
      arteckA Offline
      arteck
      Developer Most Active
      schrieb am zuletzt editiert von arteck
      #2

      @qlink sagte in SMA Forecast Charging mit APG möglich ?:

      Ist es möglich die Forecast Charging Lösung auch anstatt der Tibber Daten mit den APG Daten zu nutzen ?

      gehen tut alles.. egal welcher Anbieter.

      aber

      Falls ja, was müsste ich dazu anpassen, oder könnte jemand die Scripte entsprechend erweitern ?

      die Anpassung musst du vornehmen oder jamenden finden der das macht.. ich machs nicht SRY...

      const tibberPreisJetztDP    = tibberDP + 'extra.tibberPreisJetzt';
      const tibberPvForcastDP     = tibberDP + 'extra.tibberPvForcast';
      

      die 2 Datenpunkte müssen richtig gefüllt werden..

      tibberPreisJetzt - ist der jetzige Preis in der Stunde
      tibberPvForcast - ist ein array in 30 min Auflösung ([preis, startTime ,endTime ])

      die Preise werden in dem Script
      https://github.com/arteck/SMA_forecast_charging/blob/master/SUNNY_TRIPOWER _10.0_SE/get_tibber_data.js
      zusammen gebaut

      und ob das APG oder awattar.. ist egal solange das Format passt

      zigbee hab ich, zwave auch, nuc's genauso und HA auch

      Q 1 Antwort Letzte Antwort
      0
      • arteckA arteck

        @qlink sagte in SMA Forecast Charging mit APG möglich ?:

        Ist es möglich die Forecast Charging Lösung auch anstatt der Tibber Daten mit den APG Daten zu nutzen ?

        gehen tut alles.. egal welcher Anbieter.

        aber

        Falls ja, was müsste ich dazu anpassen, oder könnte jemand die Scripte entsprechend erweitern ?

        die Anpassung musst du vornehmen oder jamenden finden der das macht.. ich machs nicht SRY...

        const tibberPreisJetztDP    = tibberDP + 'extra.tibberPreisJetzt';
        const tibberPvForcastDP     = tibberDP + 'extra.tibberPvForcast';
        

        die 2 Datenpunkte müssen richtig gefüllt werden..

        tibberPreisJetzt - ist der jetzige Preis in der Stunde
        tibberPvForcast - ist ein array in 30 min Auflösung ([preis, startTime ,endTime ])

        die Preise werden in dem Script
        https://github.com/arteck/SMA_forecast_charging/blob/master/SUNNY_TRIPOWER _10.0_SE/get_tibber_data.js
        zusammen gebaut

        und ob das APG oder awattar.. ist egal solange das Format passt

        Q Offline
        Q Offline
        Qlink
        schrieb am zuletzt editiert von
        #3

        @arteck

        Danke für deine Rückmeldung.
        Dann versuche ich gerne selbst mein Glück, trotz meiner bescheidenen Scripting Skills.

        Ich hab das Script wie folgt nach bestem Wissen und Gewissen angepasst:

        const _apg = 'apg-info.0.marketprice.';     // Anpassen!
        
        const _apgDP1 = '0_userdata.0';
        const _apgDP2 = 'Stromversorgung.apg.';
        const _apgDP  = _apgDP1 + '.' + _apgDP2;
        
        const options = { hour12: false, hour: '2-digit', minute:'2-digit'};
           
        // bei jeder preisänderung wird die neue Stunde mit dem niedrigen Preis für die vis übernommen
        const apgPreisUebermnehmen = true;
        // 
        const _apgLevelErmitteln = false;
        
        createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgPvForcast', { 'name': 'apg formattierung für pv prognose', 'type':'array', 'read': true, 'write': false, 'role': 'json'}], function () {  }); 
        createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgPvForcastTomorrow', { 'name': 'apg rest preise für morgen', 'type':'array', 'read': true, 'write': false, 'role': 'json'}], function () {  }); 
        createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgBestPreisArrayLang', { 'name': 'apg bester preis als array', 'type':'array', 'read': true, 'write': false, 'role': 'json'}], function () {  });
        createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgBestPreisArrayLangTomorrow', { 'name': 'apg bester preis als array für Morgen', 'type':'array', 'read': true, 'write': false, 'role': 'json'}], function () {  });
        
        createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgBestPreis', { 'name': 'apg Best Preis', 'type':'number', 'read': true, 'write': false, 'role': 'state', 'def':0 , "unit": "ct" }], function () {      
          setState(_apgDP + 'extra.apgBestPreis', 0, true);
        }); 
        
        createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgPreisJetzt', { 'name': 'apg Preis Jetzt', 'type':'number', 'read': true, 'write': false, 'role': 'state', 'def':0, "unit": "ct" }], function () {        
          setState(_apgDP + 'extra.apgPreisJetzt', 0, true);
        }); 
        
        createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgPreisNächsteStunde', { 'name': 'apg Preis Nächste Stunde', 'type':'number', 'read': true, 'write': false, 'role': 'state', 'def':0, "unit": "ct" }], function () {        
          setState(_apgDP + 'extra.apgPreisNächsteStunde', 0, true);
        }); 
        
        if (_apgLevelErmitteln) {
            createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgLevelJetzt', { 'name': 'Preis Level', 'type':'string', 'read': true, 'write': false, 'role': 'text',  'def': '' }], function () {        
                setState(_apgDP + 'extra.apgLevelJetzt', '', true);
            });  
        
            createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgLevelNächsteStunde', { 'name': 'Preis Level', 'type':'string', 'read': true, 'write': false, 'role': 'text',  'def': '' }], function () {        
                setState(_apgDP + 'extra.apgLevelNächsteStunde', '', true);
            }); 
        }
        
        holePreis();
        preisJetzt();
        
        function holePreis() {
            let preiseHeute = [];
            let preiseMorgen = [];
            let preisePV = [];
            let preisePVTommorow = [];
            
            const arr1 = JSON.parse(getState(_apg +'today.jsonChartPricesToday.json').val);
            const arr2 = JSON.parse(getState(_apg +'tomorrow.jsonChart').val);
            let arrPrice = arr1;
        
            let now = new Date();
        
            if (arr2.length > 0) {
                now.setMinutes(0, 0, 0);
                const heutePreise = arrPrice.filter(price => new Date(price.startsAt) >= now);
                arrPrice = heutePreise.concat(arr2);           // füge beide zusammen
               
            } else {
                now.setHours(0, 0, 0, 0);
            }
            
            const next24Hours = new Date(now.getTime() + 24 * 60 * 60 * 1000);
        
            for (let i = 0; i < arrPrice.length; i++) {
                const element       = arrPrice[i];
                const startsAt      = element.startsAt;
                const start         = new Date(startsAt);
                const preis         = element.total;
                const end           = new Date(Date.parse(startsAt)).getTime()+3600000;            
                const startTime     = start.toLocaleTimeString('de-DE', options);
                const endTime       = new Date(end).toLocaleTimeString('de-DE', options);
                const hhStartTime   = startTime.split(':')[0];
        
                let objHeute = {};
                    
                if (start >= now && start < next24Hours) {        
        //      console.warn(`Starts at: ${start}, Total: ${preis}`);
                    objHeute.start = start.getHours();
                    objHeute.preis = preis;
                    preiseHeute.push(objHeute);
        
                    preisePV.push([preis, startTime , hhStartTime + ':30']);
                    preisePV.push([preis, hhStartTime + ':30', endTime]);            
                }
            }
        
        // preise für morgen für die VIS
            for (let m = 0; m < arr2.length; m++) {
                const element       = arr2[m];
                const startsAt      = element.startsAt;
                const start         = new Date(startsAt);
                const preis         = element.total;
                const end           = new Date(Date.parse(startsAt)).getTime()+3600000;            
                const startTime     = start.toLocaleTimeString('de-DE', options);
                const endTime       = new Date(end).toLocaleTimeString('de-DE', options);
                const hhStartTime   = startTime.split(':')[0];
        
                let objMorgen = {
                    start : start.getHours(),
                    preis : preis
                };
                           
                preiseMorgen.push(objMorgen);
        
                preisePVTommorow.push([preis, startTime , hhStartTime + ':30']);
                preisePVTommorow.push([preis, hhStartTime + ':30', endTime]);        
            }       
        
            let preiseSortLang = preiseHeute;
            preiseSortLang.sort(function(a, b) {
                return a.start - b.start;
            });
        
            let preiseSortLangTomorrow = preiseMorgen;
            preiseSortLangTomorrow.sort(function(a, b) {
                return a.start - b.start;
            });
            
            const preisePVSort = sortArrayByStartTime(preisePV, getHH());
            
            setState(_apgDP + 'extra.apgBestPreisArrayLang', preiseSortLang, true);
            setState(_apgDP + 'extra.apgBestPreisArrayLangTomorrow', preiseSortLangTomorrow, true);
            
            setState(_apgDP + 'extra.apgPvForcast', preisePVSort, true);
            setState(_apgDP + 'extra.apgPvForcastTomorrow', preisePVTommorow, true);
        
            errechneBesteUhrzeit(preiseSortLang);
        }
        
        function sortArrayByStartTime(array, currentHour) {
            // Sortiere den Array nach der Startzeit
            array.sort((a, b) => {
                const timeA = a[1].split(":").map(Number);
                const timeB = b[1].split(":").map(Number);
                
                // Vergleiche Stunden
                if (timeA[0] != timeB[0]) {
                    return timeA[0] - timeB[0];
                }
                
                // Wenn Stunden gleich sind, vergleiche Minuten
                return timeA[1] - timeB[1];
            });
        
            // Finde den Index des aktuellen Zeitpunkts
            let startIndex = array.findIndex(item => {
                const time = item[1].split(":").map(Number);
                return time[0] >= currentHour || (time[0] == currentHour && time[1] >= 30);
            });
        
            // Schneide den Array ab startIndex und setze ihn an das Ende
            const sortedArray = array.slice(startIndex).concat(array.slice(0, startIndex));
        
            return sortedArray;
        }
        
        
        
        function errechneBesteUhrzeit(allePreise) {
            const [niedrigsterIndex, zweitNiedrigsterIndex] = findeBenachbarteNiedrigstePreise(allePreise);
            const preiseKurzArr = [];
        
            preiseKurzArr.push(allePreise[niedrigsterIndex]);
            preiseKurzArr.push(allePreise[zweitNiedrigsterIndex]);
            startZeit(preiseKurzArr);
        }
        
        function findeBenachbarteNiedrigstePreise(preisArray) {     
            let niedrigsterPreisSumme = Number.POSITIVE_INFINITY;
            let niedrigsterPreisIndex1 = -1;
            let niedrigsterPreisIndex2 = -1;
        
            for (let i = 0; i < preisArray.length - 1; i++) {
                // wir bilden eine summer
                const summe = preisArray[i].preis + preisArray[i + 1].preis;
        
                // Prüfe, ob diese Summe kleiner als die bisher niedrigste ist
                if (summe < niedrigsterPreisSumme) {
                    niedrigsterPreisSumme = summe;
                    niedrigsterPreisIndex1 = i;
                    niedrigsterPreisIndex2 = i + 1;
                }
            }
        
            if (niedrigsterPreisIndex1 > niedrigsterPreisIndex2) {
                let temp = niedrigsterPreisIndex1;
                niedrigsterPreisIndex1 = niedrigsterPreisIndex2;
                niedrigsterPreisIndex2 = temp;        
            }
        
            // gebe den index raus
            return [niedrigsterPreisIndex1, niedrigsterPreisIndex2];
        }
        
        function startZeit(preiseKurz) {   
            const apgNutzenManuell = getState(_apgDP + 'extra.apgNutzenManuell').val; 
        
            const start = preiseKurz[0].start;
            const preis = preiseKurz[0].preis;
        
            preiseKurz.splice(0, 1);
        
            if (apgPreisUebermnehmen && !apgNutzenManuell) {
                setState(_apgDP + 'extra.apgNutzenManuellHH', start, true);
                setState(_apgDP + 'extra.apgBestPreis', preis, true);     
            }
        }
        
        function preisJetzt() {
            let hh          = Number(getHH());
            let preis       = getState(_apg + 'today.' + hh + '.total').val;
            let apgLevel = getState(_apg + 'today.' + hh + '.level').val;
        
            setState(_apgDP + 'extra.apgPreisJetzt', preis, true);
        
            if (_apgLevelErmitteln) {    
                setState(_apgDP + 'extra.apgLevelJetzt', apgLevel, true);
            }
        
            hh = hh + 1;
            if (hh > 23) {
                hh = 0;
            }
            
            preis       = getState(_apg + 'today.' + hh + '.total').val;
            setState(_apgDP + 'extra.apgPreisNächsteStunde', preis, true);
        
            if (_apgLevelErmitteln) {    
                apgLevel = getState(_apg + 'today.' + hh + '.level').val;
                setState(_apgDP + 'extra.apgLevelNächsteStunde', apgLevel, true);
            }
        }
        
        schedule('0 * * * *', function () {
            holePreis();
            preisJetzt();
        });
        

        Wenn ich es starte erhalte ich allerdings folgende Fehlermeldungen und es werden keine DPs unter "Objects" angelegt:

        12bebbe4-ff77-460a-a123-bc07d9682ec6-image.png

        Hast du eine Idee wo ich einen Fehler eingebaut habe ?

        Danke.

        Beste Grüße

        arteckA 1 Antwort Letzte Antwort
        0
        • Q Qlink

          @arteck

          Danke für deine Rückmeldung.
          Dann versuche ich gerne selbst mein Glück, trotz meiner bescheidenen Scripting Skills.

          Ich hab das Script wie folgt nach bestem Wissen und Gewissen angepasst:

          const _apg = 'apg-info.0.marketprice.';     // Anpassen!
          
          const _apgDP1 = '0_userdata.0';
          const _apgDP2 = 'Stromversorgung.apg.';
          const _apgDP  = _apgDP1 + '.' + _apgDP2;
          
          const options = { hour12: false, hour: '2-digit', minute:'2-digit'};
             
          // bei jeder preisänderung wird die neue Stunde mit dem niedrigen Preis für die vis übernommen
          const apgPreisUebermnehmen = true;
          // 
          const _apgLevelErmitteln = false;
          
          createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgPvForcast', { 'name': 'apg formattierung für pv prognose', 'type':'array', 'read': true, 'write': false, 'role': 'json'}], function () {  }); 
          createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgPvForcastTomorrow', { 'name': 'apg rest preise für morgen', 'type':'array', 'read': true, 'write': false, 'role': 'json'}], function () {  }); 
          createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgBestPreisArrayLang', { 'name': 'apg bester preis als array', 'type':'array', 'read': true, 'write': false, 'role': 'json'}], function () {  });
          createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgBestPreisArrayLangTomorrow', { 'name': 'apg bester preis als array für Morgen', 'type':'array', 'read': true, 'write': false, 'role': 'json'}], function () {  });
          
          createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgBestPreis', { 'name': 'apg Best Preis', 'type':'number', 'read': true, 'write': false, 'role': 'state', 'def':0 , "unit": "ct" }], function () {      
            setState(_apgDP + 'extra.apgBestPreis', 0, true);
          }); 
          
          createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgPreisJetzt', { 'name': 'apg Preis Jetzt', 'type':'number', 'read': true, 'write': false, 'role': 'state', 'def':0, "unit": "ct" }], function () {        
            setState(_apgDP + 'extra.apgPreisJetzt', 0, true);
          }); 
          
          createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgPreisNächsteStunde', { 'name': 'apg Preis Nächste Stunde', 'type':'number', 'read': true, 'write': false, 'role': 'state', 'def':0, "unit": "ct" }], function () {        
            setState(_apgDP + 'extra.apgPreisNächsteStunde', 0, true);
          }); 
          
          if (_apgLevelErmitteln) {
              createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgLevelJetzt', { 'name': 'Preis Level', 'type':'string', 'read': true, 'write': false, 'role': 'text',  'def': '' }], function () {        
                  setState(_apgDP + 'extra.apgLevelJetzt', '', true);
              });  
          
              createUserStates(_apgDP1, false, [_apgDP2 + 'extra.apgLevelNächsteStunde', { 'name': 'Preis Level', 'type':'string', 'read': true, 'write': false, 'role': 'text',  'def': '' }], function () {        
                  setState(_apgDP + 'extra.apgLevelNächsteStunde', '', true);
              }); 
          }
          
          holePreis();
          preisJetzt();
          
          function holePreis() {
              let preiseHeute = [];
              let preiseMorgen = [];
              let preisePV = [];
              let preisePVTommorow = [];
              
              const arr1 = JSON.parse(getState(_apg +'today.jsonChartPricesToday.json').val);
              const arr2 = JSON.parse(getState(_apg +'tomorrow.jsonChart').val);
              let arrPrice = arr1;
          
              let now = new Date();
          
              if (arr2.length > 0) {
                  now.setMinutes(0, 0, 0);
                  const heutePreise = arrPrice.filter(price => new Date(price.startsAt) >= now);
                  arrPrice = heutePreise.concat(arr2);           // füge beide zusammen
                 
              } else {
                  now.setHours(0, 0, 0, 0);
              }
              
              const next24Hours = new Date(now.getTime() + 24 * 60 * 60 * 1000);
          
              for (let i = 0; i < arrPrice.length; i++) {
                  const element       = arrPrice[i];
                  const startsAt      = element.startsAt;
                  const start         = new Date(startsAt);
                  const preis         = element.total;
                  const end           = new Date(Date.parse(startsAt)).getTime()+3600000;            
                  const startTime     = start.toLocaleTimeString('de-DE', options);
                  const endTime       = new Date(end).toLocaleTimeString('de-DE', options);
                  const hhStartTime   = startTime.split(':')[0];
          
                  let objHeute = {};
                      
                  if (start >= now && start < next24Hours) {        
          //      console.warn(`Starts at: ${start}, Total: ${preis}`);
                      objHeute.start = start.getHours();
                      objHeute.preis = preis;
                      preiseHeute.push(objHeute);
          
                      preisePV.push([preis, startTime , hhStartTime + ':30']);
                      preisePV.push([preis, hhStartTime + ':30', endTime]);            
                  }
              }
          
          // preise für morgen für die VIS
              for (let m = 0; m < arr2.length; m++) {
                  const element       = arr2[m];
                  const startsAt      = element.startsAt;
                  const start         = new Date(startsAt);
                  const preis         = element.total;
                  const end           = new Date(Date.parse(startsAt)).getTime()+3600000;            
                  const startTime     = start.toLocaleTimeString('de-DE', options);
                  const endTime       = new Date(end).toLocaleTimeString('de-DE', options);
                  const hhStartTime   = startTime.split(':')[0];
          
                  let objMorgen = {
                      start : start.getHours(),
                      preis : preis
                  };
                             
                  preiseMorgen.push(objMorgen);
          
                  preisePVTommorow.push([preis, startTime , hhStartTime + ':30']);
                  preisePVTommorow.push([preis, hhStartTime + ':30', endTime]);        
              }       
          
              let preiseSortLang = preiseHeute;
              preiseSortLang.sort(function(a, b) {
                  return a.start - b.start;
              });
          
              let preiseSortLangTomorrow = preiseMorgen;
              preiseSortLangTomorrow.sort(function(a, b) {
                  return a.start - b.start;
              });
              
              const preisePVSort = sortArrayByStartTime(preisePV, getHH());
              
              setState(_apgDP + 'extra.apgBestPreisArrayLang', preiseSortLang, true);
              setState(_apgDP + 'extra.apgBestPreisArrayLangTomorrow', preiseSortLangTomorrow, true);
              
              setState(_apgDP + 'extra.apgPvForcast', preisePVSort, true);
              setState(_apgDP + 'extra.apgPvForcastTomorrow', preisePVTommorow, true);
          
              errechneBesteUhrzeit(preiseSortLang);
          }
          
          function sortArrayByStartTime(array, currentHour) {
              // Sortiere den Array nach der Startzeit
              array.sort((a, b) => {
                  const timeA = a[1].split(":").map(Number);
                  const timeB = b[1].split(":").map(Number);
                  
                  // Vergleiche Stunden
                  if (timeA[0] != timeB[0]) {
                      return timeA[0] - timeB[0];
                  }
                  
                  // Wenn Stunden gleich sind, vergleiche Minuten
                  return timeA[1] - timeB[1];
              });
          
              // Finde den Index des aktuellen Zeitpunkts
              let startIndex = array.findIndex(item => {
                  const time = item[1].split(":").map(Number);
                  return time[0] >= currentHour || (time[0] == currentHour && time[1] >= 30);
              });
          
              // Schneide den Array ab startIndex und setze ihn an das Ende
              const sortedArray = array.slice(startIndex).concat(array.slice(0, startIndex));
          
              return sortedArray;
          }
          
          
          
          function errechneBesteUhrzeit(allePreise) {
              const [niedrigsterIndex, zweitNiedrigsterIndex] = findeBenachbarteNiedrigstePreise(allePreise);
              const preiseKurzArr = [];
          
              preiseKurzArr.push(allePreise[niedrigsterIndex]);
              preiseKurzArr.push(allePreise[zweitNiedrigsterIndex]);
              startZeit(preiseKurzArr);
          }
          
          function findeBenachbarteNiedrigstePreise(preisArray) {     
              let niedrigsterPreisSumme = Number.POSITIVE_INFINITY;
              let niedrigsterPreisIndex1 = -1;
              let niedrigsterPreisIndex2 = -1;
          
              for (let i = 0; i < preisArray.length - 1; i++) {
                  // wir bilden eine summer
                  const summe = preisArray[i].preis + preisArray[i + 1].preis;
          
                  // Prüfe, ob diese Summe kleiner als die bisher niedrigste ist
                  if (summe < niedrigsterPreisSumme) {
                      niedrigsterPreisSumme = summe;
                      niedrigsterPreisIndex1 = i;
                      niedrigsterPreisIndex2 = i + 1;
                  }
              }
          
              if (niedrigsterPreisIndex1 > niedrigsterPreisIndex2) {
                  let temp = niedrigsterPreisIndex1;
                  niedrigsterPreisIndex1 = niedrigsterPreisIndex2;
                  niedrigsterPreisIndex2 = temp;        
              }
          
              // gebe den index raus
              return [niedrigsterPreisIndex1, niedrigsterPreisIndex2];
          }
          
          function startZeit(preiseKurz) {   
              const apgNutzenManuell = getState(_apgDP + 'extra.apgNutzenManuell').val; 
          
              const start = preiseKurz[0].start;
              const preis = preiseKurz[0].preis;
          
              preiseKurz.splice(0, 1);
          
              if (apgPreisUebermnehmen && !apgNutzenManuell) {
                  setState(_apgDP + 'extra.apgNutzenManuellHH', start, true);
                  setState(_apgDP + 'extra.apgBestPreis', preis, true);     
              }
          }
          
          function preisJetzt() {
              let hh          = Number(getHH());
              let preis       = getState(_apg + 'today.' + hh + '.total').val;
              let apgLevel = getState(_apg + 'today.' + hh + '.level').val;
          
              setState(_apgDP + 'extra.apgPreisJetzt', preis, true);
          
              if (_apgLevelErmitteln) {    
                  setState(_apgDP + 'extra.apgLevelJetzt', apgLevel, true);
              }
          
              hh = hh + 1;
              if (hh > 23) {
                  hh = 0;
              }
              
              preis       = getState(_apg + 'today.' + hh + '.total').val;
              setState(_apgDP + 'extra.apgPreisNächsteStunde', preis, true);
          
              if (_apgLevelErmitteln) {    
                  apgLevel = getState(_apg + 'today.' + hh + '.level').val;
                  setState(_apgDP + 'extra.apgLevelNächsteStunde', apgLevel, true);
              }
          }
          
          schedule('0 * * * *', function () {
              holePreis();
              preisJetzt();
          });
          

          Wenn ich es starte erhalte ich allerdings folgende Fehlermeldungen und es werden keine DPs unter "Objects" angelegt:

          12bebbe4-ff77-460a-a123-bc07d9682ec6-image.png

          Hast du eine Idee wo ich einen Fehler eingebaut habe ?

          Danke.

          Beste Grüße

          arteckA Offline
          arteckA Offline
          arteck
          Developer Most Active
          schrieb am zuletzt editiert von
          #4

          @qlink du hast nicht alles so kopiert wie es sein sollte

          c876e483-e3c7-44ef-96f0-9b213ff3968e-grafik.png

          die müssen unter global scripts

          zigbee hab ich, zwave auch, nuc's genauso und HA auch

          Q 1 Antwort Letzte Antwort
          0
          • arteckA arteck

            @qlink du hast nicht alles so kopiert wie es sein sollte

            c876e483-e3c7-44ef-96f0-9b213ff3968e-grafik.png

            die müssen unter global scripts

            Q Offline
            Q Offline
            Qlink
            schrieb am zuletzt editiert von
            #5

            @arteck said in SMA Forecast Charging mit APG möglich ?:

            @qlink du hast nicht alles so kopiert wie es sein sollte

            c876e483-e3c7-44ef-96f0-9b213ff3968e-grafik.png

            die müssen unter global scripts

            Stimmt, das global_scripts.js ist noch "original".
            Ich habs mir allerdings durchgeschaut und finde keine Stellen die ich offensichtlich anpassen muss.

            Kannst du mir hier helfen und sagen was ich hier wo anpassen muss ?

            Danke.

            Beste Grüße

            arteckA 1 Antwort Letzte Antwort
            0
            • Q Qlink

              @arteck said in SMA Forecast Charging mit APG möglich ?:

              @qlink du hast nicht alles so kopiert wie es sein sollte

              c876e483-e3c7-44ef-96f0-9b213ff3968e-grafik.png

              die müssen unter global scripts

              Stimmt, das global_scripts.js ist noch "original".
              Ich habs mir allerdings durchgeschaut und finde keine Stellen die ich offensichtlich anpassen muss.

              Kannst du mir hier helfen und sagen was ich hier wo anpassen muss ?

              Danke.

              Beste Grüße

              arteckA Offline
              arteckA Offline
              arteck
              Developer Most Active
              schrieb am zuletzt editiert von
              #6

              @qlink da ist nix anzupassen
              du musst die unter 1d12e9ce-d6ed-4b8a-8288-5f8a2dc955d1-grafik.png

              nur reinkopieren

              zigbee hab ich, zwave auch, nuc's genauso und HA auch

              1 Antwort Letzte Antwort
              0
              • Q Offline
                Q Offline
                Qlink
                schrieb am zuletzt editiert von
                #7

                @arteck

                Ah, okay das wusste ich nicht.
                Hab ich erledigt.

                Wenn ich das Script startet kommen im Log nun folgende Fehlermeldungen:

                ef76261f-42ec-4853-a7da-f2425c8efdb7-image.png

                Der DP "0_userdata.0.Stromversorgung.apg.extra.apgNutzenManuell" scheint nicht angelegt zu werden vom Script, daher wohl auch die Meldung dass er ihn nicht findet...

                Diese DPs sind angelegt worden:
                b4611e8d-1c49-42d6-8bc9-07cc7c97f56d-image.png

                1 Antwort Letzte Antwort
                0
                • Q Offline
                  Q Offline
                  Qlink
                  schrieb am zuletzt editiert von Qlink
                  #8

                  @arteck

                  Ich bin dieses WE schon ein Stückchen weiter gekommen. Die DPs sind nun großteils alle angelegt worden.

                  Den einen oder anderen Fehler bekomme ich allerdings noch angezeigt.
                  Aber der Reihe nach.

                  Ich würde gerne zuerst mal das get_solcast_data script zum Laufen bekommen.
                  Ich bekomme folgende Fehlermeldungen im Log:

                  2025-07-14 06:01:00.652 - error: javascript.0 (26929) script.js.PV_Regelung.get_solcast_data: TypeError: Cannot read properties of undefined (reading 'length')
                  2025-07-14 06:01:00.652 - error: javascript.0 (26929) at datenErzeugen (script.js.PV_Regelung.get_solcast_data:156:31)
                  2025-07-14 06:01:00.652 - error: javascript.0 (26929) at Object. (script.js.PV_Regelung.get_solcast_data:145:13)
                  2025-07-14 06:01:00.652 - error: javascript.0 (26929) at /opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1228:38
                  2025-07-14 06:01:00.652 - error: javascript.0 (26929) at processTicksAndRejections (node:internal/process/task_queues:95:5)
                  2025-07-14 06:01:00.659 - info: javascript.0 (26929) State value to set for "0_userdata.0.Stromversorgung.pvforecast.response" has to be type "string" but received type "object"
                  

                  Hier das Script mit meinen ausgefüllten Daten: (meine key_id habe ich mit xxxx maskiert)

                  // @ts-ignore
                  const moment = require('moment');
                  const options = { hour12: false, hour: '2-digit', minute: '2-digit' };
                  
                  // welche javascript instanz wird genutzt, für diese muss in Einstellungen ASTRO Zeit generierung aktiviert sein 
                  const javascriptI = 'javascript.0';    
                  
                  // zum ändern ab hier
                  const summeDpAnlegen = true;   // einmalig manuell für 24h auf true setzten, es werden summen Dp's angelegt   <<<<<<<<-----------------------------------  wichtig
                  
                  const key_id    = "xxxx-xxxx-xxxx-xxxx";
                  
                  const seite1    = 'Gesamt';                     // name dp1  frei wählbar, sonne vormittags 
                  const seite1Key = "xxxx-xxxx-xxxx-xxxx";
                  
                  const seite2    = '';                    //name dp2  frei wählbar, sonne nachmittags, leer wenn nur eine Seite genutzt wird
                  const seite2Key = "yyyy-yyyy-yyyy-yyyy";        // nicht ändern wenn nicht genutzt
                  
                  const gesamt    = 'gesamt';                     // dp für zusammenrechnen muss in ladenNachPrognose angepasst werden wenn hier geändert
                  
                  const _influxDb                     = false;   // wenn grafana output erwünscht benötigt wird eine influx.0 instanz
                  const  influxInstance               = 'influxdb.0';
                  const _influxDbMeasurementGesamt    = 'pvforecast.0.summary.power';
                  const _influxDbMeasurementStrasse   = 'pvforecast.0.plants.strasse.power';
                  const _influxDbMeasurementGarten    = 'pvforecast.0.plants.garten.power';
                  
                  const mainObject            = '0_userdata.0.Stromversorgung.pvforecast';
                  const mainObjectToday       = '0_userdata.0.Stromversorgung.pvforecast.today';
                  const mainObjectTomorrow    = '0_userdata.0.Stromversorgung.pvforecast.tomorrow';
                  const _abbrechenBei         = '00:00';   // ab wieviel Uhr kommt nix mehr, kann so bleiben
                  
                  // pv_estimate   – Der Realist: Dies ist sozusagen die Standardvorhersage. Denk an ihn als den durchschnittlichen Wert, basierend auf den aktuellen Wetterdaten und Modellen. Er sagt uns, was wir in einem normalen Szenario erwarten können – weder zu optimistisch noch zu pessimistisch.
                  // pv_estimate10 – Der Vorsichtige: Jetzt wird's interessant. Dieser Wert ist die 10. Perzentile, also eher auf der niedrigen Seite. Er sagt uns, dass es eine 90 %ige Chance gibt, dass die tatsächliche Leistung höher ausfällt. Wenn du also lieber auf Nummer sicher gehst und nicht gerne enttäuscht wirst, ist das dein Wert.
                  // pv_estimate90 – Der Optimist: Im Gegensatz zum pv_estimate10 zeigt uns der pv_estimate90 die sonnige Seite. Dieser Wert ist die 90. Perzentile – eine Art Best-Case-Szenario. Hier sagen die Daten, dass es nur eine 10 %ige Chance gibt, dass die Leistung diesen Wert überschreitet. Ideal, wenn du die Dinge gerne von der besten Seite betrachtest.​
                  let prognose1              = 1;    // 0 = realistisch, 1 = vorsichtig, 2 = optimistisch
                  let prognose2              = 0;    // 0 = realistisch, 1 = vorsichtig, 2 = optimistisch
                  const ersteTagAbfrage      = seite2;   // wir brauchen morgends werte, welche Seite soll zuerst abgefragt werden
                  
                  //-------------------------------------------------------------
                  
                  const _baseUrl = "https://api.solcast.com.au/rooftop_sites/";
                  const _hours = 24;
                  
                  let _tickerAbholung = 0;
                  let _errorMrk = false;
                  
                  let _aufrufUrl   = '';
                  let _aufrufSeite = '';
                  
                  // ------------------------------------------------------------------------------------------------------------------
                  
                  //                       zum testen
                  //const testDaten = getState(`${mainObject}.response`).val;
                  //datenErzeugen(JSON.parse(testDaten), seite2);
                  
                  //  initialisiere einmal in den nacht um 2 Uhr
                  schedule({ astro: 'sunset' }, () => {
                      _tickerAbholung = 0;
                  });
                  
                  schedule({ astro: 'sunrise' }, () => {
                      initialPV();
                  
                      if (seite2.length > 0) {
                          _aufrufUrl   = `${seite2Key}/forecasts?format=json&api_key=${key_id}`;
                          _aufrufSeite = seite2;
                          toLog(`Hole PV ${_aufrufSeite}`, true);
                          requestData(_aufrufUrl, _aufrufSeite);
                      }
                  
                      _aufrufUrl   = `${seite1Key}/forecasts?format=json&api_key=${key_id}`;
                      _aufrufSeite = seite1;
                      toLog(`Hole PV ${_aufrufSeite}`, true);
                      requestData(_aufrufUrl, _aufrufSeite);
                  
                      _tickerAbholung = +1;
                  });
                  
                  
                  schedule('1 6 * * *', function () {   // um 6 immer abholen damit wir morgen gültige Tageswerte haben    
                      if (_tickerAbholung < 1) {     // wurde schon mal abgeholt
                          _aufrufUrl   = `${seite2Key}/forecasts?format=json&api_key=${key_id}`;
                          _aufrufSeite = ersteTagAbfrage;
                          requestData(_aufrufUrl, _aufrufSeite);
                      }
                  });
                  
                  // 10 request sind frei bei solcast.com
                  schedule('1 7,9,10 * * *', function () {
                      const _hhJetzt  = getHH();
                      const sunup = getState(javascriptI + '.variables.astro.sunrise').val;  
                      
                      let seite = seite1;
                      if (seite2.length > 0) { 
                          seite = seite2;
                      }
                  
                      if (_hhJetzt >= parseInt(sunup.slice(0, 2)) && seite2.length > 0) {  
                          _aufrufUrl   = `${seite2Key}/forecasts?format=json&api_key=${key_id}`;
                          _aufrufSeite = seite;
                          requestData(_aufrufUrl, _aufrufSeite);
                          _tickerAbholung = +1;
                      }
                  });
                  
                  schedule('2 8,12,13,15 * * *', function () {
                      const _hhJetzt            = getHH();
                      const sunup = getState(javascriptI + '.variables.astro.sunrise').val;  
                      
                      if (_hhJetzt >= parseInt(sunup.slice(0, 2))) {     
                          _aufrufUrl   = `${seite1Key}/forecasts?format=json&api_key=${key_id}`;
                          _aufrufSeite = seite1;
                          requestData(_aufrufUrl, _aufrufSeite);
                          _tickerAbholung = +1;
                      }
                  });
                   
                  schedule('5,10,15 * * * *', function () {
                      if (_errorMrk) {
                          toLog(`ERROR - Hole PV ${_aufrufSeite} ticker ${_tickerAbholung}`, true);
                          requestData(_aufrufUrl, _aufrufSeite);
                          _tickerAbholung = +1;
                      }
                  });
                   
                  // ------------------------------------------------------------------------------------------------------------------
                  
                  /*************** ab hier nix ändern  ***************************** */
                  

                  "Create states for all astro times" ist aktiviert im javascript Adapter.

                  Habe ich was zum Ausfüllen übersehen ?
                  Ich kann die Fehler leider nicht zuordnen.

                  Die DPs werden angelegt, aber es sind alle nur mit 0W befüllt:
                  2b5e2869-fb95-481d-935b-788bd1ec085f-image.png

                  Vielen Dank für deine Hilfe.

                  Beste Grüße

                  1 Antwort Letzte Antwort
                  0
                  • arteckA Offline
                    arteckA Offline
                    arteck
                    Developer Most Active
                    schrieb am zuletzt editiert von
                    #9

                    @qlink sagte in SMA Forecast Charging mit APG möglich ?:

                    script.js.PV_Regelung.get_solcast_data:156:31

                    man sollte wissen wie man ein LOG zu lesen hat

                    script.js.PV_Regelung.get_solcast_data:156:31
                    

                    bedeutet dass im script.js.PV_Regelung.get_solcast_data in der Zeile 156 position 36 der Fehler zu finden ist

                    zigbee hab ich, zwave auch, nuc's genauso und HA auch

                    Q 1 Antwort Letzte Antwort
                    0
                    • arteckA arteck

                      @qlink sagte in SMA Forecast Charging mit APG möglich ?:

                      script.js.PV_Regelung.get_solcast_data:156:31

                      man sollte wissen wie man ein LOG zu lesen hat

                      script.js.PV_Regelung.get_solcast_data:156:31
                      

                      bedeutet dass im script.js.PV_Regelung.get_solcast_data in der Zeile 156 position 36 der Fehler zu finden ist

                      Q Offline
                      Q Offline
                      Qlink
                      schrieb am zuletzt editiert von Qlink
                      #10

                      @arteck

                      das ist mir soweit klar...

                      Jedoch hab ich in diesen Bereichen des Scripts nichts verändert (weil ist ja im Abschnitt "ab hier nix ändern")

                      Zeile 156:

                              const endtime   = Date.parse(array[i].period_end);
                      

                      bzw. hier ein größerer Ausschnitt der function:

                      function datenErzeugen(array, seite) {
                          const list          = [];  
                          let heute           = true;
                      
                          for (let i = 0; i < array.length; i++) {                              
                              const endtime   = Date.parse(array[i].period_end);
                              const startTime = new Date(endtime - 1800000);
                      
                              let wert1   = array[i].pv_estimate;
                              let wert2   = array[i].pv_estimate;
                      
                              switch(prognose1) {
                                  case 1:
                                      wert1 = array[i].pv_estimate10;
                                      break;
                                  case 2:
                                      wert1 = array[i].pv_estimate90;
                                      break;
                                  default:
                                      // nix
                              } 
                      
                              switch(prognose2) {
                                  case 1:
                                      wert2 = array[i].pv_estimate10;
                                      break;
                                  case 2:
                                      wert2 = array[i].pv_estimate90;
                                      break;
                                  default:
                                      // nix
                              } 
                      
                              list[i] = {
                                  starttime: startTime / 1000,
                                  endtime: endtime / 1000,
                                  wert1: Math.round(wert1 * 1000),
                                  wert2: Math.round(wert2 * 1000),
                              };
                      
                           //   console.warn('list ' + JSON.stringify(list));
                          }
                      

                      Was soll hier falsch sein ?

                      Beste Grüße

                      arteckA 1 Antwort Letzte Antwort
                      0
                      • Q Qlink

                        @arteck

                        das ist mir soweit klar...

                        Jedoch hab ich in diesen Bereichen des Scripts nichts verändert (weil ist ja im Abschnitt "ab hier nix ändern")

                        Zeile 156:

                                const endtime   = Date.parse(array[i].period_end);
                        

                        bzw. hier ein größerer Ausschnitt der function:

                        function datenErzeugen(array, seite) {
                            const list          = [];  
                            let heute           = true;
                        
                            for (let i = 0; i < array.length; i++) {                              
                                const endtime   = Date.parse(array[i].period_end);
                                const startTime = new Date(endtime - 1800000);
                        
                                let wert1   = array[i].pv_estimate;
                                let wert2   = array[i].pv_estimate;
                        
                                switch(prognose1) {
                                    case 1:
                                        wert1 = array[i].pv_estimate10;
                                        break;
                                    case 2:
                                        wert1 = array[i].pv_estimate90;
                                        break;
                                    default:
                                        // nix
                                } 
                        
                                switch(prognose2) {
                                    case 1:
                                        wert2 = array[i].pv_estimate10;
                                        break;
                                    case 2:
                                        wert2 = array[i].pv_estimate90;
                                        break;
                                    default:
                                        // nix
                                } 
                        
                                list[i] = {
                                    starttime: startTime / 1000,
                                    endtime: endtime / 1000,
                                    wert1: Math.round(wert1 * 1000),
                                    wert2: Math.round(wert2 * 1000),
                                };
                        
                             //   console.warn('list ' + JSON.stringify(list));
                            }
                        

                        Was soll hier falsch sein ?

                        Beste Grüße

                        arteckA Offline
                        arteckA Offline
                        arteck
                        Developer Most Active
                        schrieb am zuletzt editiert von
                        #11

                        @qlink sagte in SMA Forecast Charging mit APG möglich ?:

                        array

                        das array ist bestimmt leer

                        zigbee hab ich, zwave auch, nuc's genauso und HA auch

                        1 Antwort Letzte Antwort
                        0
                        • Q Offline
                          Q Offline
                          Qlink
                          schrieb am zuletzt editiert von Qlink
                          #12

                          @arteck

                          Danke für die Rückmeldung, aber das hilft mir leider nicht wirklich weiter.

                          Wie kann ich das lösen bzw. was ist die Ursache, dass das Array leer ist ?
                          Wo liegt das Problem ?

                          Beste Grüße

                          1 Antwort Letzte Antwort
                          0
                          • Q Offline
                            Q Offline
                            Qlink
                            schrieb am zuletzt editiert von
                            #13

                            @arteck

                            das pvsolcast script hab ich nun soweit am Laufen :)

                            eine Frage zum bat_regelung script:

                            Kann es sein, dass ich den DP "0_userdata.0.Stromversorgung.aktualisierung" manuell anlegen muss ?
                            Das ist soweit ich sehe, der einzige DP, der nicht automatisch angelegt wurde, somit fehlt und klarerweise beim Script einen Fehler produziert ...

                            dd417c58-bd79-4c20-ba78-2c3a203f4295-image.png

                            Beste Grüße

                            arteckA 1 Antwort Letzte Antwort
                            0
                            • Q Qlink

                              @arteck

                              das pvsolcast script hab ich nun soweit am Laufen :)

                              eine Frage zum bat_regelung script:

                              Kann es sein, dass ich den DP "0_userdata.0.Stromversorgung.aktualisierung" manuell anlegen muss ?
                              Das ist soweit ich sehe, der einzige DP, der nicht automatisch angelegt wurde, somit fehlt und klarerweise beim Script einen Fehler produziert ...

                              dd417c58-bd79-4c20-ba78-2c3a203f4295-image.png

                              Beste Grüße

                              arteckA Offline
                              arteckA Offline
                              arteck
                              Developer Most Active
                              schrieb am zuletzt editiert von
                              #14

                              @qlink sagte in SMA Forecast Charging mit APG möglich ?:

                              aktualisierung

                              jau den hab ich vergessen..kannst aber auch auskommentieren oder löschen.. hat keine Grundfunktion

                              zigbee hab ich, zwave auch, nuc's genauso und HA auch

                              1 Antwort Letzte Antwort
                              0
                              Antworten
                              • In einem neuen Thema antworten
                              Anmelden zum Antworten
                              • Älteste zuerst
                              • Neuste zuerst
                              • Meiste Stimmen


                              Support us

                              ioBroker
                              Community Adapters
                              Donate

                              770

                              Online

                              32.6k

                              Benutzer

                              82.1k

                              Themen

                              1.3m

                              Beiträge
                              Community
                              Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                              ioBroker Community 2014-2025
                              logo
                              • Anmelden

                              • Du hast noch kein Konto? Registrieren

                              • Anmelden oder registrieren, um zu suchen
                              • Erster Beitrag
                                Letzter Beitrag
                              0
                              • Home
                              • Aktuell
                              • Tags
                              • Ungelesen 0
                              • Kategorien
                              • Unreplied
                              • Beliebt
                              • GitHub
                              • Docu
                              • Hilfe