iobroker.sprinklecontrol
Version:
Control of several sprinklers depending on weather conditions and pump performance.
497 lines (468 loc) • 18.8 kB
JavaScript
;
/*
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;