55_InfoPanel.pm 64 KB

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