// Servicemeldungen Script zum erfassen von aktuellen Servicemeldungen und halten der historie fuer einen definierten Zeitraum (Historie) // Servicemeldungen koennen ueber bekannte services gesendet werden // Trigger kann ausgewaehlt werden (entweder REGA anzahl = Servicemeldungen oder einzelne IDs) // States werden vom script angelegt // Autor Looxer01 02.11.2024 Version 1.0 (initiale Version) // Version 1.01 - 02.11.24 Sabotage Alarm fuer HM-Classic Sensoren angepasst. (Error >= 1 und <= 7) // formatierung mit Zeilenumbruch korrigiert // Version 1.02 - 03.11.24 Replacements als Funktion // Handling von geloeschten Datenpunkten // Ausschlussliste bei subscription der Geraete ID // Kosmetik // Version 1.03 - 04.11.24 Stringumwandlung gefixt / JSON fuer aktuelle Meldungen und historische Meldungen hinzugefuegt. // Version 1.04 - 05.11.24 im Datenpunkt id_JSON_Servicemeldung_Aktuell wird ein falscher Text gezeigt // Quick Fix - keine historische Meldung bei Status 0 // Version 1.05 - 10.11.24 Fix fuer Status 0 fuer historische Meldungen. Text aus Tabelle: StandardstatusMessages verwendet // Text angepasst in Tabelle fuer 0 // Batteriemeldung erweitert // Telegram Instanz und User hinzugefügt // WICHTIGE Aenderung fuer REGA subscription: Intelligenterer Umgang bei vielen Aenderungen von Anzahl der Servicmeldungen aus der CCU // Version 1.06 - 12.11.24 GruppenSelektoren auskommentiert fuer unreach // korrektur fuer REGA subcription:Timer variable zuruecksetzen // Instanzen zur Selektion sind jetzt als Variabel definiert - z.B. um CuxD auszuschliessen // HM-Classic Sabotage counts werden umgeleitet von error auf Sabotage // Version 1.07 - 13.11.24 Zähler für Sabotage funktioniert jetzt für HMClassic und HMIP // Text StandardMessages angepasst //Fuer alle Messenger Services kann die gewuenschte Instanz angegeben werden // Version 1.08 - 14.11.24 Einstellungsbereich aufgeraeumt- weitere Kosmetik // Fix Low_Bat Meldung // Version 1.09 - 15.11.24 Ueberfluessiges log fuer Json String entfernt // moegliche ungueltige StatusMeldung mit mehr Infos versehen // Create states WarnMeldung statt ErrorMeldung // Version 1.10 - 17.11.24 Versuch der Behandlung von Fehlern, wenn die ID nicht existiert - CheckAll Schleife geaendert und ForEach ersetzt // Log fuer DebugLevel1 angepasst // Version 1.11 - 17.11.24 Instanzen koennen jetzt komplett vergeben werden / 9 = nicht beruecksichtigen - z.B. bei CuxD und Wired // Version 1.12 - 17.11.24 Wired Alarme hinzugefügt // Overheat und UnderVoltage auch zu non Wired hinzugefuegt // Version 1.20 - 19.11.24 Optimierung der Selektoren // Kurztext Option fuer Messenger hinzugefügt // Einstellungsbereich restrukturiert // Code Optimierungen // //--------------------------------------------------------------------------------------------------------------------------------------------------------------- // Muss-Einstellungen ( HM-Classic , HMIP, Wired - hier müssen zwingend die Instanzen stimmmen ) //--------------------------------------------------------------------------------------------------------------------------------------------------------------- // Im folgenden sind die Instanzen gelistet fuer die die Selektion erfolgt // bitte 9 Eintragen falls eine Instanz nicht relevant ist // Gruppeninstanzen sind normalerweise nicht relevant // Wired ist momentan noch in Entwicklung // CuxD Instanzen duerfen nicht eingetragen werden const HMClassicInstanz = 0; // HM-Classic Instanz eintragen // 9 = falls nicht relevant const HMIPInstanz = 1; // Homematic IP instanz // 9 = falls nicht relevant const WiredIClassicInstanz = 9; // Wired Instanz // 9 = falls nicht relevant const GruppenInstanz = 9; // bzw virtuelle GeräteInstanz- 9 = nicht relevant - Die Gruppen werden i.d.R. nicht gebraucht - Empfehlung: 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 einen logeintrag in ein externes file (Excel Format) / nur bei GeraeteIDTrigger = true const logflag = true; const LogPath = "/opt/iobroker/log/ServicemeldungenVol2.csv"; // Pfad und Dateiname des externen Logsconstst / nur bei GeraeteIDTrigger = true //const LogPath = "/iobroker/log/ServicemeldungenVol2.csv";"; // Pfad fuer Windows/ nur bei GeraeteIDTrigger = true // iobroker ist der angenommene iobroker home-pfad // Text der erscheinen soll, wenn keine SM vorliegen const MessageBeiKeinerSM = 'Derzeit keine Servicemeldungen' // auf '' setzen wenn kein Text gezeigt werden soll //Geraete die nicht ueberwacht werden sollen. Geraete-IDs eingeben - Komma getrennt erfassen const Ausschlussliste = ['003660xxx62C5D', '00091D8xxx7410']; // immer mit Komma trennen // 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 = 0 ; // wenn GeraeteIDTrigger auf true gestellt wird, dann wird fuer jeden Datenpukt mit Relevanz fuer eine Servicemeldung eine subscription angelegt. // Vorteil ist, dass auch eine Historie und ein Log fuer Servicemeldungen geschrieben werden kann: 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: keine Servicemeldungs Historie const GeraeteIDTrigger = true; // true = viele subscriptions - false = 1 subscritpion // fuer alle Spalten mit true werden die Nachrichten ueber den zugeordneten Dienst versendet // Voraussetzung ist, dass der entsprechende Adapter installiert und konfiguriert ist const services = ['email', 'whatsApp', 'Signal', 'Telegram', 'Pushover', 'Pushsafer']; const MessengerScope = { 'UNREACH_ALARM': [false, true, false, false, false, false], 'LOWBAT_ALARM': [false, true, false, false, false, false], 'SABOTAGE_ALARM': [false, true, false, false, false, false], 'CONFIG_PENDING': [false, true, false, false, false, false], 'Sonstige': [false, false, false, false, false, false], 'keineSM': [false, true, false, false, false, false], } const MessengerInstanz = [0, 0, 0, 0, 0, 0 ]; // Instanz des Messengers const TextTypeKurz = [false, true, true, true, true, true ]; // bei true wird der Kurztext gesendet - sonst der Langtext // email-Einstellungen const emailAddresse = "Vorname-Nachname@web.de" const Headline = "ioBroker Servicemeldung"; // Ueberschrift Messages fuer email und Pushsafer // telegram Einstellungen const TelegramUser = ""; //----------------------------------------------------------------------------------------------------- //Experten Einstellungen //----------------------------------------------------------------------------------------------------- // Texte werden in "history" hinzugefuegt und etsprechend des schedules wieder geloescht -- nur relvant wenn GeraeteIDTrigger = false const scheduleTimeClearSMTexte = "2 0 1 * *"; // Sam 1. tag des monats um 00:02 morgens - sollen alle Servicemeldungen der Woche datenpunkt der SM-Texte geloescht werden // const scheduleTimeClearSMTexte = "58 23 * * 0"; // alernative 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 und TextPfade const PathRega = 'hm-rega.0.maintenance'; const id_Text_ServicemeldungLang = path+'TextLangAktuelleSM'; // Objekt wo die Servicemeldung hingeschrieben werden soll (String) = path+'TextLang'; // Objekt wo die Servicemeldung hingeschrieben werden soll (String) Lange (normale) Version const id_Text_ServicemeldungKurz = path+'TextKurzAktuelleSM'; // Objekt wo die Servicemeldung hingeschrieben werden soll (String) - kurze Version const id_Text_Servicemeldung_History = path+'TestLangVergangeneSM'; // Objekt wo die Servicemeldung hinzugefuegt werden soll (String) - 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 // Count-Pfade (Zaehlungen) const id_IST_LOWBAT = path+'Anzahl_LOWBAT' // HM classic; & HMIP = const id_IST_UNREACH = path+'Anzahl_UNREACH' const id_IST_STICKY_UNREACH = path+'Anzahl_STICKY_UNREACH' //*Anzahl Sticky Unreach (zu bestaetigende unreach) const id_IST_CONFIG_PENDING = path+'Anzahl_CONFIG_PENDING'; const id_IST_UPDATE_PENDING = path+'Anzahl_Update_PENDING'; const id_IST_DEVICE_IN_BOOTLOADER = path+'Anzahl_DEVICE_IN_BOOTLOADER'; const id_IST_ERROR = path+'Anzahl_in_ERROR'; const id_IST_ERROR_NON_FLAT_POSITIONING = path+'Anzahl_NON_FLAT_POSITIONING'; const id_IST_FAULT_REPORTING = path+'Anzahl_FAULT_REPORTING'; const id_IST_SABOTAGE = path+'Anzahl_SABOTAGE'; const id_IST_ERROR_REDUCED = path+'Anzahl_ERROR_REDUCED'; const id_IST_STICKY_SABOTAGE = path+'Anzahl_Sticky_SABOTAGE'; const id_IST_USBH_POWERFAIL = path+'Anzahl_USBH_POWERFAIL'; const id_IST_U_SOURCE_FAIL = path+'Anzahl_U_SOURCE_FAIL '; const id_IST_Gesamt = path+'Anzahl_GESAMT' const id_IST_SMAktuell = path+'Anzahl_SM-Aktuell' // Standard Servicemeldungen, wenn alle anderen nicht zutreffen const StandardstatusMessages = ['keine Stoerung', 'Stoerung', 'bestaetigte Servicemeldung']; // hier sind alle bekannten Servicemeldungen zugeordnet (ueber Status-Datenpunkte des Geraetes) const statusMessages = { UNREACH_ALARM: ['keine Kommunikationsfehler', 'Kommunikation gestoert', 'Kommunikation war gestoert'], STICKY_UNREACH_ALARM: ['keine Kommunikationsfehler', 'Kommunikation gestoert', 'Kommunikation war gestoert'], SABOTAGE_ALARM: ['Keine Sabotage', 'Sabotage', 'Sabotage aufgehoben'], LOWBAT_ALARM: ['Batterie ok', 'Batterie niedrig', 'Batterie ok'], LOW_BAT_ALARM: ['Batterie ok', 'Batterie niedrig', 'Batterie ok'], ERROR_NON_FLAT_POSITIONING_ALARM: ['Keine Meldung', 'Geraet wurde angehoben.', 'Geraet wurde angehoben: Bestaetigt'], CONFIG_PENDING_ALARM: ['keine Meldung', 'Konfigurationsdaten stehen zur Uebertragung an', 'Konfigurationsdaten standen zur Uebertragung an'], UPDATE_PENDING_ALARM: ['kein Update verfuegbar', 'Update verfuegbar', 'Update wurde eingespielt'], DEVICE_IN_BOOTLOADER_ALARM: ['Keine Meldung', 'Geraet startet neu', 'Geraet wurde neu gestartet'], }; 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' } }; 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' } }; // Umlaut Umwandlung und entfernung PUnkte - kann aber auch erweitert werden const replacements = { // Umwandlung fuer Namen der Geraete (common.name) '.': ' ', 'ä': 'ae', 'ü': 'ue', 'ö': 'oe', 'ß': 'ss' }; // moegliche Homematic Instanzen (CuxD ausgeschlossen) const instanceIds = [ { name: 'HMClassicInstanz', id: HMClassicInstanz }, { name: 'HMIPInstanz', id: HMIPInstanz }, { name: 'GruppenInstanz', id: GruppenInstanz }, { name: 'WiredIClassicInstanz', id: WiredIClassicInstanz }, ]; // 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'] }, { key: 'STICKY_UNREACH_ALARM', suffixes: ['STICKY_UNREACH_ALARM'] }, { key: 'CONFIG_PENDING_ALARM', suffixes: ['CONFIG_PENDING_ALARM'] }, { key: 'UPDATE_PENDING_ALARM', suffixes: ['UPDATE_PENDING_ALARM'] }, { key: 'LOWBAT_ALARM', suffixes: ['LOWBAT_ALARM', 'LOW_BAT_ALARM'] }, { key: 'DEVICE_IN_BOOTLOADER_ALARM', suffixes: ['DEVICE_IN_BOOTLOADER_ALARM'] }, { key: 'ERROR', suffixes: ['ERROR'] }, { key: 'FAULT_REPORTING', suffixes: ['FAULT_REPORTING'] }, { key: 'SABOTAGE_ALARM', suffixes: ['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'] } ]; //----------------------------------------------------------------------------------------------------- //Ende Einstellungen //----------------------------------------------------------------------------------------------------- // IDs für alle Instanzen und Alarmtypen sammeln const selectors = alarmTypes.map(alarmType => { const collectedIds = []; instanceIds.forEach(instance => { if (instance.id !== 9) { alarmType.suffixes.forEach(suffix => { // @ts-ignore $(`state[id=hm-rpc.${instance.id}.*.${suffix}]`).each(id => collectedIds.push(id)); // zusammenbasteln des selectors }); } }); return { name: alarmType.key, ids: collectedIds }; }); selectors.forEach(selector => { log(`fuer die Alarmgruppe: ${selector.name} wurden ${selector.ids.length} IDs gefunden${debugLevel >= 3 ? ` ${selector.ids}` : ''}`); }); let AktuelleSMjsonString = [] // alle aktuellen Servicemeldungen als JSON // Aufbau MessageCollector - Beispiel //{ "Error": { "Email": [ "Error Alarm " ], "SMS": ["Error" ] }, // "Unreach": { "Email": ["Alarm: Eindringling im Gebäude!"], "SMS": [ "Eindringling" ] } } let MessageSendCollector = {}; // alle aktuellen Servicemeldungen zum senden an messaging services // Initialisierung des Objekts für alle Alarmtypen und Services alarmTypes.forEach((type) => { const alarmKey = type.key; // Verwende die `key`-Eigenschaft als Schlüssel MessageSendCollector[alarmKey] = {}; // Erstelle ein Objekt für den Alarmtyp services.forEach((service) => { MessageSendCollector[alarmKey][service] = []; // Erstelle ein leeres Array für jeden Service }); }); // Variablen für den Timer, um die 5-Sekunden-Wartezeit zu steuern - relevant fuer HM-REGA subscription let changeTimeout = null; let countIdsInnerhalbTimeout = 0; // create States CreateStates(() => { Check_All() }); // Subscriptions erstellen if (GeraeteIDTrigger) { SubscribeGeraeteID(); }else{ SubscribeRegaDP(); } //----------------------------------------------------------------------------------------------------- // Schedule zum Loeschen des Datenpunktwertes der Histore //----------------------------------------------------------------------------------------------------- schedule(scheduleTimeClearSMTexte, function() { setState(id_Text_Servicemeldung_History, ''); setState(id_JSON_Servicemeldung_Historie, []); log(`Datenpunkt ${id_Text_Servicemeldung_History} geloescht`); }); //----------------------------------------------------------------------------------------------------- // Funktion SubscribeRegaDP // Erstellung der Subscriptions auf REGA Datenpunkt // es werden 5 Sekunden lang vorliegende Aenderungen gesammelt //----------------------------------------------------------------------------------------------------- function SubscribeRegaDP() { if (debugLevel >= 2) { log(`Routine SubscribeRegaDP wird ausgefuehrt - Pfad des Datenpunkts: ${PathRega}`, "info");} on({id: PathRega, change: 'any'}, function (obj) { if (obj.state.val === obj.oldState.val) { // Überprüfen, ob sich der Wert des Datenpunkts geändert hat return; // Keine Änderung, also nichts tun } countIdsInnerhalbTimeout++; if (debugLevel >= 2) { log(`Datenpunkt ${PathRega} hat sich geaendert. Neuer Wert: ${obj.state.val}`, "info");} // Falls schon ein Timer läuft, diesen abbrechen if (changeTimeout) { clearTimeout(changeTimeout); } // Setze einen neuen Timer auf 5 Sekunden, um die Funktion Servicemeldung aufzurufen changeTimeout = setTimeout(function() { Servicemeldung(); // Funktion wird nach 5 Sekunden ohne weitere Änderungen aufgerufen }, 5000); // 5000 ms = 5 Sekunden }); } // ende funktion //----------------------------------------------------------------------------------------------------- // Funktion Subscribe GeraeteID // Erstellung der Subscriptions auf GeraeteID und Datenpunkt Ebene //----------------------------------------------------------------------------------------------------- function SubscribeGeraeteID() { if(debugLevel >= 2) { log(`Routine SubscribeGeraeteID wird ausgefuehrt`, "info");} let callCount = 0; // fuer Timer Funktion let timeoutActive = false; // fuer Timer Funktion selectors.forEach(selector => { if (selector.ids.length > 0) { if(debugLevel >= 2) {log(`SubscribeGeraeteID: Prozessiere selector: ${selector.name}`);}; const filteredIds = selector.ids.filter(id => { // IDs filtern: nur IDs behalten, deren Geraete-ID nicht in der Ausschlussliste ist const parts = id.split('.'); // Extrahiere die Geraete-ID aus der vollstaendigen ID (dritte Stelle) const deviceId = parts[2]; // Hier ist die Geraete-ID das dritte Element if (Ausschlussliste.includes(deviceId)) { // ueberpruefen, ob die Geraete-ID in der Ausschlussliste ist if(debugLevel >= 2) { log(`ID ${deviceId} aus den subscriptions entfernt (wegen Ausschlussliste) : ${id}`);}; return false; // Diese ID wird gefiltert } return true; // Diese ID bleibt }); filteredIds.forEach(id => { on({ id: id, change: "any" }, obj => { if (obj.state.val === obj.oldState.val) { return; } if (timeoutActive) { return; // Wenn der Timeout aktiv ist, keine weitere Verarbeitung } callCount++; // Wenn mehr als 10 Aufrufe innerhalb von 1 Sekunde if (callCount >= 50) { timeoutActive = true; // Wartezeit aktivieren log("SubscribeGeraeteID: Servicemeldungen: Zu viele Servicemeldungen wurden generiert, warte 5 Minuten.","warn"); setTimeout(() => { timeoutActive = false; // Timeout beenden callCount = 0; // Zaehler zuruecksetzen log("Servicemeldungen: Wartezeit beendet, weitere Servicemeldungen sind jetzt moeglich."); }, 5 * 60 * 1000); // 5 Minuten in Millisekunden } Servicemeldung(obj); // "Servicemeldung" aufrufen setTimeout(() => { // Reset des Zaehlers nach 1 Sekunde callCount = Math.max(0, callCount - 1); }, 1000); }); }); // EndIDLoop } else { if(debugLevel >= 1 ) {log(`SubscribeGeraeteID: No matching states found for ${selector.name}`);}; } }); //endSelectorLoop } // EndFunction //----------------------------------------------------------------------------------------------------- // Kernfunktion Sevicemeldung // erstmal die aktuelle Servicemeldung analysieren und loggen //----------------------------------------------------------------------------------------------------- function Servicemeldung(obj) { if (debugLevel >= 2) log(`Routine Servicemeldung wird ausgefuehrt`, "info"); if (!GeraeteIDTrigger) { // nur wenn ueber REGA Servicemeldungen von der CCU reagiert werden soll if (debugLevel >= 1) {log(`Routine Servicemeldung wird ausgefuehrt - Es wurden insgesamt ${countIdsInnerhalbTimeout} Änderungen festgestellt.`, "info");} countIdsInnerhalbTimeout = 0; // Reset der Zählung changeTimeout = null; // Reset des Timeouts fuer den REGA Trigger const AnzahlSM = Check_All(); const regaState = getState(PathRega).val; if (debugLevel >= 1) { log("REGA Anzahl SM " + regaState + " anzahlSM " + AnzahlSM);}; if (regaState === 0 || AnzahlSM === 0) { setState(id_Text_ServicemeldungLang, MessageBeiKeinerSM); setState(id_Text_ServicemeldungKurz, MessageBeiKeinerSM); addMessageToCollector('keineSM', MessageBeiKeinerSM, MessageBeiKeinerSM); sendMessage('keineSM'); return; } sendMessage(); return; } // jetzt die subscriptions die ueber Aenderungen von Datenpunkten laufen also bei GeraeteIDTrigger = true - Servicemeldungen in die Historie schreiben const AnzahlSM = Check_All(); const id_name = obj.id.split('.')[2]; if (!existsState(obj.id)) { log("Routine Servicemeldungen - Geraet " + id_name +" scheint zwischenzeitlich nicht mehr zu existieren - Empfehlung: script neu starten","warn") return; } if (AnzahlSM === 0) { // keine Servicmeldungen delete MessageSendCollector['keineSM']; addMessageToCollector('keineSM', MessageBeiKeinerSM, MessageBeiKeinerSM); sendMessage('keineSM'); } if (Ausschlussliste.includes(id_name)) { if(debugLevel >= 2) { log(`Routine Servicemeldung ID ${id_name} hat eine Aenderung gemeldet wird aber uebersprungen wegen Ausschlussliste`, "info");} return; } let commonObj = getObject(obj.id.substring(0, obj.id.lastIndexOf('.') - 2)); let common_name = ReplaceString(commonObj.common.name); const meldungsart = obj.id.split('.')[4]; const status = obj.newState.val; const native_type = getObject(obj.id.substring(0, obj.id.lastIndexOf('.') - 2)).native.TYPE; let status_textLang = DefineServiceMessage(meldungsart, native_type, status,obj.id,"lang"); let status_textKurz = DefineServiceMessage(meldungsart, native_type, status,obj.id,"kurz"); if(debugLevel >= 1 ) { log(`ServicemeldungenVol2: neue Servicemeldung ist ${status_textLang} Meldungsart ist ${meldungsart} `);} const datum_seit = func_get_datum(obj.id); // Status "0" wurde gesetzt. Bei HM-Classic war das der Status um zu melden, dass die Servicemeldung zurueckgesetzt wurde if(status === 0 ) { status_textLang = DefineServiceMessage(meldungsart, native_type, status, obj.id, "lang") status_textKurz = DefineServiceMessage(meldungsart, native_type, status, obj.id, "kurz") if(debugLevel >= 2 ) { log(`Routine Servicemeldung - status ist 0 - status_text ist ${status_textLang} Meldungsart ist ${meldungsart} `);} } // externes log erzeugen writelog(common_name, id_name, meldungsart, status, status_textLang); // Historische Servicemeldung als Text speichern appendToState(id_Text_Servicemeldung_History, status_textLang); //historische Servicemeldung als Json speichern (additiv) let HistorischeSMjsonString = [] const stateValue = getState(id_JSON_Servicemeldung_Historie).val; if (stateValue) { try { HistorischeSMjsonString = JSON.parse(stateValue); } catch (error) { log(`Fehler beim Parsen des JSON: ${error}`); HistorischeSMjsonString = []; // Fallback auf leeres Array } } HistorischeSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name, status,status_textLang)); const jsonString = JSON.stringify(HistorischeSMjsonString); setState(id_JSON_Servicemeldung_Historie, jsonString); // Messenger Dienste aktivieren delete MessageSendCollector[meldungsart]; addMessageToCollector(meldungsart, status_textKurz, status_textLang); sendMessage(meldungsart); } //----------------------------------------------------------------------------------------------------- // Kompeletter Durchgang // jetzt alle Servicemeldungen durchzaehlen //----------------------------------------------------------------------------------------------------- function Check_All() { if(debugLevel >= 2) { log(`Routine Check_All wird ausgefuehrt`, "info");} let count_all = 0; let count_Akut = 0; let count_Akut_UNREACH_ALARM = 0; let count_Akut_Sticky_UNREACH_ALARM = 0; let count_Akut_CONFIG_PENDING_ALARM = 0; let count_Akut_UPDATE_PENDING_ALARM = 0; let count_Akut_LOWBAT_ALARM = 0; let count_Akut_DEVICE_IN_BOOTLOADER_ALARM = 0; let count_Akut_ERROR = 0; let count_Akut_FAULT_REPORTING = 0; let count_Akut_SABOTAGE_ALARM = 0; let count_Akut_Sticky_SABOTAGE_ALARM = 0; let count_Akut_ERROR_NON_FLAT_POSITIONING_ALARM = 0; let count_Akut_ERROR_REDUCED = 0; let count_Akut_USBH_POWERFAIL = 0; let count_Akut_U_SOURCE_FAIL = 0; let count_Akut_STICKY_SABOTAGE = 0; let id; let parts; let common_name; let id_name; let native_type; let GeraeteID; let meldungsart; let ServiceMeldungTextKurz; let ServiceMeldungTextLang; let status; let datum_seit; let commonObj; let ServicemeldungMessagesLang = []; let ServicemeldungMessagesKurz = []; let objectcheck; AktuelleSMjsonString = []; for (let j = 0; j < selectors.length; j++) { // Umstellung auf klassische for-Schleife und Durchgang durch alle Selectors const selector = selectors[j]; if (selector.ids.length > 0) { for (let i = 0; i < selector.ids.length; i++) { id = selector.ids[i]; parts = id.split('.'); // Extrahiere die Geraete-ID aus der vollstaendigen ID (dritte Stelle) GeraeteID = parts[2]; // Hier ist die Geraete-ID das dritte Element if (!existsState(id)) { log("Routine Check_All - Geraet "+GeraeteID +" scheint zwischenzeitlich nicht mehr zu existieren - Empfehlung: script neu starten","warn"); continue; // überspringe dieses Element und fahre mit der nächsten fort } // Überprüfen, ob die Geraete-ID in der Ausschlussliste ist if (Ausschlussliste.includes(GeraeteID)) { if(debugLevel >= 1) { log(`Routine Check_All ID ${GeraeteID} wird uebersprungen wegen Ausschlussliste`, "info"); } continue; // überspringe die ID, wenn sie in der Ausschlussliste ist } objectcheck = getObject(id.substring(0, id.lastIndexOf('.') - 2)); // evt Fehlermeldung mit falscher ID behandeln if (objectcheck && objectcheck.native && objectcheck.native.TYPE) { native_type = objectcheck.native.TYPE; } else { log(`Fehler: Das Objekt mit ID ${id} oder die native.TYPE-Eigenschaft ist nicht vorhanden.`, "warn"); continue; // Zur naesten ID im Loop } commonObj = getObject(id.substring(0, id.lastIndexOf('.') - 2)); common_name = ReplaceString(commonObj.common.name); id_name = id.split('.')[2]; meldungsart = id.split('.')[4]; status = getState(id).val; datum_seit = func_get_datum(id); // Switch-Case zur Überprüfung der verschiedenen Alarmtypen switch (selector.name) { case 'UNREACH_ALARM': if(status === 1) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextLang); addMessageToCollector('UNREACH_ALARM', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_UNREACH_ALARM++; count_Akut++; } break; case 'Sticky_UNREACH_ALARM': if(status === 1) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('Sticky_UNREACH_ALARM', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_Sticky_UNREACH_ALARM++; count_Akut++; } break; case 'UPDATE_PENDING_ALARM': if(status === 1) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('UPDATE_PENDING_ALARM', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_UPDATE_PENDING_ALARM++; count_Akut++; } break; case 'LOWBAT_ALARM': if(status === 1) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('LOWBAT_ALARM', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_LOWBAT_ALARM++; count_Akut++; } break; case 'DEVICE_IN_BOOTLOADER_ALARM': if(status === 1) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('DEVICE_IN_BOOTLOADER_ALARM', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_DEVICE_IN_BOOTLOADER_ALARM++; count_Akut++; } break; case 'ERROR': if (status >= 1 && status <= 7) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('ERROR', ServiceMeldungTextKurz, ServiceMeldungTextLang); if (status === 7) { count_Akut_SABOTAGE_ALARM++; // Nur für HM-Classic-messagetype = error status = 7 entspricht sabotage } else { count_Akut_ERROR++; } count_Akut++; } break; case 'FAULT_REPORTING': if(status === 1) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('FAULT_REPORTING', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_FAULT_REPORTING++; count_Akut++; } break; case 'SABOTAGE_ALARM': if(status === 1) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('SABOTAGE_ALARM', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_SABOTAGE_ALARM++; count_Akut++; } break; case 'ERROR_NON_FLAT_POSITIONING_ALARM': if(status === 1) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('ERROR_NON_FLAT_POSITIONING_ALARM', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_ERROR_NON_FLAT_POSITIONING_ALARM++; count_Akut++; } break; case 'CONFIG_PENDING': if(status === 1) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('CONFIG_PENDING', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_CONFIG_PENDING_ALARM++; count_Akut++; } break; case 'STICKY_SABOTAGE': if(status === 2) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('STICKY_SABOTAGE', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_STICKY_SABOTAGE++; count_Akut++; } break; default: if(status === 1) { ServiceMeldungTextLang = DefineServiceMessage(selector.name, native_type, status, id, "lang"); ServicemeldungMessagesLang.push(ServiceMeldungTextLang); ServiceMeldungTextKurz = DefineServiceMessage(selector.name, native_type, status, id, "kurz"); ServicemeldungMessagesKurz.push(ServiceMeldungTextKurz); addMessageToCollector('ERROR', ServiceMeldungTextKurz, ServiceMeldungTextLang); count_Akut_ERROR++; count_Akut++; } break; } count_all++; // Zählt die Gesamtanzahl der überprüften IDs } } else { if (debugLevel >= 1 ) {log(`No matching states found for ${selector.name}`);} } } // Ende der äußeren Schleife const jsonString = JSON.stringify(AktuelleSMjsonString); setState(id_JSON_Servicemeldung_Aktuell, jsonString); // Formatiere die Nachrichten let formattedMessagesLang = ServicemeldungMessagesLang.join('
'); let formattedMessagesKurz = ServicemeldungMessagesKurz.join('
'); // Speichere die formatierten Nachrichten if(count_Akut === 0){ setState(id_Text_ServicemeldungLang, MessageBeiKeinerSM); setState(id_Text_ServicemeldungKurz, MessageBeiKeinerSM); } else { setState(id_Text_ServicemeldungLang, formattedMessagesLang); setState(id_Text_ServicemeldungKurz, formattedMessagesKurz); } // Einzelne Zähler speichern setState(id_IST_UNREACH, count_Akut_UNREACH_ALARM); setState(id_IST_STICKY_UNREACH, count_Akut_Sticky_UNREACH_ALARM); setState(id_IST_LOWBAT, count_Akut_LOWBAT_ALARM); setState(id_IST_CONFIG_PENDING, count_Akut_CONFIG_PENDING_ALARM); setState(id_IST_UPDATE_PENDING, count_Akut_UPDATE_PENDING_ALARM); setState(id_IST_DEVICE_IN_BOOTLOADER, count_Akut_DEVICE_IN_BOOTLOADER_ALARM); setState(id_IST_ERROR, count_Akut_ERROR); setState(id_IST_ERROR_NON_FLAT_POSITIONING, count_Akut_ERROR_NON_FLAT_POSITIONING_ALARM); setState(id_IST_FAULT_REPORTING, count_Akut_FAULT_REPORTING); setState(id_IST_SABOTAGE, count_Akut_SABOTAGE_ALARM); setState(id_IST_ERROR_REDUCED, count_Akut_ERROR_REDUCED); setState(id_IST_STICKY_SABOTAGE, count_Akut_Sticky_SABOTAGE_ALARM); setState(id_IST_USBH_POWERFAIL, count_Akut_USBH_POWERFAIL); setState(id_IST_U_SOURCE_FAIL, count_Akut_U_SOURCE_FAIL); // Gesamtzähler speichern setState(id_IST_Gesamt, count_all); setState(id_IST_SMAktuell, count_Akut); if(debugLevel >= 2) { log(`Es wurden insgesamt ${count_all} IDs gecheckt - insgesamt gibt es ${count_Akut} Servicemeldungen`, "info"); log("Davon gibt es zur Zeit aktuelle Servicemeldungen: " + count_Akut); log("SM count_Akut_UNREACH_ALARM " + count_Akut_UNREACH_ALARM); log("SM count_Akut_CONFIG_PENDING_ALARM " + count_Akut_CONFIG_PENDING_ALARM); log("SM count_Akut_UPDATE_PENDING_ALARM " + count_Akut_UPDATE_PENDING_ALARM); log("SM count_Akut_LOWBAT_ALARM " + count_Akut_LOWBAT_ALARM); log("SM count_Akut_DEVICE_IN_BOOTLOADER_ALARM " + count_Akut_DEVICE_IN_BOOTLOADER_ALARM); log("SM count_Akut_ERROR " + count_Akut_ERROR); log("SM count_Akut_FAULT_REPORTING " + count_Akut_FAULT_REPORTING); log("SM count_Akut_SABOTAGE_ALARM " + count_Akut_SABOTAGE_ALARM); log("SM count_Akut_ERROR_NON_FLAT_POSITIONING_ALARM " + count_Akut_ERROR_NON_FLAT_POSITIONING_ALARM); log("SM count_Akut_ERROR_REDUCED " + count_Akut_ERROR_REDUCED); log("SM count_Akut_STICKY_SABOTAGE " + count_Akut_STICKY_SABOTAGE); log("SM count_Akut_U_SOURCE_FAIL " + count_Akut_U_SOURCE_FAIL); log("SM count_Akut_USBH_POWERFAIL " + count_Akut_USBH_POWERFAIL); } if(debugLevel >= 3) { console.log(`Gesammelte Servicemeldungen Lang: ${ServicemeldungMessagesLang}`); console.log(`Gesammelte Servicemeldungen Kurz: ${ServicemeldungMessagesKurz}`); } return count_Akut; } //----------------------------------------------------------------------------------------------------- // Message ERmittlung //----------------------------------------------------------------------------------------------------- function DefineServiceMessage(meldungsart, native_type, status, id, version) { if (debugLevel >= 2) { log(`Routine DefineServiceMessage wird ausgefuehrt meldungsart ${meldungsart}`, "info"); } let ServiceMessage; // Rueckgabewert let parts = id.split('.'); // Extrahiere die Geraete-ID aus der vollstaendigen ID (dritte Stelle) let commonObj = getObject(id.substring(0, id.lastIndexOf('.') - 2)); let common_name = ReplaceString(commonObj.common.name); let id_name = parts[2]; let datum_seit = func_get_datum(id); // Im Folgenden werden lange und kurze Versionen von Servicemeldungen erzeugt switch (meldungsart) { case "CONFIG_PENDING": ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${statusMessages.CONFIG_PENDING_ALARM[status]}` : `${common_name} ${statusMessages.CONFIG_PENDING_ALARM[status]}`; if (version === "lang") { AktuelleSMjsonString.push( createJsonEntry ( datum_seit, meldungsart, common_name, id_name, status, statusMessages.CONFIG_PENDING_ALARM[status], null) ); } break; case "LOW_BAT_ALARM": case "LOWBAT_ALARM": ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${statusMessages.LOWBAT_ALARM[status]} - Batteriebezeichnung: ${func_Batterie(native_type)}` : `${common_name} ${statusMessages.LOWBAT_ALARM[status]}`; if (version === "lang") { AktuelleSMjsonString.push( createJsonEntry ( datum_seit, meldungsart, common_name, id_name,status,statusMessages.LOWBAT_ALARM[status],func_Batterie(native_type) )); } break; case "STICKY_UNREACH_ALARM": ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${statusMessages.STICKY_UNREACH_ALARM[status]}` : `${common_name} ${statusMessages.STICKY_UNREACH_ALARM[status]}`; if (version === "lang") { AktuelleSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name,status,statusMessages.STICKY_UNREACH_ALARM[status],null )); } break; case "UNREACH_ALARM": ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${statusMessages.UNREACH_ALARM[status]}` : `${common_name} ${statusMessages.UNREACH_ALARM[status]}`; if (version === "lang") { AktuelleSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name,status,statusMessages.UNREACH_ALARM[status],null )); } break; case "DEVICE_IN_BOOTLOADER_ALARM": ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${statusMessages.DEVICE_IN_BOOTLOADER_ALARM[status]}` : `${common_name} ${statusMessages.DEVICE_IN_BOOTLOADER_ALARM[status]}`; if (version === "lang") { AktuelleSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name,status,statusMessages.DEVICE_IN_BOOTLOADER_ALARM[status],null )); } break; case "UPDATE_PENDING_ALARM": ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${statusMessages.UPDATE_PENDING_ALARM[status]}` : `${common_name} ${statusMessages.UPDATE_PENDING_ALARM[status]}`; if (version === "lang") { AktuelleSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name,status,statusMessages.UPDATE_PENDING_ALARM[status],null )); } break; case "SABOTAGE_ALARM": case "SABOTAGE": ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${statusMessages.SABOTAGE_ALARM[status]}` : `${common_name} ${statusMessages.SABOTAGE_ALARM[status]}`; if (version === "lang") { AktuelleSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name,status,statusMessages.SABOTAGE_ALARM[status],null )); } break; case "ERROR_NON_FLAT_POSITIONING_ALARM": ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${statusMessages.ERROR_NON_FLAT_POSITIONING_ALARM[status]}` : `${common_name} ${statusMessages.ERROR_NON_FLAT_POSITIONING_ALARM[status]}`; if (version === "lang") { AktuelleSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name,status,statusMessages.ERROR_NON_FLAT_POSITIONING_ALARM[status],null )); } break; case "ERROR": if (status >= 1 && status <= 7) { // nur wenn kein status = 0 if (errorMessages[native_type] && errorMessages[native_type][status]) { ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${errorMessages[native_type][status]}` : `${common_name} ${errorMessages[native_type][status]}`; if (version === "lang") { AktuelleSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name,status,errorMessages[native_type][status],null )); } } // endif errorMessages } // endif Status >=1... if (status === 0) { // nicht HIMIP Geräte die auf Error und status 0 stehen - Message aufgehoben fuer Historie ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - $StandardstatusMessages[status]}` : `${common_name} ${errorMessages[native_type][status]}`; if (version === "lang") { AktuelleSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name,status,StandardstatusMessages[status],null )); } } break; case "FAULT_REPORTING": if (faultMessages[native_type] && faultMessages[native_type][status]) { ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${faultMessages[native_type][status]}` : `${common_name} ${faultMessages[native_type][status]}`; if (version === "lang") { AktuelleSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name,status,faultMessages[native_type][status],null )); } } break; default: if (status < 0 || status >= StandardstatusMessages.length) { ServiceMessage = `datum_seit, meldungsart, common_name, id_name,- Ungueltiger Status`; // fuer ungueltige Statuswerte } else { ServiceMessage = version === "lang" ? `${datum_seit} - ${meldungsart} - ${common_name} - (${id_name}) - ${status} - ${StandardstatusMessages[status]}` : `${common_name} ${StandardstatusMessages[status]}`; if (version === "lang") { AktuelleSMjsonString.push(createJsonEntry(datum_seit, meldungsart, common_name, id_name,status,StandardstatusMessages[status],null )); } } } return ServiceMessage; // Nur einmal am Ende zurueckgeben } //----------------------------------------------------------------------------------------------------- // sendMessage Hier werden die Nachrichten fuer den jeweiligen Service aufbereitet //----------------------------------------------------------------------------------------------------- function sendMessage(messageType = null) { if (debugLevel >= 2) { console.log(`Routine sendMessage wird ausgeführt für messageType: ${messageType || 'alle'} (alle Nachrichten)`); } const messageTypesToProcess = messageType ? [messageType] : Object.keys(MessageSendCollector); // Kombiniere Nachrichten für jeden Dienst über alle Typen hinweg const combinedMessagesByService = {}; messageTypesToProcess.forEach((type) => { const messagesByService = MessageSendCollector[type] || {}; Object.keys(messagesByService).forEach((service) => { if (!combinedMessagesByService[service]) { combinedMessagesByService[service] = []; } combinedMessagesByService[service] = combinedMessagesByService[service].concat(messagesByService[service]); }); // Lösche den Typ nach der Verarbeitung delete MessageSendCollector[type]; }); // Sende kombinierte Nachrichten an die jeweiligen Dienste Object.keys(combinedMessagesByService).forEach((service) => { const combinedMessage = combinedMessagesByService[service].join('\n'); if (combinedMessage.trim().length > 0) { sendToService(service, combinedMessage); } }); } //----------------------------------------------------------------------------------------------------- // 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, combinedMessage) { if(debugLevel >=1){log(`Message wird versendet mit: ${service}: ${combinedMessage}`);} switch (service) { case "email": sendTo(`email.${MessengerInstanz[0]}`, "send", { text: combinedMessage, to: emailAddresse, subject: Headline }); break; case "whatsApp": sendTo(`whatsapp-cmb.${MessengerInstanz[1]}`, "send", { text: combinedMessage }); break; case "Signal": sendTo(`signal-cmb.${MessengerInstanz[2]}`, "send", { text: combinedMessage }); break; case "Telegram": sendTo(`telegram.${MessengerInstanz[3]}`, "send", { text: combinedMessage, user: TelegramUser // Telegram User ID, um den Nachrichteneempfänger festzulegen }); break; case "Pushover": sendTo(`pushover.${MessengerInstanz[4]}`, "send", { message: combinedMessage, sound: "" }); break; case "Pushsafer": sendTo(`pushsafer.${MessengerInstanz[5]}`, "send", { message: combinedMessage, title: Headline }); break; default: log(`Unbekannter Service: ${service}`, "warn"); } } //----------------------------------------------------------------------------------------------------- // addMessageToCollector Messages werden unter berücksichtigung der folgenden Objekte in den MessageCollector genommen // MessengerScope, Services, AlarmTypes, TextTypeKurz //----------------------------------------------------------------------------------------------------- function addMessageToCollector(messageType, MessageKurz, MessageLang) { // Überprüfung, ob der `messageType` in `alarmTypes` existiert const isValidMessageType = alarmTypes.some((type) => type.key === messageType); // Wenn der messageType ungültig ist, "Sonstige" als Fallback verwenden let actualMessageType = isValidMessageType ? messageType : "Sonstige"; if (!isValidMessageType && debugLevel >= 1 && messageType != "keineSM") { console.warn(`Unbekannter messageType "${messageType}". Fallback auf "Sonstige".`); } // Sonderbehandlung für "keineSM" - Diese wird behandelt wie jeder andere messageType if (messageType === "keineSM") { actualMessageType = "keineSM"; // Setze den messageType explizit auf "keineSM" } // Sicherstellen, dass der Typ im MessageSendCollector existiert if (!MessageSendCollector[actualMessageType]) { MessageSendCollector[actualMessageType] = {}; } // Überprüfen, ob der actualMessageType im MessengerScope vorhanden ist, falls nicht, 'Sonstige' verwenden let messengerConfig = MessengerScope[actualMessageType]; // Wenn der messageType nicht im MessengerScope definiert ist, benutze die Konfiguration für 'Sonstige' if (!messengerConfig) { messengerConfig = MessengerScope['Sonstige'] || Array(services.length).fill(false); } // Nachricht nur hinzufügen, wenn die Konfiguration für den Service aktiv ist messengerConfig.forEach((isActive, index) => { if (isActive) { const service = services[index]; // Nachricht nur hinzufügen, wenn die Konfiguration für den Service aktiv ist if (!MessageSendCollector[actualMessageType][service]) { MessageSendCollector[actualMessageType][service] = []; } // Auswahl von MessageKurz oder MessageLang basierend auf TextTypeKurz const messageToAdd = TextTypeKurz[index] ? MessageKurz : MessageLang; // Nachricht nur hinzufügen, wenn sie noch nicht existiert if (!MessageSendCollector[actualMessageType][service].includes(messageToAdd)) { MessageSendCollector[actualMessageType][service].push(messageToAdd); } } }); } //----------------------------------------------------------------------------------------------------- // ReplaceString // ersetzen entsprechend tabelle replacements //----------------------------------------------------------------------------------------------------- function ReplaceString(string) { for (const [key, value] of Object.entries(replacements)) { // Escape den Punkt (.) für den regulären 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) { // const datum = formatDate(getState(id).lc, "TT.MM.JJ SS:mm:ss"); const datum = formatDate(getState(id).ts, "TT.MM.JJ SS:mm:ss"); return datum < '01.01.71 01:00:00' ? '' : `${datum} Uhr`; } //----------------------------------------------------------------------------------------------------- // func_Batterie Batterieermittlung //----------------------------------------------------------------------------------------------------- function func_Batterie(native_type) { if(debugLevel >= 2) { log(`Routine func_Batterie wird ausgefuehrt`, "info");} const normalizedType = native_type.toUpperCase(); return Object.keys(batteryTypes).find(battery => batteryTypes[battery].some(device => device.toUpperCase() === normalizedType) ) || 'unbekannt'; } //----------------------------------------------------------------------------------------------------- // Funktion createJsonEntry erzeugen einer JSON Tabelle //----------------------------------------------------------------------------------------------------- function createJsonEntry(datum_seit, meldungsart, common_name, id_name, status, statusMessages, Batterie) { // Erstelle das JSON-Objekt return { datum_seit: datum_seit, meldungsart: meldungsart, common_name: common_name, id_name: id_name, status: status, status_message: statusMessages, batterie_bezeichnung: Batterie }; } //----------------------------------------------------------------------------------------------------- // Funktion schreibt einen Logeintrag in das Filesystem und auch in das interne Log-System (looxer) //----------------------------------------------------------------------------------------------------- function writelog(Name, id_name, SMType, SMStatus, SMStatus_Text) { if(debugLevel >= 2) { log(`Routine writelog wird ausgefuehrt`, "info");} const fs = require('fs'); // enable write fuer externes log if (!logflag) return; const logdate = formatDate(new Date(), "TT.MM.JJJJ"); const logtime = formatDate(new Date(), "SS:mm:ss"); const logEntry = `${logdate} ;${logtime} ;${Name} ;${id_name} ; ${SMType} ;${SMStatus} ;${SMStatus_Text}\n`; const headerLine = "Datum;Uhrzeit;Name;ID-Name;Meldungssart;Status;Servicemeldung\n"; fs.readFile(LogPath, 'utf8', function(err, data) { if (!err) { fs.appendFileSync(LogPath, logEntry, 'utf8'); } else { log("Logfile nicht gefunden - wird angelegt", "info"); fs.writeFileSync(LogPath, headerLine + logEntry, 'utf8'); } }); } //----------------------------------------------------------------------------------------------------- // Funktion zum Hinzufuegen einer neuen Zeile am Anfang des bestehenden State (looxer) //----------------------------------------------------------------------------------------------------- function appendToState(id, newText) { if(debugLevel >= 2) { log(`Routine appendToState wird ausgefuehrt`, "info");} const updatedText = newText + '
' + getState(id).val; setState(id, updatedText); } //----------------------------------------------------------------------------------------------------- // Funktion Create States //----------------------------------------------------------------------------------------------------- async function CreateStates(callback) { if (debugLevel >= 2) { log(`Routine CreateStates wird ausgefuehrt`, "info"); } try { 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_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_Text_Servicemeldung_History, "", { read: true, write: true, type: 'string', name: 'Servicemeldungen History', desc: 'History Servicemeldung' }); await createStateAsync(id_IST_Gesamt, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen Anzahl Total', desc: 'Anzahl Servicemeldungen' }); await createStateAsync(id_IST_SMAktuell, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen Anzahl Aktuelle SM', desc: 'Anzahl Aktuelle Servicemeldungen' }); await createStateAsync(id_IST_LOWBAT, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen Lowbat Anzahl Total', desc: 'Lowbat Anzahl Servicemeldungen' }); await createStateAsync(id_IST_UNREACH, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen Unreach Anzahl Total', desc: 'Unreach Anzahl Servicemeldungen' }); await createStateAsync(id_IST_STICKY_UNREACH, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen Sticky Unreach Anzahl Total', desc: 'Sticky Unreach Anzahl Servicemeldungen' }); await createStateAsync(id_IST_ERROR, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen ERROR Anzahl Total', desc: 'ERROR Anzahl Servicemeldungen' }); await createStateAsync(id_IST_CONFIG_PENDING, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen ausstehende Konfig Anzahl Total', desc: 'Ausstehende Konfig Anzahl Servicemeldungen' }); await createStateAsync(id_IST_UPDATE_PENDING, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen ausstehende Updates Anzahl Total', desc: 'Ausstehende Updates Anzahl Servicemeldungen' }); await createStateAsync(id_IST_DEVICE_IN_BOOTLOADER, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen DEVICE_IN_BOOTLOADER Anzahl Total', desc: 'DEVICE_IN_BOOTLOADER Anzahl Servicemeldungen' }); await createStateAsync(id_IST_ERROR_NON_FLAT_POSITIONING, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen ERROR_NON_FLAT_POSITIONING Anzahl Total', desc: 'ERROR_NON_FLAT_POSITIONING Anzahl Servicemeldungen' }); await createStateAsync(id_IST_FAULT_REPORTING, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen FAULT_REPORTING Anzahl Total', desc: 'FAULT_REPORTING Anzahl Servicemeldungen' }); await createStateAsync(id_IST_SABOTAGE, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen SABOTAGE Anzahl Total', desc: 'SABOTAGE Anzahl Servicemeldungen' }); await createStateAsync(id_IST_ERROR_REDUCED, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen ERROR_REDUCED Anzahl Total', desc: 'ERROR_REDUCED Anzahl Servicemeldungen' }); await createStateAsync(id_IST_STICKY_SABOTAGE, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen STICKY_SABOTAGE Anzahl Total', desc: 'STICKY_SABOTAGE Anzahl Servicemeldungen' }); await createStateAsync(id_IST_USBH_POWERFAIL , 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen USBH_POWERFAIL Anzahl Total', desc: 'USBH_POWERFAIL Anzahl Servicemeldungen' }); await createStateAsync(id_IST_U_SOURCE_FAIL, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen SOURCE_FAIL Anzahl Total', desc: 'SOURCE_FAIL Servicemeldungen' }); // Aufruf des Callbacks nach Abschluss aller Operationen if (callback) await callback(); } catch (error) { log(`Routine Create States - Fehler beim Erstellen der Zustaende: ${error}`, "warn"); } }