Skip to content
  • Recent
  • Tags
  • 0 Unread 0
  • Categories
  • Unreplied
  • Popular
  • 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

  • Default (No Skin)
  • No Skin
Collapse
Logo
  1. ioBroker Community Home
  2. Deutsch
  3. Einsteigerfragen
  4. Einbindung von Geräten
  5. Adapter für Ecoflow Einbindung

NEWS

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    8.1k

  • Monatsrückblick – September 2025
    BluefoxB
    Bluefox
    13
    1
    1.8k

  • Neues Video "KI im Smart Home" - ioBroker plus n8n
    BluefoxB
    Bluefox
    15
    1
    2.1k

Adapter für Ecoflow Einbindung

Scheduled Pinned Locked Moved Einbindung von Geräten
212 Posts 42 Posters 61.3k Views 41 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • W Waly_de

    @xnodkane sehr gut ... hab auch ein wenig geforscht. Meinst du nicht das 38 der wert für "Andere Verbraucher" ist?
    Und kann man nicht eine .proto für alles machen. Die anderen Felder scheint es da ja auch zu geben.
    Ich hatte das hier erstellt. Spricht da etwas gegen?

    syntax = "proto3";
    
    message PowerItem {
      optional Meta meta = 1;
      uint32 src = 2;
      uint32 dest = 3;
      uint32 cmdFunc = 8;
      CmdFunction cmdId = 9;
      uint32 unknown = 10;
      uint32 needAck = 11;
      uint64 timestamp = 14;
      string serialNumber = 25;
    }
    
    message PowerMessage {
      PowerItem item = 1;
    }
    
    message Meta {
      optional int32 value = 1;
      optional int32 PV1_Watt = 19;
      optional int32 PV2_Watt = 24;
      optional int32 ToHome_Watt = 38;
    }
    
    enum CmdFunction {
        Unknown = 0;
        PermanentWattsPack = 129;
        SupplyPriorityPack = 130;
    }
    
    xNodKaneX Offline
    xNodKaneX Offline
    xNodKane
    wrote on last edited by
    #151

    @waly_de sagte in Adapter für Ecoflow Einbindung:

    Ich hatte das hier erstellt. Spricht da etwas gegen?

    Ich habe gerade noch mal bei mir auf das Dashboard geschaut im Reiter "Bereich" und dann nochmal auf das Powerstream system drücken (Die große Ansicht).
    Würde jedenfalls hier noch die SmartPlug Daten ergänzen 😉

    syntax = "proto3";
    
    message PowerItem {
      optional Meta meta = 1;
      uint32 src = 2;
      uint32 dest = 3;
      uint32 cmdFunc = 8;
      CmdFunction cmdId = 9;
      uint32 unknown = 10;
      uint32 needAck = 11;
      uint64 timestamp = 14;
      string serialNumber = 25;
    }
    
    message PowerMessage {
      PowerItem item = 1;
    }
    
    message Meta {
      optional int32 value = 1;
      optional int32 plugPower = 10;   // power/10=W
      optional int32 pv1_Power = 19;   // power/10=W
      optional int32 pv2_Power = 24;   // power/10=W
      optional int32 toHomePower = 38; // power/10=W
    }
    
    enum CmdFunction {
        Unknown = 0;
        PermanentWattsPack = 129;
        SupplyPriorityPack = 130;
    }
    
    W 1 Reply Last reply
    1
    • xNodKaneX xNodKane

      @waly_de sagte in Adapter für Ecoflow Einbindung:

      Ich hatte das hier erstellt. Spricht da etwas gegen?

      Ich habe gerade noch mal bei mir auf das Dashboard geschaut im Reiter "Bereich" und dann nochmal auf das Powerstream system drücken (Die große Ansicht).
      Würde jedenfalls hier noch die SmartPlug Daten ergänzen 😉

      syntax = "proto3";
      
      message PowerItem {
        optional Meta meta = 1;
        uint32 src = 2;
        uint32 dest = 3;
        uint32 cmdFunc = 8;
        CmdFunction cmdId = 9;
        uint32 unknown = 10;
        uint32 needAck = 11;
        uint64 timestamp = 14;
        string serialNumber = 25;
      }
      
      message PowerMessage {
        PowerItem item = 1;
      }
      
      message Meta {
        optional int32 value = 1;
        optional int32 plugPower = 10;   // power/10=W
        optional int32 pv1_Power = 19;   // power/10=W
        optional int32 pv2_Power = 24;   // power/10=W
        optional int32 toHomePower = 38; // power/10=W
      }
      
      enum CmdFunction {
          Unknown = 0;
          PermanentWattsPack = 129;
          SupplyPriorityPack = 130;
      }
      
      W Offline
      W Offline
      Waly_de
      wrote on last edited by Waly_de
      #152

      @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;
      }
      
      1 Reply Last reply
      2
      • W Waly_de

        @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

        A Offline
        A Offline
        applepro
        wrote on last edited by
        #153

        @waly_de Hey, das einzige was ich noch nicht hinbekommen habe ist das Schriebn von Daten per Buffer, wie hast du das genau angestellt, ich kann zwar Daten senden, aber es tut sich nichts...

        Gruß
        Jannick

        W 1 Reply Last reply
        0
        • A applepro

          @waly_de Hey, das einzige was ich noch nicht hinbekommen habe ist das Schriebn von Daten per Buffer, wie hast du das genau angestellt, ich kann zwar Daten senden, aber es tut sich nichts...

          Gruß
          Jannick

          W Offline
          W Offline
          Waly_de
          wrote on last edited by
          #154

          @applepro
          genau so wie oben geschrieben... wenn du die Daten in einem hexString hast, dann kannst du damit daraus einen Buffer machen:

          const buffer = Buffer.from(hexString, "hex");
          

          und dann einfach

          sendTo('mqtt.<Instanznummer>', 'sendMessage2Client', {topic: '/app/<USER_ID>/<SERIENNUMMER>/thing/property/set', message: buffer});
          
          A 1 Reply Last reply
          0
          • W Waly_de

            @applepro
            genau so wie oben geschrieben... wenn du die Daten in einem hexString hast, dann kannst du damit daraus einen Buffer machen:

            const buffer = Buffer.from(hexString, "hex");
            

            und dann einfach

            sendTo('mqtt.<Instanznummer>', 'sendMessage2Client', {topic: '/app/<USER_ID>/<SERIENNUMMER>/thing/property/set', message: buffer});
            
            A Offline
            A Offline
            applepro
            wrote on last edited by applepro
            #155

            @waly_de mhh komisch, genau so hatte ich es eigentlich gemacht. Edit: Gerade nochmal alles sauber aufgesetzt, jetzt läuft es.

            VespaGamingV 1 Reply Last reply
            0
            • A applepro

              @waly_de mhh komisch, genau so hatte ich es eigentlich gemacht. Edit: Gerade nochmal alles sauber aufgesetzt, jetzt läuft es.

              VespaGamingV Offline
              VespaGamingV Offline
              VespaGaming
              wrote on last edited by
              #156

              @applepro @waly_de
              Ihr habt mich mit meinem Laienwissen deutlich abehängt.
              Wie könnte so ein Javascript mit .proto decodierung sowie senden aussehen?
              Daten bekomme ich ausgelesen und nach hex convertiert

              1 Reply Last reply
              0
              • T Offline
                T Offline
                TDahinten
                wrote on last edited by TDahinten
                #157

                Wäre dankbar, wenn hier die notwendigen Schritte evtl. nochmal einfacher erklärt werden würden.
                Bislang habe ich es nur geschafft, den HEX Wert darzustellen und mit https://protobuf-decoder.netlify.app/ zu entschlüsseln.
                Allerdings haben meine Versuche mit dem Senden noch nicht funktioniert.
                Der "Leistungsverbrauch von anderen Verbrauchern" hat sich leider nicht verändert, nach dem Senden.
                Vielleicht baut ja sogar jemand ein fertiges Script 😉

                Wäre wirklich prima, wenn ich den Leistungsverbrauch dynamisch anpassen könnte. Weitere Werte bräuchte ich
                eigentlich nicht für meinen Zweck.

                W 1 Reply Last reply
                0
                • T TDahinten

                  Wäre dankbar, wenn hier die notwendigen Schritte evtl. nochmal einfacher erklärt werden würden.
                  Bislang habe ich es nur geschafft, den HEX Wert darzustellen und mit https://protobuf-decoder.netlify.app/ zu entschlüsseln.
                  Allerdings haben meine Versuche mit dem Senden noch nicht funktioniert.
                  Der "Leistungsverbrauch von anderen Verbrauchern" hat sich leider nicht verändert, nach dem Senden.
                  Vielleicht baut ja sogar jemand ein fertiges Script 😉

                  Wäre wirklich prima, wenn ich den Leistungsverbrauch dynamisch anpassen könnte. Weitere Werte bräuchte ich
                  eigentlich nicht für meinen Zweck.

                  W Offline
                  W Offline
                  Waly_de
                  wrote on last edited by Waly_de
                  #158

                  @tdahinten

                  Ok... Zufällig hab ich heute ein Script fertig gestellt, das vermutlich den meisten helfen wird.

                  Man muss lediglich die App-Zugangsdaten und Seriennummern eingeben, den Rest macht das Teil hoffentlich von alleine. Ich hab auf den Mqtt-Adapter verzichtet und lieber einen eigenen Client genutzt. Damit gibt es keine "Binary/File Probleme" mehr.
                  RAW Daten werden sowohl als HEX also auch dekodiert in States gespeichert. Alle einzelnen bekannten Daten werden automatisch als States angelegt.
                  Bisher lässt sich nur ein State, nämlich die Einspeiseleistung schreiben ("...thing_property_set.setAC")

                  Dann viel Spaß damit... bin sehr gespannt ob es bei Euch funktioniert!

                  Benutzen auf eigene Gefahr !! 😉

                  /**
                   * ecoflow-iobroker-connector.js
                   * Version:      0.2    
                   * Release date: 28.06.2023
                   * Autor:        Waly_de 
                   * Forum:        https://forum.iobroker.net/topic/54929/adapter-für-ecoflow-einbindung/155
                   * 
                   *
                   * This JavaScript file establishes a simple connection between IOBroker and EcoFlow. 
                   * It automatically creates known states under 0_userdata.
                   * 
                   * Please note that adjustments in the ConfigData section are required. Here, you need to enter your access credentials 
                   * used for the EcoFlow app, as well as the serial numbers of your devices.
                   * 
                   * Important: The second serial number entry should always correspond to the Powerstream as it is associated with control functions.
                   * 
                   * If you have a state that displays the current power consumption (SmartmeterID), please provide it as well. 
                   * Configure a history for this state so that the script can determine the lowest power consumption in the last 10 minutes (configurable). 
                   * This value will be used to dynamically adjust the Powerstream's feed-in power.
                   * 
                   * You can adust Powerstream's feed-in power by your self. Just write to the State "...thing_property_set.setAC" (Watt * 10)
                   * 
                   * Not all parameters of the Powerstream data are known yet. All known parameters will be automatically created as states.
                   * By modifying the "protoSource" constant, newly discovered data will also be automatically created.
                   * 
                   * The raw data of the interface is logged as a HEX string.
                   * 
                   * Please exercise caution as this is the initial version of the script. Use it at your own risk!
                   *
                   * Requirements:
                   * - Install protobuf using the command "npm install protobufjs" from the terminal console.
                   * - The "Paho MQTT Client" is also required. If not already installed, use the command "npm install mqtt".
                   *
                   * Note: It is encouraged to discover and publish missing data definitions to improve the script.
                   * Suggestions, optimizations, and extensions are welcome at any time.
                   *
                   * Special thanks to all contributors for their valuable input and support.
                   */
                  
                  /*******  YOUR DATA HERE  ****************/
                  var ConfigData = {
                      email: "your@mail.com",
                      passwort: "yourAppPasswort!",
                      seriennummern: [
                          { seriennummer: "XXXXXXXXXXXXX", name: "PowerStream" }, //1. has to be your PowerStream, you can add more devices...
                          { seriennummer: "XXXXXXXXXXXXX", name: "DELTA Max" }
                      ],
                      SmartmeterID: "sonoff.0.Stromzaehler1.MT175_P",
                      BasePowerOffset: 50,
                      MaxPower: 600,
                      MinValueMin: 10,
                      statesPrefix: "0_userdata.0.ecoflow",
                      Debug: false
                  };
                  //***************************************/
                  
                  
                  const protoSource = `
                  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 Bat_Minutes = 60;
                  }
                  enum CmdFunction {
                      Unknown = 0;
                      PermanentWattsPack = 129;
                      SupplyPriorityPack = 130;
                  }
                  `;
                  
                  
                  const mqtt = require('mqtt');
                  const https = require('https');
                  const protobuf = require("protobufjs");
                  
                  const mqttDaten = {
                      UserID: '',
                      User: '',
                      Passwort: '',
                      URL: '',
                      Port: '',
                      protocol: '',
                      clientID: ''
                  }
                  
                  /*=======================================================
                    =========             Timer               ============
                    =======================================================*/
                  //alle 5 minuten
                  schedule('*/1 * * * *', function () {
                      SetBasePower();
                  });
                  
                  
                  
                  await getEcoFlowMqttData(ConfigData.email, ConfigData.passwort)
                  async function getEcoFlowMqttData(email, password) {
                      const options = {
                          hostname: 'api.ecoflow.com',
                          path: '/auth/login',
                          method: 'POST',
                          headers: {
                              'Host': 'api.ecoflow.com',
                              'lang': 'de-de',
                              'platform': 'android',
                              'sysversion': '11',
                              'version': '4.1.2.02',
                              'phonemodel': 'SM-X200',
                              'content-type': 'application/json',
                              'user-agent': 'okhttp/3.14.9'
                          }
                      };
                  
                      const data = {
                          appVersion: "4.1.2.02",
                          email: email,
                          os: "android",
                          osVersion: "30",
                          password: Buffer.from(password).toString('base64'),
                          scene: "IOT_APP",
                          userType: "ECOFLOW"
                      };
                  
                      function httpsRequest(options, data) {
                          return new Promise((resolve, reject) => {
                              const req = https.request(options, res => {
                                  let data = '';
                                  res.on('data', chunk => {
                                      data += chunk;
                                  });
                                  res.on('end', () => {
                                      resolve(data);
                                  });
                              });
                  
                              req.on('error', error => {
                                  reject(error);
                              });
                  
                              if (data) {
                                  req.write(JSON.stringify(data));
                              }
                  
                              req.end();
                          });
                      }
                  
                      function uuidv4() {
                          return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                              var r = Math.random() * 16 | 0,
                                  v = c === 'x' ? r : (r & 0x3 | 0x8);
                              return v.toString(16);
                          });
                      }
                  
                      let response = await httpsRequest(options, data);
                      try {
                          let token = JSON.parse(response).data.token;
                          let userid = JSON.parse(response).data.user.userId;
                      } catch (error) {
                          log(response)
                          throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                      }
                  
                      let token = JSON.parse(response).data.token;
                      let userid = JSON.parse(response).data.user.userId;
                  
                      options.path = `/iot-auth/app/certification?userId=${userid}`;
                      options.method = 'GET';
                      options.headers.authorization = `Bearer ${token}`;
                      response = await httpsRequest(options);
                      try {
                          mqttDaten.Passwort = JSON.parse(response).data.certificatePassword
                          mqttDaten.Port = JSON.parse(response).data.port
                          mqttDaten.UserID = userid
                          mqttDaten.User = JSON.parse(response).data.certificateAccount
                          mqttDaten.URL = JSON.parse(response).data.url
                          mqttDaten.protocol = JSON.parse(response).data.protocol
                          mqttDaten.clientID = "ANDROID_" + uuidv4() + "_" + userid
                      } catch (error) {
                          log(response)
                          throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                      }
                      /*    
                          console.log("UserID: " + userid);
                          console.log("User: " + JSON.parse(response).data.certificateAccount);
                          console.log("Passwort: " + JSON.parse(response).data.certificatePassword);
                          console.log("URL: " + JSON.parse(response).data.url);
                          console.log("Port: " + JSON.parse(response).data.port);
                          console.log("protocol: " + JSON.parse(response).data.protocol);
                          console.log("clientID: ANDROID_" + uuidv4() + "_" + userid);
                      */
                  
                  }
                  
                  //################ MQTT Verbindung ##################
                  
                  // Verbindung herstellen
                  const options = {
                      port: mqttDaten.Port,
                      clientId: mqttDaten.clientID,
                      username: mqttDaten.User,
                      password: mqttDaten.Passwort,
                      protocol: mqttDaten.protocol
                  };
                  
                  
                  const client = mqtt.connect("mqtt://" + mqttDaten.URL, options);
                  
                  // Auf Verbindung hergestellt Ereignis reagieren
                  client.on('connect', function () {
                      console.log('Verbunden mit dem MQTT-Broker');
                  
                      ConfigData.seriennummern.forEach(item => {
                          client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/set');
                          client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/get');
                          client.subscribe('/app/device/property/' + item.seriennummer);
                      });
                  });
                  
                  // Auf Nachricht empfangen Ereignis reagieren
                  client.on('message', async function (topic, message) {
                      var jsonMessage = ""
                      const mqState = topic.replace(/^\//, '').replace(/\//g, '_')
                      await createMyState(mqState + ".RAW")
                      try {
                          jsonMessage = JSON.parse(message);
                          if (ConfigData.Debug) log('JSON-Nachricht empfangen:' + topic + ':' + JSON.stringify(jsonMessage));
                          setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", JSON.stringify(jsonMessage))
                          generateAndSyncSub("data", jsonMessage, false, ConfigData.statesPrefix + '.' + mqState)
                      } catch (error) {
                          if (ConfigData.Debug) log('Binäre Nachricht empfangen:' + topic + ':' + message.toString('hex'));
                          await createMyState(mqState + ".RAW_HEX")
                          setState(ConfigData.statesPrefix + '.' + mqState + ".RAW_HEX", message.toString('hex'))
                          //log('Decodierte Nachricht:' + decodeAndPrint(message.toString('hex')))
                          const messagedecoded = decodeAndPrint(message.toString('hex'))
                          setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", messagedecoded)
                          generateAndSyncSub("data", JSON.parse(messagedecoded), false, ConfigData.statesPrefix + '.' + mqState)
                      }
                  });
                  // Callback für Fehler
                  client.on('error', function (error) {
                      log('Fehler bei der MQTT-Verbindung:' + error, error);
                  });
                  
                  // close connection if script stopped
                  onStop(function (callback) {
                      if (client) {
                          // close connection
                          client.end();
                          log("MQTT-Client beendet")
                      }
                      callback();
                  }, 2000);
                  //*/
                  
                  
                  
                  async function createMyState(name) {
                      if (!(await existsObjectAsync(ConfigData.statesPrefix + '.' + name))) {
                          await createStateAsync(ConfigData.statesPrefix + '.' + name, {
                              name: name.split('.').pop(),
                              role: 'state',
                              type: 'string',
                              read: true,
                              write: true
                          });
                      }
                  }
                  
                  function decodeAndPrint(hexString) {
                      const root = protobuf.parse(protoSource).root;
                      const PowerMessage = root.lookupType("PowerMessage");
                      const message = PowerMessage.decode(Buffer.from(hexString, "hex"));
                      const object = PowerMessage.toObject(message, { defaults: false });
                      return JSON.stringify(object);
                  }
                  
                  function SendProto(protomsg, topic) {
                      const root = protobuf.parse(protoSource).root;
                      const PowerMessage = root.lookupType("PowerMessage");
                      const message = PowerMessage.create(JSON.parse(protomsg));
                      const messageBuffer = PowerMessage.encode(message).finish();
                      //log("Modifizierter Hex-String:" +  Buffer.from(messageBuffer).toString("hex"));
                      //log("topic:" +  topic);
                      client.publish(topic, messageBuffer, { qos: 1 }, function (error) {
                          if (error) {
                              console.error('Fehler beim Veröffentlichen der MQTT-Nachricht:', error);
                          } else {
                              if (ConfigData.Debug) log('Die MQTT-Nachricht wurde erfolgreich veröffentlicht.');
                          }
                      });
                  }
                  
                  //############ Funktionen zum setzen von Werten
                  
                  const musterSetAC = `
                  {
                    "item": {
                      "meta": {
                        "value": 1300
                      },
                      "src": 32,
                      "dest": 53,
                      "unknown1": 1,
                      "unknown2": 1,
                      "unknown4": 3,
                      "cmdFunc": 20,
                      "cmdId": 129,
                      "unknown5": 3,
                      "needAck": 1,
                      "timestamp": {
                        "low": 35061401,
                        "high": 0,
                        "unsigned": true
                      },
                      "unknown6": 19,
                      "unknown7": 1,
                      "OS": "ios",
                      "serialNumber": "ABCxxxxxxx123"
                    }
                  }
                  `
                  await createMyState('app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC')
                  on({ id: ConfigData.statesPrefix + '.app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC', change: "any", ack: false }, function (obj) {
                      setAC(Number(obj.state.val))
                      setState(obj.id, obj.state.val, true);
                  });
                  
                  function setAC(Value = 1000) {
                      const updatedMusterSetAC = JSON.parse(musterSetAC);
                      updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                      updatedMusterSetAC.item.meta.value = Value
                      updatedMusterSetAC.item.serialNumber = ConfigData.seriennummern[0].seriennummer
                      SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/set');
                  }
                  
                  function generateAndSyncSub(id, JElements, sub = false, preset = "0_userdata.0.test") {
                      if (!JElements || typeof JElements !== 'object') {
                          log('Ungültige JElements übergeben!');
                          return;
                      }
                      for (var JElement in JElements) {
                          var AktVal;
                          if (typeof JElements[JElement] === "object") {
                              generateAndSyncSub(id + "." + JElement, JElements[JElement], true, preset);
                          } else {
                              try {
                                  if (isState2(preset + "." + id + "." + JElement)) AktVal = getState(preset + "." + id + "." + JElement).val;
                              } catch (e) {
                                  log("Fehler: " + e);
                              }
                              if (AktVal == null) {
                                  createState(preset + "." + id + "." + JElement, JElements[JElement], false);
                                  AktVal = JElements[JElement];
                              }
                              if (AktVal != JElements[JElement]) {
                                  if (isState2(preset + "." + id + "." + JElement)) setState(preset + "." + id + "." + JElement, JElements[JElement]);
                              }
                          }
                      }
                  }
                  
                  
                  /**
                   * Checks if a a given state or part of state is existing.
                   * This is a workaround, as getObject() or getState() throw warnings in the log.
                   * Set strict to true if the state shall match exactly. If it is false, it will add a wildcard * to the end.
                   * See: https://forum.iobroker.net/topic/11354/
                   * @param {string}    strStatePath     Input string of state, like 'javascript.0.switches.Osram.Bedroom'
                   * @param {boolean}   [strict=false]   Optional: if true, it will work strict, if false, it will add a wildcard * to the end of the string
                   * @return {boolean}                   true if state exists, false if not
                   */
                  function isState2(strStatePath, strict = true) {
                      let mSelector;
                      if (strict) {
                          mSelector = $(strStatePath);
                      } else {
                          mSelector = $(strStatePath + "*");
                      }
                      if (mSelector.length > 0) {
                          return true;
                      } else {
                          return false;
                      }
                  }
                  
                  
                  // ########### Grundbedarf steuern
                  // Es müssen Historydaten für den Wert des Smartmeters vorhanden sein, damit der niedrigeste wert der letzten x Minuten ermittelt werden kann
                  
                  function getLowestValue(id, minuten = 120) {
                      return new Promise((resolve, reject) => {
                          const now = new Date().getTime();
                          const range = minuten * 60 * 1000;
                          var myresult = 0;
                          getHistory(0, {
                              id: id,
                              start: now - range,
                              end: now,
                              aggregate: 'minmax'
                          }, function (err, result) {
                              if (err) {
                                  reject(err);
                              } else if (result.length) {
                                  let lowestValue = result[0].val;
                                  for (let i = 1; i < result.length; i++) {
                                      if (result[i].val < lowestValue) {
                                          lowestValue = result[i].val;
                                      }
                                  }
                                  myresult = Number(lowestValue);
                                  resolve(myresult);
                              } else {
                                  reject(new Error('No data'));
                              }
                          });
                      });
                  }
                  
                  function SetBasePower() {
                      if (isState2(ConfigData.SmartmeterID)) {
                          getLowestValue(ConfigData.SmartmeterID, ConfigData.MinValueMin)
                              .then(lowestValue => {
                                  if (lowestValue > 0) {
                                      if (ConfigData.Debug) log("Tiefster Wert der letzten " + ConfigData.MinValueMin + " Minuten: " + lowestValue);
                                      var AndereVerbraucher = 0
                                      const ToHomeId = ConfigData.statesPrefix + ".app_device_property_" + ConfigData.seriennummern[0].seriennummer + ".data.item.meta.ToHome_Power"
                                      if (isState2(ToHomeId)) {
                                          AndereVerbraucher = Number(getState(ToHomeId).val) / 10
                                          var NewValue = lowestValue + AndereVerbraucher - ConfigData.BasePowerOffset
                                          if (NewValue > ConfigData.MaxPower) NewValue = ConfigData.MaxPower
                                          if (ConfigData.Debug) {
                                              log("AndereVerbraucher: " + AndereVerbraucher)
                                              log("Macht : " + (lowestValue + AndereVerbraucher) + "W Grundbedarf")
                                              log("- Offset von: " + ConfigData.BasePowerOffset + " W = " + (NewValue) + " W Grundbedarf")
                                          }
                                          setAC(NewValue * 10);
                  
                                      }
                                  }
                              })
                              .catch(error => {
                                  log("Fehler beim Abrufen des niedrigsten Werts: " + error);
                              });
                      }
                  }
                  
                  
                  T A 2 Replies Last reply
                  1
                  • W Waly_de

                    @tdahinten

                    Ok... Zufällig hab ich heute ein Script fertig gestellt, das vermutlich den meisten helfen wird.

                    Man muss lediglich die App-Zugangsdaten und Seriennummern eingeben, den Rest macht das Teil hoffentlich von alleine. Ich hab auf den Mqtt-Adapter verzichtet und lieber einen eigenen Client genutzt. Damit gibt es keine "Binary/File Probleme" mehr.
                    RAW Daten werden sowohl als HEX also auch dekodiert in States gespeichert. Alle einzelnen bekannten Daten werden automatisch als States angelegt.
                    Bisher lässt sich nur ein State, nämlich die Einspeiseleistung schreiben ("...thing_property_set.setAC")

                    Dann viel Spaß damit... bin sehr gespannt ob es bei Euch funktioniert!

                    Benutzen auf eigene Gefahr !! 😉

                    /**
                     * ecoflow-iobroker-connector.js
                     * Version:      0.2    
                     * Release date: 28.06.2023
                     * Autor:        Waly_de 
                     * Forum:        https://forum.iobroker.net/topic/54929/adapter-für-ecoflow-einbindung/155
                     * 
                     *
                     * This JavaScript file establishes a simple connection between IOBroker and EcoFlow. 
                     * It automatically creates known states under 0_userdata.
                     * 
                     * Please note that adjustments in the ConfigData section are required. Here, you need to enter your access credentials 
                     * used for the EcoFlow app, as well as the serial numbers of your devices.
                     * 
                     * Important: The second serial number entry should always correspond to the Powerstream as it is associated with control functions.
                     * 
                     * If you have a state that displays the current power consumption (SmartmeterID), please provide it as well. 
                     * Configure a history for this state so that the script can determine the lowest power consumption in the last 10 minutes (configurable). 
                     * This value will be used to dynamically adjust the Powerstream's feed-in power.
                     * 
                     * You can adust Powerstream's feed-in power by your self. Just write to the State "...thing_property_set.setAC" (Watt * 10)
                     * 
                     * Not all parameters of the Powerstream data are known yet. All known parameters will be automatically created as states.
                     * By modifying the "protoSource" constant, newly discovered data will also be automatically created.
                     * 
                     * The raw data of the interface is logged as a HEX string.
                     * 
                     * Please exercise caution as this is the initial version of the script. Use it at your own risk!
                     *
                     * Requirements:
                     * - Install protobuf using the command "npm install protobufjs" from the terminal console.
                     * - The "Paho MQTT Client" is also required. If not already installed, use the command "npm install mqtt".
                     *
                     * Note: It is encouraged to discover and publish missing data definitions to improve the script.
                     * Suggestions, optimizations, and extensions are welcome at any time.
                     *
                     * Special thanks to all contributors for their valuable input and support.
                     */
                    
                    /*******  YOUR DATA HERE  ****************/
                    var ConfigData = {
                        email: "your@mail.com",
                        passwort: "yourAppPasswort!",
                        seriennummern: [
                            { seriennummer: "XXXXXXXXXXXXX", name: "PowerStream" }, //1. has to be your PowerStream, you can add more devices...
                            { seriennummer: "XXXXXXXXXXXXX", name: "DELTA Max" }
                        ],
                        SmartmeterID: "sonoff.0.Stromzaehler1.MT175_P",
                        BasePowerOffset: 50,
                        MaxPower: 600,
                        MinValueMin: 10,
                        statesPrefix: "0_userdata.0.ecoflow",
                        Debug: false
                    };
                    //***************************************/
                    
                    
                    const protoSource = `
                    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 Bat_Minutes = 60;
                    }
                    enum CmdFunction {
                        Unknown = 0;
                        PermanentWattsPack = 129;
                        SupplyPriorityPack = 130;
                    }
                    `;
                    
                    
                    const mqtt = require('mqtt');
                    const https = require('https');
                    const protobuf = require("protobufjs");
                    
                    const mqttDaten = {
                        UserID: '',
                        User: '',
                        Passwort: '',
                        URL: '',
                        Port: '',
                        protocol: '',
                        clientID: ''
                    }
                    
                    /*=======================================================
                      =========             Timer               ============
                      =======================================================*/
                    //alle 5 minuten
                    schedule('*/1 * * * *', function () {
                        SetBasePower();
                    });
                    
                    
                    
                    await getEcoFlowMqttData(ConfigData.email, ConfigData.passwort)
                    async function getEcoFlowMqttData(email, password) {
                        const options = {
                            hostname: 'api.ecoflow.com',
                            path: '/auth/login',
                            method: 'POST',
                            headers: {
                                'Host': 'api.ecoflow.com',
                                'lang': 'de-de',
                                'platform': 'android',
                                'sysversion': '11',
                                'version': '4.1.2.02',
                                'phonemodel': 'SM-X200',
                                'content-type': 'application/json',
                                'user-agent': 'okhttp/3.14.9'
                            }
                        };
                    
                        const data = {
                            appVersion: "4.1.2.02",
                            email: email,
                            os: "android",
                            osVersion: "30",
                            password: Buffer.from(password).toString('base64'),
                            scene: "IOT_APP",
                            userType: "ECOFLOW"
                        };
                    
                        function httpsRequest(options, data) {
                            return new Promise((resolve, reject) => {
                                const req = https.request(options, res => {
                                    let data = '';
                                    res.on('data', chunk => {
                                        data += chunk;
                                    });
                                    res.on('end', () => {
                                        resolve(data);
                                    });
                                });
                    
                                req.on('error', error => {
                                    reject(error);
                                });
                    
                                if (data) {
                                    req.write(JSON.stringify(data));
                                }
                    
                                req.end();
                            });
                        }
                    
                        function uuidv4() {
                            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                                var r = Math.random() * 16 | 0,
                                    v = c === 'x' ? r : (r & 0x3 | 0x8);
                                return v.toString(16);
                            });
                        }
                    
                        let response = await httpsRequest(options, data);
                        try {
                            let token = JSON.parse(response).data.token;
                            let userid = JSON.parse(response).data.user.userId;
                        } catch (error) {
                            log(response)
                            throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                        }
                    
                        let token = JSON.parse(response).data.token;
                        let userid = JSON.parse(response).data.user.userId;
                    
                        options.path = `/iot-auth/app/certification?userId=${userid}`;
                        options.method = 'GET';
                        options.headers.authorization = `Bearer ${token}`;
                        response = await httpsRequest(options);
                        try {
                            mqttDaten.Passwort = JSON.parse(response).data.certificatePassword
                            mqttDaten.Port = JSON.parse(response).data.port
                            mqttDaten.UserID = userid
                            mqttDaten.User = JSON.parse(response).data.certificateAccount
                            mqttDaten.URL = JSON.parse(response).data.url
                            mqttDaten.protocol = JSON.parse(response).data.protocol
                            mqttDaten.clientID = "ANDROID_" + uuidv4() + "_" + userid
                        } catch (error) {
                            log(response)
                            throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                        }
                        /*    
                            console.log("UserID: " + userid);
                            console.log("User: " + JSON.parse(response).data.certificateAccount);
                            console.log("Passwort: " + JSON.parse(response).data.certificatePassword);
                            console.log("URL: " + JSON.parse(response).data.url);
                            console.log("Port: " + JSON.parse(response).data.port);
                            console.log("protocol: " + JSON.parse(response).data.protocol);
                            console.log("clientID: ANDROID_" + uuidv4() + "_" + userid);
                        */
                    
                    }
                    
                    //################ MQTT Verbindung ##################
                    
                    // Verbindung herstellen
                    const options = {
                        port: mqttDaten.Port,
                        clientId: mqttDaten.clientID,
                        username: mqttDaten.User,
                        password: mqttDaten.Passwort,
                        protocol: mqttDaten.protocol
                    };
                    
                    
                    const client = mqtt.connect("mqtt://" + mqttDaten.URL, options);
                    
                    // Auf Verbindung hergestellt Ereignis reagieren
                    client.on('connect', function () {
                        console.log('Verbunden mit dem MQTT-Broker');
                    
                        ConfigData.seriennummern.forEach(item => {
                            client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/set');
                            client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/get');
                            client.subscribe('/app/device/property/' + item.seriennummer);
                        });
                    });
                    
                    // Auf Nachricht empfangen Ereignis reagieren
                    client.on('message', async function (topic, message) {
                        var jsonMessage = ""
                        const mqState = topic.replace(/^\//, '').replace(/\//g, '_')
                        await createMyState(mqState + ".RAW")
                        try {
                            jsonMessage = JSON.parse(message);
                            if (ConfigData.Debug) log('JSON-Nachricht empfangen:' + topic + ':' + JSON.stringify(jsonMessage));
                            setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", JSON.stringify(jsonMessage))
                            generateAndSyncSub("data", jsonMessage, false, ConfigData.statesPrefix + '.' + mqState)
                        } catch (error) {
                            if (ConfigData.Debug) log('Binäre Nachricht empfangen:' + topic + ':' + message.toString('hex'));
                            await createMyState(mqState + ".RAW_HEX")
                            setState(ConfigData.statesPrefix + '.' + mqState + ".RAW_HEX", message.toString('hex'))
                            //log('Decodierte Nachricht:' + decodeAndPrint(message.toString('hex')))
                            const messagedecoded = decodeAndPrint(message.toString('hex'))
                            setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", messagedecoded)
                            generateAndSyncSub("data", JSON.parse(messagedecoded), false, ConfigData.statesPrefix + '.' + mqState)
                        }
                    });
                    // Callback für Fehler
                    client.on('error', function (error) {
                        log('Fehler bei der MQTT-Verbindung:' + error, error);
                    });
                    
                    // close connection if script stopped
                    onStop(function (callback) {
                        if (client) {
                            // close connection
                            client.end();
                            log("MQTT-Client beendet")
                        }
                        callback();
                    }, 2000);
                    //*/
                    
                    
                    
                    async function createMyState(name) {
                        if (!(await existsObjectAsync(ConfigData.statesPrefix + '.' + name))) {
                            await createStateAsync(ConfigData.statesPrefix + '.' + name, {
                                name: name.split('.').pop(),
                                role: 'state',
                                type: 'string',
                                read: true,
                                write: true
                            });
                        }
                    }
                    
                    function decodeAndPrint(hexString) {
                        const root = protobuf.parse(protoSource).root;
                        const PowerMessage = root.lookupType("PowerMessage");
                        const message = PowerMessage.decode(Buffer.from(hexString, "hex"));
                        const object = PowerMessage.toObject(message, { defaults: false });
                        return JSON.stringify(object);
                    }
                    
                    function SendProto(protomsg, topic) {
                        const root = protobuf.parse(protoSource).root;
                        const PowerMessage = root.lookupType("PowerMessage");
                        const message = PowerMessage.create(JSON.parse(protomsg));
                        const messageBuffer = PowerMessage.encode(message).finish();
                        //log("Modifizierter Hex-String:" +  Buffer.from(messageBuffer).toString("hex"));
                        //log("topic:" +  topic);
                        client.publish(topic, messageBuffer, { qos: 1 }, function (error) {
                            if (error) {
                                console.error('Fehler beim Veröffentlichen der MQTT-Nachricht:', error);
                            } else {
                                if (ConfigData.Debug) log('Die MQTT-Nachricht wurde erfolgreich veröffentlicht.');
                            }
                        });
                    }
                    
                    //############ Funktionen zum setzen von Werten
                    
                    const musterSetAC = `
                    {
                      "item": {
                        "meta": {
                          "value": 1300
                        },
                        "src": 32,
                        "dest": 53,
                        "unknown1": 1,
                        "unknown2": 1,
                        "unknown4": 3,
                        "cmdFunc": 20,
                        "cmdId": 129,
                        "unknown5": 3,
                        "needAck": 1,
                        "timestamp": {
                          "low": 35061401,
                          "high": 0,
                          "unsigned": true
                        },
                        "unknown6": 19,
                        "unknown7": 1,
                        "OS": "ios",
                        "serialNumber": "ABCxxxxxxx123"
                      }
                    }
                    `
                    await createMyState('app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC')
                    on({ id: ConfigData.statesPrefix + '.app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC', change: "any", ack: false }, function (obj) {
                        setAC(Number(obj.state.val))
                        setState(obj.id, obj.state.val, true);
                    });
                    
                    function setAC(Value = 1000) {
                        const updatedMusterSetAC = JSON.parse(musterSetAC);
                        updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                        updatedMusterSetAC.item.meta.value = Value
                        updatedMusterSetAC.item.serialNumber = ConfigData.seriennummern[0].seriennummer
                        SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/set');
                    }
                    
                    function generateAndSyncSub(id, JElements, sub = false, preset = "0_userdata.0.test") {
                        if (!JElements || typeof JElements !== 'object') {
                            log('Ungültige JElements übergeben!');
                            return;
                        }
                        for (var JElement in JElements) {
                            var AktVal;
                            if (typeof JElements[JElement] === "object") {
                                generateAndSyncSub(id + "." + JElement, JElements[JElement], true, preset);
                            } else {
                                try {
                                    if (isState2(preset + "." + id + "." + JElement)) AktVal = getState(preset + "." + id + "." + JElement).val;
                                } catch (e) {
                                    log("Fehler: " + e);
                                }
                                if (AktVal == null) {
                                    createState(preset + "." + id + "." + JElement, JElements[JElement], false);
                                    AktVal = JElements[JElement];
                                }
                                if (AktVal != JElements[JElement]) {
                                    if (isState2(preset + "." + id + "." + JElement)) setState(preset + "." + id + "." + JElement, JElements[JElement]);
                                }
                            }
                        }
                    }
                    
                    
                    /**
                     * Checks if a a given state or part of state is existing.
                     * This is a workaround, as getObject() or getState() throw warnings in the log.
                     * Set strict to true if the state shall match exactly. If it is false, it will add a wildcard * to the end.
                     * See: https://forum.iobroker.net/topic/11354/
                     * @param {string}    strStatePath     Input string of state, like 'javascript.0.switches.Osram.Bedroom'
                     * @param {boolean}   [strict=false]   Optional: if true, it will work strict, if false, it will add a wildcard * to the end of the string
                     * @return {boolean}                   true if state exists, false if not
                     */
                    function isState2(strStatePath, strict = true) {
                        let mSelector;
                        if (strict) {
                            mSelector = $(strStatePath);
                        } else {
                            mSelector = $(strStatePath + "*");
                        }
                        if (mSelector.length > 0) {
                            return true;
                        } else {
                            return false;
                        }
                    }
                    
                    
                    // ########### Grundbedarf steuern
                    // Es müssen Historydaten für den Wert des Smartmeters vorhanden sein, damit der niedrigeste wert der letzten x Minuten ermittelt werden kann
                    
                    function getLowestValue(id, minuten = 120) {
                        return new Promise((resolve, reject) => {
                            const now = new Date().getTime();
                            const range = minuten * 60 * 1000;
                            var myresult = 0;
                            getHistory(0, {
                                id: id,
                                start: now - range,
                                end: now,
                                aggregate: 'minmax'
                            }, function (err, result) {
                                if (err) {
                                    reject(err);
                                } else if (result.length) {
                                    let lowestValue = result[0].val;
                                    for (let i = 1; i < result.length; i++) {
                                        if (result[i].val < lowestValue) {
                                            lowestValue = result[i].val;
                                        }
                                    }
                                    myresult = Number(lowestValue);
                                    resolve(myresult);
                                } else {
                                    reject(new Error('No data'));
                                }
                            });
                        });
                    }
                    
                    function SetBasePower() {
                        if (isState2(ConfigData.SmartmeterID)) {
                            getLowestValue(ConfigData.SmartmeterID, ConfigData.MinValueMin)
                                .then(lowestValue => {
                                    if (lowestValue > 0) {
                                        if (ConfigData.Debug) log("Tiefster Wert der letzten " + ConfigData.MinValueMin + " Minuten: " + lowestValue);
                                        var AndereVerbraucher = 0
                                        const ToHomeId = ConfigData.statesPrefix + ".app_device_property_" + ConfigData.seriennummern[0].seriennummer + ".data.item.meta.ToHome_Power"
                                        if (isState2(ToHomeId)) {
                                            AndereVerbraucher = Number(getState(ToHomeId).val) / 10
                                            var NewValue = lowestValue + AndereVerbraucher - ConfigData.BasePowerOffset
                                            if (NewValue > ConfigData.MaxPower) NewValue = ConfigData.MaxPower
                                            if (ConfigData.Debug) {
                                                log("AndereVerbraucher: " + AndereVerbraucher)
                                                log("Macht : " + (lowestValue + AndereVerbraucher) + "W Grundbedarf")
                                                log("- Offset von: " + ConfigData.BasePowerOffset + " W = " + (NewValue) + " W Grundbedarf")
                                            }
                                            setAC(NewValue * 10);
                    
                                        }
                                    }
                                })
                                .catch(error => {
                                    log("Fehler beim Abrufen des niedrigsten Werts: " + error);
                                });
                        }
                    }
                    
                    
                    T Offline
                    T Offline
                    TDahinten
                    wrote on last edited by TDahinten
                    #159

                    @waly_de said in Adapter für Ecoflow Einbindung:

                    Also erstmal vielen Dank, für die schnelle Unterstützung. 👍

                    Habe soweit alles im Script befüllt. Ebenfalls die mqttDaten.
                    Die notwendigen Installationen wurden durchgeführt.

                    Folgende Fehler werden im Log gemeldet::

                    error javascript.0 (1474) script.js.common.PowerStream_Script: Error: illegal token '{' (line 5)
                    error javascript.0 (1474) at decodeAndPrint (script.js.common.PowerStream_Script:629:27)
                    error javascript.0 (1474) at MqttClient.<anonymous> (script.js.common.PowerStream_Script:557:32)

                    W 1 Reply Last reply
                    0
                    • T TDahinten

                      @waly_de said in Adapter für Ecoflow Einbindung:

                      Also erstmal vielen Dank, für die schnelle Unterstützung. 👍

                      Habe soweit alles im Script befüllt. Ebenfalls die mqttDaten.
                      Die notwendigen Installationen wurden durchgeführt.

                      Folgende Fehler werden im Log gemeldet::

                      error javascript.0 (1474) script.js.common.PowerStream_Script: Error: illegal token '{' (line 5)
                      error javascript.0 (1474) at decodeAndPrint (script.js.common.PowerStream_Script:629:27)
                      error javascript.0 (1474) at MqttClient.<anonymous> (script.js.common.PowerStream_Script:557:32)

                      W Offline
                      W Offline
                      Waly_de
                      wrote on last edited by
                      #160

                      @tdahinten hmm ... guck mal bitte ob du ein Komma zu viel oder zu wenig im Bereich der Seriennummern hast. die {} müssen ein Komma getrennt sein:

                      seriennummern: [
                              { seriennummer: "XXXXXXXXXXXXX", name: "PowerStream" },
                              { seriennummer: "XXXXXXXXXXXXX", name: "DELTA Max" }
                          ],
                      
                      1 Reply Last reply
                      0
                      • W Waly_de

                        @tdahinten

                        Ok... Zufällig hab ich heute ein Script fertig gestellt, das vermutlich den meisten helfen wird.

                        Man muss lediglich die App-Zugangsdaten und Seriennummern eingeben, den Rest macht das Teil hoffentlich von alleine. Ich hab auf den Mqtt-Adapter verzichtet und lieber einen eigenen Client genutzt. Damit gibt es keine "Binary/File Probleme" mehr.
                        RAW Daten werden sowohl als HEX also auch dekodiert in States gespeichert. Alle einzelnen bekannten Daten werden automatisch als States angelegt.
                        Bisher lässt sich nur ein State, nämlich die Einspeiseleistung schreiben ("...thing_property_set.setAC")

                        Dann viel Spaß damit... bin sehr gespannt ob es bei Euch funktioniert!

                        Benutzen auf eigene Gefahr !! 😉

                        /**
                         * ecoflow-iobroker-connector.js
                         * Version:      0.2    
                         * Release date: 28.06.2023
                         * Autor:        Waly_de 
                         * Forum:        https://forum.iobroker.net/topic/54929/adapter-für-ecoflow-einbindung/155
                         * 
                         *
                         * This JavaScript file establishes a simple connection between IOBroker and EcoFlow. 
                         * It automatically creates known states under 0_userdata.
                         * 
                         * Please note that adjustments in the ConfigData section are required. Here, you need to enter your access credentials 
                         * used for the EcoFlow app, as well as the serial numbers of your devices.
                         * 
                         * Important: The second serial number entry should always correspond to the Powerstream as it is associated with control functions.
                         * 
                         * If you have a state that displays the current power consumption (SmartmeterID), please provide it as well. 
                         * Configure a history for this state so that the script can determine the lowest power consumption in the last 10 minutes (configurable). 
                         * This value will be used to dynamically adjust the Powerstream's feed-in power.
                         * 
                         * You can adust Powerstream's feed-in power by your self. Just write to the State "...thing_property_set.setAC" (Watt * 10)
                         * 
                         * Not all parameters of the Powerstream data are known yet. All known parameters will be automatically created as states.
                         * By modifying the "protoSource" constant, newly discovered data will also be automatically created.
                         * 
                         * The raw data of the interface is logged as a HEX string.
                         * 
                         * Please exercise caution as this is the initial version of the script. Use it at your own risk!
                         *
                         * Requirements:
                         * - Install protobuf using the command "npm install protobufjs" from the terminal console.
                         * - The "Paho MQTT Client" is also required. If not already installed, use the command "npm install mqtt".
                         *
                         * Note: It is encouraged to discover and publish missing data definitions to improve the script.
                         * Suggestions, optimizations, and extensions are welcome at any time.
                         *
                         * Special thanks to all contributors for their valuable input and support.
                         */
                        
                        /*******  YOUR DATA HERE  ****************/
                        var ConfigData = {
                            email: "your@mail.com",
                            passwort: "yourAppPasswort!",
                            seriennummern: [
                                { seriennummer: "XXXXXXXXXXXXX", name: "PowerStream" }, //1. has to be your PowerStream, you can add more devices...
                                { seriennummer: "XXXXXXXXXXXXX", name: "DELTA Max" }
                            ],
                            SmartmeterID: "sonoff.0.Stromzaehler1.MT175_P",
                            BasePowerOffset: 50,
                            MaxPower: 600,
                            MinValueMin: 10,
                            statesPrefix: "0_userdata.0.ecoflow",
                            Debug: false
                        };
                        //***************************************/
                        
                        
                        const protoSource = `
                        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 Bat_Minutes = 60;
                        }
                        enum CmdFunction {
                            Unknown = 0;
                            PermanentWattsPack = 129;
                            SupplyPriorityPack = 130;
                        }
                        `;
                        
                        
                        const mqtt = require('mqtt');
                        const https = require('https');
                        const protobuf = require("protobufjs");
                        
                        const mqttDaten = {
                            UserID: '',
                            User: '',
                            Passwort: '',
                            URL: '',
                            Port: '',
                            protocol: '',
                            clientID: ''
                        }
                        
                        /*=======================================================
                          =========             Timer               ============
                          =======================================================*/
                        //alle 5 minuten
                        schedule('*/1 * * * *', function () {
                            SetBasePower();
                        });
                        
                        
                        
                        await getEcoFlowMqttData(ConfigData.email, ConfigData.passwort)
                        async function getEcoFlowMqttData(email, password) {
                            const options = {
                                hostname: 'api.ecoflow.com',
                                path: '/auth/login',
                                method: 'POST',
                                headers: {
                                    'Host': 'api.ecoflow.com',
                                    'lang': 'de-de',
                                    'platform': 'android',
                                    'sysversion': '11',
                                    'version': '4.1.2.02',
                                    'phonemodel': 'SM-X200',
                                    'content-type': 'application/json',
                                    'user-agent': 'okhttp/3.14.9'
                                }
                            };
                        
                            const data = {
                                appVersion: "4.1.2.02",
                                email: email,
                                os: "android",
                                osVersion: "30",
                                password: Buffer.from(password).toString('base64'),
                                scene: "IOT_APP",
                                userType: "ECOFLOW"
                            };
                        
                            function httpsRequest(options, data) {
                                return new Promise((resolve, reject) => {
                                    const req = https.request(options, res => {
                                        let data = '';
                                        res.on('data', chunk => {
                                            data += chunk;
                                        });
                                        res.on('end', () => {
                                            resolve(data);
                                        });
                                    });
                        
                                    req.on('error', error => {
                                        reject(error);
                                    });
                        
                                    if (data) {
                                        req.write(JSON.stringify(data));
                                    }
                        
                                    req.end();
                                });
                            }
                        
                            function uuidv4() {
                                return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                                    var r = Math.random() * 16 | 0,
                                        v = c === 'x' ? r : (r & 0x3 | 0x8);
                                    return v.toString(16);
                                });
                            }
                        
                            let response = await httpsRequest(options, data);
                            try {
                                let token = JSON.parse(response).data.token;
                                let userid = JSON.parse(response).data.user.userId;
                            } catch (error) {
                                log(response)
                                throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                            }
                        
                            let token = JSON.parse(response).data.token;
                            let userid = JSON.parse(response).data.user.userId;
                        
                            options.path = `/iot-auth/app/certification?userId=${userid}`;
                            options.method = 'GET';
                            options.headers.authorization = `Bearer ${token}`;
                            response = await httpsRequest(options);
                            try {
                                mqttDaten.Passwort = JSON.parse(response).data.certificatePassword
                                mqttDaten.Port = JSON.parse(response).data.port
                                mqttDaten.UserID = userid
                                mqttDaten.User = JSON.parse(response).data.certificateAccount
                                mqttDaten.URL = JSON.parse(response).data.url
                                mqttDaten.protocol = JSON.parse(response).data.protocol
                                mqttDaten.clientID = "ANDROID_" + uuidv4() + "_" + userid
                            } catch (error) {
                                log(response)
                                throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                            }
                            /*    
                                console.log("UserID: " + userid);
                                console.log("User: " + JSON.parse(response).data.certificateAccount);
                                console.log("Passwort: " + JSON.parse(response).data.certificatePassword);
                                console.log("URL: " + JSON.parse(response).data.url);
                                console.log("Port: " + JSON.parse(response).data.port);
                                console.log("protocol: " + JSON.parse(response).data.protocol);
                                console.log("clientID: ANDROID_" + uuidv4() + "_" + userid);
                            */
                        
                        }
                        
                        //################ MQTT Verbindung ##################
                        
                        // Verbindung herstellen
                        const options = {
                            port: mqttDaten.Port,
                            clientId: mqttDaten.clientID,
                            username: mqttDaten.User,
                            password: mqttDaten.Passwort,
                            protocol: mqttDaten.protocol
                        };
                        
                        
                        const client = mqtt.connect("mqtt://" + mqttDaten.URL, options);
                        
                        // Auf Verbindung hergestellt Ereignis reagieren
                        client.on('connect', function () {
                            console.log('Verbunden mit dem MQTT-Broker');
                        
                            ConfigData.seriennummern.forEach(item => {
                                client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/set');
                                client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/get');
                                client.subscribe('/app/device/property/' + item.seriennummer);
                            });
                        });
                        
                        // Auf Nachricht empfangen Ereignis reagieren
                        client.on('message', async function (topic, message) {
                            var jsonMessage = ""
                            const mqState = topic.replace(/^\//, '').replace(/\//g, '_')
                            await createMyState(mqState + ".RAW")
                            try {
                                jsonMessage = JSON.parse(message);
                                if (ConfigData.Debug) log('JSON-Nachricht empfangen:' + topic + ':' + JSON.stringify(jsonMessage));
                                setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", JSON.stringify(jsonMessage))
                                generateAndSyncSub("data", jsonMessage, false, ConfigData.statesPrefix + '.' + mqState)
                            } catch (error) {
                                if (ConfigData.Debug) log('Binäre Nachricht empfangen:' + topic + ':' + message.toString('hex'));
                                await createMyState(mqState + ".RAW_HEX")
                                setState(ConfigData.statesPrefix + '.' + mqState + ".RAW_HEX", message.toString('hex'))
                                //log('Decodierte Nachricht:' + decodeAndPrint(message.toString('hex')))
                                const messagedecoded = decodeAndPrint(message.toString('hex'))
                                setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", messagedecoded)
                                generateAndSyncSub("data", JSON.parse(messagedecoded), false, ConfigData.statesPrefix + '.' + mqState)
                            }
                        });
                        // Callback für Fehler
                        client.on('error', function (error) {
                            log('Fehler bei der MQTT-Verbindung:' + error, error);
                        });
                        
                        // close connection if script stopped
                        onStop(function (callback) {
                            if (client) {
                                // close connection
                                client.end();
                                log("MQTT-Client beendet")
                            }
                            callback();
                        }, 2000);
                        //*/
                        
                        
                        
                        async function createMyState(name) {
                            if (!(await existsObjectAsync(ConfigData.statesPrefix + '.' + name))) {
                                await createStateAsync(ConfigData.statesPrefix + '.' + name, {
                                    name: name.split('.').pop(),
                                    role: 'state',
                                    type: 'string',
                                    read: true,
                                    write: true
                                });
                            }
                        }
                        
                        function decodeAndPrint(hexString) {
                            const root = protobuf.parse(protoSource).root;
                            const PowerMessage = root.lookupType("PowerMessage");
                            const message = PowerMessage.decode(Buffer.from(hexString, "hex"));
                            const object = PowerMessage.toObject(message, { defaults: false });
                            return JSON.stringify(object);
                        }
                        
                        function SendProto(protomsg, topic) {
                            const root = protobuf.parse(protoSource).root;
                            const PowerMessage = root.lookupType("PowerMessage");
                            const message = PowerMessage.create(JSON.parse(protomsg));
                            const messageBuffer = PowerMessage.encode(message).finish();
                            //log("Modifizierter Hex-String:" +  Buffer.from(messageBuffer).toString("hex"));
                            //log("topic:" +  topic);
                            client.publish(topic, messageBuffer, { qos: 1 }, function (error) {
                                if (error) {
                                    console.error('Fehler beim Veröffentlichen der MQTT-Nachricht:', error);
                                } else {
                                    if (ConfigData.Debug) log('Die MQTT-Nachricht wurde erfolgreich veröffentlicht.');
                                }
                            });
                        }
                        
                        //############ Funktionen zum setzen von Werten
                        
                        const musterSetAC = `
                        {
                          "item": {
                            "meta": {
                              "value": 1300
                            },
                            "src": 32,
                            "dest": 53,
                            "unknown1": 1,
                            "unknown2": 1,
                            "unknown4": 3,
                            "cmdFunc": 20,
                            "cmdId": 129,
                            "unknown5": 3,
                            "needAck": 1,
                            "timestamp": {
                              "low": 35061401,
                              "high": 0,
                              "unsigned": true
                            },
                            "unknown6": 19,
                            "unknown7": 1,
                            "OS": "ios",
                            "serialNumber": "ABCxxxxxxx123"
                          }
                        }
                        `
                        await createMyState('app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC')
                        on({ id: ConfigData.statesPrefix + '.app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC', change: "any", ack: false }, function (obj) {
                            setAC(Number(obj.state.val))
                            setState(obj.id, obj.state.val, true);
                        });
                        
                        function setAC(Value = 1000) {
                            const updatedMusterSetAC = JSON.parse(musterSetAC);
                            updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                            updatedMusterSetAC.item.meta.value = Value
                            updatedMusterSetAC.item.serialNumber = ConfigData.seriennummern[0].seriennummer
                            SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/set');
                        }
                        
                        function generateAndSyncSub(id, JElements, sub = false, preset = "0_userdata.0.test") {
                            if (!JElements || typeof JElements !== 'object') {
                                log('Ungültige JElements übergeben!');
                                return;
                            }
                            for (var JElement in JElements) {
                                var AktVal;
                                if (typeof JElements[JElement] === "object") {
                                    generateAndSyncSub(id + "." + JElement, JElements[JElement], true, preset);
                                } else {
                                    try {
                                        if (isState2(preset + "." + id + "." + JElement)) AktVal = getState(preset + "." + id + "." + JElement).val;
                                    } catch (e) {
                                        log("Fehler: " + e);
                                    }
                                    if (AktVal == null) {
                                        createState(preset + "." + id + "." + JElement, JElements[JElement], false);
                                        AktVal = JElements[JElement];
                                    }
                                    if (AktVal != JElements[JElement]) {
                                        if (isState2(preset + "." + id + "." + JElement)) setState(preset + "." + id + "." + JElement, JElements[JElement]);
                                    }
                                }
                            }
                        }
                        
                        
                        /**
                         * Checks if a a given state or part of state is existing.
                         * This is a workaround, as getObject() or getState() throw warnings in the log.
                         * Set strict to true if the state shall match exactly. If it is false, it will add a wildcard * to the end.
                         * See: https://forum.iobroker.net/topic/11354/
                         * @param {string}    strStatePath     Input string of state, like 'javascript.0.switches.Osram.Bedroom'
                         * @param {boolean}   [strict=false]   Optional: if true, it will work strict, if false, it will add a wildcard * to the end of the string
                         * @return {boolean}                   true if state exists, false if not
                         */
                        function isState2(strStatePath, strict = true) {
                            let mSelector;
                            if (strict) {
                                mSelector = $(strStatePath);
                            } else {
                                mSelector = $(strStatePath + "*");
                            }
                            if (mSelector.length > 0) {
                                return true;
                            } else {
                                return false;
                            }
                        }
                        
                        
                        // ########### Grundbedarf steuern
                        // Es müssen Historydaten für den Wert des Smartmeters vorhanden sein, damit der niedrigeste wert der letzten x Minuten ermittelt werden kann
                        
                        function getLowestValue(id, minuten = 120) {
                            return new Promise((resolve, reject) => {
                                const now = new Date().getTime();
                                const range = minuten * 60 * 1000;
                                var myresult = 0;
                                getHistory(0, {
                                    id: id,
                                    start: now - range,
                                    end: now,
                                    aggregate: 'minmax'
                                }, function (err, result) {
                                    if (err) {
                                        reject(err);
                                    } else if (result.length) {
                                        let lowestValue = result[0].val;
                                        for (let i = 1; i < result.length; i++) {
                                            if (result[i].val < lowestValue) {
                                                lowestValue = result[i].val;
                                            }
                                        }
                                        myresult = Number(lowestValue);
                                        resolve(myresult);
                                    } else {
                                        reject(new Error('No data'));
                                    }
                                });
                            });
                        }
                        
                        function SetBasePower() {
                            if (isState2(ConfigData.SmartmeterID)) {
                                getLowestValue(ConfigData.SmartmeterID, ConfigData.MinValueMin)
                                    .then(lowestValue => {
                                        if (lowestValue > 0) {
                                            if (ConfigData.Debug) log("Tiefster Wert der letzten " + ConfigData.MinValueMin + " Minuten: " + lowestValue);
                                            var AndereVerbraucher = 0
                                            const ToHomeId = ConfigData.statesPrefix + ".app_device_property_" + ConfigData.seriennummern[0].seriennummer + ".data.item.meta.ToHome_Power"
                                            if (isState2(ToHomeId)) {
                                                AndereVerbraucher = Number(getState(ToHomeId).val) / 10
                                                var NewValue = lowestValue + AndereVerbraucher - ConfigData.BasePowerOffset
                                                if (NewValue > ConfigData.MaxPower) NewValue = ConfigData.MaxPower
                                                if (ConfigData.Debug) {
                                                    log("AndereVerbraucher: " + AndereVerbraucher)
                                                    log("Macht : " + (lowestValue + AndereVerbraucher) + "W Grundbedarf")
                                                    log("- Offset von: " + ConfigData.BasePowerOffset + " W = " + (NewValue) + " W Grundbedarf")
                                                }
                                                setAC(NewValue * 10);
                        
                                            }
                                        }
                                    })
                                    .catch(error => {
                                        log("Fehler beim Abrufen des niedrigsten Werts: " + error);
                                    });
                            }
                        }
                        
                        
                        A Offline
                        A Offline
                        applepro
                        wrote on last edited by
                        #161

                        @waly_de Wow!!! Besten Dank von meiner Seite, ich wollte Ihn gerade testen, jedoch bekomme ich auch eine ähnliche Fehlermedlung:

                        08:59:38.144	error	javascript.0 (444) script.js.Solar.Abfrage_MQTT: Error: illegal token '{' (line 3)
                        08:59:38.145	error	javascript.0 (444) at decodeAndPrint (script.js.Solar.Abfrage_MQTT:314:27)
                        08:59:38.145	error	javascript.0 (444) at MqttClient.<anonymous> (script.js.Solar.Abfrage_MQTT:278:32)
                        

                        Vielen Dank schon einmal für deine Hilfe 🙂

                        Ich hatte mir in der Zwischenzeit alle States als HEX ausgelesen und mir so behelfsweise eine kurzes Skript geschrieben, welches mit Hilfe meines Powerfox Meters den jeweiligen Buffer sendet und so die Leistung anpasst, leider musste ich immer den aktuellen Einspeisewert fortschreiben, da ich diese ja nicht auslesen konnte.

                        W 1 Reply Last reply
                        0
                        • A applepro

                          @waly_de Wow!!! Besten Dank von meiner Seite, ich wollte Ihn gerade testen, jedoch bekomme ich auch eine ähnliche Fehlermedlung:

                          08:59:38.144	error	javascript.0 (444) script.js.Solar.Abfrage_MQTT: Error: illegal token '{' (line 3)
                          08:59:38.145	error	javascript.0 (444) at decodeAndPrint (script.js.Solar.Abfrage_MQTT:314:27)
                          08:59:38.145	error	javascript.0 (444) at MqttClient.<anonymous> (script.js.Solar.Abfrage_MQTT:278:32)
                          

                          Vielen Dank schon einmal für deine Hilfe 🙂

                          Ich hatte mir in der Zwischenzeit alle States als HEX ausgelesen und mir so behelfsweise eine kurzes Skript geschrieben, welches mit Hilfe meines Powerfox Meters den jeweiligen Buffer sendet und so die Leistung anpasst, leider musste ich immer den aktuellen Einspeisewert fortschreiben, da ich diese ja nicht auslesen konnte.

                          W Offline
                          W Offline
                          Waly_de
                          wrote on last edited by
                          #162

                          @applepro Bitte noch mal kopieren... Das Forum hat beim Einfügen in die protoSource lustige Eintrage gemacht... sollte jetzt klappen...

                          A 1 Reply Last reply
                          1
                          • W Waly_de

                            @applepro Bitte noch mal kopieren... Das Forum hat beim Einfügen in die protoSource lustige Eintrage gemacht... sollte jetzt klappen...

                            A Offline
                            A Offline
                            applepro
                            wrote on last edited by applepro
                            #163

                            @waly_de Läuft alles! Edit: Habe nun mein Skript mit deinen laufenden Daten gefühlt. setAC wird jede Minute neu berechnet und gesetzt. Endlich kann man auch in 1er Schritten und nicht nur in 10er Schritten die Leistung verstellen. Und endlich habe ich eine permanente 0-Einspeisung! 🙂

                            W 1 Reply Last reply
                            0
                            • A applepro

                              @waly_de Läuft alles! Edit: Habe nun mein Skript mit deinen laufenden Daten gefühlt. setAC wird jede Minute neu berechnet und gesetzt. Endlich kann man auch in 1er Schritten und nicht nur in 10er Schritten die Leistung verstellen. Und endlich habe ich eine permanente 0-Einspeisung! 🙂

                              W Offline
                              W Offline
                              Waly_de
                              wrote on last edited by Waly_de
                              #164

                              @applepro
                              Super das es klappt 😊 🎆

                              hier eine neue Version. Es gab noch ein Problem beim anlegen der States.
                              Und ich versuche hier ein Phänomen zu beseitigen: Der MQTT hört auf Nachrichten zu senden, wenn man die App auf dem Handy beendet... Bin noch nicht sicher ob es jetzt durchläuft...

                              /**
                               * ecoflow-iobroker-connector.js
                               * Version:      0.4    
                               * Release date: 29.06.2023
                               * Autor:        Waly_de 
                               * Forum:        https://forum.iobroker.net/topic/54929/adapter-für-ecoflow-einbindung/155
                               * 
                               *
                               * This JavaScript file establishes a simple connection between IOBroker and EcoFlow. 
                               * It automatically creates known states under 0_userdata.
                               * 
                               * Please note that adjustments in the ConfigData section are required. Here, you need to enter your access credentials 
                               * used for the EcoFlow app, as well as the serial numbers of your devices.
                               * 
                               * Important: The second serial number entry should always correspond to the Powerstream as it is associated with control functions.
                               * 
                               * If you have a state that displays the current power consumption (SmartmeterID), please provide it as well. 
                               * Configure a history for this state so that the script can determine the lowest power consumption in the last 10 minutes (configurable). 
                               * This value will be used to dynamically adjust the Powerstream's feed-in power.
                               * 
                               * Not all parameters of the Powerstream data are known yet. All known parameters will be automatically created as states.
                               * By modifying the "protoSource" constant, newly discovered data will also be automatically created.
                               * 
                               * The raw data of the interface is logged as a HEX string.
                               * 
                               * Please exercise caution as this is the initial version of the script. Use it at your own risk!
                               *
                               * Requirements:
                               * - Install protobuf using the command "npm install protobufjs" from the terminal console.
                               * - The "Paho MQTT Client" is also required. If not already installed, use the command "npm install mqtt".
                               *
                               * Note: It is encouraged to discover and publish missing data definitions to improve the script.
                               * Suggestions, optimizations, and extensions are welcome at any time.
                               *
                               * Special thanks to all contributors for their valuable input and support.
                               * 
                               * Changelog:
                               * -----------------------------------------------------------
                               * (0.4) 29.06.2023
                               * Da der MQTT von ecoflow regelmäßig aufhört zu senden, vor allem, wenn die App genutzt und komplett geschlossen wird, 
                               * habe ich eine Überwachung der letzten ankommenden Nachrichten eingebaut. Kommt 5 Minuten lang nichts neues vom PowerStream,
                               * Wird die Verbindung zum MQTT kompett neu aufgebaut.
                               * 
                               * Ein Fehler bei der Erstellung der States wurde beseitigt
                               * -----------------------------------------------------------
                               * 
                               */
                              
                              /*******  YOUR DATA HERE  ****************
                              var ConfigData = {
                                  email: "your@mail.com",
                                  passwort: "yourAppPasswort!",
                                  seriennummern: [
                                      { seriennummer: "XXXXXXXXXXXXX", name: "PowerStream" }, //1. has to be your PowerStream, you can add more devices...
                                      { seriennummer: "XXXXXXXXXXXXX", name: "DELTA Max" }
                                  ],
                                  SmartmeterID: "sonoff.0.Stromzaehler1.MT175_P",     //State, das den aktuellen Gesamtverbrauch in Watt enthält
                                  BasePowerOffset: 50,                                //wird vom aktuellen Verbrauch abgezogen um die Einspeiseleistung zu berechnen 
                                  MaxPower: 600,                                      //Der höchst mögliche wert in Watt für die Einspeiseleistung
                                  MinValueMin: 10,                                    //Der Zeitraum in Minuten, aus dem der letzte Gesamtverbrauchs-Minimalwert geholt werden soll 
                                  ReconnectMin: 30,                                   //Zeit in Minuten, nach der die Anwendung neu gestartet wird, wenn keine neuen Daten eintreffen
                                  statesPrefix: "0_userdata.0.ecoflow",
                                  Debug: true
                              };
                              //***************************************/
                              
                              const protoSource = `
                              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 Bat_Minutes = 60;
                              }
                              enum CmdFunction {
                                  Unknown = 0;
                                  PermanentWattsPack = 129;
                                  SupplyPriorityPack = 130;
                              }
                              `;
                              
                              const mqtt = require('mqtt');
                              const https = require('https');
                              const protobuf = require("protobufjs");
                              
                              const mqttDaten = {
                                  UserID: '',
                                  User: '',
                                  Passwort: '',
                                  URL: '',
                                  Port: '',
                                  protocol: '',
                                  clientID: ''
                              }
                              
                              /*=======================================================
                                =========             Timer               ============
                                =======================================================*/
                              //alle 5 minuten
                              schedule('*/1 * * * *', function () {
                                  CheckforReconnect(function () {
                                      SetBasePower();
                                  });
                              });
                              
                              await getEcoFlowMqttData(ConfigData.email, ConfigData.passwort)
                              async function getEcoFlowMqttData(email, password) {
                                  const options = {
                                      hostname: 'api.ecoflow.com',
                                      path: '/auth/login',
                                      method: 'POST',
                                      headers: {
                                          'Host': 'api.ecoflow.com',
                                          'lang': 'de-de',
                                          'platform': 'android',
                                          'sysversion': '11',
                                          'version': '4.1.2.02',
                                          'phonemodel': 'SM-X200',
                                          'content-type': 'application/json',
                                          'user-agent': 'okhttp/3.14.9'
                                      }
                                  };
                              
                                  const data = {
                                      appVersion: "4.1.2.02",
                                      email: email,
                                      os: "android",
                                      osVersion: "30",
                                      password: Buffer.from(password).toString('base64'),
                                      scene: "IOT_APP",
                                      userType: "ECOFLOW"
                                  };
                              
                                  function httpsRequest(options, data) {
                                      return new Promise((resolve, reject) => {
                                          const req = https.request(options, res => {
                                              let data = '';
                                              res.on('data', chunk => {
                                                  data += chunk;
                                              });
                                              res.on('end', () => {
                                                  resolve(data);
                                              });
                                          });
                              
                                          req.on('error', error => {
                                              reject(error);
                                          });
                              
                                          if (data) {
                                              req.write(JSON.stringify(data));
                                          }
                              
                                          req.end();
                                      });
                                  }
                              
                                  function uuidv4() {
                                      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                                          var r = Math.random() * 16 | 0,
                                              v = c === 'x' ? r : (r & 0x3 | 0x8);
                                          return v.toString(16);
                                      });
                                  }
                              
                                  let response = await httpsRequest(options, data);
                                  try {
                                      let token = JSON.parse(response).data.token;
                                      let userid = JSON.parse(response).data.user.userId;
                                  } catch (error) {
                                      log(response)
                                      throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                                  }
                              
                                  let token = JSON.parse(response).data.token;
                                  let userid = JSON.parse(response).data.user.userId;
                              
                                  options.path = `/iot-auth/app/certification?userId=${userid}`;
                                  options.method = 'GET';
                                  options.headers.authorization = `Bearer ${token}`;
                                  response = await httpsRequest(options);
                                  try {
                                      mqttDaten.Passwort = JSON.parse(response).data.certificatePassword
                                      mqttDaten.Port = JSON.parse(response).data.port
                                      mqttDaten.UserID = userid
                                      mqttDaten.User = JSON.parse(response).data.certificateAccount
                                      mqttDaten.URL = JSON.parse(response).data.url
                                      mqttDaten.protocol = JSON.parse(response).data.protocol
                                      mqttDaten.clientID = "ANDROID_" + uuidv4() + "_" + userid
                                  } catch (error) {
                                      log(response)
                                      throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                                  }
                                  /*    
                                      console.log("UserID: " + userid);
                                      console.log("User: " + JSON.parse(response).data.certificateAccount);
                                      console.log("Passwort: " + JSON.parse(response).data.certificatePassword);
                                      console.log("URL: " + JSON.parse(response).data.url);
                                      console.log("Port: " + JSON.parse(response).data.port);
                                      console.log("protocol: " + JSON.parse(response).data.protocol);
                                      console.log("clientID: ANDROID_" + uuidv4() + "_" + userid);
                                  */
                              
                              }
                              
                              await createMyState("LastTopic")
                              
                              //################ MQTT Verbindung ##################
                              function setupMQTTConnection() {
                                  // Verbindung herstellen
                                  const options = {
                                      port: mqttDaten.Port,
                                      clientId: mqttDaten.clientID,
                                      username: mqttDaten.User,
                                      password: mqttDaten.Passwort,
                                      protocol: mqttDaten.protocol
                                  };
                              
                                  const client = mqtt.connect("mqtt://" + mqttDaten.URL, options);
                              
                                  // Event-Handler für Verbindungsaufbau
                                  client.on('connect', function () {
                                      console.log('Verbunden mit dem Ecoflow MQTT-Broker');
                                      SubscribeEco();
                                  });
                              
                                  // Event-Handler für Fehler
                                  client.on('error', function (error) {
                                      console.log('Fehler bei der Ecoflow MQTT-Verbindung: ' + error);
                                  });
                              
                                  function SubscribeEco() {
                                      ConfigData.seriennummern.forEach(item => {
                                          client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/set');
                                          client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/get');
                                          client.subscribe('/app/device/property/' + item.seriennummer);
                                          setmusterGetPS();
                                      });
                                  }
                              
                                  // Auf Nachricht empfangen Ereignis reagieren
                                  client.on('message', async function (topic, message) {
                                      var jsonMessage = ""
                                      const mqState = topic.replace(/^\//, '').replace(/\//g, '_')
                                      await createMyState(mqState + ".RAW")
                                      setState(ConfigData.statesPrefix + ".LastTopic", topic)
                                      try {
                                          jsonMessage = JSON.parse(message);
                                          if (ConfigData.Debug) log('JSON-Nachricht empfangen:' + topic + ':' + JSON.stringify(jsonMessage));
                                          setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", JSON.stringify(jsonMessage))
                                          generateAndSyncSub("data", jsonMessage, false, ConfigData.statesPrefix + '.' + mqState)
                                      } catch (error) {
                                          if (ConfigData.Debug) log('Binäre Nachricht empfangen:' + topic + ':' + message.toString('hex'));
                                          await createMyState(mqState + ".RAW_HEX")
                                          setState(ConfigData.statesPrefix + '.' + mqState + ".RAW_HEX", message.toString('hex'))
                                          //log('Decodierte Nachricht:' + decodeAndPrint(message.toString('hex')))
                                          const messagedecoded = decodeAndPrint(message.toString('hex'))
                                          setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", messagedecoded)
                                          generateAndSyncSub("data", JSON.parse(messagedecoded), false, ConfigData.statesPrefix + '.' + mqState)
                                      }
                                  });
                              
                                  // Callback für Fehler
                                  client.on('error', function (error) {
                                      log('Fehler bei der Ecoflow MQTT-Verbindung:' + error, 'warn');
                                  });
                              
                                  client.on('reconnect', function () {
                                      console.log('Reconnecting to Ecoflow MQTT broker...');
                                  });
                                  // Weitere Event-Handler hier...
                                  return client;
                              }
                              
                              // Verbindung herstellen
                              let client = setupMQTTConnection();
                              
                              // Funktion zum Trennen und Neuaufbau der Verbindung
                              function reconnect() {
                                  client.end(); // Verbindung trennen
                                  setTimeout(function () {
                                      client = setupMQTTConnection(); // Neue Verbindung herstellen
                                      //log("Ecoflow neuverbindung");
                                  }, 2000); // Wartezeit
                              }
                              
                              // close connection if script stopped
                              onStop(function (callback) {
                                  if (client) {
                                      // close connection
                                      client.end();
                                      log("Ecoflow MQTT-Client beendet")
                                  }
                                  callback();
                              }, 2000);
                              
                              function CheckforReconnect(callback) {
                                  if (getState(ConfigData.statesPrefix + ".LastTopic")?.ts < Date.now() - ConfigData.ReconnectMin * 60 * 1000) {
                                      console.log("Der letzte Eintrag ist älter als " + ConfigData.ReconnectMin + " Minuten. Versuche Neustart.");
                                      setState(ConfigData.statesPrefix + ".LastTopic", "Last Action Restart:" + new Date().toLocaleString())
                                      runScript();
                                      return;
                                      // Wenn letzte Powerstream-Meldung älter als 5 min ist, schicke Lebenszeichen 
                                  } else if (getState(ConfigData.statesPrefix + '.app_device_property_' + ConfigData.seriennummern[0].seriennummer + '.RAW_HEX')?.ts < Date.now() - 5 * 60 * 1000) {
                                      log("Reconnect zu Ecoflow MQTT für PowerStream - Daten")
                                      //setmusterGetPS()
                                      //.ts Updaten
                                      const oldvalue = getState(ConfigData.statesPrefix + '.app_device_property_' + ConfigData.seriennummern[0].seriennummer + '.RAW_HEX').val
                                      setState(ConfigData.statesPrefix + '.app_device_property_' + ConfigData.seriennummern[0].seriennummer + '.RAW_HEX', oldvalue)
                                      reconnect();
                                      return;
                                      //runScript();
                                  } else {
                                      callback();
                                  }
                              }
                              
                              //*/
                              
                              async function createMyState(name) {
                                  if (!(await existsObjectAsync(ConfigData.statesPrefix + '.' + name))) {
                                      await createStateAsync(ConfigData.statesPrefix + '.' + name, {
                                          name: name.split('.').pop(),
                                          role: 'state',
                                          type: 'string',
                                          read: true,
                                          write: true
                                      });
                                  }
                              }
                              
                              function decodeAndPrint(hexString) {
                                  const root = protobuf.parse(protoSource).root;
                                  const PowerMessage = root.lookupType("PowerMessage");
                                  const message = PowerMessage.decode(Buffer.from(hexString, "hex"));
                                  const object = PowerMessage.toObject(message, { defaults: false });
                                  return JSON.stringify(object);
                              }
                              
                              function SendProto(protomsg, topic) {
                                  const root = protobuf.parse(protoSource).root;
                                  const PowerMessage = root.lookupType("PowerMessage");
                                  const message = PowerMessage.create(JSON.parse(protomsg));
                                  const messageBuffer = PowerMessage.encode(message).finish();
                                  //log("Modifizierter Hex-String:" +  Buffer.from(messageBuffer).toString("hex"));
                                  //log("topic:" +  topic);
                                  client.publish(topic, messageBuffer, { qos: 1 }, function (error) {
                                      if (error) {
                                          console.error('Fehler beim Veröffentlichen der MQTT-Nachricht:', error);
                                      } else {
                                          if (ConfigData.Debug) log('Die MQTT-Nachricht wurde erfolgreich veröffentlicht.');
                                      }
                                  });
                              }
                              
                              //############ Funktionen zum Setzen von Werten
                              
                              const musterSetAC = `
                              {
                                "item": {
                                  "meta": {
                                    "value": 1300
                                  },
                                  "src": 32,
                                  "dest": 53,
                                  "unknown1": 1,
                                  "unknown2": 1,
                                  "unknown4": 3,
                                  "cmdFunc": 20,
                                  "cmdId": 129,
                                  "unknown5": 3,
                                  "needAck": 1,
                                  "timestamp": {
                                    "low": 35061401,
                                    "high": 0,
                                    "unsigned": true
                                  },
                                  "unknown6": 19,
                                  "unknown7": 1,
                                  "OS": "ios",
                                  "serialNumber": "ABCxxxxxxx123"
                                }
                              }
                              `
                              const musterSetAC2 = `
                              {
                                "item": {
                                  "meta": {
                                    "value": 17477
                                  },
                                  "src": 32,
                                  "dest": 53,
                                  "unknown1": 1,
                                  "unknown2": 1,
                                  "unknown4": 3,
                                  "cmdFunc": 32,
                                  "cmdId": 11,
                                  "unknown5": 4,
                                  "needAck": 1,
                                  "timestamp": {
                                    "low": 105187935,
                                    "high": 0,
                                    "unsigned": true
                                  },
                                  "unknown6": 19,
                                  "unknown7": 1,
                                  "OS": "ios",
                                  "serialNumber": "ABCxxxxxxx123"
                                }
                              }
                              `
                              const musterGetPS = `
                              {
                                "item": {
                                  "src": 32,
                                  "dest": 32,
                                  "timestamp": {
                                    "low": 95816611,
                                    "high": 0,
                                    "unsigned": true
                                  },
                                  "OS": "ios"
                                }
                              }
                              `
                              await createMyState('app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC')
                              on({ id: ConfigData.statesPrefix + '.app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC', change: "any", ack: false }, function (obj) {
                                  setAC(Number(obj.state.val))
                                  setState(obj.id, obj.state.val, true);
                              });
                              
                              // Einstellen der Einspeiseleistung
                              function setAC(Value = 1000) {
                                  const updatedMusterSetAC = JSON.parse(musterSetAC);
                                  updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                                  updatedMusterSetAC.item.meta.value = Value
                                  updatedMusterSetAC.item.serialNumber = ConfigData.seriennummern[0].seriennummer
                                  SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/set');
                              }
                              
                              //Anmeldenachrichten der APP
                              function setmusterGetPS() {
                                  var updatedMusterSetAC = JSON.parse(musterGetPS);
                                  updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                                  SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/get');
                              
                                  updatedMusterSetAC = JSON.parse(musterSetAC2);
                                  updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                                  updatedMusterSetAC.item.serialNumber = ConfigData.seriennummern[0].seriennummer
                                  SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/set');
                              }
                              
                              
                              function generateAndSyncSub(id, JElements, sub = false, preset = "0_userdata.0") {
                                  if (!JElements || typeof JElements !== 'object') {
                                      log('Ungültige JElements übergeben!');
                                      return;
                                  }
                                  for (var JElement in JElements) {
                                      var AktVal;
                                      if (typeof JElements[JElement] === "object") {
                                          generateAndSyncSub(id + "." + JElement, JElements[JElement], true, preset);
                                      } else {
                                          try {
                                              if (isState2(preset + "." + id + "." + JElement)) AktVal = getState(preset + "." + id + "." + JElement).val; else AktVal = null
                                          } catch (e) {
                                              log("Fehler: " + e);
                                          }
                                          if (AktVal == null) {
                                              createState(preset + "." + id + "." + JElement, JElements[JElement], false);
                                              AktVal = JElements[JElement];
                                          }
                                          if (AktVal != JElements[JElement]) {
                                              if (isState2(preset + "." + id + "." + JElement)) setState(preset + "." + id + "." + JElement, JElements[JElement]);
                                          }
                                      }
                                  }
                              }
                              
                              /**
                               * Checks if a a given state or part of state is existing.
                               * This is a workaround, as getObject() or getState() throw warnings in the log.
                               * Set strict to true if the state shall match exactly. If it is false, it will add a wildcard * to the end.
                               * See: https://forum.iobroker.net/topic/11354/
                               * @param {string}    strStatePath     Input string of state, like 'javascript.0.switches.Osram.Bedroom'
                               * @param {boolean}   [strict=false]   Optional: if true, it will work strict, if false, it will add a wildcard * to the end of the string
                               * @return {boolean}                   true if state exists, false if not
                               */
                              function isState2(strStatePath, strict = true) {
                                  let mSelector;
                                  if (strict) {
                                      mSelector = $(strStatePath);
                                  } else {
                                      mSelector = $(strStatePath + "*");
                                  }
                                  if (mSelector.length > 0) {
                                      return true;
                                  } else {
                                      return false;
                                  }
                              }
                              
                              
                              // ########### Grundbedarf steuern
                              // Es müssen Historydaten für den Wert des Smartmeters vorhanden sein, damit der niedrigeste wert der letzten x Minuten ermittelt werden kann
                              
                              function getLowestValue(id, minuten = 120) {
                                  return new Promise((resolve, reject) => {
                                      const now = new Date().getTime();
                                      const range = minuten * 60 * 1000;
                                      var myresult = 0;
                                      getHistory(0, {
                                          id: id,
                                          start: now - range,
                                          end: now,
                                          aggregate: 'minmax'
                                      }, function (err, result) {
                                          if (err) {
                                              reject(err);
                                          } else if (result.length) {
                                              let lowestValue = result[0].val;
                                              for (let i = 1; i < result.length; i++) {
                                                  if (result[i].val < lowestValue) {
                                                      lowestValue = result[i].val;
                                                  }
                                              }
                                              myresult = Number(lowestValue);
                                              resolve(myresult);
                                          } else {
                                              reject(new Error('No data'));
                                          }
                                      });
                                  });
                              }
                              
                              var OldNewValue = 0
                              function SetBasePower() {
                                  if (isState2(ConfigData.SmartmeterID)) {
                                      getLowestValue(ConfigData.SmartmeterID, ConfigData.MinValueMin)
                                          .then(lowestValue => {
                                              if (lowestValue > 0) {
                                                  if (ConfigData.Debug) log("Tiefster Wert der letzten " + ConfigData.MinValueMin + " Minuten: " + lowestValue);
                                                  var AndereVerbraucher = 0
                                                  const ToHomeId = ConfigData.statesPrefix + ".app_device_property_" + ConfigData.seriennummern[0].seriennummer + ".data.item.meta.ToHome_Power"
                                                  if (isState2(ToHomeId)) {
                                                      AndereVerbraucher = Number(getState(ToHomeId).val) / 10
                                                      var NewValue = lowestValue + AndereVerbraucher - ConfigData.BasePowerOffset
                                                      if (NewValue > ConfigData.MaxPower) NewValue = ConfigData.MaxPower
                                                      if (ConfigData.Debug) {
                                                          log("AndereVerbraucher: " + AndereVerbraucher)
                                                          log("Macht : " + (lowestValue + AndereVerbraucher) + "W Grundbedarf")
                                                          log("- Offset von: " + ConfigData.BasePowerOffset + " W = " + (NewValue) + " W Grundbedarf")
                                                      }
                                                      if (OldNewValue != NewValue) setAC(NewValue * 10)
                                                      OldNewValue = NewValue
                                                  }
                                              }
                                          })
                                          .catch(error => {
                                              log("Fehler beim Abrufen des niedrigsten Werts: " + error);
                                          });
                                  }
                              }
                              
                              
                              
                              A M 2 Replies Last reply
                              1
                              • T Offline
                                T Offline
                                TDahinten
                                wrote on last edited by
                                #165

                                @waly_de

                                also nochmal herzlichen Dank 👏
                                Soweit läuft bei mir Script Version 0.2. Die 0.3 Version habe ich aktuell noch nicht eingebunden.
                                Bin echt happy.

                                1 Reply Last reply
                                0
                                • W Waly_de

                                  @applepro
                                  Super das es klappt 😊 🎆

                                  hier eine neue Version. Es gab noch ein Problem beim anlegen der States.
                                  Und ich versuche hier ein Phänomen zu beseitigen: Der MQTT hört auf Nachrichten zu senden, wenn man die App auf dem Handy beendet... Bin noch nicht sicher ob es jetzt durchläuft...

                                  /**
                                   * ecoflow-iobroker-connector.js
                                   * Version:      0.4    
                                   * Release date: 29.06.2023
                                   * Autor:        Waly_de 
                                   * Forum:        https://forum.iobroker.net/topic/54929/adapter-für-ecoflow-einbindung/155
                                   * 
                                   *
                                   * This JavaScript file establishes a simple connection between IOBroker and EcoFlow. 
                                   * It automatically creates known states under 0_userdata.
                                   * 
                                   * Please note that adjustments in the ConfigData section are required. Here, you need to enter your access credentials 
                                   * used for the EcoFlow app, as well as the serial numbers of your devices.
                                   * 
                                   * Important: The second serial number entry should always correspond to the Powerstream as it is associated with control functions.
                                   * 
                                   * If you have a state that displays the current power consumption (SmartmeterID), please provide it as well. 
                                   * Configure a history for this state so that the script can determine the lowest power consumption in the last 10 minutes (configurable). 
                                   * This value will be used to dynamically adjust the Powerstream's feed-in power.
                                   * 
                                   * Not all parameters of the Powerstream data are known yet. All known parameters will be automatically created as states.
                                   * By modifying the "protoSource" constant, newly discovered data will also be automatically created.
                                   * 
                                   * The raw data of the interface is logged as a HEX string.
                                   * 
                                   * Please exercise caution as this is the initial version of the script. Use it at your own risk!
                                   *
                                   * Requirements:
                                   * - Install protobuf using the command "npm install protobufjs" from the terminal console.
                                   * - The "Paho MQTT Client" is also required. If not already installed, use the command "npm install mqtt".
                                   *
                                   * Note: It is encouraged to discover and publish missing data definitions to improve the script.
                                   * Suggestions, optimizations, and extensions are welcome at any time.
                                   *
                                   * Special thanks to all contributors for their valuable input and support.
                                   * 
                                   * Changelog:
                                   * -----------------------------------------------------------
                                   * (0.4) 29.06.2023
                                   * Da der MQTT von ecoflow regelmäßig aufhört zu senden, vor allem, wenn die App genutzt und komplett geschlossen wird, 
                                   * habe ich eine Überwachung der letzten ankommenden Nachrichten eingebaut. Kommt 5 Minuten lang nichts neues vom PowerStream,
                                   * Wird die Verbindung zum MQTT kompett neu aufgebaut.
                                   * 
                                   * Ein Fehler bei der Erstellung der States wurde beseitigt
                                   * -----------------------------------------------------------
                                   * 
                                   */
                                  
                                  /*******  YOUR DATA HERE  ****************
                                  var ConfigData = {
                                      email: "your@mail.com",
                                      passwort: "yourAppPasswort!",
                                      seriennummern: [
                                          { seriennummer: "XXXXXXXXXXXXX", name: "PowerStream" }, //1. has to be your PowerStream, you can add more devices...
                                          { seriennummer: "XXXXXXXXXXXXX", name: "DELTA Max" }
                                      ],
                                      SmartmeterID: "sonoff.0.Stromzaehler1.MT175_P",     //State, das den aktuellen Gesamtverbrauch in Watt enthält
                                      BasePowerOffset: 50,                                //wird vom aktuellen Verbrauch abgezogen um die Einspeiseleistung zu berechnen 
                                      MaxPower: 600,                                      //Der höchst mögliche wert in Watt für die Einspeiseleistung
                                      MinValueMin: 10,                                    //Der Zeitraum in Minuten, aus dem der letzte Gesamtverbrauchs-Minimalwert geholt werden soll 
                                      ReconnectMin: 30,                                   //Zeit in Minuten, nach der die Anwendung neu gestartet wird, wenn keine neuen Daten eintreffen
                                      statesPrefix: "0_userdata.0.ecoflow",
                                      Debug: true
                                  };
                                  //***************************************/
                                  
                                  const protoSource = `
                                  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 Bat_Minutes = 60;
                                  }
                                  enum CmdFunction {
                                      Unknown = 0;
                                      PermanentWattsPack = 129;
                                      SupplyPriorityPack = 130;
                                  }
                                  `;
                                  
                                  const mqtt = require('mqtt');
                                  const https = require('https');
                                  const protobuf = require("protobufjs");
                                  
                                  const mqttDaten = {
                                      UserID: '',
                                      User: '',
                                      Passwort: '',
                                      URL: '',
                                      Port: '',
                                      protocol: '',
                                      clientID: ''
                                  }
                                  
                                  /*=======================================================
                                    =========             Timer               ============
                                    =======================================================*/
                                  //alle 5 minuten
                                  schedule('*/1 * * * *', function () {
                                      CheckforReconnect(function () {
                                          SetBasePower();
                                      });
                                  });
                                  
                                  await getEcoFlowMqttData(ConfigData.email, ConfigData.passwort)
                                  async function getEcoFlowMqttData(email, password) {
                                      const options = {
                                          hostname: 'api.ecoflow.com',
                                          path: '/auth/login',
                                          method: 'POST',
                                          headers: {
                                              'Host': 'api.ecoflow.com',
                                              'lang': 'de-de',
                                              'platform': 'android',
                                              'sysversion': '11',
                                              'version': '4.1.2.02',
                                              'phonemodel': 'SM-X200',
                                              'content-type': 'application/json',
                                              'user-agent': 'okhttp/3.14.9'
                                          }
                                      };
                                  
                                      const data = {
                                          appVersion: "4.1.2.02",
                                          email: email,
                                          os: "android",
                                          osVersion: "30",
                                          password: Buffer.from(password).toString('base64'),
                                          scene: "IOT_APP",
                                          userType: "ECOFLOW"
                                      };
                                  
                                      function httpsRequest(options, data) {
                                          return new Promise((resolve, reject) => {
                                              const req = https.request(options, res => {
                                                  let data = '';
                                                  res.on('data', chunk => {
                                                      data += chunk;
                                                  });
                                                  res.on('end', () => {
                                                      resolve(data);
                                                  });
                                              });
                                  
                                              req.on('error', error => {
                                                  reject(error);
                                              });
                                  
                                              if (data) {
                                                  req.write(JSON.stringify(data));
                                              }
                                  
                                              req.end();
                                          });
                                      }
                                  
                                      function uuidv4() {
                                          return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                                              var r = Math.random() * 16 | 0,
                                                  v = c === 'x' ? r : (r & 0x3 | 0x8);
                                              return v.toString(16);
                                          });
                                      }
                                  
                                      let response = await httpsRequest(options, data);
                                      try {
                                          let token = JSON.parse(response).data.token;
                                          let userid = JSON.parse(response).data.user.userId;
                                      } catch (error) {
                                          log(response)
                                          throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                                      }
                                  
                                      let token = JSON.parse(response).data.token;
                                      let userid = JSON.parse(response).data.user.userId;
                                  
                                      options.path = `/iot-auth/app/certification?userId=${userid}`;
                                      options.method = 'GET';
                                      options.headers.authorization = `Bearer ${token}`;
                                      response = await httpsRequest(options);
                                      try {
                                          mqttDaten.Passwort = JSON.parse(response).data.certificatePassword
                                          mqttDaten.Port = JSON.parse(response).data.port
                                          mqttDaten.UserID = userid
                                          mqttDaten.User = JSON.parse(response).data.certificateAccount
                                          mqttDaten.URL = JSON.parse(response).data.url
                                          mqttDaten.protocol = JSON.parse(response).data.protocol
                                          mqttDaten.clientID = "ANDROID_" + uuidv4() + "_" + userid
                                      } catch (error) {
                                          log(response)
                                          throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                                      }
                                      /*    
                                          console.log("UserID: " + userid);
                                          console.log("User: " + JSON.parse(response).data.certificateAccount);
                                          console.log("Passwort: " + JSON.parse(response).data.certificatePassword);
                                          console.log("URL: " + JSON.parse(response).data.url);
                                          console.log("Port: " + JSON.parse(response).data.port);
                                          console.log("protocol: " + JSON.parse(response).data.protocol);
                                          console.log("clientID: ANDROID_" + uuidv4() + "_" + userid);
                                      */
                                  
                                  }
                                  
                                  await createMyState("LastTopic")
                                  
                                  //################ MQTT Verbindung ##################
                                  function setupMQTTConnection() {
                                      // Verbindung herstellen
                                      const options = {
                                          port: mqttDaten.Port,
                                          clientId: mqttDaten.clientID,
                                          username: mqttDaten.User,
                                          password: mqttDaten.Passwort,
                                          protocol: mqttDaten.protocol
                                      };
                                  
                                      const client = mqtt.connect("mqtt://" + mqttDaten.URL, options);
                                  
                                      // Event-Handler für Verbindungsaufbau
                                      client.on('connect', function () {
                                          console.log('Verbunden mit dem Ecoflow MQTT-Broker');
                                          SubscribeEco();
                                      });
                                  
                                      // Event-Handler für Fehler
                                      client.on('error', function (error) {
                                          console.log('Fehler bei der Ecoflow MQTT-Verbindung: ' + error);
                                      });
                                  
                                      function SubscribeEco() {
                                          ConfigData.seriennummern.forEach(item => {
                                              client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/set');
                                              client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/get');
                                              client.subscribe('/app/device/property/' + item.seriennummer);
                                              setmusterGetPS();
                                          });
                                      }
                                  
                                      // Auf Nachricht empfangen Ereignis reagieren
                                      client.on('message', async function (topic, message) {
                                          var jsonMessage = ""
                                          const mqState = topic.replace(/^\//, '').replace(/\//g, '_')
                                          await createMyState(mqState + ".RAW")
                                          setState(ConfigData.statesPrefix + ".LastTopic", topic)
                                          try {
                                              jsonMessage = JSON.parse(message);
                                              if (ConfigData.Debug) log('JSON-Nachricht empfangen:' + topic + ':' + JSON.stringify(jsonMessage));
                                              setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", JSON.stringify(jsonMessage))
                                              generateAndSyncSub("data", jsonMessage, false, ConfigData.statesPrefix + '.' + mqState)
                                          } catch (error) {
                                              if (ConfigData.Debug) log('Binäre Nachricht empfangen:' + topic + ':' + message.toString('hex'));
                                              await createMyState(mqState + ".RAW_HEX")
                                              setState(ConfigData.statesPrefix + '.' + mqState + ".RAW_HEX", message.toString('hex'))
                                              //log('Decodierte Nachricht:' + decodeAndPrint(message.toString('hex')))
                                              const messagedecoded = decodeAndPrint(message.toString('hex'))
                                              setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", messagedecoded)
                                              generateAndSyncSub("data", JSON.parse(messagedecoded), false, ConfigData.statesPrefix + '.' + mqState)
                                          }
                                      });
                                  
                                      // Callback für Fehler
                                      client.on('error', function (error) {
                                          log('Fehler bei der Ecoflow MQTT-Verbindung:' + error, 'warn');
                                      });
                                  
                                      client.on('reconnect', function () {
                                          console.log('Reconnecting to Ecoflow MQTT broker...');
                                      });
                                      // Weitere Event-Handler hier...
                                      return client;
                                  }
                                  
                                  // Verbindung herstellen
                                  let client = setupMQTTConnection();
                                  
                                  // Funktion zum Trennen und Neuaufbau der Verbindung
                                  function reconnect() {
                                      client.end(); // Verbindung trennen
                                      setTimeout(function () {
                                          client = setupMQTTConnection(); // Neue Verbindung herstellen
                                          //log("Ecoflow neuverbindung");
                                      }, 2000); // Wartezeit
                                  }
                                  
                                  // close connection if script stopped
                                  onStop(function (callback) {
                                      if (client) {
                                          // close connection
                                          client.end();
                                          log("Ecoflow MQTT-Client beendet")
                                      }
                                      callback();
                                  }, 2000);
                                  
                                  function CheckforReconnect(callback) {
                                      if (getState(ConfigData.statesPrefix + ".LastTopic")?.ts < Date.now() - ConfigData.ReconnectMin * 60 * 1000) {
                                          console.log("Der letzte Eintrag ist älter als " + ConfigData.ReconnectMin + " Minuten. Versuche Neustart.");
                                          setState(ConfigData.statesPrefix + ".LastTopic", "Last Action Restart:" + new Date().toLocaleString())
                                          runScript();
                                          return;
                                          // Wenn letzte Powerstream-Meldung älter als 5 min ist, schicke Lebenszeichen 
                                      } else if (getState(ConfigData.statesPrefix + '.app_device_property_' + ConfigData.seriennummern[0].seriennummer + '.RAW_HEX')?.ts < Date.now() - 5 * 60 * 1000) {
                                          log("Reconnect zu Ecoflow MQTT für PowerStream - Daten")
                                          //setmusterGetPS()
                                          //.ts Updaten
                                          const oldvalue = getState(ConfigData.statesPrefix + '.app_device_property_' + ConfigData.seriennummern[0].seriennummer + '.RAW_HEX').val
                                          setState(ConfigData.statesPrefix + '.app_device_property_' + ConfigData.seriennummern[0].seriennummer + '.RAW_HEX', oldvalue)
                                          reconnect();
                                          return;
                                          //runScript();
                                      } else {
                                          callback();
                                      }
                                  }
                                  
                                  //*/
                                  
                                  async function createMyState(name) {
                                      if (!(await existsObjectAsync(ConfigData.statesPrefix + '.' + name))) {
                                          await createStateAsync(ConfigData.statesPrefix + '.' + name, {
                                              name: name.split('.').pop(),
                                              role: 'state',
                                              type: 'string',
                                              read: true,
                                              write: true
                                          });
                                      }
                                  }
                                  
                                  function decodeAndPrint(hexString) {
                                      const root = protobuf.parse(protoSource).root;
                                      const PowerMessage = root.lookupType("PowerMessage");
                                      const message = PowerMessage.decode(Buffer.from(hexString, "hex"));
                                      const object = PowerMessage.toObject(message, { defaults: false });
                                      return JSON.stringify(object);
                                  }
                                  
                                  function SendProto(protomsg, topic) {
                                      const root = protobuf.parse(protoSource).root;
                                      const PowerMessage = root.lookupType("PowerMessage");
                                      const message = PowerMessage.create(JSON.parse(protomsg));
                                      const messageBuffer = PowerMessage.encode(message).finish();
                                      //log("Modifizierter Hex-String:" +  Buffer.from(messageBuffer).toString("hex"));
                                      //log("topic:" +  topic);
                                      client.publish(topic, messageBuffer, { qos: 1 }, function (error) {
                                          if (error) {
                                              console.error('Fehler beim Veröffentlichen der MQTT-Nachricht:', error);
                                          } else {
                                              if (ConfigData.Debug) log('Die MQTT-Nachricht wurde erfolgreich veröffentlicht.');
                                          }
                                      });
                                  }
                                  
                                  //############ Funktionen zum Setzen von Werten
                                  
                                  const musterSetAC = `
                                  {
                                    "item": {
                                      "meta": {
                                        "value": 1300
                                      },
                                      "src": 32,
                                      "dest": 53,
                                      "unknown1": 1,
                                      "unknown2": 1,
                                      "unknown4": 3,
                                      "cmdFunc": 20,
                                      "cmdId": 129,
                                      "unknown5": 3,
                                      "needAck": 1,
                                      "timestamp": {
                                        "low": 35061401,
                                        "high": 0,
                                        "unsigned": true
                                      },
                                      "unknown6": 19,
                                      "unknown7": 1,
                                      "OS": "ios",
                                      "serialNumber": "ABCxxxxxxx123"
                                    }
                                  }
                                  `
                                  const musterSetAC2 = `
                                  {
                                    "item": {
                                      "meta": {
                                        "value": 17477
                                      },
                                      "src": 32,
                                      "dest": 53,
                                      "unknown1": 1,
                                      "unknown2": 1,
                                      "unknown4": 3,
                                      "cmdFunc": 32,
                                      "cmdId": 11,
                                      "unknown5": 4,
                                      "needAck": 1,
                                      "timestamp": {
                                        "low": 105187935,
                                        "high": 0,
                                        "unsigned": true
                                      },
                                      "unknown6": 19,
                                      "unknown7": 1,
                                      "OS": "ios",
                                      "serialNumber": "ABCxxxxxxx123"
                                    }
                                  }
                                  `
                                  const musterGetPS = `
                                  {
                                    "item": {
                                      "src": 32,
                                      "dest": 32,
                                      "timestamp": {
                                        "low": 95816611,
                                        "high": 0,
                                        "unsigned": true
                                      },
                                      "OS": "ios"
                                    }
                                  }
                                  `
                                  await createMyState('app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC')
                                  on({ id: ConfigData.statesPrefix + '.app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC', change: "any", ack: false }, function (obj) {
                                      setAC(Number(obj.state.val))
                                      setState(obj.id, obj.state.val, true);
                                  });
                                  
                                  // Einstellen der Einspeiseleistung
                                  function setAC(Value = 1000) {
                                      const updatedMusterSetAC = JSON.parse(musterSetAC);
                                      updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                                      updatedMusterSetAC.item.meta.value = Value
                                      updatedMusterSetAC.item.serialNumber = ConfigData.seriennummern[0].seriennummer
                                      SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/set');
                                  }
                                  
                                  //Anmeldenachrichten der APP
                                  function setmusterGetPS() {
                                      var updatedMusterSetAC = JSON.parse(musterGetPS);
                                      updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                                      SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/get');
                                  
                                      updatedMusterSetAC = JSON.parse(musterSetAC2);
                                      updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                                      updatedMusterSetAC.item.serialNumber = ConfigData.seriennummern[0].seriennummer
                                      SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/set');
                                  }
                                  
                                  
                                  function generateAndSyncSub(id, JElements, sub = false, preset = "0_userdata.0") {
                                      if (!JElements || typeof JElements !== 'object') {
                                          log('Ungültige JElements übergeben!');
                                          return;
                                      }
                                      for (var JElement in JElements) {
                                          var AktVal;
                                          if (typeof JElements[JElement] === "object") {
                                              generateAndSyncSub(id + "." + JElement, JElements[JElement], true, preset);
                                          } else {
                                              try {
                                                  if (isState2(preset + "." + id + "." + JElement)) AktVal = getState(preset + "." + id + "." + JElement).val; else AktVal = null
                                              } catch (e) {
                                                  log("Fehler: " + e);
                                              }
                                              if (AktVal == null) {
                                                  createState(preset + "." + id + "." + JElement, JElements[JElement], false);
                                                  AktVal = JElements[JElement];
                                              }
                                              if (AktVal != JElements[JElement]) {
                                                  if (isState2(preset + "." + id + "." + JElement)) setState(preset + "." + id + "." + JElement, JElements[JElement]);
                                              }
                                          }
                                      }
                                  }
                                  
                                  /**
                                   * Checks if a a given state or part of state is existing.
                                   * This is a workaround, as getObject() or getState() throw warnings in the log.
                                   * Set strict to true if the state shall match exactly. If it is false, it will add a wildcard * to the end.
                                   * See: https://forum.iobroker.net/topic/11354/
                                   * @param {string}    strStatePath     Input string of state, like 'javascript.0.switches.Osram.Bedroom'
                                   * @param {boolean}   [strict=false]   Optional: if true, it will work strict, if false, it will add a wildcard * to the end of the string
                                   * @return {boolean}                   true if state exists, false if not
                                   */
                                  function isState2(strStatePath, strict = true) {
                                      let mSelector;
                                      if (strict) {
                                          mSelector = $(strStatePath);
                                      } else {
                                          mSelector = $(strStatePath + "*");
                                      }
                                      if (mSelector.length > 0) {
                                          return true;
                                      } else {
                                          return false;
                                      }
                                  }
                                  
                                  
                                  // ########### Grundbedarf steuern
                                  // Es müssen Historydaten für den Wert des Smartmeters vorhanden sein, damit der niedrigeste wert der letzten x Minuten ermittelt werden kann
                                  
                                  function getLowestValue(id, minuten = 120) {
                                      return new Promise((resolve, reject) => {
                                          const now = new Date().getTime();
                                          const range = minuten * 60 * 1000;
                                          var myresult = 0;
                                          getHistory(0, {
                                              id: id,
                                              start: now - range,
                                              end: now,
                                              aggregate: 'minmax'
                                          }, function (err, result) {
                                              if (err) {
                                                  reject(err);
                                              } else if (result.length) {
                                                  let lowestValue = result[0].val;
                                                  for (let i = 1; i < result.length; i++) {
                                                      if (result[i].val < lowestValue) {
                                                          lowestValue = result[i].val;
                                                      }
                                                  }
                                                  myresult = Number(lowestValue);
                                                  resolve(myresult);
                                              } else {
                                                  reject(new Error('No data'));
                                              }
                                          });
                                      });
                                  }
                                  
                                  var OldNewValue = 0
                                  function SetBasePower() {
                                      if (isState2(ConfigData.SmartmeterID)) {
                                          getLowestValue(ConfigData.SmartmeterID, ConfigData.MinValueMin)
                                              .then(lowestValue => {
                                                  if (lowestValue > 0) {
                                                      if (ConfigData.Debug) log("Tiefster Wert der letzten " + ConfigData.MinValueMin + " Minuten: " + lowestValue);
                                                      var AndereVerbraucher = 0
                                                      const ToHomeId = ConfigData.statesPrefix + ".app_device_property_" + ConfigData.seriennummern[0].seriennummer + ".data.item.meta.ToHome_Power"
                                                      if (isState2(ToHomeId)) {
                                                          AndereVerbraucher = Number(getState(ToHomeId).val) / 10
                                                          var NewValue = lowestValue + AndereVerbraucher - ConfigData.BasePowerOffset
                                                          if (NewValue > ConfigData.MaxPower) NewValue = ConfigData.MaxPower
                                                          if (ConfigData.Debug) {
                                                              log("AndereVerbraucher: " + AndereVerbraucher)
                                                              log("Macht : " + (lowestValue + AndereVerbraucher) + "W Grundbedarf")
                                                              log("- Offset von: " + ConfigData.BasePowerOffset + " W = " + (NewValue) + " W Grundbedarf")
                                                          }
                                                          if (OldNewValue != NewValue) setAC(NewValue * 10)
                                                          OldNewValue = NewValue
                                                      }
                                                  }
                                              })
                                              .catch(error => {
                                                  log("Fehler beim Abrufen des niedrigsten Werts: " + error);
                                              });
                                      }
                                  }
                                  
                                  
                                  
                                  A Offline
                                  A Offline
                                  applepro
                                  wrote on last edited by
                                  #166

                                  @waly_de ich habe deine Version 0.3 mal einspielt und seit gestern Mittag laufen. Dabei ist mir aufgefallen, das sich trotz minütlicher Änderung des AC-Wertes, ein Wert außerhalb der Range einschleicht. Dies passiert ca. alle 5-15min. AC Wert wird dann auf 1.747w eingestellt, also max 600w. Ich habe darauf mal dein Skript durch geschaut und an folgender Stellen diesen Wert gefunden. Es handelt sich um den Wert: 17477. Ich habe diesen gegen 1400 = 140w getauscht. Aktuell läuft es seit 16h ohne das verstellen des AC-Wertes auf 17477. Kannst ja mal bei bedarf schauen. Vlt habe aber auch ich irgendwo einen Rechenfehler, der sich eingeschlichen hat.

                                  const musterSetAC2 = `
                                  {
                                    "item": {
                                      "meta": {
                                        "value": 17477 //getauscht gegen 1400
                                      },
                                      "src": 32,
                                      "dest": 53,
                                      "unknown1": 1,
                                      "unknown2": 1,
                                      "unknown4": 3,
                                      "cmdFunc": 32,
                                      "cmdId": 11,
                                      "unknown5": 4,
                                      "needAck": 1,
                                      "timestamp": {
                                        "low": 105187935,
                                        "high": 0,
                                        "unsigned": true
                                      },
                                      "unknown6": 19,
                                      "unknown7": 1,
                                      "OS": "ios",
                                      "serialNumber": "ABCxxxxxxx123"
                                    }```
                                  Gruß
                                  Jannick
                                  W 1 Reply Last reply
                                  0
                                  • A applepro

                                    @waly_de ich habe deine Version 0.3 mal einspielt und seit gestern Mittag laufen. Dabei ist mir aufgefallen, das sich trotz minütlicher Änderung des AC-Wertes, ein Wert außerhalb der Range einschleicht. Dies passiert ca. alle 5-15min. AC Wert wird dann auf 1.747w eingestellt, also max 600w. Ich habe darauf mal dein Skript durch geschaut und an folgender Stellen diesen Wert gefunden. Es handelt sich um den Wert: 17477. Ich habe diesen gegen 1400 = 140w getauscht. Aktuell läuft es seit 16h ohne das verstellen des AC-Wertes auf 17477. Kannst ja mal bei bedarf schauen. Vlt habe aber auch ich irgendwo einen Rechenfehler, der sich eingeschlichen hat.

                                    const musterSetAC2 = `
                                    {
                                      "item": {
                                        "meta": {
                                          "value": 17477 //getauscht gegen 1400
                                        },
                                        "src": 32,
                                        "dest": 53,
                                        "unknown1": 1,
                                        "unknown2": 1,
                                        "unknown4": 3,
                                        "cmdFunc": 32,
                                        "cmdId": 11,
                                        "unknown5": 4,
                                        "needAck": 1,
                                        "timestamp": {
                                          "low": 105187935,
                                          "high": 0,
                                          "unsigned": true
                                        },
                                        "unknown6": 19,
                                        "unknown7": 1,
                                        "OS": "ios",
                                        "serialNumber": "ABCxxxxxxx123"
                                      }```
                                    Gruß
                                    Jannick
                                    W Offline
                                    W Offline
                                    Waly_de
                                    wrote on last edited by
                                    #167

                                    @applepro Das ist interessant. Wie hast Du MinValueMin eingestellt?

                                    A 1 Reply Last reply
                                    0
                                    • W Waly_de

                                      @applepro Das ist interessant. Wie hast Du MinValueMin eingestellt?

                                      A Offline
                                      A Offline
                                      applepro
                                      wrote on last edited by
                                      #168

                                      @waly_de Habe ich auf 10 gelassen.

                                      1 Reply Last reply
                                      0
                                      • W Waly_de

                                        @applepro
                                        Super das es klappt 😊 🎆

                                        hier eine neue Version. Es gab noch ein Problem beim anlegen der States.
                                        Und ich versuche hier ein Phänomen zu beseitigen: Der MQTT hört auf Nachrichten zu senden, wenn man die App auf dem Handy beendet... Bin noch nicht sicher ob es jetzt durchläuft...

                                        /**
                                         * ecoflow-iobroker-connector.js
                                         * Version:      0.4    
                                         * Release date: 29.06.2023
                                         * Autor:        Waly_de 
                                         * Forum:        https://forum.iobroker.net/topic/54929/adapter-für-ecoflow-einbindung/155
                                         * 
                                         *
                                         * This JavaScript file establishes a simple connection between IOBroker and EcoFlow. 
                                         * It automatically creates known states under 0_userdata.
                                         * 
                                         * Please note that adjustments in the ConfigData section are required. Here, you need to enter your access credentials 
                                         * used for the EcoFlow app, as well as the serial numbers of your devices.
                                         * 
                                         * Important: The second serial number entry should always correspond to the Powerstream as it is associated with control functions.
                                         * 
                                         * If you have a state that displays the current power consumption (SmartmeterID), please provide it as well. 
                                         * Configure a history for this state so that the script can determine the lowest power consumption in the last 10 minutes (configurable). 
                                         * This value will be used to dynamically adjust the Powerstream's feed-in power.
                                         * 
                                         * Not all parameters of the Powerstream data are known yet. All known parameters will be automatically created as states.
                                         * By modifying the "protoSource" constant, newly discovered data will also be automatically created.
                                         * 
                                         * The raw data of the interface is logged as a HEX string.
                                         * 
                                         * Please exercise caution as this is the initial version of the script. Use it at your own risk!
                                         *
                                         * Requirements:
                                         * - Install protobuf using the command "npm install protobufjs" from the terminal console.
                                         * - The "Paho MQTT Client" is also required. If not already installed, use the command "npm install mqtt".
                                         *
                                         * Note: It is encouraged to discover and publish missing data definitions to improve the script.
                                         * Suggestions, optimizations, and extensions are welcome at any time.
                                         *
                                         * Special thanks to all contributors for their valuable input and support.
                                         * 
                                         * Changelog:
                                         * -----------------------------------------------------------
                                         * (0.4) 29.06.2023
                                         * Da der MQTT von ecoflow regelmäßig aufhört zu senden, vor allem, wenn die App genutzt und komplett geschlossen wird, 
                                         * habe ich eine Überwachung der letzten ankommenden Nachrichten eingebaut. Kommt 5 Minuten lang nichts neues vom PowerStream,
                                         * Wird die Verbindung zum MQTT kompett neu aufgebaut.
                                         * 
                                         * Ein Fehler bei der Erstellung der States wurde beseitigt
                                         * -----------------------------------------------------------
                                         * 
                                         */
                                        
                                        /*******  YOUR DATA HERE  ****************
                                        var ConfigData = {
                                            email: "your@mail.com",
                                            passwort: "yourAppPasswort!",
                                            seriennummern: [
                                                { seriennummer: "XXXXXXXXXXXXX", name: "PowerStream" }, //1. has to be your PowerStream, you can add more devices...
                                                { seriennummer: "XXXXXXXXXXXXX", name: "DELTA Max" }
                                            ],
                                            SmartmeterID: "sonoff.0.Stromzaehler1.MT175_P",     //State, das den aktuellen Gesamtverbrauch in Watt enthält
                                            BasePowerOffset: 50,                                //wird vom aktuellen Verbrauch abgezogen um die Einspeiseleistung zu berechnen 
                                            MaxPower: 600,                                      //Der höchst mögliche wert in Watt für die Einspeiseleistung
                                            MinValueMin: 10,                                    //Der Zeitraum in Minuten, aus dem der letzte Gesamtverbrauchs-Minimalwert geholt werden soll 
                                            ReconnectMin: 30,                                   //Zeit in Minuten, nach der die Anwendung neu gestartet wird, wenn keine neuen Daten eintreffen
                                            statesPrefix: "0_userdata.0.ecoflow",
                                            Debug: true
                                        };
                                        //***************************************/
                                        
                                        const protoSource = `
                                        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 Bat_Minutes = 60;
                                        }
                                        enum CmdFunction {
                                            Unknown = 0;
                                            PermanentWattsPack = 129;
                                            SupplyPriorityPack = 130;
                                        }
                                        `;
                                        
                                        const mqtt = require('mqtt');
                                        const https = require('https');
                                        const protobuf = require("protobufjs");
                                        
                                        const mqttDaten = {
                                            UserID: '',
                                            User: '',
                                            Passwort: '',
                                            URL: '',
                                            Port: '',
                                            protocol: '',
                                            clientID: ''
                                        }
                                        
                                        /*=======================================================
                                          =========             Timer               ============
                                          =======================================================*/
                                        //alle 5 minuten
                                        schedule('*/1 * * * *', function () {
                                            CheckforReconnect(function () {
                                                SetBasePower();
                                            });
                                        });
                                        
                                        await getEcoFlowMqttData(ConfigData.email, ConfigData.passwort)
                                        async function getEcoFlowMqttData(email, password) {
                                            const options = {
                                                hostname: 'api.ecoflow.com',
                                                path: '/auth/login',
                                                method: 'POST',
                                                headers: {
                                                    'Host': 'api.ecoflow.com',
                                                    'lang': 'de-de',
                                                    'platform': 'android',
                                                    'sysversion': '11',
                                                    'version': '4.1.2.02',
                                                    'phonemodel': 'SM-X200',
                                                    'content-type': 'application/json',
                                                    'user-agent': 'okhttp/3.14.9'
                                                }
                                            };
                                        
                                            const data = {
                                                appVersion: "4.1.2.02",
                                                email: email,
                                                os: "android",
                                                osVersion: "30",
                                                password: Buffer.from(password).toString('base64'),
                                                scene: "IOT_APP",
                                                userType: "ECOFLOW"
                                            };
                                        
                                            function httpsRequest(options, data) {
                                                return new Promise((resolve, reject) => {
                                                    const req = https.request(options, res => {
                                                        let data = '';
                                                        res.on('data', chunk => {
                                                            data += chunk;
                                                        });
                                                        res.on('end', () => {
                                                            resolve(data);
                                                        });
                                                    });
                                        
                                                    req.on('error', error => {
                                                        reject(error);
                                                    });
                                        
                                                    if (data) {
                                                        req.write(JSON.stringify(data));
                                                    }
                                        
                                                    req.end();
                                                });
                                            }
                                        
                                            function uuidv4() {
                                                return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                                                    var r = Math.random() * 16 | 0,
                                                        v = c === 'x' ? r : (r & 0x3 | 0x8);
                                                    return v.toString(16);
                                                });
                                            }
                                        
                                            let response = await httpsRequest(options, data);
                                            try {
                                                let token = JSON.parse(response).data.token;
                                                let userid = JSON.parse(response).data.user.userId;
                                            } catch (error) {
                                                log(response)
                                                throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                                            }
                                        
                                            let token = JSON.parse(response).data.token;
                                            let userid = JSON.parse(response).data.user.userId;
                                        
                                            options.path = `/iot-auth/app/certification?userId=${userid}`;
                                            options.method = 'GET';
                                            options.headers.authorization = `Bearer ${token}`;
                                            response = await httpsRequest(options);
                                            try {
                                                mqttDaten.Passwort = JSON.parse(response).data.certificatePassword
                                                mqttDaten.Port = JSON.parse(response).data.port
                                                mqttDaten.UserID = userid
                                                mqttDaten.User = JSON.parse(response).data.certificateAccount
                                                mqttDaten.URL = JSON.parse(response).data.url
                                                mqttDaten.protocol = JSON.parse(response).data.protocol
                                                mqttDaten.clientID = "ANDROID_" + uuidv4() + "_" + userid
                                            } catch (error) {
                                                log(response)
                                                throw new Error("Ein Fehler bei der Ermittlung der Zugangsdaten ist aufgetreten. Bitte prüfe die Zugangsdaten.");
                                            }
                                            /*    
                                                console.log("UserID: " + userid);
                                                console.log("User: " + JSON.parse(response).data.certificateAccount);
                                                console.log("Passwort: " + JSON.parse(response).data.certificatePassword);
                                                console.log("URL: " + JSON.parse(response).data.url);
                                                console.log("Port: " + JSON.parse(response).data.port);
                                                console.log("protocol: " + JSON.parse(response).data.protocol);
                                                console.log("clientID: ANDROID_" + uuidv4() + "_" + userid);
                                            */
                                        
                                        }
                                        
                                        await createMyState("LastTopic")
                                        
                                        //################ MQTT Verbindung ##################
                                        function setupMQTTConnection() {
                                            // Verbindung herstellen
                                            const options = {
                                                port: mqttDaten.Port,
                                                clientId: mqttDaten.clientID,
                                                username: mqttDaten.User,
                                                password: mqttDaten.Passwort,
                                                protocol: mqttDaten.protocol
                                            };
                                        
                                            const client = mqtt.connect("mqtt://" + mqttDaten.URL, options);
                                        
                                            // Event-Handler für Verbindungsaufbau
                                            client.on('connect', function () {
                                                console.log('Verbunden mit dem Ecoflow MQTT-Broker');
                                                SubscribeEco();
                                            });
                                        
                                            // Event-Handler für Fehler
                                            client.on('error', function (error) {
                                                console.log('Fehler bei der Ecoflow MQTT-Verbindung: ' + error);
                                            });
                                        
                                            function SubscribeEco() {
                                                ConfigData.seriennummern.forEach(item => {
                                                    client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/set');
                                                    client.subscribe('/app/' + mqttDaten.UserID + '/' + item.seriennummer + '/thing/property/get');
                                                    client.subscribe('/app/device/property/' + item.seriennummer);
                                                    setmusterGetPS();
                                                });
                                            }
                                        
                                            // Auf Nachricht empfangen Ereignis reagieren
                                            client.on('message', async function (topic, message) {
                                                var jsonMessage = ""
                                                const mqState = topic.replace(/^\//, '').replace(/\//g, '_')
                                                await createMyState(mqState + ".RAW")
                                                setState(ConfigData.statesPrefix + ".LastTopic", topic)
                                                try {
                                                    jsonMessage = JSON.parse(message);
                                                    if (ConfigData.Debug) log('JSON-Nachricht empfangen:' + topic + ':' + JSON.stringify(jsonMessage));
                                                    setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", JSON.stringify(jsonMessage))
                                                    generateAndSyncSub("data", jsonMessage, false, ConfigData.statesPrefix + '.' + mqState)
                                                } catch (error) {
                                                    if (ConfigData.Debug) log('Binäre Nachricht empfangen:' + topic + ':' + message.toString('hex'));
                                                    await createMyState(mqState + ".RAW_HEX")
                                                    setState(ConfigData.statesPrefix + '.' + mqState + ".RAW_HEX", message.toString('hex'))
                                                    //log('Decodierte Nachricht:' + decodeAndPrint(message.toString('hex')))
                                                    const messagedecoded = decodeAndPrint(message.toString('hex'))
                                                    setState(ConfigData.statesPrefix + '.' + mqState + ".RAW", messagedecoded)
                                                    generateAndSyncSub("data", JSON.parse(messagedecoded), false, ConfigData.statesPrefix + '.' + mqState)
                                                }
                                            });
                                        
                                            // Callback für Fehler
                                            client.on('error', function (error) {
                                                log('Fehler bei der Ecoflow MQTT-Verbindung:' + error, 'warn');
                                            });
                                        
                                            client.on('reconnect', function () {
                                                console.log('Reconnecting to Ecoflow MQTT broker...');
                                            });
                                            // Weitere Event-Handler hier...
                                            return client;
                                        }
                                        
                                        // Verbindung herstellen
                                        let client = setupMQTTConnection();
                                        
                                        // Funktion zum Trennen und Neuaufbau der Verbindung
                                        function reconnect() {
                                            client.end(); // Verbindung trennen
                                            setTimeout(function () {
                                                client = setupMQTTConnection(); // Neue Verbindung herstellen
                                                //log("Ecoflow neuverbindung");
                                            }, 2000); // Wartezeit
                                        }
                                        
                                        // close connection if script stopped
                                        onStop(function (callback) {
                                            if (client) {
                                                // close connection
                                                client.end();
                                                log("Ecoflow MQTT-Client beendet")
                                            }
                                            callback();
                                        }, 2000);
                                        
                                        function CheckforReconnect(callback) {
                                            if (getState(ConfigData.statesPrefix + ".LastTopic")?.ts < Date.now() - ConfigData.ReconnectMin * 60 * 1000) {
                                                console.log("Der letzte Eintrag ist älter als " + ConfigData.ReconnectMin + " Minuten. Versuche Neustart.");
                                                setState(ConfigData.statesPrefix + ".LastTopic", "Last Action Restart:" + new Date().toLocaleString())
                                                runScript();
                                                return;
                                                // Wenn letzte Powerstream-Meldung älter als 5 min ist, schicke Lebenszeichen 
                                            } else if (getState(ConfigData.statesPrefix + '.app_device_property_' + ConfigData.seriennummern[0].seriennummer + '.RAW_HEX')?.ts < Date.now() - 5 * 60 * 1000) {
                                                log("Reconnect zu Ecoflow MQTT für PowerStream - Daten")
                                                //setmusterGetPS()
                                                //.ts Updaten
                                                const oldvalue = getState(ConfigData.statesPrefix + '.app_device_property_' + ConfigData.seriennummern[0].seriennummer + '.RAW_HEX').val
                                                setState(ConfigData.statesPrefix + '.app_device_property_' + ConfigData.seriennummern[0].seriennummer + '.RAW_HEX', oldvalue)
                                                reconnect();
                                                return;
                                                //runScript();
                                            } else {
                                                callback();
                                            }
                                        }
                                        
                                        //*/
                                        
                                        async function createMyState(name) {
                                            if (!(await existsObjectAsync(ConfigData.statesPrefix + '.' + name))) {
                                                await createStateAsync(ConfigData.statesPrefix + '.' + name, {
                                                    name: name.split('.').pop(),
                                                    role: 'state',
                                                    type: 'string',
                                                    read: true,
                                                    write: true
                                                });
                                            }
                                        }
                                        
                                        function decodeAndPrint(hexString) {
                                            const root = protobuf.parse(protoSource).root;
                                            const PowerMessage = root.lookupType("PowerMessage");
                                            const message = PowerMessage.decode(Buffer.from(hexString, "hex"));
                                            const object = PowerMessage.toObject(message, { defaults: false });
                                            return JSON.stringify(object);
                                        }
                                        
                                        function SendProto(protomsg, topic) {
                                            const root = protobuf.parse(protoSource).root;
                                            const PowerMessage = root.lookupType("PowerMessage");
                                            const message = PowerMessage.create(JSON.parse(protomsg));
                                            const messageBuffer = PowerMessage.encode(message).finish();
                                            //log("Modifizierter Hex-String:" +  Buffer.from(messageBuffer).toString("hex"));
                                            //log("topic:" +  topic);
                                            client.publish(topic, messageBuffer, { qos: 1 }, function (error) {
                                                if (error) {
                                                    console.error('Fehler beim Veröffentlichen der MQTT-Nachricht:', error);
                                                } else {
                                                    if (ConfigData.Debug) log('Die MQTT-Nachricht wurde erfolgreich veröffentlicht.');
                                                }
                                            });
                                        }
                                        
                                        //############ Funktionen zum Setzen von Werten
                                        
                                        const musterSetAC = `
                                        {
                                          "item": {
                                            "meta": {
                                              "value": 1300
                                            },
                                            "src": 32,
                                            "dest": 53,
                                            "unknown1": 1,
                                            "unknown2": 1,
                                            "unknown4": 3,
                                            "cmdFunc": 20,
                                            "cmdId": 129,
                                            "unknown5": 3,
                                            "needAck": 1,
                                            "timestamp": {
                                              "low": 35061401,
                                              "high": 0,
                                              "unsigned": true
                                            },
                                            "unknown6": 19,
                                            "unknown7": 1,
                                            "OS": "ios",
                                            "serialNumber": "ABCxxxxxxx123"
                                          }
                                        }
                                        `
                                        const musterSetAC2 = `
                                        {
                                          "item": {
                                            "meta": {
                                              "value": 17477
                                            },
                                            "src": 32,
                                            "dest": 53,
                                            "unknown1": 1,
                                            "unknown2": 1,
                                            "unknown4": 3,
                                            "cmdFunc": 32,
                                            "cmdId": 11,
                                            "unknown5": 4,
                                            "needAck": 1,
                                            "timestamp": {
                                              "low": 105187935,
                                              "high": 0,
                                              "unsigned": true
                                            },
                                            "unknown6": 19,
                                            "unknown7": 1,
                                            "OS": "ios",
                                            "serialNumber": "ABCxxxxxxx123"
                                          }
                                        }
                                        `
                                        const musterGetPS = `
                                        {
                                          "item": {
                                            "src": 32,
                                            "dest": 32,
                                            "timestamp": {
                                              "low": 95816611,
                                              "high": 0,
                                              "unsigned": true
                                            },
                                            "OS": "ios"
                                          }
                                        }
                                        `
                                        await createMyState('app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC')
                                        on({ id: ConfigData.statesPrefix + '.app_' + mqttDaten.UserID + '_' + ConfigData.seriennummern[0].seriennummer + '_thing_property_set.setAC', change: "any", ack: false }, function (obj) {
                                            setAC(Number(obj.state.val))
                                            setState(obj.id, obj.state.val, true);
                                        });
                                        
                                        // Einstellen der Einspeiseleistung
                                        function setAC(Value = 1000) {
                                            const updatedMusterSetAC = JSON.parse(musterSetAC);
                                            updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                                            updatedMusterSetAC.item.meta.value = Value
                                            updatedMusterSetAC.item.serialNumber = ConfigData.seriennummern[0].seriennummer
                                            SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/set');
                                        }
                                        
                                        //Anmeldenachrichten der APP
                                        function setmusterGetPS() {
                                            var updatedMusterSetAC = JSON.parse(musterGetPS);
                                            updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                                            SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/get');
                                        
                                            updatedMusterSetAC = JSON.parse(musterSetAC2);
                                            updatedMusterSetAC.item.timestamp.low = Date.now().toString();
                                            updatedMusterSetAC.item.serialNumber = ConfigData.seriennummern[0].seriennummer
                                            SendProto(JSON.stringify(updatedMusterSetAC), '/app/' + mqttDaten.UserID + '/' + ConfigData.seriennummern[0].seriennummer + '/thing/property/set');
                                        }
                                        
                                        
                                        function generateAndSyncSub(id, JElements, sub = false, preset = "0_userdata.0") {
                                            if (!JElements || typeof JElements !== 'object') {
                                                log('Ungültige JElements übergeben!');
                                                return;
                                            }
                                            for (var JElement in JElements) {
                                                var AktVal;
                                                if (typeof JElements[JElement] === "object") {
                                                    generateAndSyncSub(id + "." + JElement, JElements[JElement], true, preset);
                                                } else {
                                                    try {
                                                        if (isState2(preset + "." + id + "." + JElement)) AktVal = getState(preset + "." + id + "." + JElement).val; else AktVal = null
                                                    } catch (e) {
                                                        log("Fehler: " + e);
                                                    }
                                                    if (AktVal == null) {
                                                        createState(preset + "." + id + "." + JElement, JElements[JElement], false);
                                                        AktVal = JElements[JElement];
                                                    }
                                                    if (AktVal != JElements[JElement]) {
                                                        if (isState2(preset + "." + id + "." + JElement)) setState(preset + "." + id + "." + JElement, JElements[JElement]);
                                                    }
                                                }
                                            }
                                        }
                                        
                                        /**
                                         * Checks if a a given state or part of state is existing.
                                         * This is a workaround, as getObject() or getState() throw warnings in the log.
                                         * Set strict to true if the state shall match exactly. If it is false, it will add a wildcard * to the end.
                                         * See: https://forum.iobroker.net/topic/11354/
                                         * @param {string}    strStatePath     Input string of state, like 'javascript.0.switches.Osram.Bedroom'
                                         * @param {boolean}   [strict=false]   Optional: if true, it will work strict, if false, it will add a wildcard * to the end of the string
                                         * @return {boolean}                   true if state exists, false if not
                                         */
                                        function isState2(strStatePath, strict = true) {
                                            let mSelector;
                                            if (strict) {
                                                mSelector = $(strStatePath);
                                            } else {
                                                mSelector = $(strStatePath + "*");
                                            }
                                            if (mSelector.length > 0) {
                                                return true;
                                            } else {
                                                return false;
                                            }
                                        }
                                        
                                        
                                        // ########### Grundbedarf steuern
                                        // Es müssen Historydaten für den Wert des Smartmeters vorhanden sein, damit der niedrigeste wert der letzten x Minuten ermittelt werden kann
                                        
                                        function getLowestValue(id, minuten = 120) {
                                            return new Promise((resolve, reject) => {
                                                const now = new Date().getTime();
                                                const range = minuten * 60 * 1000;
                                                var myresult = 0;
                                                getHistory(0, {
                                                    id: id,
                                                    start: now - range,
                                                    end: now,
                                                    aggregate: 'minmax'
                                                }, function (err, result) {
                                                    if (err) {
                                                        reject(err);
                                                    } else if (result.length) {
                                                        let lowestValue = result[0].val;
                                                        for (let i = 1; i < result.length; i++) {
                                                            if (result[i].val < lowestValue) {
                                                                lowestValue = result[i].val;
                                                            }
                                                        }
                                                        myresult = Number(lowestValue);
                                                        resolve(myresult);
                                                    } else {
                                                        reject(new Error('No data'));
                                                    }
                                                });
                                            });
                                        }
                                        
                                        var OldNewValue = 0
                                        function SetBasePower() {
                                            if (isState2(ConfigData.SmartmeterID)) {
                                                getLowestValue(ConfigData.SmartmeterID, ConfigData.MinValueMin)
                                                    .then(lowestValue => {
                                                        if (lowestValue > 0) {
                                                            if (ConfigData.Debug) log("Tiefster Wert der letzten " + ConfigData.MinValueMin + " Minuten: " + lowestValue);
                                                            var AndereVerbraucher = 0
                                                            const ToHomeId = ConfigData.statesPrefix + ".app_device_property_" + ConfigData.seriennummern[0].seriennummer + ".data.item.meta.ToHome_Power"
                                                            if (isState2(ToHomeId)) {
                                                                AndereVerbraucher = Number(getState(ToHomeId).val) / 10
                                                                var NewValue = lowestValue + AndereVerbraucher - ConfigData.BasePowerOffset
                                                                if (NewValue > ConfigData.MaxPower) NewValue = ConfigData.MaxPower
                                                                if (ConfigData.Debug) {
                                                                    log("AndereVerbraucher: " + AndereVerbraucher)
                                                                    log("Macht : " + (lowestValue + AndereVerbraucher) + "W Grundbedarf")
                                                                    log("- Offset von: " + ConfigData.BasePowerOffset + " W = " + (NewValue) + " W Grundbedarf")
                                                                }
                                                                if (OldNewValue != NewValue) setAC(NewValue * 10)
                                                                OldNewValue = NewValue
                                                            }
                                                        }
                                                    })
                                                    .catch(error => {
                                                        log("Fehler beim Abrufen des niedrigsten Werts: " + error);
                                                    });
                                            }
                                        }
                                        
                                        
                                        
                                        M Offline
                                        M Offline
                                        mattenausohz
                                        wrote on last edited by
                                        #169

                                        @waly_de Wenn der SetAC Wert sich im Objekt Baum nicht ändert: was kann da das Problem sein? Das Objekt für die aktuelle Einspeisung habe ich konfiguriert.

                                        W 1 Reply Last reply
                                        0
                                        • M mattenausohz

                                          @waly_de Wenn der SetAC Wert sich im Objekt Baum nicht ändert: was kann da das Problem sein? Das Objekt für die aktuelle Einspeisung habe ich konfiguriert.

                                          W Offline
                                          W Offline
                                          Waly_de
                                          wrote on last edited by
                                          #170

                                          @mattenausohz SetAC ändert sich nur, wenn Du ihn änderst. Im Moment dient er nur zum manuellen Schreiben des Wertes, wenn kein Stromzähler konfiguriert ist. Den aktuellen Wert für die eingestellte Einspeiseleistung (Leistungsbedarf am AC) findest du hier:

                                          0_userdata.0.ecoflow.app_device_property_<Seriennummer>.data.item.meta.Needed_Power
                                          

                                          Und das was grade wirklich in Haus geht hier:

                                          0_userdata.0.ecoflow.app_device_property_<Seriennummer>.data.item.meta.ToHome_Power
                                          
                                          
                                          M 1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          738

                                          Online

                                          32.4k

                                          Users

                                          81.4k

                                          Topics

                                          1.3m

                                          Posts
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                                          ioBroker Community 2014-2025
                                          logo
                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Recent
                                          • Tags
                                          • Unread 0
                                          • Categories
                                          • Unreplied
                                          • Popular
                                          • GitHub
                                          • Docu
                                          • Hilfe