UNPKG

iobroker.sprinklecontrol

Version:

Control of several sprinklers depending on weather conditions and pump performance.

497 lines (468 loc) 18.8 kB
'use strict'; /* info: log aufbau evaporation.js: #3.* */ const myConfig = require('./myConfig.js'); // myConfig → Speichern und abrufen von Konfigurationsdaten der Ventile const tools = require('./tools').tools; // tools => laden von Hilfsfunktionen let adapter; /* calcEvaporation */ /** * akt. Temperatur °C * - -20 bis 55°C * - ts Zeitstempel, wann der Wert aktualisiert wurde (auch ohne Wertänderung) */ const curTemperature = {val: undefined, ts: undefined}, /** * akt. LuftFeuchtigkeit in % * - 1 bis 99% * - lc: Zeitstempel, wann der Wert geändert wurde */ curHumidity = {val: undefined, lc: undefined}, /** * akt. Helligkeit (relativ) * - 0 bis 100000 * - intern Begrenzung 100...7000 * - lc: Zeitstempel, wann der Wert geändert wurde */ curIllumination = {val: undefined, lc: undefined}, /** * akt. WindGeschwindigkeit in km/h * - 0 bis 200 km/h * - lc: Zeitstempel, wann der Wert geändert wurde */ curWindSpeed = {val: undefined, lc: undefined}; /** * max. Temperatur des Tages (für Auswertung der zusätzlichen Bewässerung */ let curTemperatureMax = 0, /** * last rain container => letzter Regencontainer in mm */ lastRainCounter = 0, /** * letzte Aktualisierungszeit des Temperaturwertes */ lastChangeEvaPor = new Date(); let ETpTodayNum = 0, /** * Extraterrestrische Tagesstrahlung in W/m² * - berechneter tabellarischer Tageswert */ toDayExtraTerStr, /** * kleinste extraterrestrische Tagesstrahlung im Jahr in W/m² * - berechneter tabellarischer Mindestwert */ minExtraTerStr, /** * größter extraterrestrische Tagesstrahlung im Jahr in W/m² * - berechneter tabellarischer Maximalwert */ maxExtraTerStr; /** * Extraterrestrische Strahlung (kurzwelliger Strahlungseinfluss von der Sonne an der Obergrenze der Erdatmosphäre) in W/m² * als Tagesmittel, Nordhalbkugel (IQBAL. 1983) */ const ExtraTerStrTab = { Tag:[ 21, 52, 80, 111, 141, 172, 202, 233, 264, 294, 325, 355], 46: [135, 198, 289, 382, 452, 483, 467, 409, 324, 230, 153, 117], 47: [128, 191, 283, 378, 450, 482, 466, 406, 319, 224, 146, 110], 48: [121, 184, 277, 375, 449, 482, 465, 403, 314, 217, 139, 103], 49: [114, 177, 271, 371, 447, 481, 464, 400, 309, 211, 132, 96], 50: [107, 170, 265, 367, 445, 481, 463, 398, 304, 204, 125, 89], 51: [100, 163, 259, 363, 443, 480, 462, 394, 298, 197, 118, 83], 52: [ 93, 156, 253, 358, 441, 480, 461, 391, 293, 191, 111, 76], 53: [ 86, 149, 247, 354, 439, 479, 459, 388, 287, 184, 104, 70], 54: [ 79, 142, 240, 350, 437, 478, 458, 384, 282, 177, 97, 63], 55: [ 73, 135, 234, 345, 435, 478, 457, 381, 276, 170, 90, 56] }; /*----------------------------------------------------------- interne Funktionen -----------------------------------------------------------*/ /** * Ermittlung der heutigen extraterrestrischen Strahlung * anhand der Tabelle "ExtraTerStrTab" */ function extraTerStr () { // latitude Breitengrad const dayNr = tools.formatTime().dayNr; /** * - unterer Tabellenwert der Latitude */ const lowerLatitude = ((parseInt(adapter.config.latitude, 10) < 46 ) ? 46 : ((parseInt(adapter.config.latitude, 10) > 55) ? 55 : (parseInt(adapter.config.latitude, 10)))) || 52, /** * - oberer Tabellenwert der Latitude */ upperLatitude = ((lowerLatitude + 1) > 55) ? 55 : (lowerLatitude + 1); let currentMonth = 0, followMonth = 0; for(let i = 0; i < 12; i++) { if (ExtraTerStrTab.Tag[i] > dayNr) { followMonth = i; currentMonth = (i === 0) ? 11 : (i - 1); break; } else if (i === 11) { followMonth = 0; currentMonth = 11; break; } } toDayExtraTerStr = tools.trend( lowerLatitude, upperLatitude, tools.trend(ExtraTerStrTab.Tag[currentMonth], ExtraTerStrTab.Tag[followMonth], ExtraTerStrTab[lowerLatitude.toString()][currentMonth], ExtraTerStrTab[lowerLatitude.toString()][followMonth], dayNr), tools.trend(ExtraTerStrTab.Tag[currentMonth], ExtraTerStrTab.Tag[followMonth], ExtraTerStrTab[upperLatitude.toString()][currentMonth], ExtraTerStrTab[upperLatitude.toString()][followMonth], dayNr), +adapter.config.latitude ); minExtraTerStr = tools.trend( lowerLatitude, upperLatitude, ExtraTerStrTab[lowerLatitude.toString()][11], //alter Wert 11 ExtraTerStrTab[upperLatitude.toString()][11], //alter Wert 11 +adapter.config.latitude ); maxExtraTerStr = tools.trend( lowerLatitude, upperLatitude, ExtraTerStrTab[lowerLatitude.toString()][5], ExtraTerStrTab[upperLatitude.toString()][5], +adapter.config.latitude ); } /** * evaporation calculation * => Berechnung der Verdunstung * * @param {number} timeDifference */ function calcEvaporation (timeDifference) { if (!Number.isFinite(curIllumination.val) || !Number.isFinite(curTemperature.val) || !Number.isFinite(curHumidity.val) || !Number.isFinite(curWindSpeed.val)) { adapter.log.warn(`calcEvaporation => berechnung nicht möglich! Es fehlen Werte. ( ${curTemperature.val} °C | ${curIllumination.val} | ${curHumidity.val} % | ${curWindSpeed.val} km/h )`); return; } adapter.log.debug(`calcEvaporation => gestartet TimeDifferenz: ${timeDifference}`); // Sonnenscheindauer in % const curSunshineDuration = (curIllumination.val < 100) ? (0) : (curIllumination.val > 7000) ? (1) : ((curIllumination.val - 100) / (6900)); /** * Sättigungsdampfdruck 'Es' in hPa * m1 */ const m1 = 6.11 * ( 10 ** (( 7.48 * curTemperature.val ) / ( 237 + curTemperature.val ))); /** * Dampfdruck Ea * m2 */ const m2 = m1 * curHumidity.val / 100; /** * Globalstrahlung RG * m3 */ const m3 = (0.19 + 0.55 * curSunshineDuration) * toDayExtraTerStr; /** * Abstrahlung I in W/m² * m4 */ const m4 = 5.67E-8 * (( curSunshineDuration + 273 ) ** 4 ) * ( 0.56 - 0.08 * ( m2 ** 0.5 )) * ( 0.1 + ( 0.9 * curSunshineDuration)); /** * Strahlungsäquivalent EH in mm/d * m5 */ const m5 = ( m3 * ( 1 - 0.2 ) - m4 ) / 28.3; /** * Steigung der Sättigungsdampfdruckkurve Delta in hPa/K * m6 */ const m6 = ( m1 * 4032 ) / (( 237 + curTemperature.val ) ** 2 ); /** * Windfunktion f(v) in mm/d hPa * m7 */ const m7 = 0.13 + 0.14 * curWindSpeed.val / 3.6; /** * pot. Evapotranspiration nach Penman ETp in mm/d * eTp */ const eTp = (( m6 * m5 + 0.65 * m7 * ( m1 - m2 )) / ( m6 + 0.65 )) - 0.5; adapter.setState('evaporation.ETpCurrent', { val: Math.round(eTp * 10000) / 10000, ack: true }); addEvaporation(eTp * timeDifference); } function addEvaporation (value) { if (value < 2) { // um Fehler in der Auswertung beim Neustart zu löschen ETpTodayNum += value; } adapter.setState('evaporation.ETpToday', { val: Math.round(ETpTodayNum * 10000) / 10000, ack: true }); myConfig.applyEvaporation (value); } /* ----------------------------------------------------------- externe Funktionen ----------------------------------------------------------- */ const evaporation = { /** * Initialisierung von evaporation * - Bereitstellen von Umweltdaten * * @param {ioBroker.Adapter} myAdapter */ initEvaporation (myAdapter) { adapter = myAdapter; /** * - Sensors to calculate the evaporation are required * - Sensoren zur Berechnung der Evaporation werden benötigt * * @returns {boolean} */ function fCalculationOn () { const found = myConfig.config.find(d => d.methodControlSM === 'calculation'); return (!(typeof found === 'undefined')); } const calculationOn = fCalculationOn(); extraTerStr(); if (adapter.config.sensorOutsideTemperature !== '') { adapter.subscribeForeignStates(adapter.config.sensorOutsideTemperature); adapter.getForeignState(adapter.config.sensorOutsideTemperature, (err, state) => { if (state && state.val) { // @ts-ignore if (!Number.isNaN(Number.parseFloat(state.val))) { // @ts-ignore curTemperature.val = (parseFloat(state.val)); // @ts-ignore curTemperature.ts = state.ts; } else { adapter.log.warn(`sensorOutsideTemperature => Wrong value: ${state.val}, Type: ${typeof state.val}`); } } }); } else if (calculationOn) { adapter.log.warn('The sensor "sensorOutsideTemperature" is not saved in the configuration! The adapter cannot work like this!'); } if (adapter.config.sensorOutsideHumidity !== '') { adapter.subscribeForeignStates(adapter.config.sensorOutsideHumidity); adapter.getForeignState(adapter.config.sensorOutsideHumidity, (err, state) => { if (state && state.val) { // @ts-ignore if (!Number.isNaN(Number.parseFloat(state.val))) { // @ts-ignore curHumidity.val = (parseFloat(state.val)); // @ts-ignore curHumidity.lc = state.lc; } else { adapter.log.warn(`sensorOutsideHumidity => Wrong value: ${state.val}, Type: ${typeof state.val}`); } } }); } else if (calculationOn) { adapter.log.warn('The sensor "sensorOutsideHumidity" is not saved in the configuration! The adapter cannot work like this!'); } if (adapter.config.sensorBrightness !== '') { adapter.subscribeForeignStates(adapter.config.sensorBrightness); adapter.getForeignState(adapter.config.sensorBrightness, (err, state) => { if (state && state.val) { // @ts-ignore if (!Number.isNaN(Number.parseFloat(state.val))) { // @ts-ignore curIllumination.val = (parseFloat(state.val)); // @ts-ignore curIllumination.lc = state.lc; } else { adapter.log.warn(`sensorBrightness => Wrong value: ${ state.val }, Type: ${ typeof state.val }`); } } }); } else if (calculationOn) { adapter.log.warn('The sensor "sensorBrightness" is not saved in the configuration! The adapter cannot work like this!'); } if (adapter.config.sensorWindSpeed !== '') { adapter.subscribeForeignStates(adapter.config.sensorWindSpeed); adapter.getForeignState(adapter.config.sensorWindSpeed, (err, state) => { if (state && state.val) { // @ts-ignore if (!Number.isNaN(Number.parseFloat(state.val))) { // @ts-ignore curWindSpeed.val = (parseFloat(state.val)); // @ts-ignore curWindSpeed.lc = state.lc; } else { adapter.log.warn(`sensorWindSpeed => Wrong value: ${state.val}, Type: ${typeof state.val}`); } } }); } else if (calculationOn) { adapter.log.warn('The sensor "sensorWindSpeed" is not saved in the configuration! The adapter cannot work like this!'); } if (adapter.config.sensorRainfall !== '') { adapter.subscribeForeignStates(adapter.config.sensorRainfall); adapter.getForeignState(adapter.config.sensorRainfall, (err, state) => { if (state && state.val) { // @ts-ignore if (!Number.isNaN(Number.parseFloat(state.val))) { // @ts-ignore lastRainCounter = (parseFloat(state.val)); } else { adapter.log.warn(`sensorRainfall => Wrong value: ${state.val}, Type: ${typeof state.val}`); } } }); } }, /** * akt. Temperatur * * @param {number} value curTemperature * @param {Date} curTime ts (akt. Zeit) */ setCurTemperature (value, curTime) { curTemperature.val = value; curTemperature.ts = curTime; if (curTemperature.val > curTemperatureMax) { curTemperatureMax = curTemperature.val; adapter.setState('evaporation.dailyHighTemp', { val: curTemperatureMax, ack: true }); } /** * Abbruch bei inkorrekten Umweltdaten */ let abbruch = false; if((curTemperature.val < -20) || (curTemperature.val > 55)){ adapter.log.warn('Temperature outside the range of -20 ... 55 [°C]'); abbruch = true; } if(curHumidity.val && (curHumidity.val < 1) || (curHumidity.val > 100)) { adapter.log.warn('Humidity outside the range of 1 ... 100 [%]'); abbruch = true; } if(curIllumination.val && (curIllumination.val < 0) || (curIllumination.val > 100000)) { adapter.log.warn('Brightness out of range von 0 ... 100.000'); abbruch = true; } if(curWindSpeed.val && (curWindSpeed.val < 0) || (+curWindSpeed > 200)) { adapter.log.warn('Wind speed outside the range of 0 ... 200 km/h'); abbruch = true; } if (abbruch) { return; } /** * Zeitdifferenz in ms */ const timeDifference = (+curTime - +lastChangeEvaPor) / 86400000; // 1Tag === 24/h * 60/min * 60/s * 1000/ms === 86400000 ms adapter.log.debug(`${curTemperature.val}°C | ${curHumidity.val}% | ${curIllumination.val} | ${curWindSpeed.val}km/h | ts: ${curTime} - lastChangeEvaPor: ${lastChangeEvaPor} = timeDifference: ${timeDifference}`); if (timeDifference) { adapter.setTimeout(() => { calcEvaporation(timeDifference); }, 100); } lastChangeEvaPor = curTime; }, /** * akt. LuftFeuchtigkeit in % * * @param {number} value * @param {number} lc */ setCurHumidity (value, lc) { curHumidity.val = value; curHumidity.lc =lc; }, /** * akt. Helligkeit wird auf 0 bis 7000 begrenzt * * @param {number} value * @param {number} lc */ setCurIllumination (value, lc) { curIllumination.val = value; curIllumination.lc = lc; }, /** * akt. Windgeschwindigkeit * * @param {number} value Windgeschwindigkeit in km/h * @param {number} lc */ setCurWindSpeed (value, lc) { curWindSpeed.val = value; curWindSpeed.lc = lc; }, /** * akt. Regenmengenzähler * → Bei einer Änderung über 20 mm wird der Wert nur intern gespeichert, * es findet aber keine Anwendung statt! * * @param {number} value current rain counter → aktueller Regencontainer in mm */ setCurAmountOfRain (value) { if ((value > lastRainCounter) // es regnet && ((value - lastRainCounter) < 20) // && Plausibilitätskontrolle (Regenmenge unter 10mm) ) { addEvaporation(lastRainCounter - value); } lastRainCounter = value; adapter.log.debug(`lastRainCounter: ${lastRainCounter} curAmountOfRain: ${(lastRainCounter - value)} state.val: ${value}`); }, /** * Summe der heutigen Verdunstung * * @param {number} value */ setETpTodayNum (value) { ETpTodayNum = value; }, /** * Tageshöchstwert der Temperatur (für Auswertung der zusätzlichen Bewässerung) * * @param {number} value */ setCurTemperatureMax (value) { curTemperatureMax = value; }, /** * ETpToday und ETpYesterday in evaporation aktualisieren da ein neuer Tag beginnt */ setNewDay () { extraTerStr(); adapter.setTimeout(() => { adapter.setState('evaporation.ETpYesterday', { val: Math.round(ETpTodayNum * 10000) / 10000, ack: true }); ETpTodayNum = 0; adapter.setState('evaporation.ETpToday', { val: ETpTodayNum, ack: true }); curTemperatureMax = 0; adapter.setState('evaporation.dailyHighTemp', { val: curTemperatureMax, ack: true }); }, 100); }, /** * * @param {number} maxExtension - Wert aus der Konfiguration (wateringAdd 100...300%) des Bewässerungskreises * @returns {number} - Erweiterung/Multiplikator (1...3) der Bewässerungszeit */ timeExtension (maxExtension) { if (ETpTodayNum < 136) { // 16. Mai return 1; }else if (ETpTodayNum < 171) { // 20. Juni return tools.trend(136, 170,1, maxExtension / 100, ETpTodayNum); } else if (ETpTodayNum < 186) { // 5. Juli return maxExtension / 100; }else if (ETpTodayNum < 241) { // 29. August return tools.trend(186, 240,maxExtension / 100, 1, ETpTodayNum); }else { return 1; } }, /**Output of the current evaporation => * Ausgabe der aktuellen Verdunstung * @returns {number} */ getETpTodayNum () { return ETpTodayNum; }, getCurTemperatureMax () { return curTemperatureMax; } }; module.exports = evaporation;