UNPKG

iobroker.airquality

Version:
457 lines (456 loc) 17 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var utils = __toESM(require("@iobroker/adapter-core")); var import_api_calls = require("./lib/api_calls"); class Airquality extends utils.Adapter { constructor(options = {}) { super({ ...options, name: "airquality", useFormatDate: true }); this.on("ready", this.onReady.bind(this)); this.on("unload", this.onUnload.bind(this)); } stationList = {}; components = {}; numberOfElements = 0; /** * Is called when databases are connected and adapter received configuration. */ async onReady() { this.stationList = await (0, import_api_calls.getStations)(); this.components = await (0, import_api_calls.getComponents)(); if (this.config.stations.length == 0) { this.log.info("[onReady] No stations specified"); const home = this.getLocation(); if (home.lat > 0) { const nearestStationIdx = this.findNearestStation(home, this.stationList); this.log.info(`[onReady] nearestStationIdx: ${nearestStationIdx}`); await this.writeStationToConfig(this.stationList[nearestStationIdx].code); } } else { this.log.info("[onReady] may be loop"); await this.delay(Math.floor(Math.random() * 5e3)); const selectedStations = this.config.stations; this.log.info(`[onReady] selectedStations: ${selectedStations}`); try { for (const station of selectedStations) { this.log.debug(`[onReady] fetches data from : ${station}`); await this.parseData(await (0, import_api_calls.getMeasurements)(station)); await this.parseDataSingle(await (0, import_api_calls.getMeasurementsComp)(station, 2)); } await this.setState("info.lastUpdate", { val: Date.now(), ack: true }); } catch (error) { await this.setState("info.connection", { val: false, ack: true }); if (error instanceof Error) { this.log.error(`[onReady] Error: ${error.message}`); } else { this.log.error(`[onReady] Unknown error: ${JSON.stringify(error)}`); } } } this.log.debug("[onReady] finished - stopping instance"); this.terminate ? this.terminate("Everything done. Going to terminate till next schedule", 11) : process.exit(0); } /** * Persist the measurements * * @param station Station Code * @param sensor Sensor * @param name Description * @param value Value * @param unit Unit * @param role Role */ async persistData(station, sensor, name, value, unit, role) { const dp_Sensor = `${this.removeInvalidCharacters(station)}.${this.removeInvalidCharacters(sensor)}`; this.log.silly( `[persistData] Station "${station}" Sensor "${sensor}" Desc "${name}" with value: "${value}" and unit "${unit}" as role "${role}` ); if (isNumber(value)) { await this.setObjectNotExistsAsync(dp_Sensor, { type: "state", common: { name, type: "number", role, unit, read: true, write: false }, native: {} }); } else { await this.setObjectNotExistsAsync(dp_Sensor, { type: "state", common: { name, type: "string", role, unit, read: true, write: false }, native: {} }); } await this.setState(dp_Sensor, { val: value, ack: true, q: 0 }); function isNumber(n) { return !isNaN(parseFloat(n)) && !isNaN(n - 0); } } /** * * @param station Station Code * @param value Time of the last measurement */ async storeData_TLM(station, value) { const sensor = "Time of the last measurement"; const dp_Sensor = `${this.removeInvalidCharacters(station)}.${this.removeInvalidCharacters(sensor)}`; this.log.silly( `[storeData_TLM] Station "${station}" Sensor "${sensor}" [${dp_Sensor}] with value: "${value}"` ); await this.setObjectNotExistsAsync(dp_Sensor, { type: "state", common: { name: { en: "Time of the last measurement", de: "Zeit der letzten Messung", ru: "\u0412\u0440\u0435\u043C\u044F \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0433\u043E \u0438\u0437\u043C\u0435\u0440\u0435\u043D\u0438\u044F", pt: "Tempo da \xFAltima medi\xE7\xE3o", nl: "Tijd van de laatste meting", fr: "Dur\xE9e de la derni\xE8re mesure", it: "Tempo dell' ultima misura", es: "Tiempo de la \xFAltima medici\xF3n", pl: "Czas ostatniego pomiaru", uk: "\u0427\u0430\u0441 \u043E\u0441\u0442\u0430\u043D\u043D\u044C\u043E\u0433\u043E \u0432\u0438\u043C\u0456\u0440\u044E\u0432\u0430\u043D\u043D\u044F", "zh-cn": "\u4E0A\u6B21\u6D4B\u91CF\u7684\u65F6\u95F4" }, type: "string", role: "text", unit: "", read: true, write: false }, native: {} }); await this.setState(dp_Sensor, { val: value, ack: true, q: 0 }); } /** * * @param station Station Code * @param value Number of measurement types */ async storeData_NMT(station, value) { const sensor = "Number of measurement types"; const dp_Sensor = `${this.removeInvalidCharacters(station)}.${this.removeInvalidCharacters(sensor)}`; this.log.silly(`[storeData_NMT] Station "${station}" Sensor "${sensor}" [${dp_Sensor}] with value: "${value}"`); await this.setObjectNotExistsAsync(dp_Sensor, { type: "state", common: { name: { en: "Number of measurement types", de: "Anzahl der Messarten", ru: "\u0427\u0438\u0441\u043B\u043E \u0442\u0438\u043F\u043E\u0432 \u0438\u0437\u043C\u0435\u0440\u0435\u043D\u0438\u0439", pt: "N\xFAmero de tipos de medi\xE7\xE3o", nl: "Aantal meettypes", fr: "Nombre de types de mesure", it: "Numero di tipi di misura", es: "N\xFAmero de tipos de medici\xF3n", pl: "Liczba typ\xF3w pomiar\xF3w", uk: "\u041A\u0456\u043B\u044C\u043A\u0456\u0441\u0442\u044C \u0442\u0438\u043F\u0456\u0432 \u0432\u0438\u043C\u0456\u0440\u044E\u0432\u0430\u043D\u043D\u044F", "zh-cn": "\u8BA1\u91CF\u7C7B\u578B\u6570\u76EE" }, type: "number", role: "value", unit: "", read: true, write: false }, native: {} }); await this.setState(dp_Sensor, { val: value, ack: true, q: 0 }); } /** * Retrieves the desired data from the payload and prepares data for storage * * @param {} payload Object from Response * @returns Data to persist */ async parseDataSingle(payload) { this.log.debug(`[parseDataSingle] Payload: ${JSON.stringify(payload)}`); if (Object.keys(payload).length === 0) { return; } const localDate = /* @__PURE__ */ new Date(); const summerOffset = localDate.getTimezoneOffset() / 60; const stationId = parseInt(Object.keys(payload)[0]); await this.createObject( this.stationList[stationId].code, this.stationList[stationId].city, this.stationList[stationId].street ); const innerObject = payload[stationId]; const dateTimeStart = Object.keys(innerObject)[0]; const dateTimeEnd = innerObject[dateTimeStart][3]; const timeEndAdjusted = this.correctHour(dateTimeEnd, summerOffset * -1 - 1); const innerData = innerObject[dateTimeStart]; const typeMeasurement = innerData[0]; await this.persistData( this.stationList[stationId].code, this.components[typeMeasurement].name, this.components[typeMeasurement].desc, innerData[2], // Value this.components[typeMeasurement].unit, "value" ); this.numberOfElements++; if (this.numberOfElements > 0) { await this.storeData_TLM(this.stationList[stationId].code, timeEndAdjusted); await this.storeData_NMT(this.stationList[stationId].code, this.numberOfElements); } this.log.debug(`[parseDataComp] Measured values from ${this.numberOfElements} sensors determined`); } /** * Retrieves the desired data from the payload and prepares data for storage * * @param {} payload Object from Response * @returns Data to persist */ async parseData(payload) { this.log.debug(`[parseData] Payload: ${JSON.stringify(payload)}`); if (Object.keys(payload).length === 0) { this.log.warn("No data received"); return; } const localDate = /* @__PURE__ */ new Date(); const summerOffset = localDate.getTimezoneOffset() / 60; const stationId = parseInt(Object.keys(payload)[0]); await this.createObject( this.stationList[stationId].code, this.stationList[stationId].city, this.stationList[stationId].street ); const innerObject = payload[stationId]; const dateTimeStart = Object.keys(innerObject)[0]; const dateTimeEnd = innerObject[dateTimeStart][0]; const timeEndAdjusted = this.correctHour(dateTimeEnd, summerOffset * -1 - 1); let innerData; this.numberOfElements = 0; for (const element in innerObject) { innerData = innerObject[element]; for (const element2 in innerData) { if (Array.isArray(innerData[element2])) { this.numberOfElements++; const typeMeasurement = innerData[element2][0]; await this.persistData( this.stationList[stationId].code, this.components[typeMeasurement].name, this.components[typeMeasurement].desc, innerData[element2][1], // Value this.components[typeMeasurement].unit, "value" ); } } } if (this.numberOfElements > 0) { await this.storeData_TLM(this.stationList[stationId].code, timeEndAdjusted); await this.storeData_NMT(this.stationList[stationId].code, this.numberOfElements); } this.log.debug(`[parseData] Measured values from ${this.numberOfElements} sensors determined`); } /** * Use the specified coordinates from the configuration * * @returns Koordinates(lat, lon) */ getLocation() { this.log.debug("[getLocation] try to use the location from the system configuration"); if (this.latitude == void 0 || this.latitude == 0 || this.longitude == void 0 || this.longitude == 0) { this.log.warn( 'longitude/latitude not set in system-config - please check instance configuration of "System settings"' ); return { lat: -1, lon: -1 }; } this.log.debug(`[getLocation] using Latitude: ${this.latitude} and Longitude: ${this.longitude}`); return { lat: this.latitude, lon: this.longitude }; } /** * search for the nearest station using coordinates * * @param localHome Koordinates from system * @param coordinates Koordinates from stations * @returns stationId */ findNearestStation(localHome, coordinates) { let minDistance = Number.MAX_VALUE; let nearestStation = 0; this.log.debug(`[findNearestStation]: Latitude: ${localHome.lat} Longitude: ${localHome.lon}`); for (const key of Object.keys(coordinates)) { const distance = this.getDistanceFromLatLonInKm( localHome.lat, localHome.lon, parseFloat(coordinates[key].lat), parseFloat(coordinates[key].lon) ); if (distance < minDistance) { minDistance = distance; nearestStation = parseInt(key); } } this.log.debug(`[findNearestStation]: >>> Station Idx: ${nearestStation}`); return nearestStation; } /** * write code of Station in UI-config * * @param localStation code of mesurement station */ async writeStationToConfig(localStation) { const _station = []; await this.getForeignObject(`system.adapter.${this.namespace}`, (err, obj) => { if (err) { this.log.error(`[writeStationToConfig] ${err}`); } else { if (obj) { _station.push(localStation); obj.native.stations = _station; this.setForeignObject(obj._id, obj, (err2) => { if (err2) { this.log.error(`[writeStationToConfig] Error when writing in config: ${err2}`); } else { this.log.debug(`[writeStationToConfig] New Station in config: ${localStation}`); } }); } } }); } /** * Create a folder für station * * @param station Station Code * @param description Station City * @param location Station Street */ async createObject(station, description, location) { const dp_Folder = this.removeInvalidCharacters(station); if (await this.objectExists(dp_Folder)) { return; } await this.setObjectNotExists(dp_Folder, { type: "folder", common: { name: { en: "Measurements from station", de: "Messungen von Station", ru: "\u0418\u0437\u043C\u0435\u0440\u0435\u043D\u0438\u044F \u043D\u0430 \u0441\u0442\u0430\u043D\u0446\u0438\u0438", pt: "Medi\xE7\xF5es da esta\xE7\xE3o", nl: "Metingen vanaf het station", fr: "Mesures de la station", it: "Misure dalla stazione", es: "Medidas desde la estaci\xF3n", pl: "Pomiary ze stacji", uk: "\u0412\u0438\u043C\u0456\u0440\u044E\u0432\u0430\u043D\u043D\u044F \u0437 \u0441\u0442\u0430\u043D\u0446\u0456\u0457", "zh-cn": "\u4ECE\u8F66\u7AD9\u6D4B\u91CF" }, desc: `${description}> ${location}`, role: "info" }, native: {} }); this.log.debug(`[createObject] Station "${station}" City "${description}"`); } /** * calculates the distance between two coordinates using the Haversine formula * * @param lat1 Latitude of the place of residence * @param lon1 Longitude of the place of residence * @param lat2 Latitude of the station * @param lon2 Longitude of the station * @returns Distance to the station */ getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) { const R = 6371; const dLat = deg2rad(lat2 - lat1); const dLon = deg2rad(lon2 - lon1); const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); const d = R * c; return d; function deg2rad(deg) { return deg * (Math.PI / 180); } } /** * removes illegal characters * * @param inputString Designated name for an object/data point * @returns Cleaned name for an object/data point */ removeInvalidCharacters(inputString) { const regexPattern = "[^a-zA-Z0-9]+"; const regex = new RegExp(regexPattern, "gu"); return inputString.replace(regex, "_"); } /** * correct datestring from datestring by adding x hours * * @param s Datestring * @param offset Offset * @returns String with Date & Time */ correctHour(s, offset) { const dateString = s.split(" ")[0].split("-"); const sDate = `${dateString[2]}.${dateString[1]}.${dateString[0]}`; const timeString = s.split(" ")[1].split(":"); const hour = parseInt(timeString[0]) + offset; const sHour = hour.toString().padStart(2, "0"); const sTime = `${sHour}:${timeString[1]}`; return `${sDate} ${sTime}`; } /** * Is called when adapter shuts down - callback has to be called under any circumstances! * * @param callback Callback */ onUnload(callback) { try { callback(); } catch (e) { this.log.debug(`[onUnload] ${JSON.stringify(e)}`); callback(); } } } if (require.main !== module) { module.exports = (options) => new Airquality(options); } else { (() => new Airquality())(); } //# sourceMappingURL=main.js.map