// Beschreibung des Scriptes: https://forum.iobroker.net/topic/77816/vorlage-servicemeldungen-volume2
// Autor Looxer01 02.11.2024 Version 1.0 (initiale Version)
// Version 1.20 - 19.11.24 Optimierung der Selektoren // Kurztext Option fuer Messenger hinzugefuegt // Einstellungsbereich restrukturiert // Code Optimierungen
// Version 1.30 - 22.11.24 Kommentare hinzugefuegt // Alarmtypes koennen fuer jede Instanz getrennt ausgeschlossen werden // Ausschlussliste geht jetzt auch bei REGA subscription
// 3 Alarmtypes entfernt (error_reduced / U_Source_Fail / USBH_POWERFAIL) // System Log kann extern gespeichert werden // LOW_BAT_ALARM -Log angepasst
// Version 1.31 - 22.11.24 Korrekturen bei Log Meldungen
// Version 1.32 - 25.11.24 SubscriptionID umgestellt auf For-Schleife // Log Messages hinzugefügt // subscribe gaendert (Es gab sporadisch nicht gemeldete Servicemeldungen)
// Schwellwert fuer Ausführungsverzögerung von 50 auf 25 reduziert // Pause reduziert von 5 auf 3 Minuten
//---------------------------------------------------------------------------------------------------------------------------------------------------------------
// 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
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 GeraeteInstanz- 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 das Protokoll der Servicemeldungen in ein externes file (Excel Format) / nur bei GeraeteIDTrigger = true
const SMProtokoll = true;
const PathSMLog = "/opt/iobroker/log/ServicemeldungenVol2.csv"; // Pfad und Dateiname des externen Logsconstst / nur bei GeraeteIDTrigger = true
//const PathSMLog = "/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 = ['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 = 0 ;
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.
// 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 subscription
// 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_STICKY_SABOTAGE = path+'Anzahl_Sticky_SABOTAGE';
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'],
};
//ErrorMessages für HM-Classic Geraete
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 für HM-Classic Geraet (Heizung)
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'] },
];
// Konfigurationsobjekt fuer Ausnahmen / es koennen Arlarmtypes je Instanz ausgeschlossen werden / Filter auf AlarmTypes
const exceptions = {
HMClassicInstanz: [],
HMIPInstanz: [],
GruppenInstanz: ['ERROR_NON_FLAT_POSITIONING_ALARM'],
WiredIClassicInstanz: ['LOWBAT_ALARM', 'LOW_BAT_ALARM','ERROR_NON_FLAT_POSITIONING_ALARM'],
};
LOG(`Kategorie:Autor: Looxer01 Datum 25.11.2024 Version ist 1.32`,`Einstellungen`,`Start`,0 )
LOG(`HMClassicInstanz: ${HMClassicInstanz}; HMIPInstanz: ${HMIPInstanz}; WiredIClassicInstanz: ${WiredIClassicInstanz}; GruppenInstanz: ${GruppenInstanz}`, "Einstellungen", "Start", 2);
LOG(`GeraeteIDTrigger: ${GeraeteIDTrigger}`, "Einstellungen", "Start", 2);
LOG(`Inhalt Messengerscope: ${JSON.stringify(MessengerScope)}`, "Einstellungen", "Start", 2);
//LOG(`Inhalt Messengerscope: ${JSON.stringify(MessengerScope, null, 2)}`, "Einstellungen", "Start", 2);
LOG(`Inhalt Services: ${JSON.stringify(services)}`, "Einstellungen", "Start", 2);
LOG(`Inhalt Instanzen: ${JSON.stringify(MessengerInstanz)}`, "Einstellungen", "Start", 2);
LOG(`Inhalt TextTypeKurz: ${JSON.stringify(TextTypeKurz)}`, "Einstellungen", "Start", 2);
//-----------------------------------------------------------------------------------------------------
//Ende Einstellungen
//-----------------------------------------------------------------------------------------------------
let totalIdsCount = 0; // Variable für die Gesamtzahl der IDs
const selectors = alarmTypes.map(alarmType => {
const collectedIds = [];
instanceIds.forEach(instance => {
if (instance.id !== 9) {
// Prüfen, ob der aktuelle Alarmtype für die Instanz ausgeschlossen ist
const excludedAlarmTypes = exceptions[instance.name] || []; // Hole Ausnahmen oder leere Liste
if (!excludedAlarmTypes.includes(alarmType.key)) { // Wenn nicht in der Ausnahme
alarmType.suffixes.forEach(suffix => {
// @ts-ignore
$(`state[id=hm-rpc.${instance.id}.*.${suffix}]`).each(id => collectedIds.push(id)); // IDs sammeln
});
}
}
});
const filteredIds = collectedIds.filter(id => { // IDs anhand der Geräte-IDs in der Ausschlussliste filtern
const deviceId = id.split('.')[2]; // Geräte-ID aus der vollständigen 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 += filteredIds.length; // IDs zur Gesamtzahl hinzufügen
return { name: alarmType.key, ids: filteredIds };
});
LOG(`Es wurden ${totalIdsCount} IDs für alle Alarmtypen gefunden. Alle drei Filter (Instanzen, Alarmtypen, Einzel-IDs) angewendet`, "Ablauf", "Start", 0);
// Ergebnisse loggen (pro Alarmtype)
selectors.forEach(selector => { // Ergebnisse loggen (pro Alarmtyp)
if(SystemLog) {
LOG(`für den Alarmtype: ${selector.name} wurden ${selector.ids.length} IDs gefunden${debugLevel >= 3 ? ` ${JSON.stringify(selector.ids)}` : ''}`, "Ablauf", "Start", 3);
}else{
LOG(`für den Alarmtype: ${selector.name} wurden ${selector.ids.length} IDs gefunden${debugLevel >= 3 ? ` ${JSON.stringify(selector.ids)}` : ''}`, "Ablauf", "Start", 0);
}
});
let AktuelleSMjsonString = [] // alle aktuellen Servicemeldungen als JSON
// Aufbau MessageCollector - Beispiel
//{ "ERROR": { "Email": [ "Error Alarm " ], "SMS": ["Error" ] },
// "SABOTAGE_ALARM": { "Email": [ "Alarm: Eindringling im Gebaeude!"], "SMS": [ "Eindringling" ] } }
let MessageSendCollector = {}; // alle aktuellen Servicemeldungen zum senden an messaging services
// Initialisierung des Objekts MessageCollector fuer alle Alarmtypen und Services
alarmTypes.forEach((type) => {
const alarmKey = type.key; // Verwende die `key`-Eigenschaft als Schluessel
MessageSendCollector[alarmKey] = {}; // Erstelle ein Objekt fuer den Alarmtyp
services.forEach((service) => {
MessageSendCollector[alarmKey][service] = []; // Erstelle ein leeres Array fuer jeden Service
});
});
// create States
CreateStates(() => {
Check_All() // Check_All wird aufgerufen, wenn CreateStates fertig ist (callback)
});
// Subscriptions erstellen
if (GeraeteIDTrigger) { // Subscription auf IDs
SubscribeGeraeteID();
}else{ // Subscription auf HMREGA (nur 1 Subscripton)
SubscribeRegaDP();
}
//-----------------------------------------------------------------------------------------------------
// Schedule zum Loeschen des Datenpunktwertes der Histore einmal pro Tag
//-----------------------------------------------------------------------------------------------------
schedule(scheduleTimeClearSMTexte, function() {
setState(id_Text_Servicemeldung_History, '');
setState(id_JSON_Servicemeldung_Historie, []);
LOG(`Datenpunkte (Text und JSON) ${id_Text_Servicemeldung_History} geloescht`,`Ablauf`,`Start`,2);
});
//-----------------------------------------------------------------------------------------------------
// Funktion SubscribeRegaDP
// Erstellung der Subscriptions auf REGA Datenpunkt // es werden 5 Sekunden lang vorliegende Aenderungen gesammelt
//-----------------------------------------------------------------------------------------------------
// Initalisierung Variablen fuer den Timer, um die 5-Sekunden-Wartezeit zu steuern - relevant fuer HM-REGA subscription
let changeTimeout = null; // Globale Variable - wird in funtion servicemeldung wieder zurückgesetzt
let countIdsInnerhalbTimeout = 0; // Globale Variable - wird in funtion servicemeldung wieder zurückgesetzt
function SubscribeRegaDP() {
LOG(`Routine wird ausgefuehrt - Pfad des Datenpunkts: ${PathRega}`, "Ablauf", "SubscribeRegaDP", 2);
on({id: PathRega, change: 'any'}, function (obj) {
LOG(`Subscription Datenpunkt ${PathRega} getriggert. Neuer Wert: ${obj.state.val} alter Wert:${obj.oldState.val}`, "Ablauf", "SubscribeRegaDP", 2);
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++;
LOG(`Subscription Datenpunkt wird ausgefuehrt ${PathRega} aktiv. Neuer Wert: ${obj.state.val}`, "Ablauf", "SubscribeRegaDP", 2);
if (changeTimeout) { // Falls schon ein Timer läuft, diesen abbrechen
clearTimeout(changeTimeout);
}
changeTimeout = setTimeout(function() { // Setze einen neuen Timer auf 5 Sekunden, um die Funktion Servicemeldung aufzurufen
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() {
LOG(`Routine SubscribeGeraeteID wird ausgefuehrt`, "Ablauf", "SubscribeGeraeteID", 2);
let callCount = 0; // Zähler für Aufrufe // maximal 25
let timeoutActive = false; // Status für Timeout
for (let j = 0; j < selectors.length; j++) { // Schleife über alle Selectoren
const selector = selectors[j];
if (selector.ids.length > 0) { // Schleife über IDs der Selektoren
LOG(`Erstellung der Subscriptions - Prozessiere selector: ${selector.name}`, "Ablauf", "SubscribeGeraeteID", 2);
for (let i = 0; i < selector.ids.length; i++) { // Schleife über alle IDs des aktuellen Selectors
const id = selector.ids[i];
LOG(`Erstellung der Subscriptions - Prozessiere Datenpunkt: ${id}`, "Ablauf", "SubscribeGeraeteID", 3);
on({ id: id, change: "ne" }, obj => {
if (obj.state.val === obj.oldState.val) {
LOG(`Datenpunkt ${obj.id} hatte keine Aenderung`, "Ablauf", "SubscribeGeraeteID", 2);
return; // Wenn der Wert nicht geändert wurde, keine weitere Verarbeitung
}
LOG(`Subscription Datenpunkt Wert geaendert fuer ${obj.id} wird ausgefuehrt`, "Ablauf", "SubscribeGeraeteID", 1);
if (timeoutActive) {
return; // Wenn der Timeout aktiv ist, keine weitere Verarbeitung
}
callCount++;
if (callCount >= 25) { // Wenn mehr als 25 Aufrufe innerhalb von 1 Sekunde
timeoutActive = true; // Wartezeit aktivieren
LOG("Zu viele Servicemeldungen wurden generiert, warte 5 Minuten.", "WARN", "SubscribeGeraeteID", 0, "warn");
setTimeout(() => { // Timeout nach 5 Minuten
timeoutActive = false; // Timeout beenden
callCount = 0; // Zähler zurücksetzen
LOG("Wartezeit beendet, weitere Servicemeldungen sind jetzt moeglich.", "Ablauf", "SubscribeGeraeteID", 0);
}, 3 * 60 * 1000); // 3 Minuten in Millisekunden
}
Servicemeldung(obj);
setTimeout(() => { // Zurücksetzen des Zählers nach 1 Sekunde
callCount = Math.max(0, callCount - 1);
}, 1000);
});
} // endloop ids per selector
} else {
LOG(`No matching states found for ${selector.name}`, "Ablauf", "SubscribeGeraeteID", 2);
} // endfor loop selector
} //endif Selector
} // endfunction
//-----------------------------------------------------------------------------------------------------
// Kernfunktion Sevicemeldung
// erstmal die aktuelle Servicemeldung analysieren und loggen
//-----------------------------------------------------------------------------------------------------
function Servicemeldung(obj) {
LOG("Routine Servicemeldung wird ausgefuehrt", "Ablauf", "Servicemeldung", 2);
if (!GeraeteIDTrigger) { // nur wenn ueber REGA Servicemeldungen von der CCU reagiert werden soll
LOG(`Es wurden insgesamt ${countIdsInnerhalbTimeout} aenderungen festgestellt`, "Ablauf", "Servicemeldung", 1);
countIdsInnerhalbTimeout = 0; // Reset der Zaehlung
changeTimeout = null; // Reset des Timeouts fuer den REGA Trigger
const AnzahlSM = Check_All();
const regaState = getState(PathRega).val;
LOG(`REGA Anzahl SM: ${regaState} AnzahlSM: ${AnzahlSM}`, "Ablauf", "Servicemeldung", 1);
if (regaState === 0 || AnzahlSM === 0) {
setState(id_Text_ServicemeldungLang, MessageBeiKeinerSM);
setState(id_Text_ServicemeldungKurz, MessageBeiKeinerSM);
addMessageToCollector('keineSM', MessageBeiKeinerSM, MessageBeiKeinerSM);
sendMessage('keineSM');
return;
}
sendMessage();
return;
} // Ende Statements fuer HMREGA Subscription
// in diesem Teil werden die subscriptions (Servicemeldungen) bei GeraeteIDTriggerID = true in die Historie geschrieben
const AnzahlSM = Check_All();
const id_name = obj.id.split('.')[2];
if (!existsState(obj.id)) {
LOG(`Geraet ${id_name} scheint zwischenzeitlich nicht mehr zu existieren - Empfehlung: script neu starten`, "WARN", "Servicemeldung", 0, "warn");
return;
}
if (AnzahlSM === 0) { // keine Servicmeldungen
delete MessageSendCollector['keineSM'];
addMessageToCollector('keineSM', MessageBeiKeinerSM, MessageBeiKeinerSM);
sendMessage('keineSM');
}
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");
LOG(`neue Servicemeldung ist ${status_textLang} Meldungsart ist ${meldungsart}`, "Ergebnis", "Servicemeldung", 1);
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")
LOG(`Status ist 0 - status_text ist ${status_textLang} Meldungsart ist ${meldungsart}`, "Ablauf", "Servicemeldung", 2);
}
writelog(common_name, id_name, meldungsart, status, status_textLang); // externes log erzeugen
appendToState(id_Text_Servicemeldung_History, status_textLang); // Historische Servicemeldung als Text speichern
//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} - Historie ohne update`, "Fehler", "Servicemeldung", 0);
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 und Servicemeldungentexte speichern
//-----------------------------------------------------------------------------------------------------
function Check_All() {
LOG("Routine Check_All wird ausgefuehrt", "Ablauf", "Check_All", 2);
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_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, um "continue" zu ermoeglichen
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(`Geraet ${GeraeteID} scheint zwischenzeitlich nicht mehr zu existieren - Empfehlung: script neu starten`, "WARN", "Check_All", 0, "warn");
continue; // ueberspringe dieses Element und fahre mit der naechsten fort
}
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(`Das Objekt mit ID ${id} oder die native.TYPE-Eigenschaft ist nicht vorhanden - wird uebersprungen.`, "Fehler", "Check_All", 0, "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 ueberpruefung 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(selector.name, 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(selector.name, 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(selector.name, 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(selector.name, 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(selector.name, 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(selector.name, ServiceMeldungTextKurz, ServiceMeldungTextLang);
if (status === 7) {
count_Akut_SABOTAGE_ALARM++; // Nur fuer 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(selector.name, 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(selector.name, 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(selector.name, 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(selector.name, 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(selector.name, ServiceMeldungTextKurz, ServiceMeldungTextLang);
count_Akut_STICKY_SABOTAGE++;
count_Akut++;
}
break;
default:
if(status === 1) { // fuer alle MessageTypes, die zuvor nicht explizit behandelt wurden
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++; // Zaehlt die Gesamtanzahl der ueberprueften IDs
}
} else {
LOG(`No matching states found for ${selector.name}`, "Ablauf", "Check_All", 1);
}
} // Ende der aeusseren Schleife
const jsonString = JSON.stringify(AktuelleSMjsonString); // AtkuelleSMjasonString wird gefuellt in Funktion DefineServiceMessage
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 Zaehler 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_STICKY_SABOTAGE, count_Akut_Sticky_SABOTAGE_ALARM);
// Gesamtzaehler speichern
setState(id_IST_Gesamt, count_all);
setState(id_IST_SMAktuell, count_Akut);
LOG(`Es wurden insgesamt ${count_all} IDs gecheckt - insgesamt gibt es ${count_Akut} Servicemeldungen`, "Ergebnis", "Check_All", 2);
LOG(`Davon gibt es zur Zeit aktuelle Servicemeldungen: ${count_Akut}`, "Ergebnis", "Check_All", 2);
LOG(`SM count_Akut_UNREACH_ALARM ${count_Akut_UNREACH_ALARM}`, "Ergebnis", "Check_All", 2);
LOG(`SM count_Akut_CONFIG_PENDING_ALARM ${count_Akut_CONFIG_PENDING_ALARM}`, "Ergebnis", "Check_All", 2);
LOG(`SM count_Akut_UPDATE_PENDING_ALARM ${count_Akut_UPDATE_PENDING_ALARM}`, "Ergebnis", "Check_All", 2);
LOG(`SM count_Akut_LOWBAT_ALARM ${count_Akut_LOWBAT_ALARM}`, "Ergebnis", "Check_All", 2);
LOG(`SM count_Akut_DEVICE_IN_BOOTLOADER_ALARM ${count_Akut_DEVICE_IN_BOOTLOADER_ALARM}`, "Ergebnis", "Check_All", 2);
LOG(`SM count_Akut_ERROR ${count_Akut_ERROR}`, "Ergebnis", "Check_All", 2);
LOG(`SM count_Akut_FAULT_REPORTING ${count_Akut_FAULT_REPORTING}`, "Ergebnis", "Check_All", 2);
LOG(`SM count_Akut_SABOTAGE_ALARM ${count_Akut_SABOTAGE_ALARM}`, "Ergebnis", "Check_All", 2);
LOG(`SM count_Akut_ERROR_NON_FLAT_POSITIONING_ALARM ${count_Akut_ERROR_NON_FLAT_POSITIONING_ALARM}`, "Ergebnis", "Check_All", 2);
LOG(`SM count_Akut_STICKY_SABOTAGE ${count_Akut_STICKY_SABOTAGE}`, "Ergebnis", "Check_All", 2);
LOG(`Gesammelte Servicemeldungen Lang: ${JSON.stringify(ServicemeldungMessagesLang, null, 2)}`, "Ergebnis", "Check_All", 3);
LOG(`Gesammelte Servicemeldungen Kurz: ${JSON.stringify(ServicemeldungMessagesKurz, null, 2)}`, "Ergebnis", "Check_All", 3);
return count_Akut;
}
//-----------------------------------------------------------------------------------------------------
// Message ERmittlung
//-----------------------------------------------------------------------------------------------------
function DefineServiceMessage(meldungsart, native_type, status, id, version) {
LOG(`Routine DefineServiceMessage wird ausgefuehrt meldungsart ${meldungsart}`, "Ablauf", "DefineServiceMessage", 2);
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 Geraete 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) {
LOG(`Routine sendMessage wird ausgefuehrt meldungsart ${messageType}`, "Ablauf", "sendMessage", 2);
const messageTypesToProcess = messageType ? [messageType] : Object.keys(MessageSendCollector);
const combinedMessagesByService = {}; // Kombiniere Nachrichten fuer jeden Dienst ueber alle Typen hinweg
messageTypesToProcess.forEach((type) => {
const messagesByService = MessageSendCollector[type] || {};
Object.keys(messagesByService).forEach((service) => {
if (!combinedMessagesByService[service]) {
combinedMessagesByService[service] = [];
}
combinedMessagesByService[service] = combinedMessagesByService[service].concat(messagesByService[service]);
});
// Loesche 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);
}
});
} // ende Funktion
//-----------------------------------------------------------------------------------------------------
// 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) {
LOG(`Message wird versendet mit: ${service}: ${combinedMessage}`, "Ablauf", "sendToService", 1);
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 Nachrichteneempfaenger 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", "sendToService", "warn");
}
}
//-----------------------------------------------------------------------------------------------------
// addMessageToCollector Messages werden unter beruecksichtigung der folgenden Objekte in den MessageCollector genommen
// MessengerScope, Services, AlarmTypes, TextTypeKurz
//-----------------------------------------------------------------------------------------------------
function addMessageToCollector(messageType, MessageKurz, MessageLang) {
// ueberpruefung, ob der `messageType` in `alarmTypes` existiert
const isValidMessageType = alarmTypes.some((type) => type.key === messageType);
// Wenn der messageType ungueltig ist, "Sonstige" als Fallback verwenden
let actualMessageType = isValidMessageType ? messageType : "Sonstige";
if (!isValidMessageType && debugLevel >= 1 && messageType != "keineSM") {
LOG(`messageType ${messageType} - umgeleitet auf Sonstige`, "Ablauf", "addMessageToCollector", 3);
}
// Sonderbehandlung fuer "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] = {};
}
// ueberpruefen, 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 fuer 'Sonstige'
if (!messengerConfig) {
messengerConfig = MessengerScope['Sonstige'] || Array(services.length).fill(false); // false ist fallback - messengerConfig ist ein array
}
// Nachricht nur hinzufuegen, wenn die Konfiguration fuer den Service aktiv ist
messengerConfig.forEach((isActive, index) => {
if (isActive) {
const service = services[index];
// Nachricht nur hinzufuegen, wenn die Konfiguration fuer 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 hinzufuegen, 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 (.) 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) {
// 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) {
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)
) || '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) {
LOG(`Routine wird ausgefuehrt`, "Ablauf", "writelog", 2);
const fs = require('fs'); // enable write fuer externes log
if (!SMProtokoll) 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(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"}
// Wenn SystemLog false ist und der Debug-Level hoeher oder gleich dem uebergebenen Level, schreibe normalen Logeintrag
if (!SystemLog && debugLevel >= Level) { log(Message+" Routine:"+Routine,type); return;}
if (Level === 0) { log(Message+" Routine:"+Routine,type);} // bei level 0 soll auf jeden fall auch in das normale log geschrieben werden
// Wenn SystemLog true ist und der Debug-Level hoeher oder gleich dem uebergebenen Level
if (SystemLog && debugLevel >= 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 zum Hinzufuegen einer neuen Zeile am Anfang des bestehenden State (looxer)
//-----------------------------------------------------------------------------------------------------
function appendToState(id, newText) {
LOG(`Routine wird ausgefuehrt`, "Ablauf", "appendToState", 3);
const updatedText = newText + '
' + getState(id).val;
setState(id, updatedText);
}
//-----------------------------------------------------------------------------------------------------
// Funktion Create States
//-----------------------------------------------------------------------------------------------------
async function CreateStates(callback) {
LOG(`Kategorie:Ablauf; Routine:CreateStates; Routine wird ausgefuehrt`,2)
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_STICKY_SABOTAGE, 0, { read: true, write: true, type: 'number', name: 'Servicemeldungen STICKY_SABOTAGE Anzahl Total', desc: 'STICKY_SABOTAGE Anzahl Servicemeldungen' });
// Aufruf des Callbacks nach Abschluss aller Operationen
if (callback) await callback();
} catch (error) {
LOG(`Kategorie:WARN; Routine:CreateStates; Routine Create States - Fehler beim Erstellen der Zustaende: ${error}`,0, "warn");
}
}