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

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

Community Forum

  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. JavaScript
  5. Node.js Script in iobroker integrieren

NEWS

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

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

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

Node.js Script in iobroker integrieren

Geplant Angeheftet Gesperrt Verschoben JavaScript
40 Beiträge 10 Kommentatoren 6.9k Aufrufe 10 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • pseudorealP pseudoreal

    @unclesam @rettroo
    Danke - das stimmt. Ich habe das via blockly hinbekommen. Habe aber noch folgendes Problem, wie kann ich aus MQTT einen speziellen String aus dem Wert in den Objekten rauslesen und in Blockly verarbeiten?

    UncleSamU Offline
    UncleSamU Offline
    UncleSam
    Developer
    schrieb am zuletzt editiert von
    #23

    @pseudoreal was meinst du damit? Kannst du mal in einem Screenshot zeigen, was du wo hast und was du daraus haben willst?

    Bitte bei Problemen mit meinen Adaptern, Issue auf GitHub erfassen: Loxone | I2C | Luxtronik2
    ♡-lichen Dank an meine Sponsoren

    pseudorealP 1 Antwort Letzte Antwort
    0
    • UncleSamU UncleSam

      @pseudoreal was meinst du damit? Kannst du mal in einem Screenshot zeigen, was du wo hast und was du daraus haben willst?

      pseudorealP Offline
      pseudorealP Offline
      pseudoreal
      schrieb am zuletzt editiert von
      #24

      @unclesam
      im Objekt von MQTT habe ich eines der VTO, welches FingerPrintCheck heißt. Das dazugehörige Event hat unter Wert, den folgenden Eintrag:
      {"Data":{"FingerPrintID":6,"LocaleTime":"2021-05-03 08:17:42","UTC":1620026262},"deviceType":"VTO4202F","serialNumber":"xxxxxxxxxx"}

      Ich bräuchte nun den Datenpunkt FingerPrintID im Blockly, da ich gerne nur bei nicht erfolgreichen Fingerabdruckscans eine Aktion auslösen möchte. In einem anderen Thread wurde mir diese Seite empfohlen - https://www.machs-smart.de/json-blockly-einlesen-parsen-verarbeiten/
      Ich schau mir das mal an.

      Danke

      UncleSamU oldi-2000O 2 Antworten Letzte Antwort
      0
      • pseudorealP pseudoreal

        @unclesam
        im Objekt von MQTT habe ich eines der VTO, welches FingerPrintCheck heißt. Das dazugehörige Event hat unter Wert, den folgenden Eintrag:
        {"Data":{"FingerPrintID":6,"LocaleTime":"2021-05-03 08:17:42","UTC":1620026262},"deviceType":"VTO4202F","serialNumber":"xxxxxxxxxx"}

        Ich bräuchte nun den Datenpunkt FingerPrintID im Blockly, da ich gerne nur bei nicht erfolgreichen Fingerabdruckscans eine Aktion auslösen möchte. In einem anderen Thread wurde mir diese Seite empfohlen - https://www.machs-smart.de/json-blockly-einlesen-parsen-verarbeiten/
        Ich schau mir das mal an.

        Danke

        UncleSamU Offline
        UncleSamU Offline
        UncleSam
        Developer
        schrieb am zuletzt editiert von
        #25

        @pseudoreal Genau, ich würde einfach in einem Blockly oder JavaScript das JSON parsen und dann in einen Datenpunkt abfüllen. Das sollte recht einfach sein. Mit Blockly kann ich aber leider nicht weiterhelfen, das verwende ich nie.

        Bitte bei Problemen mit meinen Adaptern, Issue auf GitHub erfassen: Loxone | I2C | Luxtronik2
        ♡-lichen Dank an meine Sponsoren

        1 Antwort Letzte Antwort
        0
        • pseudorealP pseudoreal

          @unclesam
          im Objekt von MQTT habe ich eines der VTO, welches FingerPrintCheck heißt. Das dazugehörige Event hat unter Wert, den folgenden Eintrag:
          {"Data":{"FingerPrintID":6,"LocaleTime":"2021-05-03 08:17:42","UTC":1620026262},"deviceType":"VTO4202F","serialNumber":"xxxxxxxxxx"}

          Ich bräuchte nun den Datenpunkt FingerPrintID im Blockly, da ich gerne nur bei nicht erfolgreichen Fingerabdruckscans eine Aktion auslösen möchte. In einem anderen Thread wurde mir diese Seite empfohlen - https://www.machs-smart.de/json-blockly-einlesen-parsen-verarbeiten/
          Ich schau mir das mal an.

          Danke

          oldi-2000O Offline
          oldi-2000O Offline
          oldi-2000
          schrieb am zuletzt editiert von
          #26

          @pseudoreal Hallo ich hänge gerade genau an dem Thema Json parsen und hab leider null Ahnung davon ich habe ein Vto 4202 habe das Skript und den mqtt Adapter am laufen und bräuchte jetzt aber normale Datenpunkte z. B. für die einen Gong aber auch wenn möglich für den Türoffner usw. Hast du mir vielleicht ein Skript das funktioniert?

          F 1 Antwort Letzte Antwort
          0
          • oldi-2000O oldi-2000

            @pseudoreal Hallo ich hänge gerade genau an dem Thema Json parsen und hab leider null Ahnung davon ich habe ein Vto 4202 habe das Skript und den mqtt Adapter am laufen und bräuchte jetzt aber normale Datenpunkte z. B. für die einen Gong aber auch wenn möglich für den Türoffner usw. Hast du mir vielleicht ein Skript das funktioniert?

            F Offline
            F Offline
            fastfoot
            schrieb am zuletzt editiert von
            #27

            @oldi-2000 poste mal so ein json

            iobroker läuft unter Docker auf QNAP TS-451+
            SkriptRecovery: https://forum.iobroker.net/post/930558

            oldi-2000O 1 Antwort Letzte Antwort
            0
            • F fastfoot

              @oldi-2000 poste mal so ein json

              oldi-2000O Offline
              oldi-2000O Offline
              oldi-2000
              schrieb am zuletzt editiert von
              #28

              @fastfoot

              {
                "Data": {
                  "LocaleTime": "2021-11-20 20:12:39",
                  "State": 9,
                  "UTC": 1637439159
                },
                "deviceType": "VTO4202F-P-S2",
                "serialNumber": "7H05F88PAJ18009"
              }
              

              Das ist was Mqtt als Datenpunkt raushaut, ich benötige aber möglichst einen Datenpunkt der einfach true bei State = 1 bzw. false bei allen anderen States ist.
              Ich hab aber leider null Ahnung vom skripten. :anguished:

              F 1 Antwort Letzte Antwort
              0
              • oldi-2000O oldi-2000

                @fastfoot

                {
                  "Data": {
                    "LocaleTime": "2021-11-20 20:12:39",
                    "State": 9,
                    "UTC": 1637439159
                  },
                  "deviceType": "VTO4202F-P-S2",
                  "serialNumber": "7H05F88PAJ18009"
                }
                

                Das ist was Mqtt als Datenpunkt raushaut, ich benötige aber möglichst einen Datenpunkt der einfach true bei State = 1 bzw. false bei allen anderen States ist.
                Ich hab aber leider null Ahnung vom skripten. :anguished:

                F Offline
                F Offline
                fastfoot
                schrieb am zuletzt editiert von
                #29

                @oldi-2000 so könnte es aussehen
                vto.PNG

                iobroker läuft unter Docker auf QNAP TS-451+
                SkriptRecovery: https://forum.iobroker.net/post/930558

                1 Antwort Letzte Antwort
                0
                • H Offline
                  H Offline
                  h07d0q
                  schrieb am zuletzt editiert von
                  #30

                  @rettroo @fastfoot
                  Würde jemand von euch eine fertige, funktionierende Lösung hier veröffentlichen?
                  Dann muss nicht jeder von neu beginnen :)
                  Ich habe vor das ganze ohne den Schritt über MQTT direkt in Datenpunkte zu schreiben, sonst kann man es sich ja sparen das über iobroker laufen zu lassen.
                  Am schönsten wäre natürlich ein Adapter, aber davon bin ich weit entfernt :D

                  I 2 Antworten Letzte Antwort
                  0
                  • H h07d0q

                    @rettroo @fastfoot
                    Würde jemand von euch eine fertige, funktionierende Lösung hier veröffentlichen?
                    Dann muss nicht jeder von neu beginnen :)
                    Ich habe vor das ganze ohne den Schritt über MQTT direkt in Datenpunkte zu schreiben, sonst kann man es sich ja sparen das über iobroker laufen zu lassen.
                    Am schönsten wäre natürlich ein Adapter, aber davon bin ich weit entfernt :D

                    I Offline
                    I Offline
                    init5
                    schrieb am zuletzt editiert von
                    #31

                    @h07d0q das wäre ideal, wenn dafür jemand einen Adapter erstellen kann. Scheinbar ist die Nachfrage bei dem System nicht sonderlich hoch. Ich versuche gerade das Skript so anzupassen, dass es bei mir läuft. Sollte ich es hinbekommen, werde ich es dir zur Verfügung stellen.

                    1 Antwort Letzte Antwort
                    0
                    • H h07d0q

                      @rettroo @fastfoot
                      Würde jemand von euch eine fertige, funktionierende Lösung hier veröffentlichen?
                      Dann muss nicht jeder von neu beginnen :)
                      Ich habe vor das ganze ohne den Schritt über MQTT direkt in Datenpunkte zu schreiben, sonst kann man es sich ja sparen das über iobroker laufen zu lassen.
                      Am schönsten wäre natürlich ein Adapter, aber davon bin ich weit entfernt :D

                      I Offline
                      I Offline
                      init5
                      schrieb am zuletzt editiert von init5
                      #32

                      @h07d0q So, ich habe nun ein funktionierendes Skript am Laufen. Geklaut habe ich es von hier. Wäre schön wenn jemand einen Adapter daraus bauen kann.

                      const DigestFetch = require('digest-fetch');
                      const net = require('net');
                      const fs = require('fs');
                      const path = require('path');
                      const md5 = require('md5');
                      const mqtt = require('mqtt');
                      
                      class DahuaVTO {
                        deviceType;
                        serialNumber;
                      
                        requestId = 0;
                      
                        sessionId = 0;
                      
                        keepAliveInterval = 60;
                      
                        _keepAliveTimer;
                      
                        doorbellSocket;
                      
                        mqttClient;
                      
                        constructor() {
                          this.dahua_host = "192.168.xxx.xxx";
                          this.dahua_username = "VTOusername";
                          this.dahua_password = "VTOpassword";
                          this.mqtt_broker_host = "192.168.xxx.xxx";
                          this.mqtt_broker_port = "1883";
                          this.mqtt_broker_username = "mqttUsername";
                          this.mqtt_broker_password = "mqttPassword";
                          this.mqtt_broker_topic_prefix = "DahuaVTO";
                          this.digestClient =  new DigestFetch(
                            this.dahua_username,
                            this.dahua_password
                          );
                      
                          this.getDeviceDetails().then(({ deviceType, serialNumber }) => {
                            this.deviceType = deviceType;
                            this.serialNumber = serialNumber;
                            this.start();
                          });
                        }
                      
                        start() {
                          this.setupDoorbellSocket();
                          this.setupMQTT();
                          this.initLogin();
                        }
                      
                        async getDeviceDetails() {
                          return this.digestClient
                            .fetch(
                              `http://${this.dahua_host}/cgi-bin/magicBox.cgi?action=getSystemInfo`
                            )
                            .then((r) => r.text())
                            .then((text) => {
                              const deviceDetails = text
                                .trim()
                                .split('\n')
                                .reduce((obj, str) => {
                                  const [key, val] = str.split('=');
                                  obj[key] = val.trim();
                                  return obj;
                                }, {});
                              return deviceDetails;
                            });
                        }
                      
                        saveSnapshot(p = '/tmp/') {
                          let now = new Date();
                          let dateStr = `${now.getFullYear()}-${
                            now.getMonth() + 1
                          }-${now.getDate()}-${now.getHours()}-${now.getMinutes()}-${now.getSeconds()}`;
                          let destination = path.join(p, `DoorBell_${dateStr}.jpg`);
                          this.digestClient
                            .fetch(`http://${this.dahua_host}/cgi-bin/snapshot.cgi`)
                            .then((r) => {
                              return r.buffer();
                            })
                            .then((buf) => {
                              fs.writeFile(destination, buf, 'binary', function (err) {
                                if (err) {
                                  log('Error saving snapshot to disk', err);
                                } else {
                                  log('Snapshot saved');
                                }
                              });
                            });
                        }
                      
                        setupDoorbellSocket() {
                          let socket = new net.Socket({ readable: true, writable: true });
                          socket.on('end', function () {
                            log('Doorbell socket ended');
                          });
                          socket.on('close', function () {
                           log('Doorbell socket closed');
                            clearInterval(this._keepAliveTimer);
                          });
                          socket.on('data', this.receive.bind(this));
                          socket.on('error', function (e) {
                            log('Doorbell socket error', e);
                            this.doorbellSocket.destroy();
                            this.mqttClient.end(true);
                            clearInterval(this._keepAliveTimer);
                            this.start();    });
                          this.doorbellSocket = socket.connect({ port: 5000, host: this.dahua_host });
                        }
                      
                        setupMQTT() {
                          this.mqttClient = mqtt.connect({
                            host: this.mqtt_broker_host,
                            port: this.mqtt_broker_port,
                            username: this.mqtt_broker_username,
                            password: this.mqtt_broker_password,
                            will: {
                              topic: `${this.mqtt_broker_topic_prefix}/lwt`,
                              payload: 'connected',
                              qos: 1,
                            },
                          });
                          this.mqttClient.on('disconnect', function (packet) {
                            log('MQTTDisconnect', packet);
                          });
                          this.mqttClient.on('message', function (topic, message, packet) {
                            log('MQTTMessage', { topic, message, packet });
                          });
                        }
                      
                        publishToMQTT(name, payload) {
                          let message = JSON.stringify(payload);
                          this.mqttClient.publish(
                            `${this.mqtt_broker_topic_prefix}/${name}/Event`,
                            message
                          );
                        }
                      
                        send(data) {
                          let json = JSON.stringify(data);
                          let buf = Buffer.alloc(32);
                          let offset = buf.writeUInt32BE(0x20000000);
                          offset = buf.writeUInt32BE(0x44484950, offset);
                          offset = buf.writeDoubleBE(0, offset);
                          offset = buf.writeUInt32LE(json.length, offset);
                          offset = buf.writeUInt32LE(0, offset);
                          offset = buf.writeUInt32LE(json.length, offset);
                          offset = buf.writeUInt32LE(0, offset);
                          buf = Buffer.concat([buf, Buffer.from(json)]);
                          this.requestId += 1;
                          this.doorbellSocket.write(buf);
                        }
                      
                        receive(buf) {
                          let str = buf.slice(32).toString();
                          let obj = JSON.parse(str);
                          if (this.requestId === 1) {
                            this.handleFirstLoginPayload(obj);
                          } else if (this.requestId === 2) {
                            this.handleSecondLoginPayload(obj);
                          } else if (obj.method === 'client.notifyEventStream') {
                            this.handleEvents(obj.params.eventList);
                          } else {
                            this.handleGenericPayload(obj);
                          }
                        }
                      
                        initLogin() {
                          this.send({
                            id: 10000,
                            magic: '0x1234',
                            method: 'global.login',
                            params: {
                              clientType: '',
                              ipAddr: '(null)',
                              loginType: 'Direct',
                              password: '',
                              userName: this.dahua_username,
                            },
                            session: 0,
                          });
                        }
                      
                      
                        handleFirstLoginPayload({ session, params: { random, realm } }) {
                          this.sessionId = session;
                          let randomHash = this.genMD5Hash(random, realm);
                          this.send({
                            id: 10000, 
                            magic: '0x1234',
                            method: 'global.login',
                            session: this.sessionId,
                            params: {
                              userName: this.dahua_username,
                              password: randomHash,
                              clientType: '',
                              ipAddr: '(null)',
                              loginType: 'Direct',
                              authorityType: 'Default',
                            },
                          });
                        }
                      
                        handleSecondLoginPayload(obj) {
                          if (obj.result) {
                            log('Logging to Dahua Doorbell successful');
                            this.keepAliveInterval = obj.params.keepAliveInterval - 5;
                            this.attachEventManager();
                            this.keepConnectionAlive();
                          } else {
                            log('Failed to login. Response was: ', obj);
                          }
                        }
                      
                        handleGenericPayload(obj) {
                          if (
                            obj.result === true &&
                            obj.params &&
                            Object.hasOwnProperty.call(obj.params, 'timeout')
                          ) {
                            log('Publish KeepAlive event');
                            this.publishToMQTT('keepAlive', {
                              deviceType: this.deviceType,
                              serialNumber: this.serialNumber,
                            });
                          } else {
                            log(
                              'handleGenericPayload# Cannot handle received payload',
                              obj
                            );
                          }
                        }
                      
                        genMD5Hash(random, realm) {
                          const base_credentials = `${this.dahua_username}:${realm}:${this.dahua_password}`;
                          const pwddb_hash = md5(base_credentials).toUpperCase();
                          const base_pass = `${this.dahua_username}:${random}:${pwddb_hash}`;
                          return md5(base_pass).toUpperCase();
                        }
                      
                        attachEventManager() {
                          this.send({
                            id: this.requestId,
                            magic: '0x1234',
                            method: 'eventManager.attach',
                            params: {
                              codes: ['All'],
                            },
                            session: this.sessionId,
                          });
                        }
                      
                        handleEvents(events) {
                          events.forEach((event) => {
                            log(`Publish event ${event.Code} to MQTT`);
                            this.publishToMQTT(event.Code, {
                              Action: event.eventAction,
                              Data: event.Data,
                              deviceType: this.deviceType,
                              serialNumber: this.serialNumber,
                            });
                          });
                        }
                      
                      
                        keepConnectionAlive(delay) {
                          this._keepAliveTimer = setInterval(() => {
                            let keepAlivePayload = {
                              method: 'global.keepAlive',
                              magic: '0x1234',
                              params: {
                                timeout: delay,
                                active: true,
                              },
                              id: this.requestId,
                              session: this.sessionId,
                            };
                      
                            this.send(keepAlivePayload);
                          }, this.keepAliveInterval * 1000);
                        }
                      
                        openDoor() {
                          return this.digestClient
                            .fetch(
                              `http://${this.dahua_host}/cgi-bin/accessControl.cgi?action=openDoor&channel=1&UserID=101&Type=Remote`
                            )
                            .then((r) => {
                              if (r.ok) {
                                log('Door relay triggered');
                              } else {
                                log('Error triggering the door relay', e);
                              }
                            })
                            .catch(e => log('Connection error triggering the door relay'));
                        }
                      };
                      
                      exports.default = DahuaVTO;
                      
                      new DahuaVTO();```
                      H 1 Antwort Letzte Antwort
                      0
                      • I init5

                        @h07d0q So, ich habe nun ein funktionierendes Skript am Laufen. Geklaut habe ich es von hier. Wäre schön wenn jemand einen Adapter daraus bauen kann.

                        const DigestFetch = require('digest-fetch');
                        const net = require('net');
                        const fs = require('fs');
                        const path = require('path');
                        const md5 = require('md5');
                        const mqtt = require('mqtt');
                        
                        class DahuaVTO {
                          deviceType;
                          serialNumber;
                        
                          requestId = 0;
                        
                          sessionId = 0;
                        
                          keepAliveInterval = 60;
                        
                          _keepAliveTimer;
                        
                          doorbellSocket;
                        
                          mqttClient;
                        
                          constructor() {
                            this.dahua_host = "192.168.xxx.xxx";
                            this.dahua_username = "VTOusername";
                            this.dahua_password = "VTOpassword";
                            this.mqtt_broker_host = "192.168.xxx.xxx";
                            this.mqtt_broker_port = "1883";
                            this.mqtt_broker_username = "mqttUsername";
                            this.mqtt_broker_password = "mqttPassword";
                            this.mqtt_broker_topic_prefix = "DahuaVTO";
                            this.digestClient =  new DigestFetch(
                              this.dahua_username,
                              this.dahua_password
                            );
                        
                            this.getDeviceDetails().then(({ deviceType, serialNumber }) => {
                              this.deviceType = deviceType;
                              this.serialNumber = serialNumber;
                              this.start();
                            });
                          }
                        
                          start() {
                            this.setupDoorbellSocket();
                            this.setupMQTT();
                            this.initLogin();
                          }
                        
                          async getDeviceDetails() {
                            return this.digestClient
                              .fetch(
                                `http://${this.dahua_host}/cgi-bin/magicBox.cgi?action=getSystemInfo`
                              )
                              .then((r) => r.text())
                              .then((text) => {
                                const deviceDetails = text
                                  .trim()
                                  .split('\n')
                                  .reduce((obj, str) => {
                                    const [key, val] = str.split('=');
                                    obj[key] = val.trim();
                                    return obj;
                                  }, {});
                                return deviceDetails;
                              });
                          }
                        
                          saveSnapshot(p = '/tmp/') {
                            let now = new Date();
                            let dateStr = `${now.getFullYear()}-${
                              now.getMonth() + 1
                            }-${now.getDate()}-${now.getHours()}-${now.getMinutes()}-${now.getSeconds()}`;
                            let destination = path.join(p, `DoorBell_${dateStr}.jpg`);
                            this.digestClient
                              .fetch(`http://${this.dahua_host}/cgi-bin/snapshot.cgi`)
                              .then((r) => {
                                return r.buffer();
                              })
                              .then((buf) => {
                                fs.writeFile(destination, buf, 'binary', function (err) {
                                  if (err) {
                                    log('Error saving snapshot to disk', err);
                                  } else {
                                    log('Snapshot saved');
                                  }
                                });
                              });
                          }
                        
                          setupDoorbellSocket() {
                            let socket = new net.Socket({ readable: true, writable: true });
                            socket.on('end', function () {
                              log('Doorbell socket ended');
                            });
                            socket.on('close', function () {
                             log('Doorbell socket closed');
                              clearInterval(this._keepAliveTimer);
                            });
                            socket.on('data', this.receive.bind(this));
                            socket.on('error', function (e) {
                              log('Doorbell socket error', e);
                              this.doorbellSocket.destroy();
                              this.mqttClient.end(true);
                              clearInterval(this._keepAliveTimer);
                              this.start();    });
                            this.doorbellSocket = socket.connect({ port: 5000, host: this.dahua_host });
                          }
                        
                          setupMQTT() {
                            this.mqttClient = mqtt.connect({
                              host: this.mqtt_broker_host,
                              port: this.mqtt_broker_port,
                              username: this.mqtt_broker_username,
                              password: this.mqtt_broker_password,
                              will: {
                                topic: `${this.mqtt_broker_topic_prefix}/lwt`,
                                payload: 'connected',
                                qos: 1,
                              },
                            });
                            this.mqttClient.on('disconnect', function (packet) {
                              log('MQTTDisconnect', packet);
                            });
                            this.mqttClient.on('message', function (topic, message, packet) {
                              log('MQTTMessage', { topic, message, packet });
                            });
                          }
                        
                          publishToMQTT(name, payload) {
                            let message = JSON.stringify(payload);
                            this.mqttClient.publish(
                              `${this.mqtt_broker_topic_prefix}/${name}/Event`,
                              message
                            );
                          }
                        
                          send(data) {
                            let json = JSON.stringify(data);
                            let buf = Buffer.alloc(32);
                            let offset = buf.writeUInt32BE(0x20000000);
                            offset = buf.writeUInt32BE(0x44484950, offset);
                            offset = buf.writeDoubleBE(0, offset);
                            offset = buf.writeUInt32LE(json.length, offset);
                            offset = buf.writeUInt32LE(0, offset);
                            offset = buf.writeUInt32LE(json.length, offset);
                            offset = buf.writeUInt32LE(0, offset);
                            buf = Buffer.concat([buf, Buffer.from(json)]);
                            this.requestId += 1;
                            this.doorbellSocket.write(buf);
                          }
                        
                          receive(buf) {
                            let str = buf.slice(32).toString();
                            let obj = JSON.parse(str);
                            if (this.requestId === 1) {
                              this.handleFirstLoginPayload(obj);
                            } else if (this.requestId === 2) {
                              this.handleSecondLoginPayload(obj);
                            } else if (obj.method === 'client.notifyEventStream') {
                              this.handleEvents(obj.params.eventList);
                            } else {
                              this.handleGenericPayload(obj);
                            }
                          }
                        
                          initLogin() {
                            this.send({
                              id: 10000,
                              magic: '0x1234',
                              method: 'global.login',
                              params: {
                                clientType: '',
                                ipAddr: '(null)',
                                loginType: 'Direct',
                                password: '',
                                userName: this.dahua_username,
                              },
                              session: 0,
                            });
                          }
                        
                        
                          handleFirstLoginPayload({ session, params: { random, realm } }) {
                            this.sessionId = session;
                            let randomHash = this.genMD5Hash(random, realm);
                            this.send({
                              id: 10000, 
                              magic: '0x1234',
                              method: 'global.login',
                              session: this.sessionId,
                              params: {
                                userName: this.dahua_username,
                                password: randomHash,
                                clientType: '',
                                ipAddr: '(null)',
                                loginType: 'Direct',
                                authorityType: 'Default',
                              },
                            });
                          }
                        
                          handleSecondLoginPayload(obj) {
                            if (obj.result) {
                              log('Logging to Dahua Doorbell successful');
                              this.keepAliveInterval = obj.params.keepAliveInterval - 5;
                              this.attachEventManager();
                              this.keepConnectionAlive();
                            } else {
                              log('Failed to login. Response was: ', obj);
                            }
                          }
                        
                          handleGenericPayload(obj) {
                            if (
                              obj.result === true &&
                              obj.params &&
                              Object.hasOwnProperty.call(obj.params, 'timeout')
                            ) {
                              log('Publish KeepAlive event');
                              this.publishToMQTT('keepAlive', {
                                deviceType: this.deviceType,
                                serialNumber: this.serialNumber,
                              });
                            } else {
                              log(
                                'handleGenericPayload# Cannot handle received payload',
                                obj
                              );
                            }
                          }
                        
                          genMD5Hash(random, realm) {
                            const base_credentials = `${this.dahua_username}:${realm}:${this.dahua_password}`;
                            const pwddb_hash = md5(base_credentials).toUpperCase();
                            const base_pass = `${this.dahua_username}:${random}:${pwddb_hash}`;
                            return md5(base_pass).toUpperCase();
                          }
                        
                          attachEventManager() {
                            this.send({
                              id: this.requestId,
                              magic: '0x1234',
                              method: 'eventManager.attach',
                              params: {
                                codes: ['All'],
                              },
                              session: this.sessionId,
                            });
                          }
                        
                          handleEvents(events) {
                            events.forEach((event) => {
                              log(`Publish event ${event.Code} to MQTT`);
                              this.publishToMQTT(event.Code, {
                                Action: event.eventAction,
                                Data: event.Data,
                                deviceType: this.deviceType,
                                serialNumber: this.serialNumber,
                              });
                            });
                          }
                        
                        
                          keepConnectionAlive(delay) {
                            this._keepAliveTimer = setInterval(() => {
                              let keepAlivePayload = {
                                method: 'global.keepAlive',
                                magic: '0x1234',
                                params: {
                                  timeout: delay,
                                  active: true,
                                },
                                id: this.requestId,
                                session: this.sessionId,
                              };
                        
                              this.send(keepAlivePayload);
                            }, this.keepAliveInterval * 1000);
                          }
                        
                          openDoor() {
                            return this.digestClient
                              .fetch(
                                `http://${this.dahua_host}/cgi-bin/accessControl.cgi?action=openDoor&channel=1&UserID=101&Type=Remote`
                              )
                              .then((r) => {
                                if (r.ok) {
                                  log('Door relay triggered');
                                } else {
                                  log('Error triggering the door relay', e);
                                }
                              })
                              .catch(e => log('Connection error triggering the door relay'));
                          }
                        };
                        
                        exports.default = DahuaVTO;
                        
                        new DahuaVTO();```
                        H Offline
                        H Offline
                        h07d0q
                        schrieb am zuletzt editiert von h07d0q
                        #33

                        @init5 Vielen Dank für's Teilen!

                        Habe für mich das Skript angepasst, dass ich keinen MQTT Broker benötige. Es funktioniert bei mir so weit, dass Events ankommen und geloggt werden. Ich reagiere derzeit nur auf das eines Klingelns. Da werden im 2s Abstand Fotos aufgenommen und gespeichert, dann mittels ffmpeg zu einem gif zusammengefasst und per telegram versendet :)

                        const DigestFetch = require('digest-fetch');
                        const net = require('net');
                        const fs = require('fs');
                        const path = require('path');
                        const md5 = require('md5');
                        const __dirname = '/opt/iobroker/iobroker-data/files/';
                        
                        // Erweiterter Log im ioBroker
                        const LOG_INFO = true;    // Informationen loggen
                        const LOG_DEBUG = false;   // Erweiterter Log für Debugging
                        
                        /**
                         * Class to abstract a dahua doorbell.
                         *
                         * On instantiation it automatically connects with the doorbell, logs in and
                         * subscribes to all events.
                         *
                         * The Data is written to iobroker datapoints
                         *
                         */
                         
                        class DahuaVTO {
                          deviceType;
                          serialNumber;
                         
                          /**
                           * {Number} requestId
                           *
                           * Our Request / Response ID that must be in all requests and initated by us.
                           * This number auto-increments every time we send a message. Once we've logged in, every
                           * response contains the request id of which it's a response of, se it could be used to
                           * match responses with requests.
                           *
                           * I haven't bothered to do so because ,for what I saw, we only care about response order
                           * for the initial setup, and we do that on request at a time.
                           *
                           * If we ever make requests in parallel and we need to associate response to each request,
                           * we could use this. For not, it's just an auto-incremental number.
                           * */
                         
                          requestId = 0;
                         
                          // Session ID will be returned after successful login
                          /**
                           * {Number} sessionId
                           *
                           * When we try to log in on the doorbell we get a sessionId. From that point on every message
                           * we send over the socket needs to have include the sessionID for the doorbell to recognize us.
                           */
                          sessionId = 0;
                         
                          // Will be set after the login, but we initialize to 60s because it's a reasonable default
                          /**
                           * {Number} keepAliveInterval
                           *
                           * The number of seconds we have to space our keepAlive messages so the doorbell doesn't close
                           * the connection.
                           */
                          keepAliveInterval = 60;
                         
                          /**
                           * The ID returned by the `setInterval` call.
                           * We keep a reference in case we want to cancel it (maybe in case of failure?)
                           */
                          _keepAliveTimer;
                         
                          /**
                           * TCP socket to communicate with the doorbell external unit.
                           */
                          doorbellSocket;
                         
                         
                          constructor() {
                            this.dahua_host = "192.168.0.10";
                            this.dahua_username = "admin";
                            this.dahua_password = "password";
                            this.digestClient = new DigestFetch(
                              this.dahua_username,
                              this.dahua_password
                            );
                         
                            this.getDeviceDetails().then(({ deviceType, serialNumber }) => {
                              this.deviceType = deviceType;
                              this.serialNumber = serialNumber;
                              this.start();
                            });
                          }
                         
                          /**
                           * Starts the app by:
                           *    - Opening a TCP socket to the doorbell
                           *    - Authenticating with the doorbell and subscribing to events.
                           */
                          start() {
                            this.setupDoorbellSocket();
                            this.initLogin();
                          }
                         
                          /**
                           * Makes a request to the doorbell using digest auth to retrieve the device's information.
                           *
                           * The information is returned in plain text (not JSON) that we have to parse.
                           * For now I think we only care about device type and serial number, which can be
                           * used to disambiguate in case we have more than one doorbell.
                           */
                          async getDeviceDetails() {
                            return this.digestClient
                              .fetch(
                                `http://${this.dahua_host}/cgi-bin/magicBox.cgi?action=getSystemInfo`
                              )
                              .then((r) => r.text())
                              .then((text) => {
                                const deviceDetails = text
                                  .trim()
                                  .split('\n')
                                  .reduce((obj, str) => {
                                    const [key, val] = str.split('=');
                                    obj[key] = val.trim();
                                    return obj;
                                  }, {});
                                return deviceDetails;
                              });
                          }
                         
                          /**
                           * Creates the TCP socket connection with the doorbell on port 5000.
                           *
                           * Setups the listener for when we receive data over that socket
                           *
                           * Also setups other listeners for logging purposes mostly.
                           *
                           * If something goes wrong, we close everything and try to start over again.
                           */
                          setupDoorbellSocket() {
                            let socket = new net.Socket({ readable: true, writable: true });
                            socket.on('end', function () {
                              if (LOG_DEBUG) console.debug('Doorbell socket ended');
                            });
                            socket.on('close', function () {
                              if (LOG_DEBUG) console.debug('Doorbell socket closed');
                              clearInterval(this._keepAliveTimer);
                            });
                            socket.on('data', this.receive.bind(this));
                            socket.on('error', function (e) {
                              console.error('Doorbell socket error', e);
                              this.doorbellSocket.destroy(); // destroy the socket
                              clearInterval(this._keepAliveTimer); // Stop sending keepalive requests
                              this.start(); // Start over again.
                            });
                            this.doorbellSocket = socket.connect({ port: 5000, host: this.dahua_host });
                          }
                         
                          /**
                           * Sends a message with the given data to the doorbell's outside unit using the TCP socket.
                           * @param {string} data
                           *
                           * This is a fairly low level way of communication, so let's dive in.
                           *
                           * We write binary to the socket, so we have to use buffers.
                           *
                           * The first 32 bytes of the message are the header.
                           * After the header we concat the actual message, which is a JSON string.
                           * The header has some bits that are fixed and others that are the length of the message that will
                           * come after.
                           *
                           * I didn't reverse-engineered this myself but it works. Take it as gospel as I did.
                           */
                          send(data) {
                            let json = JSON.stringify(data);
                            let buf = Buffer.alloc(32);
                            let offset = buf.writeUInt32BE(0x20000000);
                            offset = buf.writeUInt32BE(0x44484950, offset);
                            offset = buf.writeDoubleBE(0, offset);
                            offset = buf.writeUInt32LE(json.length, offset);
                            offset = buf.writeUInt32LE(0, offset);
                            offset = buf.writeUInt32LE(json.length, offset);
                            offset = buf.writeUInt32LE(0, offset);
                            buf = Buffer.concat([buf, Buffer.from(json)]);
                            this.requestId += 1;
                            this.doorbellSocket.write(buf);
                          }
                         
                          /**
                           * Handles received messages from the TCP socket.
                           * @param {Buffer} buf
                           *
                           * The received messages are binary. Once discarded the first 32 bytes (the header),
                           * the rest of the message is parsed as as a JSON string.
                           *
                           * The header contains the length of the received response in bytes 16..20 and the expected
                           * length of the response in bytes 24..28 in case we need it, but I haven't found a
                           * reason to. Perhaps responses might be sent in several chunks? So far it doesn't seem to be
                           * the case.
                           *
                           * Since we always make requests in the exact same order, we know the first two responses are
                           * for the authentication.
                           * Subsequent responses can be either events or keepalive responses.
                           */
                          receive(buf) {
                            let str = buf.slice(32).toString();
                            let obj = JSON.parse(str);
                            if (this.requestId === 1) {
                              this.handleFirstLoginPayload(obj);
                            } else if (this.requestId === 2) {
                              this.handleSecondLoginPayload(obj);
                            } else if (obj.method === 'client.notifyEventStream') {
                              this.handleEvents(obj.params.eventList);
                            } else {
                              this.handleGenericPayload(obj);
                            }
                          }
                         
                          /**
                           * Sends the initial login request.
                           * Note that does not include any password.
                           * The response to this request will be negative but that is expected, it will contain the
                           * necessary information to login.
                           */
                          initLogin() {
                            this.send({
                              id: 10000,
                              magic: '0x1234',
                              method: 'global.login',
                              params: {
                                clientType: '',
                                ipAddr: '(null)',
                                loginType: 'Direct',
                                password: '',
                                userName: this.dahua_username,
                              },
                              session: 0,
                            });
                          }
                         
                          /**
                           * Handles the response to the initial login request.
                           *
                           * The response contains a session ID, a realm and a random, which in combination with
                           * the username and the password are used to generate an MD5 password that is used
                           * for logging in.
                           *
                           * @param {object} payload
                           */
                          handleFirstLoginPayload({ session, params: { random, realm } }) {
                            this.sessionId = session;
                            let randomHash = this.genMD5Hash(random, realm);
                            this.send({
                              id: 10000, // I assume this ID a high number just because we have to send something.
                              magic: '0x1234', // No idea what this is
                              method: 'global.login',
                              session: this.sessionId,
                              params: {
                                userName: this.dahua_username,
                                password: randomHash,
                                clientType: '',
                                ipAddr: '(null)',
                                loginType: 'Direct',
                                authorityType: 'Default',
                              },
                            });
                          }
                         
                          /**
                           * Handles the response to the second (and last) response to login request.
                           *
                           * If successful, any subsequent message that includes the session id will be accepted for
                           * as long as the socket is not closed.
                           *
                           * To prevent the socket from closing we send a keepalive message every so often.
                           *
                           * Also now that we're authenticated we subscribe to all events fired by the doorbell.
                           */
                          handleSecondLoginPayload(obj) {
                            if (obj.result) {
                              if (LOG_INFO) console.log('Login to Dahua Doorbell successful');
                              this.keepAliveInterval = obj.params.keepAliveInterval - 5;
                              this.attachEventManager();
                              this.keepConnectionAlive();
                            } else {
                              console.error('Failed to login. Response was: ', obj);
                            }
                          }
                         
                          /**
                           * Publishes Data of an event with the given name and payload.
                           * @param {string} name
                           * @param {object} payload
                           */
                          publishData(name, payload) {
                            let message = JSON.stringify(payload);
                            if (LOG_DEBUG && (name == "TimeChange" || name == "NTPAdjustTime")) {
                                console.debug(message);
                            } else if (!(name == "TimeChange" || name == "NTPAdjustTime")) console.log(message);
                          }
                        
                          /**
                           * Handles any response not handled by any other method. I believe only keepalive responses
                           * will end up here, but added some logging just in case.
                           *
                           * For now keepalive events are published, but I don't see a good reason for that.
                           */
                          handleGenericPayload(obj) {
                            if (
                              obj.result === true &&
                              obj.params &&
                              (Object.hasOwnProperty.call(obj.params, 'timeout') || Object.hasOwnProperty.call(obj.params, 'SID'))
                            ) {
                              if (LOG_DEBUG) {
                                console.debug('Publish KeepAlive event');
                                this.publishData('keepAlive', {
                                    deviceType: this.deviceType,
                                    serialNumber: this.serialNumber,
                                });
                              }
                            } else {
                              console.error('handleGenericPayload# Cannot handle received payload');
                              console.error(obj);
                            }
                          }
                         
                          /**
                           * Generates a MD5 digest of the username, password, realms and random to send as
                           * password when logging in.
                           * @param {*} random
                           * @param {*} realm
                           */
                          genMD5Hash(random, realm) {
                            const base_credentials = `${this.dahua_username}:${realm}:${this.dahua_password}`;
                            const pwddb_hash = md5(base_credentials).toUpperCase();
                            const base_pass = `${this.dahua_username}:${random}:${pwddb_hash}`;
                            return md5(base_pass).toUpperCase();
                          }
                         
                          /**
                           * Sends the message to subscribe to all dorbell events.
                           */
                          attachEventManager() {
                            this.send({
                              id: this.requestId,
                              magic: '0x1234',
                              method: 'eventManager.attach',
                              params: {
                                codes: ['All'],
                              },
                              session: this.sessionId,
                            });
                          }
                         
                          /**
                           * Handles the events sent by the doorbell.
                           *
                           * It just publishes those events along with some information of the device
                           * 
                           */
                          handleEvents(events) {
                            events.forEach((event) => {
                              if (LOG_INFO) console.log(`Publish event ${event.Code}`);
                              
                              this.publishData(event.Code, {
                                Action: event.eventAction,
                                Data: event.Data,
                                deviceType: this.deviceType,
                                serialNumber: this.serialNumber,
                              });
                        
                              if (event.Code == "CallNoAnswered") {
                                  this.saveSnapshots();
                              }
                            });
                          }
                         
                          /**
                           * Sets up a function to be called periodically to keep the socket open by sending
                           * keepalive messages.
                           * @param {Number} delay (in seconds)
                           */
                          keepConnectionAlive(delay) {
                            this._keepAliveTimer = setInterval(() => {
                              let keepAlivePayload = {
                                method: 'global.keepAlive',
                                magic: '0x1234',
                                params: {
                                  timeout: delay,
                                  active: true,
                                },
                                id: this.requestId,
                                session: this.sessionId,
                              };
                         
                              this.send(keepAlivePayload);
                            }, this.keepAliveInterval * 1000);
                          }
                         
                          /**
                           * Remotely triggers the relay 1 (e.g. to open an electric gate).
                           *
                           * In my VTO 2202 F this also triggers the voice announcing the the door has been opened.
                           */
                          openDoor() {
                            return this.digestClient
                              .fetch(
                                `http://${this.dahua_host}/cgi-bin/accessControl.cgi?action=openDoor&channel=1&UserID=101&Type=Remote`
                              )
                              .then((r) => {
                                if (r.ok) {
                                  if (LOG_INFO) console.log('Door relay triggered');
                                } else {
                                  console.error('Error triggering the door relay', e);
                                }
                              })
                              .catch(e => console.error('Connection error triggering the door relay'));
                          }
                         
                          // requestMissedCallsLog() {
                          //   this.send({
                          //     id: this.requestId,
                          //     magic: '0x1234',
                          //     method: 'RecordFinder.factory.create',
                          //     params: {
                          //       name: 'VideoTalkMissedLog',
                          //     },
                          //     session: this.sessionId,
                          //   });
                          // }
                         
                          // requestMissedCallsLog2(findToken) {
                          //   this.send({
                          //     id: this.requestId,
                          //     magic: '0x1234',
                          //     method: 'RecordFinder.startFind',
                          //     object: findToken,
                          //     params: { condition: null },
                          //     session: this.sessionId,
                          //   });
                          // }
                         
                          // requestMissedCallsLog3(findToken) {
                          //   this.send({
                          //     id: this.requestId,
                          //     magic: '0x1234',
                          //     method: 'RecordFinder.doFind',
                          //     object: findToken,
                          //     params: { count: 3 }, // Number of calls to show
                          //     session: this.sessionId,
                          //   });
                          // }
                          
                          /**
                           * Saves a snapshot of the doorbells image into the given directory (defaults to /tmp/).
                           *
                           * By default the file is named with a simple timestamp of the current time (YYYY-MM-DD-H-M-S.jpg)
                           * 
                           */
                          archiveSnapshot(p = '0_userdata.0/VTO') {
                            let now = new Date();
                            let dateStr = `${now.getFullYear()}-${
                              now.getMonth() + 1
                            }-${now.getDate()}-${now.getHours()}-${now.getMinutes()}-${now.getSeconds()}`;
                            let destination = path.join(__dirname, p, `VTO_${dateStr}.jpg`);
                            this.digestClient
                              .fetch(`http://${this.dahua_host}/cgi-bin/snapshot.cgi`)
                              .then((r) => {
                                return r.buffer();
                              })
                              .then((buf) => {
                                fs.writeFile(destination, buf, 'binary', function (err) {
                                  if (err) {
                                    console.error(err);
                                    console.error('Error saving snapshot to disk', err);
                                  } else {
                                    if (LOG_INFO) console.log('Snapshot saved: ' + destination);
                                  }
                                });
                              });
                          }
                        
                          async saveSnapshots() {
                            for (let i = 1; i < 4; i++) {
                                let destination = `/VTO/VTO_${i}.jpg`
                                this.digestClient
                                .fetch(`http://${this.dahua_host}/cgi-bin/snapshot.cgi`)
                                .then((r) => {
                                    return r.buffer();
                                })
                                .then((buf) => {
                                    writeFile('vis.0', destination, buf, function (err) {
                                        if (err) {
                                            console.error(err);
                                            console.error('Error saving snapshot to disk', err);
                                        } else {
                                            if (LOG_INFO) console.debug('Snapshot saved: ' + destination);
                                            //ggf. hier schon einzelnes Bild verschicken
                                        }
                                    });
                                });
                                await new Promise(resolve => setTimeout(resolve, 2000));
                            }
                            // Fotos wurden erstellt
                            // GIF erstellen
                            if (LOG_DEBUG) console.debug('telegram.gif erstellen...');
                            exec("ffmpeg -framerate 2 -pattern_type glob -i '/opt/iobroker/iobroker-data/files/vis.0/VTO/VTO_*.jpg' -y '/opt/iobroker/iobroker-data/files/vis.0/VTO/telegram.gif'", async function (error, result, stderr) {
                                //nach Erstellung per Telegram versenden
                                //fs.readFileSync('/opt/iobroker/iobroker-data/files/vis.0/VTO/telegram.gif');
                                if (LOG_DEBUG) console.debug(stderr);
                                if (LOG_INFO) console.log('GIF erstellt. Telegram Nachricht versenden.');
                                sendTo("telegram", "send", {
                                    text: '/opt/iobroker/iobroker-data/files/vis.0/VTO/telegram.gif', caption: 'Haustür:'
                                });
                            });
                          }
                        };
                         
                        exports.default = DahuaVTO;
                         
                        new DahuaVTO();
                        

                        Ich hoffe das hilft dem ein oder anderen Suchenden, der es einfach will :)

                        L 1 Antwort Letzte Antwort
                        0
                        • H h07d0q

                          @init5 Vielen Dank für's Teilen!

                          Habe für mich das Skript angepasst, dass ich keinen MQTT Broker benötige. Es funktioniert bei mir so weit, dass Events ankommen und geloggt werden. Ich reagiere derzeit nur auf das eines Klingelns. Da werden im 2s Abstand Fotos aufgenommen und gespeichert, dann mittels ffmpeg zu einem gif zusammengefasst und per telegram versendet :)

                          const DigestFetch = require('digest-fetch');
                          const net = require('net');
                          const fs = require('fs');
                          const path = require('path');
                          const md5 = require('md5');
                          const __dirname = '/opt/iobroker/iobroker-data/files/';
                          
                          // Erweiterter Log im ioBroker
                          const LOG_INFO = true;    // Informationen loggen
                          const LOG_DEBUG = false;   // Erweiterter Log für Debugging
                          
                          /**
                           * Class to abstract a dahua doorbell.
                           *
                           * On instantiation it automatically connects with the doorbell, logs in and
                           * subscribes to all events.
                           *
                           * The Data is written to iobroker datapoints
                           *
                           */
                           
                          class DahuaVTO {
                            deviceType;
                            serialNumber;
                           
                            /**
                             * {Number} requestId
                             *
                             * Our Request / Response ID that must be in all requests and initated by us.
                             * This number auto-increments every time we send a message. Once we've logged in, every
                             * response contains the request id of which it's a response of, se it could be used to
                             * match responses with requests.
                             *
                             * I haven't bothered to do so because ,for what I saw, we only care about response order
                             * for the initial setup, and we do that on request at a time.
                             *
                             * If we ever make requests in parallel and we need to associate response to each request,
                             * we could use this. For not, it's just an auto-incremental number.
                             * */
                           
                            requestId = 0;
                           
                            // Session ID will be returned after successful login
                            /**
                             * {Number} sessionId
                             *
                             * When we try to log in on the doorbell we get a sessionId. From that point on every message
                             * we send over the socket needs to have include the sessionID for the doorbell to recognize us.
                             */
                            sessionId = 0;
                           
                            // Will be set after the login, but we initialize to 60s because it's a reasonable default
                            /**
                             * {Number} keepAliveInterval
                             *
                             * The number of seconds we have to space our keepAlive messages so the doorbell doesn't close
                             * the connection.
                             */
                            keepAliveInterval = 60;
                           
                            /**
                             * The ID returned by the `setInterval` call.
                             * We keep a reference in case we want to cancel it (maybe in case of failure?)
                             */
                            _keepAliveTimer;
                           
                            /**
                             * TCP socket to communicate with the doorbell external unit.
                             */
                            doorbellSocket;
                           
                           
                            constructor() {
                              this.dahua_host = "192.168.0.10";
                              this.dahua_username = "admin";
                              this.dahua_password = "password";
                              this.digestClient = new DigestFetch(
                                this.dahua_username,
                                this.dahua_password
                              );
                           
                              this.getDeviceDetails().then(({ deviceType, serialNumber }) => {
                                this.deviceType = deviceType;
                                this.serialNumber = serialNumber;
                                this.start();
                              });
                            }
                           
                            /**
                             * Starts the app by:
                             *    - Opening a TCP socket to the doorbell
                             *    - Authenticating with the doorbell and subscribing to events.
                             */
                            start() {
                              this.setupDoorbellSocket();
                              this.initLogin();
                            }
                           
                            /**
                             * Makes a request to the doorbell using digest auth to retrieve the device's information.
                             *
                             * The information is returned in plain text (not JSON) that we have to parse.
                             * For now I think we only care about device type and serial number, which can be
                             * used to disambiguate in case we have more than one doorbell.
                             */
                            async getDeviceDetails() {
                              return this.digestClient
                                .fetch(
                                  `http://${this.dahua_host}/cgi-bin/magicBox.cgi?action=getSystemInfo`
                                )
                                .then((r) => r.text())
                                .then((text) => {
                                  const deviceDetails = text
                                    .trim()
                                    .split('\n')
                                    .reduce((obj, str) => {
                                      const [key, val] = str.split('=');
                                      obj[key] = val.trim();
                                      return obj;
                                    }, {});
                                  return deviceDetails;
                                });
                            }
                           
                            /**
                             * Creates the TCP socket connection with the doorbell on port 5000.
                             *
                             * Setups the listener for when we receive data over that socket
                             *
                             * Also setups other listeners for logging purposes mostly.
                             *
                             * If something goes wrong, we close everything and try to start over again.
                             */
                            setupDoorbellSocket() {
                              let socket = new net.Socket({ readable: true, writable: true });
                              socket.on('end', function () {
                                if (LOG_DEBUG) console.debug('Doorbell socket ended');
                              });
                              socket.on('close', function () {
                                if (LOG_DEBUG) console.debug('Doorbell socket closed');
                                clearInterval(this._keepAliveTimer);
                              });
                              socket.on('data', this.receive.bind(this));
                              socket.on('error', function (e) {
                                console.error('Doorbell socket error', e);
                                this.doorbellSocket.destroy(); // destroy the socket
                                clearInterval(this._keepAliveTimer); // Stop sending keepalive requests
                                this.start(); // Start over again.
                              });
                              this.doorbellSocket = socket.connect({ port: 5000, host: this.dahua_host });
                            }
                           
                            /**
                             * Sends a message with the given data to the doorbell's outside unit using the TCP socket.
                             * @param {string} data
                             *
                             * This is a fairly low level way of communication, so let's dive in.
                             *
                             * We write binary to the socket, so we have to use buffers.
                             *
                             * The first 32 bytes of the message are the header.
                             * After the header we concat the actual message, which is a JSON string.
                             * The header has some bits that are fixed and others that are the length of the message that will
                             * come after.
                             *
                             * I didn't reverse-engineered this myself but it works. Take it as gospel as I did.
                             */
                            send(data) {
                              let json = JSON.stringify(data);
                              let buf = Buffer.alloc(32);
                              let offset = buf.writeUInt32BE(0x20000000);
                              offset = buf.writeUInt32BE(0x44484950, offset);
                              offset = buf.writeDoubleBE(0, offset);
                              offset = buf.writeUInt32LE(json.length, offset);
                              offset = buf.writeUInt32LE(0, offset);
                              offset = buf.writeUInt32LE(json.length, offset);
                              offset = buf.writeUInt32LE(0, offset);
                              buf = Buffer.concat([buf, Buffer.from(json)]);
                              this.requestId += 1;
                              this.doorbellSocket.write(buf);
                            }
                           
                            /**
                             * Handles received messages from the TCP socket.
                             * @param {Buffer} buf
                             *
                             * The received messages are binary. Once discarded the first 32 bytes (the header),
                             * the rest of the message is parsed as as a JSON string.
                             *
                             * The header contains the length of the received response in bytes 16..20 and the expected
                             * length of the response in bytes 24..28 in case we need it, but I haven't found a
                             * reason to. Perhaps responses might be sent in several chunks? So far it doesn't seem to be
                             * the case.
                             *
                             * Since we always make requests in the exact same order, we know the first two responses are
                             * for the authentication.
                             * Subsequent responses can be either events or keepalive responses.
                             */
                            receive(buf) {
                              let str = buf.slice(32).toString();
                              let obj = JSON.parse(str);
                              if (this.requestId === 1) {
                                this.handleFirstLoginPayload(obj);
                              } else if (this.requestId === 2) {
                                this.handleSecondLoginPayload(obj);
                              } else if (obj.method === 'client.notifyEventStream') {
                                this.handleEvents(obj.params.eventList);
                              } else {
                                this.handleGenericPayload(obj);
                              }
                            }
                           
                            /**
                             * Sends the initial login request.
                             * Note that does not include any password.
                             * The response to this request will be negative but that is expected, it will contain the
                             * necessary information to login.
                             */
                            initLogin() {
                              this.send({
                                id: 10000,
                                magic: '0x1234',
                                method: 'global.login',
                                params: {
                                  clientType: '',
                                  ipAddr: '(null)',
                                  loginType: 'Direct',
                                  password: '',
                                  userName: this.dahua_username,
                                },
                                session: 0,
                              });
                            }
                           
                            /**
                             * Handles the response to the initial login request.
                             *
                             * The response contains a session ID, a realm and a random, which in combination with
                             * the username and the password are used to generate an MD5 password that is used
                             * for logging in.
                             *
                             * @param {object} payload
                             */
                            handleFirstLoginPayload({ session, params: { random, realm } }) {
                              this.sessionId = session;
                              let randomHash = this.genMD5Hash(random, realm);
                              this.send({
                                id: 10000, // I assume this ID a high number just because we have to send something.
                                magic: '0x1234', // No idea what this is
                                method: 'global.login',
                                session: this.sessionId,
                                params: {
                                  userName: this.dahua_username,
                                  password: randomHash,
                                  clientType: '',
                                  ipAddr: '(null)',
                                  loginType: 'Direct',
                                  authorityType: 'Default',
                                },
                              });
                            }
                           
                            /**
                             * Handles the response to the second (and last) response to login request.
                             *
                             * If successful, any subsequent message that includes the session id will be accepted for
                             * as long as the socket is not closed.
                             *
                             * To prevent the socket from closing we send a keepalive message every so often.
                             *
                             * Also now that we're authenticated we subscribe to all events fired by the doorbell.
                             */
                            handleSecondLoginPayload(obj) {
                              if (obj.result) {
                                if (LOG_INFO) console.log('Login to Dahua Doorbell successful');
                                this.keepAliveInterval = obj.params.keepAliveInterval - 5;
                                this.attachEventManager();
                                this.keepConnectionAlive();
                              } else {
                                console.error('Failed to login. Response was: ', obj);
                              }
                            }
                           
                            /**
                             * Publishes Data of an event with the given name and payload.
                             * @param {string} name
                             * @param {object} payload
                             */
                            publishData(name, payload) {
                              let message = JSON.stringify(payload);
                              if (LOG_DEBUG && (name == "TimeChange" || name == "NTPAdjustTime")) {
                                  console.debug(message);
                              } else if (!(name == "TimeChange" || name == "NTPAdjustTime")) console.log(message);
                            }
                          
                            /**
                             * Handles any response not handled by any other method. I believe only keepalive responses
                             * will end up here, but added some logging just in case.
                             *
                             * For now keepalive events are published, but I don't see a good reason for that.
                             */
                            handleGenericPayload(obj) {
                              if (
                                obj.result === true &&
                                obj.params &&
                                (Object.hasOwnProperty.call(obj.params, 'timeout') || Object.hasOwnProperty.call(obj.params, 'SID'))
                              ) {
                                if (LOG_DEBUG) {
                                  console.debug('Publish KeepAlive event');
                                  this.publishData('keepAlive', {
                                      deviceType: this.deviceType,
                                      serialNumber: this.serialNumber,
                                  });
                                }
                              } else {
                                console.error('handleGenericPayload# Cannot handle received payload');
                                console.error(obj);
                              }
                            }
                           
                            /**
                             * Generates a MD5 digest of the username, password, realms and random to send as
                             * password when logging in.
                             * @param {*} random
                             * @param {*} realm
                             */
                            genMD5Hash(random, realm) {
                              const base_credentials = `${this.dahua_username}:${realm}:${this.dahua_password}`;
                              const pwddb_hash = md5(base_credentials).toUpperCase();
                              const base_pass = `${this.dahua_username}:${random}:${pwddb_hash}`;
                              return md5(base_pass).toUpperCase();
                            }
                           
                            /**
                             * Sends the message to subscribe to all dorbell events.
                             */
                            attachEventManager() {
                              this.send({
                                id: this.requestId,
                                magic: '0x1234',
                                method: 'eventManager.attach',
                                params: {
                                  codes: ['All'],
                                },
                                session: this.sessionId,
                              });
                            }
                           
                            /**
                             * Handles the events sent by the doorbell.
                             *
                             * It just publishes those events along with some information of the device
                             * 
                             */
                            handleEvents(events) {
                              events.forEach((event) => {
                                if (LOG_INFO) console.log(`Publish event ${event.Code}`);
                                
                                this.publishData(event.Code, {
                                  Action: event.eventAction,
                                  Data: event.Data,
                                  deviceType: this.deviceType,
                                  serialNumber: this.serialNumber,
                                });
                          
                                if (event.Code == "CallNoAnswered") {
                                    this.saveSnapshots();
                                }
                              });
                            }
                           
                            /**
                             * Sets up a function to be called periodically to keep the socket open by sending
                             * keepalive messages.
                             * @param {Number} delay (in seconds)
                             */
                            keepConnectionAlive(delay) {
                              this._keepAliveTimer = setInterval(() => {
                                let keepAlivePayload = {
                                  method: 'global.keepAlive',
                                  magic: '0x1234',
                                  params: {
                                    timeout: delay,
                                    active: true,
                                  },
                                  id: this.requestId,
                                  session: this.sessionId,
                                };
                           
                                this.send(keepAlivePayload);
                              }, this.keepAliveInterval * 1000);
                            }
                           
                            /**
                             * Remotely triggers the relay 1 (e.g. to open an electric gate).
                             *
                             * In my VTO 2202 F this also triggers the voice announcing the the door has been opened.
                             */
                            openDoor() {
                              return this.digestClient
                                .fetch(
                                  `http://${this.dahua_host}/cgi-bin/accessControl.cgi?action=openDoor&channel=1&UserID=101&Type=Remote`
                                )
                                .then((r) => {
                                  if (r.ok) {
                                    if (LOG_INFO) console.log('Door relay triggered');
                                  } else {
                                    console.error('Error triggering the door relay', e);
                                  }
                                })
                                .catch(e => console.error('Connection error triggering the door relay'));
                            }
                           
                            // requestMissedCallsLog() {
                            //   this.send({
                            //     id: this.requestId,
                            //     magic: '0x1234',
                            //     method: 'RecordFinder.factory.create',
                            //     params: {
                            //       name: 'VideoTalkMissedLog',
                            //     },
                            //     session: this.sessionId,
                            //   });
                            // }
                           
                            // requestMissedCallsLog2(findToken) {
                            //   this.send({
                            //     id: this.requestId,
                            //     magic: '0x1234',
                            //     method: 'RecordFinder.startFind',
                            //     object: findToken,
                            //     params: { condition: null },
                            //     session: this.sessionId,
                            //   });
                            // }
                           
                            // requestMissedCallsLog3(findToken) {
                            //   this.send({
                            //     id: this.requestId,
                            //     magic: '0x1234',
                            //     method: 'RecordFinder.doFind',
                            //     object: findToken,
                            //     params: { count: 3 }, // Number of calls to show
                            //     session: this.sessionId,
                            //   });
                            // }
                            
                            /**
                             * Saves a snapshot of the doorbells image into the given directory (defaults to /tmp/).
                             *
                             * By default the file is named with a simple timestamp of the current time (YYYY-MM-DD-H-M-S.jpg)
                             * 
                             */
                            archiveSnapshot(p = '0_userdata.0/VTO') {
                              let now = new Date();
                              let dateStr = `${now.getFullYear()}-${
                                now.getMonth() + 1
                              }-${now.getDate()}-${now.getHours()}-${now.getMinutes()}-${now.getSeconds()}`;
                              let destination = path.join(__dirname, p, `VTO_${dateStr}.jpg`);
                              this.digestClient
                                .fetch(`http://${this.dahua_host}/cgi-bin/snapshot.cgi`)
                                .then((r) => {
                                  return r.buffer();
                                })
                                .then((buf) => {
                                  fs.writeFile(destination, buf, 'binary', function (err) {
                                    if (err) {
                                      console.error(err);
                                      console.error('Error saving snapshot to disk', err);
                                    } else {
                                      if (LOG_INFO) console.log('Snapshot saved: ' + destination);
                                    }
                                  });
                                });
                            }
                          
                            async saveSnapshots() {
                              for (let i = 1; i < 4; i++) {
                                  let destination = `/VTO/VTO_${i}.jpg`
                                  this.digestClient
                                  .fetch(`http://${this.dahua_host}/cgi-bin/snapshot.cgi`)
                                  .then((r) => {
                                      return r.buffer();
                                  })
                                  .then((buf) => {
                                      writeFile('vis.0', destination, buf, function (err) {
                                          if (err) {
                                              console.error(err);
                                              console.error('Error saving snapshot to disk', err);
                                          } else {
                                              if (LOG_INFO) console.debug('Snapshot saved: ' + destination);
                                              //ggf. hier schon einzelnes Bild verschicken
                                          }
                                      });
                                  });
                                  await new Promise(resolve => setTimeout(resolve, 2000));
                              }
                              // Fotos wurden erstellt
                              // GIF erstellen
                              if (LOG_DEBUG) console.debug('telegram.gif erstellen...');
                              exec("ffmpeg -framerate 2 -pattern_type glob -i '/opt/iobroker/iobroker-data/files/vis.0/VTO/VTO_*.jpg' -y '/opt/iobroker/iobroker-data/files/vis.0/VTO/telegram.gif'", async function (error, result, stderr) {
                                  //nach Erstellung per Telegram versenden
                                  //fs.readFileSync('/opt/iobroker/iobroker-data/files/vis.0/VTO/telegram.gif');
                                  if (LOG_DEBUG) console.debug(stderr);
                                  if (LOG_INFO) console.log('GIF erstellt. Telegram Nachricht versenden.');
                                  sendTo("telegram", "send", {
                                      text: '/opt/iobroker/iobroker-data/files/vis.0/VTO/telegram.gif', caption: 'Haustür:'
                                  });
                              });
                            }
                          };
                           
                          exports.default = DahuaVTO;
                           
                          new DahuaVTO();
                          

                          Ich hoffe das hilft dem ein oder anderen Suchenden, der es einfach will :)

                          L Offline
                          L Offline
                          LubuBroker
                          schrieb am zuletzt editiert von
                          #34

                          @h07d0q Hallo
                          Ist zwar schon ein bisschen älter, aber vielleicht kannst du mir trotzdem helfen.

                          Ich habe versucht dein Skript auszuführen.
                          Habe zuvor im JavaScript Adapter folgende einträge gemacht.
                          9f365b51-9a60-4c31-b35d-dd8db0b5fd28-image.png

                          Leider wird in deinem Skript die beiden Module Rot unterstrichen
                          44ab8867-7467-4e0a-8712-22c15968a3fd-image.png

                          Was mache ich da falsch?

                          IssiI 1 Antwort Letzte Antwort
                          0
                          • IssiI Offline
                            IssiI Offline
                            Issi
                            Developer
                            schrieb am zuletzt editiert von
                            #35

                            @h07d0q
                            Das mit dem Adapter ist nicht immer so einfach.

                            Klar kann ein Developer da einen basteln, aber ohne die nötigen Geräte zum Testen ist es immer ein Blindflug beim Programmieren, und wenn man die User das testen überlässt, ist das auch nicht immer so das Gelbe vom Ei, da man immer auf das Feedback warten muss, und sehr oft wissen die User nicht, was sie machen müssen.

                            Deswegen geht auch kaum ein Developer an Adapter-Entwicklung, wenn er keine Geräte zum Testen hat.

                            Aber du kannst gerne mal einen Adapter Request unter => ioBroker Adapter Requests erstellen, vorher natürlich schauen, ob es nicht schon einen Request gibt und vllt. hat ja einer der Devs die Geräte und macht da einen Adapter.

                            Bitte benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

                            1 Antwort Letzte Antwort
                            0
                            • L LubuBroker

                              @h07d0q Hallo
                              Ist zwar schon ein bisschen älter, aber vielleicht kannst du mir trotzdem helfen.

                              Ich habe versucht dein Skript auszuführen.
                              Habe zuvor im JavaScript Adapter folgende einträge gemacht.
                              9f365b51-9a60-4c31-b35d-dd8db0b5fd28-image.png

                              Leider wird in deinem Skript die beiden Module Rot unterstrichen
                              44ab8867-7467-4e0a-8712-22c15968a3fd-image.png

                              Was mache ich da falsch?

                              IssiI Offline
                              IssiI Offline
                              Issi
                              Developer
                              schrieb am zuletzt editiert von
                              #36

                              @lububroker

                              funktionirt den das Skript? Hast du es schon mal gestartet ?
                              Der JavaScript-Adapter hat da ab und an mal so paar Probleme und unterstreicht Module, obwohl alles ok ist

                              Bitte benutzt das Voting rechts unten im Beitrag wenn er euch geholfen hat.

                              1 Antwort Letzte Antwort
                              0
                              • OliverIOO Offline
                                OliverIOO Offline
                                OliverIO
                                schrieb am zuletzt editiert von
                                #37

                                @lububroker sagte in Node.js Script in iobroker integrieren:

                                Was mache ich da falsch?

                                Nichts.
                                Der javascript adapter findet die typen zu den bibliotheken nicht und zeigt das an.
                                die bibliotheken werden zu 99% auch funktionieren wie in der jeweiligen doku beschrieben.
                                für manche bibliotheken kann man noch die typen deklarationen installieren (typescript), dann dürfte das auch weg gehen. da bin ich mir aber nicht ganz sicher.

                                wie gesagt ist das nur optischer natur.

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

                                L 1 Antwort Letzte Antwort
                                0
                                • OliverIOO OliverIO

                                  @lububroker sagte in Node.js Script in iobroker integrieren:

                                  Was mache ich da falsch?

                                  Nichts.
                                  Der javascript adapter findet die typen zu den bibliotheken nicht und zeigt das an.
                                  die bibliotheken werden zu 99% auch funktionieren wie in der jeweiligen doku beschrieben.
                                  für manche bibliotheken kann man noch die typen deklarationen installieren (typescript), dann dürfte das auch weg gehen. da bin ich mir aber nicht ganz sicher.

                                  wie gesagt ist das nur optischer natur.

                                  L Offline
                                  L Offline
                                  LubuBroker
                                  schrieb am zuletzt editiert von
                                  #38

                                  @oliverio

                                  Nein das Skript läuft nicht.
                                  Folgendes Log erhalte ich beim Starten.

                                  2025-08-05 07:35:50.160 - info: javascript.0 (2626) Start JavaScript script.js.Türstation.Versuch2 (Javascript/js)
                                  2025-08-05 07:35:50.181 - info: javascript.0 (2626) script.js.Türstation.Versuch2: registered 0 subscriptions, 0 schedules, 0 messages, 0 logs and 0 file subscriptions
                                  2025-08-05 07:35:50.202 - error: javascript.0 (2626) script.js.Türstation.Versuch2: TypeError: Cannot read properties of undefined (reading 'trim')
                                  2025-08-05 07:35:50.203 - error: javascript.0 (2626) at script.js.Türstation.Versuch2:119:28
                                  2025-08-05 07:35:50.203 - error: javascript.0 (2626) at Array.reduce ()
                                  2025-08-05 07:35:50.203 - error: javascript.0 (2626) at script.js.Türstation.Versuch2:117:12
                                  2025-08-05 07:35:50.204 - error: javascript.0 (2626) at processTicksAndRejections (node:internal/process/task_queues:95:5)
                                  

                                  Im Code von @h07d0q habe ich lediglich die IP Adresse und das Passwort auf meine Gegebenheiten angepasst.
                                  Ich habe beim Javascript Adapter auch noch typescript als zusätzliches Modul angegeben.

                                  Im Code hat es auch noch mehr Stellen, die als Fehler deklariert werden. Allerding haben diese alle mit dem Login zu tun. Und so wie ich das verstanden habe, hat md5 und digest-fetch etwas mit Login zu tun.

                                  OliverIOO 1 Antwort Letzte Antwort
                                  0
                                  • L LubuBroker

                                    @oliverio

                                    Nein das Skript läuft nicht.
                                    Folgendes Log erhalte ich beim Starten.

                                    2025-08-05 07:35:50.160 - info: javascript.0 (2626) Start JavaScript script.js.Türstation.Versuch2 (Javascript/js)
                                    2025-08-05 07:35:50.181 - info: javascript.0 (2626) script.js.Türstation.Versuch2: registered 0 subscriptions, 0 schedules, 0 messages, 0 logs and 0 file subscriptions
                                    2025-08-05 07:35:50.202 - error: javascript.0 (2626) script.js.Türstation.Versuch2: TypeError: Cannot read properties of undefined (reading 'trim')
                                    2025-08-05 07:35:50.203 - error: javascript.0 (2626) at script.js.Türstation.Versuch2:119:28
                                    2025-08-05 07:35:50.203 - error: javascript.0 (2626) at Array.reduce ()
                                    2025-08-05 07:35:50.203 - error: javascript.0 (2626) at script.js.Türstation.Versuch2:117:12
                                    2025-08-05 07:35:50.204 - error: javascript.0 (2626) at processTicksAndRejections (node:internal/process/task_queues:95:5)
                                    

                                    Im Code von @h07d0q habe ich lediglich die IP Adresse und das Passwort auf meine Gegebenheiten angepasst.
                                    Ich habe beim Javascript Adapter auch noch typescript als zusätzliches Modul angegeben.

                                    Im Code hat es auch noch mehr Stellen, die als Fehler deklariert werden. Allerding haben diese alle mit dem Login zu tun. Und so wie ich das verstanden habe, hat md5 und digest-fetch etwas mit Login zu tun.

                                    OliverIOO Offline
                                    OliverIOO Offline
                                    OliverIO
                                    schrieb am zuletzt editiert von OliverIO
                                    #39

                                    @lububroker

                                    wenn du die fehlermeldung sorgfältig liest, wirst du feststellen das da nix mit der bibliothek drin steht

                                    2025-08-05 07:35:50.202 - error: javascript.0 (2626) script.js.Türstation.Versuch2: TypeError: Cannot read properties of undefined (reading 'trim')
                                    

                                    welcher mit diesem code teil zu tun hat

                                              .reduce((obj, str) => {
                                                const [key, val] = str.split('=');
                                                obj[key] = val.trim();
                                                return obj;
                                              }, {});
                                    

                                    die fehlermeldung sagt das trim nicht existiert, weil wahrscheinlich val zu diesem zeitpunkt kein string ist sondern was anderes. hier undefined.
                                    wenn du da ein console.log einfügst wirst du feststellen, das da ein element dabei ist welches kein = enthält. evtl eine leerzeile?

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

                                    L 1 Antwort Letzte Antwort
                                    0
                                    • OliverIOO OliverIO

                                      @lububroker

                                      wenn du die fehlermeldung sorgfältig liest, wirst du feststellen das da nix mit der bibliothek drin steht

                                      2025-08-05 07:35:50.202 - error: javascript.0 (2626) script.js.Türstation.Versuch2: TypeError: Cannot read properties of undefined (reading 'trim')
                                      

                                      welcher mit diesem code teil zu tun hat

                                                .reduce((obj, str) => {
                                                  const [key, val] = str.split('=');
                                                  obj[key] = val.trim();
                                                  return obj;
                                                }, {});
                                      

                                      die fehlermeldung sagt das trim nicht existiert, weil wahrscheinlich val zu diesem zeitpunkt kein string ist sondern was anderes. hier undefined.
                                      wenn du da ein console.log einfügst wirst du feststellen, das da ein element dabei ist welches kein = enthält. evtl eine leerzeile?

                                      L Offline
                                      L Offline
                                      LubuBroker
                                      schrieb am zuletzt editiert von LubuBroker
                                      #40

                                      @oliverio
                                      OK ich habe mal mit Hilfe von ChatGPT die Codezeile abgeändert

                                      .then((text) => {
                                        const deviceDetails = text
                                          .trim()
                                          .split('\n')
                                          .reduce((obj, str) => {
                                            const [key, val] = str.split('=');
                                            if (key && typeof val !== 'undefined') {
                                              obj[key] = val.trim();
                                            }
                                            return obj;
                                          }, {});
                                        return deviceDetails;
                                      });
                                      

                                      Jetzt läuft es. Vielen Dank

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


                                      Support us

                                      ioBroker
                                      Community Adapters
                                      Donate

                                      650

                                      Online

                                      32.4k

                                      Benutzer

                                      81.4k

                                      Themen

                                      1.3m

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

                                      • Du hast noch kein Konto? Registrieren

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