| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455 |
- #
- #
- # 02_RSS.pm
- # written by Dr. Boris Neubert 2012-03-24
- # e-mail: omega at online dot de
- #
- ##############################################
- # $Id: 02_RSS.pm 16812 2018-06-03 19:52:27Z neubert $
- package main;
- use strict;
- use warnings;
- use GD;
- use feature qw/switch/;
- use vars qw(%data);
- use HttpUtils;
- #require "98_SVG.pm"; # enable use of plotAsPng()
- sub plotAsPng(@); # forward declaration will be enough
- # to ensure correct function
- # and will avoid reloading 98_SVG.pm
- # during fhem startup/rereadcfg
- my @cmd_halign = qw(halign thalign ihalign);
- my @cmd_valign = qw(valign tvalign ivalign);
- my @valid_valign = qw(top center base bottom);
- my @valid_halign = qw(left center right justified);
- # we can
- # use vars qw(%FW_types); # device types,
- # use vars qw($FW_RET); # Returned data (html)
- # use vars qw($FW_wname); # Web instance
- # use vars qw($FW_subdir); # Sub-path in URL for extensions, e.g. 95_FLOORPLAN
- # use vars qw(%FW_pos); # scroll position
- # use vars qw($FW_cname); # Current connection name
- # http://blogs.perl.org/users/mike_b/2013/06/a-little-nicer-way-to-use-smartmatch-on-perl-518.html
- #no if $] >= 5.017011, warnings => 'experimental::smartmatch';
- no if $] >= 5.017011, warnings => 'experimental';
- #########################
- sub RSS_addExtension($$$) {
- my ( $func, $link, $friendlyname ) = @_;
- my $url = "/" . $link;
- $data{FWEXT}{$url}{FUNC} = $func;
- $data{FWEXT}{$url}{LINK} = "+$link";
- $data{FWEXT}{$url}{NAME} = $friendlyname;
- $data{FWEXT}{$url}{SCRIPT} = "RSS.js";
- $data{FWEXT}{$url}{FORKABLE} = 0;
- }
- ##################
- sub RSS_Initialize($) {
- my ($hash) = @_;
- $hash->{DefFn} = "RSS_Define";
- $hash->{UndefFn} = "RSS_Undefine";
- #$hash->{AttrFn} = "RSS_Attr";
- $hash->{AttrList} =
- "size itemtitle bg bgcolor tmin refresh areas autoreread:1,0 viewport noscroll urlOverride";
- $hash->{SetFn} = "RSS_Set";
- $hash->{NotifyFn} = "RSS_Notify";
- return undef;
- }
- ##################
- sub RSS_readLayout($) {
- my ($hash) = @_;
- my $filename = $hash->{fhem}{filename};
- my $name = $hash->{NAME};
- my ( $err, @layoutfile ) = FileRead($filename);
- if ($err) {
- Log 1, "RSS $name: $err";
- $hash->{fhem}{layout} = ("text 0.1 0.1 'Error: $err'");
- }
- else {
- $hash->{fhem}{layout} = join( "\n", @layoutfile );
- $hash->{fhem}{layout} =~ s/\n\n/\n/g;
- }
- return;
- }
- ##################
- sub RSS_Define($$) {
- my ( $hash, $def ) = @_;
- my @a = split( "[ \t]+", $def );
- return "Usage: define <name> RSS jpg|png hostname filename"
- if ( int(@a) != 5 );
- my $name = $a[0];
- my $style = $a[2];
- my $hostname = $a[3];
- my $filename = $a[4];
- $hash->{fhem}{style} = $style;
- $hash->{fhem}{hostname} = $hostname;
- $hash->{fhem}{filename} = $filename;
- $hash->{LAYOUTFILE} = $filename;
- $hash->{NOTIFYDEV} = 'global';
- RSS_addExtension( "RSS_CGI", "rss", "RSS" );
- eval "use GD::Text::Align";
- $hash->{fhem}{useTextAlign} = ( $@ ? 0 : 1 );
- if ( !( $hash->{fhem}{useTextAlign} ) ) {
- Log3 $hash, 2, "$name: Cannot use text alignment: $@";
- }
- eval "use GD::Text::Wrap";
- $hash->{fhem}{useTextWrap} = ( $@ ? 0 : 1 );
- if ( !( $hash->{fhem}{useTextWrap} ) ) {
- Log3 $hash, 2, "$name: Cannot use text wrapping: $@";
- }
- RSS_readLayout($hash);
- $hash->{STATE} = 'defined'; #$name;
- return undef;
- }
- sub RSS_Undefine($$) {
- my ( $hash, $arg ) = @_;
- # check if last device
- my $url = '/rss';
- $data{FWEXT}{$url} = undef if int( devspec2array('TYPE=RSS') ) == 1;
- return undef;
- }
- sub RSS_Notify {
- my ( $hash, $dev ) = @_;
- my $name = $hash->{NAME};
- return unless AttrVal( $name, 'autoreread', 1 );
- return if ( $dev->{NAME} ne "global" );
- return
- if ( !grep( m/^FILEWRITE $hash->{LAYOUTFILE}$/, @{ $dev->{CHANGED} } ) );
- Log3( undef, 4, "RSS: $name reread layout after edit." );
- RSS_readLayout($hash);
- return undef;
- }
- ##################
- sub RSS_Set() {
- my ( $hash, @a ) = @_;
- my $name = $a[0];
- # usage check
- my $usage = "Unknown argument, choose one of rereadcfg:noArg";
- if ( ( @a == 2 ) && ( $a[1] eq "rereadcfg" ) ) {
- RSS_readLayout($hash);
- return undef;
- }
- else {
- return $usage;
- }
- }
- ####################
- #
- sub RSS_getURL($) {
- my ($name) = @_;
- my $url = AttrVal( $name, 'urlOverride', '' );
- return $url if ( $url ne "" );
- my $hostname = $defs{$name}{fhem}{hostname};
- # http://hostname:8083/fhem
- my $proto = ( AttrVal( $FW_wname, 'HTTPS', 0 ) == 1 ) ? 'https' : 'http';
- return $proto . "://$hostname:" . $defs{$FW_wname}{PORT} . $FW_ME;
- }
- # ##################
- # sub
- # RSS_Attr(@)
- # {
- # my @a = @_;
- # my $attr= $a[2];
- #
- # if($a[0] eq "set") { # set attribute
- # if($attr eq "bgdir") {
- # }
- # }
- # elsif($a[0] eq "del") { # delete attribute
- # if($attr eq "bgdir") {
- # }
- # }
- #
- # return undef;
- #
- # }
- ##################
- # list all RSS devices
- sub RSS_Overview {
- my ( $name, $url );
- my $html = RSS_HTMLHead( "RSS Overview", undef ) . "<body>\n";
- foreach my $def ( sort keys %defs ) {
- if ( $defs{$def}{TYPE} eq "RSS" ) {
- $name = $defs{$def}{NAME};
- $url = RSS_getURL($name);
- $html .= "$name<br>\n<ul>";
- $html .=
- "<a href='$url/rss/$name.rss' target='_blank' >RSS</a><br>\n";
- $html .=
- "<a href='$url/rss/$name.html' target='_blank' >HTML</a><br>\n";
- $html .=
- "<a href='$url/rss/$name.png' target='_blank' >Portable Network Graphics</a><br>\n";
- $html .=
- "<a href='$url/rss/$name.jpg' target='_blank' >JPEG Graphics</a><br>\n";
- $html .= "</ul>\n<p>\n";
- }
- }
- $html .= "</body>\n" . RSS_HTMLTail();
- return ( "text/html; charset=utf-8", $html );
- }
- ##################
- sub RSS_splitRequest($) {
- # http://hostname:8083/fhem/rss
- # http://hostname:8083/fhem/rss/myDeviceName.rss
- # http://hostname:8083/fhem/rss/myDeviceName.jpg?t=47110.815
- # |--------- url ----------| |---name --| ext |--query--|
- my ($request) = @_;
- # http://hostname:8083/fhem/rss/myDeviceName.rss
- # http://hostname:8083/fhem/rss/myDeviceName.jpg
- # http://hostname:8083/fhem/rss/myDeviceName.png
- # http://hostname:8083/fhem/rss/myDeviceName.html
- use constant REGEXP => '^.*\/rss\/([^\/]*)\.(jpg|png|rss|html)(\?(.*))?$';
- if ( $request =~ REGEXP ) {
- return ( $1, $2, $4 );
- }
- else {
- #main::Debug "not matched";
- return ( undef, undef, undef );
- }
- }
- ##################
- sub RSS_returnRSS($) {
- my ($name) = @_;
- my $url = RSS_getURL($name);
- my $type = $defs{$name}{fhem}{style};
- my $mime = ( $type eq 'png' ) ? 'image/png' : 'image/jpeg';
- my $now = time();
- my $itemTitle = AttrVal( $name, "itemtitle", "" );
- my $titleTag =
- ( $itemTitle ne '' ) ? '<title>' . $itemTitle . '</title>' : '';
- my $code =
- "<rss version='2.0' xmlns:media='http://search.yahoo.com/mrss/'><channel><title>$name</title><ttl>1</ttl><item>$titleTag<media:content url='$url/rss/$name.$type' type='$mime'/><guid isPermaLink='false'>item_$now</guid></item></channel></rss>";
- return ( "application/xml; charset=utf-8", $code );
- }
- ##################
- sub RSS_getScript() {
- my $jsTemplate = '<script type="text/javascript" src="%s"></script>';
- my $scripts = "";
- if ( defined( $data{FWEXT} ) ) {
- foreach my $k ( sort keys %{ $data{FWEXT} } ) {
- my $h = $data{FWEXT}{$k};
- next if ( $h !~ m/HASH/ || !$h->{SCRIPT} );
- my $script = $h->{SCRIPT};
- $script =
- ( $script =~ m,^/, ) ? "$FW_ME$script" : "$FW_ME/pgm2/$script";
- $scripts .= sprintf( $jsTemplate, $script ) . "\n";
- }
- }
- return $scripts;
- }
- sub RSS_HTMLHead($$) {
- my ( $name, $refresh ) = @_;
- my ( $width, $height ) = split( /x/, AttrVal( $name, "size", "800x600" ) );
- my $viewportContent = AttrVal( $name, "viewport", "" );
- my $doctype =
- '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
- my $xmlns = 'xmlns="http://www.w3.org/1999/xhtml"';
- my $scripts = RSS_getScript();
- my $viewport =
- $viewportContent eq ""
- ? ""
- : "<meta name=\"viewport\" content=\"$viewportContent\"/>\n";
- my $code =
- "$doctype\n<html $xmlns>\n<head>\n<title>$name</title>\n$viewport$scripts</head>\n";
- }
- sub RSS_HTMLTail() {
- return "</html>";
- }
- sub RSS_returnHTML($) {
- my ($name) = @_;
- my $url = RSS_getURL($name);
- my $type = $defs{$name}{fhem}{style};
- my $img = "$url/rss/$name.$type";
- my $refresh = AttrVal( $name, 'refresh', 60 );
- my $noscroll = AttrVal( $name, 'noscroll', 0 );
- my $overflow = $noscroll ? " style=\"overflow:hidden\"" : "";
- my $areas = AttrVal( $name, 'areas', "" );
- my $embed = $defs{$name}{".embed"};
- my $r = "";
- if ( defined($refresh) && ( $refresh > 0 ) ) {
- my $handler =
- "\"setTimeout(function(){reloadImage(\'img0\')},$refresh*1000);\"";
- $r = " onload=$handler onerror=$handler";
- }
- my $code =
- RSS_HTMLHead( $name, $refresh )
- . "<body topmargin=\"0\" leftmargin=\"0\" margin=\"0\" padding=\"0\"$overflow>\n"
- . "<div id=\"rss_$name\" style=\"z-index:1;\" >\n"
- . "<img id=\"img0\" src=\"$img\" usemap=\"#map\"$r/>\n"
- . "<map name=\"map\" id=\"map\">\n$areas\n</map>\n"
- . "</div>\n"
- . "$embed\n"
- . "</body>\n"
- . RSS_HTMLTail();
- return ( "text/html; charset=utf-8", $code );
- }
- ##################
- # Library
- ##################
- sub RSS_xy {
- my ( $S, $x, $y, %params ) = @_;
- $x = $params{x} if ( $x eq 'x' );
- $y = $params{y} if ( $y eq 'y' );
- if ( ( -1 < $x ) && ( $x < 1 ) ) { $x *= $S->width; }
- if ( ( -1 < $y ) && ( $y < 1 ) ) { $y *= $S->height; }
- return ( $x, $y );
- }
- sub RSS_color {
- my ( $S, $rgb ) = @_;
- my $alpha = 0;
- my @d = split( "", $rgb );
- if ( length($rgb) == 8 ) {
- $alpha = hex("$d[6]$d[7]");
- $alpha = ( $alpha < 127 ) ? $alpha : 127;
- }
- return $S->colorAllocateAlpha( hex("$d[0]$d[1]"), hex("$d[2]$d[3]"),
- hex("$d[4]$d[5]"), $alpha );
- }
- sub RSS_itemText {
- my ( $S, $x, $y, $text, %params ) = @_;
- return unless ( defined($text) );
- if ( $params{useTextAlign} ) {
- my $align = GD::Text::Align->new(
- $S,
- color => RSS_color( $S, $params{rgb} ),
- valign => $params{tvalign},
- halign => $params{thalign},
- );
- $align->set_font( $params{font}, $params{pt} );
- $align->set_text($text);
- $align->draw( $x, $y, 0 );
- }
- else {
- $S->stringFT( RSS_color( $S, $params{rgb} ),
- $params{font}, $params{pt}, 0, $x, $y, $text );
- }
- }
- sub RSS_itemTextBox {
- my ( $S, $x, $y, $boxwidth, $bgcolor, $text, %params ) = @_;
- return unless ( defined($text) );
- if ( $params{useTextWrap} ) {
- if ( ( 0 < $boxwidth ) && ( $boxwidth < 1 ) ) {
- $boxwidth *= $S->width;
- }
- my $wrapbox = GD::Text::Wrap->new(
- $S,
- color => RSS_color( $S, $params{rgb} ),
- line_space => $params{linespace},
- text => $text,
- );
- $wrapbox->set_font( $params{font}, $params{pt} );
- $wrapbox->set( align => $params{thalign}, width => $boxwidth );
- my ( $left, $top, $right, $bottom );
- ( $left, $top, $right, $bottom ) = $wrapbox->get_bounds( $x, $y );
- $S->filledRectangle( $left, $top, $right, $bottom,
- RSS_color( $S, $bgcolor ) )
- if ( defined($bgcolor) );
- ( $left, $top, $right, $bottom ) = $wrapbox->draw( $x, $y );
- return $bottom;
- }
- else {
- RSS_itemText( $S, $x, $y, $text, %params );
- return $y;
- }
- }
- sub RSS_itemTime {
- my ( $S, $x, $y, %params ) = @_;
- my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
- localtime(time);
- RSS_itemText( $S, $x, $y, sprintf( "%02d:%02d", $hour, $min ), %params );
- }
- sub RSS_itemSeconds {
- my ( $S, $x, $y, $format, %params ) = @_;
- my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
- localtime(time);
- if ( $format eq "colon" ) {
- RSS_itemText( $S, $x, $y, sprintf( ":%02d", $sec ), %params );
- }
- else {
- RSS_itemText( $S, $x, $y, sprintf( "%02d", $sec ), %params );
- }
- }
- sub RSS_itemDate {
- my ( $S, $x, $y, %params ) = @_;
- my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
- localtime(time);
- RSS_itemText( $S, $x, $y,
- sprintf( "%02d.%02d.%04d", $mday, $mon + 1, $year + 1900 ), %params );
- }
- sub RSS_itemImg {
- my ( $S, $x, $y, $scale, $imgtype, $srctype, $arg, %params ) = @_;
- return unless ( defined($arg) );
- return if ( $arg eq "" );
- my $I;
- if ( $srctype eq "url" || $srctype eq "urlq" ) {
- my $data;
- if ( $srctype eq "url" ) {
- $data = GetFileFromURL( $arg, 3, undef, 1 );
- }
- else {
- $data = GetFileFromURLQuiet( $arg, 3, undef, 1 );
- }
- if ( $imgtype eq "gif" ) {
- $I = GD::Image->newFromGifData($data);
- }
- elsif ( $imgtype eq "png" ) {
- $I = GD::Image->newFromPngData($data);
- }
- elsif ( $imgtype eq "jpeg" ) {
- $I = GD::Image->newFromJpegData($data);
- }
- else {
- return;
- }
- }
- elsif ( $srctype eq "file" ) {
- if ( $imgtype eq "gif" ) {
- $I = GD::Image->newFromGif($arg);
- }
- elsif ( $imgtype eq "png" ) {
- $I = GD::Image->newFromPng($arg);
- }
- elsif ( $imgtype eq "jpeg" ) {
- $I = GD::Image->newFromJpeg($arg);
- }
- else {
- return;
- }
- }
- elsif ( $srctype eq "data" ) {
- if ( $imgtype eq "gif" ) {
- $I = GD::Image->newFromGifData($arg);
- }
- elsif ( $imgtype eq "png" ) {
- $I = GD::Image->newFromPngData($arg);
- }
- elsif ( $imgtype eq "jpeg" ) {
- $I = GD::Image->newFromJpegData($arg);
- }
- else {
- return;
- }
- }
- else {
- return;
- }
- eval {
- my ( $width, $height ) = $I->getBounds();
- if ( $scale =~ s/([wh])([\d]*)/$2/ )
- { # get the digit from width/hight to pixel entry
- #Debug "RSS scale $scale (1: $1 / 2: $2)contais px after Digit - width: $width / height: $height";
- if ( $1 eq "w" ) {
- $scale = $scale / $width;
- }
- else {
- $scale = $scale / $height;
- }
- }
- my ( $swidth, $sheight ) =
- ( int( $scale * $width ), int( $scale * $height ) );
- given ( $params{ihalign} ) {
- when ('center') { $x -= $swidth / 2; }
- when ('right') { $x -= $swidth; }
- default {
- } # nothing to do
- }
- given ( $params{ivalign} ) {
- when ('center') { $y -= $sheight / 2; }
- when ('base') { $y -= $sheight; }
- when ('bottom') { $y -= $sheight; }
- default {
- } # nothing to do
- }
- #Debug "RSS placing $arg ($swidth x $sheight) at ($x,$y)";
- $S->copyResampled( $I, $x, $y, 0, 0, $swidth, $sheight, $width,
- $height );
- };
- if ($@) {
- Log3 undef, 2, "RSS: cannot create image $srctype $imgtype '$arg': $@";
- }
- }
- sub RSS_itemLine {
- my ( $S, $x1, $y1, $x2, $y2, $th, %params ) = @_;
- $S->setThickness($th);
- $S->line( $x1, $y1, $x2, $y2, RSS_color( $S, $params{rgb} ) );
- }
- sub RSS_itemRect {
- my ( $S, $x1, $y1, $x2, $y2, $filled, %params ) = @_;
- $x2 = $x1 + $x2 if ( $x2 =~ /^\+/ );
- $y2 = $y1 + $y2 if ( $y2 =~ /^\+/ );
- if ($filled) {
- $S->filledRectangle( $x1, $y1, $x2, $y2,
- RSS_color( $S, $params{rgb} ) );
- }
- else {
- $S->rectangle( $x1, $y1, $x2, $y2, RSS_color( $S, $params{rgb} ) );
- }
- }
- ##################
- sub RSS_cleanLayout($) {
- my ($input) = @_;
- my @output;
- my $cont = "";
- foreach my $line ( split( "\n", $input ) ) {
- # kill trailing newline
- chomp $line;
- # kill comments and blank lines
- $line =~ s/\#.*$//;
- $line =~ s/\s+$//;
- $line = $cont . $line;
- if ( $line =~ s/\\$// ) { $cont = $line; undef $line; }
- next unless ($line);
- $cont = "";
- push @output, $line;
- }
- return @output;
- }
- sub RSS_analyzePerl($) {
- my ($expr) = @_;
- return AnalyzePerlCommand( "", $expr, 1 ); # specials have been previously stored
- }
- sub RSS_evalLayout($$@) {
- my ( $S, $name, $layout ) = @_;
- my @layout = RSS_cleanLayout($layout);
- my $lineNr;
- my %labels;
- my %count;
- # first pass
- # collect labels
- for ( $lineNr = 0 ; $lineNr <= $#layout ; $lineNr++ ) {
- if ( $layout[$lineNr] =~ /^label\s+(.+)$/ ) {
- my $label = $1;
- if ( exists( $labels{$label} ) ) {
- Log3 $name, 2, "$name: label $label redefined.";
- }
- else {
- $labels{$label} = $lineNr;
- $count{ '$' . $label } = 0;
- #Debug "defined label $label";
- }
- }
- }
- EvalSpecials( "", %count );
- # second pass
- # create actual layout
- my %pstack;
- my $pstackcount = 0;
- my %params;
- $params{font} = "Arial";
- $params{pt} = 12;
- $params{rgb} = "ffffff";
- $params{halign} = 'left';
- $params{valign} = 'base';
- $params{condition} = 1;
- # we need two pairs of align parameters
- # due to different default values for text and img
- $params{useTextAlign} = $defs{$name}{fhem}{useTextAlign};
- $params{useTextWrap} = $defs{$name}{fhem}{useTextWrap};
- $params{ihalign} = 'left';
- $params{ivalign} = 'top';
- $params{thalign} = 'left';
- $params{tvalign} = 'base';
- $params{linespace} = 0;
- $params{x} = 0;
- $params{y} = 0;
- $defs{$name}{".embed"} = "";
- my (
- $x, $y, $z, $x1, $y1,
- $x2, $y2, $scale, $bgcolor, $boxwidth,
- $text, $imgtype, $srctype, $arg, $format
- );
- for ( $lineNr = 0 ; $lineNr <= $#layout ; $lineNr++ ) {
- my $line = $layout[$lineNr];
- #Debug "Line $lineNr: $line";
- #Debug "$name: evaluating >$line<";
- # split line into command and definition
- my ( $cmd, $def ) = split( "[ \t]+", $line, 2 );
- #Debug "CMD= \"$cmd\", DEF= \"$def\"";
- # separate condition handling
- if ( $cmd eq 'condition' ) {
- $params{condition} = RSS_analyzePerl($def);
- #Debug "condition $def evaluated to " . $params{condition};
- next;
- }
- next unless ( $params{condition} );
- #Debug "before command $line: x= " . $params{x} . ", y= " . $params{y};
- eval {
- if ( $cmd eq "rgb" ) {
- $def = "\"$def\""
- if ( length($def) == 6 && $def =~ /[[:xdigit:]]{6}/ );
- $params{rgb} = RSS_analyzePerl($def);
- }
- elsif ( $cmd eq "font" ) {
- $params{font} = $def;
- }
- elsif ( $cmd eq "pt" ) {
- $def = RSS_analyzePerl($def);
- if ( $def =~ m/^[+-]/ ) {
- $params{pt} += $def;
- }
- else {
- $params{pt} = $def;
- }
- $params{pt} = 6 if ( $params{pt} < 0 );
- }
- elsif ( $cmd eq "moveto" ) {
- my ( $tox, $toy ) = split( '[ \t]+', $def, 2 );
- my ( $x, $y ) = RSS_xy( $S, $tox, $toy, %params );
- $params{x} = $x;
- $params{y} = $y;
- }
- elsif ( $cmd eq "moveby" ) {
- my ( $byx, $byy ) = split( '[ \t]+', $def, 2 );
- my ( $x, $y ) = RSS_xy( $S, $byx, $byy, %params );
- $params{x} += $x;
- $params{y} += $y;
- }
- elsif ( $cmd ~~ @cmd_halign ) {
- my $d = RSS_analyzePerl($def);
- if ( $d ~~ @valid_halign ) {
- $params{ihalign} = $d unless ( $cmd eq "thalign" );
- $params{thalign} = $d unless ( $cmd eq "ihalign" );
- }
- else {
- Log3 $name, 2, "$name: Illegal horizontal alignment $d";
- }
- }
- elsif ( $cmd ~~ @cmd_valign ) {
- my $d = RSS_analyzePerl($def);
- if ( $d ~~ @valid_valign ) {
- $params{ivalign} = $d unless ( $cmd eq "tvalign" );
- $params{tvalign} = $d unless ( $cmd eq "ivalign" );
- }
- else {
- Log3 $name, 2, "$name: Illegal vertical alignment $d";
- }
- }
- elsif ( $cmd eq "linespace" ) {
- $params{linespace} = $def;
- }
- elsif ( $cmd eq "text" ) {
- ( $x, $y, $text ) = split( "[ \t]+", $def, 3 );
- ( $x, $y ) = RSS_xy( $S, $x, $y, %params );
- $params{x} = $x;
- $params{y} = $y;
- my $txt = RSS_analyzePerl($text);
- #Debug "$name: ($x,$y) $txt";
- RSS_itemText( $S, $x, $y, $txt, %params );
- }
- elsif ( $cmd eq "textbox" ) {
- ( $x, $y, $boxwidth, $text ) = split( "[ \t]+", $def, 4 );
- ( $x, $y ) = RSS_xy( $S, $x, $y, %params );
- my $txt = RSS_analyzePerl($text);
- $y = RSS_itemTextBox( $S, $x, $y, $boxwidth, undef, $txt,
- %params );
- $params{x} = $x;
- $params{y} = $y;
- }
- elsif ( $cmd eq "textboxf" ) {
- ( $x, $y, $boxwidth, $bgcolor, $text ) =
- split( "[ \t]+", $def, 5 );
- ( $x, $y ) = RSS_xy( $S, $x, $y, %params );
- $bgcolor =
- ( $bgcolor ne "" )
- ? RSS_analyzePerl($bgcolor)
- : undef;
- my $txt = RSS_analyzePerl($text);
- $y = RSS_itemTextBox( $S, $x, $y, $boxwidth, $bgcolor, $txt,
- %params );
- $params{x} = $x;
- $params{y} = $y;
- }
- elsif ( $cmd eq "line" ) {
- ( $x1, $y1, $x2, $y2, $format ) =
- split( "[ \t]+", $def, 5 );
- ( $x1, $y1 ) = RSS_xy( $S, $x1, $y1, %params );
- ( $x2, $y2 ) = RSS_xy( $S, $x2, $y2, %params );
- $format //=
- 1; # set format to 1 as default thickness for the line
- RSS_itemLine( $S, $x1, $y1, $x2, $y2, $format, %params );
- }
- elsif ( $cmd eq "rect" ) {
- ( $x1, $y1, $x2, $y2, $format ) =
- split( "[ \t]+", $def, 5 );
- ( $x1, $y1 ) = RSS_xy( $S, $x1, $y1, %params );
- ( $x2, $y2 ) = RSS_xy( $S, $x2, $y2, %params );
- $format //= 0; # set format to 0 as default (not filled)
- RSS_itemRect( $S, $x1, $y1, $x2, $y2, $format, %params );
- }
- elsif ( $cmd eq "time" ) {
- ( $x, $y ) = split( "[ \t]+", $def, 2 );
- ( $x, $y ) = RSS_xy( $S, $x, $y, %params );
- $params{x} = $x;
- $params{y} = $y;
- RSS_itemTime( $S, $x, $y, %params );
- }
- elsif ( $cmd eq "seconds" ) {
- ( $x, $y, $format ) = split( "[ \+]", $def, 3 );
- ( $x, $y ) = RSS_xy( $S, $x, $y, %params );
- $params{x} = $x;
- $params{y} = $y;
- RSS_itemSeconds( $S, $x, $y, $format, %params );
- }
- elsif ( $cmd eq "date" ) {
- ( $x, $y ) = split( "[ \t]+", $def, 2 );
- ( $x, $y ) = RSS_xy( $S, $x, $y, %params );
- $params{x} = $x;
- $params{y} = $y;
- RSS_itemDate( $S, $x, $y, %params );
- }
- elsif ( $cmd eq "img" ) {
- ( $x, $y, $scale, $imgtype, $srctype, $arg ) =
- split( "[ \t]+", $def, 6 );
- ( $x, $y ) = RSS_xy( $S, $x, $y, %params );
- $params{x} = $x;
- $params{y} = $y;
- my $arg = RSS_analyzePerl($arg);
- RSS_itemImg( $S, $x, $y, $scale, $imgtype, $srctype, $arg,
- %params );
- }
- elsif ( $cmd eq "push" ) {
- $pstackcount++;
- while ( my ( $key, $value ) = each(%params) ) {
- $pstack{$pstackcount}{$key} = $value;
- }
- }
- elsif ( $cmd eq "pop" ) {
- return unless $pstackcount;
- while ( my ( $key, $value ) =
- each( %{ $pstack{$pstackcount} } ) )
- {
- $params{$key} = $value;
- }
- delete $pstack{$pstackcount};
- $pstackcount--;
- }
- elsif ( $cmd eq "embed" ) {
- ( $x, $y, $z, $format, $text, $arg ) =
- split( "[ \t]+", $def, 6 );
- ( $x, $y ) = RSS_xy( $S, $x, $y, %params );
- my $arg = RSS_analyzePerl($arg);
- $defs{$name}{".embed"} .=
- "<div id=\"$text\" style=\"position:"
- . $format
- . "; left:"
- . $x
- . "px; top:"
- . $y
- . "px; z-index:$z;\">\n";
- $defs{$name}{".embed"} .= $arg . "\n";
- $defs{$name}{".embed"} .= "</div>\n";
- #main::Debug "SET EMBED=" . $defs{$name}{".embed"};
- }
- elsif ( $cmd eq "label" ) {
- #Debug "encountered label $def";
- $count{ '$' . $def }++;
- if ( $count{ '$' . $def } > 99 ) {
- Log3 $name, 2, "$name: exceeded hit count for label $def";
- last;
- }
- #Debug "label $def hit count " . $count{ '$' . $def };
- EvalSpecials("", %count);
- }
- elsif ( $cmd eq "goto" ) {
- my ( $label, $if, $condition ) = split( "[ \t]+", $def, 3 );
- if ( defined($if)
- and ( ( $if ne "if" ) or ( !defined($condition) ) ) )
- {
- Log3 $name, 2,
- "$name: syntax error in goto command \"$line\".";
- next;
- }
- if ( exists $labels{$label} ) {
- if ( !defined($if)
- or RSS_analyzePerl($condition) )
- {
- $lineNr = $labels{$label} - 1;
- }
- }
- else {
- Log3 $name, 2,
- "$name: Undefined label $label in goto command.";
- }
- }
- else {
- Log3 $name, 2,
- "$name: Illegal command $cmd in layout definition.";
- }
- #Debug "after command $line: x= " . $params{x} . ", y= " . $params{y};
- };
- if ($@) {
- my $msg =
- "$name: Error from line \'$line\' in layout definition: $@";
- chomp $msg;
- Log3 $name, 2, $msg;
- }
- }
- }
- ##################
- sub RSS_returnIMG($$) {
- my ( $name, $type ) = @_;
- my ( $width, $height ) =
- split( /x/, AttrVal( $name, "size", "800x600" ) );
- #
- # increase counter
- #
- if ( defined( $defs{$name}{fhem} )
- && defined( $defs{$name}{fhem}{counter} ) )
- {
- $defs{$name}{fhem}{counter}++;
- }
- else {
- $defs{$name}{fhem}{counter} = 1;
- }
- # true color
- GD::Image->trueColor(1);
- #
- # create the image
- #
- our $S;
- # let's create a blank image, we will need it in most cases.
- $S = GD::Image->newTrueColor( $width, $height );
- my $bgcolor =
- AttrVal( $name, 'bgcolor', '000000' ); #default bg color = black
- $bgcolor = RSS_color( $S, $bgcolor );
- # $S->colorAllocate(0,0,0); # other colors seem not to work (issue with GD)
- $S->fill( 0, 0, $bgcolor );
- # wrap to make problems with GD non-lethal
- eval {
- #
- # set the background
- #
- # check if background directory is set
- my $reason = "?"; # remember reason for undefined image
- my $bgdir = AttrVal( $name, "bg", "undef" );
- if ( defined($bgdir) ) {
- my $bgnr; # item number
- if ( defined( $defs{$name}{fhem} )
- && defined( $defs{$name}{fhem}{bgnr} ) )
- {
- $bgnr = $defs{$name}{fhem}{bgnr};
- }
- else {
- $bgnr = 0;
- }
- # check if at least tmin seconds have passed
- my $t0 = 0;
- my $tmin = AttrVal( $name, "tmin", 0 );
- if ( defined( $defs{$name}{fhem} )
- && defined( $defs{$name}{fhem}{t} ) )
- {
- $t0 = $defs{$name}{fhem}{t};
- }
- my $t1 = time();
- if ( $t1 - $t0 >= $tmin ) {
- $defs{$name}{fhem}{t} = $t1;
- $bgnr++;
- }
- # detect pictures
- if ( opendir( BGDIR, $bgdir ) ) {
- my @bgfiles = grep { $_ !~ /^\./ } readdir(BGDIR);
- #foreach my $f (@bgfiles) {
- # Debug sprintf("File \"%s\"\n", $f);
- #}
- closedir(BGDIR);
- # get item number
- if ( $#bgfiles >= 0 ) {
- if ( $bgnr > $#bgfiles ) { $bgnr = 0; }
- $defs{$name}{fhem}{bgnr} = $bgnr;
- my $bgfile = $bgdir . "/" . $bgfiles[$bgnr];
- my $filetype = ( split( /\./, $bgfile ) )[-1];
- my $bg;
- $bg = newFromGif GD::Image($bgfile)
- if $filetype =~ m/^gif$/i;
- $bg = newFromJpeg GD::Image($bgfile)
- if $filetype =~ m/^jpe?g$/i;
- $bg = newFromPng GD::Image($bgfile)
- if $filetype =~ m/^png$/i;
- if ( defined($bg) ) {
- my ( $bgwidth, $bgheight ) = $bg->getBounds();
- if ( $bgwidth != $width or $bgheight != $height ) {
- # we need to resize
- my ( $w, $h );
- my ( $u, $v ) =
- ( $bgwidth / $width, $bgheight / $height );
- if ( $u > $v ) {
- $w = $width;
- $h = $bgheight / $u;
- }
- else {
- $h = $height;
- $w = $bgwidth / $v;
- }
- $S->copyResized(
- $bg,
- ( $width - $w ) / 2,
- ( $height - $h ) / 2,
- 0, 0, $w, $h, $bgwidth, $bgheight
- );
- }
- else {
- # size is as required
- # kill the predefined image and take the original
- undef $S;
- $S = $bg;
- }
- }
- else {
- undef $S;
- $reason =
- "Something was wrong with background image \"$bgfile\".";
- }
- }
- }
- }
- #
- # evaluate layout
- #
- if ( defined($S) ) {
- RSS_evalLayout( $S, $name, $defs{$name}{fhem}{layout} );
- }
- else {
- Log3 $name, 2, "$name: Could not create image. $reason";
- $S =
- GD::Image->newTrueColor( $width, $height ); # return empty image
- }
- $defs{$name}{STATE} = localtime();
- }; #warn $@ if $@;
- if ($@) {
- my $msg = $@;
- chomp $msg;
- Log3 $name, 2, $msg;
- }
- #
- # return image
- #
- return ( "image/jpeg; charset=utf-8", $S->jpeg ) if ( $type eq 'jpg' );
- return ( "image/png; charset=utf-8", $S->png ) if ( $type eq 'png' );
- }
- ##################
- #
- # here we answer any request to http://host:port/fhem/rss and below
- sub RSS_CGI() {
- my ($request) =
- @_; # /rss or /rss/name.rss or /rss/name.jpg or /rss/name.png
- my ( $name, $ext, $query ) =
- RSS_splitRequest($request); # name, ext (rss, jpg, png, html), query
- # query is unused
- #main::Debug "Request: $request";
- #main::Debug " Name : $name";
- #main::Debug " Ext : $ext";
- #main::Debug " Query : $query";
- if ( defined($name) ) {
- if ( $ext eq "" ) {
- return ( "text/plain; charset=utf-8", "Illegal extension." );
- }
- if ( !defined( $defs{$name} ) ) {
- return ( "text/plain; charset=utf-8", "Unknown RSS device: $name" );
- }
- if ( $ext eq "jpg" ) {
- return RSS_returnIMG( $name, 'jpg' );
- }
- elsif ( $ext eq "png" ) {
- return RSS_returnIMG( $name, 'png' );
- }
- elsif ( $ext eq "rss" ) {
- return RSS_returnRSS($name);
- }
- elsif ( $ext eq "html" ) {
- return RSS_returnHTML($name);
- }
- }
- else {
- return RSS_Overview();
- }
- }
- sub plotFromUrl(@) {
- my (@plotName) = @_;
- my @webs;
- @webs = devspec2array("TYPE=FHEMWEB");
- foreach (@webs) {
- if ( !InternalVal( $_, 'TEMPORARY', undef ) ) {
- $FW_wname = InternalVal( $_, 'NAME', '' );
- last;
- }
- }
- my ( $w, $h ) =
- split( ",", AttrVal( $plotName[0], "plotsize", "800,160" ) );
- my $url;
- $url =
- "<embed src=\""
- . "$FW_ME/SVG_showLog?dev="
- . $plotName[0]
- . "&logdev="
- . InternalVal( $plotName[0], "LOGDEVICE", "" )
- . "&gplotfile="
- . InternalVal( $plotName[0], "GPLOTFILE", "" )
- . "&logfile="
- . InternalVal( $plotName[0], "LOGFILE", "CURRENT" );
- $url .= "&pos=" . $plotName[1] if $plotName[1];
- $url .= "&zoom=" . $plotName[2] if $plotName[2];
- $url .= "\"";
- $url .= " type=\"image/svg+xml\"";
- $url .= " width=\"$w\"";
- $url .= " height=\"$h\"";
- $url .= " id=\"" . $plotName[0] . "\" >";
- return $url;
- }
- #
- 1;
- =pod
- =item helper
- =item summary Provides a freely configurable RSS feed and HTML page.
- =item summary_DE Stellt frei konfigurierbaren RSS-Feed und HTML-Seite bereit.
- =begin html
- <a name="RSS"></a>
- <h3>RSS</h3>
- <ul>
- Provides a freely configurable RSS feed and HTML page.<p>
- The media RSS feed delivers status pictures either in JPEG or PNG format.
- This media RSS feed can be used to feed a status display to a
- network-enabled photo frame.<p>
- In addition, a periodically refreshing HTML page is generated that shows the picture
- with an optional HTML image map.<p>
- You need to have the perl module <code>GD</code> installed. This module is most likely not
- available for small systems like Fritz!Box.<p>
- RSS is an extension to <a href="#FHEMWEB">FHEMWEB</a>. You must install FHEMWEB to use RSS.<p>
- Beginners might find the <a href="http://forum.fhem.de/index.php/topic,22520.0.html">RSS Workshop</a> useful.<p>
- A note on colors: Colors are specified as 6- or 8-digit hex numbers,
- every 2 digits determining the red, green and blue color components as in HTML
- color codes. The optional 7th and 8th digit code the alpha channel (transparency from
- 00 to 7F). Examples: <code>FF0000</code> for red, <code>C0C0C0</code> for light
- gray, <code>1C1C1C40</code> for semi-transparent gray.<p>
- <a name="RSSdefine"></a>
- <b>Define</b>
- <ul>
- <code>define <name> RSS jpg|png <hostname> <filename></code><br><br>
- Defines the RSS feed. <code>jpg</code> and <code>png</code> are fixed literals to select output format.
- <code><hostname></code> is the hostname of the fhem server as
- seen from the consumer of the RSS feed. <code><filename></code> is the
- name of the file that contains the <a href="RSSlayout">layout definition</a>.<p>
- Examples
- <ul>
- <code>define FrameRSS RSS jpg host.example.org /etc/fhem/layout</code><br>
- <code>define MyRSS RSS png 192.168.1.222 /var/fhem/conf/layout.txt</code><br>
- </ul>
- <br>
- The RSS feeds are at
- <ul>
- <code>http://host.example.org:8083/fhem/rss/FrameRSS.rss</code><br>
- <code>http://192.168.1.222:8083/fhem/rss/MyRSS.rss</code><br>
- </ul>
- <br>
- The pictures are at
- <ul>
- <code>http://host.example.org:8083/fhem/rss/FrameRSS.jpg</code><br>
- <code>http://192.168.1.222:8083/fhem/rss/MyRSS.png</code><br>
- </ul>
- <br>
- The HTML pages are at
- <ul>
- <code>http://host.example.org:8083/fhem/rss/FrameRSS.html</code><br>
- <code>http://192.168.1.222:8083/fhem/rss/MyRSS.html</code><br>
- </ul>
- <br>
- </ul>
- <a name="RSSset"></a>
- <b>Set</b>
- <ul>
- <code>set <name> rereadcfg</code>
- <br><br>
- Rereads the <a href="RSSlayout">layout definition</a> from the file. Useful to enable
- changes in the layout on-the-fly.
- <br><br>
- </ul>
- <a name="RSSattr"></a>
- <b>Attributes</b>
- <br><br>
- <ul>
- <li>autoreread<br>If set to 1, layout is automatically reread when layout file has been changed
- by FHEMWEB file editor.</li><br>
- <li>size<br>The dimensions of the picture in the format
- <code><width>x<height></code>.</li><br>
- <li>bg<br>The directory that contains the background pictures (must be in JPEG, GIF or PNG format, file
- format is guessed from file name extension).</li><br>
- <li>bgcolor <color><br>Sets the background color. </li><br>
- <li>tmin<br>The background picture is shown at least <code>tmin</code> seconds,
- no matter how frequently the RSS feed consumer accesses the page.</li><br>
- <li>refresh <interval><br>Time in seconds after which the HTML page is automatically reloaded. Defaults to 60.
- Set <interval> to 0 to disable reloading.</li><br>
- <li>viewport<br>Adds a viewport directive to the HTML header.<br>
- Example: <code>attr FrameRSS viewport width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0</code><br>
- This scales the image to fit to the browser's viewport and inhibits zooming in or out on tablets.
- </li><br>
- <li>noscroll</br>Suppresses scrollbars in browsers, if set to 1.
- </li><br>
- <li>areas<br>HTML code that goes into the image map.<br>
- Example: <code>attr FrameRSS areas <area shape="rect" coords="0,0,200,200" href="http://fhem.de"/><area shape="rect" coords="600,400,799,599" href="http://has:8083/fhem" target="_top"/></code>
- </li><br>
- <li>itemtitle</br>Adds a title tag to the RSS item that contains the specified text.
- </li><br>
- <li>urlOverride<br>Overrides the URL in the generated feed.
- If you specify
- <code>attr <name> http://otherhost.example.org:8083/foo/bar</code>, the
- JPEG picture that is at
- <code>http://host.example.org:8083/fhem/rss/FrameRSS.jpg</code>
- will be referenced as
- <code>http://otherhost.example.org:8083/foo/bar/rss/FrameRSS.jpg</code>. This is useful when
- your FHEM URLs are rewritten, e.g. if FHEM is accessed by a Reverse Proxy.</li>
- <br>
- </ul>
- <br><br>
- <b>Usage information</b>
- <br><br>
- <ul>
- If a least one RSS feed is defined, the menu entry <code>RSS</code> appears in the FHEMWEB
- side menu. If you click it you get a list of all defined RSS feeds. The URL of any such is
- RSS feed is <code>http://hostname:port/fhem/rss/name.rss</code> with <code>hostname</code> and
- <code>name</code> from the RSS feed's <a href="RSSdefine">definition</a> and the <code>port</code>
- (usually 8083) and literal <code>/fhem</code> from the underlying <a href="#FHEMWEB">FHEMWEB</a>
- definition.<p>
- Example:
- <ul><code>http://host.example.org:8083/fhem/rss/FrameRSS.rss</code></ul><p>
- The media RSS feed points to a dynamically generated picture. The URL of the picture
- belonging to the RSS can be found by replacing the extension ".rss" in feed's URL by ".jpg" or ".png"
- depending on defined output format,<p>
- Example:
- <ul><code>http://host.example.org:8083/fhem/rss/FrameRSS.jpg</code></ul><p>
- <ul><code>http://192.168.100.200:8083/fhem/rss/FrameRSS.png</code></ul><p>
- To render the picture the current, or, if <code>tmin</code> seconds have elapsed, the next
- JPEG picture from the directory <code>bg</code> is chosen and scaled to the dimensions given
- in <code>size</code>. The background is black if no usable JPEG picture can be found. Next the
- script in the <a href="RSSlayout">layout definition</a> is used to superimpose items on
- the background.<p>
- You can directly access the URL of the picture in your browser. Reload the page to see
- how it works.<p>
- The media RSS feed advertises to refresh after 1 minute (ttl). Some photo frames ignore it and
- use their preset refresh rate. Go for a photo frame with an adjustable refresh rate (e.g
- every 5 seconds) if you have the choice!<p>
- This is how the fhem config part might look like:<p>
- <code>
- define ui FHEMWEB 8083 global<br><br>
- define FrameRSS RSS jpg host.example.org /etc/fhem/layout<br>
- attr FrameRSS size 800x600<br>
- attr FrameRSS bg /usr/share/pictures<br>
- attr FrameRSS tmin 10<br>
- </code>
- </ul>
- <a name="RSSlayout"></a>
- <b>Layout definition</b>
- <br><br>
- <ul>
- The layout definition is a script for placing items on the background. It is read top-down.
- It consists of layout control commands and items placement commands. Layout control
- commands define the appearance of subsequent items. Item placement commands actually
- render items.<p>
- Everything after a # is treated as a comment and ignored. You can fold long lines by
- putting a \ at the end.<p>
- <i>General notes</i><br>
- <ol>
- <li>Use double quotes to quote literal text if perl specials are allowed.</li>
- <li>Text alignment requires the Perl module GD::Text::Align to be installed. Text wrapping (in text boxes) require GD::Text::Wrap to be installed. Debian-based systems can install both with <code>apt-get install libgd-text-perl</code>.</li>
- </ol>
- <p>
- <i>Notes on coordinates</i><br>
- <ol>
- <li>(0,0) is the upper left corner.</li>
- <li>Coordinates equal or greater than 1 are considered to be absolute pixels, coordinates between 0 and 1 are considered to
- be relative to the total width or height of the picture.</li>
- <li>Literal <code>x</code> and <code>y</code> evaluate to the most recently used x- and y-coordinate. See also moveto and moveby below.</li>
- <!--<li>You can use <code>{ <a href="#perl"><perl special></a> }</code> for x and for y.</li>-->
- </ol>
- <p>
- <i>Layout control commands</i><p>
- <ul>
- <li>moveto <x> <y><br>Moves most recently used x- and y-coordinate to the given absolute or relative position.</li><br>
- <li>moveby <x> <y><br>Moves most recently used x- and y-coordinate by the given absolute or relative amounts.</li><br>
- <li>font "<font>"<br>Sets the font. <font> is the name of a TrueType font (e.g.
- <code>Arial</code>) or the full path to a TrueType font
- (e.g. <code>/usr/share/fonts/truetype/arial.ttf</code>),
- whatever works on your system.</li><br>
- <a name="rss_rgb"></a>
- <li>rgb "<color>"<br>Sets the color. You can use
- <code>{ <a href="#perl"><perl special></a> }</code> for <color>.</li><br>
- <li>pt <pt><br>Sets the font size in points. A + or - sign in front of the the number given
- for <pt> signifies a change of the font size relative to the current size. Otherwise the absolute
- size is set. You can use
- <code>{ <a href="#perl"><perl special></a> }</code> for <pt>.</li><br>
- <li>thalign|ihalign|halign "left"|"center"|"right"<br>Sets the horizontal alignment of text, image or both. Defaults to left-aligned. You can use
- <code>{ <a href="#perl"><perl special></a> }</code> instead of the literal alignment control word.</li><br>
- <li>tvalign|ivalign|valign "top"|"center"|"base"|"bottom"<br>Sets the vertical alignment of text, image or both. Defaults to base-aligned for text and
- top-aligned for image. You can use
- <code>{ <a href="#perl"><perl special></a> }</code> instead of the literal alignment control word.</li><br>
- <li>linespace <space><br>Sets the line spacing in pixels for text boxes (see textbox item below).</li><br>
- <li>condition <condition><br>Subsequent layout control and item placement commands except for another condition command are ignored if and only if <condition> evaluates to false.</li><br>
- <li>push<br>The current parameter set (position, color, font name and size, text alignment and line spacing) is
- put (saved) on top of a stack.</li><br>
- <li>pop<br>The most recently saved (pushed) current parameter set is pulled from the top of the stack and restored.</li><br>
- <li>label <lbl><br>Define a label for this line – i.e. name this line <lbl>. This label can be used to jump back or forward to this line by the <goto> command.<br>
- A counter is implicitly defined for every label. From <a href="#perl"><perl specials></a> it can be accessed with <code>{$<lbl>}</code>.
- Example: the hit count for label <code>foo</code> is in <code>$foo</code>.
- The counter starts with zero and is increased with every hit of the label.
- </li></br>
- <li>goto <lbl><br>goto <lbl> if <code>{ <a href="#perl"><perl special></a> }</code><br>
- Jump back or forward to the line labelled <lbl>. The condition is
- optional. Example: <code>goto foo if { $foo < 5 }</code>. This can be used to create loops.
- Note: if the goto jumps forward to the label for the first time, the hit count is 0;
- if the goto jumps backward to the label for the first time, the hit count is already 1.</li><br>
- </ul>
- <i>Item placement commands</i><p>
- <ul>
- <li>text <x> <y> <text><br>Renders the text <text> at the
- position (<x>, <y>) using the current font, font size and color.
- You can use
- <code>{ <a href="#perl"><perl special></a> }</code> for <text> to fully
- access device readings and do some programming on the fly. See below for examples.</li><br>
- <li>textbox <x> <y> <boxwidth> <text><br>Same as before but text is rendered
- in a box of horizontal width <boxwidth>.</li><br>
- <li>textboxf <x> <y> <boxwidth> <bgcolor> <text><br>Same as before but
- the textbox will be filled with the given background color <bgcolor> before drawing the text.
- <bgcolor> can be used with <code>{ <a href="#perl"><perl special></a> }</code> to evalute <a href="#rss_rgb">RGB</a> value.</li><br>
- <li>time <x> <y><br>Renders the current time in HH:MM format.</li><br>
- <li>seconds <x> <y> <format><br>Renders the curent seconds. Maybe useful for a RSS Clock.</li><br>
- <li>date <x> <y><br>Renders the current date in DD.MM.YYYY format.</li><br>
- <li>line <x1> <y1> <x2> <y2> [<thickness>]<br>Draws a line from position (<x1>, <y1>) to position (<x2>, <y2>) with optional thickness (default=1).</li><br>
- <li>rect <x1> <y1> <x2> <y2> [<filled>]<br>Draws a rectangle with corners at positions (<x1>, <y1>) and (<x2>, <y2>), which is filled if the <filled> parameter is set and not zero.<br>If x2 or y2 is preceeded with a + (plus sign) then the coordinate is relative to x1 or y1, or in other words, it is the width-1 or height-1 of the rectangle, respectively.</li><br>
- <li>img <x> <y> <['w' or 'h']s> <imgtype> <srctype> <arg> <br>Renders a picture at the
- position (<x>, <y>). The <imgtype> is one of <code>gif</code>, <code>jpeg</code>, <code>png</code>.
- The picture is scaled by the factor <s> (a decimal value). If 'w' or 'h' is in front of scale-value the value is used to set width or height to the value in pixel. If <srctype> is <code>file</code>, the picture
- is loaded from the filename <arg>, if <srctype> is <code>url</code> or <code>urlq</code>, the picture
- is loaded from the URL <arg> (with or without logging the URL), if <srctype> is <code>data</code>, the picture
- is piped in from data <arg>. You can use
- <code>{ <a href="#perl"><perl special></a> }</code> for <arg>. See below for example.<br>
- <b>Warning</b>: do not load the image from URL that is served by fhem as it leads to a deadlock.</li><br>
- <li>embed <x> <y> <z> <position> <id> <element><br>
- For HTML output: embeds a <code>div</code> element into the HTML page at (<x>,<y>) with z-order <z> and positioning <position> (use <code>absolute</code>). <id> is the <code>id</code> attribute of the
- <code>div</code> element and <element> is its content.<br>
- <b>Note:</b> There are several issues with different browsers when using this.</li><br>
- </ul>
- <i>Example</i><p>
- This is how a layout definition might look like:<p>
- <code>
- font /usr/share/fonts/truetype/arial.ttf # must be a TrueType font<br>
- rgb "c0c0c0" # HTML color notation, RGB<br>
- pt 48 # font size in points<br>
- time 0.10 0.90<br>
- pt 24<br>
- text 0.10 0.95 { ReadingsVal("MyWeather","temperature","?"). "C" }<br>
- moveby 0 -25<br>
- text x y "Another text"<br>
- img 20 530 0.5 png file { "/usr/share/fhem/www/images/weather/" . ReadingsVal("MyWeather","icon","") . ".png" }<br>
- embed 0 0 2 absolute plot1 { plotFromUrl('mySVG') }
- embed 10 200 2 absolute iframe1 "<iframe width=\"420\" height=\"315\" src=\"//www.youtube.com/embed/9HShl_ufOFI\" frameborder=\"0\" allowfullscreen></iframe>"
- </code>
- <p>
- <i>Special uses</i><p>
- You can display <a href="#SVG">SVG</a> plots with the aid of the helper function <code>plotAsPng(<name>[,<zoom>[,<offset>]])</code> (in 98_SVG.pm). Examples:<p>
- <code>
- img 20 30 0.6 png data { plotAsPng("mySVGPlot") }<BR>
- img 20 30 0.6 png data { plotAsPng("mySVGPlot","qday",-1) }
- </code>
- <p>
- This requires the perl module Image::LibRSVG and librsvg. Debian-based systems can install these with <code>apt-get install libimage-librsvg-perl</code>.<p>
- For HTML output, you can use <code>plotFromURL(<name>[,<zoom>[,<offset>]])</code> instead.
- </ul>
- </ul>
- =end html
- =cut
|