Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. English
    3. Scripting / Logic
    4. JavaScript
    5. [Vorlage] Script: JSON-->Datenpunkte mit sync

    NEWS

    • ioBroker goes Matter ... Matter Adapter in Stable

    • 15. 05. Wartungsarbeiten am ioBroker Forum

    • Monatsrückblick - April 2025

    [Vorlage] Script: JSON-->Datenpunkte mit sync

    This topic has been deleted. Only users with topic management privileges can see it.
    • A
      Ahnungsbefreit last edited by

      Hallo zusammen,

      ich beschäftige mich seit einiger Zeit damit, Daten aus meiner ENPHASE Photovoltaik Anlage zu extrahieren und damit dann die üblichen "smarten" Sachen zu machen wie Visualisierung, Statistik, Überschussladen etc.

      Nach einigem Experimentieren bin ich als einzig wirklich zielführende Lösung bei einem Plugin für Homebridge gelandet, das aber leider nicht über den HAM Adapter funktioniert (es könnte etwas damit zu tun haben, dass das Plugin eine "Child Bridge" startet, aber das ist eine andere Diskussion) sondern nur über eine eigenständige Homebridge Instanz in einer eigenen VM und diese liefert dann über MQTT richtig viele Daten, verteilt auf 12 Datenpunkte mit JSON Struktur und jeder dieser JSON-Sätze hat zwischen 4 und etwa 50 Attribute. Das ist schön, aber etwas unhandlich für die Weiterverarbeitung. Zudem habe ich keine Doku für die Struktur gefunden und muss öfter mal andere Datenpunkte versuchsweise auswerten, um die Richtigen zu finden.

      Lange Rede, kurzer Sinn: Aus dieser Aufgabe ist ein einfaches JavaScript enstanden, das das Extrahieren solcher Daten und Abbilden auf eigene Datenpunkte vereinfacht, in dem man die zu extrahierenden Daten in eine Array von Objekten einträgt, Quell- und Zielpfad definiert und das Script startet.

      Danach werden die Zieldatenpunkte angelegt, wenn sie nicht schon existieren und über einen "$elektor" ein Trigger erzeugt, der dann dafür sorgt, dass die eigenen Datenpunkte immer aktualisiert werden, wenn neue JSON Daten geliefert worden sind. Da das schnell recht viel Ressourcen ziehen kann bei den Datenmengen habe ich das noch etwas optimiert, um nur dann die ganze Liste der Datenpunkte auf mögliche Aktualisierungen abzuklappern, wenn mindestens einer der relevanten JSON Datenpunkte aktualisiert wurde. Ich prüfe im Script nicht, welcher der Datenpunkte innerhalb des JSON geändert wurde, sondern aktualisiere/ändere immer alle Datenpunkte, die aus diesem JSON "gefüttert" werden.

      Vielleicht kann das ja auch für Andere nützlich sein, ich wollte gerne der Community mal etwas zurück geben auf diesem Weg. Details zu ENPHASE demnächst in einem eigenen Thread.

      'use strict';
      
      const mqttPath = 'mqtt.0.envoy.Envoy-S.';   // Quellpfad mit den JSON Daten, muss nicht aus MQTT kommen
      const userPath = '0_userdata.0.envoy.';     // Zielpfad für die Datenpunkte, die einzelne Attribute der JSON Struktur repräsentieren
      
      /*  Beispiel: Wenn "mqtt.0.envoy.Envoy-S.Production" enthält
      {
        "wattHoursToday": 2765,
        "wattHoursSevenDays": 25747,
        "wattsNow": 1601
      }
      
      und wenn man diese Werte unter 0_userdata.0.envoy.Production.wattHoursSevenDays usw. in eigene Datenpunkte schreiben möchte, dann könnte das Array
      mit den Objekten wie folgt aussehen:
      
      const mapArr = [
          { topic: 'Production', attr: 'wattHoursSevenDays', dpId: 'Production.wattHoursSevenDays', dpType: 'number', dpUnit: 'Wh', dpDef:0, dpFactor:0 },
          { topic: 'Production', attr: 'wattHoursLifetime', dpId: 'Production.wattHoursLifetime', dpType: 'number', dpUnit: 'Wh', dpDef:0, dpFactor:0 },
          { topic: 'Production', attr: 'wattsNow', dpId: 'Production.wattsNow', dpType: 'number', dpUnit: 'W', dpDef:0, dpFactor:0 },
      ];
      
      Dabei haben die Attribute die folgende Bedeutung:
      
      topic:          Der letzte Teil des Namens des Datenpunktes, auf den getriggert werden soll, also der Teil nach dem, was in mqttPath steht.
                      Bitte beachten, dass dies wirklich ein Datenpunkt sein muss, kein Folder/Channel etc.
      attr:           Das JSON Attribut, das gelesen werden soll
      dpID:           Der letzte Teil des Namens des Datenpunktes, in den geschrieben werden soll, also der Teil nach dem, was in userPath steht
      dpType:         Typ des zu schreibenden Datenpunktes, also z.B. 'number', 'string', 'boolean'
      dpUnit:         Einheit des zu schreibenden Datenpunktes, also z.B. 'Wh', 'V'
      dpDef:          Der Default Wert des zu schreibenden Datenpunktes, mit dem der Datenpunkt initialisiert wird
      dpFactor:       Für Datenpunkte vom Typ Number ein Faktor, mit dem Quellwert multipliziert wird. Damit kann man z.B. von Watt in Kilowatt umrechnen
                      oder auch das Vorzeichen umdrehen. ACHTUNG: Für alle anderen Typen von Datenpunkten MUSS hier eine 0 stehen!
      
      */
      
      // Hier die JSON Daten eintragen, die unter "<userPath>.<dpId>" gespeichert werden sollen und die Beispieldaten rauswerfen
      
      const mapArr = [
          { topic: 'Production', attr: 'wattHoursSevenDays', dpId: 'Production.wattHoursSevenDays', dpType: 'number', dpUnit: 'Wh', dpDef:0, dpFactor:0 },
          { topic: 'Production', attr: 'wattHoursLifetime', dpId: 'Production.wattHoursLifetime', dpType: 'number', dpUnit: 'Wh', dpDef:0, dpFactor:0 },
          { topic: 'Production', attr: 'wattsNow', dpId: 'Production.wattsNow', dpType: 'number', dpUnit: 'W', dpDef:0, dpFactor:0 },
      ];
      
      // Ab hier nichts mehr ändern!
      
      
      let uniqueListOfTopics =  new Set();
      
      // Datenpunkte anlegen, wenn noch nicht vorhanden
      for (const [i,e] of mapArr.entries()){
          createState(`${userPath+e.dpId}`, { name: `${e.dpId}`,  type: `${e.dpType}`, unit: `${e.dpUnit}`, def: `${e.dpDef}`});
          uniqueListOfTopics.add(e.topic);
          // log(`${userPath+userPrefix+e.dpId}`);
      }
      
      $(`state[id=${mqttPath}*]`).on(async function (obj) {
        const value = obj.state.val;
        const topic = obj.id.split('.').pop();
        // log(topic);
          if (uniqueListOfTopics.has(topic)) {
              for (const [i,e] of mapArr.entries()){
                  if(e.topic === topic){
                      // log(topic);
                      const valToRead= getAttr((function () { 
                              try {return JSON.parse(value);} 
                              catch(e) {return {};}
                      })(), e.attr);
                      // log(valToRead);
                      setState(`${userPath+e.dpId}`, e.dpFactor? valToRead * e.dpFactor : valToRead , false);
                  }
              }
          }
      });
      
      
      
      
      OliverIO 1 Reply Last reply Reply Quote 1
      • OliverIO
        OliverIO @Ahnungsbefreit last edited by OliverIO

        @ahnungsbefreit

        um das wirklich multifunktional zu machen, könnten man

        1. den Wert mapArr.attr als JSONata-Ausdruck definieren, so das der Quellwert aus einer beliebig tief verschachtelten JSON-Struktur kommen kann. so kann es nur Werte aus nur einer Ebene des Objektes lesen.

        2. Mit der folgenden Bibliothek die Differenz zwischen dem letzten JSON und dem aktuellen ziehen und nur die Werte überhaupt behandeln, die sich seit dem letzten mal geändert haben.
          https://www.npmjs.com/package/deep-object-diff

        A 1 Reply Last reply Reply Quote 0
        • A
          Ahnungsbefreit @OliverIO last edited by

          @oliverio Gute Ideen, das behalte ich mal im Hinterkopf...

          1 Reply Last reply Reply Quote 0
          • First post
            Last post

          Support us

          ioBroker
          Community Adapters
          Donate

          712
          Online

          31.6k
          Users

          79.5k
          Topics

          1.3m
          Posts

          javascript monitoring
          2
          3
          1090
          Loading More Posts
          • Oldest to Newest
          • Newest to Oldest
          • Most Votes
          Reply
          • Reply as topic
          Log in to reply
          Community
          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
          The ioBroker Community 2014-2023
          logo