Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Waly_de

    NEWS

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    • Monatsrückblick - April 2025

    W
    • Profile
    • Following 0
    • Followers 10
    • Topics 5
    • Posts 512
    • Best 64
    • Groups 2

    Waly_de

    @Waly_de

    Pro

    80
    Reputation
    182
    Profile views
    512
    Posts
    10
    Followers
    0
    Following
    Joined Last Online

    Waly_de Follow
    Pro Starter

    Best posts made by Waly_de

    • ecoflow-connector-Script zur dynamischen Leistungsanpassung

      So, meine Anbindung läuft jetzt seit einigen Tagen sauber. Daher spendiere ich dem Skript mal einen eigenen Thread.

      Anbei findet ihr ein Skript, das eine Verbindung zwischen euren ecoflow-Geräten und ioBroker herstellen kann. Dabei nutzt es die gleiche Schnittstelle wie die ecoFlow App. Ihr benötigt lediglich eure Zugangsdaten zur App und die Seriennummern eurer Geräte, um dieses Skript nutzen zu können. Alle bekannten übermittelten Daten werden in ioBroker als Zustände angelegt. Viele davon sind noch unbekannt. Wenn ihr herausfindet, was sich hinter den unbekannten Daten verbirgt, kann ich die Zustandsnamen anpassen.

      Achtung: Der ecoflow-Server sendet unfassbar viele Nachrichten. Wenn ihr mehrere Geräte habt, kann dies euer System stark belasten und sogar zu Abstürzen führen. Vielleicht bekommt Ihr auch diese Meldung und das Script wird beendet:

       [error]: javascript.0 (5490) Script script.js.EcoFlow is calling setState more than 1000 times per minute! Stopping Script now! Please check your script!
      

      Daher empfehle ich, nicht alle Geräte dauerhaft zu abonnieren (dies kann über einen Parameter in der Einstellungssektion festgelegt werden). Es werden nur die PowerStreams benötigt, um die Einspeiseleistung anpassen zu können.
      Sonst kann dieser Grenzwert aber auch in den Einstellungen der Javascript-Instanz heraufgesetzt werden. 3000 dürfte für die meisten Szenarien reichen. (Siehe Screenshot weiter unten. Der Wert befindet sich unten links)

      Damit kommen wir zur eigentlichen interessanten Funktion des Skripts:
      Wenn ihr ein Smartmeter habt, das euren aktuellen Stromverbrauch in Echtzeit anzeigen kann, könnt ihr es an ioBroker anbinden. Informationen dazu findet ihr im Netz.

      Hier hab ich das Ding gekauft (Wenn ihr über die Links kauft, bekomme ich ein paar Cent Provision ab ;-)):
      Hichi Wifi, IR Lesekopf für Stromzähler
      https://ebay.us/3X1pkH
      Der Verkäufer hat auch ein tolles Video gemacht, wie man es einrichtet 😉

      Es funktionieren aber auch viele andere Zähler wie z.B.:
      Der Shelly 3EM

      Tibber-Kunden mit Pulse empfehle ich die lokale Einbindung des Pulse als Smartmeter mit meinem Script:
      https://forum.iobroker.net/topic/70758/tibber-pulse-verbrauchsdaten-lokal-auslesen

      Das Skript passt dann die Einspeiseleistung des PowerStream dynamisch an, sodass möglichst der gesamte Verbrauch durch die Einspeisung gedeckt wird, aber nichts ins Netz verschenkt wird. Erst wenn die Batterie voll geladen ist, wird die gesamte Leistung ins Netz eingespeist (wenn ihr das möchtet).

      Das Smartmeter muss den aktuellen Verbrauch möglichst in Echtzeit und in der Einheit Watt in einem Objekt von IOBroker zur Verfügung stellen. Im Script muss dann nur noch der Pfad zu diesem Objekt unter "SmartmeterID:" eingetragen werden. Am einfachsten geht das über die Adminoberfläche von IOBroker. Klickt auf Objekte und sucht das Objekt eures Smartmeters mit dem "Watt"-Wert im Objektbaum:
      Bildschirmfoto 2023-09-04 um 09.55.08.jpg
      Wenn Ihr auf das markierte Symbol klickt, ist der Pfad in euerer Zwischenablage gespeichert.
      Jetzt müsst Ihr ihn nur noch hinter "SmartmeterID:" im Script einfügen. Achtet darauf, dass Ihr es zwischen die "" einfügt.
      Wenn alles geklappt hat, der Pfad stimmt und das Script läuft, solltet Ihr unter Objekte: "0_userdata.0.ecoflow.RealPower" sehen, dass sich dieser Wert regelmäßig anpasst.

      Es können mehrere PowerStreams konfiguriert werden. Im Moment wird jedoch nur der erste in der Konfiguration gesteuert. Ich habe zum Beispiel zwei PowerStreams, einen mit und einen ohne Batterie. Dadurch steht tagsüber mehr Leistung zum Laden der Batterie zur Verfügung. Die Daten werden vom Skript verwendet, um die optimale Einspeiseleistung zu berechnen. In Zukunft könnten auch mehrere PowerStreams gesteuert werden.

      Nochmals vielen Dank an alle hier im Forum, die mit Ihrer Arbeit die Anbindung erst möglich gemacht haben!
      Ursprünglicher Beitrag: https://forum.iobroker.net/topic/54929/adapter-für-ecoflow-einbindung/

      Wichtig: Zur Installation müssen 2 Module installiert werden. Einfach in den Einstellungen der Javascriptinstanz unter Zusätzliche Module die beiden Namen eintragen und speichern ("mqtt" und "protobufjs")

      Bildschirmfoto 2023-07-17 um 10.56.49.jpg

      Erweiterungen:
      Tibber Modul
      Kann als eigenes, neues Script neben dem Hauptscript angelegt werden.

      //*************************************************************
      // Tibber Modul
      //*************************************************************
      // Schaltet die Regelung der Powerstation ab, wenn der Strom billig ist 
      // und einen beliebigen Schalter zum Aktivieren der AC-Ladung ein, bis die Batterie 
      // den durch der "BatMax" festgelegten Ladestand in % erreicht hat
      // 
      // Diese Parameter aus dem Hauptscript sind wichtig:
      // RegulationOffPower: -2 // Wird die Regulation per State abgestellt, wird die Einspeiseleistung des ersten Powerstreams auf diesen Wert gesetzt (-1 = keine Änderung, -2 = Batterie Priomodus)
      // RegulationState: "Regulate" // Erzeugt der State zum Ein und Aus-Schalten der Regulation.
      //
      // Das Script versucht selbst die ID's für den TibberStatus und den Batteriestand zu ermitteln.
      // Wenn das nicht klappt bitte einfach die richtigen ID's hinter "batSocID" und "tibberID" angeben.
      //
      //*************************************************************
      
      //*************************************************************
      // Konfiguration laden, wenn nicht im Originalscript eingefügt
      //*************************************************************
      var ConfigData = {
          statesPrefix: '0_userdata.0.ecoflow',
          RegulationState: "Regulate"
      }
      if (typeof ConfigData.email === 'undefined') {
          try {
              let tempConfigData = getState("0_userdata.0.ecoflow.Settings.ConfigData").val
              if (typeof tempConfigData !== 'object' && tempConfigData !== null) {
                  tempConfigData = JSON.parse(tempConfigData)
              }
              if (typeof tempConfigData === 'object' && tempConfigData !== null) {
                  if (tempConfigData.email !== undefined) {
                      ConfigData = tempConfigData;
                      //log("wurde geladen als object")
                  }
              }
          } catch (error) {
              log("Konfiguration wurde nicht geladen: " + error.message)
          }
      }
      
      /***************************************
      **********  YOUR DATA HERE  ************ 
      ****************************************/
      var tibberConfig = {
          BatMax: 99,                                  //Bei diesem Ladestand stoppt die AC-Ladung und das Script wird wieder eingeschaltet 
          BatMin: 95,                                  //Der Ladestand der Batterie muss kleiner sein als BatMin , damit der Akku per AC-Ladung geladen wird 
          SwitchID: "sonoff.0.NOUS-DVES_F0A844.POWER", //ID des Schalters, der die AC-Ladung einschaltet. "true" und "false" werden gesetzt
          LevelToSwitch: [                             //Hier die Werte definieren, bei denen die AC-Ladung eingeschaltet werden soll
              //"NORMAL",
              //"CHEAP",                                 //Einkommentieren falls gewüscht 
              "VERY_CHEAP"
          ],
      }
      //***************************************/
      // Nur angeben, wenn automatische Ermittlung fehlschlägt
      //***************************************/
      let batSocID = getState(ConfigData.statesPrefix + ".Settings.Tibber.tibberBatSocID").val
      let tibberID = getState(ConfigData.statesPrefix + ".Settings.Tibber.tibberID").val
      //***************************************/
      
      
      var idOK = false
      if (!batSocID || !tibberID) {
          log("Versuche die IDs für Tibber und Batterielevel zu finden")
          $("tibberlink.*.Homes.*.CurrentPrice.level").each(function (id, i) {
              tibberID = id
              createState(ConfigData.statesPrefix + ".Settings.Tibber.tibberID", tibberID, false)
              log("TibberID gefunden und gespeichert:" + id)
          })
          $(ConfigData.statesPrefix + ".app_device_property_*.data.InverterHeartbeat.batSoc").each(function (id, i) {
              if (getState(id).val > 0) {
                  batSocID = id
                  createState(ConfigData.statesPrefix + ".Settings.Tibber.tibberBatSocID", batSocID, false)
                  log("batSocID gefunden und gespeichert:" + id)
              }
          })
          if (!batSocID || !tibberID) {
              log("Fehler bei der Ermittlung der IDs. Bitte händisch ins Script eintragen", 'error')
          } else {
              idOK = true
          }
      } else {
          idOK = true
      }
      if (idOK) {
          checkTibber()
          on({ id: tibberID, change: "ne" }, function (obj) {
              //log("Tibber Modul. tibberID Event:" + obj.state.val)
              checkTibber()
          });
          on({ id: batSocID, change: "ne" }, function (obj) {
              //log("Tibber Modul. batSocID Event:" + obj.state.val + "%")
              checkTibber()
          });
      }
      function checkTibber() {
          if (tibberID && batSocID) {
              const RegulateID = ConfigData.statesPrefix + "." + ConfigData.RegulationState
              let priceLevel = getState(tibberID).val;
              let batsoc = Number(getState(batSocID).val)
              let OldRegulate = toBoolean(getState(RegulateID).val)
              //log("Tibber Preislevel: " + priceLevel + " OldRegulate: " + OldRegulate + " batsoc: " + (batsoc))
              if ((tibberConfig.LevelToSwitch.includes(priceLevel))) {
                  if (OldRegulate) {
                      if (batsoc <= tibberConfig.BatMin) {
                          setState(RegulateID, false);  // Regulierung aus
                          setState(tibberConfig.SwitchID, true) //Schalter einschalten
                          log("Script abgeschaltet AC-Ladung Ein, weil priceLevel:" + priceLevel + " batsoc:" + batsoc + "%")
                      }
                  } else {
                      if (batsoc >= tibberConfig.BatMax) {
                          setState(RegulateID, true);  // Regulierung an
                          setState(tibberConfig.SwitchID, false) //Schalter ausschalten
                          log(" Batterie bei BatMax. Script eingeschaltet AC-Ladung Aus. priceLevel:" + priceLevel + " batsoc:" + batsoc + "%")
                      }
                  }
              } else {
                  if (!OldRegulate) {
                      setState(RegulateID, true);  // Regulierung an
                      setState(tibberConfig.SwitchID, false) //Schalter ausschalten
                      log("Script eingeschaltet AC-Ladung Aus. priceLevel:" + priceLevel + " batsoc:" + batsoc + "%")
                  }
              }
          } else {
              //log("checkTibber skip. batsocID und/oder tibberID fehlen.")
          }
      }
      //*************************************************************
      

      Tibber-Pulse als Smartmeter nutzen und lokal auslesen:
      https://forum.iobroker.net/topic/70758/tibber-pulse-verbrauchsdaten-lokal-auslesen

      Unterstütze das Projekt 'ecoflow-connector'
      Wenn dir das Script zur dynamischen Leistungsanpassung für den IObroker gefällt und du es nützlich findest, ziehe bitte in Erwägung, eine kleine Spende via PayPal zu hinterlassen.
      Jeder Beitrag hilft, das Projekt am Laufen zu halten und weitere Updates zu ermöglichen.
      Danke für deine Unterstützung!
      Jetzt Spenden

      Dieses Script wird bei Änderungen und Updates immer aktualisiert:
      Nutzung auf eigene Gefahr !

      [Bildschirmfoto 2024-03-29 um 09.37.51.jpg

      • Installation von ioBroker und Skript unter UNRAID in nur 12 Minuten
      • Video mit Erklärung der Basiskonfiguration
      • Video mit Erklärung zu AdditionalPower und Überschussladung

      Download (neues JS-Script in IOBroker anlegen und den Inhalt der Datei einfügen):

      • ecoflow-connector_v125.txt (13.05.2024)
      • 1.2.5.f1 Fork von Florian Vogt (25.06.2024)
        Feature hinzugefügt, um die Größe der Delta-Speicher beim Ausbalancieren der Entladeleistung zu berücksichtigen, damit die Batterien gleichmäßig geleert werden.
        Parameter battCapacity bei den Einstellungen für PowerStream = Kapazität der angeschlossenen Batterie in kWh, default = 1
        ecoflow-connector_v125_mod_FV.txt (25.06.2024)

      ältere Versionen:

      • ecoflow-connector_v124.txt (22.04.2024)
      • ecoflow-connector_v123.txt (21.02.2024)
      • ecoflow-connector_v121_05.01.2024.txt
      • ecoflow-connector_v12_.04.12.2023.txt
      • ecoflow-connector_v1162_04.11.2023.txt
      • ecoflow-connector_v115_02.10.2023.txt
      • ecoflow-connector_v1142_26.09.2023
      • ecoflow-connector_v1132_31.08.2023
      • ecoflow-connector_v112_17.08.2023)
      posted in JavaScript
      W
      Waly_de
    • RE: Adapter für Ecoflow Einbindung

      @applepro Es sieht ganz gut aus 🙂 Dank xNodKane's Arbeit hab ich es inzwischen geschafft sowohl einige der Statusmeldungen (PV1 und PV2 Watt sowie Leistung zum Haus) auszulesen, als auch den Wert für "Leistungsbedarf am AC..." zu schreiben.
      Allerdings gibt es noch einige Probleme.

      Das wichtigste ist, die entsprechenden Felder im MQTT auf den Typ "file" zu stellen und dann den Adapter neu zu starten...

      Danach könnt ihr mit diesem Script den Inhalt auslesen und mit https://protobuf-decoder.netlify.app/ entschlüsseln.

      getBinaryState('mqtt.<Instanznummer>.app.<USER_ID>.<SERIENNUMMER>.thing.property.set', function (err, data) {
        log("frisch ausgelesen:"+ data.toString("hex"))
      });
      

      Senden geht dann so:

      sendTo('mqtt.<Instanznummer>', 'sendMessage2Client', {topic: '/app/.<USER_ID>/<SERIENNUMMER>/thing/property/set', message:<buffer>});
      

      die Daten müssen als Buffer object übergeben werden.

      Viel Erfolg 🙂

      Ein Problem, wofür vielleicht einer von Euch eine Lösung weiß:
      Wenn ich den STATE im MQTT auf 'file' umstelle, kann ich keine Änderungen mehr Monitoren.

      on({id: /mqtt.2.app.device.property.HWXXXXXXXXXXXX/, change: 'any'}, function (obj) {
          log("EVENT!")
      });
      

      ...da kommt nach der Umstellung nichts mehr. Hat jemand eine Idee?

      VG
      Markus

      posted in Einbindung von Geräten
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      So ich wage mal die Veröffentlichung:

      (1.1.3.2) 31.08.2023 *

      • 0 bei MinValueMin greift auf Eichtzeitdaten (Realpower) zu. (nicht empfohlen)
      • Neue Writables für den Delta 2 Max: quietCfg,ACenabled,maxChgSoc,minDsgSoc,bpPowerSoc,fastChgWatts,slowChgWatts,chgPauseFlag,dcChgCfg,dcChgCfg2,USB,12VDC,smartgenClose,smartgenOpen,standbyTime,lcdTime
      • Eigene Bezeichnung für den Delta 2 Max (D2M)
      • Achtung neue Bezeichnungen für Typen: Powerstrem:"PS"; DeltaMax:"DM"; Delta2: "D2" ; Delta2 Max: "D2M"; SmartPlug: "SM"; Andere: "NA"
      • RegulationOffPower ist in die Powerstream-Einstellungen gewandert und kann für jeden PS einzeln eingestellt werden.
      • RegulationState schaltet jetzt RegulationOffPower für alle PS einzeln
      • RegulationOffPower: -2 schaltet den PS in den batterie Prioritätsmodus wenn über RegulationState abgestellt wird
      • Neue Einstellung für PS: prioOffOnDemand: Wattwert des Bedarfs, bei dem dem zurück in den Strom-Priomodus gechaltet wird. 0 für kein Rückschalten.
      • Verschiedene Optimierungen

      ecoflow-connector_v1132

      posted in JavaScript
      W
      Waly_de
    • RE: Adapter für Ecoflow Einbindung

      @xnodkane
      gut .. hab nämlich keine Smartplugs...

      Hab heute auch getüftelt...
      Hier mal meine Version ... Hab auch alles eingefügt was ich gesehen habe aber nicht zuordnen kann.

      syntax = "proto3";
      
      message PowerItem {
        optional Meta meta = 1;
        optional uint32 src = 2;
        optional uint32 dest = 3;
        optional uint32 unknown1 = 4;
        optional uint32 unknown2 = 5;
        optional uint32 unknown3 = 6;
        optional uint32 unknown4 = 7;
        optional uint32 cmdFunc = 8;
        optional CmdFunction cmdId = 9;
        optional uint32 unknown5 = 10;
        optional uint32 needAck = 11;
        uint64 timestamp = 14;
        optional uint32 unknown6 = 16; //3 Byte?
        optional uint32 unknown7 = 17; //3 Byte?
        optional string OS = 23;
        optional string serialNumber = 25;
      }
      
      message PowerMessage {
        PowerItem item = 1;
      }
      
      message Meta {
      
        optional int32 value = 1;
        optional int32 plugPower = 10; 
        optional int32 M_Unknown7 = 14; // bei Prio-änderung gesehen
        optional int32 M_Unknown8 = 15; //bei Prio-änderung gesehen
        
        optional int32 PV1_Power = 19;
        optional int32 M_Unknown1 = 21;
        optional int32 PV2_Power = 24;
        optional int32 From_Bat_Power = 29;
        optional int32 Batt_Poz = 31;
        optional int32 M_Unknown2 = 33;
        optional int32 M_Unknown3 = 35;
        optional int32 M_Unknown6 = 37;
        optional int32 ToHome_Power = 38;
        optional int32 M_Unknown9 = 43; //prio Power von summe PVx_Power abziehen ?
        optional int32 Needed_Power = 48;
        optional int32 M_Unknown4 = 59;
        optional int32 M_Unknown5 = 60;
      }
      
      enum CmdFunction {
          Unknown = 0;
          PermanentWattsPack = 129;
          SupplyPriorityPack = 130;
      }
      
      posted in Einbindung von Geräten
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @mattenausohz

      ok hat erstmal keinen Sinn mehr .. muss auch los. aber ich kann jetzt den Fehler auf einem anderen host reproduzieren ...

      kümmer mich später und melde mich....

      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @dreffi Danke für die Rückmeldung..... Da es sonst kaum Rückmeldung gibt, hoffe ich das es bei den anderen auch läuft 🙂 Leider kann ich wegen des Wetters im Moment nicht so wirklich testen. Aber aber ich habe noch mal einiges hinzugefügt und verbessert. Einige wenige Dinge muss ich noch abschließend testen, dann werd ich es hoch laden. Hier schon mal zur Vorfreude die wichtigsten Dinge:

      (1.0.0) 06.08.2023

      • Neuer State "lowestValue" zeigt die Grundlage zur Berechnung der Einspeiseleistung an und repräsentiert niedrigsten Wert des realen Verbrauchs in den letzten mit "MinValueMin" eingestellten Minuten.
      • Neue Einstellung MinValueAg: Art der Ermittlung des kleinsten Wertes 0 = Minimalwert, 1 = Durchschnittswert.
      • Neues Feature: Wenn die volle Leistung (600w) in die Batterie geht, wird die Einspeiseleistung in Stufen erhöht, auch wenn dann Leistung ins Netz geht, um möglichst das volle Potenzial der vorhandenen Solarenergie zu nutzen.
      • Neue Einstellungen: lowBatLimitPozOn, lowBatLimitPozOff und lowBatLimit. Bei Unterschreiten der Batterieladung von "lowBatLimitPozOn" % ist die maximale Einspeiseleistung auf "lowBatLimit" W limitiert, bis der Ladezustand wieder bei "lowBatLimitPozOff" ist.
      • Neue Einstellungen: RegulationState. Wenn angegeben, kann mit diesem State die Regulation ein- und ausgeschaltet werden (Wird automatisch unterhalb 0_userdata.0.ecoflow angelegt).
      • Neue Einstellungen: RegulationOffPower. Wird die Regulation per State abgestellt, wird die Einspeiseleistung des ersten Powerstreams auf diesen Wert gesetzt. (-1 = keine Änderung).

      Und ganz wichtig.... es gibt einen Spendenlink 😉 Bitte unterstützt mich bei der Weiterentwicklung.

      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @ponti92 sagte in ecoflow-connector-Script zur dynamischen Leistungsanpassung:

      Es könnte am reconnect liegen, was zwischen den beiden Werten stattfand:

      Ahh ok ...
      jetzt kann ich mir vorstellen wie es dazu kommt.
      Im Moment läuft es so, dass der Algo zur Berechnung des Realpower prüft, ob der Wert für die Tatsächlich Einspeisung "invOutputWatts" jünger als das letzte setzen der AC Leistung ist. Wenn nicht ist der wert ja veraltet und uninteressant. Ich überspringe dann den Zyklus.... wenn der wert schließlich älter als 1/2 "MinValueMin" ist, setzte ich den State invOutputWatts auf 0. weil ich davon ausgehe, dass die PS offline ist. Denke das ist ein Denkfehler...
      In diesem Fall kann dann Realpower plötzlich klein sein. Dass passiert natürlich zwangsweise dann, wenn ein reconnect nötig wird. denn dann kommen ja keine Daten mehr und es dauert vermutlich länger bis zum Reconnect als 1/2 "MinValueMin"
      Ich denke ich werde nicht sofort auf 0 setzen, sondern in dem Fall lieber weiter mit dem alten Wert arbeiten....

      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      Jetzt, wo die Sonne wieder rauskommt, wird es auch Zeit für eine neue Version des Skripts. 😉
      Ich habe den Regel-Algorithmus komplett neu programmiert. Das ist vor allem für die Regelung mehrerer PS wichtig gewesen.
      Jetzt funktioniert der Balance-Mode wesentlich besser. Es wird zunächst versucht den gesamten Bedarf aus der PV-Power aller verfügbaren PS zu decken. Erst dann werden die Batterien im Verhältnis ihrer Ladestände entladen.
      Auch sonst habe ich versucht Eure Wünsche und Anregungen umzusetzen.
      Ich poste das neue Skript zunächst nur hier unten und warte auf Euer Feedback. Ich möchte zunächst sicherstellen, dass es auch wirklich bei allen funktioniert, denn es hat sich unter der Haube viel geändert.

      Viel Spaß beim testen!

      hier das komplette Changelog der neuen Version:

      (1.1.4.1) 23.09.2023

      • Neue Einstellung: "AdditionalPower": Wenn es weitere Wechselrichter gibt, die in ioBroker erfasst werden, können diese hier angelegt werden. Die Leistung wird dann bei der Berechnung von "Realpower" berücksichtigt.
      • Delta 2 Max "Writeables" erneut überarbeitet (Danke an Ponti92 aus dem ioBroker-Forum).
      • "Maxpower" ist jetzt in den Einstellungen für jeden einzelnen PS verfügbar.
      • Komplett neues Regelwerk zum Einstellen und Verteilen der Einspeiseleistung auf mehrere PS: 
