50_HP1000.pm 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544
  1. # $Id: 50_HP1000.pm 13431 2017-02-18 14:59:51Z loredo $
  2. ##############################################################################
  3. #
  4. # 50_HP1000.pm
  5. # An FHEM Perl module to receive data from HP1000 weather stations.
  6. #
  7. # Copyright by Julian Pawlowski
  8. # e-mail: julian.pawlowski at gmail.com
  9. #
  10. # This file is part of fhem.
  11. #
  12. # Fhem is free software: you can redistribute it and/or modify
  13. # it under the terms of the GNU General Public License as published by
  14. # the Free Software Foundation, either version 2 of the License, or
  15. # (at your option) any later version.
  16. #
  17. # Fhem is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. # GNU General Public License for more details.
  21. #
  22. # You should have received a copy of the GNU General Public License
  23. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  24. #
  25. ##############################################################################
  26. package main;
  27. use strict;
  28. use warnings;
  29. use vars qw(%data);
  30. use HttpUtils;
  31. use utf8;
  32. use Encode qw(encode_utf8 decode_utf8);
  33. use Unit;
  34. use Time::Local;
  35. use List::Util qw(sum);
  36. use Scalar::Util qw(looks_like_number);
  37. use Data::Dumper;
  38. #########################
  39. sub HP1000_addExtension($$$) {
  40. my ( $name, $func, $link ) = @_;
  41. my $url = "/$link";
  42. Log3 $name, 2, "Registering HP1000 $name for URL $url...";
  43. $data{FWEXT}{$url}{deviceName} = $name;
  44. $data{FWEXT}{$url}{FUNC} = $func;
  45. $data{FWEXT}{$url}{LINK} = $link;
  46. }
  47. #########################
  48. sub HP1000_removeExtension($) {
  49. my ($link) = @_;
  50. my $url = "/$link";
  51. my $name = $data{FWEXT}{$url}{deviceName};
  52. Log3 $name, 2, "Unregistering HP1000 $name for URL $url...";
  53. delete $data{FWEXT}{$url};
  54. }
  55. ###################################
  56. sub HP1000_Initialize($) {
  57. my ($hash) = @_;
  58. Log3 $hash, 5, "HP1000_Initialize: Entering";
  59. if ( !$modules{dewpoint}{LOADED}
  60. && -f "$attr{global}{modpath}/FHEM/98_dewpoint.pm" )
  61. {
  62. my $ret = CommandReload( undef, "98_dewpoint" );
  63. Log3 undef, 1, $ret if ($ret);
  64. }
  65. $hash->{GetFn} = "HP1000_Get";
  66. $hash->{DefFn} = "HP1000_Define";
  67. $hash->{UndefFn} = "HP1000_Undefine";
  68. $hash->{DbLog_splitFn} = "Unit_DbLog_split";
  69. $hash->{parseParams} = 1;
  70. $hash->{AttrList} =
  71. "wu_push:1,0 wu_id wu_password wu_realtime:1,0 extSrvPush_Url stateReadingsLang:en,de,at,ch,nl,fr,pl stateReadings stateReadingsFormat:0,1 "
  72. . $readingFnAttributes;
  73. # Unit.pm support
  74. $hash->{readingsDesc} = {
  75. 'Activity' => { rtype => 'oknok', },
  76. 'UV' => { rtype => 'uvi', },
  77. 'UVR' => { rtype => 'uwpscm', },
  78. 'UVcondition' => { rtype => 'condition_uvi', },
  79. 'condition' => { rtype => 'condition_weather', },
  80. 'daylight' => { rtype => 'yesno', },
  81. 'dewpoint' => { rtype => 'c', formula_symbol => 'Td', },
  82. 'dewpoint_f' => { rtype => 'f', formula_symbol => 'Td', },
  83. 'extsrv_state' => { rtype => 'oknok', },
  84. 'humidity' => { rtype => 'pct', formula_symbol => 'H', },
  85. 'humidityAbs' => { rtype => 'c', formula_symbol => 'Tabs', },
  86. 'humidityAbs_f' => { rtype => 'f', formula_symbol => 'Tabs', },
  87. 'humidityCondition' => { rtype => 'condition_hum', },
  88. 'indoorDewpoint' => { rtype => 'c', formula_symbol => 'Tdi', },
  89. 'indoorDewpoint_f' => { rtype => 'f', formula_symbol => 'Tdi', },
  90. 'indoorHumidity' => { rtype => 'pct', formula_symbol => 'Hi', },
  91. 'indoorHumidityAbs' => { rtype => 'c', formula_symbol => 'Tabsi', },
  92. 'indoorHumidityAbs_f' => { rtype => 'f', formula_symbol => 'Tabsi', },
  93. 'indoorHumidityCondition' => { rtype => 'condition_hum', },
  94. 'indoorTemperature' => { rtype => 'c', formula_symbol => 'Ti', },
  95. 'indoorTemperature_f' => { rtype => 'f', formula_symbol => 'Ti', },
  96. 'israining' => { rtype => 'yesno', },
  97. 'luminosity' => { rtype => 'lx', },
  98. 'pressure' => { rtype => 'hpamb', },
  99. 'pressureAbs' => { rtype => 'hpamb', },
  100. 'pressureAbs_in' => { rtype => 'inhg', },
  101. 'pressureAbs_mm' => { rtype => 'mmhg', },
  102. 'pressure_in' => { rtype => 'inhg', },
  103. 'pressure_mm' => { rtype => 'mmhg', },
  104. 'rain' => { rtype => 'mm', },
  105. 'rain_day' => { rtype => 'mm', },
  106. 'rain_day_in' => { rtype => 'in', },
  107. 'rain_in' => { rtype => 'in', },
  108. 'rain_month' => { rtype => 'mm', },
  109. 'rain_month_in' => { rtype => 'in', },
  110. 'rain_week' => { rtype => 'mm', },
  111. 'rain_week_in' => { rtype => 'in', },
  112. 'rain_year' => { rtype => 'mm', },
  113. 'rain_year_in' => { rtype => 'in', },
  114. 'solarradiation' => { rtype => 'wpsm', },
  115. 'temperature' => { rtype => 'c', },
  116. 'temperature_f' => { rtype => 'f', },
  117. 'wind_compasspoint' => { rtype => 'compasspoint', },
  118. 'wind_compasspoint_avg2m' => { rtype => 'compasspoint', },
  119. 'wind_chill' => { rtype => 'c', formula_symbol => 'Wc', },
  120. 'wind_chill_f' => { rtype => 'f', formula_symbol => 'Wc', },
  121. 'wind_direction' =>
  122. { rtype => 'compasspoint', formula_symbol => 'Wdir', },
  123. 'wind_direction_avg2m' =>
  124. { rtype => 'compasspoint', formula_symbol => 'Wdir', },
  125. 'wind_gust' => { rtype => 'kmph', formula_symbol => 'Wg', },
  126. 'wind_gust_bft' => { rtype => 'bft', formula_symbol => 'Wg', },
  127. 'wind_gust_fts' => { rtype => 'fts', formula_symbol => 'Wg', },
  128. 'wind_gust_kn' => { rtype => 'kn', formula_symbol => 'Wg', },
  129. 'wind_gust_mph' => { rtype => 'mph', formula_symbol => 'Wg', },
  130. 'wind_gust_mph_sum10m' => { rtype => 'mph', formula_symbol => 'Wg', },
  131. 'wind_gust_mps' => { rtype => 'mps', formula_symbol => 'Wg', },
  132. 'wind_gust_sum10m' => { rtype => 'kmph', formula_symbol => 'Wg', },
  133. 'wind_speed' => { rtype => 'kmph', formula_symbol => 'W', },
  134. 'wind_speed_avg2m' => { rtype => 'kmph', formula_symbol => 'W', },
  135. 'wind_speed_bft' => { rtype => 'bft', formula_symbol => 'W', },
  136. 'wind_speed_bft_avg2m' => { rtype => 'bft', formula_symbol => 'W', },
  137. 'wind_speed_kn' => { rtype => 'kn', formula_symbol => 'W', },
  138. 'wind_speed_kn_avg2m' => { rtype => 'kn', formula_symbol => 'W', },
  139. 'wind_speed_mph' => { rtype => 'mph', formula_symbol => 'W', },
  140. 'wind_speed_mph_avg2m' => { rtype => 'mph', formula_symbol => 'W', },
  141. 'wind_speed_mps' => { rtype => 'mps', formula_symbol => 'W', },
  142. 'wind_speed_mps_avg2m' => { rtype => 'mps', formula_symbol => 'W', },
  143. 'wu_state' => { rtype => 'oknok', },
  144. };
  145. # 98_powerMap.pm support
  146. $hash->{powerMap} = {
  147. rname_E => 'energy',
  148. rname_P => 'consumption',
  149. map => {
  150. Activity => {
  151. 'dead' => 0,
  152. 'alive' => 5,
  153. },
  154. state => {
  155. '*' => 'Activity',
  156. },
  157. },
  158. };
  159. }
  160. ###################################
  161. sub HP1000_Get($$$) {
  162. my ( $hash, $a, $h ) = @_;
  163. my $name = $hash->{NAME};
  164. my $wu_id = AttrVal( $name, "wu_id", "" );
  165. Log3 $name, 5, "HP1000 $name: called function HP1000_Get()";
  166. return "Argument is missing" if ( int(@$a) < 1 );
  167. my $usage = "Unknown argument " . @$a[1] . ", choose one of";
  168. $usage .= " createWUforecast" if ( $wu_id ne "" );
  169. my $cmd = '';
  170. my $result;
  171. # createWUforecast
  172. if ( lc( @$a[1] ) eq "createwuforecast" ) {
  173. return
  174. "Attribute wu_id does not contain a PWS ID to create a Wunderground device"
  175. if ( $wu_id eq "" );
  176. my @wudev = devspec2array("TYPE=Wunderground:FILTER=PWS_ID=$wu_id");
  177. if ( !@wudev ) {
  178. return "Missing WU API key" if ( !defined( @$a[2] ) );
  179. Log3 $name, 3, "HP1000 get $name " . @$a[1] . " " . @$a[2];
  180. $result =
  181. fhem "define $name" . "_WU Wunderground " . @$a[2] . " $wu_id";
  182. $result = $name . "_WU created"
  183. if ( !defined($result) );
  184. }
  185. else {
  186. $result = "Found existing WU device for this PWS ID: $wudev[0]";
  187. }
  188. }
  189. # return usage hint
  190. else {
  191. return $usage;
  192. }
  193. return $result;
  194. }
  195. ###################################
  196. sub HP1000_Define($$$) {
  197. my ( $hash, $a, $h ) = @_;
  198. my $name = $hash->{NAME};
  199. return "Usage: define <name> HP1000 [<ID> <PASSWORD>]"
  200. if ( int(@$a) < 2 );
  201. $hash->{ID} = @$a[2] if ( defined( @$a[2] ) );
  202. $hash->{PASSWORD} = @$a[3] if ( defined( @$a[3] ) );
  203. return
  204. "Device already defined: "
  205. . $modules{HP1000}{defptr}{NAME}
  206. . " (there can only be one instance as per restriction of the weather station itself)"
  207. if ( defined( $modules{HP1000}{defptr} ) && !defined( $hash->{OLDDEF} ) );
  208. # check FHEMWEB instance
  209. if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
  210. my $FWports;
  211. foreach ( devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') ) {
  212. $hash->{FW} = $_
  213. if ( AttrVal( $_, "webname", "fhem" ) eq "weatherstation" );
  214. push( @{$FWports}, $defs{$_}->{PORT} )
  215. if ( defined( $defs{$_}->{PORT} ) );
  216. }
  217. if ( !defined( $hash->{FW} ) ) {
  218. $hash->{FW} = "WEBweatherstation";
  219. my $port = 8084;
  220. until ( !grep ( /^$port$/, @{$FWports} ) ) {
  221. $port++;
  222. }
  223. if ( !defined( $defs{ $hash->{FW} } ) ) {
  224. Log3 $name, 3,
  225. "HP1000 $name: Creating new FHEMWEB instance "
  226. . $hash->{FW}
  227. . " with webname 'weatherstation'";
  228. fhem "define " . $hash->{FW} . " FHEMWEB $port global";
  229. fhem "attr " . $hash->{FW} . " webname weatherstation";
  230. }
  231. }
  232. $hash->{FW_PORT} = $defs{ $hash->{FW} }{PORT};
  233. fhem 'attr ' . $name . ' stateReadings temperature humidity';
  234. fhem 'attr ' . $name . ' stateReadingsFormat 1';
  235. }
  236. if ( HP1000_addExtension( $name, "HP1000_CGI", "updateweatherstation" ) ) {
  237. $hash->{fhem}{infix} = "updateweatherstation";
  238. }
  239. else {
  240. return "Error registering FHEMWEB infix";
  241. }
  242. # create global unique device definition
  243. $modules{HP1000}{defptr} = $hash;
  244. RemoveInternalTimer($hash);
  245. InternalTimer( gettimeofday() + 120, "HP1000_SetAliveState", $hash, 0 );
  246. return undef;
  247. }
  248. ###################################
  249. sub HP1000_Undefine($$$) {
  250. my ( $hash, $a, $h ) = @_;
  251. my $name = $hash->{NAME};
  252. Log3 $name, 5, "HP1000 $name: called function HP1000_Undefine()";
  253. HP1000_removeExtension( $hash->{fhem}{infix} );
  254. # release global unique device definition
  255. delete $modules{HP1000}{defptr};
  256. RemoveInternalTimer($hash);
  257. return undef;
  258. }
  259. ############################################################################################################
  260. #
  261. # Begin of helper functions
  262. #
  263. ############################################################################################################
  264. #####################################
  265. sub HP1000_SetAliveState($;$) {
  266. my ( $hash, $alive ) = @_;
  267. my $name = $hash->{NAME};
  268. Log3 $name, 5, "HP1000 $name: called function HP1000_SetAliveState()";
  269. RemoveInternalTimer($hash);
  270. my $activity = "dead";
  271. $activity = "alive" if ($alive);
  272. readingsBeginUpdate($hash);
  273. readingsBulkUpdateIfChanged( $hash, "Activity", $activity );
  274. readingsEndUpdate( $hash, 1 );
  275. InternalTimer( gettimeofday() + 120, "HP1000_SetAliveState", $hash, 0 );
  276. return;
  277. }
  278. ###################################
  279. sub HP1000_CGI() {
  280. my ($request) = @_;
  281. my $hash;
  282. my $name = "";
  283. my $link;
  284. my $URI;
  285. my $result = "Initialized";
  286. my $webArgs;
  287. my $servertype;
  288. # incorrect FHEMWEB instance used
  289. if ( AttrVal( $FW_wname, "webname", "fhem" ) ne "weatherstation" ) {
  290. return ( "text/plain; charset=utf-8",
  291. "incorrect FHEMWEB instance to receive data" );
  292. }
  293. # data received
  294. elsif ( $request =~ /^\/updateweatherstation\.(\w{3})\?(.+=.+)/ ) {
  295. $servertype = lc($1);
  296. $URI = $2;
  297. # get device name
  298. $name = $data{FWEXT}{"/updateweatherstation"}{deviceName}
  299. if ( defined( $data{FWEXT}{"/updateweatherstation"} ) );
  300. # return error if no such device
  301. return ( "text/plain; charset=utf-8",
  302. "No HP1000 device for webhook /updateweatherstation" )
  303. unless ($name);
  304. # extract values from URI
  305. foreach my $pv ( split( "&", $URI ) ) {
  306. next if ( $pv eq "" );
  307. $pv =~ s/\+/ /g;
  308. $pv =~ s/%([\dA-F][\dA-F])/chr(hex($1))/ige;
  309. my ( $p, $v ) = split( "=", $pv, 2 );
  310. $webArgs->{$p} = $v ne "" ? Encode::encode_utf8($v) : $v;
  311. }
  312. if ( !defined( $webArgs->{softwaretype} )
  313. || !defined( $webArgs->{dateutc} )
  314. || !defined( $webArgs->{ID} )
  315. || !defined( $webArgs->{PASSWORD} )
  316. || !defined( $webArgs->{action} ) )
  317. {
  318. Log3 $name, 5,
  319. "HP1000: received insufficient data:\n" . Dumper($webArgs);
  320. return ( "text/plain; charset=utf-8", "Insufficient data" );
  321. }
  322. }
  323. # no data received
  324. else {
  325. return ( "text/plain; charset=utf-8", "Missing data" );
  326. }
  327. $hash = $defs{$name};
  328. delete $hash->{FORECASTDEV} if ( $hash->{FORECASTDEV} );
  329. my @wudev = devspec2array(
  330. "TYPE=Wunderground:FILTER=PWS_ID=" . AttrVal( $name, "wu_id", "-" ) );
  331. $hash->{FORECASTDEV} = $wudev[0]
  332. if ( defined( $wudev[0] ) );
  333. HP1000_SetAliveState( $hash, 1 );
  334. $hash->{IP} = $defs{$FW_cname}{PEER};
  335. $hash->{SERVER_TYPE} = $servertype;
  336. $hash->{SWVERSION} = $webArgs->{softwaretype};
  337. $hash->{INTERVAL} = (
  338. $hash->{SYSTEMTIME_UTC}
  339. ? time_str2num( $webArgs->{dateutc} ) -
  340. time_str2num( $hash->{SYSTEMTIME_UTC} )
  341. : 0
  342. );
  343. $hash->{SYSTEMTIME_UTC} = $webArgs->{dateutc};
  344. $hash->{FW} = "";
  345. $hash->{FW_PORT} = "";
  346. foreach ( devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') ) {
  347. if ( AttrVal( $_, "webname", "fhem" ) eq "weatherstation" ) {
  348. $hash->{FW} = $_;
  349. $hash->{FW_PORT} = $defs{$_}{PORT};
  350. last;
  351. }
  352. }
  353. if (
  354. defined( $hash->{ID} )
  355. && defined( $hash->{PASSWORD} )
  356. && ( $hash->{ID} ne $webArgs->{ID}
  357. || $hash->{PASSWORD} ne $webArgs->{PASSWORD} )
  358. )
  359. {
  360. Log3 $name, 4, "HP1000: received data containing wrong credentials:\n"
  361. . Dumper($webArgs);
  362. return ( "text/plain; charset=utf-8", "Wrong credentials" );
  363. }
  364. Log3 $name, 5, "HP1000: received data:\n" . Dumper($webArgs);
  365. # rename wind speed values as those are in m/sec and
  366. # we want km/h to be our metric default
  367. if ( defined( $webArgs->{windspeed} ) ) {
  368. $webArgs->{windspeedmps} = $webArgs->{windspeed};
  369. delete $webArgs->{windspeed};
  370. }
  371. if ( defined( $webArgs->{windgust} ) ) {
  372. $webArgs->{windgustmps} = $webArgs->{windgust};
  373. delete $webArgs->{windgust};
  374. }
  375. # calculate readings for Metric standard from Angloamerican standard
  376. #
  377. # humidity (special case here!)
  378. $webArgs->{inhumi} = $webArgs->{indoorhumidity}
  379. if ( defined( $webArgs->{indoorhumidity} )
  380. && !defined( $webArgs->{inhumi} ) );
  381. $webArgs->{indoorhumidity} = $webArgs->{inhumi}
  382. if ( defined( $webArgs->{inhumi} )
  383. && !defined( $webArgs->{indoorhumidity} ) );
  384. $webArgs->{outhumi} = $webArgs->{humidity}
  385. if ( defined( $webArgs->{humidity} )
  386. && !defined( $webArgs->{outhumi} ) );
  387. $webArgs->{humidity} = $webArgs->{outhumi}
  388. if ( defined( $webArgs->{outhumi} )
  389. && !defined( $webArgs->{humidity} ) );
  390. # dewpoint in Celsius (convert from dewptf)
  391. if ( defined( $webArgs->{dewptf} )
  392. && $webArgs->{dewptf} ne ""
  393. && !defined( $webArgs->{dewpoint} ) )
  394. {
  395. $webArgs->{dewpoint} =
  396. UConv::f2c( $webArgs->{dewptf} );
  397. }
  398. # relbaro in hPa (convert from baromin)
  399. if ( defined( $webArgs->{baromin} )
  400. && $webArgs->{baromin} ne ""
  401. && !defined( $webArgs->{relbaro} ) )
  402. {
  403. $webArgs->{relbaro} = UConv::inhg2hpa( $webArgs->{baromin} );
  404. }
  405. # absbaro in hPa (convert from absbaromin)
  406. if ( defined( $webArgs->{absbaromin} )
  407. && $webArgs->{absbaromin} ne ""
  408. && !defined( $webArgs->{absbaro} ) )
  409. {
  410. $webArgs->{absbaro} =
  411. UConv::inhg2hpa( $webArgs->{absbaromin} );
  412. }
  413. # rainrate in mm/h (convert from rainin)
  414. if ( defined( $webArgs->{rainin} )
  415. && $webArgs->{rainin} ne ""
  416. && !defined( $webArgs->{rainrate} ) )
  417. {
  418. $webArgs->{rainrate} = UConv::in2mm( $webArgs->{rainin} );
  419. }
  420. # dailyrain in mm (convert from dailyrainin)
  421. if ( defined( $webArgs->{dailyrainin} )
  422. && $webArgs->{dailyrainin} ne ""
  423. && !defined( $webArgs->{dailyrain} ) )
  424. {
  425. $webArgs->{dailyrain} =
  426. UConv::in2mm( $webArgs->{dailyrainin} );
  427. }
  428. # weeklyrain in mm (convert from weeklyrainin)
  429. if ( defined( $webArgs->{weeklyrainin} )
  430. && $webArgs->{weeklyrainin} ne ""
  431. && !defined( $webArgs->{weeklyrain} ) )
  432. {
  433. $webArgs->{weeklyrain} =
  434. UConv::in2mm( $webArgs->{weeklyrainin} );
  435. }
  436. # monthlyrain in mm (convert from monthlyrainin)
  437. if ( defined( $webArgs->{monthlyrainin} )
  438. && $webArgs->{monthlyrainin} ne ""
  439. && !defined( $webArgs->{monthlyrain} ) )
  440. {
  441. $webArgs->{monthlyrain} =
  442. UConv::in2mm( $webArgs->{monthlyrainin} );
  443. }
  444. # yearlyrain in mm (convert from yearlyrainin)
  445. if ( defined( $webArgs->{yearlyrainin} )
  446. && $webArgs->{yearlyrainin} ne ""
  447. && !defined( $webArgs->{yearlyrain} ) )
  448. {
  449. $webArgs->{yearlyrain} =
  450. UConv::in2mm( $webArgs->{yearlyrainin} );
  451. }
  452. # outtemp in Celsius (convert from tempf)
  453. if ( defined( $webArgs->{tempf} )
  454. && $webArgs->{tempf} ne ""
  455. && !defined( $webArgs->{outtemp} ) )
  456. {
  457. $webArgs->{outtemp} =
  458. UConv::f2c( $webArgs->{tempf} );
  459. }
  460. # intemp in Celsius (convert from indoortempf)
  461. if ( defined( $webArgs->{indoortempf} )
  462. && $webArgs->{indoortempf} ne ""
  463. && !defined( $webArgs->{intemp} ) )
  464. {
  465. $webArgs->{intemp} =
  466. UConv::f2c( $webArgs->{indoortempf} );
  467. }
  468. # windchill in Celsius (convert from windchillf)
  469. if ( defined( $webArgs->{windchillf} )
  470. && $webArgs->{windchillf} ne ""
  471. && !defined( $webArgs->{windchill} ) )
  472. {
  473. $webArgs->{windchill} =
  474. UConv::f2c( $webArgs->{windchillf} );
  475. }
  476. # windgust in km/h (convert from windgustmph)
  477. if ( defined( $webArgs->{windgustmph} )
  478. && $webArgs->{windgustmph} ne ""
  479. && !defined( $webArgs->{windgust} ) )
  480. {
  481. $webArgs->{windgust} =
  482. UConv::mph2kph( $webArgs->{windgustmph} );
  483. }
  484. # windspeed in km/h (convert from windspdmph)
  485. if ( defined( $webArgs->{windspdmph} )
  486. && $webArgs->{windspdmph} ne ""
  487. && !defined( $webArgs->{windspeed} ) )
  488. {
  489. $webArgs->{windspeed} =
  490. UConv::mph2kph( $webArgs->{windspdmph} );
  491. }
  492. # calculate readings for Angloamerican standard from Metric standard
  493. #
  494. # humidity (special case here!)
  495. $webArgs->{indoorhumidity} = $webArgs->{inhumi}
  496. if ( defined( $webArgs->{inhumi} )
  497. && !defined( $webArgs->{indoorhumidity} ) );
  498. # dewptf in Fahrenheit (convert from dewpoint)
  499. if ( defined( $webArgs->{dewpoint} )
  500. && $webArgs->{dewpoint} ne ""
  501. && !defined( $webArgs->{dewptf} ) )
  502. {
  503. $webArgs->{dewptf} =
  504. UConv::c2f( $webArgs->{dewpoint} );
  505. }
  506. # baromin in inch (convert from relbaro)
  507. if ( defined( $webArgs->{relbaro} )
  508. && $webArgs->{relbaro} ne ""
  509. && !defined( $webArgs->{baromin} ) )
  510. {
  511. $webArgs->{baromin} = UConv::hpa2inhg( $webArgs->{relbaro} );
  512. }
  513. # absbaromin in inch (convert from absbaro)
  514. if ( defined( $webArgs->{absbaro} )
  515. && $webArgs->{absbaro} ne ""
  516. && !defined( $webArgs->{absbaromin} ) )
  517. {
  518. $webArgs->{absbaromin} =
  519. UConv::hpa2inhg( $webArgs->{absbaro} );
  520. }
  521. # rainin in in/h (convert from rainrate)
  522. if ( defined( $webArgs->{rainrate} )
  523. && $webArgs->{rainrate} ne ""
  524. && !defined( $webArgs->{rainin} ) )
  525. {
  526. $webArgs->{rainin} = UConv::mm2in( $webArgs->{rainrate} );
  527. }
  528. # dailyrainin in inch (convert from dailyrain)
  529. if ( defined( $webArgs->{dailyrain} )
  530. && $webArgs->{dailyrain} ne ""
  531. && !defined( $webArgs->{dailyrainin} ) )
  532. {
  533. $webArgs->{dailyrainin} =
  534. UConv::mm2in( $webArgs->{dailyrain} );
  535. }
  536. # weeklyrainin in inch (convert from weeklyrain)
  537. if ( defined( $webArgs->{weeklyrain} )
  538. && $webArgs->{weeklyrain} ne ""
  539. && !defined( $webArgs->{weeklyrainin} ) )
  540. {
  541. $webArgs->{weeklyrainin} =
  542. UConv::mm2in( $webArgs->{weeklyrain} );
  543. }
  544. # monthlyrainin in inch (convert from monthlyrain)
  545. if ( defined( $webArgs->{monthlyrain} )
  546. && $webArgs->{monthlyrain} ne ""
  547. && !defined( $webArgs->{monthlyrainin} ) )
  548. {
  549. $webArgs->{monthlyrainin} =
  550. UConv::mm2in( $webArgs->{monthlyrain} );
  551. }
  552. # yearlyrainin in inch (convert from yearlyrain)
  553. if ( defined( $webArgs->{yearlyrain} )
  554. && $webArgs->{yearlyrain} ne ""
  555. && !defined( $webArgs->{yearlyrainin} ) )
  556. {
  557. $webArgs->{yearlyrainin} =
  558. UConv::mm2in( $webArgs->{yearlyrain} );
  559. }
  560. # tempf in Fahrenheit (convert from outtemp)
  561. if ( defined( $webArgs->{outtemp} )
  562. && $webArgs->{outtemp} ne ""
  563. && !defined( $webArgs->{tempf} ) )
  564. {
  565. $webArgs->{tempf} =
  566. UConv::c2f( $webArgs->{outtemp} );
  567. }
  568. # indoortempf in Fahrenheit (convert from intemp)
  569. if ( defined( $webArgs->{intemp} )
  570. && $webArgs->{intemp} ne ""
  571. && !defined( $webArgs->{indoortempf} ) )
  572. {
  573. $webArgs->{indoortempf} =
  574. UConv::c2f( $webArgs->{intemp} );
  575. }
  576. # windchillf in Fahrenheit (convert from windchill)
  577. if ( defined( $webArgs->{windchill} )
  578. && $webArgs->{windchill} ne ""
  579. && !defined( $webArgs->{windchillf} ) )
  580. {
  581. $webArgs->{windchillf} =
  582. UConv::c2f( $webArgs->{windchill} );
  583. }
  584. # windgustmps in m/s (convert from windgust)
  585. if ( defined( $webArgs->{windgust} )
  586. && $webArgs->{windgust} ne ""
  587. && !defined( $webArgs->{windgustmps} ) )
  588. {
  589. $webArgs->{windgustmps} =
  590. UConv::kph2mps( $webArgs->{windgust} );
  591. }
  592. # windgust in km/h (convert from windgustmps,
  593. # not exactly from angloamerican...)
  594. if ( defined( $webArgs->{windgustmps} )
  595. && $webArgs->{windgustmps} ne ""
  596. && !defined( $webArgs->{windgust} ) )
  597. {
  598. $webArgs->{windgust} =
  599. UConv::mps2kph( $webArgs->{windgustmps} );
  600. }
  601. # windgustmph in mph (convert from windgust)
  602. if ( defined( $webArgs->{windgust} )
  603. && $webArgs->{windgust} ne ""
  604. && !defined( $webArgs->{windgustmph} ) )
  605. {
  606. $webArgs->{windgustmph} =
  607. UConv::kph2mph( $webArgs->{windgust} );
  608. }
  609. # windspeedmps in m/s (convert from windspeed,
  610. # not exactly from angloamerican...)
  611. if ( defined( $webArgs->{windspeed} )
  612. && $webArgs->{windspeed} ne ""
  613. && !defined( $webArgs->{windspeedmps} ) )
  614. {
  615. $webArgs->{windspeedmps} =
  616. UConv::kph2mps( $webArgs->{windspeed} );
  617. }
  618. # windspeed in km/h (convert from windspeedmps)
  619. if ( defined( $webArgs->{windspeedmps} )
  620. && $webArgs->{windspeedmps} ne ""
  621. && !defined( $webArgs->{windspeed} ) )
  622. {
  623. $webArgs->{windspeed} =
  624. UConv::mps2kph( $webArgs->{windspeedmps} );
  625. }
  626. # windspdmph in mph (convert from windspeed)
  627. if ( defined( $webArgs->{windspeed} )
  628. && $webArgs->{windspeed} ne ""
  629. && !defined( $webArgs->{windspdmph} ) )
  630. {
  631. $webArgs->{windspdmph} =
  632. UConv::kph2mph( $webArgs->{windspeed} );
  633. }
  634. # write general readings
  635. #
  636. readingsBeginUpdate($hash);
  637. while ( ( my $p, my $v ) = each %$webArgs ) {
  638. # delete empty values
  639. if ( $v eq "" ) {
  640. delete $webArgs->{$p};
  641. next;
  642. }
  643. # ignore those values
  644. next
  645. if ( $p eq "dateutc"
  646. || $p eq "action"
  647. || $p eq "softwaretype"
  648. || $p eq "realtime"
  649. || $p eq "rtfreq"
  650. || $p eq "humidity"
  651. || $p eq "indoorhumidity"
  652. || $p eq "ID"
  653. || $p eq "PASSWORD" );
  654. $p = "_" . $p;
  655. # name translation for general readings
  656. $p = "humidity" if ( $p eq "_outhumi" );
  657. $p = "indoorHumidity" if ( $p eq "_inhumi" );
  658. $p = "luminosity" if ( $p eq "_light" );
  659. $p = "UVR" if ( $p eq "_UV" );
  660. $p = "wind_direction" if ( $p eq "_winddir" );
  661. # name translation for Metric standard
  662. $p = "dewpoint" if ( $p eq "_dewpoint" );
  663. $p = "pressure" if ( $p eq "_relbaro" );
  664. $p = "pressureAbs" if ( $p eq "_absbaro" );
  665. $p = "rain" if ( $p eq "_rainrate" );
  666. $p = "rain_day" if ( $p eq "_dailyrain" );
  667. $p = "rain_week" if ( $p eq "_weeklyrain" );
  668. $p = "rain_month" if ( $p eq "_monthlyrain" );
  669. $p = "rain_year" if ( $p eq "_yearlyrain" );
  670. $p = "temperature" if ( $p eq "_outtemp" );
  671. $p = "indoorTemperature" if ( $p eq "_intemp" );
  672. $p = "wind_chill" if ( $p eq "_windchill" );
  673. $p = "wind_gust" if ( $p eq "_windgust" );
  674. $p = "wind_gust_mps" if ( $p eq "_windgustmps" );
  675. $p = "wind_speed" if ( $p eq "_windspeed" );
  676. $p = "wind_speed_mps" if ( $p eq "_windspeedmps" );
  677. # name translation for Angloamerican standard
  678. $p = "dewpoint_f" if ( $p eq "_dewptf" );
  679. $p = "pressure_in" if ( $p eq "_baromin" );
  680. $p = "pressureAbs_in" if ( $p eq "_absbaromin" );
  681. $p = "rain_in" if ( $p eq "_rainin" );
  682. $p = "rain_day_in" if ( $p eq "_dailyrainin" );
  683. $p = "rain_week_in" if ( $p eq "_weeklyrainin" );
  684. $p = "rain_month_in" if ( $p eq "_monthlyrainin" );
  685. $p = "rain_year_in" if ( $p eq "_yearlyrainin" );
  686. $p = "temperature_f" if ( $p eq "_tempf" );
  687. $p = "indoorTemperature_f" if ( $p eq "_indoortempf" );
  688. $p = "wind_chill_f" if ( $p eq "_windchillf" );
  689. $p = "wind_gust_mph" if ( $p eq "_windgustmph" );
  690. $p = "wind_speed_mph" if ( $p eq "_windspdmph" );
  691. readingsBulkUpdate( $hash, $p, $v );
  692. }
  693. # calculate additional readings
  694. #
  695. # israining
  696. my $israining = 0;
  697. $israining = 1
  698. if ( defined( $webArgs->{rainrate} ) && $webArgs->{rainrate} > 0 );
  699. readingsBulkUpdateIfChanged( $hash, "israining", $israining );
  700. # daylight
  701. my $daylight = 0;
  702. $daylight = 1
  703. if ( defined( $webArgs->{light} ) && $webArgs->{light} > 50 );
  704. readingsBulkUpdateIfChanged( $hash, "daylight", $daylight );
  705. # condition
  706. if ( defined( $webArgs->{light} ) ) {
  707. my $temp = ( $webArgs->{outtemp} ? $webArgs->{outtemp} : "10" );
  708. my $hum = ( $webArgs->{outhumi} ? $webArgs->{outhumi} : "50" );
  709. readingsBulkUpdateIfChanged(
  710. $hash,
  711. "condition",
  712. UConv::values2weathercondition(
  713. $temp, $hum, $webArgs->{light}, $daylight, $israining
  714. )
  715. );
  716. }
  717. # humidityCondition
  718. if ( defined( $webArgs->{outhumi} ) ) {
  719. readingsBulkUpdateIfChanged( $hash, "humidityCondition",
  720. UConv::humidity2condition( $webArgs->{outhumi} ) );
  721. }
  722. # indoorHumidityCondition
  723. if ( defined( $webArgs->{inhumi} ) ) {
  724. readingsBulkUpdateIfChanged( $hash, "indoorHumidityCondition",
  725. UConv::humidity2condition( $webArgs->{inhumi} ) );
  726. }
  727. # UV (convert from uW/cm2)
  728. if ( defined( $webArgs->{UV} ) ) {
  729. $webArgs->{UVI} = UConv::uwpscm2uvi( $webArgs->{UV} );
  730. readingsBulkUpdate( $hash, "UV", $webArgs->{UVI} );
  731. }
  732. # UVcondition
  733. if ( defined( $webArgs->{UVI} ) ) {
  734. readingsBulkUpdateIfChanged( $hash, "UVcondition",
  735. UConv::uvi2condition( $webArgs->{UVI} ) );
  736. }
  737. # solarradiation in W/m2 (convert from lux)
  738. if ( defined( $webArgs->{light} ) ) {
  739. $webArgs->{solarradiation} =
  740. UConv::lux2wpsm( $webArgs->{light} );
  741. readingsBulkUpdate( $hash, "solarradiation",
  742. $webArgs->{solarradiation} );
  743. }
  744. # pressure_mm in mmHg (convert from hpa)
  745. if ( defined( $webArgs->{relbaro} ) ) {
  746. $webArgs->{barommm} = UConv::hpa2mmhg( $webArgs->{relbaro} );
  747. readingsBulkUpdate( $hash, "pressure_mm", $webArgs->{barommm} );
  748. }
  749. # pressureAbs_mm in mmHg (convert from hpa)
  750. if ( defined( $webArgs->{absbaro} ) ) {
  751. $webArgs->{absbarommm} =
  752. UConv::hpa2mmhg( $webArgs->{absbaro} );
  753. readingsBulkUpdate( $hash, "pressureAbs_mm", $webArgs->{absbarommm} );
  754. }
  755. # indoorDewpoint in Celsius
  756. if ( defined( $webArgs->{intemp} )
  757. && defined( $webArgs->{inhumi} )
  758. && exists &dewpoint_dewpoint )
  759. {
  760. my $h = (
  761. $webArgs->{inhumi} > 110
  762. ? 110
  763. : ( $webArgs->{inhumi} <= 0 ? 0.01 : $webArgs->{inhumi} )
  764. );
  765. $webArgs->{indewpoint} =
  766. round( dewpoint_dewpoint( $webArgs->{intemp}, $h ), 1 );
  767. readingsBulkUpdate( $hash, "indoorDewpoint", $webArgs->{indewpoint} );
  768. }
  769. # indoorDewpoint in Fahrenheit
  770. if ( defined( $webArgs->{indoortempf} )
  771. && defined( $webArgs->{indoorhumidity} )
  772. && exists &dewpoint_dewpoint )
  773. {
  774. my $h = (
  775. $webArgs->{indoorhumidity} > 110 ? 110
  776. : (
  777. $webArgs->{indoorhumidity} <= 0 ? 0.01
  778. : $webArgs->{indoorhumidity}
  779. )
  780. );
  781. $webArgs->{indoordewpointf} =
  782. round( dewpoint_dewpoint( $webArgs->{indoortempf}, $h ), 1 );
  783. readingsBulkUpdate( $hash, "indoorDewpoint_f",
  784. $webArgs->{indoordewpointf} );
  785. }
  786. # humidityAbs
  787. if ( defined( $webArgs->{outtemp} )
  788. && defined( $webArgs->{outhumi} )
  789. && looks_like_number( $webArgs->{outtemp} )
  790. && looks_like_number( $webArgs->{outhumi} )
  791. && exists &dewpoint_absFeuchte )
  792. {
  793. my $h = (
  794. $webArgs->{outhumi} > 110
  795. ? 110
  796. : ( $webArgs->{outhumi} <= 0 ? 0.01 : $webArgs->{outhumi} )
  797. );
  798. $webArgs->{outhumiabs} =
  799. round( dewpoint_absFeuchte( $webArgs->{outtemp}, $h ), 1 );
  800. readingsBulkUpdate( $hash, "humidityAbs", $webArgs->{outhumiabs} );
  801. }
  802. # humidityAbs_f
  803. if ( defined( $webArgs->{tempf} )
  804. && defined( $webArgs->{outhumi} )
  805. && looks_like_number( $webArgs->{tempf} )
  806. && looks_like_number( $webArgs->{outhumi} )
  807. && exists &dewpoint_absFeuchte )
  808. {
  809. my $h = (
  810. $webArgs->{outhumi} > 110
  811. ? 110
  812. : ( $webArgs->{outhumi} <= 0 ? 0.01 : $webArgs->{outhumi} )
  813. );
  814. $webArgs->{outhumiabsf} =
  815. round( dewpoint_absFeuchte( $webArgs->{tempf}, $h ), 1 );
  816. readingsBulkUpdate( $hash, "humidityAbs_f", $webArgs->{outhumiabsf} );
  817. }
  818. # indoorHumidityAbs
  819. if ( defined( $webArgs->{intemp} )
  820. && defined( $webArgs->{inhumi} )
  821. && looks_like_number( $webArgs->{intemp} )
  822. && looks_like_number( $webArgs->{inhumi} )
  823. && exists &dewpoint_absFeuchte )
  824. {
  825. my $h = (
  826. $webArgs->{inhumi} > 110
  827. ? 110
  828. : ( $webArgs->{inhumi} <= 0 ? 0.01 : $webArgs->{inhumi} )
  829. );
  830. $webArgs->{inhumiabs} =
  831. round( dewpoint_absFeuchte( $webArgs->{intemp}, $h ), 1 );
  832. readingsBulkUpdate( $hash, "indoorHumidityAbs", $webArgs->{inhumiabs} );
  833. }
  834. # indoorHumidityAbs_f
  835. if ( defined( $webArgs->{indoortempf} )
  836. && defined( $webArgs->{indoorhumidity} )
  837. && looks_like_number( $webArgs->{indoortempf} )
  838. && looks_like_number( $webArgs->{indoorhumidity} )
  839. && exists &dewpoint_absFeuchte )
  840. {
  841. my $h = (
  842. $webArgs->{indoorhumidity} > 110 ? 110
  843. : (
  844. $webArgs->{indoorhumidity} <= 0 ? 0.01
  845. : $webArgs->{indoorhumidity}
  846. )
  847. );
  848. $webArgs->{indoorhumidityabsf} =
  849. round( dewpoint_absFeuchte( $webArgs->{indoortempf}, $h ), 1 );
  850. readingsBulkUpdate( $hash, "indoorHumidityAbs_f",
  851. $webArgs->{indoorhumidityabsf} );
  852. }
  853. # wind_compasspoint
  854. if ( defined( $webArgs->{winddir} ) ) {
  855. $webArgs->{windcompasspoint} =
  856. UConv::direction2compasspoint( $webArgs->{winddir} );
  857. readingsBulkUpdate( $hash, "wind_compasspoint",
  858. $webArgs->{windcompasspoint} );
  859. }
  860. # wind_speed_bft in Beaufort (convert from km/h)
  861. if ( defined( $webArgs->{windspeed} ) ) {
  862. $webArgs->{windspeedbft} =
  863. UConv::kph2bft( $webArgs->{windspeed} );
  864. readingsBulkUpdate( $hash, "wind_speed_bft", $webArgs->{windspeedbft} );
  865. }
  866. # wind_speed_kn in kn (convert from km/h)
  867. if ( defined( $webArgs->{windspeed} ) ) {
  868. my $v = UConv::kph2kn( $webArgs->{windspeed} );
  869. $webArgs->{windspeedkn} = ( $v > 0.5 ? round( $v, 1 ) : "0.0" );
  870. readingsBulkUpdate( $hash, "wind_speed_kn", $webArgs->{windspeedkn} );
  871. }
  872. # wind_speed_fts in ft/s (convert from mph)
  873. if ( defined( $webArgs->{windspeedmph} ) ) {
  874. my $v = UConv::mph2fts( $webArgs->{windspeedmph} );
  875. $webArgs->{windspeedfts} = ( $v > 0.5 ? round( $v, 1 ) : "0.0" );
  876. readingsBulkUpdate( $hash, "wind_speed_fts", $webArgs->{windspeedfts} );
  877. }
  878. # wind_gust_bft in Beaufort (convert from km/h)
  879. if ( defined( $webArgs->{windgust} ) ) {
  880. $webArgs->{windgustbft} =
  881. UConv::kph2bft( $webArgs->{windgust} );
  882. readingsBulkUpdate( $hash, "wind_gust_bft", $webArgs->{windgustbft} );
  883. }
  884. # wind_gust_kn in m/s (convert from km/h)
  885. if ( defined( $webArgs->{windgust} ) ) {
  886. my $v = UConv::kph2kn( $webArgs->{windgust} );
  887. $webArgs->{windgustkn} = ( $v > 0.5 ? round( $v, 1 ) : "0.0" );
  888. readingsBulkUpdate( $hash, "wind_gust_kn", $webArgs->{windgustkn} );
  889. }
  890. # wind_gust_fts ft/s (convert from mph)
  891. if ( defined( $webArgs->{windgustmph} ) ) {
  892. my $v = UConv::mph2fts( $webArgs->{windgustmph} );
  893. $webArgs->{windgustfts} = ( $v > 0.5 ? round( $v, 1 ) : "0.0" );
  894. readingsBulkUpdate( $hash, "wind_gust_fts", $webArgs->{windgustfts} );
  895. }
  896. # averages/wind_direction_avg2m
  897. if ( defined( $webArgs->{winddir} ) ) {
  898. my $v = sprintf( '%0.0f',
  899. HP1000_GetAvg( $hash, "winddir", 2 * 60, $webArgs->{winddir} ) );
  900. if ( $hash->{INTERVAL} > 0 ) {
  901. readingsBulkUpdate( $hash, "wind_direction_avg2m", $v );
  902. $webArgs->{winddir_avg2m} = $v;
  903. }
  904. }
  905. # averages/wind_compasspoint_avg2m
  906. if ( defined( $webArgs->{winddir_avg2m} ) ) {
  907. $webArgs->{windcompasspoint_avg2m} =
  908. UConv::direction2compasspoint( $webArgs->{winddir_avg2m} );
  909. readingsBulkUpdate( $hash, "wind_compasspoint_avg2m",
  910. $webArgs->{windcompasspoint_avg2m} );
  911. }
  912. # averages/wind_speed_avg2m in km/h
  913. if ( defined( $webArgs->{windspeed} ) ) {
  914. my $v =
  915. HP1000_GetAvg( $hash, "windspeed", 2 * 60, $webArgs->{windspeed} );
  916. if ( $hash->{INTERVAL} > 0 ) {
  917. readingsBulkUpdate( $hash, "wind_speed_avg2m", $v );
  918. $webArgs->{windspeed_avg2m} = $v;
  919. }
  920. }
  921. # averages/wind_speed_mph_avg2m in mph
  922. if ( defined( $webArgs->{windspdmph} ) ) {
  923. my $v =
  924. HP1000_GetAvg( $hash, "windspdmph", 2 * 60, $webArgs->{windspdmph} );
  925. if ( $hash->{INTERVAL} > 0 ) {
  926. readingsBulkUpdate( $hash, "wind_speed_mph_avg2m", $v );
  927. $webArgs->{windspdmph_avg2m} = $v;
  928. }
  929. }
  930. # averages/wind_speed_bft_avg2m in Beaufort (convert from km/h)
  931. if ( defined( $webArgs->{windspeed_avg2m} ) ) {
  932. $webArgs->{windspeedbft_avg2m} =
  933. UConv::kph2bft( $webArgs->{windspeed_avg2m} );
  934. readingsBulkUpdate( $hash, "wind_speed_bft_avg2m",
  935. $webArgs->{windspeedbft_avg2m} );
  936. }
  937. # averages/wind_speed_kn_avg2m in Kn (convert from km/h)
  938. if ( defined( $webArgs->{windspeed_avg2m} ) ) {
  939. $webArgs->{windspeedkn_avg2m} =
  940. UConv::kph2kn( $webArgs->{windspeed_avg2m} );
  941. readingsBulkUpdate( $hash, "wind_speed_kn_avg2m",
  942. $webArgs->{windspeedkn_avg2m} );
  943. }
  944. # averages/wind_speed_mps_avg2m in m/s
  945. if ( defined( $webArgs->{windspeed_avg2m} ) ) {
  946. my $v = UConv::kph2mps( $webArgs->{windspeed_avg2m} );
  947. $webArgs->{windspeedmps_avg2m} =
  948. ( $v > 0.5 ? round( $v, 1 ) : "0.0" );
  949. readingsBulkUpdate( $hash, "wind_speed_mps_avg2m",
  950. $webArgs->{windspeedmps_avg2m} );
  951. }
  952. # averages/wind_gust_sum10m
  953. if ( defined( $webArgs->{windgust} ) ) {
  954. my $v =
  955. HP1000_GetSum( $hash, "windgust", 10 * 60, $webArgs->{windgust} );
  956. if ( $hash->{INTERVAL} > 0 ) {
  957. readingsBulkUpdate( $hash, "wind_gust_sum10m", $v );
  958. $webArgs->{windgust_10m} = $v;
  959. }
  960. }
  961. # averages/wind_gust_mph_sum10m
  962. if ( defined( $webArgs->{windgustmph} ) ) {
  963. my $v =
  964. HP1000_GetSum( $hash, "windgustmph", 10 * 60,
  965. $webArgs->{windgustmph} );
  966. if ( $hash->{INTERVAL} > 0 ) {
  967. readingsBulkUpdate( $hash, "wind_gust_mph_sum10m", $v );
  968. $webArgs->{windgustmph_10m} = $v;
  969. }
  970. }
  971. # from WU API - can we somehow calculate these as well?
  972. # weather - [text] -- metar style (+RA)
  973. # clouds - [text] -- SKC, FEW, SCT, BKN, OVC
  974. # soiltempf - [F soil temperature]
  975. # soilmoisture - [%]
  976. # leafwetness - [%]
  977. # visibility - [nm visibility]
  978. # condition_forecast (based on pressure trend)
  979. # dayNight
  980. # soilTemperature
  981. # brightness in % ??
  982. # state
  983. my $stateReadings = AttrVal( $name, "stateReadings", "" );
  984. my $stateReadingsLang = AttrVal( $name, "stateReadingsLang", "en" );
  985. my $stateReadingsFormat = AttrVal( $name, "stateReadingsFormat", "0" );
  986. # $result =
  987. # makeSTATE( $name, $stateReadings,
  988. # $stateReadingsLang, $stateReadingsFormat );
  989. $result = makeSTATE( $name, $stateReadings, $stateReadingsFormat );
  990. readingsBulkUpdate( $hash, "state", $result );
  991. readingsEndUpdate( $hash, 1 );
  992. HP1000_PushWU( $hash, $webArgs )
  993. if AttrVal( $name, "wu_push", 0 ) eq "1";
  994. HP1000_PushSrv( $hash, $webArgs )
  995. if AttrVal( $name, "extSrvPush_Url", undef );
  996. return ( "text/plain; charset=utf-8", "success" );
  997. }
  998. ###################################
  999. sub HP1000_GetAvg($$$$) {
  1000. my ( $hash, $t, $s, $v, $avg ) = @_;
  1001. return HP1000_GetSum( $hash, $t, $s, $v, 1 );
  1002. }
  1003. sub HP1000_GetSum($$$$;$) {
  1004. my ( $hash, $t, $s, $v, $avg ) = @_;
  1005. my $name = $hash->{NAME};
  1006. return $v if ( $avg && $hash->{INTERVAL} < 1 );
  1007. return "0" if ( $hash->{INTERVAL} < 1 );
  1008. my $max = sprintf( "%.0f", $s / $hash->{INTERVAL} );
  1009. $max = "1" if ( $max < 1 );
  1010. my $return;
  1011. my $v2 = unshift @{ $hash->{helper}{history}{$t} }, $v;
  1012. my $v3 = splice @{ $hash->{helper}{history}{$t} }, $max;
  1013. Log3 $name, 5, "HP1000 $name: Updated history for $t:"
  1014. . Dumper( $hash->{helper}{history}{$t} );
  1015. if ($avg) {
  1016. $return = sprintf( "%.1f",
  1017. sum( @{ $hash->{helper}{history}{$t} } ) /
  1018. @{ $hash->{helper}{history}{$t} } );
  1019. Log3 $name, 5, "HP1000 $name: Average for $t: $return";
  1020. }
  1021. else {
  1022. $return = sprintf( "%.1f", sum( @{ $hash->{helper}{history}{$t} } ) );
  1023. Log3 $name, 5, "HP1000 $name: Sum for $t: $return";
  1024. }
  1025. return $return;
  1026. }
  1027. ###################################
  1028. sub HP1000_PushSrv($$) {
  1029. my ( $hash, $webArgs ) = @_;
  1030. my $name = $hash->{NAME};
  1031. my $timeout = AttrVal( $name, "timeout", 7 );
  1032. my $http_noshutdown = AttrVal( $name, "http-noshutdown", "1" );
  1033. my $srv_url = AttrVal( $name, "extSrvPush_Url", "" );
  1034. my $cmd = "";
  1035. Log3 $name, 5, "HP1000 $name: called function HP1000_PushSrv()";
  1036. if ( $srv_url !~
  1037. /(https?):\/\/([\w\.]+):?(\d+)?([a-zA-Z0-9\~\!\@\#\$\%\^\&\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?/
  1038. )
  1039. {
  1040. return;
  1041. }
  1042. elsif ( $4 !~ /\?/ ) {
  1043. $cmd = "?";
  1044. }
  1045. else {
  1046. $cmd = "&";
  1047. }
  1048. $webArgs->{PASSWORD} = "";
  1049. while ( my ( $key, $value ) = each %{$webArgs} ) {
  1050. if ( $key eq "softwaretype" || $key eq "dateutc" ) {
  1051. $value = urlEncode($value);
  1052. }
  1053. $cmd .= "$key=" . $value . "&";
  1054. }
  1055. Log3 $name, 4,
  1056. "HP1000 $name: pushing data to external Server: $srv_url$cmd";
  1057. HttpUtils_NonblockingGet(
  1058. {
  1059. url => $srv_url . $cmd,
  1060. timeout => $timeout,
  1061. noshutdown => $http_noshutdown,
  1062. data => undef,
  1063. hash => $hash,
  1064. httpversion => "1.1",
  1065. callback => \&HP1000_ReturnSrv,
  1066. }
  1067. );
  1068. return;
  1069. }
  1070. ###################################
  1071. sub HP1000_PushWU($$) {
  1072. #
  1073. # See: http://wiki.wunderground.com/index.php/PWS_-_Upload_Protocol
  1074. #
  1075. my ( $hash, $webArgs ) = @_;
  1076. my $name = $hash->{NAME};
  1077. my $timeout = AttrVal( $name, "timeout", 7 );
  1078. my $http_noshutdown = AttrVal( $name, "http-noshutdown", "1" );
  1079. my $wu_user = AttrVal( $name, "wu_id", "" );
  1080. my $wu_pass = AttrVal( $name, "wu_password", "" );
  1081. Log3 $name, 5, "HP1000 $name: called function HP1000_PushWU()";
  1082. if ( $wu_user eq "" && $wu_pass eq "" ) {
  1083. Log3 $name, 4,
  1084. "HP1000 $name: missing attributes for Weather Underground transfer: wu_user and wu_password";
  1085. my $return = "error: missing attributes wu_user and wu_password";
  1086. readingsBeginUpdate($hash);
  1087. readingsBulkUpdateIfChanged( $hash, "wu_state", $return );
  1088. readingsEndUpdate( $hash, 1 );
  1089. return;
  1090. }
  1091. if ( AttrVal( $name, "wu_realtime", "1" ) eq "0" ) {
  1092. Log3 $name, 5, "HP1000 $name: Explicitly turning off realtime";
  1093. delete $webArgs->{realtime};
  1094. delete $webArgs->{rtfreq};
  1095. }
  1096. elsif ( AttrVal( $name, "wu_realtime", "0" ) eq "1" ) {
  1097. Log3 $name, 5, "HP1000 $name: Explicitly turning on realtime";
  1098. $webArgs->{realtime} = 1;
  1099. }
  1100. $webArgs->{rtfreq} = 5
  1101. if ( defined( $webArgs->{realtime} )
  1102. && !defined( $webArgs->{rtfreq} ) );
  1103. my $wu_url = (
  1104. defined( $webArgs->{realtime} )
  1105. && $webArgs->{realtime} eq "1"
  1106. ? "https://rtupdate.wunderground.com/weatherstation/updateweatherstation.php?"
  1107. : "https://weatherstation.wunderground.com/weatherstation/updateweatherstation.php?"
  1108. );
  1109. $webArgs->{ID} = $wu_user;
  1110. $webArgs->{PASSWORD} = $wu_pass;
  1111. my $cmd;
  1112. while ( my ( $key, $value ) = each %{$webArgs} ) {
  1113. if ( $key eq "softwaretype" || $key eq "dateutc" ) {
  1114. $value = urlEncode($value);
  1115. }
  1116. elsif ( $key eq "UVI" ) {
  1117. $key = "UV";
  1118. $value = $value;
  1119. }
  1120. elsif ( $key eq "UV" ) {
  1121. next;
  1122. }
  1123. $cmd .= "$key=" . $value . "&";
  1124. }
  1125. Log3 $name, 4, "HP1000 $name: pushing data to WU: " . $cmd;
  1126. HttpUtils_NonblockingGet(
  1127. {
  1128. url => $wu_url . $cmd,
  1129. timeout => $timeout,
  1130. noshutdown => $http_noshutdown,
  1131. data => undef,
  1132. hash => $hash,
  1133. callback => \&HP1000_ReturnWU,
  1134. }
  1135. );
  1136. return;
  1137. }
  1138. ###################################
  1139. sub HP1000_ReturnSrv($$$) {
  1140. my ( $param, $err, $data ) = @_;
  1141. my $hash = $param->{hash};
  1142. my $name = $hash->{NAME};
  1143. # device not reachable
  1144. if ($err) {
  1145. my $return = "error: connection timeout";
  1146. Log3 $name, 4, "HP1000 $name: EXTSRV HTTP " . $return;
  1147. readingsBeginUpdate($hash);
  1148. readingsBulkUpdateIfChanged( $hash, "extsrv_state", $return );
  1149. readingsEndUpdate( $hash, 1 );
  1150. }
  1151. # data received
  1152. elsif ($data) {
  1153. my $logprio = 5;
  1154. my $return = "ok";
  1155. if ( $param->{code} ne "200" ) {
  1156. $logprio = 4;
  1157. $return = "error " . $param->{code} . ": $data";
  1158. }
  1159. Log3 $name, $logprio,
  1160. "HP1000 $name: EXTSRV HTTP return: " . $param->{code} . " - $data";
  1161. readingsBeginUpdate($hash);
  1162. readingsBulkUpdateIfChanged( $hash, "extsrv_state", $return );
  1163. readingsEndUpdate( $hash, 1 );
  1164. }
  1165. return;
  1166. }
  1167. ###################################
  1168. sub HP1000_ReturnWU($$$) {
  1169. my ( $param, $err, $data ) = @_;
  1170. my $hash = $param->{hash};
  1171. my $name = $hash->{NAME};
  1172. # device not reachable
  1173. if ($err) {
  1174. my $return = "error: connection timeout";
  1175. Log3 $name, 4, "HP1000 $name: WU HTTP " . $return;
  1176. readingsBeginUpdate($hash);
  1177. readingsBulkUpdateIfChanged( $hash, "wu_state", $return );
  1178. readingsEndUpdate( $hash, 1 );
  1179. }
  1180. # data received
  1181. elsif ($data) {
  1182. my $logprio = 5;
  1183. my $return = "ok";
  1184. if ( $data !~ m/^success.*/ ) {
  1185. $logprio = 4;
  1186. $return = "error";
  1187. $return .= " " . $param->{code} if ( $param->{code} ne "200" );
  1188. $return .= ": $data";
  1189. }
  1190. Log3 $name, $logprio,
  1191. "HP1000 $name: WU HTTP return: " . $param->{code} . " - $data";
  1192. readingsBeginUpdate($hash);
  1193. readingsBulkUpdateIfChanged( $hash, "wu_state", $return );
  1194. readingsEndUpdate( $hash, 1 );
  1195. }
  1196. return;
  1197. }
  1198. 1;
  1199. =pod
  1200. =item device
  1201. =item summary support for Wifi-based weather stations HP1000 and WH2600
  1202. =item summary_DE Unterst&uuml;tzung f&uuml;r die WLAN-basierte HP1000 oder WH2600 Wetterstationen
  1203. =begin html
  1204. <p>
  1205. <a name="HP1000" id="HP1000"></a>
  1206. </p>
  1207. <h3>
  1208. HP1000
  1209. </h3>
  1210. <ul>
  1211. <div>
  1212. <a name="HP1000define" id="HP10000define"></a> <b>Define</b>
  1213. <div>
  1214. <ul>
  1215. <code>define &lt;WeatherStation&gt; HP1000 [&lt;ID&gt; &lt;PASSWORD&gt;]</code><br>
  1216. <br>
  1217. Provides webhook receiver for Wifi-based weather station HP1000 and WH2600 of Fine Offset Electronics (e.g. also known as Ambient Weather WS-1001-WIFI).<br>
  1218. There needs to be a dedicated FHEMWEB instance with attribute webname set to "weatherstation".<br>
  1219. No other name will work as it's hardcoded in the HP1000/WH2600 device itself!<br>
  1220. If necessary, this module will create a matching FHEMWEB instance named WEBweatherstation during initial definition.<br>
  1221. <br>
  1222. As the URI has a fixed coding as well there can only be one single HP1000/WH2600 station per FHEM installation.<br>
  1223. <br>
  1224. Example:<br>
  1225. <div>
  1226. <code># unprotected instance where ID and PASSWORD will be ignored<br>
  1227. define WeatherStation HP1000<br>
  1228. <br>
  1229. # protected instance: Weather Station needs to be configured<br>
  1230. # to send this ID and PASSWORD for data to be accepted<br>
  1231. define WeatherStation HP1000 MyHouse SecretPassword</code>
  1232. </div><br>
  1233. IMPORTANT: In your HP1000/WH2600 hardware device, make sure you use a DNS name as most revisions cannot handle IP addresses correctly.<br>
  1234. </ul>
  1235. </div><br>
  1236. </div>
  1237. <br>
  1238. <a name="HP1000Attr" id="HP10000Attr"></a> <b>Attributes</b>
  1239. <div>
  1240. <ul>
  1241. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1242. <br>
  1243. <a name="wu_id"></a><li><b>wu_id</b></li>
  1244. Weather Underground (Wunderground) station ID
  1245. <a name="wu_password"></a><li><b>wu_password</b></li>
  1246. Weather Underground (Wunderground) password
  1247. <a name="wu_push"></a><li><b>wu_push</b></li>
  1248. Enable or disable to push data forward to Weather Underground (defaults to 0=no)
  1249. <a name="wu_realtime"></a><li><b>wu_realtime</b></li>
  1250. Send the data to the WU realtime server instead of using the standard server (defaults to 1=yes)
  1251. </ul>
  1252. </div>
  1253. </ul>
  1254. =end html
  1255. =begin html_DE
  1256. <p>
  1257. <a name="HP1000" id="HP1000"></a>
  1258. </p>
  1259. <h3>
  1260. HP1000
  1261. </h3>
  1262. <ul>
  1263. <div>
  1264. <a name="HP1000define" id="HP10000define"></a> <b>Define</b>
  1265. <div>
  1266. <ul>
  1267. <code>define &lt;WeatherStation&gt; HP1000 [&lt;ID&gt; &lt;PASSWORD&gt;]</code><br>
  1268. <br>
  1269. Stellt einen Webhook f&uuml;r die WLAN-basierte HP1000 oder WH2600 Wetterstation von Fine Offset Electronics bereit (z.B. auch bekannt als Ambient Weather WS-1001-WIFI).<br>
  1270. Es muss noch eine dedizierte FHEMWEB Instanz angelegt werden, wo das Attribut webname auf "weatherstation" gesetzt wurde.<br>
  1271. Kein anderer Name funktioniert, da dieser hard im HP1000/WH2600 Ger&auml;t hinterlegt ist!<br>
  1272. Sofern notwendig, erstellt dieses Modul eine passende FHEMWEB Instanz namens WEBweatherstation w&auml;hrend der initialen Definition.<br>
  1273. <br>
  1274. Da die URI ebenfalls fest kodiert ist, kann mit einer einzelnen FHEM Installation maximal eine HP1000/WH2600 Station gleichzeitig verwendet werden.<br>
  1275. <br>
  1276. Beispiel:<br>
  1277. <div>
  1278. <code># ungesch&uuml;tzte Instanz bei der ID und PASSWORD ignoriert werden<br>
  1279. define WeatherStation HP1000<br>
  1280. <br>
  1281. # gesch&uuml;tzte Instanz: Die Wetterstation muss so konfiguriert sein, dass sie<br>
  1282. # diese ID und PASSWORD sendet, damit Daten akzeptiert werden<br>
  1283. define WeatherStation HP1000 MyHouse SecretPassword</code>
  1284. </div><br>
  1285. WICHTIG: Im HP1000/WH2600 Ger&auml;t selbst muss sichergestellt sein, dass ein DNS Name statt einer IP Adresse verwendet wird, da einige Revisionen damit nicht umgehen k&ouml;nnen.<br>
  1286. </ul>
  1287. </div><br>
  1288. </div>
  1289. <a name="HP1000Attr" id="HP10000Attr"></a> <b>Attributes</b>
  1290. <div>
  1291. <ul>
  1292. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1293. <br>
  1294. <a name="wu_id"></a><li><b>wu_id</b></li>
  1295. Weather Underground (Wunderground) Stations ID
  1296. <a name="wu_password"></a><li><b>wu_password</b></li>
  1297. Weather Underground (Wunderground) Passwort
  1298. <a name="wu_push"></a><li><b>wu_push</b></li>
  1299. Pushen der Daten zu Weather Underground aktivieren oder deaktivieren (Standard ist 0=aus)
  1300. <a name="wu_realtime"></a><li><b>wu_realtime</b></li>
  1301. Sendet die Daten an den WU Echtzeitserver statt an den Standard Server (Standard ist 1=an)
  1302. </ul>
  1303. </div>
  1304. </ul>
  1305. =end html_DE
  1306. =cut