98_dewpoint.pm 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. # $Id: 98_dewpoint.pm 6757 2014-10-12 18:58:57Z joachim09876 $
  2. ##############################################
  3. #
  4. # Dewpoint computing
  5. #
  6. # based / modified from 98_average.pm (C) by Rudolf Koenig
  7. #
  8. # Copyright (C) 2012 Willi Herzig
  9. #
  10. # This program is free software; you can redistribute it and/or
  11. # modify it under the terms of the GNU General Public License
  12. # as published by the Free Software Foundation; either version 2
  13. # of the License, or (at your option) any later version.
  14. #
  15. # This program is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU General Public License
  21. # along with this program; if not, write to the Free Software
  22. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  23. #
  24. # The GNU General Public License may also be found at http://www.gnu.org/licenses/gpl-2.0.html .
  25. #
  26. package main;
  27. use strict;
  28. use warnings;
  29. # Debug this module? YES = 1, NO = 0
  30. my $dewpoint_debug = 0;
  31. # default maximum time_diff for dewpoint
  32. my $dewpoint_time_diff_default = 1; # 1 Second
  33. ##########################
  34. sub
  35. dewpoint_Initialize($)
  36. {
  37. my ($hash) = @_;
  38. $hash->{DefFn} = "dewpoint_Define";
  39. $hash->{NotifyFn} = "dewpoint_Notify";
  40. $hash->{NotifyOrderPrefix} = "10-"; # Want to be called before the rest
  41. $hash->{AttrList} = "disable:0,1 max_timediff absFeuchte";
  42. }
  43. ##########################
  44. sub
  45. dewpoint_Define($$)
  46. {
  47. my ($hash, $def) = @_;
  48. my @a = split("[ \t][ \t]*", $def);
  49. return "wrong syntax: define <name> dewpoint (dewpoint|fan|alarm) devicename [options]"
  50. if(@a < 4);
  51. my $name = $a[0];
  52. my $cmd_type = $a[2]; # dewpoint, fan, alarm
  53. my $devname = $a[3];
  54. if ($cmd_type eq "dewpoint") {
  55. # define <name> dewpoint dewpoint devicename-regex [temp_name hum_name new_name]
  56. if(@a == 7) {
  57. $hash->{TEMP_NAME} = $a[4];
  58. $hash->{HUM_NAME} = $a[5];
  59. $hash->{NEW_NAME} = $a[6];
  60. } elsif (@a == 4) {
  61. $hash->{TEMP_NAME} = "temperature";
  62. $hash->{HUM_NAME} = "humidity";
  63. $hash->{NEW_NAME} = "dewpoint";
  64. } else {
  65. return "wrong syntax: define <name> dewpoint dewpoint devicename-regex [temp_name hum_name new_name]"
  66. }
  67. } elsif ($cmd_type eq "fan") {
  68. # define <name> dewpoint fan devicename-regex devicename-outside min_temp
  69. if (@a == 6 || @a == 7) {
  70. $hash->{DEVNAME_OUT} = $a[4];
  71. $hash->{MIN_TEMP} = $a[5];
  72. if (@a == 6) {
  73. $hash->{DIFF_TEMP} = 0;
  74. } else {
  75. $hash->{DIFF_TEMP} = $a[6];
  76. }
  77. } else {
  78. return "wrong syntax: define <name> dewpoint fan devicename-regex devicename-outside min_temp [diff_temp]"
  79. }
  80. } elsif ($cmd_type eq "alarm") {
  81. # define <name> dewpoint alarm devicename-regex devicename-reference diff_temp
  82. if (@a == 6) {
  83. $hash->{DEVNAME_REF} = $a[4];
  84. $hash->{DIFF_TEMP} = $a[5];
  85. } else {
  86. return "wrong syntax: define <name> dewpoint alarm devicename-regex devicename-reference diff_temp"
  87. }
  88. } else {
  89. return "wrong syntax: define <name> dewpoint (dewpoint|fan|alarm) devicename-regex [options]"
  90. }
  91. $hash->{CMD_TYPE} = $cmd_type;
  92. eval { "Hallo" =~ m/^$devname$/ };
  93. return "Bad regecaxp: $@" if($@);
  94. $hash->{DEV_REGEXP} = $devname;
  95. $hash->{STATE} = "active";
  96. return undef;
  97. }
  98. ##########################
  99. sub
  100. dewpoint_Notify($$)
  101. {
  102. my ($hash, $dev) = @_;
  103. my $hashName = $hash->{NAME};
  104. return "" if(AttrVal($hashName, "disable", undef));
  105. return "" if(!defined($hash->{DEV_REGEXP}));
  106. my $devName = $dev->{NAME};
  107. my $cmd_type = $hash->{CMD_TYPE};
  108. my $re = $hash->{DEV_REGEXP};
  109. # dewpoint
  110. my $temp_name = "temperature";
  111. my $hum_name = "humidity";
  112. my $new_name = "dewpoint";
  113. # fan
  114. my $devname_out = "";
  115. my $min_temp = 0;
  116. # alarm
  117. my $devname_ref = "";
  118. my $diff_temp = 0;
  119. if ($cmd_type eq "dewpoint") {
  120. if (!defined($hash->{TEMP_NAME}) || !defined($hash->{HUM_NAME}) || !defined($hash->{NEW_NAME})) {
  121. # should never happen!
  122. Log 1, "Error dewpoint: TEMP_NAME || HUM_NAME || NEW_NAME undefined";
  123. return "";
  124. }
  125. $temp_name = $hash->{TEMP_NAME};
  126. $hum_name = $hash->{HUM_NAME};
  127. $new_name = $hash->{NEW_NAME};
  128. Log 1, "dewpoint_notify: cmd_type=$cmd_type devname=$devName dewname=$hashName, dev=$devName, dev_regex=$re temp_name=$temp_name hum_name=$hum_name" if ($dewpoint_debug == 1);
  129. } elsif ($cmd_type eq "fan") {
  130. if (!defined($hash->{DEVNAME_OUT}) || !defined($hash->{MIN_TEMP})) {
  131. # should never happen!
  132. Log 1, "Error dewpoint: DEVNAME_OUT || MIN_TEMP undefined";
  133. return "";
  134. }
  135. $devname_out = $hash->{DEVNAME_OUT};
  136. $min_temp = $hash->{MIN_TEMP};
  137. $diff_temp = $hash->{DIFF_TEMP};
  138. Log 1, "dewpoint_notify: cmd_type=$cmd_type devname=$devName dewname=$hashName, dev=$devName, dev_regex=$re, devname_out=$devname_out, min_temp=$min_temp, diff_temp=$diff_temp" if ($dewpoint_debug == 1);
  139. } elsif ($cmd_type eq "alarm") {
  140. if (!defined($hash->{DEVNAME_REF}) || !defined($hash->{DIFF_TEMP})) {
  141. # should never happen!
  142. Log 1, "Error dewpoint: DEVNAME_REF || DIFF_TEMP undefined";
  143. return "";
  144. }
  145. $devname_ref = $hash->{DEVNAME_REF};
  146. $diff_temp = $hash->{DIFF_TEMP};
  147. Log 1, "dewpoint_notify: cmd_type=$cmd_type devname=$devName dewname=$hashName, dev=$devName, dev_regex=$re, devname_ref=$devname_ref, diff_temp=$diff_temp" if ($dewpoint_debug == 1);
  148. } else {
  149. # should never happen:
  150. Log 1, "Error notify_dewpoint: <1> unknown cmd_type ".$cmd_type;
  151. return "";
  152. }
  153. my $max = int(@{$dev->{CHANGED}});
  154. my $tn;
  155. my $n = -1;
  156. my $lastval;
  157. return "" if($devName !~ m/^$re$/);
  158. my $temperature = "";
  159. my $humidity = "";
  160. my $time_diff;
  161. for (my $i = 0; $i < $max; $i++) {
  162. my $s = $dev->{CHANGED}[$i];
  163. Log 1, "dewpoint_notify: s='$s'" if ($dewpoint_debug == 1);
  164. ################
  165. # Filtering
  166. next if(!defined($s));
  167. my ($evName, $val, $rest) = split(" ", $s, 3); # resets $1
  168. next if(!defined($evName));
  169. next if(!defined($val));
  170. Log 1, "dewpoint_notify: evName='$evName' val=$val'" if ($dewpoint_debug == 1);
  171. if (($evName eq "T:") && ($temp_name eq "T")) {
  172. $n = $i;
  173. #my ($evName1, $val1, $evName2, $val2, $rest) = split(" ", $s, 5); # resets $1
  174. #$lastval = $evName1." ".$val1." ".$evName2." ".$val2;
  175. $lastval = $s;
  176. if ($s =~ /T: ([-+]?[0-9]*\.[0-9]+|[-+]?[0-9]+)/) {
  177. $temperature = $1;
  178. }
  179. if ($s =~ /H: [-+]?([0-9]*\.[0-9]+|[0-9]+)/) {
  180. $humidity = $1;
  181. }
  182. Log 1, "dewpoint_notify T: H:, temp=$temperature hum=$humidity" if ($dewpoint_debug == 1);
  183. } elsif ($evName eq $temp_name.":") {
  184. $temperature = $val;
  185. Log 1, "dewpoint_notify temperature! dev=$devName, temp_name=$temp_name, temp=$temperature" if ($dewpoint_debug == 1);
  186. } elsif ($evName eq $hum_name.":") {
  187. $humidity = $val;
  188. Log 1, "dewpoint_notify humidity! dev=$devName, hum_name=$hum_name, hum=$humidity" if ($dewpoint_debug == 1);
  189. }
  190. }
  191. if ($n == -1) { $n = $max; }
  192. #if (($temperature eq "") || ($humidity eq "")) { return undef; } # no way to calculate dewpoint!
  193. $time_diff = -1;
  194. if (($humidity eq "") && (($temperature eq ""))) {
  195. return undef; # no way to calculate dewpoint!
  196. } elsif (($humidity eq "") && (($temperature ne ""))) {
  197. # temperature set, but humidity not. Try to use a valid value from the appropiate reading
  198. if (defined($dev->{READINGS}{$hum_name}{VAL}) && defined($dev->{READINGS}{$temp_name}{TIME})) {
  199. # calculate time difference
  200. $time_diff = time() - time_str2num($dev->{READINGS}{$hum_name}{TIME});
  201. $humidity = $dev->{READINGS}{$hum_name}{VAL};
  202. Log 1,">dev=$devName, hum_name=$hum_name, reference humidity=$humidity ($time_diff), temp=$temperature" if ($dewpoint_debug == 1);
  203. } else { return undef; }
  204. # Check if Attribute timeout is set
  205. my $timeout = AttrVal($hash->{NAME},"max_timediff", undef);
  206. if (defined($timeout)) {
  207. Log 1,"dewpoint timeout=$timeout" if ($dewpoint_debug == 1);
  208. } else {
  209. $timeout = $dewpoint_time_diff_default;
  210. }
  211. if ($time_diff > 0 && $time_diff > $timeout) { return undef; }
  212. } elsif (($temperature eq "") && ($humidity ne "")) {
  213. # humdidity set, but temperature not. Try to use a valid value from the appropiate reading
  214. if (defined($dev->{READINGS}{$temp_name}{VAL}) && defined($dev->{READINGS}{$temp_name}{TIME})) {
  215. # calculate time difference
  216. $time_diff = time() - time_str2num($dev->{READINGS}{$temp_name}{TIME});
  217. $temperature = $dev->{READINGS}{$temp_name}{VAL};
  218. Log 1,">dev=$devName, temp_name=$temp_name, reference temperature=$temperature ($time_diff), hum=$humidity" if ($dewpoint_debug == 1);
  219. } else { return undef; }
  220. # Check if Attribute timeout is set
  221. my $timeout = AttrVal($hash->{NAME},"max_timediff", undef);
  222. if (defined($timeout)) {
  223. Log 1,"dewpoint timeout=$timeout" if ($dewpoint_debug == 1);
  224. } else {
  225. $timeout = $dewpoint_time_diff_default;
  226. }
  227. if ($time_diff > 0 && $time_diff > $timeout) { return undef; }
  228. }
  229. # We found temperature and humidity. so we can calculate dewpoint first
  230. # Prüfen, ob humidity im erlaubten Bereich ist
  231. if (($humidity <= 0) || ($humidity >= 110)){
  232. Log 1, "Error dewpoint: humidity invalid: $humidity";
  233. return undef;
  234. }
  235. my $dewpoint = sprintf("%.1f", dewpoint_dewpoint($temperature,$humidity));
  236. Log 1, "dewpoint_notify: dewpoint=$dewpoint" if ($dewpoint_debug == 1);
  237. if ($cmd_type eq "dewpoint") {
  238. # >define <name> dewpoint dewpoint <devicename> [<temp_name> <hum_name> <new_name>]
  239. #
  240. # Calculates dewpoint for device <devicename> from temperature and humidity and write it
  241. # to new Reading dewpoint.
  242. # If optional <temp_name>, <hum_name> and <newname> is specified
  243. # then read temperature from reading <temp_name>, humidity from reading <hum_name>
  244. # and write dewpoint to reading <temp_name>.
  245. # if temp_name eq "T" then use temperature from state T: H:, add <newname> to the state
  246. # Example:
  247. # define dewtest1 dewpoint dewpoint .*
  248. # define dewtest2 dewpoint dewpoint .* T H D
  249. my $current;
  250. my $sensor;
  251. my $aFeuchte = AttrVal($hash->{NAME},"absFeuchte", undef);
  252. if (defined($aFeuchte)) {
  253. $sensor = "absFeuchte";
  254. $current = sprintf("%.1f", dewpoint_absFeuchte($temperature,$humidity));
  255. $tn = TimeNow();
  256. $dev->{READINGS}{$sensor}{TIME} = $tn;
  257. $dev->{READINGS}{$sensor}{VAL} = $current;
  258. $dev->{CHANGED}[$n++] = $sensor . ": " . $current;
  259. Log 1,"dewpoint absFeuchte= $current" if ($dewpoint_debug == 1);
  260. $aFeuchte = "A: " . $current;
  261. }
  262. $sensor = $new_name;
  263. if ($temp_name ne "T") {
  264. $current = $dewpoint;
  265. $tn = TimeNow();
  266. $dev->{READINGS}{$sensor}{TIME} = $tn;
  267. $dev->{READINGS}{$sensor}{VAL} = $current;
  268. $dev->{CHANGED}[$n++] = $sensor . ": " . $current;
  269. } else {
  270. #Log 1,">dev=$devName, lastval='$lastval' devSTATE='".$dev->{STATE}."' state=".$dev->{READINGS}{state}{VAL}."'";
  271. # state begins with "T:". append dewpoint or insert before BAT
  272. if ($lastval =~ /BAT:/) {
  273. $current = $lastval;
  274. $current =~ s/BAT:/$sensor: $dewpoint BAT:/g;
  275. } elsif ($lastval =~ /<</) {
  276. $current = $lastval;
  277. $current =~ s/<</$sensor:$dewpoint <</g;
  278. } else {
  279. $current = $lastval." ".$sensor.": ".$dewpoint;
  280. if (defined($aFeuchte)) {
  281. $current = $current." ".$aFeuchte;
  282. }
  283. }
  284. $dev->{STATE} = $current;
  285. $dev->{CHANGED}[$n++] = $current;
  286. }
  287. Log 1, "dewpoint_notify: current=$current" if ($dewpoint_debug == 1);
  288. } elsif ($cmd_type eq "fan") {
  289. # >define <name> dewpoint fan devicename devicename-outside min-temp [diff-temp]
  290. #
  291. # This define may be used to turn an fan on or off if the outside air has less
  292. # water
  293. #
  294. # - Generate reading/event "fan: on" if (dewpoint of <devicename-outside>) + diff_temp is lower
  295. # than dewpoint of <devicename> and temperature of <devicename-outside> is >= min-temp
  296. # and reading "fan" was not already "on".
  297. # - Generate reading/event "fan: off": else and if reading "fan" was not already "off".
  298. Log 1, "dewpoint_notify: fan devname_out=$devname_out, min_temp=$min_temp, diff_temp=$diff_temp" if ($dewpoint_debug == 1);
  299. my $sensor;
  300. my $current;
  301. if (exists $defs{$devname_out}{READINGS}{temperature}{VAL} && exists $defs{$devname_out}{READINGS}{humidity}{VAL}) {
  302. my $temperature_out = $defs{$devname_out}{READINGS}{temperature}{VAL};
  303. my $humidity_out = $defs{$devname_out}{READINGS}{humidity}{VAL};
  304. my $dewpoint_out = sprintf("%.1f", dewpoint_dewpoint($temperature_out,$humidity_out));;
  305. Log 1, "dewpoint_notify: fan dewpoint_out=$dewpoint_out" if ($dewpoint_debug == 1);
  306. if (($dewpoint_out + $diff_temp) < $dewpoint && $temperature_out >= $min_temp) {
  307. $current = "on";
  308. Log 1, "dewpoint_notify: fan ON" if ($dewpoint_debug == 1);
  309. } else {
  310. $current = "off";
  311. Log 1, "dewpoint_notify: fan OFF" if ($dewpoint_debug == 1);
  312. }
  313. $sensor = "fan";
  314. if (!exists $defs{$devName}{READINGS}{$sensor}{VAL} || $defs{$devName}{READINGS}{$sensor}{VAL} ne $current) {
  315. Log 1, "dewpoint_notify: CHANGE fan $current" if ($dewpoint_debug == 1);
  316. $tn = TimeNow();
  317. $dev->{READINGS}{$sensor}{TIME} = $tn;
  318. $dev->{READINGS}{$sensor}{VAL} = $current;
  319. $dev->{CHANGED}[$n++] = $sensor . ": " . $current;
  320. }
  321. } else {
  322. Log 1, "dewpoint_notify: fan devname_out=$devname_out no temperature or humidity available for dewpoint calculation" if ($dewpoint_debug == 1);
  323. }
  324. } elsif ($cmd_type eq "alarm") {
  325. # >define <name> dewpoint alarm devicename devicename-reference diff
  326. #
  327. # - Generate reading/event "alarm: on" if temperature of <devicename-reference>-<diff> is lower
  328. # than dewpoint of <devicename> and reading "alarm" was not already "on".
  329. # - Generate reading/event "alarm: off" if temperature of <devicename-reference>-<diff> is higher
  330. # than dewpoint of <devicename> and reading "alarm" was not already "off".
  331. #
  332. # You have different options to use this define:
  333. # * Use a temperature sensor in or on the wall (<devicename-reference>) and use a temp/hum sensor
  334. # to measure the dewpoint of the air. Alarm if the temperature of the wall is lower than the dewpoint of the air.
  335. # In this case the water of the air will condense on the wall because the wall is cold.
  336. # Example: define alarmtest dewpoint alarm roomsensor wallsensor 0
  337. # * If you do not have a temperature sensor in/on the wall, you may also compare the rooms dewpoint to the
  338. # temperature of the same or another inside sensor. If you think that your walls are normally 5 degrees colder
  339. # than the inside temperature, set diff to 5.
  340. # Example: define alarmtest dewpoint alarm roomsensor roomsensor 5
  341. Log 1, "dewpoint_notify: alarm devname_ref=$devname_ref, diff_temp=$diff_temp" if ($dewpoint_debug == 1);
  342. my $sensor;
  343. my $current;
  344. if (exists $defs{$devname_ref}{READINGS}{temperature}{VAL}) {
  345. my $temperature_ref = $defs{$devname_ref}{READINGS}{temperature}{VAL};
  346. Log 1, "dewpoint_notify: alarm temperature_ref=$temperature_ref" if ($dewpoint_debug == 1);
  347. if ($temperature_ref - $diff_temp < $dewpoint) {
  348. $current = "on";
  349. Log 1, "dewpoint_notify: alarm ON" if ($dewpoint_debug == 1);
  350. } else {
  351. $current = "off";
  352. Log 1, "dewpoint_notify: alarm OFF" if ($dewpoint_debug == 1);
  353. }
  354. $sensor = "alarm";
  355. if (!exists $defs{$devName}{READINGS}{$sensor}{VAL} || $defs{$devName}{READINGS}{$sensor}{VAL} ne $current) {
  356. Log 1, "dewpoint_notify: CHANGE alarm $current" if ($dewpoint_debug == 1);
  357. $tn = TimeNow();
  358. $dev->{READINGS}{$sensor}{TIME} = $tn;
  359. $dev->{READINGS}{$sensor}{VAL} = $current;
  360. $dev->{CHANGED}[$n++] = $sensor . ": " . $current;
  361. }
  362. } else {
  363. Log 1, "dewpoint_notify: alarm devname_out=$devname_out no temperature or humidity available for dewpoint calculation" if ($dewpoint_debug == 1);
  364. }
  365. } else {
  366. # should never happen:
  367. Log 1, "Error notify_dewpoint: <2> unknown cmd_type ".$cmd_type;
  368. return "";
  369. }
  370. return undef;
  371. }
  372. # -----------------------------
  373. # Dewpoint calculation.
  374. # see http://www.faqs.org/faqs/meteorology/temp-dewpoint/ "5. EXAMPLE"
  375. sub
  376. dewpoint_dewpoint($$)
  377. {
  378. my ($temperature, $humidity) = @_;
  379. my $dp;
  380. my $A = 17.2694;
  381. my $B = ($temperature > 0) ? 237.3 : 265.5;
  382. my $es = 610.78 * exp( $A * $temperature / ($temperature + $B) );
  383. my $e = $humidity/ 100 * $es;
  384. if ($e == 0) {
  385. Log 1, "Error: dewpoint() e==0: temp=$temperature, hum=$humidity";
  386. return 0;
  387. }
  388. my $e1 = $e / 610.78;
  389. my $f = log( $e1 ) / $A;
  390. my $f1 = 1 - $f;
  391. if ($f1 == 0) {
  392. Log 1, "Error: dewpoint() (1-f)==0: temp=$temperature, hum=$humidity";
  393. return 0;
  394. }
  395. $dp = $B * $f / $f1 ;
  396. return($dp);
  397. }
  398. sub
  399. dewpoint_absFeuchte ($$)
  400. {
  401. # Formeln von http://kellerlueftung.blogspot.de/p/blog-page_9.html
  402. # http://www.wettermail.de/wetter/feuchte.html
  403. my ($T, $rh) = @_;
  404. if (($rh < 0) || ($rh > 110)){
  405. Log 1, "Error dewpoint: humidity invalid: $rh";
  406. return "";
  407. }
  408. # a = 7.5, b = 237.3 für T >= 0
  409. # a = 7.6, b = 240.7 für T < 0 über Wasser (Taupunkt)
  410. my $a = ($T > 0) ? 7.5 : 7.6;
  411. my $b = ($T > 0) ? 237.3 : 240.7;
  412. my $SDD = 6.1078 * 10**(($a*$T)/($b+$T));
  413. my $DD = $rh/100 * $SDD;
  414. my $AF = (10**5) * (18.016 / 8314.3) * ($DD / (273.15 + $T));
  415. my $af = sprintf( "%.1f",$AF);
  416. return($af); # $aF = absolute Feuchte in g Wasserdampf pro m3 Luft
  417. }
  418. 1;
  419. =pod
  420. =begin html
  421. <a name="dewpoint"></a>
  422. <h3>dewpoint</h3>
  423. <ul>
  424. Dewpoint calculations. Offers three different ways to use dewpoint: <br>
  425. <ul>
  426. <li><b>dewpoint</b><br>
  427. Compute additional event dewpoint from a sensor offering temperature and humidity.</li>
  428. <li><b>fan</b><br>
  429. Generate a event to turn a fan on if the outside air has less water than the inside.</li>
  430. <li><b>alarm</b><br>
  431. Generate a mold alarm if a reference temperature is lower that the current dewpoint.</li>
  432. <br>
  433. </ul>
  434. <a name="dewpointdefine"></a>
  435. <b>Define</b>
  436. <ul>
  437. <code>define &lt;name&gt; dewpoint dewpoint &lt;devicename-regex&gt; [&lt;temp_name&gt; &lt;hum_name&gt; &lt;new_name&gt;]</code><br>
  438. <br>
  439. <ul>
  440. Calculates dewpoint for device &lt;devicename-regex&gt; from temperature and humidity
  441. and write it to a new reading named dewpoint.
  442. If optional &lt;temp_name&gt;, &lt;hum_name&gt; and &lt;new_name&gt; is specified
  443. then read temperature from reading &lt;temp_name&gt;, humidity from reading &lt;hum_name&gt;
  444. and write the calculated dewpoint to reading &lt;new_name&gt;.<br>
  445. If &lt;temp_name&gt; is T then use temperature from state T: H:, add &lt;new_name&gt; to the state.
  446. </ul>
  447. <br>
  448. Example:<PRE>
  449. # Compute the dewpoint for the temperature/humidity
  450. # events of the temp1 device and generate reading dewpoint.
  451. define dew_temp1 dewpoint dewpoint temp1
  452. define dew_temp1 dewpoint dewpoint temp1 temperature humidity dewpoint
  453. # Compute the dewpoint for the temperature/humidity
  454. # events of all devices offering temperature and humidity
  455. # and generate reading dewpoint.
  456. define dew_all dewpoint dewpoint .*
  457. define dew_all dewpoint dewpoint .* temperature humidity dewpoint
  458. # Compute the dewpoint for the temperature/humidity
  459. # events of the device Aussen_1 offering temperature and humidity
  460. # and insert is into STATE.
  461. define dew_state dewpoint dewpoint Aussen_1 T H D
  462. # Compute the dewpoint for the temperature/humidity
  463. # events of all devices offering temperature and humidity
  464. # and insert the result into the STATE.
  465. # Example STATE: "T: 10 H: 62.5" will change to
  466. # "T: 10 H: 62.5 D: 3.2"
  467. define dew_state dewpoint dewpoint .* T H D
  468. </PRE>
  469. </ul>
  470. <ul>
  471. <code>define &lt;name&gt; dewpoint fan &lt;devicename-regex&gt; &lt;devicename-outside&gt; &lt;min-temp&gt; [&lt;diff_temp&gt;]</code><br>
  472. <br>
  473. <ul>
  474. May be used to turn an fan on or off if the outside air has less water.
  475. <ul>
  476. <li>
  477. Generate event "fan: on" if (dewpoint of &lt;devicename-outside&gt;) + &lt;diff_temp&gt; is lower
  478. than dewpoint of &lt;devicename&gt; and temperature of &lt;devicename-outside&gt; is &gt;= min-temp
  479. and reading "fan" was not already "on". The event will be generated for &lt;devicename&gt;. Parameter &lt;diff-temp&gt; is optional</li>
  480. <li>Generate event "fan: off": else and if reading "fan" was not already "off".</li>
  481. </ul>
  482. </ul>
  483. <br>
  484. Example:<PRE>
  485. # Generate event "fan: on" when dewpoint of Aussen_1 is first
  486. # time lower than basement_tempsensor and outside temperature is &gt;= 0
  487. # and change it to "fan: off" is this condition changes.
  488. # Set a switch on/off (fan_switch) depending on the state.
  489. define dew_fan1 dewpoint fan basement_tempsensor Aussen_1 0
  490. define dew_fan1_on notify basement_tempsensor.*fan:.*on set fan_switch on
  491. define dew_fan1_off notify basement_tempsensor.*fan:.*off set fan_switch off
  492. </PRE>
  493. </ul>
  494. <ul>
  495. <code>define &lt;name&gt; dewpoint alarm &lt;devicename-regex&gt; &lt;devicename-reference&gt; &lt;diff-temp&gt;</code><br>
  496. <br>
  497. <ul>
  498. Generate a mold alarm if a reference temperature is lower that the current dewpoint.
  499. <ul>
  500. <li>
  501. Generate reading/event "alarm: on" if temperature of &lt;devicename-reference&gt; - &lt;diff-temp&gt; is lower
  502. than dewpoint of &lt;devicename&gt; and reading "alarm" was not already "on". The event will be generated for &lt;devicename&gt;.</li>
  503. <li>Generate reading/event "alarm: off" if temperature of &lt;devicename-reference&gt; - &lt;diff-temp&gt; is higher than dewpoint of &lt;devicename&gt; and reading "alarm" was not already "off".</li>
  504. </ul>
  505. </ul>
  506. <br>
  507. Example:<PRE>
  508. # Using a wall temperature sensor (wallsensor) and a temp/hum sensor
  509. # (roomsensor) to alarm if the temperature of the wall is lower than
  510. # the dewpoint of the air. In this case the water of the air will
  511. # condense on the wall because the wall is cold.
  512. # Set a switch on (alarm_siren) if alarm is on using notify.
  513. define dew_alarm1 dewpoint alarm roomsensor wallsensor 0
  514. define roomsensor_alarm_on notify roomsensor.*alarm:.*on set alarm_siren on
  515. define roomsensor_alarm_off notify roomsensor.*alarm:.*off set alarm_siren off
  516. # If you do not have a temperature sensor in/on the wall, you may also
  517. # compare the rooms dewpoint to the temperature of the same or another
  518. # inside sensor. Alarm is temperature is 5 degrees colder than the
  519. # inside dewpointinside.
  520. define dev_alarm2 dewpoint alarm roomsensor roomsensor 5
  521. </PRE>
  522. </ul>
  523. <a name="dewpointset"></a>
  524. <b>Set</b> <ul>N/A</ul><br>
  525. <a name="dewpointget"></a>
  526. <b>Get</b> <ul>N/A</ul><br>
  527. <a name="dewpointattr"></a>
  528. <b>Attributes</b>
  529. <ul>
  530. <li><a href="#disable">disable</a></li>
  531. <li>absFeuchte</li><br>
  532. <ul>
  533. AbsFeuchte also becomes by the absolute humidity set the attribute into the Readings of all things.
  534. One can by these show information also in the status.
  535. Example: (<Adapter> = the FHEM name of the adapter this one you must change)
  536. <PRE>
  537. stateFormat:
  538. {sprintf("T: %.1f H: %.1f D: %.1f A: %.1f", ReadingsVal("<Adapter>","temperature",0), ReadingsVal("<Adapter>","H",0), ReadingsVal("<Adapter>","dewpoint",0), ReadingsVal("<Adapter>","absFeuchte",0))}
  539. </PRE>
  540. </ul>
  541. <li>max_timediff<br>
  542. Maximum time difference in seconds allowed between the temperature and humidity values for a device. dewpoint uses the Readings for temperature or humidity if they are not delivered in the event. This is necessary for using dewpoint with event-on-change-reading. Also needed for sensors that do deliver temperature and humidity in different events like for example technoline sensors TX3TH.<br>
  543. If not set default is 1 second.
  544. <br><br>
  545. Examples:<PRE>
  546. # allow maximum time difference of 60 seconds
  547. define dew_all dewpoint dewpoint .*
  548. attr dew_all max_timediff 60
  549. </li><br>
  550. </ul>
  551. </ul>
  552. =end html
  553. =begin html_DE
  554. <a name="dewpoint"></a>
  555. <h3>dewpoint</h3>
  556. <ul>
  557. Berechnungen des Taupunkts. Es gibt drei Varianten, das Modul dewpoint zu verwenden: <br>
  558. <ul>
  559. <li><b>dewpoint</b>: Taupunkt<br>
  560. Erzeugt ein zus&auml;tzliches Ereignis "dewpoint" aus Temperatur- und Luftfeuchtewerten eines F&uuml;hlers.</li>
  561. <li><b>fan</b>: L&uuml;fter<br>
  562. Erzeugt ein Ereignis, um einen L&uuml;fter einzuschalten, wenn die Au&szlig;enluft
  563. weniger Wasser als die Raumluft enth&auml;lt.</li>
  564. <li><b>alarm</b>: Alarm<br>
  565. Erzeugt einen Schimmel-Alarm, wenn eine Referenz-Temperatur unter den Taupunkt f&auml;llt.</li>
  566. </ul>
  567. <br/>
  568. <a name="dewpointdefine"></a>
  569. <b>Define</b>
  570. <ul>
  571. <code>define &lt;name&gt; dewpoint dewpoint &lt;devicename-regex&gt; [&lt;temp_name&gt;
  572. &lt;hum_name&gt; &lt;new_name&gt;]</code><br>
  573. <br/>
  574. Berechnet den Taupunkt des Ger&auml;ts &lt;devicename-regex&gt; basierend auf Temperatur
  575. und Luftfeuchte und erzeugt daraus ein neues Reading namens dewpoint.<br/>
  576. Wenn &lt;temp_name&gt;, &lt;hum_name&gt; und &lt;new_name&gt; angegeben sind,
  577. werden die Temperatur aus dem Reading &lt;temp_name&gt;, die Luftfeuchte aus dem
  578. Reading &lt;hum_name&gt; gelesen und als berechneter Taupunkt ins Reading &lt;new_name&gt; geschrieben.<br>
  579. Wenn &lt;temp_name&gt; T lautet, wird die Temperatur aus state T: H: benutzt
  580. und &lt;new_name&gt; zu state hinzugef&uuml;gt.
  581. <br/>
  582. Beispiele:
  583. <pre>
  584. # Berechnet den Taupunkt aufgrund von Temperatur und Luftfeuchte
  585. # in Ereignissen, die vom Ger&auml;t temp1 erzeugt wurden und erzeugt ein Reading dewpoint.
  586. define dew_temp1 dewpoint dewpoint temp1
  587. define dew_temp1 dewpoint dewpoint temp1 temperature humidity dewpoint
  588. # Berechnet den Taupunkt aufgrund von Temperatur und Luftfeuchte
  589. # in Ereignissen, die von allen Ger&auml;ten erzeugt wurden die diese Werte ausgeben
  590. # und erzeugt ein Reading dewpoint.
  591. define dew_all dewpoint dewpoint .*
  592. define dew_all dewpoint dewpoint .* temperature humidity dewpoint
  593. # Berechnet den Taupunkt aufgrund von Temperatur und Luftfeuchte
  594. # in Ereignissen, die vom Ger&auml;t Aussen_1 erzeugt wurden und erg&auml;nzt
  595. # mit diesem Wert den Status STATE.
  596. define dew_state dewpoint dewpoint Aussen_1 T H D
  597. # Berechnet den Taupunkt aufgrund von Temperatur und Luftfeuchte
  598. # in Ereignissen, die von allen Ger&auml;ten erzeugt wurden die diese Werte ausgeben
  599. # und erg&auml;nzt mit diesem Wert den Status STATE.
  600. # Beispiel STATE: "T: 10 H: 62.5" wird ver&auml;ndert nach
  601. # "T: 10 H: 62.5 D: 3.2"
  602. define dew_state dewpoint dewpoint .* T H D
  603. </pre>
  604. <br/>
  605. <br/>
  606. <code>define &lt;name&gt; dewpoint fan &lt;devicename-regex&gt; &lt;devicename-outside&gt; &lt;min-temp&gt; [&lt;diff_temp&gt;]</code><br>
  607. <br>
  608. <ul>
  609. <li>Erzeugt ein Ereignis, um einen L&uuml;fter einzuschalten, wenn die Au&szlig;enluft
  610. weniger Wasser als die Raumluft enth&auml;lt.</li>
  611. <li>Erzeugt das Ereignis "fan: on" wenn (Taupunkt von &lt;devicename-outside&gt;) +
  612. &lt;diff_temp&gt; ist niedriger als der Taupunkt von &lt;devicename&gt; und die Temperatur
  613. von &lt;devicename-outside&gt; &gt;= min-temp ist. Das Ereignis wird nur erzeugt wenn das
  614. Reading "fan" nicht schon "on" war. Das Ereignis wird f&uuml;r das Ger&auml;t &lt;devicename&gt; erzeugt.
  615. Der Parameter &lt;diff-temp&gt; ist optional.</li>
  616. <li>Andernfalls wird das Ereignis "fan: off" erzeugt, wenn das Reading von "fan" nicht bereits "off" war.</li>
  617. </ul>
  618. <br>
  619. Beispiel:
  620. <pre>
  621. # Erzeugt das Ereignis "fan: on", wenn der Taupunkt des Ger&auml;ts Aussen_1 zum ersten Mal
  622. # niedriger ist als der Taupunkt des Ger&auml;ts basement_tempsensor und die
  623. # Au&szlig;entemperatur &gt;= 0 ist und wechselt nach "fan: off" wenn diese Bedingungen nicht
  624. # mehr zutreffen.
  625. # Schaltet den Schalter fan_switch abh&auml;ngig vom Zustand ein oder aus.
  626. define dew_fan1 dewpoint fan basement_tempsensor Aussen_1 0
  627. define dew_fan1_on notify basement_tempsensor.*fan:.*on set fan_switch on
  628. define dew_fan1_off notify basement_tempsensor.*fan:.*off set fan_switch off
  629. </pre>
  630. <code>define &lt;name&gt; dewpoint alarm &lt;devicename-regex&gt; &lt;devicename-reference&gt; &lt;diff-temp&gt;</code><br>
  631. <br>
  632. <ul>
  633. <li>Erzeugt einen Schimmel-Alarm, wenn eine Referenz-Temperatur unter den Taupunkt f&auml;llt.</li>
  634. <li>Erzeugt ein Reading/Ereignis "alarm: on" wenn die Temperatur von
  635. &lt;devicename-reference&gt; - &lt;diff-temp&gt; unter den Taupunkt von
  636. &lt;devicename&gt; f&auml;llt und das Reading "alarm" nicht bereits "on" ist.
  637. Das Ereignis wird f&uuml;r &lt;devicename&gt; erzeugt.</li>
  638. <li>Erzeugt ein Reading/Ereignis "alarm: off" wenn die Temperatur von
  639. &lt;devicename-reference&gt; - &lt;diff-temp&gt; &uuml;ber den Taupunkt
  640. von &lt;devicename&gt; steigt und das Reading "alarm" nicht bereits "off" ist.</li>
  641. </ul>
  642. <br>
  643. Beispiel:
  644. <pre>
  645. # Es wird ein Anlegef&uuml;hler (Wandsensor) und ein Thermo-/Hygrometer (Raumf&uuml;hler)
  646. # verwendet, um einen Alarm zu erzeugen, wenn die Wandtemperatur
  647. # unter den Taupunkt der Luft f&auml;llt. In diesem Fall w&uuml;rde sich Wasser an der Wand
  648. # niederschlagen (kondensieren), weil die Wand zu kalt ist.
  649. # Der Schalter einer Sirene (alarm_siren) wird &uuml;ber ein notify geschaltet.
  650. define dew_alarm1 dewpoint alarm roomsensor wallsensor 0
  651. define roomsensor_alarm_on notify roomsensor.*alarm:.*on set alarm_siren on
  652. define roomsensor_alarm_off notify roomsensor.*alarm:.*off set alarm_siren off
  653. # Ohne Wandsensor l&auml;sst sich auch der Taupunkt eines Raums mit der Temperatur desselben
  654. # (oder eines anderen) F&uuml;hlers vergleichen.
  655. # Die Alarmtemperatur ist 5 Grad niedriger gesetzt als die des Vergleichsthermostats.
  656. define dev_alarm2 dewpoint alarm roomsensor roomsensor 5
  657. </pre>
  658. </ul>
  659. <a name="dewpointset"></a>
  660. <b>Set</b> <ul>N/A</ul><br>
  661. <a name="dewpointget"></a>
  662. <b>Get</b> <ul>N/A</ul><br>
  663. <a name="dewpointattr"></a>
  664. <b>Attributes</b>
  665. <ul>
  666. <li><a href="#disable">disable</a></li>
  667. <li>absFeuchte</li><br>
  668. <ul>
  669. Durch setzen des Attributes absFeuchte wird in den Readings auch die absolute Feuchte mit ausgerechnet.
  670. Durch <a href="#stateFormat">stateFormat</a> kann man diese Info auch im Status anzeigen.
  671. Beispiel: (<Adapter> = Der FHEM Name des Adapters der geändert werden muss)
  672. <pre>
  673. stateFormat:
  674. {sprintf("T: %.1f H: %.1f D: %.1f A: %.1f", ReadingsVal("<Adapter>","temperature",0), ReadingsVal("<Adapter>","H",0), ReadingsVal("<Adapter>","dewpoint",0), ReadingsVal("<Adapter>","absFeuchte",0))}
  675. </pre>
  676. </ul>
  677. <li>max_timediff</li><br>
  678. <ul>
  679. Maximale erlaubter Zeitunterschied in Sekunden zwischen den Temperatur- und Luftfeuchtewerten eines
  680. Ger&auml;ts. dewpoint verwendet Readings von Temperatur oder Luftfeuchte wenn sie nicht im Ereignis
  681. mitgeliefert werden. Das ist sowohl f&uuml;r den Betrieb mit event-on-change-reading n&ouml;tig
  682. als auch bei Sensoren die Temperatur und Luftfeuchte in getrennten Ereignissen kommunizieren
  683. (z.B. Technoline Sensoren TX3TH).<br/>
  684. Der Standardwert ist 1 Sekunde.
  685. <br><br>
  686. Beispiel:
  687. <pre>
  688. # Maximal erlaubter Zeitunterschied soll 60 Sekunden sein
  689. define dew_all dewpoint dewpoint .*
  690. attr dew_all max_timediff 60
  691. </pre>
  692. </ul>
  693. </ul>
  694. </ul>
  695. =end html_DE
  696. =cut