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

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

Community Forum

  1. ioBroker Community Home
  2. English
  3. Scripting / Logic
  4. JavaScript
  5. [Vorlage] Script: JSON-->Datenpunkte mit sync

NEWS

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

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

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

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

Geplant Angeheftet Gesperrt Verschoben JavaScript
javascriptmonitoring
3 Beiträge 2 Kommentatoren 1.4k Aufrufe 2 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • A Online
    A Online
    Ahnungsbefreit
    schrieb am zuletzt editiert von
    #1

    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);
                }
            }
        }
    });
    
    
    
    

    Wenn meine Antwort hilfreich war, dürft Ihr das gerne durch einen "Upvote" (der kleine "^" unten rechts) zum Ausdruck bringen.

    OliverIOO 1 Antwort Letzte Antwort
    1
    • A Ahnungsbefreit

      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);
                  }
              }
          }
      });
      
      
      
      
      OliverIOO Offline
      OliverIOO Offline
      OliverIO
      schrieb am zuletzt editiert von OliverIO
      #2

      @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

      Meine Adapter und Widgets
      TVProgram, SqueezeboxRPC, OpenLiga, RSSFeed, MyTime,, pi-hole2, vis-json-template, skiinfo, vis-mapwidgets, vis-2-widgets-rssfeed
      Links im Profil

      A 1 Antwort Letzte Antwort
      0
      • OliverIOO 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 Online
        A Online
        Ahnungsbefreit
        schrieb am zuletzt editiert von
        #3

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

        Wenn meine Antwort hilfreich war, dürft Ihr das gerne durch einen "Upvote" (der kleine "^" unten rechts) zum Ausdruck bringen.

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


        Support us

        ioBroker
        Community Adapters
        Donate

        589

        Online

        32.4k

        Benutzer

        81.4k

        Themen

        1.3m

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

        • Du hast noch kein Konto? Registrieren

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