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. Visualisierung
  4. [Vorlage] Variable Zeitsteuerung mit VIS Editor

NEWS

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

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

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

[Vorlage] Variable Zeitsteuerung mit VIS Editor

Geplant Angeheftet Gesperrt Verschoben Visualisierung
vistemplate
919 Beiträge 47 Kommentatoren 280.1k Aufrufe 75 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.
  • smartboartS smartboart

    @Glasfaser sagte in [Vorlage] Variable Zeitsteuerung mit VIS Editor:

    Teste mal nur mit einer Bedingung , alle drei Bedingungen von dir durch , was ist dann !?

    Dann passt der Vergleich mit der Objektliste, trotzdem noch der Hinweis...
    Unbenannt.JPG

    GlasfaserG Offline
    GlasfaserG Offline
    Glasfaser
    schrieb am zuletzt editiert von Glasfaser
    #612

    @smartboart

    und welche meckert er an .... eigentlich nur value 1 , das wieder kein Wert vorhanden ist !?

    EDIT :

    starte dein System mal komplett neu

    Synology 918+ 16GB - ioBroker in Docker v9 , VISO auf Trekstor Primebook C13 13,3" , Hikvision Domkameras mit Surveillance Station .. CCU RaspberryMatic in Synology VM .. Zigbee CC2538+CC2592 .. Sonoff .. KNX .. Modbus ..

    smartboartS 1 Antwort Letzte Antwort
    0
    • GlasfaserG Glasfaser

      @smartboart

      und welche meckert er an .... eigentlich nur value 1 , das wieder kein Wert vorhanden ist !?

      EDIT :

      starte dein System mal komplett neu

      smartboartS Offline
      smartboartS Offline
      smartboart
      schrieb am zuletzt editiert von
      #613

      @Glasfaser sagte in [Vorlage] Variable Zeitsteuerung mit VIS Editor:

      und welche meckert er an .... eigentlich nur value 1 , das wieder kein Wert vorhanden ist !?

      ja
      javascript.1 (1759) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer. Bitte korrigieren und übernehmen!

      1 Antwort Letzte Antwort
      0
      • smartboartS Offline
        smartboartS Offline
        smartboart
        schrieb am zuletzt editiert von smartboart
        #614

        Könnte es daran liegen, dass ich dasPopup mittels state umschalte, das habe ich im Script angepasst...
        Aber macht keinen Sinn...

        // Funktion zum Öffnen/Schließen der Dialogbox (EDIT-PopUp)
        function dialogCtrl(cmd){
            if (cmd == "open"){
            setState('javascript.0.Vis.ViewWechsel',43);
                // Für MaterialDesignWidget
                setState("javascript.1.Timer." + path + ".MaterialDialogWidgetOpen", true);
            }
            else if (cmd == "close"){
            setState('javascript.0.Vis.ViewWechsel',42);
                // Für MaterialDesignWidget
                setState("javascript.1.Timer." + path + ".MaterialDialogWidgetOpen", false);
            }
        }
        

        (arbeite mit view in widget)

        /* ####################################################################### */
        // Nachfolgende Einträge zwingend anpassen:
        
        // Eine Aufzählung für Geräte (z.B. Rollläden) angeben:
        var deviceEnum = "enum.functions.Zeitgesteuert";
        
        // Eine Aufzählung für States der Bedingungen:
        var deviceCond = "enum.functions.timerconditions";
        
        
        /* ####################################################################### */
        // Nachfolgende Einträge nur optional anpassen:
        
        // Hauptpfad des Timers unter javascript.1
        var path = "Devices";
        
        // Schrittweite der Minuten in DropDown für manuelle Zeitangabe
        var minIncrement = 2;
        
        // HTML Code aufteilen? Wenn pro Gerät eine eigenständige HTML Tabelle verwendet werden soll
        var splitHTML = false;
        // false: HTML Code wird vollständig in "Timer.Devices.TableHTML" geschrieben
        // true : HTML Code nach Geräten aufteilen -> "Timer.Devices.HTML_<GeräteName>" 
        
        // Log-Modus
        var stdLog = true; // elementare Ausgabe, Schedule erstellt/gelöscht
        var debugLog = false; // zusätzliche Ausgaben, z.B. zu den Bedingungen
        // Logausgabe manipulieren, um z.B. Log-Parser zu verwenden
        var logPraefix = "Timer: ";
        var logSuffix = "";
        
        // Anzeige nächster Timer mit Sollwerten?
        // true: mit Sollwerten
        // false: ohne Sollwerten
        var showValues = true;
        
        // Timer-Nr und/oder Symbol anzeigen? (true = Sichtbar)
        // Mindestens eine Spalte muss true sein, sonst wird Timer-Nummer angezeigt.
        var showTimerNr = true;
        var showSymbol = true;
        
        // Spalte für Gruppennummer anzeigen?
        var showGroupNr = true;
        
        // Symbole für Timer-Status in Tabelle, kopiert aus: https://emojipedia.org/
        var symbDisab = "❌";
        var symbEnab = "️️✅";
        
        // Schriftgröße innerhalb Tabelle (Einheit "em")
        var fontSize = 1.0;
        
        // Soll-Werte für States, die nicht als Bools hinterlegt sind. Zahlenwerte können hier angepasst/gelöscht werden.
        var sollDropDown = "0;5;10;15;20;30;40;50;60;70;80;90;100;Auf;Ab";
        // Soll-Werte für Bool-States true/false (werden automatisch erkannt)
        var sollDropDownBool = "An;Aus";
        // Falls Soll-Werte individualisiert wurden, bitte nachfolgend die realen Werte hinterlegen/anpassen
        var sollWertMapping = {"Auf": 100, "Ab": 0, "An": true, "Aus": false}
        
        // Gruppennamen für Timer
        // Es können auch mehr oder weniger als 10 Namen angegeben werden, 
        var grpNames = "A;B;C;D;E;F;G;H;I;J";
        
        // Funktionen in Tabelle mit Einfach-Klick (= true) oder Doppel-Klick (= false) ausführen?
        // default: oneClick = false
        var oneClick = true;
        
        // Minimaler Zeitversatz zwischen Ansteuerung der Geräte (in Millisekunden)
        // Realer Zeitversatz ergibt sich aus "Tabellenposition des Geräts (beginnend bei 0) * sendWithOffset
        // Vorteilhaft, falls Signale bei zeitgleicher Ansteuerung verloren gehen könnten (z.B. 433MHz Aktoren)
        var sendWithOffset = 200;
        
        /* ####################################################################### */
        /* #### BEI MANUELLEM UPDATE, SKRIPT UNTERHALB DIESER ZEILE ERSETZEN! #### */
        
        /*
        * Weitere Infos: https://forum.iobroker.net/topic/23346/vorlage-variable-zeitsteuerung-mit-vis-editor
        * Autor: Giuseppe Sicilia (Forum: GiuseppeS)
        
        * Changelog 30.06.2020 (Skript)
        * - Codeoptimierung, Error-Handling wenn Buttons nicht gemäß Standard genutzt werden
        * - Bugfix: Alle gemerkten Timer werden nun ausgeführt, wenn Bedingungen nächträglich erfüllt werden.
        *
        * Changelog 20.06.2020 (Skript)
        * - State "javascript.1.Timer.AtHomeSimul.TableJSON" gelöscht, wird nicht mehr benötigt.
        * - Bugfix: Bei Tabellen mit modifizierter Reihenfolge funktioniert nun auch die Filterung korrekt!
        *
        * Changelog 17.06.2020 (Skript)
        * - Steuerung der Geräte mit Versatz möglich. Neue (optionale) Variable "sendWithOffset"
        *
        * Changelog 30.05.2020 (Skript & VIS)
        * - Bugfix "ErrorMessage" im PopUp
        * - Neue Variable im Edit-Bereich: logSuffix
        *   Kann genutzt werden, um Log-Ausgabe noch flexibler anzupassen (Ist für manuelles Update nicht zwingend neu anzulegen)
        *
        * Changelog 29.05.2020 v2
        * - DialogBox Button "Abbrechen" ersetzt durch Standard-Button. Schließen des Dialogs über Skript
        *   (Bugfix bei Verwendung von MD-Adapter Dialog)
        *
        * Changelog 29.05.2020
        * - Steuerung des Dialog Widgets aus "Material Design Adapter" über State "javascript.1.Timer." + path + ".MaterialDialogWidgetOpen"
        * - Meldung bei fehlerhaften Bedingungen in PopUp
        *   -> Bei manuellem Update, Widgets "Berechnete Uhrzeit" und "ErrorMsg" aus Export übernehmen
        *
        * Changelog 26.04.2020
        * - Minütliches Flackern der nächsten Timer abgestellt. Nur noch bei Änderungen gibts ein Flackern
        * - Bedingungen werden während der Eingabe ausgewertet und farblich im Editor hervorgehoben (Danke an HelmutS)
        * - Wenn Bedingungen leer oder fehlerhaft sind, wird das PopUp-Fenster nicht geschlossen. Log wird ausgegeben.
        * 
        * Changelog 15.04.2020
        * - PopUp-Editor ohne zusätzliche PNGs für Tage, rein als HTML-Button (siehe Screenshot)
        * - Funktionen innerhalb Tabelle können nun wahlweise mit Einfach-Klick statt Doppelklick ausgeführt werden
        *   (außer in Spalte "Device", diese Spalte dient als Haupt-Markierung für ADD/DEL, hier wird immer mit Doppelklick der Editor geöffnet)
        *   Neue Variable "oneClick" im Variablen-Bereich hinzugefügt (Default: oneClick = false)
        * - Neue Variablen müssen ab dieser Version bei einem manuellen Update nicht zwingend übernommen werden!
        *   Falls neue Variablen im oberen Bereich nicht existieren, wird der Default-Wert der neuen Variable angenommen.
        *   So soll sichergestellt werden, dass neue Funktionen die Funktionsweise älterer Versionen nicht beeinflusst  
        *
        * Changelog 29.03.2020 v2
        * - Sollwerte können über Variablen-Feld oben einfacher angepasst werden
        * - Zusätzlich zwei Variablen im oberen Feld: "sollDropDownBool" und "sollWertMapping"
        *
        * Changelog 29.03.2020
        * - Einzelne Aktive Background-Timer aus "Timer merken" können vorzeitig über Doppelklick auf die Bedingungszahl gelöscht werden
        * - Das Löschen aller aktiven Background-Timer kann über ein Doppelklick auf Tabellen-Überschrift "Bed" oder
        *   separat über das neue State "javascript.1.Timer.Devices.ResetBackgroundTimers" durchgeführt werden.
        *
        * Changelog 26.03.2020
        * - Bugfix für font-size der Tabelle (wurde zuvor nicht korrekt übernommen)
        * - Gruppenzuordnungen unterteilt in "Zeiten" und "Bedingungen"
        * - Funktion "Timer merken" hinzugefügt:
        *   Timer wird gemerkt für den Fall dass die Bedingungen erst nach Trigger-Uhrzeit "true" werden.
        *   Timer werden aus der "Merkliste" vorzeitig gelöscht, falls sich die Ziel Objekt-ID anderweitig ändert
        *   oder der nächste Timer des Devices aktiviert wird.
        * - "javascript.1.Timer.Devices.Editor.DropDownNr" wird seit Touch-Bedienung nicht mehr benötigt. Kann gelöscht werden.
        *
        * Changelog 03.02.2020
        * - Bugfix Gruppenzuordnung
        *
        * Changelog 30.01.2020
        * - Optik PopUp für Gruppenzuordnung angepasst
        * - Namen der Gruppen im Skript nach oben gesetzt, für bessere Anpassung
        *
        * Changelog 26.01.2020
        * - Timer werden Gruppen zugeordnet (aktuell statisch bis zu 10 Gruppen möglich)
        * - Änderungen über alle Timer einer Gruppe verteilen möglich
        * - Gruppennummer kann optional in Tabelle angezeigt werden
        * - Neue Spalte mit Symbolen (Aktiv-Status) anzeigbar und darüber auch manipulierbar (Doppelklick)
        * - Hinweis: Entweder Symbole oder Timer-Nummer muss angezeigt werden um Timer über Doppelklick zu aktivieren/deaktivieren
        * - Schriftgröße über Variable "fontSize" änderbar
        * - HTML-Code-Generierung aufgeräumt
        * - PopUp mit DropDown für Gruppenzuordnung erweitert
        *
        * Changelog 24.01.2020
        * - Bugfix bzgl. Doppelklick zum Editieren und Aktivieren/Deaktivieren der einzelnen Timer
        *
        * Changelog 19.01.2020
        * - Auswahl des Timers direkt über Tabelle (onclick event)
        * - Edit mit Doppelklick Gerät oder Ist-Zeit (dblclick event)
        * - Aktivieren/Deaktivieren des Timers über Doppelklick auf Timer-Nummer
        * - DropDown in VIS zu Filter umgestellt, default = kein Filter (DropDown auch löschbar!)
        * - Filter bei Split-Darstellung ohne Funktion
        *
        * Changelog 15.09.2019
        * - Sollwerte in PopUp werden je nach Device mit An/Aus oder Zahlenwerte befüllt
        *   PopUp wurde entsprechend angepasst
        * - Einführung neuer Variable logPraefix
        *
        * Changelog 14.09.2019
        * - Bugfix bei Anzeige NextTimer
        * - Weniger Fehlerausgaben bei erstem Start des Skripts
        * 
        * Changelog 08.09.2019
        * - Minuten Incremente in PopUp Editor über Variable steuerbar
        * 
        * Changelog 07.09.2019
        * - Logausgabe wenn Timer auslöst wenn stdLog oder debugLog = true ist.
        * 
        * Changelog 29.08.2019
        * - Ein neuer Timer wird auf Basis des zuletzt angewählten Timers erstellt (bezieht sich ausschließlich auf die Nummer!)
        * 
        * Changelog 29.08.2019
        * - Änderung von Bedingungen werden direkt bei Änderung getriggert (ohne minütliches Cron)
        * - Hauptpfad des Timers variabel umstellbar ohne Suchen/Ersetzen
        * - Zukünftige Timer werden in States "javascript.1.Timer. + path + .NextDevice(s)" ausgegeben
        * 
        * Changelog 28.07.2019
        * - einige Konsole Ausgaben mit switch debugLog bzw stdLog versehen
        * - Reihenfolge in Tabelle über Reihenfolge in "Timer.Devices.DropDownDevice" änderbar ("Timer.Devices.Editor.DropDownDevice" kann gelöscht werden)
        */
        
        /* ####################################################################### */
        
        
        var device_members = getObject(deviceEnum).common.members;
        var condition_members = getObject(deviceCond).common.members;
        var TageJSON = {1: "Mo", 2: "Di", 3: "Mi", 4: "Do", 5: "Fr", 6: "Sa", 7: "So"};
        var dblClickBlocker = false
        
        
        // Falls neue Variablen fehlen, ab hier defaults setzen:
        if (typeof oneClick === 'undefined') { var oneClick = false; }
        if (typeof logSuffix === 'undefined') { var logSuffix = ""; }
        if (typeof sendWithOffset === 'undefined') { var sendWithOffset = 200; }
        
        
        // Anzahl Keys in JSON ermitteln
        function length(obj) {
            return Object.keys(obj).length;
        }
        
        // Einfaches Kopieren von JSON Objekten
        function jsonCopy(src) {
            return JSON.parse(JSON.stringify(src));
        }
        
        // Erstellen des DropDown-Inhalts für Minuten Auswahl in PopUp Editor
        function setMinutesDropDown() {
        	var strDropDown = "";
            for (var i = 0; i < 60; i += minIncrement) {
                var tmp = (i <= 9 ? "0" : "") + i;
                strDropDown += tmp + ";";
            } 
            strDropDown = strDropDown.slice(0, strDropDown.length - 1); // Entfernen letztes Semikolon
            setState("javascript.1.Timer." + path + ".Editor.DropDownMinutes", strDropDown);
        }
        
        
        // Zeigt alle kommenden Timer in Liste (optimiert für DropDown-Liste)
        function nextTimer(){
            var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
            var timeStamp, checkTime, firstKey, splitKey, newKey;
            var allTimer = {};
            Object.keys(TimerJSON).forEach(function(key) {
                for(var i = 1; i <= length(TimerJSON[key]); i++) {
                    // Hier werden alle Timer durchlaufen
                    if (TimerJSON[key][i].Aktiv && TimerJSON[key][i].ConditionsTrue){
                        // Timer ist Aktiv und Bedingungen sind erfüllt
                        var tmp = TimerJSON[key][i].CronTage.split(",");
                        for(var j = 0; j < tmp.length; j++) {
                            tmp[j] = (tmp[j] == 0 ? 7 : tmp[j]);
                            timeStamp = tmp[j] + " " + TimerJSON[key][i].Zeit;
                            if (showValues){
                                if (allTimer.hasOwnProperty(timeStamp)) {allTimer[timeStamp] += ", " + key + " (" + TimerJSON[key][i].Sollwert + ")"}
                                else {allTimer[timeStamp] = key + " (" + TimerJSON[key][i].Sollwert + ")";}
                            } else {
                                if (allTimer.hasOwnProperty(timeStamp)) {allTimer[timeStamp] += ", " + key}
                                else {allTimer[timeStamp] = key;}
                            }
                        }
                    }
                }
            });
            var allTimerLength = length(allTimer);
            var d = new Date();
            var actDay = (d.getDay() == 0 ? 7 : d.getDay());
            var actTime = ('0' + d.getHours()).slice(-2) + ":" + ('0' + d.getMinutes()).slice(-2);
            var actTimeStamp = actDay + " " + actTime;
        
            if (allTimerLength == 0){
                setState("javascript.1.Timer." + path + ".NextDevice", "Keine Timer");
                if (getState("javascript.1.Timer." + path + ".NextDevices").val != "Keine Timer"){
                    setState("javascript.1.Timer." + path + ".NextDevices", "Keine Timer");
                }
            }
            else if (allTimerLength == 1){
                firstKey = Object.keys(allTimer)[0];
                splitKey = firstKey.split(" ");
                newKey = TageJSON[splitKey[0]] + " " + splitKey[1];
                setState("javascript.1.Timer." + path + ".NextDevice", newKey + " - " + allTimer[firstKey]);
                if (getState("javascript.1.Timer." + path + ".NextDevices").val != newKey + " - " + allTimer[firstKey]){
                    setState("javascript.1.Timer." + path + ".NextDevices", newKey + " - " + allTimer[firstKey]);
                }
            }
            else {
                var listBefore = "";
                var listAfter = "";
                var listComplete = "";
                Object.keys(allTimer).sort().forEach(function(key) {
                    if (key > actTimeStamp){
                        splitKey = key.split(" ");
                        newKey = ( parseInt(splitKey[0]) == actDay ? "" : TageJSON[splitKey[0]]) + " " + splitKey[1];
                        listAfter += newKey + " - " + allTimer[key] + ";" ;
                    }
                    else {
                        splitKey = key.split(" ");
                        newKey = TageJSON[splitKey[0]] + " " + splitKey[1];
                        listBefore += newKey + " - " + allTimer[key] + ";" ;
                };
                });
                listComplete = listAfter + listBefore;
                setState("javascript.1.Timer." + path + ".NextDevice", listComplete.split(";")[0]);
                if (getState("javascript.1.Timer." + path + ".NextDevices").val != listComplete.slice(0, listComplete.length - 1)){
                    setState("javascript.1.Timer." + path + ".NextDevices", listComplete.slice(0, listComplete.length - 1));
                }
            }
        }
        
        
        // Initiales erstellen des JSON für States der Bedingungen
        // Aufruf erfolgt aus main-Function
        function createConditionsJSON(){
            var ConditionJSON = {};
            var dropDownListe = "";
            // ConditionJSON wird bei jedem Skript-Start neu erstellt da keine relevanten Alt-Daten vorhanden sind
            for(var i = 0; i < condition_members.length; i++) {
                var condName = getObject(condition_members[i]).common.name;
                ConditionJSON[condName] = condition_members[i];
            }
            // DropDown-Liste: Für alphabetische Sortierung nicht in for-Schleife oben integrierbar
            Object.keys(ConditionJSON).sort().forEach(function(key) {
                dropDownListe += key + ";";
            });
            dropDownListe = dropDownListe.slice(0, dropDownListe.length - 1); // Entfernen letztes Semikolon
            setState("javascript.1.Timer." + path + ".Editor.ConditionKeyDropDown", dropDownListe);
            setState("javascript.1.Timer." + path + ".ConditionJSON", JSON.stringify(ConditionJSON));
        }
        
        
        // Bedingungen der Timer werden geprüft und in TimerJSON geschrieben
        // Verwendung für VIS: HTML-Tabelle und Next-Timer
        // Falls Bedingungen für "gemerkte" Time true werden, dann wird Device gesteuert
        function updateCond(){
            var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
            Object.keys(TimerJSON).forEach(function(key) { // Alle Devices werden durchlaufen
                for(let i = 1; i <= length(TimerJSON[key]); i++) { // Alle Timer innerhalb Devices werden durchlaufen
                    let deviceNr = TimerJSON[key][i]["DeviceNr"];
                    let scheduleNr = deviceNr * 10 + i;
                    let result = condEval(TimerJSON[key][i]);
                    TimerJSON[key][i].ConditionsTrue = result;
        
                    // Wenn Bedingungen true sind und aktueller Timer als gemerkter Timer aktiv ist, dann ausführen
                    if (result && subscribesList[key] == scheduleNr){
                        setTimeout(function(){
                            subscribesList[key] = 0;
                            if(debugLog) console.log("Timer im Hintergrund: " + JSON.stringify(subscribesList));
                            let sollwert =  TimerJSON[key][i]["Sollwert"];
                            if(stdLog) console.log(logPraefix + key + " (" + sollwert + ") -> Timer ausgeführt. Bedingung(en) nachträglich erfüllt!" + logSuffix);
                            setState(TimerJSON[key][i]["ObjID"], mapping(sollwert));
                        }, deviceNr*sendWithOffset/2)
                    }
                }
            });
            setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
        }
        
        
        // Führt Evaluation der Bedingungen durch und gibt true oder false zurück
        // Aufruf nur aus Funktion autoScheduler
        function condEval(DeviceJSON){
            var sumEval, strEval1, strEval2, strEval3;
            var conditionsNr = parseInt(DeviceJSON.ConditionsNr);
            switch(conditionsNr){
                case 0:
                    sumEval = true;
                    break;
                case 1:
                    strEval1 = eval(DeviceJSON.Conditions[1].ConditionStr);
                    sumEval = strEval1;
                    break;
                case 2:
                    strEval1 = eval(DeviceJSON.Conditions[1].ConditionStr);
                    strEval2 = eval(DeviceJSON.Conditions[2].ConditionStr);
                    sumEval = (strEval1 && strEval2);
                    break;
                case 3:
                    strEval1 = eval(DeviceJSON.Conditions[1].ConditionStr);
                    strEval2 = eval(DeviceJSON.Conditions[2].ConditionStr);
                    strEval3 = eval(DeviceJSON.Conditions[3].ConditionStr);
                    sumEval = (strEval1 && strEval2 && strEval3);
                    break;
            }
            return sumEval;
        }
        
        
        // Sollwert Mapping zu Variable "sollWertMapping" im oberen Bereich
        function mapping(sollwert){
            // Verschiedene Mappings aus Editor-DropDown zu realen States erstellen
            if(sollWertMapping.hasOwnProperty(sollwert)){
                sollwert = sollWertMapping[sollwert]
            } else (sollwert = parseInt(sollwert));
        
            return sollwert
        }
        
        // Schedules werden variabel erstellt; zunächst wird gelöscht und wenn create=true wird neu erstellt
        // Auswertung der Bedingungen erfolgt erst bei Ausführung des Schedules
        var subscribesList = {}; // -> Gespeicherte Timer werden hier notiert; genutzt für Darstellung in VIS und Abfragen. Beispiel-Aufbau: {"Rollo_Balkon": 22, ...}
        var cronArr = [];        // -> Enthält Schedules über alle Devices und Timer, die aktiv sind
        function autoScheduler(TimerJSON, device, nr) {
            var condArr = [];
            var deviceNr = TimerJSON[device][nr].DeviceNr;
            var scheduleNr = (deviceNr * 10) + nr;
            var aktiv = TimerJSON[device][nr].Aktiv;
            var cronString = TimerJSON[device][nr].Cron;
            var objID = TimerJSON[device][nr].ObjID;
            var conditionState = TimerJSON[device][nr].ConditionsTrue;
            var rememberState = TimerJSON[device][nr].RememberTimer;
            var conditionsNr = parseInt(TimerJSON[device][nr].ConditionsNr);
            var sollwert = TimerJSON[device][nr].Sollwert;
        
            // Timer zunächst immer löschen (weil täglich neue Astro-Zeiten und Randoms genutzt werden sollen)
            if (cronArr[scheduleNr]){
                if(stdLog) console.log("Schedule für \"" + device + " #" + nr + "\" [" + scheduleNr + "] gelöscht!");
                clearSchedule(cronArr[scheduleNr]); cronArr[scheduleNr] = null;
            } else{
                if(debugLog) console.log("Schedule für \"" + device + " #" + nr + "\" (" + scheduleNr + ") nicht vorhanden! Kein Löschen notwendig!");
            }
            // Timer neu erstellen falls AKTIV == true
            if (aktiv){
        
                if(stdLog) console.log("Schedule aktiviert: \"" + device + " #" + nr + "\": [" + scheduleNr + "] | " + cronString + " | " + objID + " | " + sollwert);
                
                cronArr[scheduleNr] = schedule(cronString, function(){
                    if( condEval(TimerJSON[device][nr]) ){
                        setTimeout(function(){
                            if(stdLog) console.log(logPraefix + device + " (" + sollwert + ")" + logSuffix);
                            setState(objID, mapping(sollwert));
                            tableMain(500); // aktualisieren der Tabelle
                        }, deviceNr*sendWithOffset/2)
                    } else if (rememberState){
                        if(stdLog) console.log(logPraefix + device + " (" + sollwert + ") -> Timer gespeichert. Bedingung(en) noch nicht erfüllt!" + logSuffix);
                        subscribesList[device] = scheduleNr;
                        if(debugLog) console.log("Timer im Hintergrund: " + JSON.stringify(subscribesList));
                        tableMain(500);
                    } else {
                        if(stdLog) console.log(logPraefix + device + " (" + sollwert + ") -> Nicht ausgeführt. Bedingung(en) nicht erfüllt!" + logSuffix);
                        tableMain(500);
                    };
                });
            } else {
                // Falls Timer deaktiviert wird, während es gespeichert war... In Liste zurücksetzen!
                if (subscribesList[device] == scheduleNr){
                    if(debugLog) console.log("Schedule für \"" + device + " #" + nr + "\" [" + scheduleNr + "] -> Timer aus Speicher entfernt!");
                    subscribesList[device] = 0;
                    if(debugLog) console.log("Timer im Hintergrund: " + JSON.stringify(subscribesList));
                }
            }
        }
        
        
        // Background-Timer aus "Timer merken" löschen
        function resetBackgroundTimers(target){
            if (target == "all"){
                Object.keys(subscribesList).forEach(function(device) {
                    if (subscribesList[device] > 0){
                        if(stdLog) console.log("Aktiver Background-Timer für \"" + device + "\" gelöscht!");
                        subscribesList[device] = 0;
                    }
                });
            } else {
                if (subscribesList[target] > 0){
                    if(stdLog) console.log("Aktiver Background-Timer für \"" + target + "\" gelöscht!");
                    subscribesList[target] = 0;
                } else {
                    if(stdLog) console.log("Kein aktiver Background-Timer für \"" + target + "\" gefunden!");
                }
            }
            if(debugLog) console.log("Timer im Hintergrund: " + JSON.stringify(subscribesList));
            tableMain(500);
        }
        
        
        // Cron Tage in Wochentage umwandlen und ggf. abkürzen mit "von - bis"
        function shortDays(cronTage) {
        	var cronSplit, cronLast;
        	var tageVIS = "";
        	
        	if (cronTage.substring(0,2) == ",0"){
            // 0 mit 7 ersetzen und ans Ende setzen
        	  cronTage = cronTage.substring(2,cronTage.length) + ",7";
        	}
        	// Erstes Komma entfernen
        	cronTage = cronTage.substring(1, cronTage.length);
        
            if (cronTage.length == 1){
                tageVIS = TageJSON[cronTage];
            }
            else if (cronTage.length == 13){
                tageVIS = "täglich";
            }
            else {
                cronSplit = cronTage.split(",");
                cronLast = cronSplit.length - 1;
                // Wenn Anzahl der Elemente = mathematische Differenz dann "aufeinanderfolgend", also kürzen
                if ((cronSplit[cronLast] - cronSplit[0]) == cronLast) {
                tageVIS = TageJSON[cronSplit[0]] + " - " + TageJSON[cronSplit[cronLast]];
                }
                else {
                for (var j = 0; j <= cronLast ; j++){
                    tageVIS += TageJSON[cronSplit[j]] + ", ";
                }
                // letztes Komma entfernen
                tageVIS = tageVIS.substring(0, tageVIS.length-1);
                }
            }
          return tageVIS;
        }
        
        // Input:  Minute + Stunde + Zufallsbereich (1 bis 59 Min) + Vorzeichen von Zufall
        // Return: Json Object mit Struktur von timeJSON, siehe unten
        function randomTime(min,std,rand,opt) {
            // Erstellung des JSON mit Vorbelegung der Keys
            var timeJSON = {"Zeit": "23:59", "Cron": "59 23 * * *", "Std": "23", "Min": "59"};
            if (rand > 0){
                if (rand > 59) {rand = 59;}
                if (opt === "pm"){var delta_min = Math.floor(Math.random() * (rand - (-1 * rand) + 1) + (-1 * rand));}
                if (opt === "p"){var delta_min = Math.floor(Math.random() * (rand + 1));}
                if (opt === "m"){var delta_min = Math.floor(Math.random() * (rand + 1) - rand);}
            	min += delta_min;
            	if (min >= 60){std++;min -= 60;}
            	else if (min < 0){std--;min += 60;}
            	if (std >= 24){std -= 24;}
            	else if (std < 0){std += 24;}
            }
        	timeJSON.Zeit = (std <= 9 ? "0" : "") + std + ":" + (min <= 9 ? "0" : "") + min;
        	timeJSON.Std = std;
        	timeJSON.Min = min;
        	timeJSON.Cron = min + " " + std + " *" + " * "; // Wochentage für Cron bewusst nicht vorhanden, wird später angehängt
            return timeJSON;
        }
        
        // Input timeJSON aus function randomTime + Offset + Vorzeichen von Offset
        // Output timeJSON mit verrechnetem Offset
        function offsetTime(randJSON,offset,opt) {
            var min = randJSON.Min, std = randJSON.Std, delta_min = 0;
            var timeJSON = {"Zeit": "23:59", "Cron": "59 23 * * *", "Std": "23", "Min": "59"};
            if (offset > 0){
                if (offset > 59) {offset = 59;}
                if (opt === "p"){ delta_min = offset }
                if (opt === "m"){ delta_min = -1 * offset}
            	min += delta_min;
            	if (min >= 60){std++;min -= 60;}
            	else if (min < 0){std--;min += 60;}
            	if (std >= 24){std -= 24;}
            	else if (std < 0){std += 24;}
            }
        	timeJSON.Zeit = (std <= 9 ? "0" : "") + std + ":" + (min <= 9 ? "0" : "") + min;
        	timeJSON.Std = std;
        	timeJSON.Min = min;
        	timeJSON.Cron = min + " " + std + " *" + " * "; // Wochentage für Cron bewusst nicht vorhanden, wird später angehängt
            return timeJSON;
        }
        
        // Setzt die 3 Felder für Astro-DropDown Werte, Texte und das Json für spätere Berechnungen.
        function setAstro() {
            var strWerte = "manuell";
            var strTexte = "manuell";
            var AstroJSON = {};
            var tmpAstro;
            var astro_times = ["sunrise", "sunriseEnd", "goldenHourEnd", "solarNoon", "goldenHour", "sunsetStart", "sunset", "dusk", "nauticalDusk", "nadir", "nauticalDawn", "dawn"]
            var defaultJSON = {"Zeit" : "10:00", "Std" : 10, "Min" : 0};
            
            astro_times.forEach(function(entry) {
                tmpAstro = entry;
                var zeit = formatDate(getDateObject(getAstroDate(tmpAstro, undefined, 0)), "hh:mm");
                var zeitSplit = zeit.split(':');
                AstroJSON[tmpAstro] = jsonCopy(defaultJSON);
        		AstroJSON[tmpAstro].Zeit = zeit;
        		AstroJSON[tmpAstro].Std = parseInt(zeitSplit[0]);
        		AstroJSON[tmpAstro].Min = parseInt(zeitSplit[1]);
                strTexte += ";" + tmpAstro + ", " + zeit;
                strWerte += ";" + tmpAstro;
            });
        
        	setState("javascript.1.Timer." + path + ".Editor.DropDownAstroTexte", strTexte);
        	setState("javascript.1.Timer." + path + ".Editor.DropDownAstroWerte", strWerte);
        	setState("javascript.1.Timer." + path + ".AstroJSON", JSON.stringify(AstroJSON));
        }
        
        
        // recalc wird automatisch täglich ausgeführt und zieht Random und Astro neu an
        // Trigger erfolgt vorzugsweise nach Trigger für setAstro
        // recalc erfolgt auch wenn CopyAll im Editor PopUp aktiviert wird
        function recalc() {
            var CalcJSON = {};
            var astro, device, nr;
            var AstroJSON = JSON.parse(getState("javascript.1.Timer." + path + ".AstroJSON").val);
            var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
            Object.keys(TimerJSON).forEach(function(key) {
                for(var i = 1; i <= length(TimerJSON[key]); i++) {
                    astro = TimerJSON[key][i].Astro;
                    device = key;
                    nr = i;
                    if (astro === "manuell" ){
                        CalcJSON = randomTime(parseInt(TimerJSON[key][i].Min,10),parseInt(TimerJSON[key][i].Std,10),TimerJSON[key][i].Random,TimerJSON[key][i].RandPM);
                    }
                    else {
                        CalcJSON = randomTime(parseInt(AstroJSON[astro].Min,10),parseInt(AstroJSON[astro].Std,10),TimerJSON[key][i].Random,TimerJSON[key][i].RandPM);
                        CalcJSON = offsetTime(CalcJSON,TimerJSON[key][i].Offset,TimerJSON[key][i].OffsetPM);
                    }
                    TimerJSON[key][i].Zeit = CalcJSON.Zeit;
                    TimerJSON[key][i].Cron = CalcJSON.Cron + TimerJSON[key][i].CronTage;
                    TimerJSON[key][i].ConditionsTrue = condEval(TimerJSON[key][i]);
                    autoScheduler(TimerJSON, key, i);
                }
            });
            setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
            setState("javascript.1.Timer." + path + ".Editor.Nummer", "", true);
            setState("javascript.1.Timer." + path + ".Editor.Device", "", true);
        }
        
        
        // Auswahl des Timers wird automatisch zurückgesetzt; für dauerhaft bessere Optik und Schutz vor Fehleingaben
        var focusTimeOut;
        function delFocusOnTimer(option) {
            if (option){
                if (focusTimeOut) {clearTimeout(focusTimeOut); focusTimeOut = null;}
                focusTimeOut = setTimeout(function(){
                        setState("javascript.1.Timer." + path + ".Editor.Nummer", "", true);
                        setState("javascript.1.Timer." + path + ".Editor.Device", "", true);
                    }, 5000)
            } else {
                if (focusTimeOut) {clearTimeout(focusTimeOut); focusTimeOut = null;}
            }
        }
        
        
        // Funktion zum Öffnen/Schließen der Dialogbox (EDIT-PopUp)
        function dialogCtrl(cmd){
            if (cmd == "open"){
            setState('javascript.0.Vis.ViewWechsel',43);
                // Für MaterialDesignWidget
                setState("javascript.1.Timer." + path + ".MaterialDialogWidgetOpen", true);
            }
            else if (cmd == "close"){
            setState('javascript.0.Vis.ViewWechsel',42);
                // Für MaterialDesignWidget
                setState("javascript.1.Timer." + path + ".MaterialDialogWidgetOpen", false);
            }
        }
        
        // ENDE DER HILFS-FUNKTIONEN
        // ##########################################################################################################
        // ##########################################################################################################
        // Trigger bzw. Schedules für Funktionen
        
        function activateSchedules(){
        
            // Astro-Zeiten werden täglich aktualisiert, anschließend neu Berechnung der Timer
            schedule('0 4 * * *', setAstro);
            schedule('1 4 * * *', recalc);
        
            // Darstellung zukünftiger Timer
            schedule('30 * * * * *', nextTimer);
        }
        
        // ENDE DER Trigger bzw. Schedules
        // ##########################################################################################################
        // ##########################################################################################################
        // NACHFOLGEND DIE TRIGGER AUS VIS
        
        function activateTrigger(){
        
        // Subscribtions für Klick-Events:
        
            // One-Click Aktion aus Tabelle zur Auswahl des Timers
            on({id: "javascript.1.Timer." + path + ".clickTarget", change: "any"}, function (obj) {
            	
                if (dblClickBlocker) {return}; // Doppelte Ausführung One-Click bei Double-Click vermeiden
                dblClickBlocker = true;
                setTimeout(function(){
                    dblClickBlocker = false;
                }, 600);
                
                if (debugLog) console.log("Klick aus Tabelle erkannt. Übergebener Wert: " + obj.state.val);
        
                setState("javascript.1.Timer.ActiveTable", path);
                var tmp = obj.state.val.split("~");
                if (tmp[0] != "all") { // "all" ist ein Tag der Überschriften, daher folgt keine Timer-Auswahl
                    setState("javascript.1.Timer." + path + ".Editor.Device", tmp[0]);
                    setStateDelayed("javascript.1.Timer." + path + ".Editor.Nummer", parseInt(tmp[1]), 50, false);
                }
        
                if (oneClick == true){ // Wenn Tabellen Funktionen mit One-Click gewünscht werden ...
                    setTimeout(function(){
                        var btnSource = obj.state.val.split("~")[2]; // Button-Funktion wird eingelesen
        
                        if (btnSource == "time"){ // Edit-Dialog öffnen bei Doppelklick IST-Zeit-Button
                            dialogCtrl("open");
                            delFocusOnTimer(false); // Auswahl des Timers nicht automatisch zurücksetzen!
                        }
                        if (btnSource == "nr"){ // Aktivieren/Deaktivieren des Timers
                            setState("javascript.1.Timer." + path + ".Editor.Aktiv", !getState("javascript.1.Timer." + path + ".Editor.Aktiv").val);
                        }
                        if (btnSource == "cond"){ // Aktivieren/Deaktivieren des Timers
                            resetBackgroundTimers(obj.state.val.split("~")[0]);
                        }
                    }, 100)
                }
            });
        
            // Double-Click Aktion aus Tabelle für Spezialfunktionen
            on({id: "javascript.1.Timer." + path + ".dblClickTarget", change: "any"}, function (obj) {
            	
                if(debugLog) console.log("Doppelklick aus Tabelle erkannt. Übergebener Wert: " + obj.state.val);
                var btnSource = obj.state.val.split("~")[2]; // Button-Funktion wird eingelesen
        
                if (btnSource == "dev"){ // Edit-Dialog öffnen bei Doppelklick Geräte-Button
                    dialogCtrl("open");
                    delFocusOnTimer(false); // Auswahl des Timers nicht automatisch zurücksetzen!
                }
                if (oneClick == false){ // Restl. Doppelklick-Funktionen deaktivieren, wenn One-Click gewünscht wird
                    if (btnSource == "time"){ // Edit-Dialog öffnen bei Doppelklick IST-Zeit-Button
                        dialogCtrl("open");
                        delFocusOnTimer(false); // Auswahl des Timers nicht automatisch zurücksetzen!
                    }
                    if (btnSource == "nr"){ // Aktivieren/Deaktivieren des Timers
                        setState("javascript.1.Timer." + path + ".Editor.Aktiv", !getState("javascript.1.Timer." + path + ".Editor.Aktiv").val);
                    }
                    if (btnSource == "cond"){ // Aktivieren/Deaktivieren des Timers
                        resetBackgroundTimers(obj.state.val.split("~")[0]);
                    }
                }
            });
        
        // ENDE Subscribtions für Klick-Events
        // ###################################
        // Subscribtions für MAINVIEW
        
            // Alle Backgroud-Timer aus "Timer merken" löschen
            on({id: "javascript.1.Timer." + path + ".ResetBackgroundTimers", change: "ne"}, function (obj) {
                if (obj.state.val) {
                    resetBackgroundTimers("all"); // = Alle löschen
                    setStateDelayed("javascript.1.Timer." + path + ".ResetBackgroundTimers", false, 500, false);
                }
            });
        
        
        
            // Device aus Filter-DropDown in VIS, triggert ausschließlich HTML-Darstellung
            on({id: "javascript.1.Timer." + path + ".FilterDevice", change: "ne", ack: false}, function (obj) {
                tableMain(0);
            });
        
        
            // Trigger wenn Device-Fokus geändert wird
            on({id: "javascript.1.Timer." + path + ".Editor.Device", change: "any", ack: false}, function (obj) {   // auf Pulldownmenü das jeweilige device auslesen
                var device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);       //einlesen der Einträge aus State
                if (getObject(TimerJSON[device][1].ObjID).common.type == "boolean"){
                    setState("javascript.1.Timer." + path + ".Editor.SollwertDropDown", sollDropDownBool);
                } else {
                    setState("javascript.1.Timer." + path + ".Editor.SollwertDropDown", sollDropDown);
                }
            });
        
        
            // Trigger wenn Timer-Nummer vom Device aktualisiert wird 
            on({id: "javascript.1.Timer." + path + ".Editor.Nummer", change: "any", ack: false}, function (obj) {
                var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
                var device;
                var nr = getState("javascript.1.Timer." + path + ".Editor.Nummer").val;
        
                delFocusOnTimer(true);
        
                if (nr == "+"){ // Neuer Timer soll hinzugefügt werden
                    // Error-Handling
                    if (getState("javascript.1.Timer." + path + ".Editor.Device").val == ""){
                        console.warn("Es wurde kein Timer gewählt. Bitte Timer wählen und Button 'ADD' innerhalb 5s betätigen")
                        return
                    }
                    device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                    var devlen = length(TimerJSON[device]);
                    var baseNr = obj.oldState.val;
        
                    if (baseNr == devlen){// Neuen Timer anhängen falls Basis für neuen Timer der letzte Timer war
                        nr = devlen + 1;
                        TimerJSON[device][nr] = TimerJSON[device][devlen];
                        // Falls kopierter Timer aktiv war wird der Neue auch direkt gesetzt
                        TimerJSON[device][nr].ConditionsTrue = condEval(TimerJSON[device][nr]);
                        autoScheduler(TimerJSON, device, nr);
                        setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                        setState("javascript.1.Timer." + path + ".Editor.Nummer", nr);
                    } else {
                        // Mittendrin einfügen: Alle Timer darüber um eine Position verschieben
                        // -> Timer zunächst deaktivieren, dann verschieben und erneut aktivieren, alte Position löschen
                        for(var j = devlen; j > baseNr; j--){
                            var tmpAktiv = TimerJSON[device][j].Aktiv;
                            TimerJSON[device][j].Aktiv = false
                            TimerJSON[device][j].ConditionsTrue = condEval(TimerJSON[device][j]);
                            autoScheduler(TimerJSON, device, j);
                            TimerJSON[device][j+1] = TimerJSON[device][j];
                            TimerJSON[device][j+1].Aktiv = tmpAktiv;
                            TimerJSON[device][j+1].ConditionsTrue = condEval(TimerJSON[device][j+1]);
                            autoScheduler(TimerJSON, device, j+1);
                            delete TimerJSON[device][j];
                        }
                        nr = baseNr + 1;
                        TimerJSON[device][nr] = TimerJSON[device][baseNr];
                        TimerJSON[device][nr].ConditionsTrue = condEval(TimerJSON[device][nr]);
                        autoScheduler(TimerJSON, device, nr);
                        setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                        setState("javascript.1.Timer." + path + ".Editor.Nummer", nr);
                    }
                }
                device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                setState("javascript.1.Timer." + path + ".Editor.Aktiv", TimerJSON[device][nr].Aktiv, true);
                setState("javascript.1.Timer." + path + ".Editor.Zeit", TimerJSON[device][nr].Zeit, true);
                setState("javascript.1.Timer." + path + ".Editor.Cron", TimerJSON[device][nr].Cron, true);
                setState("javascript.1.Timer." + path + ".Editor.TageVIS", TimerJSON[device][nr].TageVIS, true);
                setState("javascript.1.Timer." + path + ".Editor.CronTage", TimerJSON[device][nr].CronTage, true);
                setState("javascript.1.Timer." + path + ".Editor.WTagMo", TimerJSON[device][nr].Mo, true);
                setState("javascript.1.Timer." + path + ".Editor.WTagDi", TimerJSON[device][nr].Di, true);
                setState("javascript.1.Timer." + path + ".Editor.WTagMi", TimerJSON[device][nr].Mi, true);
                setState("javascript.1.Timer." + path + ".Editor.WTagDo", TimerJSON[device][nr].Do, true);
                setState("javascript.1.Timer." + path + ".Editor.WTagFr", TimerJSON[device][nr].Fr, true);
                setState("javascript.1.Timer." + path + ".Editor.WTagSa", TimerJSON[device][nr].Sa, true);
                setState("javascript.1.Timer." + path + ".Editor.WTagSo", TimerJSON[device][nr].So, true);
                setState("javascript.1.Timer." + path + ".Editor.Std", TimerJSON[device][nr].Std, true);
                setState("javascript.1.Timer." + path + ".Editor.Min", TimerJSON[device][nr].Min, true);
                setState("javascript.1.Timer." + path + ".Editor.Sollwert", TimerJSON[device][nr].Sollwert, true);
                setState("javascript.1.Timer." + path + ".Editor.DropDownAstro", TimerJSON[device][nr].Astro, true);
                setState("javascript.1.Timer." + path + ".Editor.Random", TimerJSON[device][nr].Random, true);
                setState("javascript.1.Timer." + path + ".Editor.RandPM", TimerJSON[device][nr].RandPM, true);
                setState("javascript.1.Timer." + path + ".Editor.Offset", TimerJSON[device][nr].Offset, true);
                setState("javascript.1.Timer." + path + ".Editor.OffsetPM", TimerJSON[device][nr].OffsetPM, true);
                setState("javascript.1.Timer." + path + ".Editor.RememberTimer", TimerJSON[device][nr].RememberTimer, true);
                setState("javascript.1.Timer." + path + ".Editor.ConditionsNr", TimerJSON[device][nr].ConditionsNr, true);
                setState("javascript.1.Timer." + path + ".Editor.Gruppe", TimerJSON[device][nr].Gruppe, true);
        
                for (let i = 1; i <= 3; i++){
                    setState("javascript.1.Timer." + path + ".Editor.Condition" + i, TimerJSON[device][nr].Conditions[i].ConditionStr, true);
                    setState("javascript.1.Timer." + path + ".Editor.Cond" + i + "State", TimerJSON[device][nr].Conditions[i].CondState, true);
                    setState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Comp", TimerJSON[device][nr].Conditions[i].CondComp, true);
                    setState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Value", TimerJSON[device][nr].Conditions[i].CondValue, true);
                    if (i <= TimerJSON[device][nr].ConditionsNr){
                        setState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Result", eval(TimerJSON[device][nr].Conditions[i].ConditionStr));
                    } else {
                        setState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Result", false);
                    }
                }
        
            });
        
        
            // Wenn Status "Aktiv" geändert wird erfolgt sofortiges Sichern des TimerJSON
            on({id: "javascript.1.Timer." + path + ".Editor.Aktiv", change: "any", ack: false}, function (obj) {
                
                var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val); // einlesen der Einträge aus State
                var device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                var nr = getState("javascript.1.Timer." + path + ".Editor.Nummer").val;
                TimerJSON[device][nr].Aktiv = getState("javascript.1.Timer." + path + ".Editor.Aktiv").val;
                // Schedule setzen bzw. löschen
                TimerJSON[device][nr].ConditionsTrue = condEval(TimerJSON[device][nr]);
                autoScheduler(TimerJSON, device, nr);
                setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON)); // rückschreiben in State
            });
        
        
            // Trigger des Delete-Button für Löschen eines Timer-Eintrags
            on({id: "javascript.1.Timer." + path + ".Editor.Del", change: "any", ack: false}, function (obj) {
                
                // Error-Handling
                if (getState("javascript.1.Timer." + path + ".Editor.Device").val == ""){
                    console.warn("Es wurde kein Timer gewählt. Bitte Timer wählen und Button 'DEL' innerhalb 5s betätigen")
                    return
                }
                
                var device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                var nr = getState("javascript.1.Timer." + path + ".Editor.Nummer").val;
                var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
                var devlen = length(TimerJSON[device]);
        
                // Zeit für Timer Fokus wird neu gestartet
                delFocusOnTimer(true);
            
                if (devlen > 1 ){
                    // Aktueller Timer wird gelöscht
                    TimerJSON[device][nr].Aktiv = false;
                    autoScheduler(TimerJSON, device, nr); // Timer wird gelöscht da Aktiv=false
                    delete TimerJSON[device][nr];
                    
                    if (nr < devlen){ // Wenn gelöschter Timer mittendrin, dann Rest verschieben
                        for (var j = nr; j < devlen ; j++) {
                            var tmpAktiv = TimerJSON[device][j+1].Aktiv
                            TimerJSON[device][j+1].Aktiv = false
                            autoScheduler(TimerJSON, device, j+1);          // Timer wird gelöscht da Aktiv=false
                            TimerJSON[device][j] = TimerJSON[device][j+1];  // Timer wird auf niedrigere Position kopiert
                            delete TimerJSON[device][j+1];
                            TimerJSON[device][j].Aktiv = tmpAktiv;
                            TimerJSON[device][j].ConditionsTrue = condEval(TimerJSON[device][j]);
                            autoScheduler(TimerJSON, device, j);
                        }
                    }
                    devlen = length(TimerJSON[device]);
                    setState("javascript.1.Timer." + path + ".Editor.Nummer", devlen);
                    setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                }
                else {
                    // Letzter Timer und somit Device wird gelöscht
                    TimerJSON[device][1].Aktiv = false;
                    autoScheduler(TimerJSON, device, 1); // Timer wird gelöscht da Aktiv=false
                    delete TimerJSON[device];
                    var dropDownListe = getState("javascript.1.Timer." + path + ".DropDownDevice").val;
        
                    console.log(dropDownListe.includes(";" + device + ";"));
        
                    if(dropDownListe.includes(device + ";")){
                        console.log("Device gefunden")
                        dropDownListe = dropDownListe.replace(device + ";", "");
                    }
                    else if(dropDownListe.includes(";" + device)){
                        console.log("Device am Ende gefunden")
                        dropDownListe = dropDownListe.replace(";" + device, "");
                    }
                    setState("javascript.1.Timer." + path + ".DropDownDevice", dropDownListe, true);
                    setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                    setState("javascript.1.Timer." + path + ".Editor.Device", Object.keys(TimerJSON)[0]);
                    setState("javascript.1.Timer." + path + ".Editor.Nummer", 1);
                }
        
            });
        
        // ENDE Subscribtions für MAINVIEW
        // ###############################
        // Subscribtions für EDITOR
        
        
            // Trigger zum Erstellen der Bedingungen als String für späteres eval() (3x)
            on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1Comp' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1State' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1Value'), change: "any", ack: false}, function (obj) {
                var ConditionJSON = JSON.parse(getState("javascript.1.Timer." + path + ".ConditionJSON").val);
                var Cond1State = getState("javascript.1.Timer." + path + ".Editor.Cond1State").val
                var Cond1Comp = getState("javascript.1.Timer." + path + ".Editor.Cond1Comp").val
                var Cond1Value = getState("javascript.1.Timer." + path + ".Editor.Cond1Value").val
                var strCond1 = "getState(\"" + ConditionJSON[Cond1State] + "\").val " + Cond1Comp + " " + Cond1Value
                setState("javascript.1.Timer." + path + ".Editor.Condition1", strCond1);
                if (Cond1State != "" && Cond1Comp != "" && Cond1Value != "") {
                    setState("javascript.1.Timer." + path + ".Editor.Cond1Result", eval(strCond1));
                }
            });
        
            on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond2Comp' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond2State' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond2Value'), change: "any", ack: false}, function (obj) {
                var ConditionJSON = JSON.parse(getState("javascript.1.Timer." + path + ".ConditionJSON").val);
                var Cond2State = getState("javascript.1.Timer." + path + ".Editor.Cond2State").val
                var Cond2Comp = getState("javascript.1.Timer." + path + ".Editor.Cond2Comp").val
                var Cond2Value = getState("javascript.1.Timer." + path + ".Editor.Cond2Value").val
                var strCond2 = "getState(\"" + ConditionJSON[Cond2State] + "\").val " + Cond2Comp + " " + Cond2Value
                setState("javascript.1.Timer." + path + ".Editor.Condition2", strCond2);
                if (Cond2State != "" && Cond2Comp != "" && Cond2Value != "") {
                    setState("javascript.1.Timer." + path + ".Editor.Cond2Result", eval(strCond2));
                }
            });
        
            on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond3Comp' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond3State' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond3Value'), change: "any", ack: false}, function (obj) {
                var ConditionJSON = JSON.parse(getState("javascript.1.Timer." + path + ".ConditionJSON").val);
                var Cond3State = getState("javascript.1.Timer." + path + ".Editor.Cond3State").val
                var Cond3Comp = getState("javascript.1.Timer." + path + ".Editor.Cond3Comp").val
                var Cond3Value = getState("javascript.1.Timer." + path + ".Editor.Cond3Value").val
                var strCond3 = "getState(\"" + ConditionJSON[Cond3State] + "\").val " + Cond3Comp + " " + Cond3Value
                setState("javascript.1.Timer." + path + ".Editor.Condition3", strCond3);
                if (Cond3State != "" && Cond3Comp != "" && Cond3Value != "") {
                    setState("javascript.1.Timer." + path + ".Editor.Cond3Result", eval(strCond3));
                }
            });
        
        
            // Bei Änderung der Zeiten oder Astros im PopUp-View werden direkt End-Zeiten berechnet
            on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Std' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Min' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Random' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.RandPM' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Offset' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.OffsetPM' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.DropDownAstro'), change: "ne", ack: false}, function (obj) {
                
                var astro = getState("javascript.1.Timer." + path + ".Editor.DropDownAstro").val;
                var CalcJSON = {};
                var min, std;
                
                var rand = getState("javascript.1.Timer." + path + ".Editor.Random").val;
                var randpm = getState("javascript.1.Timer." + path + ".Editor.RandPM").val;
                var offset = getState("javascript.1.Timer." + path + ".Editor.Offset").val;
                var offsetPM = getState("javascript.1.Timer." + path + ".Editor.OffsetPM").val;
                
                if (astro === "manuell" ){
                    min = getState("javascript.1.Timer." + path + ".Editor.Min").val;
                    std = getState("javascript.1.Timer." + path + ".Editor.Std").val;
                    CalcJSON = randomTime(parseInt(min,10),parseInt(std,10),rand,randpm);
                }
                else {
                    var AstroJSON = JSON.parse(getState("javascript.1.Timer." + path + ".AstroJSON").val);
                    CalcJSON = randomTime(AstroJSON[astro].Min,AstroJSON[astro].Std,rand,randpm);
                    CalcJSON = offsetTime(CalcJSON,offset,offsetPM);
                }
                // Eintrag für vollst. Cron aktualisieren
                setState("javascript.1.Timer." + path + ".Editor.Zeit", CalcJSON.Zeit);
                var CronTage = getState("javascript.1.Timer." + path + ".Editor.CronTage").val;
                setState("javascript.1.Timer." + path + ".Editor.Cron", (CalcJSON.Cron + CronTage));
                
            });
        
        
            // Änderung der ausgewählten Tage triggern sofort den Tage-String-Eintrag, so wird OK-Trigger übersichtlicher
            on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagDi' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagDo' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagFr' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagMi' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagMo' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagSa' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagSo'), change: "ne"}, function (obj) {
                
                var strTage = "";  // Nur für Anzeige
                var cronTage = ""; // Für setzen von Cron-Schedule
                if(getState("javascript.1.Timer." + path + ".Editor.WTagSo").val){cronTage += ",0";}
                if(getState("javascript.1.Timer." + path + ".Editor.WTagMo").val){cronTage += ",1";}
                if(getState("javascript.1.Timer." + path + ".Editor.WTagDi").val){cronTage += ",2";}
                if(getState("javascript.1.Timer." + path + ".Editor.WTagMi").val){cronTage += ",3";}
                if(getState("javascript.1.Timer." + path + ".Editor.WTagDo").val){cronTage += ",4";}
                if(getState("javascript.1.Timer." + path + ".Editor.WTagFr").val){cronTage += ",5";}
                if(getState("javascript.1.Timer." + path + ".Editor.WTagSa").val){cronTage += ",6";}
                
                // String für VIS übersetzen, kürzen und setzen
                setState("javascript.1.Timer." + path + ".Editor.TageVIS", shortDays(cronTage));
                
                cronTage = cronTage.substring(1, cronTage.length);
                setState("javascript.1.Timer." + path + ".Editor.CronTage", cronTage);
        
                // Cron-Eintrag anpassen
                var cronSplit = getState("javascript.1.Timer." + path + ".Editor.Cron").val.split(" ");
                setState("javascript.1.Timer." + path + ".Editor.Cron", (cronSplit[0] + " " + cronSplit[1] + " " + cronSplit[2] + " " + cronSplit[3] + " " + cronTage));
                
            });
        
        
            // Trigger für OK-Button in PopUp-View; alle Werte werden in TimerJSON gesichert
            on({id: "javascript.1.Timer." + path + ".Editor.OK", change: "any", ack: false}, function (obj) {
                
                var device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                var nr = getState("javascript.1.Timer." + path + ".Editor.Nummer").val;
                var group = getState("javascript.1.Timer." + path + ".Editor.Gruppe").val;
                var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val); //einlesen der Einträge aus State
                var copyAll = getState("javascript.1.Timer." + path + ".Editor.CopyAll").val;
                var copyCond = getState("javascript.1.Timer." + path + ".Editor.CopyCond").val;
                var errorMsg = "";
        
                TimerJSON[device][nr].Gruppe = getState("javascript.1.Timer." + path + ".Editor.Gruppe").val;
                TimerJSON[device][nr].ConditionsNr = getState("javascript.1.Timer." + path + ".Editor.ConditionsNr").val;
        
                // Validierung der Bedingungen vor Übernahme des Timers, wenn nicht valide, wird Fenster nicht geschlossen
                var returnFlag = false;
                for (let i = 1; i <= TimerJSON[device][nr].ConditionsNr; i++){
                    var condStr = getState("javascript.1.Timer." + path + ".Editor.Condition" + i).val;
                    try {
                        eval(condStr);
                        if (condStr == "") {
                            errorMsg += "Bedingung " + i + " wurde gesetzt, ist aber leer.\nBitte korrigieren und übernehmen!";
                            returnFlag = true;
                        }
                    } catch (e){
                        errorMsg += "Fehler in Bedingung: " + i + ".\nBitte korrigieren und übernehmen!";
                        returnFlag = true;
                    }
                }
        
                
                if (returnFlag) {
                    console.log(errorMsg); 
                    setState("javascript.1.Timer." + path + ".ErrorMsg", "Bedingung(en) fehlerhaft!");
                    setTimeout(() => {
                        setState("javascript.1.Timer." + path + ".ErrorMsg", "");
                    }, 5000)
                    return
                }
        
                if (!copyAll || !copyCond) {
                    if (!copyAll){
                        TimerJSON[device][nr].Zeit = getState("javascript.1.Timer." + path + ".Editor.Zeit").val;
                        TimerJSON[device][nr].Std = getState("javascript.1.Timer." + path + ".Editor.Std").val;
                        TimerJSON[device][nr].Min = getState("javascript.1.Timer." + path + ".Editor.Min").val;
                        TimerJSON[device][nr].Cron = getState("javascript.1.Timer." + path + ".Editor.Cron").val;
                        TimerJSON[device][nr].Sollwert = getState("javascript.1.Timer." + path + ".Editor.Sollwert").val;
                        TimerJSON[device][nr].TageVIS = getState("javascript.1.Timer." + path + ".Editor.TageVIS").val;
                        TimerJSON[device][nr].CronTage = getState("javascript.1.Timer." + path + ".Editor.CronTage").val;
                        TimerJSON[device][nr].Astro = getState("javascript.1.Timer." + path + ".Editor.DropDownAstro").val;
                        TimerJSON[device][nr].Random = getState("javascript.1.Timer." + path + ".Editor.Random").val;
                        TimerJSON[device][nr].RandPM = getState("javascript.1.Timer." + path + ".Editor.RandPM").val;
                        TimerJSON[device][nr].Offset = getState("javascript.1.Timer." + path + ".Editor.Offset").val;
                        TimerJSON[device][nr].OffsetPM = getState("javascript.1.Timer." + path + ".Editor.OffsetPM").val;
                        TimerJSON[device][nr].Mo = getState("javascript.1.Timer." + path + ".Editor.WTagMo").val;
                        TimerJSON[device][nr].Di = getState("javascript.1.Timer." + path + ".Editor.WTagDi").val;
                        TimerJSON[device][nr].Mi = getState("javascript.1.Timer." + path + ".Editor.WTagMi").val;
                        TimerJSON[device][nr].Do = getState("javascript.1.Timer." + path + ".Editor.WTagDo").val;
                        TimerJSON[device][nr].Fr = getState("javascript.1.Timer." + path + ".Editor.WTagFr").val;
                        TimerJSON[device][nr].Sa = getState("javascript.1.Timer." + path + ".Editor.WTagSa").val;
                        TimerJSON[device][nr].So = getState("javascript.1.Timer." + path + ".Editor.WTagSo").val;
                    }
                    if (!copyCond){
                        TimerJSON[device][nr].RememberTimer = getState("javascript.1.Timer." + path + ".Editor.RememberTimer").val;
                        TimerJSON[device][nr].ConditionsNr = getState("javascript.1.Timer." + path + ".Editor.ConditionsNr").val;
                        for (let i = 1; i <= 3; i++){
                            if (i <= TimerJSON[device][nr].ConditionsNr){
                                TimerJSON[device][nr].Conditions[i].ConditionStr = getState("javascript.1.Timer." + path + ".Editor.Condition" + i).val; 
                                TimerJSON[device][nr].Conditions[i].CondState = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "State").val; 
                                TimerJSON[device][nr].Conditions[i].CondComp = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Comp").val; 
                                TimerJSON[device][nr].Conditions[i].CondValue = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Value").val; 
                            } else {
                                TimerJSON[device][nr].Conditions[i].ConditionStr = ""; 
                                TimerJSON[device][nr].Conditions[i].CondState = ""; 
                                TimerJSON[device][nr].Conditions[i].CondComp = ""; 
                                TimerJSON[device][nr].Conditions[i].CondValue = ""; 
                            }
                        }
                    }
                    // Schedule setzen bzw. löschen wenn sowohl Zeiten als auch Beidngungen nicht für Gruppe gelten sollen
                    if (!copyAll && !copyCond) {
                        TimerJSON[device][nr].ConditionsTrue = condEval(TimerJSON[device][nr]);
                        autoScheduler(TimerJSON, device, nr);
                        setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                    }
                }
        
                if (copyAll || copyCond){
                    Object.keys(TimerJSON).forEach(function(key) {
                        device = key;
                        for(let nr = 1; nr <= length(TimerJSON[key]); nr++) {
                            if (TimerJSON[device][nr]["Gruppe"] == group){
                                if (copyAll){
                                    setState("javascript.1.Timer." + path + ".Editor.CopyAll", false);
                                    TimerJSON[device][nr].Zeit = getState("javascript.1.Timer." + path + ".Editor.Zeit").val;
                                    TimerJSON[device][nr].Std = getState("javascript.1.Timer." + path + ".Editor.Std").val;
                                    TimerJSON[device][nr].Min = getState("javascript.1.Timer." + path + ".Editor.Min").val;
                                    TimerJSON[device][nr].Cron = getState("javascript.1.Timer." + path + ".Editor.Cron").val;
                                    TimerJSON[device][nr].Sollwert = getState("javascript.1.Timer." + path + ".Editor.Sollwert").val;
                                    TimerJSON[device][nr].TageVIS = getState("javascript.1.Timer." + path + ".Editor.TageVIS").val;
                                    TimerJSON[device][nr].CronTage = getState("javascript.1.Timer." + path + ".Editor.CronTage").val;
                                    TimerJSON[device][nr].Astro = getState("javascript.1.Timer." + path + ".Editor.DropDownAstro").val;
                                    TimerJSON[device][nr].Random = getState("javascript.1.Timer." + path + ".Editor.Random").val;
                                    TimerJSON[device][nr].RandPM = getState("javascript.1.Timer." + path + ".Editor.RandPM").val;
                                    TimerJSON[device][nr].Offset = getState("javascript.1.Timer." + path + ".Editor.Offset").val;
                                    TimerJSON[device][nr].OffsetPM = getState("javascript.1.Timer." + path + ".Editor.OffsetPM").val;
                                    TimerJSON[device][nr].Mo = getState("javascript.1.Timer." + path + ".Editor.WTagMo").val;
                                    TimerJSON[device][nr].Di = getState("javascript.1.Timer." + path + ".Editor.WTagDi").val;
                                    TimerJSON[device][nr].Mi = getState("javascript.1.Timer." + path + ".Editor.WTagMi").val;
                                    TimerJSON[device][nr].Do = getState("javascript.1.Timer." + path + ".Editor.WTagDo").val;
                                    TimerJSON[device][nr].Fr = getState("javascript.1.Timer." + path + ".Editor.WTagFr").val;
                                    TimerJSON[device][nr].Sa = getState("javascript.1.Timer." + path + ".Editor.WTagSa").val;
                                    TimerJSON[device][nr].So = getState("javascript.1.Timer." + path + ".Editor.WTagSo").val;
                                }
                                if (copyCond){
                                    setState("javascript.1.Timer." + path + ".Editor.CopyCond", false);
                                    TimerJSON[device][nr].RememberTimer = getState("javascript.1.Timer." + path + ".Editor.RememberTimer").val;
                                    TimerJSON[device][nr].ConditionsNr = getState("javascript.1.Timer." + path + ".Editor.ConditionsNr").val;
                                    for (let i = 1; i <= 3; i++){
                                        if (i <= TimerJSON[device][nr].ConditionsNr){
                                            TimerJSON[device][nr].Conditions[i].ConditionStr = getState("javascript.1.Timer." + path + ".Editor.Condition" + i).val; 
                                            TimerJSON[device][nr].Conditions[i].CondState = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "State").val; 
                                            TimerJSON[device][nr].Conditions[i].CondComp = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Comp").val; 
                                            TimerJSON[device][nr].Conditions[i].CondValue = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Value").val; 
                                        } else {
                                            TimerJSON[device][nr].Conditions[i].ConditionStr = ""; 
                                            TimerJSON[device][nr].Conditions[i].CondState = ""; 
                                            TimerJSON[device][nr].Conditions[i].CondComp = ""; 
                                            TimerJSON[device][nr].Conditions[i].CondValue = ""; 
                                        }
                                    }
                                }
                            }
                        }
                    });
                    setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                    setTimeout(recalc,100);
                }
                
                // Dialog schließen
                dialogCtrl("close");
        
                // Zeit für Timer Fokus wird neu gestartet
                delFocusOnTimer(true);
                
            });
        
        
            // Button Abbrechen
            on({id: "javascript.1.Timer." + path + ".ButtonAbbrechen", change: "any", ack: false}, function (obj) {
                // Dialog schließen
                dialogCtrl("close");
        
                // Zeit für Timer Fokus wird neu gestartet
                delFocusOnTimer(true);
            });
        
        // ENDE Subscribtions für EDITOR
        // #############################
        // Sonstige funktionale Trigger
        
            // Trigger zur Erstellung der Tabelle in VIS
            on({id: "javascript.1.Timer." + path + ".TimerJSON", change: "ne"}, function (obj) {
                tableMain(0);
            });
        
            // Bedingungen für Timer werden auf Änderung geprüft (Trigger auf Array aus Aufzählung in "deviceCond")
            on({id: condition_members, change: "any"}, function (obj) {
                updateCond();
            });
        
        
            // Trigger auf Sollwerte/Devices: Wenn Sollwert geändert, dann löschen des Hintergrund-Timer (falls vorhanden)
            on({id: device_members, change: "ne"}, function (obj) {
                // Zurücksetzen gemerkter Timer aus "subscribesList{}" wenn Device getriggert wurde
                let device = obj.common.name;
                if (subscribesList[device] > 0){
                    subscribesList[device] = 0;
                    if(stdLog) console.log("Aktiver Background-Timer für \"" + device + "\" gelöscht: Device wurde extern verändert!");
                    if(debugLog) console.log("Timer im Hintergrund: " + JSON.stringify(subscribesList));
                    tableMain(500);
                }
            });
        
        }
        // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        // +++++++++++++++++++++  initiales Erstellen und Schreiben der Objekte im State nur beim ersten Start ++++++++++++++++++
        // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        
        
        createState("Timer." + path + ".ConditionJSON", "", {
            name: 'JSON für Conditions in VIS',
            desc: 'JSON für Conditions in VIS',
            type: 'string',
            role: 'value',
            unit: ''
        });
        createState("Timer." + path + ".ResetBackgroundTimers", false, {
            type: "boolean", 
            role: "button"
        });
        createState("Timer." + path + ".MaterialDialogWidgetOpen", false, {
            type: "boolean", 
            role: "state"
        });
        createState("Timer." + path + ".ErrorMsg", "", {
            type: "string", 
            role: "state"
        });
        createState("Timer.ActiveTable");
        createState("Timer." + path + ".ButtonAbbrechen");
        createState("Timer." + path + ".NextDevice");
        createState("Timer." + path + ".FilterDevice");
        createState("Timer." + path + ".NextDevices");
        createState("Timer." + path + ".clickTarget");
        createState("Timer." + path + ".dblClickTarget");
        createState("Timer." + path + ".AstroJSON");
        createState("Timer." + path + ".DropDownDevice");
        createState("Timer." + path + ".DropDownGruppe");
        createState("Timer." + path + ".Editor.DropDownMinutes");
        createState("Timer." + path + ".Editor.Gruppe");
        createState("Timer." + path + ".Editor.Del");
        createState("Timer." + path + ".Editor.OK");
        createState("Timer." + path + ".Editor.CopyAll");
        createState("Timer." + path + ".Editor.CopyCond");
        createState("Timer." + path + ".Editor.DropDownAstro");
        createState("Timer." + path + ".Editor.DropDownAstroWerte");
        createState("Timer." + path + ".Editor.DropDownAstroTexte");
        createState("Timer." + path + ".Editor.Device");
        createState("Timer." + path + ".Editor.Nummer");
        createState("Timer." + path + ".TimerJSON");
        createState("Timer." + path + ".Editor.Aktiv");
        createState("Timer." + path + ".Editor.Zeit");
        createState("Timer." + path + ".Editor.Cron");
        createState("Timer." + path + ".Editor.TageVIS");
        createState("Timer." + path + ".Editor.CronTage");
        createState("Timer." + path + ".Editor.WTagMo");
        createState("Timer." + path + ".Editor.WTagDi");
        createState("Timer." + path + ".Editor.WTagMi");
        createState("Timer." + path + ".Editor.WTagDo");
        createState("Timer." + path + ".Editor.WTagFr");
        createState("Timer." + path + ".Editor.WTagSa");
        createState("Timer." + path + ".Editor.WTagSo");
        createState("Timer." + path + ".Editor.Std");
        createState("Timer." + path + ".Editor.Min");
        createState("Timer." + path + ".Editor.Sollwert");
        createState("Timer." + path + ".Editor.SollwertDropDown");
        createState("Timer." + path + ".Editor.Random");
        createState("Timer." + path + ".Editor.RandPM");
        createState("Timer." + path + ".Editor.Offset");
        createState("Timer." + path + ".Editor.OffsetPM");
        createState("Timer." + path + ".Editor.RememberTimer");
        createState("Timer." + path + ".Editor.ConditionKeyDropDown");
        createState("Timer." + path + ".Editor.Condition");
        createState("Timer." + path + ".Editor.ConditionsNr");
        createState("Timer." + path + ".Editor.Condition1");
        createState("Timer." + path + ".Editor.Cond1State");
        createState("Timer." + path + ".Editor.Cond1Comp");
        createState("Timer." + path + ".Editor.Cond1Value");
        createState("Timer." + path + ".Editor.Cond1Result");
        createState("Timer." + path + ".Editor.Condition2");
        createState("Timer." + path + ".Editor.Cond2State");
        createState("Timer." + path + ".Editor.Cond2Comp");
        createState("Timer." + path + ".Editor.Cond2Value");
        createState("Timer." + path + ".Editor.Cond2Result");
        createState("Timer." + path + ".Editor.Condition3");
        createState("Timer." + path + ".Editor.Cond3State");
        createState("Timer." + path + ".Editor.Cond3Comp");
        createState("Timer." + path + ".Editor.Cond3Value");
        createState("Timer." + path + ".Editor.Cond3Result");
        
        var DefaultInhalte = {
            "1":
                {"ObjID": "",
                "DeviceNr": "", // wird für cron-schedules genutzt
                "Aktiv": false,
                "Zeit":"10:00",
                "Std": "10",
                "Min": "00",
                "Sollwert":"100", // Sollwert der Geräte
                "TageVIS": "täglich", // Für Anzeige
                "CronTage": "0,1,2,3,4,5,6",
                "Cron": "0 10 * * 0,1,2,3,4,5,6",
                "Astro":"manuell",
                "Gruppe": grpNames.split(";")[0],
                "Random":"0",
                "RandPM":"pm",
                "Offset":"0",
                "OffsetPM":"m",
                "RememberTimer": false,
                "ConditionsNr": "0",
                "ConditionsTrue": true,
                "Conditions":{
                    "1":{
                        "ConditionStr": "",
                        "CondState": "",
                        "CondComp": "==",
                        "CondValue": ""
                    },
                    "2":{
                        "ConditionStr": "",
                        "CondState": "",
                        "CondComp": "==",
                        "CondValue": ""
                    },
                    "3":{
                        "ConditionStr": "",
                        "CondState": "",
                        "CondComp": "==",
                        "CondValue": ""
                    }
                },
                "Mo": true,
                "Di": true,
                "Mi": true,
                "Do": true,
                "Fr": true,
                "Sa": true,
                "So": true,
                },
            "2":
                {"ObjID": "",
                "DeviceNr": "", // wird für cron-schedules genutzt
                "Aktiv": false,
                "Zeit":"19:00",
                "Std": "19",
                "Min": "00",
                "Sollwert": "0", // Sollwert der Geräte
                "TageVIS": "täglich", // Für Anzeige
                "CronTage": "0,1,2,3,4,5,6",
                "Cron": "0 19 * * 0,1,2,3,4,5,6",
                "Astro":"manuell",
                "Gruppe": grpNames.split(";")[1],
                "Random":"0",
                "RandPM":"pm",
                "Offset":"0",
                "OffsetPM":"m",
                "RememberTimer": false,
                "ConditionsTrue": true,
                "ConditionsNr": "0",
                "Conditions":{
                    "1":{
                        "ConditionStr": "",
                        "CondState": "",
                        "CondComp": "==",
                        "CondValue": ""
                    },
                    "2":{
                        "ConditionStr": "",
                        "CondState": "",
                        "CondComp": "==",
                        "CondValue": ""
                    },
                    "3":{
                        "ConditionStr": "",
                        "CondState": "",
                        "CondComp": "==",
                        "CondValue": ""
                    }
                },
                "Mo": true,
                "Di": true,
                "Mi": true,
                "Do": true,
                "Fr": true,
                "Sa": true,
                "So": true,
                },
        };
        
        function main () {
            var dropDownListe = "";
            var devName;
            var TimerJSON = {};
            var idCounter = 0;
            if (debugLog) stdLog = true;
            if (!showTimerNr && !showSymbol){showTimerNr = true;}
            // ConditionJSON wird mit jedem Start neu eingelesen
            createConditionsJSON();
            setTimeout(updateCond,500);
            setMinutesDropDown();
            setAstro();
            if (getState("javascript.1.Timer." + path + ".TimerJSON").val === null) {
                // Erste Initialisierung falls Objekte noch nicht existieren
                console.warn("States werden neu erstellt! Script bitte erneut starten!");
                for(var i = 0; i < device_members.length; i++) {
                    devName = getObject(device_members[i]).common.name;
                    dropDownListe += devName + ";";
                    TimerJSON[devName] = jsonCopy(DefaultInhalte);
                    TimerJSON[devName][1].ObjID = TimerJSON[devName][2].ObjID = device_members[i];
                    TimerJSON[devName][1].DeviceNr = TimerJSON[devName][2].DeviceNr = idCounter;
                    if (getObject(device_members[i]).common.type == "boolean"){
                        TimerJSON[devName][1].Sollwert = "An";
                        TimerJSON[devName][2].Sollwert = "Aus";
                    }
                    idCounter += 2;
                }
                dropDownListe = dropDownListe.slice(0, dropDownListe.length - 1); // Entfernen letztes Semikolon
                setState("javascript.1.Timer." + path + ".DropDownDevice", dropDownListe, true);
                setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON)); //rückschreiben der Einträge in State
            }
            else {
                TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
                dropDownListe = getState("javascript.1.Timer." + path + ".DropDownDevice").val;
                // Wenn dropdown leer weil neu vom Skript-Update, erst json komplett einlesen
                if (getState("javascript.1.Timer." + path + ".DropDownDevice").val === null) {
                    dropDownListe = "";
                    Object.keys(TimerJSON).forEach(function(key) {
                        dropDownListe += key + ";";
                    });
                    dropDownListe = dropDownListe.slice(0, dropDownListe.length - 1); // Entfernen letztes Semikolon
                    setState("javascript.1.Timer." + path + ".DropDownDevice", dropDownListe, true);
                }
                // Check ob neue Device hinzugekommen sind
                var addedDevice = false;
                for(var i = 0; i < device_members.length; i++) {
                    devName = getObject(device_members[i]).common.name;
                    if(!TimerJSON.hasOwnProperty(devName)){
                        addedDevice = true;
                        console.log("Device # " + devName + " # fehlt und wird neu hinzugefügt!");
                        // Zunächst DropDownListe für Devices erweitern
                        dropDownListe += ";" + devName;
                        // Device mit DefaultInhalt erstellen
                        TimerJSON[devName] = jsonCopy(DefaultInhalte);
                        TimerJSON[devName][1].ObjID = TimerJSON[devName][2].ObjID = device_members[i];
                        if (getObject(device_members[i]).common.type == "boolean"){
                            TimerJSON[devName][1].Sollwert = "An";
                            TimerJSON[devName][2].Sollwert = "Aus";
                        }
                    }
                }
                // Für Erweiterung der JSON-Objekte nach Skript-Update
                var flagGroup = false
                Object.keys(TimerJSON).forEach(function(key) {
                    for(let i = 1; i <= length(TimerJSON[key]); i++) {
                        // Key "Gruppe" neu hinzufügen
                        if(!TimerJSON[key][i].hasOwnProperty("Gruppe")){
                            flagGroup = true
                            TimerJSON[key][i]["Gruppe"] = grpNames.split(";")[0];
                        }
                        // Key "RememberTimer" neu hinzufügen
                        if(!TimerJSON[key][i].hasOwnProperty("RememberTimer")){
                            TimerJSON[key][i]["RememberTimer"] = false;
                        }
                    };
                });
                if (flagGroup){
                    setState("javascript.1.Timer." + path + ".DropDownGruppe", grpNames, true);
                }
        
                // Schedules werden immer nach Start des Skripts automatisch erstellt
                // recalc Funktion nicht erlaubt, da JSON in State nicht aktuell sein muss (z.B. neue Devices in Aufzählung erkannt)
                Object.keys(TimerJSON).forEach(function(key) {
                    subscribesList[key] = 0;
                    for(let i = 1; i <= length(TimerJSON[key]); i++) {
                        TimerJSON[key][i].DeviceNr = idCounter;
                        TimerJSON[key][i].ConditionsTrue = condEval(TimerJSON[key][i]);
                        autoScheduler(TimerJSON, key, i);
                    };
                    idCounter += 2;
                });
                // States sichern
                setState("javascript.1.Timer." + path + ".DropDownDevice", dropDownListe, true); 
                setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON)); //rückschreiben der Einträge in State
            }
            setState("javascript.1.Timer." + path + ".DropDownGruppe", grpNames, true); 
            //set default Filter
            setState("javascript.1.Timer." + path + ".FilterDevice", "Alle"); 
            for (var firstKey in TimerJSON) break;
            setTimeout(activateSchedules,1000); // Crons aktivieren
            setTimeout(activateTrigger,1000);   // Trigger aktivieren
            setTimeout(setState, 1500, "javascript.1.Timer." + path + ".Editor.Device", firstKey);
            setTimeout(setState, 1800, "javascript.1.Timer." + path + ".Editor.Nummer", 1)
            tableMain(2000);
        }
        setTimeout(main,1500);
        
        
        //##########################################################################################################
        // NACHFOLGEND die Erstellung der JSON Tabelle und HTML Aufbereitung
        //##########################################################################################################
        
        function buildTableArray() {
            var tabelle = [];
            var tmpAstro, tmpRand, tmpOffset, dropDownListe, dropDownItems, key, tmpClass;
            var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
            // Reihenfolge aus "Timer.Devices.DropDownDevice" entnehmen, statt alle Keys durchlaufen 
            dropDownListe = getState("javascript.1.Timer." + path + ".DropDownDevice").val;
            dropDownItems = dropDownListe.split(";");
            for(var i = 0; i < dropDownItems.length; i++) {
                key = dropDownItems[i];
                for (var j = 1; j <= Object.keys(TimerJSON[key]).length; j++) {
                    var tempJsonNr = TimerJSON[key][j]; // JSON Objekt wird umkopiert um im weiteren Verlauf ohne Indizes zu arbeiten
                    var scheduleNr = ( tempJsonNr.DeviceNr * 10 ) + j;
                    tmpAstro = tempJsonNr.Astro == "manuell" ? (tempJsonNr.Std + ":" + tempJsonNr.Min) : tempJsonNr.Astro;
                    if (tempJsonNr.Random != "0"){
                        switch(tempJsonNr.RandPM){
                            case "p": tmpRand = "+" + " " + tempJsonNr.Random; break;
                            case "m": tmpRand = "-" + " " + tempJsonNr.Random; break;
                            case "pm": tmpRand = "±" + " " + tempJsonNr.Random; break;
                        }
                    } else {
                        tmpRand = " "
                    }
                    if (tempJsonNr.Offset != "0"){
                        switch(tempJsonNr.OffsetPM){
                            case "p": tmpOffset = "+" + " " + tempJsonNr.Offset; break;
                            case "m": tmpOffset = "-" + " " + tempJsonNr.Offset; break;
                        }
                    } else {
                        tmpOffset = " "
                    }
                    if (tempJsonNr.Astro == "manuell"){tmpOffset = " "}
                    tmpClass = "";
                    if (tempJsonNr.RememberTimer){
                        if (tempJsonNr.ConditionsTrue){
                            tmpClass = "class=timer-remember-green-glow";
                        } else{
                            if (subscribesList[key] == scheduleNr){
                                tmpClass = "class=timer-remember-red-blink";
                            } else {
                                tmpClass = "class=timer-remember-red-glow";
                            }
                        }
                    }
                    tabelle.push({
                        "Geraet"    : key,
                        "Nr"        : j, 
                        "Aktiv"     : tempJsonNr.Aktiv,
                        "Gruppe"    : tempJsonNr.Gruppe,
                        "CondNr"    : tempJsonNr.ConditionsNr,
                        "CondTrue"  : tempJsonNr.ConditionsTrue,
                        "Class"     : tmpClass,
                        "Zeit"      : tempJsonNr.Zeit,
                        "Tage"      : tempJsonNr.TageVIS,
                        "Sollwert"  : tempJsonNr.Sollwert,
                        "Astro"     : tmpAstro,
                        "offset"    : tmpOffset,
                        "rand"      : tmpRand,
                    });
                }
            }
            return tabelle;
        }
        
        
        // Button überträgt bei Klick den Wert:
        // Pfad~~~Gerät~Timer-Nummer~Funktion
        function getButtonCode(buttonVal, buttonText, color){
            var htmlButton;
            htmlButton = "<button style=\""
        		        + "border:none; "
        		        + "background-color:transparent; "
        		        + "color:" + color + "; "
        		        + "font-size:1.0em; "
        		        + "text-align:left"
        		        + "\" value=\"" + buttonVal + "\""
                        + "onclick=\"setOnClick" + path + "(this.value)\""
                        + "ondblclick=\"setOnDblClick" + path + "(this.value)\">" + buttonText + "</button>"
            return htmlButton
        }
        
        // Button überträgt bei Klick den Wert:
        // Pfad~~~Gerät~Timer-Nummer~Funktion
        function getFakeButtonCode(buttonText){
            var htmlButton;
            htmlButton = "<button style=\""
        		        + "border:none; "
        		        + "background-color:transparent; "
                        + "color:white; "
        		        + "font-size:1.0em; "
        		        + "text-align:left\" >" + buttonText + "</button>"
            return htmlButton
        }
        
        
        function jsonToHtml(tabelle, withDevice) {
        	var html = "";
            var astro = "";
            var tmpTage = "";
            var backgroundTimerExists = false;
        
            // Klasse für das Blinken der Bedingungen wenn Timer im Hintergrund
            html = "<style>\n"
                + ".timer-remember-green-glow {\n"
                + "filter: drop-shadow(0px 0px 2px #4CAF50) drop-shadow(0px 0px 2px #4CAF50) drop-shadow(0px 0px 4px #4CAF50)\n"
                + "}\n"
                + ".timer-remember-red-glow {\n"
                + "filter: drop-shadow(0px 0px 2px #F44336) drop-shadow(0px 0px 2px #F44336) drop-shadow(0px 0px 4px #F44336)\n"
                + "}\n"
                + ".timer-remember-red-blink {\n"
                + "animation: timer-remember-blink-ani 1s linear infinite;\n"
                + "}\n"
                + "@keyframes timer-remember-blink-ani {\n"
                + "0%,50% {filter: drop-shadow(0px 0px 4px #F44336) drop-shadow(0px 0px 4px #F44336) drop-shadow(0px 0px 4px #F44336); }\n"
                + "51% {filter: none;}\n"
                + "}\n"
                + "</style>\n";
        
            // Prüfen ob aktive Background-Timer existieren, damit "Bed" in Überschrift entsprechend dargestellt werden kann
            for (var i=0; i<tabelle.length; i++){
                if (tabelle[i].Class == "class=timer-remember-red-blink"){
                    backgroundTimerExists = true;
                }
            }
        
            // Überschriften der Tabelle
            html += "<table style='font-size:" + fontSize + "em;width:100%;'><thead>\n<tr>\n"
                 + ( withDevice  ?  "<th style='text-align:left;'>" + getFakeButtonCode("Device") + "</th>\n"  : "" ) /* Wenn splitHTML true ist, dann keine Spalte "Device" */
                 + ( showTimerNr ?  "<th style='text-align:left;'>" + getFakeButtonCode("Nr") + "</th>\n"      : "" )
                 + ( showSymbol  ?  "<th style='text-align:left;'>" + getFakeButtonCode("Aktiv") + "</th>\n"   : "" )
                 /* Nachfolgend die Darstellung von "Bed" in zwei Zeilen zwecks lesbarkeit */
                 + ( !backgroundTimerExists  ?  "<th style='text-align:left;'>" + getFakeButtonCode("Bed") + "</th>\n"   : "" )
                 + ( backgroundTimerExists  ?  "<th class=timer-remember-red-blink style='text-align:left;'>" + getButtonCode("all~0~cond", "Bed", "red") + "</th>\n"   : "" )
                 + ( showGroupNr ?  "<th style='text-align:left;'>Grp</th>\n"                                : "" )
                 + "<th style='text-align:left;'>" + getFakeButtonCode("Zeit") + "</th>\n"
                 + "<th style='text-align:left;'>Wochentag</th>\n"
                 + "<th style='text-align:left;'>Soll</th>\n"
                 + "<th style='text-align:left;'>Astro</th>\n"
                 + "<th style='text-align:left;'>Offset</th>\n"
                 + "<th style='text-align:left;'>Zufall</th>\n"
                 + "</tr></thead><tbody>\n\n";
        
            // Erstellen der einzelnen Tabelleneinträge
        	for (var i=0; i<tabelle.length; i++){
        
            	html += "<tr>\n"
                      + ( withDevice  ? "<td>" + getButtonCode(tabelle[i].Geraet + "~" + tabelle[i].Nr + "~dev", tabelle[i].Geraet, "white") + "</td>\n" : "" )
                      + ( showTimerNr ? "<td>" + getButtonCode(tabelle[i].Geraet + "~" + tabelle[i].Nr + "~nr", tabelle[i].Nr, ( tabelle[i].Aktiv ? "#00FF7F"  : "#FF0000" )) + "</td>\n" : "" )
                      + ( showSymbol  ? "<td>" + getButtonCode(tabelle[i].Geraet + "~" + tabelle[i].Nr + "~nr", ( tabelle[i].Aktiv ? symbEnab : symbDisab ), "white") + "</td>\n" : "" )
                      + ( tabelle[i].CondNr > 0 ? "<td " + tabelle[i].Class + ">" + getButtonCode(tabelle[i].Geraet + "~" + tabelle[i].Nr + "~cond", "*" + tabelle[i].CondNr, ( tabelle[i].CondTrue ? "#00FF7F" : "#FF0000" ) ) + "</td>\n" : "<td> </td>\n" )
                      + ( showGroupNr ? "<td>" + tabelle[i].Gruppe + "</td>" : "" )
                      + "<td>" + getButtonCode(tabelle[i].Geraet + "~" + tabelle[i].Nr + "~time", tabelle[i].Zeit, "white") + "</td>\n"
                      + "<td>" + tabelle[i].Tage + "</td>\n"
                      + "<td>" + tabelle[i].Sollwert + "</td>\n"
                      + "<td>" + tabelle[i].Astro + "</td>\n"
                      + "<td>" + tabelle[i].offset + "</td>\n"
                      + "<td>" + tabelle[i].rand + "</td>\n"
                      + "</tr>\n\n";
            }
            html += "</body></table>\n\n";
        
            // Funktionen für Klick und Doppel-Klick werden direkt im html Code der Buttons hinterlegt    
            html += "<script>\n"
                  + "\n"
                  + "function setOnClick" + path + "(val) {\n"
                  + "var objID = \"javascript.1.Timer." + path + ".clickTarget\";\n"
                  + "servConn.setState(objID, val);}\n"
                  + "\n"
                  + "function setOnDblClick" + path + "(val) {\n"
                  + "var objID = \"javascript.1.Timer." + path + ".dblClickTarget\";\n"
                  + "servConn.setState(objID, val);}\n"
                  + "\n"
                  + "</script>";
        
        	return html;
        }
        
        var tableTimeout;
        function tableMain(delay) {
            
            if (tableTimeout) {clearTimeout(tableTimeout); tableTimeout = null;}
        
            tableTimeout = setTimeout(function(){
                
                var device;
                var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
                var filterDev = getState("javascript.1.Timer." + path + ".FilterDevice").val;
                var tabelle = buildTableArray();
        
                if (splitHTML) {
                    var tableIndex = 0;
                    Object.keys(TimerJSON).forEach(function(device) {
                        var timerNr = Object.keys(TimerJSON[device]).length;
                        var strState = "Timer." + path + ".HTML_" + device;
                        // Falls Device mit einem Punkt endet, muss dieser entfernt werden um State zu erstellen
                        strState = (strState.slice(-1) == ".") ? strState.slice(0, strState.length - 1) : strState;
                        createState(strState);
                        setState(strState, jsonToHtml(tabelle.slice(tableIndex, tableIndex + timerNr), false));
                        tableIndex += timerNr;
                    });
                }
                else {
                    var strState = "javascript.1.Timer." + path + ".TableHTML";
                    createState("Timer." + path + ".TableHTML");
                    if (filterDev == "Alle") {
                        setState(strState, jsonToHtml(tabelle, true));
                    } else {
                        var filteredTable = [];
                        for (let i = 0; i < tabelle.length; i++){
                            device = tabelle[i]["Geraet"];
                            if ( device == filterDev){
                                filteredTable.push(tabelle[i]);
                            }
                        }
                        setState(strState, jsonToHtml(filteredTable, false));
                    }
                }
            },delay);
        }
        
        
        

        und original view verhält sich genauso...
        starte nachher malneu wenn ich zu Hause bin...

        GlasfaserG 1 Antwort Letzte Antwort
        0
        • smartboartS smartboart

          Könnte es daran liegen, dass ich dasPopup mittels state umschalte, das habe ich im Script angepasst...
          Aber macht keinen Sinn...

          // Funktion zum Öffnen/Schließen der Dialogbox (EDIT-PopUp)
          function dialogCtrl(cmd){
              if (cmd == "open"){
              setState('javascript.0.Vis.ViewWechsel',43);
                  // Für MaterialDesignWidget
                  setState("javascript.1.Timer." + path + ".MaterialDialogWidgetOpen", true);
              }
              else if (cmd == "close"){
              setState('javascript.0.Vis.ViewWechsel',42);
                  // Für MaterialDesignWidget
                  setState("javascript.1.Timer." + path + ".MaterialDialogWidgetOpen", false);
              }
          }
          

          (arbeite mit view in widget)

          /* ####################################################################### */
          // Nachfolgende Einträge zwingend anpassen:
          
          // Eine Aufzählung für Geräte (z.B. Rollläden) angeben:
          var deviceEnum = "enum.functions.Zeitgesteuert";
          
          // Eine Aufzählung für States der Bedingungen:
          var deviceCond = "enum.functions.timerconditions";
          
          
          /* ####################################################################### */
          // Nachfolgende Einträge nur optional anpassen:
          
          // Hauptpfad des Timers unter javascript.1
          var path = "Devices";
          
          // Schrittweite der Minuten in DropDown für manuelle Zeitangabe
          var minIncrement = 2;
          
          // HTML Code aufteilen? Wenn pro Gerät eine eigenständige HTML Tabelle verwendet werden soll
          var splitHTML = false;
          // false: HTML Code wird vollständig in "Timer.Devices.TableHTML" geschrieben
          // true : HTML Code nach Geräten aufteilen -> "Timer.Devices.HTML_<GeräteName>" 
          
          // Log-Modus
          var stdLog = true; // elementare Ausgabe, Schedule erstellt/gelöscht
          var debugLog = false; // zusätzliche Ausgaben, z.B. zu den Bedingungen
          // Logausgabe manipulieren, um z.B. Log-Parser zu verwenden
          var logPraefix = "Timer: ";
          var logSuffix = "";
          
          // Anzeige nächster Timer mit Sollwerten?
          // true: mit Sollwerten
          // false: ohne Sollwerten
          var showValues = true;
          
          // Timer-Nr und/oder Symbol anzeigen? (true = Sichtbar)
          // Mindestens eine Spalte muss true sein, sonst wird Timer-Nummer angezeigt.
          var showTimerNr = true;
          var showSymbol = true;
          
          // Spalte für Gruppennummer anzeigen?
          var showGroupNr = true;
          
          // Symbole für Timer-Status in Tabelle, kopiert aus: https://emojipedia.org/
          var symbDisab = "❌";
          var symbEnab = "️️✅";
          
          // Schriftgröße innerhalb Tabelle (Einheit "em")
          var fontSize = 1.0;
          
          // Soll-Werte für States, die nicht als Bools hinterlegt sind. Zahlenwerte können hier angepasst/gelöscht werden.
          var sollDropDown = "0;5;10;15;20;30;40;50;60;70;80;90;100;Auf;Ab";
          // Soll-Werte für Bool-States true/false (werden automatisch erkannt)
          var sollDropDownBool = "An;Aus";
          // Falls Soll-Werte individualisiert wurden, bitte nachfolgend die realen Werte hinterlegen/anpassen
          var sollWertMapping = {"Auf": 100, "Ab": 0, "An": true, "Aus": false}
          
          // Gruppennamen für Timer
          // Es können auch mehr oder weniger als 10 Namen angegeben werden, 
          var grpNames = "A;B;C;D;E;F;G;H;I;J";
          
          // Funktionen in Tabelle mit Einfach-Klick (= true) oder Doppel-Klick (= false) ausführen?
          // default: oneClick = false
          var oneClick = true;
          
          // Minimaler Zeitversatz zwischen Ansteuerung der Geräte (in Millisekunden)
          // Realer Zeitversatz ergibt sich aus "Tabellenposition des Geräts (beginnend bei 0) * sendWithOffset
          // Vorteilhaft, falls Signale bei zeitgleicher Ansteuerung verloren gehen könnten (z.B. 433MHz Aktoren)
          var sendWithOffset = 200;
          
          /* ####################################################################### */
          /* #### BEI MANUELLEM UPDATE, SKRIPT UNTERHALB DIESER ZEILE ERSETZEN! #### */
          
          /*
          * Weitere Infos: https://forum.iobroker.net/topic/23346/vorlage-variable-zeitsteuerung-mit-vis-editor
          * Autor: Giuseppe Sicilia (Forum: GiuseppeS)
          
          * Changelog 30.06.2020 (Skript)
          * - Codeoptimierung, Error-Handling wenn Buttons nicht gemäß Standard genutzt werden
          * - Bugfix: Alle gemerkten Timer werden nun ausgeführt, wenn Bedingungen nächträglich erfüllt werden.
          *
          * Changelog 20.06.2020 (Skript)
          * - State "javascript.1.Timer.AtHomeSimul.TableJSON" gelöscht, wird nicht mehr benötigt.
          * - Bugfix: Bei Tabellen mit modifizierter Reihenfolge funktioniert nun auch die Filterung korrekt!
          *
          * Changelog 17.06.2020 (Skript)
          * - Steuerung der Geräte mit Versatz möglich. Neue (optionale) Variable "sendWithOffset"
          *
          * Changelog 30.05.2020 (Skript & VIS)
          * - Bugfix "ErrorMessage" im PopUp
          * - Neue Variable im Edit-Bereich: logSuffix
          *   Kann genutzt werden, um Log-Ausgabe noch flexibler anzupassen (Ist für manuelles Update nicht zwingend neu anzulegen)
          *
          * Changelog 29.05.2020 v2
          * - DialogBox Button "Abbrechen" ersetzt durch Standard-Button. Schließen des Dialogs über Skript
          *   (Bugfix bei Verwendung von MD-Adapter Dialog)
          *
          * Changelog 29.05.2020
          * - Steuerung des Dialog Widgets aus "Material Design Adapter" über State "javascript.1.Timer." + path + ".MaterialDialogWidgetOpen"
          * - Meldung bei fehlerhaften Bedingungen in PopUp
          *   -> Bei manuellem Update, Widgets "Berechnete Uhrzeit" und "ErrorMsg" aus Export übernehmen
          *
          * Changelog 26.04.2020
          * - Minütliches Flackern der nächsten Timer abgestellt. Nur noch bei Änderungen gibts ein Flackern
          * - Bedingungen werden während der Eingabe ausgewertet und farblich im Editor hervorgehoben (Danke an HelmutS)
          * - Wenn Bedingungen leer oder fehlerhaft sind, wird das PopUp-Fenster nicht geschlossen. Log wird ausgegeben.
          * 
          * Changelog 15.04.2020
          * - PopUp-Editor ohne zusätzliche PNGs für Tage, rein als HTML-Button (siehe Screenshot)
          * - Funktionen innerhalb Tabelle können nun wahlweise mit Einfach-Klick statt Doppelklick ausgeführt werden
          *   (außer in Spalte "Device", diese Spalte dient als Haupt-Markierung für ADD/DEL, hier wird immer mit Doppelklick der Editor geöffnet)
          *   Neue Variable "oneClick" im Variablen-Bereich hinzugefügt (Default: oneClick = false)
          * - Neue Variablen müssen ab dieser Version bei einem manuellen Update nicht zwingend übernommen werden!
          *   Falls neue Variablen im oberen Bereich nicht existieren, wird der Default-Wert der neuen Variable angenommen.
          *   So soll sichergestellt werden, dass neue Funktionen die Funktionsweise älterer Versionen nicht beeinflusst  
          *
          * Changelog 29.03.2020 v2
          * - Sollwerte können über Variablen-Feld oben einfacher angepasst werden
          * - Zusätzlich zwei Variablen im oberen Feld: "sollDropDownBool" und "sollWertMapping"
          *
          * Changelog 29.03.2020
          * - Einzelne Aktive Background-Timer aus "Timer merken" können vorzeitig über Doppelklick auf die Bedingungszahl gelöscht werden
          * - Das Löschen aller aktiven Background-Timer kann über ein Doppelklick auf Tabellen-Überschrift "Bed" oder
          *   separat über das neue State "javascript.1.Timer.Devices.ResetBackgroundTimers" durchgeführt werden.
          *
          * Changelog 26.03.2020
          * - Bugfix für font-size der Tabelle (wurde zuvor nicht korrekt übernommen)
          * - Gruppenzuordnungen unterteilt in "Zeiten" und "Bedingungen"
          * - Funktion "Timer merken" hinzugefügt:
          *   Timer wird gemerkt für den Fall dass die Bedingungen erst nach Trigger-Uhrzeit "true" werden.
          *   Timer werden aus der "Merkliste" vorzeitig gelöscht, falls sich die Ziel Objekt-ID anderweitig ändert
          *   oder der nächste Timer des Devices aktiviert wird.
          * - "javascript.1.Timer.Devices.Editor.DropDownNr" wird seit Touch-Bedienung nicht mehr benötigt. Kann gelöscht werden.
          *
          * Changelog 03.02.2020
          * - Bugfix Gruppenzuordnung
          *
          * Changelog 30.01.2020
          * - Optik PopUp für Gruppenzuordnung angepasst
          * - Namen der Gruppen im Skript nach oben gesetzt, für bessere Anpassung
          *
          * Changelog 26.01.2020
          * - Timer werden Gruppen zugeordnet (aktuell statisch bis zu 10 Gruppen möglich)
          * - Änderungen über alle Timer einer Gruppe verteilen möglich
          * - Gruppennummer kann optional in Tabelle angezeigt werden
          * - Neue Spalte mit Symbolen (Aktiv-Status) anzeigbar und darüber auch manipulierbar (Doppelklick)
          * - Hinweis: Entweder Symbole oder Timer-Nummer muss angezeigt werden um Timer über Doppelklick zu aktivieren/deaktivieren
          * - Schriftgröße über Variable "fontSize" änderbar
          * - HTML-Code-Generierung aufgeräumt
          * - PopUp mit DropDown für Gruppenzuordnung erweitert
          *
          * Changelog 24.01.2020
          * - Bugfix bzgl. Doppelklick zum Editieren und Aktivieren/Deaktivieren der einzelnen Timer
          *
          * Changelog 19.01.2020
          * - Auswahl des Timers direkt über Tabelle (onclick event)
          * - Edit mit Doppelklick Gerät oder Ist-Zeit (dblclick event)
          * - Aktivieren/Deaktivieren des Timers über Doppelklick auf Timer-Nummer
          * - DropDown in VIS zu Filter umgestellt, default = kein Filter (DropDown auch löschbar!)
          * - Filter bei Split-Darstellung ohne Funktion
          *
          * Changelog 15.09.2019
          * - Sollwerte in PopUp werden je nach Device mit An/Aus oder Zahlenwerte befüllt
          *   PopUp wurde entsprechend angepasst
          * - Einführung neuer Variable logPraefix
          *
          * Changelog 14.09.2019
          * - Bugfix bei Anzeige NextTimer
          * - Weniger Fehlerausgaben bei erstem Start des Skripts
          * 
          * Changelog 08.09.2019
          * - Minuten Incremente in PopUp Editor über Variable steuerbar
          * 
          * Changelog 07.09.2019
          * - Logausgabe wenn Timer auslöst wenn stdLog oder debugLog = true ist.
          * 
          * Changelog 29.08.2019
          * - Ein neuer Timer wird auf Basis des zuletzt angewählten Timers erstellt (bezieht sich ausschließlich auf die Nummer!)
          * 
          * Changelog 29.08.2019
          * - Änderung von Bedingungen werden direkt bei Änderung getriggert (ohne minütliches Cron)
          * - Hauptpfad des Timers variabel umstellbar ohne Suchen/Ersetzen
          * - Zukünftige Timer werden in States "javascript.1.Timer. + path + .NextDevice(s)" ausgegeben
          * 
          * Changelog 28.07.2019
          * - einige Konsole Ausgaben mit switch debugLog bzw stdLog versehen
          * - Reihenfolge in Tabelle über Reihenfolge in "Timer.Devices.DropDownDevice" änderbar ("Timer.Devices.Editor.DropDownDevice" kann gelöscht werden)
          */
          
          /* ####################################################################### */
          
          
          var device_members = getObject(deviceEnum).common.members;
          var condition_members = getObject(deviceCond).common.members;
          var TageJSON = {1: "Mo", 2: "Di", 3: "Mi", 4: "Do", 5: "Fr", 6: "Sa", 7: "So"};
          var dblClickBlocker = false
          
          
          // Falls neue Variablen fehlen, ab hier defaults setzen:
          if (typeof oneClick === 'undefined') { var oneClick = false; }
          if (typeof logSuffix === 'undefined') { var logSuffix = ""; }
          if (typeof sendWithOffset === 'undefined') { var sendWithOffset = 200; }
          
          
          // Anzahl Keys in JSON ermitteln
          function length(obj) {
              return Object.keys(obj).length;
          }
          
          // Einfaches Kopieren von JSON Objekten
          function jsonCopy(src) {
              return JSON.parse(JSON.stringify(src));
          }
          
          // Erstellen des DropDown-Inhalts für Minuten Auswahl in PopUp Editor
          function setMinutesDropDown() {
          	var strDropDown = "";
              for (var i = 0; i < 60; i += minIncrement) {
                  var tmp = (i <= 9 ? "0" : "") + i;
                  strDropDown += tmp + ";";
              } 
              strDropDown = strDropDown.slice(0, strDropDown.length - 1); // Entfernen letztes Semikolon
              setState("javascript.1.Timer." + path + ".Editor.DropDownMinutes", strDropDown);
          }
          
          
          // Zeigt alle kommenden Timer in Liste (optimiert für DropDown-Liste)
          function nextTimer(){
              var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
              var timeStamp, checkTime, firstKey, splitKey, newKey;
              var allTimer = {};
              Object.keys(TimerJSON).forEach(function(key) {
                  for(var i = 1; i <= length(TimerJSON[key]); i++) {
                      // Hier werden alle Timer durchlaufen
                      if (TimerJSON[key][i].Aktiv && TimerJSON[key][i].ConditionsTrue){
                          // Timer ist Aktiv und Bedingungen sind erfüllt
                          var tmp = TimerJSON[key][i].CronTage.split(",");
                          for(var j = 0; j < tmp.length; j++) {
                              tmp[j] = (tmp[j] == 0 ? 7 : tmp[j]);
                              timeStamp = tmp[j] + " " + TimerJSON[key][i].Zeit;
                              if (showValues){
                                  if (allTimer.hasOwnProperty(timeStamp)) {allTimer[timeStamp] += ", " + key + " (" + TimerJSON[key][i].Sollwert + ")"}
                                  else {allTimer[timeStamp] = key + " (" + TimerJSON[key][i].Sollwert + ")";}
                              } else {
                                  if (allTimer.hasOwnProperty(timeStamp)) {allTimer[timeStamp] += ", " + key}
                                  else {allTimer[timeStamp] = key;}
                              }
                          }
                      }
                  }
              });
              var allTimerLength = length(allTimer);
              var d = new Date();
              var actDay = (d.getDay() == 0 ? 7 : d.getDay());
              var actTime = ('0' + d.getHours()).slice(-2) + ":" + ('0' + d.getMinutes()).slice(-2);
              var actTimeStamp = actDay + " " + actTime;
          
              if (allTimerLength == 0){
                  setState("javascript.1.Timer." + path + ".NextDevice", "Keine Timer");
                  if (getState("javascript.1.Timer." + path + ".NextDevices").val != "Keine Timer"){
                      setState("javascript.1.Timer." + path + ".NextDevices", "Keine Timer");
                  }
              }
              else if (allTimerLength == 1){
                  firstKey = Object.keys(allTimer)[0];
                  splitKey = firstKey.split(" ");
                  newKey = TageJSON[splitKey[0]] + " " + splitKey[1];
                  setState("javascript.1.Timer." + path + ".NextDevice", newKey + " - " + allTimer[firstKey]);
                  if (getState("javascript.1.Timer." + path + ".NextDevices").val != newKey + " - " + allTimer[firstKey]){
                      setState("javascript.1.Timer." + path + ".NextDevices", newKey + " - " + allTimer[firstKey]);
                  }
              }
              else {
                  var listBefore = "";
                  var listAfter = "";
                  var listComplete = "";
                  Object.keys(allTimer).sort().forEach(function(key) {
                      if (key > actTimeStamp){
                          splitKey = key.split(" ");
                          newKey = ( parseInt(splitKey[0]) == actDay ? "" : TageJSON[splitKey[0]]) + " " + splitKey[1];
                          listAfter += newKey + " - " + allTimer[key] + ";" ;
                      }
                      else {
                          splitKey = key.split(" ");
                          newKey = TageJSON[splitKey[0]] + " " + splitKey[1];
                          listBefore += newKey + " - " + allTimer[key] + ";" ;
                  };
                  });
                  listComplete = listAfter + listBefore;
                  setState("javascript.1.Timer." + path + ".NextDevice", listComplete.split(";")[0]);
                  if (getState("javascript.1.Timer." + path + ".NextDevices").val != listComplete.slice(0, listComplete.length - 1)){
                      setState("javascript.1.Timer." + path + ".NextDevices", listComplete.slice(0, listComplete.length - 1));
                  }
              }
          }
          
          
          // Initiales erstellen des JSON für States der Bedingungen
          // Aufruf erfolgt aus main-Function
          function createConditionsJSON(){
              var ConditionJSON = {};
              var dropDownListe = "";
              // ConditionJSON wird bei jedem Skript-Start neu erstellt da keine relevanten Alt-Daten vorhanden sind
              for(var i = 0; i < condition_members.length; i++) {
                  var condName = getObject(condition_members[i]).common.name;
                  ConditionJSON[condName] = condition_members[i];
              }
              // DropDown-Liste: Für alphabetische Sortierung nicht in for-Schleife oben integrierbar
              Object.keys(ConditionJSON).sort().forEach(function(key) {
                  dropDownListe += key + ";";
              });
              dropDownListe = dropDownListe.slice(0, dropDownListe.length - 1); // Entfernen letztes Semikolon
              setState("javascript.1.Timer." + path + ".Editor.ConditionKeyDropDown", dropDownListe);
              setState("javascript.1.Timer." + path + ".ConditionJSON", JSON.stringify(ConditionJSON));
          }
          
          
          // Bedingungen der Timer werden geprüft und in TimerJSON geschrieben
          // Verwendung für VIS: HTML-Tabelle und Next-Timer
          // Falls Bedingungen für "gemerkte" Time true werden, dann wird Device gesteuert
          function updateCond(){
              var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
              Object.keys(TimerJSON).forEach(function(key) { // Alle Devices werden durchlaufen
                  for(let i = 1; i <= length(TimerJSON[key]); i++) { // Alle Timer innerhalb Devices werden durchlaufen
                      let deviceNr = TimerJSON[key][i]["DeviceNr"];
                      let scheduleNr = deviceNr * 10 + i;
                      let result = condEval(TimerJSON[key][i]);
                      TimerJSON[key][i].ConditionsTrue = result;
          
                      // Wenn Bedingungen true sind und aktueller Timer als gemerkter Timer aktiv ist, dann ausführen
                      if (result && subscribesList[key] == scheduleNr){
                          setTimeout(function(){
                              subscribesList[key] = 0;
                              if(debugLog) console.log("Timer im Hintergrund: " + JSON.stringify(subscribesList));
                              let sollwert =  TimerJSON[key][i]["Sollwert"];
                              if(stdLog) console.log(logPraefix + key + " (" + sollwert + ") -> Timer ausgeführt. Bedingung(en) nachträglich erfüllt!" + logSuffix);
                              setState(TimerJSON[key][i]["ObjID"], mapping(sollwert));
                          }, deviceNr*sendWithOffset/2)
                      }
                  }
              });
              setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
          }
          
          
          // Führt Evaluation der Bedingungen durch und gibt true oder false zurück
          // Aufruf nur aus Funktion autoScheduler
          function condEval(DeviceJSON){
              var sumEval, strEval1, strEval2, strEval3;
              var conditionsNr = parseInt(DeviceJSON.ConditionsNr);
              switch(conditionsNr){
                  case 0:
                      sumEval = true;
                      break;
                  case 1:
                      strEval1 = eval(DeviceJSON.Conditions[1].ConditionStr);
                      sumEval = strEval1;
                      break;
                  case 2:
                      strEval1 = eval(DeviceJSON.Conditions[1].ConditionStr);
                      strEval2 = eval(DeviceJSON.Conditions[2].ConditionStr);
                      sumEval = (strEval1 && strEval2);
                      break;
                  case 3:
                      strEval1 = eval(DeviceJSON.Conditions[1].ConditionStr);
                      strEval2 = eval(DeviceJSON.Conditions[2].ConditionStr);
                      strEval3 = eval(DeviceJSON.Conditions[3].ConditionStr);
                      sumEval = (strEval1 && strEval2 && strEval3);
                      break;
              }
              return sumEval;
          }
          
          
          // Sollwert Mapping zu Variable "sollWertMapping" im oberen Bereich
          function mapping(sollwert){
              // Verschiedene Mappings aus Editor-DropDown zu realen States erstellen
              if(sollWertMapping.hasOwnProperty(sollwert)){
                  sollwert = sollWertMapping[sollwert]
              } else (sollwert = parseInt(sollwert));
          
              return sollwert
          }
          
          // Schedules werden variabel erstellt; zunächst wird gelöscht und wenn create=true wird neu erstellt
          // Auswertung der Bedingungen erfolgt erst bei Ausführung des Schedules
          var subscribesList = {}; // -> Gespeicherte Timer werden hier notiert; genutzt für Darstellung in VIS und Abfragen. Beispiel-Aufbau: {"Rollo_Balkon": 22, ...}
          var cronArr = [];        // -> Enthält Schedules über alle Devices und Timer, die aktiv sind
          function autoScheduler(TimerJSON, device, nr) {
              var condArr = [];
              var deviceNr = TimerJSON[device][nr].DeviceNr;
              var scheduleNr = (deviceNr * 10) + nr;
              var aktiv = TimerJSON[device][nr].Aktiv;
              var cronString = TimerJSON[device][nr].Cron;
              var objID = TimerJSON[device][nr].ObjID;
              var conditionState = TimerJSON[device][nr].ConditionsTrue;
              var rememberState = TimerJSON[device][nr].RememberTimer;
              var conditionsNr = parseInt(TimerJSON[device][nr].ConditionsNr);
              var sollwert = TimerJSON[device][nr].Sollwert;
          
              // Timer zunächst immer löschen (weil täglich neue Astro-Zeiten und Randoms genutzt werden sollen)
              if (cronArr[scheduleNr]){
                  if(stdLog) console.log("Schedule für \"" + device + " #" + nr + "\" [" + scheduleNr + "] gelöscht!");
                  clearSchedule(cronArr[scheduleNr]); cronArr[scheduleNr] = null;
              } else{
                  if(debugLog) console.log("Schedule für \"" + device + " #" + nr + "\" (" + scheduleNr + ") nicht vorhanden! Kein Löschen notwendig!");
              }
              // Timer neu erstellen falls AKTIV == true
              if (aktiv){
          
                  if(stdLog) console.log("Schedule aktiviert: \"" + device + " #" + nr + "\": [" + scheduleNr + "] | " + cronString + " | " + objID + " | " + sollwert);
                  
                  cronArr[scheduleNr] = schedule(cronString, function(){
                      if( condEval(TimerJSON[device][nr]) ){
                          setTimeout(function(){
                              if(stdLog) console.log(logPraefix + device + " (" + sollwert + ")" + logSuffix);
                              setState(objID, mapping(sollwert));
                              tableMain(500); // aktualisieren der Tabelle
                          }, deviceNr*sendWithOffset/2)
                      } else if (rememberState){
                          if(stdLog) console.log(logPraefix + device + " (" + sollwert + ") -> Timer gespeichert. Bedingung(en) noch nicht erfüllt!" + logSuffix);
                          subscribesList[device] = scheduleNr;
                          if(debugLog) console.log("Timer im Hintergrund: " + JSON.stringify(subscribesList));
                          tableMain(500);
                      } else {
                          if(stdLog) console.log(logPraefix + device + " (" + sollwert + ") -> Nicht ausgeführt. Bedingung(en) nicht erfüllt!" + logSuffix);
                          tableMain(500);
                      };
                  });
              } else {
                  // Falls Timer deaktiviert wird, während es gespeichert war... In Liste zurücksetzen!
                  if (subscribesList[device] == scheduleNr){
                      if(debugLog) console.log("Schedule für \"" + device + " #" + nr + "\" [" + scheduleNr + "] -> Timer aus Speicher entfernt!");
                      subscribesList[device] = 0;
                      if(debugLog) console.log("Timer im Hintergrund: " + JSON.stringify(subscribesList));
                  }
              }
          }
          
          
          // Background-Timer aus "Timer merken" löschen
          function resetBackgroundTimers(target){
              if (target == "all"){
                  Object.keys(subscribesList).forEach(function(device) {
                      if (subscribesList[device] > 0){
                          if(stdLog) console.log("Aktiver Background-Timer für \"" + device + "\" gelöscht!");
                          subscribesList[device] = 0;
                      }
                  });
              } else {
                  if (subscribesList[target] > 0){
                      if(stdLog) console.log("Aktiver Background-Timer für \"" + target + "\" gelöscht!");
                      subscribesList[target] = 0;
                  } else {
                      if(stdLog) console.log("Kein aktiver Background-Timer für \"" + target + "\" gefunden!");
                  }
              }
              if(debugLog) console.log("Timer im Hintergrund: " + JSON.stringify(subscribesList));
              tableMain(500);
          }
          
          
          // Cron Tage in Wochentage umwandlen und ggf. abkürzen mit "von - bis"
          function shortDays(cronTage) {
          	var cronSplit, cronLast;
          	var tageVIS = "";
          	
          	if (cronTage.substring(0,2) == ",0"){
              // 0 mit 7 ersetzen und ans Ende setzen
          	  cronTage = cronTage.substring(2,cronTage.length) + ",7";
          	}
          	// Erstes Komma entfernen
          	cronTage = cronTage.substring(1, cronTage.length);
          
              if (cronTage.length == 1){
                  tageVIS = TageJSON[cronTage];
              }
              else if (cronTage.length == 13){
                  tageVIS = "täglich";
              }
              else {
                  cronSplit = cronTage.split(",");
                  cronLast = cronSplit.length - 1;
                  // Wenn Anzahl der Elemente = mathematische Differenz dann "aufeinanderfolgend", also kürzen
                  if ((cronSplit[cronLast] - cronSplit[0]) == cronLast) {
                  tageVIS = TageJSON[cronSplit[0]] + " - " + TageJSON[cronSplit[cronLast]];
                  }
                  else {
                  for (var j = 0; j <= cronLast ; j++){
                      tageVIS += TageJSON[cronSplit[j]] + ", ";
                  }
                  // letztes Komma entfernen
                  tageVIS = tageVIS.substring(0, tageVIS.length-1);
                  }
              }
            return tageVIS;
          }
          
          // Input:  Minute + Stunde + Zufallsbereich (1 bis 59 Min) + Vorzeichen von Zufall
          // Return: Json Object mit Struktur von timeJSON, siehe unten
          function randomTime(min,std,rand,opt) {
              // Erstellung des JSON mit Vorbelegung der Keys
              var timeJSON = {"Zeit": "23:59", "Cron": "59 23 * * *", "Std": "23", "Min": "59"};
              if (rand > 0){
                  if (rand > 59) {rand = 59;}
                  if (opt === "pm"){var delta_min = Math.floor(Math.random() * (rand - (-1 * rand) + 1) + (-1 * rand));}
                  if (opt === "p"){var delta_min = Math.floor(Math.random() * (rand + 1));}
                  if (opt === "m"){var delta_min = Math.floor(Math.random() * (rand + 1) - rand);}
              	min += delta_min;
              	if (min >= 60){std++;min -= 60;}
              	else if (min < 0){std--;min += 60;}
              	if (std >= 24){std -= 24;}
              	else if (std < 0){std += 24;}
              }
          	timeJSON.Zeit = (std <= 9 ? "0" : "") + std + ":" + (min <= 9 ? "0" : "") + min;
          	timeJSON.Std = std;
          	timeJSON.Min = min;
          	timeJSON.Cron = min + " " + std + " *" + " * "; // Wochentage für Cron bewusst nicht vorhanden, wird später angehängt
              return timeJSON;
          }
          
          // Input timeJSON aus function randomTime + Offset + Vorzeichen von Offset
          // Output timeJSON mit verrechnetem Offset
          function offsetTime(randJSON,offset,opt) {
              var min = randJSON.Min, std = randJSON.Std, delta_min = 0;
              var timeJSON = {"Zeit": "23:59", "Cron": "59 23 * * *", "Std": "23", "Min": "59"};
              if (offset > 0){
                  if (offset > 59) {offset = 59;}
                  if (opt === "p"){ delta_min = offset }
                  if (opt === "m"){ delta_min = -1 * offset}
              	min += delta_min;
              	if (min >= 60){std++;min -= 60;}
              	else if (min < 0){std--;min += 60;}
              	if (std >= 24){std -= 24;}
              	else if (std < 0){std += 24;}
              }
          	timeJSON.Zeit = (std <= 9 ? "0" : "") + std + ":" + (min <= 9 ? "0" : "") + min;
          	timeJSON.Std = std;
          	timeJSON.Min = min;
          	timeJSON.Cron = min + " " + std + " *" + " * "; // Wochentage für Cron bewusst nicht vorhanden, wird später angehängt
              return timeJSON;
          }
          
          // Setzt die 3 Felder für Astro-DropDown Werte, Texte und das Json für spätere Berechnungen.
          function setAstro() {
              var strWerte = "manuell";
              var strTexte = "manuell";
              var AstroJSON = {};
              var tmpAstro;
              var astro_times = ["sunrise", "sunriseEnd", "goldenHourEnd", "solarNoon", "goldenHour", "sunsetStart", "sunset", "dusk", "nauticalDusk", "nadir", "nauticalDawn", "dawn"]
              var defaultJSON = {"Zeit" : "10:00", "Std" : 10, "Min" : 0};
              
              astro_times.forEach(function(entry) {
                  tmpAstro = entry;
                  var zeit = formatDate(getDateObject(getAstroDate(tmpAstro, undefined, 0)), "hh:mm");
                  var zeitSplit = zeit.split(':');
                  AstroJSON[tmpAstro] = jsonCopy(defaultJSON);
          		AstroJSON[tmpAstro].Zeit = zeit;
          		AstroJSON[tmpAstro].Std = parseInt(zeitSplit[0]);
          		AstroJSON[tmpAstro].Min = parseInt(zeitSplit[1]);
                  strTexte += ";" + tmpAstro + ", " + zeit;
                  strWerte += ";" + tmpAstro;
              });
          
          	setState("javascript.1.Timer." + path + ".Editor.DropDownAstroTexte", strTexte);
          	setState("javascript.1.Timer." + path + ".Editor.DropDownAstroWerte", strWerte);
          	setState("javascript.1.Timer." + path + ".AstroJSON", JSON.stringify(AstroJSON));
          }
          
          
          // recalc wird automatisch täglich ausgeführt und zieht Random und Astro neu an
          // Trigger erfolgt vorzugsweise nach Trigger für setAstro
          // recalc erfolgt auch wenn CopyAll im Editor PopUp aktiviert wird
          function recalc() {
              var CalcJSON = {};
              var astro, device, nr;
              var AstroJSON = JSON.parse(getState("javascript.1.Timer." + path + ".AstroJSON").val);
              var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
              Object.keys(TimerJSON).forEach(function(key) {
                  for(var i = 1; i <= length(TimerJSON[key]); i++) {
                      astro = TimerJSON[key][i].Astro;
                      device = key;
                      nr = i;
                      if (astro === "manuell" ){
                          CalcJSON = randomTime(parseInt(TimerJSON[key][i].Min,10),parseInt(TimerJSON[key][i].Std,10),TimerJSON[key][i].Random,TimerJSON[key][i].RandPM);
                      }
                      else {
                          CalcJSON = randomTime(parseInt(AstroJSON[astro].Min,10),parseInt(AstroJSON[astro].Std,10),TimerJSON[key][i].Random,TimerJSON[key][i].RandPM);
                          CalcJSON = offsetTime(CalcJSON,TimerJSON[key][i].Offset,TimerJSON[key][i].OffsetPM);
                      }
                      TimerJSON[key][i].Zeit = CalcJSON.Zeit;
                      TimerJSON[key][i].Cron = CalcJSON.Cron + TimerJSON[key][i].CronTage;
                      TimerJSON[key][i].ConditionsTrue = condEval(TimerJSON[key][i]);
                      autoScheduler(TimerJSON, key, i);
                  }
              });
              setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
              setState("javascript.1.Timer." + path + ".Editor.Nummer", "", true);
              setState("javascript.1.Timer." + path + ".Editor.Device", "", true);
          }
          
          
          // Auswahl des Timers wird automatisch zurückgesetzt; für dauerhaft bessere Optik und Schutz vor Fehleingaben
          var focusTimeOut;
          function delFocusOnTimer(option) {
              if (option){
                  if (focusTimeOut) {clearTimeout(focusTimeOut); focusTimeOut = null;}
                  focusTimeOut = setTimeout(function(){
                          setState("javascript.1.Timer." + path + ".Editor.Nummer", "", true);
                          setState("javascript.1.Timer." + path + ".Editor.Device", "", true);
                      }, 5000)
              } else {
                  if (focusTimeOut) {clearTimeout(focusTimeOut); focusTimeOut = null;}
              }
          }
          
          
          // Funktion zum Öffnen/Schließen der Dialogbox (EDIT-PopUp)
          function dialogCtrl(cmd){
              if (cmd == "open"){
              setState('javascript.0.Vis.ViewWechsel',43);
                  // Für MaterialDesignWidget
                  setState("javascript.1.Timer." + path + ".MaterialDialogWidgetOpen", true);
              }
              else if (cmd == "close"){
              setState('javascript.0.Vis.ViewWechsel',42);
                  // Für MaterialDesignWidget
                  setState("javascript.1.Timer." + path + ".MaterialDialogWidgetOpen", false);
              }
          }
          
          // ENDE DER HILFS-FUNKTIONEN
          // ##########################################################################################################
          // ##########################################################################################################
          // Trigger bzw. Schedules für Funktionen
          
          function activateSchedules(){
          
              // Astro-Zeiten werden täglich aktualisiert, anschließend neu Berechnung der Timer
              schedule('0 4 * * *', setAstro);
              schedule('1 4 * * *', recalc);
          
              // Darstellung zukünftiger Timer
              schedule('30 * * * * *', nextTimer);
          }
          
          // ENDE DER Trigger bzw. Schedules
          // ##########################################################################################################
          // ##########################################################################################################
          // NACHFOLGEND DIE TRIGGER AUS VIS
          
          function activateTrigger(){
          
          // Subscribtions für Klick-Events:
          
              // One-Click Aktion aus Tabelle zur Auswahl des Timers
              on({id: "javascript.1.Timer." + path + ".clickTarget", change: "any"}, function (obj) {
              	
                  if (dblClickBlocker) {return}; // Doppelte Ausführung One-Click bei Double-Click vermeiden
                  dblClickBlocker = true;
                  setTimeout(function(){
                      dblClickBlocker = false;
                  }, 600);
                  
                  if (debugLog) console.log("Klick aus Tabelle erkannt. Übergebener Wert: " + obj.state.val);
          
                  setState("javascript.1.Timer.ActiveTable", path);
                  var tmp = obj.state.val.split("~");
                  if (tmp[0] != "all") { // "all" ist ein Tag der Überschriften, daher folgt keine Timer-Auswahl
                      setState("javascript.1.Timer." + path + ".Editor.Device", tmp[0]);
                      setStateDelayed("javascript.1.Timer." + path + ".Editor.Nummer", parseInt(tmp[1]), 50, false);
                  }
          
                  if (oneClick == true){ // Wenn Tabellen Funktionen mit One-Click gewünscht werden ...
                      setTimeout(function(){
                          var btnSource = obj.state.val.split("~")[2]; // Button-Funktion wird eingelesen
          
                          if (btnSource == "time"){ // Edit-Dialog öffnen bei Doppelklick IST-Zeit-Button
                              dialogCtrl("open");
                              delFocusOnTimer(false); // Auswahl des Timers nicht automatisch zurücksetzen!
                          }
                          if (btnSource == "nr"){ // Aktivieren/Deaktivieren des Timers
                              setState("javascript.1.Timer." + path + ".Editor.Aktiv", !getState("javascript.1.Timer." + path + ".Editor.Aktiv").val);
                          }
                          if (btnSource == "cond"){ // Aktivieren/Deaktivieren des Timers
                              resetBackgroundTimers(obj.state.val.split("~")[0]);
                          }
                      }, 100)
                  }
              });
          
              // Double-Click Aktion aus Tabelle für Spezialfunktionen
              on({id: "javascript.1.Timer." + path + ".dblClickTarget", change: "any"}, function (obj) {
              	
                  if(debugLog) console.log("Doppelklick aus Tabelle erkannt. Übergebener Wert: " + obj.state.val);
                  var btnSource = obj.state.val.split("~")[2]; // Button-Funktion wird eingelesen
          
                  if (btnSource == "dev"){ // Edit-Dialog öffnen bei Doppelklick Geräte-Button
                      dialogCtrl("open");
                      delFocusOnTimer(false); // Auswahl des Timers nicht automatisch zurücksetzen!
                  }
                  if (oneClick == false){ // Restl. Doppelklick-Funktionen deaktivieren, wenn One-Click gewünscht wird
                      if (btnSource == "time"){ // Edit-Dialog öffnen bei Doppelklick IST-Zeit-Button
                          dialogCtrl("open");
                          delFocusOnTimer(false); // Auswahl des Timers nicht automatisch zurücksetzen!
                      }
                      if (btnSource == "nr"){ // Aktivieren/Deaktivieren des Timers
                          setState("javascript.1.Timer." + path + ".Editor.Aktiv", !getState("javascript.1.Timer." + path + ".Editor.Aktiv").val);
                      }
                      if (btnSource == "cond"){ // Aktivieren/Deaktivieren des Timers
                          resetBackgroundTimers(obj.state.val.split("~")[0]);
                      }
                  }
              });
          
          // ENDE Subscribtions für Klick-Events
          // ###################################
          // Subscribtions für MAINVIEW
          
              // Alle Backgroud-Timer aus "Timer merken" löschen
              on({id: "javascript.1.Timer." + path + ".ResetBackgroundTimers", change: "ne"}, function (obj) {
                  if (obj.state.val) {
                      resetBackgroundTimers("all"); // = Alle löschen
                      setStateDelayed("javascript.1.Timer." + path + ".ResetBackgroundTimers", false, 500, false);
                  }
              });
          
          
          
              // Device aus Filter-DropDown in VIS, triggert ausschließlich HTML-Darstellung
              on({id: "javascript.1.Timer." + path + ".FilterDevice", change: "ne", ack: false}, function (obj) {
                  tableMain(0);
              });
          
          
              // Trigger wenn Device-Fokus geändert wird
              on({id: "javascript.1.Timer." + path + ".Editor.Device", change: "any", ack: false}, function (obj) {   // auf Pulldownmenü das jeweilige device auslesen
                  var device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                  var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);       //einlesen der Einträge aus State
                  if (getObject(TimerJSON[device][1].ObjID).common.type == "boolean"){
                      setState("javascript.1.Timer." + path + ".Editor.SollwertDropDown", sollDropDownBool);
                  } else {
                      setState("javascript.1.Timer." + path + ".Editor.SollwertDropDown", sollDropDown);
                  }
              });
          
          
              // Trigger wenn Timer-Nummer vom Device aktualisiert wird 
              on({id: "javascript.1.Timer." + path + ".Editor.Nummer", change: "any", ack: false}, function (obj) {
                  var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
                  var device;
                  var nr = getState("javascript.1.Timer." + path + ".Editor.Nummer").val;
          
                  delFocusOnTimer(true);
          
                  if (nr == "+"){ // Neuer Timer soll hinzugefügt werden
                      // Error-Handling
                      if (getState("javascript.1.Timer." + path + ".Editor.Device").val == ""){
                          console.warn("Es wurde kein Timer gewählt. Bitte Timer wählen und Button 'ADD' innerhalb 5s betätigen")
                          return
                      }
                      device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                      var devlen = length(TimerJSON[device]);
                      var baseNr = obj.oldState.val;
          
                      if (baseNr == devlen){// Neuen Timer anhängen falls Basis für neuen Timer der letzte Timer war
                          nr = devlen + 1;
                          TimerJSON[device][nr] = TimerJSON[device][devlen];
                          // Falls kopierter Timer aktiv war wird der Neue auch direkt gesetzt
                          TimerJSON[device][nr].ConditionsTrue = condEval(TimerJSON[device][nr]);
                          autoScheduler(TimerJSON, device, nr);
                          setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                          setState("javascript.1.Timer." + path + ".Editor.Nummer", nr);
                      } else {
                          // Mittendrin einfügen: Alle Timer darüber um eine Position verschieben
                          // -> Timer zunächst deaktivieren, dann verschieben und erneut aktivieren, alte Position löschen
                          for(var j = devlen; j > baseNr; j--){
                              var tmpAktiv = TimerJSON[device][j].Aktiv;
                              TimerJSON[device][j].Aktiv = false
                              TimerJSON[device][j].ConditionsTrue = condEval(TimerJSON[device][j]);
                              autoScheduler(TimerJSON, device, j);
                              TimerJSON[device][j+1] = TimerJSON[device][j];
                              TimerJSON[device][j+1].Aktiv = tmpAktiv;
                              TimerJSON[device][j+1].ConditionsTrue = condEval(TimerJSON[device][j+1]);
                              autoScheduler(TimerJSON, device, j+1);
                              delete TimerJSON[device][j];
                          }
                          nr = baseNr + 1;
                          TimerJSON[device][nr] = TimerJSON[device][baseNr];
                          TimerJSON[device][nr].ConditionsTrue = condEval(TimerJSON[device][nr]);
                          autoScheduler(TimerJSON, device, nr);
                          setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                          setState("javascript.1.Timer." + path + ".Editor.Nummer", nr);
                      }
                  }
                  device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                  setState("javascript.1.Timer." + path + ".Editor.Aktiv", TimerJSON[device][nr].Aktiv, true);
                  setState("javascript.1.Timer." + path + ".Editor.Zeit", TimerJSON[device][nr].Zeit, true);
                  setState("javascript.1.Timer." + path + ".Editor.Cron", TimerJSON[device][nr].Cron, true);
                  setState("javascript.1.Timer." + path + ".Editor.TageVIS", TimerJSON[device][nr].TageVIS, true);
                  setState("javascript.1.Timer." + path + ".Editor.CronTage", TimerJSON[device][nr].CronTage, true);
                  setState("javascript.1.Timer." + path + ".Editor.WTagMo", TimerJSON[device][nr].Mo, true);
                  setState("javascript.1.Timer." + path + ".Editor.WTagDi", TimerJSON[device][nr].Di, true);
                  setState("javascript.1.Timer." + path + ".Editor.WTagMi", TimerJSON[device][nr].Mi, true);
                  setState("javascript.1.Timer." + path + ".Editor.WTagDo", TimerJSON[device][nr].Do, true);
                  setState("javascript.1.Timer." + path + ".Editor.WTagFr", TimerJSON[device][nr].Fr, true);
                  setState("javascript.1.Timer." + path + ".Editor.WTagSa", TimerJSON[device][nr].Sa, true);
                  setState("javascript.1.Timer." + path + ".Editor.WTagSo", TimerJSON[device][nr].So, true);
                  setState("javascript.1.Timer." + path + ".Editor.Std", TimerJSON[device][nr].Std, true);
                  setState("javascript.1.Timer." + path + ".Editor.Min", TimerJSON[device][nr].Min, true);
                  setState("javascript.1.Timer." + path + ".Editor.Sollwert", TimerJSON[device][nr].Sollwert, true);
                  setState("javascript.1.Timer." + path + ".Editor.DropDownAstro", TimerJSON[device][nr].Astro, true);
                  setState("javascript.1.Timer." + path + ".Editor.Random", TimerJSON[device][nr].Random, true);
                  setState("javascript.1.Timer." + path + ".Editor.RandPM", TimerJSON[device][nr].RandPM, true);
                  setState("javascript.1.Timer." + path + ".Editor.Offset", TimerJSON[device][nr].Offset, true);
                  setState("javascript.1.Timer." + path + ".Editor.OffsetPM", TimerJSON[device][nr].OffsetPM, true);
                  setState("javascript.1.Timer." + path + ".Editor.RememberTimer", TimerJSON[device][nr].RememberTimer, true);
                  setState("javascript.1.Timer." + path + ".Editor.ConditionsNr", TimerJSON[device][nr].ConditionsNr, true);
                  setState("javascript.1.Timer." + path + ".Editor.Gruppe", TimerJSON[device][nr].Gruppe, true);
          
                  for (let i = 1; i <= 3; i++){
                      setState("javascript.1.Timer." + path + ".Editor.Condition" + i, TimerJSON[device][nr].Conditions[i].ConditionStr, true);
                      setState("javascript.1.Timer." + path + ".Editor.Cond" + i + "State", TimerJSON[device][nr].Conditions[i].CondState, true);
                      setState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Comp", TimerJSON[device][nr].Conditions[i].CondComp, true);
                      setState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Value", TimerJSON[device][nr].Conditions[i].CondValue, true);
                      if (i <= TimerJSON[device][nr].ConditionsNr){
                          setState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Result", eval(TimerJSON[device][nr].Conditions[i].ConditionStr));
                      } else {
                          setState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Result", false);
                      }
                  }
          
              });
          
          
              // Wenn Status "Aktiv" geändert wird erfolgt sofortiges Sichern des TimerJSON
              on({id: "javascript.1.Timer." + path + ".Editor.Aktiv", change: "any", ack: false}, function (obj) {
                  
                  var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val); // einlesen der Einträge aus State
                  var device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                  var nr = getState("javascript.1.Timer." + path + ".Editor.Nummer").val;
                  TimerJSON[device][nr].Aktiv = getState("javascript.1.Timer." + path + ".Editor.Aktiv").val;
                  // Schedule setzen bzw. löschen
                  TimerJSON[device][nr].ConditionsTrue = condEval(TimerJSON[device][nr]);
                  autoScheduler(TimerJSON, device, nr);
                  setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON)); // rückschreiben in State
              });
          
          
              // Trigger des Delete-Button für Löschen eines Timer-Eintrags
              on({id: "javascript.1.Timer." + path + ".Editor.Del", change: "any", ack: false}, function (obj) {
                  
                  // Error-Handling
                  if (getState("javascript.1.Timer." + path + ".Editor.Device").val == ""){
                      console.warn("Es wurde kein Timer gewählt. Bitte Timer wählen und Button 'DEL' innerhalb 5s betätigen")
                      return
                  }
                  
                  var device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                  var nr = getState("javascript.1.Timer." + path + ".Editor.Nummer").val;
                  var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
                  var devlen = length(TimerJSON[device]);
          
                  // Zeit für Timer Fokus wird neu gestartet
                  delFocusOnTimer(true);
              
                  if (devlen > 1 ){
                      // Aktueller Timer wird gelöscht
                      TimerJSON[device][nr].Aktiv = false;
                      autoScheduler(TimerJSON, device, nr); // Timer wird gelöscht da Aktiv=false
                      delete TimerJSON[device][nr];
                      
                      if (nr < devlen){ // Wenn gelöschter Timer mittendrin, dann Rest verschieben
                          for (var j = nr; j < devlen ; j++) {
                              var tmpAktiv = TimerJSON[device][j+1].Aktiv
                              TimerJSON[device][j+1].Aktiv = false
                              autoScheduler(TimerJSON, device, j+1);          // Timer wird gelöscht da Aktiv=false
                              TimerJSON[device][j] = TimerJSON[device][j+1];  // Timer wird auf niedrigere Position kopiert
                              delete TimerJSON[device][j+1];
                              TimerJSON[device][j].Aktiv = tmpAktiv;
                              TimerJSON[device][j].ConditionsTrue = condEval(TimerJSON[device][j]);
                              autoScheduler(TimerJSON, device, j);
                          }
                      }
                      devlen = length(TimerJSON[device]);
                      setState("javascript.1.Timer." + path + ".Editor.Nummer", devlen);
                      setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                  }
                  else {
                      // Letzter Timer und somit Device wird gelöscht
                      TimerJSON[device][1].Aktiv = false;
                      autoScheduler(TimerJSON, device, 1); // Timer wird gelöscht da Aktiv=false
                      delete TimerJSON[device];
                      var dropDownListe = getState("javascript.1.Timer." + path + ".DropDownDevice").val;
          
                      console.log(dropDownListe.includes(";" + device + ";"));
          
                      if(dropDownListe.includes(device + ";")){
                          console.log("Device gefunden")
                          dropDownListe = dropDownListe.replace(device + ";", "");
                      }
                      else if(dropDownListe.includes(";" + device)){
                          console.log("Device am Ende gefunden")
                          dropDownListe = dropDownListe.replace(";" + device, "");
                      }
                      setState("javascript.1.Timer." + path + ".DropDownDevice", dropDownListe, true);
                      setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                      setState("javascript.1.Timer." + path + ".Editor.Device", Object.keys(TimerJSON)[0]);
                      setState("javascript.1.Timer." + path + ".Editor.Nummer", 1);
                  }
          
              });
          
          // ENDE Subscribtions für MAINVIEW
          // ###############################
          // Subscribtions für EDITOR
          
          
              // Trigger zum Erstellen der Bedingungen als String für späteres eval() (3x)
              on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1Comp' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1State' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1Value'), change: "any", ack: false}, function (obj) {
                  var ConditionJSON = JSON.parse(getState("javascript.1.Timer." + path + ".ConditionJSON").val);
                  var Cond1State = getState("javascript.1.Timer." + path + ".Editor.Cond1State").val
                  var Cond1Comp = getState("javascript.1.Timer." + path + ".Editor.Cond1Comp").val
                  var Cond1Value = getState("javascript.1.Timer." + path + ".Editor.Cond1Value").val
                  var strCond1 = "getState(\"" + ConditionJSON[Cond1State] + "\").val " + Cond1Comp + " " + Cond1Value
                  setState("javascript.1.Timer." + path + ".Editor.Condition1", strCond1);
                  if (Cond1State != "" && Cond1Comp != "" && Cond1Value != "") {
                      setState("javascript.1.Timer." + path + ".Editor.Cond1Result", eval(strCond1));
                  }
              });
          
              on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond2Comp' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond2State' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond2Value'), change: "any", ack: false}, function (obj) {
                  var ConditionJSON = JSON.parse(getState("javascript.1.Timer." + path + ".ConditionJSON").val);
                  var Cond2State = getState("javascript.1.Timer." + path + ".Editor.Cond2State").val
                  var Cond2Comp = getState("javascript.1.Timer." + path + ".Editor.Cond2Comp").val
                  var Cond2Value = getState("javascript.1.Timer." + path + ".Editor.Cond2Value").val
                  var strCond2 = "getState(\"" + ConditionJSON[Cond2State] + "\").val " + Cond2Comp + " " + Cond2Value
                  setState("javascript.1.Timer." + path + ".Editor.Condition2", strCond2);
                  if (Cond2State != "" && Cond2Comp != "" && Cond2Value != "") {
                      setState("javascript.1.Timer." + path + ".Editor.Cond2Result", eval(strCond2));
                  }
              });
          
              on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond3Comp' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond3State' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond3Value'), change: "any", ack: false}, function (obj) {
                  var ConditionJSON = JSON.parse(getState("javascript.1.Timer." + path + ".ConditionJSON").val);
                  var Cond3State = getState("javascript.1.Timer." + path + ".Editor.Cond3State").val
                  var Cond3Comp = getState("javascript.1.Timer." + path + ".Editor.Cond3Comp").val
                  var Cond3Value = getState("javascript.1.Timer." + path + ".Editor.Cond3Value").val
                  var strCond3 = "getState(\"" + ConditionJSON[Cond3State] + "\").val " + Cond3Comp + " " + Cond3Value
                  setState("javascript.1.Timer." + path + ".Editor.Condition3", strCond3);
                  if (Cond3State != "" && Cond3Comp != "" && Cond3Value != "") {
                      setState("javascript.1.Timer." + path + ".Editor.Cond3Result", eval(strCond3));
                  }
              });
          
          
              // Bei Änderung der Zeiten oder Astros im PopUp-View werden direkt End-Zeiten berechnet
              on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Std' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Min' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Random' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.RandPM' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Offset' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.OffsetPM' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.DropDownAstro'), change: "ne", ack: false}, function (obj) {
                  
                  var astro = getState("javascript.1.Timer." + path + ".Editor.DropDownAstro").val;
                  var CalcJSON = {};
                  var min, std;
                  
                  var rand = getState("javascript.1.Timer." + path + ".Editor.Random").val;
                  var randpm = getState("javascript.1.Timer." + path + ".Editor.RandPM").val;
                  var offset = getState("javascript.1.Timer." + path + ".Editor.Offset").val;
                  var offsetPM = getState("javascript.1.Timer." + path + ".Editor.OffsetPM").val;
                  
                  if (astro === "manuell" ){
                      min = getState("javascript.1.Timer." + path + ".Editor.Min").val;
                      std = getState("javascript.1.Timer." + path + ".Editor.Std").val;
                      CalcJSON = randomTime(parseInt(min,10),parseInt(std,10),rand,randpm);
                  }
                  else {
                      var AstroJSON = JSON.parse(getState("javascript.1.Timer." + path + ".AstroJSON").val);
                      CalcJSON = randomTime(AstroJSON[astro].Min,AstroJSON[astro].Std,rand,randpm);
                      CalcJSON = offsetTime(CalcJSON,offset,offsetPM);
                  }
                  // Eintrag für vollst. Cron aktualisieren
                  setState("javascript.1.Timer." + path + ".Editor.Zeit", CalcJSON.Zeit);
                  var CronTage = getState("javascript.1.Timer." + path + ".Editor.CronTage").val;
                  setState("javascript.1.Timer." + path + ".Editor.Cron", (CalcJSON.Cron + CronTage));
                  
              });
          
          
              // Änderung der ausgewählten Tage triggern sofort den Tage-String-Eintrag, so wird OK-Trigger übersichtlicher
              on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagDi' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagDo' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagFr' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagMi' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagMo' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagSa' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.WTagSo'), change: "ne"}, function (obj) {
                  
                  var strTage = "";  // Nur für Anzeige
                  var cronTage = ""; // Für setzen von Cron-Schedule
                  if(getState("javascript.1.Timer." + path + ".Editor.WTagSo").val){cronTage += ",0";}
                  if(getState("javascript.1.Timer." + path + ".Editor.WTagMo").val){cronTage += ",1";}
                  if(getState("javascript.1.Timer." + path + ".Editor.WTagDi").val){cronTage += ",2";}
                  if(getState("javascript.1.Timer." + path + ".Editor.WTagMi").val){cronTage += ",3";}
                  if(getState("javascript.1.Timer." + path + ".Editor.WTagDo").val){cronTage += ",4";}
                  if(getState("javascript.1.Timer." + path + ".Editor.WTagFr").val){cronTage += ",5";}
                  if(getState("javascript.1.Timer." + path + ".Editor.WTagSa").val){cronTage += ",6";}
                  
                  // String für VIS übersetzen, kürzen und setzen
                  setState("javascript.1.Timer." + path + ".Editor.TageVIS", shortDays(cronTage));
                  
                  cronTage = cronTage.substring(1, cronTage.length);
                  setState("javascript.1.Timer." + path + ".Editor.CronTage", cronTage);
          
                  // Cron-Eintrag anpassen
                  var cronSplit = getState("javascript.1.Timer." + path + ".Editor.Cron").val.split(" ");
                  setState("javascript.1.Timer." + path + ".Editor.Cron", (cronSplit[0] + " " + cronSplit[1] + " " + cronSplit[2] + " " + cronSplit[3] + " " + cronTage));
                  
              });
          
          
              // Trigger für OK-Button in PopUp-View; alle Werte werden in TimerJSON gesichert
              on({id: "javascript.1.Timer." + path + ".Editor.OK", change: "any", ack: false}, function (obj) {
                  
                  var device = getState("javascript.1.Timer." + path + ".Editor.Device").val;
                  var nr = getState("javascript.1.Timer." + path + ".Editor.Nummer").val;
                  var group = getState("javascript.1.Timer." + path + ".Editor.Gruppe").val;
                  var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val); //einlesen der Einträge aus State
                  var copyAll = getState("javascript.1.Timer." + path + ".Editor.CopyAll").val;
                  var copyCond = getState("javascript.1.Timer." + path + ".Editor.CopyCond").val;
                  var errorMsg = "";
          
                  TimerJSON[device][nr].Gruppe = getState("javascript.1.Timer." + path + ".Editor.Gruppe").val;
                  TimerJSON[device][nr].ConditionsNr = getState("javascript.1.Timer." + path + ".Editor.ConditionsNr").val;
          
                  // Validierung der Bedingungen vor Übernahme des Timers, wenn nicht valide, wird Fenster nicht geschlossen
                  var returnFlag = false;
                  for (let i = 1; i <= TimerJSON[device][nr].ConditionsNr; i++){
                      var condStr = getState("javascript.1.Timer." + path + ".Editor.Condition" + i).val;
                      try {
                          eval(condStr);
                          if (condStr == "") {
                              errorMsg += "Bedingung " + i + " wurde gesetzt, ist aber leer.\nBitte korrigieren und übernehmen!";
                              returnFlag = true;
                          }
                      } catch (e){
                          errorMsg += "Fehler in Bedingung: " + i + ".\nBitte korrigieren und übernehmen!";
                          returnFlag = true;
                      }
                  }
          
                  
                  if (returnFlag) {
                      console.log(errorMsg); 
                      setState("javascript.1.Timer." + path + ".ErrorMsg", "Bedingung(en) fehlerhaft!");
                      setTimeout(() => {
                          setState("javascript.1.Timer." + path + ".ErrorMsg", "");
                      }, 5000)
                      return
                  }
          
                  if (!copyAll || !copyCond) {
                      if (!copyAll){
                          TimerJSON[device][nr].Zeit = getState("javascript.1.Timer." + path + ".Editor.Zeit").val;
                          TimerJSON[device][nr].Std = getState("javascript.1.Timer." + path + ".Editor.Std").val;
                          TimerJSON[device][nr].Min = getState("javascript.1.Timer." + path + ".Editor.Min").val;
                          TimerJSON[device][nr].Cron = getState("javascript.1.Timer." + path + ".Editor.Cron").val;
                          TimerJSON[device][nr].Sollwert = getState("javascript.1.Timer." + path + ".Editor.Sollwert").val;
                          TimerJSON[device][nr].TageVIS = getState("javascript.1.Timer." + path + ".Editor.TageVIS").val;
                          TimerJSON[device][nr].CronTage = getState("javascript.1.Timer." + path + ".Editor.CronTage").val;
                          TimerJSON[device][nr].Astro = getState("javascript.1.Timer." + path + ".Editor.DropDownAstro").val;
                          TimerJSON[device][nr].Random = getState("javascript.1.Timer." + path + ".Editor.Random").val;
                          TimerJSON[device][nr].RandPM = getState("javascript.1.Timer." + path + ".Editor.RandPM").val;
                          TimerJSON[device][nr].Offset = getState("javascript.1.Timer." + path + ".Editor.Offset").val;
                          TimerJSON[device][nr].OffsetPM = getState("javascript.1.Timer." + path + ".Editor.OffsetPM").val;
                          TimerJSON[device][nr].Mo = getState("javascript.1.Timer." + path + ".Editor.WTagMo").val;
                          TimerJSON[device][nr].Di = getState("javascript.1.Timer." + path + ".Editor.WTagDi").val;
                          TimerJSON[device][nr].Mi = getState("javascript.1.Timer." + path + ".Editor.WTagMi").val;
                          TimerJSON[device][nr].Do = getState("javascript.1.Timer." + path + ".Editor.WTagDo").val;
                          TimerJSON[device][nr].Fr = getState("javascript.1.Timer." + path + ".Editor.WTagFr").val;
                          TimerJSON[device][nr].Sa = getState("javascript.1.Timer." + path + ".Editor.WTagSa").val;
                          TimerJSON[device][nr].So = getState("javascript.1.Timer." + path + ".Editor.WTagSo").val;
                      }
                      if (!copyCond){
                          TimerJSON[device][nr].RememberTimer = getState("javascript.1.Timer." + path + ".Editor.RememberTimer").val;
                          TimerJSON[device][nr].ConditionsNr = getState("javascript.1.Timer." + path + ".Editor.ConditionsNr").val;
                          for (let i = 1; i <= 3; i++){
                              if (i <= TimerJSON[device][nr].ConditionsNr){
                                  TimerJSON[device][nr].Conditions[i].ConditionStr = getState("javascript.1.Timer." + path + ".Editor.Condition" + i).val; 
                                  TimerJSON[device][nr].Conditions[i].CondState = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "State").val; 
                                  TimerJSON[device][nr].Conditions[i].CondComp = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Comp").val; 
                                  TimerJSON[device][nr].Conditions[i].CondValue = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Value").val; 
                              } else {
                                  TimerJSON[device][nr].Conditions[i].ConditionStr = ""; 
                                  TimerJSON[device][nr].Conditions[i].CondState = ""; 
                                  TimerJSON[device][nr].Conditions[i].CondComp = ""; 
                                  TimerJSON[device][nr].Conditions[i].CondValue = ""; 
                              }
                          }
                      }
                      // Schedule setzen bzw. löschen wenn sowohl Zeiten als auch Beidngungen nicht für Gruppe gelten sollen
                      if (!copyAll && !copyCond) {
                          TimerJSON[device][nr].ConditionsTrue = condEval(TimerJSON[device][nr]);
                          autoScheduler(TimerJSON, device, nr);
                          setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                      }
                  }
          
                  if (copyAll || copyCond){
                      Object.keys(TimerJSON).forEach(function(key) {
                          device = key;
                          for(let nr = 1; nr <= length(TimerJSON[key]); nr++) {
                              if (TimerJSON[device][nr]["Gruppe"] == group){
                                  if (copyAll){
                                      setState("javascript.1.Timer." + path + ".Editor.CopyAll", false);
                                      TimerJSON[device][nr].Zeit = getState("javascript.1.Timer." + path + ".Editor.Zeit").val;
                                      TimerJSON[device][nr].Std = getState("javascript.1.Timer." + path + ".Editor.Std").val;
                                      TimerJSON[device][nr].Min = getState("javascript.1.Timer." + path + ".Editor.Min").val;
                                      TimerJSON[device][nr].Cron = getState("javascript.1.Timer." + path + ".Editor.Cron").val;
                                      TimerJSON[device][nr].Sollwert = getState("javascript.1.Timer." + path + ".Editor.Sollwert").val;
                                      TimerJSON[device][nr].TageVIS = getState("javascript.1.Timer." + path + ".Editor.TageVIS").val;
                                      TimerJSON[device][nr].CronTage = getState("javascript.1.Timer." + path + ".Editor.CronTage").val;
                                      TimerJSON[device][nr].Astro = getState("javascript.1.Timer." + path + ".Editor.DropDownAstro").val;
                                      TimerJSON[device][nr].Random = getState("javascript.1.Timer." + path + ".Editor.Random").val;
                                      TimerJSON[device][nr].RandPM = getState("javascript.1.Timer." + path + ".Editor.RandPM").val;
                                      TimerJSON[device][nr].Offset = getState("javascript.1.Timer." + path + ".Editor.Offset").val;
                                      TimerJSON[device][nr].OffsetPM = getState("javascript.1.Timer." + path + ".Editor.OffsetPM").val;
                                      TimerJSON[device][nr].Mo = getState("javascript.1.Timer." + path + ".Editor.WTagMo").val;
                                      TimerJSON[device][nr].Di = getState("javascript.1.Timer." + path + ".Editor.WTagDi").val;
                                      TimerJSON[device][nr].Mi = getState("javascript.1.Timer." + path + ".Editor.WTagMi").val;
                                      TimerJSON[device][nr].Do = getState("javascript.1.Timer." + path + ".Editor.WTagDo").val;
                                      TimerJSON[device][nr].Fr = getState("javascript.1.Timer." + path + ".Editor.WTagFr").val;
                                      TimerJSON[device][nr].Sa = getState("javascript.1.Timer." + path + ".Editor.WTagSa").val;
                                      TimerJSON[device][nr].So = getState("javascript.1.Timer." + path + ".Editor.WTagSo").val;
                                  }
                                  if (copyCond){
                                      setState("javascript.1.Timer." + path + ".Editor.CopyCond", false);
                                      TimerJSON[device][nr].RememberTimer = getState("javascript.1.Timer." + path + ".Editor.RememberTimer").val;
                                      TimerJSON[device][nr].ConditionsNr = getState("javascript.1.Timer." + path + ".Editor.ConditionsNr").val;
                                      for (let i = 1; i <= 3; i++){
                                          if (i <= TimerJSON[device][nr].ConditionsNr){
                                              TimerJSON[device][nr].Conditions[i].ConditionStr = getState("javascript.1.Timer." + path + ".Editor.Condition" + i).val; 
                                              TimerJSON[device][nr].Conditions[i].CondState = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "State").val; 
                                              TimerJSON[device][nr].Conditions[i].CondComp = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Comp").val; 
                                              TimerJSON[device][nr].Conditions[i].CondValue = getState("javascript.1.Timer." + path + ".Editor.Cond" + i + "Value").val; 
                                          } else {
                                              TimerJSON[device][nr].Conditions[i].ConditionStr = ""; 
                                              TimerJSON[device][nr].Conditions[i].CondState = ""; 
                                              TimerJSON[device][nr].Conditions[i].CondComp = ""; 
                                              TimerJSON[device][nr].Conditions[i].CondValue = ""; 
                                          }
                                      }
                                  }
                              }
                          }
                      });
                      setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON));  // rückschreiben in State
                      setTimeout(recalc,100);
                  }
                  
                  // Dialog schließen
                  dialogCtrl("close");
          
                  // Zeit für Timer Fokus wird neu gestartet
                  delFocusOnTimer(true);
                  
              });
          
          
              // Button Abbrechen
              on({id: "javascript.1.Timer." + path + ".ButtonAbbrechen", change: "any", ack: false}, function (obj) {
                  // Dialog schließen
                  dialogCtrl("close");
          
                  // Zeit für Timer Fokus wird neu gestartet
                  delFocusOnTimer(true);
              });
          
          // ENDE Subscribtions für EDITOR
          // #############################
          // Sonstige funktionale Trigger
          
              // Trigger zur Erstellung der Tabelle in VIS
              on({id: "javascript.1.Timer." + path + ".TimerJSON", change: "ne"}, function (obj) {
                  tableMain(0);
              });
          
              // Bedingungen für Timer werden auf Änderung geprüft (Trigger auf Array aus Aufzählung in "deviceCond")
              on({id: condition_members, change: "any"}, function (obj) {
                  updateCond();
              });
          
          
              // Trigger auf Sollwerte/Devices: Wenn Sollwert geändert, dann löschen des Hintergrund-Timer (falls vorhanden)
              on({id: device_members, change: "ne"}, function (obj) {
                  // Zurücksetzen gemerkter Timer aus "subscribesList{}" wenn Device getriggert wurde
                  let device = obj.common.name;
                  if (subscribesList[device] > 0){
                      subscribesList[device] = 0;
                      if(stdLog) console.log("Aktiver Background-Timer für \"" + device + "\" gelöscht: Device wurde extern verändert!");
                      if(debugLog) console.log("Timer im Hintergrund: " + JSON.stringify(subscribesList));
                      tableMain(500);
                  }
              });
          
          }
          // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
          // +++++++++++++++++++++  initiales Erstellen und Schreiben der Objekte im State nur beim ersten Start ++++++++++++++++++
          // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
          
          
          createState("Timer." + path + ".ConditionJSON", "", {
              name: 'JSON für Conditions in VIS',
              desc: 'JSON für Conditions in VIS',
              type: 'string',
              role: 'value',
              unit: ''
          });
          createState("Timer." + path + ".ResetBackgroundTimers", false, {
              type: "boolean", 
              role: "button"
          });
          createState("Timer." + path + ".MaterialDialogWidgetOpen", false, {
              type: "boolean", 
              role: "state"
          });
          createState("Timer." + path + ".ErrorMsg", "", {
              type: "string", 
              role: "state"
          });
          createState("Timer.ActiveTable");
          createState("Timer." + path + ".ButtonAbbrechen");
          createState("Timer." + path + ".NextDevice");
          createState("Timer." + path + ".FilterDevice");
          createState("Timer." + path + ".NextDevices");
          createState("Timer." + path + ".clickTarget");
          createState("Timer." + path + ".dblClickTarget");
          createState("Timer." + path + ".AstroJSON");
          createState("Timer." + path + ".DropDownDevice");
          createState("Timer." + path + ".DropDownGruppe");
          createState("Timer." + path + ".Editor.DropDownMinutes");
          createState("Timer." + path + ".Editor.Gruppe");
          createState("Timer." + path + ".Editor.Del");
          createState("Timer." + path + ".Editor.OK");
          createState("Timer." + path + ".Editor.CopyAll");
          createState("Timer." + path + ".Editor.CopyCond");
          createState("Timer." + path + ".Editor.DropDownAstro");
          createState("Timer." + path + ".Editor.DropDownAstroWerte");
          createState("Timer." + path + ".Editor.DropDownAstroTexte");
          createState("Timer." + path + ".Editor.Device");
          createState("Timer." + path + ".Editor.Nummer");
          createState("Timer." + path + ".TimerJSON");
          createState("Timer." + path + ".Editor.Aktiv");
          createState("Timer." + path + ".Editor.Zeit");
          createState("Timer." + path + ".Editor.Cron");
          createState("Timer." + path + ".Editor.TageVIS");
          createState("Timer." + path + ".Editor.CronTage");
          createState("Timer." + path + ".Editor.WTagMo");
          createState("Timer." + path + ".Editor.WTagDi");
          createState("Timer." + path + ".Editor.WTagMi");
          createState("Timer." + path + ".Editor.WTagDo");
          createState("Timer." + path + ".Editor.WTagFr");
          createState("Timer." + path + ".Editor.WTagSa");
          createState("Timer." + path + ".Editor.WTagSo");
          createState("Timer." + path + ".Editor.Std");
          createState("Timer." + path + ".Editor.Min");
          createState("Timer." + path + ".Editor.Sollwert");
          createState("Timer." + path + ".Editor.SollwertDropDown");
          createState("Timer." + path + ".Editor.Random");
          createState("Timer." + path + ".Editor.RandPM");
          createState("Timer." + path + ".Editor.Offset");
          createState("Timer." + path + ".Editor.OffsetPM");
          createState("Timer." + path + ".Editor.RememberTimer");
          createState("Timer." + path + ".Editor.ConditionKeyDropDown");
          createState("Timer." + path + ".Editor.Condition");
          createState("Timer." + path + ".Editor.ConditionsNr");
          createState("Timer." + path + ".Editor.Condition1");
          createState("Timer." + path + ".Editor.Cond1State");
          createState("Timer." + path + ".Editor.Cond1Comp");
          createState("Timer." + path + ".Editor.Cond1Value");
          createState("Timer." + path + ".Editor.Cond1Result");
          createState("Timer." + path + ".Editor.Condition2");
          createState("Timer." + path + ".Editor.Cond2State");
          createState("Timer." + path + ".Editor.Cond2Comp");
          createState("Timer." + path + ".Editor.Cond2Value");
          createState("Timer." + path + ".Editor.Cond2Result");
          createState("Timer." + path + ".Editor.Condition3");
          createState("Timer." + path + ".Editor.Cond3State");
          createState("Timer." + path + ".Editor.Cond3Comp");
          createState("Timer." + path + ".Editor.Cond3Value");
          createState("Timer." + path + ".Editor.Cond3Result");
          
          var DefaultInhalte = {
              "1":
                  {"ObjID": "",
                  "DeviceNr": "", // wird für cron-schedules genutzt
                  "Aktiv": false,
                  "Zeit":"10:00",
                  "Std": "10",
                  "Min": "00",
                  "Sollwert":"100", // Sollwert der Geräte
                  "TageVIS": "täglich", // Für Anzeige
                  "CronTage": "0,1,2,3,4,5,6",
                  "Cron": "0 10 * * 0,1,2,3,4,5,6",
                  "Astro":"manuell",
                  "Gruppe": grpNames.split(";")[0],
                  "Random":"0",
                  "RandPM":"pm",
                  "Offset":"0",
                  "OffsetPM":"m",
                  "RememberTimer": false,
                  "ConditionsNr": "0",
                  "ConditionsTrue": true,
                  "Conditions":{
                      "1":{
                          "ConditionStr": "",
                          "CondState": "",
                          "CondComp": "==",
                          "CondValue": ""
                      },
                      "2":{
                          "ConditionStr": "",
                          "CondState": "",
                          "CondComp": "==",
                          "CondValue": ""
                      },
                      "3":{
                          "ConditionStr": "",
                          "CondState": "",
                          "CondComp": "==",
                          "CondValue": ""
                      }
                  },
                  "Mo": true,
                  "Di": true,
                  "Mi": true,
                  "Do": true,
                  "Fr": true,
                  "Sa": true,
                  "So": true,
                  },
              "2":
                  {"ObjID": "",
                  "DeviceNr": "", // wird für cron-schedules genutzt
                  "Aktiv": false,
                  "Zeit":"19:00",
                  "Std": "19",
                  "Min": "00",
                  "Sollwert": "0", // Sollwert der Geräte
                  "TageVIS": "täglich", // Für Anzeige
                  "CronTage": "0,1,2,3,4,5,6",
                  "Cron": "0 19 * * 0,1,2,3,4,5,6",
                  "Astro":"manuell",
                  "Gruppe": grpNames.split(";")[1],
                  "Random":"0",
                  "RandPM":"pm",
                  "Offset":"0",
                  "OffsetPM":"m",
                  "RememberTimer": false,
                  "ConditionsTrue": true,
                  "ConditionsNr": "0",
                  "Conditions":{
                      "1":{
                          "ConditionStr": "",
                          "CondState": "",
                          "CondComp": "==",
                          "CondValue": ""
                      },
                      "2":{
                          "ConditionStr": "",
                          "CondState": "",
                          "CondComp": "==",
                          "CondValue": ""
                      },
                      "3":{
                          "ConditionStr": "",
                          "CondState": "",
                          "CondComp": "==",
                          "CondValue": ""
                      }
                  },
                  "Mo": true,
                  "Di": true,
                  "Mi": true,
                  "Do": true,
                  "Fr": true,
                  "Sa": true,
                  "So": true,
                  },
          };
          
          function main () {
              var dropDownListe = "";
              var devName;
              var TimerJSON = {};
              var idCounter = 0;
              if (debugLog) stdLog = true;
              if (!showTimerNr && !showSymbol){showTimerNr = true;}
              // ConditionJSON wird mit jedem Start neu eingelesen
              createConditionsJSON();
              setTimeout(updateCond,500);
              setMinutesDropDown();
              setAstro();
              if (getState("javascript.1.Timer." + path + ".TimerJSON").val === null) {
                  // Erste Initialisierung falls Objekte noch nicht existieren
                  console.warn("States werden neu erstellt! Script bitte erneut starten!");
                  for(var i = 0; i < device_members.length; i++) {
                      devName = getObject(device_members[i]).common.name;
                      dropDownListe += devName + ";";
                      TimerJSON[devName] = jsonCopy(DefaultInhalte);
                      TimerJSON[devName][1].ObjID = TimerJSON[devName][2].ObjID = device_members[i];
                      TimerJSON[devName][1].DeviceNr = TimerJSON[devName][2].DeviceNr = idCounter;
                      if (getObject(device_members[i]).common.type == "boolean"){
                          TimerJSON[devName][1].Sollwert = "An";
                          TimerJSON[devName][2].Sollwert = "Aus";
                      }
                      idCounter += 2;
                  }
                  dropDownListe = dropDownListe.slice(0, dropDownListe.length - 1); // Entfernen letztes Semikolon
                  setState("javascript.1.Timer." + path + ".DropDownDevice", dropDownListe, true);
                  setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON)); //rückschreiben der Einträge in State
              }
              else {
                  TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
                  dropDownListe = getState("javascript.1.Timer." + path + ".DropDownDevice").val;
                  // Wenn dropdown leer weil neu vom Skript-Update, erst json komplett einlesen
                  if (getState("javascript.1.Timer." + path + ".DropDownDevice").val === null) {
                      dropDownListe = "";
                      Object.keys(TimerJSON).forEach(function(key) {
                          dropDownListe += key + ";";
                      });
                      dropDownListe = dropDownListe.slice(0, dropDownListe.length - 1); // Entfernen letztes Semikolon
                      setState("javascript.1.Timer." + path + ".DropDownDevice", dropDownListe, true);
                  }
                  // Check ob neue Device hinzugekommen sind
                  var addedDevice = false;
                  for(var i = 0; i < device_members.length; i++) {
                      devName = getObject(device_members[i]).common.name;
                      if(!TimerJSON.hasOwnProperty(devName)){
                          addedDevice = true;
                          console.log("Device # " + devName + " # fehlt und wird neu hinzugefügt!");
                          // Zunächst DropDownListe für Devices erweitern
                          dropDownListe += ";" + devName;
                          // Device mit DefaultInhalt erstellen
                          TimerJSON[devName] = jsonCopy(DefaultInhalte);
                          TimerJSON[devName][1].ObjID = TimerJSON[devName][2].ObjID = device_members[i];
                          if (getObject(device_members[i]).common.type == "boolean"){
                              TimerJSON[devName][1].Sollwert = "An";
                              TimerJSON[devName][2].Sollwert = "Aus";
                          }
                      }
                  }
                  // Für Erweiterung der JSON-Objekte nach Skript-Update
                  var flagGroup = false
                  Object.keys(TimerJSON).forEach(function(key) {
                      for(let i = 1; i <= length(TimerJSON[key]); i++) {
                          // Key "Gruppe" neu hinzufügen
                          if(!TimerJSON[key][i].hasOwnProperty("Gruppe")){
                              flagGroup = true
                              TimerJSON[key][i]["Gruppe"] = grpNames.split(";")[0];
                          }
                          // Key "RememberTimer" neu hinzufügen
                          if(!TimerJSON[key][i].hasOwnProperty("RememberTimer")){
                              TimerJSON[key][i]["RememberTimer"] = false;
                          }
                      };
                  });
                  if (flagGroup){
                      setState("javascript.1.Timer." + path + ".DropDownGruppe", grpNames, true);
                  }
          
                  // Schedules werden immer nach Start des Skripts automatisch erstellt
                  // recalc Funktion nicht erlaubt, da JSON in State nicht aktuell sein muss (z.B. neue Devices in Aufzählung erkannt)
                  Object.keys(TimerJSON).forEach(function(key) {
                      subscribesList[key] = 0;
                      for(let i = 1; i <= length(TimerJSON[key]); i++) {
                          TimerJSON[key][i].DeviceNr = idCounter;
                          TimerJSON[key][i].ConditionsTrue = condEval(TimerJSON[key][i]);
                          autoScheduler(TimerJSON, key, i);
                      };
                      idCounter += 2;
                  });
                  // States sichern
                  setState("javascript.1.Timer." + path + ".DropDownDevice", dropDownListe, true); 
                  setState("javascript.1.Timer." + path + ".TimerJSON", JSON.stringify(TimerJSON)); //rückschreiben der Einträge in State
              }
              setState("javascript.1.Timer." + path + ".DropDownGruppe", grpNames, true); 
              //set default Filter
              setState("javascript.1.Timer." + path + ".FilterDevice", "Alle"); 
              for (var firstKey in TimerJSON) break;
              setTimeout(activateSchedules,1000); // Crons aktivieren
              setTimeout(activateTrigger,1000);   // Trigger aktivieren
              setTimeout(setState, 1500, "javascript.1.Timer." + path + ".Editor.Device", firstKey);
              setTimeout(setState, 1800, "javascript.1.Timer." + path + ".Editor.Nummer", 1)
              tableMain(2000);
          }
          setTimeout(main,1500);
          
          
          //##########################################################################################################
          // NACHFOLGEND die Erstellung der JSON Tabelle und HTML Aufbereitung
          //##########################################################################################################
          
          function buildTableArray() {
              var tabelle = [];
              var tmpAstro, tmpRand, tmpOffset, dropDownListe, dropDownItems, key, tmpClass;
              var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
              // Reihenfolge aus "Timer.Devices.DropDownDevice" entnehmen, statt alle Keys durchlaufen 
              dropDownListe = getState("javascript.1.Timer." + path + ".DropDownDevice").val;
              dropDownItems = dropDownListe.split(";");
              for(var i = 0; i < dropDownItems.length; i++) {
                  key = dropDownItems[i];
                  for (var j = 1; j <= Object.keys(TimerJSON[key]).length; j++) {
                      var tempJsonNr = TimerJSON[key][j]; // JSON Objekt wird umkopiert um im weiteren Verlauf ohne Indizes zu arbeiten
                      var scheduleNr = ( tempJsonNr.DeviceNr * 10 ) + j;
                      tmpAstro = tempJsonNr.Astro == "manuell" ? (tempJsonNr.Std + ":" + tempJsonNr.Min) : tempJsonNr.Astro;
                      if (tempJsonNr.Random != "0"){
                          switch(tempJsonNr.RandPM){
                              case "p": tmpRand = "+" + " " + tempJsonNr.Random; break;
                              case "m": tmpRand = "-" + " " + tempJsonNr.Random; break;
                              case "pm": tmpRand = "±" + " " + tempJsonNr.Random; break;
                          }
                      } else {
                          tmpRand = " "
                      }
                      if (tempJsonNr.Offset != "0"){
                          switch(tempJsonNr.OffsetPM){
                              case "p": tmpOffset = "+" + " " + tempJsonNr.Offset; break;
                              case "m": tmpOffset = "-" + " " + tempJsonNr.Offset; break;
                          }
                      } else {
                          tmpOffset = " "
                      }
                      if (tempJsonNr.Astro == "manuell"){tmpOffset = " "}
                      tmpClass = "";
                      if (tempJsonNr.RememberTimer){
                          if (tempJsonNr.ConditionsTrue){
                              tmpClass = "class=timer-remember-green-glow";
                          } else{
                              if (subscribesList[key] == scheduleNr){
                                  tmpClass = "class=timer-remember-red-blink";
                              } else {
                                  tmpClass = "class=timer-remember-red-glow";
                              }
                          }
                      }
                      tabelle.push({
                          "Geraet"    : key,
                          "Nr"        : j, 
                          "Aktiv"     : tempJsonNr.Aktiv,
                          "Gruppe"    : tempJsonNr.Gruppe,
                          "CondNr"    : tempJsonNr.ConditionsNr,
                          "CondTrue"  : tempJsonNr.ConditionsTrue,
                          "Class"     : tmpClass,
                          "Zeit"      : tempJsonNr.Zeit,
                          "Tage"      : tempJsonNr.TageVIS,
                          "Sollwert"  : tempJsonNr.Sollwert,
                          "Astro"     : tmpAstro,
                          "offset"    : tmpOffset,
                          "rand"      : tmpRand,
                      });
                  }
              }
              return tabelle;
          }
          
          
          // Button überträgt bei Klick den Wert:
          // Pfad~~~Gerät~Timer-Nummer~Funktion
          function getButtonCode(buttonVal, buttonText, color){
              var htmlButton;
              htmlButton = "<button style=\""
          		        + "border:none; "
          		        + "background-color:transparent; "
          		        + "color:" + color + "; "
          		        + "font-size:1.0em; "
          		        + "text-align:left"
          		        + "\" value=\"" + buttonVal + "\""
                          + "onclick=\"setOnClick" + path + "(this.value)\""
                          + "ondblclick=\"setOnDblClick" + path + "(this.value)\">" + buttonText + "</button>"
              return htmlButton
          }
          
          // Button überträgt bei Klick den Wert:
          // Pfad~~~Gerät~Timer-Nummer~Funktion
          function getFakeButtonCode(buttonText){
              var htmlButton;
              htmlButton = "<button style=\""
          		        + "border:none; "
          		        + "background-color:transparent; "
                          + "color:white; "
          		        + "font-size:1.0em; "
          		        + "text-align:left\" >" + buttonText + "</button>"
              return htmlButton
          }
          
          
          function jsonToHtml(tabelle, withDevice) {
          	var html = "";
              var astro = "";
              var tmpTage = "";
              var backgroundTimerExists = false;
          
              // Klasse für das Blinken der Bedingungen wenn Timer im Hintergrund
              html = "<style>\n"
                  + ".timer-remember-green-glow {\n"
                  + "filter: drop-shadow(0px 0px 2px #4CAF50) drop-shadow(0px 0px 2px #4CAF50) drop-shadow(0px 0px 4px #4CAF50)\n"
                  + "}\n"
                  + ".timer-remember-red-glow {\n"
                  + "filter: drop-shadow(0px 0px 2px #F44336) drop-shadow(0px 0px 2px #F44336) drop-shadow(0px 0px 4px #F44336)\n"
                  + "}\n"
                  + ".timer-remember-red-blink {\n"
                  + "animation: timer-remember-blink-ani 1s linear infinite;\n"
                  + "}\n"
                  + "@keyframes timer-remember-blink-ani {\n"
                  + "0%,50% {filter: drop-shadow(0px 0px 4px #F44336) drop-shadow(0px 0px 4px #F44336) drop-shadow(0px 0px 4px #F44336); }\n"
                  + "51% {filter: none;}\n"
                  + "}\n"
                  + "</style>\n";
          
              // Prüfen ob aktive Background-Timer existieren, damit "Bed" in Überschrift entsprechend dargestellt werden kann
              for (var i=0; i<tabelle.length; i++){
                  if (tabelle[i].Class == "class=timer-remember-red-blink"){
                      backgroundTimerExists = true;
                  }
              }
          
              // Überschriften der Tabelle
              html += "<table style='font-size:" + fontSize + "em;width:100%;'><thead>\n<tr>\n"
                   + ( withDevice  ?  "<th style='text-align:left;'>" + getFakeButtonCode("Device") + "</th>\n"  : "" ) /* Wenn splitHTML true ist, dann keine Spalte "Device" */
                   + ( showTimerNr ?  "<th style='text-align:left;'>" + getFakeButtonCode("Nr") + "</th>\n"      : "" )
                   + ( showSymbol  ?  "<th style='text-align:left;'>" + getFakeButtonCode("Aktiv") + "</th>\n"   : "" )
                   /* Nachfolgend die Darstellung von "Bed" in zwei Zeilen zwecks lesbarkeit */
                   + ( !backgroundTimerExists  ?  "<th style='text-align:left;'>" + getFakeButtonCode("Bed") + "</th>\n"   : "" )
                   + ( backgroundTimerExists  ?  "<th class=timer-remember-red-blink style='text-align:left;'>" + getButtonCode("all~0~cond", "Bed", "red") + "</th>\n"   : "" )
                   + ( showGroupNr ?  "<th style='text-align:left;'>Grp</th>\n"                                : "" )
                   + "<th style='text-align:left;'>" + getFakeButtonCode("Zeit") + "</th>\n"
                   + "<th style='text-align:left;'>Wochentag</th>\n"
                   + "<th style='text-align:left;'>Soll</th>\n"
                   + "<th style='text-align:left;'>Astro</th>\n"
                   + "<th style='text-align:left;'>Offset</th>\n"
                   + "<th style='text-align:left;'>Zufall</th>\n"
                   + "</tr></thead><tbody>\n\n";
          
              // Erstellen der einzelnen Tabelleneinträge
          	for (var i=0; i<tabelle.length; i++){
          
              	html += "<tr>\n"
                        + ( withDevice  ? "<td>" + getButtonCode(tabelle[i].Geraet + "~" + tabelle[i].Nr + "~dev", tabelle[i].Geraet, "white") + "</td>\n" : "" )
                        + ( showTimerNr ? "<td>" + getButtonCode(tabelle[i].Geraet + "~" + tabelle[i].Nr + "~nr", tabelle[i].Nr, ( tabelle[i].Aktiv ? "#00FF7F"  : "#FF0000" )) + "</td>\n" : "" )
                        + ( showSymbol  ? "<td>" + getButtonCode(tabelle[i].Geraet + "~" + tabelle[i].Nr + "~nr", ( tabelle[i].Aktiv ? symbEnab : symbDisab ), "white") + "</td>\n" : "" )
                        + ( tabelle[i].CondNr > 0 ? "<td " + tabelle[i].Class + ">" + getButtonCode(tabelle[i].Geraet + "~" + tabelle[i].Nr + "~cond", "*" + tabelle[i].CondNr, ( tabelle[i].CondTrue ? "#00FF7F" : "#FF0000" ) ) + "</td>\n" : "<td> </td>\n" )
                        + ( showGroupNr ? "<td>" + tabelle[i].Gruppe + "</td>" : "" )
                        + "<td>" + getButtonCode(tabelle[i].Geraet + "~" + tabelle[i].Nr + "~time", tabelle[i].Zeit, "white") + "</td>\n"
                        + "<td>" + tabelle[i].Tage + "</td>\n"
                        + "<td>" + tabelle[i].Sollwert + "</td>\n"
                        + "<td>" + tabelle[i].Astro + "</td>\n"
                        + "<td>" + tabelle[i].offset + "</td>\n"
                        + "<td>" + tabelle[i].rand + "</td>\n"
                        + "</tr>\n\n";
              }
              html += "</body></table>\n\n";
          
              // Funktionen für Klick und Doppel-Klick werden direkt im html Code der Buttons hinterlegt    
              html += "<script>\n"
                    + "\n"
                    + "function setOnClick" + path + "(val) {\n"
                    + "var objID = \"javascript.1.Timer." + path + ".clickTarget\";\n"
                    + "servConn.setState(objID, val);}\n"
                    + "\n"
                    + "function setOnDblClick" + path + "(val) {\n"
                    + "var objID = \"javascript.1.Timer." + path + ".dblClickTarget\";\n"
                    + "servConn.setState(objID, val);}\n"
                    + "\n"
                    + "</script>";
          
          	return html;
          }
          
          var tableTimeout;
          function tableMain(delay) {
              
              if (tableTimeout) {clearTimeout(tableTimeout); tableTimeout = null;}
          
              tableTimeout = setTimeout(function(){
                  
                  var device;
                  var TimerJSON = JSON.parse(getState("javascript.1.Timer." + path + ".TimerJSON").val);
                  var filterDev = getState("javascript.1.Timer." + path + ".FilterDevice").val;
                  var tabelle = buildTableArray();
          
                  if (splitHTML) {
                      var tableIndex = 0;
                      Object.keys(TimerJSON).forEach(function(device) {
                          var timerNr = Object.keys(TimerJSON[device]).length;
                          var strState = "Timer." + path + ".HTML_" + device;
                          // Falls Device mit einem Punkt endet, muss dieser entfernt werden um State zu erstellen
                          strState = (strState.slice(-1) == ".") ? strState.slice(0, strState.length - 1) : strState;
                          createState(strState);
                          setState(strState, jsonToHtml(tabelle.slice(tableIndex, tableIndex + timerNr), false));
                          tableIndex += timerNr;
                      });
                  }
                  else {
                      var strState = "javascript.1.Timer." + path + ".TableHTML";
                      createState("Timer." + path + ".TableHTML");
                      if (filterDev == "Alle") {
                          setState(strState, jsonToHtml(tabelle, true));
                      } else {
                          var filteredTable = [];
                          for (let i = 0; i < tabelle.length; i++){
                              device = tabelle[i]["Geraet"];
                              if ( device == filterDev){
                                  filteredTable.push(tabelle[i]);
                              }
                          }
                          setState(strState, jsonToHtml(filteredTable, false));
                      }
                  }
              },delay);
          }
          
          
          

          und original view verhält sich genauso...
          starte nachher malneu wenn ich zu Hause bin...

          GlasfaserG Offline
          GlasfaserG Offline
          Glasfaser
          schrieb am zuletzt editiert von Glasfaser
          #615

          @smartboart

          Schlingel ... hast EDIT gemacht :)

          und original view verhält sich genauso...

          dann starte mal durch

          Synology 918+ 16GB - ioBroker in Docker v9 , VISO auf Trekstor Primebook C13 13,3" , Hikvision Domkameras mit Surveillance Station .. CCU RaspberryMatic in Synology VM .. Zigbee CC2538+CC2592 .. Sonoff .. KNX .. Modbus ..

          smartboartS 1 Antwort Letzte Antwort
          0
          • GlasfaserG Glasfaser

            @smartboart

            Schlingel ... hast EDIT gemacht :)

            und original view verhält sich genauso...

            dann starte mal durch

            smartboartS Offline
            smartboartS Offline
            smartboart
            schrieb am zuletzt editiert von smartboart
            #616

            @Glasfaser verhält sich genauso, habe ich eben mit der neuen original view gemacht
            fahr nachher mal runter..

            1 Antwort Letzte Antwort
            0
            • smartboartS Offline
              smartboartS Offline
              smartboart
              schrieb am zuletzt editiert von smartboart
              #617

              Neustart ist erfolgt
              Also wenn ich die Bedingungen einzeln öffne und fülle..
              also erst 1 und einstellen dann auf 2 und einstellen und dann auf 3 und einstellen passt alles...aber immer noch die Meldung, welche eigentlich immer kommt...egal ob ich nur eine oder 3 Bedingungen einstelle.

              Hab auch mal alle gleichzeitig eingestellt, da passt jetzt auch die Reihenfolge.....

              Aber immer noch:
              javascript.1 2020-07-14 14:15:51.814 info Bitte korrigieren und übernehmen!
              javascript.1 2020-07-14 14:15:51.814 info Bitte korrigieren und übernehmen!Bedingung 3 wurde gesetzt, ist aber leer.
              javascript.1 2020-07-14 14:15:51.814 info Bitte korrigieren und übernehmen!Bedingung 2 wurde gesetzt, ist aber leer.
              javascript.1 2020-07-14 14:15:51.814 info (1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer.

              Edit:
              muss dazu sagen, ohne Bedingungen funktionert alles einwandfrei...

              G 1 Antwort Letzte Antwort
              0
              • smartboartS smartboart

                Neustart ist erfolgt
                Also wenn ich die Bedingungen einzeln öffne und fülle..
                also erst 1 und einstellen dann auf 2 und einstellen und dann auf 3 und einstellen passt alles...aber immer noch die Meldung, welche eigentlich immer kommt...egal ob ich nur eine oder 3 Bedingungen einstelle.

                Hab auch mal alle gleichzeitig eingestellt, da passt jetzt auch die Reihenfolge.....

                Aber immer noch:
                javascript.1 2020-07-14 14:15:51.814 info Bitte korrigieren und übernehmen!
                javascript.1 2020-07-14 14:15:51.814 info Bitte korrigieren und übernehmen!Bedingung 3 wurde gesetzt, ist aber leer.
                javascript.1 2020-07-14 14:15:51.814 info Bitte korrigieren und übernehmen!Bedingung 2 wurde gesetzt, ist aber leer.
                javascript.1 2020-07-14 14:15:51.814 info (1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer.

                Edit:
                muss dazu sagen, ohne Bedingungen funktionert alles einwandfrei...

                G Offline
                G Offline
                GiuseppeS
                schrieb am zuletzt editiert von
                #618

                @smartboart

                Zu deinem letzten Beitrag habe ich es nicht zu 100% verstanden. Daher bitte folgendes umsetzen, falls noch nicht geschehen:

                Bitte zunächst nur eine einzige Bedingung nutzen/voreinstellen. Bedingung vollständig eingeben und übernehmen. Fehler wird angezeigt?
                Wenn ja: nimm anderen State als Bedingung. D.h. trage für die Bedingung etwas anderes ein.

                Wenn weiterhin Fehler vorkommen, bitte systematisch wie folgt vorgehen:
                Anzahl Bedingungen auf 0 setzen und übernehmen. Relevante Editor States müssten leer sein.
                Danach eine einzige Bedingung ausfüllen und States vom Editor mit Screenshot posten.

                smartboartS 1 Antwort Letzte Antwort
                0
                • G GiuseppeS

                  @smartboart

                  Zu deinem letzten Beitrag habe ich es nicht zu 100% verstanden. Daher bitte folgendes umsetzen, falls noch nicht geschehen:

                  Bitte zunächst nur eine einzige Bedingung nutzen/voreinstellen. Bedingung vollständig eingeben und übernehmen. Fehler wird angezeigt?
                  Wenn ja: nimm anderen State als Bedingung. D.h. trage für die Bedingung etwas anderes ein.

                  Wenn weiterhin Fehler vorkommen, bitte systematisch wie folgt vorgehen:
                  Anzahl Bedingungen auf 0 setzen und übernehmen. Relevante Editor States müssten leer sein.
                  Danach eine einzige Bedingung ausfüllen und States vom Editor mit Screenshot posten.

                  smartboartS Offline
                  smartboartS Offline
                  smartboart
                  schrieb am zuletzt editiert von
                  #619

                  @GiuseppeS Hallo Giuseppe, Danke..
                  Habe ich genau so umgesetzt...Es ist egal welche meine enumerierten Bedingungen ich auswähle, sie verhalten sich alle gleich..
                  Ich habe mich exakt an deine anweisungen gehalten.
                  Kann es sein , dass es Probleme mit Leerzecihen oder Sonderzeichen gibt?
                  Hier die Bilder... und der log zu den tests..
                  Unbenannt1.JPG Unbenannt.JPG

                  javascript.1	2020-07-14 14:50:14.674	info	Bitte korrigieren und übernehmen!
                  javascript.1	2020-07-14 14:50:14.674	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer.
                  javascript.1	2020-07-14 14:49:51.645	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule aktiviert: "Luftentfeuchter #1": [81] | 0 10 * * 0,1,2,3,4,5,6 | sonoff.0.Steckdose_1.POWER | An
                  javascript.1	2020-07-14 14:49:51.644	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule für "Luftentfeuchter #1" [81] gelöscht!
                  javascript.1	2020-07-14 14:49:45.684	info	Bitte korrigieren und übernehmen!
                  javascript.1	2020-07-14 14:49:45.684	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer.
                  javascript.1	2020-07-14 14:49:27.338	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule aktiviert: "Luftentfeuchter #1": [81] | 0 10 * * 0,1,2,3,4,5,6 | sonoff.0.Steckdose_1.POWER | An
                  javascript.1	2020-07-14 14:49:27.337	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule für "Luftentfeuchter #1" [81] gelöscht!
                  javascript.1	2020-07-14 14:49:14.012	info	Bitte korrigieren und übernehmen!
                  javascript.1	2020-07-14 14:49:14.012	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer.
                  javascript.1	2020-07-14 14:47:31.338	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule aktiviert: "Luftentfeuchter #1": [81] | 0 10 * * 0,1,2,3,4,5,6 | sonoff.0.Steckdose_1.POWER | An
                  javascript.1	2020-07-14 14:47:31.337	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule für "Luftentfeuchter #1" [81] gelöscht!
                  javascript.1	2020-07-14 14:47:29.444	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule aktiviert: "Luftentfeuchter #1": [81] | 0 10 * * 0,1,2,3,4,5,6 | sonoff.0.Steckdose_1.POWER | An
                  javascript.1	2020-07-14 14:47:29.441	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule für "Luftentfeuchter #1" [81] gelöscht!
                  
                  G 1 Antwort Letzte Antwort
                  0
                  • smartboartS smartboart

                    @GiuseppeS Hallo Giuseppe, Danke..
                    Habe ich genau so umgesetzt...Es ist egal welche meine enumerierten Bedingungen ich auswähle, sie verhalten sich alle gleich..
                    Ich habe mich exakt an deine anweisungen gehalten.
                    Kann es sein , dass es Probleme mit Leerzecihen oder Sonderzeichen gibt?
                    Hier die Bilder... und der log zu den tests..
                    Unbenannt1.JPG Unbenannt.JPG

                    javascript.1	2020-07-14 14:50:14.674	info	Bitte korrigieren und übernehmen!
                    javascript.1	2020-07-14 14:50:14.674	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer.
                    javascript.1	2020-07-14 14:49:51.645	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule aktiviert: "Luftentfeuchter #1": [81] | 0 10 * * 0,1,2,3,4,5,6 | sonoff.0.Steckdose_1.POWER | An
                    javascript.1	2020-07-14 14:49:51.644	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule für "Luftentfeuchter #1" [81] gelöscht!
                    javascript.1	2020-07-14 14:49:45.684	info	Bitte korrigieren und übernehmen!
                    javascript.1	2020-07-14 14:49:45.684	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer.
                    javascript.1	2020-07-14 14:49:27.338	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule aktiviert: "Luftentfeuchter #1": [81] | 0 10 * * 0,1,2,3,4,5,6 | sonoff.0.Steckdose_1.POWER | An
                    javascript.1	2020-07-14 14:49:27.337	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule für "Luftentfeuchter #1" [81] gelöscht!
                    javascript.1	2020-07-14 14:49:14.012	info	Bitte korrigieren und übernehmen!
                    javascript.1	2020-07-14 14:49:14.012	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer.
                    javascript.1	2020-07-14 14:47:31.338	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule aktiviert: "Luftentfeuchter #1": [81] | 0 10 * * 0,1,2,3,4,5,6 | sonoff.0.Steckdose_1.POWER | An
                    javascript.1	2020-07-14 14:47:31.337	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule für "Luftentfeuchter #1" [81] gelöscht!
                    javascript.1	2020-07-14 14:47:29.444	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule aktiviert: "Luftentfeuchter #1": [81] | 0 10 * * 0,1,2,3,4,5,6 | sonoff.0.Steckdose_1.POWER | An
                    javascript.1	2020-07-14 14:47:29.441	info	(1294) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Schedule für "Luftentfeuchter #1" [81] gelöscht!
                    
                    G Offline
                    G Offline
                    GiuseppeS
                    schrieb am zuletzt editiert von
                    #620

                    @smartboart
                    Jetzt hab ich es erst gesehen. Dein State Condition1 ist leer. Deshalb die Fehlermeldung. Es gibt ein Trigger im Skript, sobald etwas bei den Bedingungen eingegeben wird, muss ein String erstellt und anschließend evaluiert werden. So schaut der Trigger aus:

                        on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1Comp' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1State' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1Value'), change: "any", ack: false}, function (obj) {
                            var ConditionJSON = JSON.parse(getState("javascript.0.Timer." + path + ".ConditionJSON").val);
                            var Cond1State = getState("javascript.0.Timer." + path + ".Editor.Cond1State").val
                            var Cond1Comp = getState("javascript.0.Timer." + path + ".Editor.Cond1Comp").val
                            var Cond1Value = getState("javascript.0.Timer." + path + ".Editor.Cond1Value").val
                            var strCond1 = "getState(\"" + ConditionJSON[Cond1State] + "\").val " + Cond1Comp + " " + Cond1Value
                            setState("javascript.0.Timer." + path + ".Editor.Condition1", strCond1);
                            if (Cond1State != "" && Cond1Comp != "" && Cond1Value != "") {
                                setState("javascript.0.Timer." + path + ".Editor.Cond1Result", eval(strCond1));
                            }
                        });
                    

                    Passt hier evtl etwas nicht mit deiner JavaScript Instanz 1 statt 0? Hast du oben im Trigger das angepasst? Hatte ich ursprünglich aus Blockly übernommen, deshalb ist es mit Regexp statt Array...

                    smartboartS 2 Antworten Letzte Antwort
                    2
                    • G GiuseppeS

                      @smartboart
                      Jetzt hab ich es erst gesehen. Dein State Condition1 ist leer. Deshalb die Fehlermeldung. Es gibt ein Trigger im Skript, sobald etwas bei den Bedingungen eingegeben wird, muss ein String erstellt und anschließend evaluiert werden. So schaut der Trigger aus:

                          on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1Comp' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1State' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1Value'), change: "any", ack: false}, function (obj) {
                              var ConditionJSON = JSON.parse(getState("javascript.0.Timer." + path + ".ConditionJSON").val);
                              var Cond1State = getState("javascript.0.Timer." + path + ".Editor.Cond1State").val
                              var Cond1Comp = getState("javascript.0.Timer." + path + ".Editor.Cond1Comp").val
                              var Cond1Value = getState("javascript.0.Timer." + path + ".Editor.Cond1Value").val
                              var strCond1 = "getState(\"" + ConditionJSON[Cond1State] + "\").val " + Cond1Comp + " " + Cond1Value
                              setState("javascript.0.Timer." + path + ".Editor.Condition1", strCond1);
                              if (Cond1State != "" && Cond1Comp != "" && Cond1Value != "") {
                                  setState("javascript.0.Timer." + path + ".Editor.Cond1Result", eval(strCond1));
                              }
                          });
                      

                      Passt hier evtl etwas nicht mit deiner JavaScript Instanz 1 statt 0? Hast du oben im Trigger das angepasst? Hatte ich ursprünglich aus Blockly übernommen, deshalb ist es mit Regexp statt Array...

                      smartboartS Offline
                      smartboartS Offline
                      smartboart
                      schrieb am zuletzt editiert von smartboart
                      #621

                      @GiuseppeS ahhhhhh!!!!Du bis spitze........ja den hab ich übersehen.....klar....hatte einfach über die suchfunktion alles mit javascript.0 ersetzt gegen javascript.1....das javascript\.0\.Timer\ ist mir natürlich durch die lappen gegangen..... Sorry sorry
                      Danke euch beiden für die Geduld...

                      GlasfaserG 1 Antwort Letzte Antwort
                      1
                      • G GiuseppeS

                        @smartboart
                        Jetzt hab ich es erst gesehen. Dein State Condition1 ist leer. Deshalb die Fehlermeldung. Es gibt ein Trigger im Skript, sobald etwas bei den Bedingungen eingegeben wird, muss ein String erstellt und anschließend evaluiert werden. So schaut der Trigger aus:

                            on({id: new RegExp('javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1Comp' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1State' + "|" + 'javascript\\.0\\.Timer\\.' + path + '\\.Editor\\.Cond1Value'), change: "any", ack: false}, function (obj) {
                                var ConditionJSON = JSON.parse(getState("javascript.0.Timer." + path + ".ConditionJSON").val);
                                var Cond1State = getState("javascript.0.Timer." + path + ".Editor.Cond1State").val
                                var Cond1Comp = getState("javascript.0.Timer." + path + ".Editor.Cond1Comp").val
                                var Cond1Value = getState("javascript.0.Timer." + path + ".Editor.Cond1Value").val
                                var strCond1 = "getState(\"" + ConditionJSON[Cond1State] + "\").val " + Cond1Comp + " " + Cond1Value
                                setState("javascript.0.Timer." + path + ".Editor.Condition1", strCond1);
                                if (Cond1State != "" && Cond1Comp != "" && Cond1Value != "") {
                                    setState("javascript.0.Timer." + path + ".Editor.Cond1Result", eval(strCond1));
                                }
                            });
                        

                        Passt hier evtl etwas nicht mit deiner JavaScript Instanz 1 statt 0? Hast du oben im Trigger das angepasst? Hatte ich ursprünglich aus Blockly übernommen, deshalb ist es mit Regexp statt Array...

                        smartboartS Offline
                        smartboartS Offline
                        smartboart
                        schrieb am zuletzt editiert von smartboart
                        #622

                        @GiuseppeS Richtig tolles Script!
                        Damit kann ich mal locker 15 - 20 Scripte von mir einsparen

                        Ist noch geplant über die Bedingungen abschalten zu lassen? Also entweder wie jetzt nach zeit und bedingung erfüllt und vor erreichen der Zeit wenn Bedingung erfüllt?
                        Das wäre das i Tüpfelchen...
                        EDIT: und noch ein paar bedingungen Zusätzlich...

                        G 1 Antwort Letzte Antwort
                        0
                        • smartboartS smartboart

                          @GiuseppeS ahhhhhh!!!!Du bis spitze........ja den hab ich übersehen.....klar....hatte einfach über die suchfunktion alles mit javascript.0 ersetzt gegen javascript.1....das javascript\.0\.Timer\ ist mir natürlich durch die lappen gegangen..... Sorry sorry
                          Danke euch beiden für die Geduld...

                          GlasfaserG Offline
                          GlasfaserG Offline
                          Glasfaser
                          schrieb am zuletzt editiert von
                          #623

                          @smartboart sagte in [Vorlage] Variable Zeitsteuerung mit VIS Editor:

                          .......ja den hab ich übersehen.

                          Na ... dann ist ja gut das der Fehler behoben ist ;)

                          Synology 918+ 16GB - ioBroker in Docker v9 , VISO auf Trekstor Primebook C13 13,3" , Hikvision Domkameras mit Surveillance Station .. CCU RaspberryMatic in Synology VM .. Zigbee CC2538+CC2592 .. Sonoff .. KNX .. Modbus ..

                          1 Antwort Letzte Antwort
                          0
                          • smartboartS smartboart

                            @GiuseppeS Richtig tolles Script!
                            Damit kann ich mal locker 15 - 20 Scripte von mir einsparen

                            Ist noch geplant über die Bedingungen abschalten zu lassen? Also entweder wie jetzt nach zeit und bedingung erfüllt und vor erreichen der Zeit wenn Bedingung erfüllt?
                            Das wäre das i Tüpfelchen...
                            EDIT: und noch ein paar bedingungen Zusätzlich...

                            G Offline
                            G Offline
                            GiuseppeS
                            schrieb am zuletzt editiert von
                            #624

                            @smartboart

                            Geplant ist es nicht, dass rein über Bedingungen geschaltet wird. Da gäbe es einfach zu viele verschiedene Wünsche bzw Abhängigkeiten. Dann wäre dieses Skript ein Ersatz für Blockly. Einfache "wenn dies, dann tu jenes" ist mit Blockly auch schnell erledigt und Bedarf keiner Editierung in VIS. Dieses Skript hatte ich ursprünglich erstellt, um die Uhrzeiten meiner Rollläden schnell über VIS anzupassen, dann kamen Bedingungen hinzu und zum Schluss die verzögerte Ausführung.

                            Aktuelle Planung für Updates:

                            • Bei verzögerte Ausführung aufgrund nachträglich erfüllter Bedingungen, auch Random optional beachten.
                            • auch wenn das Skript mehrfach genutzt wird, wird das Editor-View nur einmalig verwendet. Mehrfachnutzung somit vereinfacht.
                            • Aufgrund heutiger Erfahrung: Instanz von JavaScript wird im Skript automatisch übernommen ;-) (Views müssten dennoch angepasst werden)

                            Letzten Punkt mache ich noch rein, die ersten Beiden sind bereits umgesetzt und ausgetestet.

                            Bzgl. weiterer Bedingungen, d.h. mehr als drei:
                            Grundsätzlich sehr einfach erweiterbar. Aber mir ging der Platz im PopUp Menü aus 😂. Ich nutze diese Tabelle auf dem Smartphone und mehr als drei Bedingungen bekomme ich nicht unter.

                            Ein Gedanke mit dem ich spiele: Editor in HTML umsetzen. Bedeutet, dass die eigentliche Tabelle zum Editor wird, aber das wird sehr umfangreich im Code. Daher überlege ich noch...

                            Ansonsten, bei Ideen, einfach melden. Wenn's mit den drei Bedingungen öfters hakt, kann ich mir da noch was einfallen lassen. Evtl. hat ein anderer User auch ein Schmerz mit nur drei Bedingungen?

                            @Glasfaser
                            Deine Unterstützung bei Problemfällen ist einzigartig :+1:

                            GlasfaserG smartboartS 2 Antworten Letzte Antwort
                            1
                            • G GiuseppeS

                              @smartboart

                              Geplant ist es nicht, dass rein über Bedingungen geschaltet wird. Da gäbe es einfach zu viele verschiedene Wünsche bzw Abhängigkeiten. Dann wäre dieses Skript ein Ersatz für Blockly. Einfache "wenn dies, dann tu jenes" ist mit Blockly auch schnell erledigt und Bedarf keiner Editierung in VIS. Dieses Skript hatte ich ursprünglich erstellt, um die Uhrzeiten meiner Rollläden schnell über VIS anzupassen, dann kamen Bedingungen hinzu und zum Schluss die verzögerte Ausführung.

                              Aktuelle Planung für Updates:

                              • Bei verzögerte Ausführung aufgrund nachträglich erfüllter Bedingungen, auch Random optional beachten.
                              • auch wenn das Skript mehrfach genutzt wird, wird das Editor-View nur einmalig verwendet. Mehrfachnutzung somit vereinfacht.
                              • Aufgrund heutiger Erfahrung: Instanz von JavaScript wird im Skript automatisch übernommen ;-) (Views müssten dennoch angepasst werden)

                              Letzten Punkt mache ich noch rein, die ersten Beiden sind bereits umgesetzt und ausgetestet.

                              Bzgl. weiterer Bedingungen, d.h. mehr als drei:
                              Grundsätzlich sehr einfach erweiterbar. Aber mir ging der Platz im PopUp Menü aus 😂. Ich nutze diese Tabelle auf dem Smartphone und mehr als drei Bedingungen bekomme ich nicht unter.

                              Ein Gedanke mit dem ich spiele: Editor in HTML umsetzen. Bedeutet, dass die eigentliche Tabelle zum Editor wird, aber das wird sehr umfangreich im Code. Daher überlege ich noch...

                              Ansonsten, bei Ideen, einfach melden. Wenn's mit den drei Bedingungen öfters hakt, kann ich mir da noch was einfallen lassen. Evtl. hat ein anderer User auch ein Schmerz mit nur drei Bedingungen?

                              @Glasfaser
                              Deine Unterstützung bei Problemfällen ist einzigartig :+1:

                              GlasfaserG Offline
                              GlasfaserG Offline
                              Glasfaser
                              schrieb am zuletzt editiert von
                              #625

                              @GiuseppeS

                              Danke ...gebe nur Vorlagen ;) ..... den Rest kannst du machen :grin:

                              Synology 918+ 16GB - ioBroker in Docker v9 , VISO auf Trekstor Primebook C13 13,3" , Hikvision Domkameras mit Surveillance Station .. CCU RaspberryMatic in Synology VM .. Zigbee CC2538+CC2592 .. Sonoff .. KNX .. Modbus ..

                              1 Antwort Letzte Antwort
                              1
                              • G GiuseppeS

                                @smartboart

                                Geplant ist es nicht, dass rein über Bedingungen geschaltet wird. Da gäbe es einfach zu viele verschiedene Wünsche bzw Abhängigkeiten. Dann wäre dieses Skript ein Ersatz für Blockly. Einfache "wenn dies, dann tu jenes" ist mit Blockly auch schnell erledigt und Bedarf keiner Editierung in VIS. Dieses Skript hatte ich ursprünglich erstellt, um die Uhrzeiten meiner Rollläden schnell über VIS anzupassen, dann kamen Bedingungen hinzu und zum Schluss die verzögerte Ausführung.

                                Aktuelle Planung für Updates:

                                • Bei verzögerte Ausführung aufgrund nachträglich erfüllter Bedingungen, auch Random optional beachten.
                                • auch wenn das Skript mehrfach genutzt wird, wird das Editor-View nur einmalig verwendet. Mehrfachnutzung somit vereinfacht.
                                • Aufgrund heutiger Erfahrung: Instanz von JavaScript wird im Skript automatisch übernommen ;-) (Views müssten dennoch angepasst werden)

                                Letzten Punkt mache ich noch rein, die ersten Beiden sind bereits umgesetzt und ausgetestet.

                                Bzgl. weiterer Bedingungen, d.h. mehr als drei:
                                Grundsätzlich sehr einfach erweiterbar. Aber mir ging der Platz im PopUp Menü aus 😂. Ich nutze diese Tabelle auf dem Smartphone und mehr als drei Bedingungen bekomme ich nicht unter.

                                Ein Gedanke mit dem ich spiele: Editor in HTML umsetzen. Bedeutet, dass die eigentliche Tabelle zum Editor wird, aber das wird sehr umfangreich im Code. Daher überlege ich noch...

                                Ansonsten, bei Ideen, einfach melden. Wenn's mit den drei Bedingungen öfters hakt, kann ich mir da noch was einfallen lassen. Evtl. hat ein anderer User auch ein Schmerz mit nur drei Bedingungen?

                                @Glasfaser
                                Deine Unterstützung bei Problemfällen ist einzigartig :+1:

                                smartboartS Offline
                                smartboartS Offline
                                smartboart
                                schrieb am zuletzt editiert von
                                #626

                                @GiuseppeS sagte in [Vorlage] Variable Zeitsteuerung mit VIS Editor:

                                Wenn's mit den drei Bedingungen öfters hakt, kann ich mir da noch was einfallen lassen. Evtl. hat ein anderer User auch ein Schmerz mit nur drei Bedingungen

                                Danke für die Ausführungen... Ich melde mich schon mal für weitere Bedingungen...

                                1 Antwort Letzte Antwort
                                0
                                • smartboartS Offline
                                  smartboartS Offline
                                  smartboart
                                  schrieb am zuletzt editiert von
                                  #627

                                  Haette noch ne Idee... In meinen Scripten verabeite ich Bedingungen auch innerhalb einer Zeitspanne.. Is time in range... Ist ein globales Script hier aus dem forum... Waere das nicht noch was für die Zeitsteuerung? Also wenn Zeit in Bereich und Bedingungen erfüllt wird geschaltet...

                                  G 1 Antwort Letzte Antwort
                                  0
                                  • smartboartS smartboart

                                    Haette noch ne Idee... In meinen Scripten verabeite ich Bedingungen auch innerhalb einer Zeitspanne.. Is time in range... Ist ein globales Script hier aus dem forum... Waere das nicht noch was für die Zeitsteuerung? Also wenn Zeit in Bereich und Bedingungen erfüllt wird geschaltet...

                                    G Offline
                                    G Offline
                                    GiuseppeS
                                    schrieb am zuletzt editiert von
                                    #628

                                    @smartboart
                                    Grundsätzlich wäre es schon möglich; Beispiel:

                                    Zeitspanne 18 bis 19h, Licht einschalten wenn niemand daheim.

                                    Timer 1 um 18h einschalten, Bedingung: nichtDaheim==true, Timer merken

                                    Timer 2 um 19h, Licht ausschalten.

                                    Wenn Timer 2 ausgeführt wird, obwohl Timer 1 noch im Hintergrund auf gültige Bedingung wartet, wird Timer 1 aus dem Hintergrund gelöscht.

                                    Nachteil hierbei ist: Gerät wird definitiv mit Timer 2 gesetzt, An oder Aus (z.B.).

                                    Abhilfe: Könnte bei Sollwerten "Reset" hinzufügen. Dieser würde nur einen evtl gemerkten Timer löschen, aber das Gerät an sich würde nicht geschaltet werden.

                                    smartboartS 1 Antwort Letzte Antwort
                                    0
                                    • G GiuseppeS

                                      @smartboart
                                      Grundsätzlich wäre es schon möglich; Beispiel:

                                      Zeitspanne 18 bis 19h, Licht einschalten wenn niemand daheim.

                                      Timer 1 um 18h einschalten, Bedingung: nichtDaheim==true, Timer merken

                                      Timer 2 um 19h, Licht ausschalten.

                                      Wenn Timer 2 ausgeführt wird, obwohl Timer 1 noch im Hintergrund auf gültige Bedingung wartet, wird Timer 1 aus dem Hintergrund gelöscht.

                                      Nachteil hierbei ist: Gerät wird definitiv mit Timer 2 gesetzt, An oder Aus (z.B.).

                                      Abhilfe: Könnte bei Sollwerten "Reset" hinzufügen. Dieser würde nur einen evtl gemerkten Timer löschen, aber das Gerät an sich würde nicht geschaltet werden.

                                      smartboartS Offline
                                      smartboartS Offline
                                      smartboart
                                      schrieb am zuletzt editiert von smartboart
                                      #629

                                      @GiuseppeS ja... Oder... Ein zusätzlichen state für die aktivierung Zeit in Bereich Auswahl im setup menue.. Wenn der aktiv geschaltet wird, wird die das "bis Zeit setup" sichtbar und die Funktion Zeit in Bereich aktiv also vorhandener timer wird zu start Zeit und zusätzlicher zu Endzeit... Werden die Bedingungen nicht erfüllt innerhalb dessen, schaltet eben nix..Eine zusätzliche aus Programmierung würde einfach ins leere abgesetzt... Kann man aber mit dem zu schaltenden aktor mit sich selbst verriegelt, wenn man das moechte
                                      .. Also wenn ein dann aus...

                                      G 1 Antwort Letzte Antwort
                                      0
                                      • smartboartS smartboart

                                        @GiuseppeS ja... Oder... Ein zusätzlichen state für die aktivierung Zeit in Bereich Auswahl im setup menue.. Wenn der aktiv geschaltet wird, wird die das "bis Zeit setup" sichtbar und die Funktion Zeit in Bereich aktiv also vorhandener timer wird zu start Zeit und zusätzlicher zu Endzeit... Werden die Bedingungen nicht erfüllt innerhalb dessen, schaltet eben nix..Eine zusätzliche aus Programmierung würde einfach ins leere abgesetzt... Kann man aber mit dem zu schaltenden aktor mit sich selbst verriegelt, wenn man das moechte
                                        .. Also wenn ein dann aus...

                                        G Offline
                                        G Offline
                                        GiuseppeS
                                        schrieb am zuletzt editiert von
                                        #630

                                        @smartboart
                                        Grundsätzlich verstehe ich deinen Ansatz, allerdings müsste ich in diesem Fall sehr viel erweitern + beachten.

                                        Ende der Zeitspanne: hier müsste auch Astro und manuelle Angabe möglich sein. Wenn das Ende erst am Folgetag stattfindet, wird's komplizierter.

                                        Ein größeres Problem ist die VIS: wohin mit dem Switch, den drei Zeit-Eingabefeldern + zugehörigem Text?
                                        Außerdem ist die Größe des PopUps nicht dynamisch, d.h. der Platz für die Eingaben der Zeitspannen muss immer vorgehalten werden. Ich habe gesehen, du hast es mit einem vollständigem View umgesetzt, da hast du natürlich mehr Platz.

                                        Wie weiter oben erwähnt, kann ich anbieten, den Sollwert "Reset" hinzuzufügen, der den Hintergrund-Timer ausschließlich löscht ohne dass ein Sollwert an das Gerät gesendet wird. Wenn das für dich in Frage kommt, kurze Rückmeldung. Wenn du hierbei eher blockly nutzen willst, passt es aber auch 😉

                                        Gestern hatte ich mal recherchiert, wie Dropdown Menüs und Zeiteingaben direkt in html umgesetzt werden können. Denke, dass ich mittelfristig das zusätzliche View mit dem Editor entfallen lassen könnte. Aber das muss ich noch austesten. Idee: Die Timer-Tabelle wird dann vollständig zum Editor. Alternative Idee: der Editor wird unterhalb des zu editierenden Timers als zusätzliche 2-3 Zeilen eingeblendet. Html ist aber bei mir viel try-and-error. Muss schauen wie ich es seitens Code integrieren kann.
                                        Jedenfalls könnte man mit dem html Editor mehr dynamisch unterbringen, dann können wir das Thema "Zeitspanne" nochmal bereden ;-)

                                        smartboartS 1 Antwort Letzte Antwort
                                        0
                                        • G GiuseppeS

                                          @smartboart
                                          Grundsätzlich verstehe ich deinen Ansatz, allerdings müsste ich in diesem Fall sehr viel erweitern + beachten.

                                          Ende der Zeitspanne: hier müsste auch Astro und manuelle Angabe möglich sein. Wenn das Ende erst am Folgetag stattfindet, wird's komplizierter.

                                          Ein größeres Problem ist die VIS: wohin mit dem Switch, den drei Zeit-Eingabefeldern + zugehörigem Text?
                                          Außerdem ist die Größe des PopUps nicht dynamisch, d.h. der Platz für die Eingaben der Zeitspannen muss immer vorgehalten werden. Ich habe gesehen, du hast es mit einem vollständigem View umgesetzt, da hast du natürlich mehr Platz.

                                          Wie weiter oben erwähnt, kann ich anbieten, den Sollwert "Reset" hinzuzufügen, der den Hintergrund-Timer ausschließlich löscht ohne dass ein Sollwert an das Gerät gesendet wird. Wenn das für dich in Frage kommt, kurze Rückmeldung. Wenn du hierbei eher blockly nutzen willst, passt es aber auch 😉

                                          Gestern hatte ich mal recherchiert, wie Dropdown Menüs und Zeiteingaben direkt in html umgesetzt werden können. Denke, dass ich mittelfristig das zusätzliche View mit dem Editor entfallen lassen könnte. Aber das muss ich noch austesten. Idee: Die Timer-Tabelle wird dann vollständig zum Editor. Alternative Idee: der Editor wird unterhalb des zu editierenden Timers als zusätzliche 2-3 Zeilen eingeblendet. Html ist aber bei mir viel try-and-error. Muss schauen wie ich es seitens Code integrieren kann.
                                          Jedenfalls könnte man mit dem html Editor mehr dynamisch unterbringen, dann können wir das Thema "Zeitspanne" nochmal bereden ;-)

                                          smartboartS Offline
                                          smartboartS Offline
                                          smartboart
                                          schrieb am zuletzt editiert von smartboart
                                          #631

                                          @GiuseppeS sagte in [Vorlage] Variable Zeitsteuerung mit VIS Editor:

                                          blockly nutzen willst, passt es aber auch

                                          Blocky benutze ich nicht , schreibe meine Skripte mit javascript...Den Vorschlag den Timer zu merken und bei erfüllung der Bedingungen zu schalten finde finde ich aber gut!Ja ich schalte meine views mittels state um, und habe den Konfigurator als view realisiert.Dadurch habe ich natürlich gut reden, weil jede menge platz...
                                          Screenshot_20200716_123305_de.ozerov.fully.jpg

                                          dasmit dem XML ist ne super idee, aber mich stört die konfiguration über pop up nicht...im gegenteil finde das sogar übersichtlicher..Ist für mich also nicht so relevant...
                                          wenn ich 3 Wünsche frei hätte würde ich

                                          1. weitere Bedingungen wählen

                                          2. Timer merken und bei Erfüllung der Bedingung schalten. Mit dem nächsten Ausbefehl den merker zurück setzen.

                                          3. sind doch nur 2 :-)

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


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          798

                                          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