<- Startseite / Anleitungen / Smarthome / ioBroker

ioBroker - FAQ Adapter Entwicklung

Kurze Antworten zu Fragen, die sich bei der Adapterentwicklung immer wieder stellen. Gesammelte Infos aus der ioBroker Development Gruppe, der Doku und eigenen Erfahrungen.

Passwort verschlüsselt speichern

Beispiel aus dem Circuit Adapter (angepasste Version vom ioBroker.meross Adapter von Apollon77).

Das Passwort wird im Ciruit Adapter unter adapter.config.client_secret gespeichert. Unter index-m.html (HTML Seite für die Adaptereinstellungen) lautet die ID id="client_secret".

Passwort verschlüsseln, Anpassungen main()

decrpyt() Funktion am Anfang der main.js:

function decrypt(key, value) {
 let result = "";
 for (let i = 0; i < value.length; ++i) {
  result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i));
 }
 adapter.log.debug("client_secret decrypt ready");
 return result;
}
1
2
3
4
5
6
7
8

Im Adapter-Objekt wird die Funktion onReady() angesprungen, sobald der Adapter bereit zum Start ist:

const adapter = utils.adapter({
    name: "circuit",

    // The ready callback is called when databases are connected and adapter received configuration.
    // start here!
    ready: onReady, // Main method defined below for readability
1
2
3
4
5
6

Funktion onReady(). Von dort wird dann die eigentliche Hauptfunktion (hier main()) aufgerufen, sobald das Passwort entschlüsselt wurde:

//*********************************************************************
//* onReady (Adapter Ready)
//*********************************************************************

// entschlüsselt das gespeicherte Secret und startet main()

function onReady() {
 adapter.log.debug("ready - Adapter: databases are connected and adapter received configuration");
 adapter.log.silly("config.client_secret verschlüsselt: " + adapter.config.client_secret);

 adapter.getForeignObject("system.config", (err, obj) => {
  if (obj && obj.native && obj.native.secret) {
   //noinspection JSUnresolvedVariable
   adapter.config.client_secret = decrypt(obj.native.secret, adapter.config.client_secret);
  } else {
   //noinspection JSUnresolvedVariable
   adapter.config.client_secret = decrypt("Zgfr56gFe87jJOM", adapter.config.client_secret);
  }
  main();
 });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Bei aktuellen ioBroker Versionen wird mit dem individuellen Secret der Installation (obj.native.secret) verschlüsselt. Bei älteren Versionen wird in dem Beispiel der Schlüssel Zgfr56gFe87jJOMverwendet.

Passwort verschlüsseln, Anpassungen index_m.html

Neue Funktionen encrypt(), decrypt() und Austausch der Funktion loadHelper() aus dem Adapter-Template:

<script type="text/javascript">

  var secret;
     function encrypt(key, value) {
         var result = '';
         for(var i = 0; i < value.length; ++i) {
             result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i));
         }
         return result;
     }
     function decrypt(key, value) {
         var result = '';
         for(var i = 0; i < value.length; ++i) {
             result += String.fromCharCode(key[i % key.length].charCodeAt(0) ^ value.charCodeAt(i));
         }
         return result;
     }


  // the function loadSettings has to exist ...
  function loadHelper(settings, onChange) {
      // example: select elements with id=key and class=value and insert value
         if (!settings) return;
         if (settings.electricityPollingInterval === undefined) settings.electricityPollingInterval = 20;
         $('.value').each(function () {
             var $key = $(this);
             var id = $key.attr('id');
             if (id === 'client_secret') {
                 settings[id] = decrypt(secret, settings[id]);
             }
             if ($key.attr('type') === 'checkbox') {
           // do not call onChange direct, because onChange could expect some arguments
                 $key.prop('checked', settings[id]).change(function() {
                     onChange();
                 });
             } else {
           // do not call onChange direct, because onChange could expect some arguments
                 $key.val(settings[id]).change(function() {
                     onChange();
                 }).keyup(function() {
                     onChange();
                 });
             }
         });
   onChange(false);
   // function Materialize.updateTextFields(); to reinitialize all the Materialize labels on the page if you are dynamically adding inputs.
         M.updateTextFields();
     }



  // This will be called by the admin adapter when the settings page loads
     function load(settings, onChange) {
         socket.emit('getObject', 'system.config', function (err, obj) {
             secret = (obj.native ? obj.native.secret : '') || 'Zgfr56gFe87jJOM';
             loadHelper(settings, onChange);
   });
   onChange(false);
     }

  // This will be called by the admin adapter when the user presses the save button 
  function save(callback) {
         // example: select elements with class=value and build settings object
         var obj = {};
         $('.value').each(function () {
             var $this = $(this);
             var id = $this.attr('id');
             if ($this.attr('type') === 'checkbox') {
                 obj[id] = $this.prop('checked');
             } else {
                 var value = $this.val();
                 if (id === 'client_secret') {
                     value = encrypt(secret, value);
                 }
                 obj[id] = value;
             }
         });

   callback(obj);
  }
 </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

Das Eingabefeld für das Passwort (oder hier: client_secret):

<div class="col s6 input-field">
    <input type="password" class="value" id="client_secret" />
    <label for="client_secret" class="translate">Client Secret</label>
   </div>
1
2
3
4

Adapter soll sich selbst beenden

Der eigene Adapter kann sich selbst beenden (gestoppt werden), wenn man "system.adapter." + adapter.namespace + ".alive" auf false setzt.

Die Funktion wird mit adapter.setForeignState("system.adapter." + adapter.namespace + ".alive", false); aufgerufen.

geplant zu Adapter soll sich selbst beenden

Adapter wird beendet, startet aber von selbst wieder neu, wenn der ANwender in den Einstellungen was ändert:

adapter.terminate(reason)
1
adapter.stop()
1

disable ist im Gegensatz zu stop() aber garantiert immer definiert:

adapter.disable()
1

Adapter startet sich selbst neu

Geplant:

adapter.restart()
1

Aktuell:

Quelle: https://github.com/foxriver76/ioBroker.xbox/blob/275e03635d657e7b18762166b4feca96fc4b1b1c/main.js#L630

Adapter aus sich selbst heraus neustarten

Github: Funktion aus dem xbox Adapter

function restartAdapter() {
    adapter.getForeignObject('system.adapter.' + adapter.namespace, (err, obj) => {
        if (obj) adapter.setForeignObject('system.adapter.' + adapter.namespace, obj);
    });
} // endFunctionRestartAdapter
1
2
3
4
5

Neue Adapterinstanz gestoppt installieren

Im Standard wird eine neue Adapterinstanz direkt gestartet. Möchte man eine neue Instanz im Stop-Zustand hinzufügen, muss in der io-package.json der Wert "common.enabled=false" gesetzt werden.

Namespace Adapter

adapter.namespace gibt den Adapternamen inkl. der Instanz aus, z.B. circuit.0.

Verbindungszustand des Adapters unter Instanzen anzeigen

Unter Admin/Adapter/Instanzen wird mit einem farbigen Kreis der Zustand eines Adapters angezeigt.

Farbe Bedeutung der Farbe
grau Adapter ist ausgeschaltet
grün Adapter ist aktiv und funktioniert
gelb Adapter ist aktiv, hat aber keine Verbindung zum verwendeten Dienst
rot Adapter ist angeschaltet, ist aber ohne Funktion (Fehler)

Die Zustände grau, grün und rot werden grundsätzlich von ioBroker über den js-controller bestimmt. Den Verbindungszustand gelb zu einem Dienst, den der Adapter bedienen möchte, muss vom Entwickler des Adapters eingebaut werden.

Der Verbindungszustand wird über das Objekt info.connectionsignalisiert, welches man in der io-packages.json einrichten und dann im Adapter, je nach Zustand, ändern kann.

Part innerhalb der io-packages.json:

{
    "common": {
    "instanceObjects": [{
        "_id": "info.connection",
        "type": "state",
        "common": {
            "role": "indicator.connected",
            "name": "If communication with circuit works",
            "type": "boolean",
            "read": true,
            "write": false,
            "def": false
        },
        "native": {}
    }]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Beispiel für den Verbindungszustand in der main.js aus dem Circuit Adapter:

        client.addEventListener("connectionStateChanged", function (evt) {

            if(evt.state === "Connected") {
                adapter.setState("info.connection", true, true);
                adapter.log.debug("Adapterfarbe: grün");
            } else {
                adapter.setState("info.connection", false, true);
                adapter.log.debug("Adapterfarbe: gelb");
            }
        });
1
2
3
4
5
6
7
8
9
10

Das Beispiel passt für den Circuit Adapter

Achtung! Das Beispiel aus der main.jspasst genau für die Funktion vom Circuit SDK. An welcher Stelle der Status gesetzt wird hängt vom jeweiligem Adapter ab und muss vom Entwickler entsprechend eingebaut werden.

Die Beschreibung zu info.connection findet man in der ioBroker Doku.

Adapter Informationen unabhängig von config und states speichern

ungeprüft

Tipp wurde nicht geprüft.

1.)

Erstelle ein state.. "adapter.0.info.objects" dort speichern und lesen.

2.)

Best practice wäre ein State in info.wieauchimmer oder file im iobroker-Data/namespace. Siehe smartvisu bzw History Adapter Influxdb hat auch was drin wo nicht geschriebene Daten gespeichert und beim nächsten Start wieder geladen werden.

JSON Objekt zur Übersetzung erstellen

Unter https://translator.iobroker.in ist der ioBroker Translator erreichbar, der eine Unterstützung bei der Übersetzung in neun Sprachen bietet.

Der Text muss in Englisch eingegeben werden und wird dann automatisch per Google Translate in acht weiteren Sprachen übersetzt.

Das erstellte JSON Objekt kann in die Zwischenablage kopiert werden und dann z.B. in die io-packages.json und für den Admin in die Datei word.js an den entsprechenden Stellen einkopiert werden.

Den eingestellten Loglevel des Adapters abfragen

Den Loglevel, unter dem der eigene Adapter läuft kann man über adapter.log.levelabfragen.

admin_m.html

Grid

Letzte Änderung: 2019-6-16 8:40:16 PM