50_HP1000.pm 60 KB

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