NEWS
Modulare Skripe
-
Hallo alle!
Meine Skriptsammlung ist mittlerweile recht groß und ich würde sie gerne in Modulen verwalten. Ein Modul soll hierbei folgendes können
- Persistenten Zustand haben (zumindest bis zum Adapter-Neustart)
- Interne Datenstrukturen kapseln und vor dem "Nutzer" verstecken
- Nur bestimmte Funktionalität für andere Module und Skripte anbieten
- Eigene subscriptions/schedules für die interne Funktionalität haben
Ein Modul sieht bei mir so aus:
const someModule = function() { const internaldata1 = { }; // ... doSomeFancyInitialization(); on("x", ...) on("x", ...) // ... return { function1() { }, function2() { } } }();
Nun habe ich die Möglichkeit, dieses Modul entweder Global oder als normales Skript zu speichern und beides ist mit Problemen behaftet:
- Als normales Skript sind someModule.function1 und someModule.function2 nicht von anderen Skripten erreichbar
- Als globales Skript hat jedes normale Skript eine eigene Instanz des Moduls (Code wird einfach "davor" gehangen) und ich habe keinen zentralen State
Folgendes kam mir in den Sinn, ich hätte hier aber gerne die Meinung der Experten:
- Alle internen Daten als Datenpunkte speichern und alle Modulinstanzen subscriben sich darauf um ihren internen Zustand synchron zu halten
- Bekommt man dann alles synchron? Ich könnte mir vorstellen dass das eine komplexe Aufgabe ist
- Alle Module als echte node-Module direkt in node_modules speichern
- Kann ich dann aus meinen Modulen heraus die ioBroker-Funktionen getState etc. nutzen?
Für Input bin ich sehr dankbar!
-
@azamir sagte in Modulare Skripe:
Kann ich dann aus meinen Modulen heraus die ioBroker-Funktionen getState etc. nutzen?
Nein, habe es gerade getestet mit folgendem Modul test.js:
'use strict'; module.exports = function (id) { return getState(id); };
mit diesem Aufruf
const test = require('../../../iobroker-data/modules/test.js'); log(test('javascript.1.test.array1'));
erhalte ich folgende Fehlermeldungen:
javascript.1 2019-12-21 19:50:19.705 error (6331) at module.exports (/opt/iobroker/iobroker-data/modules/test.js:3:4) javascript.1 2019-12-21 19:50:19.705 error (6331) ReferenceError: getState is not defined
-
@paul53 Vielen Dank für den schnellen Test. Ich habe selber noch keine Node-Module geschrieben. Hätte ich gewusst dass das so einfach ist, hätte ich es auch selber tun können Sorry
Vielleicht wird getState ja von irgendeinem Modul zur Verfügung gestellt, das man wiederum in seinem Modul per require einbinden muss (und hoffentlich kann)?
-
Hi, also
was du hier vor hast habe ich in meinen telegram npm paket indirekt eigentlich umgesetzt,
das nutze ich um dynamisch Menüs in Telegram zu generierenIch übergebe im JavaScript -Adapter in einem initialen javascript einfach an meinem NPM Paket die JavaScript Instanz
und habe dann im Modul vollen Zugriff auf alle iobroker funktionen
Um das jetzt der korrekte weg ist kann ich dir aber nicht sagen.
wie ich das umgesetzt habe kannst du hier schauen :https://github.com/Nahasapeemapetilon/MyTelegramMenu
viele grüße -
@azamir sagte in Modulare Skripe:
Kann ich dann aus meinen Modulen heraus die ioBroker-Funktionen getState etc. nutzen?
Nur wenn du deine Module jeweils in eine Funktion kapselst, der du diese Funktionen beim Laden übergibst, etwa so:
// modul.js module.exports = function(getState, ... /* weitere benötigte Funktionen */) { // hier der Modul-Code } // skript.js const modul = require("../pfad/zum/modul.js")(getState, ... /* weitere benötigte Funktionen */);
-
Ich möchte den Thread nicht kapern, wollte selber einen solchen eröffnen.
Habe vom scripten leider viel zu wenig Ahnung und wollte für die Nutzung von tts via sonos ein "Modul" schreiben, dem man irgendwie bei anderen Aufrufen einige Parameter übergeben kann.
Deswegen habe ich die Variablen verwendet.https://github.com/Homoran/iobroker.sonos_api#beispiel-fenster-offen-meldung
Der untere Teil mit der Übergabe an die Sonos api sollte ein "Modul" werden.
Wie kann man so etwas verwenden
- "Modul" sichern
- Werte durch ein anderes Skript übergeben und das Modul aufrufen
-
@Homoran sagte in Modulare Skripe:
Werte durch ein anderes Skript übergeben und das Modul aufrufen
Die Essenz steht direkt über deinem Post Der Trick liegt im Export einer Funktion sowie dem Aufrufen dieser Funktion beim Laden (Klammern hinter require("...")).
-
@AlCalzone sagte in Modulare Skripe:
Die Essenz steht direkt über deinem Post
Hab ich fast befürchtet
Dabrauche ich aber noch Jahrzehnte bis ich das verstehe - ich werde mich bemühen.
-
Ich stehe vor dem selben Problem und experimentiere gerade mit einer Lösung über eval (ich weiß, das ist eigentlich evil). Als globale Funktion habe ich ein load Skript, dass wir folgt aussieht
const loadCache = {}; function load (filename) { if (!loadCache[filename]) { const path = require("path"); const fs = require("fs"); const fn = path.resolve("opt", "iobroker", "iobroker-scripts", "scripts", "Modules", filename) + ".js"; const source = fs.readFileSync(fn, "utf8"); var module = {}; eval(source); loadCache[filename] = module.exports; } return loadCache[filename]; };
In meinen Skripten kann ich miit einem einfachen
var meinModul = load("mein_modul");
die Datei /opt/iobroker/iobroker-scripts/scripts/Modules/mein_modul.js laden und deren "module.exports"-Wert bekommen. Offene Ideen sind im Moment noch:
- Das globale require überladen, damit es natürlicher aussieht
- Den Pfad irgendwie zur Laufzeit bestimmen, damit er nicht hart eingebrannt ist
Aber zumindest läuft es erstmal und ich kann anfangen, etwas Ordnung zu schaffen.
-
@zerraxys Bedeutet das dann aber nicht, dass der loadCache in jeden Skript einzeln gefüllt wird?
-
@Nahasapee Vielen Dank, ich schaue mir das mal genauer an und poste dann hier meine Erkenntnisse und was ich daraus gemacht habe.
-
Ich habe ein bisschen rumprobiert und folgendes getan:
- Verzeichnis
/opt/iobroker/node_modules/ioBroker.javascript/node_modules/mymodules
angelegt npm init
darin ausgeführt- Folgendes in die
index.js
geschrieben
let modules = {}; let instance = null; let initialized = null; // above only boilerplate code modules.inspect = function() { return { initialized() { return initialized; }, instance() { return instance; } } }() // below only boilerplate code module.exports = function(js_instance) { if (!instance) { console.log("Initializing modules"); initialized = new Date(); instance = js_instance; } return modules; }
mymodules
in der Adapterkonfiguration zu den benötigten Modulen hinzugefügt- Um zu prüfen, ob meine Datenstrukturen auch wirklich nur einmal initialisiert werden, habe ich zwei identische Skripte mit folgendem Inhalt angelegt:
const mymodules = require('mymodules')(this); console.log(mymodules.inspect.initialized());
Beide Skripte geben den gleichen Zeitstempel aus, wodurch ich davon ausgehe dass die Initialisierung tatsächlich nur einmal stattgefunden hat. Ich kann nun (und man möge mich bitte korrigieren wenn ich einen Denkfehler gemacht habe!) an beliebigen Stellen in meinen Skripten - vorzugsweise am Anfang wo
this
noch ganz eindeutig definiert ist - mit einemconst mymodules = require('mymodules')(this);
auf meine Module zugreifen
Nächste Schritte:
- Module selber auch wieder per
require
in das "Obermodul" einbinden - Irgendwie so hosten, dass ich bequeme Versionsverwaltung habe aber es trotzdem nicht öffentlich machen muss (Hat jemand eine Idee?)
Danke für eure guten Ideen und ich bin immer offen für Verbesserungsvorschläge!
- Verzeichnis
-
@azamir Ja, ich denke das ist der Fall. Aber Skripte laufen doch auch so unabhängig voneinander. Ich wüsste nicht, wie man Daten zwischen denen teilen kann.