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 gespeichert
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.
// 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);
});