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.1k

  • 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.3k 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.
  • G gender

    Hallo zusammen,
    auch von mir erstmal ein riesiges Dankeschön an @GiuseppeS für das Skript.
    Hatte mir selbst ein Skript für meine Rolladensteuerung gebastelt, allerdings ist so mit VIS-Unterstützung natürlich noch besser.

    Ich habe einen Feature-Request:

    Folgendes Szenario: Ich habe meine Rolladen bisher immer in Abhängigkeit von der Sonnenuntergangszeit +- eine Random-Zeit runterfahren lassen (so, dass sie 1. nicht alle gleichzeitig und 2. nicht jeden Tag zur gleichen Zeit runterfahren).
    Allerdings habe ich dies außerdem noch von meinem Helligkeitssensor abhängig gemacht (d.h. die Rolladen sollten nur runterfahren, wenn auch ein gewisser Helligkeitswert unterschritten ist, da der Helligkeitswert verlässlicher ist, als die Sonnenzeit).

    Dieses Szenario kann ich mit dem Skript durch das Setzen von Bedingungen ebenfalls umsetzen. Nun ist es aber so, dass der Helligkeitswert immer erst nach der Sonnenuntergangszeit erreicht wird. Im Skript kann ich für diesen Fall natürlich "Timer merken" einstellen. Das klappt auch.
    Es ist allerdings ärgerlich, dass dann die Random-Werte natürlich nicht mehr gelten. D.h. sobald dann diese Bedingung erfüllt ist, fahren doch alle Rolladen gleichzeitig runter und das würde ich gerne vermeiden.

    Gibt es eine Möglichkeit, dass so einzubauen, dass, nachdem die "Timer merken"-Funktion greift, dennoch +- Random-Werte berücksichtig werden?!

    (ich weiß, ist recht speziell, aber wollte zumindest gefragt haben).

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

    @gender
    Freut mich, dass du mit dem Skript gut zurecht kommst und alle Funktionen nutzt ;-)

    Zum Anliegen: Es ist mit Einschränkungen umsetzbar. Aufwand hält sich unter Berücksichtigung der Einschränkungen in Grenzen.

    Zur Umsetzung:

    • Random Zeit würde als Betrag übernommen. Z.Bsp.: Random "-5" wird als "+5" gewertet. Wenn eine Bedingung erfüllt wird, können wir nicht in die Vergangenheit reisen.
    • wenn eine Bedingung erfüllt wird und die Random-Zeit läuft, lässt sich dieser Countdown nicht unterbrechen! Auch nicht durch einen nachfolgenden Timer oder falls ein Rollladen zwischenzeitlich manuell verfahren wird. Wenn die Bedingungen zwischenzeitlich nicht mehr erfüllt sind, würde dieser Timer dennoch auslösen!
    • Umsetzung würde ich mit einer "optionalen" Variable im oberen Skript-Bereich anstreben. Wer es nicht braucht, muss die Variable nicht anlegen und merkt bei zukünftigen Updates nichts davon. Im VIS PopUp würde ich es nicht unterbringen wollen, da die vielen Optionen einen noch mehr erschlagen würden.

    Zum zweiten Punkt würde ich nochmal schauen, ob zumindest die Bedingungen zur Ausführung hin beachtet werden können. Müsste ich testen...

    Aktuell habe ich bereits ein anderes Update bei mir laufen, wobei ich noch testen muss. Das müsste ich erstmal abschließen. Es würde zukünftig nur ein PopUp benötigt werden. Wenn das Skript mehrfach genutzt wird, müsste somit nur das Tabellen-View erneuert importiert/kopiert werden. Das Skript müsste natürlich auch in diesem Fall mehrfach laufen, das geht nicht anders.

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

      Hi mal ne Frage zu den Bedingungen...Ich bekomme ständig diese Meldung obwohles nicht leer ist und auch vernünfitig ausschaut..

      12:18:16.688	info	javascript.1 (1759) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer. Bitte korrigieren und übernehmen!
      12:18:41.945	info	javascript.1 (1759) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer. Bitte korrigieren und übernehmen!Bedingung 2 wurde gesetzt, ist aber leer. Bitte korrigieren und übernehmen!
      

      Unbenannt.JPG

      G 1 Antwort Letzte Antwort
      0
      • smartboartS smartboart

        Hi mal ne Frage zu den Bedingungen...Ich bekomme ständig diese Meldung obwohles nicht leer ist und auch vernünfitig ausschaut..

        12:18:16.688	info	javascript.1 (1759) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer. Bitte korrigieren und übernehmen!
        12:18:41.945	info	javascript.1 (1759) script.js.common.Zeitsteuerung.Variable_Zeitsteuerung_Vis: Bedingung 1 wurde gesetzt, ist aber leer. Bitte korrigieren und übernehmen!Bedingung 2 wurde gesetzt, ist aber leer. Bitte korrigieren und übernehmen!
        

        Unbenannt.JPG

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

        @smartboart
        Diese Meldung müsste kommen, wenn im Editor-View eine unvollständige oder fehlerhafte Bedingung eingegeben wird. Der Editor PopUp kann dann nur über "abbrechen" geschlossen werden. Ist das der Fall? In diesem Fall wäre ein Screenshot vom Editor gut.
        Oder erscheinen diese Meldungen im laufenden Betrieb, ohne dass der Editor geöffnet ist?

        smartboartS 3 Antworten Letzte Antwort
        0
        • G GiuseppeS

          @smartboart
          Diese Meldung müsste kommen, wenn im Editor-View eine unvollständige oder fehlerhafte Bedingung eingegeben wird. Der Editor PopUp kann dann nur über "abbrechen" geschlossen werden. Ist das der Fall? In diesem Fall wäre ein Screenshot vom Editor gut.
          Oder erscheinen diese Meldungen im laufenden Betrieb, ohne dass der Editor geöffnet ist?

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

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

          Diese Meldung müsste kommen, wenn im Editor-View eine unvollständige oder fehlerhafte Bedingung eingegeben wird. Der Editor PopUp kann dann nur über "abbrechen" geschlossen werden. Ist das der Fall?

          ja

          1 Antwort Letzte Antwort
          0
          • G GiuseppeS

            @smartboart
            Diese Meldung müsste kommen, wenn im Editor-View eine unvollständige oder fehlerhafte Bedingung eingegeben wird. Der Editor PopUp kann dann nur über "abbrechen" geschlossen werden. Ist das der Fall? In diesem Fall wäre ein Screenshot vom Editor gut.
            Oder erscheinen diese Meldungen im laufenden Betrieb, ohne dass der Editor geöffnet ist?

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

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

            Oder erscheinen diese Meldungen im laufenden Betrieb, ohne dass der Editor geöffnet ist?

            nein

            1 Antwort Letzte Antwort
            0
            • G GiuseppeS

              @smartboart
              Diese Meldung müsste kommen, wenn im Editor-View eine unvollständige oder fehlerhafte Bedingung eingegeben wird. Der Editor PopUp kann dann nur über "abbrechen" geschlossen werden. Ist das der Fall? In diesem Fall wäre ein Screenshot vom Editor gut.
              Oder erscheinen diese Meldungen im laufenden Betrieb, ohne dass der Editor geöffnet ist?

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

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

              diesem Fall wäre ein Screenshot vom Editor gut.

              Unbenannt.JPG

              Unbenannt.JPG

              GlasfaserG 1 Antwort Letzte Antwort
              0
              • smartboartS smartboart

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

                diesem Fall wäre ein Screenshot vom Editor gut.

                Unbenannt.JPG

                Unbenannt.JPG

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

                @smartboart

                ist da ein dreher drin .

                Laut Datenpunkt ist Value 1 = Lüften Bedingung ...
                Datenpunkt ist Value 2 = Wemo ....

                im Editor anderes herum !?

                2.JPG 1.JPG

                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

                  ist da ein dreher drin .

                  Laut Datenpunkt ist Value 1 = Lüften Bedingung ...
                  Datenpunkt ist Value 2 = Wemo ....

                  im Editor anderes herum !?

                  2.JPG 1.JPG

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

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

                  ist da ein dreher drin .

                  ja krass ist mir garnicht aufgefallen....wie kommt der denn zustande...in meiner View stimmt die zuordnung.habe ich gerade überprüft...

                  GlasfaserG 1 Antwort Letzte Antwort
                  0
                  • smartboartS smartboart

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

                    ist da ein dreher drin .

                    ja krass ist mir garnicht aufgefallen....wie kommt der denn zustande...in meiner View stimmt die zuordnung.habe ich gerade überprüft...

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

                    @smartboart

                    Schau mal im Vis Editor ob alle Felder stimmen ...
                    Zeile 1 Value 1
                    Zeile 2 Value 2
                    usw.

                    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

                      Schau mal im Vis Editor ob alle Felder stimmen ...
                      Zeile 1 Value 1
                      Zeile 2 Value 2
                      usw.

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

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

                      Schau mal im Vis Editor ob alle Felder stimmen ...
                      Zeile 1 Value 1
                      Zeile 2 Value 2
                      usw.

                      ja habe ich schon gemacht...Reihenfolge stimmt...

                      GlasfaserG 1 Antwort Letzte Antwort
                      0
                      • smartboartS smartboart

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

                        Schau mal im Vis Editor ob alle Felder stimmen ...
                        Zeile 1 Value 1
                        Zeile 2 Value 2
                        usw.

                        ja habe ich schon gemacht...Reihenfolge stimmt...

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

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

                        ...Reihenfolge stimmt...

                        mhh.. :thinking_face:

                        Wie schon @GiuseppeS schreibt , kann dann der Wert falsch sein ... das würde dann auch dazu passen was ich schon gesehen habe ....

                        Beispiel : erwartet ist false ist aber eine 200

                        Sehr komisch !??

                        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 sagte in [Vorlage] Variable Zeitsteuerung mit VIS Editor:

                          ...Reihenfolge stimmt...

                          mhh.. :thinking_face:

                          Wie schon @GiuseppeS schreibt , kann dann der Wert falsch sein ... das würde dann auch dazu passen was ich schon gesehen habe ....

                          Beispiel : erwartet ist false ist aber eine 200

                          Sehr komisch !??

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

                          @Glasfaser ne die States sind schon vertauscht wenn ich die Objektliste und die View vergleichen...Aber im Editor ist die Reihenfolge

                          javascript.1.Timer.Devices.Editor.Cond1State
                          javascript.1.Timer.Devices.Editor.Cond2State
                          javascript.1.Timer.Devices.Editor.Cond3State

                          javascript.1.Timer.Devices.Editor.Cond1Comp
                          javascript.1.Timer.Devices.Editor.Cond2Comp
                          javascript.1.Timer.Devices.Editor.Cond3Comp

                          GlasfaserG 1 Antwort Letzte Antwort
                          0
                          • smartboartS smartboart

                            @Glasfaser ne die States sind schon vertauscht wenn ich die Objektliste und die View vergleichen...Aber im Editor ist die Reihenfolge

                            javascript.1.Timer.Devices.Editor.Cond1State
                            javascript.1.Timer.Devices.Editor.Cond2State
                            javascript.1.Timer.Devices.Editor.Cond3State

                            javascript.1.Timer.Devices.Editor.Cond1Comp
                            javascript.1.Timer.Devices.Editor.Cond2Comp
                            javascript.1.Timer.Devices.Editor.Cond3Comp

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

                            @smartboart

                            kann gerade nicht folgen ... das hier was sich von alleine etwas vertauscht !?

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

                            EDIT :

                            bzw. nimm mal die Original View !?

                            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

                              kann gerade nicht folgen ... das hier was sich von alleine etwas vertauscht !?

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

                              EDIT :

                              bzw. nimm mal die Original View !?

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

                              @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 1 Antwort Letzte Antwort
                              0
                              • 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
                                          Antworten
                                          • In einem neuen Thema antworten
                                          Anmelden zum Antworten
                                          • Älteste zuerst
                                          • Neuste zuerst
                                          • Meiste Stimmen


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          715

                                          Online

                                          32.4k

                                          Benutzer

                                          81.5k

                                          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