NEWS
Typprüfung mit javascript.d.ts
-
Bei der Deklaration mit var kann das vorkommen, das die Deklaration script übergreifender ist. Ist mir auch schon auf die Füße gefallen.
Nimm lieber let, das hat nur den lokalen scope.
Ich versuche mittlerweile alles nur noch mit let zu deklarieren (außer ich will bewusst den Scope vergrößern) und habe keine Probleme mehr.
let logFarbig = true;
Mit let kannst du die Gültigkeit einer Variablen auch nur auf einen Block eingrenzen. Also beispielsweise nur für eine for Schleife. `
Unabhängig von der Möglichkeit let zu verwenden…
Ich bin der Meinung, dass sich das Verhalten von vscode vorher nicht so war.
Hat jemand eine Idee, wie ich verhindern kann, das vscode Variablen, die in unterschiedlichen Skripten deklariert werden anmeckert.
Die Skripte sind für sich eigenständig und haben nichts miteinander zu tun.
Ich verwende:
/// <reference path="../javascript.d.ts"> /// <reference path="../javascript-global.d.ts"> // @ts-check</reference></reference>
javascript.d.ts" für den Javascript Adapter
javascript-global.d.ts für meine eigenen globalen Funktionen
Gefühlt zeitgleich mit dem Problem werden nun meine globalen Funktionen bei der Typehilfe nicht mehr korrekt angezeigt.
Die Variablennamen werden angezeigt, aber nicht mehr die Beschreibung und die richtigen Typen (immer any, als z.B. string).
Ich vermute, es ist etwas in der javascript-global.d.ts. Sehe aber nicht, was es sein könnte.
! ````
import child_process = require("child_process");
! // tslint:disable:no-namespace
declare global {namespace iobGlobal {
! type loglevel = "debug2" | "debug1" | "debug" | "info" | "warn" | "error";
}
! // Logikwerte:
// ===========================================
/**
* true/false als Ergebnis ob der aktuelle Tag ein Arbeitstag ist
/
function arbeitstag(): boolean;
! /*
* true/false als Ergebnis ob der aktuelle Tag ein Urlaubstag ist
/
function urlaubstag(): boolean;
! /*
* true wenn die aktuelle Zeit im angegebenen Zeitraum liegt
* @param strLower Startzeit formatiert als hh:mm:ss
* @param strUpper Endzzeit formatiert als hh:mm:ss
/
function isTimeInRange(strLower:string, strUpper:string): boolean;
! // Werte ausgeben
// ===========================================
/*
* gibt den Wochentag (Montag, Dienstag, ...) als Text zurück
/
function wochentag(): string;
! /*
* Gibt das Delta zwischen einem im Datenpunkt gespeicherten
* Epoch Wert und der aktuellen Zeit formatiert zurück.
*
* Zum speichern einer Zeit in einen Datenpunkt kann die Funktion
* dateEpochNow() verwendet werden.
/
function deltazeit(datenpunkt:string): string;
! /*
* gibt das aktuelle Datum & die Zeit formatiert aus
* Defaultformat: "YYYY-MM-DD hh:mm:ss"
* @param format Format der Ausgabe, Format wie bei formateDate()
* @param msBool true: ms im Format .ms werden angehängt
/
function dateNow(format?:string,msBool?:boolean): string;
! /*
* Gibt die Anzahl der gefundenen Subscriptions zurück.
* Listet alle Subscriptions innerhalb der Javascript-Instanz auf, die dem Filter entsprechen, im Log auf.
* Defaultformat: "YYYY-MM-DD hh:mm:ss"
* @param scriptname (optional) "alle", "Name des Skripts", "FEQ", usw.
* @param level (optional) loglevel für logs, default: "info"
* // Filter (scriptname):
* --------------------
* sucht den String in den überwachten Datenpunkten der Javascript-Instanz
* und sucht den String in den Scriptnamen innerhalb der Javascript-Instanz
* keine Angabe -> Filter = "alle" (alle Subscriptions der Instanz)
*
* loglevel
* --------
* loglevel = "debug2" | "debug1" | "debug" | "info" | "warn" | "error"
*
* Beispiele:
* ----------
* prinSubs(); listet alle Subscriptions innerhalb der Javascript-Instanz auf
* prinSubs("alle"); listet alle Subscriptions innerhalb der Javascript-Instanz auf
* printSubs("Skriptname"); alle Subscriptions innerhalb des aufrufenden Scripts
* printSubs("FEQ"); alle Subscriptions, die im Script oder Objekt "FEQ" enthalten
/
function printSubs(scriptname?:string,level?:iobGlobal.loglevel): number;
! /*
* Gibt eine zufällige Zahl zwischen min und max zurück
* @param min kleinster möglicher Wert der Zufahlszahl
* @param max größter möglicher Wert der Zufahlszahl
/
function rand(min:number,max:number) : number;
! // Funktionen
// ===========================================
! /*
* Rundet eine Zahl auf die angegebenen Stellen
* @param wert Zahl, die gerundet werden soll
* @param stellen Anzahl der Stellen
/
function runden(wert: number, stellen: number): number;
! /*
* Ansage über den sayit Adapter
* @param text Ansage, die abgespielt werden soll
* @param vol (optional) Lautstärke der Ansage von 1-100
* @param instanz? (optional) ohne Angabe: default Instanz. Mit Angabe: sayit Instanz
*/
function sayit(text: string, vol?: number, instanz?: number): void;/** * formatiert eine Übergabe in Sekunden zu einem lesbaren String * @param sekunden Sekunden, die lesbar ausgegeben werden sollen */ function sek2txt(sekunden:number | string): string;
! /**
* Loglevel innerhalb eines Javascripts
* das Script muss am die Variable des gewünschten Loglevels definiert haben, z.B.:
* var loglevel = "info";
* @param logtext Text, der in der Logausgabe erscheinen soll
* @param level "debug2" | "debug1" | "debug" | "info" | "warn" | "error"
* @param color? (optional) ohne Angabe: vordefinierte Farbe für den level, mit Angabe: gültige html Farbe
* Rückgabe: verwendeter Loglevel zur Information
/
function logs(logtext:string,level:iobGlobal.loglevel,color?:string):string;
! /*
* Schreibt einen Text mit Zeitstempel in eine Logdatei
* Datei: /opt/iobroker/iobroker-data/scripts-logf.log
* var loglevel = "info";
* @param text Text, der mit einem Zeitstempel in die Logdatei geschrieben werden soll
/
function logf(text:string);
! function event(text: string, list?: string) : void;
! // Helfer:
// ===========================================
/*
* liefere Anzahl n nbsp in utf-8, wenn str nicht angegeben oder n-mal str
* @param n Anzahl von nbsp;, bzw. wenn str angegeben: ANzahl des Strings in str
* @param str (optional) String, welche n mal erzeugt wird
/
function fill(n:number,str?:string): string;
! /*
* gibt das aktuelle Datum in Epoch zurück
/
function dateEpochNow() : number;
! /*
* entfernt aus einem html String die html Tags
*/
function html2str(html:string) : number;
! // Workaround, Fehler in der ioBroker Typprüfung
// ===========================================
function getSubscriptions(): any;
! const name: string;
! }
! ````[EDIT]
Das Thema mit den anmecker der "doppelten" Variablen lässt sich soweit eingrenzen, dass immer das Skript zählt, welches sich in einer höheren Verzeichnisebene befindet.
Beispiel:
1. Ebene: var id = [""]
2. Ebene: var id = "";
Dann meckert die Typprüfung, das in der Datei in der 2. Ebene ein anderer Typ verwendet wird (string statt string[]).
Die Skripte haben so nichts miteinander zu tun.
kann man das eventuell mit einer Einstellung in vscode beeinflussen?
-
Ich kenne deinen Code nicht, aber eigentlich sollte VS in einer JS-Datei keine Variablen sehen, die einer anderen Datei definiert wurden, egal wo sie liegt.
Dazu gibt es zwei Ausnahmen:
1. export/import durch Module
2. im globalen scope deklarierte Variablen (declare global { … })
Ich vermute dein Problem liegt an Punkt 2. Wenn die Skripte nichts miteinander zu tun haben und beide im globalen scope eine Variable unterschiedlich definieren, musst du dein Projekt möglicherweise anders strukturieren. Z.b. mit den Arbeitsbereichen, aber die hab ich selbst noch nicht benutzt und weiß nicht wie sich das verhält.
Kannst du mal ein Minimalbeispiel (Ordnerstruktur mit Dateiinhalten) erzeugen, damit ich dein Problem nachvollziehen kann?
-
Kannst du mal ein Minimalbeispiel (Ordnerstruktur mit Dateiinhalten) erzeugen, damit ich dein Problem nachvollziehen kann? `
Am schnellsten als Bild:
Im Verzeichnis Skripte liegen:
javascript.d.ts
javascript-global.d.ts
test.js
Test/test2.js
Der Inhalt javascript-global.d.ts von steht zwei Posts drüber (viewtopic.php?f=21&t=11777&p=139202#p139128).
test.js
/// <reference path="javascript.d.ts"> /// <reference path="javascript-global.d.ts"> // @ts-check // Datei: Skripte/test.js var hallo = "text";</reference></reference>
Test/test2.js
`/// <reference path="../javascript.d.ts"> /// <reference path="../javascript-global.d.ts"> // @ts-check // Datei: Skripte/Test/test2.js var hallo = [];[/code]</reference></reference>` Das Problem ist nicht nur, dass ich die in unterschiedlichen Skripten die Variablen anders verwenden (mal als String, mal als any (leeres Array))... (da könnte ich mir noch mit einer anderen Namensgebung helfen - Bezeichnung der Variablen) ...schlimmer ist, dass in einem anderen Skript deklarierte Variablen im neuen Skript nicht angemeckert werden, wenn sie dort vergessen wurden.
-
Kannst du mal ein Minimalbeispiel (Ordnerstruktur mit Dateiinhalten) erzeugen, damit ich dein Problem nachvollziehen kann? `
Und ein Minimalbeispiel für eine fehlende Meldung bei fehlender Deklaration:
filename="Beispiel Typeprüfung 2.png" index="0">~~
-
Hier die Erklärung für das Verhalten.
https://www.typescriptlang.org/docs/han … dules.html
` > Starting with ECMAScript 2015, JavaScript has a concept of modules. TypeScript shares this concept.Modules are executed within their own scope, not in the global scope; this means that variables, functions, classes, etc. declared in a module are not visible outside the module unless they are explicitly exported using one of the export forms. Conversely, to consume a variable, function, class, interface, etc. exported from a different module, it has to be imported using one of the import forms.
Modules are declarative; the relationships between modules are specified in terms of imports and exports at the file level.
Modules import one another using a module loader. At runtime the module loader is responsible for locating and executing all dependencies of a module before executing it. Well-known modules loaders used in JavaScript are the CommonJS module loader for Node.js and require.js for Web applications.
In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well). `
Da kannst du leider fast nichts dran ändern, das ist eine TypeScript-Eigenheit. Du kannst aber erzwingen, dass eine JS-Datei als Modul gehandhabt wird. Das geht fast genauso wie ich in der .d.ts-Datei gemacht habe, mit einem Dummy-Import (bzw. require).
-
Hier die Erklärung für das Verhalten.
Da kannst du leider fast nichts dran ändern, das ist eine TypeScript-Eigenheit. Du kannst aber erzwingen, dass eine JS-Datei als Modul gehandhabt wird. Das geht fast genauso wie ich in der .d.ts-Datei gemacht habe, mit einem Dummy-Import (bzw. require).
Unbenannt.PNG `
Ich danke Dir für Deine Mühe und Geduld! Wahnsinn!
Vor allem für die mitgelieferte Erklärung :!: :idea:
Werde dass dann mit dem "require("fs");" Dummy umgehen.
Du bist doch bestimmt tausendmal intensiver mit vscode unterwegs.
Organisierst Du Dein Skriptverzeichnis anders oder nutzt Du eine Namensgebung, mit der das nicht passieren kann?
Ich habe das Gefühl, bei mir ist noch viel Luft nach oben :mrgreen:
-
Bei dem Thema Typprüfung lass ich nicht locker :mrgreen:
Das Thema bringt mich einen Riesenschritt weiter, läuft aber an vielen Stellen noch nicht rund.
Das nächste "Problem".
Die Infos für die eigenen globalen Funktionen erscheinen erst, wenn der erste Parameter begonnen wurde zu tippen.
Bei den Funktionen aus dem Javascript-Adapter kommt die Hilfe direkt nach der Klammer.
Wie es jetzt bei mir ist, ist es nur bedingt hilfreich, siehe:
-
Hmm dazu müsste ich deine global-ts-Datei mal sehen. Eigentlich müsste das gleich funktionieren. Bei mir gehts:
Auf jeden Fall kannst du per STRG+Leertaste das Fenster auch manuell öffnen. -
Hmm dazu müsste ich deine global-ts-Datei mal sehen. Eigentlich müsste das gleich funktionieren. Bei mir gehts:
Auf jeden Fall kannst du per STRG+Leertaste das Fenster auch manuell öffnen. `
Die global-ts:
! ````
import child_process = require("child_process");
! // tslint:disable:no-namespace
declare global {namespace iobGlobal {
! type loglevel = "debug2" | "debug1" | "debug" | "info" | "warn" | "error";
}
! // Logikwerte:
// ===========================================
/**
* true/false als Ergebnis ob der aktuelle Tag ein Arbeitstag ist
/
function arbeitstag(): boolean;
! /*
* true/false als Ergebnis ob der aktuelle Tag ein Urlaubstag ist
/
function urlaubstag(): boolean;
! /*
* true wenn die aktuelle Zeit im angegebenen Zeitraum liegt
* @param strLower Startzeit formatiert als hh:mm:ss
* @param strUpper Endzzeit formatiert als hh:mm:ss
/
function isTimeInRange(strLower:string, strUpper:string): boolean;
! // Werte ausgeben
// ===========================================
/*
* gibt den Wochentag (Montag, Dienstag, ...) als Text zurück
/
function wochentag(): string;
! /*
* Gibt das Delta zwischen einem im Datenpunkt gespeicherten
* Epoch Wert und der aktuellen Zeit formatiert zurück.
* Der Datenpunkt wird danach aktualisiert.
* Zum speichern einer Zeit in einen Datenpunkt kann die Funktion
* dateEpochNow() verwendet werden.
* @param datenpunkt ID, in der die letzt Änderung im Epoche steht.
/
function deltazeit(datenpunkt:string): string;
! /*
* gibt das aktuelle Datum & die Zeit formatiert aus
* Defaultformat: "YYYY-MM-DD hh:mm:ss"
* @param format Format der Ausgabe, Format wie bei formateDate()
* @param msBool true: ms im Format .ms werden angehängt
/
function dateNow(format?:string,msBool?:boolean): string;
! /*
* Gibt die Anzahl der gefundenen Subscriptions zurück.
* Listet alle Subscriptions innerhalb der Javascript-Instanz auf, die dem Filter entsprechen, im Log auf.
* Defaultformat: "YYYY-MM-DD hh:mm:ss"
* @param scriptname (optional) "alle", "Name des Skripts", "FEQ", usw.
* @param level (optional) loglevel für logs, default: "info"
* // Filter (scriptname):
* --------------------
* sucht den String in den überwachten Datenpunkten der Javascript-Instanz
* und sucht den String in den Scriptnamen innerhalb der Javascript-Instanz
* keine Angabe -> Filter = "alle" (alle Subscriptions der Instanz)
*
* loglevel
* --------
* loglevel = "debug2" | "debug1" | "debug" | "info" | "warn" | "error"
*
* Beispiele:
* ----------
* prinSubs(); listet alle Subscriptions innerhalb der Javascript-Instanz auf
* prinSubs("alle"); listet alle Subscriptions innerhalb der Javascript-Instanz auf
* printSubs("Skriptname"); alle Subscriptions innerhalb des aufrufenden Scripts
* printSubs("FEQ"); alle Subscriptions, die im Script oder Objekt "FEQ" enthalten
/
function printSubs(scriptname?:string,level?:iobGlobal.loglevel): number;
! /*
* Gibt eine zufällige Zahl zwischen min und max zurück
* @param min kleinster möglicher Wert der Zufahlszahl
* @param max größter möglicher Wert der Zufahlszahl
/
function rand(min:number,max:number) : number;
! // Funktionen
// ===========================================
! /*
* Rundet eine Zahl auf die angegebenen Stellen
* @param wert Zahl, die gerundet werden soll
* @param stellen Anzahl der Stellen
/
function runden(wert: number, stellen: number): number;
! /*
* Ansage über den sayit Adapter
* @param text Ansage, die abgespielt werden soll
* @param vol (optional) Lautstärke der Ansage von 1-100
* @param instanz? (optional) ohne Angabe: default Instanz. Mit Angabe: sayit Instanz
*/
function sayit(text: string, vol?: number, instanz?: number): void;/** * formatiert eine Übergabe in Sekunden zu einem lesbaren String * @param sekunden Sekunden, die lesbar ausgegeben werden sollen */ function sek2txt(sekunden:number | string): string;
! /**
* Loglevel innerhalb eines Javascripts
* das Script muss am die Variable des gewünschten Loglevels definiert haben, z.B.:
* var loglevel = "info";
* @param logtext Text, der in der Logausgabe erscheinen soll
* @param level "debug2" | "debug1" | "debug" | "info" | "warn" | "error"
* @param color? (optional) ohne Angabe: vordefinierte Farbe für den level, mit Angabe: gültige html Farbe
* Rückgabe: verwendeter Loglevel zur Information
/
function logs(logtext:string,level:iobGlobal.loglevel,color?:string):string;
! /*
* Schreibt einen Text mit Zeitstempel in eine Logdatei
* Datei: /opt/iobroker/iobroker-data/scripts-logf.log
* @param text Text, der mit einem Zeitstempel in die Logdatei geschrieben werden soll
/
function logf(text:string);
! /*
* Fügt einen Eintrag (Text) einer Ereignisliste hinzu
* @param text Eintrag, der der Liste hinzugefügt werden soll. Formatierungen mit html Tags möglich
* @param list (optional) Liste, in der der Eintrag erscheinen soll. Ohne Angabe: Liste "default"
* @param color (optional) wenn die ganze Zeile mit einer html Farbe eingefärbt werden soll
/
function event(text: string, list?: string, color?: string) : void;
! // Helfer:
// ===========================================
/*
* liefere Anzahl n nbsp in utf-8, wenn str nicht angegeben oder n-mal str
* @param n Anzahl von nbsp;, bzw. wenn str angegeben: ANzahl des Strings in str
* @param str (optional) String, welche n mal erzeugt wird
/
function fill(n:number,str?:string): string;
! /*
* gibt das aktuelle Datum in Epoch zurück
/
function dateEpochNow() : number;
! /*
* entfernt aus einem html String die html Tags
*/
function html2str(html:string) : number;
! // Workaround, Fehler in der ioBroker Typprüfung
// ===========================================
function getSubscriptions(): any;
! const name: string;
! }
! ````STRG+Leertaste probiere ich direkt
-
So etwas getestet.
Neuer Ordner, zwei Testdateien angelegt (test.js und test.d.ts).
Es verhält sich schon anders als bei Dir, aber besser als in meiner Arbeitsumgebung.
Die Funktion wird während des tippens nicht vorgeschlagen.
Erst, wenn ich "Klammer auf" drücke erscheint die Beschreibung.
-
Die gleichen Dateien in einem Arbeitsordner in meinem Skript Arbeitsverzeichnis:
Wenn ich nun die "Klammer auf drücke, wird abc gegen AbortController( ersetzt.
Wenn ich das nicht will, muss ich direkt nach abc die [ESC] Taste drücken.
Du hast require("fs"); in der *.d.ts Datei gesetzt. Reicht das aus Deiner Sicht dort?
Ich hätte das jetzt bei jedem Skript davor gesetzt.
-
In meinem Arbeitsbereich verhält sich vscode total schizophren :shock:
tippe ich abc ein kommen andere Vorschläge.
Ich muss ESC drücken, damit mit der Klammer auf das abc nicht durch einen Vorschlag ersetzt wird.
In diesem Moment erscheint der Hilfetext zur Funktion abc().
Gleichzeitig wird abc unterstrichen und angemeckert, dass die Funktion unbekannt ist.
Verlasse ich einmal die Klammern () verschwindet der Hilfetext und tauch auch nicht mehr auf.
-
Schau dir mal meinen Screenshot bzw. javascript.d.ts genau an. Auch diese Datei muss in ein Modul "gezwungen" werden. Anders als bei JS reicht require nicht aus, es muss das import keyword fallen.
// entweder so import * as fs from "fs"; // oder so import fs = require("fs");
-
Hallo,
ich hab auch noch mal eine Frage…
Wenn ich am Anfang der Datei die Referenz auf die Definitionsdatei setze
///
klappt das im VS Code super, aber im IOBroker Admin bekomme ich beim Compilieren dann natürlich einen Fehler:
12:40:00.730 [error] javascript.0 script.js.common.xTest_ts: TypeScript compilation failed: /// ^ ERROR: File '/opt/iobroker/node_modules/iobroker.javascript/common/javascript.d.ts' not found.
Daher muss ich das vorher immer bearbeiten (mache aus den /// ein ////) oder habe Pech - wenn ich das vergesse. Da ich mit dem "js2fs" plugin arbeite muss ich das immer vor jedem Speichern im VS Code machen - wo das vergessen schon mal häufiger passiert.
Gibt es hier einen besseren Weg?
Danke & Gruß
Robert
-
Du arbeitest scheinbar mit TypeScripts.
Der TypeScript-Modus lädt die Datei automatisch für dich, da sie bereits in den JavaScript-Adapter integriert ist und TypeScript zwangsläufig diese Definitionen benötigt.
Du musst VSCode nur beibringen, dass es sich bei deiner Ordnerstruktur um ein Projekt handelt. Das geht mit einer leeren tsconfig.json im Hauptordner, danach werden .d.ts-Dateien automatisch eingebunden:
Besser ist natürlich eine passende tsconfig.json, z.B. für NodeJS 4+{ "compilerOptions": { "target": "es5", "lib": [ "es6", ] }, }
-
Danke, werde ich heute Abend ausprobieren.
-
Schau dir mal meinen Screenshot bzw. javascript.d.ts genau an. Auch diese Datei muss in ein Modul "gezwungen" werden. Anders als bei JS reicht require nicht aus, es muss das import keyword fallen.
// entweder so import * as fs from "fs"; // oder so import fs = require("fs"); ```` `
Verrückt. kaum macht man es richtig, funktioniert es. Nun ja, fast
Beim Testordner mit der Funktion abc() funktioniert es nun einwandfrei.
Nur mein eigentlicher Arbeitsbereich macht die Selben Probleme, wie oben beschrieben.
Dort werden die Funktionen zwar erkannt, die Parameter aber weiterhin mit any angezeigt und ohne Beschreibung.
Zur Eingrenzung habe ich dann meine globale Typedatei in den Testordner mit der abc() Funktion kopiert.
Hier funktionieren die Funktionen aus der globalen Datei auch einwandfrei.
Zur weiteren Eingrenzung habe ich dann auch die jsonconfig.json in den Testordner kopiert, um sicherzustellen, dass die Probleme nicht durch diese Datei kommen. Auch hier funktioniert es einwandfrei.
Problem gefunden
argh… während ich das hier tippe habe ich dann noch den Ordner global mit den globalen Skripten in den Testordner reinkopiert.
In diesem Moment funktioniert es nicht mehr sauber. :oops:
Die globalen Funktionen haben noch nicht das
require("fs");
am Anfang des Skripts.Werde dann jetzt erst einmal alle Skripte mit dem
require("fs");
versehen... sind ein paar...Danke für Deine Mühe!!!
-
Danke, bei mir funktioniert es prinzipiell.
Jetzt habe ich jedoch ein anderes Problem festgestellt:
Das fehlt in der javascript.d.ts tatsächlich.
In der Doku steht: "Same as javascript setTimeout."
-
Das fehlt in der javascript.d.ts tatsächlich.
In der Doku steht: "Same as javascript setTimeout." `
Korrekt, das kommt auch nicht in die javascript.d.ts, da es sich um Grundfunktionalität handelt. Dein Problem scheint aufzutreten, wenn im Ordner eine tsconfig.json existiert. Warum auch immer.Beheben kannst du es, indem du die NodeJS-Typings im Arbeitsbereich installierst.
npm i @types/node
für die neueste Version, bzw.
npm i @types/node@4
für NodeJS 4.
-
Wen es interessiert, ich habe den VSCode-Editor ("Monaco") in den Skriptadapter eingebaut.
Das ganze benötigt noch ein wenig Arbeit, da insbesondere der Wechsel von Blockly zu JS nicht funktioniert. Aber wer das nicht benötigt, darf gerne schon mal testen:
Installation aus eigener URL: https://github.com/AlCalzone/ioBroker.j … ter-editor
--
–
–