55_InfoPanel.pm 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923
  1. # $Id: 55_InfoPanel.pm 14101 2017-04-25 10:19:12Z betateilchen $
  2. =for comment
  3. ##############################################
  4. #
  5. # 55_InfoPanel.pm written by betateilchen
  6. #
  7. # forked from 02_RSS.pm by Dr. Boris Neubert
  8. #
  9. ##############################################
  10. #
  11. # This module uses vTicker,
  12. # a jQuery plug-in that adds scrolling vertical tickers
  13. #
  14. # vTicker is published under MIT and BSD licenses
  15. #
  16. # Copyright (c) 2009–2011
  17. # Permission is hereby granted, free of charge, to any person obtaining a copy
  18. # of this software and associated documentation files (the "Software"), to deal
  19. # in the Software without restriction, including without limitation the rights
  20. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  21. # copies of the Software, and to permit persons to whom the Software is
  22. # furnished to do so, subject to the following conditions:
  23. # The above copyright notice and this permission notice shall be included in
  24. # all copies or substantial portions of the Software.
  25. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  26. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  27. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  28. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  29. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  30. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  31. # THE SOFTWARE.
  32. #
  33. ##############################################
  34. #
  35. # 2015-02-06 - 7892 - module moved from ./contrib to ./FHEM
  36. # 2015-02-08 - 7921 - changed: additional modules are optional
  37. # 2015-02-10 - 7931 - added: direct link to circle,ellipse,rect
  38. # 2015-02-11 - 7933 - added: item counter
  39. # 2015-02-11 - 7944 - added: items push/pop
  40. # 2015-02-12 - 7947 - changed: turtle added to new elements
  41. # 2015-02-14 - 7967 - added: item ticker
  42. # changed: img syntax
  43. # 2015-02-15 - 7989 - added: item embed
  44. # 7995 - added: xcondition (can be overridden by set)
  45. # set ovClear,ovDisable,ovEnable
  46. # get counter, layout, overrides
  47. # - removed: trashcan due to performance issues
  48. # - added: break condition for includes
  49. # 2015-02-24 - 8092 - added: longpoll support (experimental)
  50. # 2015-02-25 - 8095 - changed: iframe handling for secret div
  51. # 2015-03-07 - 8168 - fixed: handling for bg img (Bugzilla #8)
  52. # 2015-03-22 - 8268 - added: attribute showTime
  53. # 2015-03-24 - 8281 - added: BACK as special link to return
  54. # to $FW_httpheader{Referer}
  55. # changed: limit refresh to at least 60 secs
  56. # to prevent performance issues
  57. # 2015-03-29 - 8334 - changed: commandref updated
  58. # 2015-09-25 - - changed: support ReadingsVal() in ticker
  59. # with \n in text
  60. # 2016-09-04 - 12114 - added: movecalculated
  61. #
  62. ##############################################
  63. =cut
  64. package main;
  65. use strict;
  66. use warnings;
  67. use Data::Dumper;
  68. use feature qw/switch/;
  69. use vars qw(%data);
  70. use HttpUtils;
  71. my @valid_valign = qw(auto baseline middle center hanging);
  72. my @valid_halign = qw(start middle end);
  73. my $useImgTools = 1;
  74. no if $] >= 5.017011, warnings => 'experimental';
  75. sub btIP_Define;
  76. sub btIP_Undef;
  77. sub btIP_Set;
  78. sub btIP_Get;
  79. sub btIP_Notify;
  80. sub btIP_readLayout;
  81. sub btIP_itemArea;
  82. sub btIP_itemButton;
  83. sub btIP_itemCircle;
  84. sub btIP_itemCounter;
  85. sub btIP_itemDate;
  86. sub btIP_itemEllipse;
  87. sub btIP_itemGroup;
  88. sub btIP_itemImg;
  89. sub _btIP_imgData;
  90. sub _btIP_imgRescale;
  91. sub btIP_itemLine;
  92. sub btIP_itemLongpoll;
  93. sub btIP_itemPlot;
  94. sub btIP_itemRect;
  95. sub btIP_itemSeconds;
  96. sub btIP_itemText;
  97. sub btIP_itemTextBox;
  98. sub btIP_itemTicker;
  99. sub btIP_itemTime;
  100. sub btIP_changeColor;
  101. sub btIP_color;
  102. sub btIP_FileRead;
  103. sub btIP_findTarget;
  104. sub btIP_xy;
  105. sub btIP_ReturnSVG;
  106. sub btIP_evalLayout;
  107. sub btIP_addExtension;
  108. sub btIP_CGI;
  109. sub btIP_splitRequest;
  110. sub btIP_returnHTML;
  111. sub btIP_HTMLHead;
  112. sub btIP_getScript;
  113. sub btIP_HTMLTail;
  114. sub btIP_Overview;
  115. sub btIP_getURL;
  116. ######################################
  117. sub InfoPanel_Initialize($) {
  118. my ($hash) = @_;
  119. eval "use MIME::Base64";
  120. $useImgTools = 0 if($@);
  121. Log3(undef,4,"InfoPanel: MIME::Base64 missing.") unless $useImgTools;
  122. eval "use Image::Info qw(image_info dim)";
  123. $useImgTools = 0 if($@);
  124. Log3(undef,4,"InfoPanel: Image::Info missing.") unless $useImgTools;
  125. $hash->{DefFn} = "btIP_Define";
  126. $hash->{UndefFn} = "btIP_Undef";
  127. $hash->{SetFn} = "btIP_Set";
  128. $hash->{GetFn} = "btIP_Get";
  129. $hash->{NotifyFn} = "btIP_Notify";
  130. $hash->{AttrList} = "autoreread:1,0 useViewPort:1,0 bgcolor refresh size ";
  131. $hash->{AttrList} .= "mobileApp:none,apple,other ";
  132. $hash->{AttrList} .= "title noscript showTime:1,0 ";
  133. $hash->{AttrList} .= " bgcenter:1,0 bgdir bgopacity tmin" if $useImgTools;
  134. return undef;
  135. }
  136. sub btIP_Define {
  137. my ($hash, $def) = @_;
  138. my @a = split("[ \t]+", $def);
  139. return "Usage: define <name> InfoPanel filename" if(int(@a) != 3);
  140. my $name= $a[0];
  141. my $filename= $a[2];
  142. $hash->{NOTIFYDEV} = 'global';
  143. $hash->{fhem}{div} = '';
  144. $hash->{LAYOUTFILE} = $filename;
  145. btIP_addExtension("btIP_CGI","btip","InfoPanel");
  146. btIP_readLayout($hash);
  147. readingsSingleUpdate($hash,'state','defined',1);
  148. return undef;
  149. }
  150. sub btIP_Undef {
  151. my ($hash, $arg) = @_;
  152. # check if last device
  153. my $url = '/btip';
  154. delete $data{FWEXT}{$url} if int(devspec2array('TYPE=InfoPanel')) == 1;
  155. return undef;
  156. }
  157. sub btIP_Set {
  158. my ($hash, @a) = @_;
  159. my $name = $a[0];
  160. my $usage= "Unknown argument, choose one of reread:noArg ovClear ovEnable ovDisable";
  161. my $ret = undef;
  162. given ($a[1]) {
  163. when ("ovClear") {
  164. if ($a[2] eq "all") {
  165. delete $defs{$name}{fhem}{override};
  166. } else {
  167. delete $defs{$name}{fhem}{override}{$a[2]};
  168. }
  169. }
  170. when ("ovDisable") {
  171. $defs{$name}{fhem}{override}{$a[2]} = 0;
  172. }
  173. when ("ovEnable") {
  174. $defs{$name}{fhem}{override}{$a[2]} = 1;
  175. }
  176. when ("reread") {
  177. btIP_readLayout($hash);
  178. }
  179. default {
  180. $ret = $usage;
  181. }
  182. }
  183. return $ret;
  184. }
  185. sub btIP_Get {
  186. my ($hash, @a) = @_;
  187. my $name = $a[0];
  188. my $usage= "Unknown argument, choose one of reread:noArg counter:noArg layout:noArg overrides:noArg";
  189. my $ret = undef;
  190. given ($a[1]) {
  191. when ("counter") {
  192. $ret = $defs{$name}{fhem}{counter};
  193. }
  194. when ("layout") {
  195. $ret = $defs{$name}{fhem}{layout};
  196. }
  197. when ("overrides") {
  198. last if(!defined($defs{$name}{fhem}{override}));
  199. foreach my $key ( keys %{$defs{$name}{fhem}{override}} ) {
  200. $ret .= "$key => $defs{$name}{fhem}{override}{$key} \n";
  201. }
  202. }
  203. default {
  204. $ret = $usage;
  205. }
  206. }
  207. return $ret;
  208. }
  209. sub btIP_Notify {
  210. my ($hash,$dev) = @_;
  211. return unless AttrVal($hash->{NAME},'autoreload',1);
  212. return if($dev->{NAME} ne "global");
  213. return if(!grep(m/^FILEWRITE $hash->{LAYOUTFILE}$/, @{$dev->{CHANGED}}));
  214. Log3(undef, 4, "InfoPanel: $hash->{NAME} reread layout after edit.");
  215. undef = btIP_readLayout($hash);
  216. return undef;
  217. }
  218. sub btIP_readLayout {
  219. my ($hash)= @_;
  220. my $filename= $hash->{LAYOUTFILE};
  221. my $name= $hash->{NAME};
  222. my $level = 0;
  223. my ($err, @layoutfile) = FileRead($filename);
  224. if($err) {
  225. Log 1, "InfoPanel $name: $err";
  226. $hash->{fhem}{layout} = "text ERROR 50 50 \"Error on reading layout!\"";
  227. } else {
  228. $hash->{fhem}{layout} = join("\n", @layoutfile);
  229. while($hash->{fhem}{layout} =~ m/\@include/ && $level < 1000) {
  230. $level++;
  231. my (@layout2,@include);
  232. foreach my $ll (@layoutfile) {
  233. if($ll !~ m/^\@include/) {
  234. push(@layout2,$ll);
  235. } elsif ($ll =~ m/^\@include/) {
  236. my ($cmd, $def) = split("[ \t]+", $ll, 2);
  237. ($err,@include) = FileRead($def) if($def);
  238. splice(@layout2,-1,0,@include) unless $err;
  239. }
  240. }
  241. @layoutfile = @layout2;
  242. @layout2 = undef;
  243. $hash->{fhem}{layout} = join("\n",@layoutfile);
  244. }
  245. while($hash->{fhem}{layout} =~ m/\@finclude/ && $level < 1000) {
  246. $level++;
  247. my (@layout2,@include);
  248. foreach my $ll (@layoutfile) {
  249. if($ll !~ m/^\@finclude/) {
  250. push(@layout2,$ll);
  251. } else {
  252. my ($cmd, $def) = split("[ \t]+", $ll, 2);
  253. @include = split("\n",AnalyzePerlCommand(undef,$def));
  254. splice(@layout2,-1,0,@include);
  255. }
  256. }
  257. @layoutfile = @layout2;
  258. @layout2 = undef;
  259. $hash->{fhem}{layout} = join("\n",@layoutfile);
  260. }
  261. $hash->{fhem}{layout} = "text ERROR 50 50 \"Loop detected in includes!\"" if ($level >= 1000);
  262. $hash->{fhem}{layout} =~ s/\n\n/\n/g;
  263. }
  264. return;
  265. }
  266. ##################
  267. #
  268. # Layout evaluation
  269. #
  270. ##### Items
  271. sub btIP_itemArea {
  272. my ($id,$x1,$y1,$x2,$y2,$link,$target,%params)= @_;
  273. $id = ($id eq '-') ? createUniqueId() : $id;
  274. my $oldrgb = $params{rgb};
  275. $params{rgb} = '00000000';
  276. my $output = btIP_itemRect($id,$x1,$y1,$x2,$y2,0,0,1,0,$link,$target,%params);
  277. $params{rgb} = $oldrgb;
  278. return $output;
  279. }
  280. sub btIP_itemButton {
  281. my ($id,$x1,$y1,$x2,$y2,$rx,$ry,$link,$text,%params)= @_;
  282. $id = ($id eq '-') ? createUniqueId() : $id;
  283. my $width = $x2 - $x1;
  284. my $height = $y2 - $y1;
  285. my $oldrgb = $params{rgb};
  286. $params{rgb} = $params{boxcolor};
  287. my $output = btIP_itemRect($id,$x1,$y1,$x2,$y2,$rx,$ry,1,0,$link,undef,%params);
  288. $params{rgb} = $oldrgb;
  289. my ($oldhalign,$oldvalign) = ($params{thalign},$params{tvalign});
  290. ($params{thalign},$params{tvalign}) = ("middle","middle");
  291. my $textoutput .= btIP_itemText("${id}_text",($x1+$x2)/2,($y1+$y2)/2,$text,%params);
  292. ($params{thalign},$params{tvalign}) = ($oldhalign,$oldvalign);
  293. $output =~ s/<\/a>/$textoutput<\/a>/;
  294. return $output;
  295. }
  296. sub btIP_itemCircle {
  297. my ($id,$x,$y,$r,$filled,$stroked,$link,%params)= @_;
  298. $id = ($id eq '-') ? createUniqueId() : $id;
  299. my $target;
  300. ($link,$target) = btIP_findTarget($link);
  301. my $output = "";
  302. $output .= "<a xlink:href=\"$link\" target=\"$target\">\n" if($link && length($link));
  303. $output .= "<circle id=\”$id\” cx=\"$x\" cy=\"$y\" r=\"$r\" ";
  304. if($filled > 0 || $stroked > 0) {
  305. $output .= "style=\"";
  306. if($filled > 0) {
  307. my ($r,$g,$b,$a) = btIP_color($params{rgb});
  308. $output .= "fill:rgb($r,$g,$b); fill-opacity:$a; ";
  309. }
  310. if($stroked > 0) {
  311. my ($r,$g,$b,$a) = btIP_color($params{rgb});
  312. $output .= "stroke:rgb($r,$g,$b); stroke-width:$stroked; ";
  313. $output .= "fill:none; " if ($filled == 0);
  314. }
  315. $output .= "\" ";
  316. } else {
  317. $output .= "style=\"fill:none; stroke-width:0; \" ";
  318. }
  319. $output .= "/>\n";
  320. $output .= "</a>\n" if($link && length($link));
  321. return $output;
  322. }
  323. sub btIP_itemCounter {
  324. my ($id,$x,$y,%params)= @_;
  325. $id = ($id eq '-') ? createUniqueId() : $id;
  326. return btIP_itemText($id,$x,$y,$params{counter},%params);
  327. }
  328. sub btIP_itemDate {
  329. my ($id,$x,$y,%params)= @_;
  330. $id = ($id eq '-') ? createUniqueId() : $id;
  331. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  332. return btIP_itemText($id,$x,$y,sprintf("%02d.%02d.%04d", $mday, $mon+1, $year+1900),%params);
  333. }
  334. sub btIP_itemEllipse {
  335. my ($id,$x,$y,$rx,$ry,$filled,$stroked,$link,%params)= @_;
  336. $id = ($id eq '-') ? createUniqueId() : $id;
  337. my $target;
  338. ($link,$target) = btIP_findTarget($link);
  339. my $output = "";
  340. $output .= "<a xlink:href=\"$link\" target=\"$target\">\n" if($link && length($link));
  341. $output .= "<ellipse $id=\"$id\" cx=\"$x\" cy=\"$y\" rx=\"$rx\" ry=\"$ry\" ";
  342. if($filled > 0 || $stroked > 0) {
  343. $output .= "style=\"";
  344. if($filled > 0) {
  345. my ($r,$g,$b,$a) = btIP_color($params{rgb});
  346. $output .= "fill:rgb($r,$g,$b); fill-opacity:$a; ";
  347. }
  348. if($stroked > 0) {
  349. my ($r,$g,$b,$a) = btIP_color($params{rgb});
  350. $output .= "stroke:rgb($r,$g,$b); stroke-width:$stroked; ";
  351. $output .= "fill:none; " if ($filled == 0);
  352. }
  353. $output .= "\" ";
  354. } else {
  355. $output .= "style=\"fill:none; stroke-width:0; \" ";
  356. }
  357. $output .= "/>\n";
  358. $output .= "</a>\n" if($link && length($link));
  359. return $output;
  360. }
  361. sub btIP_itemEmbed {
  362. my ($id,$x,$y,$width,$height,$arg,%params) = @_;
  363. my $embed = "<div id=\"${id}_embedded\" style=\"position:absolute; top:${y}px; left:${x}px; ".
  364. "width:${width}px; height:${height}px; z-index:2; \" >\n".
  365. "$arg\n".
  366. "</div>\n";
  367. return $embed;
  368. }
  369. sub btIP_itemGroup {
  370. my($id,$type,$x,$y,%params) = @_;
  371. return "</g>\n" if $type eq 'close';
  372. $id = ($id eq '-') ? createUniqueId() : $id;
  373. return "<g id=\"$id\" transform=\"translate($x,$y)\" >\n" if $type eq 'open';
  374. }
  375. sub btIP_itemImg {
  376. return unless $useImgTools;
  377. my ($id,$x,$y,$scale,$link,$srctype,$arg,%params)= @_;
  378. $id = ($id eq '-') ? createUniqueId() : $id;
  379. return unless(defined($arg));
  380. return if($arg eq "");
  381. my ($counter,$data,$info,$width,$height,$mimetype,$output);
  382. if($srctype eq 'file') {
  383. ($counter,$data) = btIP_FileRead($arg);
  384. return unless $counter;
  385. } elsif ($srctype eq "url" || $srctype eq "urlq") {
  386. if($srctype eq "url") {
  387. $data= GetFileFromURL($arg,3,undef,1);
  388. } else {
  389. $data= GetFileFromURLQuiet($arg,3,undef,1);
  390. }
  391. } elsif ($srctype eq 'data') {
  392. $data = $arg;
  393. } else {
  394. Log3(undef,4,"InfoPanel: unknown sourcetype $srctype for image tag");
  395. return "";
  396. }
  397. ($width,$height,$mimetype,undef) = _btIP_imgData($data,1);
  398. if($mimetype eq 'image/svg+xml') {
  399. if($data !~ m/viewBox/) {
  400. $data =~ s/width=/viewBox="0 0 $width $height"\n\twidth=/;
  401. }
  402. ($width,$height) = _btIP_imgRescale($width,$height,$scale);
  403. $data =~ s/width=".*"/width="$width"/;
  404. $data =~ s/height=".*"/height="$height"/;
  405. $scale = 1;
  406. (undef,undef,undef,$data) = _btIP_imgData($data,$scale);
  407. } else {
  408. ($width,$height,$mimetype,$data) = _btIP_imgData($data,$scale);
  409. }
  410. my $target;
  411. ($link,$target) = btIP_findTarget($link);
  412. $output = "<!-- s: $scale w: $width h: $height t: $mimetype -->\n";
  413. $output .= "<a xlink:href=\"$link\" target=\"$target\">\n" if($link && length($link));
  414. $output .= "<image id=\"$id\" x=\"$x\" y=\"$y\" width=\"${width}px\" height=\"${height}px\" \nxlink:href=\"$data\" />\n";
  415. $output .= "</a>\n" if($link && length($link));
  416. return ($output,$width,$height);
  417. }
  418. sub _btIP_imgData {
  419. my ($arg,$scale) = @_;
  420. my $info = image_info(\$arg);
  421. my $width = $info->{width};
  422. my $height = $info->{height};
  423. $width =~ s/px//;
  424. $height =~ s/px//;
  425. $width =~ s/pt//;
  426. $height =~ s/pt//;
  427. ($width,$height)= _btIP_imgRescale($width,$height,$scale);
  428. my $mimetype = $info->{file_media_type};
  429. if($FW_userAgent =~ m/Trident/ && $mimetype =~ m/svg/) {
  430. $arg =~ s/width=".*"//g;
  431. $arg =~ s/height=".*"//g;
  432. }
  433. my $data = "data:$mimetype;base64,".encode_base64($arg);
  434. return ($width,$height,$mimetype,$data);
  435. }
  436. sub _btIP_imgRescale {
  437. my ($width,$height,$scale) = @_;
  438. if ($scale =~ s/([whWH])([\d]*)/$2/) {
  439. $scale = (uc($1) eq "W") ? $scale/$width : $scale/$height;
  440. }
  441. $width = int($scale*$width);
  442. $height = int($scale*$height);
  443. return ($width,$height);
  444. }
  445. sub btIP_itemLine {
  446. my ($id,$x1,$y1,$x2,$y2,$th,%params)= @_;
  447. $id = ($id eq '-') ? createUniqueId() : $id;
  448. $th //= 1;
  449. my ($r,$g,$b,$a) = btIP_color($params{rgb});
  450. return "<line id=\"$id\" x1=\"$x1\" y1=\"$y1\" x2=\"$x2\" y2=\"$y2\" style=\"stroke:rgb($r,$g,$b); stroke-width:$th; stroke-opacity:$a; \" />\n";
  451. }
  452. sub btIP_itemLongpoll {
  453. my ($id,$x,$y,$text,%params)= @_;
  454. my ($iconName,undef,undef) = FW_dev2image($id);
  455. my $iconURL = FW_IconURL($iconName) if defined($iconName);
  456. my $color = substr($params{rgb},0,6);
  457. my $opacity = hex(substr($params{rgb},6,2))/255;
  458. my $output = "<div informId=\"$id\" style=\"position:absolute; top:${y}px; left:${x}px; ";
  459. $output .= "font-family:$params{font}; font-size:$params{pt}; color:#$color; opacity:$opacity; " if defined($text);
  460. $output .= "margin-top:0px; z-index:3; \" >\n";
  461. $output .= "$text\n" if defined($text);
  462. $output .= "<img src=\"$iconURL\">\n" unless defined($text);
  463. $output .= "</div>\n";
  464. $defs{$params{name}}{fhem}{div} .= $output;
  465. return "";
  466. }
  467. sub btIP_itemPlot {
  468. my ($id,$x,$y,$scale,$inline,$arg) = @_;
  469. my (@plotName) = split(";",$arg);
  470. $id = ($id eq '-') ? createUniqueId() : $id;
  471. my (@webs,$width,$height,$newWidth,$newHeight,$output,$mimetype,$svgdata);
  472. @webs=devspec2array("TYPE=FHEMWEB");
  473. foreach(@webs) {
  474. if(!InternalVal($_,'TEMPORARY',undef)) {
  475. $FW_wname=InternalVal($_,'NAME','');
  476. last;
  477. }
  478. }
  479. if(!$useImgTools) {
  480. $scale = 1;
  481. $inline = 0;
  482. }
  483. ($width,$height) = split(",", AttrVal($plotName[0],"plotsize","800,160"));
  484. ($newWidth,$newHeight) = _btIP_imgRescale($width,$height,$scale);
  485. if($inline == 1) {
  486. # embed base64 data
  487. $FW_RET = undef;
  488. $FW_webArgs{dev} = $plotName[0];
  489. $FW_webArgs{logdev} = InternalVal($plotName[0], "LOGDEVICE", "");
  490. $FW_webArgs{gplotfile} = InternalVal($plotName[0], "GPLOTFILE", "");
  491. $FW_webArgs{logfile} = InternalVal($plotName[0], "LOGFILE", "CURRENT");
  492. $FW_pos{zoom} = $plotName[1] if(length($plotName[1]));
  493. $FW_pos{off} = $plotName[2] if(length($plotName[2]));
  494. $FW_plotsize = "$newWidth,$newHeight";
  495. ($mimetype, $svgdata) = SVG_showLog("unused");
  496. $svgdata =~ s/<\/svg>/<polyline opacity="0" points="0,0 $newWidth,$newHeight"\/><\/svg>/;
  497. (undef,undef,undef,$svgdata) = _btIP_imgData($svgdata,1);
  498. $output = "<!-- s: $scale ow: $width oh: $height nw: $newWidth nh: $newHeight t: $mimetype -->\n";
  499. $output .= "<image id=\"$id\" x=\"$x\" y=\"$y\" width=\"${newWidth}px\" height=\"${newHeight}px\" \n";
  500. $output .= "xlink:href=\"$svgdata\" />\n";
  501. } else {
  502. # embed link to plot
  503. my $url;
  504. $url = "$FW_ME/SVG_showLog?dev=". $plotName[0].
  505. "&amp;logdev=". InternalVal($plotName[0], "LOGDEVICE", "").
  506. "&amp;gplotfile=". InternalVal($plotName[0], "GPLOTFILE", "").
  507. "&amp;logfile=". InternalVal($plotName[0], "LOGFILE", "CURRENT").
  508. "&amp;plotsize=". "$newWidth,$newHeight";
  509. $url .= "&amp;pos=";
  510. $url .= "zoom=". "$plotName[1];" if(length($plotName[1]));
  511. $url .= "off=". $plotName[2] if(length($plotName[2]));
  512. $output = "<!-- $url -->\n";
  513. $output .= "<image id=\"$id\" x=\"$x\" y=\"$y\" width=\"${newWidth}px\" height=\"${newHeight}px\" \nxlink:href=\"$url\" />\n";
  514. }
  515. return ($output,$newWidth,$newHeight);
  516. }
  517. sub btIP_itemRect {
  518. my ($id,$x1,$y1,$x2,$y2,$rx,$ry,$filled,$stroked,$link,$target,%params)= @_;
  519. $id = ($id eq '-') ? createUniqueId() : $id;
  520. $target //= "";
  521. ($link,$target) = btIP_findTarget($link) unless ($target ne "");
  522. my $width = $x2 - $x1;
  523. my $height = $y2 - $y1;
  524. $filled //= 0;
  525. $stroked //= 0;
  526. my $output = "";
  527. $output .= "<a id=\”${id}_link\” xlink:href=\"$link\" target=\"$target\">\n" if($link && length($link));
  528. $output .= "<rect id=\”${id}_rect\” x=\"$x1\" y=\"$y1\" width=\"$width\" height=\"$height\" rx=\"$rx\" ry=\"$ry\" ";
  529. if($filled > 0 || $stroked > 0) {
  530. $output .= "style=\"";
  531. if($filled > 0) {
  532. my ($r,$g,$b,$a) = btIP_color($params{rgb});
  533. $output .= "fill:rgb($r,$g,$b); fill-opacity:$a; ";
  534. }
  535. if($stroked > 0) {
  536. my ($r,$g,$b,$a) = btIP_color($params{rgb});
  537. $output .= "stroke:rgb($r,$g,$b); stroke-width:$stroked; ";
  538. $output .= "fill:none; " if ($filled == 0);
  539. }
  540. $output .= "\" ";
  541. } else {
  542. $output .= "style=\"fill:none; stroke-width:0; \" ";
  543. }
  544. $output .= "/>\n";
  545. $output .= "</a>\n" if($link && length($link));
  546. return $output;
  547. }
  548. sub btIP_itemSeconds {
  549. my ($id,$x,$y,$format,%params)= @_;
  550. $id = ($id eq '-') ? createUniqueId() : $id;
  551. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  552. if ($format eq "colon")
  553. { return btIP_itemText($id,$x,$y,sprintf(":%02d", $sec),%params); }
  554. else
  555. { return btIP_itemText($id,$x,$y,sprintf("%02d", $sec),%params); }
  556. }
  557. sub btIP_itemText {
  558. my ($id,$x,$y,$text,%params)= @_;
  559. return unless(defined($text));
  560. $id = ($id eq '-') ? createUniqueId() : $id;
  561. my ($r,$g,$b,$a) = btIP_color($params{rgb});
  562. my $output = "<text id=\”$id\” x=\"$x\" y=\"$y\" ".
  563. "fill=\"rgb($r,$g,$b)\" fill-opacity=\"$a\" ".
  564. "font-family=\"$params{font}\" ".
  565. "font-style=\"$params{fontstyle}\" ".
  566. "font-size=\"$params{pt}px\" ".
  567. "font-weight=\"$params{fontweight}\" ".
  568. "text-anchor=\"$params{thalign}\" ".
  569. "text-decoration=\"$params{textdecoration}\" ".
  570. "alignment-baseline=\"$params{tvalign}\" >\n".
  571. "$text\n".
  572. "</text>\n";
  573. return $output;
  574. }
  575. sub btIP_itemTextBox {
  576. my ($id,$x,$y,$boxwidth,$boxheight,$text,$link,%params)= @_;
  577. return unless(defined($text));
  578. $id = ($id eq '-') ? createUniqueId() : $id;
  579. my $color = substr($params{rgb},0,6);
  580. $link =~ s/"//g;
  581. my $target;
  582. ($link,$target) = btIP_findTarget($link);
  583. my ($d,$output);
  584. if(defined($params{boxcolor})) {
  585. my $orgcolor = $params{rgb};
  586. $params{rgb} = $params{boxcolor};
  587. my $bx1 = $x - $params{padding};
  588. my $by1 = $y - $params{padding};
  589. my $bx2 = $x + $boxwidth + $params{padding};
  590. my $by2 = $y + $boxheight + $params{padding};
  591. $output .= btIP_itemRect("box_$id",$bx1,$by1,$bx2,$by2,1,1,1,0,undef,undef,%params);
  592. $params{rgb} = $orgcolor;
  593. } else {
  594. $output = "";
  595. }
  596. $d = "<div id=\"text_$id\" style=\"position:absolute; top:".$y."px; left:".$x."px; ".
  597. "width:".$boxwidth."px; height:".$boxheight."px; text-overflow:ellipsis; z-index:2\" >\n".
  598. "<style type=\"text/css\">a {text-decoration: none;}</style>\n";
  599. $d .= "<a href=\"$link\" target=\"$target\">\n" if($link && length($link));
  600. $d .= "<p style=\"font-family:$params{font}; font-size:$params{pt}; color:#$color; ".
  601. "width:".$boxwidth."px; height:".$boxheight."px; ".
  602. "margin-top:0px; text-align:$params{tbalign}; text-overflow:ellipsis; ".
  603. "\">\n$text\n</p>\n";
  604. $d .= "</a>" if($link && length($link));
  605. $d .= "</div>\n";
  606. $defs{$params{name}}{fhem}{div} .= $d;
  607. return $output;
  608. }
  609. sub btIP_itemTicker {
  610. my ($id,$x,$y,$width,$items,$speed,$arg,%params) = @_;
  611. $id = ($id eq '-') ? createUniqueId() : $id;
  612. my $pause = 2 * $speed;
  613. my $color = substr($params{rgb},0,6);
  614. $arg =~ s/\\n/\n/g; # support ReadingsVal() with \n in text
  615. my @a = split("\n",$arg);
  616. my $liTemplate = '<li>%s</li>'."\n";
  617. my $ticker = "<div id=\"${id}_tickercontent\" >\n".
  618. "<script>\$(function() {\$('#${id}_ticker').vTicker('init', ".
  619. "{speed: $speed, pause: $pause, mousePause:true, showItems: $items, padding:$params{padding}});});</script>\n".
  620. "<div id=\"${id}_ticker\" style=\"position:relative; top:${y}px; left:${x}px; z-index:1; ".
  621. "width:${width}px; text-align:$params{tbalign}; ".
  622. "font-family:$params{font}; font-size:$params{pt}; color:#$color; \" >\n<ul>\n";
  623. foreach (@a) {$ticker .= sprintf($liTemplate,$_)};
  624. $ticker .= "</ul>\n</div>\n</div>\n";
  625. return $ticker;
  626. }
  627. sub btIP_itemTime {
  628. my ($id,$x,$y,%params)= @_;
  629. $id = ($id eq '-') ? createUniqueId() : $id;
  630. my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  631. return btIP_itemText($id,$x,$y,sprintf("%02d:%02d", $hour, $min),%params);
  632. }
  633. ##### Helper
  634. sub btIP_changeColor {
  635. my($file,$oldcolor,$newcolor) = @_;
  636. Log3(undef,4,"InfoPanel: read file $file for changeColor");
  637. my ($counter,$data) = btIP_FileRead($file);
  638. return unless $counter;
  639. if($newcolor =~ /[[:xdigit:]]{6}/) {
  640. Log3(undef,4,"InfoPanel: changing color from $oldcolor to $newcolor");
  641. $data =~ s/fill="#$oldcolor"/fill="#$newcolor"/g;
  642. $data =~ s/fill:#$oldcolor/fill:#$newcolor/g;
  643. } else {
  644. Log3(undef,4,"InfoPanel: invalid rgb value for changeColor!");
  645. }
  646. return $data;
  647. }
  648. sub btIP_color {
  649. my ($rgb)= @_;
  650. my $alpha = 1;
  651. my @d= split("", $rgb);
  652. if(length($rgb) == 8) {
  653. $alpha = hex("$d[6]$d[7]");
  654. $alpha = $alpha/255;
  655. }
  656. return (hex("$d[0]$d[1]"),hex("$d[2]$d[3]"),hex("$d[4]$d[5]"),$alpha);
  657. }
  658. sub btIP_FileRead {
  659. my ($file) = @_;
  660. my ($data,$counter);
  661. Log3(undef,4,"InfoPanel: looking for img $file");
  662. if(configDBUsed()){
  663. Log3(undef,4,"InfoPanel: reading from configDB");
  664. ($data,$counter) = _cfgDB_Fileexport($file,1);
  665. Log3(undef,4,"InfoPanel: file not found in database") unless $counter;
  666. }
  667. if(!$counter) {
  668. Log3(undef,4,"InfoPanel: reading from filesystem");
  669. my $length = -s "$file";
  670. open(GRAFIK, "<", $file) or die("File not found $!");
  671. binmode(GRAFIK);
  672. $counter = read(GRAFIK, $data, $length);
  673. close(GRAFIK);
  674. Log3(undef,4,"InfoPanel: file not found in filesystem") unless $counter;
  675. }
  676. return "" unless $counter;
  677. Log3(undef,4,"InfoPanel: file found.");
  678. return ($counter,$data);
  679. }
  680. sub btIP_findTarget {
  681. my ($link) = shift;
  682. return unless length($link);
  683. my $target = 'secret';
  684. $target = '_top' if $link =~ s/^-//;
  685. $target = '_blank' if $link =~ s/^\+//;
  686. $link = $FW_httpheader{Referer} if $link eq 'BACK';
  687. return ($link,$target);
  688. }
  689. sub btIP_xy {
  690. my ($x,$y,%params)= @_;
  691. $x = $params{width} if ($x eq 'max');
  692. $y = $params{height} if ($y eq 'max');
  693. $x = $params{xx} if ($x eq 'x');
  694. $y = $params{yy} if ($y eq 'y');
  695. if((-1 < $x) && ($x < 1)) { $x *= $params{width}; }
  696. if((-1 < $y) && ($y < 1)) { $y *= $params{height}; }
  697. return($x,$y);
  698. }
  699. ##################
  700. #
  701. # create SVG content
  702. #
  703. sub btIP_returnSVG {
  704. my ($name)= @_;
  705. #
  706. # increase counter
  707. #
  708. if(defined($defs{$name}{fhem}) && defined($defs{$name}{fhem}{counter})) {
  709. $defs{$name}{fhem}{counter}++;
  710. } else {
  711. $defs{$name}{fhem}{counter}= 1;
  712. }
  713. my ($width,$height)= split(/x/, AttrVal($name,"size","800x600"));
  714. my $bgcolor = AnalyzePerlCommand(undef,AttrVal($name,'bgcolor','"000000"'));
  715. $bgcolor = substr($bgcolor,0,6);
  716. my $output = "";
  717. our $svg = "";
  718. eval {
  719. $svg = "\n<svg \n".
  720. "xmlns=\"http://www.w3.org/2000/svg\"\nxmlns:xlink=\"http://www.w3.org/1999/xlink\"\n".
  721. "width=\"".$width."px\" height=\"".$height."px\" \n".
  722. "viewPort=\"0 0 $width $height\"\n".
  723. "style=\"stroke-width: 0px; ";
  724. $svg .= "background-color:$bgcolor; " unless $bgcolor eq 'none';
  725. # set the background
  726. # check if background directory is set
  727. my $reason= "?"; # remember reason for undefined image
  728. my $bgdir= AnalyzePerlCommand(undef,AttrVal($name,"bgdir",""));
  729. if(defined($bgdir)){
  730. my $bgnr; # item number
  731. if(defined($defs{$name}{fhem}) && defined($defs{$name}{fhem}{bgnr})) {
  732. $bgnr= $defs{$name}{fhem}{bgnr};
  733. } else {
  734. $bgnr= 0;
  735. }
  736. # check if at least tmin seconds have passed
  737. my $t0= 0;
  738. my $tmin= AttrVal($name,"tmin",0);
  739. if(defined($defs{$name}{fhem}) && defined($defs{$name}{fhem}{t})) {
  740. $t0= $defs{$name}{fhem}{t};
  741. }
  742. my $t1= time();
  743. if($t1-$t0>= $tmin) {
  744. $defs{$name}{fhem}{t}= $t1;
  745. $bgnr++;
  746. }
  747. if(opendir(BGDIR, $bgdir)){
  748. my @bgfiles= grep {$_ !~ /^\./} readdir(BGDIR);
  749. closedir(BGDIR);
  750. if($#bgfiles>=0) {
  751. if($bgnr > $#bgfiles) { $bgnr= 0; }
  752. $defs{$name}{fhem}{bgnr}= $bgnr;
  753. my $bgfile = $bgdir . "/" . $bgfiles[$bgnr];
  754. my $info = image_info($bgfile);
  755. my $bgwidth = $info->{width};
  756. my $bgheight = $info->{height};
  757. my ($u,$v) = ($bgwidth/$width, $bgheight/$height);
  758. my ($w,$h);
  759. if($u>$v) {
  760. $w= $width;
  761. $h= $bgheight/$u;
  762. } else {
  763. $h= $height;
  764. $w= $bgwidth/$v;
  765. }
  766. my $scale = ($u>$v) ? 1/$u : 1/$v;
  767. my ($bgx,$bgy) = (0,0);
  768. $bgx = ($width - $w)/2 if AttrVal($name,'bgcenter',1);
  769. $bgy = ($height - $h)/2 if AttrVal($name,'bgcenter',1);
  770. ($output,undef,undef) = btIP_itemImg('bgImage',$bgx,$bgy,$scale,undef,'file',$bgfile,undef);
  771. my $opacity = AttrVal($name,'bgopacity',1);
  772. $output =~ s/<image\ /<image\ opacity="$opacity" /;
  773. }
  774. } # end opendir()
  775. } # end defined()
  776. $svg .= "\" >\n";
  777. $svg .= "$output\n";
  778. $svg = btIP_evalLayout($svg, $name, $defs{$name}{fhem}{layout});
  779. readingsSingleUpdate($defs{$name},'state',localtime(),1) if(AttrVal($name,'showTime',1));
  780. }; #warn $@ if $@;
  781. if($@) {
  782. my $msg= $@;
  783. chomp $msg;
  784. Log3($name, 2, $msg);
  785. }
  786. $svg .= "\nSorry, your browser does not support inline SVG.\n</svg>\n";
  787. return $svg;
  788. }
  789. sub btIP_evalLayout {
  790. my ($svg,$name,$layout)= @_;
  791. my ($width,$height)= split(/x/, AttrVal($name,"size","800x600"));
  792. my @layout= split("\n", $layout);
  793. my $pstackcount = 0;
  794. my %pstack;
  795. my %params;
  796. $params{name} = $name;
  797. $params{counter} = $defs{$name}{fhem}{counter};
  798. $params{xx} = 0;
  799. $params{yy} = 0;
  800. $params{groupx} = 0;
  801. $params{groupy} = 0;
  802. $params{width} = $width;
  803. $params{height} = $height;
  804. $params{rgb} = 'FFFFFF';
  805. $params{condition} = 1;
  806. $params{boxcolor} = undef;
  807. $params{tbalign} = 'left';
  808. $params{padding} = 0;
  809. $params{font} = 'Arial';
  810. $params{pt} = 12;
  811. $params{fontstyle} = 'initial';
  812. $params{fontweight} = 'normal';
  813. $params{textdecoration} = 'none';
  814. $params{thalign} = 'start';
  815. $params{tvalign} = 'auto';
  816. $defs{$name}{fhem}{div} = undef;
  817. my ($id,$x,$y,$x1,$y1,$x2,$y2,$radius,$rx,$ry);
  818. my ($scale,$inline,$boxwidth,$boxheight,$boxcolor);
  819. my ($speed,$bgcolor,$fgcolor);
  820. my ($text,$link,$target,$imgtype,$srctype,$arg,$format,$filled,$stroked);
  821. my $cont= "";
  822. foreach my $line (@layout) {
  823. # kill trailing newline
  824. chomp $line;
  825. # kill comments and blank lines
  826. $line=~ s/\#.*$//;
  827. $line=~ s/\@.*$//;
  828. $line=~ s/\s+$//;
  829. $line= $cont . $line;
  830. if($line=~ s/\\$//) { $cont= $line; undef $line; }
  831. next unless($line);
  832. $cont= "";
  833. # Debug "$name: evaluating >$line<";
  834. # split line into command and definition
  835. my ($cmd, $def)= split("[ \t]+", $line, 2);
  836. # Debug "CMD= \"$cmd\", DEF= \"$def\"";
  837. # separate condition handling
  838. if($cmd =~ m/condition/) {
  839. if($cmd =~ m/^xcond/) {
  840. ($id,$arg) = split("[ \t]+", $def, 2);
  841. $params{condition} = AnalyzePerlCommand(undef,$arg);
  842. my $override = $defs{$name}{fhem}{override}{$id};
  843. $override //= $params{condition};
  844. if($params{condition}) {
  845. $params{condition} = AnalyzePerlCommand(undef,$arg) && $override;
  846. } else {
  847. $params{condition} = AnalyzePerlCommand(undef,$arg) || $override;
  848. }
  849. } else {
  850. $params{condition} = AnalyzePerlCommand(undef,$def);
  851. }
  852. next;
  853. }
  854. next unless($params{condition});
  855. # Debug "before command $line: x= " . $params{xx} . ", y= " . $params{yy};
  856. eval {
  857. given($cmd) {
  858. when("area") {
  859. ($id,$x1,$y1,$x2,$y2,$link,$target)= split("[ \t]+", $def, 7);
  860. $target //= "";
  861. ($x1,$y1)= btIP_xy($x1,$y1,%params);
  862. ($x2,$y2)= btIP_xy($x2,$y2,%params);
  863. $link = AnalyzePerlCommand(undef,$link);
  864. $params{xx} = $x1;
  865. $params{yy} = $y2;
  866. $svg .= btIP_itemArea($id,$x1,$y1,$x2,$y2,$link,$target,%params);
  867. }
  868. when("boxcolor"){
  869. $def = "\"$def\"" if(length($def) == 6 && $def =~ /[[:xdigit:]]{6}/);
  870. $params{boxcolor} = AnalyzePerlCommand(undef, $def);
  871. }
  872. when("button") {
  873. ($id,$x1,$y1,$x2,$y2,$rx,$ry,$link,$text)= split("[ \t]+", $def, 9);
  874. ($x1,$y1)= btIP_xy($x1,$y1,%params);
  875. ($x2,$y2)= btIP_xy($x2,$y2,%params);
  876. ($rx,$ry)= btIP_xy($rx,$ry,%params);
  877. $params{xx} = $x1;
  878. $params{yy} = $y2;
  879. $link = AnalyzePerlCommand(undef,$link);
  880. $link = (length($link)) ? $link : "-$params{name}.html";
  881. $text = AnalyzePerlCommand(undef,$text);
  882. $svg .= btIP_itemButton($id,$x1,$y1,$x2,$y2,$rx,$ry,$link,$text,%params);
  883. }
  884. when("buttonpanel"){
  885. $defs{$params{name}}{fhem}{div} .= "<div id=\"hiddenDiv\" ".
  886. "style=\"display:none\" >".
  887. "<iframe id=\"secretFrame\" name=\"secret\" src=\"\"></iframe></div>\n";
  888. }
  889. when("circle") {
  890. ($id,$x1,$y1,$radius,$filled,$stroked,$link)= split("[ \t]+", $def, 7);
  891. ($x1,$y1)= btIP_xy($x1,$y1,%params);
  892. $params{xx} = $x1;
  893. $params{yy} = $y1+$radius;
  894. $filled //= 0;
  895. $stroked //= 0;
  896. $link //= "";
  897. $link = AnalyzePerlCommand(undef,$link);
  898. $svg .= btIP_itemCircle($id,$x1,$y1,$radius,$filled,$stroked,$link,%params);
  899. }
  900. when("counter") {
  901. ($id,$x,$y)= split("[ \t]+", $def, 3);
  902. ($x,$y)= btIP_xy($x,$y,%params);
  903. $params{xx} = $x;
  904. $params{yy} = $y;
  905. $svg .= btIP_itemCounter($id,$x,$y,%params);
  906. }
  907. when("date") {
  908. ($id,$x,$y)= split("[ \t]+", $def, 3);
  909. ($x,$y)= btIP_xy($x,$y,%params);
  910. $params{xx} = $x;
  911. $params{yy} = $y;
  912. $svg .= btIP_itemDate($id,$x,$y,%params);
  913. }
  914. when("ellipse") {
  915. ($id,$x1,$y1,$rx,$ry,$filled,$stroked,$link)= split("[ \t]+", $def, 8);
  916. ($x1,$y1) = btIP_xy($x1,$y1,%params);
  917. ($rx,$ry) = btIP_xy($rx,$ry,%params);
  918. $params{xx} = $x1;
  919. $params{yy} = $y1+$ry;
  920. $filled //= 0;
  921. $stroked //= 0;
  922. $link //= "";
  923. $link = AnalyzePerlCommand(undef,$link);
  924. $svg .= btIP_itemEllipse($id,$x1,$y1,$rx,$ry,$filled,$stroked,$link,%params);
  925. }
  926. when("embed") {
  927. ($id,$x,$y,$width,$height,$arg)= split("[ \t]+", $def, 6);
  928. ($x,$y)= btIP_xy($x,$y,%params);
  929. ($width,$height)= btIP_xy($width,$height,%params);
  930. $params{xx} = $x;
  931. $params{yy} = $y;
  932. $arg = AnalyzePerlCommand(undef,$arg);
  933. $defs{$name}{fhem}{div} .= btIP_itemEmbed($id,$x,$y,$width,$height,$arg,%params);
  934. }
  935. when("font") {
  936. $params{font} = $def;
  937. }
  938. when("group") {
  939. ($id,$text,$x,$y) = split("[ \t]+", $def, 4);
  940. $x //= $params{xx};
  941. $y //= $params{yy};
  942. ($x,$y)= btIP_xy($x,$y,%params);
  943. $params{xx} = $x;
  944. $params{yy} = $y;
  945. if($text eq 'open') {
  946. $params{groupx} = $x;
  947. $params{groupy} = $y;
  948. } else {
  949. $params{groupx} = 0;
  950. $params{groupy} = 0;
  951. }
  952. $svg .= btIP_itemGroup($id,$text,$x,$y,%params);
  953. }
  954. when("img") {
  955. ($id,$x,$y,$scale,$link,$srctype,$arg) = split("[ \t]+", $def,7);
  956. ($x,$y) = btIP_xy($x,$y,%params);
  957. $params{xx} = $x;
  958. $params{yy} = $y;
  959. $arg = AnalyzePerlCommand(undef,$arg);
  960. $link = AnalyzePerlCommand(undef,$link);
  961. my($output,$width,$height)= btIP_itemImg($id,$x,$y,$scale,$link,$srctype,$arg,%params);
  962. $svg .= $output;
  963. $params{xx} = $x;
  964. $params{yy} = $y+$height;
  965. }
  966. when("line") {
  967. ($id,$x1,$y1,$x2,$y2,$format) = split("[ \t]+", $def, 6);
  968. ($x1,$y1) = btIP_xy($x1,$y1,%params);
  969. ($x2,$y2) = btIP_xy($x2,$y2,%params);
  970. $format //= 1;
  971. $svg .= btIP_itemLine($id,$x1,$y1,$x2,$y2,$format,%params);
  972. }
  973. when("longpoll") {
  974. ($id,$x,$y,$text)= split("[ \t]+", $def, 4);
  975. $text //= undef;
  976. $text = AnalyzePerlCommand(undef,$text) if defined($text);
  977. ($x,$y)= btIP_xy($x,$y,%params);
  978. $x += $params{groupx};
  979. $y += $params{groupy};
  980. $params{xx} = $x;
  981. $params{yy} = $y;
  982. $svg .= btIP_itemLongpoll($id,$x,$y,$text,%params);
  983. }
  984. when("movecalculated") {
  985. my ($tox,$toy)= split('[ \t]+', $def, 2);
  986. $params{xx} = AnalyzePerlCommand(undef,$tox);
  987. $params{yy} = AnalyzePerlCommand(undef,$toy);
  988. }
  989. when("moveby") {
  990. my ($byx,$byy) = split('[ \t]+', $def, 2);
  991. my ($x,$y)= btIP_xy($byx,$byy,%params);
  992. $params{xx} += $x;
  993. $params{yy} += $y;
  994. }
  995. when("moveto") {
  996. my ($tox,$toy)= split('[ \t]+', $def, 2);
  997. my ($x,$y)= btIP_xy($tox,$toy,%params);
  998. $params{xx} = $x;
  999. $params{yy} = $y;
  1000. }
  1001. when("padding") {
  1002. $params{padding}= AnalyzePerlCommand(undef,$def);
  1003. }
  1004. when("plain") {
  1005. $svg .= AnalyzePerlCommand(undef,$def);
  1006. }
  1007. when("plot") {
  1008. ($id,$x,$y,$scale,$inline,$arg)= split("[ \t]+", $def,6);
  1009. ($x,$y)= btIP_xy($x,$y,%params);
  1010. $arg = AnalyzePerlCommand(undef, $arg);
  1011. my($output,$width,$height)= btIP_itemPlot($id,$x,$y,$scale,$inline,$arg,%params);
  1012. $svg .= $output;
  1013. $params{xx} = $x;
  1014. $params{yy} = $y+$height;
  1015. }
  1016. when("pop") {
  1017. return unless $pstackcount;
  1018. foreach my $key ( keys %{$pstack{$pstackcount}} ) {
  1019. # Debug "pop key: $key, value: $pstack{$pstackcount}{$key}";
  1020. $params{$key} = $pstack{$pstackcount}{$key};
  1021. }
  1022. delete $pstack{$pstackcount};
  1023. $pstackcount--;
  1024. }
  1025. when("pt") {
  1026. $def = AnalyzePerlCommand(undef, $def);
  1027. if($def =~ m/^[+-]/) {
  1028. $params{pt} += $def;
  1029. } else {
  1030. $params{pt} = $def;
  1031. }
  1032. $params{pt} = 6 if($params{pt} < 0);
  1033. }
  1034. when("push") {
  1035. $pstackcount++;
  1036. foreach my $key ( keys %params ) {
  1037. # Debug "push key: $key, value: $params{$key}";
  1038. $pstack{$pstackcount}{$key} = $params{$key};
  1039. }
  1040. }
  1041. when("rect") {
  1042. ($id,$x1,$y1,$x2,$y2,$rx,$ry,$filled,$stroked,$link)= split("[ \t]+", $def, 10);
  1043. ($x1,$y1)= btIP_xy($x1,$y1,%params);
  1044. ($x2,$y2)= btIP_xy($x2,$y2,%params);
  1045. ($rx,$ry) = btIP_xy($rx,$ry,%params);
  1046. $params{xx} = $x1;
  1047. $params{yy} = $y2;
  1048. $filled //= 0; # set 0 as default (not filled)
  1049. $stroked //= 0; # set 0 as default (not stroked)
  1050. $link //= "";
  1051. $link = AnalyzePerlCommand(undef,$link);
  1052. $svg .= btIP_itemRect($id,$x1,$y1,$x2,$y2,$rx,$ry,$filled,$stroked,$link,undef,%params);
  1053. }
  1054. when("rgb"){
  1055. $def = "\"$def\"" if(length($def) == 6 && $def =~ /[[:xdigit:]]{6}/);
  1056. $params{rgb} = AnalyzePerlCommand(undef, $def);
  1057. }
  1058. when("seconds") {
  1059. ($id,$x,$y,$format) = split("[ \+]", $def,4);
  1060. ($x,$y)= btIP_xy($x,$y,%params);
  1061. $params{xx} = $x;
  1062. $params{yy} = $y;
  1063. $svg .= btIP_itemSeconds($id,$x,$y,$format,%params);
  1064. }
  1065. when("text") {
  1066. ($id,$x,$y,$text)= split("[ \t]+", $def, 4);
  1067. ($x,$y)= btIP_xy($x,$y,%params);
  1068. $params{xx} = $x;
  1069. $params{yy} = $y;
  1070. $text= AnalyzePerlCommand(undef, $text);
  1071. $svg .= btIP_itemText($id,$x,$y,$text,%params);
  1072. }
  1073. when("lptext") {
  1074. $svg .= "\n<!-- lptext no longer provided. Use longpoll instead. -->\n\n";
  1075. Log3($name, 2, "InfoPanel $name: command 'lptext' no longer supported.");
  1076. }
  1077. when("textbox") {
  1078. ($id,$x,$y,$boxwidth,$boxheight,$link,$text)= split("[ \t]+", $def, 7);
  1079. ($x,$y)= btIP_xy($x,$y,%params);
  1080. $text = AnalyzePerlCommand(undef, $text);
  1081. $text =~ s/\n/<br\/>/g;
  1082. $link = AnalyzePerlCommand(undef, $link);
  1083. $svg .= btIP_itemTextBox($id,$x,$y,$boxwidth,$boxheight,$text,$link,%params);
  1084. $params{xx} = $x;
  1085. $params{yy} = $y + $boxheight;
  1086. }
  1087. when("textboxalign") {
  1088. $params{tbalign} = $def;
  1089. }
  1090. when("textdesign") {
  1091. my @args = split(/,/,$def);
  1092. my @deco = qw(underline overline line-through); #text-decoration
  1093. my @style = qw(italic oblique); #font-style
  1094. my @weight = qw(bold); #font-weight
  1095. $params{fontstyle} = "initial";
  1096. $params{fontweight} = "initial";
  1097. $params{textdecoration} = "none";
  1098. foreach my $s (@args) {
  1099. if($s ne 'clear') {
  1100. $params{fontstyle} = "$s " if($s ~~ @style);
  1101. $params{fontweight} = "$s " if($s ~~ @weight);
  1102. $params{textdecoration} = "$s " if($s ~~ @deco);
  1103. }
  1104. }
  1105. }
  1106. when("ticker") {
  1107. ($id,$x,$y,$width,$format,$speed,$arg)= split("[ \t]+", $def, 7);
  1108. ($x,$y)= btIP_xy($x,$y,%params);
  1109. $params{xx} = $x;
  1110. $params{yy} = $y;
  1111. $arg = AnalyzePerlCommand(undef,$arg);
  1112. $defs{$name}{fhem}{div} .= btIP_itemTicker($id,$x,$y,$width,$format,$speed,$arg,%params);
  1113. }
  1114. when("time") {
  1115. ($id,$x,$y)= split("[ \t]+", $def, 3);
  1116. ($x,$y)= btIP_xy($x,$y,%params);
  1117. $params{xx} = $x;
  1118. $params{yy} = $y;
  1119. $svg .= btIP_itemTime($id,$x,$y,%params);
  1120. }
  1121. when("trash") {
  1122. $svg .= "\n<!-- Trashcan no longer provided by module due to perfomance issues. -->\n\n";
  1123. Log3($name, 2, "InfoPanel $name: command 'trash' no longer supported.");
  1124. }
  1125. when("thalign"){
  1126. my $d = AnalyzePerlCommand(undef, $def);
  1127. if($d ~~ @valid_halign) {
  1128. $params{thalign}= $d;
  1129. } else {
  1130. Log3($name, 2, "InfoPanel $name: Illegal horizontal alignment $d");
  1131. }
  1132. }
  1133. when("tvalign"){
  1134. my $d = AnalyzePerlCommand(undef, $def);
  1135. if($d ~~ @valid_valign) {
  1136. $params{tvalign}= $d;
  1137. } else {
  1138. Log3($name, 2, "InfoPanel $name: Illegal vertical alignment $d");
  1139. }
  1140. }
  1141. default {
  1142. Log3($name, 2, "InfoPanel $name: Illegal command $cmd in layout definition.");
  1143. } # default
  1144. } # given
  1145. } # eval
  1146. #Debug "after command $line: x= " . $params{xx} . ", y= " . $params{yy};
  1147. } # foreach
  1148. return $svg;
  1149. }
  1150. ##################
  1151. #
  1152. # here we answer any request to http://host:port/fhem/btip
  1153. #
  1154. sub btIP_addExtension {
  1155. my ($func,$link,$friendlyname)= @_;
  1156. my $url = "/" . $link;
  1157. $data{FWEXT}{$url}{FUNC} = $func;
  1158. $data{FWEXT}{$url}{LINK} = "+$link";
  1159. $data{FWEXT}{$url}{NAME} = $friendlyname;
  1160. $data{FWEXT}{$url}{FORKABLE} = 0;
  1161. $data{FWEXT}{jquery}{SCRIPT} = "/pgm2/jquery.min.js" unless $data{FWEXT}{jquery}{SCRIPT};
  1162. $data{FWEXT}{jqueryvticker}{SCRIPT} = "/pgm2/jquery.vticker.min.js" unless $data{FWEXT}{jqueryvticker}{SCRIPT};
  1163. }
  1164. sub btIP_CGI{
  1165. my ($request) = @_;
  1166. my ($name,$ext)= btIP_splitRequest($request);
  1167. if(defined($name)) {
  1168. if($ext eq "") {
  1169. return("text/plain; charset=utf-8", "Illegal extension.");
  1170. }
  1171. if(!defined($defs{$name})) {
  1172. return("text/plain; charset=utf-8", "Unknown InfoPanel device: $name");
  1173. }
  1174. if($ext eq "png") {
  1175. return btIP_returnPNG($name);
  1176. }
  1177. if($ext eq "info" || $ext eq "html") {
  1178. return btIP_returnHTML($name);
  1179. }
  1180. } else {
  1181. return btIP_Overview();
  1182. }
  1183. }
  1184. sub btIP_splitRequest {
  1185. my ($request) = @_;
  1186. if($request =~ /^.*\/btip$/) {
  1187. # http://localhost:8083/fhem/btip
  1188. return (undef,undef); # name, ext
  1189. } else {
  1190. my $call= $request;
  1191. $call =~ s/^.*\/btip\/([^\/]*)$/$1/;
  1192. my $name= $call;
  1193. $name =~ s/^(.*)\.(png|svg|info|html)$/$1/;
  1194. my $ext= $call;
  1195. $ext =~ s/^$name\.(.*)$/$1/;
  1196. return ($name,$ext);
  1197. }
  1198. }
  1199. ####################
  1200. #
  1201. # HTML Stuff
  1202. #
  1203. sub btIP_returnHTML {
  1204. my ($name) = @_;
  1205. my $refresh = AttrVal($name, 'refresh', 60);
  1206. $refresh = ($refresh && $refresh < 59) ? 60 : $refresh;
  1207. my $title = AttrVal($name, 'title', $name);
  1208. my $viewport= "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, minimum-scale=1.0, maximum-scale=1.0\"/>";
  1209. $viewport = AttrVal($name,"useViewPort",1) ? $viewport : "";
  1210. my $webApp = "";
  1211. $webApp = "<meta name=\"apple-mobile-web-app-capable\" content=\"yes\"/>" if (AttrVal($name,'mobileApp','none') eq 'apple');
  1212. $webApp = "<meta name=\"mobile-web-app-capable\" content=\"yes\"/>" if (AttrVal($name,'mobileApp','none') eq 'other');
  1213. my $gen = 'generated="'.(time()-1).'"';
  1214. my $code = btIP_HTMLHead($name,$title,$viewport,$webApp,$refresh);
  1215. my $csrf= ($FW_CSRF ? "fwcsrf='$defs{$FW_wname}{CSRFTOKEN}'" : "");
  1216. $code .= "<body $csrf topmargin=\"0\" leftmargin=\"0\" margin=\"0\" padding=\"0\" ".
  1217. "$gen longpoll=\"1\" longpollfilter=\"room=all\" >\n".
  1218. "<div id=\"svg_content\" style=\"position:absolute; top:0px; left:0px; z-index:1\" >\n".
  1219. btIP_returnSVG($name)."\n</div>\n";
  1220. $code .= $defs{$name}{fhem}{div} if($defs{$name}{fhem}{div});
  1221. $code .= "</body>\n".btIP_HTMLTail();
  1222. return ("text/html; charset=utf-8", $code);
  1223. }
  1224. sub btIP_HTMLHead {
  1225. my ($name,$title,$viewport,$webApp,$refresh) = @_;
  1226. my $doctype = '<?xml version="1.0" encoding="utf-8" standalone="no"?> '."\n".
  1227. '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" '.
  1228. '"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">'."\n";
  1229. my $xmlns = "";
  1230. my $r = (defined($refresh) && $refresh) ? "<meta http-equiv=\"refresh\" content=\"$refresh\"/>" : "";
  1231. my $scripts = btIP_getScript($name);
  1232. my $meta = "<meta charset=\"UTF-8\">";
  1233. my $code = "$doctype\n<html $xmlns>\n<head>\n<title>$title</title>\n$meta\n$r\n$viewport\n$webApp\n$scripts</head>\n";
  1234. return $code;
  1235. }
  1236. sub btIP_getScript {
  1237. my ($name) = shift;
  1238. return "" if AttrVal($name,'noscript',0);
  1239. my $scripts= "";
  1240. my $jsTemplate = '<script type="text/javascript" src="%s"></script>';
  1241. if(defined($data{FWEXT})) {
  1242. foreach my $k (sort keys %{$data{FWEXT}}) {
  1243. my $h = $data{FWEXT}{$k};
  1244. next if($h !~ m/HASH/ || !$h->{SCRIPT});
  1245. my $script = $h->{SCRIPT};
  1246. $script = ($script =~ m,^/,) ? "$FW_ME$script" : "$FW_ME/pgm2/$script" unless ($script =~ m,^http,);
  1247. $scripts .= sprintf($jsTemplate, $script);
  1248. }
  1249. }
  1250. # $scripts .= sprintf($jsTemplate,"/fhem/pgm2/cordova-2.3.0.js");
  1251. # $scripts .= sprintf($jsTemplate,"/fhem/pgm2/webviewcontrol.js");
  1252. $scripts .= sprintf($jsTemplate,"/fhem/pgm2/fhemweb.js");
  1253. $scripts =~ s/script>/script>\n/g;
  1254. return $scripts;
  1255. }
  1256. sub btIP_HTMLTail {
  1257. return "</html>";
  1258. }
  1259. sub btIP_Overview {
  1260. my ($name, $url);
  1261. my $html= btIP_HTMLHead(undef, "InfoPanel Overview", undef, undef) . "<body>\n";
  1262. foreach my $def (sort keys %defs) {
  1263. if($defs{$def}{TYPE} eq "InfoPanel") {
  1264. $name= $defs{$def}{NAME};
  1265. $url= btIP_getURL();
  1266. $html.= "$name<br>\n<ul>";
  1267. $html.= "<a href='$url/btip/$name.html' target='_blank'>HTML</a><br>\n";
  1268. $html.= "</ul>\n<p>\n";
  1269. }
  1270. }
  1271. $html.="</body>\n" . btIP_HTMLTail();
  1272. return ("text/html; charset=utf-8", $html);
  1273. }
  1274. sub btIP_getURL {
  1275. my $proto = (AttrVal($FW_wname, 'HTTPS', 0) == 1) ? 'https' : 'http';
  1276. return $proto."://$FW_httpheader{Host}$FW_ME";
  1277. }
  1278. 1;
  1279. #
  1280. =pod
  1281. =item helper
  1282. =item summary create a simple status display
  1283. =item summary_DE erzeugt ein einfaches Statusdisplay
  1284. =begin html
  1285. <a name="InfoPanel"></a>
  1286. <h3>InfoPanel</h3>
  1287. <ul>
  1288. InfoPanel is an extension to <a href="#FHEMWEB">FHEMWEB</a>. You must install FHEMWEB to use InfoPanel.<br/>
  1289. <br/>
  1290. <br/>
  1291. <b>Prerequesits</b><br/>
  1292. <br/>
  1293. <ul>
  1294. <li>InfoPanel is an extension to <a href="#FHEMWEB">FHEMWEB</a>. You must install FHEMWEB to use InfoPanel.</li>
  1295. <br/>
  1296. <li>Module uses following additional Perl modules:<br/><br/>
  1297. <ul><code>MIME::Base64 Image::Info</code></ul><br/>
  1298. If not already installed in your environment, please install them using appropriate commands from your environment.<br/><br/>
  1299. Package installation in debian environments: <code>apt-get install libmime-base64-perl libimage-info-perl</code></li>
  1300. <br/>
  1301. <li>You can use this module without the two additional perl modules, but in this case, you have to accept some limitations:<br/>
  1302. <br/>
  1303. <ul>
  1304. <li>layout tag img can not be used</li>
  1305. <li>layout tag plot can only handle scale = 1 and inline = 0</li>
  1306. </ul>
  1307. </li>
  1308. </ul>
  1309. <br/><br/>
  1310. <a name="InfoPaneldefine"></a>
  1311. <b>Define</b><br/><br/>
  1312. <ul>
  1313. <code>define &lt;name&gt; InfoPanel &lt;layoutFileName&gt;</code><br/>
  1314. <br/>
  1315. Example:<br/><br>
  1316. <ul><code>define myInfoPanel InfoPanel ./FHEM/panel.layout</code><br/></ul>
  1317. </ul>
  1318. <br/><br/>
  1319. <a name="InfoPanelset"></a>
  1320. <b>Set-Commands</b><br/><br/>
  1321. <ul>
  1322. <li><code>set &lt;name&gt; reread</code>
  1323. <ul><br/>
  1324. Rereads the <a href="#InfoPanellayout">layout definition</a> from the file.<br/><br/>
  1325. <b>Important:</b><br/>
  1326. <ul>
  1327. Layout will be reread automatically if edited via fhem's "Edit files" function.<br/>
  1328. Autoread can be disabled via <a href="#InfoPanelattr">attribute</a>.
  1329. </ul>
  1330. </ul></li><br/>
  1331. <li><code>set &lt;name&gt; ovEnable &lt;xconditionName&gt;</code>
  1332. <ul><br/>
  1333. set an override "1" to named xcondition
  1334. </ul>
  1335. </li><br/>
  1336. <li><code>set &lt;name&gt; ovDisable &lt;xconditionName&gt;</code>
  1337. <ul><br/>
  1338. set an override "0" to named xcondition
  1339. </ul>
  1340. </li><br/>
  1341. <li><code>set &lt;name&gt; ovClear &lt;xconditionName&gt|all;</code>
  1342. <ul><br/>
  1343. delete an existing overrides to named xcondition. "all" will clear all overrides.<br/>
  1344. </ul>
  1345. </li>
  1346. </ul>
  1347. <br/><br/>
  1348. <a name="InfoPanelget"></a>
  1349. <b>Get-Commands</b><br/><br/>
  1350. <ul>
  1351. <li><code>get &lt;name&gt; counter</code>
  1352. <ul><br/>
  1353. return value from internal counter<br/>
  1354. </ul></li><br/>
  1355. <li><code>get &lt;name&gt; layout</code>
  1356. <ul><br/>
  1357. return complete layout definition<br/>
  1358. </ul></li><br/>
  1359. <li><code>get &lt;name&gt; overrides</code>
  1360. <ul><br/>
  1361. return list of defined overrides<br/>
  1362. </ul></li><br/>
  1363. <br/>
  1364. </ul>
  1365. <br/><br/>
  1366. <a name="InfoPanelattr"></a>
  1367. <b>Attributes</b><br/><br/>
  1368. <ul>
  1369. <li><b>autoreread</b> - disables automatic layout reread after edit if set to 1</li>
  1370. <li><b>refresh</b> - time (in seconds) after which the HTML page will be reloaded automatically.<br/>
  1371. Any values below 60 seconds will not become valid.</li>
  1372. <li><b>showTime</b> - disables generation timestamp in state if set to 0</li>
  1373. <li><b>size</b> - The dimensions of the picture in the format
  1374. <code>&lt;width&gt;x&lt;height&gt;</code></li>
  1375. <li><b>useViewPort</b> - add viewport meta tag to fit mobile displays</li>
  1376. <li><b>mobileApp</b> - add support for mobile fullscreen experience</li>
  1377. <li><b>title</b> - webpage title to be shown in Browser</li>
  1378. <br/>
  1379. <li><b>bgcenter</b> - background images will not be centered if attribute set to 0. Default: show centered</li>
  1380. <li><b>bgcolor</b> - defines the background color, use html-hexcodes to specify color, eg 00FF00 for green background. Default color is black. You can use bgcolor=none to disable use of any background color</li>
  1381. <li><b>bgdir</b> - directory containing background images</li>
  1382. <li><b>bgopacity</b> - set opacity for background image, values 0...1.0</li>
  1383. <li><b>tmin</b> - background picture will be shown at least <code>tmin</code> seconds,
  1384. no matter how frequently the RSS feed consumer accesses the page.</li>
  1385. <br/>
  1386. <b>Important:</b> bgcolor and bgdir will be evaluated by <code>{ <a href="#perl">&lt;perl special&gt;</a> }</code> use quotes for absolute values!<br/>
  1387. </ul>
  1388. <br/><br/>
  1389. <a name="InfoPanelreadings"></a>
  1390. <b>Generated Readings/Events:</b><br/><br/>
  1391. <ul>
  1392. <li>state - show time and date of last layout evaluation</li>
  1393. </ul>
  1394. <br/><br/>
  1395. <a name="InfoPanellayout"></a>
  1396. <b>Layout definition</b><br/>
  1397. <br/>
  1398. <ul>
  1399. All parameters in curly brackets can be evaluated by <code>{ <a href="#perl">&lt;perl special&gt;</a> }</code></br>
  1400. <br/>
  1401. <li><code>area &lt;id&gt; &lt;x1&gt; &lt;y1&gt; &lt;x2&gt; &lt;y2&gt; &lt;{link}&gt;</code><br/>
  1402. <br/>
  1403. <ul>create a responsive area which will call a link when clicked.<br/>
  1404. <br/>
  1405. id = element id<br/>
  1406. x1,y1 = upper left corner<br/>
  1407. x2,y2 = lower right corner<br/>
  1408. link = url to be called<br/>
  1409. </ul></li><br/>
  1410. <br>
  1411. <li><code>boxcolor &lt;{rgba}&gt;</code><br/>
  1412. <br/>
  1413. <ul>define an rgb color code to be used for filling button and textbox<br/>
  1414. </ul></li><br/>
  1415. <br>
  1416. <li><code>button &lt;id&gt; &lt;x1&gt; &lt;y1&gt; &lt;x2&gt; &lt;y2&gt; &lt;r1&gt; &lt;r2&gt; &lt;link&gt; &lt;text&gt;</code><br/>
  1417. <br/>
  1418. <ul>create a responsive colored button which will call a link when clicked.<br/>
  1419. <br/>
  1420. id = element id<br/>
  1421. x1,y1 = upper left corner<br/>
  1422. x2,y2 = lower right corner<br/>
  1423. r1,r2 = radius for rounded corners<br/>
  1424. link = url to be called<br/>
  1425. text = text that will be written onto the button<br/>
  1426. <br/>
  1427. button will be filled with color defined by "boxcolor"<br/>
  1428. text color will be read from "rgb" value<br/>
  1429. </ul></li><br/>
  1430. <br/>
  1431. <li><code>buttonpanel</code><br/>
  1432. <br/>
  1433. <ul>needed once in your layout file if you intend to use buttons in the same layout.<br/>
  1434. </ul></li><br/>
  1435. <br/>
  1436. <li><code>circle &lt;id&gt; &lt;x&gt; &lt;y&gt; &lt;r&gt; [&lt;fill&gt;] [&lt;stroke-width&gt;] [&lt;link&gt;]</code><br/>
  1437. <br/>
  1438. <ul>create a circle<br/>
  1439. <br/>
  1440. id = element id<br/>
  1441. x,y = center coordinates of circle<br/>
  1442. r = radius<br/>
  1443. fill = circle will be filled with "rgb" color if set to 1. Default = 0<br/>
  1444. stroke-width = defines stroke width to draw around the circle. Default = 0<br/>
  1445. link = URL to be linked to item<br/>
  1446. </ul></li><br/>
  1447. <br/>
  1448. <li><code>counter &lt;id&gt; &lt;x&gt; &lt;y&gt;</code><br/>
  1449. <br/>
  1450. <ul>print internal counter<br/>
  1451. <br/>
  1452. id = element id<br/>
  1453. x,y = position<br/>
  1454. </ul></li><br/>
  1455. <br/>
  1456. <li><code>date &lt;id&gt; &lt;x&gt; &lt;y&gt;</code><br/>
  1457. <br/>
  1458. <ul>print date<br/>
  1459. <br/>
  1460. id = element id<br/>
  1461. x,y = position<br/>
  1462. </ul></li><br/>
  1463. <br/>
  1464. <li><code>embed &lt;id&gt; &lt;x&gt; &lt;y&gt; &lt;width&gt; &lt;height&gt; &lt;{object}&gt;</code><br/>
  1465. <br/>
  1466. <ul>embed any object<br/>
  1467. <br/>
  1468. id = element id<br/>
  1469. x,y = position<br/>
  1470. width,height = containers's dimension<br/>
  1471. object = object to embed<br/>
  1472. </ul></li><br/>
  1473. <br/>
  1474. <li><code>ellipse &lt;id&gt; &lt;x&gt; &lt;y&gt; &lt;r1&gt; &lt;r2&gt; [&lt;fill&gt;] [&lt;stroke-width&gt;] [&lt;link&gt;]</code><br/>
  1475. <br/>
  1476. <ul>create an ellipse<br/>
  1477. <br/>
  1478. id = element id<br/>
  1479. x,y = center coordinates of ellipse<br/>
  1480. r1,r2 = radius<br/>
  1481. fill = ellipse will be filled with "rgb" color if set to 1. Default = 0<br/>
  1482. stroke-width = defines stroke width to draw around the ellipse. Default = 0<br/>
  1483. link = URL to be linked to item<br/>
  1484. </ul></li><br/>
  1485. <br/>
  1486. <li><code>font &lt;font-family&gt;</code><br/>
  1487. <br/>
  1488. <ul>define font family used for text elements (text, date, time, seconds ...)<br/>
  1489. <br/>
  1490. Example: <code>font arial</code><br/>
  1491. </ul></li><br/>
  1492. <br/>
  1493. <li><code>group &lt;id&gt; open &lt;x&gt; &lt;y&gt;<br/>
  1494. group - close</code>&nbsp;&nbsp;(id will not be evaluated, just give any value)<br/>
  1495. <br/>
  1496. <ul>group items<br/>
  1497. <br/>
  1498. open|close = define start and end of group<br/>
  1499. x,y = upper left corner as reference for all grouped items, will be inherited to all elements.<br/>
  1500. <br/>
  1501. Examples:<br/>
  1502. <code>
  1503. group - open 150 150<br/>
  1504. rect ...<br/>
  1505. img ...<br/>
  1506. group - close<br/>
  1507. </code>
  1508. </ul></li><br/>
  1509. <br/>
  1510. <li><code>img &lt;id&gt; &lt;x&gt; &lt;y&gt; &lt;scale&gt; &lt;link&gt; &lt;sourceType&gt; &lt;{dataSource}&gt;s</code><br/>
  1511. <br/>
  1512. <ul>embed an image into InfoPanel<br/>
  1513. <br/>
  1514. id = element id<br/>
  1515. x,y = upper left corner<br/>
  1516. scale = scale to be used for resizing; may be factor or defined by width or height<br/>
  1517. link = URL to be linked to item, use "" if not needed<br/>
  1518. sourceType = file | url | data<br/>
  1519. dataSource = where to read data from, depends on sourceType<br/>
  1520. </ul></li><br/>
  1521. <br/>
  1522. <li><code>line &lt;id&gt; &lt;x1&gt; &lt;y1&gt; &lt;x2&gt; &lt;y2&gt; [&lt;stroke&gt;]</code><br/>
  1523. <br/>
  1524. <ul>draw a line<br/>
  1525. <br/>
  1526. id = element id<br/>
  1527. x1,y1 = coordinates (start)<br/>
  1528. x2,y2 = coordinates (end)<br/>
  1529. stroke = stroke width for line; if omitted, default = 0<br/>
  1530. </ul></li><br/>
  1531. <br/>
  1532. <li><code>moveby &lt;x&gt; &lt;y&gt;</code><br/>
  1533. <br/>
  1534. <ul>move most recently x- and y-coordinates by given steps<br/>
  1535. </ul></li><br/>
  1536. <br/>
  1537. <li><code>movecalculated &lt;{perlSpecial x}&gt; &lt;{perlSpecial y}&gt;</code><br/>
  1538. <br/>
  1539. <ul>calculate x- and y-coordinates by perlSpecials<br/>
  1540. </ul></li><br/>
  1541. <br/>
  1542. <li><code>moveto &lt;x&gt; &lt;y&gt;</code><br/>
  1543. <br/>
  1544. <ul>move x- and y-coordinates to the given positon<br/>
  1545. </ul></li><br/>
  1546. <br/>
  1547. <li><code>padding &lt;width&gt;</code><br/>
  1548. <br/>
  1549. <ul>border width (in pixel) to be used in textboxes<br/>
  1550. </ul></li><br/>
  1551. <br/>
  1552. <li><code>plot &lt;id&gt; &lt;x&gt; &lt;y&gt; &lt;scale&gt; &lt;inline&gt; &lt;{plotName}&gt;</code><br/>
  1553. <br/>
  1554. <ul>embed an SVG plot into InfoPanel<br/>
  1555. <br/>
  1556. id = element id<br/>
  1557. x,y = upper left corner<br/>
  1558. scale = scale to be used for resizing; may be factor or defined by width or height<br/>
  1559. inline = embed plot as data (inline=1) or as link (inline=0)<br/>
  1560. plotName = name of desired SVG device from your fhem installation<br/>
  1561. </ul></li><br/>
  1562. <br/>
  1563. <li><code>pop</code><br/>
  1564. <br/>
  1565. <ul>fetch last parameter set from stack and set it actice<br/>
  1566. </ul></li><br/>
  1567. <br/>
  1568. <li><code>pt &lt;[+-]font-size&gt;</code><br/>
  1569. <br/>
  1570. <ul>define font size used for text elements (text, date, time, seconds ...)<br/>
  1571. can be given as absolute or relative value.<br/>
  1572. <br/>
  1573. Examples:<br/>
  1574. <code>pt 12</code><br/>
  1575. <code>pt +3</code><br/>
  1576. <code>pt -2</code><br/>
  1577. </ul></li><br/>
  1578. <br/>
  1579. <li><code>push</code><br/>
  1580. <br/>
  1581. <ul>push active parameter set onto stack<br/>
  1582. </ul></li><br/>
  1583. <br/>
  1584. <li><code>rect &lt;id&gt; &lt;x1&gt; &lt;y1&gt; &lt;x2&gt; &lt;y2&gt; &lt;r1&gt; &lt;r2&gt; [&lt;fill&gt;] [&lt;stroke-width&gt;] [&lt;link&gt;]</code><br/>
  1585. <br/>
  1586. <ul>create a rectangle<br/>
  1587. <br/>
  1588. id = element id<br/>
  1589. x1,y1 = upper left corner<br/>
  1590. x2,y2 = lower right corner<br/>
  1591. r1,r2 = radius for rounded corners<br/>
  1592. fill = rectangle will be filled with "rgb" color if set to 1. Default = 0<br/>
  1593. stroke-width = defines stroke width to draw around the rectangle. Default = 0<br/>
  1594. link = URL to be linked to item<br/>
  1595. </ul></li><br/>
  1596. <br/>
  1597. <li><code>rgb &lt;{rgb[a]}&gt;</code><br/>
  1598. <br/>
  1599. <ul>define rgba value (hex digits!) used for text, lines, circles, ellipses<br/>
  1600. <br/>
  1601. <code>r = red value</code><br/>
  1602. <code>g = green value</code><br/>
  1603. <code>b = blue value</code><br/>
  1604. <code>a = alpha value used for opacity; optional</code><br/>
  1605. </ul></li><br/>
  1606. <br/>
  1607. <li><code>seconds &lt;id&gt; &lt;x&gt; &lt;y&gt; [&lt;format&gt;]</code><br/>
  1608. <br/>
  1609. <ul>print seconds<br/>
  1610. <br/>
  1611. id = element id<br/>
  1612. x,y = position<br/>
  1613. format = seconds will be preceeded by ':' if set to 'colon'; optional<br/>
  1614. </ul></li><br/>
  1615. <br/>
  1616. <li><code>text &lt;id&gt; &lt;x&gt; &lt;y&gt; &lt;{text}&gt;</code><br/>
  1617. <br/>
  1618. <ul>print text<br/>
  1619. <br/>
  1620. id = element id<br/>
  1621. x,y = position<br/>
  1622. text = text content to be printed<br/>
  1623. </ul></li><br/>
  1624. <br/>
  1625. <li><code>textbox &lt;id&gt; &lt;x&gt; &lt;y&gt; &lt;boxWidth&gt; &lt;boxHeight&gt; &lt;{link}&gt; &lt;{text}&gt; </code><br/>
  1626. <br/>
  1627. <ul>create a textbox to print text with auto wrapping<br/>
  1628. <br/>
  1629. id = element id<br/>
  1630. x,y = upper left corner<br/>
  1631. boxWidth,boxHeight = dimensions of textbox<br/>
  1632. link = url to be used when clicked; use "" if not needed<br/>
  1633. text = text to be printed in textbox<br/>
  1634. <br/>
  1635. <b>Important:</b> textboxes are not responsive via area tag. Use optional link parameter in textbox tag<br/>
  1636. </ul></li><br/>
  1637. <br/>
  1638. <li><code>textboxalign &lt;align&gt;</code><br/>
  1639. <br/>
  1640. <ul>define horizontal alignment for text inside textboxes<br/>
  1641. <br/>
  1642. valid values: left center right justify<br/>
  1643. </ul></li><br/>
  1644. <br/>
  1645. <li><code>textdesign &lt;align&gt;</code><br/>
  1646. <br/>
  1647. <ul>define comma-separated list for text design and decoration<br/>
  1648. <br/>
  1649. valid values: underline overline line-through bold italic oblique clear<br/>
  1650. <br/>
  1651. Examples:<br/>
  1652. <code>
  1653. textdesign underline<br/>
  1654. textdesign bold,italic,underline
  1655. </code><br/>
  1656. <br/>
  1657. <b>Important:</b> "clear" resets all to default values!<br/>
  1658. </ul></li><br/>
  1659. <br/>
  1660. <li><code>thalign &lt;align&gt;</code><br/>
  1661. <br/>
  1662. <ul>define horizontal alignment for text output<br/>
  1663. <br/>
  1664. valid values: start middle end<br/>
  1665. </ul></li><br/>
  1666. <br/>
  1667. <li><code>ticker &lt;id&gt; &lt;x&gt; &lt;y&gt; &lt;width&gt; &lt;items&gt; &lt;speed&gt; &lt;{data}&gt;</code><br/>
  1668. <br/>
  1669. <ul>create a vertical ticker<br/>
  1670. <br/>
  1671. id = element id<br/>
  1672. x,y = position<br/>
  1673. width = width<br/>
  1674. items = number of items to be displayed simultanously<br/>
  1675. speed = scroll speed<br/>
  1676. data = list of text items, separated by <code>\n</code><br/>
  1677. </ul></li><br/>
  1678. <br/>
  1679. <li><code>time &lt;id&gt; &lt;x&gt; &lt;y&gt;</code><br/>
  1680. <br/>
  1681. <ul>print time<br/>
  1682. <br/>
  1683. id = element id<br/>
  1684. x,y = position<br/>
  1685. </ul></li><br/>
  1686. <br/>
  1687. <li><code>tvalign &lt;align&gt;</code><br/>
  1688. <br/>
  1689. <ul>define vertical alignment for text output<br/>
  1690. <br/>
  1691. valid values: auto baseline middle center hanging<br/>
  1692. </ul></li><br/>
  1693. <br/>
  1694. </ul>
  1695. <br/>
  1696. <b>Author's notes</b><br/>
  1697. <br/>
  1698. <ul>
  1699. <li>Have fun!</li><br/>
  1700. </ul>
  1701. </ul>
  1702. =end html
  1703. =begin html_DE
  1704. <a name="InfoPanel"></a>
  1705. <h3>InfoPanel</h3>
  1706. <ul>
  1707. Sorry, keine deutsche Dokumentation vorhanden.<br/><br/>
  1708. Die englische Doku gibt es hier: <a href='commandref.html#InfoPanel'>InfoPanel</a><br/>
  1709. </ul>
  1710. =end html_DE
  1711. =cut