59_PROPLANTA.pm 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. ####################################################################################################
  2. # $Id: 59_PROPLANTA.pm 15358 2017-10-30 20:04:27Z tupol $
  3. #
  4. # 59_PROPLANTA.pm
  5. #
  6. # (c) 2014 Torsten Poitzsch
  7. # (c) 2014-2016 tupol http://forum.fhem.de/index.php?action=profile;u=5432
  8. #
  9. # Weather forecast values for 12 days are captured from www.proplanta.de
  10. # inspired by 23_KOSTALPIKO.pm
  11. #
  12. # Copyright notice
  13. #
  14. # This script is free software; you can redistribute it and/or modify
  15. # it under the terms of the GNU General Public License as published by
  16. # the Free Software Foundation; either version 2 of the License, or
  17. # (at your option) any later version.
  18. #
  19. # The GNU General Public License can be found at
  20. # http://www.gnu.org/copyleft/gpl.html.
  21. # A copy is found in the text file GPL.txt and important notices to the license
  22. # from the author is found in LICENSE.txt distributed with these scripts.
  23. #
  24. # This script is distributed in the hope that it will be useful,
  25. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. # GNU General Public License for more details.
  28. #
  29. # This copyright notice MUST APPEAR in all copies of the script!
  30. #
  31. ####################################################################################################
  32. ###############################################
  33. # parser for the weather data
  34. package MyProplantaParser;
  35. use base qw(HTML::Parser);
  36. use Time::HiRes qw(usleep);
  37. our @texte = ();
  38. my $lookupTag = "span|b";
  39. my $curTag = "";
  40. my $curReadingName = "";
  41. my $curRowID = "";
  42. my $curCol = 0;
  43. our $startDay = 0;
  44. my $curTextPos = 0;
  45. my $curReadingType = 0;
  46. # 1 = span|b Text, 2 = readingName, 3 = Tag-Type
  47. # Tag-Types:
  48. # 1 = Number Col 3
  49. # 2 = Number Col 2-5
  50. # 3 = Number Col 2|4|6|8
  51. # 4 = Intensity-Text Col 2-5
  52. # 5 = Time Col 2-5
  53. # 6 = Time Col 3
  54. # 7 = alternative text of image Col 2-5 (weather state)
  55. # 8 = MinMaxNummer Col 3
  56. # 9 = Date Col 2-5 / bold
  57. # 10 = alternative text of Col 3 (Wind direction)
  58. # 11 = alternative text of image Col 3 (current weather state)
  59. my @knownNoneIDs = ( ["Temperatur", "temperature", 1]
  60. ,["relative Feuchte", "humidity", 1]
  61. ,["Sichtweite", "visibility", 1]
  62. ,["Wetterzustand", "weather", 11]
  63. ,["Windrichtung", "windDir", 10]
  64. ,["Windgeschwindigkeit", "wind", 1]
  65. ,["Luftdruck", "pressure", 1]
  66. ,["Taupunkt", "dewPoint", 1]
  67. ,["Uhrzeit", "obsTime", 6]
  68. ,["Höhe der", "cloudBase", 8]
  69. );
  70. # 1 = Tag-ID, 2 = readingName, 3 = Tag-Type (see above)
  71. my @knownIDs = (
  72. ["DATUM", "date", 9]
  73. ,["TMAX", "tempMax", 2]
  74. ,["TMIN", "tempMin", 2]
  75. ,["NW", "chOfRainDay", 2]
  76. ,["NW_Nacht", "chOfRainNight", 2]
  77. ,["BF", "frost", 4]
  78. ,["VERDUNST", "evapor", 4]
  79. ,["TAUBILDUNG", "dew", 4]
  80. ,["SD", "sun", 2]
  81. ,["UV", "uv", 2]
  82. ,["GS", "rad", 3]
  83. ,["WETTER_ID", "weather", 7]
  84. ,["WETTER_ID_MORGENS", "weatherMorning", 7]
  85. ,["WETTER_ID_TAGSUEBER", "weatherDay", 7]
  86. ,["WETTER_ID_ABENDS", "weatherEvening", 7]
  87. ,["WETTER_ID_NACHT", "weatherNight", 7]
  88. ,["T_0", "temp00", 2]
  89. ,["T_3", "temp03", 2]
  90. ,["T_6", "temp06", 2]
  91. ,["T_9", "temp09", 2]
  92. ,["T_12", "temp12", 2]
  93. ,["T_15", "temp15", 2]
  94. ,["T_18", "temp18", 2]
  95. ,["T_21", "temp21", 2]
  96. ,["NW_0", "chOfRain00", 2]
  97. ,["NW_3", "chOfRain03", 2]
  98. ,["NW_6", "chOfRain06", 2]
  99. ,["NW_9", "chOfRain09", 2]
  100. ,["NW_12", "chOfRain12", 2]
  101. ,["NW_15", "chOfRain15", 2]
  102. ,["NW_18", "chOfRain18", 2]
  103. ,["NW_21", "chOfRain21", 2]
  104. ,["NS_0", "rain00", 2]
  105. ,["NS_3", "rain03", 2]
  106. ,["NS_6", "rain06", 2]
  107. ,["NS_9", "rain09", 2]
  108. ,["NS_12", "rain12", 2]
  109. ,["NS_15", "rain15", 2]
  110. ,["NS_18", "rain18", 2]
  111. ,["NS_21", "rain21", 2]
  112. ,["NS_24H", "rain", 2]
  113. ,["BD_0", "cloud00", 2]
  114. ,["BD_3", "cloud03", 2]
  115. ,["BD_6", "cloud06", 2]
  116. ,["BD_9", "cloud09", 2]
  117. ,["BD_12", "cloud12", 2]
  118. ,["BD_15", "cloud15", 2]
  119. ,["BD_18", "cloud18", 2]
  120. ,["BD_21", "cloud21", 2]
  121. ,["MA", "moonRise", 5]
  122. ,["MU", "moonSet", 5]
  123. ,["WGRAD_0", "windDir00", 2]
  124. ,["WGRAD_3", "windDir03", 2]
  125. ,["WGRAD_6", "windDir06", 2]
  126. ,["WGRAD_9", "windDir09", 2]
  127. ,["WGRAD_12", "windDir12", 2]
  128. ,["WGRAD_15", "windDir15", 2]
  129. ,["WGRAD_18", "windDir18", 2]
  130. ,["WGRAD_21", "windDir21", 2]
  131. ,["WGESCHW_0", "wind00", 2]
  132. ,["WGESCHW_3", "wind03", 2]
  133. ,["WGESCHW_6", "wind06", 2]
  134. ,["WGESCHW_9", "wind09", 2]
  135. ,["WGESCHW_12", "wind12", 2]
  136. ,["WGESCHW_15", "wind15", 2]
  137. ,["WGESCHW_18", "wind18", 2]
  138. ,["WGESCHW_21", "wind21", 2]
  139. );
  140. my %intensity = ( "keine" => 0
  141. ,"nein" => 0
  142. ,"gering" => 1
  143. ,"leicht" => 1
  144. ,"ja" => 1
  145. ,"mäßig" => 2
  146. ,"stark" => 3
  147. );
  148. my %winddir = ( "Nord" => 0
  149. ,"Nord-Nordost" => 23
  150. ,"Nordost" => 45
  151. ,"Ost-Nordost" => 68
  152. ,"Ost" => 90
  153. ,"Ost-Südost" => 113
  154. ,"Südost" => 135
  155. ,"Süd-Südost" => 158
  156. ,"Süd" => 180
  157. ,"Süd-Südwest" => 203
  158. ,"Südwest" => 225
  159. ,"West-Südwest" => 248
  160. ,"West" => 270
  161. ,"West-Nordwest" => 203
  162. ,"Nordwest" => 225
  163. ,"Nord-Nordwest" => 248
  164. );
  165. ##############################################
  166. sub get_wday($)
  167. {
  168. my ($date) = @_;
  169. my @wday_txt = qw(So Mo Di Mi Do Fr Sa);
  170. my @th=localtime $date;
  171. return $wday_txt [$th[6]];
  172. }
  173. ##############################################
  174. # here HTML::text/start/end are overridden
  175. sub text
  176. {
  177. my ( $self, $text ) = @_;
  178. my $found = 0;
  179. my $readingName;
  180. # Wait 1ms to reduce CPU load and hence blocking of FHEM by it (workaround until a better solution is available)
  181. usleep (1000);
  182. if ( $curTag =~ $lookupTag ) {
  183. $curTextPos++;
  184. $text =~ s/^\s+//; # trim string
  185. $text =~ s/\s+$//;
  186. $text =~ s/0/0/g; # replace 0
  187. $text =~ s/1/1/g; # replace 1
  188. $text =~ s/2/2/g; # replace 2
  189. $text =~ s/3/3/g; # replace 3
  190. $text =~ s/4/4/g; # replace 4
  191. $text =~ s/5/5/g; # replace 5
  192. $text =~ s/6/6/g; # replace 6
  193. $text =~ s/7/7/g; # replace 7
  194. $text =~ s/8/8/g; # replace 8
  195. $text =~ s/9/9/g; # replace 9
  196. # Tag-Type 0 = Check for readings without tag-ID (current readings)
  197. if ($curReadingType == 0) {
  198. if ($startDay == 0 && $curCol == 1 && $curTextPos == 1)
  199. {
  200. foreach my $r (@knownNoneIDs)
  201. {
  202. if ( $$r[0] eq $text )
  203. {
  204. $curReadingName = $$r[1];
  205. $curReadingType = $$r[2];
  206. last;
  207. }
  208. }
  209. }
  210. }
  211. # Tag-Type 1 = Number Col 3
  212. elsif ($curReadingType == 1) {
  213. if ( $curCol == 3 )
  214. {
  215. $readingName = $curReadingName;
  216. if ( $text =~ m/([-,\+]?\d+[,\.]?\d*)/ )
  217. {
  218. $text = $1;
  219. $text =~ tr/,/./; # komma durch punkt ersetzen
  220. }
  221. push( @texte, $readingName."|".$text );
  222. $curReadingType = 0;
  223. }
  224. }
  225. # Tag-Type 2 = Number Col 2-5
  226. elsif ($curReadingType == 2) {
  227. if ( 1 < $curCol && $curCol <= 5 )
  228. {
  229. $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName;
  230. if ( $text =~ m/([-+]?\d+[,.]?\d*)/ )
  231. {
  232. $text = $1;
  233. $text =~ tr/,/./; # komma durch punkt ersetzen
  234. }
  235. push( @texte, $readingName."|".$text );
  236. }
  237. }
  238. # Tag-Type 3 = Number Col 2|4|6|8
  239. elsif ($curReadingType == 3) {
  240. if ( 2 <= $curCol && $curCol <= 5 )
  241. {
  242. if ( $curTextPos % 2 == 1 )
  243. {
  244. $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName;
  245. $text =~ tr/,/./; # komma durch punkt ersetzen
  246. push( @texte, $readingName."|".$text );
  247. }
  248. }
  249. }
  250. # Tag-Type 4 = Intensity-Text Col 2-5
  251. elsif ($curReadingType == 4) {
  252. if ( 2 <= $curCol && $curCol <= 5 )
  253. {
  254. $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName;
  255. $text = $intensity{$text} if defined $intensity{$text};
  256. push( @texte, $readingName . "|" . $text );
  257. }
  258. }
  259. # Tag-Type 5 = Time Col 2-5
  260. elsif ($curReadingType == 5) {
  261. if ( 2 <= $curCol && $curCol <= 5 )
  262. {
  263. $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName;
  264. if ( $text =~ m/([012-]?[-0-9][.:][-0-5][-0-9])/ )
  265. {
  266. $text = $1;
  267. $text =~ tr/./:/; # Punkt durch Doppelpunkt ersetzen
  268. }
  269. push( @texte, $readingName."|".$text );
  270. }
  271. }
  272. # Tag-Type 6 = Time Col 3
  273. elsif ($curReadingType == 6) {
  274. if ( $curCol == 3 )
  275. {
  276. $readingName = $curReadingName;
  277. if ( $text =~ m/([012-]?[-0-9][.:][-0-5][-0-9])/ )
  278. {
  279. $text = $1;
  280. $text =~ tr/./:/; # Punkt durch Doppelpunkt ersetzen
  281. }
  282. push( @texte, $readingName."|".$text );
  283. }
  284. }
  285. # Tag-Type 8 = MinMaxNumber Col 3
  286. elsif ($curReadingType == 8) {
  287. if ( $curCol == 3 )
  288. {
  289. $readingName = $curReadingName;
  290. if ( $text =~ m/(\d+)\s*-\s*(\d+)/ )
  291. {
  292. push( @texte, $readingName."Min|".$1 );
  293. push( @texte, $readingName."Max|".$2 );
  294. }
  295. else
  296. {
  297. push( @texte, $readingName."Min|-" );
  298. push( @texte, $readingName."Max|-" );
  299. }
  300. }
  301. }
  302. # Tag-Type 9 = Date Col 2-5
  303. elsif ($curReadingType == 9 && $curTag eq "b") {
  304. if ( 1 < $curCol && $curCol <= 5 ) {
  305. $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName;
  306. push( @texte, $readingName."|".$text );
  307. }
  308. }
  309. }
  310. }
  311. ##############################################
  312. sub start
  313. {
  314. my ( $self, $tagname, $attr, $attrseq, $origtext ) = @_;
  315. $curTag = $tagname;
  316. if ( $tagname eq "tr" ) {
  317. $curReadingType = 0;
  318. $curCol = 0;
  319. $curTextPos = 0;
  320. if ( defined( $attr->{id} ) ) {
  321. foreach my $r (@knownIDs) {
  322. if ( $$r[0] eq $attr->{id} ) {
  323. $curReadingName = $$r[1];
  324. $curReadingType = $$r[2];
  325. last;
  326. }
  327. }
  328. }
  329. }
  330. elsif ($tagname eq "td") {
  331. $curCol++;
  332. $curTextPos = 0;
  333. }
  334. #wetterstate and icon - process immediately
  335. elsif ($tagname eq "img"
  336. && ( $curReadingType == 7 && 2 <= $curCol && $curCol <= 5
  337. || $curReadingType == 11 && $curCol == 3) ) {
  338. # process alternative text
  339. $readingName = $curReadingName;
  340. $readingName = "fc" . ($startDay+$curCol-2) . "_" . $readingName if $curReadingType == 7;
  341. $text = $attr->{alt};
  342. $text =~ s/Wetterzustand: //;
  343. $text =~ s/ö/oe/;
  344. $text =~ s/ä/ae/;
  345. $text =~ s/ü/ue/;
  346. $text =~ s/ß/ss/;
  347. push( @texte, $readingName . "|" . $text );
  348. # Image URL
  349. push( @texte, $readingName."Icon" . "|" . $attr->{src} );
  350. }
  351. #wind direction - process immediately
  352. elsif ($tagname eq "img" && $curReadingType == 10 && $curCol == 3 ) {
  353. # process alternative text
  354. $readingName = $curReadingName;
  355. $text = $attr->{alt};
  356. $text =~ s/Windrichtung: //;
  357. $text = $winddir{$text} if defined $winddir{$text};
  358. # $text =~ s/ö/oe/;
  359. # $text =~ s/ä/ae/;
  360. # $text =~ s/ü/ue/;
  361. # $text =~ s/ß/ss/;
  362. push( @texte, $readingName . "|" . $text );
  363. # Image URL
  364. push( @texte, $readingName."Icon" . "|" . $attr->{src} );
  365. }
  366. }
  367. ##############################################
  368. sub end
  369. {
  370. my ( $self, $tagname, $attr, $attrseq, $origtext ) = @_;
  371. $curTag = "";
  372. if ( $tagname eq "tr" ) {
  373. $curReadingType = 0
  374. };
  375. }
  376. ##############################################
  377. package main;
  378. use strict;
  379. use feature qw/say switch/;
  380. use warnings;
  381. my $missingModul;
  382. eval "use LWP::UserAgent;1" or $missingModul .= "LWP::UserAgent ";
  383. eval "use HTTP::Request;1" or $missingModul .= "HTTP::Request ";
  384. eval "use HTML::Parser;1" or $missingModul .= "HTML::Parser ";
  385. eval "use MIME::Base64;1" or $missingModul .= "MIME::Base64 ";
  386. require 'Blocking.pm';
  387. require 'HttpUtils.pm';
  388. use vars qw($readingFnAttributes);
  389. use vars qw(%defs);
  390. my $MODUL = "PROPLANTA";
  391. # my %url_template_1 =( "de" => "http://www.proplanta.de/Wetter/LOKALERORT-Wetter.html"
  392. # , "at" => "http://www.proplanta.de/Agrarwetter-Oesterreich/LOKALERORT/"
  393. # , "ch" => "http://www.proplanta.de/Agrarwetter-Schweiz/LOKALERORT/"
  394. # , "fr" => "http://www.proplanta.de/Agrarwetter-Frankreich/LOKALERORT.html"
  395. # , "it" => "http://www.proplanta.de/Agrarwetter-Italien/LOKALERORT.html"
  396. # );
  397. my %url_template = ( "de" => "https://www.proplanta.de/Wetter/profi-wetter.php?SITEID=60&PLZ=LOKALERORT&STADT=LOKALERORT&WETTERaufrufen=stadt&Wtp=&SUCHE=Wetter&wT="
  398. , "at" => "https://www.proplanta.de/Wetter-Oesterreich/profi-wetter-at.php?SITEID=70&PLZ=LOKALERORT&STADT=LOKALERORT&WETTERaufrufen=stadt&Wtp=&SUCHE=Wetter&wT="
  399. , "ch" => "https://www.proplanta.de/Wetter-Schweiz/profi-wetter-ch.php?SITEID=80&PLZ=LOKALERORT&STADT=LOKALERORT&WETTERaufrufen=stadt&Wtp=&SUCHE=Wetter&wT="
  400. , "fr" => "https://www.proplanta.de/Wetter-Frankreich/profi-wetter-fr.php?SITEID=50&PLZ=LOKALERORT&STADT=LOKALERORT&WETTERaufrufen=stadt&Wtp=&SUCHE=Wetter-Frankreich&wT="
  401. , "it" => "https://www.proplanta.de/Wetter-Italien/profi-wetter-it.php?SITEID=40&PLZ=LOKALERORT&STADT=LOKALERORT&WETTERaufrufen=stadt&Wtp=&SUCHE=Wetter-Italien&wT="
  402. );
  403. ########################################
  404. sub PROPLANTA_Log($$$)
  405. {
  406. my ( $hash, $loglevel, $text ) = @_;
  407. my $xline = ( caller(0) )[2];
  408. my $xsubroutine = ( caller(1) )[3];
  409. my $sub = ( split( ':', $xsubroutine ) )[2];
  410. $sub =~ s/PROPLANTA_//;
  411. my $instName = ( ref($hash) eq "HASH" ) ? $hash->{NAME} : $hash;
  412. Log3 $instName, $loglevel, "$MODUL $instName: $sub.$xline " . $text;
  413. }
  414. ###################################
  415. sub PROPLANTA_Initialize($)
  416. {
  417. my ($hash) = @_;
  418. $hash->{DefFn} = "PROPLANTA_Define";
  419. $hash->{UndefFn} = "PROPLANTA_Undef";
  420. $hash->{SetFn} = "PROPLANTA_Set";
  421. $hash->{AttrList} = "INTERVAL URL disable:0,1 forecastDays:4,7,11,14 " . $readingFnAttributes;
  422. }
  423. ###################################
  424. sub PROPLANTA_Define($$)
  425. {
  426. my ( $hash, $def ) = @_;
  427. my $name = $hash->{NAME};
  428. my $lang = "";
  429. my @a = split( "[ \t][ \t]*", $def );
  430. return "Error: Perl moduls ".$missingModul."are missing on this system"
  431. if $missingModul;
  432. return "Wrong syntax: use define <name> PROPLANTA [City] [CountryCode]" if int(@a) > 4;
  433. $lang = "de" if int(@a) == 3;
  434. $lang = lc( $a[3] ) if int(@a) == 4;
  435. if ( $lang ne "")
  436. { # {my $test="http://www.proplanta.de/Wetter/LOKALERORT-Wetter.html";; $test =~ s/LOKALERORT/München/g;; return $test;;}
  437. return "Wrong country code '$lang': use " . join(" | ", keys( %url_template ) ) unless defined( $url_template{$lang} );
  438. my $URL = $url_template{$lang};
  439. my $ort_encode= $a[2];
  440. # change Umlaute from UTF8 in Percent-encode
  441. $ort_encode =~ s/Ä|Ä/%C4/g;
  442. $ort_encode =~ s/Ö|Ö/%D6/g;
  443. $ort_encode =~ s/Ü|Ü/%DC/g;
  444. $ort_encode =~ s/ß|ß/%DF/g;
  445. $ort_encode =~ s/ä|ä/%E4/g;
  446. $ort_encode =~ s/ö|ö/%F6/g;
  447. $ort_encode =~ s/ü|ü/%FC/g;
  448. $URL =~ s/LOKALERORT/$ort_encode/g;
  449. $hash->{URL} = $URL;
  450. # $URL = $url_template_2{$lang};
  451. # $URL =~ s/LOKALERORT/$ort/g;
  452. # $hash->{URL2} = $URL;
  453. }
  454. $hash->{STATE} = "Initializing";
  455. $hash->{fhem}{LOCAL} = 0;
  456. $hash->{INTERVAL} = 3600;
  457. $hash->{fhem}{modulVersion} = '$Date: 2017-10-30 21:04:27 +0100 (Mon, 30 Oct 2017) $';
  458. RemoveInternalTimer($hash);
  459. #Get first data after 32 seconds
  460. InternalTimer( gettimeofday() + 32, "PROPLANTA_Start", $hash, 0 );
  461. return undef;
  462. }
  463. #####################################
  464. sub PROPLANTA_Undef($$)
  465. {
  466. my ( $hash, $arg ) = @_;
  467. RemoveInternalTimer( $hash );
  468. BlockingKill( $hash->{helper}{RUNNING_PID} ) if ( defined( $hash->{helper}{RUNNING_PID} ) );
  469. return undef;
  470. }
  471. #####################################
  472. sub PROPLANTA_Set($@)
  473. {
  474. my ($hash, $name, $cmd, @val) = @_;
  475. # my $reUINT = '^([\\+]?\\d+)$';
  476. my $usage = "Unknown argument $cmd, choose one of update:noArg ";
  477. return $usage unless defined $cmd;
  478. if ( $cmd eq "?" ) {
  479. return $usage;
  480. }
  481. elsif ( $cmd eq "update" ) {
  482. Log3 $name, 3, "PROPLANTA: set $name $cmd ".join(" ", @val);
  483. $hash->{fhem}{LOCAL} = 1;
  484. PROPLANTA_Start($hash);
  485. $hash->{fhem}{LOCAL} = 0;
  486. }
  487. else {
  488. return $usage;
  489. }
  490. return;
  491. }
  492. #####################################
  493. # acquires the html page
  494. sub PROPLANTA_HtmlAcquire($$)
  495. {
  496. my ($hash, $URL) = @_;
  497. my $name = $hash->{NAME};
  498. return unless (defined($hash->{NAME}));
  499. PROPLANTA_Log $hash, 4, "Start capturing of $URL";
  500. my $err_log = "";
  501. my $agent = LWP::UserAgent->new( env_proxy => 1, keep_alive => 1, protocols_allowed => ['http', 'https'], timeout => 10
  502. , agent => "Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko" );
  503. my $request = HTTP::Request->new( GET => $URL );
  504. my $response = $agent->request($request);
  505. $err_log = "Can't get $URL -- " . $response->status_line
  506. unless $response->is_success;
  507. if ( $err_log ne "" )
  508. {
  509. readingsSingleUpdate($hash, "lastConnection", $response->status_line, 1);
  510. PROPLANTA_Log $hash, 1, "Error: $err_log";
  511. return "Error|Error " . $response->status_line;
  512. }
  513. PROPLANTA_Log $hash, 4, length($response->content)." characters captured";
  514. return $response->content;
  515. }
  516. #####################################
  517. sub PROPLANTA_Start($)
  518. {
  519. my ($hash) = @_;
  520. my $name = $hash->{NAME};
  521. return unless (defined($hash->{NAME}));
  522. $hash->{INTERVAL} = AttrVal( $name, "INTERVAL", $hash->{INTERVAL} );
  523. readingsSingleUpdate($hash, "state", "disabled", 1) if AttrVal($name, "disable", 0 ) == 1;
  524. if($hash->{INTERVAL} > 0) {
  525. # reset timer if interval is defined
  526. RemoveInternalTimer( $hash );
  527. InternalTimer(gettimeofday() + $hash->{INTERVAL}, "PROPLANTA_Start", $hash, 1 );
  528. return undef if AttrVal($name, "disable", 0 ) == 1 && !$hash->{fhem}{LOCAL};
  529. }
  530. if ( AttrVal( $name, 'URL', '') eq '' && not defined( $hash->{URL} ) )
  531. {
  532. PROPLANTA_Log $hash, 3, "missing URL";
  533. return;
  534. }
  535. # "Set update"-action will kill a running update child process
  536. if (defined ($hash->{helper}{RUNNING_PID}) && $hash->{fhem}{LOCAL})
  537. {
  538. BlockingKill($hash->{helper}{RUNNING_PID});
  539. delete( $hash->{helper}{RUNNING_PID} );
  540. PROPLANTA_Log $hash, 4, "Killing old forked process";
  541. }
  542. unless (defined ($hash->{helper}{RUNNING_PID}))
  543. {
  544. $hash->{helper}{RUNNING_PID} =
  545. BlockingCall(
  546. "PROPLANTA_Run", # callback worker task
  547. $name, # name of the device
  548. "PROPLANTA_Done", # callback result method
  549. 120, # timeout seconds
  550. "PROPLANTA_Aborted", # callback for abortion
  551. $hash ); # parameter for abortion
  552. PROPLANTA_Log $hash, 4, "Start forked process to capture html";
  553. }
  554. else
  555. {
  556. PROPLANTA_Log $hash, 1, "Could not start forked process, old process still running";
  557. }
  558. }
  559. #####################################
  560. sub PROPLANTA_Run($)
  561. {
  562. eval "setpriority( 0, 0, 10);"; #work-around for old perl
  563. my ($name) = @_;
  564. my $ptext=$name;
  565. my $URL;
  566. my $response;
  567. return unless ( defined($name) );
  568. my $hash = $defs{$name};
  569. return unless (defined($hash->{NAME}));
  570. my $readingStartTime = time();
  571. my $fcDays = AttrVal( $name, 'forecastDays', 14 );
  572. my $parser = MyProplantaParser->new;
  573. # get date from Attribut URL only
  574. my $attrURL = AttrVal( $name, 'URL', "" );
  575. if ($attrURL ne "") {
  576. $response = PROPLANTA_HtmlAcquire($hash,$attrURL);
  577. if ($response =~ /^Error\|/) {
  578. $ptext .= "|".$response;
  579. }
  580. else {
  581. PROPLANTA_Log $hash, 4, "Start HTML parsing of captured page";
  582. $parser->report_tags(qw(tr td span b img));
  583. @MyProplantaParser::texte = ();
  584. $MyProplantaParser::startDay = 0;
  585. # parsing the complete html-page-response, needs some time
  586. $parser->parse($response);
  587. }
  588. }
  589. # Get data from location specified in define
  590. else {
  591. $URL = $hash->{URL};
  592. my @URL_days = (0, 4, 7, 11);
  593. foreach (@URL_days) {
  594. last unless $_ < $fcDays;
  595. $response = PROPLANTA_HtmlAcquire($hash,$URL . $_);
  596. $MyProplantaParser::startDay = $_;
  597. if ($response !~ /^Error\|/) {
  598. PROPLANTA_Log $hash, 4, "Start HTML parsing of captured page";
  599. $parser->parse($response);
  600. }
  601. }
  602. PROPLANTA_Log $hash, 4, "Found terms: " . @MyProplantaParser::texte;
  603. # pack the results in a single string
  604. if (@MyProplantaParser::texte > 0) {
  605. $ptext .= "|". join('|', @MyProplantaParser::texte);
  606. }
  607. PROPLANTA_Log $hash, 5, "Parsed string: " . $ptext;
  608. }
  609. $ptext .= "|durationFetchReadings|";
  610. $ptext .= sprintf "%.2f", time() - $readingStartTime;
  611. return $ptext;
  612. }
  613. #####################################
  614. # asyncronous callback by blocking
  615. sub PROPLANTA_Done($)
  616. {
  617. my ($string) = @_;
  618. return unless ( defined($string) );
  619. # all term are separated by "|" , the first is the name of the instance
  620. my ( $name, %values ) = split( "\\|", $string );
  621. my $hash = $defs{$name};
  622. return unless ( defined($hash->{NAME}) );
  623. PROPLANTA_Log $hash, 4, "Forked process successfully finished";
  624. # delete the marker for RUNNING_PID process
  625. delete( $hash->{helper}{RUNNING_PID} );
  626. # Wetterdaten speichern
  627. readingsBeginUpdate($hash);
  628. if ( defined $values{Error} )
  629. {
  630. readingsBulkUpdate( $hash, "lastConnection", $values{Error} );
  631. }
  632. else
  633. {
  634. my $x = 0;
  635. my $fcDays = AttrVal( $name, 'forecastDays', 14 );
  636. while (my ($rName, $rValue) = each(%values) )
  637. {
  638. if ($fcDays < 14 && $rName =~ /^fc(\d+)_/)
  639. {
  640. next unless $1 < $fcDays;
  641. }
  642. readingsBulkUpdate( $hash, $rName, $rValue );
  643. PROPLANTA_Log $hash, 5, "reading:$rName value:$rValue";
  644. }
  645. if (keys %values > 0)
  646. {
  647. my $newState;
  648. if (defined $values{fc0_tempMin} && defined $values{fc0_tempMax})
  649. {
  650. $newState = "Tmin: " . $values{fc0_tempMin} . " Tmax: " . $values{fc0_tempMax};
  651. # Achtung! Nach Mitternacht fehlen für 1 h die aktuellen Werte
  652. $newState .= " T: " . $values{temperature} . " H: " . $values{humidity} . " W: " . $values{wind} . " P: " . $values{pressure}
  653. if defined $values{temperature} && defined $values{humidity} && defined $values{wind} && defined $values{pressure};
  654. }
  655. else
  656. {
  657. $newState = "Error: Could not capture all data. Please check URL or city name.";
  658. }
  659. readingsBulkUpdate($hash, "state", $newState);
  660. readingsBulkUpdate( $hash, "lastConnection", keys( %values )." values captured in ".$values{durationFetchReadings}." s" );
  661. PROPLANTA_Log $hash, 4, keys( %values )." values captured";
  662. }
  663. else
  664. {
  665. readingsBulkUpdate( $hash, "lastConnection", "no data found" );
  666. PROPLANTA_Log $hash, 1, "No data found. Check city name or URL.";
  667. }
  668. }
  669. readingsEndUpdate( $hash, 1 );
  670. }
  671. #####################################
  672. sub PROPLANTA_Aborted($)
  673. {
  674. my ($hash) = @_;
  675. delete( $hash->{helper}{RUNNING_PID} );
  676. PROPLANTA_Log $hash, 4, "Forked process timed out";
  677. }
  678. ##### noch nicht fertig ###########
  679. sub #####################################
  680. PROPLANTA_Html(@)
  681. {
  682. my ($d,$days) = @_;
  683. $d = "<none>" if(!$d);
  684. $days = 3 unless defined $days;
  685. return "$d is not a PROPLANTA instance<br>"
  686. if(!$defs{$d} || $defs{$d}{TYPE} ne "PROPLANTA");
  687. my $uselocal= 0; #AttrVal($d,"localicons",0);
  688. my $isday;
  689. if ( exists &isday) {
  690. $isday = isday();
  691. }
  692. else {
  693. $isday = 1; #($hour>6 && $hour<19);
  694. }
  695. my $ret = "<table border=0><thead align=center>";
  696. $ret .= sprintf '<tr><th colspan=9 align=left>%s</th></tr>', $defs{$d}{DEF};
  697. $ret .= '<tr><th>Tag</th><th>morgens</th><th>tagsueber</th><th>abends</th><th>nachts</th><th>min</th><th>max</th><th>Regen tags</th><th>Frost</th></tr></thead>';
  698. $ret .= "<tbody align=center>";
  699. # define MyForecast weblink htmlCode { PROPLANTA_Html("ProPlanta_Wetter") }
  700. for(my $i=0; $i<$days; $i++) {
  701. $ret .= sprintf('<tr><td>%s</td><td>%s<br><img src="%s"></td><td>%s<br><img src="%s"></td><td>%s<br><img src="%s"></td><td>%s<br><img src="%s"></td><td>%s&deg;C</td><td>%s&deg;C</td><td>%s %%</td><td>%s</td></tr>',
  702. ReadingsVal($d, "fc".$i."_date", ""),
  703. ReadingsVal($d, "fc".$i."_weatherMorning", ""), ReadingsVal($d, "fc".$i."_weatherMorningIcon", ""),
  704. ReadingsVal($d, "fc".$i."_weatherDay", ""), ReadingsVal($d, "fc".$i."_weatherDayIcon", ""),
  705. ReadingsVal($d, "fc".$i."_weatherEvening", ""), ReadingsVal($d, "fc".$i."_weatherEveningIcon", ""),
  706. ReadingsVal($d, "fc".$i."_weatherNight", ""), ReadingsVal($d, "fc".$i."_weatherNightIcon", ""),
  707. ReadingsVal($d, "fc".$i."_tempMin", ""), ReadingsVal($d, "fc".$i."_tempMax", ""),
  708. ReadingsVal($d, "fc".$i."_chOfRainDay", ""),
  709. ReadingsVal($d, "fc".$i."_frost", "") ? "ja" : "nein"
  710. );
  711. }
  712. # for(my $i=0; $i<=4; $i++) {
  713. # $ret .= sprintf('<tr><td>%s</td><td>%s: %s<br>min %s °C max %s °C<br>wind: %s km/h %s<br>precip: %s mm</td></tr>',
  714. # WWOIconIMGTag(ReadingsVal($d, "fc${i}_weatherDayIcon", ""),$uselocal,$isday),
  715. # ReadingsVal($d, "fc${i}_date", ""),
  716. # ReadingsVal($d, "fc${i}_weatherDay", ""),
  717. # ReadingsVal($d, "fc${i}_tempMinC", ""), ReadingsVal($d, "fc${i}_tempMaxC", ""),
  718. # }
  719. $ret .= "</tbody></table>";
  720. return $ret;
  721. }
  722. #####################################
  723. 1;
  724. =pod
  725. =item device
  726. =item summary extracts weather data from www.proplanta.de
  727. =item summary_DE Extrahiert Wetterdaten von www.proplanta.de
  728. =begin html
  729. <a name="PROPLANTA"></a>
  730. <h3>PROPLANTA</h3>
  731. <div>
  732. <ul>
  733. The module extracts weather data from <a href="http://www.proplanta.de">www.proplanta.de</a>.
  734. <br>
  735. The website provides a forecast for 12 days, for the first 7 days in a 3-hours-interval.
  736. <br>
  737. This modul causes a high CPU load. It is recommended to reduce the number of captured forecast days.
  738. <br>
  739. It uses the perl moduls HTTP::Request, LWP::UserAgent and HTML::Parse.
  740. <br/><br/>
  741. <a name="PROPLANTAdefine"></a>
  742. <b>Define</b>
  743. <ul>
  744. <br>
  745. <code>define &lt;name&gt; PROPLANTA [City] [CountryCode]</code>
  746. <br>
  747. Example:
  748. <br>
  749. <code>define wetter PROPLANTA Bern ch</code>
  750. <br>
  751. <code>define wetter PROPLANTA Wittingen+(Niedersachsen)</code>
  752. <br>&nbsp;
  753. <li><code>[City]</code>
  754. <br>
  755. Optional. The city must be selectable on <a href="http://www.proplanta.de">www.proplanta.de</a>.
  756. <br>
  757. Please pay attention to the <b>Capital</b> letters in the city names.
  758. Spaces within the name are replaced by a + (plus).
  759. </li><br>
  760. <li><code>[CountryCode]</code>
  761. <br>
  762. Optional. Possible values: de (default), at, ch, fr, it
  763. </li><br>
  764. The function <code>PROPLANTA_Html</code> creates a HTML code for a weather forecast for the given days (default is 3).
  765. <br>
  766. Example:
  767. <br>
  768. <code>define HTMLForecast weblink htmlCode { PROPLANTA_Html("ProPlanta_Wetter"[, days])}</code>
  769. <br/><br/>
  770. </ul>
  771. <br>
  772. <a name="PROPLANTAset"></a>
  773. <b>Set</b>
  774. <ul>
  775. <br>
  776. <li><code>set &lt;name&gt; update</code>
  777. <br>
  778. The weather data are immediately polled from the website.
  779. </li><br>
  780. </ul>
  781. <br>
  782. <a name="PROPLANTAattr"></a>
  783. <b>Attributes</b>
  784. <ul>
  785. <br>
  786. <li><code>forecastDays &lt;4-14&gt;</code>
  787. <br>
  788. Number of days for which the forecast data shall be fetched. Default is 14 days (incl. today).
  789. </li><br>
  790. <li><code>INTERVAL &lt;seconds&gt;</code>
  791. <br>
  792. Poll interval for weather data in seconds (default 3600 = 1 hour)
  793. </li><br>
  794. <li><code>URL &lt;internet address&gt;</code>
  795. <br>
  796. URL to extract information from. Overwrites the values in the 'define' term.
  797. </li><br>
  798. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  799. </ul>
  800. <br>
  801. <a name="PROPLANTAreading"></a>
  802. <b>Forecast readings</b>
  803. <ul>
  804. <br>
  805. <li><b>fc</b><i>0|1|2|3|...|13</i><b>_...</b> - forecast values for <i>today|tommorrow|in 2|3|...|13 days</i></li>
  806. <li><b>fc</b><i>0</i><b>_...<i>00|03|06|09|12|15|18|21</i></b> - forecast values for <i>today</i> at <i>00|03|06|09|12|15|18|21</i> o'clock</li>
  807. <li><b>fc</b><i>0</i><b>_chOfRain</b><i>Day|Night</i> - chance of rain <i>today</i> by <i>day|night</i> in %</li>
  808. <li><b>fc</b><i>0</i><b>_chOfRain</b><i>15</i> - chance of rain <i>today</i> at <i>15:00</i> in %</li>
  809. <li><b>fc</b><i>0</i><b>_cloud</b><i>15</i> - cloud coverage <i>today</i> at <i>15:00</i> in %</li>
  810. <li><b>fc</b><i>0</i><b>_dew</b> - dew formation <i>today</i> (0=none, 1=small, 2=medium, 3=strong)</li>
  811. <li><b>fc</b><i>0</i><b>_evapor</b> - evaporation <i>today</i> (0=none, 1=small, 2=medium, 3=strong)</li>
  812. <li><b>fc</b><i>0</i><b>_frost</b> - ground frost <i>today</i> (0=no, 1=yes)</li>
  813. <li><b>fc</b><i>0</i><b>_moon</b><i>Rise|Set</i> - moon <i>rise|set today</i></li>
  814. <li><b>fc</b><i>0</i><b>_rad</b> - global radiation <i>today</i></li>
  815. <li><b>fc</b><i>0</i><b>_rain</b><i>15</i> - amount of rainfall <i>today</i> at <i>15:00</i> o'clock in mm</li>
  816. <li><b>fc</b><i>0</i><b>_sun</b> - relative sun shine duration <i>today</i> in % (between sun rise and set)</li>
  817. <li><b>fc</b><i>0</i><b>_temp</b><i>Min|Max</i> - <i>minimal|maximal</i> temperature <i>today</i> in &deg;C</li>
  818. <li><b>fc</b><i>0</i><b>_temp</b><i>15</i> - temperatur <i>today</i> at <i>15:00</i> o'clock in &deg;C</li>
  819. <li><b>fc</b><i>0</i><b>_uv</b> - UV-Index <i>today</i></li>
  820. <li><b>fc</b><i>0</i><b>_weather</b><i>Morning|Day|Evening|Night</i> - weather situation <i>today morning|during day|in the evening|during night</i></li>
  821. <li><b>fc</b><i>0</i><b>_weather</b><i>Day</i><b>Icon</b> - icon of weather situation <i>today</i> by <i>day</i></li>
  822. <li><b>fc</b><i>0</i><b>_wind</b><i>15</i> - wind speed <i>today</i> at <i>15</i>:00 Uhr in km/h</li>
  823. <li><b>fc</b><i>0</i><b>_windDir</b><i>15</i> - wind direction <i>today</i> at <i>15</i>:00 Uhr in &deg;</li>
  824. <li>etc.</li>
  825. </ul>
  826. <br>
  827. </ul>
  828. </div>
  829. =end html
  830. =begin html_DE
  831. <a name="PROPLANTA"></a>
  832. <h3>PROPLANTA</h3>
  833. <div>
  834. <ul>
  835. <a name="PROPLANTAdefine"></a>
  836. Das Modul extrahiert Wetterdaten von der Website <a href="http://www.proplanta.de">www.proplanta.de</a>.
  837. <br/>
  838. Es stellt eine Vorhersage f&uuml;r 12 Tage zur Verf&uuml;gung - w&auml;hrend der ersten 7 Tage im 3-Stunden-Intervall.
  839. <br>
  840. Dieses Modul erzeugt eine hohe CPU-Last. Es wird deshalb empfohlen, die auszulesenden Vorhersagetage zu reduzieren.
  841. <br>
  842. <i>Es nutzt die Perl-Module HTTP::Request, LWP::UserAgent und HTML::Parse</i>.
  843. <br>
  844. F&uuml;r detailierte Anleitungen bitte die <a href="http://www.fhemwiki.de/wiki/PROPLANTA"><b>FHEM-Wiki</b></a> konsultieren und erg&auml;nzen.
  845. <br/><br/>
  846. <b>Define</b>
  847. <ul>
  848. <br>
  849. <code>define &lt;Name&gt; PROPLANTA [Stadt] [L&auml;ndercode]</code>
  850. <br>
  851. Beispiel:
  852. <br>
  853. <code>define wetter PROPLANTA Bern ch</code>
  854. <br>
  855. <code>define wetter PROPLANTA Wittingen+(Niedersachsen)</code>
  856. <br>&nbsp;
  857. <li><code>[Stadt]</code>
  858. <br>
  859. Optional. Die Stadt muss auf <a href="http://www.proplanta.de">www.proplanta.de</a> ausw&auml;hlbar sein.
  860. <br>
  861. Wichtig!! Auf die <b>gro&szlig;en</b> Anfangsbuchstaben achten.
  862. Leerzeichen im Stadtnamen werden durch ein + (Plus) ersetzt.
  863. </li><br>
  864. <li><code>[L&auml;ndercode]</code>
  865. <br>
  866. Optional. M&ouml;gliche Werte: de (Standard), at, ch, fr, it
  867. </li><br>
  868. &Uuml;ber die Funktion <code>PROPLANTA_Html</code> wird ein HTML-Code f&uuml;r eine Vorhersage für die angegebenen Anzahl Tage (standardmäßig 3) erzeugt.
  869. <br>
  870. Beispiel:
  871. <br>
  872. <code>define Vorschau weblink htmlCode {PROPLANTA_Html("Wetter"[, Tage])}</code>
  873. <br/><br/>
  874. </ul>
  875. <br>
  876. <a name="PROPLANTAset"></a>
  877. <b>Set</b>
  878. <ul>
  879. <br>
  880. <li><code>set &lt;name&gt; update</code>
  881. <br>
  882. Startet sofort ein neues Auslesen der Wetterdaten.
  883. </li><br>
  884. </ul>
  885. <a name="PROPLANTAattr"></a>
  886. <b>Attribute</b>
  887. <ul>
  888. <br>
  889. <li><code>forecastDays &lt;4-14&gt;</code>
  890. <br>
  891. Anzahl Tage, f&uuml;r die die Vorhersage ausgelesen werden soll. Standard ist 14 Tage (inkl. heute).
  892. </li><br>
  893. <li><code>INTERVAL &lt;Abfrageinterval&gt;</code>
  894. <br>
  895. Abfrageinterval in Sekunden (Standard 3600 = 1 Stunde)
  896. </li><br>
  897. <li><code>URL &lt;Internetadresse&gt;</code>
  898. <br>
  899. Internetadresse, von der die Daten ausgelesen werden (&uuml;berschreibt die Werte im 'define'-Term)
  900. </li><br>
  901. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  902. </ul>
  903. <br><br>
  904. <a name="PROPLANTAreading"></a>
  905. <b>Vorhersagewerte</b>
  906. <ul>
  907. <br>
  908. <li><b>fc</b><i>0|1|2|3...|13</i><b>_...</b> - Vorhersagewerte f&uumlr <i>heute|morgen|&uuml;bermorgen|in 3|...|13 Tagen</i></li>
  909. <li><b>fc</b><i>0</i><b>_...<i>00|03|06|09|12|15|18|21</i></b> - Vorhersagewerte f&uumlr <i>heute</i> um <i>00|03|06|09|12|15|18|21</i> Uhr</li>
  910. <li><b>fc</b><i>0</i><b>_chOfRain</b><i>Day|Night</i> - <i>heutiges</i> Niederschlagsrisiko <i>tags&uuml;ber|nachts</i> in %</li>
  911. <li><b>fc</b><i>1</i><b>_chOfRain</b><i>15</i> - <i>morgiges</i> Niederschlagsrisiko um <i>15</i>:00 Uhr in %</li>
  912. <li><b>fc</b><i>2</i><b>_cloud</b><i>15</i> - Wolkenbedeckungsgrad <i>&uuml;bermorgen</i> um <i>15</i>:00 Uhr in %</li>
  913. <li><b>fc</b><i>0</i><b>_dew</b> - Taubildung <i>heute</i> (0=keine, 1=leicht, 2=m&auml;&szlig;ig, 3=stark)</li>
  914. <li><b>fc</b><i>0</i><b>_evapor</b> - Verdunstung <i>heute</i> (0=keine, 1=gering, 2=m&auml;&szlig;ig, 3=stark)</li>
  915. <li><b>fc</b><i>0</i><b>_frost</b> - Bodenfrost <i>heute</i> (0=nein, 1=ja)</li>
  916. <li><b>fc</b><i>1</i><b>_moon</b><i>Rise|Set</i> - Mond<i>auf|unter</i>gang <i>morgen</i></li>
  917. <li><b>fc</b><i>0</i><b>_rad</b> - Globalstrahlung <i>heute</i></li>
  918. <li><b>fc</b><i>0</i><b>_rain</b><i>15</i> - Niederschlagsmenge <i>heute</i> um <i>15</i>:00 Uhr in mm</li>
  919. <li><b>fc</b><i>0</i><b>_sun</b> - relative Sonnenscheindauer <i>heute</i> in % (zwischen Sonnenauf- und -untergang)</li>
  920. <li><b>fc</b><i>0</i><b>_temp</b><i>Min|Max</i> - <i>Minimal|Maximal</i>temperatur <i>heute</i> in &deg;C</li>
  921. <li><b>fc</b><i>0</i><b>_temp</b><i>15</i> - Temperatur <i>heute</i> um <i>15</i>:00 Uhr in &deg;C</li>
  922. <li><b>fc</b><i>0</i><b>_uv</b> - UV-Index <i>heute</i></li>
  923. <li><b>fc</b><i>0</i><b>_weather</b><i>Morning|Day|Evening|Night</i> - Wetterzustand <i>heute morgen|tags&uuml;ber|abends|nachts</i></li>
  924. <li><b>fc</b><i>0</i><b>_weather</b><i>Day</i><b>Icon</b> - Icon Wetterzustand <i>heute tags&uuml;ber</i></li>
  925. <li><b>fc</b><i>0</i><b>_wind</b><i>15</i> - Windgeschwindigkeit <i>heute</i> um <i>15</i>:00 Uhr in km/h</li>
  926. <li><b>fc</b><i>0</i><b>_windDir</b><i>15</i> - Windrichtung <i>heute</i> um <i>15</i>:00 Uhr in &deg;</li>
  927. <li>etc.</li>
  928. </ul>
  929. <br>
  930. <b>Aktuelle Werte</b>
  931. <ul>
  932. <br>
  933. <li><b>cloudBase</b><i>Min|Max</i> - H&ouml;he der <i>minimalen|maximalen</i> Wolkenuntergrenze in m</li>
  934. <li><b>dewPoint</b> - Taupunkt in &deg;C</li>
  935. <li><b>humidity</b> - relative Feuchtigkeit in %</li>
  936. <li><b>obs_time</b> - Uhrzeit der Wetterbeobachtung</li>
  937. <li><b>pressure</b> - Luftdruck in hPa</li>
  938. <li><b>temperature</b> - Temperature in &deg;C</li>
  939. <li><b>visibility</b> - Sichtweite in km</li>
  940. <li><b>weather</b> - Wetterzustand</li>
  941. <li><b>weatherIcon</b> - Icon Wetterzustand</li>
  942. <li><b>wind</b> - Windgeschwindigkeit in km/h</li>
  943. <li><b>windDir</b> - Windrichtung in &deg;</li>
  944. </ul>
  945. <br><br>
  946. </ul>
  947. </div>
  948. =end html_DE
  949. =cut