NEWS
Skript AlleStörungen
-
Hallo. Heute stelle ich euch einmal mein Skript "Alle Störungen" vor. Dieses Skript liest Daten einer Website (in diesem Falle allestörungen.de [at] [ch]) aus. Es ermittelt wie auch andere Skripts (z.B. Wetter, Luftdaten, Pollen, Biowetter, usw.) Daten und schreibt diese zur Weiterverarbeitung in einzelne Datenpunkte.
Habe zwar nicht daran gedacht gleich ein Skript zu erstellen, aber man hatte bei Problemen doch dann schon mal auf der genannten Seite nachgesehen.
Da wir ja viele Daten sammeln, speichern und visualisieren - ist dies vielleicht ein weiteres Skript für Nutzer vom ioBroker.
//Version 1.0.1 - 20.09.2024 //Ersteller Ro75. //Voraussetzungen (Version 1.0.0 getestet mit) //NodeJS: 20.x //Javascript-Adapter: 8.7.6 //Admin-Adapter: 7.0.23 //JS-Controller: 6.0.11 const sMainPath = '0_userdata.0.allestoerungen.'; //Zentraler Datenpunkt zur Datenablage const sMainURL = 'https://xn--allestrungen-9ib.de/stoerung/'; //Website die ausgelesen wird //Konfiguration der auszulesenden Dienste - kann frei zusammengestellt werden (Suche auf https://xn--allestrungen-9ib.de/ verwenden) //Dienste werden durch ein "," (KOMMA) getrennt //Innterhalb eines Dienstes gibt es zwie Werte, gertrennt durch ":" (Doppelpunkt) //Die linke Seite repräsentiert die Bezeichnung, die rechte Seite den Unterpfad in der Website const aDienste = 'Amazon Alexa:amazon-alexa,Amazon:amazon,Amazon Music:amazon-music,Amazon Prime Video:amazon-prime-instant-video,Amazon Web Services:aws-amazon-web-services,Blau:blau-de,Cloudflare:cloudflare,DHL:dhl,Disney+:disney-plus,EA:ea,Facebook:facebook,Facebook Messenger:facebook-messenger,Gmail:gmail,GMX:gmx,Google:google,Google Calendar:google-calendar,Hue:hue,Instagram:instagram,Microsoft 365:microsoft-365,Netatmo:netatmo,Netflix:netflix,Paramount+:paramountplus,Paypal:paypal,Playstation Network:playstation-network,Snapchat:snapchat,SKY:sky,Telekom:deutsche-telekom,TikTok:tiktok,TuneIn:tunein,Visa:visa,Volksbanken Raiffeisenbanken:volksbanken-und-raiffeisenbanken,Whatsapp:whatsapp,X (Twitter):Twitter,Xbox Live:xbox-live,YouTube:youtube'; //Anlage der Datenpunkte, basierend auf const aDienste function Initalisierung(){ let dienste = aDienste.split(','); for (let i = 0; i <= dienste.length-1; i++) { let dienste_data = dienste[i].split(':'); //Hauptordner createState(sMainPath+dienste_data[0], '', {name: dienste_data[0],type: 'string', read: true, write: true}); //Name vom Dienst createState(sMainPath+dienste_data[0]+'.name', dienste_data[0], {name: 'Dienst', type: 'string', read: true, write: true, desc: 'Dienst'}); //Logo vom Dienst createState(sMainPath+dienste_data[0]+'.image_url', '', {name: 'Logo',type: 'string', read: true, write: true, desc: 'Logo'}); //Störung laut Website (Datum + Uhrzeit) createState(sMainPath+dienste_data[0]+'.disruption', '', {name: 'Störung',type: 'string', read: true, write: true, desc: 'Störung'}); // Störung % Satz 1 createState(sMainPath+dienste_data[0]+'.disruption_1', 0, {name: 'Störung 1',type: 'number', read: true, write: true, desc: 'Störung Satz 1', unit: '%'}); createState(sMainPath+dienste_data[0]+'.disruption_desc_1', '', {name: 'Beschreibung Störung 1',type: 'string', read: true, write: true, desc: 'Beschreibung Störung Satz 1'}); // Störung % Satz 2 createState(sMainPath+dienste_data[0]+'.disruption_2', 0, {name: 'Störung 2',type: 'number', read: true, write: true, desc: 'Störung Satz 2', unit: '%'}); createState(sMainPath+dienste_data[0]+'.disruption_desc_2', '', {name: 'Beschreibung Störung 2',type: 'string', read: true, write: true, desc: 'Beschreibung Störung Satz 1'}); // Störung % Satz 3 createState(sMainPath+dienste_data[0]+'.disruption_3', 0, {name: 'Störung 3',type: 'number', read: true, write: true, desc: 'Störung Satz 3', unit: '%'}); createState(sMainPath+dienste_data[0]+'.disruption_desc_3', '', {name: 'Beschreibung Störung 3',type: 'string', read: true, write: true, desc: 'Beschreibung Störung Satz 1'}); // Daten-Array der letztes 24 Stunden bezogen auf Werte createState(sMainPath+dienste_data[0]+'.data_plain', '', {name: 'Werte',type: 'string', read: true, write: true, desc: 'Werte'}); // Daten-Array der letztes 24 Stunden bezogen auf Zeit createState(sMainPath+dienste_data[0]+'.data_time_plain', '', {name: 'Zeit',type: 'string', read: true, write: true, desc: 'Zeit'}); // Daten-Array der letztes 24 Stunden als JSON - kann direkt weiter verarbeitet werden (auch Grafana) createState(sMainPath+dienste_data[0]+'.data_json', '', {name: 'JSON',type: 'string', def: '[]', read: true, write: true, desc: 'JSON Widget/Simple-API/Grafana'}); // Min Meldungen der letzten 24 Stunden createState(sMainPath+dienste_data[0]+'.min', 0, {name: 'Min',type: 'number', read: true, write: true, desc: 'kleinster Wert'}); // Max Meldungen der letzten 24 Stunden createState(sMainPath+dienste_data[0]+'.max', 0, {name: 'Max',type: 'number', read: true, write: true, desc: 'größter Wert'}); // Meldungen letzter Stand createState(sMainPath+dienste_data[0]+'.last', 0, {name: 'Last',type: 'number', read: true, write: true, desc: 'letzter Wert'}); // Schwellenpunkt für eigenen Alarm - standardmäßig sind je Dienst 300 eingetragen, kann individuell im DP angepasst werden createState(sMainPath+dienste_data[0]+'.alarm_threshold', 300, {name: 'Threshold',type: 'number', read: true, write: true, desc: 'Schwelle Alarm'}); // Alarm, resultierend aus 'alarm_threshold' und 'last' createState(sMainPath+dienste_data[0]+'.alarm', false, {name: 'Alarm',type: 'boolean', read: true, write: true, desc: 'Alarm'}); // stand der Daten createState(sMainPath+dienste_data[0]+'.lasttimestand', '', {name: 'letzter Stand',type: 'string', read: true, write: true, desc: 'letzter Stand'}); //Hinweis: mit den Werten 'data_*', min, max, last, alarm ist eine umfangreiche Visualisierung möglich } //JSON createState(sMainPath+'data_json', '', {name: 'JSON',type: 'string', def: '[]', read: true, write: true, desc: 'JSON Widget'}); //erster Datenabruf Dienste_Main(); } //START Initalisierung(); //automatisierte Datenabfrage: standard sind 30 Minuten - kann geändert werden (aber nicht zu oft - keine Erfahrung mit blocken) schedule('*/30 * * * *', Dienste_Main); //Hauptfunktion async function Dienste_Main(){ let dienste = aDienste.split(','); for (let i = 0; i <= dienste.length-1; i++) { let dienste_data = dienste[i].split(':'); let dienst_url = dienste_data[1]; try { let response = await httpGetAsync(sMainURL+dienst_url+'/', {timeout: 10000}); if (response.statusCode == 200) { //Logo vom Dienst await Dienst_Image(response.data,sMainPath+dienste_data[0]); //Wann war letzte Störung, laut Website await Dienst_letzte_stoerung(response.data,sMainPath+dienste_data[0]); //Abruf der Hauptdaten () await Dienst_Data(response.data,sMainPath+dienste_data[0]); //Indikator in % für Probleme mit Festnetz Internet await IndicatorChart(1,response.data,sMainPath+dienste_data[0]); //Indikator in % für Probleme mit Mobiles Internet await IndicatorChart(2,response.data,sMainPath+dienste_data[0]); //Indikator in % für Probleme mit Total Blackout await IndicatorChart(3,response.data,sMainPath+dienste_data[0]); setState(sMainPath+dienste_data[0]+'.lasttimestand',new Date().toDateString() + ' '+ new Date().toTimeString(),true); } response = null; } catch (error) { console.error('AlleStörungen:', error); } } setTimeout(function(){ CreateJSON(); },1000); } async function Dienst_Data(sData,sMainDP){ let Position = sData.indexOf("data: ["); if (Position > 0) { var info = sData.substr(Position+7, 10000); var pos = info.indexOf("],"); var str = info.substr(0, pos).trim(); var daten = await Dienst_Data_Details(str); var Splitdaten = daten.split('||'); setState(sMainDP+'.data_json',Splitdaten[0],true); setState(sMainDP+'.data_plain',Splitdaten[1],true); setState(sMainDP+'.data_time_plain',Splitdaten[2],true); setState(sMainDP+'.min',parseInt(Splitdaten[3]),true); setState(sMainDP+'.max',parseInt(Splitdaten[4]),true); setState(sMainDP+'.last',parseInt(Splitdaten[5]),true); var bAlert = false; if (parseInt(Splitdaten[5]) >= getState(sMainDP+'.alarm_threshold').val) { bAlert = true; } setState(sMainDP+'.alarm',bAlert,true); info = null; } } async function Dienst_Data_Details(sData){ let JSON_List = []; var Plain_List = ''; var Time_List = ''; var lMin = 100000000; var lMax = 0; var lLast = 0; let sDataDetail = sData.split('},'); for (let i = 0; i <= sDataDetail.length-2; i++) { let dData = sDataDetail[i].replace('{ x: ','').replace(' y: ','').replace('+00:00','.000Z').replace(/[&\/\\'"*?<>{}]/g,'').replace('\n','').trim(); dData = dData.split(','); JSON_List.push({"time": dData[0], "value": dData[1]}); if (Plain_List == '') { Plain_List = dData[1]; } else { Plain_List = Plain_List+','+dData[1]; } if (lMax < parseFloat(dData[1])) { lMax = parseFloat(dData[1]); } if (lMin > parseFloat(dData[1])) { lMin = parseFloat(dData[1]); } if (Time_List == '') { Time_List = dData[0]; } else { Time_List = Time_List+','+dData[0]; } lLast = dData[1]; } return JSON.stringify(JSON_List)+'||'+Plain_List+'||'+Time_List+'||'+lMin+'||'+lMax+'||'+lLast; } async function Dienst_Image(sData,sMainDP){ let Position = sData.indexOf("'og:image' content='"); if (Position > 0) { var info = sData.substr(Position+20, 200); var pos = info.indexOf("'>"); var str = info.substr(0, pos); setState(sMainDP+'.image_url',str,true); info = null; } } async function Dienst_letzte_stoerung(sData,sMainDP){ let Position = sData.indexOf("<p class='text-danger'>"); if (Position > 0) { var info = sData.substr(Position+23, 200); var pos=info.indexOf("</p>"); var str = info.substr(0, pos).replace('Letzte Störung: ',''); setState(sMainDP+'.disruption',str.trim(),true); info = null; } else { setState(sMainDP+'.disruption','',true); } } async function IndicatorChart(lItem,sData,sMainDP){ var lValue = 0; var Position = sData.indexOf("<div class='col-4'>"); var info = sData.substr(Position+19, 30000); var Position2 = info.indexOf("indicator_"+lItem); if (Position2 > 0) { var info2 = info.substr(Position2+11, 5000); var Position3 = info2.indexOf("tage'>"); if (Position3 > 0) { var info3 = info2.substr(Position3+6, 50); var pos=info3.indexOf("</div>"); var str = info3.substr(0, pos); lValue = parseInt(str); } } setState(sMainDP+'.disruption_'+lItem,lValue,true); //Beschreibung var sInfo = ''; if (Position2 > 0) { var info2 = info.substr(Position2+11, 5000); var Position3 = info2.indexOf("_name'>"); if (Position3 > 0) { var info3 = info2.substr(Position3+7, 200); var pos=info3.indexOf("</div>"); var str = info3.substr(0, pos); sInfo = str.trim(); } } setState(sMainDP+'.disruption_desc_'+lItem,sInfo,true); } function CreateJSON() { let JSON_List = []; let dienste = aDienste.split(','); for (let i = 0; i <= dienste.length-1; i++) { var rDienst = sMainPath+dienste[i].split(':')[0]; var dis1 = getState(rDienst+'.disruption_desc_1').val; if (dis1 != '') { dis1 = dis1 + ': '+getState(rDienst+'.disruption_1').val+'%'; } var dis2 = getState(rDienst+'.disruption_desc_2').val; if (dis2 != '') { dis2 = dis2 + ': '+getState(rDienst+'.disruption_2').val+'%'; } var dis3 = getState(rDienst+'.disruption_desc_3').val; if (dis3 != '') { dis3 = dis3 + ': '+getState(rDienst+'.disruption_3').val+'%'; } var alarm = ''; if (getState(rDienst+'.alarm').val == false) { alarm = 'nein'; } else { alarm = 'ja'; } JSON_List.push({"dienst": dienste[i].split(':')[0], "logo": getState(rDienst+'.image_url').val, "min": getState(rDienst+'.min').val, "max": getState(rDienst+'.max').val, "last": getState(rDienst+'.last').val, "stoerung1": dis1, "stoerung2": dis2, "stoerung3": dis3, "alarm": alarm}); } setState(sMainPath+'data_json', JSON.stringify(JSON_List), true); JSON_List = []; }
Im Grunde sollte alles selbsterklärend sein. Die hinzugefügten Kommentare geben Info und Einstellmöglichkeiten. Für Anregungen bin ich offen, auch wenn es ggfs. nicht gleich umgesetzt werden kann (die gute Zeit).
Visualisierung als Liste
Visualisierung mit Grafana (via Simple-API)
Ich wünsche euch viel Spaß.
Ro75.
NACHTRAG 20.09.2024:
Das Skript kann auch für Österreich und Schweiz Anwendung finden. Dazu muss die Konstante "sMainURL" angepasst werden.
Österreich: const sMainURL = 'https://xn--allestrungen-9ib.at/stoerung/'; Schweiz: const sMainURL = 'https://xn--allestrungen-9ib.ch/stoerung/';
ACHTUNG:
Die Auflistung der Dienste in der Konstante "aDienste" muss aber für das jeweilige Land individuell angepasst werden, da es in jedem Land spezifische Dienste gibt, die in anderen Ländern nicht bekannt sind. Wird also ein Dienst in Österreich verwendet, welcher nur in Deutschland verfügbar ist (z.B. All-Inkl), wird es zu Fehlermeldungen bei der Auswerung kommen.Version 1.01 - 20.09.2024
- Zentrale JSON hinzugefügt
- Fehlerbehandlung
-
@ro75 sagte in Skript AlleStörungen:
const sMainURL = 'https://allestörungen.de/stoerung/'; //Website die ausgelesen wird
allestrungen
Stimmt die Url?
Geht das auch für Österreich?
-
@sigi234 die von mir angegebene URL ist korrekt, wegen dem Umlaut. Sollte aber auch allestoerungen.de gehen.
Gibt es für Österreich eine separate Adresse? Hab ich jetzt noch nicht geschaut. Aber ändere es mal statt .de in .at ab
Edit: eventuell musst du aber die Liste der Dienste anpassen, da es einige in Österreich nicht gibt.
Ro75
-
@ro75 die Idee selber finde ich spannend.
Ohne das Skript jetzt am PC angeschaut zu haben (etwas nervig auf dem Handy), würde ich es gut finden, wenn man seine PLZ sowie die genutzten Dienste angeben könnte.
Vllt. findet sich ja jemand, der das Skript später zu einem Adapter macht
-
@ro75 sagte in Skript AlleStörungen:
@sigi234 die von mir angegebene URL ist korrekt, wegen dem Umlaut. Sollte aber auch allestoerungen.de gehen.
Gibt es für Österreich eine separate Adresse? Hab ich jetzt noch nicht geschaut. Aber ändere es mal statt .de in .at ab
Edit: eventuell musst du aber die Liste der Dienste anpassen, da es einige in Österreich nicht gibt.
Ro75
Funktioniert mit at wunderbar, Danke
-
@kuddel schaue es dir an. Die Dienste kannst du selbst bestimmen.
Ro75
-
@ro75 sehr starke Arbeit!
könnte man da noch Telegram mit rein hauen? das bei einer neuen Störung eine Nachricht bekommt? wer Hammer!
Danke für deine Arbeit!
-
Zu Vis2 muss ich mir noch so meine Gedanken machen, aber es wird
-
Einganspost um den NACHTRAG 20.09.2024: erweitert.
Ro75.
-
Eingangspost um Visualisierungsbeispiele erweitert.
Ro75.