50_HP1000.pm 61 KB

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