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

  • Default (No Skin)
  • No Skin
Collapse
Logo
  1. ioBroker Community Home
  2. Deutsch
  3. Tester
  4. Test Dreame Home Adapter

NEWS

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

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

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

Test Dreame Home Adapter

Scheduled Pinned Locked Moved Tester
241 Posts 39 Posters 48.8k Views 44 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • T Offline
    T Offline
    tombox
    wrote on last edited by tombox
    #1

    Hi ich habe ein neuen Adapter für Dreame Home App erstellt für Geräte die nicht in der mihome app und damit nicht im mihome vaccum adapter unterstützt werden.
    Es werden alle neue Geräte wie L10 L20 und x40 unterstützt

    Er soll nur zum Zustandsüberwachung und Steuern dienen. Eine Map Unterstützung ist nicht geplant.

    Zum Installieren:
    https://github.com/TA2k/ioBroker.dreame

    Unter Adapter Experten Modus aktivieren:
    9309457a-cad0-4ff4-946f-28df05d32801-image.png

    Unter Adapter das Github Icon klicken:
    ef5f973f-4a70-43be-bf9a-460726a69d1d-image.png
    Benutzerdefiniert auswählen und die Url https://github.com/TA2k/ioBroker.dreame einfügen.
    8328414c-da64-41d4-b524-5a75a25cb683-image.png

    Dann unter Adapter den Dream Adapter suchen und ganz rechts auf das Plus klicken.
    f59f304a-7769-463f-84d2-e36b461a87bd-image.png

    Loginablauf:

    1. Dreame Login Daten eingeben

    deviceId.status

    Current Status of the devices

    deviceId.remote
    Remote control of the devices
    Start: dreame.0.xxxxx.remote.start-sweep
    Stop: dreame.0.xxxxx.remote.start-charge

    Start Shortcut:

    dreame.0.XXXXXXXX.remote.start-clean

    [
                    {
                        "piid": 1,
                        "value": 25
                    },
                    {
                        "piid": 10,
                        "value": "32"
                    }
    ]
    

    "value": "32" -> Shortcut id

    List of shortcuts:

    dreame.0.XXXXX.status.4-48

    Names are base64 encoded
    If there is no 4-48 state you have to start a short cut

    Room cleaning

    dreame.0.XXXX.remote.start-clean

     [
                    {
                        "piid": 1,
                        "value": 18
                    },
                    {
                        "piid": 10,
                        "value": "{\"selects\":[[X,1,3,2,1]]}"
                    }
                ]
    

    X = room id

    Multiple Rooms:

     [
                    {
                        "piid": 1,
                        "value": 18
                    },
                    {
                        "piid": 10,
                        "value": "{\"selects\":[[X,1,3,2,1],[Y,1,3,2,1]]}"
                    }
                ]
    

    X = room 1
    Y = room 2

    Karte wechseln
    dreame.XXXXXXX.remote.update-map

     [
                    {
                        "piid": 4,
                        "value": "{\"sm\":{},\"mapid\":X}"
                    }
                ]
    

    X = mapId
    Liste der Ids:
    dreame.0.XXXXXXX.map.maps

    Control Clean Modes

    Enable CleanGenius:

    [
                {
    
                    "value": "{\"k\":\"SmartHost\",\"v\":1}",
                    "siid": 4,
                    "piid": 50
                }
            ]
    

    Disable CleanGenius:

     [
                {
    
                    "value": "{\"k\":\"SmartHost\",\"v\":0}",
                    "siid": 4,
                    "piid": 50
                }
            ]
    

    CleanGenius Deep Cleaning: "v":2

    CleanGenius Mode: value: 3 or value 2

    [
                {
    
                    "value": 2,
                    "siid": 28,
                    "piid": 5
                }
            ]
    
    

    Change Cleaning Mode:

    [
    {
    
                    "value": 5122,
                    "siid": 4,
                    "piid": 23
                }
            ]
    

    Values: 5120, 5121, 5122...

    Vaccuum Mode:

    [
    {
    
                    "value": 2,
                    "siid": 4,
                    "piid": 4
                }
            ]
    
    

    Mop Intensity:

    [
                {
    
                    "value": 28,
                    "siid": 28,
                    "piid": 1
                }
            ]
    

    Route:

     [
                {
    
                    "value": "{\"k\":\"CleanRoute\",\"v\":1}",
                    "siid": 4,
                    "piid": 50
                }
            ]
    
    D M mcBirneM 3 Replies Last reply
    1
    • T tombox

      Hi ich habe ein neuen Adapter für Dreame Home App erstellt für Geräte die nicht in der mihome app und damit nicht im mihome vaccum adapter unterstützt werden.
      Es werden alle neue Geräte wie L10 L20 und x40 unterstützt

      Er soll nur zum Zustandsüberwachung und Steuern dienen. Eine Map Unterstützung ist nicht geplant.

      Zum Installieren:
      https://github.com/TA2k/ioBroker.dreame

      Unter Adapter Experten Modus aktivieren:
      9309457a-cad0-4ff4-946f-28df05d32801-image.png

      Unter Adapter das Github Icon klicken:
      ef5f973f-4a70-43be-bf9a-460726a69d1d-image.png
      Benutzerdefiniert auswählen und die Url https://github.com/TA2k/ioBroker.dreame einfügen.
      8328414c-da64-41d4-b524-5a75a25cb683-image.png

      Dann unter Adapter den Dream Adapter suchen und ganz rechts auf das Plus klicken.
      f59f304a-7769-463f-84d2-e36b461a87bd-image.png

      Loginablauf:

      1. Dreame Login Daten eingeben

      deviceId.status

      Current Status of the devices

      deviceId.remote
      Remote control of the devices
      Start: dreame.0.xxxxx.remote.start-sweep
      Stop: dreame.0.xxxxx.remote.start-charge

      Start Shortcut:

      dreame.0.XXXXXXXX.remote.start-clean

      [
                      {
                          "piid": 1,
                          "value": 25
                      },
                      {
                          "piid": 10,
                          "value": "32"
                      }
      ]
      

      "value": "32" -> Shortcut id

      List of shortcuts:

      dreame.0.XXXXX.status.4-48

      Names are base64 encoded
      If there is no 4-48 state you have to start a short cut

      Room cleaning

      dreame.0.XXXX.remote.start-clean

       [
                      {
                          "piid": 1,
                          "value": 18
                      },
                      {
                          "piid": 10,
                          "value": "{\"selects\":[[X,1,3,2,1]]}"
                      }
                  ]
      

      X = room id

      Multiple Rooms:

       [
                      {
                          "piid": 1,
                          "value": 18
                      },
                      {
                          "piid": 10,
                          "value": "{\"selects\":[[X,1,3,2,1],[Y,1,3,2,1]]}"
                      }
                  ]
      

      X = room 1
      Y = room 2

      Karte wechseln
      dreame.XXXXXXX.remote.update-map

       [
                      {
                          "piid": 4,
                          "value": "{\"sm\":{},\"mapid\":X}"
                      }
                  ]
      

      X = mapId
      Liste der Ids:
      dreame.0.XXXXXXX.map.maps

      Control Clean Modes

      Enable CleanGenius:

      [
                  {
      
                      "value": "{\"k\":\"SmartHost\",\"v\":1}",
                      "siid": 4,
                      "piid": 50
                  }
              ]
      

      Disable CleanGenius:

       [
                  {
      
                      "value": "{\"k\":\"SmartHost\",\"v\":0}",
                      "siid": 4,
                      "piid": 50
                  }
              ]
      

      CleanGenius Deep Cleaning: "v":2

      CleanGenius Mode: value: 3 or value 2

      [
                  {
      
                      "value": 2,
                      "siid": 28,
                      "piid": 5
                  }
              ]
      
      

      Change Cleaning Mode:

      [
      {
      
                      "value": 5122,
                      "siid": 4,
                      "piid": 23
                  }
              ]
      

      Values: 5120, 5121, 5122...

      Vaccuum Mode:

      [
      {
      
                      "value": 2,
                      "siid": 4,
                      "piid": 4
                  }
              ]
      
      

      Mop Intensity:

      [
                  {
      
                      "value": 28,
                      "siid": 28,
                      "piid": 1
                  }
              ]
      

      Route:

       [
                  {
      
                      "value": "{\"k\":\"CleanRoute\",\"v\":1}",
                      "siid": 4,
                      "piid": 50
                  }
              ]
      
      D Offline
      D Offline
      dirkhe
      Developer
      wrote on last edited by
      #2

      @tombox super, dass du damit angefangen hast, ich bjn noch nicht dazu kommen.
      Installiert habe ich ihn schon mal und er schreibt auch Datenpunkte. Es scheint mir so, dass du ganz viele generisch schreibst, weil ich habe da ganz viele mit Nummern, ala 2-94 wo dann die results reingeschrieben werden. Aber ich denke, das wirst du ja selber auch haben und es dient noch testzwecken.
      Ich wollte dir zumindest mal eine rückmeldung geben.
      Wenn jch das immer lese, wieviele den dreame adapter fordern, ist die Resonanz hier dünn.

      T 1 Reply Last reply
      0
      • T tombox

        Hi ich habe ein neuen Adapter für Dreame Home App erstellt für Geräte die nicht in der mihome app und damit nicht im mihome vaccum adapter unterstützt werden.
        Es werden alle neue Geräte wie L10 L20 und x40 unterstützt

        Er soll nur zum Zustandsüberwachung und Steuern dienen. Eine Map Unterstützung ist nicht geplant.

        Zum Installieren:
        https://github.com/TA2k/ioBroker.dreame

        Unter Adapter Experten Modus aktivieren:
        9309457a-cad0-4ff4-946f-28df05d32801-image.png

        Unter Adapter das Github Icon klicken:
        ef5f973f-4a70-43be-bf9a-460726a69d1d-image.png
        Benutzerdefiniert auswählen und die Url https://github.com/TA2k/ioBroker.dreame einfügen.
        8328414c-da64-41d4-b524-5a75a25cb683-image.png

        Dann unter Adapter den Dream Adapter suchen und ganz rechts auf das Plus klicken.
        f59f304a-7769-463f-84d2-e36b461a87bd-image.png

        Loginablauf:

        1. Dreame Login Daten eingeben

        deviceId.status

        Current Status of the devices

        deviceId.remote
        Remote control of the devices
        Start: dreame.0.xxxxx.remote.start-sweep
        Stop: dreame.0.xxxxx.remote.start-charge

        Start Shortcut:

        dreame.0.XXXXXXXX.remote.start-clean

        [
                        {
                            "piid": 1,
                            "value": 25
                        },
                        {
                            "piid": 10,
                            "value": "32"
                        }
        ]
        

        "value": "32" -> Shortcut id

        List of shortcuts:

        dreame.0.XXXXX.status.4-48

        Names are base64 encoded
        If there is no 4-48 state you have to start a short cut

        Room cleaning

        dreame.0.XXXX.remote.start-clean

         [
                        {
                            "piid": 1,
                            "value": 18
                        },
                        {
                            "piid": 10,
                            "value": "{\"selects\":[[X,1,3,2,1]]}"
                        }
                    ]
        

        X = room id

        Multiple Rooms:

         [
                        {
                            "piid": 1,
                            "value": 18
                        },
                        {
                            "piid": 10,
                            "value": "{\"selects\":[[X,1,3,2,1],[Y,1,3,2,1]]}"
                        }
                    ]
        

        X = room 1
        Y = room 2

        Karte wechseln
        dreame.XXXXXXX.remote.update-map

         [
                        {
                            "piid": 4,
                            "value": "{\"sm\":{},\"mapid\":X}"
                        }
                    ]
        

        X = mapId
        Liste der Ids:
        dreame.0.XXXXXXX.map.maps

        Control Clean Modes

        Enable CleanGenius:

        [
                    {
        
                        "value": "{\"k\":\"SmartHost\",\"v\":1}",
                        "siid": 4,
                        "piid": 50
                    }
                ]
        

        Disable CleanGenius:

         [
                    {
        
                        "value": "{\"k\":\"SmartHost\",\"v\":0}",
                        "siid": 4,
                        "piid": 50
                    }
                ]
        

        CleanGenius Deep Cleaning: "v":2

        CleanGenius Mode: value: 3 or value 2

        [
                    {
        
                        "value": 2,
                        "siid": 28,
                        "piid": 5
                    }
                ]
        
        

        Change Cleaning Mode:

        [
        {
        
                        "value": 5122,
                        "siid": 4,
                        "piid": 23
                    }
                ]
        

        Values: 5120, 5121, 5122...

        Vaccuum Mode:

        [
        {
        
                        "value": 2,
                        "siid": 4,
                        "piid": 4
                    }
                ]
        
        

        Mop Intensity:

        [
                    {
        
                        "value": 28,
                        "siid": 28,
                        "piid": 1
                    }
                ]
        

        Route:

         [
                    {
        
                        "value": "{\"k\":\"CleanRoute\",\"v\":1}",
                        "siid": 4,
                        "piid": 50
                    }
                ]
        
        M Offline
        M Offline
        Markus 6
        wrote on last edited by
        #3

        @tombox Super, dass sich Jemand dem Adapter angenommen hat.
        Installiert habe ich ihn bereits.
        Gibt es aktuell schon die Möglichkeit die Shortcuts anzusteuern? Ich konnte bisher nichts finden in den Befehlen.

        1 Reply Last reply
        0
        • D dirkhe

          @tombox super, dass du damit angefangen hast, ich bjn noch nicht dazu kommen.
          Installiert habe ich ihn schon mal und er schreibt auch Datenpunkte. Es scheint mir so, dass du ganz viele generisch schreibst, weil ich habe da ganz viele mit Nummern, ala 2-94 wo dann die results reingeschrieben werden. Aber ich denke, das wirst du ja selber auch haben und es dient noch testzwecken.
          Ich wollte dir zumindest mal eine rückmeldung geben.
          Wenn jch das immer lese, wieviele den dreame adapter fordern, ist die Resonanz hier dünn.

          T Offline
          T Offline
          tombox
          wrote on last edited by
          #4

          @dirkhe Mit welchem Dreame hast du getestet.
          Ja ich versuche immer generisch da ich nicht die Zeit habe den Adapter jede Woche anzupassen.
          Leider sind die Datenpunkte vom L10 verfügbar

          D 1 Reply Last reply
          0
          • T tombox

            @dirkhe Mit welchem Dreame hast du getestet.
            Ja ich versuche immer generisch da ich nicht die Zeit habe den Adapter jede Woche anzupassen.
            Leider sind die Datenpunkte vom L10 verfügbar

            D Offline
            D Offline
            dirkhe
            Developer
            wrote on last edited by
            #5

            @tombox ich habe den l20
            Screenshot_20240624-001221_Browser.png

            T 1 Reply Last reply
            0
            • D dirkhe

              @tombox ich habe den l20
              Screenshot_20240624-001221_Browser.png

              T Offline
              T Offline
              tombox
              wrote on last edited by tombox
              #6

              @dirkhe hast du das genaue Modell
              dreame.0.XXXXXXXXX.general.model

              @Markus-6

              dreame.0.XXXXXXXX.remote.start-clean
              [
              {
              "piid": 1,
              "value": 25
              },
              {
              "piid": 10,
              "value": "32"
              }
              ]

              und short cuts ist 32 oder 33 oder 34..
              Liste:
              dreame.0.XXXXX.status.4-48

              D M 2 Replies Last reply
              0
              • T tombox

                @dirkhe hast du das genaue Modell
                dreame.0.XXXXXXXXX.general.model

                @Markus-6

                dreame.0.XXXXXXXX.remote.start-clean
                [
                {
                "piid": 1,
                "value": 25
                },
                {
                "piid": 10,
                "value": "32"
                }
                ]

                und short cuts ist 32 oder 33 oder 34..
                Liste:
                dreame.0.XXXXX.status.4-48

                D Offline
                D Offline
                dirkhe
                Developer
                wrote on last edited by
                #7

                @tombox general habe ich nicht, nur remote und status. Darin habe ich auch kein modell entdeckt. In der app finde ich auch keine modellbezeichnung

                1 Reply Last reply
                0
                • T tombox

                  @dirkhe hast du das genaue Modell
                  dreame.0.XXXXXXXXX.general.model

                  @Markus-6

                  dreame.0.XXXXXXXX.remote.start-clean
                  [
                  {
                  "piid": 1,
                  "value": 25
                  },
                  {
                  "piid": 10,
                  "value": "32"
                  }
                  ]

                  und short cuts ist 32 oder 33 oder 34..
                  Liste:
                  dreame.0.XXXXX.status.4-48

                  M Offline
                  M Offline
                  Markus 6
                  wrote on last edited by
                  #8

                  @tombox Super! Danke es funktioniert. Dann brauch ich nicht mehr den Umweg über HomeAssistant gehen. Danke nochmals!

                  1 Reply Last reply
                  0
                  • H Offline
                    H Offline
                    hahne
                    wrote on last edited by
                    #9

                    Super das sich wer gefunden hat um einen Adapter zu entwickeln.
                    Ist geplant oder möglich NUR spezielle Räume reinigen zu lassen?
                    Grüße
                    Vielen Dank

                    T 1 Reply Last reply
                    0
                    • H hahne

                      Super das sich wer gefunden hat um einen Adapter zu entwickeln.
                      Ist geplant oder möglich NUR spezielle Räume reinigen zu lassen?
                      Grüße
                      Vielen Dank

                      T Offline
                      T Offline
                      tombox
                      wrote on last edited by
                      #10

                      @hahne habe ich mal in Beitrag 1 und readme eingefügt

                      1 Reply Last reply
                      0
                      • H Offline
                        H Offline
                        hahne
                        wrote on last edited by
                        #11

                        Super, toll das das auch klappt.
                        Ist geplant das der Adapter noch ein wenig "Eleganter" wird?
                        Sprich das man States mit true/false oder mit Sring Werten schalten kann, ohne diese JSON werte? Wäre glaube für viele (inklusive mir 😆 ) einfacher zu handhaben.

                        Oder gibt es eine Übersicht was z.B. piid, value zu bedeuten hat bzw. wo diese Werte herkommen?
                        Danke nochmal für den Adapter

                        T 1 Reply Last reply
                        0
                        • H hahne

                          Super, toll das das auch klappt.
                          Ist geplant das der Adapter noch ein wenig "Eleganter" wird?
                          Sprich das man States mit true/false oder mit Sring Werten schalten kann, ohne diese JSON werte? Wäre glaube für viele (inklusive mir 😆 ) einfacher zu handhaben.

                          Oder gibt es eine Übersicht was z.B. piid, value zu bedeuten hat bzw. wo diese Werte herkommen?
                          Danke nochmal für den Adapter

                          T Offline
                          T Offline
                          tombox
                          wrote on last edited by
                          #12

                          @hahne Ist von meiner Seite nicht geplant aber kann von jedem hinzugefügt werden

                          1 Reply Last reply
                          0
                          • H Offline
                            H Offline
                            hahne
                            wrote on last edited by
                            #13

                            Gibt es eine Möglichkeit zwischen den einzelnen Reinigungsmodi zu wechseln also Saugen, Wischen und Saugen + wischen?
                            Grüße

                            1 Reply Last reply
                            0
                            • H Offline
                              H Offline
                              Heinz2100
                              wrote on last edited by
                              #14

                              Hallo. Danke für den tollen Adapter. Funktioniert soweit via Shortcuts auch perfekt. Habe nur eine Frage: Wie kann man denn die verschiedenen Karten/Stockwerke via Remote Befehl wechseln, weil die Shortcuts anscheinend (schlauerweise) dieselben Nummern haben und in jedem Stockwerk wieder neu bei 32 beginnen. Dankeschön

                              S 1 Reply Last reply
                              0
                              • H Heinz2100

                                Hallo. Danke für den tollen Adapter. Funktioniert soweit via Shortcuts auch perfekt. Habe nur eine Frage: Wie kann man denn die verschiedenen Karten/Stockwerke via Remote Befehl wechseln, weil die Shortcuts anscheinend (schlauerweise) dieselben Nummern haben und in jedem Stockwerk wieder neu bei 32 beginnen. Dankeschön

                                S Offline
                                S Offline
                                SmartiSmart
                                wrote on last edited by SmartiSmart
                                #15

                                Kann jemand kurz erklären, wie es gemacht wird? Der Adapter läuft und auch die ganzen Objekte für meine beiden Sauger sind vorhanden.
                                Ich stehe aber total auf dem Schlauch, was ich jetzt machen muss, damit der Sauger über IOBroker gestartet wird.
                                Bislang habe ich alles mit Blockly gemacht, aber ich vermute, dass wird hier ja nicht ausreichen.

                                Danke schonmal für die Hilfe.

                                wawyoW 1 Reply Last reply
                                0
                                • S SmartiSmart

                                  Kann jemand kurz erklären, wie es gemacht wird? Der Adapter läuft und auch die ganzen Objekte für meine beiden Sauger sind vorhanden.
                                  Ich stehe aber total auf dem Schlauch, was ich jetzt machen muss, damit der Sauger über IOBroker gestartet wird.
                                  Bislang habe ich alles mit Blockly gemacht, aber ich vermute, dass wird hier ja nicht ausreichen.

                                  Danke schonmal für die Hilfe.

                                  wawyoW Offline
                                  wawyoW Offline
                                  wawyo
                                  Developer
                                  wrote on last edited by
                                  #16

                                  Hallo zusammen,
                                  ich habe mich intensiv mit dem Adapter beschäftigt, da mich der "dreame.0..status.map-data" Objekt interessiert hat. Dabei konnte ich den Adapter so anpassen, dass ich neue Baum-Objekte hinzufügen konnte (dreame.0..map) . Dies ermöglichte es mir, eine Fülle von Daten über die Position des Saugroboters (L20) zu sammeln. Es scheint, als ob noch viele weitere Daten vorhanden sind. Unter "Clean Set" sind alle Räume mit den entsprechenden Einstellungen vorhanden.

                                  Um das Ziel zu erreichen, den Saugroboter in Echtzeit zu verfolgen, benötige ich nun Unterstützung von weiteren Personen. Unten finden Sie den modifizierten Code für eine detaillierte Ansicht.
                                  DreameMap.png

                                  'use strict';
                                  
                                  /*
                                   * Created with @iobroker/create-adapter v2.6.3
                                   */
                                  
                                  // The adapter-core module gives you access to the core ioBroker functions
                                  // you need to create an adapter
                                  const utils = require('@iobroker/adapter-core');
                                  const axios = require('axios').default;
                                  const Json2iob = require('json2iob');
                                  const crypto = require('crypto');
                                  const mqtt = require('mqtt');
                                  const zlib = require("node:zlib");
                                  
                                  class Dreame extends utils.Adapter {
                                    /**
                                     * @param {Partial<utils.AdapterOptions>} [options={}]
                                     */
                                    constructor(options) {
                                      super({
                                        ...options,
                                        name: 'dreame',
                                      });
                                      this.on('ready', this.onReady.bind(this));
                                      this.on('stateChange', this.onStateChange.bind(this));
                                      this.on('unload', this.onUnload.bind(this));
                                      this.deviceArray = [];
                                      this.states = {};
                                      this.json2iob = new Json2iob(this);
                                      this.requestClient = axios.create({
                                        withCredentials: true,
                                        timeout: 3 * 60 * 1000, //3min client timeout
                                      });
                                  
                                      this.remoteCommands = {};
                                      this.specStatusDict = {};
                                      this.specPropsToIdDict = {};
                                      this.specActiosnToIdDict = {};
                                    }
                                  
                                    /**
                                     * Is called when databases are connected and adapter received configuration.
                                     */
                                    async onReady() {
                                      this.setState('info.connection', false, true);
                                      if (this.config.interval < 0.5) {
                                        this.log.info('Set interval to minimum 0.5');
                                        this.config.interval = 0.5;
                                      }
                                      if (this.config.interval > 2147483647) {
                                        this.log.info('Set interval to maximum 2147483647');
                                        this.config.interval = 2147483647;
                                      }
                                      if (!this.config.username || !this.config.password) {
                                        this.log.error('Please set username and password in the instance settings');
                                        return;
                                      }
                                  
                                      this.updateInterval = null;
                                      this.reLoginTimeout = null;
                                      this.refreshTokenTimeout = null;
                                      this.session = {};
                                      this.subscribeStates('*.remote.*');
                                  
                                      this.log.info('Login to Dreame Cloud...');
                                      await this.login();
                                  
                                      if (this.session.access_token) {
                                        await this.getDeviceList();
                                        await this.fetchSpecs();
                                        await this.createRemotes();
                                        await this.updateDevicesViaSpec();
                                        await this.connectMqtt();
                                        this.updateInterval = setInterval(
                                          async () => {
                                            await this.updateDevicesViaSpec();
                                          },
                                          this.config.interval * 60 * 1000,
                                        );
                                        this.refreshTokenInterval = setInterval(
                                          async () => {
                                            await this.refreshToken();
                                          },
                                          (this.session.expires_in - 100 || 3500) * 1000,
                                        );
                                      }
                                    }
                                  
                                    async login() {
                                      await this.requestClient({
                                        method: 'post',
                                        url: 'https://eu.iot.dreame.tech:13267/dreame-auth/oauth/token',
                                        headers: {
                                          'user-agent': 'Dart/3.2 (dart:io)',
                                          'dreame-meta': 'cv=i_829',
                                          'dreame-rlc': '1a9bb36e6b22617cf465363ba7c232fb131899d593e8d1a1-1',
                                          'tenant-id': '000000',
                                          host: 'eu.iot.dreame.tech:13267',
                                          authorization: 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=',
                                          'content-type': 'application/x-www-form-urlencoded',
                                          'dreame-auth': 'bearer',
                                        },
                                        data: {
                                          grant_type: 'password',
                                          scope: 'all',
                                          platform: 'IOS',
                                          type: 'account',
                                          username: this.config.username,
                                          password: crypto
                                            .createHash('md5')
                                            .update(this.config.password + 'RAylYC%fmSKp7%Tq')
                                            .digest('hex'),
                                          country: 'DE',
                                          lang: 'de',
                                        },
                                      })
                                        .then((response) => {
                                          this.log.debug('Login response: ' + JSON.stringify(response.data));
                                          this.session = response.data;
                                          this.setState('info.connection', true, true);
                                        })
                                        .catch((error) => {
                                          this.log.error('Login error: ' + error);
                                          error.response && this.log.error('Login error response: ' + JSON.stringify(error.response.data));
                                          this.setState('info.connection', false, true);
                                        });
                                    }
                                    async getDeviceList() {
                                      await this.requestClient({
                                        method: 'post',
                                        maxBodyLength: Infinity,
                                        url: 'https://eu.iot.dreame.tech:13267/dreame-user-iot/iotuserbind/device/listV2',
                                        headers: {
                                          'user-agent': 'Dart/3.2 (dart:io)',
                                          'dreame-meta': 'cv=i_829',
                                          'dreame-rlc': '1a9bb36e6b22617cf465363ba7c232fb131899d593e8d1a1-1',
                                          'tenant-id': '000000',
                                          host: 'eu.iot.dreame.tech:13267',
                                          authorization: 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=',
                                          'content-type': 'application/json',
                                          'dreame-auth': 'bearer ' + this.session.access_token,
                                        },
                                        data: {
                                          sharedStatus: 1,
                                          current: 1,
                                          size: 100,
                                          lang: 'de',
                                          timestamp: Date.now(),
                                        },
                                      })
                                        .then(async (response) => {
                                          /*
                                          example response:
                                          {
                                      "code": 0,
                                      "success": true,
                                      "data": {
                                          "page": {
                                              "records": [
                                                  {
                                                      "id": "xxx",
                                                      "did": "xxxx",
                                                      "model": "dreame.vacuum.r2449k",
                                                      "ver": "4.3.9_1252",
                                                      "customName": "",
                                                      "property": "{\"iotId\":\"xxxxxxxx\",\"lwt\":1,\"mac\":\"\"}",
                                                      "mac": "7",
                                                      "vendor": "ali",
                                                      "master": true,
                                                      "masterUid": "",
                                                      "masterUid2UUID": null,
                                                      "masterName": null,
                                                      "permissions": "",
                                                      "bindDomain": "10000.mt.eu.iot.dreame.tech:19973",
                                                      "sharedTimes": 0,
                                                      "sharedStatus": 1,
                                                      "calltag": null,
                                                      "updateTime": "2024-06-07 12:03:09",
                                                      "lang": null,
                                                      "deviceInfo": {
                                                          "productId": "10279",
                                                          "categoryPath": "/lifeapps/vacuum",
                                                          "model": "dreame.vacuum.r2449k",
                                                          "remark": "",
                                                          "feature": "video_ali,fastCommand",
                                                          "videoDynamicVendor": true,
                                                          "defaultVendors": [
                                                              "ali"
                                                          ],
                                                          "scType": "WIFI",
                                                          "extendScType": [
                                                              "QR_CODE"
                                                          ],
                                                          "status": "Live",
                                                          "mainImage": {
                                                              "as": "1",
                                                              "caption": "1",
                                                              "height": 0,
                                                              "width": 0,
                                                              "imageUrl": "https://oss.iot.dreame.tech/pub/pic/000000/ali_dreame/dreame.vacuum.r2449k/9acf24adb5ca3d15341fd869f2aa985f20240311084500.png",
                                                              "smallImageUrl": ""
                                                          },
                                                          "popup": {
                                                              "as": "1",
                                                              "caption": "1",
                                                              "height": 0,
                                                              "width": 0,
                                                              "imageUrl": "https://oss.iot.dreame.tech/pub/pic/000000/ali_dreame/dreame.vacuum.r2449k/9acf24adb5ca3d15341fd869f2aa985f20240311084500.png",
                                                              "smallImageUrl": ""
                                                          },
                                                          "icon": {
                                                              "as": "1",
                                                              "caption": "1",
                                                              "height": 0,
                                                              "width": 0,
                                                              "imageUrl": "https://oss.iot.dreame.tech/pub/pic/000000/ali_dreame/dreame.vacuum.r2449k/9acf24adb5ca3d15341fd869f2aa985f20240311084500.png",
                                                              "smallImageUrl": ""
                                                          },
                                                          "overlook": {
                                                              "as": "1",
                                                              "caption": "1",
                                                              "height": 0,
                                                              "width": 0,
                                                              "imageUrl": "https://oss.iot.dreame.tech/pub/pic/000000/ali_dreame/dreame.vacuum.r2449k/9acf24adb5ca3d15341fd869f2aa985f20240311084500.png",
                                                              "smallImageUrl": ""
                                                          },
                                                          "images": [],
                                                          "extensionId": "1228",
                                                          "updatedAt": "1711721850712",
                                                          "createdAt": "1705565151025",
                                                          "releaseAt": "1710848634605",
                                                          "quickConnectStatus": -1,
                                                          "quickConnects": {},
                                                          "permit": "video",
                                                          "firmwareDevelopType": "SINGLE_PLATFORM",
                                                          "bindType": "",
                                                          "displayName": "X40 Ultra Complete",
                                                          "liveKeyDefine": {},
                                                          "qaKeyDefine": {}
                                                      },
                                                      "online": true,
                                                      "latestStatus": 21,
                                                      "battery": 100,
                                                      "videoStatus": "{\"operType\":\"end\",\"operation\":\"monitor\",\"result\":0,\"status\":0}",
                                                      "region": null,
                                                      "featureCode": -1,
                                                      "featureCode2": 31,
                                                      "keyDefine": {
                                                          "ver": 1,
                                                          "url": "https://cnbj2.fds.api.xiaomi.com/000000-public/file/54587b0364cdd763deba93a974ef5aa05cbe7dcc_dreame.vacuum.r2449k_iotKeyValue_translate_1.json"
                                                      }
                                                  }
                                              ],
                                              "total": "1",
                                              "size": "100",
                                              "current": "1",
                                              "orders": [],
                                              "optimizeCountSql": true,
                                              "hitCount": false,
                                              "searchCount": true,
                                              "pages": "1"
                                          }
                                      },
                                      "msg": "操作成功"
                                  }
                                          */
                                          this.log.debug('Device list response: ' + JSON.stringify(response.data));
                                  
                                          if (
                                            response.data.code == '0' &&
                                            response.data &&
                                            response.data.data &&
                                            response.data.data.page &&
                                            response.data.data.page.records
                                          ) {
                                            this.deviceArray = response.data.data.page.records;
                                            for (const device of this.deviceArray) {
                                              await this.extendObject(device.did, {
                                                type: 'device',
                                                common: {
                                                  name: device.customName || device.deviceInfo.displayName || device.model,
                                                },
                                                native: {},
                                              });
                                              if (device.keyDefine) {
                                                const iotKeyValue = await this.requestClient({
                                                  method: 'get',
                                                  url: device.keyDefine.url,
                                                })
                                                  .then((response) => {
                                                    this.log.debug('iotKeyValue response: ' + JSON.stringify(response.data));
                                                    return response.data;
                                                  })
                                                  .catch((error) => {
                                                    this.log.error('iotKeyValue error: ' + error);
                                                    error.response &&
                                                      this.log.error('iotKeyValue error response: ' + JSON.stringify(error.response.data));
                                                  });
                                                /*{
                                              "keyDefine": {
                                                "2.1": {
                                                  "de": {
                                                    "1": "Reinigung"}},
                                              "ver": 1,
                                              "model": "dreame.vacuum.r2449k",
                                              "hash": "54587b0364cdd763deba93a974ef5aa05cbe7dcc"
                                            }*/
                                  
                                                if (iotKeyValue && iotKeyValue.keyDefine) {
                                                  //replace dot in id with - and select en language
                                  
                                                  for (const key in iotKeyValue.keyDefine) {
                                                    if (Object.hasOwnProperty.call(iotKeyValue.keyDefine, key)) {
                                                      const element = iotKeyValue.keyDefine[key];
                                                      if (element['en'] && element['en'] !== 'null') {
                                                        this.states[device.did][key.replace(/\./g, '-')] = element['en'];
                                                      }
                                                    }
                                                  }
                                                }
                                              }
                                              this.json2iob.parse(device.did + '.general', device, {
                                                states: { latestStatus: this.states[device.did] },
                                                channelName: 'General Updated at Start',
                                              });
                                            }
                                          } else {
                                            this.log.error('No Devices found: ' + JSON.stringify(response.data));
                                          }
                                        })
                                        .catch((error) => {
                                          this.log.error('Device list error: ' + error);
                                          error.response && this.log.error('Device list error response: ' + JSON.stringify(error.response.data));
                                        });
                                    }
                                  
                                    async fetchSpecs() {
                                      this.log.info('Fetching Specs');
                                      const allDevices = await this.requestClient({
                                        url: 'https://miot-spec.org/miot-spec-v2/instances?status=all',
                                      }).catch((error) => {
                                        this.log.error('failing to get all devices');
                                        this.log.error(error);
                                        error.response && this.log.error(JSON.stringify(error.response.data));
                                      });
                                  
                                      const specs = [];
                                      for (const device of this.deviceArray) {
                                        const type = allDevices.data.instances
                                          .filter((obj) => {
                                            return obj.model === device.model && obj.status === 'released';
                                          })
                                          .map((obj) => {
                                            return obj.type;
                                          });
                                        if (type.length === 0) {
                                          this.log.info(`No spec found for ${device.model} set to default spec type`);
                                          type[0] = 'urn:miot-spec-v2:device:vacuum:0000A006:dreame-r2320:1';
                                        }
                                        device.spec_type = type[0];
                                        specs.push(type[0]);
                                      }
                                      await this.requestClient({
                                        method: 'post',
                                        url: 'https://miot-spec.org/miot-spec-v2/instance',
                                        data: {
                                          urns: specs,
                                        },
                                      })
                                        .then(async (res) => {
                                          this.specs = res.data;
                                        })
                                        .catch((error) => {
                                          this.log.error(error);
                                          error.response && this.log.error(JSON.stringify(error.response.data));
                                        });
                                    }
                                    async createRemotes() {
                                      for (const device of this.deviceArray) {
                                        if (this.specs[device.spec_type]) {
                                          this.log.debug(JSON.stringify(this.specs[device.spec_type]));
                                          await this.extractRemotesFromSpec(device);
                                        }
                                        const remoteArray = this.remoteCommands[device.model] || [];
                                        for (const remote of remoteArray) {
                                          await this.extendObject(device.did + '.remotePlugins', {
                                            type: 'channel',
                                            common: {
                                              name: 'Remote Controls extracted from Plugin definition',
                                              desc: 'Not so reliable alternative remotes',
                                            },
                                            native: {},
                                          });
                                          this.setObjectNotExists(device.did + '.remotePlugins.customCommand', {
                                            type: 'state',
                                            common: {
                                              name: 'Send Custom command via Plugin',
                                              type: 'mixed',
                                              role: 'state',
                                              def: 'set_level_favorite,16',
                                              write: true,
                                              read: true,
                                            },
                                            native: {},
                                          });
                                          let name = remote;
                                          let params = '';
                                          if (typeof remote === 'object') {
                                            name = remote.type;
                                            params = remote.params;
                                          }
                                          try {
                                            this.setObjectNotExists(device.did + '.remotePlugins.' + name, {
                                              type: 'state',
                                              common: {
                                                name: name + ' ' + params || '',
                                                type: 'mixed',
                                                role: 'state',
                                                def: false,
                                                write: true,
                                                read: true,
                                              },
                                              native: {},
                                            });
                                          } catch (error) {
                                            this.log.error(error);
                                          }
                                        }
                                      }
                                    }
                                    async extractRemotesFromSpec(device) {
                                      const spec = this.specs[device.spec_type];
                                      this.log.info(`Extracting remotes from spec for ${device.model} ${spec.description}`);
                                      this.log.info(
                                        'You can detailed information about status and remotes here: http://www.merdok.org/miotspec/?model=' +
                                          device.model,
                                      );
                                      let siid = 0;
                                      this.specStatusDict[device.did] = [];
                                  
                                      this.specActiosnToIdDict[device.did] = {};
                                      this.specPropsToIdDict[device.did] = {};
                                      for (const service of spec.services) {
                                        if (service.iid) {
                                          siid = service.iid;
                                        } else {
                                          siid++;
                                        }
                                        const typeArray = service.type.split(':');
                                        if (typeArray[3] === 'device-information') {
                                          continue;
                                        }
                                        if (!service.properties) {
                                          this.log.warn(`No properties for ${device.model} ${service.description} cannot extract information`);
                                          continue;
                                        }
                                  
                                        try {
                                          let piid = 0;
                                          for (const property of service.properties) {
                                            if (property.iid) {
                                              piid = property.iid;
                                            } else {
                                              piid++;
                                            }
                                            const remote = {
                                              siid: siid,
                                              piid: piid,
                                              did: device.did,
                                              model: device.model,
                                              name: service.description + ' ' + property.description + ' ' + service.iid + '-' + property.iid,
                                              type: property.type,
                                              access: property.access,
                                            };
                                            const typeName = property.type.split(':')[3];
                                            let path = 'status';
                                            let write = false;
                                  
                                            if (property.access.includes('write')) {
                                              path = 'remote';
                                              write = true;
                                            }
                                  
                                            const [type, role] = this.getRole(property.format, write, property['value-range']);
                                            this.log.debug(`Found remote for ${device.model} ${service.description} ${property.description}`);
                                  
                                            await this.extendObject(device.did + '.' + path, {
                                              type: 'channel',
                                              common: {
                                                name: path + ' extracted from Spec definition',
                                              },
                                              native: {},
                                            });
                                            if (path === 'remote') {
                                              await this.extendObject(device.did + '.' + path + '.customCommand', {
                                                type: 'state',
                                                common: {
                                                  name: 'Send Custom command via Spec',
                                                  type: 'string',
                                                  role: 'json',
                                                  def: `{
                                              "aiid": 9,
                                              "in": [
                                                  {
                                                      "order": 4,
                                                      "region": [
                                                          1
                                                      ],
                                                      "type": "order"
                                                  }
                                              ],
                                              "siid": 5
                                          }`,
                                                  write: true,
                                                  read: true,
                                                },
                                                native: {},
                                              });
                                            }
                                            const states = {};
                                            if (property['value-list']) {
                                              for (const value of property['value-list']) {
                                                states[value.value] = value.description;
                                              }
                                            }
                                            let unit;
                                            if (property.unit && property.unit !== 'none') {
                                              unit = property.unit;
                                            }
                                            await this.extendObject(device.did + '.' + path + '.' + typeName, {
                                              type: 'state',
                                              common: {
                                                name: remote.name || '',
                                                type: type,
                                                role: role,
                                                unit: unit,
                                                min: property['value-range'] ? property['value-range'][0] : undefined,
                                                max: property['value-range'] ? property['value-range'][1] : undefined,
                                                states: property['value-list'] ? states : undefined,
                                                write: write,
                                                read: true,
                                              },
                                              native: {
                                                siid: siid,
                                                piid: piid,
                                                did: device.did,
                                                model: device.model,
                                                name: service.description + ' ' + property.description,
                                                type: property.type,
                                                access: property.access,
                                              },
                                            });
                                  
                                            if (property.access.includes('notify')) {
                                              this.specStatusDict[device.did].push({
                                                did: device.did,
                                                siid: remote.siid,
                                                code: 0,
                                                piid: remote.piid,
                                                updateTime: 0,
                                              });
                                            }
                                            this.specPropsToIdDict[device.did][remote.siid + '-' + remote.piid] =
                                              device.did + '.' + path + '.' + typeName;
                                          }
                                          //extract actions
                                          let aiid = 0;
                                          if (service.actions) {
                                            for (const action of service.actions) {
                                              if (action.iid) {
                                                aiid = action.iid;
                                              } else {
                                                aiid++;
                                              }
                                              const remote = {
                                                siid: siid,
                                                aiid: aiid,
                                                did: device.did,
                                                model: device.model,
                                                name: service.description + ' ' + action.description + ' ' + service.iid + '-' + action.iid,
                                                type: action.type,
                                                access: action.access,
                                              };
                                              const typeName = action.type.split(':')[3];
                                  
                                              const path = 'remote';
                                              const write = true;
                                  
                                              let [type, role] = this.getRole(action.format, write, action['value-range']);
                                              this.log.debug(`Found actions for ${device.model} ${service.description} ${action.description}`);
                                  
                                              await this.extendObject(device.did + '.' + path, {
                                                type: 'channel',
                                                common: {
                                                  name: 'Remote Controls extracted from Spec definition',
                                                },
                                                native: {},
                                              });
                                              const states = {};
                                              if (action['value-list']) {
                                                for (const value of action['value-list']) {
                                                  states[value.value] = value.description;
                                                }
                                              }
                                              let def = '[]';
                                              if (action.in.length) {
                                                remote.name = remote.name + ' in[';
                                  
                                                for (const inParam of action.in) {
                                                  type = 'string';
                                                  role = 'text';
                                                  def = JSON.stringify(action.in);
                                                  const prop = service.properties.filter((obj) => {
                                                    return obj.iid === inParam;
                                                  });
                                                  if (prop.length > 0) {
                                                    remote.name = remote.name + prop[0].description + '';
                                                  }
                                                  if (action.in.indexOf(inParam) !== action.in.length - 1) {
                                                    remote.name = remote.name + ',';
                                                  }
                                                }
                                  
                                                remote.name = remote.name + ']';
                                              }
                                  
                                              if (action.out.length) {
                                                remote.name = remote.name + ' out[';
                                  
                                                for (const outParam of action.out) {
                                                  const prop = service.properties.filter((obj) => {
                                                    return obj.iid === outParam;
                                                  });
                                                  if (prop.length > 0) {
                                                    remote.name = remote.name + prop[0].description;
                                                  }
                                                  if (action.out.indexOf(outParam) !== action.out.length - 1) {
                                                    remote.name = remote.name + ',';
                                                  }
                                                }
                                                remote.name = remote.name + ']';
                                              }
                                              let unit;
                                              if (action.unit && action.unit !== 'none') {
                                                unit = action.unit;
                                              }
                                              await this.extendObject(device.did + '.' + path + '.' + typeName, {
                                                type: 'state',
                                                common: {
                                                  name: remote.name || '',
                                                  type: type,
                                                  role: role,
                                                  unit: unit,
                                                  min: action['value-range'] ? action['value-range'][0] : undefined,
                                                  max: action['value-range'] ? action['value-range'][1] : undefined,
                                                  states: action['value-list'] ? states : undefined,
                                                  write: write,
                                                  read: true,
                                                  def: def != null ? def : undefined,
                                                },
                                                native: {
                                                  siid: siid,
                                                  aiid: aiid,
                                                  did: device.did,
                                                  model: device.model,
                                                  in: action.in,
                                                  out: action.out,
                                                  name: service.description + ' ' + action.description,
                                                  type: action.type,
                                                  access: action.access,
                                                },
                                              });
                                              this.specActiosnToIdDict[device.did][service.iid + '-' + action.iid] =
                                                device.did + '.' + path + '.' + typeName;
                                            }
                                          }
                                        } catch (error) {
                                          this.log.error('Error while extracting spec for ' + device.model);
                                          this.log.error(error);
                                          this.log.error(error.stack);
                                          this.log.info(JSON.stringify(service));
                                        }
                                      }
                                    }
                                    async updateDevicesViaSpec() {
                                      for (const device of this.deviceArray) {
                                        if (this.specStatusDict[device.did]) {
                                          //split array in chunks of 50
                                          const chunkSize = 50;
                                          for (let i = 0; i < this.specStatusDict[device.did].length; i += chunkSize) {
                                            const chunk = this.specStatusDict[device.did].slice(i, i + chunkSize);
                                  
                                            const requestId = Math.floor(Math.random() * 9000) + 1000;
                                            const data = {
                                              did: device.did,
                                              id: requestId,
                                              data: {
                                                did: device.did,
                                                id: requestId,
                                                method: 'get_properties',
                                                params: chunk,
                                                from: 'XXXXXX',
                                              },
                                            };
                                            await this.requestClient({
                                              method: 'post',
                                              url: 'https://eu.iot.dreame.tech:13267/dreame-iot-com-10000/device/sendCommand',
                                              headers: {
                                                'user-agent': 'Dart/3.2 (dart:io)',
                                                'dreame-meta': 'cv=i_829',
                                                'dreame-rlc': '1a9bb36e6b22617cf465363ba7c232fb131899d593e8d1a1-1',
                                                'tenant-id': '000000',
                                                host: 'eu.iot.dreame.tech:13267',
                                                authorization: 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=',
                                                'content-type': 'application/json',
                                                'dreame-auth': 'bearer ' + this.session.access_token,
                                              },
                                              data: data,
                                            })
                                              .then(async (res) => {
                                                if (res.data.code !== 0) {
                                                  if (res.data.code === -8) {
                                                    this.log.debug(
                                                      `Error getting spec update for ${device.name} (${device.did}) with ${JSON.stringify(data)}`,
                                                    );
                                  
                                                    this.log.debug(JSON.stringify(res.data));
                                                    return;
                                                  }
                                                  this.log.info(
                                                    `Error getting spec update for ${device.name} (${device.did}) with ${JSON.stringify(data)}`,
                                                  );
                                                  this.log.debug(JSON.stringify(res.data));
                                                  return;
                                                }
                                                this.log.debug(JSON.stringify(res.data));
                                                for (const element of res.data.data.result) {
                                                  const path = this.specPropsToIdDict[device.did][element.siid + '-' + element.piid];
                                  				  this.log.info(' Update: ' + path);
                                                  if (path) {
                                                    this.log.debug(`Set ${path} to ${element.value}`);
                                                    if (element.value != null) {
                                                      this.setState(path, element.value, true);
                                                    }
                                                  }
                                                }
                                              })
                                              .catch((error) => {
                                                if (error.response) {
                                                  if (error.response.status === 401) {
                                                    error.response && this.log.debug(JSON.stringify(error.response.data));
                                                    this.log.info(' receive 401 error. Refresh Token in 60 seconds');
                                                    this.refreshTokenTimeout && clearTimeout(this.refreshTokenTimeout);
                                                    this.refreshTokenTimeout = setTimeout(() => {
                                                      this.refreshToken();
                                                    }, 1000 * 60);
                                  
                                                    return;
                                                  }
                                  
                                                  this.log.error(error);
                                                  error.stack && this.log.error(error.stack);
                                                  error.response && this.log.error(JSON.stringify(error.response.data));
                                                  return;
                                                }
                                  
                                                this.log.debug(error);
                                                this.log.debug(JSON.stringify(error));
                                              });
                                          }
                                        }
                                      }
                                    }
                                    getRole(element, write, valueRange) {
                                      if (!element) {
                                        return ['string', 'json'];
                                      }
                                      if (element === 'bool' && !write) {
                                        return ['boolean', 'indicator'];
                                      }
                                      if (element === 'bool' && write) {
                                        return ['boolean', 'switch'];
                                      }
                                      if ((element.indexOf('int') !== -1 || valueRange) && !write) {
                                        return ['number', 'value'];
                                      }
                                      if ((element.indexOf('int') !== -1 || valueRange) && write) {
                                        return ['number', 'level'];
                                      }
                                  
                                      return ['string', 'text'];
                                    }
                                    async connectMqtt() {
                                      if (this.mqttClient) {
                                        this.mqttClient.end();
                                      }
                                      const url = this.deviceArray[0].bindDomain || 'app.mt.eu.iot.dreame.tech:19973';
                                      this.mqttClient = mqtt.connect('mqtts://' + url, {
                                        clientId: 'p_' + crypto.randomBytes(8).toString('hex'),
                                        username: this.session.uid,
                                        password: this.session.access_token,
                                        rejectUnauthorized: false,
                                        reconnectPeriod: 10000,
                                      });
                                  
                                      this.mqttClient.on('connect', () => {
                                        this.log.info('Connected to MQTT');
                                        for (const device of this.deviceArray) {
                                          this.mqttClient.subscribe(`/status/${device.did}/${this.session.uid}/${device.model}/eu/`);
                                        }
                                      });
                                      this.mqttClient.on('message', async (topic, message) => {
                                        // message is Buffer
                                        this.log.debug(topic.toString());
                                        this.log.debug(message.toString());
                                        /*
                                        {"id":92,"did":XXXXXX,"data":{"id":92,"method":"properties_changed","params":[{"did":"XXXXX","siid":2,"piid":6,"value":1},{"did":"XXXXX","siid":4,"piid":23,"value":5121}]}}
                                        */
                                        try {
                                          message = JSON.parse(message.toString());
                                  		  	//this.log.info(' Get Message:' + JSON.stringify(message));
                                        } catch (error) {
                                          this.log.error(error);
                                          return;
                                        }
                                        if (message.data && message.data.method === 'properties_changed') {
                                  
                                          for (const element of message.data.params) {
                                            if (!this.specPropsToIdDict[element.did]) {
                                              this.log.debug(`No spec found for ${element.did}`);
                                              continue;
                                            }
                                  
                                  		  if (JSON.stringify(element.siid) === '6' && JSON.stringify(element.piid) === '1') {
                                  		     this.log.info(' Map data:' + JSON.stringify(element.value));
                                  			  let encode = JSON.stringify(element.value);
                                  			  let mappath = `${element.did}` + '.map.';
                                  			  this.uncompress(encode, mappath);
                                  		  }
                                  		  //this.log.info(' Map data:' + JSON.stringify(element.siid) + ' => ' + JSON.stringify(element.piid));
                                  
                                            let path = this.specPropsToIdDict[element.did][element.siid + '-' + element.piid];
                                            if (!path) {
                                              this.log.debug(`No path found for ${element.did} ${element.siid}-${element.piid}`);
                                  
                                              path = `${element.did}.status.${element.siid}-${element.piid}`;
                                  
                                              await this.extendObject(path, {
                                                type: 'state',
                                                common: {
                                                  name: path,
                                                  type: 'mixed',
                                                  role: 'state',
                                                  write: false,
                                                  read: true,
                                                },
                                                native: {},
                                              });
                                              this.setState(path, JSON.stringify(element.value), true);
                                              path = `${element.did}.remote.${element.siid}-${element.piid}`;
                                  			this.log.info(' Update: SIID:' + element.siid + ' PIID: ' + element.piid);
                                              await this.extendObject(path, {
                                                type: 'state',
                                                common: {
                                                  name: path,
                                                  type: 'mixed',
                                                  role: 'state',
                                                  write: true,
                                                  read: true,
                                                },
                                                native: {},
                                              });
                                            }
                                            if (path) {
                                              this.log.debug(`Set ${path} to ${element.value}`);
                                              if (element.value != null) {
                                                // this.setState(path, JSON.stringify(element.value), true);
                                                this.json2iob.parse(path, JSON.stringify(element));
                                              }
                                            }
                                          }
                                        }
                                      });
                                      this.mqttClient.on('error', async (error) => {
                                        this.log.error(error);
                                        if (error.message && error.message.includes('Not authorized')) {
                                          this.log.error('Not authorized to connect to MQTT');
                                          this.setState('info.connection', false, true);
                                          await this.refreshToken();
                                        }
                                      });
                                      this.mqttClient.on('close', () => {
                                        this.log.info('MQTT Connection closed');
                                      });
                                    }
                                  
                                  async uncompress(In_Compressed, In_path){
                                      var input_Raw = In_Compressed.replace(/-/g, '+').replace(/_/g, '/');
                                  	var encodedData = Buffer.from(input_Raw, 'base64');
                                      var decode = zlib.inflateSync(encodedData);
                                  	/*csvar mapHeader = decode.toString().split("{");
                                      let GetHeader = mapHeader[0];
                                  	this.log.info(' decode Header 1: ' + GetHeader);
                                  	try {
                                  		var encodedDataH = Buffer.from(GetHeader, 'base64');
                                  		this.log.info(' Base64 decode Header : ' + encodedDataH);
                                  		var decodeHeader = zlib.inflateSync(encodedDataH);
                                  		} catch (e) {
                                          this.log.info(' Error decode Header 2: ' + e);
                                          } finally {
                                          this.log.info(' decode Header 2: ' + decodeHeader);
                                          }
                                      */
                                  
                                  	var jsondecode = decode.toString().match(/[{\[]{1}([,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]|".*?")+[}\]]{1}/gis);
                                  	const jsonread = JSON.parse(jsondecode);
                                  
                                  	for (var [key, value] of Object.entries(jsonread)) {
                                         this.log.info(' decode Map JSON:' + `${key}: ${value}`);
                                  
                                  	    if(Object.prototype.toString.call(value) !== '[object Object]'){
                                  	      if (value != null) {
                                  			let pathMap = In_path + key;
                                  			await this.extendObject(pathMap, {
                                                type: 'state',
                                                common: {
                                                  name: pathMap,
                                                  type: 'mixed',
                                                  role: 'state',
                                                  write: false,
                                                  read: true,
                                                },
                                                native: {},
                                              });
                                  
                                  			this.setState(pathMap, value, true);
                                  		  }
                                  	    }
                                  
                                  	   if (typeof value === 'object' && value !== null){
                                  		   if(Object.prototype.toString.call(value) === '[object Object]'){
                                  		       for (var [Subkey, Subvalue] of Object.entries(value)) {
                                                    this.log.info(' decode subkey ' + key + ' ==> ' + `${Subkey}: ${Subvalue}`);
                                  				  if (value != null) {
                                  					  let pathMap = In_path + key + '.' + Subkey;
                                  					  await this.extendObject(pathMap, {
                                  						type: 'state',
                                                          common: {
                                                          name: pathMap,
                                  						type: 'mixed',
                                                          role: 'state',
                                                          write: false,
                                                          read: true,
                                                          },
                                                          native: {},
                                                       });
                                  
                                  			         this.setState(pathMap, Subvalue, true);
                                  		          }
                                  		       }
                                              }
                                  	   }
                                  
                                  	}
                                  }
                                  
                                    async refreshToken() {
                                      await this.requestClient({
                                        method: 'post',
                                        url: 'https://eu.iot.dreame.tech:13267/dreame-auth/oauth/token',
                                        headers: {
                                          'user-agent': 'Dart/3.2 (dart:io)',
                                          'dreame-meta': 'cv=i_829',
                                          'dreame-rlc': '1a9bb36e6b22617cf465363ba7c232fb131899d593e8d1a1-1',
                                          'tenant-id': '000000',
                                          host: 'eu.iot.dreame.tech:13267',
                                          authorization: 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=',
                                          'content-type': 'application/x-www-form-urlencoded',
                                        },
                                        data: {
                                          grant_type: 'refresh_token',
                                          refresh_token: this.session.refresh_token,
                                        },
                                      })
                                        .then((response) => {
                                          this.log.debug('Login response: ' + JSON.stringify(response.data));
                                          this.session = response.data;
                                          this.setState('info.connection', true, true);
                                          //reconnect mqtt
                                          this.connectMqtt();
                                        })
                                        .catch((error) => {
                                          this.log.error('Refresh Token  error: ' + error);
                                          error.response && this.log.error('Refresh Token error response: ' + JSON.stringify(error.response.data));
                                          this.setState('info.connection', false, true);
                                        });
                                    }
                                  
                                  
                                    /**
                                     * Is called when adapter shuts down - callback has to be called under any circumstances!
                                     * @param {() => void} callback
                                     */
                                    onUnload(callback) {
                                      try {
                                        callback();
                                      } catch (e) {
                                        callback();
                                      }
                                    }
                                  
                                    /**
                                     * Is called if a subscribed state changes
                                     * @param {string} id
                                     * @param {ioBroker.State | null | undefined} state
                                     */
                                    async onStateChange(id, state) {
                                      if (state) {
                                        if (!state.ack) {
                                          const deviceId = id.split('.')[2];
                                          const folder = id.split('.')[3];
                                          let command = id.split('.')[4];
                                          this.log.debug(`Receive command ${command} for ${deviceId} in folder ${folder} with value ${state.val} `);
                                          // let type;
                                          if (command) {
                                            // type = command.split("-")[1];
                                            command = command.split('-')[0];
                                          }
                                          if (id.split('.')[4] === 'Refresh') {
                                            this.updateDevicesViaSpec();
                                            return;
                                          }
                                          //{"id":0,"method":"app_start","params":[{"clean_mop":0}]}
                                  
                                          const stateObject = await this.getObjectAsync(id);
                                  
                                          const requestId = Math.floor(Math.random() * 9000) + 1000;
                                  
                                          const data = {
                                            did: deviceId,
                                            id: requestId,
                                            data: {
                                              did: deviceId,
                                              id: requestId,
                                              method: 'action',
                                              params: {},
                                              from: 'XXXXXX',
                                            },
                                          };
                                          if (stateObject && stateObject.native.piid) {
                                            data.data.params = {
                                              did: deviceId,
                                              siid: stateObject.native.siid,
                                              piid: stateObject.native.piid,
                                              value: state.val,
                                            };
                                          }
                                          if (stateObject && stateObject.native.aiid) {
                                            data.data.params = { did: deviceId, siid: stateObject.native.siid, aiid: stateObject.native.aiid };
                                            if (typeof state.val !== 'boolean') {
                                              try {
                                                data.data.params['in'] = JSON.parse(state.val);
                                              } catch (error) {
                                                this.log.error(error);
                                                return;
                                              }
                                            }
                                  
                                            const device = this.deviceArray.filter((obj) => {
                                              return obj.did === deviceId;
                                            })[0];
                                            if (device && device.model.includes('mower')) {
                                              data.data.params.siid += 3;
                                            }
                                  
                                            // data.params.in = [];
                                          }
                                          if (command === 'customCommand') {
                                            try {
                                              data.data.params = JSON.parse(state.val);
                                              data.data.params.did = deviceId;
                                            } catch (error) {
                                              this.log.error(error);
                                              return;
                                            }
                                          }
                                          this.log.info(`Send: ${JSON.stringify(data)} to ${deviceId}`);
                                  
                                          await this.requestClient({
                                            method: 'post',
                                            url: 'https://eu.iot.dreame.tech:13267/dreame-iot-com-10000/device/sendCommand',
                                            headers: {
                                              'user-agent': 'Dart/3.2 (dart:io)',
                                              'dreame-meta': 'cv=i_829',
                                              'dreame-rlc': '1a9bb36e6b22617cf465363ba7c232fb131899d593e8d1a1-1',
                                              'tenant-id': '000000',
                                              host: 'eu.iot.dreame.tech:13267',
                                              authorization: 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=',
                                              'content-type': 'application/json',
                                              'dreame-auth': 'bearer ' + this.session.access_token,
                                            },
                                            data: data,
                                          })
                                            .then(async (res) => {
                                              if (res.data.code !== 0) {
                                                this.log.error('Error setting device state');
                                                this.log.error(JSON.stringify(res.data));
                                                return;
                                              }
                                              if (res.data.result && res.data.result.length > 0) {
                                                res.data = res.data.result[0];
                                              }
                                              this.log.info(JSON.stringify(res.data));
                                              if (!res.data.result) {
                                                return;
                                              }
                                              const result = res.data.result;
                                              if (result.out) {
                                                const path = this.specActiosnToIdDict[result.did][result.siid + '-' + result.aiid];
                                                this.log.debug(path);
                                                const stateObject = await this.getObjectAsync(path);
                                                if (stateObject && stateObject.native.out) {
                                                  const out = stateObject.native.out;
                                                  for (const outItem of out) {
                                                    const index = out.indexOf(outItem);
                                                    const outPath = this.specPropsToIdDict[result.did][result.siid + '-' + outItem];
                                                    // await this.setState(outPath, result.out[index], true);
                                                    this.json2iob.parse(outPath, result.out[index]);
                                                    this.log.info('Set ' + outPath + ' to ' + result.out[index]);
                                                  }
                                                } else {
                                                  this.log.info(JSON.stringify(result.out));
                                                }
                                              }
                                            })
                                            .catch(async (error) => {
                                              this.log.error(error);
                                              error.response && this.log.error(JSON.stringify(error.response.data));
                                            });
                                          this.refreshTimeout = setTimeout(async () => {
                                            this.log.info('Update devices');
                                            await this.updateDevicesViaSpec();
                                          }, 10 * 1000);
                                        }
                                      }
                                    }
                                  }
                                  
                                  if (require.main !== module) {
                                    // Export the constructor in compact mode
                                    /**
                                     * @param {Partial<utils.AdapterOptions>} [options={}]
                                     */
                                    module.exports = (options) => new Dreame(options);
                                  } else {
                                    // otherwise start the instance directly
                                    new Dreame();
                                  }
                                  
                                  

                                  @SmartiSmart

                                  Mit dem folgenden Code kann ich dem Saugroboter über Alexa Befehle geben, um bestimmte Räume zu säubern. Beachten Sie, dass die Räume und das „Dreame“-Objekt zuvor angepasst werden mussten.

                                  on({ id: 'alexa2.0.History.summary' /* summary */, change: 'any' }, async (obj) => {
                                    let value = obj.state.val;
                                    let oldValue = obj.oldState.val;
                                    AlexaHistorysummary = getState('alexa2.0.History.summary').val;
                                    SearchClean = AlexaHistorysummary.lastIndexOf('sauber machen') + 1;
                                    SerachMoreClean = AlexaHistorysummary.lastIndexOf('sehr dreckig') + 1;
                                    ArrayRoom = ['schlafzimmer', 'toilette', 'abstellraum', 'kinderzimmer', 'esszimmer', 'arbeitszimmer','eingang', 'wohnzimmer', 'flur', 'küche', '];
                                    SerachRoomName = '';
                                    Room1 = false;
                                    Room2 = false;
                                    Room3 = false;
                                    Room4 = false;
                                    Room5 = false;
                                    Room6 = false;
                                    Room7 = false;
                                    Room8 = false;
                                    Room9 = false;
                                    Room10 = false;
                                    ResetRoomNumber = 0;
                                    FoundRoomState = false;
                                    StringToSend = '';
                                    CollectName = '';
                                    for (var i_index in ArrayRoom) {
                                      i = ArrayRoom[i_index];
                                      ResetRoomNumber = (typeof ResetRoomNumber === 'number' ? ResetRoomNumber : 0) + 1;
                                      SerachRoom = AlexaHistorysummary.lastIndexOf(i) + 1;
                                      if (parseFloat(SerachRoom) > 0) {
                                        if (parseFloat(SerachMoreClean) > 0) {
                                          SearchClean = 1;
                                          CleanSettings = ',3,3,3,1],';
                                        } else {
                                          CleanSettings = ',1,3,2,1],';
                                        }
                                        FoundRoomState = true;
                                        CollectName = [i,' und ',CollectName].join('');
                                        StringToSend = ['[',ResetRoomNumber,CleanSettings,StringToSend].join('');
                                      }
                                    }
                                    if (parseFloat(SearchClean) > 0) {
                                      CleanRoomCode = String(StringToSend) + String(StringToSend.slice(0, StripString - 1));
                                      CleanRoomCode = CleanRoomCode.slice(0, (CleanRoomCode.lastIndexOf(' und ') + 1) - 1);
                                      StringToSend = [' [{"piid": 1,"value": 18},{"piid": 10,"value": "{\\"selects\\": [',CleanRoomCode,']}"}]'].join('');
                                      console.log(('Now: ' + String(StringToSend)));
                                      CollectName = CollectName.slice(0, (CollectName.lastIndexOf(' und ') + 1) - 1);
                                      LastAlexa = getState('alexa2.0.History.serialNumber').val;
                                      if (FoundRoomState == true) {
                                        setStateDelayed((['alexa2.0.Echo-Devices.',LastAlexa,'.Commands.deviceStop'].join('')), true, false, parseInt(((0) || '').toString(), 10), true);
                                        setStateDelayed((['alexa2.0.Echo-Devices.',LastAlexa,'.Commands.deviceStop'].join('')), false, false, parseInt(((100) || '').toString(), 10), true);
                                        setStateDelayed((['alexa2.0.Echo-Devices.',LastAlexa,'.Commands.speak'].join('')), (['OK! ',CollectName,' sauber machen.'].join('')), false, parseInt(((0) || '').toString(), 10), true);
                                        setState('dreame.0.*********.remote.stop-clean' /* vacuum-extend stop-clean 4-2 */, '[ 1 ]');
                                        setStateDelayed('dreame.0.********.remote.start-clean' /* vacuum-extend start-clean 4-1 in[clean-extend-data,work-mode] */, StringToSend, 300, false);
                                      }
                                    }
                                  });
                                  
                                  wawyoW S 2 Replies Last reply
                                  0
                                  • wawyoW wawyo

                                    Hallo zusammen,
                                    ich habe mich intensiv mit dem Adapter beschäftigt, da mich der "dreame.0..status.map-data" Objekt interessiert hat. Dabei konnte ich den Adapter so anpassen, dass ich neue Baum-Objekte hinzufügen konnte (dreame.0..map) . Dies ermöglichte es mir, eine Fülle von Daten über die Position des Saugroboters (L20) zu sammeln. Es scheint, als ob noch viele weitere Daten vorhanden sind. Unter "Clean Set" sind alle Räume mit den entsprechenden Einstellungen vorhanden.

                                    Um das Ziel zu erreichen, den Saugroboter in Echtzeit zu verfolgen, benötige ich nun Unterstützung von weiteren Personen. Unten finden Sie den modifizierten Code für eine detaillierte Ansicht.
                                    DreameMap.png

                                    'use strict';
                                    
                                    /*
                                     * Created with @iobroker/create-adapter v2.6.3
                                     */
                                    
                                    // The adapter-core module gives you access to the core ioBroker functions
                                    // you need to create an adapter
                                    const utils = require('@iobroker/adapter-core');
                                    const axios = require('axios').default;
                                    const Json2iob = require('json2iob');
                                    const crypto = require('crypto');
                                    const mqtt = require('mqtt');
                                    const zlib = require("node:zlib");
                                    
                                    class Dreame extends utils.Adapter {
                                      /**
                                       * @param {Partial<utils.AdapterOptions>} [options={}]
                                       */
                                      constructor(options) {
                                        super({
                                          ...options,
                                          name: 'dreame',
                                        });
                                        this.on('ready', this.onReady.bind(this));
                                        this.on('stateChange', this.onStateChange.bind(this));
                                        this.on('unload', this.onUnload.bind(this));
                                        this.deviceArray = [];
                                        this.states = {};
                                        this.json2iob = new Json2iob(this);
                                        this.requestClient = axios.create({
                                          withCredentials: true,
                                          timeout: 3 * 60 * 1000, //3min client timeout
                                        });
                                    
                                        this.remoteCommands = {};
                                        this.specStatusDict = {};
                                        this.specPropsToIdDict = {};
                                        this.specActiosnToIdDict = {};
                                      }
                                    
                                      /**
                                       * Is called when databases are connected and adapter received configuration.
                                       */
                                      async onReady() {
                                        this.setState('info.connection', false, true);
                                        if (this.config.interval < 0.5) {
                                          this.log.info('Set interval to minimum 0.5');
                                          this.config.interval = 0.5;
                                        }
                                        if (this.config.interval > 2147483647) {
                                          this.log.info('Set interval to maximum 2147483647');
                                          this.config.interval = 2147483647;
                                        }
                                        if (!this.config.username || !this.config.password) {
                                          this.log.error('Please set username and password in the instance settings');
                                          return;
                                        }
                                    
                                        this.updateInterval = null;
                                        this.reLoginTimeout = null;
                                        this.refreshTokenTimeout = null;
                                        this.session = {};
                                        this.subscribeStates('*.remote.*');
                                    
                                        this.log.info('Login to Dreame Cloud...');
                                        await this.login();
                                    
                                        if (this.session.access_token) {
                                          await this.getDeviceList();
                                          await this.fetchSpecs();
                                          await this.createRemotes();
                                          await this.updateDevicesViaSpec();
                                          await this.connectMqtt();
                                          this.updateInterval = setInterval(
                                            async () => {
                                              await this.updateDevicesViaSpec();
                                            },
                                            this.config.interval * 60 * 1000,
                                          );
                                          this.refreshTokenInterval = setInterval(
                                            async () => {
                                              await this.refreshToken();
                                            },
                                            (this.session.expires_in - 100 || 3500) * 1000,
                                          );
                                        }
                                      }
                                    
                                      async login() {
                                        await this.requestClient({
                                          method: 'post',
                                          url: 'https://eu.iot.dreame.tech:13267/dreame-auth/oauth/token',
                                          headers: {
                                            'user-agent': 'Dart/3.2 (dart:io)',
                                            'dreame-meta': 'cv=i_829',
                                            'dreame-rlc': '1a9bb36e6b22617cf465363ba7c232fb131899d593e8d1a1-1',
                                            'tenant-id': '000000',
                                            host: 'eu.iot.dreame.tech:13267',
                                            authorization: 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=',
                                            'content-type': 'application/x-www-form-urlencoded',
                                            'dreame-auth': 'bearer',
                                          },
                                          data: {
                                            grant_type: 'password',
                                            scope: 'all',
                                            platform: 'IOS',
                                            type: 'account',
                                            username: this.config.username,
                                            password: crypto
                                              .createHash('md5')
                                              .update(this.config.password + 'RAylYC%fmSKp7%Tq')
                                              .digest('hex'),
                                            country: 'DE',
                                            lang: 'de',
                                          },
                                        })
                                          .then((response) => {
                                            this.log.debug('Login response: ' + JSON.stringify(response.data));
                                            this.session = response.data;
                                            this.setState('info.connection', true, true);
                                          })
                                          .catch((error) => {
                                            this.log.error('Login error: ' + error);
                                            error.response && this.log.error('Login error response: ' + JSON.stringify(error.response.data));
                                            this.setState('info.connection', false, true);
                                          });
                                      }
                                      async getDeviceList() {
                                        await this.requestClient({
                                          method: 'post',
                                          maxBodyLength: Infinity,
                                          url: 'https://eu.iot.dreame.tech:13267/dreame-user-iot/iotuserbind/device/listV2',
                                          headers: {
                                            'user-agent': 'Dart/3.2 (dart:io)',
                                            'dreame-meta': 'cv=i_829',
                                            'dreame-rlc': '1a9bb36e6b22617cf465363ba7c232fb131899d593e8d1a1-1',
                                            'tenant-id': '000000',
                                            host: 'eu.iot.dreame.tech:13267',
                                            authorization: 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=',
                                            'content-type': 'application/json',
                                            'dreame-auth': 'bearer ' + this.session.access_token,
                                          },
                                          data: {
                                            sharedStatus: 1,
                                            current: 1,
                                            size: 100,
                                            lang: 'de',
                                            timestamp: Date.now(),
                                          },
                                        })
                                          .then(async (response) => {
                                            /*
                                            example response:
                                            {
                                        "code": 0,
                                        "success": true,
                                        "data": {
                                            "page": {
                                                "records": [
                                                    {
                                                        "id": "xxx",
                                                        "did": "xxxx",
                                                        "model": "dreame.vacuum.r2449k",
                                                        "ver": "4.3.9_1252",
                                                        "customName": "",
                                                        "property": "{\"iotId\":\"xxxxxxxx\",\"lwt\":1,\"mac\":\"\"}",
                                                        "mac": "7",
                                                        "vendor": "ali",
                                                        "master": true,
                                                        "masterUid": "",
                                                        "masterUid2UUID": null,
                                                        "masterName": null,
                                                        "permissions": "",
                                                        "bindDomain": "10000.mt.eu.iot.dreame.tech:19973",
                                                        "sharedTimes": 0,
                                                        "sharedStatus": 1,
                                                        "calltag": null,
                                                        "updateTime": "2024-06-07 12:03:09",
                                                        "lang": null,
                                                        "deviceInfo": {
                                                            "productId": "10279",
                                                            "categoryPath": "/lifeapps/vacuum",
                                                            "model": "dreame.vacuum.r2449k",
                                                            "remark": "",
                                                            "feature": "video_ali,fastCommand",
                                                            "videoDynamicVendor": true,
                                                            "defaultVendors": [
                                                                "ali"
                                                            ],
                                                            "scType": "WIFI",
                                                            "extendScType": [
                                                                "QR_CODE"
                                                            ],
                                                            "status": "Live",
                                                            "mainImage": {
                                                                "as": "1",
                                                                "caption": "1",
                                                                "height": 0,
                                                                "width": 0,
                                                                "imageUrl": "https://oss.iot.dreame.tech/pub/pic/000000/ali_dreame/dreame.vacuum.r2449k/9acf24adb5ca3d15341fd869f2aa985f20240311084500.png",
                                                                "smallImageUrl": ""
                                                            },
                                                            "popup": {
                                                                "as": "1",
                                                                "caption": "1",
                                                                "height": 0,
                                                                "width": 0,
                                                                "imageUrl": "https://oss.iot.dreame.tech/pub/pic/000000/ali_dreame/dreame.vacuum.r2449k/9acf24adb5ca3d15341fd869f2aa985f20240311084500.png",
                                                                "smallImageUrl": ""
                                                            },
                                                            "icon": {
                                                                "as": "1",
                                                                "caption": "1",
                                                                "height": 0,
                                                                "width": 0,
                                                                "imageUrl": "https://oss.iot.dreame.tech/pub/pic/000000/ali_dreame/dreame.vacuum.r2449k/9acf24adb5ca3d15341fd869f2aa985f20240311084500.png",
                                                                "smallImageUrl": ""
                                                            },
                                                            "overlook": {
                                                                "as": "1",
                                                                "caption": "1",
                                                                "height": 0,
                                                                "width": 0,
                                                                "imageUrl": "https://oss.iot.dreame.tech/pub/pic/000000/ali_dreame/dreame.vacuum.r2449k/9acf24adb5ca3d15341fd869f2aa985f20240311084500.png",
                                                                "smallImageUrl": ""
                                                            },
                                                            "images": [],
                                                            "extensionId": "1228",
                                                            "updatedAt": "1711721850712",
                                                            "createdAt": "1705565151025",
                                                            "releaseAt": "1710848634605",
                                                            "quickConnectStatus": -1,
                                                            "quickConnects": {},
                                                            "permit": "video",
                                                            "firmwareDevelopType": "SINGLE_PLATFORM",
                                                            "bindType": "",
                                                            "displayName": "X40 Ultra Complete",
                                                            "liveKeyDefine": {},
                                                            "qaKeyDefine": {}
                                                        },
                                                        "online": true,
                                                        "latestStatus": 21,
                                                        "battery": 100,
                                                        "videoStatus": "{\"operType\":\"end\",\"operation\":\"monitor\",\"result\":0,\"status\":0}",
                                                        "region": null,
                                                        "featureCode": -1,
                                                        "featureCode2": 31,
                                                        "keyDefine": {
                                                            "ver": 1,
                                                            "url": "https://cnbj2.fds.api.xiaomi.com/000000-public/file/54587b0364cdd763deba93a974ef5aa05cbe7dcc_dreame.vacuum.r2449k_iotKeyValue_translate_1.json"
                                                        }
                                                    }
                                                ],
                                                "total": "1",
                                                "size": "100",
                                                "current": "1",
                                                "orders": [],
                                                "optimizeCountSql": true,
                                                "hitCount": false,
                                                "searchCount": true,
                                                "pages": "1"
                                            }
                                        },
                                        "msg": "操作成功"
                                    }
                                            */
                                            this.log.debug('Device list response: ' + JSON.stringify(response.data));
                                    
                                            if (
                                              response.data.code == '0' &&
                                              response.data &&
                                              response.data.data &&
                                              response.data.data.page &&
                                              response.data.data.page.records
                                            ) {
                                              this.deviceArray = response.data.data.page.records;
                                              for (const device of this.deviceArray) {
                                                await this.extendObject(device.did, {
                                                  type: 'device',
                                                  common: {
                                                    name: device.customName || device.deviceInfo.displayName || device.model,
                                                  },
                                                  native: {},
                                                });
                                                if (device.keyDefine) {
                                                  const iotKeyValue = await this.requestClient({
                                                    method: 'get',
                                                    url: device.keyDefine.url,
                                                  })
                                                    .then((response) => {
                                                      this.log.debug('iotKeyValue response: ' + JSON.stringify(response.data));
                                                      return response.data;
                                                    })
                                                    .catch((error) => {
                                                      this.log.error('iotKeyValue error: ' + error);
                                                      error.response &&
                                                        this.log.error('iotKeyValue error response: ' + JSON.stringify(error.response.data));
                                                    });
                                                  /*{
                                                "keyDefine": {
                                                  "2.1": {
                                                    "de": {
                                                      "1": "Reinigung"}},
                                                "ver": 1,
                                                "model": "dreame.vacuum.r2449k",
                                                "hash": "54587b0364cdd763deba93a974ef5aa05cbe7dcc"
                                              }*/
                                    
                                                  if (iotKeyValue && iotKeyValue.keyDefine) {
                                                    //replace dot in id with - and select en language
                                    
                                                    for (const key in iotKeyValue.keyDefine) {
                                                      if (Object.hasOwnProperty.call(iotKeyValue.keyDefine, key)) {
                                                        const element = iotKeyValue.keyDefine[key];
                                                        if (element['en'] && element['en'] !== 'null') {
                                                          this.states[device.did][key.replace(/\./g, '-')] = element['en'];
                                                        }
                                                      }
                                                    }
                                                  }
                                                }
                                                this.json2iob.parse(device.did + '.general', device, {
                                                  states: { latestStatus: this.states[device.did] },
                                                  channelName: 'General Updated at Start',
                                                });
                                              }
                                            } else {
                                              this.log.error('No Devices found: ' + JSON.stringify(response.data));
                                            }
                                          })
                                          .catch((error) => {
                                            this.log.error('Device list error: ' + error);
                                            error.response && this.log.error('Device list error response: ' + JSON.stringify(error.response.data));
                                          });
                                      }
                                    
                                      async fetchSpecs() {
                                        this.log.info('Fetching Specs');
                                        const allDevices = await this.requestClient({
                                          url: 'https://miot-spec.org/miot-spec-v2/instances?status=all',
                                        }).catch((error) => {
                                          this.log.error('failing to get all devices');
                                          this.log.error(error);
                                          error.response && this.log.error(JSON.stringify(error.response.data));
                                        });
                                    
                                        const specs = [];
                                        for (const device of this.deviceArray) {
                                          const type = allDevices.data.instances
                                            .filter((obj) => {
                                              return obj.model === device.model && obj.status === 'released';
                                            })
                                            .map((obj) => {
                                              return obj.type;
                                            });
                                          if (type.length === 0) {
                                            this.log.info(`No spec found for ${device.model} set to default spec type`);
                                            type[0] = 'urn:miot-spec-v2:device:vacuum:0000A006:dreame-r2320:1';
                                          }
                                          device.spec_type = type[0];
                                          specs.push(type[0]);
                                        }
                                        await this.requestClient({
                                          method: 'post',
                                          url: 'https://miot-spec.org/miot-spec-v2/instance',
                                          data: {
                                            urns: specs,
                                          },
                                        })
                                          .then(async (res) => {
                                            this.specs = res.data;
                                          })
                                          .catch((error) => {
                                            this.log.error(error);
                                            error.response && this.log.error(JSON.stringify(error.response.data));
                                          });
                                      }
                                      async createRemotes() {
                                        for (const device of this.deviceArray) {
                                          if (this.specs[device.spec_type]) {
                                            this.log.debug(JSON.stringify(this.specs[device.spec_type]));
                                            await this.extractRemotesFromSpec(device);
                                          }
                                          const remoteArray = this.remoteCommands[device.model] || [];
                                          for (const remote of remoteArray) {
                                            await this.extendObject(device.did + '.remotePlugins', {
                                              type: 'channel',
                                              common: {
                                                name: 'Remote Controls extracted from Plugin definition',
                                                desc: 'Not so reliable alternative remotes',
                                              },
                                              native: {},
                                            });
                                            this.setObjectNotExists(device.did + '.remotePlugins.customCommand', {
                                              type: 'state',
                                              common: {
                                                name: 'Send Custom command via Plugin',
                                                type: 'mixed',
                                                role: 'state',
                                                def: 'set_level_favorite,16',
                                                write: true,
                                                read: true,
                                              },
                                              native: {},
                                            });
                                            let name = remote;
                                            let params = '';
                                            if (typeof remote === 'object') {
                                              name = remote.type;
                                              params = remote.params;
                                            }
                                            try {
                                              this.setObjectNotExists(device.did + '.remotePlugins.' + name, {
                                                type: 'state',
                                                common: {
                                                  name: name + ' ' + params || '',
                                                  type: 'mixed',
                                                  role: 'state',
                                                  def: false,
                                                  write: true,
                                                  read: true,
                                                },
                                                native: {},
                                              });
                                            } catch (error) {
                                              this.log.error(error);
                                            }
                                          }
                                        }
                                      }
                                      async extractRemotesFromSpec(device) {
                                        const spec = this.specs[device.spec_type];
                                        this.log.info(`Extracting remotes from spec for ${device.model} ${spec.description}`);
                                        this.log.info(
                                          'You can detailed information about status and remotes here: http://www.merdok.org/miotspec/?model=' +
                                            device.model,
                                        );
                                        let siid = 0;
                                        this.specStatusDict[device.did] = [];
                                    
                                        this.specActiosnToIdDict[device.did] = {};
                                        this.specPropsToIdDict[device.did] = {};
                                        for (const service of spec.services) {
                                          if (service.iid) {
                                            siid = service.iid;
                                          } else {
                                            siid++;
                                          }
                                          const typeArray = service.type.split(':');
                                          if (typeArray[3] === 'device-information') {
                                            continue;
                                          }
                                          if (!service.properties) {
                                            this.log.warn(`No properties for ${device.model} ${service.description} cannot extract information`);
                                            continue;
                                          }
                                    
                                          try {
                                            let piid = 0;
                                            for (const property of service.properties) {
                                              if (property.iid) {
                                                piid = property.iid;
                                              } else {
                                                piid++;
                                              }
                                              const remote = {
                                                siid: siid,
                                                piid: piid,
                                                did: device.did,
                                                model: device.model,
                                                name: service.description + ' ' + property.description + ' ' + service.iid + '-' + property.iid,
                                                type: property.type,
                                                access: property.access,
                                              };
                                              const typeName = property.type.split(':')[3];
                                              let path = 'status';
                                              let write = false;
                                    
                                              if (property.access.includes('write')) {
                                                path = 'remote';
                                                write = true;
                                              }
                                    
                                              const [type, role] = this.getRole(property.format, write, property['value-range']);
                                              this.log.debug(`Found remote for ${device.model} ${service.description} ${property.description}`);
                                    
                                              await this.extendObject(device.did + '.' + path, {
                                                type: 'channel',
                                                common: {
                                                  name: path + ' extracted from Spec definition',
                                                },
                                                native: {},
                                              });
                                              if (path === 'remote') {
                                                await this.extendObject(device.did + '.' + path + '.customCommand', {
                                                  type: 'state',
                                                  common: {
                                                    name: 'Send Custom command via Spec',
                                                    type: 'string',
                                                    role: 'json',
                                                    def: `{
                                                "aiid": 9,
                                                "in": [
                                                    {
                                                        "order": 4,
                                                        "region": [
                                                            1
                                                        ],
                                                        "type": "order"
                                                    }
                                                ],
                                                "siid": 5
                                            }`,
                                                    write: true,
                                                    read: true,
                                                  },
                                                  native: {},
                                                });
                                              }
                                              const states = {};
                                              if (property['value-list']) {
                                                for (const value of property['value-list']) {
                                                  states[value.value] = value.description;
                                                }
                                              }
                                              let unit;
                                              if (property.unit && property.unit !== 'none') {
                                                unit = property.unit;
                                              }
                                              await this.extendObject(device.did + '.' + path + '.' + typeName, {
                                                type: 'state',
                                                common: {
                                                  name: remote.name || '',
                                                  type: type,
                                                  role: role,
                                                  unit: unit,
                                                  min: property['value-range'] ? property['value-range'][0] : undefined,
                                                  max: property['value-range'] ? property['value-range'][1] : undefined,
                                                  states: property['value-list'] ? states : undefined,
                                                  write: write,
                                                  read: true,
                                                },
                                                native: {
                                                  siid: siid,
                                                  piid: piid,
                                                  did: device.did,
                                                  model: device.model,
                                                  name: service.description + ' ' + property.description,
                                                  type: property.type,
                                                  access: property.access,
                                                },
                                              });
                                    
                                              if (property.access.includes('notify')) {
                                                this.specStatusDict[device.did].push({
                                                  did: device.did,
                                                  siid: remote.siid,
                                                  code: 0,
                                                  piid: remote.piid,
                                                  updateTime: 0,
                                                });
                                              }
                                              this.specPropsToIdDict[device.did][remote.siid + '-' + remote.piid] =
                                                device.did + '.' + path + '.' + typeName;
                                            }
                                            //extract actions
                                            let aiid = 0;
                                            if (service.actions) {
                                              for (const action of service.actions) {
                                                if (action.iid) {
                                                  aiid = action.iid;
                                                } else {
                                                  aiid++;
                                                }
                                                const remote = {
                                                  siid: siid,
                                                  aiid: aiid,
                                                  did: device.did,
                                                  model: device.model,
                                                  name: service.description + ' ' + action.description + ' ' + service.iid + '-' + action.iid,
                                                  type: action.type,
                                                  access: action.access,
                                                };
                                                const typeName = action.type.split(':')[3];
                                    
                                                const path = 'remote';
                                                const write = true;
                                    
                                                let [type, role] = this.getRole(action.format, write, action['value-range']);
                                                this.log.debug(`Found actions for ${device.model} ${service.description} ${action.description}`);
                                    
                                                await this.extendObject(device.did + '.' + path, {
                                                  type: 'channel',
                                                  common: {
                                                    name: 'Remote Controls extracted from Spec definition',
                                                  },
                                                  native: {},
                                                });
                                                const states = {};
                                                if (action['value-list']) {
                                                  for (const value of action['value-list']) {
                                                    states[value.value] = value.description;
                                                  }
                                                }
                                                let def = '[]';
                                                if (action.in.length) {
                                                  remote.name = remote.name + ' in[';
                                    
                                                  for (const inParam of action.in) {
                                                    type = 'string';
                                                    role = 'text';
                                                    def = JSON.stringify(action.in);
                                                    const prop = service.properties.filter((obj) => {
                                                      return obj.iid === inParam;
                                                    });
                                                    if (prop.length > 0) {
                                                      remote.name = remote.name + prop[0].description + '';
                                                    }
                                                    if (action.in.indexOf(inParam) !== action.in.length - 1) {
                                                      remote.name = remote.name + ',';
                                                    }
                                                  }
                                    
                                                  remote.name = remote.name + ']';
                                                }
                                    
                                                if (action.out.length) {
                                                  remote.name = remote.name + ' out[';
                                    
                                                  for (const outParam of action.out) {
                                                    const prop = service.properties.filter((obj) => {
                                                      return obj.iid === outParam;
                                                    });
                                                    if (prop.length > 0) {
                                                      remote.name = remote.name + prop[0].description;
                                                    }
                                                    if (action.out.indexOf(outParam) !== action.out.length - 1) {
                                                      remote.name = remote.name + ',';
                                                    }
                                                  }
                                                  remote.name = remote.name + ']';
                                                }
                                                let unit;
                                                if (action.unit && action.unit !== 'none') {
                                                  unit = action.unit;
                                                }
                                                await this.extendObject(device.did + '.' + path + '.' + typeName, {
                                                  type: 'state',
                                                  common: {
                                                    name: remote.name || '',
                                                    type: type,
                                                    role: role,
                                                    unit: unit,
                                                    min: action['value-range'] ? action['value-range'][0] : undefined,
                                                    max: action['value-range'] ? action['value-range'][1] : undefined,
                                                    states: action['value-list'] ? states : undefined,
                                                    write: write,
                                                    read: true,
                                                    def: def != null ? def : undefined,
                                                  },
                                                  native: {
                                                    siid: siid,
                                                    aiid: aiid,
                                                    did: device.did,
                                                    model: device.model,
                                                    in: action.in,
                                                    out: action.out,
                                                    name: service.description + ' ' + action.description,
                                                    type: action.type,
                                                    access: action.access,
                                                  },
                                                });
                                                this.specActiosnToIdDict[device.did][service.iid + '-' + action.iid] =
                                                  device.did + '.' + path + '.' + typeName;
                                              }
                                            }
                                          } catch (error) {
                                            this.log.error('Error while extracting spec for ' + device.model);
                                            this.log.error(error);
                                            this.log.error(error.stack);
                                            this.log.info(JSON.stringify(service));
                                          }
                                        }
                                      }
                                      async updateDevicesViaSpec() {
                                        for (const device of this.deviceArray) {
                                          if (this.specStatusDict[device.did]) {
                                            //split array in chunks of 50
                                            const chunkSize = 50;
                                            for (let i = 0; i < this.specStatusDict[device.did].length; i += chunkSize) {
                                              const chunk = this.specStatusDict[device.did].slice(i, i + chunkSize);
                                    
                                              const requestId = Math.floor(Math.random() * 9000) + 1000;
                                              const data = {
                                                did: device.did,
                                                id: requestId,
                                                data: {
                                                  did: device.did,
                                                  id: requestId,
                                                  method: 'get_properties',
                                                  params: chunk,
                                                  from: 'XXXXXX',
                                                },
                                              };
                                              await this.requestClient({
                                                method: 'post',
                                                url: 'https://eu.iot.dreame.tech:13267/dreame-iot-com-10000/device/sendCommand',
                                                headers: {
                                                  'user-agent': 'Dart/3.2 (dart:io)',
                                                  'dreame-meta': 'cv=i_829',
                                                  'dreame-rlc': '1a9bb36e6b22617cf465363ba7c232fb131899d593e8d1a1-1',
                                                  'tenant-id': '000000',
                                                  host: 'eu.iot.dreame.tech:13267',
                                                  authorization: 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=',
                                                  'content-type': 'application/json',
                                                  'dreame-auth': 'bearer ' + this.session.access_token,
                                                },
                                                data: data,
                                              })
                                                .then(async (res) => {
                                                  if (res.data.code !== 0) {
                                                    if (res.data.code === -8) {
                                                      this.log.debug(
                                                        `Error getting spec update for ${device.name} (${device.did}) with ${JSON.stringify(data)}`,
                                                      );
                                    
                                                      this.log.debug(JSON.stringify(res.data));
                                                      return;
                                                    }
                                                    this.log.info(
                                                      `Error getting spec update for ${device.name} (${device.did}) with ${JSON.stringify(data)}`,
                                                    );
                                                    this.log.debug(JSON.stringify(res.data));
                                                    return;
                                                  }
                                                  this.log.debug(JSON.stringify(res.data));
                                                  for (const element of res.data.data.result) {
                                                    const path = this.specPropsToIdDict[device.did][element.siid + '-' + element.piid];
                                    				  this.log.info(' Update: ' + path);
                                                    if (path) {
                                                      this.log.debug(`Set ${path} to ${element.value}`);
                                                      if (element.value != null) {
                                                        this.setState(path, element.value, true);
                                                      }
                                                    }
                                                  }
                                                })
                                                .catch((error) => {
                                                  if (error.response) {
                                                    if (error.response.status === 401) {
                                                      error.response && this.log.debug(JSON.stringify(error.response.data));
                                                      this.log.info(' receive 401 error. Refresh Token in 60 seconds');
                                                      this.refreshTokenTimeout && clearTimeout(this.refreshTokenTimeout);
                                                      this.refreshTokenTimeout = setTimeout(() => {
                                                        this.refreshToken();
                                                      }, 1000 * 60);
                                    
                                                      return;
                                                    }
                                    
                                                    this.log.error(error);
                                                    error.stack && this.log.error(error.stack);
                                                    error.response && this.log.error(JSON.stringify(error.response.data));
                                                    return;
                                                  }
                                    
                                                  this.log.debug(error);
                                                  this.log.debug(JSON.stringify(error));
                                                });
                                            }
                                          }
                                        }
                                      }
                                      getRole(element, write, valueRange) {
                                        if (!element) {
                                          return ['string', 'json'];
                                        }
                                        if (element === 'bool' && !write) {
                                          return ['boolean', 'indicator'];
                                        }
                                        if (element === 'bool' && write) {
                                          return ['boolean', 'switch'];
                                        }
                                        if ((element.indexOf('int') !== -1 || valueRange) && !write) {
                                          return ['number', 'value'];
                                        }
                                        if ((element.indexOf('int') !== -1 || valueRange) && write) {
                                          return ['number', 'level'];
                                        }
                                    
                                        return ['string', 'text'];
                                      }
                                      async connectMqtt() {
                                        if (this.mqttClient) {
                                          this.mqttClient.end();
                                        }
                                        const url = this.deviceArray[0].bindDomain || 'app.mt.eu.iot.dreame.tech:19973';
                                        this.mqttClient = mqtt.connect('mqtts://' + url, {
                                          clientId: 'p_' + crypto.randomBytes(8).toString('hex'),
                                          username: this.session.uid,
                                          password: this.session.access_token,
                                          rejectUnauthorized: false,
                                          reconnectPeriod: 10000,
                                        });
                                    
                                        this.mqttClient.on('connect', () => {
                                          this.log.info('Connected to MQTT');
                                          for (const device of this.deviceArray) {
                                            this.mqttClient.subscribe(`/status/${device.did}/${this.session.uid}/${device.model}/eu/`);
                                          }
                                        });
                                        this.mqttClient.on('message', async (topic, message) => {
                                          // message is Buffer
                                          this.log.debug(topic.toString());
                                          this.log.debug(message.toString());
                                          /*
                                          {"id":92,"did":XXXXXX,"data":{"id":92,"method":"properties_changed","params":[{"did":"XXXXX","siid":2,"piid":6,"value":1},{"did":"XXXXX","siid":4,"piid":23,"value":5121}]}}
                                          */
                                          try {
                                            message = JSON.parse(message.toString());
                                    		  	//this.log.info(' Get Message:' + JSON.stringify(message));
                                          } catch (error) {
                                            this.log.error(error);
                                            return;
                                          }
                                          if (message.data && message.data.method === 'properties_changed') {
                                    
                                            for (const element of message.data.params) {
                                              if (!this.specPropsToIdDict[element.did]) {
                                                this.log.debug(`No spec found for ${element.did}`);
                                                continue;
                                              }
                                    
                                    		  if (JSON.stringify(element.siid) === '6' && JSON.stringify(element.piid) === '1') {
                                    		     this.log.info(' Map data:' + JSON.stringify(element.value));
                                    			  let encode = JSON.stringify(element.value);
                                    			  let mappath = `${element.did}` + '.map.';
                                    			  this.uncompress(encode, mappath);
                                    		  }
                                    		  //this.log.info(' Map data:' + JSON.stringify(element.siid) + ' => ' + JSON.stringify(element.piid));
                                    
                                              let path = this.specPropsToIdDict[element.did][element.siid + '-' + element.piid];
                                              if (!path) {
                                                this.log.debug(`No path found for ${element.did} ${element.siid}-${element.piid}`);
                                    
                                                path = `${element.did}.status.${element.siid}-${element.piid}`;
                                    
                                                await this.extendObject(path, {
                                                  type: 'state',
                                                  common: {
                                                    name: path,
                                                    type: 'mixed',
                                                    role: 'state',
                                                    write: false,
                                                    read: true,
                                                  },
                                                  native: {},
                                                });
                                                this.setState(path, JSON.stringify(element.value), true);
                                                path = `${element.did}.remote.${element.siid}-${element.piid}`;
                                    			this.log.info(' Update: SIID:' + element.siid + ' PIID: ' + element.piid);
                                                await this.extendObject(path, {
                                                  type: 'state',
                                                  common: {
                                                    name: path,
                                                    type: 'mixed',
                                                    role: 'state',
                                                    write: true,
                                                    read: true,
                                                  },
                                                  native: {},
                                                });
                                              }
                                              if (path) {
                                                this.log.debug(`Set ${path} to ${element.value}`);
                                                if (element.value != null) {
                                                  // this.setState(path, JSON.stringify(element.value), true);
                                                  this.json2iob.parse(path, JSON.stringify(element));
                                                }
                                              }
                                            }
                                          }
                                        });
                                        this.mqttClient.on('error', async (error) => {
                                          this.log.error(error);
                                          if (error.message && error.message.includes('Not authorized')) {
                                            this.log.error('Not authorized to connect to MQTT');
                                            this.setState('info.connection', false, true);
                                            await this.refreshToken();
                                          }
                                        });
                                        this.mqttClient.on('close', () => {
                                          this.log.info('MQTT Connection closed');
                                        });
                                      }
                                    
                                    async uncompress(In_Compressed, In_path){
                                        var input_Raw = In_Compressed.replace(/-/g, '+').replace(/_/g, '/');
                                    	var encodedData = Buffer.from(input_Raw, 'base64');
                                        var decode = zlib.inflateSync(encodedData);
                                    	/*csvar mapHeader = decode.toString().split("{");
                                        let GetHeader = mapHeader[0];
                                    	this.log.info(' decode Header 1: ' + GetHeader);
                                    	try {
                                    		var encodedDataH = Buffer.from(GetHeader, 'base64');
                                    		this.log.info(' Base64 decode Header : ' + encodedDataH);
                                    		var decodeHeader = zlib.inflateSync(encodedDataH);
                                    		} catch (e) {
                                            this.log.info(' Error decode Header 2: ' + e);
                                            } finally {
                                            this.log.info(' decode Header 2: ' + decodeHeader);
                                            }
                                        */
                                    
                                    	var jsondecode = decode.toString().match(/[{\[]{1}([,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]|".*?")+[}\]]{1}/gis);
                                    	const jsonread = JSON.parse(jsondecode);
                                    
                                    	for (var [key, value] of Object.entries(jsonread)) {
                                           this.log.info(' decode Map JSON:' + `${key}: ${value}`);
                                    
                                    	    if(Object.prototype.toString.call(value) !== '[object Object]'){
                                    	      if (value != null) {
                                    			let pathMap = In_path + key;
                                    			await this.extendObject(pathMap, {
                                                  type: 'state',
                                                  common: {
                                                    name: pathMap,
                                                    type: 'mixed',
                                                    role: 'state',
                                                    write: false,
                                                    read: true,
                                                  },
                                                  native: {},
                                                });
                                    
                                    			this.setState(pathMap, value, true);
                                    		  }
                                    	    }
                                    
                                    	   if (typeof value === 'object' && value !== null){
                                    		   if(Object.prototype.toString.call(value) === '[object Object]'){
                                    		       for (var [Subkey, Subvalue] of Object.entries(value)) {
                                                      this.log.info(' decode subkey ' + key + ' ==> ' + `${Subkey}: ${Subvalue}`);
                                    				  if (value != null) {
                                    					  let pathMap = In_path + key + '.' + Subkey;
                                    					  await this.extendObject(pathMap, {
                                    						type: 'state',
                                                            common: {
                                                            name: pathMap,
                                    						type: 'mixed',
                                                            role: 'state',
                                                            write: false,
                                                            read: true,
                                                            },
                                                            native: {},
                                                         });
                                    
                                    			         this.setState(pathMap, Subvalue, true);
                                    		          }
                                    		       }
                                                }
                                    	   }
                                    
                                    	}
                                    }
                                    
                                      async refreshToken() {
                                        await this.requestClient({
                                          method: 'post',
                                          url: 'https://eu.iot.dreame.tech:13267/dreame-auth/oauth/token',
                                          headers: {
                                            'user-agent': 'Dart/3.2 (dart:io)',
                                            'dreame-meta': 'cv=i_829',
                                            'dreame-rlc': '1a9bb36e6b22617cf465363ba7c232fb131899d593e8d1a1-1',
                                            'tenant-id': '000000',
                                            host: 'eu.iot.dreame.tech:13267',
                                            authorization: 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=',
                                            'content-type': 'application/x-www-form-urlencoded',
                                          },
                                          data: {
                                            grant_type: 'refresh_token',
                                            refresh_token: this.session.refresh_token,
                                          },
                                        })
                                          .then((response) => {
                                            this.log.debug('Login response: ' + JSON.stringify(response.data));
                                            this.session = response.data;
                                            this.setState('info.connection', true, true);
                                            //reconnect mqtt
                                            this.connectMqtt();
                                          })
                                          .catch((error) => {
                                            this.log.error('Refresh Token  error: ' + error);
                                            error.response && this.log.error('Refresh Token error response: ' + JSON.stringify(error.response.data));
                                            this.setState('info.connection', false, true);
                                          });
                                      }
                                    
                                    
                                      /**
                                       * Is called when adapter shuts down - callback has to be called under any circumstances!
                                       * @param {() => void} callback
                                       */
                                      onUnload(callback) {
                                        try {
                                          callback();
                                        } catch (e) {
                                          callback();
                                        }
                                      }
                                    
                                      /**
                                       * Is called if a subscribed state changes
                                       * @param {string} id
                                       * @param {ioBroker.State | null | undefined} state
                                       */
                                      async onStateChange(id, state) {
                                        if (state) {
                                          if (!state.ack) {
                                            const deviceId = id.split('.')[2];
                                            const folder = id.split('.')[3];
                                            let command = id.split('.')[4];
                                            this.log.debug(`Receive command ${command} for ${deviceId} in folder ${folder} with value ${state.val} `);
                                            // let type;
                                            if (command) {
                                              // type = command.split("-")[1];
                                              command = command.split('-')[0];
                                            }
                                            if (id.split('.')[4] === 'Refresh') {
                                              this.updateDevicesViaSpec();
                                              return;
                                            }
                                            //{"id":0,"method":"app_start","params":[{"clean_mop":0}]}
                                    
                                            const stateObject = await this.getObjectAsync(id);
                                    
                                            const requestId = Math.floor(Math.random() * 9000) + 1000;
                                    
                                            const data = {
                                              did: deviceId,
                                              id: requestId,
                                              data: {
                                                did: deviceId,
                                                id: requestId,
                                                method: 'action',
                                                params: {},
                                                from: 'XXXXXX',
                                              },
                                            };
                                            if (stateObject && stateObject.native.piid) {
                                              data.data.params = {
                                                did: deviceId,
                                                siid: stateObject.native.siid,
                                                piid: stateObject.native.piid,
                                                value: state.val,
                                              };
                                            }
                                            if (stateObject && stateObject.native.aiid) {
                                              data.data.params = { did: deviceId, siid: stateObject.native.siid, aiid: stateObject.native.aiid };
                                              if (typeof state.val !== 'boolean') {
                                                try {
                                                  data.data.params['in'] = JSON.parse(state.val);
                                                } catch (error) {
                                                  this.log.error(error);
                                                  return;
                                                }
                                              }
                                    
                                              const device = this.deviceArray.filter((obj) => {
                                                return obj.did === deviceId;
                                              })[0];
                                              if (device && device.model.includes('mower')) {
                                                data.data.params.siid += 3;
                                              }
                                    
                                              // data.params.in = [];
                                            }
                                            if (command === 'customCommand') {
                                              try {
                                                data.data.params = JSON.parse(state.val);
                                                data.data.params.did = deviceId;
                                              } catch (error) {
                                                this.log.error(error);
                                                return;
                                              }
                                            }
                                            this.log.info(`Send: ${JSON.stringify(data)} to ${deviceId}`);
                                    
                                            await this.requestClient({
                                              method: 'post',
                                              url: 'https://eu.iot.dreame.tech:13267/dreame-iot-com-10000/device/sendCommand',
                                              headers: {
                                                'user-agent': 'Dart/3.2 (dart:io)',
                                                'dreame-meta': 'cv=i_829',
                                                'dreame-rlc': '1a9bb36e6b22617cf465363ba7c232fb131899d593e8d1a1-1',
                                                'tenant-id': '000000',
                                                host: 'eu.iot.dreame.tech:13267',
                                                authorization: 'Basic ZHJlYW1lX2FwcHYxOkFQXmR2QHpAU1FZVnhOODg=',
                                                'content-type': 'application/json',
                                                'dreame-auth': 'bearer ' + this.session.access_token,
                                              },
                                              data: data,
                                            })
                                              .then(async (res) => {
                                                if (res.data.code !== 0) {
                                                  this.log.error('Error setting device state');
                                                  this.log.error(JSON.stringify(res.data));
                                                  return;
                                                }
                                                if (res.data.result && res.data.result.length > 0) {
                                                  res.data = res.data.result[0];
                                                }
                                                this.log.info(JSON.stringify(res.data));
                                                if (!res.data.result) {
                                                  return;
                                                }
                                                const result = res.data.result;
                                                if (result.out) {
                                                  const path = this.specActiosnToIdDict[result.did][result.siid + '-' + result.aiid];
                                                  this.log.debug(path);
                                                  const stateObject = await this.getObjectAsync(path);
                                                  if (stateObject && stateObject.native.out) {
                                                    const out = stateObject.native.out;
                                                    for (const outItem of out) {
                                                      const index = out.indexOf(outItem);
                                                      const outPath = this.specPropsToIdDict[result.did][result.siid + '-' + outItem];
                                                      // await this.setState(outPath, result.out[index], true);
                                                      this.json2iob.parse(outPath, result.out[index]);
                                                      this.log.info('Set ' + outPath + ' to ' + result.out[index]);
                                                    }
                                                  } else {
                                                    this.log.info(JSON.stringify(result.out));
                                                  }
                                                }
                                              })
                                              .catch(async (error) => {
                                                this.log.error(error);
                                                error.response && this.log.error(JSON.stringify(error.response.data));
                                              });
                                            this.refreshTimeout = setTimeout(async () => {
                                              this.log.info('Update devices');
                                              await this.updateDevicesViaSpec();
                                            }, 10 * 1000);
                                          }
                                        }
                                      }
                                    }
                                    
                                    if (require.main !== module) {
                                      // Export the constructor in compact mode
                                      /**
                                       * @param {Partial<utils.AdapterOptions>} [options={}]
                                       */
                                      module.exports = (options) => new Dreame(options);
                                    } else {
                                      // otherwise start the instance directly
                                      new Dreame();
                                    }
                                    
                                    

                                    @SmartiSmart

                                    Mit dem folgenden Code kann ich dem Saugroboter über Alexa Befehle geben, um bestimmte Räume zu säubern. Beachten Sie, dass die Räume und das „Dreame“-Objekt zuvor angepasst werden mussten.

                                    on({ id: 'alexa2.0.History.summary' /* summary */, change: 'any' }, async (obj) => {
                                      let value = obj.state.val;
                                      let oldValue = obj.oldState.val;
                                      AlexaHistorysummary = getState('alexa2.0.History.summary').val;
                                      SearchClean = AlexaHistorysummary.lastIndexOf('sauber machen') + 1;
                                      SerachMoreClean = AlexaHistorysummary.lastIndexOf('sehr dreckig') + 1;
                                      ArrayRoom = ['schlafzimmer', 'toilette', 'abstellraum', 'kinderzimmer', 'esszimmer', 'arbeitszimmer','eingang', 'wohnzimmer', 'flur', 'küche', '];
                                      SerachRoomName = '';
                                      Room1 = false;
                                      Room2 = false;
                                      Room3 = false;
                                      Room4 = false;
                                      Room5 = false;
                                      Room6 = false;
                                      Room7 = false;
                                      Room8 = false;
                                      Room9 = false;
                                      Room10 = false;
                                      ResetRoomNumber = 0;
                                      FoundRoomState = false;
                                      StringToSend = '';
                                      CollectName = '';
                                      for (var i_index in ArrayRoom) {
                                        i = ArrayRoom[i_index];
                                        ResetRoomNumber = (typeof ResetRoomNumber === 'number' ? ResetRoomNumber : 0) + 1;
                                        SerachRoom = AlexaHistorysummary.lastIndexOf(i) + 1;
                                        if (parseFloat(SerachRoom) > 0) {
                                          if (parseFloat(SerachMoreClean) > 0) {
                                            SearchClean = 1;
                                            CleanSettings = ',3,3,3,1],';
                                          } else {
                                            CleanSettings = ',1,3,2,1],';
                                          }
                                          FoundRoomState = true;
                                          CollectName = [i,' und ',CollectName].join('');
                                          StringToSend = ['[',ResetRoomNumber,CleanSettings,StringToSend].join('');
                                        }
                                      }
                                      if (parseFloat(SearchClean) > 0) {
                                        CleanRoomCode = String(StringToSend) + String(StringToSend.slice(0, StripString - 1));
                                        CleanRoomCode = CleanRoomCode.slice(0, (CleanRoomCode.lastIndexOf(' und ') + 1) - 1);
                                        StringToSend = [' [{"piid": 1,"value": 18},{"piid": 10,"value": "{\\"selects\\": [',CleanRoomCode,']}"}]'].join('');
                                        console.log(('Now: ' + String(StringToSend)));
                                        CollectName = CollectName.slice(0, (CollectName.lastIndexOf(' und ') + 1) - 1);
                                        LastAlexa = getState('alexa2.0.History.serialNumber').val;
                                        if (FoundRoomState == true) {
                                          setStateDelayed((['alexa2.0.Echo-Devices.',LastAlexa,'.Commands.deviceStop'].join('')), true, false, parseInt(((0) || '').toString(), 10), true);
                                          setStateDelayed((['alexa2.0.Echo-Devices.',LastAlexa,'.Commands.deviceStop'].join('')), false, false, parseInt(((100) || '').toString(), 10), true);
                                          setStateDelayed((['alexa2.0.Echo-Devices.',LastAlexa,'.Commands.speak'].join('')), (['OK! ',CollectName,' sauber machen.'].join('')), false, parseInt(((0) || '').toString(), 10), true);
                                          setState('dreame.0.*********.remote.stop-clean' /* vacuum-extend stop-clean 4-2 */, '[ 1 ]');
                                          setStateDelayed('dreame.0.********.remote.start-clean' /* vacuum-extend start-clean 4-1 in[clean-extend-data,work-mode] */, StringToSend, 300, false);
                                        }
                                      }
                                    });
                                    
                                    wawyoW Offline
                                    wawyoW Offline
                                    wawyo
                                    Developer
                                    wrote on last edited by
                                    #17

                                    Bitte entschuldigen Sie, falls ich nicht sofort antworte – das echte Leben hat manchmal Vorrang 😉

                                    Michael LadstätterM 1 Reply Last reply
                                    0
                                    • wawyoW wawyo

                                      Bitte entschuldigen Sie, falls ich nicht sofort antworte – das echte Leben hat manchmal Vorrang 😉

                                      Michael LadstätterM Offline
                                      Michael LadstätterM Offline
                                      Michael Ladstätter
                                      wrote on last edited by
                                      #18

                                      @wawyo Hallo
                                      Ich teste auch gerade den Dreame Adapter und er sendet auch Daten aber ich würde gerne deine Version mit den Map daten und Räumen testen wenn das möglich ist

                                      LG

                                      wawyoW 1 Reply Last reply
                                      0
                                      • Michael LadstätterM Michael Ladstätter

                                        @wawyo Hallo
                                        Ich teste auch gerade den Dreame Adapter und er sendet auch Daten aber ich würde gerne deine Version mit den Map daten und Räumen testen wenn das möglich ist

                                        LG

                                        wawyoW Offline
                                        wawyoW Offline
                                        wawyo
                                        Developer
                                        wrote on last edited by
                                        #19

                                        @michael-ladstätter
                                        Hier ist die vollständige Anleitung, inklusive der Befehle zur Aktualisierung und Neustart der Instanz:

                                        Die modifizierte Version der main.js kann derzeit nur manuell aktualisiert werden und ist nicht über GitHub verfügbar. Um die Änderungen vorzunehmen, folgen Sie bitte diesen Schritten:

                                        1. Kopieren Sie den bereitgestellten Code und speichern Sie ihn unter dem Namen main.js.

                                        2. Stellen Sie über SSH eine Verbindung zu Ihrem Server her und führen Sie den folgenden Befehl aus, um die Berechtigungen für die Datei zu ändern:

                                          sudo chmod 644 /opt/iobroker/node_modules/iobroker.dreame/main.js
                                          
                                        3. Öffnen Sie FileZilla und navigieren Sie zu folgendem Verzeichnis: /opt/iobroker/node_modules/iobroker.dreame.

                                        4. Sichern Sie die originale main.js-Datei an einem sicheren Ort.

                                        5. Ersetzen Sie die originale main.js-Datei durch die heruntergeladene und modifizierte Version, die Sie in Schritt 1 erstellt haben.

                                        6. Führen Sie den folgenden Befehl aus, um die Änderungen hochzuladen:

                                          sudo iobroker upload dreame
                                          
                                        7. Starten Sie die dreame-Instanz neu, um die Änderungen zu übernehmen:

                                          sudo iobroker restart dreame
                                          

                                        Durch diese Schritte wird die modifizierte main.js erfolgreich implementiert und die Änderungen werden aktiv 😉

                                        Michael LadstätterM 1 Reply Last reply
                                        0
                                        • wawyoW wawyo

                                          @michael-ladstätter
                                          Hier ist die vollständige Anleitung, inklusive der Befehle zur Aktualisierung und Neustart der Instanz:

                                          Die modifizierte Version der main.js kann derzeit nur manuell aktualisiert werden und ist nicht über GitHub verfügbar. Um die Änderungen vorzunehmen, folgen Sie bitte diesen Schritten:

                                          1. Kopieren Sie den bereitgestellten Code und speichern Sie ihn unter dem Namen main.js.

                                          2. Stellen Sie über SSH eine Verbindung zu Ihrem Server her und führen Sie den folgenden Befehl aus, um die Berechtigungen für die Datei zu ändern:

                                            sudo chmod 644 /opt/iobroker/node_modules/iobroker.dreame/main.js
                                            
                                          3. Öffnen Sie FileZilla und navigieren Sie zu folgendem Verzeichnis: /opt/iobroker/node_modules/iobroker.dreame.

                                          4. Sichern Sie die originale main.js-Datei an einem sicheren Ort.

                                          5. Ersetzen Sie die originale main.js-Datei durch die heruntergeladene und modifizierte Version, die Sie in Schritt 1 erstellt haben.

                                          6. Führen Sie den folgenden Befehl aus, um die Änderungen hochzuladen:

                                            sudo iobroker upload dreame
                                            
                                          7. Starten Sie die dreame-Instanz neu, um die Änderungen zu übernehmen:

                                            sudo iobroker restart dreame
                                            

                                          Durch diese Schritte wird die modifizierte main.js erfolgreich implementiert und die Änderungen werden aktiv 😉

                                          Michael LadstätterM Offline
                                          Michael LadstätterM Offline
                                          Michael Ladstätter
                                          wrote on last edited by
                                          #20

                                          @wawyo Hallo
                                          Danke für die tolle Anleitung
                                          Habe ich umgesetzt aber leider tauchen bei mir weder der Ordner maps noch cleanset auf
                                          einige Sachen haben sich geändert und es wurden aus vielen datenpunkten die vorher aus zahlen bestand ist jetzt ein text in der bezeichnung

                                          wawyoW 1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          591

                                          Online

                                          32.4k

                                          Users

                                          81.4k

                                          Topics

                                          1.3m

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

                                          • Don't have an account? Register

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