Im "Balance Mode" wird zunächst nur die verfügbare PV-Leistung aller PS eingespeist; erst danach wird aus den Batterien entnommen. Dabei richtet sich die Entladeleistung nach dem Ladestand der Batterien. Alle PS werden bei einem Messintervall gleichzeitig geregelt.
      • "battOnSwitchPrio" überarbeitet und verbessert.
      • SmartPlugs sollten nun bei Verwendung den Regelprozess nicht mehr komplett verwirren.
      • Für SmartPlugs gibt es nun auch historische Leistungsdaten in den Objekten.
      • Auch für PS wurden die historischen Leistungsdaten neu angelegt. Die Interpretation dieser Daten steht noch aus. Vielleicht möchte das einer von euch machen? U.U stimmen die Feldnamen nicht mit den darin gespeicherten Werten überein.
      • Neue Einstellung: "SmartmeterTimeoutMin" und "SmartmeterFallbackPower". Wenn der letzte Wert vom Smartmeter älter als "SmartmeterTimeoutMin" ist, wird das Skript mit "SmartmeterFallbackPower" als aktuellem Realpowerwert weiterarbeiten, bis wieder aktuelle Daten geliefert werden.
      • Bekannte Writeables werden nun automatisch angelegt, auch ohne dass sie in der App geändert werden müssen (Voraussetzung ist die richtige Einstellung des Typs bei den Geräteeinstellungen)
      • Verschiedene Optimierungen und Bugfixes

      ecoflow-connector_v1141.txt

      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      Ich hab mich jetzt mal darin versucht, ein Video zu erstellen, in dem ich zunächst die Basiskonfiguration und die einzelnen Parameter darin erkläre.
      Würde mich freuen, wenn ihr mir mal sagt, ob euch das hilft. Vielleicht kann ich davon noch mehr machen.

      https://youtu.be/Drb4SHVvhRQ?si=MFi3mqYpfL8rGkxn

      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @mario1995 das hast du gut gelöst!
      Was hast du denn für eine Batterie angeschlossen?
      Wird denn in der App, wenn du den Power Stream anklickst die angeschlossene Batterie und der aktuelle Batteriestand richtig angezeigt?
      Für mich klingt das eher nach einem Softwareproblem zwischen den Ecoflow.
      Vielleicht hast du auch das Batterie Kabel vom Power Stream zur Delta in den falschen Port bei der Delta gesteckt? Das Kabel muss auf jeden Fall mit Port eins verbunden sein.

      posted in JavaScript
      W
      Waly_de

    Latest posts made by Waly_de

    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @sirdir guter Hinweis mit der Client ID.
      Ich hab das Grip mal angepasst und eine Client ID fest vergeben. Damit sollte das Problem erledigt sein.

      const axios = require('axios');
      const mqtt = require('mqtt');
      const crypto = require('crypto');
       
      //****** HIER DEINE DATEN ******
      const accessKey = 'XXXXXXXXXXXXXXXX'; // Ersetze dies mit deinem tatsächlichen Access Key
      const secretKey = 'XXXXXXXXXXXXXXXX'; // Ersetze dies mit deinem tatsächlichen Secret Key
      const deviceSNs = ['XXXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXXX']; // Mehrere Geräte-Seriennummern
      const MQTT_Clientid = 1
      //*********************/
       
       
      // Konfigurationen
      const host = 'api-e.ecoflow.com';
      const mqttHost = 'mqtt.ecoflow.com';
      const mqttPort = 8883;
      const mqttProtocol = 'mqtts';
      const mqttCert = '0_userdata.0.ecoflow.mqttCert'
      // Globale Variablen für client und certification
      let client;
      let certification;
       
      if (!existsState(mqttCert)) {
          createState(mqttCert, "");
      }
       
      // Hilfsfunktion zur Erstellung eines HMAC-SHA256-Signatur
      function createSignature(params, secretKey) {
          const queryString = Object.keys(params)
              .sort()
              .map(key => `${key}=${params[key]}`)
              .join('&');
          return crypto.createHmac('sha256', secretKey).update(queryString).digest('hex');
      }
       
      // Funktion zur Überprüfung, ob Zertifikat bereits vorhanden ist
      async function loadMQTTCertification() {
          return new Promise((resolve, reject) => {
              // Überprüfen, ob der State existiert
       
              getState(mqttCert, (err, state) => {
                  if (err) {
                      console.error('Fehler beim Überprüfen des Zertifikat-States:', err);
                      return reject(err);
                  }
                  if (state && state.val) {
                      try {
                          const storedCert = JSON.parse(state.val);
                          // Überprüfen, ob die Felder im Zertifikat vorhanden sind
                          if (storedCert.certificateAccount && storedCert.certificatePassword) {
                              console.log('Zertifikat aus ioBroker geladen');
                              return resolve(storedCert);
                          }
                      } catch (error) {
                          console.error('Fehler beim Parsen des Zertifikats:'+ error);
                      }
                  }
                  resolve(null); // Kein Zertifikat gefunden oder ungültig
              });
          });
      }
       
      // Zertifikate für MQTT abrufen und speichern
      async function getMQTTCertification() {
          const nonce = Math.floor(Math.random() * 1000000);
          const timestamp = Date.now();
          const params = {
              accessKey: accessKey,
              nonce: nonce,
              timestamp: timestamp
          };
       
          const signature = createSignature(params, secretKey);
       
          try {
              const response = await axios.get(`https://${host}/iot-open/sign/certification`, {
                  headers: {
                      accessKey: accessKey,
                      nonce: nonce,
                      timestamp: timestamp,
                      sign: signature
                  }
              });
       
              const certData = response.data.data;
              // Zertifikat in ioBroker speichern
              setState(mqttCert, JSON.stringify(certData), true);
              console.log('Zertifikat erfolgreich abgerufen und in ioBroker gespeichert');
              return certData;
          } catch (error) {
              console.error('Fehler beim Abrufen der MQTT-Zertifikate:'+ error);
              throw error;
          }
      }
       
      // Funktion zum Setzen von Parametern (z.B. permanentWatts) ohne client und certification als Parameter
      function setPermanentWatts(deviceSN, wattsValue) {
          if (!client || !certification) {
              console.error("MQTT-Client oder Zertifizierung nicht bereit.");
              return;
          }
       
          const setTopic = `/open/${certification.certificateAccount}/${deviceSN}/set`;
       
          const message = {
              id: Date.now(), // Einzigartige ID
              version: "1.0",
              cmdCode: "WN511_SET_PERMANENT_WATTS_PACK", // Befehlscode zum Setzen von permanentWatts
              params: {
                  permanentWatts: wattsValue // Der Wert, den du setzen möchtest
              }
          };
       
          client.publish(setTopic, JSON.stringify(message), (err) => {
              if (!err) {
                  console.log(`Befehl zum Setzen von permanentWatts auf ${wattsValue} W für Gerät ${deviceSN} wurde gesendet.`);
              } else {
                  console.error(`Fehler beim Senden des permanentWatts-Befehls für Gerät ${deviceSN}:` + err);
              }
          });
      }
       
      // MQTT-Verbindung herstellen und Daten empfangen
      async function startMQTTClient() {
          try {
              // Zertifikat aus ioBroker laden, falls vorhanden
              certification = await loadMQTTCertification();
              if (!certification) {
                  // Zertifikat neu generieren, wenn es nicht vorhanden ist
                  certification = await getMQTTCertification();
              }
       
              client = mqtt.connect(`${mqttProtocol}://${mqttHost}:${mqttPort}`, {
                  clientId: 'EcoFlowClient_' + MQTT_Clientid,
                  username: certification.certificateAccount,
                  password: certification.certificatePassword,
                  protocol: mqttProtocol
              });
       
              client.on('connect', () => {
                  console.log('Verbunden mit dem MQTT-Broker');
       
                  // Abonnieren von Status und anderen Daten für jedes Gerät
                  deviceSNs.forEach((deviceSN) => {
                      const quotaTopic = `/open/${certification.certificateAccount}/${deviceSN}/quota`;
                      const statusTopic = `/open/${certification.certificateAccount}/${deviceSN}/status`;
       
                      // Abonnieren des Status-Themas
                      client.subscribe(statusTopic, (err) => {
                          if (!err) {
                              console.log(`Abonniert auf Status-Topic: ${statusTopic}`);
                          } else {
                              console.error(`Fehler beim Abonnieren des Status-Themas für Gerät ${deviceSN}:` + err);
                          }
                      });
       
                      // Abonnieren des Quota-Themas
                      client.subscribe(quotaTopic, (err) => {
                          if (!err) {
                              console.log(`Abonniert auf Quota-Topic: ${quotaTopic}`);
                          } else {
                              console.error(`Fehler beim Abonnieren des Quota-Themas für Gerät ${deviceSN}:` + err);
                          }
                      });
                  });
              });
       
              client.on('message', (topic, message) => {
                  console.log(`Nachricht empfangen von Topic ${topic}: ${message.toString()}`);
              });
       
              client.on('error', (err) => {
                  console.error('MQTT-Verbindungsfehler:' + err);
                  client.end();  // Trenne die Verbindung bei einem Fehler
                  console.log('Verbindung getrennt aufgrund eines Fehlers ' + err);
              });
       
              client.on('close', () => {
                  console.log('MQTT-Verbindung geschlossen');
              });
       
          } catch (error) {
              console.error('Fehler beim Starten des MQTT-Clients:' + error);
          }
      }
      // Schließe die Verbindung, wenn das Skript gestoppt wird
      onStop(function (callback) {
          if (client) {
              client.end();
              log("Script gestoppt");
          }
          callback();
      }, 2000);
      // Start der MQTT-Client-Verbindung
      startMQTTClient();
       
       
       
      // Beispielaufruf von setPermanentWatts außerhalb von startMQTTClient()
      setTimeout(() => {
          setPermanentWatts(deviceSNs[0], 3000); // Setzt permanentWatts für ein bestimmtes Gerät auf 300 W
      }, 5000); // Warte 5 Sekunden, damit der Client verbunden ist
      
      
      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @waly_de okay, ich konnte heute noch ein bisschen Zeit aufbringen und hab es tatsächlich geschafft mit dem offiziellen API Server zu kommunizieren. Auch das Schreiben von Werten ist mir gelungen.
      Hier ist der Ansatz:

      const axios = require('axios');
      const mqtt = require('mqtt');
      const crypto = require('crypto');
      
      //****** HIER DEINE DATEN ******
      const accessKey = 'XXXXXXXXXXXXXXXX'; // Ersetze dies mit deinem tatsächlichen Access Key
      const secretKey = 'XXXXXXXXXXXXXXXX'; // Ersetze dies mit deinem tatsächlichen Secret Key
      const deviceSNs = ['XXXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXXX']; // Mehrere Geräte-Seriennummern
      const MQTT_Clientid = 1
      //*********************/
      
      
      // Konfigurationen
      const host = 'api-e.ecoflow.com';
      const mqttHost = 'mqtt.ecoflow.com';
      const mqttPort = 8883;
      const mqttProtocol = 'mqtts';
      const mqttCert = '0_userdata.0.ecoflow.mqttCert'
      // Globale Variablen für client und certification
      let client;
      let certification;
      
      if (!existsState(mqttCert)) {
          createState(mqttCert, "");
      }
      
      // Hilfsfunktion zur Erstellung eines HMAC-SHA256-Signatur
      function createSignature(params, secretKey) {
          const queryString = Object.keys(params)
              .sort()
              .map(key => `${key}=${params[key]}`)
              .join('&');
          return crypto.createHmac('sha256', secretKey).update(queryString).digest('hex');
      }
      
      // Funktion zur Überprüfung, ob Zertifikat bereits vorhanden ist
      async function loadMQTTCertification() {
          return new Promise((resolve, reject) => {
              // Überprüfen, ob der State existiert
      
              getState(mqttCert, (err, state) => {
                  if (err) {
                      console.error('Fehler beim Überprüfen des Zertifikat-States:', err);
                      return reject(err);
                  }
                  if (state && state.val) {
                      try {
                          const storedCert = JSON.parse(state.val);
                          // Überprüfen, ob die Felder im Zertifikat vorhanden sind
                          if (storedCert.certificateAccount && storedCert.certificatePassword) {
                              console.log('Zertifikat aus ioBroker geladen');
                              return resolve(storedCert);
                          }
                      } catch (error) {
                          console.error('Fehler beim Parsen des Zertifikats:'+ error);
                      }
                  }
                  resolve(null); // Kein Zertifikat gefunden oder ungültig
              });
          });
      }
      
      // Zertifikate für MQTT abrufen und speichern
      async function getMQTTCertification() {
          const nonce = Math.floor(Math.random() * 1000000);
          const timestamp = Date.now();
          const params = {
              accessKey: accessKey,
              nonce: nonce,
              timestamp: timestamp
          };
      
          const signature = createSignature(params, secretKey);
      
          try {
              const response = await axios.get(`https://${host}/iot-open/sign/certification`, {
                  headers: {
                      accessKey: accessKey,
                      nonce: nonce,
                      timestamp: timestamp,
                      sign: signature
                  }
              });
      
              const certData = response.data.data;
              // Zertifikat in ioBroker speichern
              setState(mqttCert, JSON.stringify(certData), true);
              console.log('Zertifikat erfolgreich abgerufen und in ioBroker gespeichert');
              return certData;
          } catch (error) {
              console.error('Fehler beim Abrufen der MQTT-Zertifikate:'+ error);
              throw error;
          }
      }
      
      // Funktion zum Setzen von Parametern (z.B. permanentWatts) ohne client und certification als Parameter
      function setPermanentWatts(deviceSN, wattsValue) {
          if (!client || !certification) {
              console.error("MQTT-Client oder Zertifizierung nicht bereit.");
              return;
          }
      
          const setTopic = `/open/${certification.certificateAccount}/${deviceSN}/set`;
      
          const message = {
              id: Date.now(), // Einzigartige ID
              version: "1.0",
              cmdCode: "WN511_SET_PERMANENT_WATTS_PACK", // Befehlscode zum Setzen von permanentWatts
              params: {
                  permanentWatts: wattsValue // Der Wert, den du setzen möchtest
              }
          };
      
          client.publish(setTopic, JSON.stringify(message), (err) => {
              if (!err) {
                  console.log(`Befehl zum Setzen von permanentWatts auf ${wattsValue} W für Gerät ${deviceSN} wurde gesendet.`);
              } else {
                  console.error(`Fehler beim Senden des permanentWatts-Befehls für Gerät ${deviceSN}:` + err);
              }
          });
      }
      
      // MQTT-Verbindung herstellen und Daten empfangen
      async function startMQTTClient() {
          try {
              // Zertifikat aus ioBroker laden, falls vorhanden
              certification = await loadMQTTCertification();
              if (!certification) {
                  // Zertifikat neu generieren, wenn es nicht vorhanden ist
                  certification = await getMQTTCertification();
              }
      
              client = mqtt.connect(`${mqttProtocol}://${mqttHost}:${mqttPort}`, {
                  clientId: 'EcoFlowClient_' + MQTT_Clientid,
                  username: certification.certificateAccount,
                  password: certification.certificatePassword,
                  protocol: mqttProtocol
              });
      
              client.on('connect', () => {
                  console.log('Verbunden mit dem MQTT-Broker');
      
                  // Abonnieren von Status und anderen Daten für jedes Gerät
                  deviceSNs.forEach((deviceSN) => {
                      const quotaTopic = `/open/${certification.certificateAccount}/${deviceSN}/quota`;
                      const statusTopic = `/open/${certification.certificateAccount}/${deviceSN}/status`;
      
                      // Abonnieren des Status-Themas
                      client.subscribe(statusTopic, (err) => {
                          if (!err) {
                              console.log(`Abonniert auf Status-Topic: ${statusTopic}`);
                          } else {
                              console.error(`Fehler beim Abonnieren des Status-Themas für Gerät ${deviceSN}:` + err);
                          }
                      });
      
                      // Abonnieren des Quota-Themas
                      client.subscribe(quotaTopic, (err) => {
                          if (!err) {
                              console.log(`Abonniert auf Quota-Topic: ${quotaTopic}`);
                          } else {
                              console.error(`Fehler beim Abonnieren des Quota-Themas für Gerät ${deviceSN}:` + err);
                          }
                      });
                  });
              });
      
              client.on('message', (topic, message) => {
                  console.log(`Nachricht empfangen von Topic ${topic}: ${message.toString()}`);
              });
      
              client.on('error', (err) => {
                  console.error('MQTT-Verbindungsfehler:' + err);
                  client.end();  // Trenne die Verbindung bei einem Fehler
                  console.log('Verbindung getrennt aufgrund eines Fehlers ' + err);
              });
      
              client.on('close', () => {
                  console.log('MQTT-Verbindung geschlossen');
              });
      
          } catch (error) {
              console.error('Fehler beim Starten des MQTT-Clients:' + error);
          }
      }
      // Schließe die Verbindung, wenn das Skript gestoppt wird
      onStop(function (callback) {
          if (client) {
              client.end();
              log("Script gestoppt");
          }
          callback();
      }, 2000);
      // Start der MQTT-Client-Verbindung
      startMQTTClient();
      
      
      
      // Beispielaufruf von setPermanentWatts außerhalb von startMQTTClient()
      setTimeout(() => {
          setPermanentWatts(deviceSNs[0], 3000); // Setzt permanentWatts für ein bestimmtes Gerät auf 300 W
      }, 5000); // Warte 5 Sekunden, damit der Client verbunden ist
      

      Ich würde mich freuen, wenn ihr euch näher damit befasst. bevor ich das ins Skript einbauen kann, wird noch einige Zeit vergehen. Vielleicht schafft ja einer von euch.

      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @sirdir
      Sorry, dass ich mich hier so rausnehmen. Aber ich hab wirklich im Moment üble Probleme, worüber ich jetzt hier nicht jammern möchte.
      Ich finde es aber auch extrem frustrierend, dass der Hersteller, obwohl wir hier Funktionserweiterungen entwickelt haben, ganz ohne Gegenleistung, die dazu führen, dass mehr Leute, auch im professionellen Bereich auf EF Produkte zurückgreifen, nicht einmal versucht, Kontakt aufzunehmen, geschweige denn Hilfe anbietet, im gegenteil. Die API Dokumentation ist relativ frustrierend, ich habe schon einige Versuche gemacht damit zu arbeiten, leider bisher ziemlich erfolglos.
      Natürlich wäre eine Umstellung auf die offizielle API sehr wünschenswert, solange das keine Nachteile bei der Geschwindigkeit oder der Qualität der Regulierung einbringt.
      Ich lade euch herzlich ein, an dieser API Anbindung zu arbeiten.
      Theoretisch gibt es dort auch einen MQTT server, der uns die relevanten Daten liefern kann.

      Hier die Einsiegsseite für die API
      https://developer-eu.ecoflow.com/us

      Hier mal einer meiner Versuche mit der API zu kommunizieren.
      Verbindung wird aufgebaut, aber es kommen keine Telegramme:
      Auch konnte ich keine Daten an meine Geräte senden.

      const axios = require('axios');
      const mqtt = require('mqtt');
      const crypto = require('crypto');
      
      // Konfigurationen
      const host = 'api-e.ecoflow.com';
      const mqttHost = 'mqtt.ecoflow.com';
      const mqttPort = 8883;
      const mqttProtocol = 'mqtts';
      const accessKey = 'DLXXXXXXXXXXXXXXXXXXXXXXXXX6D6'; // Ersetze dies mit deinem tatsächlichen Access Key
      const secretKey = 'XxXXXXXXXXXXXXXXXXXXXXXXXXXLA'; // Ersetze dies mit deinem tatsächlichen Secret Key
      const deviceSN = 'HW5XXXXXXXXXX55'; // Ersetze dies mit der tatsächlichen Seriennummer deines Geräts
      
      // Hilfsfunktion zur Erstellung eines HMAC-SHA256-Signatur
      function createSignature(params, secretKey) {
          const queryString = Object.keys(params)
              .sort()
              .map(key => `${key}=${params[key]}`)
              .join('&');
          return crypto.createHmac('sha256', secretKey).update(queryString).digest('hex');
      }
      
      // Zertifikate für MQTT abrufen
      async function getMQTTCertification() {
          const nonce = Math.floor(Math.random() * 1000000);
          const timestamp = Date.now();
          const params = {
              accessKey: accessKey,
              nonce: nonce,
              timestamp: timestamp
          };
      
          const signature = createSignature(params, secretKey);
      
          try {
              const response = await axios.get(`https://${host}/iot-open/sign/certification`, {
                  headers: {
                      accessKey: accessKey,
                      nonce: nonce,
                      timestamp: timestamp,
                      sign: signature
                  }
              });
              return response.data.data;
          } catch (error) {
              console.error('Fehler beim Abrufen der MQTT-Zertifikate:', error);
              throw error;
          }
      }
      
      // MQTT-Verbindung herstellen und Daten empfangen
      async function startMQTTClient() {
          try {
              const certification = await getMQTTCertification();
      
              const client = mqtt.connect(`${mqttProtocol}://${mqttHost}:${mqttPort}`, {
                  username: certification.certificateAccount,
                  password: certification.certificatePassword,
                  protocol: mqttProtocol
              });
      
              const deviceTopic = `/open/${certification.certificateAccount}/${deviceSN}/quota`;
              const powerstreamTopic = `/open/${certification.certificateAccount}/${deviceSN}/status`;
      
      
      
              client.on('connect', () => {
                  console.log('Verbunden mit dem MQTT-Broker');
      
                  // Geräteliste anfordern
                  //const deviceListTopic = `/open/${certification.certificateAccount.replace("open-", "")}/${deviceSN}/get`;
      			const deviceListTopic = `/open/${certification.certificateAccount}/${deviceSN}/get`;
                  client.subscribe(deviceListTopic, (err) => {
                      if (!err) {
                          console.log(`Abonniert auf Geräteliste-Topic: ${deviceListTopic}`);
                          client.publish(deviceListTopic, JSON.stringify({}));
                      } else {
                          console.error('Fehler beim Abonnieren des Geräteliste-Themas:', err);
                      }
                  });
      
                  // Powerstream-Daten abonnieren
                  client.subscribe(powerstreamTopic, (err) => {
                      if (!err) {
                          console.log(`Abonniert auf Powerstream-Daten: ${powerstreamTopic}`);
                      } else {
                          console.error('Fehler beim Abonnieren des Powerstream-Themas:', err);
                      }
                  });
                   // Alle Topics abonnieren, um alle Nachrichten zu empfangen
                  client.subscribe('#', (err) => {  // '#' ist ein Wildcard-Topic, das alle Topics abonniert
                      if (!err) {
                          console.log('Abonniert auf alle Topics (#)');
                      } else {
                          console.error('Fehler beim Abonnieren aller Topics:', err);
                      }
                  });
              });
      
              
              client.on('message', (topic, message) => {
                  // Ausgabe des empfangenen Telegramms
                  console.log(`Nachricht empfangen von Topic ${topic}: ${message.toString()}`);
              });
      
              client.on('error', (err) => {
                  console.error('MQTT-Verbindungsfehler:', err);
              });
      
              client.on('close', () => {
                  console.log('MQTT-Verbindung geschlossen');
              });
      
          } catch (error) {
              console.error('Fehler beim Starten des MQTT-Clients:', error);
          }
      }
      
      startMQTTClient();
      

      Ihr müsst euch dafür als Entwickler registrieren (sehr einfach) und euch den
      accessKey und secretKey dort generieren.

      Bei mir treten diese Probleme übrigens nicht auf.
      Was mich noch interessieren würde: wenn keine Daten mehr am Skript ankommen, und ihr das Skript im iobroker einige Male neu startet, kommen dann wieder Daten?

      Mehr kann ich im Moment nicht beitragen. Sorry.

      posted in JavaScript
      W
      Waly_de
    • RE: CAN-BUS Interface ESP32 > MQTT und Stiebel Eltron WP steuern

      @marius-kossinna schön das es funktioniert 🙂
      Ja, ich kann alle Werte auch schreiben. Wobei die Pumpe nicht jede Änderung auch annimmt. Manches ist read only.
      Es sind ja auch verschiedene Geräte mit verschiedenen Geräte ID vorhanden. So kann es zum Beispiel sein, dass dein Bedienpaneel eine eigene Adresse hat und der dort eingestellte Wert sich dann im Steuergerät der Pumpe wieder findet, dort aber nicht geändert werden kann (read Only).

      posted in ioBroker Allgemein
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @ksbweb https://www.offgridtec.com/media/product_attachements/EcoFlow_PowerStream_Datenblatt.pdf
      Der Powerstream hat am DC Eingang / Ausgang , also da wo deine Delta dran hängt, maximal 600 W Leistung.... auch die 800W version.
      Lediglich auf der PV Seite hast du zweimal 400 W also 800 W und auf der AC Seite ebenfalls 800 W.
      D.h. wenn du die Batterie lädst, gehen maximal 600 W in die Batterie und wenn du keine Sonne hast und nur aus der Batterie einspeist, dann sind es auch nur 600W

      Eingespeist werden können nur dann 800 W, wenn entweder zweimal 400 W über die PV Module anliegen, oder mindestens 200 W PV power und 600 W aus der Batterie kommen.
      Alles klar??

      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @maik-becker leider im Moment nicht. Da muss man wirklich genauer hinsehen und Protokoll Dateien untersuchen und und und, Dazu habe ich im Moment leider keine Zeit.

      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @gooflo Vielen Dank.. ich lade es hoch (Ich kann es aber auch nicht testen, weil ich nur eine Delta Batterie habe. Aber es wird schon laufen. 😉 )

      Hier die Version von Florian:
      1.2.5.f1 Fork von Florian Vogt

      • Feature hinzugefügt, um die Größe der Delta-Speicher beim Ausbalancieren der Entladeleistung zu berücksichtigen, damit die Batterien gleichmäßig geleert werden
        Parameter battCapacity bei den Einstellungen für PowerStream = Kapazität der angeschlossenen Batterie in kWh, default = 1

      ecoflow-connector_v125_mod_FV.txt

      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @maik-becker sagte in ecoflow-connector-Script zur dynamischen Leistungsanpassung:

      @waly_de Hallo, auch wenn du Privat viel zu tun hast, wäre es möglich, wenn das mit einbaust noch prüfen zu lassen ob die PS schon Strom einspeisen. Wenn ja den soll er die DM D2M nicht weiter laden. Wäre nett von dir.

      Das verstehe ich nicht:
      PV Energie von 400 W steht zur Verfügung Dein Haus Strombedarf ist 200 W.
      Also gehen 200 W ins Haus und 200 W in die Batterie. Oder reden wir vom Überschuss laden? Das ist bereits so programmiert, dass es bei fehlenden Überschuss abgeschaltet wird.
      Verzögerung, schalt Zeiten und Bedingungen dafür lassen sich in den Settings einstellen

      posted in JavaScript
      W
      Waly_de
    • RE: ecoflow-connector-Script zur dynamischen Leistungsanpassung

      @gooflo das ist ja cool! 🙂 sehr schöne Idee!
      Ich hab leider im Moment privat so viel um die Ohren, dass ich mich um nichts kümmern kann.
      Wenn du magst, schick mir doch bitte die letzte Version vom Skript mit deinem Code. Dann stelle ich das so online für alle. ich denke, wenn standardmäßig die Batteriekapazität auf null eingestellt wird, dürfte die Regelung auch wenn man es nicht eingestellt keinen Unterschied machen, oder?
      Ich muss dieses Skript unbedingt mal auf github bringen, damit sowas einfacher wird.

      posted in JavaScript
      W
      Waly_de
    • RE: CAN-BUS Interface ESP32 > MQTT und Stiebel Eltron WP steuern

      @alex_46 glückwunsch, du hast es tatsächlich hinbekommen 😉

      Die Auswertung ist relativ kompliziert ich mach das mit einem Java Skript
      Ich poste das Skript hier einfach mal. Bitte entschuldige das ein bisschen Kraut und Rüben ist, aber es ist nur für mich, die Arbeitsversion quasi. Das Thema ist ja sehr speziell und es gibt nicht so viele, die sich dafür interessieren, darum habe ich da nicht so viel Mühe investiert.

      Das wichtigste:
      Die Elster Definitiondatei muss an dem entsprechenden Pfad in deiner IOBroker Installation liegen und es müssen entsprechende Zugriffsrechte auf diese Datei (lesend) existieren.

      Einige Werte werden ja automatisch in regelmäßigen Abständen übertragen.
      Für alle anderen solltest du einmal die Funktion scanAllElsterIds mit der richtigen Geräte ID durchlaufen lassen und danach wieder aus kommentieren.

      Viel Erfolg bei der Umsetzung! Bei mir läuft das seit der ersten in Betriebname ohne irgendwelche Probleme. Schätze, seit einem halben Jahr.

      ElsterTable.inc

      const DEBUG = false
      let filePath = '/opt/iobroker/iobroker-data/ElsterTable.inc';
      let elsterJsObject = convertToJsObject(filePath);
      //const CanScnifferId = "mqtt.0.cansniffer"
      const CanScnifferId = "mqtt.0.cansniffer2.142"
      let scanAllElsterIdsTimerVar, scanAllStatestimerVar, scanShortStatestimerVar
      let GlobscanAllElsterIds = {
          lastPosition: 0,
          lastkey: "0",
          //geraet: 0x0180
      }
      // Das muss einmal pro Zieladresse ausgeführt werden um zu scannen welche Datenpunke abfragbar sind
      // Jeweils einmall pro Adresse 
      // scanAllElsterIds(0x301,0)
      let GlobscanAllStates = {
          lastPosition: 0,
          lastkey: "0",
          intervall: 10 * 60 * 1000,
      }
      scanAllStates()
      // Die Hier angegebenen Datenpunkte werden in einem unter Intervall angegebenen Intervall in Millisekunden regelmäßig aktiv abgefragt
      let GlobshortScan = {
          lastPosition: 0,
          lastkey: "0",
          intervall: 30000,
          iDs: [
              { geraet: 0x0180, elsterName: "WPVORLAUFIST" },
              { geraet: 0x0180, elsterName: "RUECKLAUFISTTEMP" },
              { geraet: 0x0180, elsterName: "MASCHINENDRUCK" },
              { geraet: 0x0301, elsterName: "VORLAUFSOLLTEMP" },
              { geraet: 0x0180, elsterName: "PUFFERSOLL" },
          ]
      }
      scanShortStates()
      on({ id: CanScnifferId + '.pload', change: "ne" }, function (obj) {
          //if (DEBUG || false) log(obj.state.val)
          const telegramObj = parseCanTelegram(JSON.parse(obj.state.val))
          if (Number(telegramObj.elsterIndex) == Number(GlobscanAllElsterIds.lastkey)) {
              //log("gleich")
              //log(Number(telegramObj.elsterIndex) + " - " + Number(GlobscanAllElsterIds.lastkey))
              clearTimeout(scanAllElsterIdsTimerVar)
              scanAllElsterIds(GlobscanAllElsterIds.geraet, GlobscanAllElsterIds.lastPosition + 1)
          }
          if (Number(telegramObj.elsterIndex) == Number(GlobscanAllStates.lastkey)) {
              //log("gleich")
              //log(Number(telegramObj.elsterIndex) + " - " + Number(GlobscanAllElsterIds.lastkey))
              clearTimeout(scanAllStatestimerVar)
              scanAllStates(GlobscanAllStates.lastPosition + 1)
          }
          if (Number(telegramObj.elsterIndex) == Number(GlobshortScan.lastkey)) {
              //log("gleich")
              //log(Number(telegramObj.elsterIndex) + " - " + Number(GlobscanAllElsterIds.lastkey))
              clearTimeout(scanShortStatestimerVar)
              scanShortStates(GlobshortScan.lastPosition + 1)
          }
          //evaluateMessages(JSON.parse(obj.state.val))
          //log(JSON.stringify(telegramObj));
          //writeLog(JSON.stringify(telegramObj)), 'CanLog.csv').catch(error => console.error(JSON.stringify(error)));
      });
      function convertToJsObject(filePath) {
          // In einer echten JavaScript-Umgebung müsste hier die fs-Bibliothek verwendet werden,
          // um die Datei zu lesen. Der folgende Code ist eine vereinfachte Darstellung.
          const fs = require('fs');
          let elsterTable = {};
          // Lies die Datei synchron, dies ist in einem realen Szenario zu vermeiden
          let fileContent = fs.readFileSync(filePath, 'utf-8');
          let lines = fileContent.split('\n');
          lines.forEach((line) => {
              // Ignorieren von Kommentaren und leeren Zeilen
              if (line.startsWith('//') || line.trim() === '' || line.startsWith('{') || line.startsWith('}')) {
                  return;
              }
              // Entfernen von Anführungszeichen und geschweiften Klammern, dann Aufspaltung der Zeile in Teile
              let parts = line.replace('{', '').replace('}', '').replace(/"/g, '').trim().split(',');
              parts = parts.map(part => part.trim()).filter(part => part);
      
              // Überprüfen, ob die Zeile drei Teile enthält: Name, Index und Typ
              if (parts.length === 3) {
                  let [name, index, type_] = parts;
                  try {
                      // Versuche, den Index von Hex zu Dezimal zu konvertieren
                      let indexKey = parseInt(index, 16);
                      // Hinzufügen zum JavaScript-Objekt
                      elsterTable[indexKey] = { "name": name, "type": type_ };
                  } catch (error) {
                      // Wenn der Index nicht in einen Integer umgewandelt werden kann, ignorieren wir diese Zeile
                      console.error('Konvertierungsfehler:', error);
                  }
              }
          });
          return elsterTable;
      }
      // Das ElsterTable-Objekt, das zuvor erstellt wurde
      let elsterTable = elsterJsObject
      
      // Funktion zum Konvertieren der Hex-Daten in lesbare Werte
      function convertData(hexString) {
          let bytes = [];
          for (let i = 0; i < hexString.length; i += 2) {
              bytes.push(parseInt(hexString.substr(i, 2), 16));
          }
          let rawValue = (bytes[1] << 8) | bytes[2]; // Korrigierte Byte-Position
          return rawValue / 10; // Annahme: Der Wert ist durch 10 zu teilen
      }
      // Funktion zum Auswerten der Nachrichten
      function evaluateMessages(message) {
          //messages.forEach((message) => {
          let id = message.ID;
          let data = message.Data.replace(/\s/g, "");
      
          // Der Index ist das dritte Byte in der Nachricht, hier als Hex-String
          let indexHex = data.substr(4, 2);
          log(indexHex)
          let index = parseInt(indexHex, 16);
          let valueHEX = data.substr(6, 4);
          let value = parseInt(valueHEX, 16)
          log("Index:" + index)
          log("Data:" + data)
          log("Value Hex:" + valueHEX)
          log("Value Dezi:" + value)
          // Hole den Eintrag aus dem ElsterTable
          let entry = elsterTable[index];
          if (entry) {
              log(`Gerät: ${id}, Parameter: ${entry.name}, Wert: ${value / 10}`);
          } else {
              log(`Unbekannter Index: ${indexHex}. Message: ${message}`);
          }
      }
      function parseMessage(str) {
          // Zerlege den String in Teile und extrahiere relevante Teile
          const parts = str.trim().split(/\s+/);
          const idPart = parts[1].substring(2); // Entferne '0x'
          const dataPart = parts.slice(4).join('').replace("Data:", ""); // Entferne 'Data:'
      
          // Erstelle das messages-Objekt
          return {
              id: idPart, // Entfernt '0x'
              data: dataPart // Entfernt 'Data:'
          };
      }
      // Beispiel für die Verwendung der Funktion
      //const messageStr = {"ID": "0x301", "Len": "7", "Data": "C0 01 11 00 DF 00 00"};
      //evaluateMessages(messageStr)
      // ****************************************************************
      // :
      function parseCanTelegram(telegram) {
          // Entferne alle Leerzeichen und prüfe das Format
          let dataString = telegram.Data.replace(/\s/g, '');
          if (dataString.length % 2 !== 0) {
              throw new Error('Ungültige Datenlänge');
          }
          // Zerlege die Daten in ihre Teile
          let parts = dataString.match(/.{1,2}/g).map(byte => parseInt(byte, 16));
          // Berechne die CAN-ID
          let calculatedId = (8 * (parts[0] & 0xF0)) + (parts[1] & 0x0F);
          let calculatedIdHex = calculatedId.toString(16).toUpperCase();
          calculatedIdHex = "0x" + ("000" + calculatedIdHex).slice(-4);
          // Überprüfe den Typ des Telegramms
          let typID = (parts[0] & 0x0F)
          let type
          switch (typID) {
              case 0:
                  type = "write"
                  break;
              case 1:
                  type = "read"
                  break;
              case 2:
                  type = "response"
                  break;
              case 3:
                  type = "ack"
                  break;
              case 4:
                  type = "write ack"
                  break;
              case 5:
                  type = "write respond"
                  break;
              case 6:
                  type = "system"
                  break;
              case 7:
                  type = "system respond"
                  break;
              default:
                  type = "Unbekannt"
          }
          // Bestimme den Elster-Index und erhöhe ihn um 1
          let elsterIndex = parts[2] === 0xFA ? (((parts[3] << 8) + parts[4])) : (parts[2]);
          // Konvertiere den Elster-Index in vierstellige Hexadezimalzahl und entferne das zuvor hinzugefügte +1
          let elsterIndexHex = elsterIndex.toString(16).toUpperCase();
          let elsterFunktion = elsterTable[elsterIndex];
          elsterIndexHex = "0x" + ("000" + elsterIndexHex).slice(-4);
          let data = parts.slice(3).map(byte => byte.toString(16).padStart(2, '0')).join(' ').toUpperCase();
          // Nutzdaten, beginnend nach dem Elster-Index
          let dataIndexStart = parts[2] === 0xFA ? 5 : 3;
          let dataBytes = parts.slice(dataIndexStart, dataIndexStart + 2);
          let dataHex = dataBytes.map(byte => byte.toString(16).padStart(2, '0')).join('').toUpperCase();
          let dataDecimal = (dataBytes[0] << 8) + dataBytes[1];
          let dataDecimalCon = AllValuesConverter(dataDecimal, elsterFunktion.type, true)
          let StateToSet = "0_userdata.0.WP13." + telegram.ID.replace("0x", "") + "." + elsterFunktion.name
          //log(StateToSet)
          //log("state:" + StateToSet + " dataDecimalCon: " + dataDecimalCon)
          if (dataDecimal != 32768) {
              createState(StateToSet, dataDecimalCon, false)
              setState(StateToSet, dataDecimalCon, true)
          }
          return {
              raw: dataString,
              calculatedId: calculatedIdHex,
              type,
              elsterIndex: elsterIndexHex,
              elsterFunktion,
              dataHex,
              dataDecimal,
              FromID: telegram.ID,
              Len: telegram.Len
          };
      }
      function AllValuesConverter(value, TypeString, read = true) {
          //log("value to String: " +  value.toString().toLowerCase() )
          switch (TypeString) {
              case "et_datum":
                  if (read) {
                      return convertBytesToDatum(Number(value))
                  } else {
                      return convertDatumToBytes(value)
                  }
                  break;
              case "et_dec_val":
                  if (read) {
                      return Number((interpretAsSigned16(value) / 10).toFixed(1))
                  } else {
                      return Number((interpretAsSigned16(value) * 10).toFixed(0))
                  }
                  break;
              case "et_time_domain":
                  if (read) {
                      if (value == 0xFFFF) return "NA"
                      if (value == 0x8080) return "-----"
                      return convertBytesToTime(Number(value))
                  } else {
                      if (value.toString() == "NA") return 0xFFFF
                      if (value.toString() == "-----") return 0x8080
                      log("Zeitrückgabe: " + value + " zu: " + Number(convertTimeRangeToBytes(value.toString())))
                      return Number(convertTimeRangeToBytes(value.toString()))
                  }
                  break;
              case "et_betriebsart":
                  if (read) {
                      if (value == 0x0000) return "Notbetrieb"
                      if (value == 0x0100) return "Bereitschaft"
                      if (value == 0x0200) return "Automatik"
                      if (value == 0x0300) return "Tagbetrieb"
                      if (value == 0x0400) return "Absenkbetrieb"
                      if (value == 0x0500) return "Warmwasser"
                      if (value == 0x0B00) return "FEKAuto"
                      return value
                  } else {
                      //log("value to String: " +  value.toString().toLowerCase() )
                      if (value.toString().toLowerCase() == "notbetrieb") return 0x0000
                      if (value.toString().toLowerCase() == "bereitschaft") return 0x0100
                      if (value.toString().toLowerCase() == "automatik") return 0x0200
                      if (value.toString().toLowerCase() == "tagbetrieb") return 0x0300
                      if (value.toString().toLowerCase() == "absenkbetrieb") return 0x0400
                      if (value.toString().toLowerCase() == "warmwasser") return 0x0500
                      if (value.toString().toLowerCase() == "fekauto") return 0x0B00
                      return Number(value)
                  }
                  break;
              case "et_little_endian":
              case "et_little_bool":
                  return littleEndianHexConvert(value)
                  break;
              default:
                  return value
          }
      }
      function interpretAsSigned16(uint16) {
          // Prüfen, ob die Zahl größer als der maximal positive Wert für einen vorzeichenbehafteten 16-Bit-Integer ist
          if (uint16 > 32767) {
              return uint16 - 65536;
          } else {
              return uint16;
          }
      }
      // Von Little Endian Hex zu Dezimal
      function littleEndianHexConvert(value) {
          // Verschieben Sie die Bytes und kombinieren Sie sie
          let lowByte = value & 0xFF; // Extrahieren Sie das niedrigwertige Byte
          let highByte = (value & 0xFF00) >> 8; // Extrahieren und verschieben Sie das hochwertige Byte
          return (highByte | (lowByte << 8));
      }
      function convertBytesToTime(value) {
          let startByte = value >> 8;
          let endByte = value & 0xFF;
          return convertToTime(startByte) + ' - ' + convertToTime(endByte);
      }
      function convertBytesToDatum(value) {
          let startByte = value >> 8;
          let endByte = value & 0xFF;
          return (startByte) + '.' + (endByte);
      }
      function convertToTime(value) {
          let hours = Math.floor(value / 4);
          let minutes = (value % 4) * 15;
          return hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0');
      }
      function convertTimeToBytes(startTime, endTime) {
          let startUnits = convertToUnits(startTime);
          let endUnits = convertToUnits(endTime);
          return (startUnits << 8) + endUnits;
      }
      function convertToUnits(time) {
          let [hours, minutes] = time.split(':').map(Number);
          return hours * 4 + Math.floor(minutes / 15);
      }
      function convertTimeRangeToBytes(timeRange) {
          // Entfernen Sie alle Leerzeichen aus dem String
          let cleanedTimeRange = timeRange.replace(/\s/g, '');
      
          // Teilen Sie den bereinigten String in Start- und Endzeit
          let [startTime, endTime] = cleanedTimeRange.split('-');
          if (!endTime) return timeRange
          return convertTimeToBytes(startTime, endTime);
      }
      function convertDatumToBytes(datum) {
          let cleaneddatum = datum.replace(/\s/g, '');
          let [tag, monat] = cleaneddatum.split('.').map(Number);
          //log(tag + " " + monat + " " + typeof(monat))
          if (!monat) return datum
          return ((tag << 8) + (monat));
      }
      //console.log(convertBytesToTime(0x8080)); // Gibt "00:00 - 01:15" aus
      //console.log(convertTimeToBytes("15:30", "01:15").toString(16)); // Gibt "105" aus
      //console.log(convertTimeRangeToBytes("32:00 - 32:00").toString(16)); // Gibt "105" aus
      //log(convertBytesToDatum(3851)) // Gibt "15.11" aus
      //log(convertDatumToBytes("15.11"))
      // Beispiel-Telegramm
      let telegram = {
          "ID": "0x680",
          "Len": "6",
          "Data": "61010112340000"
      }
      //console.log(parseCanTelegram(telegram));
      function buildCanIdWithTypeId(canId, typeId, elsterIndexHex, HexDaten) {
          let hexbuffer = Buffer.from("0000000000", "hex")
          hexbuffer[0] = Math.floor(parseInt(canId, 16) / 8) + parseInt(typeId, 16)
          hexbuffer[1] = (parseInt(canId, 16) % 8)
          let elsterBytes = Buffer.from(elsterIndexHex, "hex");
          hexbuffer[2] = elsterBytes[1];
          //hexbuffer[3] = elsterBytes[1];
          let DatenBytes = Buffer.from(HexDaten, "hex");
          hexbuffer[3] = DatenBytes[0];
          hexbuffer[4] = DatenBytes[1];
          return hexbuffer.toString('hex')
      }
      /***************************************
      **********  LOG FILE ERSTELLEN  ************ 
      ****************************************/
      const logpath = '/opt/iobroker/log/';
      const fs = require('fs').promises;
      async function writeLog(logEntry, filename = "ECOTest2.csv") {
          // Get the current date and time
          const now = new Date();
          const dateTime = now.toLocaleString();
      
          // Format the log entry
          const formattedEntry = `${dateTime}\t${logEntry}\n`;
      
          try {
              // Check if the file already exists
              await fs.stat(logpath + filename);
          } catch (error) {
              if (error.code === 'ENOENT') {
                  // If the file does not exist, we need to add the header
                  const header = 'DateTime\tLog Entry\n';
                  await fs.writeFile(logpath + filename, header);
                  log("neue datei erstellt");
              } else {
                  // Some other error occurred
                  throw error;
              }
          }
          // Append the log entry to the file
          try {
              await fs.appendFile(logpath + filename, formattedEntry);
              //console.log('Log entry saved!');
          } catch (error) {
              throw error;
          }
      }
      // Anwendung:
      //writeLog('This is a test entry.', 'Filename.csv').catch(error => console.error(error));
      //***************************************
      //***************************************
      function initElsterFrame(senderId, receiverId, elsterIdx, pdaten, typ = 1) {
          senderId = Number(senderId)
          const address = (Number(receiverId) & 0x780) >> 3;
          let data = new Array(7); // Erstellt ein Array mit 5 Elementen für die Datenbytes
          // Die Id und die Länge des Frames werden nicht direkt gesetzt, da in JavaScript
          // üblicherweise Objekte anstelle von direkt manipulierten Byte-Arrays verwendet werden.
          data[0] = (Number(address) & 0xF0) + Number(typ);
          data[1] = Number(receiverId) & 0x07;
          data[2] = 0xFA;
          data[3] = (Number(elsterIdx) >> 8) & 0xFF; // Höherwertiges Byte von ElsterIdx
          data[4] = Number(elsterIdx) & 0xFF; // Niederwertiges Byte von ElsterIdx
          data[5] = (Number(pdaten) >> 8) & 0xFF; // Niederwertiges Byte von ElsterIdx
          data[6] = Number(pdaten) & 0xFF; // Niederwertiges Byte von ElsterIdx
          return {
              ID: "0x" + senderId.toString(16),
              Len: "7",
              Data: data.map(byte => byte.toString(16).padStart(2, '0')).join('').toUpperCase()
          };
      }
      // Änderungen senden:
      on({ id: new RegExp("0_userdata.0.WP13\..*"), change: "any", ack: false }, function (obj) {
          log("was geändert:" + obj.id + " ack:" + obj.state.ack)
          readDataFromObject(obj, 'write')
      });
      function readDataFromObject(obj, art = 'readonly') {
          let ID, elstername, mtelegram
          if (obj.id) {
              ID = "0x" + ((obj.id).split('.').slice(-2, -1)[0])
              elstername = ((obj.id).split('.').slice(-1)[0])
          } else {
              ID = "0x" + ((obj._id).split('.').slice(-2, -1)[0])
              elstername = ((obj._id).split('.').slice(-1)[0])
          }
          let elsterObjekt = Object.entries(elsterJsObject).find(([key, value]) => value.name === elstername);
      
          let elsterID = elsterObjekt[0]; // Der Schlüssel
          let gesuchtesObjekt = elsterObjekt[1]; // Das gesuchte Objekt
          // Verwenden Sie hier elsterID und gesuchtesObjekt wie benötigt
          // log (JSON.stringify(gesuchtesObjekt) + " : " + obj.state.val + " Konvertiert: " + AllValuesConverter(obj.state.val, gesuchtesObjekt.type ,true))
      
          //let elsterID = Object.keys(elsterJsObject).find(key => elsterJsObject[key].name === elstername);
          /*
              log("Gerät: " + ID)
              log("elstername: " + elstername)
              log("elsterIDHex: " + Number(elsterID).toString(16))
              log("value: " + obj.state.val)
              log("art: " + art)
          */
          if (art == 'write') {
              let dataDecimalCon = AllValuesConverter((obj.state.val), gesuchtesObjekt.type, false)
              mtelegram = JSON.stringify(initElsterFrame("0x680", ID, elsterID, dataDecimalCon, 2))
              log(" Wert Setzten: " + mtelegram)
              setState(CanScnifferId + '.canSend', mtelegram)
              setTimeout(function () {
                  mtelegram = JSON.stringify(initElsterFrame("0x680", ID, elsterID, 0, 1))
                  //log(" Wert abrufen: " + mtelegram)
                  setState(CanScnifferId + '.canSend', mtelegram)
              }, 2 * 1000);
          } else if (art == 'readonly') {
              mtelegram = JSON.stringify(initElsterFrame("0x680", ID, elsterID, 0, 1))
              //log(" Wert abrufen: " + mtelegram)
              setState(CanScnifferId + '.canSend', mtelegram)
          }
      }
      //Zum Scannen aller verfügbaren Parameter eines gerätes
      function scanAllElsterIds(geraet = 0x180, lastPosition = 0) {
          GlobscanAllElsterIds.geraet = geraet
          let canId, mtelegram
          let maxScan
          let keys = Object.keys(elsterJsObject);
          maxScan = keys.length
          if (lastPosition >= maxScan) {
              let Zeitkompl = Math.floor((Date.now() - GlobscanAllStates.Starttime) / 1000)
              if (DEBUG) log("Scan all ElsterIds beendet. Scandauer: " + Zeitkompl + " Sekunden für " + lastPosition + " Scans")
              GlobscanAllElsterIds.lastPosition = 0
              GlobscanAllElsterIds.lastkey = "0"
              GlobscanAllElsterIds.Starttime = 0
              return
          } else if (lastPosition == 0) {
              if (DEBUG) log("Scan all States gestartet")
              GlobscanAllElsterIds.Starttime = Date.now()
          }
          let key = keys[lastPosition]
          if (DEBUG) if (lastPosition % 20 == 0) log("(" + ((100 / maxScan) * lastPosition).toFixed(0) + "%) Scanposition: " + lastPosition + " von " + maxScan + " elsterID:" + key + " elstername:" + elsterJsObject[key].name)
          //log(key)
          mtelegram = JSON.stringify(initElsterFrame("0x680", geraet, key, 0, 1))
          //log(lastPosition + " von " + maxScan + " Wert abrufen: " + mtelegram)
          setState(CanScnifferId + '.canSend', mtelegram)
          GlobscanAllElsterIds.lastPosition = lastPosition
          GlobscanAllElsterIds.lastkey = key
          scanAllElsterIdsTimerVar = setTimeout(function () {
              scanAllElsterIds(geraet, lastPosition + 1); // Setzt den nächsten Aufruf auf 1000 ms später
          }, 5000)
      }
      //Zum Scannen aller Angelegten States
      function scanAllStates(lastPosition = 0) {
          let mSelector = $("0_userdata.0.WP13.*");
          let maxScan = mSelector.length
          if (lastPosition >= maxScan - 1) {
              let Zeitkompl = Math.floor((Date.now() - GlobscanAllStates.Starttime) / 1000)
              if (DEBUG) log("Scan all States beendet. Scandauer: " + Zeitkompl + " Sekunden für " + lastPosition + " Scans")
              GlobscanAllStates.lastPosition = 0
              GlobscanAllStates.lastkey = "0"
              GlobscanAllStates.Starttime = 0
              setTimeout(function () {
                  scanAllStates(0); // Setzt den nächsten Aufruf auf 1000 ms später
              }, GlobscanAllStates.intervall)
              return
          } else if (lastPosition == 0) {
              if (DEBUG) log("Scan all States gestartet")
              GlobscanAllStates.Starttime = Date.now()
          }
          //log("mSelector " + mSelector[lastPosition])
          let elstername = ((mSelector[lastPosition]).split('.').slice(-1)[0])
          let geraeteId = (mSelector[lastPosition]).split('.').slice(-2)[0]
          //log("GeraeteID:" + geraeteId )
          let elsterID = Object.keys(elsterJsObject).find(key => elsterJsObject[key].name === elstername);
          GlobscanAllStates.lastPosition = lastPosition
          GlobscanAllStates.lastkey = elsterID
          if (DEBUG) if (lastPosition % 20 == 0) log("ScanAllStates (" + ((100 / maxScan) * lastPosition).toFixed(0) + "%) Positiom:" + lastPosition + " von " + maxScan + " geraeteId:" + geraeteId + " elsterID:" + elsterID + " elstername:" + elstername)
          let myObj = getObject(mSelector[lastPosition])
          myObj.state = getState(mSelector[lastPosition])
          myObj.id = mSelector[lastPosition]
          //log(JSON.stringify(myObj))
          readDataFromObject(myObj)
          scanAllStatestimerVar = setTimeout(function () {
              if (elstername != "DATUM") log("Timeout position:" + lastPosition + " geraeteId:" + geraeteId + " elsterID:" + elsterID + " elstername:" + elstername)
              scanAllStates(lastPosition + 1); // Setzt den nächsten Aufruf auf 1000 ms später
          }, 5000)
      }
      function scanShortStates(lastPosition = 0) {
          let maxScan = GlobshortScan.iDs.length
          //log("Anzahl IDs Short:" + maxScan)
          if (lastPosition >= maxScan) {
              let Zeitkompl = Math.floor((Date.now() - GlobshortScan.Starttime) / 1000)
              if (DEBUG) log("Scan Short States beendet. Scandauer: " + Zeitkompl + " Sekunden für " + lastPosition + " Scans")
              GlobshortScan.lastPosition = 0
              GlobshortScan.lastkey = "0"
              GlobshortScan.Starttime = 0
              setTimeout(function () {
                  scanShortStates(0); // Setzt den nächsten Aufruf auf 1000 ms später
              }, GlobshortScan.intervall)
              return
          } else if (lastPosition == 0) {
              if (DEBUG) log("Scan Short States gestartet")
              GlobshortScan.Starttime = Date.now()
          }
          //log("mSelector " + mSelector[lastPosition])
          let elstername = GlobshortScan.iDs[lastPosition].elsterName
          let elsterID = Object.keys(elsterJsObject).find(key => elsterJsObject[key].name === elstername);
          GlobshortScan.lastPosition = lastPosition
          GlobshortScan.lastkey = elsterID
          let esterState = "0_userdata.0.WP13." + GlobshortScan.iDs[lastPosition].geraet.toString(16).replace("tzt0x", "") + "." + elstername
          //log(esterState)
          if (DEBUG) if (lastPosition % 20 == 0) log("ScanShortStates (" + ((100 / maxScan) * lastPosition).toFixed(0) + "%) Positiom:" + lastPosition + " von " + maxScan + " elsterID:" + elsterID + " elstername:" + elstername)
          let myObj = getObject(esterState)
          myObj.state = getState(esterState)
          myObj.id = esterState
          //log(JSON.stringify(myObj))
          readDataFromObject(myObj)
          scanShortStatestimerVar = setTimeout(function () {
              log("Timeout Shortscan position:" + lastPosition + " elsterID:" + elsterID + " elstername:" + elstername)
              scanShortStates(lastPosition + 1); // Setzt den nächsten Aufruf auf 1000 ms später
          }, 5000)
      }
      /*
      //Objekte löschen:
      $('0_userdata.0.WP13.180.EL_*').each(function (id, i) {
                  log(i + " - " + id + " existsObject: " + existsObject(id))
                  //deleteObject(id,true);
      });
      */
      // Beispielaufruf
      let canId, mtelegram
      let Senderid = "0x680"
      canId = "0x180"; // 384 in dezimal
      //canId = "0x601"; // 384 in dezimal
      let typeId = 1; // 1 in dezimal
      let elsterIndexHex = "0x17a0"
      let pHexDaten = 0x0000
      mtelegram = JSON.stringify(initElsterFrame(Senderid, canId, elsterIndexHex, pHexDaten, typeId))
      //console.log("Generiertes Telegramm:" + mtelegram);
      //setState( CanScnifferId +'.canSend', mtelegram)
      /*
      setState( CanScnifferId +'.canSend', mtelegram, function () {
          setTimeout(function () {
              elsterIndexHex = "0x1700"
              pHexDaten = 0x8080
              mtelegram = JSON.stringify(initElsterFrame(Senderid, canId, elsterIndexHex, pHexDaten, typeId))
              console.log("Generiertes Telegramm:" + mtelegram);
              setState( CanScnifferId +'.canSend', mtelegram, function () {
                  setTimeout(function () {
                      elsterIndexHex = "0x1722"
                      pHexDaten = 0x8080
                      mtelegram = JSON.stringify(initElsterFrame(Senderid, canId, elsterIndexHex, pHexDaten, typeId))
                      console.log("Generiertes Telegramm:" + mtelegram);
                      setState( CanScnifferId +'.canSend', mtelegram)
                  }, 200);
              })
          }, 200);
      })
      */
      //console.log(mtelegram); 
      setTimeout(function () {
          typeId = 1;
          //elsterIndexHex = "0x1720"
          //HexDaten = "0000"
          mtelegram = JSON.stringify(initElsterFrame(Senderid, canId, elsterIndexHex, pHexDaten, typeId))
          //console.log("Generiertes Telegramm:" + mtelegram);
          //setState( CanScnifferId +'.canSend', mtelegram)
      }, 2 * 1000);
      //
      
      posted in ioBroker Allgemein
      W
      Waly_de
    Community
    Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
    The ioBroker Community 2014-2023
    logo