Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. [Vorlage] Servicemeldungen Volume2

NEWS

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

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

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

[Vorlage] Servicemeldungen Volume2

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
224 Beiträge 14 Kommentatoren 44.3k Aufrufe 24 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • Eduard77E Eduard77

    @looxer01 sagte in [Vorlage] Servicemeldungen Volume2:

    772df247-ced6-4905-955c-dd461bf32aec

    die DP "772df247-ced6-4905-955c-dd461bf32aec" habe ich jetzt gefunden.
    Aber er ist nicht in "Devices" sondern in "Groups" Ordner.
    hmip.0.groups.772df247-ced6-4905-955c-dd461bf32aec

    685db9bb-e705-4f92-b631-3ff8630b056b-grafik.png

    L Offline
    L Offline
    looxer01
    schrieb am zuletzt editiert von
    #193

    @edis77
    Hi,
    ich habe jetzt mal einen quick und dirty fix gemacht.
    Es wäre toll , wenn du den testen könntest

    er unterscheidet sich nur wenige Zeilen vom ursprünglichen Code. Die erste Zeile gibt es auch nur einmal im code und die Zeilen können so identifiziert werden. Liegt ungefähr bei Zeile 277. einfach austauschen

    Falls es funktioniert würde ich eine Version mit dem finalen code zur Verfügung stellen.

    vG Looxer

    const selectors = alarmTypes.map(alarmType => {
        const collectedIds = [];
        instanceIds.forEach(instance => {
            if (instance.Instanz !== 9) {                                   // Umbenennung von `instance.id` zu `instance.Instanz`
                const excludedAlarmTypes = exceptions[instance.name] || []; // Hole Ausnahmen oder leere Liste
                if (!excludedAlarmTypes.includes(alarmType.key)) {          // Wenn Alarmtype nicht in der Ausnahme
                    alarmType.suffixes.forEach(suffix => {                  // wenn suffix (meldungsart) nicht in der Ausnahme
                        if (!excludedAlarmTypes.includes(suffix)) { 
                            // IDs sammeln, ohne sofort zu filtern
                            // @ts-ignore
                            $(`state[id=${instance.adapter}.${instance.Instanz}.*.${suffix}]`).each(id => collectedIds.push(id)); 
                        }
                    });
                }
            }
        });
        const filteredIds = collectedIds.filter(id => !id.includes("groups"));  // for accessPoint und HCU sollten die Ids, die "groups" enthalten ausgeschlossen werden . das sind z.B. Raeume
        const finalFilteredIds = filteredIds.filter(id => {                     // IDs anhand der Geraete-IDs in der Ausschlussliste filtern          
            const deviceId = id.split('.')[2];                                  // Geraete-ID aus der vollstaendigen ID extrahieren (z. B. "0000D7099xxx26" aus "hm-rpc.1.0000D7099xxx26.0.LOW_BAT_ALARM")
            return !Ausschlussliste.includes(deviceId);                         // Behalten, wenn nicht in der Ausschlussliste
        });
    
        totalIdsCount += finalFilteredIds.length;                                // IDs zur Gesamtzahl hinzufuegen
        return { name: alarmType.key, ids: finalFilteredIds };
    });
    
    Eduard77E 1 Antwort Letzte Antwort
    0
    • L looxer01

      @edis77
      Hi,
      ich habe jetzt mal einen quick und dirty fix gemacht.
      Es wäre toll , wenn du den testen könntest

      er unterscheidet sich nur wenige Zeilen vom ursprünglichen Code. Die erste Zeile gibt es auch nur einmal im code und die Zeilen können so identifiziert werden. Liegt ungefähr bei Zeile 277. einfach austauschen

      Falls es funktioniert würde ich eine Version mit dem finalen code zur Verfügung stellen.

      vG Looxer

      const selectors = alarmTypes.map(alarmType => {
          const collectedIds = [];
          instanceIds.forEach(instance => {
              if (instance.Instanz !== 9) {                                   // Umbenennung von `instance.id` zu `instance.Instanz`
                  const excludedAlarmTypes = exceptions[instance.name] || []; // Hole Ausnahmen oder leere Liste
                  if (!excludedAlarmTypes.includes(alarmType.key)) {          // Wenn Alarmtype nicht in der Ausnahme
                      alarmType.suffixes.forEach(suffix => {                  // wenn suffix (meldungsart) nicht in der Ausnahme
                          if (!excludedAlarmTypes.includes(suffix)) { 
                              // IDs sammeln, ohne sofort zu filtern
                              // @ts-ignore
                              $(`state[id=${instance.adapter}.${instance.Instanz}.*.${suffix}]`).each(id => collectedIds.push(id)); 
                          }
                      });
                  }
              }
          });
          const filteredIds = collectedIds.filter(id => !id.includes("groups"));  // for accessPoint und HCU sollten die Ids, die "groups" enthalten ausgeschlossen werden . das sind z.B. Raeume
          const finalFilteredIds = filteredIds.filter(id => {                     // IDs anhand der Geraete-IDs in der Ausschlussliste filtern          
              const deviceId = id.split('.')[2];                                  // Geraete-ID aus der vollstaendigen ID extrahieren (z. B. "0000D7099xxx26" aus "hm-rpc.1.0000D7099xxx26.0.LOW_BAT_ALARM")
              return !Ausschlussliste.includes(deviceId);                         // Behalten, wenn nicht in der Ausschlussliste
          });
      
          totalIdsCount += finalFilteredIds.length;                                // IDs zur Gesamtzahl hinzufuegen
          return { name: alarmType.key, ids: finalFilteredIds };
      });
      
      Eduard77E Online
      Eduard77E Online
      Eduard77
      schrieb am zuletzt editiert von Eduard77
      #194

      @looxer01
      ich habe die stelle ausgetauscht und bekomme beim Skriptstart folgende Fehler.

      javascript.0	20:54:51.514	info	Stopping script script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint
      javascript.0	20:56:05.774	info	Start JavaScript script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint (Javascript/js)
      javascript.0	20:56:05.778	error	script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint compile failed: at script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint:1173
      

      0f5a9a77-0433-4c6f-ba9b-247026c82036-image.png

      Und folgenses im LOG.

      2025-02-28 20:54:48.578 - info: admin.0 (329) ==> Connected system.user.admin from ::ffff:192.168.178.20
      2025-02-28 20:54:51.514 - info: javascript.0 (92966) Stopping script script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint
      2025-02-28 20:56:05.774 - info: javascript.0 (92966) Start JavaScript script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint (Javascript/js)
      2025-02-28 20:56:05.778 - error: javascript.0 (92966) script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint compile failed:
      at script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint:1173
      2025-02-28 20:56:05.779 - error: javascript.0 (92966) })();
      2025-02-28 20:56:05.779 - error: javascript.0 (92966) ^
      2025-02-28 20:56:05.779 - error: javascript.0 (92966) SyntaxError: Unexpected token '}'
      2025-02-28 20:56:05.779 - error: javascript.0 (92966) at new Script (node:vm:117:7)
      2025-02-28 20:56:05.779 - error: javascript.0 (92966) at createVM (/opt/iobroker/node_modules/iobroker.javascript/main.js:1956:21)
      2025-02-28 20:56:05.779 - error: javascript.0 (92966) at prepareScript (/opt/iobroker/node_modules/iobroker.javascript/main.js:2224:37)
      2025-02-28 20:56:05.779 - error: javascript.0 (92966) at /opt/iobroker/node_modules/iobroker.javascript/main.js:2322:17
      2025-02-28 20:56:05.779 - error: javascript.0 (92966) at Immediate._onImmediate (/opt/iobroker/node_modules/iobroker.javascript/main.js:1732:17)
      2025-02-28 20:56:05.779 - error: javascript.0 (92966) at processImmediate (node:internal/timers:485:21)
      2025-02-28 20:56:57.027 - info: admin.0 (329) <== Disconnect system.user.admin from ::ffff:192.168.178.20 javascript
      
      L 1 Antwort Letzte Antwort
      0
      • Eduard77E Eduard77

        @looxer01
        ich habe die stelle ausgetauscht und bekomme beim Skriptstart folgende Fehler.

        javascript.0	20:54:51.514	info	Stopping script script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint
        javascript.0	20:56:05.774	info	Start JavaScript script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint (Javascript/js)
        javascript.0	20:56:05.778	error	script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint compile failed: at script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint:1173
        

        0f5a9a77-0433-4c6f-ba9b-247026c82036-image.png

        Und folgenses im LOG.

        2025-02-28 20:54:48.578 - info: admin.0 (329) ==> Connected system.user.admin from ::ffff:192.168.178.20
        2025-02-28 20:54:51.514 - info: javascript.0 (92966) Stopping script script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint
        2025-02-28 20:56:05.774 - info: javascript.0 (92966) Start JavaScript script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint (Javascript/js)
        2025-02-28 20:56:05.778 - error: javascript.0 (92966) script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint compile failed:
        at script.js.Test_und_nicht_fertige_Skripte.Servicemeldungen_von_AccessPoint:1173
        2025-02-28 20:56:05.779 - error: javascript.0 (92966) })();
        2025-02-28 20:56:05.779 - error: javascript.0 (92966) ^
        2025-02-28 20:56:05.779 - error: javascript.0 (92966) SyntaxError: Unexpected token '}'
        2025-02-28 20:56:05.779 - error: javascript.0 (92966) at new Script (node:vm:117:7)
        2025-02-28 20:56:05.779 - error: javascript.0 (92966) at createVM (/opt/iobroker/node_modules/iobroker.javascript/main.js:1956:21)
        2025-02-28 20:56:05.779 - error: javascript.0 (92966) at prepareScript (/opt/iobroker/node_modules/iobroker.javascript/main.js:2224:37)
        2025-02-28 20:56:05.779 - error: javascript.0 (92966) at /opt/iobroker/node_modules/iobroker.javascript/main.js:2322:17
        2025-02-28 20:56:05.779 - error: javascript.0 (92966) at Immediate._onImmediate (/opt/iobroker/node_modules/iobroker.javascript/main.js:1732:17)
        2025-02-28 20:56:05.779 - error: javascript.0 (92966) at processImmediate (node:internal/timers:485:21)
        2025-02-28 20:56:57.027 - info: admin.0 (329) <== Disconnect system.user.admin from ::ffff:192.168.178.20 javascript
        
        L Offline
        L Offline
        looxer01
        schrieb am zuletzt editiert von
        #195

        @edis77
        Hi,
        ja, ist nicht richtig reinkopiert. lösche die Zeilen 273 - 386
        und kopier nochmal die Sequenz wie zuvor gepostet.

        vG Looxer

        1 Antwort Letzte Antwort
        0
        • Eduard77E Online
          Eduard77E Online
          Eduard77
          schrieb am zuletzt editiert von
          #196

          @looxer01
          Habe jetzt so gemacht wie du beschreiben hast, aber bekomme wieder Warnung.

          2be5ab4c-acdc-4052-9c4f-17ff734951cc-image.png

          anbei mein geänderte Skript zur Prüfung.

          // Beschreibung des Scriptes: https://forum.iobroker.net/topic/77816/vorlage-servicemeldungen-volume2
          // Autor Looxer01 02.11.2024 Version 1.0 (initiale Version)
          // Version 3.00 - 05.01.2025 Variable MessageBeiKeinerSM in die Experteneinstellungen geschoben // Problem bei der Datumsberechnung behoben // Zaehler fuer Historie hinzugefuegt
          //                           die Datenpunkte fuer Text-Format werden jetzt optional gefuellt
          // Version 3.10 - 01.02.2024 Wartezeit fuer HM-Rega von 2 auf 3,5 Sekunden erhoeht - Funktion der Tabelle MessengerScope erweitert
          // Version 3.20 - 24.02.2024 Doppelte Nachrichten im messageCollector verhindern - Vermeidung Meldung "keine Servicemeldungen..." in der Historie wenn bereits vorhanden - Wartezeit für Rega-Trigger wieder auf 5s
          //                           StickyMessages koennen automatisch bestaetigt werden
          //---------------------------------------------------------------------------------------------------------------------------------------------------------------
          // Muss-Einstellungen                                 ( HM-Classic , HMIP, Wired - hier muessen zwingend die Instanzen stimmmen )
          //---------------------------------------------------------------------------------------------------------------------------------------------------------------
          // Im folgenden sind die Instanzen gelistet fuer die die Selektion erfolgt bzw nicht erfolgt (Filter)
          // bitte 9 Eintragen falls eine Instanz nicht relevant ist // Gruppeninstanzen sind normalerweise nicht relevant
          // CuxD Instanzen duerfen nicht eingetragen werden  --- ACHTUNG: bei HMIPAccessPoint-Nutzung muss GeraeteTriggerID auf true stehen
          const HMClassicInstanz      = 9;        // HM-Classic Instanz eintragen     // 9 = falls nicht relevant
          const HMIPInstanz           = 9;        // Homematic IP instanz             // 9 = falls nicht relevant
          const WiredIClassicInstanz  = 9;        // Wired Instanz                    // 9 = falls nicht relevant
          const GruppenInstanz        = 9;        // virtuelle GeraeteInstanz- 9 = nicht relevant - Die Gruppen werden i.d.R. nicht gebraucht - Empfehlung: 9
          let   HMIPAccessPoint       = 0;        // AccessPoint Servicemeldungen - (normalerweise instanz = 0) bei nicht Verwendung Instanz = 9
          //---------------------------------------------------------------------------------------------------------------------------------------------------------------
          // Kann-Einstellungen 
          //---------------------------------------------------------------------------------------------------------------------------------------------------------------
          // Pfad kann angepasst werden fuer userdata pfad einfach // entfernen
          const path      = "javascript.0.ServicemeldungenVol2.";
          //const path    = "0_userdata.0.ServicemeldungenVol2."; // alternativ zum javascript pfad
          
          // schreibt das Protokoll der Servicemeldungen in ein externes file (Excel Format)
          const SMProtokoll = true;             
          const PathSMLog = "/opt/iobroker/log/ServicemeldungenVol2.csv";             // Pfad und Dateiname des externen Logs
          //const PathSMLog = "/iobroker/log/ServicemeldungenVol2.csv";            // Pfad fuer Windows/  iobroker ist der angenommene iobroker home-pfad
          
          //Geraete die nicht ueberwacht werden sollen. Geraete-IDs eingeben - Komma getrennt erfassen
          const Ausschlussliste = ['0000D7099xxx26', '00091D8xxx7410']; // immer mit Komma trennen // Filter auf Einzel-IDs
          
          // debug level kann eingestellt werden - wenn alles laeuft dann 0 = ruhe im log
          // debug level 0   kein log // debug level 1 - nur die wichtigsten Meldungen werden gelistet // debug level 2 - mehr als nur die wichtigsten Meldungen aber ohne einzelne IDs
          // debug level 3 - hier werden auch einzelne IDs gelistet (koennten lange listen werden)
          const debugLevel = 2 ;     // Empfehlung = 1
          const SystemLog = false;  // schreib das Sytemprotokoll in ein externes log (sollte normalerweise deaktviert sein  nur bei Problemen verwenden)           
          const PathSystemLog = "/opt/iobroker/log/ServicemeldungenSystemLog.csv";             // Pfad und Dateiname des externen Logs 
          //const PathSystemLog = "/iobroker/log/ServicemeldungenSystemLog.csv";             // Pfad fuer Windows
          
          // wenn GeraeteIDTrigger auf true gestellt wird, dann wird fuer jeden Datenpukt mit Relevanz fuer eine Servicemeldung eine subscription angelegt.
          // Vorteile: mehr Details in der Historie von Servicemeldungen: Nachteil: bei 80 CCU Geraeten ungefaehr 300 Susbsriptions
          // Wenn die variable auf false steht, dann wird auf hm.rega.0.maintenance eine subsription angelegt: Vorteil: 1 Subscription , Nachteil: erweiterte Funktion fuer nicht CCU Datenpunkte nicht verfuegbar
          // bei HMIP AccessPoint Cloud HCU - funktioniert das nur bei GeraeteTrigger = true
          let GeraeteIDTrigger = false; // true = viele subscriptions aber praezise Historie der Servicemeldungen - false = 1 subscription - weniger praezise Historie - Empfehlung = false
          
          // Speicherung der Servicemeldungen im  Text-Format - Empfehlung = false // davon ausgehend, dass das JSON Format in der Visualisierung gut dargestellt werden kann
          const UpdateTEXT_Datenpunkte = false; // die kurz- und langen Servicemeldungen als auch die historischen werden immer im JSON Format abgelegt. Alternativ koennen diese auch in einem TEXT Format in Datenpunkten gespeichert werden
          // Speicherung der Servicemeldungen im  HTML-Format - Wird empfohlen zu nutzen, wenn es Problem bei der Visualisierung von JSON gibt.
          const UpdateHTML_Datenpunkte = true; // die langen Servicemeldungen als auch die historischen werden immer im JSON Format abgelegt. Alternativ koennen diese auch in einem HTML Format in Datenpunkten gespeichert werden
          // Die Autobestaetigung bestaetigt automatisch evt Sticky-Meldungen der CCU 
          const AutoBestaetigungCCUMeldungen = true; // Empfehlung ist true - nur relevant fur HM-Classic Geraete //bei true sollte in der CCU die autobestaetigung ausgeschaltet werden
          
          //  fuer alle Spalten mit true werden die Nachrichten ueber den zugeordneten Dienst versendet, vorausgesetzt der Messenge Adapter ist in iobroker installiert/konfiguriert
              const services =               ['email',    'whatsApp',     'Signal',  'Telegram',    'Pushover', 'Pushsafer', 'Pushsafer',];
              const MessengerScope = {
              'UNREACH_ALARM':                [true,        false,          false,      true,          false,      false,       false,],
              'STICKY_UNREACH_ALARM':         [false,       false,          false,      false,          false,      false,       false,],   // Sticky sollte nicht gesendet werden - alles auf false setzen  
              'LOWBAT_ALARM':                 [true,        false,          false,      true,          false,      false,       false,],
              'SABOTAGE_ALARM':               [true,        false,          false,      true,          false,      false,       false,],
              'STICKY_SABOTAGE_ALARM':        [false,       false,          false,      false,          false,      false,       false,],   // Sticky sollte nicht gesendet werden - alles auf false setzen  
              'CONFIG_PENDING_ALARM':         [true,        false,          false,      true,          false,      false,       false,],
              'Sonstige':                     [true,        false,          false,      true,          false,      false,       false,], // text ist fest verdrahtet nicht ändern
              'keineSM':                      [true,        false,          false,      true,          false,      false,       false,], // text ist fest verdrahtet nicht ändern
              }
              const MessengerInstanz =        [0,             0,               0,          0,              0,          0,            1,  ]; // Instanz des Messengers
              const TextTypeKurz      =       [false,        true,            true,       true,           true,      true,         false,]; // bei true wird der Kurztext gesendet - sonst der Langtext
          
          // email-Einstellungen
          let emailAddresse = "Vorname-Nachname@web.de"   // Empfängeraddresse
          
          const Headline = "ioBroker Servicemeldung";     // Ueberschrift Messages fuer email und Pushsafer
          // telegram Einstellungen
          const TelegramUser = "Eduard";
          
          //-----------------------------------------------------------------------------------------------------
          //Experten Einstellungen - Empfehlung:  Absprache mit dem Autor
          //-----------------------------------------------------------------------------------------------------
          const MessageBeiKeinerSM = 'Derzeit keine Servicemeldungen'     // Text der erscheinen soll, wenn keine SM vorliegen Ein Kurztext ist erforderlich
          //const NichtRelevantText = 'n/a';                                // statt n/a kann ein beliebiger anderer Text in den JSON verwendet werden
          const NichtRelevantText = "......";
          // die folgenden Einstellungen sind nur relvant bei Verwendung von HTML als Datenpunkte
              // Definieren der Farbwerte für Kopfzeile, gerade und ungerade Zeilen
              const headerColor = '#333333';  // Standardfarbe (Fuellfarbe) für die Kopfzeile
              const evenRowColor = '#4e5049'; // Standardfarbe (Fuellfarbe) für gerade Zeilen
              const oddRowColor = '#333333';  // Standardfarbe (Fuellfarbe) für ungerade Zeilen
              // Definieren der Textfarben für Kopfzeile, gerade und ungerade Zeilen
              const headerTextColor = 'white'; // Textfarbe für Kopfzeile
              const evenRowTextColor = 'white'; // Textfarbe für gerade Zeilen
              const oddRowTextColor = 'white';  // Textfarbe für ungerade Zeilen
              // Definieren von Zellenumrandungseinstellungen
              const useBorderAll =    true; // true, wenn ein Gitternetz erzeugt werden soll
              const borderColor = '#000000'; // Farbe des Gitternetzes (Zellenumrandung)
              const borderWidth = '1px'; // Strichstärke der Zellenumrandung
              
          //Schedule Zeit fuer refresh der Historie
          const ScheduleAktiv = true;                         // Bei "false" wird der schedule nicht durchlaufen. Manuelles Löschen kann über den Datenpunkt id_Button_Refresh_Historie (Button) möglich
          const scheduleTimeClearSMTexte = "2 0 1 * *";       // am 1. tag des monats um 00:02 morgens sollen alle Servicemeldungen des Monats geloescht werden
          // const scheduleTimeClearSMTexte = "58 23 * * 0";  // alternative Sonntags um 23:58 Uhr sollen alle Servicemeldungen der Woche im datenpunkt der SM-Texte geloescht werden 
          
          //Batterie-Zuordnungen fuer Servicmeldungen
          const batteryTypes = {
              '1x CR2016':    ['HM-RC-4', 'HM-RC-4-B', 'HM-RC-Key3', 'HM-RC-Key3-B', 'HM-RC-P1', 'HM-RC-Sec3', 'HM-RC-Sec3-B', 'ZEL STG RM HS 4'],
              '1x CR2032':    ['HM-PB-2-WM', 'HM-PB-4-WM', 'HM-PBI-4-FM', 'HM-SCI-3-FM', 'HM-Sec-TiS', 'HM-SwI-3-FM', 'HmIP-FCI1'],
              '2x LR14':      ['HM-Sec-Sir-WM', 'HM-OU-CFM-TW', 'HM-OU-CFM-Pl', 'HM-OU-CF-Pl'],
              '2x LR44/AG13': ['HM-Sec-SC', 'HM-Sec-SC2L', 'HM-Sec-SC-2', 'HM-Sec-RHS'],
              '2x LR6/AA':    ['HM-CC-VD', 'HM-CC-RT-DN', 'HM-Sec-WDS', 'HM-Sec-WDS-2', 'HM-CC-TC', 'HM-Dis-TD-T', 'HB-UW-Sen-THPL-I', 'HM-WDS40-TH-I', 'HM-WDS40-TH-I-2', 
                               'HM-WDS10-TH-O', 'HmIP-SMI',                     'HMIP-eTRV', 'HM-WDS30-OT2-SM-2', 'HmIP-SMO', 'HmIP-SMO-A', 'HmIP-SPI', 'HmIP-eTRV-2', 
                               'HmIP-SPDR', 'HmIP-STHO-A', 'HmIP-eTRV-B', 'HmIP-PCBS-BAT', 'HmIP-STHO', 'HmIP-eTRV-C', 
                               'HmIP-WGC', 'HmIP-eTRV-C-2', 'HmIP-eTRV-E', 'HmIP-eTRV-2 I9F', 'HmIP-eTRV-E-S', 'ELV-SH-SW1-BAT'],
              '3x LR6/AA':    ['HmIP-SWO-PL', 'HM-Sec-MDIR', 'HM-Sec-MDIR-2', 'HM-Sec-SD', 'HM-Sec-Key', 'HM-Sec-Key-S', 'HM-Sec-Key-O', 'HM-Sen-Wa-Od', 'HM-Sen-MDIR', 
                               'HM-Sen-MDIR-O', 'HM-Sen-MDIR-O-2', 'HM-WDS100-C6-O', 'HM-WDS100-C6-O-2', 'HmIP-ASIR', 'HmIP-SWO-B', 'HM-Sen-MDIR-O-3', 'HM-Sec-MDIR-3', 
                               'HmIP-SWO-PR', 'HmIP-DLD', 'HmIP-ASIR-2'],
              '4x LR6/AA':    ['HM-CCU-1', 'HM-ES-TX-WM', 'HM-WDC7000'],
              '1x LR3/AAA':   ['HM-RC-4-2', 'HM-RC-4-3', 'HM-RC-Key4-2', 'HM-RC-Key4-3', 'HM-RC-Sec4-2', 'HM-RC-Sec4-3', 'HM-Sec-RHS-2', 'HM-Sec-SCo', 'HmIP-KRC4', 
                               'HmIP-KRCA', 'HmIP-SRH', 'HMIP-SWDO', 'HmIP-DBB', 'HmIP-RCB1', 'HmIP-KRCK', 'HmIP-SWDO-2'],
              '2x LR3/AAA':   ['HmIP-WRCR', 'HmIP-SWD','HM-TC-IT-WM-W-EU', 'HM-Dis-WM55', 'HM-Dis-EP-WM55', 'HM-PB-2-WM55', 'HM-PB-2-WM55-2', 'HM-PB-6-WM55', 'HM-PBI-2-FM', 'HM-RC-8', 'HM-Sen-DB-PCB', 
                              'HM-Sen-EP', 'HM-Sen-MDIR-SM', 'HM-Sen-MDIR-WM55', 'HM-WDS30-T-O', 'HM-WDS30-OT2-SM', 'HmIP-STH', 'HmIP-STHD', 'HmIP-WRC2', 'HmIP-WRC6', 'HmIP-WTH', 'HmIP-WTH-2', 
                              'HmIP-SAM', 'HmIP-SLO', 'HMIP-SWDO-I', 'HmIP-FCI6', 'HmIP-SMI55', 'HM-PB-2-FM', 'HmIP-SWDM', 'HmIP-SCI', 'HmIP-SWDM-B2', 'HmIP-RC8', 'ALPHA-IP-RBG', 'HmIP-DSD-PCB', 
                              'HmIP-WRCD', 'HmIP-WRC2-A', 'HmIP-WTH-B-2', 'HmIP-WTH-A', 'HmIP-STV', 'HmIP-WKP'],
              '3x LR3/AAA':   ['HM-PB-4Dis-WM', 'HM-PB-4Dis-WM-2', 'HM-RC-Dis-H-x-EU', 'HM-Sen-LI-O'],
              '3x AAA Akkus - bitte laden': ['HM-RC-19', 'HM-RC-19-B', 'HM-RC-12', 'HM-RC-12-B', 'HM-RC-12-W'],
              '3x LR14/C':    ['HmIP-MP3P'],
              '9Volt Block leer oder unbestimmt': ['HM-LC-Sw1-Ba-PCB', 'HM-LC-Sw4-PCB', 'HM-MOD-EM-8', 'HM-MOD-Re-8', 'HM-Sen-RD-O', 'HM-OU-CM-PCB', 'HM-LC-Sw4-WM'],
              'Festbatterie leer': ['HmIP-STE2-PCB', 'HM-Sec-SD-2', 'HmIP-SWSD', 'HmIP-PCBS'],
              'ohne Batterie': ['HM-LC-Sw1PBU-FM', 'HM-LC-Sw1-Pl-DN-R1', 'HM-LC-Sw1-DR', 'HM-LC-RGBW-WM', 'HM-LC-Sw1-Pl-CT-R1', 'HmIP-HEATING', 'HM-LC-Sw1-FM', 'HM-LC-Sw2-FM', 'HM-LC-Sw4-DR', 
                                  'HM-LC-Sw1-Pl', 'HM-LC-Sw1-Pl-2', 'HM-LC-Sw4-Ba-PCB', 'HM-LC-Sw1-SM', 'HM-LC-Sw4-SM', 'HM-Sys-sRP-Pl', 'HM-LC-Sw2PBU-FM', 'HM-LC-Sw1-PCB', 'HM-LC-Sw4-DR-2'],
          
              'Akku entladen - bitte aufladen': ['HM-Sec-Win', 'HM-Sec-SFA-SM', 'HM-RC-19-SW']
          };
          // Rega Pfad
          const PathRega = ['hm-rega.0.maintenance',];                            // Array um ggf die HCU zu konfigurieren
          // Pfade fuer die Speicherung aktueller und vergangener Servicemeldungen
          const id_Text_ServicemeldungLang        = path+'TextLangAktuelleSM';    // Objekt wo die Servicemeldung hingeschrieben werden soll (String) = path+'TextLang';
          const id_Text_ServicemeldungKurz        = path+'TextKurzAktuelleSM';    // Objekt wo die Servicemeldung hingeschrieben werden soll (String) - kurze Version
          const id_Text_Servicemeldung_History    = path+'TextLangVergangeneSM';  // Objekt wo die Servicemeldung hinzugefuegt werden soll (String) - Lange Version (es gibt nur lang)
          const id_HTML_Servicemeldung_Aktuell    = path+'HtmlAktuelleSM';        // Objekt wo die Servicemeldung hingeschrieben werden soll (HTML formatiere Tabelle) = path+'htmlLang';
          const id_HTML_Servicemeldung_History    = path+'HtmlVergangeneSM';      // Objekt wo die Servicemeldung hinzugefuegt werden soll ((HTML formatiere Tabelle) - Lange Version (es gibt nur lang)
          const id_JSON_Servicemeldung_Aktuell    = path+'JSONAktuelleSM';        // JSON Tabelle Datenpunkt Aktuelle SM
          const id_JSON_Servicemeldung_Historie   = path+'JSONVergangeneSM';      // JSON Tabelle Datenpunkt Historische SM
          const id_Button_Refresh_Historie        = path+'ButtonRefreshHistorie'; // Wenn DP auf true gesetzt wird, dann wird die historie refreshed (geloescht)
          // Count-Pfade  und counts in einer Tabelle - wird auch fuer CREATE STATES verwendet
          const Zaehler = [
            { alarmtype: 'UNREACH_ALARM',                    count: 0,                    Datenpunkt: path+'Anzahl_UNREACH' },
            { alarmtype: 'STICKY_UNREACH_ALARM',             count: 0,                    Datenpunkt: path+'Anzahl_STICKY_UNREACH' },
            { alarmtype: 'CONFIG_PENDING_ALARM',             count: 0,                    Datenpunkt: path+'Anzahl_CONFIG_PENDING' },
            { alarmtype: 'UPDATE_PENDING_ALARM',             count: 0,                    Datenpunkt: path+'Anzahl_Update_PENDING' },
            { alarmtype: 'LOWBAT_ALARM',                     count: 0,                    Datenpunkt: path+'Anzahl_LOWBAT' },
            { alarmtype: 'DEVICE_IN_BOOTLOADER_ALARM',       count: 0,                    Datenpunkt: path+'Anzahl_DEVICE_IN_BOOTLOADER' },
            { alarmtype: 'ERROR',                            count: 0,                    Datenpunkt: path+'Anzahl_in_ERROR' },
            { alarmtype: 'FAULT_REPORTING',                  count: 0,                    Datenpunkt: path+'Anzahl_FAULT_REPORTING' },
            { alarmtype: 'SABOTAGE_ALARM',                   count: 0,                    Datenpunkt: path+'Anzahl_SABOTAGE' },
            { alarmtype: 'ERROR_NON_FLAT_POSITIONING_ALARM', count: 0,                    Datenpunkt: path+'Anzahl_NON_FLAT_POSITIONING' },
            { alarmtype: 'STICKY_SABOTAGE_ALARM',            count: 0,                    Datenpunkt: path+'Anzahl_Sticky_SABOTAGE' },
            { alarmtype: 'SMAktuell',                        count: 0,                    Datenpunkt: path+'Anzahl_SM-Aktuell' },
            { alarmtype: 'SMHistorie',                       count: 0,                    Datenpunkt: path+'Anzahl_SM-Historie' },
            { alarmtype: 'Gesamt',                           count: 0,                    Datenpunkt: path+'Anzahl_GESAMT' }
          ];
          // statusmessages je messagetype und   adapter // Fallback ist fuer unbekannte messagetypes - z.B. alle Error Messages // FALLBACK repraesentiert Standardmessages
          const statusMessages = {
              UNREACH_ALARM:                    { "hm-rpc": { 0: "keine Kommunikationsfehler",     1: "Kommunikation gestoert",                            2: "Kommunikation war gestoert" } },
              STICKY_UNREACH_ALARM:             { "hm-rpc": { 0: "keine Kommunikationsfehler",     1: "Sticky Kommunikation gestoert",                     2: "Sticky Kommunikation war gestoert" } },
              SABOTAGE_ALARM:                   { "hm-rpc": { 0: "Keine Sabotage",                 1: "Sabotage",                                          2: "Sabotage aufgehoben" } },
              STICKY_SABOTAGE_ALARM:            { "hm-rpc": { 0: "Keine Sabotage",                 1: "Sticky Sabotage",                                   2: "Sticky Sabotage aufgehoben" } },
              LOWBAT_ALARM:                     { "hm-rpc": { 0: "Batterie ok",                    1: "Batterie niedrig",                                  2: "Batterie ok" } },
              LOW_BAT_ALARM:                    { "hm-rpc": { 0: "Batterie ok",                    1: "Batterie niedrig",                                  2: "Batterie ok" } },
              ERROR_NON_FLAT_POSITIONING_ALARM: { "hm-rpc": { 0: "Keine Meldung",                  1: "Geraet wurde angehoben.",                           2: "Geraet wurde angehoben: Bestaetigt" } },
              CONFIG_PENDING_ALARM:             { "hm-rpc": { 0: "keine Meldung",                  1: "Konfigurationsdaten stehen zur Uebertragung an",    2: "Konfigurationsdaten standen zur Uebertragung an",}, },
              UPDATE_PENDING_ALARM:             { "hm-rpc": { 0: "kein Update verfuegbar",         1: "Update verfuegbar",                                 2: "Update wurde eingespielt" } },
              ERROR_OVERHEAT_ALARM:             { "hm-rpc": { 0: "kein Overheat Alarm",            1: "Overheat gemeldet",                                 2: "Overheat geloest" } },
              ERROR_UNDERVOLTAGE_ALARM:         { "hm-rpc": { 0: "Kein Undervoltage Alarm",        1: "Undervoltage gemeldet",                             2: "Undervoltage geloest" } },
              DEVICE_IN_BOOTLOADER_ALARM:       { "hm-rpc": { 0: "Keine Meldung",                  1: "Geraet startet neu",                                2: "Geraet wurde neu gestartet" } },
              DUTY_CYCLE:                       { "hm-rpc": { false: "Geraete-Duty Cycle ok",      true: "Geraete-Duty Cycle erreicht",                    null: "unbekannter Status (Duty_Cycle" } },
              lowBat:                           { "hmip":   { false: "Batterie ok",                true: "Batterie niedrig",                               null: "Batterie ok" } },
              unreach:                          { "hmip":   { false: "keine Kommunikationsfehler", true: "Kommunikation gestoert",                         null: "Kommunikation war gestoert" } },
              sabotage:                         { "hmip":   { false: "Keine Sabotage",             true: "Sabotage",                                       null: "Sabotage aufgehoben" } },
              configPending:                    { "hmip":   { false: "Keine Meldung",              true: "Konfigurationsdaten stehen zur Uebertragung an", null: "Konfigurationsdaten standen zur Uebertragung an" } },
              FALLBACK:                         { "hm-rpc": { 0: "keine Stoerung",                 1: "Stoerung",                                          2: "Stoerung aufgehoben",
                                                              false: "Keine Stoerung",             true: "Stoerung",                                       null: "unbekannter Status Fallback"},
                                                  "hmip":   { false: "keine Stoerung",             true: "Stoerung",                                       null: "Stoerung aufgehoben" }, }
          };
          //ErrorMessages fuer HM-Classic Geraete - Sonderfaelle
          const errorMessages = {
              'HM-Sec-RHS':   { 7: 'Sabotage' },
              'HM-Sec-RHS-2': { 7: 'Sabotage' },
              'HM-Sec-SC':    { 7: 'Sabotage' },
              'HM-Sec-SC-2':  { 7: 'Sabotage' },
              'HM-Sec-SCo':   { 7: 'Sabotage' },
              'HM-Sec-MD':    { 7: 'Sabotage' },
              'HM-Sec-MDIR':  { 7: 'Sabotage' },
              'HM-Sec-MDIR-2':{ 7: 'Sabotage' },
              'HM-Sec-Key':   { 1: 'Einkuppeln fehlgeschlagen', 2: 'Motorlauf abgebrochen' },
              'HM-Sec-Key-S': { 1: 'Einkuppeln fehlgeschlagen', 2: 'Motorlauf abgebrochen' },
              'HM-Sec-Key-O': { 1: 'Einkuppeln fehlgeschlagen', 2: 'Motorlauf abgebrochen' },
              'HM-CC-VD':     { 1: 'Ventil Antrieb blockiert',  2: 'Ventil nicht montiert', 3: 'Stellbereich zu klein', 4: 'Batteriezustand niedrig'
              }
          };
          //ErrorMessages fuer HM-Classic Geraet (Heizung) - Sonderfaelle
          const faultMessages = {
              'HM-CC-RT-DN': {
                  0: 'keine Stoerung',
                  1: 'Ventil blockiert',
                  2: 'Einstellbereich Ventil zu gross',
                  3: 'Einstellbereich Ventil zu klein',
                  4: 'Kommunikationsfehler',
                  6: 'Spannung Batterien/Akkus gering',
                  7: 'Fehlstellung Ventil'
              }
          };
          // hier koennen die Alarmgruppen ggf erweitert werden - Aus Alarmgruppe und Instanz wird der Selector gebastelt und in Tabelle Selectors gesammelt
          const alarmTypes = [
              { key: 'UNREACH_ALARM',                     suffixes: ['UNREACH_ALARM','unreach' ] },//UNREACH_ALARM = HM-Classic & HMIP-CCU - unreach = HMIP Accesspoint
             // { key: 'STICKY_UNREACH_ALARM',              suffixes: ['STICKY_UNREACH_ALARM'] },
              { key: 'CONFIG_PENDING_ALARM',              suffixes: ['CONFIG_PENDING_ALARM','configPending'] }, //configPending ist eine HMIP Meldung
              { key: 'UPDATE_PENDING_ALARM',              suffixes: ['UPDATE_PENDING_ALARM'] },
              { key: 'LOWBAT_ALARM',                      suffixes: ['LOWBAT_ALARM', 'LOW_BAT_ALARM','lowBat'] }, //LOWBAT_ALARM = HM-Classic - LOW_BAT_ALARM = HMIP CCU - lowBat = HMIP Accesspoint
              { key: 'DEVICE_IN_BOOTLOADER_ALARM',        suffixes: ['DEVICE_IN_BOOTLOADER_ALARM'] },
              { key: 'ERROR',                             suffixes: ['ERROR','DUTY_CYCLE'] },                  // error ist ein Sammler fuer hier nicht definierte Meldungen
              { key: 'FAULT_REPORTING',                   suffixes: ['FAULT_REPORTING'] },
              { key: 'SABOTAGE_ALARM',                    suffixes: ['SABOTAGE_ALARM','sabotage'] }, // sabotage ist eine HMIP Meldung
            //  { key: 'STICKY_SABOTAGE_ALARM',             suffixes: ['STICKY_SABOTAGE_ALARM'] },
              { key: 'ERROR_NON_FLAT_POSITIONING_ALARM',  suffixes: ['ERROR_NON_FLAT_POSITIONING_ALARM'] },
              { key: 'OVERHEAT_ALARM',                    suffixes: ['ERROR_OVERHEAT_ALARM'] },
              { key: 'UNDERVOLTAGE_ALARM',                suffixes: ['ERROR_UNDERVOLTAGE_ALARM'] },
          ];
          // Umlaut Umwandlung und entfernung PUnkte - kann aber auch erweitert werden
          const replacements = { '.': ' ', 'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss' }; // Umwandlung fuer Namen der Geraete (common.name)
          // Definition der Datenstrukturen fuer MetaDaten je nach Adapter, da abweichend
          const StrukturDefinition = [
              { Adapter: 'hm-rpc',   GeraeteID: 2, AlarmFeld: 4, nativeType: 3,                                             common_name:  3 }, // die Ziffer ist die Positinierung des Feldes 'hm-rpc.1.00085D89B14067.0.UPDATE_PENDING_ALARM' 0=Adapter - 2 = ID 4= Feld Alarm / native Type = die ersten Strings bis zur dritten STelle fuer getObject
              { Adapter: 'hmip',     GeraeteID: 3, AlarmFeld: 6, nativeType: 'hmip.xinstancex.devices.xidx.info.modelType', common_name: 'hmip.xinstancex.devices.xidx.info.label' }, // Positionierung wie bei Rm-rpc - bei hmip wird native type aber ueber den DP ausgelesen und nicht getObject "xidx" wird dann mit der geraeteiD ersetzt
          ]
          // Moegliche Homematic Instanzen (CuxD ausgeschlossen) // Adapter hinzugefuegt um ggf HCU zu konfigurieren
          const instanceIds = [
              { name: 'HMClassicInstanz',     adapter: 'hm-rpc', Instanz: HMClassicInstanz     },
              { name: 'HMIPInstanz',          adapter: 'hm-rpc', Instanz: HMIPInstanz          },
              { name: 'GruppenInstanz',       adapter: 'hm-rpc', Instanz: GruppenInstanz       },
              { name: 'WiredIClassicInstanz', adapter: 'hm-rpc', Instanz: WiredIClassicInstanz },
              { name: 'HMIPAccessPoint',      adapter: 'hmip'  , Instanz: HMIPAccessPoint      },
          ];
          // Die StickingMappingsTabelle stellt sicher, dass die Sticky entsprechend dieser Regel bestaetigt werden kann
          const StickyMappings = {
              STICKY_UNREACH_ALARM:            ['UNREACH_ALARM'],
              STICKY_SABOTAGE_ALARM:           ['SABOTAGE_ALARM'],
          };
          
          // Konfigurationsobjekt fuer Ausnahmen / es koennen Arlarmtypes je Instanz ausgeschlossen werden / Filter auf AlarmTypes
          const exceptions = {
              HMClassicInstanz:       [],
              HMIPInstanz:            ['DUTY_CYCLE'],
              GruppenInstanz:         ['ERROR_NON_FLAT_POSITIONING_ALARM'],
              WiredIClassicInstanz:   ['LOWBAT_ALARM', 'LOW_BAT_ALARM','ERROR_NON_FLAT_POSITIONING_ALARM'],
          };
          //-----------------------------------------------------------------------------------------------------
          //Ende Einstellungen
          //-----------------------------------------------------------------------------------------------------
          LOG(`Script: Servicemeldungen Volume 2 - Autor: Looxer01 Datum: 24.02.2025 Version:3.20`,`Einstellungen`,`Start`,0 )
          if(HMIPAccessPoint !== 9 && !GeraeteIDTrigger) {
              GeraeteIDTrigger = true // Wenn der HMIPAccessPoint aktiv ist, dann funktioniert der REGA Trigger nicht. also setzen auf geraeteID Trigger=true
              LOG(`HMIP AccessPoint bzw Cloud HCU sind aktiv - GeraeteTriggerID muss aktiviert sein - wurde aktiviert. Bitte auch manuell korrigieren und auf GeraeteTriggerID = true setzen`,`Einstellungen`,`Start`,0 )
          } 
          LOG(`HMClassicInstanz: ${HMClassicInstanz}; HMIPInstanz: ${HMIPInstanz}; WiredIClassicInstanz: ${WiredIClassicInstanz}; GruppenInstanz: ${GruppenInstanz}`, "Einstellungen", "Start", 2);
          LOG(`Inhalt instanceIds: ${JSON.stringify(instanceIds)}`, "Einstellungen", "Start", 2);
          LOG(`GeraeteIDTrigger: ${GeraeteIDTrigger}`, "Einstellungen", "Start", 2);
          LOG(`Inhalt Messengerscope: ${JSON.stringify(MessengerScope)}`, "Einstellungen", "Start", 2);
          LOG(`Inhalt Services: ${JSON.stringify(services)}`, "Einstellungen", "Start", 2);
          LOG(`Inhalt Messenger-Instanzen: ${JSON.stringify(MessengerInstanz)}`, "Einstellungen", "Start", 2);
          LOG(`Inhalt TextTypeKurz: ${JSON.stringify(TextTypeKurz)}`, "Einstellungen", "Start", 2);
          let HistorischeMeldungenJSON = [], AktuelleMeldungenJSON = [], MessageSendCollector = {};                    
          // jetzt selectors fuellen anhand der Tabelle InstanceIDs, sowie der Auscchlusstabellen
          let totalIdsCount = 0;                                                  // Variable fuer die Gesamtzahl der IDs
          const selectors = alarmTypes.map(alarmType => {
              const collectedIds = [];
              instanceIds.forEach(instance => {
                  if (instance.Instanz !== 9) {                                   // Umbenennung von `instance.id` zu `instance.Instanz`
                      const excludedAlarmTypes = exceptions[instance.name] || []; // Hole Ausnahmen oder leere Liste
                      if (!excludedAlarmTypes.includes(alarmType.key)) {          // Wenn Alarmtype nicht in der Ausnahme
                          alarmType.suffixes.forEach(suffix => {                  // wenn suffix (meldungsart) nicht in der Ausnahme
                              if (!excludedAlarmTypes.includes(suffix)) { 
                                  // IDs sammeln, ohne sofort zu filtern
                                  // @ts-ignore
                                  $(`state[id=${instance.adapter}.${instance.Instanz}.*.${suffix}]`).each(id => collectedIds.push(id)); 
                              }
                          });
                      }
                  }
              });
              const filteredIds = collectedIds.filter(id => !id.includes("groups"));  // for accessPoint und HCU sollten die Ids, die "groups" enthalten ausgeschlossen werden . das sind z.B. Raeume
              const finalFilteredIds = filteredIds.filter(id => {                     // IDs anhand der Geraete-IDs in der Ausschlussliste filtern          
                  const deviceId = id.split('.')[2];                                  // Geraete-ID aus der vollstaendigen ID extrahieren (z. B. "0000D7099xxx26" aus "hm-rpc.1.0000D7099xxx26.0.LOW_BAT_ALARM")
                  return !Ausschlussliste.includes(deviceId);                         // Behalten, wenn nicht in der Ausschlussliste
              });
           
              totalIdsCount += finalFilteredIds.length;                                // IDs zur Gesamtzahl hinzufuegen
              return { name: alarmType.key, ids: finalFilteredIds };
          });
              AktuelleMeldungenJSON = [];
              let jsonString = getState(id_JSON_Servicemeldung_Historie).val || '[]';    // Lade den aktuellen JSON-String des Historie-Datenpunkts
              HistorischeMeldungenJSON = JSON.parse(jsonString);  
              Zaehler.forEach(entry => { entry.count = 0; }); // Alle count-Werte im Objekt auf 0 setzen
              for (let j = 0; j < selectors.length; j++) {
                  const selector = selectors[j];
                  if (selector.ids.length > 0) {
                      for (let i = 0; i < selector.ids.length; i++) {
                          let id = selector.ids[i], status = getState(id).val;
                          if (!existsState(id)) {
                              LOG(`Geraet ${obj.id} scheint zwischenzeitlich nicht mehr zu existieren - Empfehlung: script neu starten`, "WARN", "Check_All", 0, "warn");
                              continue;
                          }
                          switch (selector.name) {        // fuer alle alarmtypes - erst die Sonderfaelle
                              case 'ERROR':
                                  if (status >= 1 && status <= 7) {
                                      if (status === 7) { // Sonderfall bei HM-Classic
                                          processServiceMessage('SABOTAGE_ALARM', status, id,RunType);
                                      } else {            
                                          processServiceMessage(selector.name, status, id,RunType); 
                                      } 
                                  }
                                  break;
                          default:                    // Standardfall fuer alle anderen alarmtypes
                                  if(status === 1 || status === true ) {processServiceMessage(selector.name, status, id,RunType);  } // bei status = true = AccessPoint
                                  break;
                          }
                          increaseCount('Gesamt');
                      } // Ende Loop der Mitglieder des Alarmtypes
                  }  // Endif selector ist gefuellt
              }    // Ende Loop ueber den Selector
              const AnzahlAktuellerSM = Zaehler.find(e => e.alarmtype === 'SMAktuell').count;
              // Sequenz zur Speicherung der aktuellen Servicemeldungen in die JSON -Datenpunkte--------------------------
              if (AnzahlAktuellerSM  === 0) {
                  AktuelleMeldungenJSON =CreateJsonEntry([] ,func_get_datum(),'', '', '','',func_get_datum() + " - " + MessageBeiKeinerSM,MessageBeiKeinerSM,null,false );  // Keine aktuellen Meldungen -> "Keine Meldungen" hinzufuegen
                  let jsonString = JSON.stringify(AktuelleMeldungenJSON);
                  setState(id_JSON_Servicemeldung_Aktuell, jsonString);           // Aktuelle SM wird in JSON-Datenpunkt als String gespeichert
              } else {      
                  let jsonString = JSON.stringify(AktuelleMeldungenJSON);              // AktuelleMeldungenJSON wird in Funktion DefineServiceMessage gefuellt
                  setState(id_JSON_Servicemeldung_Aktuell, jsonString);               // Aktuelle SM wird in JSON-Datenpunkt als String gespeichert
              }
              if (GeraeteIDTrigger && RunType === "trigger" && !existsState(obj.id)) { // obj.id nicht mehr in ioBroker aber noch im Selector
                  const GeraeteId = ExtractMetaData(obj.id, "GeraeteID");                        
                  LOG(`Geraet ${GeraeteId} scheint zwischenzeitlich nicht mehr zu existieren - Empfehlung: script neu starten`, "WARN", "Check_All", 0, "warn");
                  return;
              }
              // Speziell fuer GerateIDTrigger = true ist, dass es "aufgehoben"-Meldungen gibt, die fuer REGA Trigger nicht ausgewertet werden koennen. diese duerfen auch nicht als aktuelle meldungen in den DP gelangen
              let ExtendedAktuelleMeldungenJSON = [...AktuelleMeldungenJSON]; // Erstelle eine flat copy
              if (GeraeteIDTrigger && RunType === "trigger") {
                  const GeraeteId = ExtractMetaData(obj.id, "GeraeteID");
                  const status = obj.newState.val;
                  const common_name = ExtractMetaData(obj.id, "CommonName");
                  const native_type = ExtractMetaData(obj.id, "nativeType");
                  const meldungsart = ExtractMetaData(obj.id, "meldungsart");
                  let status_textLang = DefineServiceMessage(native_type, status, obj.id, "lang");
                  let status_textPure = DefineServiceMessage(native_type, status, obj.id, "pure");
                  if (!checkIFJSONfEntryExists(ExtendedAktuelleMeldungenJSON,func_get_datum(obj.id), GeraeteId,status, meldungsart) ) {       // checken ob ein Eintrag schon in der Historie ist, wenn nicht dann hinzufuegen
                      ExtendedAktuelleMeldungenJSON = CreateJsonEntry(ExtendedAktuelleMeldungenJSON, func_get_datum(obj.id), meldungsart, common_name, GeraeteId, status, status_textLang, status_textPure, func_Batterie(native_type), true)
                  }
              }
              // Historische Meldungen hinzufuegen, wenn nicht bereits vorhanden
              ExtendedAktuelleMeldungenJSON.forEach(aktuelleMeldung => {        // Iteriere ueber alle aktuellen Meldungen und fuege aktuelle meldungen in die historie wenn noch nicht vorhanden
                  if (!checkIFJSONfEntryExists(HistorischeMeldungenJSON,aktuelleMeldung.datum_seit  ,aktuelleMeldung.GeraeteId, aktuelleMeldung.status, aktuelleMeldung.meldungsart) ) {       // checken ob ein Eintrag schon in der Historie ist, wenn nicht dann hinzufuegen
                      if(!isLatestEntryMessageBeiKeinerSM(ExtendedAktuelleMeldungenJSON)) {    // nicht wenn nicht "keine Servicemeldungen" in aktuellJSON vorhanden ist
                          const message_kurz = aktuelleMeldung.common_name + " " + aktuelleMeldung.status_message_Pure
                          addMessageToCollector(aktuelleMeldung.meldungsart, message_kurz, aktuelleMeldung.status_message_Lang);
                          HistorischeMeldungenJSON = CreateJsonEntry(HistorischeMeldungenJSON, aktuelleMeldung.datum_seit, aktuelleMeldung.meldungsart, aktuelleMeldung.common_name, aktuelleMeldung.GeraeteId, aktuelleMeldung.status, aktuelleMeldung.status_message_Lang, aktuelleMeldung.status_message_Pure, aktuelleMeldung.batterie_bezeichnung, true )
                          writelog(aktuelleMeldung.common_name, aktuelleMeldung.GeraeteId, aktuelleMeldung.meldungsart, aktuelleMeldung.status, aktuelleMeldung.status_message_Lang);       // externes log erzeugen
                      }
                  }
              });
              // Handling "keine aktuellen Servicemeldungen"
              if (AnzahlAktuellerSM  === 0 && RunType === "trigger") {       // wenn keine SM vorliegen, die Meldung "keine Servicmeldungen" in die History
                  if(!isLatestEntryMessageBeiKeinerSM(HistorischeMeldungenJSON)) {    // nur  wenn  "keine Servicemeldungen" in Historische Meldung nicht bereits an erster Stelle vorhanden ist
                      HistorischeMeldungenJSON =CreateJsonEntry(HistorischeMeldungenJSON ,func_get_datum(),'', '', '','',func_get_datum() + " - " + MessageBeiKeinerSM,MessageBeiKeinerSM,null,false );  // Keine aktuellen Meldungen
                      writelog("", "", "", "", MessageBeiKeinerSM);       //  log erzeugen
                      addMessageToCollector('keineSM', MessageBeiKeinerSM, MessageBeiKeinerSM); // Vorbereitung Messagesending
                      sendMessage('keineSM');       
                  }                                                         
              }
              // AutoBestaetigung fuer StickyMessages, wenn diese nicht mehr aktuell sind. nur relevant fuer HM-Classic
              if (AutoBestaetigungCCUMeldungen ) {ConfirmStickyMessages() }
              // Messages senden fuer alle Faelle mit Servicemeldungen
              if (AnzahlAktuellerSM  > 0 && RunType ==="trigger") {            // Servicemeldungen versenden, falls servicemeldungen vorliegen
                  sendMessage();
              }
              HistorischeMeldungenJSON = Markiere_HistSM_als_Aufgehoben(HistorischeMeldungenJSON) // markiert hist messages als erledigt falls nicht mehr in aktuelle meldungen enthalten
              jsonString = JSON.stringify(HistorischeMeldungenJSON);                  // Vorbereitung speichern
              setState(id_JSON_Servicemeldung_Historie, jsonString);                  // hist JSON-Daten speichern
              // falls so eingestellt werden jetzt die TEXTe aus den JSON (aktuell und historie) erzeugt und in die Datenpunkte geschrieben
              GenerateTEXT_Datenpunkte(); 
              generateHtmlTable(AktuelleMeldungenJSON,"AKT");                 // Aktuelle SM wird in HTML Format gespeichert falls so eingestellt
              generateHtmlTable(HistorischeMeldungenJSON,"HIST");             // Historische  SM wird in HTML Format gespeichert falls so eingestellt
              // anzahl der Eintraege in HistorischeMeldungenJSON ermitteln und alle counts in die Statistik schreiben
              let entry = Zaehler.find(item => item.alarmtype === "SMHistorie");  
              if (entry) {entry.count = HistorischeMeldungenJSON.length;    }          // Wenn der Eintrag gefunden wird, aktualisiere den count-Wert
              Zaehler.forEach(entry => {    // JETZT: ZaehlerStaende in die ioBroker - Datenpunkte-------------------fuer aktuelle Meldungen
                  setState(entry.Datenpunkt, entry.count);
              });
              // Log Ausgabe der Ergebnisse--------------------------------------------
              LOG(`Es wurden insgesamt ${Zaehler.find(e => e.alarmtype === 'Gesamt').count} IDs gecheckt - insgesamt gibt es ${AnzahlAktuellerSM} Servicemeldungen`, "Ergebnis", "Check_All", 2);
              Zaehler.forEach(({ alarmtype, count }) => {
                  LOG(`SMAktuell fuer alarmtype: ${alarmtype} insgesamt festgestellte Servicemeldungen: ${count}`, "Ergebnis", "Check_All", 2);
              });
              LOG(`Zeitverbrauch fuer Routine Check_All: ${Date.now() - startTime} Millisekunden`, "Ergebnis", "Check_All", 1);
              AktuelleMeldungenJSON =[]; HistorischeMeldungenJSON = [];  MessageSendCollector = {};    // Speicher freigeben
          }
          //-----------------------------------------------------------------------------------------------------
          //  Function processServiceMessage // Zuordnung der richtigen Servicemeldung zum Alarmtype / Zaehlung der Faelle
          //-----------------------------------------------------------------------------------------------------
          function processServiceMessage(alarmtype, status, id, RunType) {
              const native_type = ExtractMetaData(id, "nativeType");
              const meldungsart = ExtractMetaData(id, "meldungsart");
              const common_name = ReplaceString(ExtractMetaData(id, "CommonName"));
              const GeraeteId = ExtractMetaData(id, "GeraeteID");
              let ServiceMeldungTextLang = DefineServiceMessage(native_type, status, id, "lang");     // aktuelle Servicemeldung ableiten
              let ServiceMeldungTextPure = DefineServiceMessage(native_type, status, id, "pure");
              AktuelleMeldungenJSON = CreateJsonEntry(AktuelleMeldungenJSON, func_get_datum(id), meldungsart, common_name, GeraeteId, status, ServiceMeldungTextLang, ServiceMeldungTextPure, func_Batterie(native_type), true); // nur einmal unabhaengig der Version
              increaseCount(alarmtype); increaseCount('SMAktuell');
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion ConfirmStickyMessages Autoconfirm Servicemeldungen (STICKY bei HM-Classic)
          //-----------------------------------------------------------------------------------------------------
          function ConfirmStickyMessages() {
              LOG("Routine ConfirmStickyMessages wird ausgeführt", "Ablauf", "ConfirmStickyMessages", 2);
              let Adapter, Instanz, StickyMeldungsart, StickyMappedString, ID_Constructed, GeraeteID;
              let AckFlag = false; let delay = 0;
              const foundInstance = instanceIds.find(item => item.name === 'HMClassicInstanz'); // die HMClassic Instanz wird ermittelt
              if (foundInstance) {
                  Adapter = foundInstance.adapter;
                  Instanz = foundInstance.Instanz;
              }
              for (let i = 0; i < AktuelleMeldungenJSON.length; i++) {
                  const aktuelleMeldung = AktuelleMeldungenJSON[i];
                  StickyMeldungsart = aktuelleMeldung.meldungsart;
                  StickyMappedString = StickyMappings[StickyMeldungsart];
                  GeraeteID = aktuelleMeldung.GeraeteId;
                  if (StickyMappedString === undefined) { continue }
                  ID_Constructed = Adapter + "." + Instanz + "." + GeraeteID + ".0." + StickyMeldungsart; // Gilt nur bei HM-Classic
                  AckFlag = CheckStickyNochAktuell(StickyMappedString, GeraeteID); // kann die Message bestaetigt werden ? true = ja
                  if (AckFlag) {
                      setTimeout(() => {
                          setState(ID_Constructed, 2);
                          LOG("Eine Sticky Meldung wurde bestaetigt fuer ID " + ID_Constructed, "Ergebnis", "ConfirmStickyMessages", 2);
                      }, delay); //  Verzögerung
                      delay = delay + 500
                  } else {
                      LOG("Eine Sticky Meldung ist noch aktuell und wird nicht bestaetigt fuer ID " + ID_Constructed, "Ergebnis", "ConfirmStickyMessages", 2);
                  }
              }
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion CheckStickyNochAktuell checked of Sticky Meldung bestaetigt werden kann 
          //-----------------------------------------------------------------------------------------------------
          function CheckStickyNochAktuell(MappedString, GeraeteID) {
              let AckFlag = true;
              MappedString = String(MappedString); // Wandelt MappedString in einen String um
              GeraeteID = String(GeraeteID); // Wandelt GeraeteID in einen String um
              for (let j = 0; j < AktuelleMeldungenJSON.length; j++) {    // Iteriere über alle aktuellen Meldungen, um nach einer Nicht-Sticky-Meldung zu suchen
                  const AktuelleMeldung = AktuelleMeldungenJSON[j];
                  const aktuelleMeldungsart = String(AktuelleMeldung.meldungsart).trim();
                  const aktuelleGeraeteId = String(AktuelleMeldung.GeraeteId).trim();
                  if (aktuelleMeldungsart === MappedString && aktuelleGeraeteId === GeraeteID) {  // ueberpruefen ob die Sticky message noch aktuell ist
                      AckFlag = false;
                      break; // Schleife abbrechen
                  }
              }
              return AckFlag;
          }
          //-----------------------------------------------------------------------------------------------------
          // Function increaseCount // Diese Funktion reduziert Wiederholungen der Case abfragen in Check_ALL
          //-----------------------------------------------------------------------------------------------------
          function increaseCount(alarmtype) {
              const entry = Zaehler.find(e => e.alarmtype === alarmtype);
              if (entry) entry.count++;
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion DefineServiceMessage  Message ERmittlung
          //-----------------------------------------------------------------------------------------------------
          function DefineServiceMessage(native_type, status, id, version) {
              const meldungsart = ExtractMetaData(id, "meldungsart");
              LOG(`Routine DefineServiceMessage wird ausgefuehrt meldungsart ${meldungsart} und ID:${id}`, "Ablauf", "DefineServiceMessage", 2);
              const adapter = ExtractMetaData(id, "adapter");
              const GeraeteId = ExtractMetaData(id, "GeraeteID");
              const common_name = ReplaceString(ExtractMetaData(id, "CommonName"));
              const datum_seit = func_get_datum(id);
              let matchingAlarmType = meldungsart;
              let isValidMessageType = alarmTypes.some((type) => type.suffixes.includes(meldungsart));
              if (isValidMessageType) {    // Bestimmen des passenden Alarmtyps
                  matchingAlarmType = alarmTypes.find((type) => type.suffixes.includes(meldungsart)).key;
                  if (matchingAlarmType === "ERROR" && meldungsart !== "ERROR") {
                      matchingAlarmType = meldungsart;
                  }
              }
              const createServiceMessage = (message) => {    // Hilfsfunktion zum Erzeugen der ServiceMessage
                  if (version === "lang") {        // Unterschiedliche Formate je nach Version
                      let Status_Message_Lang = `${datum_seit} - ${meldungsart} - ${common_name} - (${GeraeteId}) - ${status} - ${message}`;
                      return Status_Message_Lang;
                  } else if (version === "pure") {
                      return message;  // Nur die pure status Nachricht
                  } else {
                      return `${common_name} ${message}`;  // Kurze Nachricht, falls kurze Nachrichten gewuenscht fuer das messaging
                  }
              };
              switch (matchingAlarmType) {    // Logik je nach meldungsart
                  case "LOWBAT_ALARM":
                      const lowBatMessage = `${getStatusMessage(meldungsart, status, adapter)} - Batteriebezeichnung: ${func_Batterie(native_type)}`;
                      return createServiceMessage(lowBatMessage);
                  case "ERROR":
                      if (status >= 1 && status <= 7 && errorMessages[native_type] && errorMessages[native_type][status]) {
                          return createServiceMessage(errorMessages[native_type][status]);
                      }
                      if (status === 0) {
                          const fallbackMessage = getStatusMessage("FALLBACK", status, adapter);
                          return createServiceMessage(fallbackMessage);
                      }
                      break;
                  case "FAULT_REPORTING":
                      if (faultMessages[native_type] && faultMessages[native_type][status]) {
                          return createServiceMessage(faultMessages[native_type][status]);
                      }
                      break;
                  default:
                      if (isValidMessageType) {
                          return createServiceMessage(getStatusMessage(meldungsart, status, adapter));
                      } else {
                          const fallbackMessage = getStatusMessage("FALLBACK", status, adapter);
                          return createServiceMessage(fallbackMessage);
                      }
              }
              return createServiceMessage(getStatusMessage("FALLBACK", status, adapter));    // kann eigentlich nicht aufteten
          }
          //-----------------------------------------------------------------------------------------------------
          //Funktion getStatusMessage zur Abfrage der Meldung je messageType, Adapter und Status (fuer Routine DefineServiceMessage)
          //-----------------------------------------------------------------------------------------------------
          function getStatusMessage(messageType, statusKey, adapter) {
              if ( statusMessages[messageType] && statusMessages[messageType][adapter] &&statusMessages[messageType][adapter][statusKey] !== undefined  ) {    // Existenzpruefung- ggf regagieren
                  return statusMessages[messageType][adapter][statusKey];
              } else {
                  return `Keine passende Meldung fuer MessageType: "${messageType}", Adapter: "${adapter}", Status: "${statusKey}".`;
              }
          }
          //-----------------------------------------------------------------------------------------------------
          // addMessageToCollector  Messages werden unter beruecksichtigung der folgenden Objekte in den MessageCollector genommen
          // MessengerScope, Services, AlarmTypes, TextTypeKurz
          //-----------------------------------------------------------------------------------------------------
          function addMessageToCollector(messageType, MessageKurz, MessageLang) {
              let actualMessageType, matchingAlarmType;
              if (messageType !== "keineSM") {  
                  let isValidMessageType = alarmTypes.some((type) => type.suffixes.includes(messageType));    
                  if (isValidMessageType) {                                                                   
                      matchingAlarmType = alarmTypes.find((type) => type.suffixes.includes(messageType)).key;
                      actualMessageType = messageType;  
                  } else {
                      LOG(`MessageType: ${messageType} ist nicht konfiguriert in alarmTypes`, "WARN", "addMessageToCollector", 0);
                  }
              }
              if (messageType === "keineSM") {  
                  actualMessageType = "keineSM";
                  matchingAlarmType = "keineSM";
              }
              if (!MessageSendCollector[actualMessageType]) {  
                  MessageSendCollector[actualMessageType] = {};  
              }
              let messengerConfig = MessengerScope[matchingAlarmType] || MessengerScope['Sonstige'] || Array(services.length).fill(false);
              messengerConfig.forEach((isActive, index) => {  
                  if (isActive) {  
                      const service = services[index];
                      const instance = MessengerInstanz[index];
                      if (instance !== null && !isNaN(instance) && instance >= 0) {
                          if (!MessageSendCollector[actualMessageType][service]) {
                              MessageSendCollector[actualMessageType][service] = [];
                          }
                          const messageToAdd = TextTypeKurz[index] ? MessageKurz : MessageLang;
                          const existingMessages = MessageSendCollector[actualMessageType][service];
                          // **Doppelprüfung:** Gibt es bereits eine Nachricht mit demselben Text und Instanz?
                          const isDuplicate = existingMessages.some(msg => msg.message.trim() === messageToAdd.trim() && msg.instance === instance);
                          if (!isDuplicate) {
                              MessageSendCollector[actualMessageType][service].push({  
                                  message: messageToAdd + '\n',
                                  instance: instance
                              });
                              LOG(`Nachricht hinzugefügt für ${actualMessageType} - Service: ${service} - Instanz: ${instance}`, "Ablauf", "addMessageToCollector", 3);
                          } else {
                              LOG(`Doppelte Nachricht erkannt und verworfen für ${actualMessageType} - Service: ${service} - Instanz: ${instance}`, "DEBUG", "addMessageToCollector", 3);
                          }
                      } else {
                          LOG(`Ungültige Instanz für Service: ${service} (Instanz: ${instance})`, "WARN", "addMessageToCollector", 0);
                      }
                  }
              });
          }
           //-----------------------------------------------------------------------------------------------------
          // sendMessage  Hier werden die Nachrichten fuer den jeweiligen Service aufbereitet
          //-----------------------------------------------------------------------------------------------------
          function sendMessage(messageType = null) {
              LOG(`Routine sendMessage wird ausgefuehrt, meldungsart: ${messageType}`, "Ablauf", "sendMessage", 2);
              const messageTypesToProcess = messageType ? [messageType] : Object.keys(MessageSendCollector);    // Bestimme die MessageTypes, die verarbeitet werden sollen
              messageTypesToProcess.forEach((type) => {    
                  const messagesByService = MessageSendCollector[type];  
                  if (messagesByService) {
                      Object.keys(messagesByService).forEach((service) => { 
                          const serviceMessages = messagesByService[service]; 
                          let messagesToRemove = [];  
                          serviceMessages.forEach((item) => {                
                              const { message, instance } = item;
                              sendToService(service, message, instance);
                              messagesToRemove.push(item);
                          });
                          messagesToRemove.forEach((item) => {                // Entferne alle verarbeiteten Nachrichten
                              const serviceMessagesIndex = serviceMessages.indexOf(item);
                              if (serviceMessagesIndex > -1) {
                                  serviceMessages.splice(serviceMessagesIndex, 1);
                              }
                          });
                      });
                  }
              });
          }
          //-----------------------------------------------------------------------------------------------------
          // sendToService    - hier wird der Versand vorgenommen
          // Reihenfolge:       Email, WhatsApp, Signal, Telegram, Pushover, Pushsafer
          // MessengerInstanz = [0,       0,        0,      0,         0,        0]    Instanzen der Messenger-Dienste in 
          //-----------------------------------------------------------------------------------------------------
          function sendToService(service, message, instance) {
          LOG(`Message wird versendet mit: ${service} : ${instance} : ${message}`, "Ablauf", "sendToService", 2);
              switch (service) {
                  case "email":
                      sendTo(`email.${instance}`, "send", {
                          text: message, 
                          to: emailAddresse,
                          subject: Headline
                      });
                      break;
                  case "whatsApp":
                      sendTo(`whatsapp-cmb.${instance}`, "send", {
                          text: message
                      });
                      break;
                  case "Signal":
                      sendTo(`signal-cmb.${instance}`, "send", {
                          text: message
                      });
                      break;
                  case "Telegram":
                      sendTo(`telegram.${instance}`, "send", {
                          text: message,
                          user: TelegramUser  // Telegram User ID, um den Nachrichteneempfaenger festzulegen
                      });
                      break;
                  case "Pushover":
                      sendTo(`pushover.${instance}`, "send", {
                          message: message,
                          sound: ""
                      });
                      break;
                  case "Pushsafer":
                      sendTo(`pushsafer.${instance}`, "send", {
                          message: message,
                          title: Headline
                      });
                      break;
                  default:
                      LOG(`Unbekannter Service: ${service}`, "WARN", "sendToService", "warn");
              }
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion ExtractMetaData  Verarbeitung des Datenpunkts zur Extraktion der Metadaten: GeraeteId, meldungsart
          //-----------------------------------------------------------------------------------------------------
          function ExtractMetaData(datenpunkt, ExtractType) {
              LOG(`Routine ExtractMetaData wird ausgefuehrt datenpunkt fuer Metadaten ${datenpunkt} ExtractType ${ExtractType} `, "Ablauf", "ExtractMetaData", 2);
              ExtractType = ExtractType.toUpperCase();
              const teile = datenpunkt.split('.');                                        // Splitte den Datenpunkt in Teile
              const adapter = teile[0];                                                   // Der erste Teil ist immer der Adapter
              const instance = teile[1];                                                  // Der zweite  Teil ist immer die  Instance
              const struktur = StrukturDefinition.find(s => s.Adapter === adapter);       // Strukturzeile aus Tabelle StrukturDefinition fuer den Adapter aus dem uebergebenen DP
              const GeraeteID = teile[struktur.GeraeteID];                                // Extrahiere Felder basierend auf der Strukturdefinition
              if (!struktur) {
                  LOG(`Die Struktur fuer MetaDaten Extract ist nicht korrekt eingestellt. Der uebergebenen datenpunkt war:$(datenpunkt)- Abbruch`, "Fehler", "ExtractMetaData", 0);
                  return null;
              }
              if(ExtractType === "ADAPTER") { return adapter; }
              if(ExtractType === "GERAETEID") { return GeraeteID; }
              if(ExtractType === "MELDUNGSART") {
                  const meldungsart = teile[struktur.AlarmFeld];                                       // Extrahiere Felder basierend auf der Strukturdefinition
                  return meldungsart;
              }
              if(ExtractType === "NATIVETYPE" && typeof struktur.nativeType === 'number') {
                  const MetaDP = datenpunkt.split('.').slice(0, struktur.nativeType).join('.');        // ergibt den Datenpunkt fuer getObject z.B. hm-rpc.1.00085D89B14067
                  const nativeType  = getObject(MetaDP).native.TYPE;
                  return nativeType;
              }
              if(ExtractType === "NATIVETYPE" && typeof struktur.nativeType === 'string') {           // bei string liegt der native-type im datenpunkt
                  let  DPNativeTypeDP = struktur.nativeType;
                  DPNativeTypeDP = DPNativeTypeDP.replace('xidx', GeraeteID);
                  DPNativeTypeDP = DPNativeTypeDP.replace('xinstancex', instance);
                  let nativeType = getState(DPNativeTypeDP).val  
                  return nativeType;
              }
              if(ExtractType === "COMMONNAME" && typeof struktur.common_name === 'number') {        
                  const MetaDP = datenpunkt.split('.').slice(0, struktur.common_name).join('.');        // ergibt den Datenpunkt fuer getObject z.B. hm-rpc.1.00085D89B14067
                  const common_name  = getObject(MetaDP).common.name;
                  return common_name;
              }
              if(ExtractType === "COMMONNAME" && typeof struktur.common_name === 'string') {           // bei string liegt der common_name im datenpunkt
                  let DPcommmon_name = struktur.common_name;
                  DPcommmon_name = DPcommmon_name.replace('xidx', GeraeteID).replace('xinstancex', instance);
                  let common_name = getState(DPcommmon_name).val  
                  return common_name;
              }
              LOG(`Der Datenpunkt/ExtractType, die erforderlich zur Ermittlung von Metadaten sind nicht bekannt Der Datenpunkt lautet ${datenpunkt} ExtractType ${ExtractType}  - Abbruch`, "Fehler", "ExtractMetaData", 0);
              return null
          }
          //-----------------------------------------------------------------------------------------------------
          // ReplaceString  // ersetzen  entsprechend tabelle replacements
          //-----------------------------------------------------------------------------------------------------
          function ReplaceString(string) {
              for (const [key, value] of Object.entries(replacements)) {
                  // Escape den Punkt (.) fuer den regulaeren Ausdruck
                  const escapedKey = key.replace('.', '\\.');
                  string = string.replace(new RegExp(escapedKey, 'g'), value);
              }
              return string;
          }
          //-----------------------------------------------------------------------------------------------------
          // func_get_datum  aktuelles Datum formatiert
          //-----------------------------------------------------------------------------------------------------
          function func_get_datum(id) {
              let datum;
              if (id && getState(id)) {
                  datum = formatDate(getState(id).lc, "TT.MM.JJ SS:mm:ss");
              } else {
                  datum = formatDate(new Date(), "TT.MM.JJ SS:mm:ss"); // Aktuelles Datum
              }
              let datumDate = new Date(datum); // korrekte verwendung des datumsformates
              let cutoffDate = new Date('1971-01-01T01:00:00');
              return datumDate < cutoffDate ? '' : `${datum} Uhr`;
          }
          //-----------------------------------------------------------------------------------------------------
          // func_Batterie Batterieermittlung
          //-----------------------------------------------------------------------------------------------------
          function func_Batterie(native_type) {
              LOG(`Routine wird ausgefuehrt`, "Ablauf", "func_Batterie", 2);
              const normalizedType = native_type.toUpperCase();
              return Object.keys(batteryTypes).find(battery => 
                  batteryTypes[battery].some(device => device.toUpperCase() === normalizedType)
              ) || NichtRelevantText;
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion CreateJsonEntry erzeugen einer JSON Tabelle 
          //-----------------------------------------------------------------------------------------------------
          function CreateJsonEntry(existingJson = [], datum_seit = '', meldungsart = '', common_name = '', GeraeteId = '', status = '', Status_Lang = '', Status_Pure, Batterie = '',sortierung = false) {
              let parsedJson;
              if (Array.isArray(existingJson)) {     // ueberpruefen, ob existingJson bereits ein Array ist
                  parsedJson = existingJson; // Nur das Array uebernehmen
              } else {
                  parsedJson = [];  // Falls es kein Array ist, setze es auf ein leeres Array
              }
              const neuerEintrag = { 
                  datum_seit: datum_seit || func_get_datum(),     // Fallback auf aktuelles Datum
                  meldungsart: meldungsart || NichtRelevantText,
                  common_name: common_name || NichtRelevantText,
                  GeraeteId: GeraeteId || NichtRelevantText,
                  status: status || NichtRelevantText,                                // Status entweder number oder boolean
                  status_message_Lang: Status_Lang || 'Keine Meldung',    // Langer Text
                  status_message_Pure: Status_Pure || 'Keine Meldung',    // nur die Status aus den Status-Tabellen
                  batterie_bezeichnung: Batterie || NichtRelevantText,
                  SM_aufgehoben_seit:  "aktiv"          // status ob eine message bereits aufgeboben ist. wenn aufgehoben wird ein Datum eingestellt
              };
              parsedJson.unshift(neuerEintrag);
              if(sortierung) { sortJason(parsedJson);}
              return parsedJson;  // Gib das aktualisierte Array zurueck
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion checkIfEntryExists Checken of ein Eintrag schon vorhanden ist
          //-----------------------------------------------------------------------------------------------------
          function checkIFJSONfEntryExists(parsedJson, datum_seit, GeraeteId, status, meldungsart) {
              if (!Array.isArray(parsedJson)) { // Überprüfen, ob parsedJson ein Array ist
                  LOG(`parsedJson ist kein Array`, "WARN", "checkIFJSONfEntryExists", 1);
                  return false;
              }
              for (let entry of parsedJson) { // Durch das Array iterieren und kriterien checken // meldungsart ist optional
                  if (entry.datum_seit === datum_seit && entry.GeraeteId === GeraeteId && entry.status === status && (meldungsart === undefined || entry.meldungsart === meldungsart)  ) {
                      return true; // Eintrag gefunden
                  }
              }
              return false; // Kein passender Eintrag gefunden
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion isLatestEntryMessageBeiKeinerSM Checken ob die letzte message der variablen MessageBeiKeinerSM entspricht
          //-----------------------------------------------------------------------------------------------------
          function isLatestEntryMessageBeiKeinerSM(existingJson = []) {
              if (!Array.isArray(existingJson) || existingJson.length === 0) {    // ueberpruefen, ob existingJson ein Array ist und Eintraege enthaelt
                  return false;
              }
              const latestEntry = existingJson[0];    // Neuesten Eintrag extrahieren (erster Eintrag im Array)
              if (latestEntry.status_message_Lang && latestEntry.status_message_Lang.includes(MessageBeiKeinerSM)) {
                  return true;
              }
              return false;
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion Markiere_HistSM_als_Aufgehoben Zuordnung von historischen Messages zu "aufgehoben" mit Datum der Zuordnung
          //-----------------------------------------------------------------------------------------------------
          function Markiere_HistSM_als_Aufgehoben(HistorischeMeldungenJSON_transfer) {
              const maxTimeDiffMs = 1000; // z.B. 1000 ms fuer eine Sekunde Unterschied
              const nurKeineMeldungen = AktuelleMeldungenJSON[0].status_message_Lang.includes(MessageBeiKeinerSM);    // Pruefen, ob AktuelleMeldungenJSON nur eine Meldung mit "Derzeit keine Servicemeldungen" enthaelt
              if (nurKeineMeldungen) {
                  HistorischeMeldungenJSON_transfer.forEach(meldung => {        // Alle historischen Eintraege als aufgehoben markieren
                      if (meldung.SM_aufgehoben_seit === "aktiv") {
                          meldung.SM_aufgehoben_seit = func_get_datum();
                      }
                  });
                  return HistorischeMeldungenJSON_transfer;
              }
              HistorischeMeldungenJSON_transfer.forEach(historischeMeldung => {    // Iteriere ueber alle historischen Meldungen (aeussere Schleife)
                  if (historischeMeldung.SM_aufgehoben_seit !== "aktiv") return; // Wenn die historische Meldung bereits aufgehoben wurde, ueberspringen
                  let zugeordnet = false;
                  const historischeDatum = extractDate(historischeMeldung.datum_seit);
                  AktuelleMeldungenJSON.forEach(aktuelleMeldung => {        // Iteriere ueber alle aktuellen Meldungen (innere Schleife)
                      const aktuelleDatum = extractDate(aktuelleMeldung.datum_seit);
                      const gleicheGeraeteID = historischeMeldung.GeraeteId === aktuelleMeldung.GeraeteId;
                      const gleicheStatusMessage = historischeMeldung.status_message_Pure === aktuelleMeldung.status_message_Pure;
                      const zeitDifferenz = Math.abs(aktuelleDatum.getTime() - historischeDatum.getTime());            // Berechne den Zeitunterschied in Millisekunden
                      const gleicheOderNaheZeit = zeitDifferenz <= maxTimeDiffMs;            // Pruefe, ob die Zeitdifferenz innerhalb des erlaubten Bereichs liegt
                      if (gleicheGeraeteID && gleicheStatusMessage && gleicheOderNaheZeit) {
                          historischeMeldung.SM_aufgehoben_seit = "aktiv"; // Als nicht aufgehoben markieren
                          zugeordnet = true
                      }
                  });
                  if (!zugeordnet) {
                      historischeMeldung.SM_aufgehoben_seit = func_get_datum() // kein pendant gefunden, also muss die message obsolet sein
                  }
              });
              return HistorischeMeldungenJSON_transfer;
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion sortJason zum Sortieren des History Jason nach datum
          //-----------------------------------------------------------------------------------------------------
          function sortJason(parsedJson) {
              if (!Array.isArray(parsedJson)) { LOG("Die Eingabe fuer sortJason ist kein Array.", "Fehler", "sortJason", 2); return[] ;}
              const parseDate = (datum) => {
                  try {
                      const [date, time] = datum.split(' '); // Datum und Zeit trennen
                      const [day, month, year] = date.split('.'); // Datum aufteilen in Tag, Monat, Jahr
                      const [hours, minutes, seconds] = time.split(':'); // Zeit aufteilen in Stunden, Minuten, Sekunden
                      return new Date(`20${year}-${month}-${day}T${hours}:${minutes}:${seconds}`); // ISO-Format erstellen
                  } catch (error) {
                      LOG(`Fehler beim Parsen des Datums: ${datum}`, "Fehler", "sortJason", 1);
                      return null;  // Rueckgabe von null, wenn das Datum ungueltig ist
                  }
              };
              return parsedJson.sort((a, b) => {
                  const dateA = parseDate(a.datum_seit);
                  const dateB = parseDate(b.datum_seit);
                  if (dateA === null && dateB === null) return 0; // Keine aenderung bei ungueltigen Datumswerten
                  if (dateA === null) return 1; // Null-Werte ans Ende verschieben
                  if (dateB === null) return -1;
                  return dateB.getTime() - dateA.getTime(); // Neueste zuerst
              });
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion extractDatum // Hilfsfunktion zur Extraktion des Datums        // Beispieltext: "01.12.24 12:04:55 Uhr - SABOTAGE_ALARM ..."
          //-----------------------------------------------------------------------------------------------------
          function extractDate(text) {
              if (!text || typeof text !== "string") {
                  LOG(`extractDate Fehler: Ungueltiger Eingabetext: ${text}`, "Fehler", "extractDate", 2);
                  return new Date(0); // Fallback auf ein Default-Datum
              }
              const match = text.match(/^(\d{2})\.(\d{2})\.(\d{2}) (\d{2}):(\d{2}):(\d{2})/);    // Regulaerer Ausdruck, um Datum und Uhrzeit zu extrahieren
              if (!match) {
                  LOG(`extractDate Fehler: Kein gueltiges Datum gefunden: ${text}`, "Fehler", "extractDate", 2);
                  return new Date(0); // Fallback auf ein Default-Datum
              }
              const [_, day, month, year, hours, minutes, seconds] = match;    // Teile aus dem Match extrahieren
              const fullYear = year.length === 2 ? `20${year}` : year;    // Jahr vervollstaendigen, falls noetig
              const dateString = `${fullYear}-${month}-${day}T${hours}:${minutes}:${seconds}`;    // Erstellen des Datums im Format "YYYY-MM-DDTHH:mm:ss"
              const date = new Date(dateString);    // Umwandlung in ein Date-Objekt
              if (isNaN(date.getTime())) {    // ueberpruefen, ob das Datum gueltig ist
                  LOG(`extractDate Fehler: Ungueltiges Datum erzeugt: ${dateString}`, "Fehler", "extractDate", 2);
                  return new Date(0);
              }
              return date;
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion GenerateTEXT_Datenpunkte // Generiert TEXT format aus den JSON Arrays und speichert diese in die Datenpunkte
          //-----------------------------------------------------------------------------------------------------
          function GenerateTEXT_Datenpunkte() {
              LOG(`Routine wird ausgefuehrt`, "Ablauf", "GenerateTEXT_Datenpunkte", 2);
              if(!UpdateTEXT_Datenpunkte){return}; // wenn keine TEXT-Datenpunkte updates erfolgen sollen nicht ausfuehren
              let  SMAktuellMessageLangTEXT = [], SMHistMessageLangTEXT = [],SMAktuellMessageKurzTEXT = [];
              HistorischeMeldungenJSON.forEach(Meldung => {        // Iteriere durch HistorischeMeldungenJSON und extrahiere die status_message_Lang
                  SMHistMessageLangTEXT.push(Meldung.status_message_Lang + " - Meldung aufgehoben: " + Meldung.SM_aufgehoben_seit + "<br>");            // Status-Lang-Text extrahieren und ins Array pushen
              });
              setState(id_Text_Servicemeldung_History, SMHistMessageLangTEXT.join(''));        // Umwandlung des Arrays in einen String und Speichern im Datenpunkt id_Text_ServicemeldungLang
              AktuelleMeldungenJSON.forEach(Meldung => {        // Iteriere durch AktuelleMeldungenJSON und extrahiere die status_message_Pure und status_message_Lang
                  if (Meldung.status_message_Pure) {
                      SMAktuellMessageKurzTEXT.push(Meldung.datum_seit + " " + Meldung.status_message_Pure + "<br>"); // Status-Kurz-Text extrahieren und ins Array pushen
                  }
                  if (Meldung.status_message_Lang) {
                      SMAktuellMessageLangTEXT.push(Meldung.status_message_Lang + "<br>"); // Status-Lang-Text extrahieren und ins Array pushen
                  }
              });
              setState(id_Text_ServicemeldungKurz, SMAktuellMessageKurzTEXT.join(''));        // Umwandlung der Arrays in Strings und Speichern in den entsprechenden Datenpunkten
              setState(id_Text_ServicemeldungLang, SMAktuellMessageLangTEXT.join(''));
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion generateHtmlTable // Funktion, aus den JSON Tabellen ein HTML-Format erzeugt
          //-----------------------------------------------------------------------------------------------------
          function generateHtmlTable(data, HistOderAkt) {  
              if (!UpdateHTML_Datenpunkte) { return; }        // Keine Updates, wenn HTML-Datenpunkte deaktiviert sind
              const uniqueClass = "custom-table";    // Eindeutige Klasse für die Tabelle
              const cellPadding = '6px';
              // Baue die Tabelle mit einer eindeutigen Klasse auf
              let table = `
                  <table class="${uniqueClass}" style="
                      width: 100%;
                      border-collapse: collapse; /* Rahmen zusammenführen */
                      border-spacing: 0; /* Kein Abstand zwischen Zellen */
                  ">
              `;
              // Scoped Stile für die Tabelle
              table += `
                  <style>
                      .${uniqueClass} th, .${uniqueClass} td {
                          padding: ${cellPadding};
                          ${useBorderAll ? `border: ${borderWidth} solid ${borderColor};` : 'border: none;'} /* Innerer Rahmen basierend auf useBorderAll */
                      }
                      .${uniqueClass} thead th {
                          position: sticky;
                          top: 0;
                          background-color: ${headerColor};
                          color: ${headerTextColor};
                          z-index: 1;
                      }
                  </style>
              `;
              // Kopfzeile
              table += `
                  <thead>
                  <tr>
                      <th style="text-align: left;">Datum_Seit</th>
                      <th style="text-align: left;">Meldungsart</th>
                      <th style="text-align: left;">Name</th>
                      <th style="text-align: left;">GeraeteId</th>
                      <th style="text-align: left;">Status</th>
                      <th style="text-align: left;">Batterie</th>
                      <th style="text-align: left;">SM_aufgehoben_seit</th>
                  </tr>
                  </thead>
              `;
              // Tabellenkörper
              table += '<tbody>';
              data.forEach((item, index) => {
                  const rowColor = index % 2 === 0 ? evenRowColor : oddRowColor; // Farben der Zeilen
                  const textColor = index % 2 === 0 ? evenRowTextColor : oddRowTextColor; // Textfarbe
                  table += `
                  <tr style="background-color: ${rowColor}; color: ${textColor};">
                      <td>${item.datum_seit}</td>
                      <td>${item.meldungsart}</td>
                      <td>${item.common_name}</td>
                      <td>${item.GeraeteId}</td>
                      <td>${item.status_message_Pure}</td>
                      <td>${item.batterie_bezeichnung}</td>
                      <td>${item.SM_aufgehoben_seit}</td>
                  </tr>
                  `;
              });
              table += '</tbody>';
              table += '</table>';
              // Zustand setzen je nach HistOderAkt
              if (HistOderAkt === "AKT") {
                  setState(id_HTML_Servicemeldung_Aktuell, table);  
              } else {
                  setState(id_HTML_Servicemeldung_History, table); 
              }
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion Refresh_History loescht die Historie - Ausloeser entweder subcription oder schedule
          //-----------------------------------------------------------------------------------------------------
          function Refresh_History() {
              let jsonString = getState(id_JSON_Servicemeldung_Historie).val || '[]'; // zunaechst die JSON Eintraege
              HistorischeMeldungenJSON = JSON.parse(jsonString);
              HistorischeMeldungenJSON = HistorischeMeldungenJSON.filter(meldung => meldung.SM_aufgehoben_seit === "aktiv");    // Filtern der Meldungen, um nur die zu behalten, die den Wert "aktiv" in "SM_aufgehoben_seit" haben
              let updatedJsonString = JSON.stringify(HistorischeMeldungenJSON);    // Speichern der gefilterten historischen Meldungen zurueck in den Datenpunkt
              setState(id_JSON_Servicemeldung_Historie, updatedJsonString);
              Check_All("init")
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion schreibt einen Logeintrag in das Filesystem und auch in das interne Log-System
          //-----------------------------------------------------------------------------------------------------
          function writelog(Name, GeraeteId, SMType, SMStatus, SMStatus_Text) {
              if (!SMProtokoll) return;
              const fs = require('fs');                     // enable write fuer externes log
              const logdate = formatDate(new Date(), "TT.MM.JJJJ");
              const logtime = formatDate(new Date(), "SS:mm:ss");
              const logEntry = `${logdate} ;${logtime} ;${Name} ;${GeraeteId} ; ${SMType} ;${SMStatus} ;${SMStatus_Text}\n`;
              const headerLine = "Datum;Uhrzeit;Name;ID-Name;Meldungssart;Status;Servicemeldung\n";
              fs.readFile(PathSMLog, 'utf8', function(err, data) {
                  if (!err) {
                      fs.appendFileSync(PathSMLog, logEntry, 'utf8');
                  } else {
                      LOG(`Logfile nicht gefunden - wird angelegt`, "Ablauf", "writelog", 0);
                      fs.writeFileSync(PathSMLog, headerLine + logEntry, 'utf8');
                  }
              });
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion schreibt die Syste-Log-Eintraege in ein externes CSV - File
          //-----------------------------------------------------------------------------------------------------
          function LOG(Message,Kategorie,Routine, Level, type) {
              if(type !== "warn" && type !== "error") {type = "info"}
              if (!SystemLog && debugLevel >= Level) { log(Message+" Routine:"+Routine,type); return;}     // Wenn SystemLog false ist und der Debug-Level hoeher oder gleich dem uebergebenen Level, schreibe normalen Logeintrag
              if  (Level === 0) { log(Message+" Routine:"+Routine,type);} // bei level 0 soll auf jeden fall auch in das normale log geschrieben werden
              if (SystemLog && debugLevel >= Level) {         // Wenn SystemLog true ist und der Debug-Level hoeher oder gleich dem uebergebenen Level
                  const fs = require('fs');
                  const now = new Date();
                  const logdate = `${now.getDate().toString().padStart(2, '0')}.${(now.getMonth() + 1).toString().padStart(2, '0')}.${now.getFullYear()}`;
                  const logtime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}:${now.getMilliseconds().toString().padStart(3, '0')}`;
                  const logEntry = `${logdate};${logtime};${Level};${Kategorie};${Routine};${Message}\n`;
                  const headerLine = "Datum;Uhrzeit;Debug Level;Kategorie;Routine;Log-Message\n";  
                  const logFilePath = PathSystemLog || './defaultLog.csv'; // falls PathSystemLog nicht definiert ist, standardmaessigen Pfad verwenden
                  try {
                      if (!fs.existsSync(logFilePath)) {
                          log(`Routine:LOG - Logfile nicht gefunden - wird angelegt`,"info")
                          fs.writeFileSync(logFilePath, headerLine + logEntry, 'utf8');  // Datei erstellen und Header hinzufuegen
                      } else {
                          fs.appendFileSync(logFilePath, logEntry, 'utf8'); // Eintrag zum Logfile hinzufuegen
                      }
                  } catch (err) {
                      log(`Routine:LOG - Fehler beim Schreiben in das Logfile: ${err}`, "error"); // Fehler beim Schreiben
                  }
              }
          }
          //-----------------------------------------------------------------------------------------------------
          // Funktion Create States
          //-----------------------------------------------------------------------------------------------------
          async function CreateStates(callback) {
              LOG(`Routine wird ausgefuehrt`, "Ablauf", "CreateStates", 2);
              try {
                  if(UpdateTEXT_Datenpunkte) {    // nur wenn Text-Datenpunkte auch gewuenscht sind
                      await createStateAsync(id_Text_ServicemeldungLang, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen LangText', desc: 'LangText Servicemeldung' });
                      await createStateAsync(id_Text_ServicemeldungKurz, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen KurzText', desc: 'KurzText Servicemeldung' });
                      await createStateAsync(id_Text_Servicemeldung_History, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen History', desc: 'History Servicemeldung' });
                  }
                  if(UpdateHTML_Datenpunkte) {    // nur wenn Text-Datenpunkte auch gewuenscht sind
                      await createStateAsync(id_HTML_Servicemeldung_Aktuell, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen HTML-Text', desc: 'HTML-Text Servicemeldung' });
                      await createStateAsync(id_HTML_Servicemeldung_History, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen HTML-History', desc: 'HTML-History Servicemeldung' });
                  }
                  await createStateAsync(id_JSON_Servicemeldung_Aktuell, "", { read: true, write: true, type: 'string', name: 'Aktuelle Servicemeldungen als JSON', desc: 'Vergangene Servicemeldung JSON' });
                  await createStateAsync(id_JSON_Servicemeldung_Historie, "", { read: true, write: true, type: 'string',name: 'Servicemeldungen History', desc: 'History Servicemeldung' });
                  await createStateAsync(id_Button_Refresh_Historie, false, { read: true, write: true, type: 'boolean', name: 'Refresh Button History', desc: 'Loescht die Historie Servicemeldungem wenn true' });
                  for (const { alarmtype, count, Datenpunkt } of Zaehler) {               // Schleife ueber das Zaehler-Array und anlegen der States
                      await createStateAsync(Datenpunkt, count, {read: true, write: true,type: 'number',name: `Servicemeldungen Anzahl ${alarmtype}`,desc: `Anzahl ${alarmtype} Servicemeldungen`});
                  }
              if (callback) await callback();         // Aufruf des Callbacks nach Abschluss aller Operationen
                  } catch (error) {
                  LOG(`Kategorie:WARN; Routine:CreateStates; Routine Create States - Fehler beim Erstellen der Zustaende: ${error}`, 0, "warn");
              }
              if (!UpdateTEXT_Datenpunkte) {
                  if (existsState(id_Text_ServicemeldungLang)) { deleteState(id_Text_ServicemeldungLang); }
                  if (existsState(id_Text_ServicemeldungKurz)) { deleteState(id_Text_ServicemeldungKurz); }
                  if (existsState(id_Text_Servicemeldung_History)) { deleteState(id_Text_Servicemeldung_History); }
              }
              if(!UpdateHTML_Datenpunkte) {    // nur wenn Text-Datenpunkte auch gewuenscht sind
                  if (existsState(id_HTML_Servicemeldung_Aktuell)) { deleteState(id_HTML_Servicemeldung_Aktuell); }
                  if (existsState(id_HTML_Servicemeldung_History)) { deleteState(id_HTML_Servicemeldung_History); }
              }
          }
          
          L 1 Antwort Letzte Antwort
          0
          • Eduard77E Eduard77

            @looxer01
            Habe jetzt so gemacht wie du beschreiben hast, aber bekomme wieder Warnung.

            2be5ab4c-acdc-4052-9c4f-17ff734951cc-image.png

            anbei mein geänderte Skript zur Prüfung.

            // Beschreibung des Scriptes: https://forum.iobroker.net/topic/77816/vorlage-servicemeldungen-volume2
            // Autor Looxer01 02.11.2024 Version 1.0 (initiale Version)
            // Version 3.00 - 05.01.2025 Variable MessageBeiKeinerSM in die Experteneinstellungen geschoben // Problem bei der Datumsberechnung behoben // Zaehler fuer Historie hinzugefuegt
            //                           die Datenpunkte fuer Text-Format werden jetzt optional gefuellt
            // Version 3.10 - 01.02.2024 Wartezeit fuer HM-Rega von 2 auf 3,5 Sekunden erhoeht - Funktion der Tabelle MessengerScope erweitert
            // Version 3.20 - 24.02.2024 Doppelte Nachrichten im messageCollector verhindern - Vermeidung Meldung "keine Servicemeldungen..." in der Historie wenn bereits vorhanden - Wartezeit für Rega-Trigger wieder auf 5s
            //                           StickyMessages koennen automatisch bestaetigt werden
            //---------------------------------------------------------------------------------------------------------------------------------------------------------------
            // Muss-Einstellungen                                 ( HM-Classic , HMIP, Wired - hier muessen zwingend die Instanzen stimmmen )
            //---------------------------------------------------------------------------------------------------------------------------------------------------------------
            // Im folgenden sind die Instanzen gelistet fuer die die Selektion erfolgt bzw nicht erfolgt (Filter)
            // bitte 9 Eintragen falls eine Instanz nicht relevant ist // Gruppeninstanzen sind normalerweise nicht relevant
            // CuxD Instanzen duerfen nicht eingetragen werden  --- ACHTUNG: bei HMIPAccessPoint-Nutzung muss GeraeteTriggerID auf true stehen
            const HMClassicInstanz      = 9;        // HM-Classic Instanz eintragen     // 9 = falls nicht relevant
            const HMIPInstanz           = 9;        // Homematic IP instanz             // 9 = falls nicht relevant
            const WiredIClassicInstanz  = 9;        // Wired Instanz                    // 9 = falls nicht relevant
            const GruppenInstanz        = 9;        // virtuelle GeraeteInstanz- 9 = nicht relevant - Die Gruppen werden i.d.R. nicht gebraucht - Empfehlung: 9
            let   HMIPAccessPoint       = 0;        // AccessPoint Servicemeldungen - (normalerweise instanz = 0) bei nicht Verwendung Instanz = 9
            //---------------------------------------------------------------------------------------------------------------------------------------------------------------
            // Kann-Einstellungen 
            //---------------------------------------------------------------------------------------------------------------------------------------------------------------
            // Pfad kann angepasst werden fuer userdata pfad einfach // entfernen
            const path      = "javascript.0.ServicemeldungenVol2.";
            //const path    = "0_userdata.0.ServicemeldungenVol2."; // alternativ zum javascript pfad
            
            // schreibt das Protokoll der Servicemeldungen in ein externes file (Excel Format)
            const SMProtokoll = true;             
            const PathSMLog = "/opt/iobroker/log/ServicemeldungenVol2.csv";             // Pfad und Dateiname des externen Logs
            //const PathSMLog = "/iobroker/log/ServicemeldungenVol2.csv";            // Pfad fuer Windows/  iobroker ist der angenommene iobroker home-pfad
            
            //Geraete die nicht ueberwacht werden sollen. Geraete-IDs eingeben - Komma getrennt erfassen
            const Ausschlussliste = ['0000D7099xxx26', '00091D8xxx7410']; // immer mit Komma trennen // Filter auf Einzel-IDs
            
            // debug level kann eingestellt werden - wenn alles laeuft dann 0 = ruhe im log
            // debug level 0   kein log // debug level 1 - nur die wichtigsten Meldungen werden gelistet // debug level 2 - mehr als nur die wichtigsten Meldungen aber ohne einzelne IDs
            // debug level 3 - hier werden auch einzelne IDs gelistet (koennten lange listen werden)
            const debugLevel = 2 ;     // Empfehlung = 1
            const SystemLog = false;  // schreib das Sytemprotokoll in ein externes log (sollte normalerweise deaktviert sein  nur bei Problemen verwenden)           
            const PathSystemLog = "/opt/iobroker/log/ServicemeldungenSystemLog.csv";             // Pfad und Dateiname des externen Logs 
            //const PathSystemLog = "/iobroker/log/ServicemeldungenSystemLog.csv";             // Pfad fuer Windows
            
            // wenn GeraeteIDTrigger auf true gestellt wird, dann wird fuer jeden Datenpukt mit Relevanz fuer eine Servicemeldung eine subscription angelegt.
            // Vorteile: mehr Details in der Historie von Servicemeldungen: Nachteil: bei 80 CCU Geraeten ungefaehr 300 Susbsriptions
            // Wenn die variable auf false steht, dann wird auf hm.rega.0.maintenance eine subsription angelegt: Vorteil: 1 Subscription , Nachteil: erweiterte Funktion fuer nicht CCU Datenpunkte nicht verfuegbar
            // bei HMIP AccessPoint Cloud HCU - funktioniert das nur bei GeraeteTrigger = true
            let GeraeteIDTrigger = false; // true = viele subscriptions aber praezise Historie der Servicemeldungen - false = 1 subscription - weniger praezise Historie - Empfehlung = false
            
            // Speicherung der Servicemeldungen im  Text-Format - Empfehlung = false // davon ausgehend, dass das JSON Format in der Visualisierung gut dargestellt werden kann
            const UpdateTEXT_Datenpunkte = false; // die kurz- und langen Servicemeldungen als auch die historischen werden immer im JSON Format abgelegt. Alternativ koennen diese auch in einem TEXT Format in Datenpunkten gespeichert werden
            // Speicherung der Servicemeldungen im  HTML-Format - Wird empfohlen zu nutzen, wenn es Problem bei der Visualisierung von JSON gibt.
            const UpdateHTML_Datenpunkte = true; // die langen Servicemeldungen als auch die historischen werden immer im JSON Format abgelegt. Alternativ koennen diese auch in einem HTML Format in Datenpunkten gespeichert werden
            // Die Autobestaetigung bestaetigt automatisch evt Sticky-Meldungen der CCU 
            const AutoBestaetigungCCUMeldungen = true; // Empfehlung ist true - nur relevant fur HM-Classic Geraete //bei true sollte in der CCU die autobestaetigung ausgeschaltet werden
            
            //  fuer alle Spalten mit true werden die Nachrichten ueber den zugeordneten Dienst versendet, vorausgesetzt der Messenge Adapter ist in iobroker installiert/konfiguriert
                const services =               ['email',    'whatsApp',     'Signal',  'Telegram',    'Pushover', 'Pushsafer', 'Pushsafer',];
                const MessengerScope = {
                'UNREACH_ALARM':                [true,        false,          false,      true,          false,      false,       false,],
                'STICKY_UNREACH_ALARM':         [false,       false,          false,      false,          false,      false,       false,],   // Sticky sollte nicht gesendet werden - alles auf false setzen  
                'LOWBAT_ALARM':                 [true,        false,          false,      true,          false,      false,       false,],
                'SABOTAGE_ALARM':               [true,        false,          false,      true,          false,      false,       false,],
                'STICKY_SABOTAGE_ALARM':        [false,       false,          false,      false,          false,      false,       false,],   // Sticky sollte nicht gesendet werden - alles auf false setzen  
                'CONFIG_PENDING_ALARM':         [true,        false,          false,      true,          false,      false,       false,],
                'Sonstige':                     [true,        false,          false,      true,          false,      false,       false,], // text ist fest verdrahtet nicht ändern
                'keineSM':                      [true,        false,          false,      true,          false,      false,       false,], // text ist fest verdrahtet nicht ändern
                }
                const MessengerInstanz =        [0,             0,               0,          0,              0,          0,            1,  ]; // Instanz des Messengers
                const TextTypeKurz      =       [false,        true,            true,       true,           true,      true,         false,]; // bei true wird der Kurztext gesendet - sonst der Langtext
            
            // email-Einstellungen
            let emailAddresse = "Vorname-Nachname@web.de"   // Empfängeraddresse
            
            const Headline = "ioBroker Servicemeldung";     // Ueberschrift Messages fuer email und Pushsafer
            // telegram Einstellungen
            const TelegramUser = "Eduard";
            
            //-----------------------------------------------------------------------------------------------------
            //Experten Einstellungen - Empfehlung:  Absprache mit dem Autor
            //-----------------------------------------------------------------------------------------------------
            const MessageBeiKeinerSM = 'Derzeit keine Servicemeldungen'     // Text der erscheinen soll, wenn keine SM vorliegen Ein Kurztext ist erforderlich
            //const NichtRelevantText = 'n/a';                                // statt n/a kann ein beliebiger anderer Text in den JSON verwendet werden
            const NichtRelevantText = "......";
            // die folgenden Einstellungen sind nur relvant bei Verwendung von HTML als Datenpunkte
                // Definieren der Farbwerte für Kopfzeile, gerade und ungerade Zeilen
                const headerColor = '#333333';  // Standardfarbe (Fuellfarbe) für die Kopfzeile
                const evenRowColor = '#4e5049'; // Standardfarbe (Fuellfarbe) für gerade Zeilen
                const oddRowColor = '#333333';  // Standardfarbe (Fuellfarbe) für ungerade Zeilen
                // Definieren der Textfarben für Kopfzeile, gerade und ungerade Zeilen
                const headerTextColor = 'white'; // Textfarbe für Kopfzeile
                const evenRowTextColor = 'white'; // Textfarbe für gerade Zeilen
                const oddRowTextColor = 'white';  // Textfarbe für ungerade Zeilen
                // Definieren von Zellenumrandungseinstellungen
                const useBorderAll =    true; // true, wenn ein Gitternetz erzeugt werden soll
                const borderColor = '#000000'; // Farbe des Gitternetzes (Zellenumrandung)
                const borderWidth = '1px'; // Strichstärke der Zellenumrandung
                
            //Schedule Zeit fuer refresh der Historie
            const ScheduleAktiv = true;                         // Bei "false" wird der schedule nicht durchlaufen. Manuelles Löschen kann über den Datenpunkt id_Button_Refresh_Historie (Button) möglich
            const scheduleTimeClearSMTexte = "2 0 1 * *";       // am 1. tag des monats um 00:02 morgens sollen alle Servicemeldungen des Monats geloescht werden
            // const scheduleTimeClearSMTexte = "58 23 * * 0";  // alternative Sonntags um 23:58 Uhr sollen alle Servicemeldungen der Woche im datenpunkt der SM-Texte geloescht werden 
            
            //Batterie-Zuordnungen fuer Servicmeldungen
            const batteryTypes = {
                '1x CR2016':    ['HM-RC-4', 'HM-RC-4-B', 'HM-RC-Key3', 'HM-RC-Key3-B', 'HM-RC-P1', 'HM-RC-Sec3', 'HM-RC-Sec3-B', 'ZEL STG RM HS 4'],
                '1x CR2032':    ['HM-PB-2-WM', 'HM-PB-4-WM', 'HM-PBI-4-FM', 'HM-SCI-3-FM', 'HM-Sec-TiS', 'HM-SwI-3-FM', 'HmIP-FCI1'],
                '2x LR14':      ['HM-Sec-Sir-WM', 'HM-OU-CFM-TW', 'HM-OU-CFM-Pl', 'HM-OU-CF-Pl'],
                '2x LR44/AG13': ['HM-Sec-SC', 'HM-Sec-SC2L', 'HM-Sec-SC-2', 'HM-Sec-RHS'],
                '2x LR6/AA':    ['HM-CC-VD', 'HM-CC-RT-DN', 'HM-Sec-WDS', 'HM-Sec-WDS-2', 'HM-CC-TC', 'HM-Dis-TD-T', 'HB-UW-Sen-THPL-I', 'HM-WDS40-TH-I', 'HM-WDS40-TH-I-2', 
                                 'HM-WDS10-TH-O', 'HmIP-SMI',                     'HMIP-eTRV', 'HM-WDS30-OT2-SM-2', 'HmIP-SMO', 'HmIP-SMO-A', 'HmIP-SPI', 'HmIP-eTRV-2', 
                                 'HmIP-SPDR', 'HmIP-STHO-A', 'HmIP-eTRV-B', 'HmIP-PCBS-BAT', 'HmIP-STHO', 'HmIP-eTRV-C', 
                                 'HmIP-WGC', 'HmIP-eTRV-C-2', 'HmIP-eTRV-E', 'HmIP-eTRV-2 I9F', 'HmIP-eTRV-E-S', 'ELV-SH-SW1-BAT'],
                '3x LR6/AA':    ['HmIP-SWO-PL', 'HM-Sec-MDIR', 'HM-Sec-MDIR-2', 'HM-Sec-SD', 'HM-Sec-Key', 'HM-Sec-Key-S', 'HM-Sec-Key-O', 'HM-Sen-Wa-Od', 'HM-Sen-MDIR', 
                                 'HM-Sen-MDIR-O', 'HM-Sen-MDIR-O-2', 'HM-WDS100-C6-O', 'HM-WDS100-C6-O-2', 'HmIP-ASIR', 'HmIP-SWO-B', 'HM-Sen-MDIR-O-3', 'HM-Sec-MDIR-3', 
                                 'HmIP-SWO-PR', 'HmIP-DLD', 'HmIP-ASIR-2'],
                '4x LR6/AA':    ['HM-CCU-1', 'HM-ES-TX-WM', 'HM-WDC7000'],
                '1x LR3/AAA':   ['HM-RC-4-2', 'HM-RC-4-3', 'HM-RC-Key4-2', 'HM-RC-Key4-3', 'HM-RC-Sec4-2', 'HM-RC-Sec4-3', 'HM-Sec-RHS-2', 'HM-Sec-SCo', 'HmIP-KRC4', 
                                 'HmIP-KRCA', 'HmIP-SRH', 'HMIP-SWDO', 'HmIP-DBB', 'HmIP-RCB1', 'HmIP-KRCK', 'HmIP-SWDO-2'],
                '2x LR3/AAA':   ['HmIP-WRCR', 'HmIP-SWD','HM-TC-IT-WM-W-EU', 'HM-Dis-WM55', 'HM-Dis-EP-WM55', 'HM-PB-2-WM55', 'HM-PB-2-WM55-2', 'HM-PB-6-WM55', 'HM-PBI-2-FM', 'HM-RC-8', 'HM-Sen-DB-PCB', 
                                'HM-Sen-EP', 'HM-Sen-MDIR-SM', 'HM-Sen-MDIR-WM55', 'HM-WDS30-T-O', 'HM-WDS30-OT2-SM', 'HmIP-STH', 'HmIP-STHD', 'HmIP-WRC2', 'HmIP-WRC6', 'HmIP-WTH', 'HmIP-WTH-2', 
                                'HmIP-SAM', 'HmIP-SLO', 'HMIP-SWDO-I', 'HmIP-FCI6', 'HmIP-SMI55', 'HM-PB-2-FM', 'HmIP-SWDM', 'HmIP-SCI', 'HmIP-SWDM-B2', 'HmIP-RC8', 'ALPHA-IP-RBG', 'HmIP-DSD-PCB', 
                                'HmIP-WRCD', 'HmIP-WRC2-A', 'HmIP-WTH-B-2', 'HmIP-WTH-A', 'HmIP-STV', 'HmIP-WKP'],
                '3x LR3/AAA':   ['HM-PB-4Dis-WM', 'HM-PB-4Dis-WM-2', 'HM-RC-Dis-H-x-EU', 'HM-Sen-LI-O'],
                '3x AAA Akkus - bitte laden': ['HM-RC-19', 'HM-RC-19-B', 'HM-RC-12', 'HM-RC-12-B', 'HM-RC-12-W'],
                '3x LR14/C':    ['HmIP-MP3P'],
                '9Volt Block leer oder unbestimmt': ['HM-LC-Sw1-Ba-PCB', 'HM-LC-Sw4-PCB', 'HM-MOD-EM-8', 'HM-MOD-Re-8', 'HM-Sen-RD-O', 'HM-OU-CM-PCB', 'HM-LC-Sw4-WM'],
                'Festbatterie leer': ['HmIP-STE2-PCB', 'HM-Sec-SD-2', 'HmIP-SWSD', 'HmIP-PCBS'],
                'ohne Batterie': ['HM-LC-Sw1PBU-FM', 'HM-LC-Sw1-Pl-DN-R1', 'HM-LC-Sw1-DR', 'HM-LC-RGBW-WM', 'HM-LC-Sw1-Pl-CT-R1', 'HmIP-HEATING', 'HM-LC-Sw1-FM', 'HM-LC-Sw2-FM', 'HM-LC-Sw4-DR', 
                                    'HM-LC-Sw1-Pl', 'HM-LC-Sw1-Pl-2', 'HM-LC-Sw4-Ba-PCB', 'HM-LC-Sw1-SM', 'HM-LC-Sw4-SM', 'HM-Sys-sRP-Pl', 'HM-LC-Sw2PBU-FM', 'HM-LC-Sw1-PCB', 'HM-LC-Sw4-DR-2'],
            
                'Akku entladen - bitte aufladen': ['HM-Sec-Win', 'HM-Sec-SFA-SM', 'HM-RC-19-SW']
            };
            // Rega Pfad
            const PathRega = ['hm-rega.0.maintenance',];                            // Array um ggf die HCU zu konfigurieren
            // Pfade fuer die Speicherung aktueller und vergangener Servicemeldungen
            const id_Text_ServicemeldungLang        = path+'TextLangAktuelleSM';    // Objekt wo die Servicemeldung hingeschrieben werden soll (String) = path+'TextLang';
            const id_Text_ServicemeldungKurz        = path+'TextKurzAktuelleSM';    // Objekt wo die Servicemeldung hingeschrieben werden soll (String) - kurze Version
            const id_Text_Servicemeldung_History    = path+'TextLangVergangeneSM';  // Objekt wo die Servicemeldung hinzugefuegt werden soll (String) - Lange Version (es gibt nur lang)
            const id_HTML_Servicemeldung_Aktuell    = path+'HtmlAktuelleSM';        // Objekt wo die Servicemeldung hingeschrieben werden soll (HTML formatiere Tabelle) = path+'htmlLang';
            const id_HTML_Servicemeldung_History    = path+'HtmlVergangeneSM';      // Objekt wo die Servicemeldung hinzugefuegt werden soll ((HTML formatiere Tabelle) - Lange Version (es gibt nur lang)
            const id_JSON_Servicemeldung_Aktuell    = path+'JSONAktuelleSM';        // JSON Tabelle Datenpunkt Aktuelle SM
            const id_JSON_Servicemeldung_Historie   = path+'JSONVergangeneSM';      // JSON Tabelle Datenpunkt Historische SM
            const id_Button_Refresh_Historie        = path+'ButtonRefreshHistorie'; // Wenn DP auf true gesetzt wird, dann wird die historie refreshed (geloescht)
            // Count-Pfade  und counts in einer Tabelle - wird auch fuer CREATE STATES verwendet
            const Zaehler = [
              { alarmtype: 'UNREACH_ALARM',                    count: 0,                    Datenpunkt: path+'Anzahl_UNREACH' },
              { alarmtype: 'STICKY_UNREACH_ALARM',             count: 0,                    Datenpunkt: path+'Anzahl_STICKY_UNREACH' },
              { alarmtype: 'CONFIG_PENDING_ALARM',             count: 0,                    Datenpunkt: path+'Anzahl_CONFIG_PENDING' },
              { alarmtype: 'UPDATE_PENDING_ALARM',             count: 0,                    Datenpunkt: path+'Anzahl_Update_PENDING' },
              { alarmtype: 'LOWBAT_ALARM',                     count: 0,                    Datenpunkt: path+'Anzahl_LOWBAT' },
              { alarmtype: 'DEVICE_IN_BOOTLOADER_ALARM',       count: 0,                    Datenpunkt: path+'Anzahl_DEVICE_IN_BOOTLOADER' },
              { alarmtype: 'ERROR',                            count: 0,                    Datenpunkt: path+'Anzahl_in_ERROR' },
              { alarmtype: 'FAULT_REPORTING',                  count: 0,                    Datenpunkt: path+'Anzahl_FAULT_REPORTING' },
              { alarmtype: 'SABOTAGE_ALARM',                   count: 0,                    Datenpunkt: path+'Anzahl_SABOTAGE' },
              { alarmtype: 'ERROR_NON_FLAT_POSITIONING_ALARM', count: 0,                    Datenpunkt: path+'Anzahl_NON_FLAT_POSITIONING' },
              { alarmtype: 'STICKY_SABOTAGE_ALARM',            count: 0,                    Datenpunkt: path+'Anzahl_Sticky_SABOTAGE' },
              { alarmtype: 'SMAktuell',                        count: 0,                    Datenpunkt: path+'Anzahl_SM-Aktuell' },
              { alarmtype: 'SMHistorie',                       count: 0,                    Datenpunkt: path+'Anzahl_SM-Historie' },
              { alarmtype: 'Gesamt',                           count: 0,                    Datenpunkt: path+'Anzahl_GESAMT' }
            ];
            // statusmessages je messagetype und   adapter // Fallback ist fuer unbekannte messagetypes - z.B. alle Error Messages // FALLBACK repraesentiert Standardmessages
            const statusMessages = {
                UNREACH_ALARM:                    { "hm-rpc": { 0: "keine Kommunikationsfehler",     1: "Kommunikation gestoert",                            2: "Kommunikation war gestoert" } },
                STICKY_UNREACH_ALARM:             { "hm-rpc": { 0: "keine Kommunikationsfehler",     1: "Sticky Kommunikation gestoert",                     2: "Sticky Kommunikation war gestoert" } },
                SABOTAGE_ALARM:                   { "hm-rpc": { 0: "Keine Sabotage",                 1: "Sabotage",                                          2: "Sabotage aufgehoben" } },
                STICKY_SABOTAGE_ALARM:            { "hm-rpc": { 0: "Keine Sabotage",                 1: "Sticky Sabotage",                                   2: "Sticky Sabotage aufgehoben" } },
                LOWBAT_ALARM:                     { "hm-rpc": { 0: "Batterie ok",                    1: "Batterie niedrig",                                  2: "Batterie ok" } },
                LOW_BAT_ALARM:                    { "hm-rpc": { 0: "Batterie ok",                    1: "Batterie niedrig",                                  2: "Batterie ok" } },
                ERROR_NON_FLAT_POSITIONING_ALARM: { "hm-rpc": { 0: "Keine Meldung",                  1: "Geraet wurde angehoben.",                           2: "Geraet wurde angehoben: Bestaetigt" } },
                CONFIG_PENDING_ALARM:             { "hm-rpc": { 0: "keine Meldung",                  1: "Konfigurationsdaten stehen zur Uebertragung an",    2: "Konfigurationsdaten standen zur Uebertragung an",}, },
                UPDATE_PENDING_ALARM:             { "hm-rpc": { 0: "kein Update verfuegbar",         1: "Update verfuegbar",                                 2: "Update wurde eingespielt" } },
                ERROR_OVERHEAT_ALARM:             { "hm-rpc": { 0: "kein Overheat Alarm",            1: "Overheat gemeldet",                                 2: "Overheat geloest" } },
                ERROR_UNDERVOLTAGE_ALARM:         { "hm-rpc": { 0: "Kein Undervoltage Alarm",        1: "Undervoltage gemeldet",                             2: "Undervoltage geloest" } },
                DEVICE_IN_BOOTLOADER_ALARM:       { "hm-rpc": { 0: "Keine Meldung",                  1: "Geraet startet neu",                                2: "Geraet wurde neu gestartet" } },
                DUTY_CYCLE:                       { "hm-rpc": { false: "Geraete-Duty Cycle ok",      true: "Geraete-Duty Cycle erreicht",                    null: "unbekannter Status (Duty_Cycle" } },
                lowBat:                           { "hmip":   { false: "Batterie ok",                true: "Batterie niedrig",                               null: "Batterie ok" } },
                unreach:                          { "hmip":   { false: "keine Kommunikationsfehler", true: "Kommunikation gestoert",                         null: "Kommunikation war gestoert" } },
                sabotage:                         { "hmip":   { false: "Keine Sabotage",             true: "Sabotage",                                       null: "Sabotage aufgehoben" } },
                configPending:                    { "hmip":   { false: "Keine Meldung",              true: "Konfigurationsdaten stehen zur Uebertragung an", null: "Konfigurationsdaten standen zur Uebertragung an" } },
                FALLBACK:                         { "hm-rpc": { 0: "keine Stoerung",                 1: "Stoerung",                                          2: "Stoerung aufgehoben",
                                                                false: "Keine Stoerung",             true: "Stoerung",                                       null: "unbekannter Status Fallback"},
                                                    "hmip":   { false: "keine Stoerung",             true: "Stoerung",                                       null: "Stoerung aufgehoben" }, }
            };
            //ErrorMessages fuer HM-Classic Geraete - Sonderfaelle
            const errorMessages = {
                'HM-Sec-RHS':   { 7: 'Sabotage' },
                'HM-Sec-RHS-2': { 7: 'Sabotage' },
                'HM-Sec-SC':    { 7: 'Sabotage' },
                'HM-Sec-SC-2':  { 7: 'Sabotage' },
                'HM-Sec-SCo':   { 7: 'Sabotage' },
                'HM-Sec-MD':    { 7: 'Sabotage' },
                'HM-Sec-MDIR':  { 7: 'Sabotage' },
                'HM-Sec-MDIR-2':{ 7: 'Sabotage' },
                'HM-Sec-Key':   { 1: 'Einkuppeln fehlgeschlagen', 2: 'Motorlauf abgebrochen' },
                'HM-Sec-Key-S': { 1: 'Einkuppeln fehlgeschlagen', 2: 'Motorlauf abgebrochen' },
                'HM-Sec-Key-O': { 1: 'Einkuppeln fehlgeschlagen', 2: 'Motorlauf abgebrochen' },
                'HM-CC-VD':     { 1: 'Ventil Antrieb blockiert',  2: 'Ventil nicht montiert', 3: 'Stellbereich zu klein', 4: 'Batteriezustand niedrig'
                }
            };
            //ErrorMessages fuer HM-Classic Geraet (Heizung) - Sonderfaelle
            const faultMessages = {
                'HM-CC-RT-DN': {
                    0: 'keine Stoerung',
                    1: 'Ventil blockiert',
                    2: 'Einstellbereich Ventil zu gross',
                    3: 'Einstellbereich Ventil zu klein',
                    4: 'Kommunikationsfehler',
                    6: 'Spannung Batterien/Akkus gering',
                    7: 'Fehlstellung Ventil'
                }
            };
            // hier koennen die Alarmgruppen ggf erweitert werden - Aus Alarmgruppe und Instanz wird der Selector gebastelt und in Tabelle Selectors gesammelt
            const alarmTypes = [
                { key: 'UNREACH_ALARM',                     suffixes: ['UNREACH_ALARM','unreach' ] },//UNREACH_ALARM = HM-Classic & HMIP-CCU - unreach = HMIP Accesspoint
               // { key: 'STICKY_UNREACH_ALARM',              suffixes: ['STICKY_UNREACH_ALARM'] },
                { key: 'CONFIG_PENDING_ALARM',              suffixes: ['CONFIG_PENDING_ALARM','configPending'] }, //configPending ist eine HMIP Meldung
                { key: 'UPDATE_PENDING_ALARM',              suffixes: ['UPDATE_PENDING_ALARM'] },
                { key: 'LOWBAT_ALARM',                      suffixes: ['LOWBAT_ALARM', 'LOW_BAT_ALARM','lowBat'] }, //LOWBAT_ALARM = HM-Classic - LOW_BAT_ALARM = HMIP CCU - lowBat = HMIP Accesspoint
                { key: 'DEVICE_IN_BOOTLOADER_ALARM',        suffixes: ['DEVICE_IN_BOOTLOADER_ALARM'] },
                { key: 'ERROR',                             suffixes: ['ERROR','DUTY_CYCLE'] },                  // error ist ein Sammler fuer hier nicht definierte Meldungen
                { key: 'FAULT_REPORTING',                   suffixes: ['FAULT_REPORTING'] },
                { key: 'SABOTAGE_ALARM',                    suffixes: ['SABOTAGE_ALARM','sabotage'] }, // sabotage ist eine HMIP Meldung
              //  { key: 'STICKY_SABOTAGE_ALARM',             suffixes: ['STICKY_SABOTAGE_ALARM'] },
                { key: 'ERROR_NON_FLAT_POSITIONING_ALARM',  suffixes: ['ERROR_NON_FLAT_POSITIONING_ALARM'] },
                { key: 'OVERHEAT_ALARM',                    suffixes: ['ERROR_OVERHEAT_ALARM'] },
                { key: 'UNDERVOLTAGE_ALARM',                suffixes: ['ERROR_UNDERVOLTAGE_ALARM'] },
            ];
            // Umlaut Umwandlung und entfernung PUnkte - kann aber auch erweitert werden
            const replacements = { '.': ' ', 'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss' }; // Umwandlung fuer Namen der Geraete (common.name)
            // Definition der Datenstrukturen fuer MetaDaten je nach Adapter, da abweichend
            const StrukturDefinition = [
                { Adapter: 'hm-rpc',   GeraeteID: 2, AlarmFeld: 4, nativeType: 3,                                             common_name:  3 }, // die Ziffer ist die Positinierung des Feldes 'hm-rpc.1.00085D89B14067.0.UPDATE_PENDING_ALARM' 0=Adapter - 2 = ID 4= Feld Alarm / native Type = die ersten Strings bis zur dritten STelle fuer getObject
                { Adapter: 'hmip',     GeraeteID: 3, AlarmFeld: 6, nativeType: 'hmip.xinstancex.devices.xidx.info.modelType', common_name: 'hmip.xinstancex.devices.xidx.info.label' }, // Positionierung wie bei Rm-rpc - bei hmip wird native type aber ueber den DP ausgelesen und nicht getObject "xidx" wird dann mit der geraeteiD ersetzt
            ]
            // Moegliche Homematic Instanzen (CuxD ausgeschlossen) // Adapter hinzugefuegt um ggf HCU zu konfigurieren
            const instanceIds = [
                { name: 'HMClassicInstanz',     adapter: 'hm-rpc', Instanz: HMClassicInstanz     },
                { name: 'HMIPInstanz',          adapter: 'hm-rpc', Instanz: HMIPInstanz          },
                { name: 'GruppenInstanz',       adapter: 'hm-rpc', Instanz: GruppenInstanz       },
                { name: 'WiredIClassicInstanz', adapter: 'hm-rpc', Instanz: WiredIClassicInstanz },
                { name: 'HMIPAccessPoint',      adapter: 'hmip'  , Instanz: HMIPAccessPoint      },
            ];
            // Die StickingMappingsTabelle stellt sicher, dass die Sticky entsprechend dieser Regel bestaetigt werden kann
            const StickyMappings = {
                STICKY_UNREACH_ALARM:            ['UNREACH_ALARM'],
                STICKY_SABOTAGE_ALARM:           ['SABOTAGE_ALARM'],
            };
            
            // Konfigurationsobjekt fuer Ausnahmen / es koennen Arlarmtypes je Instanz ausgeschlossen werden / Filter auf AlarmTypes
            const exceptions = {
                HMClassicInstanz:       [],
                HMIPInstanz:            ['DUTY_CYCLE'],
                GruppenInstanz:         ['ERROR_NON_FLAT_POSITIONING_ALARM'],
                WiredIClassicInstanz:   ['LOWBAT_ALARM', 'LOW_BAT_ALARM','ERROR_NON_FLAT_POSITIONING_ALARM'],
            };
            //-----------------------------------------------------------------------------------------------------
            //Ende Einstellungen
            //-----------------------------------------------------------------------------------------------------
            LOG(`Script: Servicemeldungen Volume 2 - Autor: Looxer01 Datum: 24.02.2025 Version:3.20`,`Einstellungen`,`Start`,0 )
            if(HMIPAccessPoint !== 9 && !GeraeteIDTrigger) {
                GeraeteIDTrigger = true // Wenn der HMIPAccessPoint aktiv ist, dann funktioniert der REGA Trigger nicht. also setzen auf geraeteID Trigger=true
                LOG(`HMIP AccessPoint bzw Cloud HCU sind aktiv - GeraeteTriggerID muss aktiviert sein - wurde aktiviert. Bitte auch manuell korrigieren und auf GeraeteTriggerID = true setzen`,`Einstellungen`,`Start`,0 )
            } 
            LOG(`HMClassicInstanz: ${HMClassicInstanz}; HMIPInstanz: ${HMIPInstanz}; WiredIClassicInstanz: ${WiredIClassicInstanz}; GruppenInstanz: ${GruppenInstanz}`, "Einstellungen", "Start", 2);
            LOG(`Inhalt instanceIds: ${JSON.stringify(instanceIds)}`, "Einstellungen", "Start", 2);
            LOG(`GeraeteIDTrigger: ${GeraeteIDTrigger}`, "Einstellungen", "Start", 2);
            LOG(`Inhalt Messengerscope: ${JSON.stringify(MessengerScope)}`, "Einstellungen", "Start", 2);
            LOG(`Inhalt Services: ${JSON.stringify(services)}`, "Einstellungen", "Start", 2);
            LOG(`Inhalt Messenger-Instanzen: ${JSON.stringify(MessengerInstanz)}`, "Einstellungen", "Start", 2);
            LOG(`Inhalt TextTypeKurz: ${JSON.stringify(TextTypeKurz)}`, "Einstellungen", "Start", 2);
            let HistorischeMeldungenJSON = [], AktuelleMeldungenJSON = [], MessageSendCollector = {};                    
            // jetzt selectors fuellen anhand der Tabelle InstanceIDs, sowie der Auscchlusstabellen
            let totalIdsCount = 0;                                                  // Variable fuer die Gesamtzahl der IDs
            const selectors = alarmTypes.map(alarmType => {
                const collectedIds = [];
                instanceIds.forEach(instance => {
                    if (instance.Instanz !== 9) {                                   // Umbenennung von `instance.id` zu `instance.Instanz`
                        const excludedAlarmTypes = exceptions[instance.name] || []; // Hole Ausnahmen oder leere Liste
                        if (!excludedAlarmTypes.includes(alarmType.key)) {          // Wenn Alarmtype nicht in der Ausnahme
                            alarmType.suffixes.forEach(suffix => {                  // wenn suffix (meldungsart) nicht in der Ausnahme
                                if (!excludedAlarmTypes.includes(suffix)) { 
                                    // IDs sammeln, ohne sofort zu filtern
                                    // @ts-ignore
                                    $(`state[id=${instance.adapter}.${instance.Instanz}.*.${suffix}]`).each(id => collectedIds.push(id)); 
                                }
                            });
                        }
                    }
                });
                const filteredIds = collectedIds.filter(id => !id.includes("groups"));  // for accessPoint und HCU sollten die Ids, die "groups" enthalten ausgeschlossen werden . das sind z.B. Raeume
                const finalFilteredIds = filteredIds.filter(id => {                     // IDs anhand der Geraete-IDs in der Ausschlussliste filtern          
                    const deviceId = id.split('.')[2];                                  // Geraete-ID aus der vollstaendigen ID extrahieren (z. B. "0000D7099xxx26" aus "hm-rpc.1.0000D7099xxx26.0.LOW_BAT_ALARM")
                    return !Ausschlussliste.includes(deviceId);                         // Behalten, wenn nicht in der Ausschlussliste
                });
             
                totalIdsCount += finalFilteredIds.length;                                // IDs zur Gesamtzahl hinzufuegen
                return { name: alarmType.key, ids: finalFilteredIds };
            });
                AktuelleMeldungenJSON = [];
                let jsonString = getState(id_JSON_Servicemeldung_Historie).val || '[]';    // Lade den aktuellen JSON-String des Historie-Datenpunkts
                HistorischeMeldungenJSON = JSON.parse(jsonString);  
                Zaehler.forEach(entry => { entry.count = 0; }); // Alle count-Werte im Objekt auf 0 setzen
                for (let j = 0; j < selectors.length; j++) {
                    const selector = selectors[j];
                    if (selector.ids.length > 0) {
                        for (let i = 0; i < selector.ids.length; i++) {
                            let id = selector.ids[i], status = getState(id).val;
                            if (!existsState(id)) {
                                LOG(`Geraet ${obj.id} scheint zwischenzeitlich nicht mehr zu existieren - Empfehlung: script neu starten`, "WARN", "Check_All", 0, "warn");
                                continue;
                            }
                            switch (selector.name) {        // fuer alle alarmtypes - erst die Sonderfaelle
                                case 'ERROR':
                                    if (status >= 1 && status <= 7) {
                                        if (status === 7) { // Sonderfall bei HM-Classic
                                            processServiceMessage('SABOTAGE_ALARM', status, id,RunType);
                                        } else {            
                                            processServiceMessage(selector.name, status, id,RunType); 
                                        } 
                                    }
                                    break;
                            default:                    // Standardfall fuer alle anderen alarmtypes
                                    if(status === 1 || status === true ) {processServiceMessage(selector.name, status, id,RunType);  } // bei status = true = AccessPoint
                                    break;
                            }
                            increaseCount('Gesamt');
                        } // Ende Loop der Mitglieder des Alarmtypes
                    }  // Endif selector ist gefuellt
                }    // Ende Loop ueber den Selector
                const AnzahlAktuellerSM = Zaehler.find(e => e.alarmtype === 'SMAktuell').count;
                // Sequenz zur Speicherung der aktuellen Servicemeldungen in die JSON -Datenpunkte--------------------------
                if (AnzahlAktuellerSM  === 0) {
                    AktuelleMeldungenJSON =CreateJsonEntry([] ,func_get_datum(),'', '', '','',func_get_datum() + " - " + MessageBeiKeinerSM,MessageBeiKeinerSM,null,false );  // Keine aktuellen Meldungen -> "Keine Meldungen" hinzufuegen
                    let jsonString = JSON.stringify(AktuelleMeldungenJSON);
                    setState(id_JSON_Servicemeldung_Aktuell, jsonString);           // Aktuelle SM wird in JSON-Datenpunkt als String gespeichert
                } else {      
                    let jsonString = JSON.stringify(AktuelleMeldungenJSON);              // AktuelleMeldungenJSON wird in Funktion DefineServiceMessage gefuellt
                    setState(id_JSON_Servicemeldung_Aktuell, jsonString);               // Aktuelle SM wird in JSON-Datenpunkt als String gespeichert
                }
                if (GeraeteIDTrigger && RunType === "trigger" && !existsState(obj.id)) { // obj.id nicht mehr in ioBroker aber noch im Selector
                    const GeraeteId = ExtractMetaData(obj.id, "GeraeteID");                        
                    LOG(`Geraet ${GeraeteId} scheint zwischenzeitlich nicht mehr zu existieren - Empfehlung: script neu starten`, "WARN", "Check_All", 0, "warn");
                    return;
                }
                // Speziell fuer GerateIDTrigger = true ist, dass es "aufgehoben"-Meldungen gibt, die fuer REGA Trigger nicht ausgewertet werden koennen. diese duerfen auch nicht als aktuelle meldungen in den DP gelangen
                let ExtendedAktuelleMeldungenJSON = [...AktuelleMeldungenJSON]; // Erstelle eine flat copy
                if (GeraeteIDTrigger && RunType === "trigger") {
                    const GeraeteId = ExtractMetaData(obj.id, "GeraeteID");
                    const status = obj.newState.val;
                    const common_name = ExtractMetaData(obj.id, "CommonName");
                    const native_type = ExtractMetaData(obj.id, "nativeType");
                    const meldungsart = ExtractMetaData(obj.id, "meldungsart");
                    let status_textLang = DefineServiceMessage(native_type, status, obj.id, "lang");
                    let status_textPure = DefineServiceMessage(native_type, status, obj.id, "pure");
                    if (!checkIFJSONfEntryExists(ExtendedAktuelleMeldungenJSON,func_get_datum(obj.id), GeraeteId,status, meldungsart) ) {       // checken ob ein Eintrag schon in der Historie ist, wenn nicht dann hinzufuegen
                        ExtendedAktuelleMeldungenJSON = CreateJsonEntry(ExtendedAktuelleMeldungenJSON, func_get_datum(obj.id), meldungsart, common_name, GeraeteId, status, status_textLang, status_textPure, func_Batterie(native_type), true)
                    }
                }
                // Historische Meldungen hinzufuegen, wenn nicht bereits vorhanden
                ExtendedAktuelleMeldungenJSON.forEach(aktuelleMeldung => {        // Iteriere ueber alle aktuellen Meldungen und fuege aktuelle meldungen in die historie wenn noch nicht vorhanden
                    if (!checkIFJSONfEntryExists(HistorischeMeldungenJSON,aktuelleMeldung.datum_seit  ,aktuelleMeldung.GeraeteId, aktuelleMeldung.status, aktuelleMeldung.meldungsart) ) {       // checken ob ein Eintrag schon in der Historie ist, wenn nicht dann hinzufuegen
                        if(!isLatestEntryMessageBeiKeinerSM(ExtendedAktuelleMeldungenJSON)) {    // nicht wenn nicht "keine Servicemeldungen" in aktuellJSON vorhanden ist
                            const message_kurz = aktuelleMeldung.common_name + " " + aktuelleMeldung.status_message_Pure
                            addMessageToCollector(aktuelleMeldung.meldungsart, message_kurz, aktuelleMeldung.status_message_Lang);
                            HistorischeMeldungenJSON = CreateJsonEntry(HistorischeMeldungenJSON, aktuelleMeldung.datum_seit, aktuelleMeldung.meldungsart, aktuelleMeldung.common_name, aktuelleMeldung.GeraeteId, aktuelleMeldung.status, aktuelleMeldung.status_message_Lang, aktuelleMeldung.status_message_Pure, aktuelleMeldung.batterie_bezeichnung, true )
                            writelog(aktuelleMeldung.common_name, aktuelleMeldung.GeraeteId, aktuelleMeldung.meldungsart, aktuelleMeldung.status, aktuelleMeldung.status_message_Lang);       // externes log erzeugen
                        }
                    }
                });
                // Handling "keine aktuellen Servicemeldungen"
                if (AnzahlAktuellerSM  === 0 && RunType === "trigger") {       // wenn keine SM vorliegen, die Meldung "keine Servicmeldungen" in die History
                    if(!isLatestEntryMessageBeiKeinerSM(HistorischeMeldungenJSON)) {    // nur  wenn  "keine Servicemeldungen" in Historische Meldung nicht bereits an erster Stelle vorhanden ist
                        HistorischeMeldungenJSON =CreateJsonEntry(HistorischeMeldungenJSON ,func_get_datum(),'', '', '','',func_get_datum() + " - " + MessageBeiKeinerSM,MessageBeiKeinerSM,null,false );  // Keine aktuellen Meldungen
                        writelog("", "", "", "", MessageBeiKeinerSM);       //  log erzeugen
                        addMessageToCollector('keineSM', MessageBeiKeinerSM, MessageBeiKeinerSM); // Vorbereitung Messagesending
                        sendMessage('keineSM');       
                    }                                                         
                }
                // AutoBestaetigung fuer StickyMessages, wenn diese nicht mehr aktuell sind. nur relevant fuer HM-Classic
                if (AutoBestaetigungCCUMeldungen ) {ConfirmStickyMessages() }
                // Messages senden fuer alle Faelle mit Servicemeldungen
                if (AnzahlAktuellerSM  > 0 && RunType ==="trigger") {            // Servicemeldungen versenden, falls servicemeldungen vorliegen
                    sendMessage();
                }
                HistorischeMeldungenJSON = Markiere_HistSM_als_Aufgehoben(HistorischeMeldungenJSON) // markiert hist messages als erledigt falls nicht mehr in aktuelle meldungen enthalten
                jsonString = JSON.stringify(HistorischeMeldungenJSON);                  // Vorbereitung speichern
                setState(id_JSON_Servicemeldung_Historie, jsonString);                  // hist JSON-Daten speichern
                // falls so eingestellt werden jetzt die TEXTe aus den JSON (aktuell und historie) erzeugt und in die Datenpunkte geschrieben
                GenerateTEXT_Datenpunkte(); 
                generateHtmlTable(AktuelleMeldungenJSON,"AKT");                 // Aktuelle SM wird in HTML Format gespeichert falls so eingestellt
                generateHtmlTable(HistorischeMeldungenJSON,"HIST");             // Historische  SM wird in HTML Format gespeichert falls so eingestellt
                // anzahl der Eintraege in HistorischeMeldungenJSON ermitteln und alle counts in die Statistik schreiben
                let entry = Zaehler.find(item => item.alarmtype === "SMHistorie");  
                if (entry) {entry.count = HistorischeMeldungenJSON.length;    }          // Wenn der Eintrag gefunden wird, aktualisiere den count-Wert
                Zaehler.forEach(entry => {    // JETZT: ZaehlerStaende in die ioBroker - Datenpunkte-------------------fuer aktuelle Meldungen
                    setState(entry.Datenpunkt, entry.count);
                });
                // Log Ausgabe der Ergebnisse--------------------------------------------
                LOG(`Es wurden insgesamt ${Zaehler.find(e => e.alarmtype === 'Gesamt').count} IDs gecheckt - insgesamt gibt es ${AnzahlAktuellerSM} Servicemeldungen`, "Ergebnis", "Check_All", 2);
                Zaehler.forEach(({ alarmtype, count }) => {
                    LOG(`SMAktuell fuer alarmtype: ${alarmtype} insgesamt festgestellte Servicemeldungen: ${count}`, "Ergebnis", "Check_All", 2);
                });
                LOG(`Zeitverbrauch fuer Routine Check_All: ${Date.now() - startTime} Millisekunden`, "Ergebnis", "Check_All", 1);
                AktuelleMeldungenJSON =[]; HistorischeMeldungenJSON = [];  MessageSendCollector = {};    // Speicher freigeben
            }
            //-----------------------------------------------------------------------------------------------------
            //  Function processServiceMessage // Zuordnung der richtigen Servicemeldung zum Alarmtype / Zaehlung der Faelle
            //-----------------------------------------------------------------------------------------------------
            function processServiceMessage(alarmtype, status, id, RunType) {
                const native_type = ExtractMetaData(id, "nativeType");
                const meldungsart = ExtractMetaData(id, "meldungsart");
                const common_name = ReplaceString(ExtractMetaData(id, "CommonName"));
                const GeraeteId = ExtractMetaData(id, "GeraeteID");
                let ServiceMeldungTextLang = DefineServiceMessage(native_type, status, id, "lang");     // aktuelle Servicemeldung ableiten
                let ServiceMeldungTextPure = DefineServiceMessage(native_type, status, id, "pure");
                AktuelleMeldungenJSON = CreateJsonEntry(AktuelleMeldungenJSON, func_get_datum(id), meldungsart, common_name, GeraeteId, status, ServiceMeldungTextLang, ServiceMeldungTextPure, func_Batterie(native_type), true); // nur einmal unabhaengig der Version
                increaseCount(alarmtype); increaseCount('SMAktuell');
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion ConfirmStickyMessages Autoconfirm Servicemeldungen (STICKY bei HM-Classic)
            //-----------------------------------------------------------------------------------------------------
            function ConfirmStickyMessages() {
                LOG("Routine ConfirmStickyMessages wird ausgeführt", "Ablauf", "ConfirmStickyMessages", 2);
                let Adapter, Instanz, StickyMeldungsart, StickyMappedString, ID_Constructed, GeraeteID;
                let AckFlag = false; let delay = 0;
                const foundInstance = instanceIds.find(item => item.name === 'HMClassicInstanz'); // die HMClassic Instanz wird ermittelt
                if (foundInstance) {
                    Adapter = foundInstance.adapter;
                    Instanz = foundInstance.Instanz;
                }
                for (let i = 0; i < AktuelleMeldungenJSON.length; i++) {
                    const aktuelleMeldung = AktuelleMeldungenJSON[i];
                    StickyMeldungsart = aktuelleMeldung.meldungsart;
                    StickyMappedString = StickyMappings[StickyMeldungsart];
                    GeraeteID = aktuelleMeldung.GeraeteId;
                    if (StickyMappedString === undefined) { continue }
                    ID_Constructed = Adapter + "." + Instanz + "." + GeraeteID + ".0." + StickyMeldungsart; // Gilt nur bei HM-Classic
                    AckFlag = CheckStickyNochAktuell(StickyMappedString, GeraeteID); // kann die Message bestaetigt werden ? true = ja
                    if (AckFlag) {
                        setTimeout(() => {
                            setState(ID_Constructed, 2);
                            LOG("Eine Sticky Meldung wurde bestaetigt fuer ID " + ID_Constructed, "Ergebnis", "ConfirmStickyMessages", 2);
                        }, delay); //  Verzögerung
                        delay = delay + 500
                    } else {
                        LOG("Eine Sticky Meldung ist noch aktuell und wird nicht bestaetigt fuer ID " + ID_Constructed, "Ergebnis", "ConfirmStickyMessages", 2);
                    }
                }
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion CheckStickyNochAktuell checked of Sticky Meldung bestaetigt werden kann 
            //-----------------------------------------------------------------------------------------------------
            function CheckStickyNochAktuell(MappedString, GeraeteID) {
                let AckFlag = true;
                MappedString = String(MappedString); // Wandelt MappedString in einen String um
                GeraeteID = String(GeraeteID); // Wandelt GeraeteID in einen String um
                for (let j = 0; j < AktuelleMeldungenJSON.length; j++) {    // Iteriere über alle aktuellen Meldungen, um nach einer Nicht-Sticky-Meldung zu suchen
                    const AktuelleMeldung = AktuelleMeldungenJSON[j];
                    const aktuelleMeldungsart = String(AktuelleMeldung.meldungsart).trim();
                    const aktuelleGeraeteId = String(AktuelleMeldung.GeraeteId).trim();
                    if (aktuelleMeldungsart === MappedString && aktuelleGeraeteId === GeraeteID) {  // ueberpruefen ob die Sticky message noch aktuell ist
                        AckFlag = false;
                        break; // Schleife abbrechen
                    }
                }
                return AckFlag;
            }
            //-----------------------------------------------------------------------------------------------------
            // Function increaseCount // Diese Funktion reduziert Wiederholungen der Case abfragen in Check_ALL
            //-----------------------------------------------------------------------------------------------------
            function increaseCount(alarmtype) {
                const entry = Zaehler.find(e => e.alarmtype === alarmtype);
                if (entry) entry.count++;
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion DefineServiceMessage  Message ERmittlung
            //-----------------------------------------------------------------------------------------------------
            function DefineServiceMessage(native_type, status, id, version) {
                const meldungsart = ExtractMetaData(id, "meldungsart");
                LOG(`Routine DefineServiceMessage wird ausgefuehrt meldungsart ${meldungsart} und ID:${id}`, "Ablauf", "DefineServiceMessage", 2);
                const adapter = ExtractMetaData(id, "adapter");
                const GeraeteId = ExtractMetaData(id, "GeraeteID");
                const common_name = ReplaceString(ExtractMetaData(id, "CommonName"));
                const datum_seit = func_get_datum(id);
                let matchingAlarmType = meldungsart;
                let isValidMessageType = alarmTypes.some((type) => type.suffixes.includes(meldungsart));
                if (isValidMessageType) {    // Bestimmen des passenden Alarmtyps
                    matchingAlarmType = alarmTypes.find((type) => type.suffixes.includes(meldungsart)).key;
                    if (matchingAlarmType === "ERROR" && meldungsart !== "ERROR") {
                        matchingAlarmType = meldungsart;
                    }
                }
                const createServiceMessage = (message) => {    // Hilfsfunktion zum Erzeugen der ServiceMessage
                    if (version === "lang") {        // Unterschiedliche Formate je nach Version
                        let Status_Message_Lang = `${datum_seit} - ${meldungsart} - ${common_name} - (${GeraeteId}) - ${status} - ${message}`;
                        return Status_Message_Lang;
                    } else if (version === "pure") {
                        return message;  // Nur die pure status Nachricht
                    } else {
                        return `${common_name} ${message}`;  // Kurze Nachricht, falls kurze Nachrichten gewuenscht fuer das messaging
                    }
                };
                switch (matchingAlarmType) {    // Logik je nach meldungsart
                    case "LOWBAT_ALARM":
                        const lowBatMessage = `${getStatusMessage(meldungsart, status, adapter)} - Batteriebezeichnung: ${func_Batterie(native_type)}`;
                        return createServiceMessage(lowBatMessage);
                    case "ERROR":
                        if (status >= 1 && status <= 7 && errorMessages[native_type] && errorMessages[native_type][status]) {
                            return createServiceMessage(errorMessages[native_type][status]);
                        }
                        if (status === 0) {
                            const fallbackMessage = getStatusMessage("FALLBACK", status, adapter);
                            return createServiceMessage(fallbackMessage);
                        }
                        break;
                    case "FAULT_REPORTING":
                        if (faultMessages[native_type] && faultMessages[native_type][status]) {
                            return createServiceMessage(faultMessages[native_type][status]);
                        }
                        break;
                    default:
                        if (isValidMessageType) {
                            return createServiceMessage(getStatusMessage(meldungsart, status, adapter));
                        } else {
                            const fallbackMessage = getStatusMessage("FALLBACK", status, adapter);
                            return createServiceMessage(fallbackMessage);
                        }
                }
                return createServiceMessage(getStatusMessage("FALLBACK", status, adapter));    // kann eigentlich nicht aufteten
            }
            //-----------------------------------------------------------------------------------------------------
            //Funktion getStatusMessage zur Abfrage der Meldung je messageType, Adapter und Status (fuer Routine DefineServiceMessage)
            //-----------------------------------------------------------------------------------------------------
            function getStatusMessage(messageType, statusKey, adapter) {
                if ( statusMessages[messageType] && statusMessages[messageType][adapter] &&statusMessages[messageType][adapter][statusKey] !== undefined  ) {    // Existenzpruefung- ggf regagieren
                    return statusMessages[messageType][adapter][statusKey];
                } else {
                    return `Keine passende Meldung fuer MessageType: "${messageType}", Adapter: "${adapter}", Status: "${statusKey}".`;
                }
            }
            //-----------------------------------------------------------------------------------------------------
            // addMessageToCollector  Messages werden unter beruecksichtigung der folgenden Objekte in den MessageCollector genommen
            // MessengerScope, Services, AlarmTypes, TextTypeKurz
            //-----------------------------------------------------------------------------------------------------
            function addMessageToCollector(messageType, MessageKurz, MessageLang) {
                let actualMessageType, matchingAlarmType;
                if (messageType !== "keineSM") {  
                    let isValidMessageType = alarmTypes.some((type) => type.suffixes.includes(messageType));    
                    if (isValidMessageType) {                                                                   
                        matchingAlarmType = alarmTypes.find((type) => type.suffixes.includes(messageType)).key;
                        actualMessageType = messageType;  
                    } else {
                        LOG(`MessageType: ${messageType} ist nicht konfiguriert in alarmTypes`, "WARN", "addMessageToCollector", 0);
                    }
                }
                if (messageType === "keineSM") {  
                    actualMessageType = "keineSM";
                    matchingAlarmType = "keineSM";
                }
                if (!MessageSendCollector[actualMessageType]) {  
                    MessageSendCollector[actualMessageType] = {};  
                }
                let messengerConfig = MessengerScope[matchingAlarmType] || MessengerScope['Sonstige'] || Array(services.length).fill(false);
                messengerConfig.forEach((isActive, index) => {  
                    if (isActive) {  
                        const service = services[index];
                        const instance = MessengerInstanz[index];
                        if (instance !== null && !isNaN(instance) && instance >= 0) {
                            if (!MessageSendCollector[actualMessageType][service]) {
                                MessageSendCollector[actualMessageType][service] = [];
                            }
                            const messageToAdd = TextTypeKurz[index] ? MessageKurz : MessageLang;
                            const existingMessages = MessageSendCollector[actualMessageType][service];
                            // **Doppelprüfung:** Gibt es bereits eine Nachricht mit demselben Text und Instanz?
                            const isDuplicate = existingMessages.some(msg => msg.message.trim() === messageToAdd.trim() && msg.instance === instance);
                            if (!isDuplicate) {
                                MessageSendCollector[actualMessageType][service].push({  
                                    message: messageToAdd + '\n',
                                    instance: instance
                                });
                                LOG(`Nachricht hinzugefügt für ${actualMessageType} - Service: ${service} - Instanz: ${instance}`, "Ablauf", "addMessageToCollector", 3);
                            } else {
                                LOG(`Doppelte Nachricht erkannt und verworfen für ${actualMessageType} - Service: ${service} - Instanz: ${instance}`, "DEBUG", "addMessageToCollector", 3);
                            }
                        } else {
                            LOG(`Ungültige Instanz für Service: ${service} (Instanz: ${instance})`, "WARN", "addMessageToCollector", 0);
                        }
                    }
                });
            }
             //-----------------------------------------------------------------------------------------------------
            // sendMessage  Hier werden die Nachrichten fuer den jeweiligen Service aufbereitet
            //-----------------------------------------------------------------------------------------------------
            function sendMessage(messageType = null) {
                LOG(`Routine sendMessage wird ausgefuehrt, meldungsart: ${messageType}`, "Ablauf", "sendMessage", 2);
                const messageTypesToProcess = messageType ? [messageType] : Object.keys(MessageSendCollector);    // Bestimme die MessageTypes, die verarbeitet werden sollen
                messageTypesToProcess.forEach((type) => {    
                    const messagesByService = MessageSendCollector[type];  
                    if (messagesByService) {
                        Object.keys(messagesByService).forEach((service) => { 
                            const serviceMessages = messagesByService[service]; 
                            let messagesToRemove = [];  
                            serviceMessages.forEach((item) => {                
                                const { message, instance } = item;
                                sendToService(service, message, instance);
                                messagesToRemove.push(item);
                            });
                            messagesToRemove.forEach((item) => {                // Entferne alle verarbeiteten Nachrichten
                                const serviceMessagesIndex = serviceMessages.indexOf(item);
                                if (serviceMessagesIndex > -1) {
                                    serviceMessages.splice(serviceMessagesIndex, 1);
                                }
                            });
                        });
                    }
                });
            }
            //-----------------------------------------------------------------------------------------------------
            // sendToService    - hier wird der Versand vorgenommen
            // Reihenfolge:       Email, WhatsApp, Signal, Telegram, Pushover, Pushsafer
            // MessengerInstanz = [0,       0,        0,      0,         0,        0]    Instanzen der Messenger-Dienste in 
            //-----------------------------------------------------------------------------------------------------
            function sendToService(service, message, instance) {
            LOG(`Message wird versendet mit: ${service} : ${instance} : ${message}`, "Ablauf", "sendToService", 2);
                switch (service) {
                    case "email":
                        sendTo(`email.${instance}`, "send", {
                            text: message, 
                            to: emailAddresse,
                            subject: Headline
                        });
                        break;
                    case "whatsApp":
                        sendTo(`whatsapp-cmb.${instance}`, "send", {
                            text: message
                        });
                        break;
                    case "Signal":
                        sendTo(`signal-cmb.${instance}`, "send", {
                            text: message
                        });
                        break;
                    case "Telegram":
                        sendTo(`telegram.${instance}`, "send", {
                            text: message,
                            user: TelegramUser  // Telegram User ID, um den Nachrichteneempfaenger festzulegen
                        });
                        break;
                    case "Pushover":
                        sendTo(`pushover.${instance}`, "send", {
                            message: message,
                            sound: ""
                        });
                        break;
                    case "Pushsafer":
                        sendTo(`pushsafer.${instance}`, "send", {
                            message: message,
                            title: Headline
                        });
                        break;
                    default:
                        LOG(`Unbekannter Service: ${service}`, "WARN", "sendToService", "warn");
                }
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion ExtractMetaData  Verarbeitung des Datenpunkts zur Extraktion der Metadaten: GeraeteId, meldungsart
            //-----------------------------------------------------------------------------------------------------
            function ExtractMetaData(datenpunkt, ExtractType) {
                LOG(`Routine ExtractMetaData wird ausgefuehrt datenpunkt fuer Metadaten ${datenpunkt} ExtractType ${ExtractType} `, "Ablauf", "ExtractMetaData", 2);
                ExtractType = ExtractType.toUpperCase();
                const teile = datenpunkt.split('.');                                        // Splitte den Datenpunkt in Teile
                const adapter = teile[0];                                                   // Der erste Teil ist immer der Adapter
                const instance = teile[1];                                                  // Der zweite  Teil ist immer die  Instance
                const struktur = StrukturDefinition.find(s => s.Adapter === adapter);       // Strukturzeile aus Tabelle StrukturDefinition fuer den Adapter aus dem uebergebenen DP
                const GeraeteID = teile[struktur.GeraeteID];                                // Extrahiere Felder basierend auf der Strukturdefinition
                if (!struktur) {
                    LOG(`Die Struktur fuer MetaDaten Extract ist nicht korrekt eingestellt. Der uebergebenen datenpunkt war:$(datenpunkt)- Abbruch`, "Fehler", "ExtractMetaData", 0);
                    return null;
                }
                if(ExtractType === "ADAPTER") { return adapter; }
                if(ExtractType === "GERAETEID") { return GeraeteID; }
                if(ExtractType === "MELDUNGSART") {
                    const meldungsart = teile[struktur.AlarmFeld];                                       // Extrahiere Felder basierend auf der Strukturdefinition
                    return meldungsart;
                }
                if(ExtractType === "NATIVETYPE" && typeof struktur.nativeType === 'number') {
                    const MetaDP = datenpunkt.split('.').slice(0, struktur.nativeType).join('.');        // ergibt den Datenpunkt fuer getObject z.B. hm-rpc.1.00085D89B14067
                    const nativeType  = getObject(MetaDP).native.TYPE;
                    return nativeType;
                }
                if(ExtractType === "NATIVETYPE" && typeof struktur.nativeType === 'string') {           // bei string liegt der native-type im datenpunkt
                    let  DPNativeTypeDP = struktur.nativeType;
                    DPNativeTypeDP = DPNativeTypeDP.replace('xidx', GeraeteID);
                    DPNativeTypeDP = DPNativeTypeDP.replace('xinstancex', instance);
                    let nativeType = getState(DPNativeTypeDP).val  
                    return nativeType;
                }
                if(ExtractType === "COMMONNAME" && typeof struktur.common_name === 'number') {        
                    const MetaDP = datenpunkt.split('.').slice(0, struktur.common_name).join('.');        // ergibt den Datenpunkt fuer getObject z.B. hm-rpc.1.00085D89B14067
                    const common_name  = getObject(MetaDP).common.name;
                    return common_name;
                }
                if(ExtractType === "COMMONNAME" && typeof struktur.common_name === 'string') {           // bei string liegt der common_name im datenpunkt
                    let DPcommmon_name = struktur.common_name;
                    DPcommmon_name = DPcommmon_name.replace('xidx', GeraeteID).replace('xinstancex', instance);
                    let common_name = getState(DPcommmon_name).val  
                    return common_name;
                }
                LOG(`Der Datenpunkt/ExtractType, die erforderlich zur Ermittlung von Metadaten sind nicht bekannt Der Datenpunkt lautet ${datenpunkt} ExtractType ${ExtractType}  - Abbruch`, "Fehler", "ExtractMetaData", 0);
                return null
            }
            //-----------------------------------------------------------------------------------------------------
            // ReplaceString  // ersetzen  entsprechend tabelle replacements
            //-----------------------------------------------------------------------------------------------------
            function ReplaceString(string) {
                for (const [key, value] of Object.entries(replacements)) {
                    // Escape den Punkt (.) fuer den regulaeren Ausdruck
                    const escapedKey = key.replace('.', '\\.');
                    string = string.replace(new RegExp(escapedKey, 'g'), value);
                }
                return string;
            }
            //-----------------------------------------------------------------------------------------------------
            // func_get_datum  aktuelles Datum formatiert
            //-----------------------------------------------------------------------------------------------------
            function func_get_datum(id) {
                let datum;
                if (id && getState(id)) {
                    datum = formatDate(getState(id).lc, "TT.MM.JJ SS:mm:ss");
                } else {
                    datum = formatDate(new Date(), "TT.MM.JJ SS:mm:ss"); // Aktuelles Datum
                }
                let datumDate = new Date(datum); // korrekte verwendung des datumsformates
                let cutoffDate = new Date('1971-01-01T01:00:00');
                return datumDate < cutoffDate ? '' : `${datum} Uhr`;
            }
            //-----------------------------------------------------------------------------------------------------
            // func_Batterie Batterieermittlung
            //-----------------------------------------------------------------------------------------------------
            function func_Batterie(native_type) {
                LOG(`Routine wird ausgefuehrt`, "Ablauf", "func_Batterie", 2);
                const normalizedType = native_type.toUpperCase();
                return Object.keys(batteryTypes).find(battery => 
                    batteryTypes[battery].some(device => device.toUpperCase() === normalizedType)
                ) || NichtRelevantText;
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion CreateJsonEntry erzeugen einer JSON Tabelle 
            //-----------------------------------------------------------------------------------------------------
            function CreateJsonEntry(existingJson = [], datum_seit = '', meldungsart = '', common_name = '', GeraeteId = '', status = '', Status_Lang = '', Status_Pure, Batterie = '',sortierung = false) {
                let parsedJson;
                if (Array.isArray(existingJson)) {     // ueberpruefen, ob existingJson bereits ein Array ist
                    parsedJson = existingJson; // Nur das Array uebernehmen
                } else {
                    parsedJson = [];  // Falls es kein Array ist, setze es auf ein leeres Array
                }
                const neuerEintrag = { 
                    datum_seit: datum_seit || func_get_datum(),     // Fallback auf aktuelles Datum
                    meldungsart: meldungsart || NichtRelevantText,
                    common_name: common_name || NichtRelevantText,
                    GeraeteId: GeraeteId || NichtRelevantText,
                    status: status || NichtRelevantText,                                // Status entweder number oder boolean
                    status_message_Lang: Status_Lang || 'Keine Meldung',    // Langer Text
                    status_message_Pure: Status_Pure || 'Keine Meldung',    // nur die Status aus den Status-Tabellen
                    batterie_bezeichnung: Batterie || NichtRelevantText,
                    SM_aufgehoben_seit:  "aktiv"          // status ob eine message bereits aufgeboben ist. wenn aufgehoben wird ein Datum eingestellt
                };
                parsedJson.unshift(neuerEintrag);
                if(sortierung) { sortJason(parsedJson);}
                return parsedJson;  // Gib das aktualisierte Array zurueck
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion checkIfEntryExists Checken of ein Eintrag schon vorhanden ist
            //-----------------------------------------------------------------------------------------------------
            function checkIFJSONfEntryExists(parsedJson, datum_seit, GeraeteId, status, meldungsart) {
                if (!Array.isArray(parsedJson)) { // Überprüfen, ob parsedJson ein Array ist
                    LOG(`parsedJson ist kein Array`, "WARN", "checkIFJSONfEntryExists", 1);
                    return false;
                }
                for (let entry of parsedJson) { // Durch das Array iterieren und kriterien checken // meldungsart ist optional
                    if (entry.datum_seit === datum_seit && entry.GeraeteId === GeraeteId && entry.status === status && (meldungsart === undefined || entry.meldungsart === meldungsart)  ) {
                        return true; // Eintrag gefunden
                    }
                }
                return false; // Kein passender Eintrag gefunden
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion isLatestEntryMessageBeiKeinerSM Checken ob die letzte message der variablen MessageBeiKeinerSM entspricht
            //-----------------------------------------------------------------------------------------------------
            function isLatestEntryMessageBeiKeinerSM(existingJson = []) {
                if (!Array.isArray(existingJson) || existingJson.length === 0) {    // ueberpruefen, ob existingJson ein Array ist und Eintraege enthaelt
                    return false;
                }
                const latestEntry = existingJson[0];    // Neuesten Eintrag extrahieren (erster Eintrag im Array)
                if (latestEntry.status_message_Lang && latestEntry.status_message_Lang.includes(MessageBeiKeinerSM)) {
                    return true;
                }
                return false;
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion Markiere_HistSM_als_Aufgehoben Zuordnung von historischen Messages zu "aufgehoben" mit Datum der Zuordnung
            //-----------------------------------------------------------------------------------------------------
            function Markiere_HistSM_als_Aufgehoben(HistorischeMeldungenJSON_transfer) {
                const maxTimeDiffMs = 1000; // z.B. 1000 ms fuer eine Sekunde Unterschied
                const nurKeineMeldungen = AktuelleMeldungenJSON[0].status_message_Lang.includes(MessageBeiKeinerSM);    // Pruefen, ob AktuelleMeldungenJSON nur eine Meldung mit "Derzeit keine Servicemeldungen" enthaelt
                if (nurKeineMeldungen) {
                    HistorischeMeldungenJSON_transfer.forEach(meldung => {        // Alle historischen Eintraege als aufgehoben markieren
                        if (meldung.SM_aufgehoben_seit === "aktiv") {
                            meldung.SM_aufgehoben_seit = func_get_datum();
                        }
                    });
                    return HistorischeMeldungenJSON_transfer;
                }
                HistorischeMeldungenJSON_transfer.forEach(historischeMeldung => {    // Iteriere ueber alle historischen Meldungen (aeussere Schleife)
                    if (historischeMeldung.SM_aufgehoben_seit !== "aktiv") return; // Wenn die historische Meldung bereits aufgehoben wurde, ueberspringen
                    let zugeordnet = false;
                    const historischeDatum = extractDate(historischeMeldung.datum_seit);
                    AktuelleMeldungenJSON.forEach(aktuelleMeldung => {        // Iteriere ueber alle aktuellen Meldungen (innere Schleife)
                        const aktuelleDatum = extractDate(aktuelleMeldung.datum_seit);
                        const gleicheGeraeteID = historischeMeldung.GeraeteId === aktuelleMeldung.GeraeteId;
                        const gleicheStatusMessage = historischeMeldung.status_message_Pure === aktuelleMeldung.status_message_Pure;
                        const zeitDifferenz = Math.abs(aktuelleDatum.getTime() - historischeDatum.getTime());            // Berechne den Zeitunterschied in Millisekunden
                        const gleicheOderNaheZeit = zeitDifferenz <= maxTimeDiffMs;            // Pruefe, ob die Zeitdifferenz innerhalb des erlaubten Bereichs liegt
                        if (gleicheGeraeteID && gleicheStatusMessage && gleicheOderNaheZeit) {
                            historischeMeldung.SM_aufgehoben_seit = "aktiv"; // Als nicht aufgehoben markieren
                            zugeordnet = true
                        }
                    });
                    if (!zugeordnet) {
                        historischeMeldung.SM_aufgehoben_seit = func_get_datum() // kein pendant gefunden, also muss die message obsolet sein
                    }
                });
                return HistorischeMeldungenJSON_transfer;
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion sortJason zum Sortieren des History Jason nach datum
            //-----------------------------------------------------------------------------------------------------
            function sortJason(parsedJson) {
                if (!Array.isArray(parsedJson)) { LOG("Die Eingabe fuer sortJason ist kein Array.", "Fehler", "sortJason", 2); return[] ;}
                const parseDate = (datum) => {
                    try {
                        const [date, time] = datum.split(' '); // Datum und Zeit trennen
                        const [day, month, year] = date.split('.'); // Datum aufteilen in Tag, Monat, Jahr
                        const [hours, minutes, seconds] = time.split(':'); // Zeit aufteilen in Stunden, Minuten, Sekunden
                        return new Date(`20${year}-${month}-${day}T${hours}:${minutes}:${seconds}`); // ISO-Format erstellen
                    } catch (error) {
                        LOG(`Fehler beim Parsen des Datums: ${datum}`, "Fehler", "sortJason", 1);
                        return null;  // Rueckgabe von null, wenn das Datum ungueltig ist
                    }
                };
                return parsedJson.sort((a, b) => {
                    const dateA = parseDate(a.datum_seit);
                    const dateB = parseDate(b.datum_seit);
                    if (dateA === null && dateB === null) return 0; // Keine aenderung bei ungueltigen Datumswerten
                    if (dateA === null) return 1; // Null-Werte ans Ende verschieben
                    if (dateB === null) return -1;
                    return dateB.getTime() - dateA.getTime(); // Neueste zuerst
                });
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion extractDatum // Hilfsfunktion zur Extraktion des Datums        // Beispieltext: "01.12.24 12:04:55 Uhr - SABOTAGE_ALARM ..."
            //-----------------------------------------------------------------------------------------------------
            function extractDate(text) {
                if (!text || typeof text !== "string") {
                    LOG(`extractDate Fehler: Ungueltiger Eingabetext: ${text}`, "Fehler", "extractDate", 2);
                    return new Date(0); // Fallback auf ein Default-Datum
                }
                const match = text.match(/^(\d{2})\.(\d{2})\.(\d{2}) (\d{2}):(\d{2}):(\d{2})/);    // Regulaerer Ausdruck, um Datum und Uhrzeit zu extrahieren
                if (!match) {
                    LOG(`extractDate Fehler: Kein gueltiges Datum gefunden: ${text}`, "Fehler", "extractDate", 2);
                    return new Date(0); // Fallback auf ein Default-Datum
                }
                const [_, day, month, year, hours, minutes, seconds] = match;    // Teile aus dem Match extrahieren
                const fullYear = year.length === 2 ? `20${year}` : year;    // Jahr vervollstaendigen, falls noetig
                const dateString = `${fullYear}-${month}-${day}T${hours}:${minutes}:${seconds}`;    // Erstellen des Datums im Format "YYYY-MM-DDTHH:mm:ss"
                const date = new Date(dateString);    // Umwandlung in ein Date-Objekt
                if (isNaN(date.getTime())) {    // ueberpruefen, ob das Datum gueltig ist
                    LOG(`extractDate Fehler: Ungueltiges Datum erzeugt: ${dateString}`, "Fehler", "extractDate", 2);
                    return new Date(0);
                }
                return date;
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion GenerateTEXT_Datenpunkte // Generiert TEXT format aus den JSON Arrays und speichert diese in die Datenpunkte
            //-----------------------------------------------------------------------------------------------------
            function GenerateTEXT_Datenpunkte() {
                LOG(`Routine wird ausgefuehrt`, "Ablauf", "GenerateTEXT_Datenpunkte", 2);
                if(!UpdateTEXT_Datenpunkte){return}; // wenn keine TEXT-Datenpunkte updates erfolgen sollen nicht ausfuehren
                let  SMAktuellMessageLangTEXT = [], SMHistMessageLangTEXT = [],SMAktuellMessageKurzTEXT = [];
                HistorischeMeldungenJSON.forEach(Meldung => {        // Iteriere durch HistorischeMeldungenJSON und extrahiere die status_message_Lang
                    SMHistMessageLangTEXT.push(Meldung.status_message_Lang + " - Meldung aufgehoben: " + Meldung.SM_aufgehoben_seit + "<br>");            // Status-Lang-Text extrahieren und ins Array pushen
                });
                setState(id_Text_Servicemeldung_History, SMHistMessageLangTEXT.join(''));        // Umwandlung des Arrays in einen String und Speichern im Datenpunkt id_Text_ServicemeldungLang
                AktuelleMeldungenJSON.forEach(Meldung => {        // Iteriere durch AktuelleMeldungenJSON und extrahiere die status_message_Pure und status_message_Lang
                    if (Meldung.status_message_Pure) {
                        SMAktuellMessageKurzTEXT.push(Meldung.datum_seit + " " + Meldung.status_message_Pure + "<br>"); // Status-Kurz-Text extrahieren und ins Array pushen
                    }
                    if (Meldung.status_message_Lang) {
                        SMAktuellMessageLangTEXT.push(Meldung.status_message_Lang + "<br>"); // Status-Lang-Text extrahieren und ins Array pushen
                    }
                });
                setState(id_Text_ServicemeldungKurz, SMAktuellMessageKurzTEXT.join(''));        // Umwandlung der Arrays in Strings und Speichern in den entsprechenden Datenpunkten
                setState(id_Text_ServicemeldungLang, SMAktuellMessageLangTEXT.join(''));
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion generateHtmlTable // Funktion, aus den JSON Tabellen ein HTML-Format erzeugt
            //-----------------------------------------------------------------------------------------------------
            function generateHtmlTable(data, HistOderAkt) {  
                if (!UpdateHTML_Datenpunkte) { return; }        // Keine Updates, wenn HTML-Datenpunkte deaktiviert sind
                const uniqueClass = "custom-table";    // Eindeutige Klasse für die Tabelle
                const cellPadding = '6px';
                // Baue die Tabelle mit einer eindeutigen Klasse auf
                let table = `
                    <table class="${uniqueClass}" style="
                        width: 100%;
                        border-collapse: collapse; /* Rahmen zusammenführen */
                        border-spacing: 0; /* Kein Abstand zwischen Zellen */
                    ">
                `;
                // Scoped Stile für die Tabelle
                table += `
                    <style>
                        .${uniqueClass} th, .${uniqueClass} td {
                            padding: ${cellPadding};
                            ${useBorderAll ? `border: ${borderWidth} solid ${borderColor};` : 'border: none;'} /* Innerer Rahmen basierend auf useBorderAll */
                        }
                        .${uniqueClass} thead th {
                            position: sticky;
                            top: 0;
                            background-color: ${headerColor};
                            color: ${headerTextColor};
                            z-index: 1;
                        }
                    </style>
                `;
                // Kopfzeile
                table += `
                    <thead>
                    <tr>
                        <th style="text-align: left;">Datum_Seit</th>
                        <th style="text-align: left;">Meldungsart</th>
                        <th style="text-align: left;">Name</th>
                        <th style="text-align: left;">GeraeteId</th>
                        <th style="text-align: left;">Status</th>
                        <th style="text-align: left;">Batterie</th>
                        <th style="text-align: left;">SM_aufgehoben_seit</th>
                    </tr>
                    </thead>
                `;
                // Tabellenkörper
                table += '<tbody>';
                data.forEach((item, index) => {
                    const rowColor = index % 2 === 0 ? evenRowColor : oddRowColor; // Farben der Zeilen
                    const textColor = index % 2 === 0 ? evenRowTextColor : oddRowTextColor; // Textfarbe
                    table += `
                    <tr style="background-color: ${rowColor}; color: ${textColor};">
                        <td>${item.datum_seit}</td>
                        <td>${item.meldungsart}</td>
                        <td>${item.common_name}</td>
                        <td>${item.GeraeteId}</td>
                        <td>${item.status_message_Pure}</td>
                        <td>${item.batterie_bezeichnung}</td>
                        <td>${item.SM_aufgehoben_seit}</td>
                    </tr>
                    `;
                });
                table += '</tbody>';
                table += '</table>';
                // Zustand setzen je nach HistOderAkt
                if (HistOderAkt === "AKT") {
                    setState(id_HTML_Servicemeldung_Aktuell, table);  
                } else {
                    setState(id_HTML_Servicemeldung_History, table); 
                }
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion Refresh_History loescht die Historie - Ausloeser entweder subcription oder schedule
            //-----------------------------------------------------------------------------------------------------
            function Refresh_History() {
                let jsonString = getState(id_JSON_Servicemeldung_Historie).val || '[]'; // zunaechst die JSON Eintraege
                HistorischeMeldungenJSON = JSON.parse(jsonString);
                HistorischeMeldungenJSON = HistorischeMeldungenJSON.filter(meldung => meldung.SM_aufgehoben_seit === "aktiv");    // Filtern der Meldungen, um nur die zu behalten, die den Wert "aktiv" in "SM_aufgehoben_seit" haben
                let updatedJsonString = JSON.stringify(HistorischeMeldungenJSON);    // Speichern der gefilterten historischen Meldungen zurueck in den Datenpunkt
                setState(id_JSON_Servicemeldung_Historie, updatedJsonString);
                Check_All("init")
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion schreibt einen Logeintrag in das Filesystem und auch in das interne Log-System
            //-----------------------------------------------------------------------------------------------------
            function writelog(Name, GeraeteId, SMType, SMStatus, SMStatus_Text) {
                if (!SMProtokoll) return;
                const fs = require('fs');                     // enable write fuer externes log
                const logdate = formatDate(new Date(), "TT.MM.JJJJ");
                const logtime = formatDate(new Date(), "SS:mm:ss");
                const logEntry = `${logdate} ;${logtime} ;${Name} ;${GeraeteId} ; ${SMType} ;${SMStatus} ;${SMStatus_Text}\n`;
                const headerLine = "Datum;Uhrzeit;Name;ID-Name;Meldungssart;Status;Servicemeldung\n";
                fs.readFile(PathSMLog, 'utf8', function(err, data) {
                    if (!err) {
                        fs.appendFileSync(PathSMLog, logEntry, 'utf8');
                    } else {
                        LOG(`Logfile nicht gefunden - wird angelegt`, "Ablauf", "writelog", 0);
                        fs.writeFileSync(PathSMLog, headerLine + logEntry, 'utf8');
                    }
                });
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion schreibt die Syste-Log-Eintraege in ein externes CSV - File
            //-----------------------------------------------------------------------------------------------------
            function LOG(Message,Kategorie,Routine, Level, type) {
                if(type !== "warn" && type !== "error") {type = "info"}
                if (!SystemLog && debugLevel >= Level) { log(Message+" Routine:"+Routine,type); return;}     // Wenn SystemLog false ist und der Debug-Level hoeher oder gleich dem uebergebenen Level, schreibe normalen Logeintrag
                if  (Level === 0) { log(Message+" Routine:"+Routine,type);} // bei level 0 soll auf jeden fall auch in das normale log geschrieben werden
                if (SystemLog && debugLevel >= Level) {         // Wenn SystemLog true ist und der Debug-Level hoeher oder gleich dem uebergebenen Level
                    const fs = require('fs');
                    const now = new Date();
                    const logdate = `${now.getDate().toString().padStart(2, '0')}.${(now.getMonth() + 1).toString().padStart(2, '0')}.${now.getFullYear()}`;
                    const logtime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}:${now.getMilliseconds().toString().padStart(3, '0')}`;
                    const logEntry = `${logdate};${logtime};${Level};${Kategorie};${Routine};${Message}\n`;
                    const headerLine = "Datum;Uhrzeit;Debug Level;Kategorie;Routine;Log-Message\n";  
                    const logFilePath = PathSystemLog || './defaultLog.csv'; // falls PathSystemLog nicht definiert ist, standardmaessigen Pfad verwenden
                    try {
                        if (!fs.existsSync(logFilePath)) {
                            log(`Routine:LOG - Logfile nicht gefunden - wird angelegt`,"info")
                            fs.writeFileSync(logFilePath, headerLine + logEntry, 'utf8');  // Datei erstellen und Header hinzufuegen
                        } else {
                            fs.appendFileSync(logFilePath, logEntry, 'utf8'); // Eintrag zum Logfile hinzufuegen
                        }
                    } catch (err) {
                        log(`Routine:LOG - Fehler beim Schreiben in das Logfile: ${err}`, "error"); // Fehler beim Schreiben
                    }
                }
            }
            //-----------------------------------------------------------------------------------------------------
            // Funktion Create States
            //-----------------------------------------------------------------------------------------------------
            async function CreateStates(callback) {
                LOG(`Routine wird ausgefuehrt`, "Ablauf", "CreateStates", 2);
                try {
                    if(UpdateTEXT_Datenpunkte) {    // nur wenn Text-Datenpunkte auch gewuenscht sind
                        await createStateAsync(id_Text_ServicemeldungLang, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen LangText', desc: 'LangText Servicemeldung' });
                        await createStateAsync(id_Text_ServicemeldungKurz, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen KurzText', desc: 'KurzText Servicemeldung' });
                        await createStateAsync(id_Text_Servicemeldung_History, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen History', desc: 'History Servicemeldung' });
                    }
                    if(UpdateHTML_Datenpunkte) {    // nur wenn Text-Datenpunkte auch gewuenscht sind
                        await createStateAsync(id_HTML_Servicemeldung_Aktuell, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen HTML-Text', desc: 'HTML-Text Servicemeldung' });
                        await createStateAsync(id_HTML_Servicemeldung_History, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen HTML-History', desc: 'HTML-History Servicemeldung' });
                    }
                    await createStateAsync(id_JSON_Servicemeldung_Aktuell, "", { read: true, write: true, type: 'string', name: 'Aktuelle Servicemeldungen als JSON', desc: 'Vergangene Servicemeldung JSON' });
                    await createStateAsync(id_JSON_Servicemeldung_Historie, "", { read: true, write: true, type: 'string',name: 'Servicemeldungen History', desc: 'History Servicemeldung' });
                    await createStateAsync(id_Button_Refresh_Historie, false, { read: true, write: true, type: 'boolean', name: 'Refresh Button History', desc: 'Loescht die Historie Servicemeldungem wenn true' });
                    for (const { alarmtype, count, Datenpunkt } of Zaehler) {               // Schleife ueber das Zaehler-Array und anlegen der States
                        await createStateAsync(Datenpunkt, count, {read: true, write: true,type: 'number',name: `Servicemeldungen Anzahl ${alarmtype}`,desc: `Anzahl ${alarmtype} Servicemeldungen`});
                    }
                if (callback) await callback();         // Aufruf des Callbacks nach Abschluss aller Operationen
                    } catch (error) {
                    LOG(`Kategorie:WARN; Routine:CreateStates; Routine Create States - Fehler beim Erstellen der Zustaende: ${error}`, 0, "warn");
                }
                if (!UpdateTEXT_Datenpunkte) {
                    if (existsState(id_Text_ServicemeldungLang)) { deleteState(id_Text_ServicemeldungLang); }
                    if (existsState(id_Text_ServicemeldungKurz)) { deleteState(id_Text_ServicemeldungKurz); }
                    if (existsState(id_Text_Servicemeldung_History)) { deleteState(id_Text_Servicemeldung_History); }
                }
                if(!UpdateHTML_Datenpunkte) {    // nur wenn Text-Datenpunkte auch gewuenscht sind
                    if (existsState(id_HTML_Servicemeldung_Aktuell)) { deleteState(id_HTML_Servicemeldung_Aktuell); }
                    if (existsState(id_HTML_Servicemeldung_History)) { deleteState(id_HTML_Servicemeldung_History); }
                }
            }
            
            L Offline
            L Offline
            looxer01
            schrieb am zuletzt editiert von
            #197

            @edis77
            ok, kein Problem. Ich erstelle eine Version, die aber dann schon final ist.
            Ich bin sicher, dass das beschriebene Problem damit behoben wird.

            kann aber Morgen werden.

            vG Looxer

            1 Antwort Letzte Antwort
            0
            • L Offline
              L Offline
              looxer01
              schrieb am zuletzt editiert von looxer01
              #198

              Hi Version 3.21 ist online

              • ACHTUNG die JSON fuer aktuelle und historische Servicemeldungen wurde um ein Feld erweitert. Bitte sorgt dafür, dass ihr keine aktuellen STICKY-Meldungen habt, wenn diese Script-Version aktiviert wird (gibt dann Fehlermeldungen). Ansonsten kann die Version ohne Probleme aktiviert werden.
              • Tabelle "instanceIds" um feld "Messaging" erweitert(Ausschalten von Nachrichten Adapter/Instanz Kombi). Somit kann z.B. für die Gruppenmeldungen generell ein Nachrichtenversand verhindert werden
              • DataPoint Filter für selektor hinzugefuegt. Der Filter laesst sich flexibel mit wildcards in einer Tabelle einstellen. Notwendig wurde dieser für den HM-AccessPoint, um Datenpunkte mit Alarmtypes auszuschliessen, die aber nur für Gruppen relevant waren und nicht für einzelne Geräte

              vG Looxer

              @Edis77
              Diese Version sollte dein Problem beheben.

              Eduard77E 1 Antwort Letzte Antwort
              1
              • L looxer01

                Hi Version 3.21 ist online

                • ACHTUNG die JSON fuer aktuelle und historische Servicemeldungen wurde um ein Feld erweitert. Bitte sorgt dafür, dass ihr keine aktuellen STICKY-Meldungen habt, wenn diese Script-Version aktiviert wird (gibt dann Fehlermeldungen). Ansonsten kann die Version ohne Probleme aktiviert werden.
                • Tabelle "instanceIds" um feld "Messaging" erweitert(Ausschalten von Nachrichten Adapter/Instanz Kombi). Somit kann z.B. für die Gruppenmeldungen generell ein Nachrichtenversand verhindert werden
                • DataPoint Filter für selektor hinzugefuegt. Der Filter laesst sich flexibel mit wildcards in einer Tabelle einstellen. Notwendig wurde dieser für den HM-AccessPoint, um Datenpunkte mit Alarmtypes auszuschliessen, die aber nur für Gruppen relevant waren und nicht für einzelne Geräte

                vG Looxer

                @Edis77
                Diese Version sollte dein Problem beheben.

                Eduard77E Online
                Eduard77E Online
                Eduard77
                schrieb am zuletzt editiert von
                #199

                @looxer01
                Danke für das Anpassung.
                Skript läuft und keine Fehler im LOG.

                1 Antwort Letzte Antwort
                1
                • L Offline
                  L Offline
                  looxer01
                  schrieb am zuletzt editiert von looxer01
                  #200

                  Hi,
                  Version 3.30 ist online mit der Möglichkeit der Sprachausgabe.

                  Ich schreibe gerade für mich ein Alarmanlagenscript. Sprachausgabe ist eine von vielen Anforderungen.
                  Dabei nutze ich viele Elemente des Servicemeldungsscripts. Daher habe ich die Sprachausgabe dem Servicemeldungsscript jetzt hinzugefügt.

                  Die Sprachausgabe erfolgt über den "sayit" Adapter.
                  Eine Uhrzeit von bis kann konfiguriert werden, damit es Nachts nicht zu Störungen kommt.
                  Ich meine, dass die Sprachausgabe insbesondere bei Sabotage Sinn macht.
                  Anmerkung: im sayit Adapter kann je instanz ein anderes ausgabegerät festgelegt werden.
                  Im servicemeldungsscript können mehrere Instanzen konfiguriert werden - das passt also.

                  Die Konfig findet ihr in der Tabelle messenger scope und die Uhrzeiten für die Sprachausgaben darunter.

                  noch ein Hinweis:
                  im SayIt Adapter kann man "Browser" als Ausgabegerät konfigurieren. Damit können also eventuelle Tablets z.B. mit dem FullyKioskBrowser als Ausgabegeräte genutzt werden

                  vG Looxer

                  1 Antwort Letzte Antwort
                  0
                  • L looxer01

                    Hi,
                    Das vorliegende Script logged Servicemeldungen von der CCU/AccessPoint/HCU-

                    Für welche Umgebungen eignet sich das Skript?

                    • CCU (bzw. Raspberry Pi-Ersatz)
                    • CCU FUNK
                    • CCU Wired
                    • HMIP Access Point (Achtung: „GeraeteIDTrigger“ muss auf „true“ gesetzt sein)
                    • HMIP HCU Cloud (Achtung: „GeraeteIDTrigger“ muss auf „true“ gesetzt sein)

                    Es handelt sich dabei nicht um ein „entweder-oder“ – die Instanzen werden parallel überwacht.

                    Was kann das Skript?

                    • Überprüfung der Servicemeldungen: Das Skript prüft, ob Servicemeldungen vorliegen.
                    • Speicherung der Servicemeldungen: Servicemeldungen werden in Lang- und Kurztexten (Text, JSON, HTML) gespeichert.
                    • Zählung von Servicemeldungen: Es werden Zählungen nach Meldungsarten und insgesamt gespeichert.
                    • Erstellung der erforderlichen Datenpunkte: Das Skript legt automatisch die notwendigen Datenpunkte an.
                    • Reaktion auf Geräteänderungen: Es reagiert entweder auf Veränderungen auf Geräteebene (viele Subscriptions – z.B. 298 bei 80 Geräten) oder, standardmäßig, auf Veränderungen der Anzahl der in der CCU gemeldeten Servicemeldungen (1 Subscription)
                    • Historie: Es wird eine Historie geführt, die auch zeigt, wann Servicemeldungen wieder aufgehoben wurden.
                    • HMIP Access Point/HCU Support: Wenn der Access Point genutzt wird, muss auf die Geräte-ID getriggert werden (da der REGA-Datenpunkt fehlt).
                    • Externe Log-Datei: Es kann ein externes Log im Excel-Format fortgeschrieben werden. Hierin werden alle Servicemeldungen langfristig fortgeschrieben
                    • Heizungsgruppenmeldungen: Heizungsgruppenmeldungen sind standardmäßig deaktiviert. Sie können jedoch bei Bedarf aktiviert werden.
                    • Servicemeldungen Bestätigung: Das Skript bestätigt auf Wunsch CCU Servicemeldungen.
                    • Flexible Messaging Services: Es können email', 'whatsApp', 'Signal','Telegram', 'Pushover', 'Pushsafer oder auch SPRACHE zum versenden von Servicemeldungen eingestellt werden. Das Versenden kann per MessageType vereinbart werden.
                    • Flexibles Tabellenwerk: Anpassungen für z.B. neue MessageTypes von Servicemeldungen bei neuen Geräten können ohne Programmierung im Tabellenwerk konfiguriert werden

                    Einfache Nutzung: Das Skript ist sofort lauffähig, vorausgesetzt, die Instanzen (CCU etc.) sind korrekt eingerichtet.
                    Das Skript arbeitet in der aktuellen Version stabil und zuverlässig.

                    Zusätzliche Hinweise:

                    In der Standardkonfiguration des Skripts werden 2 Subscriptions und 1 Schedule erstellt.
                    Ich verwende das Skript mit einer Subscription auf Ebene Einzel-ID (Einstellung „GeraeteIDTrigger“). Bei dieser Konfiguration wird eine Warnung im Log angezeigt, wenn mehr als 100 Subscriptions von dem Servicemeldungs-Skript erstellt werden.
                    Diese Grenze kann in den „Instanzen JavaScript“-Einstellungen angepasst werden. Für diesen Anwendungsfall sind keine Performance-Probleme zu erwarten.
                    Channel Selector: Das Skript verwendet statische Channel-Selector. Das bedeutet, dass Änderungen an den Datenstrukturen durch die CCU (z.B. das Löschen oder Hinzufügen von Geräten) nicht bekannt sind, solange das Skript nicht neu gestartet wird.
                    Falls Geräte hinzugefügt oder gelöscht werden, muss das Skript neu gestartet werden.

                    und hier das Script:
                    Servicemeldungen_Vol2_3-32.txt

                    Dazu stelle ich zwei Views für VIS zur Verfügung:
                    Screenshot 2025-01-24 211730.png
                    Den View gibt es für das JSON Format also auch für das HTML Format. Beide sehen weitestgehend identisch aus

                    1. JSON View: Diese View ist für die Darstellung der JSON-Datenpunkte gedacht. Die Datenpunkte sind auf „javascript.0....“ ausgerichtet, was ggf. angepasst werden muss. Die View kann in VIS über „view importieren“ geladen werden. Der Inventwo-Adapter muss installiert sein. Falls du VIS2 nutzt, empfiehlt sich momentan die HTML-Variante.
                      View_SM_JSON.txt

                    2. HTML View: Diese View zeigt die HTML-Datenpunkte an. Auch hier müssen die Datenpunkte ggf. angepasst werden. Die View kann ebenfalls in VIS über „view importieren“ geladen werden. Die verwendeten Widgets sind klassische ioBroker-Widgets und funktionieren sowohl in VIS1 als auch in VIS2.
                      View_SM_HTML.txt

                    Die Views enthalten einen Push-Button. Wenn der gedrückt wird, wird die Meldungshistorie ohne Nachfrage gelöscht

                    Da das Skript bei niedrigem Batteriestand automatisch die erforderlichen Batterien überprüft, müssen die Geräte mit den Batterien in eine Liste eingetragen werden. Mit dem folgenden Script kannst du überprüfen, ob die Liste vollständig ist. Alle Geräte mit unbekannten Batterien werden aufgelistet. Du kannst mir diese Liste dann zur Verfügung stellen, und ich pflege sie gerne ein.
                    hier das Checkscript:
                    Batterie-Check-1-1.txt

                    Zusätzlich stelle ich ein Tool zur Verfügung, das alle ioBroker-Subscriptions auflistet. Dies steht nicht direkt im Zusammenhang mit dem Servicemeldungsskript, ist aber hilfreich, um transparent zu sehen, was insgesamt abonniert wurde. Über eine Variable kann die Liste auch auf ein bestimmtes Skript eingeschränkt werden. Das Ergebnis kann in eine externe Datei geschrieben werden.
                    Hier das script
                    ListSubscriptions_1.0

                    rantanplanR Online
                    rantanplanR Online
                    rantanplan
                    schrieb am zuletzt editiert von
                    #201

                    @looxer01 Hallo

                    Ich habe hier eine Servicemeldung die Dein Script nicht mitbekommt.

                    Bei dem Gerät handelt es sich um ein HM-Classic "HM-CC-RT-DN".
                    Die Servicemeldung lautet: "Leere Batterie".

                    In Deinem Script habe ich mal folgendes hinzugefügt.

                    //ErrorMessages fuer HM-Classic Geraet (Heizung) - Sonderfaelle
                    const faultMessages = {
                        'HM-CC-RT-DN': {
                            0: 'keine Stoerung',
                            1: 'Ventil blockiert',
                            2: 'Einstellbereich Ventil zu gross',
                            3: 'Einstellbereich Ventil zu klein',
                            4: 'Kommunikationsfehler',
                            6: 'Spannung Batterien/Akkus gering',
                            7: 'Fehlstellung Ventil',
                    	8: 'Leere Batterie'       //von mir hinzugefügt !!!
                        }
                    };
                    

                    Hat aber nichts gebracht. (Vielleicht weil die Meldung schon vorhanden ist?)

                    Grüße

                    Rantanplan

                    CCU3 / MS Server 2019(VM) / Scripten mit Blockly

                    L 1 Antwort Letzte Antwort
                    0
                    • rantanplanR rantanplan

                      @looxer01 Hallo

                      Ich habe hier eine Servicemeldung die Dein Script nicht mitbekommt.

                      Bei dem Gerät handelt es sich um ein HM-Classic "HM-CC-RT-DN".
                      Die Servicemeldung lautet: "Leere Batterie".

                      In Deinem Script habe ich mal folgendes hinzugefügt.

                      //ErrorMessages fuer HM-Classic Geraet (Heizung) - Sonderfaelle
                      const faultMessages = {
                          'HM-CC-RT-DN': {
                              0: 'keine Stoerung',
                              1: 'Ventil blockiert',
                              2: 'Einstellbereich Ventil zu gross',
                              3: 'Einstellbereich Ventil zu klein',
                              4: 'Kommunikationsfehler',
                              6: 'Spannung Batterien/Akkus gering',
                              7: 'Fehlstellung Ventil',
                      	8: 'Leere Batterie'       //von mir hinzugefügt !!!
                          }
                      };
                      

                      Hat aber nichts gebracht. (Vielleicht weil die Meldung schon vorhanden ist?)

                      Grüße

                      L Offline
                      L Offline
                      looxer01
                      schrieb am zuletzt editiert von looxer01
                      #202

                      @rantanplan
                      Hi,
                      kannst du mal die Objektliste für das Gerät checken ?
                      In den Objektdaten stehen ja die möglichen Status. (also mindestens die 0 - 7)
                      Ich muss halt wissen welcher Datenpunkt den Status für die leere Batterie mit welcher Ausprägung enthält.
                      (leider besitze ich kein classic mehr)

                      Kannst du das Protokoll laufen lassen mit debug level 2 und posten ?
                      muss der Zeitpunkt sein bei dem die CCU die Servicemeldung lowbat erzeugt.

                      vG Looxer

                      Edit: der relevante Datenpunkt müsste FAULT_REPORTING sein, wenn es der ist, den du im Blick hast.
                      Allerdings war ich der Meinung, dass es auch einen "lowbat"-Datenpunkt geben müsste.

                      E 1 Antwort Letzte Antwort
                      0
                      • L looxer01

                        @rantanplan
                        Hi,
                        kannst du mal die Objektliste für das Gerät checken ?
                        In den Objektdaten stehen ja die möglichen Status. (also mindestens die 0 - 7)
                        Ich muss halt wissen welcher Datenpunkt den Status für die leere Batterie mit welcher Ausprägung enthält.
                        (leider besitze ich kein classic mehr)

                        Kannst du das Protokoll laufen lassen mit debug level 2 und posten ?
                        muss der Zeitpunkt sein bei dem die CCU die Servicemeldung lowbat erzeugt.

                        vG Looxer

                        Edit: der relevante Datenpunkt müsste FAULT_REPORTING sein, wenn es der ist, den du im Blick hast.
                        Allerdings war ich der Meinung, dass es auch einen "lowbat"-Datenpunkt geben müsste.

                        E Online
                        E Online
                        emil70
                        schrieb am zuletzt editiert von emil70
                        #203

                        @looxer01

                        Der LOWBAT Datenpunkt sieht so aus

                        {
                          "type": "state",
                          "common": {
                            "name": "Heizung Büro:0.LOWBAT",
                            "role": "indicator.lowbat",
                            "def": false,
                            "type": "boolean",
                            "read": true,
                            "write": false
                          },
                          "native": {
                            "DEFAULT": false,
                            "FLAGS": 9,
                            "ID": "LOWBAT",
                            "MAX": true,
                            "MIN": false,
                            "OPERATIONS": 5,
                            "TAB_ORDER": 3,
                            "TYPE": "BOOL",
                            "UNIT": ""
                          },
                          "from": "system.adapter.hm-rega.0",
                          "user": "system.user.admin",
                          "ts": 1733764617734,
                          "_id": "hm-rpc.0.NEQ0871182.0.LOWBAT",
                          "acl": {
                            "object": 1636,
                            "state": 1636,
                            "owner": "system.user.admin",
                            "ownerGroup": "system.group.administrator"
                          }
                        }
                        

                        der LOWBAT_ALARM Datenpunkt so

                        {
                          "type": "state",
                          "common": {
                            "name": "Heizung Büro:0.LOWBAT_ALARM",
                            "type": "number",
                            "role": "indicator.alarm",
                            "read": true,
                            "write": true,
                            "def": 0,
                            "states": {
                              "0": "NO ALARM",
                              "1": "ALARM",
                              "2": "ACKNOWLEDGED"
                            }
                          },
                          "native": {
                            "Name": "Heizung Büro:0.LOWBAT_ALARM",
                            "TypeName": "ALARM",
                            "DP": "16869"
                          },
                          "from": "system.adapter.hm-rega.0",
                          "user": "system.user.admin",
                          "ts": 1733806812677,
                          "_id": "hm-rpc.0.NEQ0871182.0.LOWBAT_ALARM",
                          "acl": {
                            "object": 1636,
                            "state": 1636,
                            "owner": "system.user.admin",
                            "ownerGroup": "system.group.administrator"
                          }
                        }
                        

                        der BATTERY_STATE Datenpunkt so

                        {
                          "type": "state",
                          "common": {
                            "name": "Heizung Büro.BATTERY_STATE",
                            "role": "value.voltage",
                            "def": 0,
                            "type": "number",
                            "read": true,
                            "write": false,
                            "min": 1.5,
                            "max": 4.6,
                            "unit": "V"
                          },
                          "native": {
                            "CONTROL": "NONE",
                            "DEFAULT": 0,
                            "FLAGS": 1,
                            "ID": "BATTERY_STATE",
                            "MAX": 4.6,
                            "MIN": 1.5,
                            "OPERATIONS": 5,
                            "TAB_ORDER": 2,
                            "TYPE": "FLOAT",
                            "UNIT": "V"
                          },
                          "from": "system.adapter.hm-rega.0",
                          "user": "system.user.admin",
                          "ts": 1733764617915,
                          "_id": "hm-rpc.0.NEQ0871182.4.BATTERY_STATE",
                          "acl": {
                            "object": 1636,
                            "state": 1636,
                            "owner": "system.user.admin",
                            "ownerGroup": "system.group.administrator"
                          }
                        }
                        

                        Screenshot 2025-03-19 at 12-33-17 objects - f5529c8310eb.png
                        Hoffe das ist das was du brauchst

                        gruss emil70

                        iobroker(V9) und Pi-hole läuft über docker (js-controller 6.0.11 und admin v7.1.15) auf einem synology DS918+ mit DSM 7.1.1-42962 Update 6

                        L 1 Antwort Letzte Antwort
                        1
                        • E emil70

                          @looxer01

                          Der LOWBAT Datenpunkt sieht so aus

                          {
                            "type": "state",
                            "common": {
                              "name": "Heizung Büro:0.LOWBAT",
                              "role": "indicator.lowbat",
                              "def": false,
                              "type": "boolean",
                              "read": true,
                              "write": false
                            },
                            "native": {
                              "DEFAULT": false,
                              "FLAGS": 9,
                              "ID": "LOWBAT",
                              "MAX": true,
                              "MIN": false,
                              "OPERATIONS": 5,
                              "TAB_ORDER": 3,
                              "TYPE": "BOOL",
                              "UNIT": ""
                            },
                            "from": "system.adapter.hm-rega.0",
                            "user": "system.user.admin",
                            "ts": 1733764617734,
                            "_id": "hm-rpc.0.NEQ0871182.0.LOWBAT",
                            "acl": {
                              "object": 1636,
                              "state": 1636,
                              "owner": "system.user.admin",
                              "ownerGroup": "system.group.administrator"
                            }
                          }
                          

                          der LOWBAT_ALARM Datenpunkt so

                          {
                            "type": "state",
                            "common": {
                              "name": "Heizung Büro:0.LOWBAT_ALARM",
                              "type": "number",
                              "role": "indicator.alarm",
                              "read": true,
                              "write": true,
                              "def": 0,
                              "states": {
                                "0": "NO ALARM",
                                "1": "ALARM",
                                "2": "ACKNOWLEDGED"
                              }
                            },
                            "native": {
                              "Name": "Heizung Büro:0.LOWBAT_ALARM",
                              "TypeName": "ALARM",
                              "DP": "16869"
                            },
                            "from": "system.adapter.hm-rega.0",
                            "user": "system.user.admin",
                            "ts": 1733806812677,
                            "_id": "hm-rpc.0.NEQ0871182.0.LOWBAT_ALARM",
                            "acl": {
                              "object": 1636,
                              "state": 1636,
                              "owner": "system.user.admin",
                              "ownerGroup": "system.group.administrator"
                            }
                          }
                          

                          der BATTERY_STATE Datenpunkt so

                          {
                            "type": "state",
                            "common": {
                              "name": "Heizung Büro.BATTERY_STATE",
                              "role": "value.voltage",
                              "def": 0,
                              "type": "number",
                              "read": true,
                              "write": false,
                              "min": 1.5,
                              "max": 4.6,
                              "unit": "V"
                            },
                            "native": {
                              "CONTROL": "NONE",
                              "DEFAULT": 0,
                              "FLAGS": 1,
                              "ID": "BATTERY_STATE",
                              "MAX": 4.6,
                              "MIN": 1.5,
                              "OPERATIONS": 5,
                              "TAB_ORDER": 2,
                              "TYPE": "FLOAT",
                              "UNIT": "V"
                            },
                            "from": "system.adapter.hm-rega.0",
                            "user": "system.user.admin",
                            "ts": 1733764617915,
                            "_id": "hm-rpc.0.NEQ0871182.4.BATTERY_STATE",
                            "acl": {
                              "object": 1636,
                              "state": 1636,
                              "owner": "system.user.admin",
                              "ownerGroup": "system.group.administrator"
                            }
                          }
                          

                          Screenshot 2025-03-19 at 12-33-17 objects - f5529c8310eb.png
                          Hoffe das ist das was du brauchst

                          L Offline
                          L Offline
                          looxer01
                          schrieb am zuletzt editiert von looxer01
                          #204

                          @emil70
                          @rantanplan

                          vielen Dank, das ist es was ich brauchte
                          Allerdings stelle ich fest, dass es sich hier um einen Standardfall handelt.
                          LOWBAT_ALARM wird mit 1 gesetzt womit das Gerät selektiert werden sollte, wie andere classic Geräte auch.

                          Die erste Frage ist, ob das Script überhaupt ausgelöst wird. Das hängt mit der GeraeteIDTrigger einstellung zusammen
                          Entweder weil der Datenpunkt hm-rega.0.maintenance verändert wurde (bei GeraeteIDTrigger = false) oder
                          weil die 1 im Datenpunkt LOWBAT_ALARM gesetzt wurde (bei GeraeteIDTrigger = true).
                          Wenn es auslöst, dann hilft ein Protokoll. Das kann ja auch über längere Zeit aktiv gesetzt werden und als Datei abgespeichert werden.
                          So stört es dann nicht im Protokoll und du brauchst nicht danach im Protokoll zu suchen.

                          vG Looxer

                          rantanplanR 1 Antwort Letzte Antwort
                          0
                          • L looxer01

                            @emil70
                            @rantanplan

                            vielen Dank, das ist es was ich brauchte
                            Allerdings stelle ich fest, dass es sich hier um einen Standardfall handelt.
                            LOWBAT_ALARM wird mit 1 gesetzt womit das Gerät selektiert werden sollte, wie andere classic Geräte auch.

                            Die erste Frage ist, ob das Script überhaupt ausgelöst wird. Das hängt mit der GeraeteIDTrigger einstellung zusammen
                            Entweder weil der Datenpunkt hm-rega.0.maintenance verändert wurde (bei GeraeteIDTrigger = false) oder
                            weil die 1 im Datenpunkt LOWBAT_ALARM gesetzt wurde (bei GeraeteIDTrigger = true).
                            Wenn es auslöst, dann hilft ein Protokoll. Das kann ja auch über längere Zeit aktiv gesetzt werden und als Datei abgespeichert werden.
                            So stört es dann nicht im Protokoll und du brauchst nicht danach im Protokoll zu suchen.

                            vG Looxer

                            rantanplanR Online
                            rantanplanR Online
                            rantanplan
                            schrieb am zuletzt editiert von
                            #205

                            @looxer01
                            Ich habe mal im Script debugLevel auf 2 gesetzt.
                            GeraeteId steht bei mir auf false.

                            Dann habe ich neue Batterien eingesetzt.
                            Meldung auf der CCU3 weg.

                            Dann wieder die alten Batterien eingesetzt.
                            Systemmeldung auf der CCU3 "Leere Batterie"

                            Hier das LOG.
                            ServicemeldungenSystemLog.csv

                            Aktuell sind noch die leeren Batterien drin.
                            Sieht so aus:


                            Bild_1.png

                            Object Daten WZ-VD-Regal:4.FAULT_REPORTING

                            {
                             "type": "state",
                             "common": {
                               "def": 0,
                               "type": "number",
                               "read": true,
                               "write": false,
                               "min": 0,
                               "max": 7,
                               "states": {
                                 "0": "NO_FAULT",
                                 "1": "VALVE_TIGHT",
                                 "2": "ADJUSTING_RANGE_TOO_LARGE",
                                 "3": "ADJUSTING_RANGE_TOO_SMALL",
                                 "4": "COMMUNICATION_ERROR",
                                 "5": "",
                                 "6": "LOWBAT",
                                 "7": "VALVE_ERROR_POSITION"
                               },
                               "role": "indicator",
                               "name": "WZ-VD-Regal:4.FAULT_REPORTING"
                             },
                             "native": {
                               "CONTROL": "NONE",
                               "DEFAULT": 0,
                               "FLAGS": 9,
                               "ID": "FAULT_REPORTING",
                               "MAX": 7,
                               "MIN": 0,
                               "OPERATIONS": 5,
                               "TAB_ORDER": 1,
                               "TYPE": "ENUM",
                               "UNIT": "",
                               "VALUE_LIST": [
                                 "NO_FAULT",
                                 "VALVE_TIGHT",
                                 "ADJUSTING_RANGE_TOO_LARGE",
                                 "ADJUSTING_RANGE_TOO_SMALL",
                                 "COMMUNICATION_ERROR",
                                 "",
                                 "LOWBAT",
                                 "VALVE_ERROR_POSITION"
                               ]
                             },
                             "from": "system.adapter.hm-rega.0",
                             "user": "system.user.admin",
                             "ts": 1740918410792,
                             "_id": "hm-rpc.0.OEQ2637929.4.FAULT_REPORTING",
                             "acl": {
                               "object": 1636,
                               "state": 1636,
                               "owner": "system.user.admin",
                               "ownerGroup": "system.group.administrator"
                             }
                            }
                            

                            Object Daten WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM

                            {
                             "type": "state",
                             "common": {
                               "name": "WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM",
                               "type": "number",
                               "role": "indicator.alarm",
                               "read": true,
                               "write": true,
                               "def": 0,
                               "states": {
                                 "0": "NO ALARM",
                                 "1": "ALARM",
                                 "2": "ACKNOWLEDGED"
                               }
                             },
                             "native": {
                               "Name": "WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM",
                               "TypeName": "ALARM",
                               "DP": "16978"
                             },
                             "from": "system.adapter.hm-rega.0",
                             "user": "system.user.admin",
                             "ts": 1548955628248,
                             "_id": "hm-rpc.0.OEQ2637929.4.FAULT_REPORTING-6_ALARM",
                             "acl": {
                               "object": 1636,
                               "state": 1636,
                               "owner": "system.user.admin",
                               "ownerGroup": "system.group.administrator"
                             }
                            }
                            

                            Was kann ich noch tun?

                            Grüße

                            Rantanplan

                            CCU3 / MS Server 2019(VM) / Scripten mit Blockly

                            L 3 Antworten Letzte Antwort
                            0
                            • rantanplanR rantanplan

                              @looxer01
                              Ich habe mal im Script debugLevel auf 2 gesetzt.
                              GeraeteId steht bei mir auf false.

                              Dann habe ich neue Batterien eingesetzt.
                              Meldung auf der CCU3 weg.

                              Dann wieder die alten Batterien eingesetzt.
                              Systemmeldung auf der CCU3 "Leere Batterie"

                              Hier das LOG.
                              ServicemeldungenSystemLog.csv

                              Aktuell sind noch die leeren Batterien drin.
                              Sieht so aus:


                              Bild_1.png

                              Object Daten WZ-VD-Regal:4.FAULT_REPORTING

                              {
                               "type": "state",
                               "common": {
                                 "def": 0,
                                 "type": "number",
                                 "read": true,
                                 "write": false,
                                 "min": 0,
                                 "max": 7,
                                 "states": {
                                   "0": "NO_FAULT",
                                   "1": "VALVE_TIGHT",
                                   "2": "ADJUSTING_RANGE_TOO_LARGE",
                                   "3": "ADJUSTING_RANGE_TOO_SMALL",
                                   "4": "COMMUNICATION_ERROR",
                                   "5": "",
                                   "6": "LOWBAT",
                                   "7": "VALVE_ERROR_POSITION"
                                 },
                                 "role": "indicator",
                                 "name": "WZ-VD-Regal:4.FAULT_REPORTING"
                               },
                               "native": {
                                 "CONTROL": "NONE",
                                 "DEFAULT": 0,
                                 "FLAGS": 9,
                                 "ID": "FAULT_REPORTING",
                                 "MAX": 7,
                                 "MIN": 0,
                                 "OPERATIONS": 5,
                                 "TAB_ORDER": 1,
                                 "TYPE": "ENUM",
                                 "UNIT": "",
                                 "VALUE_LIST": [
                                   "NO_FAULT",
                                   "VALVE_TIGHT",
                                   "ADJUSTING_RANGE_TOO_LARGE",
                                   "ADJUSTING_RANGE_TOO_SMALL",
                                   "COMMUNICATION_ERROR",
                                   "",
                                   "LOWBAT",
                                   "VALVE_ERROR_POSITION"
                                 ]
                               },
                               "from": "system.adapter.hm-rega.0",
                               "user": "system.user.admin",
                               "ts": 1740918410792,
                               "_id": "hm-rpc.0.OEQ2637929.4.FAULT_REPORTING",
                               "acl": {
                                 "object": 1636,
                                 "state": 1636,
                                 "owner": "system.user.admin",
                                 "ownerGroup": "system.group.administrator"
                               }
                              }
                              

                              Object Daten WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM

                              {
                               "type": "state",
                               "common": {
                                 "name": "WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM",
                                 "type": "number",
                                 "role": "indicator.alarm",
                                 "read": true,
                                 "write": true,
                                 "def": 0,
                                 "states": {
                                   "0": "NO ALARM",
                                   "1": "ALARM",
                                   "2": "ACKNOWLEDGED"
                                 }
                               },
                               "native": {
                                 "Name": "WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM",
                                 "TypeName": "ALARM",
                                 "DP": "16978"
                               },
                               "from": "system.adapter.hm-rega.0",
                               "user": "system.user.admin",
                               "ts": 1548955628248,
                               "_id": "hm-rpc.0.OEQ2637929.4.FAULT_REPORTING-6_ALARM",
                               "acl": {
                                 "object": 1636,
                                 "state": 1636,
                                 "owner": "system.user.admin",
                                 "ownerGroup": "system.group.administrator"
                               }
                              }
                              

                              Was kann ich noch tun?

                              Grüße

                              L Offline
                              L Offline
                              looxer01
                              schrieb am zuletzt editiert von
                              #206

                              @rantanplan

                              @rantanplan sagte in [Vorlage] Servicemeldungen Volume2:

                              Was kann ich noch tun?

                              im moment nichts. Ich erstelle einen Fix und poste ihn hier.
                              Ursache ist, so glaube ich, klar.

                              vG Looxer

                              1 Antwort Letzte Antwort
                              0
                              • rantanplanR rantanplan

                                @looxer01
                                Ich habe mal im Script debugLevel auf 2 gesetzt.
                                GeraeteId steht bei mir auf false.

                                Dann habe ich neue Batterien eingesetzt.
                                Meldung auf der CCU3 weg.

                                Dann wieder die alten Batterien eingesetzt.
                                Systemmeldung auf der CCU3 "Leere Batterie"

                                Hier das LOG.
                                ServicemeldungenSystemLog.csv

                                Aktuell sind noch die leeren Batterien drin.
                                Sieht so aus:


                                Bild_1.png

                                Object Daten WZ-VD-Regal:4.FAULT_REPORTING

                                {
                                 "type": "state",
                                 "common": {
                                   "def": 0,
                                   "type": "number",
                                   "read": true,
                                   "write": false,
                                   "min": 0,
                                   "max": 7,
                                   "states": {
                                     "0": "NO_FAULT",
                                     "1": "VALVE_TIGHT",
                                     "2": "ADJUSTING_RANGE_TOO_LARGE",
                                     "3": "ADJUSTING_RANGE_TOO_SMALL",
                                     "4": "COMMUNICATION_ERROR",
                                     "5": "",
                                     "6": "LOWBAT",
                                     "7": "VALVE_ERROR_POSITION"
                                   },
                                   "role": "indicator",
                                   "name": "WZ-VD-Regal:4.FAULT_REPORTING"
                                 },
                                 "native": {
                                   "CONTROL": "NONE",
                                   "DEFAULT": 0,
                                   "FLAGS": 9,
                                   "ID": "FAULT_REPORTING",
                                   "MAX": 7,
                                   "MIN": 0,
                                   "OPERATIONS": 5,
                                   "TAB_ORDER": 1,
                                   "TYPE": "ENUM",
                                   "UNIT": "",
                                   "VALUE_LIST": [
                                     "NO_FAULT",
                                     "VALVE_TIGHT",
                                     "ADJUSTING_RANGE_TOO_LARGE",
                                     "ADJUSTING_RANGE_TOO_SMALL",
                                     "COMMUNICATION_ERROR",
                                     "",
                                     "LOWBAT",
                                     "VALVE_ERROR_POSITION"
                                   ]
                                 },
                                 "from": "system.adapter.hm-rega.0",
                                 "user": "system.user.admin",
                                 "ts": 1740918410792,
                                 "_id": "hm-rpc.0.OEQ2637929.4.FAULT_REPORTING",
                                 "acl": {
                                   "object": 1636,
                                   "state": 1636,
                                   "owner": "system.user.admin",
                                   "ownerGroup": "system.group.administrator"
                                 }
                                }
                                

                                Object Daten WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM

                                {
                                 "type": "state",
                                 "common": {
                                   "name": "WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM",
                                   "type": "number",
                                   "role": "indicator.alarm",
                                   "read": true,
                                   "write": true,
                                   "def": 0,
                                   "states": {
                                     "0": "NO ALARM",
                                     "1": "ALARM",
                                     "2": "ACKNOWLEDGED"
                                   }
                                 },
                                 "native": {
                                   "Name": "WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM",
                                   "TypeName": "ALARM",
                                   "DP": "16978"
                                 },
                                 "from": "system.adapter.hm-rega.0",
                                 "user": "system.user.admin",
                                 "ts": 1548955628248,
                                 "_id": "hm-rpc.0.OEQ2637929.4.FAULT_REPORTING-6_ALARM",
                                 "acl": {
                                   "object": 1636,
                                   "state": 1636,
                                   "owner": "system.user.admin",
                                   "ownerGroup": "system.group.administrator"
                                 }
                                }
                                

                                Was kann ich noch tun?

                                Grüße

                                L Offline
                                L Offline
                                looxer01
                                schrieb am zuletzt editiert von looxer01
                                #207

                                @rantanplan
                                kannst du diese Tabelle austauschen und dann nochmal mit debug laufen lassen ?

                                const alarmTypes = [
                                    { key: 'UNREACH_ALARM',                     suffixes: ['UNREACH_ALARM','unreach' ] },//UNREACH_ALARM = HM-Classic & HMIP-CCU - unreach = HMIP Accesspoint
                                    { key: 'STICKY_UNREACH_ALARM',              suffixes: ['STICKY_UNREACH_ALARM'] },
                                    { key: 'CONFIG_PENDING_ALARM',              suffixes: ['CONFIG_PENDING_ALARM','configPending'] }, //configPending ist eine HMIP Meldung
                                    { key: 'UPDATE_PENDING_ALARM',              suffixes: ['UPDATE_PENDING_ALARM'] },
                                    { key: 'LOWBAT_ALARM',                      suffixes: ['LOWBAT_ALARM', 'LOW_BAT_ALARM','lowBat'] }, //LOWBAT_ALARM = HM-Classic - LOW_BAT_ALARM = HMIP CCU - lowBat = HMIP Accesspoint
                                    { key: 'DEVICE_IN_BOOTLOADER_ALARM',        suffixes: ['DEVICE_IN_BOOTLOADER_ALARM'] },
                                    { key: 'ERROR',                             suffixes: ['ERROR','DUTY_CYCLE'] },                  // error ist ein Sammler fuer hier nicht definierte Meldungen
                                    { key: 'FAULT_REPORTING',                   suffixes: ['FAULT_REPORTING','FAULT_REPORTING-1_ALARM','FAULT_REPORTING-2_ALARM','FAULT_REPORTING-3_ALARM','FAULT_REPORTING-4_ALARM','FAULT_REPORTING-5_ALARM','FAULT_REPORTING-6_ALARM'] },
                                    { key: 'SABOTAGE_ALARM',                    suffixes: ['SABOTAGE_ALARM','sabotage'] }, // sabotage ist eine HMIP Meldung
                                    { key: 'STICKY_SABOTAGE_ALARM',             suffixes: ['STICKY_SABOTAGE_ALARM'] },
                                    { key: 'ERROR_NON_FLAT_POSITIONING_ALARM',  suffixes: ['ERROR_NON_FLAT_POSITIONING_ALARM'] },
                                    { key: 'OVERHEAT_ALARM',                    suffixes: ['ERROR_OVERHEAT_ALARM'] },
                                    { key: 'UNDERVOLTAGE_ALARM',                suffixes: ['ERROR_UNDERVOLTAGE_ALARM'] },
                                ];
                                

                                Die Änderung befindet sich in Zeile 9.

                                1 Antwort Letzte Antwort
                                0
                                • rantanplanR rantanplan

                                  @looxer01
                                  Ich habe mal im Script debugLevel auf 2 gesetzt.
                                  GeraeteId steht bei mir auf false.

                                  Dann habe ich neue Batterien eingesetzt.
                                  Meldung auf der CCU3 weg.

                                  Dann wieder die alten Batterien eingesetzt.
                                  Systemmeldung auf der CCU3 "Leere Batterie"

                                  Hier das LOG.
                                  ServicemeldungenSystemLog.csv

                                  Aktuell sind noch die leeren Batterien drin.
                                  Sieht so aus:


                                  Bild_1.png

                                  Object Daten WZ-VD-Regal:4.FAULT_REPORTING

                                  {
                                   "type": "state",
                                   "common": {
                                     "def": 0,
                                     "type": "number",
                                     "read": true,
                                     "write": false,
                                     "min": 0,
                                     "max": 7,
                                     "states": {
                                       "0": "NO_FAULT",
                                       "1": "VALVE_TIGHT",
                                       "2": "ADJUSTING_RANGE_TOO_LARGE",
                                       "3": "ADJUSTING_RANGE_TOO_SMALL",
                                       "4": "COMMUNICATION_ERROR",
                                       "5": "",
                                       "6": "LOWBAT",
                                       "7": "VALVE_ERROR_POSITION"
                                     },
                                     "role": "indicator",
                                     "name": "WZ-VD-Regal:4.FAULT_REPORTING"
                                   },
                                   "native": {
                                     "CONTROL": "NONE",
                                     "DEFAULT": 0,
                                     "FLAGS": 9,
                                     "ID": "FAULT_REPORTING",
                                     "MAX": 7,
                                     "MIN": 0,
                                     "OPERATIONS": 5,
                                     "TAB_ORDER": 1,
                                     "TYPE": "ENUM",
                                     "UNIT": "",
                                     "VALUE_LIST": [
                                       "NO_FAULT",
                                       "VALVE_TIGHT",
                                       "ADJUSTING_RANGE_TOO_LARGE",
                                       "ADJUSTING_RANGE_TOO_SMALL",
                                       "COMMUNICATION_ERROR",
                                       "",
                                       "LOWBAT",
                                       "VALVE_ERROR_POSITION"
                                     ]
                                   },
                                   "from": "system.adapter.hm-rega.0",
                                   "user": "system.user.admin",
                                   "ts": 1740918410792,
                                   "_id": "hm-rpc.0.OEQ2637929.4.FAULT_REPORTING",
                                   "acl": {
                                     "object": 1636,
                                     "state": 1636,
                                     "owner": "system.user.admin",
                                     "ownerGroup": "system.group.administrator"
                                   }
                                  }
                                  

                                  Object Daten WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM

                                  {
                                   "type": "state",
                                   "common": {
                                     "name": "WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM",
                                     "type": "number",
                                     "role": "indicator.alarm",
                                     "read": true,
                                     "write": true,
                                     "def": 0,
                                     "states": {
                                       "0": "NO ALARM",
                                       "1": "ALARM",
                                       "2": "ACKNOWLEDGED"
                                     }
                                   },
                                   "native": {
                                     "Name": "WZ-VD-Regal:4.FAULT_REPORTING-6_ALARM",
                                     "TypeName": "ALARM",
                                     "DP": "16978"
                                   },
                                   "from": "system.adapter.hm-rega.0",
                                   "user": "system.user.admin",
                                   "ts": 1548955628248,
                                   "_id": "hm-rpc.0.OEQ2637929.4.FAULT_REPORTING-6_ALARM",
                                   "acl": {
                                     "object": 1636,
                                     "state": 1636,
                                     "owner": "system.user.admin",
                                     "ownerGroup": "system.group.administrator"
                                   }
                                  }
                                  

                                  Was kann ich noch tun?

                                  Grüße

                                  L Offline
                                  L Offline
                                  looxer01
                                  schrieb am zuletzt editiert von
                                  #208

                                  @rantanplan
                                  ignoriere die voherige message.
                                  Nutze stattdessen die angehängte Version. Ich musste noch ein paar Codezeilen hinzufügen.
                                  Es konnte gar nicht funktionieren

                                  Servicemeldungen_Vol2_3-30_Rantanplan.txt

                                  rantanplanR 2 Antworten Letzte Antwort
                                  0
                                  • L looxer01

                                    @rantanplan
                                    ignoriere die voherige message.
                                    Nutze stattdessen die angehängte Version. Ich musste noch ein paar Codezeilen hinzufügen.
                                    Es konnte gar nicht funktionieren

                                    Servicemeldungen_Vol2_3-30_Rantanplan.txt

                                    rantanplanR Online
                                    rantanplanR Online
                                    rantanplan
                                    schrieb am zuletzt editiert von
                                    #209

                                    @looxer01 sagte in [Vorlage] Servicemeldungen Volume2:

                                    @rantanplan
                                    ignoriere die voherige message.

                                    Zu spät:blush:

                                    Mit leeren Batterien kam die Meldung ".....Ventil blockiert"

                                    Damit meine "Arbeit" nicht umsonst war, hier das Log.:grimacing:
                                    ServicemeldungenSystemLog.csv

                                    Probiere jetzt Dein geändertes Script.

                                    Rantanplan

                                    CCU3 / MS Server 2019(VM) / Scripten mit Blockly

                                    1 Antwort Letzte Antwort
                                    0
                                    • L looxer01

                                      @rantanplan
                                      ignoriere die voherige message.
                                      Nutze stattdessen die angehängte Version. Ich musste noch ein paar Codezeilen hinzufügen.
                                      Es konnte gar nicht funktionieren

                                      Servicemeldungen_Vol2_3-30_Rantanplan.txt

                                      rantanplanR Online
                                      rantanplanR Online
                                      rantanplan
                                      schrieb am zuletzt editiert von
                                      #210

                                      @looxer01
                                      Nach dem Script-Start wird "Einstellbereich Ventil zu gross" gemeldet
                                      Auf der CCU3 keine Servicemeldung.

                                      Hier das Log.
                                      ServicemeldungenSystemLog.csv

                                      Habe auch mal das andere Log aktiviert.
                                      ServicemeldungenVol2.csv

                                      Sehe gerade, dass "FAULT_REPORTING-6_ALARM" in ioBroker tatsächlich auf '2' steht. Warum auch immer.
                                      CCU3 sagt "alles ok".

                                      Tausche jetzt nochmal die Batterien.

                                      Rantanplan

                                      CCU3 / MS Server 2019(VM) / Scripten mit Blockly

                                      L 2 Antworten Letzte Antwort
                                      0
                                      • rantanplanR rantanplan

                                        @looxer01
                                        Nach dem Script-Start wird "Einstellbereich Ventil zu gross" gemeldet
                                        Auf der CCU3 keine Servicemeldung.

                                        Hier das Log.
                                        ServicemeldungenSystemLog.csv

                                        Habe auch mal das andere Log aktiviert.
                                        ServicemeldungenVol2.csv

                                        Sehe gerade, dass "FAULT_REPORTING-6_ALARM" in ioBroker tatsächlich auf '2' steht. Warum auch immer.
                                        CCU3 sagt "alles ok".

                                        Tausche jetzt nochmal die Batterien.

                                        L Offline
                                        L Offline
                                        looxer01
                                        schrieb am zuletzt editiert von looxer01
                                        #211

                                        @rantanplan

                                        • es wäre gut zu wissen wie Object Daten WZ-VD-Regal:4.FAULT_REPORTING ausgeprägt ist. Denn dort gibt es die status 1 - 7. bei den anderen nicht. (das hatte ich leider falsch gesehen)
                                          In der Regel werden die ALARM-Datenpunkte augelöst.

                                        • Ein quick Fix wäre 'FAULT_REPORTING-6_ALARM' unter den AlarmType LowBat zu listen. Dann fehlen aber immer noch die anderen Alarme.

                                        ich muss nochmal nachdenken.

                                        EDIT
                                        ok, nachgedacht.
                                        Der Ansatz wäre

                                        • 'FAULT_REPORTING-6_ALARM' unter lowbat zu führen. Dann bekommst du das auch klassifiziert als lowbat und nicht als 'FaultMessage"
                                        • Die anderen werde ich dann vom status her als FaultMessages führen und die status 1-7 (ohne 6) entsprechend als status messages entsprechend der Tabelle: faultMessages aus dem Script darstellen

                                        brauche ich jetzt aber ein wenig Zeit für

                                        1 Antwort Letzte Antwort
                                        0
                                        • rantanplanR rantanplan

                                          @looxer01
                                          Nach dem Script-Start wird "Einstellbereich Ventil zu gross" gemeldet
                                          Auf der CCU3 keine Servicemeldung.

                                          Hier das Log.
                                          ServicemeldungenSystemLog.csv

                                          Habe auch mal das andere Log aktiviert.
                                          ServicemeldungenVol2.csv

                                          Sehe gerade, dass "FAULT_REPORTING-6_ALARM" in ioBroker tatsächlich auf '2' steht. Warum auch immer.
                                          CCU3 sagt "alles ok".

                                          Tausche jetzt nochmal die Batterien.

                                          L Offline
                                          L Offline
                                          looxer01
                                          schrieb am zuletzt editiert von
                                          #212

                                          @rantanplan
                                          ich habe eine neue Version gebaut und hoffe, dass es jetzt ok ist.

                                          Du solltest jetzt eine lowbat message erhalten für dieses gerät.

                                          für alle anderen messages (Ventilstellung etc) erhälts du "Fault Messages"

                                          Servicemeldungen_Vol2_3-30_Rantanplan2.txt

                                          Falls ok, werde ich das in die nächste Version übernehmen.

                                          vG Looxer

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


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          822

                                          Online

                                          32.4k

                                          Benutzer

                                          81.5k

                                          Themen

                                          1.3m

                                          Beiträge
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                          ioBroker Community 2014-2025
                                          logo
                                          • Anmelden

                                          • Du hast noch kein Konto? Registrieren

                                          • Anmelden oder registrieren, um zu suchen
                                          • Erster Beitrag
                                            Letzter Beitrag
                                          0
                                          • Home
                                          • Aktuell
                                          • Tags
                                          • Ungelesen 0
                                          • Kategorien
                                          • Unreplied
                                          • Beliebt
                                          • GitHub
                                          • Docu
                                          • Hilfe