Navigation

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

    NEWS

    • Amazon Alexa - ioBroker Skill läuft aus ?

    • Monatsrückblick – September 2025

    • Neues Video "KI im Smart Home" - ioBroker plus n8n

    • Profile
    • Following 0
    • Followers 0
    • Topics 5
    • Posts 335
    • Best 43
    • Groups 2

    maxclaudi

    @maxclaudi

    46
    Reputation
    37
    Profile views
    335
    Posts
    0
    Followers
    0
    Following
    Joined Last Online

    maxclaudi Follow
    Pro Starter

    Best posts made by maxclaudi

    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      @daniel-8

      Änderung ist trivial, muss nur drauf kommen.
      Jetzt müssten auch 3600 sekunden möglich sein usw.
      Empfehle weiterhin 60sek.

      Bitte testen ist nur eine Zeile Code geändert.

      curl - Linux:

      // konfiguration
       
      const dpSmartModeInfo    = "0_userdata.0.zendureSmartMode.smartModeInfo";
      const dpSetSmartMode     = "0_userdata.0.zendureSmartMode.setSmartMode";
      const dpSetSmartModeResult = "0_userdata.0.zendureSmartMode.setResult";
      const dpTimestamp        = "0_userdata.0.zendureSmartMode.timestamp";
       
      const intervalGet = 60;                 // Sekunden
      const IP = "192.168.177.103";           // IP des Zendure Geräts
      const SN = "EXXXXXXXXXXXXX0";           // Seriennummer
       
      //-----------
      // dp anlegen
       
      createState(dpSmartModeInfo, 0, {
          name: "SmartMode Info",
          type: "number",
          role: "state",
          read: true,
          write: false,
          min: 0,
          max: 1
      }, () => {});
       
      createState(dpSetSmartMode, 0, {
          name: "SmartMode Set",
          type: "number",
          role: "state",
          read: true,
          write: true,
          min: 0,
          max: 1
      }, () => {});
       
      createState(dpSetSmartModeResult, "", {
          name: "SmartMode Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      createState(dpTimestamp, "", {
          name: "Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
       
      // time
       
      function formatTime(ts) {
          // ts ist Unix Sekunden
          const d = new Date(ts * 1000);
          const pad = n => n.toString().padStart(2, "0");
          return `${pad(d.getDate())}.${pad(d.getMonth()+1)}.${d.getFullYear().toString().slice(-2)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
      }
       
       
      //curl HTTP GET
       
      function getReport() {
          const cmd = `curl -s "http://${IP}/properties/report"`;
          exec(cmd, (error, stdout, stderr) => {
              if (error) {
                  console.error("GET Fehler:", stderr);
                  return;
              }
              try {
                  const data = JSON.parse(stdout);
                  if (data && data.timestamp) {
                      setState(dpTimestamp, formatTime(data.timestamp), true);
                  }
                  if (data && data.properties && typeof data.properties.smartMode !== "undefined") {
                      setState(dpSmartModeInfo, data.properties.smartMode, true);
                  }
              } catch (e) {
                  console.error("JSON Parse Fehler:", e);
              }
          });
      }
       
       
      //curl HTTP POST zum Setzen
       
      function setSmartMode(val) {
          const payload = `{"sn":"${SN}","properties":{"smartMode":${val}}}`;
          const cmd = `curl -s -X POST "http://${IP}/properties/write" -H "Content-Type: application/json" -d '${payload}'`;
          exec(cmd, (error, stdout, stderr) => {
              if (error) {
                  console.error("POST Fehler:", stderr);
                  setState(dpSetSmartModeResult, "error", true);
                  return;
              }
              setState(dpSetSmartModeResult, "ok", true);
          });
      }
       
       
      // interval
       
      getReport();  // sofort bei start
      setInterval(getReport, intervalGet * 1000);
      
      // trigger wenn SmartMode gesetzt wird
       
      on({id: dpSetSmartMode, ack: false}, obj => {
          const val = parseInt(obj.state.val, 10);
          if (val === 0 || val === 1) {
              setSmartMode(val);
          }
      });
      

      plattformunabhängig - Linux, Windows...

      // konfiguration
      const dpSmartModeInfo      = "0_userdata.0.zendureSmartMode.smartModeInfo";
      const dpSetSmartMode       = "0_userdata.0.zendureSmartMode.setSmartMode";
      const dpSetSmartModeResult = "0_userdata.0.zendureSmartMode.setResult";
      const dpTimestamp          = "0_userdata.0.zendureSmartMode.timestamp";
       
      const intervalGet = 60;                 // Sekunden
      const IP = "192.168.177.103";           // IP des Zendure Geräts
      const SN = "EXXXXXXXXXXXXX0";           // Seriennummer
       
      const http = require("http");           // Node.js Standardmodul
       
       
      //-----------
      // dp
       
      createState(dpSmartModeInfo, 0, {
          name: "SmartMode Info",
          type: "number",
          role: "state",
          read: true,
          write: false,
          min: 0,
          max: 1
      }, () => {});
       
      createState(dpSetSmartMode, 0, {
          name: "SmartMode Set",
          type: "number",
          role: "state",
          read: true,
          write: true,
          min: 0,
          max: 1
      }, () => {});
       
      createState(dpSetSmartModeResult, "", {
          name: "SmartMode Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      createState(dpTimestamp, "", {
          name: "Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
       
      // time
       
      function formatTime(ts) {
          const d = new Date(ts * 1000);
          const pad = n => n.toString().padStart(2, "0");
          return `${pad(d.getDate())}.${pad(d.getMonth()+1)}.${d.getFullYear().toString().slice(-2)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
      }
       
       
      //HTTP GET
       
      function getReport() {
          const options = {
              hostname: IP,
              port: 80,
              path: "/properties/report",
              method: "GET",
              timeout: 3000
          };
       
          const req = http.request(options, res => {
              let data = "";
              res.on("data", chunk => data += chunk);
              res.on("end", () => {
                  try {
                      const json = JSON.parse(data);
                      if (json.timestamp) {
                          setState(dpTimestamp, formatTime(json.timestamp), true);
                      }
                      if (json.properties && typeof json.properties.smartMode !== "undefined") {
                          setState(dpSmartModeInfo, json.properties.smartMode, true);
                      }
                  } catch (e) {
                      console.error("GET JSON Parse Fehler:", e);
                  }
              });
          });
       
          req.on("error", err => console.error("HTTP GET Fehler:", err.message));
          req.end();
      }
       
       
      //HTTP POST zum Setzen
       
      function setSmartMode(val) {
          const payload = JSON.stringify({
              sn: SN,
              properties: { smartMode: val }
          });
       
          const options = {
              hostname: IP,
              port: 80,
              path: "/properties/write",
              method: "POST",
              headers: {
                  "Content-Type": "application/json",
                  "Content-Length": Buffer.byteLength(payload)
              },
              timeout: 3000
          };
       
          const req = http.request(options, res => {
              let data = "";
              res.on("data", chunk => data += chunk);
              res.on("end", () => {
                  if (res.statusCode >= 200 && res.statusCode < 300) {
                      setState(dpSetSmartModeResult, "ok", true);
                  } else {
                      console.error("POST Antwort:", res.statusCode, data);
                      setState(dpSetSmartModeResult, "error", true);
                  }
              });
          });
       
          req.on("error", err => {
              console.error("HTTP POST Fehler:", err.message);
              setState(dpSetSmartModeResult, "error", true);
          });
       
          req.write(payload);
          req.end();
      }
       
       
      // interval
       
      getReport();  // sofort einmal abrufen
      setInterval(getReport, intervalGet * 1000);
       
       
      // trigger wenn SmartMode gesetzt wird
       
      on({id: dpSetSmartMode, ack: false}, obj => {
          const val = parseInt(obj.state.val, 10);
          if (val === 0 || val === 1) {
              setSmartMode(val);
          }
      });
      
      
      
      posted in JavaScript
      maxclaudi
      maxclaudi
    • RE: OpenDTU-onBattery Blockly DPL Ein/Aus

      edit 14.10.2025: einfachste, beste Lösung:
      -> Firmware: maxLimit mit zusätzlichen MQTT Datenpunkten.


      ⚠️ Lizenz & Haftung

      Diese Firmware basiert auf OpenDTU-onBattery (GPLv2, Fork von OpenDTU).
      Sie enthält eigene Anpassungen und wird nicht offiziell unterstützt.

      Beschreibung und wichtige Hinweise zur Installation: hier im Thread

      Verwendung auf eigene Verantwortung – keine Haftung für Schäden oder Fehlfunktionen.

      update 14.10.2025 / FirmwareBuild: esp32s3_usb.firmware

      OpenDtu-OnBattery_2025.09.16DplOff-maxLimit-MQTT.generic_esp32s3_usb.firmware.zip


      Der ursprüngliche Beitrag bleibt im Spoiler erhalten – er ist zwar inzwischen veraltet, zeigt aber schön die Entstehungsgeschichte des Projekts.

      einfacher:

      Dynamic Power Limiter nur über einen Datenpunkt ein- und ausschalten.

      Wird der DPL über ein Script ausgeschaltet, behält der Inverter das letzte Limit (non persistent) bei.

      Deshalb wird im folgenden Blockly beim Auschalten des DPL auch das Limit wieder auf 100% gesetzt.

      Der DPL kann einfach per Datenpunkt in einem Blockly ein- oder ausgeschaltet werden.
      Dazu wird der Datenpunkt zum Einschalten aktualisiert mit wahr.
      Zum Ausschalten mit falsch.

      01-dpl-ein-aus.png


      Im Blockly sind 2 Inverter zur DPL Steuerung berücksichtigt.
      Wird nur 1 Inverter zur DPL Steuerung verwendet, kann die Variable und der Block gelöscht werden oder einfach bei beiden Variablen die gleiche Seriennummer eintragen.
      Falls doch mal später 2 Inverter für DPL aktiviert werden sollen.

      Seriennummer werden nur nach dem Ausschalten verwendet, damit der/die Inverter wieder volle Leistung liefern.

      Werte werden nur non-persistent gesetzt, wie bei der DPL-Steuerung.


      Für das Script wurde manuell 1 Verzeichnis und 1 Datenpunkt angelegt:

      Verzeichnis:
      openDTUoB
      0_userdata.0.Datenpunkte.openDTUoB

      Datenpunkt:
      dplEinAus
      "type": "boolean"
      0_userdata.0.Datenpunkte.openDTUoB.dplEinAus


      Vor dem Ausführen des Scripts müsen folgende Eintragungen vorgenommen werden:

      IP OpenDTUon Battery
      Password
      1te Seriennummer des Inverters für die DPL-Steuerung
      2te Seriennummer des Inverters, der auch DPL steuert (bei nur einem aktivierten Inverter einfach die gleiche Seriennummer vom ersten Inverter eintragen).

      04-dpl-ein-aus.png


      Blockly:

      <xml xmlns="https://developers.google.com/blockly/xml">
        <variables>
          <variable id="P.,=V-_lWMTIt*,^9bWa">jsonObj</variable>
          <variable id="?Ekt8|,iPfa0uAPY)N%A">attr</variable>
          <variable id="y`qH:gKyqipx04VGZ@;=">val</variable>
          <variable id="T[fn@X+yakzmBpi9]n*E">openDTUonBatteryIP</variable>
          <variable id="nh}jM|3k}%Qhny;,Etjk">OpenDTUonBatteryPassword</variable>
          <variable id="/X1](JXiM-@K#NE`C4,%">InverterSerial</variable>
          <variable id="b+E=vG0=nXY*@8jhUpT1">InverterSerial2</variable>
          <variable id="2|vbm;5buC6^$eogp=fe">urlDPLstate</variable>
          <variable id="PWsI)BqL)EeZPzj!8-VY">set</variable>
          <variable id="JppG`(9.sfDt)j^gha}{">cUrlLimitPart1</variable>
          <variable id="Xy47v*SILC`=7N1mM,:5">cUrlLimitPart2</variable>
          <variable id="_+8y375~59B=8rd!@Ae%">jsonObjekt</variable>
        </variables>
        <block type="comment" id="g{c,O//*067~CDT{26F+" x="212" y="-112">
          <field name="COMMENT">OpenDTUonBattery&amp;#10;DPL Ein/Aus</field>
          <next>
            <block type="on_ext" id="MpdxhvC3:):%?cL5ChV-">
              <mutation xmlns="http://www.w3.org/1999/xhtml" items="1"></mutation>
              <field name="CONDITION">ne</field>
              <field name="ACK_CONDITION"></field>
              <value name="OID0">
                <shadow type="field_oid" id="j)L11f:Db#X;sEL6awg/">
                  <field name="oid">0_userdata.0.Datenpunkte.openDTUoB.dplEinAus</field>
                </shadow>
              </value>
              <statement name="STATEMENT">
                <block type="comment" id="wPs7.T(3#lmg-Q#YnMcz">
                  <field name="COMMENT">IP von OpenDTUonBattery:</field>
                  <next>
                    <block type="variables_set" id="xiC72+QQD{FQ44VC6wQn">
                      <field name="VAR" id="T[fn@X+yakzmBpi9]n*E">openDTUonBatteryIP</field>
                      <value name="VALUE">
                        <block type="text" id="3|rd+!6W]T02z}tGd1/m">
                          <field name="TEXT">192.170.178.250</field>
                        </block>
                      </value>
                      <next>
                        <block type="comment" id="8!|A_f5hyHJr/:f!|;r.">
                          <field name="COMMENT">OpenDTUonBattery Passwort:</field>
                          <next>
                            <block type="variables_set" id="ItJFflir$91[$;pF5+MA">
                              <field name="VAR" id="nh}jM|3k}%Qhny;,Etjk">OpenDTUonBatteryPassword</field>
                              <value name="VALUE">
                                <block type="text" id="xYm]l*(;=w@0xJQnWi5-">
                                  <field name="TEXT">openDTU42</field>
                                </block>
                              </value>
                              <next>
                                <block type="variables_set" id="u:x]^bL1{Q9m2)/M*CRl">
                                  <field name="VAR" id="/X1](JXiM-@K#NE`C4,%">InverterSerial</field>
                                  <value name="VALUE">
                                    <block type="text" id="m)|9(*pwq()mn.3Ron~:">
                                      <field name="TEXT">11611111111</field>
                                    </block>
                                  </value>
                                  <next>
                                    <block type="variables_set" id="++@7g{b1u:LHh]h:CEq+">
                                      <field name="VAR" id="b+E=vG0=nXY*@8jhUpT1">InverterSerial2</field>
                                      <value name="VALUE">
                                        <block type="text" id="m8cbvw?0pnZNAUaiU8Zq">
                                          <field name="TEXT">112122222222</field>
                                        </block>
                                      </value>
                                      <next>
                                        <block type="comment" id="8ezl|ED@6X29f4k@:.2H">
                                          <field name="COMMENT">----</field>
                                          <next>
                                            <block type="variables_set" id="!#IE.`!)HPf,NcPW*miy">
                                              <field name="VAR" id="2|vbm;5buC6^$eogp=fe">urlDPLstate</field>
                                              <value name="VALUE">
                                                <block type="text_join" id="F#[x6:MOpP{a#hljyq@2">
                                                  <mutation items="5"></mutation>
                                                  <value name="ADD0">
                                                    <block type="text" id="!@~5;?O8`oIO%oN]bLJa">
                                                      <field name="TEXT">curl -u "admin:</field>
                                                    </block>
                                                  </value>
                                                  <value name="ADD1">
                                                    <block type="variables_get" id="SFT0:Zq378qj5]2?a5#b">
                                                      <field name="VAR" id="nh}jM|3k}%Qhny;,Etjk">OpenDTUonBatteryPassword</field>
                                                    </block>
                                                  </value>
                                                  <value name="ADD2">
                                                    <block type="text" id="5}ok+uklC%b/F}gtZr`^">
                                                      <field name="TEXT">" http://</field>
                                                    </block>
                                                  </value>
                                                  <value name="ADD3">
                                                    <block type="variables_get" id="C|ac)ks!*i8cZS5zb!0?">
                                                      <field name="VAR" id="T[fn@X+yakzmBpi9]n*E">openDTUonBatteryIP</field>
                                                    </block>
                                                  </value>
                                                  <value name="ADD4">
                                                    <block type="text" id="!=iiY$6m(O#=Q|km-7W1">
                                                      <field name="TEXT">/api/powerlimiter/config</field>
                                                    </block>
                                                  </value>
                                                </block>
                                              </value>
                                              <next>
                                                <block type="variables_set" id="Nw*-3DKlvV=)l.++!yg%">
                                                  <field name="VAR" id="PWsI)BqL)EeZPzj!8-VY">set</field>
                                                  <value name="VALUE">
                                                    <block type="text_join" id="b3+b3^R@5s5Vbpj^o9!6">
                                                      <mutation items="2"></mutation>
                                                      <value name="ADD0">
                                                        <block type="variables_get" id="%UJkDROf_mMO^oE4EKW.">
                                                          <field name="VAR" id="2|vbm;5buC6^$eogp=fe">urlDPLstate</field>
                                                        </block>
                                                      </value>
                                                      <value name="ADD1">
                                                        <block type="text" id="o}*[n$;A?-]x1P{Ppan.">
                                                          <field name="TEXT"> -d 'data=</field>
                                                        </block>
                                                      </value>
                                                    </block>
                                                  </value>
                                                  <next>
                                                    <block type="variables_set" id="1%5eOCEM.zYy[-sZ0a43">
                                                      <field name="VAR" id="JppG`(9.sfDt)j^gha}{">cUrlLimitPart1</field>
                                                      <value name="VALUE">
                                                        <block type="text_join" id="B,v7UaJQkq-DSzv41b;v">
                                                          <mutation items="5"></mutation>
                                                          <value name="ADD0">
                                                            <block type="text" id=";4j$FAn0X}CSE_D5:/kl">
                                                              <field name="TEXT">curl -u "admin:</field>
                                                            </block>
                                                          </value>
                                                          <value name="ADD1">
                                                            <block type="variables_get" id="Ea#Og5Jr^hwL!_Oh-[Ua">
                                                              <field name="VAR" id="nh}jM|3k}%Qhny;,Etjk">OpenDTUonBatteryPassword</field>
                                                            </block>
                                                          </value>
                                                          <value name="ADD2">
                                                            <block type="text" id="kBsI#4)FR#3@EAh.=@b]">
                                                              <field name="TEXT">" http://</field>
                                                            </block>
                                                          </value>
                                                          <value name="ADD3">
                                                            <block type="variables_get" id=",/9?^Tx*?hC1YP`-X]37">
                                                              <field name="VAR" id="T[fn@X+yakzmBpi9]n*E">openDTUonBatteryIP</field>
                                                            </block>
                                                          </value>
                                                          <value name="ADD4">
                                                            <block type="text" id="p5zvA0V{e$4V$#_9gWcY">
                                                              <field name="TEXT">/api/limit/config -d 'data={"serial":"</field>
                                                            </block>
                                                          </value>
                                                        </block>
                                                      </value>
                                                      <next>
                                                        <block type="variables_set" id="u|60ca%8U|+rvrL1*bdK">
                                                          <field name="VAR" id="Xy47v*SILC`=7N1mM,:5">cUrlLimitPart2</field>
                                                          <value name="VALUE">
                                                            <block type="text" id="`Z^Zv4qi(GN;drYs.f6I">
                                                              <field name="TEXT">","limit_value":100,"limit_type":1}'</field>
                                                            </block>
                                                          </value>
                                                          <next>
                                                            <block type="comment" id="Y+nX-p}*xExWw]O`OX~{">
                                                              <field name="COMMENT">aktueller DPL state:</field>
                                                              <next>
                                                                <block type="exec" id="I~3.fAPDzJ[1T(TlzoOT">
                                                                  <mutation xmlns="http://www.w3.org/1999/xhtml" with_statement="true"></mutation>
                                                                  <field name="WITH_STATEMENT">TRUE</field>
                                                                  <field name="LOG"></field>
                                                                  <value name="COMMAND">
                                                                    <shadow type="text" id="9;-TT-}BY,qROECr/9_6">
                                                                      <field name="TEXT"></field>
                                                                    </shadow>
                                                                    <block type="variables_get" id="i4]srTbBS]Jm}Z^t,,i^">
                                                                      <field name="VAR" id="2|vbm;5buC6^$eogp=fe">urlDPLstate</field>
                                                                    </block>
                                                                  </value>
                                                                  <statement name="STATEMENT">
                                                                    <block type="variables_set" id="U*PNhv3v$WIWPSb|ja,I">
                                                                      <field name="VAR" id="_+8y375~59B=8rd!@Ae%">jsonObjekt</field>
                                                                      <value name="VALUE">
                                                                        <block type="convert_json2object" id="y.{-xr4=@LsO?lm{BV16">
                                                                          <value name="VALUE">
                                                                            <block type="exec_result" id="6ho:XZ{%G?n+B=|[OsSQ">
                                                                              <field name="ATTR">result</field>
                                                                            </block>
                                                                          </value>
                                                                        </block>
                                                                      </value>
                                                                      <next>
                                                                        <block type="comment" id="A(14glg}PvpJG@9qX7Z+">
                                                                          <field name="COMMENT">DPL Ein:</field>
                                                                          <next>
                                                                            <block type="controls_if" id=")aeo4TK/:D$bt5(cFhVN">
                                                                              <value name="IF0">
                                                                                <block type="logic_compare" id="%pI.k62s._dcfZnb8SHC">
                                                                                  <field name="OP">EQ</field>
                                                                                  <value name="A">
                                                                                    <block type="on_source" id="C-u,UgBdHv4sZCblY)(F">
                                                                                      <field name="ATTR">state.val</field>
                                                                                    </block>
                                                                                  </value>
                                                                                  <value name="B">
                                                                                    <block type="logic_boolean" id="e..27D@{cN0JPJc_@bv;">
                                                                                      <field name="BOOL">TRUE</field>
                                                                                    </block>
                                                                                  </value>
                                                                                </block>
                                                                              </value>
                                                                              <statement name="DO0">
                                                                                <block type="procedures_callcustomnoreturn" id="D9g{#*qgs_-7rfz6|TYD">
                                                                                  <mutation name="change">
                                                                                    <arg name="jsonObj"></arg>
                                                                                    <arg name="attr"></arg>
                                                                                    <arg name="val"></arg>
                                                                                  </mutation>
                                                                                  <value name="ARG0">
                                                                                    <block type="variables_get" id="yi4H-8|bl|J[{ecr@w?*">
                                                                                      <field name="VAR" id="_+8y375~59B=8rd!@Ae%">jsonObjekt</field>
                                                                                    </block>
                                                                                  </value>
                                                                                  <value name="ARG1">
                                                                                    <block type="text" id="/1aiKrRmt}{2sRQ`BE^S">
                                                                                      <field name="TEXT">enabled</field>
                                                                                    </block>
                                                                                  </value>
                                                                                  <value name="ARG2">
                                                                                    <block type="logic_boolean" id="]!TrP#dnQ%e`r1%@n;)0">
                                                                                      <field name="BOOL">TRUE</field>
                                                                                    </block>
                                                                                  </value>
                                                                                  <next>
                                                                                    <block type="exec" id="xWQm84D2~+;KrfUenNP|">
                                                                                      <mutation xmlns="http://www.w3.org/1999/xhtml" with_statement="false"></mutation>
                                                                                      <field name="WITH_STATEMENT">FALSE</field>
                                                                                      <field name="LOG"></field>
                                                                                      <value name="COMMAND">
                                                                                        <shadow type="text" id="Y[J#ti:!)V_p[~D/~=BP">
                                                                                          <field name="TEXT">pwd</field>
                                                                                        </shadow>
                                                                                        <block type="text_join" id="?#1pjgc[4w-Nbot8xQpL">
                                                                                          <mutation items="3"></mutation>
                                                                                          <value name="ADD0">
                                                                                            <block type="variables_get" id=";wn*ZeQHpIf@a_40]mk$">
                                                                                              <field name="VAR" id="PWsI)BqL)EeZPzj!8-VY">set</field>
                                                                                            </block>
                                                                                          </value>
                                                                                          <value name="ADD1">
                                                                                            <block type="convert_object2json" id="`s4:$VMpnCx?$#E/lH89">
                                                                                              <field name="PRETTIFY">FALSE</field>
                                                                                              <value name="VALUE">
                                                                                                <block type="variables_get" id="akzUA)PF0iBQcIE,ZBTo">
                                                                                                  <field name="VAR" id="_+8y375~59B=8rd!@Ae%">jsonObjekt</field>
                                                                                                </block>
                                                                                              </value>
                                                                                            </block>
                                                                                          </value>
                                                                                          <value name="ADD2">
                                                                                            <block type="text" id="tazI8QS$S%YmkBw8,!R]">
                                                                                              <field name="TEXT">'</field>
                                                                                            </block>
                                                                                          </value>
                                                                                        </block>
                                                                                      </value>
                                                                                    </block>
                                                                                  </next>
                                                                                </block>
                                                                              </statement>
                                                                              <next>
                                                                                <block type="comment" id="zV97]OnS?/IjoV1xAl_T">
                                                                                  <field name="COMMENT">DPL Aus:</field>
                                                                                  <next>
                                                                                    <block type="controls_if" id="7{aiQa33_g$QnlPw!:;F">
                                                                                      <value name="IF0">
                                                                                        <block type="logic_compare" id="XYHuB8ZNE3]RuqS}0zLa">
                                                                                          <field name="OP">EQ</field>
                                                                                          <value name="A">
                                                                                            <block type="on_source" id="kRJjzY0xkd07ZrzDPb1?">
                                                                                              <field name="ATTR">state.val</field>
                                                                                            </block>
                                                                                          </value>
                                                                                          <value name="B">
                                                                                            <block type="logic_boolean" id="{2pCy)Ah-nq3V.,{DVo1">
                                                                                              <field name="BOOL">FALSE</field>
                                                                                            </block>
                                                                                          </value>
                                                                                        </block>
                                                                                      </value>
                                                                                      <statement name="DO0">
                                                                                        <block type="procedures_callcustomnoreturn" id="-Hz0vX5GM=t[9OHH8]Wg">
                                                                                          <mutation name="change">
                                                                                            <arg name="jsonObj"></arg>
                                                                                            <arg name="attr"></arg>
                                                                                            <arg name="val"></arg>
                                                                                          </mutation>
                                                                                          <value name="ARG0">
                                                                                            <block type="variables_get" id="v[6xm8C%Otou%c3=g710">
                                                                                              <field name="VAR" id="_+8y375~59B=8rd!@Ae%">jsonObjekt</field>
                                                                                            </block>
                                                                                          </value>
                                                                                          <value name="ARG1">
                                                                                            <block type="text" id="tL?JD.@HaQSk5`{9Wm]{">
                                                                                              <field name="TEXT">enabled</field>
                                                                                            </block>
                                                                                          </value>
                                                                                          <value name="ARG2">
                                                                                            <block type="logic_boolean" id="WMimixsaaPP)fhHu2.J$">
                                                                                              <field name="BOOL">FALSE</field>
                                                                                            </block>
                                                                                          </value>
                                                                                          <next>
                                                                                            <block type="exec" id="/p+Uv~V#hIePApTb=KTR">
                                                                                              <mutation xmlns="http://www.w3.org/1999/xhtml" with_statement="false"></mutation>
                                                                                              <field name="WITH_STATEMENT">FALSE</field>
                                                                                              <field name="LOG"></field>
                                                                                              <value name="COMMAND">
                                                                                                <shadow type="text" id="Y[J#ti:!)V_p[~D/~=BP">
                                                                                                  <field name="TEXT">pwd</field>
                                                                                                </shadow>
                                                                                                <block type="text_join" id="?fTh]i~D]=Ue}GsE8^;i">
                                                                                                  <mutation items="3"></mutation>
                                                                                                  <value name="ADD0">
                                                                                                    <block type="variables_get" id="$!],od*U?4dNiG;_YIzl">
                                                                                                      <field name="VAR" id="PWsI)BqL)EeZPzj!8-VY">set</field>
                                                                                                    </block>
                                                                                                  </value>
                                                                                                  <value name="ADD1">
                                                                                                    <block type="convert_object2json" id="|pyw%?Kwve~3iciql_#f">
                                                                                                      <field name="PRETTIFY">FALSE</field>
                                                                                                      <value name="VALUE">
                                                                                                        <block type="variables_get" id="z0~!%^yv(pHWcQpw)5``">
                                                                                                          <field name="VAR" id="_+8y375~59B=8rd!@Ae%">jsonObjekt</field>
                                                                                                        </block>
                                                                                                      </value>
                                                                                                    </block>
                                                                                                  </value>
                                                                                                  <value name="ADD2">
                                                                                                    <block type="text" id="/g9Zs!_,BY#!LKzHEF`/">
                                                                                                      <field name="TEXT">'</field>
                                                                                                    </block>
                                                                                                  </value>
                                                                                                </block>
                                                                                              </value>
                                                                                              <next>
                                                                                                <block type="comment" id="ql1mac4Qf*!]/(y:*HR+">
                                                                                                  <field name="COMMENT">DPLinverter01  Limit 100%</field>
                                                                                                  <next>
                                                                                                    <block type="exec" id="iNs^J_^oAm89Vr+p^dcU">
                                                                                                      <mutation xmlns="http://www.w3.org/1999/xhtml" with_statement="false"></mutation>
                                                                                                      <field name="WITH_STATEMENT">FALSE</field>
                                                                                                      <field name="LOG"></field>
                                                                                                      <value name="COMMAND">
                                                                                                        <shadow type="text" id="C9zK,I#|DhseRSSl*?8X">
                                                                                                          <field name="TEXT"></field>
                                                                                                        </shadow>
                                                                                                        <block type="text_join" id="~7~(vzW]+%{R35ES=iGn">
                                                                                                          <mutation items="3"></mutation>
                                                                                                          <value name="ADD0">
                                                                                                            <block type="variables_get" id=".v]Hhm{LD(-@){kzp()2">
                                                                                                              <field name="VAR" id="JppG`(9.sfDt)j^gha}{">cUrlLimitPart1</field>
                                                                                                            </block>
                                                                                                          </value>
                                                                                                          <value name="ADD1">
                                                                                                            <block type="variables_get" id="OZY@nUX[m`.Rp[12/e{g">
                                                                                                              <field name="VAR" id="/X1](JXiM-@K#NE`C4,%">InverterSerial</field>
                                                                                                            </block>
                                                                                                          </value>
                                                                                                          <value name="ADD2">
                                                                                                            <block type="variables_get" id="]wG/EOqQ!/tw6ZlRcab0">
                                                                                                              <field name="VAR" id="Xy47v*SILC`=7N1mM,:5">cUrlLimitPart2</field>
                                                                                                            </block>
                                                                                                          </value>
                                                                                                        </block>
                                                                                                      </value>
                                                                                                      <next>
                                                                                                        <block type="comment" id="aI[_!O+Ua%`p6/mqrCjz">
                                                                                                          <field name="COMMENT">DPLinverter02  Limit 100%</field>
                                                                                                          <next>
                                                                                                            <block type="exec" id="v[cN9cA@.Ejy;Z1G81kX">
                                                                                                              <mutation xmlns="http://www.w3.org/1999/xhtml" with_statement="false"></mutation>
                                                                                                              <field name="WITH_STATEMENT">FALSE</field>
                                                                                                              <field name="LOG"></field>
                                                                                                              <value name="COMMAND">
                                                                                                                <shadow type="text" id="C9zK,I#|DhseRSSl*?8X">
                                                                                                                  <field name="TEXT"></field>
                                                                                                                </shadow>
                                                                                                                <block type="text_join" id="Wd`VvP4NqCJ}/*|~IbTV">
                                                                                                                  <mutation items="3"></mutation>
                                                                                                                  <value name="ADD0">
                                                                                                                    <block type="variables_get" id="hy+6K,WWY.Y`%h-+p;v1">
                                                                                                                      <field name="VAR" id="JppG`(9.sfDt)j^gha}{">cUrlLimitPart1</field>
                                                                                                                    </block>
                                                                                                                  </value>
                                                                                                                  <value name="ADD1">
                                                                                                                    <block type="variables_get" id="W/O@o[J$nrG+r..LB`}A">
                                                                                                                      <field name="VAR" id="b+E=vG0=nXY*@8jhUpT1">InverterSerial2</field>
                                                                                                                    </block>
                                                                                                                  </value>
                                                                                                                  <value name="ADD2">
                                                                                                                    <block type="variables_get" id="q%FGBbHAZ^N/`|V3wzs%">
                                                                                                                      <field name="VAR" id="Xy47v*SILC`=7N1mM,:5">cUrlLimitPart2</field>
                                                                                                                    </block>
                                                                                                                  </value>
                                                                                                                </block>
                                                                                                              </value>
                                                                                                            </block>
                                                                                                          </next>
                                                                                                        </block>
                                                                                                      </next>
                                                                                                    </block>
                                                                                                  </next>
                                                                                                </block>
                                                                                              </next>
                                                                                            </block>
                                                                                          </next>
                                                                                        </block>
                                                                                      </statement>
                                                                                    </block>
                                                                                  </next>
                                                                                </block>
                                                                              </next>
                                                                            </block>
                                                                          </next>
                                                                        </block>
                                                                      </next>
                                                                    </block>
                                                                  </statement>
                                                                </block>
                                                              </next>
                                                            </block>
                                                          </next>
                                                        </block>
                                                      </next>
                                                    </block>
                                                  </next>
                                                </block>
                                              </next>
                                            </block>
                                          </next>
                                        </block>
                                      </next>
                                    </block>
                                  </next>
                                </block>
                              </next>
                            </block>
                          </next>
                        </block>
                      </next>
                    </block>
                  </next>
                </block>
              </statement>
            </block>
          </next>
        </block>
        <block type="procedures_defcustomnoreturn" id="nxZk[TB}x%yvkJ058U]o" x="738" y="862">
          <mutation statements="false">
            <arg name="jsonObj" varid="P.,=V-_lWMTIt*,^9bWa"></arg>
            <arg name="attr" varid="?Ekt8|,iPfa0uAPY)N%A"></arg>
            <arg name="val" varid="y`qH:gKyqipx04VGZ@;="></arg>
          </mutation>
          <field name="NAME">change</field>
          <field name="SCRIPT">anNvbk9ialthdHRyXSA9IHZhbDs=</field>
          <comment pinned="false" h="80" w="160">Beschreibe diese Funktion …</comment>
        </block>
      </xml>
      

      Damit kann in jedem Script der Dynamic Power Limiter ein- oder ausgeschaltet werden.

      Ist bestimmt brauchbar.

      Vorschläge oder Verbesserungen willkommen.

      Würde mich mal über eine Rückmeldung oder einen Beitrag freuen.
      Nicht, dass ich hier Alleinunterhalter bin 🙂

      posted in Blockly
      maxclaudi
      maxclaudi
    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      Frage:
      Mit dem Script liefert MqttConnectInfo Strings: "connected" / "disconnected"

      Soll das zum leichteren Auswerten in Blockly umgeschrieben werden zu einer Nummer?
      1 wenn MQTT verbunden / enabled
      0 wenn getrennt / disabled

      posted in JavaScript
      maxclaudi
      maxclaudi
    • Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      Für alle mit den neuen Geräten:

      • SolarFlow 800
      • SolarFlow 800 Pro
      • SolarFlow 2400 AC

      Immer wieder taucht die Frage auf z.B. (z. B. Michi 0 ), wie man den SmartMode:1 setzen kann.
      Bisher wurde das noch nirgends wirklich anwenderfreundlich beantwortet.

      Hier nun die Lösung:

      Mit diesem Script können nicht nur smartMode:1 und die MQTT-Verbindung (de-)aktiviert werden, sondern auch einiges mehr.


      Update 15.10.2025 21:21h– Neues Script
      ioBroker JavaScript: Zendure SolarFlow 800 Pro (SF2400AC) via zenSDK HTTP
      Kein Hack, dafür wurde die offizielle zenSDK verwendet.
      plattformunabhängig - Linux, Windows

      Jetzt können auch Commands gesetzt werden, wie in einem Adapter. 🙂

      Muss man nicht nutzen, kann man aber zusätzlich einsetzen – z. B. falls MQTT mal Probleme macht oder wenn weitere set-Befehle dazukommen sollen.

      Hinweis:
      Damit neue Datenpunkte automatisch aus dem JSON erstellt werden:
      → Instanzen → JavaScript-Adapter → Allgemeine Einstellungen → „Enable command setObject“ aktivieren!

      So viel wie möglich wurde ohne Gerät getestet und simuliert.
      Extra ohne Schleifen und strukturiert geschrieben, damit jeder das Script nachvollziehen kann.

      Intervalle

      • intervalGet sollte nicht < 30 s gesetzt werden – das bringt keinen Mehrwert.
      • intervalMqtt = 300 s ist völlig ausreichend, um zu prüfen, ob MQTT verbunden ist.

      Eigene Daten anpassen
      Zendure Gerät:

      const IP = "192.168.177.103";   // IP des Zendure Geräts
      const SN = "EXXXXXXXXXXXXX0";   // Seriennummer
      

      Intervalle für Abfragen (in Sekunden):

      const intervalGet = 60;   // SmartMode (default: 60 s, nicht < 30)
      const intervalMqtt = 300; // MQTT-Status (default: 300 s, > SmartMode)
      

      MQTT-Broker Konfiguration:

      const mqttBrokerIp = "192.168.177.200"; // IP MQTT Broker (auch in App eingestellt)
      const mqttPort = 1883;                  // Port MQTT Broker (auch in App eingestellt)           
      const mqttUsername = "Daisy";           // MQTT Username
      const mqttPassword = "coco";            // MQTT Passwort
      
      

      Maximale Input-/Output-Limits:

      // Maximum Input-Limit
      const maxInputLimit = 800;   // SF800Pro
      //const maxInputLimit = 2400; // SF2400AC
      
      // Maximum Output-Limit
      const maxOutputLimit = 800;   // SF800Pro
      //const maxOutputLimit = 2400; // SF2400AC
      
      

      Hinweise

      • wichtig: wenn MQTT über dieses Script eingeschaltet wird, hat es vermutlich Vorrang!
        Es werden ggf. andere MQTT-Einstellungen in der App durch das Script überschrieben.

      • Wer meine Arbeit nützlich findet und sich bedanken möchte:
        Ein Klick auf den Pfeil nach oben (unten am Post, neben Zitieren) reicht völlig. Das ist für mich mehr als genug. 🙂

      • Dieser erste Post wird bei Änderungen editiert, damit die Scripte immer direkt hier auffindbar bleiben.

      Viel Spaß! ☀


      //
      // ioBroker JavaScript: Zendure SolarFlow 800 Pro (SF2400AC) via zenSDK HTTP
      // by maxclaudi 2025.10.15 21:21h for ioBroker Forum
      //------------------------------------------------------------------------------------
      // konfiguration
      //
      // Damit neue Datenpunkte automatisch aus dem JSON erstellt werden:
      // -> Instanzen -> javascript-Adapter -> Allgemeine Einstellungen -> Enable command"setObject“ aktivieren!
      //
      // Intervalle für Abfragen (Sekunden)
      const intervalGet = 60;                 // sek SmartMode   (default:  60 sek, not <30)
      const intervalMqtt = 300;               // sek MQTT-Status (default: 300 sek, > SmartMode)
       
      // IP und Seriennummer Zendure Gerät
      const IP = "192.168.40.111";           // IP des Zendure Geräts
      const SN = "EXXXXXXXXXXXXX0";           // Seriennummer
       
      // MQTT Broker Konfiguration
      const mqttBrokerIp = "192.168.40.200";  // IP MQTT Broker und in App eingestellt 
      const mqttPort = 1883;                // PORT MQTT Broker und in App eingestellt           
      const mqttUsername = "Daisy";           // MQTT Username
      const mqttPassword = "coco";            // MQTT Passwort
       
      //maximum inputLimit SF800PRO: 800W / SF2400AC: 2400W
      const maxInputLimit = 1000; //SF800Pro
      //const maxInputLimit = 2400; //SF2400AC
       
      //maximum outputLimit SF800PRO: 800W / SF2400AC: 2400W
      const maxOutputLimit = 800; //SF800Pro
      //const maxOutputLimit = 2400; //SF2400AC
      //------------------------------------------------------------------------------------ 
      //------------------------------------------------------------------------------------
      // Datenpunkte
      const folderZendureApi = '0_userdata.0.zendureApi';
      //------------------------------------------------------------------------------------
      const dpSmartModeInfo              = folderZendureApi + ".zendureSmartMode.smartModeInfo";
      const dpSetSmartMode               = folderZendureApi + ".zendureSmartMode.setSmartMode";
      const dpSetSmartModeResult         = folderZendureApi + ".zendureSmartMode.setResult";
      const dpTimestamp                  = folderZendureApi + ".zendureSmartMode.timestamp";
       
      const dpMqttConnectInfo            = folderZendureApi + ".zendureMqttState.mqttConnectInfo";
      const dpSetMqttConnect             = folderZendureApi + ".zendureMqttState.setMqttConnect";
      const dpSetMqttConnectResult       = folderZendureApi + ".zendureMqttState.setMqttConnectResult";
      const dpMqttTimestamp              = folderZendureApi + ".zendureMqttState.mqttTimestamp";
       
      //set dp
      const dpSetAcMode                  = folderZendureApi + ".control.acMode.setAcMode";
      const dpSetAcModeResult            = folderZendureApi + ".control.acMode.setAcModeResult";
      const dpSetAcModeResultTS          = folderZendureApi + ".control.acMode.setAcModeResultTs";
       
      const dpSetInputLimit              = folderZendureApi + ".control.inputLimit.setInputLimit";
      const dpSetInputLimitResult        = folderZendureApi + ".control.inputLimit.setInputLimitResult";
      const dpSetInputLimitResultTS      = folderZendureApi + ".control.inputLimit.setInputLimitResultTs";
       
      const dpSetOutputLimit             = folderZendureApi + ".control.outputLimit.setOutputLimit";
      const dpSetOutputLimitResult       = folderZendureApi + ".control.outputLimit.setOutputLimitResult";
      const dpSetOutputLimitResultTS     = folderZendureApi + ".control.outputLimit.setOutputLimitResultTs";
       
      const dpSetSocSet                  = folderZendureApi + ".control.socSet.setSocSet";
      const dpSetSocSetResult            = folderZendureApi + ".control.socSet.setSocSetResult";
      const dpSetSocSetResultTS          = folderZendureApi + ".control.socSet.setSocSetResultTs";
       
      const dpSetMinSoc                  = folderZendureApi + ".control.minSoc.setMinSoc";
      const dpSetMinSocResult            = folderZendureApi + ".control.minSoc.setMinSocResult";
      const dpSetMinSocResultTS          = folderZendureApi + ".control.minSoc.setMinSocResultTs";
       
      const dpSetGridReverse             = folderZendureApi + ".control.gridReverse.setGridReverse";
      const dpSetGridReverseResult       = folderZendureApi + ".control.gridReverse.setGridReverseResult";
      const dpSetGridReverseResultTS     = folderZendureApi + ".control.gridReverse.setGridReverseResultTs";
       
      const dpSetGridStandard            = folderZendureApi + ".control.gridStandard.setGridStandard";
      const dpSetGridStandardResult      = folderZendureApi + ".control.gridStandard.setGridStandardResult";
      const dpSetGridStandardResultTS    = folderZendureApi + ".control.gridStandard.setGridStandardResultTs";
      
      const dpSetInverseMaxPower         = folderZendureApi + ".control.inverseMaxPower.setInverseMaxPower";
      const dpSetInverseMaxPowerResult   = folderZendureApi + ".control.inverseMaxPower.setInverseMaxPowerResult";
      const dpSetInverseMaxPowerResultTS = folderZendureApi + ".control.inverseMaxPower.setInverseMaxPowerResultTs"; 
      //------------------------------------------------------------------------------------
      const http = require("http");           // Node.js http Standardmodul
      //------------------------------------------------------------------------------------
      
      // Mapping: Payload key, optional transformation, result and timestamp dp
      const dpMap = {
          [dpSetAcMode]:          { key: "acMode", result: dpSetAcModeResult, resultTS: dpSetAcModeResultTS, minNormal: 1, maxNormal: 2 },
          [dpSetInputLimit]:      { key: "inputLimit", result: dpSetInputLimitResult, resultTS: dpSetInputLimitResultTS, minNormal: 0, maxNormal: maxInputLimit },
          [dpSetOutputLimit]:     { key: "outputLimit", result: dpSetOutputLimitResult, resultTS: dpSetOutputLimitResultTS, minNormal: 0, maxNormal: maxOutputLimit },
          [dpSetSocSet]:          { key: "socSet", transform: v => v*10, result: dpSetSocSetResult, resultTS: dpSetSocSetResultTS, minNormal: 70, maxNormal: 100 },
          [dpSetMinSoc]:          { key: "minSoc", transform: v => v*10, result: dpSetMinSocResult, resultTS: dpSetMinSocResultTS, minNormal: 5, maxNormal: 50 },
          [dpSetGridReverse]:     { key: "gridReverse", result: dpSetGridReverseResult, resultTS: dpSetGridReverseResultTS, minNormal: 0, maxNormal: 2 },
          [dpSetGridStandard]:    { key: "gridStandard", result: dpSetGridStandardResult, resultTS: dpSetGridStandardResultTS, minNormal: 0, maxNormal: 2 },
          [dpSetInverseMaxPower]: { key: "inverseMaxPower", result: dpSetInverseMaxPowerResult, resultTS: dpSetInverseMaxPowerResultTS, minNormal: 600, maxNormal: maxOutputLimit }
      };
       
      // Queue HTTP
      const curlQueue = [];
      let curlRunning = false;
       
      function runQueue() {
          if (curlRunning || curlQueue.length === 0) return;
          const task = curlQueue.shift();
          curlRunning = true;
          task.fn(() => {
              curlRunning = false;
              runQueue();
          });
      }
       
      //dp have to exist - get
      function delay(ms) {
          return new Promise(resolve => setTimeout(resolve, ms));
      }
       
      // dp create
      createState(dpSmartModeInfo, 0, {
              name: "SmartMode Info",
              type: "number",
              role: "state",
              read: true,
              write: false,
              min: 0,
              max: 1
              }, async () => {
                 await delay(1);
                 getReport();
      });
       
      createState(dpSetSmartMode, -1, {
             name: "SmartMode Set",
      	   type: "number",
      	   role: "state",
      	   read: true,
      	   write: true,
      	   min: -1,
      	   max: 1
      }, () => {});
       
      createState(dpSetSmartModeResult, "", {
             name: "SmartMode Set Result",
      	   type: "string",
      	   role: "info",
      	   read: true,
      	   write: false
      }, () => {});
       
      createState(dpTimestamp, "", {
             name: "Timestamp",
      	   type: "string",
      	   role: "info",
      	   read: true,
      	   write: false
             }, async () => {
                 await delay(1);
      });
       
      createState(dpMqttConnectInfo, 0, {
          name: "MQTT Connect Info",
          type: "number", 
          role: "state",
          read: true,
          write: false,
          min: 0,
          max: 1
          }, async () => {
             await delay(1);
          getMqttStatus();
      });
       
      createState(dpSetMqttConnect, -1, {
             name: "MQTT Connect Set",
      	   type: "number",
      	   role: "state",
      	   read: true,
      	   write: true,
      	   min: -1,
      	   max: 1
      }, () => {});
       
      createState(dpSetMqttConnectResult, "", {
             name: "MQTT Connect Result",
      	   type: "string",
      	   role: "info",
      	   read: true,
      	   write: false
      }, () => {});
       
      createState(dpMqttTimestamp, "", {
             name: "MQTT Timestamp",
      	   type: "string",
      	   role: "info",
      	   read: true,
      	   write: false
      }, async () => { await delay(1); });
       
      // set > result ts dp
      // acMode
      createState(dpSetAcMode, -1, {
             name: "set acMode, 1: charging 2: discharging",
      	   desc: "1: Input, 2: Output",
      	   type: "number",
      	   role: "state",
      	   read: true,
      	   write: true,
      	   min: -1,
      	   max: 2
      }, () => {});
       
      createState(dpSetAcModeResult, "", {
          name: "AcMode Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      createState(dpSetAcModeResultTS, "", {
          name: "AcMode Set Result Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      // inputLimit
      createState(dpSetInputLimit, -1, {
             name: "set inputLimit",
      	   desc: "AC charging power limit",
      	   type: "number",
      	   role: "state",
      	   read: true,
      	   write: true,
      	   unit: 'W',
      	   min: -1,
      	   max: maxInputLimit //SF800Pro: 1000W, SF2400AC: 2400W
      }, () => {});
       
      createState(dpSetInputLimitResult, "", {
          name: "InputLimit Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      createState(dpSetInputLimitResultTS, "", {
          name: "InputLimit Set Result Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      // outputLimit
      createState(dpSetOutputLimit, -1, {
             name: "set outputLimit",
      	   desc: "Output power limit",
      	   type: "number",
      	   role: "state",
      	   read: true,
      	   write: true,
      	   unit: 'W',
      	   min: -1,
      	   max: maxOutputLimit //SF800Pro: 800W, SF2400AC: 2400W
      }, () => {});
       
      createState(dpSetOutputLimitResult, "", {
          name: "OutputLimit Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      createState(dpSetOutputLimitResultTS, "", {
          name: "OutputLimit Set Result Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      // socSet
      createState(dpSetSocSet, -1, {
             name: "set socSet 70-100%",
      	   desc: "Stop charging at battery percentage",
      	   type: "number",
      	   role: "state",
      	   read: true,
      	   write: true,
      	   unit: '%',
      	   min: -1,
      	   max: 100
      }, () => {});
       
      createState(dpSetSocSetResult, "", {
          name: "SocSet Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      createState(dpSetSocSetResultTS, "", {
          name: "SocSet Set Result Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      // minSoc
      createState(dpSetMinSoc, -1, {
             name: "set minSoc 0-50%",
      	   desc: "Stop discharging at battery percentage",
      	   type: "number",
      	   role: "state",
      	   read: true,
      	   write: true,
      	   unit: '%',
      	   min: -1,
      	   max: 50
      }, () => {});
       
      createState(dpSetMinSocResult, "", {
          name: "MinSoc Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      createState(dpSetMinSocResultTS, "", {
          name: "MinSoc Set Result Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      // gridReverse
      createState(dpSetGridReverse, -1, {
             name: "setGridReverse, 0: off, 1: rev. 2: no rev",
      	   desc: "0: Disabled, 1: Allowed reverse flow, 2: Forbidden reverse flow",
      	   type: "number",
      	   role: "state",
      	   read: true,
      	   write: true,
      	   min: -1,
      	   max: 2
      }, () => {});
       
      createState(dpSetGridReverseResult, "", {
          name: "GridReverse Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      createState(dpSetGridReverseResultTS, "", {
          name: "GridReverse Set Result Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      // gridStandard
      createState(dpSetGridStandard, -1, {
             name: "setGridStandard, 0: Germany / 2: France",
      	   desc: "Grid connection standard 0: Germany 1: France 2: Austria",
      	   type: "number",
      	   role: "state",
      	   read: true,
      	   write: true,
      	   min: -1,
      	   max: 2
      }, () => {});
       
      createState(dpSetGridStandardResult, "", {
          name: "GridStandard Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      createState(dpSetGridStandardResultTS, "", {
          name: "GridStandard Set Result Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
      
      // inverseMaxPower
      createState(dpSetInverseMaxPower, -1, {
             name: "set inverseMaxPower",
      	   desc: "Maximum output power limit",
      	   type: "number",
      	   role: "state",
      	   read: true,
      	   write: true,
      	   unit: 'W',
      	   min: -1,
      	   max: maxOutputLimit
      }, () => {});
       
      createState(dpSetInverseMaxPowerResult, "", {
          name: "inverseMaxPower Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
       
      createState(dpSetInverseMaxPowerResultTS, "", {
          name: "inverseMaxPower Set Result Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
      
      // intervals start																											  
      setInterval(getReport, intervalGet*1000);
      setInterval(getMqttStatus, intervalMqtt*1000);
       
      // trigger smartMode
      on({id: dpSetSmartMode, ack:false}, obj => {
          const val = parseInt(obj.state.val,10);
          if(val===0||val===1) setSmartMode(val);
      	setState(dpSetSmartMode, -1, { ack: true });
      });
       
      // trigger MQTT connect
      on({id: dpSetMqttConnect, ack:false}, obj => {
          const val = parseInt(obj.state.val,10);
          if(val===0||val===1) setMqttConnect(val);
      	setState(dpSetMqttConnect, -1, { ack: true });
      });
       
      // trigger dp set
      Object.keys(dpMap).forEach(id => {
          on({ id: id, ack: false }, obj => {
              const val = obj.state.val;
              const { key, transform, result, resultTS, minNormal, maxNormal } = dpMap[id];
      
              // Prüfen, ob Wert im gültigen Bereich liegt
              if (val >= minNormal && val <= maxNormal) {
                  const valueToSend = transform ? transform(val) : val;
                  setControlDP(id, valueToSend);  // POST senden
              } else {
                  log(`Value ${val} for ${id} is not allowed`, 'warn');
              }
              setState(id, -1, { ack: true });
          });
      });
      
      // time format
      function formatTime(ts) {
      	// ts unix sek							
          const d = new Date(ts * 1000);
          const pad = n => n.toString().padStart(2, "0");
          return `${pad(d.getDate())}.${pad(d.getMonth()+1)}.${d.getFullYear().toString().slice(-2)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
      }
      
      // helper HTTP-error
      function handleHttpError(context, err, options) {
          const transient = [
              "EHOSTUNREACH", "ECONNRESET", "ECONNREFUSED", "ETIMEDOUT",
              "socket hang up", "ENETUNREACH", "EAI_AGAIN", "ENOTFOUND"
          ];
      
          const msg = err && err.message ? err.message : String(err);
          const isTransient = transient.some(code => msg.includes(code));
      
          //info_error
      	//log(`${context}: ${isTransient ? "war vorübergehend im Intervall nicht erfolgreich:" : "Warnung"} (${msg})`, isTransient ? "info" : "warn");
          log(`${context}: ${isTransient ? "war vorübergehend im Intervall nicht erfolgreich:" : "Warnung"} (${msg})`, "info");
      
          // set Timestamp
          if (options && options.timestamp) {
              setState(options.timestamp, formatTime(Math.floor(Date.now() / 1000)), true);
          }
      }
      
      // smartMode GET report
      function getReport() {
          curlQueue.push({
              fn: async done => {      //async
                  const options = {
                      hostname: IP,
                      port: 80,
                      path: "/properties/report",
                      method: "GET",
                      timeout: 5000
                  };
                  const req = http.request(options, res => {
                      let data = "";
                      res.on("data", chunk => data += chunk);
                      res.on("end", async () => {     // async
                          try {
                              const json = JSON.parse(data);
                              if (json.properties && typeof json.properties.smartMode !== "undefined") {
                                  setState(dpSmartModeInfo, json.properties.smartMode, true);
                                  setState(dpTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                              }
                              //JSON
                              await processJson(json);
                          } catch (e) {
                              log("GET JSON Parse Fehler: " + e, "info"); //no valid JSON
                              setState(dpTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                          }
                          done();
                      });
                  });
                  req.on("error", err => { 
                      handleHttpError("HTTP GET", err, { timestamp: dpTimestamp }); 
                      done(); 
                  });
                  req.end();
              }
          });
          runQueue();
      }
      
      // smartMode POST
      function setSmartMode(val) {
          curlQueue.push({
              fn: done => {
                  const payload = JSON.stringify({ sn: SN, properties: { smartMode: val } });
                  const options = {
                      hostname: IP,
                      port: 80,
                      path: "/properties/write",
                      method: "POST",
                      headers: {
                          "Content-Type": "application/json",
                          "Content-Length": Buffer.byteLength(payload)
                      },
                      timeout: 5000
                  };
      
                  const req = http.request(options, res => {
                      let data = "";
                      res.on("data", chunk => data += chunk);
      
                      res.on("end", () => {
                          let ok = false;
      
                          if (data) {
                              try {
                                  const json = JSON.parse(data);
                                  ok = json.success === true;
                              } catch (e) {
                                  ok = false; // no valid JSON
                              }
                          }
      					setState(dpSetSmartModeResult, ok ? `ok set ${val}` : `error set ${val}`, true);
                          setState(dpTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                          done();
                      });
                  });
      
                  req.on("error", err => {
                      handleHttpError("HTTP POST", err, { timestamp: dpTimestamp });
                      setState(dpSetSmartModeResult, `error set ${val}`, true);
                      setState(dpTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                      done();
                  });
      
                  req.write(payload);
                  req.end();
              }
          });
      
          runQueue();
      }
       
      // MQTT state GET
      function getMqttStatus() {
          curlQueue.push({
              fn: done => {
                  const options = {
                      hostname: IP,
                      port: 80,
                      path: "/rpc?method=HA.Mqtt.GetStatus",
                      method: "GET",
                      timeout: 5000
                  };
                  const req = http.request(options, res => {
                      let data = "";
                      res.on("data", chunk => data += chunk);
                      res.on("end", () => {
                          try {
                              const json = JSON.parse(data);
                              const state = json.connected ? 1 : 0;
                              setState(dpMqttConnectInfo, state, true);
                              setState(dpMqttTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                          } catch (e) {
                              log(`MQTT JSON Parse Fehler: ${e}`, "info"); //no valid JSON
                              setState(dpMqttTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                          }
                          done();
                      });
                  });
                  req.on("error", err => { 
                      handleHttpError("HTTP MQTT GET", err, { timestamp: dpMqttTimestamp }); 
                      done(); 
                  });
                  req.end();
              }
          });
          runQueue();
      }
       
      // MQTT POST
      function setMqttConnect(val) {
          curlQueue.push({
              fn: done => {
                  const enable = !!val;
                  const payload = JSON.stringify({
                      sn: SN,
                      method: "HA.Mqtt.SetConfig",
                      params: {
                          config: {
                              enable: enable,
                              server: mqttBrokerIp,
                              port: mqttPort,
                              protocol: "mqtt",
                              username: mqttUsername,
                              password: mqttPassword
                          }
                      }
                  });
      
                  const options = {
                      hostname: IP,
                      port: 80,
                      path: "/rpc",
                      method: "POST",
                      headers: {
                          "Content-Type": "application/json",
                          "Content-Length": Buffer.byteLength(payload)
                      },
                      timeout: 5000
                  };
      
                  const req = http.request(options, res => {
                      let data = "";
                      res.on("data", chunk => data += chunk);
      
                      res.on("end", () => {
                          let ok = false;
      
                          // HTTP 200 = Verbindung ok, Request angekommen
                          if (res.statusCode === 200) {
                              if (data) {
                                  try {
                                      const json = JSON.parse(data);
                                      ok = json.success === true;
                                  } catch (e) {
                                      ok = false; // no valid JSON
                                  }
                              }
                          }
      					setState(dpSetMqttConnectResult, ok ? `ok set ${val}` : `error set ${val}`, true);
                          setState(dpMqttTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                          done();
                      });
                  });
      
                  req.on("error", err => {
                      handleHttpError("HTTP MQTT POST", err, { timestamp: dpMqttTimestamp });
                      setState(dpSetMqttConnectResult, `error set ${val}`, true);
                      setState(dpMqttTimestamp, formatTime(Math.floor(Date.now() / 1000)), true);
                      done();
                  });
      
                  req.write(payload);
                  req.end();
              }
          });
      
          runQueue();
      }
      
      // Generic POST function with result and timestamp handling
      function setControlDP(id, val) {
          if (!dpMap[id]) return;
          const { key, transform, result, resultTS } = dpMap[id];
          const value = transform ? transform(val) : val;
      
          curlQueue.push({
              fn: done => {
                  const payload = JSON.stringify({ sn: SN, properties: { [key]: value } });
                  const options = {
                      hostname: IP,
                      port: 80,
                      path: "/properties/write",
                      method: "POST",
                      headers: {
                          "Content-Type": "application/json",
                          "Content-Length": Buffer.byteLength(payload)
                      },
                      timeout: 5000
                  };
      
                  const req = http.request(options, res => {
                      let data = "";
                      res.on("data", chunk => data += chunk);
      
                      res.on("end", () => {
                          let ok = false;
      
                          // HTTP 200 = Connection established
                          if (res.statusCode === 200) {
                              if (data) {
                                  try {
                                      const json = JSON.parse(data);
                                      ok = json.success === true;
                                  } catch (e) {
                                      ok = false; // no valid JSON
                                  }
                              }
                          }
      
                          // Write success or failure to data points
                          if (result) {
                              const valueToSend = transform ? transform(val) : val;  // Wert, der gesendet wurde
                              setState(result, ok ? `ok set ${valueToSend}` : `error set ${valueToSend}`, true);
                          }
                          if (resultTS) setState(resultTS, formatTime(Math.floor(Date.now() / 1000)), true);
                          done();
                      });
                  });
                  
      			req.on("error", err => {
                      handleHttpError("HTTP POST " + key, err, { timestamp: resultTS });
                      if (result) {
                          const valueToSend = transform ? transform(val) : val;  // Wert, der gesendet wurde
                          setState(result, `error set ${valueToSend}`, true);
                      }
                      if (resultTS) setState(resultTS, formatTime(Math.floor(Date.now() / 1000)), true);
                      done();
                  });
                  req.write(payload);
                  req.end();
              }
          });
          runQueue();
      }
      
      //------------------------------------------------------------------------------------
      // JSON
      //------------------------------------------------------------------------------------
      async function processJson(obj) {
          try {
              if (!obj || !obj.product) return;
       
              const product = obj.product;
              const basePath = `${folderZendureApi}.${product}`;
       
              // create product folder if not exist
              if (!existsObject(basePath)) {
                  setObject(basePath, {
                      type: 'folder',
                      common: { name: product },
                      native: {},
                  });
              }
       
              // create main keys
              const mainKeys = ['timestamp', 'messageId', 'sn', 'version', 'product'];
              for (const key of mainKeys) {
                  if (obj[key] !== undefined) {
                      const statePath = `${basePath}.${key}`;
                      if (existsState(statePath)) {
                          setState(statePath, obj[key], true);
                      } else {
                          createState(statePath, obj[key], {
                              name: key,
                              type: typeof obj[key],
                              role: 'info',
                              read: true,
                              write: false,
                          });
                      }
                  }
              }
       
              // properties
              if (obj.properties) {
                  await iter(`${basePath}.properties`, obj.properties);
              }
       
              // PackData
              if (obj.packData && Array.isArray(obj.packData)) {
                  await iter(`${basePath}`, { packData: obj.packData, timestamp: obj.timestamp });
              }
          } catch (e) {
              log(`Error processing JSON: ${e}`, 'info');
          }
      }
       
      //------------------------------------------------------------------------------------
      // Battery
      //------------------------------------------------------------------------------------
      function getBatteryType(sn, model) {
        let batType = '';
        if (sn?.startsWith('A')) batType = 'AB1000';
        else if (sn?.startsWith('B')) batType = 'AB1000S';
        else if (sn?.startsWith('C')) batType = sn[3] === 'F' ? 'AB2000S' : sn[3] === 'E' ? 'AB2000X' : 'AB2000';
        else if (sn?.startsWith('F')) batType = 'AB3000X';
        if (model?.trim()) batType = model.trim();
        return batType || 'unknown';
      } 
      
      //------------------------------------------------------------------------------------
      // helper Iteration
      //------------------------------------------------------------------------------------
      async function iter(id, obj) {
          try {
      		if (!obj || typeof obj !== "object") {
                 //log("iter: Ungültiges oder leeres Objekt übersprungen", "info");
                 return;
                 }
      		for (let i in obj) {
                  if (i === 'packData' && Array.isArray(obj[i])) {
                      const ts = obj.timestamp ? obj.timestamp * 1000 : Date.now();
                      for (const pack of obj[i]) {
                          if (!pack.sn) continue;
                          const sn = pack.sn;
                          const path = `${id}.packData.${sn}`;
       
                          if (!existsObject(path)) {
                              setObject(path, {
                                  type: 'folder',
                                  common: { name: sn },
                                  native: {},
                              });
                          }
       
                          for (let [key, val] of Object.entries(pack)) {
                              const statePath = `${path}.${key}`;
                              switch (key) {
                                  case 'batcur':
                                      val = (val << 16 >> 16) / 10;
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Battery Current',
                                              type: 'number',
                                              desc: 'battery current',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: 'A',
                                          });
                                      }
                                      break;
      							case 'heatState':
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Heat State, 0: Not heating, 1: heating',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;		
      							case 'maxTemp':
                                      val = (val - 2731) / 10;
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Highest Akku Temperature',
                                              type: 'number',
                                              desc: 'maximum temperature',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: '°C',
                                          });
                                      }
                                      break;
                                  case 'maxVol':
                                      val = val / 100;
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Highest Cell Voltage',
                                              type: 'number',
                                              desc: 'highest cell voltage',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: 'V',
                                          });
                                      }
                                      break;
                                  case 'minVol':
                                      val = val / 100;
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Lowest Cell Voltage',
                                              type: 'number',
                                              desc: 'lowest cell voltage',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: 'V',
                                          });
                                      }
                                      break;
      							case 'packType':
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'packType',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;
      							case 'power':
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Battery Power',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: 'W',
                                          });
                                      }
      								break;
      							case 'sn':
                                  const batType = getBatteryType(val, pack.model);
                                  // Modelltyp als Datenpunkt setzen/erstellen
                                  const modelPath = `${path}.model`;
                                  if (existsState(modelPath)) {
                                      setState(modelPath, { val: batType, ts, ack: true });
                                      } else {
                                          createState(modelPath, batType, {
                                          name: 'Battery Model',
                                          type: 'string',
                                          role: 'text',
                                          read: true,
                                          write: false,
                                          });
                                         }
                                      break;
      							case 'socLevel':
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Battery SoC Level',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: '%',
                                          });
                                      }
      								break;
      							case 'softVersion':
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Battery Software Version',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
      								break;
      							case 'state':
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Battery, 0: Standby, 1: Charging, 2: Discharging',
                                              type: 'number',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
      								break;	
                                  case 'totalVol':
                                      val = val / 100;
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: 'Total Voltage',
                                              type: 'number',
                                              desc: 'total voltage',
                                              role: 'value',
                                              read: true,
                                              write: false,
                                              unit: 'V',
                                          });
                                      }
                                      break;
                                  default:
                                      if (existsState(statePath)) {
                                          setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                              name: key,
                                              type: typeof val,
                                              role: 'value',
                                              read: true,
                                              write: false,
                                          });
                                      }
                                      break;
                              }
                          }
                      }
                  } else {
                      const ts = Date.now();
                      const statePath = `${id}.${i}`;
                      let val = obj[i];
       
                      switch (i) {
                          case 'BatVolt':
                              val = val / 100;
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: 'Battery Voltage',
                                      type: 'number',
                                      desc: 'battery voltage',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                      unit: 'V',
                                  });
                              }
                              break;
      					case 'chargeMaxLimit':
                              val = val / 10;
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: 'maximum Charge Limit',
                                      type: 'number',
                                      desc: 'Battery Charge Limit Maximum',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                      unit: '%',
                                  });
                              }
                              break;
                          case 'acMode':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: 'acMode, 1: input mode / 2: output mode',
                                      type: 'number',
                                      desc: '1: charging / 2: discharging',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                  });
                              }
                              break;
      					case 'dataReady':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: 'Data Ready, 0: Not ready, 1: Ready',
                                      type: 'number',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                  });
                              }
                              break;	
      					case 'dcStatus':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: 'dcStatus, 0: Stopped, 1: Battery input, 2: Battery output',
                                      type: 'number',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                  });
                              }
                              break;						
      					case 'electricLevel':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: 'Total Battery Charge Level',
                                      type: 'number',
                                      desc: 'SoC',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                      unit: '%',
                                  });
                              }
                              break;
                          case 'gridInputPower':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: 'Grid Input Power to Battery',
                                      type: 'number',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                      unit: 'W',
                                  });
                              }
                              break;
                          case 'gridReverse':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: '0: Disabled, 1: Allowed reverse flow, 2: Forbidden reverse flow',
                                      type: 'number',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                  });
                              }
                              break;
                          case 'gridStandard':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: 'Grid connection standard 0: Germany 1: France 2: Austria',
                                      type: 'number',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                  });
                              }
                              break;
                          case 'gridState':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: 'GridState, 0: Not connected, 1: Connected',
                                      type: 'number',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                  });
                              }
                              break;									
                          case 'heatState':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: 'Heat State, 0: Not heating, 1: heating',
                                      type: 'number',
                                      role: 'value',
                                      read: true,
                                      write: false,
                                  });
                              }
                              break;		
      					case 'hyperTmp':
                              val = (val - 2731) / 10.0;
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'Enclosure Temperature',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          unit: '°C',
                                          });
                                      }
                              break;
      					case 'inputLimit':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'AC charging power limit to Battery',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          unit: 'W',
                                          });
                                      }
                              break;
      					case 'inverseMaxPower': 
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'Device Maximum Output Power Limit',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          unit: 'W',
                                          });
                                      }
                              break;
      					case 'minSoc':
                              val = val / 10;
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'min SoC',
                                          type: 'number',
                                          desc: 'minimum Battery SoCset',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          unit: '%',
                                          });
                                      }
                              break;
      					case 'outputHomePower':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'outputHomePower',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      							        unit: 'W',
                                          });
      								}
                              break;
      					case 'outputLimit':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'Output power limit',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      							        unit: 'W',
                                          });
      								}
                              break;
      					case 'outputPackPower':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'Output power to battery pack (charging)',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      							        unit: 'W',
                                          });
      								}
                              break;
      					case 'packInputPower':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'Battery pack input power (discharging)',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      							        unit: 'W',
                                          });
      								}
                              break;
      					case 'packNum':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'Number of battery packs',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          });
      								}
                              break;
      					case 'packState':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'Battery State, 0: Standby, 1: Charging, 2: Discharging',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          });
      								}
                              break;
      					case 'pass':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'Bypass, 0: No, 1: Yes',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          });
      								}
                              break;
      					case 'pvStatus':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'PV State producing, 0: Stopped, 1: Running',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          });
      								}
                              break;	
      					case 'remainOutTime':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'Estimated discharge time in minutes, if not predictable: 59940',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      									unit: 'min',
                                          });
      								}
                              break;		
      					case 'reverseState':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'Reverse flow, 0: No, 1: Reverse flow',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          });
      								}
                              break;			
      					case 'rssi':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: 'Received Signal Strength Indicator',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      									unit: 'dBm',
                                          });
      								}
                              break;
      					case 'smartMode':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
      									createState(statePath, val, {
                                          name: '1: parameter written to RAM / 0: parameter is written to flash.',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          });
      								}
                              break;		
      					case 'socLimit':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'socLimit-Info 0: normal, 1: Charge limit reached, 2: Discharge limit reached',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          });
                                      }
                              break;
      					case 'socSet':
                              val = val / 10;
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'max SoC',
                                          type: 'number',
                                          desc: 'maximum Battery SoCset',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          unit: '%',
                                          });
                                      }
                              break;
      					case 'socStatus':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'Info of Calibrating, 0: No / 1: Calibrating',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
                                          });
                                      }
                              break;
      					case 'solarInputPower':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'Total Solar Input Power',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      									unit: 'W',
                                          });
                                      }
                              break;
                          case 'solarPower1':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'Solar line 1 input power',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      									unit: 'W',
                                          });
                                      }
                              break;
                          case 'solarPower2':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'Solar line 2 input power',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      									unit: 'W',
                                          });
                                      }
                              break;
                          case 'solarPower3':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'Solar line 3 input power',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      									unit: 'W',
                                          });
                                      }
                              break;
                          case 'solarPower4':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'Solar line 4 input power',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      									unit: 'W',
                                          });
                                      }
                              break;
                          case 'solarPower5':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'Solar line 5 input power',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      									unit: 'W',
                                          });
                                      }
                              break;
                          case 'solarPower6':
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ts, ack: true });
                                      } else {
                                          createState(statePath, val, {
                                          name: 'Solar line 6 input power',
                                          type: 'number',
                                          role: 'value',
                                          read: true,
                                          write: false,
      									unit: 'W',
                                          });
                                      }
                              break;
                          case 'timestamp':
                              const timestampVal = obj[i];
                              const timestampFormatted = formatTime(timestampVal);
                              if (existsState(statePath)) setState(statePath, timestampVal, true);
                              else createState(statePath, timestampVal, {
                                  name: 'unix timestamp (timestamp)',
                                  type: 'number',
                                  desc: 'unix timestamp in Seconds since Jan 01 1970 (UTC)',
                                  role: 'value',
                                  read: true,
                                  write: false,
                              });
                              if (existsState(id + '.timeUpdateTimestamp')) setState(id + '.timeUpdateTimestamp', timestampFormatted, true);
                              else createState(id + '.timeUpdateTimestamp', timestampFormatted, {
                                  name: 'TimeUpdate (timestamp)',
                                  type: 'string',
                                  desc: 'unix timestamp in readable Format (timestamp)',
                                  read: true,
                                  write: false,
                              });
                              break;
                          case 'ts':
                              const tsVal = obj[i];
                              const tsFormatted = formatTime(tsVal);
                              if (existsState(statePath)) setState(statePath, tsVal, true);
                              else createState(statePath, tsVal, {
                                  name: 'unix timestamp (ts)',
                                  type: 'number',
                                  desc: 'unix timestamp in Seconds since Jan 01 1970 (UTC)',
                                  role: 'value',
                                  read: true,
                                  write: false,
                              });
                              if (existsState(id + '.timeUpdateTs')) setState(id + '.timeUpdateTs', tsFormatted, true);
                              else createState(id + '.timeUpdateTs', tsFormatted, {
                                  name: 'TimeUpdate (ts)',
                                  type: 'string',
                                  desc: 'unix timestamp in readable Format (ts)',
                                  read: true,
                                  write: false,
                              });
                              break;			
                          default:
                              if (existsState(statePath)) {
                                  setState(statePath, { val, ack: true });
                              } else {
                                  createState(statePath, val, {
                                      name: i,
                                      type: typeof val,
                                      role: 'value',
                                      read: true,
                                      write: false,
                                  });
                              }
                              break;
                      }
                  }
              }
          } catch (e) {
              log(`Error in iter: ${e}`, 'info');
          }
      }
      

      Historisch:
      Frühere Versionen der Scripte sind hier (im Spoiler) zu finden.
      Die kompletten Scripte sind zu groß für diesen Post, daher habe ich sie dort ausgelagert.

      posted in JavaScript
      maxclaudi
      maxclaudi
    • RE: Zendure Solar Flow ohne Cloud MQTT-Broker MIT Authentication

      @rene55
      👍
      DeviceID ist klar denke ich?

      Nennt sich beim Solar Flow Adapter: Device Key

      posted in Blockly
      maxclaudi
      maxclaudi
    • RE: Test Adapter Zendure Solarflow

      @rene55

      also ich hab vorher noch sichergestellt, dass der Server 100% keine Anmeldedaten verlangt und port 1883 frei von anderen Instanzen ist.

      edit:
      passt, laut den Bildern von Arne muss nicht mal der Port angegeben werden 🙂
      Wird wohl intern automatisch auf port 1883 gesetzt.
      Sollte der Adapter nicht die Objekte erstellen, dann einfach Port mit angeben.

      posted in Tester
      maxclaudi
      maxclaudi
    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      @michi-0 sagte in Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro):

      @maxclaudi
      Ihr habt ein Tempo...

      Mir würde vermutlich reichen, wenn mir der SmartMode Status in einem Datenpunkt angezeigt wird und ich den kontrollieren und steuern kann. Den Rest würde ich vermutlich auch weiterhin nur über MQTT machen.

      👍

      Zendure hat auch angekündigt, dass sie den Datenpunkt irgendwann auch aufnehmen werden, dann wäre alles andere überflüssig.
      Aber wenn nicht oder bis dahin hier die Lösungen.
      2 Scripte, die von den Funktionen identisch sind.
      Script 1: nur für iob unter Linux.
      Srcipt 2: plattformunabhängig, also auch für Windows.

      @Daniel-8 und @Michi-0 , bitte testen:


      Beschreibung für beide scripts:
      Script hat folgende Funktion:

      • Alle 60 Sekunden (anpassbar) wird properties/report geholt.
      • timestamp wird in ein lesbares Datum umgewandelt und in dpTimestamp geschrieben.
      • Aktueller smartMode wird in dpSmartModeInfo geschrieben (read only).
      • Wenn man setSmartMode mit 0 oder 1 steuert, sendet das Script automatisch einen POST an /properties/write und schreibt "ok" oder "error" in setResult.

      Beim Start werden vier Datenpunkte erstellt.
      Diese können oben im script konfiguriert werden.

      const dpSmartModeInfo = "0_userdata.0.zendureSmartMode.smartModeInfo";
      const dpSetSmartMode = "0_userdata.0.zendureSmartMode.setSmartMode";
      const dpSetSmartModeResult = "0_userdata.0.zendureSmartMode.setResult";
      const dpTimestamp = "0_userdata.0.zendureSmartMode.timestamp";

      60 Sekunden Interval ist unkritisch und aktuell.
      Bei Bedarf kann das auch oben im Script geändert werden:
      const intervalGet = 60; // Sekunden

      Oben trägt man auch die IP und Seriennummer des Zendure Geräts ein:

      const IP = "192.168.177.103"; // IP Zendure
      const SN = "EXXXXXXXXXXXXX0"; // Seriennummer Zendure

      Die Datenpunkte können mit anderen scripts / Blocklys gelesen werden oder dpSetSmartMode auf 1 oder 0 gesetzt werden.
      smartMode:1 ist wichtig.


      Script 1, mit curl umgesetzt, funktioniert nur wenn iob auf Linux läuft:

      // konfiguration
      
      const dpSmartModeInfo    = "0_userdata.0.zendureSmartMode.smartModeInfo";
      const dpSetSmartMode     = "0_userdata.0.zendureSmartMode.setSmartMode";
      const dpSetSmartModeResult = "0_userdata.0.zendureSmartMode.setResult";
      const dpTimestamp        = "0_userdata.0.zendureSmartMode.timestamp";
      
      const intervalGet = 60;                 // Sekunden
      const IP = "192.168.177.103";           // IP des Zendure Geräts
      const SN = "EXXXXXXXXXXXXX0";           // Seriennummer
      
      //-----------
      // dp anlegen
      
      createState(dpSmartModeInfo, 0, {
          name: "SmartMode Info",
          type: "number",
          role: "state",
          read: true,
          write: false,
          min: 0,
          max: 1
      }, () => {});
      
      createState(dpSetSmartMode, 0, {
          name: "SmartMode Set",
          type: "number",
          role: "state",
          read: true,
          write: true,
          min: 0,
          max: 1
      }, () => {});
      
      createState(dpSetSmartModeResult, "", {
          name: "SmartMode Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
      
      createState(dpTimestamp, "", {
          name: "Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
      
      
      // time
      
      function formatTime(ts) {
          // ts ist Unix Sekunden
          const d = new Date(ts * 1000);
          const pad = n => n.toString().padStart(2, "0");
          return `${pad(d.getDate())}.${pad(d.getMonth()+1)}.${d.getFullYear().toString().slice(-2)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
      }
      
      
      //curl HTTP GET
      
      function getReport() {
          const cmd = `curl -s "http://${IP}/properties/report"`;
          exec(cmd, (error, stdout, stderr) => {
              if (error) {
                  console.error("GET Fehler:", stderr);
                  return;
              }
              try {
                  const data = JSON.parse(stdout);
                  if (data && data.timestamp) {
                      setState(dpTimestamp, formatTime(data.timestamp), true);
                  }
                  if (data && data.properties && typeof data.properties.smartMode !== "undefined") {
                      setState(dpSmartModeInfo, data.properties.smartMode, true);
                  }
              } catch (e) {
                  console.error("JSON Parse Fehler:", e);
              }
          });
      }
      
      
      //curl HTTP POST zum Setzen
      
      function setSmartMode(val) {
          const payload = `{"sn":"${SN}","properties":{"smartMode":${val}}}`;
          const cmd = `curl -s -X POST "http://${IP}/properties/write" -H "Content-Type: application/json" -d '${payload}'`;
          exec(cmd, (error, stdout, stderr) => {
              if (error) {
                  console.error("POST Fehler:", stderr);
                  setState(dpSetSmartModeResult, "error", true);
                  return;
              }
              setState(dpSetSmartModeResult, "ok", true);
          });
      }
      
      
      // interval
      
      getReport();  // sofort bei start
      schedule(`*/${intervalGet} * * * * *`, getReport); // alle x Sekunden
      
      // trigger wenn SmartMode gesetzt wird
      
      on({id: dpSetSmartMode, ack: false}, obj => {
          const val = parseInt(obj.state.val, 10);
          if (val === 0 || val === 1) {
              setSmartMode(val);
          }
      });
      

      Script 2:
      Funktionen identisch zum ersten.
      Skript ist plattformunabhängig und sollte damit für Windows-Installationen von ioBroker funktionieren (ungetestet).
      Statt curl werden ioBroker/Node.js vorhandene Standardbibliotheken (http und https) verwendet.

      // konfiguration
      const dpSmartModeInfo      = "0_userdata.0.zendureSmartMode.smartModeInfo";
      const dpSetSmartMode       = "0_userdata.0.zendureSmartMode.setSmartMode";
      const dpSetSmartModeResult = "0_userdata.0.zendureSmartMode.setResult";
      const dpTimestamp          = "0_userdata.0.zendureSmartMode.timestamp";
      
      const intervalGet = 60;                 // Sekunden
      const IP = "192.168.177.103";           // IP des Zendure Geräts
      const SN = "EXXXXXXXXXXXXX0";           // Seriennummer
      
      const http = require("http");           // Node.js Standardmodul
      
      
      //-----------
      // dp
      
      createState(dpSmartModeInfo, 0, {
          name: "SmartMode Info",
          type: "number",
          role: "state",
          read: true,
          write: false,
          min: 0,
          max: 1
      }, () => {});
      
      createState(dpSetSmartMode, 0, {
          name: "SmartMode Set",
          type: "number",
          role: "state",
          read: true,
          write: true,
          min: 0,
          max: 1
      }, () => {});
      
      createState(dpSetSmartModeResult, "", {
          name: "SmartMode Set Result",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
      
      createState(dpTimestamp, "", {
          name: "Timestamp",
          type: "string",
          role: "info",
          read: true,
          write: false
      }, () => {});
      
      
      // time
      
      function formatTime(ts) {
          const d = new Date(ts * 1000);
          const pad = n => n.toString().padStart(2, "0");
          return `${pad(d.getDate())}.${pad(d.getMonth()+1)}.${d.getFullYear().toString().slice(-2)} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
      }
      
      
      //HTTP GET
      
      function getReport() {
          const options = {
              hostname: IP,
              port: 80,
              path: "/properties/report",
              method: "GET",
              timeout: 3000
          };
      
          const req = http.request(options, res => {
              let data = "";
              res.on("data", chunk => data += chunk);
              res.on("end", () => {
                  try {
                      const json = JSON.parse(data);
                      if (json.timestamp) {
                          setState(dpTimestamp, formatTime(json.timestamp), true);
                      }
                      if (json.properties && typeof json.properties.smartMode !== "undefined") {
                          setState(dpSmartModeInfo, json.properties.smartMode, true);
                      }
                  } catch (e) {
                      console.error("GET JSON Parse Fehler:", e);
                  }
              });
          });
      
          req.on("error", err => console.error("HTTP GET Fehler:", err.message));
          req.end();
      }
      
      
      //HTTP POST zum Setzen
      
      function setSmartMode(val) {
          const payload = JSON.stringify({
              sn: SN,
              properties: { smartMode: val }
          });
      
          const options = {
              hostname: IP,
              port: 80,
              path: "/properties/write",
              method: "POST",
              headers: {
                  "Content-Type": "application/json",
                  "Content-Length": Buffer.byteLength(payload)
              },
              timeout: 3000
          };
      
          const req = http.request(options, res => {
              let data = "";
              res.on("data", chunk => data += chunk);
              res.on("end", () => {
                  if (res.statusCode >= 200 && res.statusCode < 300) {
                      setState(dpSetSmartModeResult, "ok", true);
                  } else {
                      console.error("POST Antwort:", res.statusCode, data);
                      setState(dpSetSmartModeResult, "error", true);
                  }
              });
          });
      
          req.on("error", err => {
              console.error("HTTP POST Fehler:", err.message);
              setState(dpSetSmartModeResult, "error", true);
          });
      
          req.write(payload);
          req.end();
      }
      
      
      // interval
      
      getReport();  // sofort einmal abrufen
      schedule(`*/${intervalGet} * * * * *`, getReport); // alle x Sekunden
      
      
      // trigger wenn SmartMode gesetzt wird
      
      on({id: dpSetSmartMode, ack: false}, obj => {
          const val = parseInt(obj.state.val, 10);
          if (val === 0 || val === 1) {
              setSmartMode(val);
          }
      });
      

      Hinweis: Bei den Scripts wird davon ausgegangen, dass Zendure für HTTP den Standarport 80 verwendet wie von @Daniel-8 getestet.
      Wenn es bei jemand nicht so sein sollte, dann im script die 2 Port (port: 80,) bei Get und POST anpassen.

      posted in JavaScript
      maxclaudi
      maxclaudi
    • RE: Zendure Solar Flow ohne Cloud MQTT-Broker MIT Authentication

      @rene55
      und Alle:

      Die generierten Passwörter wurden getestet, sind richtig und funktionieren.

      Aber:
      Der iobroker SolarFlow-Adapter hat keine Zugangsdaten für den lokalen Betrieb!
      Deshalb funktioniert der Adapter von @nograx NUR wenn mann die Anmeldedaten mit in die URL übergibt.

      Es wäre schön, wenn @nograx irgendwann in der Instanz des solarflow-Adapters eine Möglichkeit zum abspeichern von User und Password für einen lokalen MQTT-Server einpflegen würde.
      Geht aber auch so!

      Mit mosquitto-Server ausführlich getestet.

      Wenn ein mosquitto-Server läuft verbinden sich die Zendure Geräte mit Passwörtern lokal einwandfrei.

      posted in Blockly
      maxclaudi
      maxclaudi
    • RE: Test Adapter Zendure Solarflow

      @lesiflo
      Lokal ist super.
      Es wird zuverlässig, schnell und oft aktualisiert 🙂

      posted in Tester
      maxclaudi
      maxclaudi
    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      @daniel-8 sagte in Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro):

      Ja mehr gab es nicht

      👍 Dann mach ich mich mal an die Arbeit 🙂

      posted in JavaScript
      maxclaudi
      maxclaudi

    Latest posts made by maxclaudi

    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      @daniel-8 sagte in Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro):

      @maxclaudi

      Habe das neue Script getestet und erhalte folgende error Meldungen

      javascript.0	20:17:09.451	error	
      ReferenceError: Cannot access 'dpMap' before initialization
      javascript.0	20:17:09.452	error	
          at script.js.common.Garten.Balkonkraftwerke.Zendure_http_Abfrage_Set:432:13
      javascript.0	20:17:09.452	error	
          at script.js.common.Garten.Balkonkraftwerke.Zendure_http_Abfrage_Set:1627:3```
      

      Eingangspost aktualisiert, bitte Rückmeldung.
      Jetzt werden alle erhältlichen Batterie-Modelle erkannt die es von Zendure gibt, inkl. alle X Modelle 🙂

      posted in JavaScript
      maxclaudi
      maxclaudi
    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      Werde immer wieder privat gefragt, was „besser“ ist:
      HTTP oder MQTT?
      Deshalb antworte ich hier einmal öffentlich darauf – vielleicht hilft’s ja auch anderen weiter. 😊

      Vorweg:
      Ich habe das Script für Euch geschrieben.
      Ich selbst kann es gar nicht verwenden, weil mein Zendure-Gerät keinen Web-Server hat.
      Aber ich helfe gern weiter damit Befehle im RAM landen – und nicht ständig ins Flash geschrieben werden.
      Wollte auch einfach mal sehen, was technisch machbar ist,

      Nachdem ich mich intensiver mit der zenSDK, den Keys & Values und etlichen Logs (danke an alle, die mir was geschickt haben!) beschäftigt habe,
      wurde schnell klar: Da geht richtig was 🙂
      Selbst den MQTT-Client aktivieren, deaktivieren und konfigurieren.

      Der HTTP-Server scheint bei allen Geräten immer aktiv zu sein.
      Zu viel MQTT verursacht schlicht mehr Traffic,
      während HTTP-Anfragen quasi 0 Belastung bringen.

      Wenn ich ein Zendure-Gerät mit HTTP-Webserver hätte,
      würde ich mein Script simultan mit MQTT laufen lassen.
      Ich würde dabei genau die im Script vorgesehenen Intervalle nutzen:

      • 60 Sekunden für get report / smartMode
      • 300 Sekunden für die MQTT-Überwachung.

      Wichtig ist mir auch der Hinweis auf smartMode = 1:
      Bitte überwacht das – und nutzt es bei eigener Automatik oder Blockly-Steuerung unbedingt mit!

      Viele hatten anfangs Zweifel, ob das wirklich funktioniert oder überhaupt etwas bringt.
      Aber ich verweise da ganz offiziell auf die zenSDK von Zendure und Entwickler David:
      👉 zenSDK smartMode – Dokumentation

      Dort steht eindeutig:
      1: The setting parameter is not written to flash.
      0: The setting parameter is written to flash.

      Selbst bei meinem HUB2000 (09/2024) lässt sich smartMode:1 setzen – und es funktioniert einwandfrei.

      Ob Ihr das Script komplett nutzt oder nur teilweise, bleibt natürlich jedem selbst überlassen.
      Beim Get Report werden ohnehin alle Werte abgefragt – ob ihr nur smartMode daraus verwendet oder mehr,
      macht keinen Unterschied und verursacht auch keinen zusätzlichen Traffic.


      Zur eigenen Entscheidungsfindung:

      1. Grundsätzlich: HTTP vs MQTT bei Zendure
      Merkmal HTTP (zenSDK / REST) MQTT (lokal)
      Verbindungstyp Direkt (Client → Gerät, kein Broker nötig) Broker-basiert, Gerät <-> ioBroker
      Last / Traffic Nur bei Abruf oder Befehl → minimal Dauerverbindung, Keepalive, Topics → leicht mehr Traffic
      Latenz Antwort dauert typischerweise 500–3000 ms Quasi sofort (50–200 ms)
      Stabilität Sehr robust, solange Gerät erreichbar ist Instabil, wenn Broker oder WLAN wackeln → Gerät schaltet MQTT selbständig aus
      Befehlsumfang Groß (Properties, Steuerbefehle etc.) (noch?) Eingeschränkt (z. T. nur subset, meist Status und einfache Kommandos)
      Einrichtung / Wartung Kein Setup nötig, immer aktiv Muss im Gerät aktiviert bleiben – sonst inaktiv oder bei Brokerverlust nach Timeout deaktiviert
      Rückmeldung (ACK/State) Nur auf Anfrage (Polling nötig) Automatisch per Publish bei jeder Änderung

      1. ioBroker-Betrieb

      HTTP (zenSDK)

      Vorteile
      Immer verfügbar (lokaler Webserver läuft immer).
      Alle Befehle nutzbar (auch seltene/komplexe).
      Kein Risiko durch MQTT-Abbruch.
      Kein zusätzlicher MQTT-Traffic im lokalen Netz.

      Nachteile
      Kein Echtzeit-Push, man muss pollen (z. B. alle 30–60 s).
      Etwas höhere Latenz bei jeder Anfrage (2–3 s).

      Fazit: Sehr stabil, vollständige Kontrolle, braucht aber periodisches Polling.


      MQTT (lokal)

      Vorteile
      Schnelle Push-Updates (kein Polling nötig).
      Gut, um Zustände automatisch im ioBroker zu aktualisieren.
      Einfach lesbar über MQTT-Adapter.

      Nachteile
      Gerät schaltet MQTT von selbst ab, wenn Broker nicht erreichbar ist → man verliert Verbindung ohne Warnung.
      Muss über HTTP oder App reaktiviert werden.
      (Noch) weniger Steuerbefehle verfügbar.

      Fazit: Ideal als Zustands-Kanal, evtl. nicht als primäre Steuerung.


      1. Kombination
        bewährter und stabiler Hybrid-Ansatz:
      Aufgabe Empfohlenes Protokoll
      smartMode überwachen / schalten → HTTP GET (60s) /POST
      Zustände lesen (Status, SOC, Power etc.) → MQTT (solange verbunden)
      Fallback, wenn MQTT ausfällt → HTTP-GET (Polling 300 s)
      Befehle senden (z. B. Mode, Limits, AC/DC On/Off) → HTTP-POST
      MQTT-Status überwachen (aktiv/inaktiv) → HTTP-Poll alle 2–5 Min. (prüfen mqttConnect DP)

      Das ergibt:

      • minimale Netzlast,
      • volle Befehlsabdeckung,
      • automatischen Fallback, falls MQTT aussteigt.

      1. Praxis-Tipp für ioBroker-Skript
      • Polling-Intervall HTTP: 60s ist perfekt, 30s nur wenn man schnelle Reaktion braucht.
      • MQTT aktivieren: per HTTP einmalig beim Start oder nach Timeout prüfen (dpSetMqttConnect setzen).
      • Bei MQTT-Ausfall: automatisch HTTP-only weiterarbeiten.

      1. Fazit
      Auswertung Beschreibung
      + HTTP als Primärsteuerung Vollständig, robust, kein Verbindungsstress
      + MQTT als Statuskanal Automatische Statusupdates, schnell
      + Simultan Ideal: HTTP für Befehle + MQTT für Zustände
      - Nur MQTT allein kann sich abschalten, leicht mehr Traffic
      - Nur HTTP (ohne Polling) Kein Live-Update – Status hinkt hinterher
      posted in JavaScript
      maxclaudi
      maxclaudi
    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      Update 15.10.2025 14:10h
      Das aktualisierte Script ist im ersten Post des Threads zu finden.


      Neu:

      1. Erfolgreiche HTTP-Verbindung und Wert-Setzung werden jetzt ausgewertet
      → Im Result-Datenpunkt erscheint nun eine Rückmeldung:
      "ok set <wert>" oder "error set <wert>".

      2. Für die Auswertung der aktuellen Zustände
      bitte ausschließlich die read-only-Datenpunkte unter properties verwenden.

      3. Die Control-Datenpunkte:

      • setSmartMode
      • setMqttConnect
      • setAcMode
      • setInputLimit
      • setOutputLimit
      • setSocSet
      • setMinSoc
      • setGridReverse
      • setGridStandard
      • setInverseMaxPower
        werden jetzt mit -1 initialisiert
        und nach jedem Setzen automatisch wieder auf -1 zurückgesetzt.

      Jeder gesetzte Wert wird vor der Ausführung auf Gültigkeit überprüft.
      Ist der Wert nicht erlaubt, wird die Anfrage verworfen und es erscheint im Log:

      Value xxx for id is not allowed
      

      4. Vorübergehende Verbindungsprobleme (WiFi, Netzwerk etc.)
      werden nur noch als "info" im Log protokolliert – keine Warnungen mehr.


      💡 Hinweis: Warum -1 bei den Control-Datenpunkten?

      Nach jedem Schaltvorgang wird der Wert automatisch wieder auf -1 gesetzt.
      Das stellt sicher, dass auch über Blockly oder andere Skripte
      derselbe Befehl mehrfach zuverlässig gesendet werden kann –
      selbst wenn der vorherige Wert identisch war.

      Hintergrund:
      In ioBroker kann über Blockly kein ack: false gesetzt werden.
      Ohne diesen automatischen Rücksprung auf -1
      würde ein identischer Wert nicht erneut übertragen werden.

      Der Mechanismus sorgt also für sauberes, wiederholbares Schalten – auch mit Blockly!

      Beispiel:
      Wenn über HTTP ein Wert auf 1 gesetzt und später über MQTT auf 0 geändert wurde,
      würde der Datenpunkt (vom HTTP-zendSDK) noch 1 enthalten –
      ein erneutes Senden von 1 wäre dann nicht möglich.
      Mit dem -1 -Reset funktioniert das jetzt jederzeit korrekt.


      Hinweis für VIS-Benutzer

      Ich selbst verwende kein VIS.
      VIS-Nutzer können aber einfach den aktuellen Status aus den
      properties-(read-only)-Datenpunkten visualisieren.

      Zum Steuern und Setzen von Funktionen wie

      • smartMode (1 = ein / 0 = aus) oder
      • MQTT aktivieren (1) bzw. deaktivieren (0)

      können Buttons angelegt werden.
      Dabei wird jeweils ein Button für „Ein“ und einer für „Aus“ benötigt.


      Mir sind möglicherweise noch weitere beschreibbare Keys bekannt.
      Diese habe ich bewusst nicht ins Script aufgenommen,
      da sie ohne Testgerät nicht sicher geprüft werden können.

      Viel Spaß ☀

      posted in JavaScript
      maxclaudi
      maxclaudi
    • RE: OpenDTU-onBattery Blockly DPL Ein/Aus

      @beowolf

      update 14.10.2025 10:45h


      MQTT-Datenpunkte

      Die Steuerung erfolgt ausschließlich über MQTT.
      Dabei gilt:

      1. Automatisch angelegt:

      mqtt.x.<baseTopic>.dpl.dplState
      
      • wird automatisch erstellt
      • Liefert den aktuellen Status des Dynamic Power Limiters
      • Werte: 0 = aus, 1 = ein (nur lesbar)

      2. Manuell anlegen (Pflicht!)

      mqtt.x.<baseTopic>.dpl.dplCmd
      

      Datenpunkt dplCmd muss manuell erstellt werden, sonst funktioniert die MQTT-Steuerung nicht.

      ❗ Sollte dplCmd automatisch angelegt worden sein → löschen und manuell neu anlegen!

      Beispiel-Einstellungen:

      "common": {
        "name": "dplCmd",
        "desc": "DPL on: 1, off: 0",
        "role": "state",
        "type": "number",
        "read": true,
        "write": true,
        "min": -1,
        "max": 1,
        "def": -1
      }
      

      ⚠️ Nicht als Boolean anlegen!


      ⚙️ Funktionsweise

      dplCmd: 1 → DPL-Steuerung aktiv
      dplCmd: 0 → DPL deaktiviert, alle "solarbetriebenen" WR gehen automatisch auf maximale Leistung.

      Nach jedem Schaltvorgang setzt sich dplCmd automatisch wieder auf -1 zurück.

      Damit wird sichergestellt, dass auch bei ioBroker über Blockly oder Scripts mehrfach derselbe Befehl zuverlässig gesendet werden kann –
      selbst wenn der vorherige Wert identisch war.

      Hintergrund:
      In ioBroker kann über Blockly kein ack: false gesetzt werden.
      Ohne den automatischen Rücksprung auf -1 würde der identische Wert daher nicht erneut übertragen.
      Der Mechanismus sorgt also für sauberes und wiederholbares Schalten – auch bei Blockly!

      oDTUoB-maxLimit-mqtt_DPLonOff.png


      Schritt-für-Schritt für ioBroker-Nutzer

      1. WebUI OpenDTU-oB-Patch öffnen → Dynamic Power Limiter einmal ein- oder ausschalten
        → dplState wird automatisch angelegt.

      2. iobroker Objekte → Expertenmodus aktivieren → manuell neuen Datenpunkt anlegen:

      mqtt.x.<baseTopic>.dpl.dplCmd
      

      Typ: state, Zahl (number), siehe Einstellungen oben.

      1. DPL erneut in der WebUI schalten → danach ist MQTT-Steuerung aktiv.

      2. Ablesen des Status
        → Nur über dplState (0/1)
        → dplCmd wird nach dem Senden automatisch auf -1 gesetzt.

      3. Nach DPL-Deaktivierung
        → "solarbetriebene" Wechselrichter gehen automatisch auf volle Leistung (kein manuelles Setzen nötig)
        → "batteriebetriebene" Wechselrichter verhalten sich wie im original Repo.


      Hinweise zur Nutzung

      • Jeder Schaltvorgang schreibt in den Flash-Speicher → nicht wie ein Blinker ständig ein-/ausschalten!
        Etwas Geduld nach jedem Wechsel schont Hardware & Nerven. 😉

      • Patch ist minimalinvasiv:
        Keine Zusatzdatenpunkte
        Kein Eingriff in nicht benötigte Codebereiche
        MQTT-Race-Conditions werden sauber abgefangen

      • Rückkehr zur Original-Firmware jederzeit über Web-Firmware möglich.


      ⚠️ Lizenz & Haftung

      Diese Firmware basiert auf OpenDTU-onBattery (GPLv2, Fork von OpenDTU).
      Sie enthält eigene Anpassungen und wird nicht offiziell unterstützt.

      Verwendung auf eigene Verantwortung – keine Haftung für Schäden oder Fehlfunktionen.

      OpenDtu-OnBattery_2025.09.16DplOff-maxLimit-MQTT.generic_esp32s3.zip

      Hier im Thread finden sich weitere Firmware-Builds; direkte Links dazu sind im Eingangspost zu finden.

      posted in Blockly
      maxclaudi
      maxclaudi
    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      @mabbi
      Dankeschön 👍
      geht nur ums auswerten SF800 (PRO)<>SF2400

      posted in JavaScript
      maxclaudi
      maxclaudi
    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      Bisher habe ich nur ein Log von @daniel-8 (SF800Pro) – ohne seine Hilfe und dieses Log hätte es das Script nicht gegeben.
      Danke, Daniel!


      Mir fehlt noch ein Log eines SF2400AC.

      edit: Danke @Mabbi 👍

      posted in JavaScript
      maxclaudi
      maxclaudi
    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      @daniel-8

      Update 10.10.2025 15:47h
      Hier eine Liste typischer Fehlermeldungen, die in der ioBroker-JavaScript-Sandbox bei httpGet oder http.request auftreten können – selbst wenn alles korrekt programmiert ist, aber die Verbindung oder das Gerät Probleme macht.

      Fehlermeldung Bedeutung Typische Ursache
      EHOSTUNREACH Host (IP) nicht erreichbar Gerät offline, WLAN-Repeater gewechselt, Mesh-Roaming, kurzzeitig kein Netz
      ECONNREFUSED Verbindung aktiv abgelehnt Gerät ist erreichbar, Dienst (Port 80) reagiert gerade nicht, z. B. Neustart
      ETIMEDOUT Timeout (keine Antwort innerhalb der Zeit) Gerät zu beschäftigt, schwaches WLAN, Ping funktioniert, HTTP antwortet zu spät
      socket hang up Verbindung unerwartet beendet Gerät hat Verbindung abgebrochen (z. B. keine Antwort innerhalb der Zeit)
      ECONNRESET Verbindung vom Zielgerät zurückgesetzt Meist firmwareseitig, Request nicht vollständig verarbeitet
      ENETUNREACH Netzwerkroute nicht erreichbar Router kurzzeitig ohne Route zur IP (häufig bei WLAN-Mesh während Kanalwechsel)
      EAI_AGAIN Temporäres DNS-Problem DNS-Server antwortet nicht schnell genug
      ENOTFOUND Hostname konnte nicht aufgelöst werden IP ok, aber Name nicht gefunden (z. B. bei Geräten mit mDNS wie zendure.local)

      Hinweis:
      Dies sind harmlose, verbindungsbedingte Fehler. Solche Kommunikationsfehler können auch bei anderen Protokollen (z. B. MQTT) auftreten.

      Bisher wurden diese Fälle als „Fehler“ im LOG angezeigt, damit man sieht, dass ein Abruf oder Befehl nicht ausgeführt werden konnte. Das Script arbeitet jedoch zuverlässig weiter.


      Script-Update:

      1. Die üblichen Verdächtigen werden jetzt nicht mehr als „Fehler“, sondern als „Warnung“ ins LOG geschrieben – das hält das LOG sauberer und reduziert Panik.

      2. Ein zusätzlicher Datenpunkt SetInverseMaxPower wurde hinzugefügt.

      Viel Spaß! ☀

      posted in JavaScript
      maxclaudi
      maxclaudi
    • RE: Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro)

      @daniel-8 sagte in Zendure SmartMode:1 SolarFlow2400 AC SolarFlow800 ( u. Pro):

      @maxclaudi

      Heute morgen ziemlich um die gleiche Zeit (20 Minuten früher) eine andere Meldung. Ich schätzte da ist wieder der Speicher irgendwie hochgefahren.

      error: javascript.0 (32915) script.js.common.Garten.Balkonkraftwerke.Zendure_http_Abfrage_Set: HTTP GET Fehler: socket hang up
      

      Danke Daniel.

      Kurzinfo zum „socket hang up“:
      Die Verbindung zum Zendure-Gerät wurde vom Gerät oder vom Netzwerk vorzeitig beendet – völlig normal und unkritisch.

      Passiert z. B. wenn

      • das Gerät kurz nicht erreichbar ist (WLAN-Störung, Interferenzen, Mesh-Wechsel, Sleep-Modus, Reichweite usw.),
      • oder die Antwort zu lange dauert und http.request den Socket schließt (z. B. Gerät zu beschäftigt).

      👉 Der socket hang up ist kein Scriptfehler, sondern nur ein temporärer Netzwerkzustand.
      Das Script läuft ganz normal weiter.
      (ja, ich weiß – sagst du auch nicht 😉, nur als Hinweis für alle.)

      Im Log steht das als „Fehler“, weil in diesem Moment keine Verbindung zustande kam.
      Man könnte das Script so anpassen, dass statt „Fehler“ eine „Warnung“ geloggt wird – das ändert aber nichts daran, dass diese eine Abfrage im Intervall einfach übersprungen wird.

      Vermutlich liegt die Ursache in einer kleinen WLAN-Reichweiten- oder Interferenzsituation (z. B. andere Geräte auf demselben Kanal).

      Solche Situationen kommen auch bei MQTT oder anderen Verbindungen gelegentlich vor – unabhängig davon, ob sie im Log auftauchen oder nicht.

      Also alles gut 🙂

      posted in JavaScript
      maxclaudi
      maxclaudi
    • RE: OpenDTU-onBattery Blockly DPL Ein/Aus

      @beowolf sagte in OpenDTU-onBattery Blockly DPL Ein/Aus:

      Es war keine Seltenheit, das der DPL gerade wieder unter geregelt hat, weil zu viel Sonne da war, und sich dann die Kochplatten wieder gemeldet haben. Also wieder rauf bis zur nächsten Wolke usw. usw..

      Dafür soll mqtt Mode benutzt werden.
      MQTT Mode 0/2 funktioniert, hat aber Einschränkungen: Neustarts, WiFi-Abbrüche oder Änderungen in der WebUI setzen den Mode einfach zurück auf 0.
      Man hat auch wieder das Pending-Problem mit längerem schalten.

      Da ist es viel einfacher den DPL zu deaktivieren

      So sehe ich das auch und ist "kein Fehler" im System.
      Ohne große zusätzliche, fehleranfällige Steuerung, sofort die volle Leistung da.
      So soll es sein, wenn man sich das wünscht.


      Ein Schalter, wie in meiner Switch-Version, oder maxLimit, ist dagegen relativ einfach umzusetzen, sobald man den Code verstanden hat – der Schaltzustand bleibt dabei dauerhaft erhalten, auch nach Neustarts, WiFi-Abbrüchen oder Änderungen in der WebUI.
      Schaltet auch sofort (bei Deaktivierung des DPL) auf maximales Limit.

      Es wäre interessant, die Auswahl den Usern selbst überlassen zu können.

      Besonders bei "solarbetriebenen“ Wechselrichtern macht das automatische Setzen auf das unterste Limit nach DPL-Deaktivierung wenig Sinn.


      Da ist es viel einfacher den DPL zu deaktivieren, dann gehen halt hin und wieder 1,8 KW ins Netz wo sie eigentlich nicht hin dürfen.

      Tatsächlich fließt dabei nur sehr wenig ins Netz – umgerechnet etwa 0,15 kWh bei 5 Minuten.

      Naja – unsere Lösung funktioniert, und darauf kommt es an. 😉

      posted in Blockly
      maxclaudi
      maxclaudi
    • RE: OpenDTU-onBattery Blockly DPL Ein/Aus

      @beowolf sagte in OpenDTU-onBattery Blockly DPL Ein/Aus:

      Ob deine Funktion wohl in die "offizielle" Version übernommen wird?

      @beowolf
      Ich denke eher nicht, dass meine Funktion in die offizielle Version übernommen wird.
      Ich hatte dazu ein Request gestellt – die Entwickler sahen darin jedoch keine Notwendigkeit, da das Verhalten beim Deaktivieren des DPL sehr individuell sei und man nicht alle Wünsche abbilden könne.

      Daraufhin habe ich mich selbst tiefer in den Code eingearbeitet und eine eigene Lösung umgesetzt, die in meiner Firmware-Variante verfügbar ist.
      Das war zwar einiges an Arbeit, läuft aber inzwischen stabil und wie gewünscht.

      Inzwischen habe ich gesehen, dass im offiziellen Umfeld an genau dem Bereich gearbeitet wird, den ich mit meinen Änderungen erweitert hatte.
      Es gibt sogar Commits auf meinem maxLimit- und switchLimit-Branch von Mitgliedern des OpenDTU-onBattery-Teams.
      Ob das direkt übernommen wird oder nur als Anregung dient, bleibt abzuwarten – schön zu sehen, dass das Thema offenbar doch aufgegriffen wurde.

      Da GitHub künftig verpflichtend eine 2FA-Authentifizierung (Zwei-Faktor-Authentifizierung) voraussetzt, werde ich meine Aktivitäten dort einstellen.

      Hier im Forum geht’s aber trotzdem weiter – mit Austausch, Ideen und vielleicht auch neuen Projekten.
      Bei Fragen oder Interesse kann man sich gern hier an mich wenden.

      Also: nicht abschalten, hier geht’s ja weiter 😉

      posted in Blockly
      maxclaudi
      maxclaudi
    Community
    Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
    The ioBroker Community 2014-2023
    logo