Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Skripten / Logik
    4. JavaScript
    5. [Vorlage] JS: Log-Datei aufbereiten für VIS

    NEWS

    • Neuer Blog: Fotos und Eindrücke aus Solingen

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    [Vorlage] JS: Log-Datei aufbereiten für VIS

    This topic has been deleted. Only users with topic management privileges can see it.
    • O
      Oli @Mic last edited by

      @Mic

      sorry, aber so richtig scheint das noch nicht zu funktionieren

      Der Zeitstempel wird leider noch nicht übergeben

      Mic sigi234 2 Replies Last reply Reply Quote 0
      • Mic
        Mic Developer @Oli last edited by

        @Oli
        Bitte poste mal Details zur Vorgehensweise, auch ggf. mit Screenshots.

        O 1 Reply Last reply Reply Quote 0
        • Negalein
          Negalein Global Moderator @Mic last edited by

          @Mic sagte in [Vorlage] JS: Log-Datei aufbereiten für VIS:

          Einfach alles unterhalb hier ersetzen

          hab ich gemacht. Quasi die alte Konfig ins neue Script eingefügt.

          Beim Start kommen sofort Fehler.

          javascript.0	2020-02-11 20:32:23.897	error	(2798) at process._tickCallback (internal/process/next_tick.js:68:7)
          javascript.0	2020-02-11 20:32:23.897	error	(2798) at promise.then (/opt/iobroker/node_modules/standard-as-callback/built/index.js:19:49)
          javascript.0	2020-02-11 20:32:23.897	error	(2798) at tryCatcher (/opt/iobroker/node_modules/standard-as-callback/built/utils.js:11:23)
          javascript.0	2020-02-11 20:32:23.897	error	(2798) at client.get (/opt/iobroker/node_modules/iobroker.js-controller/lib/states/statesInRedis.js:572:33)
          javascript.0	2020-02-11 20:32:23.897	error	(2798) at adapter.getForeignState (/opt/iobroker/node_modules/iobroker.javascript/main.js:1055:17)
          javascript.0	2020-02-11 20:32:23.897	error	(2798) at createProblemObject (/opt/iobroker/node_modules/iobroker.javascript/main.js:1464:17)
          javascript.0	2020-02-11 20:32:23.896	error	(2798) at prepareScript (/opt/iobroker/node_modules/iobroker.javascript/main.js:1411:37)
          javascript.0	2020-02-11 20:32:23.896	error	(2798) at compile (/opt/iobroker/node_modules/iobroker.javascript/main.js:1188:28)
          javascript.0	2020-02-11 20:32:23.896	error	(2798) at Object.createScript (vm.js:277:10)
          javascript.0	2020-02-11 20:32:23.896	error	(2798) at new Script (vm.js:83:7)
          javascript.0	2020-02-11 20:32:23.896	error	(2798) SyntaxError: Identifier 'LOG_PATT' has already been declared
          javascript.0	2020-02-11 20:32:23.895	error	(2798) ^
          javascript.0	2020-02-11 20:32:23.895	error	(2798) const LOG_PATT = '([0-9_.\\-:\\s]*)(\\s+\\- )(silly|debug|info|warn|error|)(: )([a-z0-9.\\-]*)(\\s)(.*)';
          javascript.0	2020-02-11 20:32:23.894	error	at script.js.System.IOBroker_Log:413
          javascript.0	2020-02-11 20:32:23.894	error	(2798) script.js.System.IOBroker_Log compile failed:
          
          Mic 1 Reply Last reply Reply Quote 0
          • sigi234
            sigi234 Forum Testing Most Active @Oli last edited by

            @Oli sagte in [Vorlage] JS: Log-Datei aufbereiten für VIS:

            @Mic

            sorry, aber so richtig scheint das noch nicht zu funktionieren

            Der Zeitstempel wird leider noch nicht übergeben

            Hast du Material Design Widgets installiert?

            O 1 Reply Last reply Reply Quote 0
            • O
              Oli @sigi234 last edited by

              @sigi234

              ja habe ich, warum?

              sigi234 1 Reply Last reply Reply Quote 0
              • Mic
                Mic Developer @Negalein last edited by

                @Negalein sagte in [Vorlage] JS: Log-Datei aufbereiten für VIS:

                Beim Start kommen sofort Fehler.

                Oh, das sollte nicht sein. Von welcher Version kommst du denn?

                Negalein 1 Reply Last reply Reply Quote 0
                • O
                  Oli @Mic last edited by

                  @Mic

                  scheint an meiner VIS zu liegen, laut Zeitstempel um 20:37 Uhr gelöscht

                  VIS zeigt folgendes an:

                  e5aa9971-fc1c-43f7-9b86-76e7593957d8-image.png

                  Das Widget ist folgendermaßen eingestellt:

                  5b92ec50-5f30-4698-af1e-354e00e25330-image.png

                  1 Reply Last reply Reply Quote 0
                  • sigi234
                    sigi234 Forum Testing Most Active @Oli last edited by

                    @Oli sagte in [Vorlage] JS: Log-Datei aufbereiten für VIS:

                    @sigi234

                    ja habe ich, warum?

                    Widget:

                    [{"tpl":"tplVis-materialdesign-Button-State","data":{"oid":"0_userdata.0.Log-Script.All.visView1.clearJSON","g_fixed":false,"g_visibility":false,"g_css_font_text":true,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":true,"g_gestures":false,"g_signals":false,"g_last_change":true,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","buttonStyle":"outlined","vibrateOnMobilDevices":"50","iconPosition":"right","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"timestamp","lc-is-interval":false,"lc-is-moment":false,"lc-format":"DD.MM.YYYY hh:mm:ss","lc-position-vert":"bottom","lc-position-horz":"left","lc-offset-vert":"-21","lc-offset-horz":"142","lc-font-size":"x-small","lc-font-family":"RobotoCondensed-Light","lc-font-style":"","lc-bkg-color":"#000000","lc-color":"#ffffff","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":"1","value":"true","buttontext":"Clear selectet Log","colorPress":"#d40202","labelWidth":"","image":"trash-can-outline","imageColor":"#d40202","iconHeight":"30","lc-oid":"0_userdata.0.Log-Script.All.visView1.clearJSON"},"style":{"left":"158px","top":"749px","width":"170px","height":"44px","z-index":"1","border-width":"1px","border-style":"solid","border-color":"#c9c9c9","border-radius":"5px","color":"#ffffff","font-family":"RobotoCondensed-Light"},"widgetSet":"materialdesign"}]
                    
                    1 Reply Last reply Reply Quote 0
                    • Negalein
                      Negalein Global Moderator @Mic last edited by

                      @Mic sagte in [Vorlage] JS: Log-Datei aufbereiten für VIS:

                      Oh, das sollte nicht sein. Von welcher Version kommst du denn?

                      Habs gerade nochmal neu von deiner GIT-Seite kopiert und meine Config eingefügt.
                      Mic-M iobroker.logfile-script  Parses log file, applies filters, and sets states for visualization.png

                      Selbes Ergebnis

                      javascript.0	2020-02-11 20:51:01.763	error	(2798) at process._tickCallback (internal/process/next_tick.js:68:7)
                      javascript.0	2020-02-11 20:51:01.763	error	(2798) at promise.then (/opt/iobroker/node_modules/standard-as-callback/built/index.js:19:49)
                      javascript.0	2020-02-11 20:51:01.763	error	(2798) at tryCatcher (/opt/iobroker/node_modules/standard-as-callback/built/utils.js:11:23)
                      javascript.0	2020-02-11 20:51:01.763	error	(2798) at client.get (/opt/iobroker/node_modules/iobroker.js-controller/lib/states/statesInRedis.js:572:33)
                      javascript.0	2020-02-11 20:51:01.763	error	(2798) at adapter.getForeignState (/opt/iobroker/node_modules/iobroker.javascript/main.js:1055:17)
                      javascript.0	2020-02-11 20:51:01.763	error	(2798) at createProblemObject (/opt/iobroker/node_modules/iobroker.javascript/main.js:1464:17)
                      javascript.0	2020-02-11 20:51:01.763	error	(2798) at prepareScript (/opt/iobroker/node_modules/iobroker.javascript/main.js:1411:37)
                      javascript.0	2020-02-11 20:51:01.763	error	(2798) at compile (/opt/iobroker/node_modules/iobroker.javascript/main.js:1188:28)
                      javascript.0	2020-02-11 20:51:01.763	error	(2798) at Object.createScript (vm.js:277:10)
                      javascript.0	2020-02-11 20:51:01.763	error	(2798) at new Script (vm.js:83:7)
                      javascript.0	2020-02-11 20:51:01.763	error	(2798) SyntaxError: Identifier 'LOG_PATT' has already been declared
                      javascript.0	2020-02-11 20:51:01.762	error	(2798) ^
                      javascript.0	2020-02-11 20:51:01.762	error	(2798) const LOG_PATT = '([0-9_.\\-:\\s]*)(\\s+\\- )(silly|debug|info|warn|error|)(: )([a-z0-9.\\-]*)(\\s)(.*)';
                      javascript.0	2020-02-11 20:51:01.762	error	at script.js.System.IOBroker_Log:413
                      javascript.0	2020-02-11 20:51:01.762	error	(2798) script.js.System.IOBroker_Log compile failed:
                      

                      /*************************************************************************************************************************
                       * ---------------------------
                       * Log Script für ioBroker zum Aufbereiten des Logs für Visualisierungen (vis), oder um
                       * auf Log-Ereignisse zu reagieren.
                       * ---------------------------
                       *
                       * Das Script nimmt jeden neuen Logeintrag des ioBrokers und wendet entsprechend gesetzte
                       * Filter an, um den Eintrag dann in den entsprechenden Datenpunkten dieses Scripts abzulegen.
                       
                       * Es stehen auch JSON-Datenpunkte zur Verfügung, mit diesen kann im vis eine
                       * Tabelle ausgegeben werden (z.B. über das Widget 'basic - Table' oder 'materialdesign - Table').
                       *
                       * Aktuelle Version: https://github.com/Mic-M/iobroker.logfile-script
                       * Support:          https://forum.iobroker.net/topic/13971/vorlage-log-datei-aufbereiten-f%C3%BCr-vis-javascript
                       * Autor:            Mic (ioBroker) | Mic-M (github)
                       * -----------------------------------------------------------------------------------------------------------------------
                       * VORAUSSETZUNGEN
                       * 1.) Nur falls Datenpunkte unterhalb '0_userdata.0' abgelegt werden sollen:
                       *     In der Instanz des JavaScript-Adapters die Option [Erlaube das Kommando "setObject"] aktivieren.
                       *     Siehe auch: https://github.com/Mic-M/iobroker.createUserStates
                       * 2.) Dieses Script benötigt die JavaScript-Adapter-Version 4.3.0 (2019-10-09) oder höher.
                       *     Wer eine ältere Version einsetzt: Bitte Script-Version 2.0.2 verwenden.
                       * =====================================================================================
                       * -----------------------------------------------------------------------------------------------------------------------
                       * Change Log:
                       *  4.0.1 Mic   - Add "jsonDateFormat: 'dd.mm. hh:mm'," to FILTER_LOG, id 'all'.
                       *  4.0 Mic     + To allow individual settings per each defined LOG_FILTER, the following global 
                       *                settings were moved to LOG_FILTER:
                       *                 * JSON_DATE_FORMAT                   -> jsonDateFormat
                       *                 * JSON_LEN                           -> jsonLogLength
                       *                 * JSON_NO_ENTRIES                    -> jsonMaxLines
                       *                 * JSON_APPLY_CSS_LIMITED_TO_LEVEL    -> jsonCssToLevel
                       *                 * L_SORT_ORDER_DESC                  -> sortDescending
                       *              + Code improvements
                       *              + Renamed LOG_NO_OF_ENTRIES to MAX_LOG_LINES
                       *  ---------------------------------------------------------------------------------------------------- 
                       *  3.4 Mic     + Support both '0_userdata.0' and 'javascript.x' for state creation
                       *  3.3 Mic     - Fix state path
                       *  3.2 Mic     + Create all states under 0_userdata.0, and no longer under javascript.<instance> (like javascript.0)
                       *  3.1 Mic     + Change to stable as tests were successful
                       *              + Add new option REMOVE_PID: The js-controller version 2.0+ adds the PID number inside brackets 
                       *                to the beginning of the message. Setting REMOVE_PID = false will remove it.
                       *  3.0Alpha Mic + Major Change: JavaScript adapter 4.3+ now provides onLog() function: 
                       *                 https://github.com/ioBroker/ioBroker.javascript/blob/master/docs/en/javascript.md#onlog 
                       *                 We are using this new function to streamline this log script tremendously and to remove node-tail.
                       *  ---------------------------------------------------------------------------------------------------- 
                       *  2.0.2 Mic   + Changed certain functions to async to get rid of setTimout() and for the sake of better error handling.
                       *              + startTailingProcess(): ensure the tailing starts if the file is present (wait to be created)
                       *  2.0.1a Mic  Removed constant MERGE_LOGLINES_ACTIVE
                       *  2.0.0a Mic  Major improvements and fixes:
                       *              + Change from instant state update to schedule (STATE_UPDATE_SCHEDULE). The instant update, so once
                       *                new log entries coming in, caused several issues (setting and getting state values (getState() and 
                       *                setState()) within <1ms simply does not work.
                       *              - Fix issue with merging log lines
                       *              + Moved global option MERGE_LOGLINES_ACTIVE to LOG_FILTER, for allowing turning on/off for each filter id.
                       *              + Several other code improvements
                       *              Note: For upgrading from previous version: replace script entirely, re-enter all your options, 
                       *                    and delete all existing states prior to first activation of this script.
                       *  ---------------------------------------------------------------------------------------------------- 
                       *  1.5.1 Mic - Set option MERGE_LOGLINES_ACTIVE to 'false' as default, as users reported issues. See 
                       *              https://forum.iobroker.net/post/288772 . Also option MERGE_LOGLINES_ACTIVE being marked as "experimental"
                       *              in the comments. Requires further investigation.
                       *  1.5  Mic - Fix issue with option MERGE_LOGLINES_ACTIVE
                       *  1.4  Mic + New option MERGE_LOGLINES_TXT for an individual (e.g. localized) string other than 'entries'.
                                   - Fix JSON span class closing
                       *  1.3  Mic + New option MERGE_LOGLINES_ACTIVE: Merge Loglines with same log message to only one line and adds leading
                       *             '[123 entries]' to log message.
                       *  1.2  Mic - Fixed issue #6 (Button javascript.0.Log-Script.logXxxx.clearJSON not working reliably)
                       *  1.1  Mic + 1. 1.0x script seems to work reliable per user feedback and my own test, so pushing into 1.1 stable.
                       *           + New state '.logMostRecent': provides just the most recent log entry to work with "on /
                       *             subscribe" on this state and trigger actions accordingly.
                       *  1.02 alpha  Mic  - fix restarting at 0:00 (note: restarting is needed due to log file name change)
                       *  1.01 alpha  Mic  - fix: creating new file system log file only if not yet existing
                       *  1.00 alpha  Mic  + Entirely recoded to implement node-tail (https://github.com/lucagrulla/node-tail).
                       *  ----------------------------------------------------------------------------------------------------
                       *  0.8.1 Mic - Fix: L_SORT_ORDER_DESC was not defined (renamed constant name was not changed in config)
                       *  0.8 Mic - Fix: Script caused a "file not found" error if executed right at or shortly after midnight.
                       *  0.7 Mic - Fix: States "...clearDateTime" will not get an initial date value on first script start,
                       *                 also fix for "on({id: ".
                       *  0.6 Mic + Put 0.5.1 BETA into stable
                       *          + New option L_APPLY_CSS. If true, it will add <span class='log-info'>xxx</span>
                       *            to each log string. 'log-info' for level info, 'log-error' for error, etc.
                       *            This makes it easy to format a JSON table with CSS.
                       *  0.5.1 BETA Mic + New States "Clear JSON log ..." and "Clear JSON log - Date/Time ...".
                       *                   When the button "Clear JSON log" is pushed, the current date/time
                       *                   will be set into the date/time state. Once refreshed
                       *                   (per schedule in the script, e.g. after 2 minutes), the JSON
                       *                   will be cleaned and display just newer logs.
                       *                   Use Case: In vis, you can now add a button "clear log" or
                       *                   "Mark as read". If you hit the button, the log will be
                       *                   cleared and just new log items will be displayed.
                       *                   *** THIS IS STILL BEING TESTED *** therefore a beta release...
                       *  0.5  Mic + New parameter 'clean' to remove certain strings
                       *             from the log line.
                       *           + New parameter 'columns' for JSON output to specify which columns
                       *             to be shown, and in which order.
                       *           + New state "JSONcount" to have the number of log lines in state
                       *           - Fixed a few issues
                       *  0.4  Mic - Bug fix: improved validation of log line consistency
                       *  0.3  Mic + Added filtering and blacklist
                       *           - Several fixes
                       *  0.2  Mic - Bug fix: corrected wrong function name
                       *  0.1  Mic * Initial release
                       ************************************************************************************************************************/
                      
                      /*******************************************************************************
                       * Konfiguration: Pfade
                       ******************************************************************************/
                      // Pfad, unter dem die States (Datenpunkte) in den Objekten angelegt werden.
                      // Es wird die Anlage sowohl unterhalb '0_userdata.0' als auch 'javascript.x' unterstützt.
                      const LOG_STATE_PATH = '0_userdata.0.Log-Script';
                      
                      
                      // Pfad zum Log-Verzeichnis auf dem Server.
                      // Standard-Pfad unter Linux: '/opt/iobroker/log/'. Wenn das bei dir auch so ist, dann einfach belassen.
                      const LOG_FS_PATH = '/opt/iobroker/log/';
                      
                      /*******************************************************************************
                       * Konfiguration: Alle Logeinträge - Global
                       ******************************************************************************/
                      
                      // Zahl: Maximale Anzahl der letzten Logeinträge in den Datenpunkten. Alle älteren werden entfernt.
                      // Bitte nicht allzu viele behalten, denn das kostet Performance.
                      const MAX_LOG_LINES = 100;
                      
                      // Der js-Controller Version 2.0 oder größer fügt Logs teils vorne die PID in Klammern hinzu, 
                      // also z.B. "(12234) Terminated (15): Without reason". 
                      // Mit dieser Option lassen sich die PIDs aus den Logzeilen entfernen.
                      const REMOVE_PID = true;
                      
                      /**
                       * Schwarze Liste (Black list)
                       * Falls einer dieser Satzteile/Begriffe in einer Logzeile enthalten ist, dann wird der Log-Eintrag
                       * komplett ignoriert, egal was weiter unten eingestellt wird.
                       * Dies dient dazu, um penetrante Logeinträge gar nicht erst zu berücksichtigen.
                       * Bitte beachten: 
                       * 1. Mindestens 3 Zeichen erforderlich, sonst wird es nicht berücksichtigt (würde auch wenig Sinn ergeben).
                       * 2. Bestehende Datenpunkt-Inhalte dieses Scripts bei Anpassung dieser Option werden nicht nachträglich neu gefiltert,
                       *    sondern nur alle neu hinzugefügten Log-Einträge ab Speichern des Scripts werden berücksichtigt.
                       */
                      const BLACKLIST_GLOBAL = [
                          '<==Disconnect system.user.admin from ::ffff:', // web.0 Adapter
                          'system.adapter.ical.0 terminated with code 0 (OK)', 
                          'bring.0 Cannot get translations: RequestError',
                          ' reconnected. Old secret ', // Sonoff
                          'Popup-News readed...', // info.0 Adapter
                          '[warn] Projects disabled : set editorTheme.projects.enabled=true to enable', // see https://forum.iobroker.net/topic/12260/
                      	'',
                      	'',
                      ];
                      
                      /**
                       * Zusatz-Einstellung für Option "merge" für LOG_FILTER (unter "Konfiguration: Datenpunkte und Filter"):
                       * In MERGE_LOGLINES_TXT kann hier ein anderes Wort eingetragen werden, z.B. 'entries' oder 'Zeilen', damit [123 entries] 
                       * oder [123 Zeilen] vorangestellt wird anstatt [123 Einträge].
                       * HINWEIS: Falls MERGE_LOGLINES_TXT geändert wird: bitte alle Datenpunkte des Scripts löschen und dann Script neu starten.
                       */
                      const MERGE_LOGLINES_TXT = 'Einträge';
                      
                      /**
                       *  Für JSON-Tabelle: Füge CSS-Klasse hinzu je nach Log-Level (debug, silly, info, warn und error), um Tabellen-Text zu formatieren.
                       *  Beispiel für Log-Level "debug": ersetzt "xxx" durch "<span class='log-debug'>xxx</span>""
                       *  Es wird jeweils "log-" vorangestellt, also: debug -> log-debug, silly -> log-silly, info -> log-info, etc.
                       *  Etwa für Widget "basic - Table" im vis können im Reiter "CSS" z.B. folgende Zeilen hinzugefügt werden,
                       *  um Warnungen in oranger und Fehler in roter Farbe anzuzeigen.
                       *         .log-warn { color: orange; }
                       *         .log-error { color: red; }
                       *  Tipp: In LOG_FILTER kann dann bei den einzelnen Filtern mittels "jsonCssToLevel" eingestellt werden, dass das CSS
                       *        nur  für die Spalte "level" (also debug, error, info) und nicht auf alle Spalten angewendet wird.
                       */
                      const JSON_APPLY_CSS = true;
                      
                      
                      /*******************************************************************************
                       * Konfiguration: Datenpunkte und Filter
                       ******************************************************************************
                       * Dies ist das Herzstück dieses Scripts: hier werden die Datenpunkte konfiguriert, die erstellt werden sollen. 
                       * Hierbei kannst du entsprechend Filter setzen, also z.B. Wörter/Begriffe, die in Logeinträgen enthalten sein
                       * müssen, damit sie in den jeweiligen Datenpunkten aufgenommen werden.
                       * --------------------------------------------------------------------------------------------------------------------------
                       * id:              Ein Begriff ohne Leerzeichen, z.B. "error", "sonoff", homematic, etc. Die ID wird dann Teil der
                       *                  Datenpunkte, z.B. "javascript.0.Log-Script.logHomematic.log" mit automatisch vorangestelltem "log".
                       * --------------------------------------------------------------------------------------------------------------------------
                       * filter_all:      ALLE Begriffe müssen in der Logzeile enthalten sein. Ist einer der Begriffe nicht enthalten, dann wird der 
                       *                  komplette Logeintrag auch nicht übernommen. Leeres Array [] eingeben, falls hier filtern nicht gewünscht.
                       * --------------------------------------------------------------------------------------------------------------------------
                       * filter_any:      Mindestens einer der gelisteten Begriffe muss enthalten sein. Leeres Array [] eingeben, falls hier filtern
                       *                  nicht gewünscht.
                       * --------------------------------------------------------------------------------------------------------------------------
                       * blacklist:       Schwarze Liste: Wenn einer dieser Begriffe im Logeintrag enthalten ist, so wird der komplette Logeintrag 
                       *                  nicht übernommen, egal was vorher in filter_all oder filter_any definiert ist.
                       *                  Mindestens 3 Zeichen erforderlich, sonst wird es nicht berücksichtigt.
                       *                  HINWEIS: BLACKLIST_GLOBAL wird vorher schon angewendet, hier kannst du einfach nur noch eine individuelle 
                       *                  Blackliste pro id definieren.
                       * --------------------------------------------------------------------------------------------------------------------------
                       * clean:           Der Log-Eintrag wird um diese Zeichenfolgen bereinigt, d.h. diese werden entfernt, aber die restliche Zeile 
                       *                  bleibt bestehen. Z.B. um unerwünschte Zeichenfolgen zu entfernen oder Log-Ausgaben zu kürzen.
                       * --------------------------------------------------------------------------------------------------------------------------
                       * merge:           Log-Einträge mit gleichem Text zusammenfassen. Beispiel:
                       *                      -----------------------------------------------------------------------------------
                       *                      2019-08-17 20:00:00.335 - info: javascript.0 script.js.Wetter: Wetterdaten abrufen.
                       *                      2019-08-17 20:15:00.335 - info: javascript.0 script.js.Wetter: Wetterdaten abrufen.
                       *                      2019-08-17 20:30:00.335 - info: javascript.0 script.js.Wetter: Wetterdaten abrufen.
                       *                      -----------------------------------------------------------------------------------
                       *                  Daraus wird dann nur noch eine Logzeile mit letztem Datum/Uhrzeit und hinzufügen von "[3 Einträge]":
                       *                      -----------------------------------------------------------------------------------
                       *                      2019-08-17 20:30:00.335 - info: javascript.0 [3 Einträge] script.js.Wetter: Wetterdaten abrufen.
                       *                      -----------------------------------------------------------------------------------
                       *                  Zum aktivieren: true eintragen, zum deaktivieren: false eintragen.
                       * --------------------------------------------------------------------------------------------------------------------------
                       * sortDescending:  Wenn true: Sortiert die Logeinträge absteigend, also neuester oben. 
                       *                  Wenn false: Sortiert die Logeinträge aufsteigend, also ältester oben. 
                       * --------------------------------------------------------------------------------------------------------------------------
                       * jsonColumns:     Nur für JSON (für vis). 
                       *                  Folgende Spalten gibt es: 'date','level','source','msg'. Hier können einzelne Spalten entfernt oder die 
                       *                  Reihenfolge verändert werden. Bitte keine anderen Spalten eintragen, sondern nur 'date','level','source','msg'.
                       * --------------------------------------------------------------------------------------------------------------------------
                       * jsonDateFormat:  Datumsformat für JSON Log. Z.B. volles Datum mit 'YYYY-MM-DD HH:MM:SS' oder nur Uhrzeit mit "HH:MM:SS". Die 
                       *                  Platzhalter YYYY, MM, DD usw. werden jeweils ersetzt.
                       *                  YYYY = Jahr 4stellig (z.B. 2019), YY = Jahr 2stellig (z.B. 19), MM = Monat, DD = Tag, HH = Stunde, MM = Minute, 
                       *                  SS = Sekunde. Groß- oder Kleinschreibung ist egal, d.h. YYYY ist das gleiche wie yy.
                       *                  Die Verbinder (-, :, Leerzeichen, etc.) können im Prinzip frei gewählt werden.
                       *                  Beispiele: 'HH:MM:SS' für 19:37:25, 'HH:MM' für 19:37, 'DD.MM. HH:MM' für '25.07. 19:37'
                       * --------------------------------------------------------------------------------------------------------------------------
                       * jsonLogLength:   Maximale Anzahl Zeichen jeder einzelnen Log-Meldung im JSON-Log. Alles was länger ist, wird abgeschnitten.
                       * --------------------------------------------------------------------------------------------------------------------------
                       * jsonMaxLines:    Maximale Anzahl der letzten Logeinträge im JSON-Log. Alle älteren werden entfernt.
                       *                  Falls in MAX_LOG_LINES z.B. "100" gesetzt wird, wird hier bei 100 der Cut gemacht, selbst wenn in 
                       *                  jsonMaxLines etwa 250 eingetragen wird. D.h. im Bedarf zuerst MAX_LOG_LINES anpassen/erhöhen.
                       * --------------------------------------------------------------------------------------------------------------------------
                       * jsonCssToLevel:  Wenn true, dann wird JSON_APPLY_CSS nur für die Spalte "level" (also debug, error, info) angewendet, 
                       *                  aber nicht für die restlichen Spalten wie Datum, Log-Eintrag, etc.
                                          Falls alle Spalten das CSS bekommen sollen: auf false setzen.
                       * --------------------------------------------------------------------------------------------------------------------------
                       *
                       * WEITERER HINWEIS: 
                       * Bestehende Datenpunkt-Inhalte dieses Scripts bei Anpassung dieser Option werden nicht nachträglich neu 
                       * gefiltert, sondern nur alle neu hinzugefügten Log-Einträge ab Speichern des Scripts werden berücksichtigt.
                       * --------------------------------------------------------------------------------------------------------------------------
                       */
                      const LOG_FILTER = [
                      
                        // Beispiel für individuellen Eintrag. Hier wird euer Hubschrauber-Landeplatz überwacht :-) Wir wollen nur Einträge 
                        // vom Adapter 'hubschr.0'. Dabei sollen entweder Wetterwarnungen, Alarme, oder UFOs gemeldet werden. Alles unter 
                        // Windstärke "5 Bft" interessiert uns dabei nicht, daher haben wir '0 Bft' bis '4 Bft' auf die Blackliste gesetzt.
                        // Außerdem entfernen wir von der Log-Zeile die Zeichenfolgen '****', '!!!!' und 'ufo gesichtet', der Rest bleibt 
                        // aber bestehen. Zudem haben wir unter jsonColumns die Spaltenreihenfolge geändert. 'level' herausgenommen, und Quelle 
                        // ganz vorne.
                      /*
                        {
                          id:             'hubschrauberlandeplatz',
                          filter_all:     ['hubschr.0'],
                          filter_any:     ['wetterwarnung', 'alarm', 'ufo'],
                          blacklist:      ['0 Bft', '1 Bft', '2 Bft', '3 Bft', '4 Bft'],
                          clean:          ['****', '!!!!', 'ufo gesichtet'],
                          merge:          true,
                          sortDescending: true,
                          jsonDateFormat: 'dd.mm. hh:mm',       
                          jsonColumns:    ['source','date','msg'],
                          jsonLogLength:  100,
                          jsonMaxLines:   10,
                          jsonCssToLevel: true,
                        }, 
                      */
                      
                      /*
                        {
                          id:             'all',    // Beispiel "all": hier kommen alle Logeinträge rein, keine Filterung
                          filter_all:     ['', ''], // wird ignoriert, wenn leer
                          filter_any:     ['', ''], // wird ignoriert, wenn leer
                          blacklist:      ['', ''], // wird ignoriert, wenn leer
                          clean:          ['', '', ''], // wird ignoriert, wenn leer
                          merge:          true,
                          sortDescending: true,
                          jsonDateFormat: 'dd.mm. hh:mm',       
                          jsonColumns:    ['date','level','source','msg'],  // Spaltenreihenfolge für JSON (Tabelle in vis)
                          jsonLogLength:  100,
                          jsonMaxLines:   10,
                          jsonCssToLevel: true,
                        },
                      */
                      
                      /*
                        {
                          id:             'info',
                          filter_all:     [' - info: '], // nur Logeinträge mit Level 'info'
                          filter_any:     ['', ''],
                          blacklist:      ['', ''],
                          clean:          ['', '', ''],
                          merge:          true,
                          sortDescending: true,
                          jsonDateFormat: 'dd.mm. hh:mm',
                          jsonColumns:    ['date','level','source','msg'],
                          jsonLogLength:  100,
                          jsonMaxLines:   60,
                          jsonCssToLevel: true,
                        },
                      */
                      
                        {
                          id:             'debug',
                          filter_all:     [' - debug: ', ''],  // nur Logeinträge mit Level 'debug'
                          filter_any:     [''],
                          blacklist:      ['', '', ''],
                          clean:          ['', '', ''],
                          merge:          true,
                          sortDescending: true,
                          jsonColumns:    ['date','level','source','msg'],
                          jsonDateFormat: 'dd.mm. hh:mm',
                          jsonLogLength:  100,
                          jsonMaxLines:   60,
                          jsonCssToLevel: true,
                        },
                        {
                          id:             'warnanderror',
                          filter_all:     ['', ''],
                          filter_any:     [' - error: ', ' - warn: '],  // nur Logeinträge mit Levels 'warn' und 'error'
                          blacklist:      ['', 'no playback content', 'Ignore! Actual secret is '],
                          clean:          ['', '', ''],
                          merge:          true,
                          sortDescending: true,
                          jsonDateFormat: 'dd.mm. hh:mm',
                          jsonColumns:    ['date','level','source','msg'],
                          jsonLogLength:  100,
                          jsonMaxLines:   60,
                          jsonCssToLevel: true,
                        },
                      
                      /*
                        {
                          // Beispiel, um einen bestimmten Adapter zu überwachen.
                          // Hier werden alle Fehler und Warnungen des Homematic-Adapters hm-rpc.0 gelistet.
                          id:             'homematic',
                          filter_all:     ['hm-rpc.0', ''],  // hm-rpc.0 muss enthalten sein.
                          filter_any:     [' - error: ', ' - warn: '],  // entweder error oder warn
                          blacklist:      ['', '', ''],
                          clean:          ['', '', ''],
                          merge:          true,
                          sortDescending: true,
                          jsonDateFormat: 'dd.mm. hh:mm',
                          jsonColumns:    ['date','level','source','msg'],
                          jsonLogLength:  100,
                          jsonMaxLines:   60,
                          jsonCssToLevel: true,
                        },
                      */
                      
                      ];
                      
                      
                      /*******************************************************************************
                       * Konfiguration: Konsolen-Ausgaben
                       ******************************************************************************/
                      
                      // Auf true setzen, wenn zur Fehlersuche einige Meldungen ausgegeben werden sollen.
                      // Ansonsten bitte auf false stellen.
                      const LOG_DEBUG = false;
                      
                      // Auf true setzen, wenn ein paar Infos dieses Scripts im Log ausgegeben werden dürfen, bei false bleiben die Infos komplett weg.
                      const LOG_INFO = true;
                      
                      
                      /*******************************************************************************
                       * Experten-Konfiguration
                       ******************************************************************************/
                      
                      // Wie oft Datenpunkte aktualisieren?
                      // Neu reinkommende Logeinträge werden erst mal gesammelt (in Variable G_NewLogLinesArrayToProcess). Diese werden dann 
                      // regelmäßig in den Datenpunkten geschrieben. Sinnvoll ist hier nicht kürzer als 2-3 Sekunden, und nicht länger als 
                      // ein paar Minuten. Zu kurzes Intervall: Script kommt nicht mehr nach. Zu lange: falls viele Logeinträge reinkommen, 
                      // kann sich vieles "aufstauen" zur Abarbeitung. Benutze den "Cron"-Button oben rechts für komfortable Einstellung.
                      const STATE_UPDATE_SCHEDULE = '*/1 * * * *'; // jede Minute
                      
                      
                      // Leer lassen! Nur setzen, falls ein eigener Filename für das Logfile verwendet wird für Debug.
                      const DEBUG_CUSTOM_FILENAME = '';
                      
                      // Regex für die Aufteilung des Logs in 1-Datum/Zeit, 3-Level, 5-Quelle und 7-Logtext.
                      // Ggf. anzupassen bei anderem Datumsformat im Log. Wir erwarten ein Format
                      // wie z.B.: '2018-07-22 12:45:02.769  - info: javascript.0 Stop script script.js.ScriptAbc'
                      // Da als String, wurden alle Backslashes "\" mit einem zweiten Backslash escaped.
                      const LOG_PATT =  '([0-9_.\\-:\\s]*)(\\s+\\- )(silly|debug|info|warn|error|)(: )([a-z0-9.\\-]*)(\\s)(.*)';
                      
                      // Debug: Ignore. Wenn dieses String in der Logzeile enthalten ist, dann ignorieren wir es.
                      // Dient dazu, dass wir während des Scripts ins Log schreiben können, ohne dass das dieses Script berücksichtigt.
                      const DEBUG_IGNORE_STR = '[LOGSCRIPT_IGNORE]'; // Muss ein  individuelles String sein. Sonst gibt es ggf. eine Endlos-Schleife.
                      
                      
                      // Debug: Prüfen, ob jede Logzeile erfasst wird, in dem wir diese direkt danach noch mal ins Log schreiben.
                      // Bitte nur auf Anweisung vom Entwickler einschalten. Sonst wird jeder Logeintrag noch einmal wiederholt, 
                      // mit führendem DEBUG_EXTENDED_STR am Anfang und max. DEBUG_EXTENDED_NO_OF_CHARS an Anzahl Zeichen.
                      const DEBUG_EXTENDED = false;
                      const DEBUG_EXTENDED_STR = '[LOGSCRIPT_DEBUG_EXTENDED]'; // Muss ein  individuelles String sein. Sonst gibt es ggf. eine Endlos-Schleife.
                      const DEBUG_EXTENDED_NO_OF_CHARS = 120;
                      
                      /*************************************************************************************************************************
                       * Ab hier nichts mehr ändern / Stop editing here!
                       *************************************************************************************************************************/
                      
                      
                      /*************************************************************************************************************************
                       * Global variables and constants
                       *************************************************************************************************************************/
                      
                      // Final state path
                      const FINAL_STATE_LOCATION = validateStatePath(LOG_STATE_PATH, false);
                      const FINAL_STATE_PATH = validateStatePath(LOG_STATE_PATH, true);
                      
                      // Regex für die Aufteilung des Logs in 1-Datum/Zeit, 3-Level, 5-Quelle und 7-Logtext.
                      // Ggf. anzupassen bei anderem Datumsformat im Log. Wir erwarten ein Format
                      // wie z.B.: '2018-07-22 12:45:02.769  - info: javascript.0 Stop script script.js.ScriptAbc'
                      // Da als String, wurden alle Backslashes "\" mit einem zweiten Backslash escaped.
                      const LOG_PATT =  '([0-9_.\\-:\\s]*)(\\s+\\- )(silly|debug|info|warn|error|)(: )([a-z0-9.\\-]*)(\\s)(.*)';
                      
                      // Final number of VIS states. We do not allow more than 9.
                      function finalVisStatesQty(qty) {
                          let result = 0;
                          if( (!isLikeEmpty(qty)) && (qty > 0) )  {
                              if(qty > 9) {
                                  result = 9;
                              } else {
                                  result = qty;
                              }
                          }
                          return result; 
                      };
                      const FINAL_NUM_OF_VIS_VIEWS = finalVisStatesQty(NUMBER_OF_VIS_VIEWS);
                      
                      // Merge loglines: define pattern (and escape the merge text)
                      // We added an additional backslash '\' to each backslash as these need to be escaped.
                      const MERGE_REGEX_PATT = '^\[(\\d+)\\s' + escapeRegExp(MERGE_LOGLINES_TXT) + '\]\\s(.*)';
                      
                      // Log Handler variable for ioBroker function onLog()
                      let G_LogHandler;  // being set later
                      
                      // Schedule for logfile update
                      let G_Schedule_StateUpdate; // being set later
                      
                      // We add here all the new log lines to be processed regularly (per STATE_UPDATE_SCHEDULE);
                      let G_NewLogLinesArrayToProcess = [];
                      
                      /*************************************************************************************************************************
                       * init - This is executed on every script (re)start.
                       *************************************************************************************************************************/
                      init();
                      function init() {
                      
                          // Unsubscribe log handler
                          onLogUnregister(G_LogHandler);
                          
                          // Create all script states
                          const NEEDED_STATES = buildNeededStates();
                          createUserStates(FINAL_STATE_LOCATION, false, NEEDED_STATES[0], function() { // force = false
                              createUserStates(FINAL_STATE_LOCATION, true, NEEDED_STATES[1], function() { // force = true
                                  
                                  // -- All states created, so we continue by using callback
                      
                                  // Subscribe on changes: Pressed button "clearJSON"
                                  subscribeClearJson();
                      
                                  // Subscribe to log handler
                                  G_LogHandler = onLog('*', data => { // please ignore the red squiggly underline under '*', see Github issue: https://github.com/ioBroker/ioBroker.javascript/issues/457
                                      processNewLogLine(data);
                                  });
                      
                                  // Schedule writing changes into states
                                  clearSchedule(G_Schedule_StateUpdate);
                                  G_Schedule_StateUpdate = schedule(STATE_UPDATE_SCHEDULE, processNewLogsPerSchedule);
                      
                                  // Subscribe to clear all states
                                  stateSubscribeClearAllJSON()
                      
                                  // Subscribe to states for the VIS views
                                  if(FINAL_NUM_OF_VIS_VIEWS > 0) {
                                      stateSubscribeVisWhichFilter(); // which Filter
                                      stateSubscribeVisClearJson(); // clear JSON
                                  }
                      
                                  // Message
                                  if (LOG_INFO) log('Start monitoring of the ioBroker log...', 'info');
                      
                              });
                          });
                      
                      }
                      
                      function processNewLogLine(data) {
                          
                          // Convert to Log Line
                          // TODO: This is a quick implementation of new function onLog().
                          //       We need to entirely rewrite script later to fully use the data object.
                          //       However, at this time, we convert it to a standard log line being expected.
                      
                          // First, remove PID if desired
                          let msg = data.message;
                          if (REMOVE_PID) msg = removePID(msg);
                      
                          // Now convert to log line
                          let newLogEntry = timestampToLogDate(data.ts) + '  - ' + data.severity + ': ' + msg;
                      
                          // Check if we have DEBUG_IGNORE_STR in the new log line
                          if(! newLogEntry.includes(DEBUG_IGNORE_STR)) {
                      
                              if (newLogEntry.length > 45) {  // a log line with less than 45 chars is not a valid log line.
                      
                                  // Cleanse and apply blacklist
                                  newLogEntry = cleanseLogLine(newLogEntry);
                      
                                  // Push result into logArrayFinal
                                  G_NewLogLinesArrayToProcess.push(newLogEntry);
                      
                                  // some debugging
                                  if (SCRIPT_DEBUG) log (DEBUG_IGNORE_STR + '===============================================================');
                                  if (SCRIPT_DEBUG) log (DEBUG_IGNORE_STR + 'New Log Entry, Len (' + newLogEntry.length + '), content: [' + newLogEntry + ']');
                      
                                  // This is for debugging purposes, and it will log every new log entry once again. See DEBUG_EXTENDED option above.
                                  if (DEBUG_EXTENDED) {
                                      if (! newLogEntry.includes(DEBUG_EXTENDED_STR)) { // makes sure no endless loop here.
                                          log(DEBUG_EXTENDED_STR + newLogEntry.substring(0, DEBUG_EXTENDED_NO_OF_CHARS));
                                              
                                      }
                                  }
                              }
                          }
                      
                      }
                      
                      /**
                       * Called per schedule STATE_UPDATE_SCHEDULE.
                       * It processes G_NewLogLinesArrayToProcess
                       */
                      function processNewLogsPerSchedule() {
                          if (! isLikeEmpty (G_NewLogLinesArrayToProcess) ) {
                      
                              // We use array spreads '...' to copy array. If not, array is changed by reference and not value.
                              // That means, if we change the target array, it will also change the source array.
                              // See https://stackoverflow.com/questions/7486085/copy-array-by-value
                              let logArrayToProcess = [...G_NewLogLinesArrayToProcess];
                              G_NewLogLinesArrayToProcess.length = 0; // emptying array. https://stackoverflow.com/questions/4804235/difference-between-array-length-0-and-array
                      
                              /**
                               * Apply the filters as set in LOG_FILTER and split up log levels into elements of an array
                               * logArrayToProcessFiltered will look as follows:
                               *   logArrayToProcessFiltered = [
                               *     ['info':'15.08.2019 09:27:55.476 info adapt.0 some log', 'error':''],
                               *     ['info':'15.08.2019 09:33:58.522 info adapt.0 some more log', 'error':''],
                               *     ['info':'', 'error':'15.08.2019 09:37:55.807 error adapt.0 some error log']
                               *   ]
                               */
                              let logArrayToProcessFiltered = [];
                              for (let lpEntry of logArrayToProcess) {
                                  let logEntryFilteredArray = applyFilter(lpEntry);
                                  logArrayToProcessFiltered.push(logEntryFilteredArray);
                              }
                      
                              // Further process and finally set states with our results.
                              processLogArrayAndSetStates(logArrayToProcessFiltered);
                      
                          }
                      
                          // Finally, set updatestate with current date/time
                          setState(FINAL_STATE_PATH + '.All.lastTimeUpdated', Date.now());    
                      
                      }
                      
                      
                      
                      /*************************************************************************************************************************
                       * Filtering
                       *************************************************************************************************************************/
                      
                      /**
                       * This function applies the filters as set in LOG_FILTER.
                       * Also, it splits up the log levels into elements of an array we return by this function.
                       * @param {string} strLogEntry
                       * @return {array}  split up log levels as elements within this array, like: ['info':'logtext', 'error':'logtext'] etc.
                       */
                      function applyFilter(strLogEntry) {
                          // We add one element per each filter to the Array ('all', 'error', etc.)
                          let logArrayProcessed = [];
                          for (let j = 0; j < LOG_FILTER.length; j++) {
                              logArrayProcessed[LOG_FILTER[j].id] = '';
                          }
                      
                          // We apply regex here. This will also eliminate all log lines without proper info
                          // like date/time, log level, and entry.
                          let arrSplitLogLine = logLineSplit(strLogEntry);
                          if (arrSplitLogLine !== false) {
                      
                              if (isLikeEmpty(LOG_FILTER) === false) {
                                          
                                  // Now let's iterate over the filter array elements
                                  // We check if both the "all" and "any" filters  apply. If yes, - and blacklist false - we add the log line.
                                  for (let k = 0; k < LOG_FILTER.length; k++) {
                                      if ( (strMatchesTerms(strLogEntry, LOG_FILTER[k].filter_all, 'every') === true)
                                      && (strMatchesTerms(strLogEntry, LOG_FILTER[k].filter_any, 'some') === true)
                                      && (strMatchesTerms(strLogEntry, LOG_FILTER[k].blacklist, 'blacklist') === false) ) {
                                          logArrayProcessed[LOG_FILTER[k].id] = logArrayProcessed[LOG_FILTER[k].id] + strLogEntry + "\n";
                                      }
                                      
                                      // Now we remove terms if desired
                                      if (isLikeEmpty(LOG_FILTER[k].clean) === false) {
                                          for (let lpTerm of LOG_FILTER[k].clean) {
                                              if (lpTerm !== '') {
                                                  logArrayProcessed[LOG_FILTER[k].id] = logArrayProcessed[LOG_FILTER[k].id].replace(lpTerm, '');
                                              }
                                          }
                                      }
                                  }
                              }
                          }
                          return logArrayProcessed;
                      }
                      
                      /*************************************************************************************************************************
                       * Further processing
                       *************************************************************************************************************************/
                      
                      /**
                       * Further processes the log array and set states accordingly.
                       * 
                       * @param  arrayLogInput             The Array of the log input.
                       *                                   Array is like: 
                       *                                   [
                       *                                      ['info':'15.08.2019 09:27:55.476 info adapt.0 some log', 'error':''],
                       *                                      ['info':'15.08.2019 09:33:58.522 info adapt.0 some more log', 'error':''],
                       *                                      ['info':'', 'error':'15.08.2019 09:37:55.807 error adapt.0 some error log'],
                       *                                   ]
                       **/
                      function processLogArrayAndSetStates(arrayLogInput) {
                      
                          /*****************
                           * [1] Build array from LOG_FILTER. Looks like: arrayFilterIds = ['info', 'error', 'warn'].
                           * Also, build result array to keep our results. Lools like resultArr = [info: '', error: '', warn: '']
                           *****************/
                          let arrayFilterIds = [];
                          let resultArr = [];
                          for (let i = 0; i < LOG_FILTER.length; i++) {
                              arrayFilterIds.push(LOG_FILTER[i].id); // each LOG_FILTER id into array
                              resultArr[LOG_FILTER[i].id] = '';
                          }
                          /*****************
                           * [2] Process element by element, so ['info':'log test', 'error':'log test'] of given array.
                           * We fill the result array accordingly.
                           *****************/
                          for (let lpElement of arrayLogInput) {
                      
                              // Loop thru our new array arrayFilterIds and fill result array
                              for (let k = 0; k < arrayFilterIds.length; k++) {
                      
                                  // some variables
                                  let lpFilterId = arrayFilterIds[k]; // Filter ID from LOG_FILTER, like 'error', 'info', 'custom', etc.
                                  let lpNewLogLine = lpElement[lpFilterId]; // Current log line of provided array element of 'error', 'info', 'custom' etc.
                      
                                  if (isLikeEmpty(lpNewLogLine)) {
                                      // No log content for the given filter id.
                                      if (SCRIPT_DEBUG) log (DEBUG_IGNORE_STR + 'Filter  [' + lpFilterId + ']: No match.');
                                  } else {
                      
                                      if (SCRIPT_DEBUG) log (DEBUG_IGNORE_STR + 'Filter [' + lpFilterId + ']: Match! New Log Line length: (' + lpNewLogLine.length + ')');
                      
                                      // Append new log line to result array
                                      if (isLikeEmpty(resultArr[lpFilterId])) {
                                          resultArr[lpFilterId] = lpNewLogLine; 
                                      } else {
                                          resultArr[lpFilterId] = lpNewLogLine + resultArr[lpFilterId]; // "\n" not needed, always added above
                                      }
                                  }
                              }
                          }
                      
                          /*****************
                           * [3] We merge with the current state.
                           *****************/
                          for (let k = 0; k < arrayFilterIds.length; k++) {
                              let lpFilterId = arrayFilterIds[k]; // Filter ID from LOG_FILTER, like 'error', 'info', 'custom', etc.
                              let lpStatePath1stPart = FINAL_STATE_PATH + '.log' + cleanseStatePath(lpFilterId); // Get Path to state
                              let lpNewFinalLog = resultArr[lpFilterId];
                      
                              if (! isLikeEmpty(lpNewFinalLog) )  {
                      
                                  // Get state value
                      			let strCurrentStateLog = getState(lpStatePath1stPart + '.log').val; // Get state contents of loop item
                                  
                                  // Add state log lines to our final log
                                  if (! isLikeEmpty(strCurrentStateLog)) {
                                      lpNewFinalLog = lpNewFinalLog + strCurrentStateLog; // "\n" not needed, always added above
                                  }            
                      
                                  // Convert to array for easier handling
                                  let lpNewFinalLogArray = lpNewFinalLog.split(/\r?\n/);
                      
                                  // Remove duplicates
                                  lpNewFinalLogArray = arrayRemoveDublicates(lpNewFinalLogArray);
                      
                                  // Remove empty values
                                  lpNewFinalLogArray = cleanArray(lpNewFinalLogArray);
                      
                                  // Sort array descending
                                  lpNewFinalLogArray = sortLogArrayByDate(lpNewFinalLogArray, 'desc');
                      
                                  // Merge Loglines if multiple values and add leading '[123 entries]' to log message
                                  let doMerge = getConfigValuePerKey(LOG_FILTER, 'id', lpFilterId, 'merge');
                                  if (doMerge || doMerge === 'true') {    // also check for string 'true' in case user used string
                                      lpNewFinalLogArray = mergeLogLines(lpNewFinalLogArray);
                                  }
                      
                                  // We need a separate array for JSON
                                  let lpNewFinalLogArrayJSON = lpNewFinalLogArray;
                      
                                  // Let's remove elements if time of when button '.clearJSON' was pressed is greater than log date.
                                  lpNewFinalLogArrayJSON = clearJsonByDate(lpNewFinalLogArrayJSON, lpStatePath1stPart + '.clearJSON');              
                      
                                  // Just keep the first x elements of the log. JSON log length is being set individually.
                                  lpNewFinalLogArray = lpNewFinalLogArray.slice(0, MAX_LOG_LINES);
                                  lpNewFinalLogArrayJSON = lpNewFinalLogArrayJSON.slice(0, getConfigValuePerKey(LOG_FILTER, 'id', lpFilterId, 'jsonMaxLines'));
                      
                                  // Get just the most recent log entry into string
                                  let lpMostRecent = lpNewFinalLogArray[0];
                      
                                  // Sort ascending if desired
                                  if (!getConfigValuePerKey(LOG_FILTER, 'id', lpFilterId, 'sortDescending')) {
                                      lpNewFinalLogArray = lpNewFinalLogArray.reverse();
                                      lpNewFinalLogArrayJSON = lpNewFinalLogArrayJSON.reverse();
                                  }
                      
                                  // ** Finally set the states
                      
                                  ///////////////////////////////
                                  // -1- Full Log, String, separated by "\n"
                                  ///////////////////////////////
                                  let strResult = lpNewFinalLogArray.join("\n");
                                  if (SCRIPT_DEBUG) log (DEBUG_IGNORE_STR + 'New length to be set into state: (' + strResult.length + '), state: [' + lpStatePath1stPart + '.log' + ']');
                      
                                  setState(lpStatePath1stPart + '.log', strResult);
                      
                                  ///////////////////////////////
                                  // -2- JSON, with elements date and msg
                                  ///////////////////////////////
                                  // Let's put together the JSON
                                  let jsonArr = [];
                                  for (let j = 0; j < lpNewFinalLogArrayJSON.length; j++) {
                                      // Get 4 elements in array: datetime, level, source, message
                                      let arrSplitLogLine = logLineSplit(lpNewFinalLogArrayJSON[j]);
                                      if (arrSplitLogLine !== false) {
                                          let strLogMsg = arrSplitLogLine.message;
                                          // Reduce the length for each log message per "jsonLogLength"
                                          strLogMsg = strLogMsg.substr(0, LOG_FILTER[k].jsonLogLength);
                                          // ++++++
                                          // Build the final Array
                                          // ++++++
                                          // We need this section to generate the JSON with the columns (which ones, and order) as specified in LOG_FILTER
                                          let objectJSONentry = {}; // object (https://stackoverflow.com/a/13488998)
                                          if (isLikeEmpty(LOG_FILTER[k].jsonColumns)) log('Columns not specified in "jsonColumns".', 'warn');
                                          // Prepare CSS
                                          let strCSS1, strCSS2;
                                          let strCSS1_level, strCSS2_level;
                                          if (JSON_APPLY_CSS) {
                                              strCSS1 = "<span class='log-" + arrSplitLogLine.level + "'>";
                                              strCSS2 = '</span>';
                                              strCSS1_level = strCSS1;
                                              strCSS2_level = strCSS2;
                                              if (LOG_FILTER[k].jsonCssToLevel) {
                                                  strCSS1 = '';
                                                  strCSS2 = '';
                                              }
                                          }
                      
                                          for (let lpCol of LOG_FILTER[k].jsonColumns) {
                                              switch (lpCol) {
                                                  case 'date' :
                                                      objectJSONentry.date = strCSS1 + formatLogDateStr(arrSplitLogLine.datetime, LOG_FILTER[k].jsonDateFormat) + strCSS2;
                                                      break;
                                                  case 'level' :
                                                      objectJSONentry.level = strCSS1_level + arrSplitLogLine.level + strCSS2_level;
                                                      break;
                                                  case 'source' :
                                                      objectJSONentry.source = strCSS1 + arrSplitLogLine.source + strCSS2;
                                                      break;
                                                  case 'msg' :
                                                      objectJSONentry.msg = strCSS1 + strLogMsg + strCSS2;
                                                      break;
                                                  default:
                                                      //nothing;
                                              }
                                          }
                                          // Ok, so now we have the JSON entry.
                                          jsonArr.push(objectJSONentry);
                                      }
                      
                                  }
                                  if (! isLikeEmpty(lpNewFinalLogArrayJSON)) {
                                      setState(lpStatePath1stPart + '.logJSON', JSON.stringify(jsonArr));
                                      setState(lpStatePath1stPart + '.logJSONcount', lpNewFinalLogArrayJSON.length);
                                  } 
                              }
                          }
                      
                          /*****************
                           * [4] Finally done. Now we set states for the VIS views.
                           *****************/
                          if(FINAL_NUM_OF_VIS_VIEWS > 0) {
                              
                              setTimeout(function() { // Apply timeout, to ensure all states are set.
                      
                                  for(let i = 0; i < FINAL_NUM_OF_VIS_VIEWS; i++) {
                                      let lpStateViewSelFilter = FINAL_STATE_PATH + '.All.visView' + (i+1); // like: 0_userdata.0.Log-Script.All.visView1.whichFilter
                                      let currentVisFilter = getState(lpStateViewSelFilter + '.whichFilter').val;
                                      if(! isLikeEmpty(currentVisFilter)) {
                                          let jsonStateToGet = LOG_STATE_PATH + '.log' + currentVisFilter + '.logJSON';
                                          if( isState(jsonStateToGet) ) {
                                              setState(lpStateViewSelFilter + '.outputJSON', getState(jsonStateToGet).val); // Finally: set state
                                              setState(lpStateViewSelFilter + '.outputJSONcount', getState(jsonStateToGet + 'count').val); // Finally: set state
                                          } else {
                                              log('Log-Script-Fehler: Gewählter Filter ' +  currentVisFilter + ', aber Datenpunkt [' + jsonStateToGet + '] ist nicht vorhanden.', 'error');
                                          }
                                      }
                                  }
                              }, 2000);
                          }
                      }
                      
                      /*************************************************************************************************************************
                       * Script specific supporting functions
                       *************************************************************************************************************************/
                      
                      /**
                       * Subscribe to all Log-Script.logXXXX.clearJSON
                       * This will allow to clear JSON log if button is pressed.
                       */
                      function subscribeClearJson() {
                          // Set current date to state if button is pressed
                          let logSubscribe = '';
                          for (let i = 0; i < LOG_FILTER.length; i++) {
                              let lpFilterId = cleanseStatePath(LOG_FILTER[i].id);
                              let lpStateFirstPart = FINAL_STATE_PATH + '.log' + lpFilterId;
                              logSubscribe += ( (logSubscribe === '') ? '' : ', ') + lpFilterId;
                              on({id: lpStateFirstPart + '.clearJSON', change: 'any', val: true}, function(obj) {
                                  let stateBtnPth = obj.id // e.g. [javascript.0.Log-Script.logInfo.clearJSON]
                                  let firstPart = stateBtnPth.substring(0, stateBtnPth.length-10); // get first part of obj.id, like "javascript.0.Log-Script.logInfo"
                                  let filterID = firstPart.slice(firstPart.lastIndexOf('.') + 1); // gets the filter id, like "logInfo"
                                  if (SCRIPT_DEBUG) log(DEBUG_IGNORE_STR + 'Clear JSON states for [' + filterID + '].');
                                  // We clear the according JSON states
                                  setState(firstPart + '.logJSON', '[]');
                                  setState(firstPart + '.logJSONcount', 0);
                      
                              });
                          }
                          if (SCRIPT_DEBUG) log (DEBUG_IGNORE_STR + 'Subscribing to Clear JSON Buttons: ' + logSubscribe)
                      }
                      
                      /**
                       * Subscribe to all .All.clearAllJSON
                       */
                      function stateSubscribeClearAllJSON() {
                      
                          on({id: FINAL_STATE_PATH + '.All.clearAllJSON', change: 'any', val: true}, function (obj) {
                      
                              // 1. All fitered log states
                              const ALL_FILTER_IDS = getAllFilterIds();
                              for (let loopFilterId of ALL_FILTER_IDS) {
                                  // Version 4.5.6
                                  // See https://forum.iobroker.net/post/375932
                                  // We do no longer set the target states directly, but use the according clearJSON instead.
                                  // This is to ensure that user can use the clearJSON timestamp for the time of last clearJSON.
                                  setState(FINAL_STATE_PATH + '.log' + loopFilterId + '.clearJSON', true);
                                  // setState(FINAL_STATE_PATH + '.log' + loopFilterId + '.logJSON', '[]');
                                  // setState(FINAL_STATE_PATH + '.log' + loopFilterId + '.logJSONcount', 0);
                      
                              }
                      
                              // 2. Vis add-ons
                              if(FINAL_NUM_OF_VIS_VIEWS > 0) {
                                  for(let i = 0; i < FINAL_NUM_OF_VIS_VIEWS; i++) {
                                      let lpState = FINAL_STATE_PATH + '.All.visView' + (i+1);
                                      setState(lpState + '.outputJSON', '[]');
                                      setState(lpState + '.outputJSONcount', 0);
                                  }
                              }
                          });
                      
                      }
                      
                      
                      /**
                       * Subscribe to .All.visViewX.whichFilter
                       */
                      function stateSubscribeVisWhichFilter() {
                          if(FINAL_NUM_OF_VIS_VIEWS > 0) {
                              for(let i = 0; i < FINAL_NUM_OF_VIS_VIEWS; i++) {
                                  let lpState = FINAL_STATE_PATH + '.All.visView' + (i+1);
                                  on({id: lpState + '.whichFilter', change: 'ne'}, function (obj) {
                                      if(!isLikeEmpty(obj.state.val)) {
                                          let firstStatePart = obj.id.substr(0, (obj.id.length - obj.id.split('.').pop().length - 1)); // Gibt von '0_userdata.0.System.Log-Script.All.visView1.whichFilter' nur den Hauptteil ohne String nach letztem Punkt zurück, also "0_userdata.0.System.Log-Script.All.visView1"
                                          setState(firstStatePart + '.' + 'outputJSON', getState(LOG_STATE_PATH + '.log' + obj.state.val + '.logJSON').val);
                                          setState(firstStatePart + '.' + 'outputJSONcount', getState(LOG_STATE_PATH + '.log' + obj.state.val + '.logJSONcount').val);
                                      }
                                  });
                              }
                          }
                      }
                      
                      /**
                       * Subscribe to all .All.visViewX.clearJSON
                       */
                      function stateSubscribeVisClearJson() {
                          if(FINAL_NUM_OF_VIS_VIEWS > 0) {
                              for(let i = 0; i < FINAL_NUM_OF_VIS_VIEWS; i++) {
                                  let lpState = FINAL_STATE_PATH + '.All.visView' + (i+1);
                                  on({id: lpState + '.clearJSON', change: 'any', val: true}, function (obj) {
                                      let firstStatePart = obj.id.substr(0, (obj.id.length - obj.id.split('.').pop().length - 1)); // Gibt von '0_userdata.0.System.Log-Script.All.visView1.clearJSON' nur den Hauptteil ohne String nach letztem Punkt zurück, also "0_userdata.0.System.Log-Script.All.visView1"
                                      let selectedFilter = getState(firstStatePart + '.whichFilter').val;
                                      if(!isLikeEmpty(selectedFilter)) {    
                                          setState(firstStatePart + '.outputJSON', '[]'); // .All.visViewX.outputJSON leeren
                                          setState(firstStatePart + '.outputJSONcount', 0); // .All.visViewX.outputJSONcount leeren
                                          setState(FINAL_STATE_PATH + '.log' + selectedFilter + '.clearJSON', true); // Log-Script.logXXXX.clearJSON leeren
                                      }             
                                  });
                              }
                          }
                      }
                      
                      /**
                       * Get all filter ids into an array (Homematic, Warnanderror, etc.)
                       * @return [array] Filter ids in Array
                       */
                      function getAllFilterIds() {
                          let allFilterIds = [];
                          for(let i = 0; i < LOG_FILTER.length; i++) {
                              let lpIDClean = cleanseStatePath(LOG_FILTER[i].id)
                              if (LOG_FILTER[i].id !== '') allFilterIds.push(lpIDClean); // für VIS add-on states
                          };
                          return allFilterIds;
                      }
                      
                      
                      /**
                       * Reformats a log date string accordingly
                       * @param {string}    strDate   The date to convert
                       * @param {string}    format    e.g. 'yyyy-mm-dd HH:MM:SS'. Both upper case and lower case letters are allowed.
                       * @return {string}             Returns the resulting date string
                       */
                      function formatLogDateStr(strDate, format) {
                      
                          let strResult = format.toLowerCase();
                          strResult = strResult.replace('yyyy', strDate.substr(0,4));
                          strResult = strResult.replace('yy', strDate.substr(2,2));
                          strResult = strResult.replace('mm', strDate.substr(5,2));
                          strResult = strResult.replace('dd', strDate.substr(8,2));
                          strResult = strResult.replace('hh', strDate.substr(11,2));
                          strResult = strResult.replace('mm', strDate.substr(14,2));
                          strResult = strResult.replace('ss', strDate.substr(17,2));
                      
                          return strResult;
                      
                      }
                      
                      /**
                       * Cleanse the log line
                       * @param {string}   logLine    The log line to be cleansed.
                       * @return {string}             The cleaned log line
                       */
                      function cleanseLogLine(logLine) {
                          // Remove color escapes - https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings
                          let logLineResult = logLine.replace(/\u001b[.*?m/g, ''); 
                          // Sometimes, a log line starts with the term "undefined", so we remove it.
                          if (logLineResult.substr(0,9) === 'undefined') logLineResult = logLineResult.substr(9,99999);
                          // Remove white space, tab stops, new line
                          logLineResult = logLineResult.replace(/\s\s+/g, ' ');
                          // Check against global blacklist
                          if(strMatchesTerms(logLineResult, BLACKLIST_GLOBAL, 'blacklist')) logLineResult = '';
                      
                      
                          return logLineResult;
                      }
                      
                      
                      
                      /**
                       * Sorts the log array by date. We expect the first 23 chars of each element being a date in string format.
                       * @param {array} inputArray       Array to process
                       * @param {string}  order          'asc' or 'desc' for ascending or descending order
                       */
                      function sortLogArrayByDate(inputArray, order) {
                          var result = inputArray.sort(function(a,b){
                                  // Turn your strings into dates, and then subtract them
                                  // to get a value that is either negative, positive, or zero.
                                  a = new Date(a.substr(0,23));
                                  b = new Date(b.substr(0,23));
                                  if (order === 'asc') {
                                      return a - b;
                                  } else {
                                      return b - a;
                                  }
                      
                          });
                      
                          return result;
                      }
                      
                      
                      /**
                       * Splits a given log entry into an array with 4 elements.
                       * @param {string}  inputValue  Log line like '2018-07-22 11:47:53.019  - info: javascript.0 script.js ...'
                       * @return {object}   Array with 4 elements: 
                       *                     0. datetime (e.g. 2018-07-22 11:47:53.019),
                       *                     1. level (e.g. info)
                       *                     2. source (e.g. javascript.0)
                       *                     3. message (e.g. script.js....)
                       *                     Returns FALSE if no match or input value not valid
                       */
                      function logLineSplit(inputValue) {
                      
                          // Get RegEx ready
                          let mRegEx = new RegExp(LOG_PATT, 'g');
                      
                          // Split
                          let returnObj = {}
                          let m;
                          do {
                              m = mRegEx.exec(inputValue);
                              if (m) {
                                  returnObj.datetime = m[1];
                                  returnObj.spaceAt2 = m[2];
                                  returnObj.level = m[3];
                                  returnObj.spaceAt4 = m[4];
                                  returnObj.source = m[5];
                                  returnObj.spaceAt6 = m[6];
                                  returnObj.message = m[7];
                              } 
                          } while (m);
                      
                          // Now we check if we have valid entries we want
                          if ((returnObj.datetime === undefined)
                              || (returnObj.level === undefined)
                              || (returnObj.source === undefined)
                              || (returnObj.message === undefined)
                          ) {
                             return false; // no valid hits
                          }
                          // We can return the array now, since it meets all requirements
                          return returnObj;
                      
                      }
                      
                      /**
                       * Merges date/time, level, source and message to a logline
                       * @param  {array}    inputValue   Array with 4 elements: date/time, level, source, message
                       * @return {string}   Merged log line as string. Empty string '', if input value not valid.
                      																			  
                       */
                      function logLineMerge(inputValue) {
                      
                          if (inputValue.length === 4) {
                              let mergedLine = inputValue[0] + ' - ' + inputValue[1] + ': ' + inputValue[2] + ' ' + inputValue[3];
                              return mergedLine;
                          } else {
                              // We expect a size of 4, so go out
                              return '';
                          }
                      
                      }
                      
                      
                      /**
                       * Merge Loglines if multiple values and add leading '[123 entries]' to log message
                       * @param {array}  logArray        array of log entries
                       * @return {array} the new merged log array
                       */
                      function mergeLogLines(logArray) {
                      
                          // We use array spreads '...' to copy array. If not, array is changed by reference and not value.
                          // That means, if we change the target array, it will also change the source array.
                          // See https://stackoverflow.com/questions/7486085/copy-array-by-value
                          let arrCopy = [...logArray];
                          let arrNew = [];
                      
                          for (let i = 0; i < arrCopy.length; i++) {
                      
                              if (! isLikeEmpty(arrCopy[i])) {
                      
                                  let lpEntry = arrCopy[i];
                                  let lineWithoutDate = lpEntry.substring(23);
                                  let lpLineSplit = logLineSplit(lpEntry);
                      
                                  // Get multiple values
                                  let lpMulti = arrayGetElements(arrCopy, removeLeading123entries(lpLineSplit.message), false);
                                  let result = lpEntry;
                                  let lineCounter = 0;
                                  if (lpMulti.length > 1) { // Treffer - die aktuelle Zeile zählt ja auch mit.
                                      lineCounter = lpMulti.length;
                                      let hitLeadingNumber = -1;
                                      for (let hitLine of lpMulti) {
                                          let hitLineSplit = logLineSplit(hitLine);
                                          // Check if hit contains '[123 entries]'. If yes, get the number out of it into lineCounter.
                                          // If not, we just count with 1.
                                          hitLeadingNumber = checkForMultiEntry(hitLineSplit.message);
                                          if (hitLeadingNumber > 1) {
                                              lineCounter = hitLeadingNumber + lpMulti.length - 1;
                                          }
                                      }
                                  } else {
                                      lineCounter = 1;
                                  }
                      
                                  if (lineCounter > 1) {
                      
                                          // remove from array by filling empty value
                                          arrCopy = arrayReplaceElementsByValue(arrCopy, removeLeading123entries(lpLineSplit.message), '', false);
                                          // new result
                                          result = logLineMerge([lpLineSplit.datetime, lpLineSplit.level, lpLineSplit.source, '[' + lineCounter + ' ' + MERGE_LOGLINES_TXT + '] ' + removeLeading123entries(lpLineSplit.message)]);
                                  }
                                  arrNew.push(result);
                              } 
                          }
                      
                          return arrNew;
                      
                          /**
                           * @param  {string}   strInput    A log message with potential leading '[123 entries]'
                           * @return {string}   string without leading '[123 entries]', if it is there
                           */
                          function removeLeading123entries(strInput) {
                      
                              let mRegEx = new RegExp(MERGE_REGEX_PATT);
                              let matches = mRegEx.exec(strInput);
                              if (matches === null) {
                                  return strInput;
                              } else {
                                  return matches[2];
                              }
                          }
                      
                          /**
                           * @param  {string}   strInput    A log message checking for leading '[123 entries]'
                           * @return {number}   returns the number 123 from '[123 entries]' if any match, or -1 if not found
                           */
                          function checkForMultiEntry(strInput) {
                      
                              // Get RegEx ready
                              let mRegEx = new RegExp(MERGE_REGEX_PATT);
                              let matches = mRegEx.exec(strInput);
                              if (matches === null) {
                                  return -1;
                              } else {
                                  return parseInt(matches[1]);
                              }
                          }
                      
                      }
                      
                      
                      
                      /*************
                       * Get the file system path and filename of the current log file.
                       * 
                       * ioBroker creates a log file every midnight at 0:00 under '/opt/iobroker/log/'
                       * Syntax of the log file is: iobroker.YYYY-MM-DD.log
                       * This function returns the full path to the log file, considering the current date/time when this function is called.
                       * @return {string}      Path and file name to log file.
                       */
                      function getCurrentFullFsLogPath() {
                          let strLogPathFinal = LOG_FS_PATH;
                          if (strLogPathFinal.slice(-1) !== '/') strLogPathFinal = strLogPathFinal + '/';
                          let strFullLogPath = strLogPathFinal + DEBUG_CUSTOM_FILENAME;
                          if (DEBUG_CUSTOM_FILENAME === '') strFullLogPath = strLogPathFinal + 'iobroker.' + getCurrentISODate() + '.log';
                          return strFullLogPath;
                      }
                      
                      /**
                       * Clear array: if stateForTimeStamp is greater or equal than log date, we remove the entire log entry
                       * @param {array} inputArray     Array of log entries
                       * @param {string} stateForTimeStamp     state of which we need the time stamp
                       * @return {array} cleaned log
                       */
                      function clearJsonByDate(inputArray, stateForTimeStamp) {
                          let dtState = new Date(getState(stateForTimeStamp).ts);
                          if (SCRIPT_DEBUG) log (DEBUG_IGNORE_STR + 'Time of last change of state [' + stateForTimeStamp + ']: ' + dtState);
                          let newArray = [];
                          for (let lpLog of inputArray) {
                              let dtLog = new Date(lpLog.substr(0,23));
                              if (dtLog.getTime() >= dtState.getTime()) {
                                  newArray.push(lpLog);            
                              }
                        }
                        return newArray;
                      }
                      
                      /**
                       * Build an array of states we need to create.
                       * @return {array} 2 array of states to be created. First is for force=false, second is for force=true.
                       */
                      function buildNeededStates() {
                      
                          let debugCleanIDs = '';  // für Debug-Ausgabe
                          let statesArray = [];
                      
                          for(let i = 0; i < LOG_FILTER.length; i++) {
                              if (LOG_FILTER[i].id !== '') {
                                  let lpIDClean = cleanseStatePath(LOG_FILTER[i].id);
                                  debugCleanIDs += ((debugCleanIDs === '') ? '' : '; ') + lpIDClean; // für Debug-Ausgabe
                      
                                  statesArray.push({ id:'log' + lpIDClean + '.log', name:'Filtered Log - ' + lpIDClean, type:"string", role: "state", def: ""});
                                  statesArray.push({ id:'log' + lpIDClean + '.logJSON', name:'Filtered Log - ' + lpIDClean + ' - JSON', type:"string", role: "state", def: ""});
                                  statesArray.push({ id:'log' + lpIDClean + '.logJSONcount', name:'Filtered Log - Count of JSON ' + lpIDClean, role: "state", type:"number", def: 0});
                                  statesArray.push({ id:'log' + lpIDClean + '.clearJSON', name:'Clear JSON log ' + lpIDClean, role: "button", type:"boolean", def: false});
                      
                              }
                          }
                          if (SCRIPT_DEBUG) log (DEBUG_IGNORE_STR + 'createLogStates(): Clean IDs: ' + debugCleanIDs);
                      
                      
                          let finalStatesForceFalse = [];
                          let finalStatesForceTrue = [];
                          for (let s=0; s < statesArray.length; s++) {
                              finalStatesForceFalse.push([FINAL_STATE_PATH + '.' + statesArray[s].id, {
                                  'name': statesArray[s].name,
                                  'desc': statesArray[s].name,
                                  'type': statesArray[s].type,
                                  'read': true,
                                  'write': true,
                                  'role': statesArray[s].role,
                                  'def': statesArray[s].def,
                              }]);
                          }
                      
                          // Jetzt noch die VIS Add-on states (All.visView1, All.visView2, etc.)
                          if(FINAL_NUM_OF_VIS_VIEWS > 0) {
                              let dropdown = '';
                              const ALL_FILTER_IDS = getAllFilterIds();
                              for (let lpEntry of ALL_FILTER_IDS) {
                                  dropdown += '"' + lpEntry + '":"' + lpEntry + '",'; // fill JSON string
                              }
                              dropdown = dropdown.substr(0, dropdown.length-1); // remove last comma ","
                              dropdown = '{' + dropdown + '}'; // finalize JSON string
                              let dropdownJSON = JSON.parse(dropdown); // convert to JSON
                              for(let i = 0; i < FINAL_NUM_OF_VIS_VIEWS; i++) {
                                  let lpStateVisViews = FINAL_STATE_PATH + '.All.visView' + (i+1);
                                  finalStatesForceTrue.push ([lpStateVisViews + '.whichFilter', {'name':'Für VIS-Button/Auswahlmenü des anzuzeigenden Filter in JSON-Tabelle', 'type':'string', 'read':false, 'write':true, 'role':'value', 'states': dropdownJSON, 'def': ALL_FILTER_IDS[0]}]); // force true to rebuild state drop-down
                                  finalStatesForceTrue.push ([lpStateVisViews + '.outputJSON',  {'name': 'JSON-Ausgabe des in whichFilter gewählten Filters', 'type': 'string', 'read': true, 'write': false, 'role': 'state', 'def': '' }]); // force true to empty on re-start of script.
                                  finalStatesForceFalse.push ([lpStateVisViews + '.outputJSONcount',  {'name': 'Anzahl Zeilen in JSON-Ausgabe des in whichFilter gewählten Filters', 'type': 'number', 'read': true, 'write': false, 'role': 'state', 'def': 0 }]);
                                  finalStatesForceFalse.push([lpStateVisViews + '.clearJSON',   {'name':'Clear currently selected JSON Log', 'type':'boolean', 'read':false, 'write':true, 'role':'button', 'def': false }]);
                              }
                          }
                      
                          // Nun noch Button zum leeren ALLER JSONs
                          finalStatesForceFalse.push([FINAL_STATE_PATH + '.All.clearAllJSON',   {'name':'Clear ALL JSON logs', 'type':'boolean', 'read':false, 'write':true, 'role':'button', 'def': false }]);
                      
                          // Zeit letztes Update
                          finalStatesForceFalse.push([FINAL_STATE_PATH + '.All.lastTimeUpdated',   {'name':'Date/Time of last update', 'type':'number', 'read':true, 'write':false, 'role':'value.time'}]);
                      
                          // Done.
                          return [finalStatesForceFalse, finalStatesForceTrue];
                      }
                      
                      /**
                       * Converts a timestamp to log date format, like 2019-10-15 16:38:00.260.
                       * @param {object}  timeStamp   The date/time timestamp to convert.
                       * @return {string} The resulting log date format as string.
                       */
                      function timestampToLogDate(timeStamp) {
                      
                          let date = new Date(timeStamp);
                          // Need to convert to local time as this time provided from onLog() is UTC
                          // https://stackoverflow.com/questions/6525538/convert-utc-date-time-to-local-date-time/18330682
                          let localDate = new Date(date.getTime() - date.getTimezoneOffset()*60*1000);
                      
                          // Convert to ISO string, so like 2019-10-15T16:38:00.260Z
                          let strResult = localDate.toISOString();
                      
                          // date.toISOString() adds T and Z, so we remove these letters, as the log do not show these.
                          strResult = strResult.replace('T', ' ');  // remove T
                          strResult = strResult.replace('Z', '');  // remove Z at the end
                          return strResult;
                      
                      }
                      
                      /**
                       * Remove PID in log message 
                       * The js-controller version 2.0+ adds the PID number inside brackets to the beginning of the message. We remove it here.
                       * @param {string} msg   The log message, like: 'javascript.0 (123) Logtext 123 Logtext 123 Logtext 123 Logtext 123'
                       */
                      function removePID(msg) {
                      
                          // First: Split source and message text. 
                          // Input is like: 'javascript.0 (123) Logtext 123 Logtext 123 Logtext 123 Logtext 123'
                          let regexp = /^(\S+)\s(.*)/;
                          let matches_array = msg.match(regexp);
                          let strFirst = matches_array[1];    // like 'javascript.0'
                          let strRest = matches_array[2];     // like '(123) Logtext 123 Logtext 123 Logtext 123 Logtext 123'
                          
                          // Next, we remove the PID
                          strRest = strRest.replace(/^\([0-9]{1,9}\)\s/, '');
                      
                          // Last, we put the two strings together again
                          return strFirst + ' ' + strRest;
                      
                      }
                      
                      
                      
                      
                      /*************************************************************************************************************************
                       * onStop - Being executed once this ioBroker Script stops. 
                       *************************************************************************************************************************/
                      // This is to end the Tale. Not sure, if we indeed need it, but just in case...
                      onStop(function myScriptStop () {
                      
                          // Unsubscribe log handler
                          onLogUnregister(G_LogHandler);
                          if (LOG_INFO) log('Unsubscribed to Log Handler.', 'info');
                      
                      }, 0);
                      
                      
                      
                      /*************************************************************************************************************************
                       * General supporting functions
                       *************************************************************************************************************************/
                      
                      /**
                       * Remove Duplicates from Array
                       * Source - https://stackoverflow.com/questions/23237704/nodejs-how-to-remove-duplicates-from-array
                       * @param {array} inputArray       Array to process
                       * @return {array}  Array without duplicates.
                       */
                      function arrayRemoveDublicates(inputArray) {
                          let uniqueArray;
                          uniqueArray = inputArray.filter(function(elem, pos) {
                              return inputArray.indexOf(elem) == pos;
                          });
                          return uniqueArray;
                      }
                      
                      /**
                       * Clean Array: Removes all falsy values: undefined, null, 0, false, NaN and "" (empty string)
                       * Source: https://stackoverflow.com/questions/281264/remove-empty-elements-from-an-array-in-javascript
                       * @param {array} inputArray       Array to process
                       * @return {array}  Cleaned array
                       */
                      function cleanArray(inputArray) {
                        var newArray = [];
                        for (let i = 0; i < inputArray.length; i++) {
                          if (inputArray[i]) {
                            newArray.push(inputArray[i]);
                          }
                        }
                        return newArray;
                      }
                      
                      
                      /**
                       * Checks if Array or String is not undefined, null or empty.
                       * 08-Sep-2019: added check for [ and ] to also catch arrays with empty strings.
                       * @param inputVar - Input Array or String, Number, etc.
                       * @return true if it is undefined/null/empty, false if it contains value(s)
                       * Array or String containing just whitespaces or >'< or >"< or >[< or >]< is considered empty
                       */
                      function isLikeEmpty(inputVar) {
                          if (typeof inputVar !== 'undefined' && inputVar !== null) {
                              let strTemp = JSON.stringify(inputVar);
                              strTemp = strTemp.replace(/\s+/g, ''); // remove all whitespaces
                              strTemp = strTemp.replace(/\"+/g, "");  // remove all >"<
                              strTemp = strTemp.replace(/\'+/g, "");  // remove all >'<
                              strTemp = strTemp.replace(/[+/g, "");  // remove all >[<
                              strTemp = strTemp.replace(/]+/g, "");  // remove all >]<
                              if (strTemp !== '') {
                                  return false;
                              } else {
                                  return true;
                              }
                          } else {
                              return true;
                          }
                      }
                      
                      /**
                       * Returns the current date in ISO format "YYYY-MM-DD".
                       * @return  {string}    Date in ISO format
                       */
                      function getCurrentISODate() {
                          let currDate = new Date();
                          return currDate.getFullYear() + '-' + zeroPad((currDate.getMonth() + 1), 2) + '-' + zeroPad(currDate.getDate(), 2);
                      }
                      
                      /**
                       * Fügt Vornullen zu einer Zahl hinzu, macht also z.B. aus 7 eine "007". 
                       * zeroPad(5, 4);    // wird "0005"
                       * zeroPad('5', 6);  // wird "000005"
                       * zeroPad(1234, 2); // wird "1234" :)
                       * @param  {string|number}  num     Zahl, die Vornull(en) bekommen soll
                       * @param  {number}         places  Anzahl Stellen.
                       * @return {string}         Zahl mit Vornullen wie gewünscht.
                       */
                      function zeroPad(num, places) {
                          let zero = places - num.toString().length + 1;
                          return Array(+(zero > 0 && zero)).join("0") + num;        
                      } 
                      
                      
                      /**
                       * Will just keep lower case letters, numbers, '-' and '_' and removes the rest
                       * Also, capitalize first Letter.
                       */
                      function cleanseStatePath(stringInput) {
                          let strProcess = stringInput;
                          strProcess = strProcess.replace(/([^a-z0-9_\-]+)/gi, '');
                          strProcess = strProcess.toLowerCase();
                          strProcess = strProcess.charAt(0).toUpperCase() + strProcess.slice(1);
                          return strProcess;
                      
                      }
                      
                      
                      /**
                       * Checks if the string provided contains either every or some terms.
                       * Source: https://stackoverflow.com/questions/36283767/javascript-select-the-string-if-it-matches-multiple-words-in-array
                       * @param {string} strInput - The string on which we run this search
                       * @param {array} arrayTerms - The terms we are searching, e.g. ["hue", "error", "raspberry"]
                       * @param {string} type - 'every': all terms must match to be true,
                       *                        'some': at least one term (or more) must match
                       *                        'blacklist': different here: function will always
                       *                         return FALSE, but if one of the arrayTerms contains
                       *                         minimum 3 chars and is found in provided string,
                       *                         we return TRUE (= blacklisted item found).
                       * @return {boolean}       true, if it contains ALL words, false if not all words (or none)
                       *                         Also, will return true if arrayTerms is not array or an empty array
                       */
                      function strMatchesTerms(strInput, arrayTerms, type) {
                          if(type === 'blacklist') {
                              if (Array.isArray(arrayTerms)) {
                                  let arrayTermsNew = [];
                                  for (let lpTerm of arrayTerms) {
                                      if (lpTerm.length >= 3) {
                                          arrayTermsNew.push(lpTerm);
                                      }
                                  }
                                  if(isLikeEmpty(arrayTermsNew) === false) {
                                      let bResultBL = arrayTermsNew.some(function(word) {
                                          return strInput.indexOf(word) > -1;
                                      });
                                      return bResultBL;
                                  } else {
                                      return false; // return false if no items to be blacklisted
                                  }
                              } else {
                                  return false; // we return false if the arrayTerms given is not an array. Want to make sure if we really should blacklist...
                              }
                      
                          } else {
                              if (Array.isArray(arrayTerms)) {
                                  if(type === 'every') {
                                      let bResultEvery = arrayTerms.every(function(word) {
                                          return strInput.indexOf(word) > -1;
                                      });
                                      return bResultEvery;
                                  } else if(type === 'some') {
                                      let bResultSome = arrayTerms.some(function(word) {
                                          return strInput.indexOf(word) > -1;
                                      });
                                      return bResultSome;
                                  }
                      
                              } else {
                                  return true; // we return true if the arrayTerms given is not an array
                              }
                          }
                      }
                      
                      /**
                       * Checks if a a given state or part of state is existing.
                       * This is a workaround, as getObject() or getState() throw warnings in the log.
                       * Set strict to true if the state shall match exactly. If it is false, it will add a wildcard * to the end.
                       * See: https://forum.iobroker.net/topic/11354/
                       * @param {string}    strStatePath     Input string of state, like 'javas-cript.0.switches.Osram.Bedroom'
                       * @param {boolean}   [strict=true]    Optional: Default is true. If true, it will work strict, if false, it will add a wildcard * to the end of the string
                       * @return {boolean}                   true if state exists, false if not
                       */
                      function isState(strStatePath, strict) {
                      
                          if(strict === undefined) strict = true;
                      
                          let mSelector;
                          if (strict) {
                              mSelector = $('state[id=' + strStatePath + '$]');
                          } else {
                              mSelector = $('state[id=' + strStatePath + ']');
                          }
                          if (mSelector.length > 0) {
                              return true;
                          } else {
                              return false;
                          }
                      }
                      
                      
                      /**
                       * Removing Array element(s) by input value. 
                       * @param {array}   arr             the input array
                       * @param {string}  valRemove       the value to be removed
                       * @param {boolean} [exact=true]    OPTIONAL: default is true. if true, it must fully match. if false, it matches also if valRemove is part of element string
                       * @return {array}  the array without the element(s)
                       */
                      function arrayRemoveElementsByValue(arr, valRemove, exact) {
                      
                          for ( let i = 0; i < arr.length; i++){ 
                              if (exact) {
                                  if ( arr[i] === valRemove) {
                                      arr.splice(i, 1);
                                      i--; // required, see https://love2dev.com/blog/javascript-remove-from-array/
                                  }
                              } else {
                                  if (arr[i].indexOf(valRemove) != -1) {
                                      arr.splice(i, 1);
                                      i--; // see above
                                  }
                              }
                          }
                          return arr;
                      }
                      
                      /**
                       * Escapes a string for use in RegEx as (part of) pattern
                       * Source: https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
                       * @param {string} inputStr  The input string to be escaped
                       * @return {string}  The escaped string
                       */
                      function escapeRegExp(inputStr) {
                          return inputStr.replace(/[.*+?^${}()|[]\]/g, '\\$&'); // $& means the whole matched string
                      }
                      
                      
                      /**
                       * Get all elements of an array if found
                       * @param {array}   arr             the input array
                       * @param {string}  valFind         the value to find
                       * @param {boolean} [exact=true]    OPTIONAL: default is true. if true, it must fully match. if false, it matches also if valRemove is part of element string
                       * @return {array}  an array with all hits or empty array if no hits.
                       */
                      function arrayGetElements(arr, valFind, exact) {
                          let resultArr = [];
                          for ( let i = 0; i < arr.length; i++){ 
                              if (exact) {
                                  if ( arr[i] === valFind) {
                                      resultArr.push(arr[i]);
                                  }
                              } else {
                                  if (arr[i].indexOf(valFind) != -1) {
                                      resultArr.push(arr[i]);
                                  }
                              }
                          }
                          return resultArr;
                      }
                      
                      /**
                       * Replace Array element(s) by input value. 
                       * @param {array}   arr             the input array
                       * @param {string}  valReplace      the value to search for
                       * @param {string}  newValue        the new value
                       * @param {boolean} [exact=true]    OPTIONAL: default is true. if true, it must fully match. if false, it matches also if valRemove is part of element string
                       * @return {array}  the array with replaced the element(s)
                       */
                      function arrayReplaceElementsByValue(arr, valReplace, newValue, exact) {
                      
                          for ( let i = 0; i < arr.length; i++){ 
                              if (exact) {
                                  if ( arr[i] === valReplace) {
                                      arr[i] = newValue;
                                  }
                              } else {
                                  if (arr[i].indexOf(valReplace) != -1) {
                                      arr[i] = newValue;
                                  }
                              }
                          }
                          return arr;
                      }
                      
                      /**
                       * Retrieve values from a CONFIG variable, example:
                       * const CONF = [{car: 'bmw', color: 'black', hp: '250'}, {car: 'audi', color: 'blue', hp: '190'}]
                       * To get the color of the Audi, use: getConfigValuePerKey(CONF, 'car', 'audi', 'color')
                       * To find out which car has 190 hp, use: getConfigValuePerKey(CONF, 'hp', '190', 'car')
                       * @param {object}  config     The configuration variable/constant
                       * @param {string}  key1       Key to look for.
                       * @param {string}  key1Value  The value the key should have
                       * @param {string}  key2       The key which value we return
                       * @returns {any}    Returns the element's value, or number -1 of nothing found.
                       */
                      function getConfigValuePerKey(config, key1, key1Value, key2) {
                          for (let lpConfDevice of config) {
                              if ( lpConfDevice[key1] === key1Value ) {
                                  if (lpConfDevice[key2] === undefined) {
                                      return -1;
                                  } else {
                                      return lpConfDevice[key2];
                                  }
                              }
                          }
                          return -1;
                      }
                      
                      
                      /**
                       * For a given state path, we extract the location '0_userdata.0' or 'javascript.0' or add '0_userdata.0', if missing.
                       * @param {string}  path            Like: 'Computer.Control-PC', 'javascript.0.Computer.Control-PC', '0_userdata.0.Computer.Control-PC'
                       * @param {boolean} returnFullPath  If true: full path like '0_userdata.0.Computer.Control-PC', if false: just location like '0_userdata.0' or 'javascript.0'
                       * @return {string}                 Path
                       */
                      function validateStatePath(path, returnFullPath) {
                          if (path.startsWith('.')) path = path.substr(1);    // Remove first dot
                          if (path.endsWith('.'))   path = path.slice(0, -1); // Remove trailing dot
                          if (path.length < 1) log('Provided state path is not valid / too short.', 'error')
                          let match = path.match(/^((javascript\.([1-9][0-9]|[0-9])\.)|0_userdata\.0\.)/);
                          let location = (match == null) ? '0_userdata.0' : match[0].slice(0, -1); // default is '0_userdata.0'.
                          if(returnFullPath) {
                              return (path.indexOf(location) == 0) ? path : (location + '.' + path);
                          } else {
                              return location;
                          }
                      }
                      
                      
                      /**
                       * Create states under 0_userdata.0 or javascript.x
                       * Current Version:     https://github.com/Mic-M/iobroker.createUserStates
                       * Support:             https://forum.iobroker.net/topic/26839/
                       * Autor:               Mic (ioBroker) | Mic-M (github)
                       * Version:             1.1 (26 January 2020)
                       * Example:             see https://github.com/Mic-M/iobroker.createUserStates#beispiel
                       * -----------------------------------------------
                       * PLEASE NOTE: Per https://github.com/ioBroker/ioBroker.javascript/issues/474, the used function setObject() 
                       *              executes the callback PRIOR to completing the state creation. Therefore, we use a setTimeout and counter. 
                       * -----------------------------------------------
                       * @param {string} where          Where to create the state: '0_userdata.0' or 'javascript.x'.
                       * @param {boolean} force         Force state creation (overwrite), if state is existing.
                       * @param {array} statesToCreate  State(s) to create. single array or array of arrays
                       * @param {object} [callback]     Optional: a callback function -- This provided function will be executed after all states are created.
                       */
                      function createUserStates(where, force, statesToCreate, callback = undefined) {
                       
                          const WARN = false; // Only for 0_userdata.0: Throws warning in log, if state is already existing and force=false. Default is false, so no warning in log, if state exists.
                          const LOG_DEBUG = false; // To debug this function, set to true
                          // Per issue #474 (https://github.com/ioBroker/ioBroker.javascript/issues/474), the used function setObject() executes the callback 
                          // before the state is actual created. Therefore, we use a setTimeout and counter as a workaround.
                          const DELAY = 50; // Delay in milliseconds (ms). Increase this to 100, if it is not working.
                      
                          // Validate "where"
                          if (where.endsWith('.')) where = where.slice(0, -1); // Remove trailing dot
                          if ( (where.match(/^((javascript\.([1-9][0-9]|[0-9]))$|0_userdata\.0$)/) == null) ) {
                              log('This script does not support to create states under [' + where + ']', 'error');
                              return;
                          }
                      
                          // Prepare "statesToCreate" since we also allow a single state to create
                          if(!Array.isArray(statesToCreate[0])) statesToCreate = [statesToCreate]; // wrap into array, if just one array and not inside an array
                      
                          // Add "where" to STATES_TO_CREATE
                          for (let i = 0; i < statesToCreate.length; i++) {
                              let lpPath = statesToCreate[i][0].replace(/\.*\./g, '.'); // replace all multiple dots like '..', '...' with a single '.'
                              lpPath = lpPath.replace(/^((javascript\.([1-9][0-9]|[0-9])\.)|0_userdata\.0\.)/,'') // remove any javascript.x. / 0_userdata.0. from beginning
                              lpPath = where + '.' + lpPath; // add where to beginning of string
                              statesToCreate[i][0] = lpPath;
                          }
                      
                          if (where != '0_userdata.0') {
                              // Create States under javascript.x
                              let numStates = statesToCreate.length;
                              statesToCreate.forEach(function(loopParam) {
                                  if (LOG_DEBUG) log('[Debug] Now we are creating new state [' + loopParam[0] + ']');
                                  let loopInit = (loopParam[1]['def'] == undefined) ? null : loopParam[1]['def']; // mimic same behavior as createState if no init value is provided
                                  createState(loopParam[0], loopInit, force, loopParam[1], function() {
                                      numStates--;
                                      if (numStates === 0) {
                                          if (LOG_DEBUG) log('[Debug] All states processed.');
                                          if (typeof callback === 'function') { // execute if a function was provided to parameter callback
                                              if (LOG_DEBUG) log('[Debug] Function to callback parameter was provided');
                                              return callback();
                                          } else {
                                              return;
                                          }
                                      }
                                  });
                              });
                          } else {
                              // Create States under 0_userdata.0
                              let numStates = statesToCreate.length;
                              let counter = -1;
                              statesToCreate.forEach(function(loopParam) {
                                  counter += 1;
                                  if (LOG_DEBUG) log ('[Debug] Currently processing following state: [' + loopParam[0] + ']');
                                  if( ($(loopParam[0]).length > 0) && (existsState(loopParam[0])) ) { // Workaround due to https://github.com/ioBroker/ioBroker.javascript/issues/478
                                      // State is existing.
                                      if (WARN && !force) log('State [' + loopParam[0] + '] is already existing and will no longer be created.', 'warn');
                                      if (!WARN && LOG_DEBUG) log('[Debug] State [' + loopParam[0] + '] is already existing. Option force (=overwrite) is set to [' + force + '].');
                                      if(!force) {
                                          // State exists and shall not be overwritten since force=false
                                          // So, we do not proceed.
                                          numStates--;
                                          if (numStates === 0) {
                                              if (LOG_DEBUG) log('[Debug] All states successfully processed!');
                                              if (typeof callback === 'function') { // execute if a function was provided to parameter callback
                                                  if (LOG_DEBUG) log('[Debug] An optional callback function was provided, which we are going to execute now.');
                                                  return callback();
                                              }
                                          } else {
                                              // We need to go out and continue with next element in loop.
                                              return; // https://stackoverflow.com/questions/18452920/continue-in-cursor-foreach
                                          }
                                      } // if(!force)
                                  }
                      
                                  // State is not existing or force = true, so we are continuing to create the state through setObject().
                                  let obj = {};
                                  obj.type = 'state';
                                  obj.native = {};
                                  obj.common = loopParam[1];
                                  setObject(loopParam[0], obj, function (err) {
                                      if (err) {
                                          log('Cannot write object for state [' + loopParam[0] + ']: ' + err);
                                      } else {
                                          if (LOG_DEBUG) log('[Debug] Now we are creating new state [' + loopParam[0] + ']')
                                          let init = null;
                                          if(loopParam[1].def === undefined) {
                                              if(loopParam[1].type === 'number') init = 0;
                                              if(loopParam[1].type === 'boolean') init = false;
                                              if(loopParam[1].type === 'string') init = '';
                                          } else {
                                              init = loopParam[1].def;
                                          }
                                          setTimeout(function() {
                                              setState(loopParam[0], init, true, function() {
                                                  if (LOG_DEBUG) log('[Debug] setState durchgeführt: ' + loopParam[0]);
                                                  numStates--;
                                                  if (numStates === 0) {
                                                      if (LOG_DEBUG) log('[Debug] All states processed.');
                                                      if (typeof callback === 'function') { // execute if a function was provided to parameter callback
                                                          if (LOG_DEBUG) log('[Debug] Function to callback parameter was provided');
                                                          return callback();
                                                      }
                                                  }
                                              });
                                          }, DELAY + (20 * counter) );
                                      }
                                  });
                              });
                          }
                      }
                      

                      Mic-M created this issue in ioBroker/ioBroker.javascript

                      closed onLog() with wildcard *: not identified by editor #457

                      Mic-M created this issue in ioBroker/ioBroker.javascript

                      closed setObject() function: callback not working as intended. #474

                      Mic-M created this issue in ioBroker/ioBroker.javascript

                      closed setObject() function: callback not working as intended. #474

                      Mic-M created this issue in ioBroker/ioBroker.javascript

                      closed 0_userdata.0: existsState() vs. $-Selector $().length after state deletion #478

                      Mic 1 Reply Last reply Reply Quote 0
                      • Mic
                        Mic Developer @Negalein last edited by

                        @Negalein
                        Das obige Script von dir wirft im JS-Adapter zahlreiche Fehler, siehe rote Markierungen rechts.
                        xxxxx.png

                        So sieht das von Github aus:
                        xxxgit.png

                        Am besten startest du noch mal mit dem aktuellen Github-Script.

                        Negalein 1 Reply Last reply Reply Quote 0
                        • Negalein
                          Negalein Global Moderator @Mic last edited by

                          @Mic sagte in [Vorlage] JS: Log-Datei aufbereiten für VIS:

                          Am besten startest du noch mal mit dem aktuellen Github-Script.

                          jetzt passt es.
                          DP wurden sofort angelegt.

                          1 Reply Last reply Reply Quote 0
                          • MartinK.
                            MartinK. @Mic last edited by

                            @Mic Absolut genial gemacht! Sogar für Anfänger wie mich alles zu verstehen 🙂 ... läuft auf anhieb!!! 😀 👍 👍

                            1 Reply Last reply Reply Quote 1
                            • B
                              blackeagle998 last edited by blackeagle998

                              @Mic
                              Das Skript läuft wie immer super, solange ich mindestens eine VIS View angebe.
                              Ändere ich den Wert "const NUMBER_OF_VIS_VIEWS = 1;" auf 0, erhalte ich im LOG folgende Fehler:

                              javascript.0	2020-02-13 12:02:44.106	error	(10714) at process._tickCallback (internal/process/next_tick.js:68:7)
                              javascript.0	2020-02-13 12:02:44.106	error	(10714) at promise.then (/opt/iobroker/node_modules/standard-as-callback/built/index.js:19:49)
                              javascript.0	2020-02-13 12:02:44.105	error	(10714) at tryCatcher (/opt/iobroker/node_modules/standard-as-callback/built/utils.js:11:23)
                              javascript.0	2020-02-13 12:02:44.105	error	(10714) at client.set.err (/opt/iobroker/node_modules/iobroker.js-controller/lib/states/statesInRedis.js:534:55)
                              javascript.0	2020-02-13 12:02:44.105	error	(10714) at err (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:318:38)
                              javascript.0	2020-02-13 12:02:44.105	error	(10714) at Object.<anonymous> (script.js.common.02_IO-Broker.Logfile:1708:44)
                              javascript.0	2020-02-13 12:02:44.105	error	(10714) at script.js.common.02_IO-Broker.Logfile:421:9
                              javascript.0	2020-02-13 12:02:44.104	error	(10714) at createUserStates (script.js.common.02_IO-Broker.Logfile:1629:43)
                              javascript.0	2020-02-13 12:02:44.103	error	(10714) Error in callback: TypeError: Cannot read property 'replace' of undefined
                              

                              Die Zeilen 1627 bis 1633 enthalten folgenden Code:

                               // Add "where" to STATES_TO_CREATE
                                  for (let i = 0; i < statesToCreate.length; i++) {
                                      let lpPath = statesToCreate[i][0].replace(/\.*\./g, '.'); // replace all multiple dots like '..', '...' with a single '.'
                                      lpPath = lpPath.replace(/^((javascript\.([1-9][0-9]|[0-9])\.)|0_userdata\.0\.)/,'') // remove any javascript.x. / 0_userdata.0. from beginning
                                      lpPath = where + '.' + lpPath; // add where to beginning of string
                                      statesToCreate[i][0] = lpPath;
                                  }
                              

                              Das soll nur als Hinweis dienen, da das Skript ja für die VIS Aufbereitung gedacht ist und somit mindestens eine VIS View vorhanden sein sollte.

                              Mic 1 Reply Last reply Reply Quote 0
                              • Mic
                                Mic Developer @blackeagle998 last edited by

                                @blackeagle998 sagte in [Vorlage] JS: Log-Datei aufbereiten für VIS:

                                Das soll nur als Hinweis dienen

                                Vielen Dank für den Test. Habe ich in Version 4.7 korrigiert. Für ein Update reicht es, alles unterhalb des folgenden Kommentars zu ersetzen:

                                /*************************************************************************************************************************
                                 * Ab hier nichts mehr ändern / Stop editing here!
                                 *************************************************************************************************************************/
                                
                                1 Reply Last reply Reply Quote 0
                                • Latzi
                                  Latzi last edited by

                                  @Mic

                                  ich hab im Log folgende Fehlermeldungen:

                                  2020-02-14 20:06:16.407 - error: rtspStream.0 (14568) uncaught exception: i is not defined
                                  2020-02-14 20:06:16.408 - error: rtspStream.0 (14568) ReferenceError: i is not defined
                                  2020-02-14 20:06:17.452 - error: host.Pi4-Server Caught by controller[43]: frame=95046 fps= 15 q=6.1 size= 315696kB time=01:45:32.56 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.452 - error: host.Pi4-Server Caught by controller[44]: frame=95054 fps= 15 q=3.9 size= 315727kB time=01:45:33.06 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.452 - error: host.Pi4-Server Caught by controller[45]: frame=95061 fps= 15 q=4.7 size= 315740kB time=01:45:33.64 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.452 - error: host.Pi4-Server Caught by controller[46]: frame=95069 fps= 15 q=5.0 size= 315772kB time=01:45:34.07 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.453 - error: host.Pi4-Server Caught by controller[47]: frame=95076 fps= 15 q=4.7 size= 315785kB time=01:45:34.65 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.453 - error: host.Pi4-Server Caught by controller[48]: frame=95084 fps= 15 q=4.6 size= 315819kB time=01:45:35.15 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.453 - error: host.Pi4-Server Caught by controller[49]: frame=95092 fps= 15 q=5.7 size= 315848kB time=01:45:35.66 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.453 - error: host.Pi4-Server Caught by controller[50]: frame=95100 fps= 15 q=4.7 size= 315864kB time=01:45:36.16 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.453 - error: host.Pi4-Server Caught by controller[51]: frame=95108 fps= 15 q=4.6 size= 315900kB time=01:45:36.66 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.453 - error: host.Pi4-Server Caught by controller[52]: frame=95115 fps= 15 q=5.0 size= 315932kB time=01:45:37.24 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.454 - error: host.Pi4-Server Caught by controller[53]: frame=95123 fps= 15 q=4.2 size= 315949kB time=01:45:37.74 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.454 - error: host.Pi4-Server Caught by controller[54]: frame=95131 fps= 15 q=4.7 size= 315975kB time=01:45:38.25 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.454 - error: host.Pi4-Server Caught by controller[55]: frame=95139 fps= 15 q=4.6 size= 316009kB time=01:45:38.75 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.465 - error: host.Pi4-Server Caught by controller[56]: frame=95146 fps= 15 q=6.0 size= 316021kB time=01:45:39.26 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.465 - error: host.Pi4-Server Caught by controller[57]: frame=95154 fps= 15 q=4.8 size= 316056kB time=01:45:39.76 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.465 - error: host.Pi4-Server Caught by controller[58]: frame=95162 fps= 15 q=3.6 size= 316087kB time=01:45:40.26 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.465 - error: host.Pi4-Server Caught by controller[59]: ReferenceError: i is not defined
                                  2020-02-14 20:06:17.465 - error: host.Pi4-Server Caught by controller[59]: at WebSocketServer.wsServer.broadcast (/opt/iobroker/node_modules/node-rtsp-stream/videoStream.js:103:54)
                                  2020-02-14 20:06:17.466 - error: host.Pi4-Server Caught by controller[59]: at VideoStream.on (/opt/iobroker/node_modules/node-rtsp-stream/videoStream.js:109:26)
                                  2020-02-14 20:06:17.466 - error: host.Pi4-Server Caught by controller[59]: at VideoStream.emit (events.js:198:13)
                                  2020-02-14 20:06:17.466 - error: host.Pi4-Server Caught by controller[59]: at Mpeg1Muxer.mpeg1Muxer.on (/opt/iobroker/node_modules/node-rtsp-stream/videoStream.js:47:17)
                                  2020-02-14 20:06:17.466 - error: host.Pi4-Server Caught by controller[59]: at Mpeg1Muxer.emit (events.js:198:13)
                                  2020-02-14 20:06:17.466 - error: host.Pi4-Server Caught by controller[59]: at Socket.Mpeg1Muxer.stream.stdout.on (/opt/iobroker/node_modules/node-rtsp-stream/mpeg1muxer.js:39:17)
                                  2020-02-14 20:06:17.467 - error: host.Pi4-Server Caught by controller[59]: at Socket.emit (events.js:198:13)
                                  2020-02-14 20:06:17.467 - error: host.Pi4-Server Caught by controller[59]: at addChunk (_stream_readable.js:288:12)
                                  2020-02-14 20:06:17.467 - error: host.Pi4-Server Caught by controller[59]: at readableAddChunk (_stream_readable.js:269:11)
                                  2020-02-14 20:06:17.467 - error: host.Pi4-Server Caught by controller[59]: at Socket.Readable.push (_stream_readable.js:224:10)
                                  2020-02-14 20:06:17.467 - error: host.Pi4-Server Caught by controller[59]: frame=95169 fps= 15 q=4.4 size= 316101kB time=01:45:40.84 bitrate= 408.4kbits/s speed= 1x
                                  2020-02-14 20:06:17.467 - error: host.Pi4-Server Caught by controller[60]: frame=95177 fps= 15 q=5.3 size= 316135kB time=01:45:41.34 bitrate= 408.4kbits/s speed= 1x 
                                  

                                  Es wird mir im DP error aber nichts angezeigt. Ich habe auch nichts deratiges in der blacklist. Hast du eine Erklärung dafür?

                                  Glasfaser Mic 2 Replies Last reply Reply Quote 0
                                  • Glasfaser
                                    Glasfaser @Latzi last edited by Glasfaser

                                    @Latzi

                                    Teste mal mit dem Skript von Mic , hast du dort eine Log Ausgabe ?

                                    log('Error-Meldung', 'error');
                                    
                                    log('Warn-Meldung', 'warn');
                                    
                                    // Nochmal nach 2 Sekunden.
                                    
                                    setTimeout(function() {
                                    
                                        log('Error-Meldung', 'error');
                                    
                                        log('Warn-Meldung', 'warn');
                                    
                                    }, 2*1000);
                                    
                                    // Nach 30 Sekunden noch eine Warnmeldung.
                                    
                                    setTimeout(function() {
                                    
                                        log('Neue Warn-Meldung nach 30 Sekunden', 'warn');
                                    
                                    }, 30*1000);
                                    
                                    
                                    Latzi 1 Reply Last reply Reply Quote 0
                                    • Latzi
                                      Latzi @Glasfaser last edited by

                                      @Glasfaser ja, die Meldungen kommen. Liegt wohl an der Art und Weise wie rtspstream die Fehler generiert.
                                      Danke dir für die Unterstützung!

                                      Glasfaser 1 Reply Last reply Reply Quote 0
                                      • Glasfaser
                                        Glasfaser @Latzi last edited by Glasfaser

                                        @Latzi

                                        Habe mal ein Fehler simuliert .... rtspstream wird im LOG angezeigt

                                        1.JPG

                                        Zeig doch mal deine Usereinstellung , für die ERROR Filterung.

                                        Latzi 1 Reply Last reply Reply Quote 0
                                        • Latzi
                                          Latzi @Glasfaser last edited by

                                          @Glasfaser
                                          mein Block error sieht folgendermaßen aus:

                                           {
                                              id:             'error',
                                              filter_all:     [' - error:', ''],  // nur Logeinträge mit Level 'error'
                                              filter_any:     [''],
                                              blacklist:      ['', '', ''],
                                              clean:          ['', '', ''],
                                              merge:          true,
                                              sortDescending: true,
                                              jsonColumns:    ['date','level','source','msg'],
                                              jsonDateFormat: 'dd.mm. hh:mm',
                                              jsonLogLength:  200,
                                              jsonMaxLines:   60,
                                              jsonCssToLevel: true,
                                            }
                                          

                                          Kannst du das bitte mal mit deinem Block vergleichen?

                                          Glasfaser 1 Reply Last reply Reply Quote 0
                                          • Glasfaser
                                            Glasfaser @Latzi last edited by

                                            @Latzi sagte in [Vorlage] JS: Log-Datei aufbereiten für VIS:

                                            Füge mal das ein :

                                             {
                                            
                                                id:             'error',
                                            
                                                filter_all:     [' - error: ', ''],  // nur Logeinträge mit Level 'error'
                                            
                                                filter_any:     [''],
                                            
                                                blacklist:      ['', '', ''],
                                            
                                                clean:          ['', '', ''],
                                            
                                                merge:          true,
                                            
                                                sortDescending: true,
                                            
                                                jsonColumns:    ['date','level','source','msg'],
                                            
                                                jsonDateFormat: 'dd.mm. hh:mm',
                                            
                                                jsonLogLength:  200,
                                            
                                                jsonMaxLines:   60,
                                            
                                                jsonCssToLevel: true,
                                            
                                              }
                                            
                                            Latzi 1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post

                                            Support us

                                            ioBroker
                                            Community Adapters
                                            Donate

                                            922
                                            Online

                                            31.9k
                                            Users

                                            80.1k
                                            Topics

                                            1.3m
                                            Posts

                                            60
                                            617
                                            122560
                                            Loading More Posts
                                            • Oldest to Newest
                                            • Newest to Oldest
                                            • Most Votes
                                            Reply
                                            • Reply as topic
                                            Log in to reply
                                            Community
                                            Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                                            The ioBroker Community 2014-2023
                                            logo