NEWS
[Vorlage] Luftqualitätswerte abrufen
-
Ich möchte euch mein Skript zum Abrufen der Luftqualitätsdaten über die Api des Umweltbundesamtes zur Verfügung stellen.
Funktion:
auf der SEITE des Umwelbundesamtes die StationsID raussuchen
Es können auch mehrere Stationen durch Komma getrennt im Skript eingetragen werden.
Dann werden die Daten (letzter Stündlicher Mittelwert) unter dem eingestellten Datenpunkt um **:10 gespeichert. (um 10, nach da ich festgestellt habe, dass manche Werte erst später kommen)v2.1 - Datumsfix => Danke an @Sborg
- Fehlerbehandlung für nicht abgerufene Daten eingebaut
v2.2 - fix Clear Timeouts und Schedules beim Stoppen
v2.3 - fix Fehlerbearbeitung
v2.4 - Die API scheint keine Stunde 0 korrekt verarbeiten zu können, deswegen werden hier zwischen 23 Uhr und 1 Uhr keine Daten geliefert.
Es wird nur noch "kein Datensatz" geloggt, anstatt einen Fehler auszulösen.
v2.5 - Abfrage an API angepasst (1- 24 Uhr wird dort gefordert).
v2.6 - Anpassung zu Sommerzeit-Problemen
v2.7 - Schönheitskorrekturen im Code
v2.8 - Werte werden mit ack=true gespeichertHinweis:
Um den Datumswechsel fehlen meist zwischen 0 und 2 Uhr immer mal wieder Daten, die von der API nicht geliefert werden. Später am Tag sind sie zwar vorhanden, aber das Skript ist nur für die letzten aktuellen Werte gedacht. Dies wird nur als normale Log-Meldung geführt.// Luftqualität v2.8 // v1 - Erst-Version // v2 - Qualitätsindizes hinzugefügt // v2.1 - Datumsberechnung korrigiert => Danke an @SBorg // - Fehlerbehandlung für nicht abgerufene Daten eingebaut // v2.2 - fix Clear Timeouts und Schedules // v2.3 - fix Fehlerbearbeitung // v2.4 - Die API scheint keine Stunde 0 korrekt verarbeiten zu können, deswegen werden hier zwischen 23 Uhr und 1 Uhr keine Daten geliefert. // Es wird nur noch kein Datensatz gelogt anstatt einen Fehler auszulösen // v2.5 - Abfrage an API angepasst (1- 24 Uhr wird dort gefordert). // v2.6 - Anpassung zu Sommerzeit-Problemen // v2.7 - Schönheitskorrekturen im Code // v2.8 - Werte werden mit ack=true gespeichert // User Einstellungen const stations = ["DEBW019", "DEBY052"]; // Stationen, können durch Komma getrennt werden z.B. ["DEBW019", "DEBY007"] const datenpunkt_pre ="0_userdata.0" // => wird in 0_userdata.0.Luftqualität gespeichert const debug = false; // Abfrage findet um **:10 stündlich statt (um fehlende Daten eventl. nachzufüllen). Die Daten werden sowieso nur stündlich von der letzten Stunde zur Verfügung gestellt... // Datenverarbeitung var dp_path = datenpunkt_pre + ".Luftqualität."; var timer; var components; var stations_list; var re_init = false; var re_init_timeout; const fetch = require('node-fetch'); function createBaseFolder(ID, type, name) { /** geklaut bei grrfield * Erstellt Basisfolder und stellt den richtigen Typ ein * @param {string} ID ID des Folders * @param {any} type Typ des Folders * @param {string} [name] (optional) Name des Folders */ createState(ID, function() { let obj=getObject(ID); obj.type=type; if(name != undefined) obj.common.name=name; obj.common.role=''; setObject(ID, obj); }); } const get_stations = async () => { const url_stations = "https://umweltbundesamt.api.proxy.bund.dev/api/air_data/v3/stations/json?lang=de"; var stations_json = fetch(url_stations) .then(response => response.json()) .then(data => { //if (debug) console.log(data); return data}) .catch ((err) => { console.error("failed fetch " + err); } ); return stations_json; } const send_request = async (station_code) => { const d = new Date(); if (debug) console.log ("TIMEZONE: " + d.getTimezoneOffset()); if (d.getTimezoneOffset() == -120) d.setTime(d.getTime()-3600000); let month_to = (d.getMonth()+1) < 10 ? "0"+ (d.getMonth()+1) : (d.getMonth()+1); let day_to = (d.getDate()) < 10 ? "0"+ (d.getDate()) : (d.getDate()); let date_to = d.getFullYear() +'-'+ month_to +'-' + day_to; let hour_to = d.getHours(); if (debug) console.log ("to => " + date_to + " " + hour_to); const now_round = new Date (date_to +' '+ hour_to + ":00"); let now = now_round.getTime()-3600000; d.setTime(now); let month_from = (d.getMonth()+1) < 10 ? "0"+ (d.getMonth()+1) : (d.getMonth()+1); let day_from = (d.getDate() < 10) ? "0"+ (d.getDate()) : d.getDate(); let date_from = d.getFullYear() +'-'+ month_from +'-' + day_from; let hour_from = d.getHours(); if (debug) console.log ("from => " + date_from + " " + hour_from); if (hour_to == 0){ hour_to =24; date_to = date_from if (debug) console.warn ("STUNDE 0 => to => " + date_to + " " + hour_to); } else if (hour_from == 0){ let fixed_now = d.getTime()-3600000; d.setTime(fixed_now); month_from = (d.getMonth()+1) < 10 ? "0"+ (d.getMonth()+1) : (d.getMonth()+1); day_from = (d.getDate() < 10) ? "0"+ (d.getDate()) : d.getDate(); date_from = d.getFullYear() +'-'+ month_from +'-' + day_from; hour_from = 24; if (debug) console.warn ("STUNDE 0 => from => " + date_from + " " + hour_from); } const url_server = "https://umweltbundesamt.api.proxy.bund.dev/api/air_data/v3/airquality/json?date_from=" + date_from + "&time_from=" + hour_from + "&date_to=" +date_to +"&time_to=" + hour_to + "&station=" + station_code + "&lang=de"; if (debug) console.log("SERVER-URL: "+ url_server); var request = fetch(url_server) .then(response => response.json()) .then(data => { if (debug) console.log(data); return data}) .catch ((err) => { console.error("failed fetch " + err); } ); return request; }; const get_components = async () => { var list = fetch("https://umweltbundesamt.api.proxy.bund.dev/api/air_data/v3/components/json?lang=de&index=id") .then(response => response.json()) .then(data => { if (debug) console.log(data); return data}) .catch ((err) => { console.error("failed fetch " + err); } ); return list; }; async function get_data (station_code) { if (re_init) return; var data = await send_request(station_code); if (Object.keys(data.data).length == 0){ let time_req = new Date(); if (time_req.getHours() == 0 || time_req.getHours() == 1) console.log ('Keine Daten Empfangen (evtl. "Stunde 0" - BUG)'); else console.log ('Keine Daten Empfangen'); return; } else if (typeof components === 'undefined'){ console.warn ("Keine components geladen => Neue Initialisierung in 15 Sekunden"); clearSchedule(timer); re_init = true; re_init_timeout = setTimeout(function(){ re_init = false; init(); },15000); return; } else if (typeof stations_list === 'undefined'){ console.warn ("Keine Stationsliste geladen => Neue Initialisierung in 15 Sekunden"); re_init = true; re_init_timeout = setTimeout(function(){ re_init = false; init(); },15000); return; } if (!existsObject(dp_path + station_code)){ if (debug) console.warn("not exist => " + stations_list.data[data.request.station][2]); createBaseFolder(dp_path + station_code, "folder", stations_list.data[data.request.station][2]); } var measure_set = data.data[data.request.station][data.request.datetime_from]; for (let i = 3 ; i < measure_set.length; i++) { var id = dp_path + station_code + "." + components[measure_set[i][0]][1]; if (debug) console.log ("ID: " + id); // Roh-Wert anlegen/speichern existsState(id, (err, isExists) => { var idwork = dp_path + station_code + "." + components[measure_set[i][0]][1]; if (debug){ console.log ("IDwork: " + idwork); console.log ("initval: " + measure_set[i][1]); console.log ("Name: " + components[measure_set[i][0]][4] + "(" + components[measure_set[i][0]][1] + ")"); console.log ("unit: " + components[measure_set[i][0]][3]); } if (err) console.error (err); if (isExists) { if (debug) console.log ("Exists writing new value"); setState(idwork, measure_set[i][1], true); } else{ if (debug) console.log ("Not Exists - creating State"); createState(idwork, measure_set[i][1], { name: components[measure_set[i][0]][4] + " (" + components[measure_set[i][0]][2] + ")", unit: components[measure_set[i][0]][3], type: "number", role: "value", read: true, write: true} , () => { log('Dp '+ idwork + ' erstellt!'); }); } }); // Qualitätsindex anlegen/speichern gerundet let id_index = id + "_index"; existsState(id_index, (err, isExists) => { let id_index_work = id_index; if (debug){ console.log ("ID_index_work: " + id_index_work); console.log ("initval: " + measure_set[i][2]); console.log ("Name: Qualitätsindex " + components[measure_set[i][0]][4]); } if (err) console.error (err); if (isExists) { if (debug) console.log ("Exists writing new value"); setState(id_index_work, measure_set[i][2], true); } else{ if (debug) console.log ("Not Exists - creating State"); createState(id_index_work, measure_set[i][2], { name: "Qualitätsindex " + components[measure_set[i][0]][4], type: "number", role: "indicator", read: true, write: true} , () => { log('Dp '+ id_index_work + ' erstellt!'); }); } }); // Roh-Wert Qualitätsindex anlegen/speichern gerundet let id_index_val = id + "_index_val"; existsState(id_index_val, (err, isExists) => { let id_index_val_work = id_index_val; if (debug){ console.log ("IDwork: " + id_index_val_work); console.log ("initval: " + measure_set[i][3]); console.log ("Name: Qualitätsindex (roh)" + components[measure_set[i][0]][4]); } if (err) console.error (err); if (isExists) { if (debug) console.log ("Exists writing new value"); setState(id_index_val_work, parseFloat(measure_set[i][3]), true); } else{ if (debug) console.log ("Not Exists - creating State"); createState(id_index_val_work, parseFloat(measure_set[i][3]), { name: "Qualitätsindex " + components[measure_set[i][0]][4] + " (roh)", type: "number", role: "indicator", read: true, write: true} , () => { log('Dp '+ id_index_val_work + ' erstellt!'); }); } }); } // timestamp let id_ts = dp_path + station_code + ".ts"; existsState(id_ts, (err, isExists) => { let id_ts_work = id_ts; if (debug){ console.log ("IDwork: " + id_ts); console.log ("initval: " + measure_set[0]); } if (err) console.error (err); let ts = new Date(measure_set[0]+"Z"); let string_ts = ts.getDate()+ "." + (ts.getMonth()+1) + "." + ts.getFullYear() + " " + ts.getHours() + ":" + (ts.getMinutes() < 10 ? "0" + ts.getMinutes() : ts.getMinutes()); if (isExists) { if (debug) console.log ("Exists writing new value"); setState(id_ts_work, string_ts, true); } else { if (debug) console.log ("Not Exists - creating State"); createState(id_ts, string_ts, { name: "Ende der Messung", type: "string", role: "value", read: true, write: true} , () => { log('Dp '+ id_ts + ' erstellt!'); }); } }); } onStop (() => { clearSchedule(timer); clearTimeout(re_init_timeout); }); async function init(){ stations_list = await get_stations(); components = await get_components(); stations.forEach (get_data); // init für sofortige Datenverfügbarkeit } init(); // timer um */10 timer = schedule({minute: [10]}, () => { stations.forEach (get_data); });
-
@boronsbruder Gerade getestet, funktioniert einwandfrei. Vielen Dank!
-
@boronsbruder
Ich schreibe es dir "hier", nicht im anderen Thread#55 let hour = d.getHours()-1;
Müsstest du noch abfangen, denn um "0" Uhr bekommst du dann "-1"
#56 let date = d.getFullYear + "-" + d.getMonth + "-" + d.getDay;
ändern inlet date = d.getFullYear() + "-" + (d.getMonth()+1) + "-" + d.getDate();
Month ist 0 bis11 und Day ist der Tag der Woche
Die API bügelt das dann aber zum aktuellen Datum um.
-
@boronsbruder
vielen dank, lüppt super -
Version 2.3 eingestellt
// v2.2 - fix Clear Timeouts und Schedules beim Stoppen
// v2.3 - fix Fehlerbearbeitung!!! Sollte das Skript vor den Fixes nicht mehr gestoppt werden können (tritt normalerweise nur bei einem Fehler beim Abrufen der Daten auf), dann bitte die Javascript-Instanz neustarten: Dann werden alle nicht gelöschten Timeouts gestoppt
-
Es gibt eine Version 2.4:
Da die API die Stunde 0 nicht korrekt zu verarbeiten scheint, wird zwischen 23 und 1 Uhr nur eine "Keine Datensatz"-Meldung geloggt, anstatt einen Fehler auszulösen.
Zumindest bis ich das geklärt habe. Stehe schon in Kontakt mit denen, aber die verstehen gerade nicht ganz, was ich von ihnen willEdit:
Ich glaube ich habe den "Fehler" gefunden:
Stunde 0 ist bei denen Stunde 24 des Vortages... (ohne Worte) wahrscheinlich, dass sie leichter in UTC umwandeln können....
Test läuft - dann gibts morgen die gefixte Version...
-
@boronsbruder sagte in [Vorlage] Luftqualitätswerte abrufen:
v2.5 - Abfrage an API angepasst (1- 24 Uhr wird dort gefordert).
Hinweis:
Um den Datumswechsel fehlen meist zwischen 0 und 2 Uhr immer mal wieder Daten, die von der API nicht geliefert werden. Später am Tag sind sie zwar vorhanden, aber das Skript ist nur für die letzten aktuellen Werte gedacht. Dies wird nur als normale Log-Meldung geführt. -
@boronsbruder Danke für die Arbeit. Ist die v2.5 schon verfügbar?
-
@rene55 sagte in [Vorlage] Luftqualitätswerte abrufen:
@boronsbruder Danke für die Arbeit. Ist die v2.5 schon verfügbar?
Jo, steht oben drin!
-
@boronsbruder Hätte ich mir gedacht - stünde dort nicht Luftqualität v2.4 zuoberst.
-
@rene55 sagte in [Vorlage] Luftqualitätswerte abrufen:
@boronsbruder Hätte ich mir gedacht - stünde dort nicht Luftqualität v2.4 zuoberst.
Uups, hab vergessen die Versionsnummer anzupassen...
Hab nur die Änderungsliste angepasst...Geändert... Sry...
-
Mit der Zeitumstellung gibt es ein Problem.
Fix ist oben drin mit Version 2.6 -
v 2.7 ist online
Debug ist wieder deaktiviert und kleine Schönheitskorrekturen -
@boronsbruder Bei mir kommen die Werte unbestätigt an.
Könntest du das Script so anpassen, dass die Werte bestätigt abgelegt werden?
Dann kann ich die auch direkt auf meiner AWTRIX-Uhr anzeigen.Dank dir.
-
@siggi0904
Sobald ich mal wieder Zeit hab, kann ich das ändern... -
@boronsbruder wär cool, dank dir im Voraus.
-
v2.8 online
Änderung => Daten werden mit ack=true gespeichert -
@boronsbruder Ich habe mich auf der Seite vom UBA auch mal umgesehen. Hier wird immer mit der v2 gearbeitet. Wie kommst du schon auf v3? Hast du da Insiderwissen?
-
@rene55
Guggst du: https://www.umweltbundesamt.de/dokument/schnittstellenbeschreibung-luftdaten-apiund folgst dem Link unter "Dokumente"; oder Direkt Klick
Dort kann man auch nachlesen, dass die V2 Anfang 2024 abgeschaltet werden soll...
-
@sborg Danke! Warum finde ich solche Infos nicht