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

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. ioBroker Allgemein
  4. [Vorschlag] Core-API und Javascript-Adapter => Promises

NEWS

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    11
    1
    324

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    24
    1
    1.5k

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

[Vorschlag] Core-API und Javascript-Adapter => Promises

Geplant Angeheftet Gesperrt Verschoben ioBroker Allgemein
17 Beiträge 5 Kommentatoren 4.0k Aufrufe
  • Ä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.
  • AlCalzoneA Offline
    AlCalzoneA Offline
    AlCalzone
    Developer
    schrieb am zuletzt editiert von
    #1

    Ich habe ein recht technisches Feature-Request, wer keine Ahnung von JavaScript hat, braucht nicht weiterlesen ;)

    Vorab: https://developers.google.com/web/fundamentals/getting-started/primers/promises stellen ein Framework dar, das es erlaubt den Ablauf von asynchronem Code leicht verständlich zu modellieren. Anstatt

    funktion() -> callback() -> callback() -> callback()
    

    kann asynchroner Code mittels Promises folgendermaßen geschrieben werden:

    funktion()
      .then(something1)
      .then(something2)
      .catch(onerror)
    
    

    und erlaubt zudem einfachere (automatisierte) Fehlererkennung.

    Funktionen, die einen Node.JS-style Callback erwarten (wie diese hier)

    function doSomething(arg, callback) {
    // do something with arg, then...
    // either
    callback(error, null);
    // or
    callback(null, result);
    }
    
    

    können in Promise-style folgendermaßen aussehen:

    function doSomethingPromisified(arg) {
      return new Promise(function (resolve, reject)
      // do something with arg, then...
      // either
      resolve(result);
      //or
      reject(error);
      //or
      throw error;
      });
    }
    
    

    und relativ einfach mit einer Wrapper-funktion in eine Promise-Funktion umgewandelt werden. Eine solche Wrapper-Funktion könnte (in ES2017-Syntax) so aussehen:

    function promisify(fn, context) {
        return function (...args) {
            context = context || this;
            return new Promise(function (resolve, reject) {
                fn.apply(context, [...args, function (error, result) {
                    if (error)
                        return reject(error);
                    else
                        return resolve(result);
                }]);
            });
        }
    }
    
    

    Das dürfte in ioBroker eine ganze Menge Funktionen betreffen, insbesondere getState/getObject/etc. in Adaptern.

    Diese könnten dann mit

    	var getStateAsync = promisify(getState);
    
    

    etc. in ihre Promise-Versionen umgewandelt werden. Groß Funktionen umzuschreiben wird also nicht nötig sein.

    Das klingt jetzt alles noch nicht so toll, aber…

    Der große Vorteil kommt dann mit der neuesten JavaScript-Version (ECMAScript 2017). Dann können async-Funktionen definiert werden, die sich lesen wie synchrone Funktionen, aber im Hintergrund asynchron arbeiten. Eine Funktion, die beispielsweise auf die Antwort von "doSomething" wartet (während diese Funktion mit Geräten kommuniziert), könnte so aussehen:

    async function shutOffDevice() {
      try {
        let result = await doSomething('off');
        // ^ hier wird die Funktion "pausiert" während doSomething auf den Abschluss einer asynchronen Aktion wartet (HTTP/Socket.IO/HDD-Zugriffe...)
    
        let anotherResult = await doSomethingElseWith(result);
        //...
      } catch (e) {
        // Fehlerbehandlung. Fängt Fehler aus doSomething, doSomethingElseWith und dieser Funktion
      }
    }
    
    

    Mittels https://babeljs.io/ können ältere Versionen von NodeJS dazu gebracht werden, den Code der neuen Syntax bereits jetzt auszuführen, man ist also nicht nur auf Version 7+ beschränkt.

    Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

    1 Antwort Letzte Antwort
    0
    • apollon77A Offline
      apollon77A Offline
      apollon77
      schrieb am zuletzt editiert von
      #2

      Grundsätzlich ist es eine gute Idee, das gesamte Core von ioBroker umzubauen ist aber ne heiden Arbeit.

      Von daher ist meine Frage: Was ist denn für die die "Core API"?

      Ist es das was die Adapter-Klasse als Basis für Adapter-Entwicklung bietet oder das was die Javascript-Engine bietet? Was genau würdest Diu denn als notwendig ansehen.

      Ich habe gesehen das einige Adapter schon auf Promise-Basis gebaut sind, ich glaube die meisten von soef inkl. ner eigenen "Adapter-Wrapper-Klasse" um die Promises einzuschieben.

      Die operative Frage bei Promises ist für mich immer noch der node-js-Support. Ob Babel dafür sooo geeignet ist bin ich mir nicht sicher …

      Ich persönlich würde durchaus die Idee sehen für bestimmte Themen und Nutzer aktueller nodejs-Versionen Wrapper bereitzustellen (wie oben geschrieben vllt für die Adapter-Klasse oder JavaScript-Funktionen) ... aber was ist sonst der Benefit?

      Beitrag hat geholfen? Votet rechts unten im Beitrag :-) https://paypal.me/Apollon77 / https://github.com/sponsors/Apollon77

      • Debug-Log für Instanz einschalten? Admin -> Instanzen -> Expertenmodus -> Instanz aufklappen - Loglevel ändern
      • Logfiles auf Platte /opt/iobroker/log/… nutzen, Admin schneidet Zeilen ab
      1 Antwort Letzte Antwort
      0
      • AlCalzoneA Offline
        AlCalzoneA Offline
        AlCalzone
        Developer
        schrieb am zuletzt editiert von
        #3

        @apollon77:

        Grundsätzlich ist es eine gute Idee, das gesamte Core von ioBroker umzubauen ist aber ne heiden Arbeit.

        Von daher ist meine Frage: Was ist denn für die die "Core API"? `

        Da stecke ich nicht genug drin, aber ich würde mit den Funktionen anfangen, die am häufigsten in Adaptern verwendet werden, sprich get/set/create State/Object und die entsprechenden Funktionen im JS-Adapter. Komplette interna umzubauen macht vermutlich keinen Sinn, außer Bluefox sieht hierfür irgendwann Bedarf.

        Die o.g. Funktionen kommen mir daher in den Sinne dass ihr Verwendungszweck meist synchroner Natur ist

        • lege Objekt an, dann ändere den Wert

        • schalte Gerät (setState), dann führe eine andere Funktion aus

        • lese Wert, dann arbeite damit weiter (adapter)
          in der Anwendung müssen sie aber häufig asynchron verwendet werden.

        > Ich persönlich würde durchaus die Idee sehen für bestimmte Themen und Nutzer aktueller nodejs-Versionen Wrapper bereitzustellen (wie oben geschrieben vllt für die Adapter-Klasse oder JavaScript-Funktionen) … aber was ist sonst der Benefit?
        Weniger und übersichtlicherer Code für Adapterentwickler und Script-Nerds wie mich (mein Core-Skript ist ca 2000 Zeilen lang, dazu kommt nochmal knapp genauso viel außerhalb ioBroker).

        Erleichtert Anfängern den Einstieg in diese Themen.

        getState("abc")
          .then(function(result) { ... })
          .catch(function(error) {...});
        

        bzw.

        var result = await getState("abc")
        

        ist IMO einfacher zu vermitteln als

        getState("abc", function(error, result) {
          if(error) {
            //....
          }
          else {
            //...
          }
        })
        
        Die operative Frage bei Promises ist für mich immer noch der node-js-Support. Ob Babel dafür sooo geeignet ist bin ich mir nicht sicher ...
        

        Gut, dann bei nicht-nativem Support: http://bluebirdjs.com/docs/getting-started.html

        Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

        1 Antwort Letzte Antwort
        0
        • frankjokeF Offline
          frankjokeF Offline
          frankjoke
          schrieb am zuletzt editiert von
          #4

          Alcazone,

          ich verwende auch ausschließlich promises, in den Adaptern und im Javascriopt-Adapter bei scripts.

          Ich habe mir ein Library geschrieben welches ich in beiden Anwendungen verwende. Siehe meine StateMaschine die ich diese Woche vorgestellt habe: http://forum.iobroker.net/viewtopic.php?f=21&t=5556

          Dort definiere ich von Zeile 20-~83 einige Funktionen mit denen ich:

          • aus jeder Funtion mit normalem Callback (1 Argument ohne error oder 2 Argumente mit error) eine Funktion machen kann die eine promise zurückgibt, c1pP und c2pP, und dann weiter unten einige Standardinterfaces für ioBroker mit
          const pExec = c2pP(exec),       // pExec is a Promise-version of exec(command,callback)
              pCst = c1pP(createState),  // pCst is a Promise-Version of createState(id,...,callback)
              pSst = c2pP(setState),     // pSst is a Promise-Version of setState(id,...,callback) 
              pGst = c2pP(getState);
          

          Auch Funktionen wie wait oder Schleifen die strikt hintereinander ausgeführt werden (Die Funktionen müssen nur selbst Promises zurückgeben) sind dabei und ein pGet welches Webseiten abrufen kann und mit promises arbeitet, für https und http.

          Damit kann ich dann z.B.:

          pSst('xxxx id',true)

          .then(() => wait(1000))

          .then(() => pSst('xxxx id', false)

          .catch(e => _W(Error ${e});

          Willst du Promises in Javascript oder in Adaptern verwenden?

          p.s.: Bin draufgekommen dass einige der ioBroker-Funktionen ein SetTimeout mit einer ms einfügen! Ist irgendwo schon vorgekommen wo gar kein callback verfügbar ist, also die Funktion eigentlich synchron sein sollte, habs aber jetzt vergessen…

          Frank,

          NUC's, VM's und Raspi's unter Raspian, Ubuntu und Debian zum Testen.
          Adapter die ich selbst beigesteuert habe: BMW, broadlink2, radar2, systeminfo, km200, xs1 und einige im Anmarsch!

          1 Antwort Letzte Antwort
          0
          • BluefoxB Offline
            BluefoxB Offline
            Bluefox
            schrieb am zuletzt editiert von
            #5

            Die Promises sind nicht schlecht nur aus Erfahrung weiß ich, dass es Probleme zwischen 0.12, 4, 6 gibt.

            Es gibt bluebird, q, native promises und die unterscheiden sich.

            Manchmal darf man

            getState('aaa')
            .then(function(result){})
            .fail(function (e) {
            });
            

            schreiben.

            Manchmal

            getState('aaa')
            .then(function(result){})
            .fail(function (e) {
            }).catch(function () {
            });
            
            getState('aaa')
            .then(function(result){})
            .fail(function (e) {
            }).catch(function () {
            });
            

            manchmal

            getState('aaa')
            .then(function(result){})
            .fail(function (e) {
            }).fin(function () {
                // close files, database connections, stop servers, conclude tests
            });
            

            Und wenn so was wie

            getState('aaa')
            .then(function(result){ 
                throw new Error('error');
            });
            
            

            dann wirst du NIE dein Exception sehen und wirst SEHR lange debuggen, wo der Fehler ist.

            Wie gesagt, wenn jemand die gut findet, habe ich nichts dagegen.

            Nur sehe ich immer noch mehr Problemen als Nützlichkeit dabei. Ihr habt einfach noch nie ein problem mit Native => Q => Bluebird => throw new Error() gesucht… Ich schon.

            1 Antwort Letzte Antwort
            0
            • AlCalzoneA Offline
              AlCalzoneA Offline
              AlCalzone
              Developer
              schrieb am zuletzt editiert von
              #6

              @Bluefox:

              getState('aaa')
              .then(function(result){})
              .fail(function (e) {
              }).fin(function () {
                  // close files, database connections, stop servers, conclude tests
              });
              ```` `  
              

              fail/finally sind soweit ich weiß nicht im ECMAScript-Standard, nur then/catch werden unterstützt. Die ganzen Libraries können idR. mehr, welche da am sinnvollsten ist, weiß ich nicht. Ich wäre mit native schon glücklich.

              @Bluefox:

              Und wenn so was wie

              getState('aaa')
              .then(function(result){ 
                  throw new Error('error');
              });
              
              

              dann wirst du NIE dein Exception sehen und wirst SEHR lange debuggen, wo der Fehler ist. `
              Dieser Code müsste an entsprechender Stelle im JS-Adapter/adapter.js eingefügt werden, um genau diese versteckten Fehler in die Konsole zu leiten.

              // Unbehandelte Fehler tracen
              process.on('unhandledRejection', r => {
                  console.log("unhandled promise rejection: " + r);
              });
              
              

              Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

              1 Antwort Letzte Antwort
              0
              • AlCalzoneA Offline
                AlCalzoneA Offline
                AlCalzone
                Developer
                schrieb am zuletzt editiert von
                #7

                @fsjoke:

                Alcazone,

                ich verwende auch ausschließlich promises, in den Adaptern und im Javascriopt-Adapter bei scripts.

                […]

                Dort definiere ich von Zeile 20-~83 einige Funktionen mit denen ich:

                aus jeder Funtion […] eine Funktion machen kann die eine promise zurückgibt `
                Jap, sowas habe ich auch, wäre halt praktisch, wenn das von Hause aus gehen würde.

                @fsjoke:

                Willst du Promises in Javascript oder in Adaptern verwenden? `
                Primär in JavaScript, ich habe nur vor kurzem einen Thread bezüglich Adapter-Entwicklung gelesen, bei dem mir aufgefallen ist, dass getState dort asynchron ist. Daher würde sich das anbieten.

                Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

                1 Antwort Letzte Antwort
                0
                • BluefoxB Offline
                  BluefoxB Offline
                  Bluefox
                  schrieb am zuletzt editiert von
                  #8

                  @AlCalzone:

                  @Bluefox:

                  getState('aaa')
                  .then(function(result){})
                  .fail(function (e) {
                  }).fin(function () {
                      // close files, database connections, stop servers, conclude tests
                  });
                  ```` `  
                  

                  fail/finally sind soweit ich weiß nicht im ECMAScript-Standard, nur then/catch werden unterstützt. Die ganzen Libraries können idR. mehr, welche da am sinnvollsten ist, weiß ich nicht. Ich wäre mit native schon glücklich.

                  @Bluefox:

                  Und wenn so was wie

                  getState('aaa')
                  .then(function(result){ 
                      throw new Error('error');
                  });
                  
                  

                  dann wirst du NIE dein Exception sehen und wirst SEHR lange debuggen, wo der Fehler ist. `
                  Dieser Code müsste an entsprechender Stelle im JS-Adapter/adapter.js eingefügt werden, um genau diese versteckten Fehler in die Konsole zu leiten.

                  // Unbehandelte Fehler tracen
                  process.on('unhandledRejection', r => {
                      console.log("unhandled promise rejection: " + r);
                  });
                  
                  ```` `  
                  

                  Das geht nur wenn du native promises verwendest. Und was wenn 0.12 + bluebird?

                  0.12 will ich ende April rausschmeißen und dann kann man über so was nachdenken.

                  1 Antwort Letzte Antwort
                  0
                  • frankjokeF Offline
                    frankjokeF Offline
                    frankjoke
                    schrieb am zuletzt editiert von
                    #9

                    Bluefox,

                    ja, ich verwende nur die native nodejs promises und seit 4 gibts da keine Probleme. Vesuche generell keine Zusatzmodule einzusetzten da diese zusätzlichen Arbeistspeicher und Zeit (zumindest beim Laden) beanspruchen. Ich hasse Module die wegen zwei Abfragen z.B. lodash brauchen.

                    Habe deswegen (mittels den eigenen Promises-Routinen) auch z.B. das Async-package aus allen Adaptern und Scripten entfernt und versuche ausschließlich in nodejs-integrierte Module zu verwenden. Deshalb hab ich mir auch mein pGet geschrieben um ohne dem request-Modul auszukommen das leider rausgeflogen ist.

                    Für den JS-Adapter wäre es wirklich nicht schlecht in die Sandbox einige Promisified-Funktionen einzubinden.

                    Es wäre auch nicht schlecht die Sandbox unter 'use strict'; laufen zu lassen da dies wesentlich mehr Programmierfehler aufdeckt.

                    Und zusätzlich wäre es toll diese 'Global script' in eine require-Funktion umzuwandeln. Also ich kann global scripts definieren die ich mit 'require' im script importiere anstatt alle Globals die aktiv sind vorne anzuhängen.

                    Frank,

                    NUC's, VM's und Raspi's unter Raspian, Ubuntu und Debian zum Testen.
                    Adapter die ich selbst beigesteuert habe: BMW, broadlink2, radar2, systeminfo, km200, xs1 und einige im Anmarsch!

                    1 Antwort Letzte Antwort
                    0
                    • AlCalzoneA Offline
                      AlCalzoneA Offline
                      AlCalzone
                      Developer
                      schrieb am zuletzt editiert von
                      #10

                      @Bluefox:

                      Das geht nur wenn du native promises verwendest. […]

                      0.12 will ich ende April rausschmeißen und dann kann man über so was nachdenken. `

                      Klingt nach einem Deal!

                      @fsjoke:

                      Und zusätzlich wäre es toll diese 'Global script' in eine require-Funktion umzuwandeln. Also ich kann global scripts definieren die ich mit 'require' im script importiere anstatt alle Globals die aktiv sind vorne anzuhängen. `
                      Gute Idee. Ist aber vermutlich ein breaking change für alle Nutzer der global scripts. Vielleicht stattdessen ein weiterer Ordner "Module"?

                      Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

                      1 Antwort Letzte Antwort
                      0
                      • frankjokeF Offline
                        frankjokeF Offline
                        frankjoke
                        schrieb am zuletzt editiert von
                        #11

                        Ja, Module gingen auch, ist nur wirklich blöd dass die globalen scripts immer angehängt werden, auch bei separaten Instanzen.

                        Ich habe seit ich meine StateMaschine verwende Gott sei Dank auf 2 Scripts (und eine Instanz) reduziert und auch node-red rausgeschmissen.

                        Aber beim Testen von globalen scripts stört es sehr dass alles neu gestartet wird.

                        Frank,

                        NUC's, VM's und Raspi's unter Raspian, Ubuntu und Debian zum Testen.
                        Adapter die ich selbst beigesteuert habe: BMW, broadlink2, radar2, systeminfo, km200, xs1 und einige im Anmarsch!

                        1 Antwort Letzte Antwort
                        0
                        • htreckslerH Offline
                          htreckslerH Offline
                          htrecksler
                          Forum Testing
                          schrieb am zuletzt editiert von
                          #12

                          > Also ich kann global scripts definieren die ich mit 'require' im script importiere anstatt alle Globals die aktiv sind vorne anzuhängen

                          Ist das ein großer Aufwand das zu tun? Ich nutzte sehr gerne die "global scripts" (zumindest am Anfang), aber irgendwann wurde mir das zu unübersichtlich.

                          Also habe ich mir in einem Texteditor quasi eine Funktionssammlung angelegt aus der ich einzelne Funktionen in ein Script "kopiere" (händisch)

                          Mich hat irgendwie gestört, das immer das gesamte globale Script vorangestellt wurde.

                          Es wäre toll wenn man einzelne Funktionsblöcke definieren und diese bei Bedarf in das Script laden könnte.

                          Gruss Hermann

                          ioBroker auf Proxmox (Debian) auf IntelNuc als Produktivsystem

                          1 Antwort Letzte Antwort
                          0
                          • AlCalzoneA Offline
                            AlCalzoneA Offline
                            AlCalzone
                            Developer
                            schrieb am zuletzt editiert von
                            #13

                            Um hierauf mal zurück zu kommen. Folgende Funktionen der Adapter-Klasse habe ich aktuell als Promises gekapselt:

                            adapter.objects.getObjectList
                            adapter.getForeignObject     
                            adapter.setForeignObject     
                            adapter.getForeignObjects    
                            adapter.getForeignState      
                            adapter.getObject            
                            adapter.setObject            
                            adapter.getState             
                            adapter.setState             
                            adapter.createState          
                            

                            extendObject wäre in dieser Liste vermutlich auch gut aufgehoben.

                            Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

                            1 Antwort Letzte Antwort
                            0
                            • frankjokeF Offline
                              frankjokeF Offline
                              frankjoke
                              schrieb am zuletzt editiert von
                              #14

                              Hallo mitsammen!

                              Verwende ausschließlich Promises in meinen Adaptern, und dabei die internen ab node 4.3 hatte ich nie Probleme.

                              Um die Adapterentwicklung zu vereinfachen hab ich mir ein Skelett erstellt welche die meisten verwendeten Prozeduren in Promises umwandelt und auch einige angern (wie http/https get, exec, …).

                              Es schaut ca so aus:

                              ! ````
                              "use strict";
                              const util = require('util');
                              const http = require('http');
                              const https = require('https');
                              const exec = require('child_process').exec;
                              const querystring = require('querystring');
                              const assert = require('assert');
                              ! function _O(obj, level) { return util.inspect(obj, false, level || 2, false).replace(/\n/g, ' '); }
                              function _N(fun) { return setTimeout.apply(null, [fun, 0].concat(Array.prototype.slice.call(arguments, 1))); } // move fun to next schedule keeping arguments
                              function _D(l, v) { adapter.log.debug(l); return v === undefined ? l : v; } // version for final adapter, use below for testing
                              //function _D(str, val) { adapter.log.info(debug: ${str}); return val !== undefined ? val : str; } // Write debug message in log, optionally return 2nd argument
                              function _I(l, v) { adapter.log.info(l); return v === undefined ? l : v; }
                              function _W(l, v) { adapter.log.warn(l); return v === undefined ? l : v; }
                              function _T(i) {
                              var t = typeof i; if (t === 'object') {
                              if (Array.isArray(i)) t = 'array';
                              else if (i instanceof RegExp) t = 'regexp';
                              else if (i === null) t = 'null';
                              } else if (t === 'number' && isNaN(i)) t = 'NaN';
                              return t;
                              }
                              ! const P = {
                              res: (what) => Promise.resolve(what),
                              rej: (what) => Promise.reject(what),
                              wait: (time, arg) => new Promise(res => setTimeout(res, time, arg)),
                              ! series: (obj, promfn, delay) => { // fun gets(item) and returns a promise
                              assert(typeof promfn === 'function', 'series(obj,promfn,delay) error: promfn is not a function!');
                              delay = delay || 0;
                              let p = Promise.resolve();
                              const nv = [],
                              f = (k) => p = p.then(() => promfn(k).then(res => P.wait(delay, nv.push(res))));
                              for (let item of obj)
                              f(item);
                              return p.then(() => nv);
                              },
                              ! c2p: (f) => {
                              assert(typeof f === 'function', 'c2p (f) error: f is not a function!');
                              if (!f)
                              throw new Error(f = null in c2pP definition!);
                              return function() {
                              const args = Array.prototype.slice.call(arguments);
                              return new Promise((res, rej) => {
                              args.push((err, result) => (err && _N(rej, err)) || _N(res, result));
                              f.apply(this, args);
                              });
                              }
                              },
                              ! c1p: (f) => {
                              assert(typeof f === 'function', 'c1p (f) error: f is not a function!');
                              return function() {
                              const args = Array.prototype.slice.call(arguments);
                              return new Promise((res, rej) => {
                              args.push((result) => _N(res, result));
                              f.apply(this, args);
                              });
                              };
                              },
                              ! retry: (nretry, fn, arg) => {
                              return fn(arg).catch(err => {
                              if (nretry <= 0)
                              throw err;
                              return P.retry(nretry - 1, fn,arg);
                              });
                              },

                              repeat: (nretry, fn, arg) => {
                                  return fn(arg).then(() => Promise.reject()).catch(err => { 
                                      if (nretry <= 0)
                                          return Promise.resolve();
                                      return P.repeat(nretry - 1, fn,arg); 
                                  });
                              },
                              

                              ! exec: (command) => {
                              const istest = command.startsWith('!');
                              return new Promise((resolve, reject) => {
                              exec(istest ? command.slice(1) : command, (error, stdout, stderr) => {
                              if (istest && error) {
                              error[stderr] = stderr;
                              return reject(error);
                              }
                              resolve(stdout);
                              });
                              });
                              },
                              ! get: (url,retry) => { // get a web page either with http or https and return a promise for the data, could be done also with request but request is now an external package and http/https are part of nodejs.
                              const fun = typeof url === 'string' && url.trim().toLowerCase().startsWith('https') ||
                              url.protocol == 'https' ? https.get : http.get;
                              return (new Promise((resolve,reject)=> {
                              fun(url, (res) => {
                              const statusCode = res.statusCode;
                              const contentType = res.headers['content-type'];
                              if (statusCode !== 200) {
                              const error = new Error(Request Failed. Status Code: ${statusCode});
                              res.resume(); // consume response data to free up memory
                              return reject(error);
                              }
                              res.setEncoding('utf8');
                              var rawData = '';
                              res.on('data', (chunk) => rawData += chunk);
                              res.on('end', () => resolve(rawData));
                              }).on('error', (e) => reject(e));
                              })).catch(err => {
                              if (!retry) reject(err);
                              return P.wait(100,retry -1).then(a => P.get(url,a));
                              });
                              },
                              ! initAdapter: () => {
                              P.getObjectList = P.c2p(adapter.objects.getObjectList),
                              P.getForeignObject = P.c2p(adapter.getForeignObject),
                              P.setForeignObject = P.c2p(adapter.setForeignObject),
                              P.getForeignObjects = P.c2p(adapter.getForeignObjects),
                              P.getForeignState = P.c2p(adapter.getForeignState),
                              P.getState = P.c2p(adapter.getState),
                              P.setState = P.c2p(adapter.setState),
                              P.getObject = P.c2p(adapter.getObject),
                              P.deleteState = P.c2p(adapter.deleteState),
                              P.delObject = P.c2p(adapter.delObject),
                              P.setObject = P.c2p(adapter.setObject),
                              P.createState = P.c2p(adapter.createState),
                              P.extendObject = P.c2p(adapter.extendObject);
                              }

                              }

                              ! var isStopping = false;
                              var scanTimer = null;
                              ! function stop(dostop) {
                              isStopping = true;
                              if (scanTimer)
                              clearInterval(scanTimer);
                              scanTimer = null;
                              _W('Adapter disconnected and stopped');
                              }
                              ! adapter.on('message', obj => processMessage(obj));
                              ! adapter.on('ready', () => main(P.initAdapter()));
                              ! adapter.on('unload', () => stop(false));
                              ! function processMessage(obj) {
                              if (obj && obj.command) {
                              _D(process Message ${_O(obj)});
                              switch (obj.command) {
                              case 'ping': {
                              // Try to connect to mqtt broker
                              if (obj.callback && obj.message) {
                              ping.probe(obj.message, { log: adapter.log.debug }, function (err, result) {
                              adapter.sendTo(obj.from, obj.command, res, obj.callback);
                              });
                              }
                              break;
                              }
                              }
                              }
                              adapter.getMessage(function (err, obj) {
                              if (obj) {
                              processMessage(obj);
                              }
                              });
                              }
                              ! ````

                              P.c2p erzeugt eine Promisifierte Version einer Funktion die fun(…, callback(error, data)) also ein callback mit error == null erwartet.

                              Bei callbacks ohne error nehm ich c1p .

                              repeat, rety und series sind konstrukte die andere Promises hintereinander ausführen.

                              exec und get verwend ich oft in allen Adaptern.

                              Beim adapter.ready wird main() aufgerufen und vorher die ioBroker adapterfunktionen mit c2p erstellt da einige von uhnen noch nicht bereit sind wenn der Adapter nicht gestartet ist.

                              Damit brauch ich nur die 2 Zeilen einfügen um utils und den Adapter zu laden und kann dann mit dem Content loslegen.

                              Frank,

                              NUC's, VM's und Raspi's unter Raspian, Ubuntu und Debian zum Testen.
                              Adapter die ich selbst beigesteuert habe: BMW, broadlink2, radar2, systeminfo, km200, xs1 und einige im Anmarsch!

                              1 Antwort Letzte Antwort
                              0
                              • AlCalzoneA Offline
                                AlCalzoneA Offline
                                AlCalzone
                                Developer
                                schrieb am zuletzt editiert von
                                #15

                                Bin inzwischen auch hingegangen und ähnlich wie du im ready-Callback das Adapter-Objekt um promisifizierte Funktionen, farbigen Log, etc. erweitert, siehe https://github.com/AlCalzone/iobroker.t … /global.ts

                                Gleichzeitig nutze ich diesen Import um das Adapter-Objekt in eingebundenen (projektinternen) Modulen verfügbar zu machen ohne jedesmal die Variable zu übergeben.

                                Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

                                1 Antwort Letzte Antwort
                                0
                                • frankjokeF Offline
                                  frankjokeF Offline
                                  frankjoke
                                  schrieb am zuletzt editiert von
                                  #16

                                  Ahh, du bist schon einen Schritt weiter: Typescript!

                                  Ich habe bei meinen neuesten Adapter meine defaults etwas geändert und in ein Objekt konzentriert:

                                  ! ```
                                  const A = { // my Adapter object encapsulating all my default adapter variables/functions and Promises isStopping: false, scanDelay: 5 * 60 * 1000, // in ms = 5 min scanTimer: null, ! stop: (dostop) => { A.isStopping = true; if (A.scanTimer) clearInterval(A.scanTimer); A.scanTimer = null; if (adapter && adapter.log && adapter.log.warn) A.W(Adapter disconnected and stopped with (${dostop})); if(dostop) { A.E("Adapter will exit in lates 2 sec!"); setTimeout(process.exit,2000,55); } }, ! res: (what) => Promise.resolve(what), rej: (what) => Promise.reject(what), wait: (time, arg) => new Promise(res => setTimeout(res, time, arg)), ! O: (obj, level) => util.inspect(obj, false, level || 2, false).replace(/\n/g, ' '), ! // function _J(str) { try { return JSON.parse(str); } catch (e) { return {'error':'JSON Parse Error of:'+str}}} N: (fun) => setTimeout.apply(null, [fun, 0].concat(Array.prototype.slice.call(arguments, 1))), // move fun to next schedule keeping arguments D: (l, v) => (adapter.log.debug(l), v === undefined ? l : v), // D: (str, val) => (adapter.log.info(debug: ${str}), val !== undefined ? val : str), // Write debug message in log, optionally return 2nd argument I: (l, v) => (adapter.log.info(l), v === undefined ? l : v), W: (l, v) => (adapter.log.warn(l), v === undefined ? l : v), E: (l, v) => (adapter.log.error(l), v === undefined ? l : v), T: (i) => { var t = typeof i; if (t === 'object') { if (Array.isArray(i)) t = 'array'; else if (i instanceof RegExp) t = 'regexp'; else if (i === null) t = 'null'; } else if (t === 'number' && isNaN(i)) t = 'NaN'; return t; }, ! series: (obj, promfn, delay) => { // fun gets(item) and returns a promise assert(typeof promfn === 'function', 'series(obj,promfn,delay) error: promfn is not a function!'); delay = parseInt(delay); let p = Promise.resolve(); const nv = [], f = delay > 0 ? (k) => p = p.then(() => promfn(k).then(res => A.wait(delay, nv.push(res)))) : (k) => p = p.then(() => promfn(k)); for (let item of obj) f(item); return p.then(() => nv); }, ! c2p: (f) => { assert(typeof f === 'function', 'c2p (f) error: f is not a function!'); if (!f) throw new Error(f = null in c2pP definition!); return function () { const args = Array.prototype.slice.call(arguments); return new Promise((res, rej) => { args.push((err, result) => (err && rej(err)) || res(result)); f.apply(this, args); }); } }, ! c1p: (f) => { assert(typeof f === 'function', 'c1p (f) error: f is not a function!'); return function () { const args = Array.prototype.slice.call(arguments); return new Promise((res, rej) => { args.push((result) => res(result)); f.apply(this, args); }); }; }, ! c1pe: (f) => { // one parameter != null = error assert(typeof f === 'function', 'c1pe (f) error: f is not a function!'); return function () { const args = Array.prototype.slice.call(arguments); return new Promise((res, rej) => { args.push((result) => !result ? res(result) : rej(result)); f.apply(this, args); }); }; }, ! retry: (nretry, fn, arg) => { assert(typeof fn === 'function', 'retry (,fn,) error: fn is not a function!'); return fn(arg).catch(err => { if (nretry <= 0) throw err; return A.retry(nretry - 1, fn, arg); }); }, ! repeat: (nretry, fn, arg) => { assert(typeof fn === 'function', 'repeat (,fn,) error: fn is not a function!'); return fn(arg).then(() => Promise.reject()).catch(err => { if (nretry <= 0) return Promise.resolve(); return A.repeat(nretry - 1, fn, arg); }); }, ! exec: (command) => { assert(typeof fn === 'string', 'exec (fn) error: fn is not a string!'); const istest = command.startsWith('!'); return new Promise((resolve, reject) => { exec(istest ? command.slice(1) : command, (error, stdout, stderr) => { if (istest && error) { error[stderr] = stderr; return reject(error); } resolve(stdout); }); }); }, ! get: (url, retry) => { // get a web page either with http or https and return a promise for the data, could be done also with request but request is now an external package and http/https are part of nodejs. const fun = typeof url === 'string' && url.trim().toLowerCase().startsWith('https') || url.protocol == 'https' ? https.get : http.get; return (new Promise((resolve, reject) => { fun(url, (res) => { const statusCode = res.statusCode; const contentType = res.headers['content-type']; if (statusCode !== 200) { const error = new Error(Request Failed. Status Code: ${statusCode}); res.resume(); // consume response data to free up memory return reject(error); } res.setEncoding('utf8'); var rawData = ''; res.on('data', (chunk) => rawData += chunk); res.on('end', () => resolve(rawData)); }).on('error', (e) => reject(e)); })).catch(err => { if (!retry) reject(err); return A.wait(100, retry - 1).then(a => A.get(url, a)); }); }, ! initAdapter: () => { A.ains = adapter.name + '.' + adapter.instance; A.ain = A.ains + '.'; A.D(Adapter ${A.ains} starting.); A.getObjectList = A.c2p(adapter.objects.getObjectList), A.getForeignObject = A.c2p(adapter.getForeignObject), A.setForeignObject = A.c2p(adapter.setForeignObject), A.getForeignObjects = A.c2p(adapter.getForeignObjects), A.getForeignState = A.c2p(adapter.getForeignState), A.getState = A.c2p(adapter.getState), A.setState = A.c2p(adapter.setState), A.getObject = A.c2p(adapter.getObject), A.deleteState = (id) => A.c1pe(adapter.deleteState)(id).catch(res => res == 'Not exists' ? A.res() : A.rej(res)), A.delState = (id, opt) => A.c1pe(adapter.delState)(id, opt).catch(res => res == 'Not exists' ? A.res() : A.rej(res)), A.delObject = (id, opt) => A.c1pe(adapter.delObject)(id, opt).catch(res => res == 'Not exists' ? A.res() : A.rej(res)), A.removeState = (id, opt) => A.delState(id, opt).then(() => A.delObject(id, opt)), A.setObject = A.c2p(adapter.setObject), A.createState = A.c2p(adapter.createState), A.extendObject = A.c2p(adapter.extendObject); A.states = {}; (!adapter.config.forceinit ? A.res({ rows: [] }) : A.getObjectList({ startkey: A.ain, endkey: A.ain + '\u9999' })) .then(res => A.series(res.rows, (i) => A.removeState(A.D('deleteState: ' + i.doc.common.name, i.doc.common.name)), 2)) .then(res => res, err => A.E('err from A.series: ' + err)) .then(() => A.getObjectList({ include_docs: true })) .then(res => { res = res && res.rows ? res.rows : []; A.objects = {}; for (let i of res) A.objects[i.doc._id] = i.doc; if (A.objects['system.config'] && A.objects['system.config'].common.language) adapter.config.lang = A.objects['system.config'].common.language; if (A.objects['system.config'] && A.objects['system.config'].common.latitude) { adapter.config.latitude = parseFloat(A.objects['system.config'].common.latitude); adapter.config.longitude = parseFloat(A.objects['system.config'].common.longitude); } return res.length; }, err => A.E('err from getObjectList: ' + err, 'no')) .then(len => { A.D(${adapter.name} received ${len} objects with config ${Object.keys(adapter.config)}); // A.D('System Objects: '+A.O(A.objects,5)) adapter.subscribeStates('*'); return main(); }).catch(err => A.W(Error in adapter.ready: ${err})); }, ! changeState: function (id, value, ack, always) { assert(typeof id === 'string', 'changeState (id,,,) error: id is not a string!'); always = always === undefined ? false : !!always; ack = ack === undefined ? true : !!ack; return A.getState(id) .then(st => st && !always && st.val == value && st.ack == ack ? A.res() : A.setState(id, value, ack)) .catch(err => A.W(Error in A.setState(${id},${value},${ack}): ${err}, A.setState(id, value, ack))); }, ! makeState: function (ido, value, ack) { ack = ack === undefined || !!ack; let id = ido; if (typeof id === 'string') ido = id.endsWith('Percent') ? { unit: "%" } : {}; else if (typeof id.id === 'string') { id = id.id; } else return Promise.reject(A.W(Invalid makeState id: ${A.O(id)})); if (A.states[id]) return A.changeState(id, value, ack); // A.D(Make State ${id} and set value to:${A.O(value)} ack:${ack}) ///TC var st = { common: { name: id, // You can add here some description read: true, write: false, state: 'state', role: 'value', type: typeof value }, type: 'state', _id: id }; ! for (let i in ido) if (i != 'id' && i != 'val') st.common[i] = ido[i]; ! return A.extendObject(id, st, null) .then(x => A.states[id] = x) .then(() => st.common.state == 'state' ? A.changeState(id, value, ack) : A.res()) .catch(err => A.D(MS ${A.O(err)}, id)); }, ! processMessage: (obj) => { if (obj && obj.command) { A.D(process Message ${A.O(obj)}); switch (obj.command) { case 'ping': // Try to connect to mqtt broker if (obj.callback && obj.message) { ping.probe(obj.message, { log: adapter.log.debug }, function (err, result) { adapter.sendTo(obj.from, obj.command, res, obj.callback); }); } break; case 'send': // e.g. send email or pushover or whatever A.D(A.ains + ' send command from message'); if (obj.callback) // Send response in callback if required adapter.sendTo(obj.from, obj.command, 'Message received', obj.callback); break; } } adapter.getMessage((err, obj) => obj ? A.processMessage(obj) : null); } } ! adapter.on('message', obj => A.processMessage(obj)); ! adapter.on('ready', () => A.initAdapter()); ! adapter.on('unload', () => A.stop(false));
                                  ! Da ist das main() nur noch 20 Zeilen lang!
                                  ! Beim debuggen bin ich draufgekommen dass ich eine eigene Promisify für die Funktionen brauchen die nur ein (err) Argument im callback verwenden:
                                  ! deleteState, delObject, delState.
                                  ! Hab auch alle drei geändert um den 'Not exist'-Fehler nicht als reject zu sehen.[/i][/i]

                                  Frank,

                                  NUC's, VM's und Raspi's unter Raspian, Ubuntu und Debian zum Testen.
                                  Adapter die ich selbst beigesteuert habe: BMW, broadlink2, radar2, systeminfo, km200, xs1 und einige im Anmarsch!

                                  1 Antwort Letzte Antwort
                                  0
                                  • AlCalzoneA Offline
                                    AlCalzoneA Offline
                                    AlCalzone
                                    Developer
                                    schrieb am zuletzt editiert von
                                    #17

                                    @fsjoke:

                                    Ahh, du bist schon einen Schritt weiter: Typescript! `
                                    Ja: https://github.com/ioBroker/ioBroker.template-ts

                                    Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

                                    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
                                    FAQ Cloud / IOT
                                    HowTo: Node.js-Update
                                    HowTo: Backup/Restore
                                    Downloads
                                    BLOG

                                    615

                                    Online

                                    32.5k

                                    Benutzer

                                    81.7k

                                    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