55_InfoPanel.pm 64 KB

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