2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-20 01:06:04 +00:00

02_FTUISRV: if and loop constructs added

git-svn-id: https://svn.fhem.de/fhem/trunk@12299 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
viegener 2016-10-08 23:33:10 +00:00
parent 19b9b22925
commit 32a094be65
2 changed files with 344 additions and 85 deletions

View File

@ -1,6 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- feature: 02_FTUISRV: if and loop constructs added
- bugfix: 74_AMAD: fix FHEM crash when use AMAD and apptime togehter
- new: 98_TRAFFIC: provide traffic details with Google Distance API
- feature: 98_Hyperion: set configFile only available if at least two files

View File

@ -60,13 +60,18 @@
# add documentation for device readings (set logic)
# allow reading values also in inc tag
# 0.3 - 2016-04-25 - Version for publication in SVN
#
#
# Allow replacements setp by step in headerline --> ?> must be escaped to ?\>
# added if else endif for segments ftui-if=( <expr> )
# simplified keyvalue parsing
# simplified include in separate sub
# add loopinc for looping include multiple times loopinc="<path>" <key>=( <expr> ) <keyvalues>
# summary for commandref
# added if and loopinc to commandref
#
################################################################
#TODO:
#
# Allow if for separate sections
# log count of replacements
#
# deepcopy only if new keys found
#
##############################################
@ -89,9 +94,6 @@ my $FTUISRV_matchlink = "^\/?(([^\/]*(\/[^\/]+)*)\/?)\$";
my $FTUISRV_matchtemplatefile = "^.*\.ftui\.[^\.]+\$";
##### <\?ftui-inc="([^"\?]+)"\s+([^\?]*)\?>
my $FTUISRV_ftuimatch_inc = '<\?ftui-inc="([^"\?]+)"\s+([^\?]*)\?>';
#my $FTUISRV_ftuimatch_header = '<\?ftui-header="([^"\?]*)"\s+([^\?]*)\?>';
my $FTUISRV_ftuimatch_header = '<\?ftui-header="([^"\?]*)"\s+(.*?)\?>';
@ -99,16 +101,24 @@ my $FTUISRV_ftuimatch_keysegment = '^\s*([^=\s]+)(="([^"]*)")?\s*';
my $FTUISRV_ftuimatch_keygeneric = '<\?ftui-key=([^\s]+)\s*\?>';
my $FTUISRV_ftuimatch_if_het = '^(.*?)<\?ftui-if=\((.*?)\)\s*\?>(.*)$';
my $FTUISRV_ftuimatch_else_ht = '^(.*?)<\?ftui-else\s*\?>(.*)$';
my $FTUISRV_ftuimatch_endif_ht = '^(.*?)<\?ftui-endif\s*\?>(.*)$';
my $FTUISRV_ftuimatch_inc_hfvt = '^(.*?)<\?ftui-inc="([^"\?]+)"\s+([^\?]*)\?>(.*?)$';
#my $FTUISRV_ftuimatch_loopinc_hefvt = '^(.*?)<\?ftui-loopinc=\((.*?)\)\s+"([^"\?]+)"\s+([^\?]*)\?>(.*?)$';
my $FTUISRV_ftuimatch_loopinc_hfkevt = '^(.*?)<\?ftui-loopinc="([^"\?]+)"\s+([^=\s]+)=\s*\((.+?)\)\s+([^\?]*)\?>(.*?)$';
#########################
# FORWARD DECLARATIONS
sub FTUISRV_handletemplatefile( $$$$ );
sub FTUISRV_validateHtml( $$$$ );
sub FTUISRV_handleIf( $$$ );
#########################
@ -604,6 +614,286 @@ sub popTag( $$ ) {
##############################################
##################
#
# handle a ftui template string
# name of the current ftui device
# filename full fledged filename to be handled
# string with content to be replaced
# parhash reference to a hash with the current key-values
# returns
# contents
sub FTUISRV_replaceKeys( $$$$ ) {
my ($name, $filename, $content, $parhash) = @_;
# make replacements of keys from hash
while ( $content =~ /<\?ftui-key=([^\s]+)\s*\?>/g ) {
my $key = $1;
my $value = $parhash->{$key};
if ( ! defined( $value ) ) {
Log3 $name, 4, "$name: unmatched key in file :$filename: key :$key:";
$value = "";
}
Log3 $name, 4, "$name: replace key in file :$filename: key :$key: with :$value:";
$content =~ s/<\?ftui-key=$key\s*\?>/$value/sg;
}
# while ( $content =~ /$FTUISRV_ftuimatch_keygeneric/s ) {
while ( $content =~ /<\?ftui-key=([^\s]+)\s*\?>/g ) {
Log3 $name, 4, "$name: unmatched key in file :$filename: key :$1:";
}
return $content;
}
##################
#
# handle a ftui template for ifs
# name of the current ftui device
# filename full fledged filename to be handled
# string with content to be replaced
# returns
# contents
sub FTUISRV_handleIf( $$$ ) {
my ($name, $filename, $content) = @_;
# Look for if expression
my $done = "";
return $content if ( $content !~ /$FTUISRV_ftuimatch_if_het/s );
$done .= $1;
my $expr = $2;
my $rest = $3;
# handle rest to check recursively for further ifs
$rest = FTUISRV_handleIf( $name, $filename, $rest );
# identify then and else parts
my $then;
my $else = "";
if ( $rest =~ /$FTUISRV_ftuimatch_else_ht/s ) {
$then = $1;
$rest = $2;
}
if ( $rest =~ /$FTUISRV_ftuimatch_endif_ht/s ) {
$else = $1;
$rest = $2;
if ( ! defined($then) ) {
$then = $else;
$else = "";
}
}
# check expression
my %dummy;
my ($err, @a) = ReplaceSetMagic(\%dummy, 0, ( $expr ) );
if ( $err ) {
Log3 $name, 1, "$name: FTUISRV_handleIf failed on ReplaceSetmagic with :$err: on header :$expr:";
} else {
Log3 $name, 4, "$name: FTUISRV_handleIf expr before setmagic :".$expr.":";
$expr = join(" ", @a);
# need to trim result of replacesetmagic -> multiple elements some space
$expr =~ s/^\s+|\s+$//g;
Log3 $name, 4, "$name: FTUISRV_handleIf expr elements :".scalar(@a).":";
Log3 $name, 4, "$name: FTUISRV_handleIf expr after setmagic :".$expr.":";
}
# put then/else depending on expr
$done .= ( ( $expr ) ? $then : $else );
$done .= $rest;
return $done;
}
##################
#
# handle a ftui template for incs and then this as the template
# name of the current ftui device
# filename full fledged filename to be handled
# curdir current directory for filename
# string with content to be replaced
# parhash reference to a hash with the current key-values
# validated is ref to hash with filenames
# returns
# err (might be undef)
# contents
sub FTUISRV_handleInc( $$$$$$ ) {
my ($name, $filename, $curdir, $content, $parhash, $validatehash) = @_;
# Look for if expression
my $done = "";
my $rest = $content;
while ( $rest =~ /$FTUISRV_ftuimatch_inc_hfvt/s ) {
$done .= $1;
my $incfile = $2;
my $values = $3;
$rest = $4;
Log3 $name, 4, "$name: include found :$filename: inc :$incfile: vals :$values:";
return ("$name: Empty file name in include :$filename:", $content) if ( length($incfile) == 0 );
# replace [device:reading] or even perl expressions with replaceSetMagic
my %dummy;
Log3 $name, 4, "$name: FTUISRV_handleInc ReplaceSetmagic INC before :$values:";
my ($err, @a) = ReplaceSetMagic(\%dummy, 0, ( $values ) );
if ( $err ) {
Log3 $name, 1, "$name: FTUISRV_handleInc ($filename) failed on ReplaceSetmagic with :$err: on INC :$values:";
} else {
$values = join(" ", @a);
Log3 $name, 4, "$name: FTUISRV_handleInc ($filename) ReplaceSetmagic INC after :".$values.":";
}
# deepcopy parhash here
my $incparhash = deepcopy( $parhash );
# parse $values + add keys to inchash
while ( $values =~ s/$FTUISRV_ftuimatch_keysegment//s ) {
my $skey = $1;
my $sval = $3;
$sval="" if ( ! defined($sval) );
Log3 $name, 4, "$name: a key :$skey: = :$sval: ";
$incparhash->{$skey} = $sval;
}
# build new filename (if not absolute already)
$incfile = $curdir.$incfile if ( substr($incfile,0,1) ne "/" );
Log3 $name, 4, "$name: start handling include (rec) :$incfile:";
my $inccontent;
my $dummy;
($err, $dummy, $inccontent) = FTUISRV_handletemplatefile( $name, $incfile, $incparhash, $validatehash );
Log3 $name, 4, "$name: done handling include (rec) :$incfile: ".(defined($err)?"Err: ".$err:"ok");
# error will always result in stopping recursion
return ($err." (included)", $content) if ( defined($err) );
$done .= $inccontent;
# Log3 $name, 3, "$name: done handling include new content:----------------\n$content\n--------------------";
last if ( length($rest) == 0 );
}
$done .= $rest;
return ( undef, $done );
}
##################
#
# handle a ftui template for loopInc and then the file as a template for all expression results
# name of the current ftui device
# filename full fledged filename to be handled
# curdir current directory for filename
# string with content to be replaced
# parhash reference to a hash with the current key-values
# validated is ref to hash with filenames
# returns
# err (might be undef)
# contents
sub FTUISRV_handleLoopInc( $$$$$$ ) {
my ($name, $filename, $curdir, $content, $parhash, $validatehash) = @_;
# Look for if expression
my $done = "";
my $rest = $content;
while ( $rest =~ /$FTUISRV_ftuimatch_loopinc_hfkevt/s ) {
$done .= $1;
my $incfile = $2;
my $key = $3;
my $expr = $4;
my $values = $5;
$rest = $6;
Log3 $name, 1, "$name: include loop found :$filename: key :$key: expr:$expr:\n inc :$incfile: vals :$values:";
return ("$name: Empty file name in loopinc :$filename:", $content) if ( length($incfile) == 0 );
# Evaluate expression as command to get list of entries for loop ???
my $result = AnalyzeCommand(undef, $expr);
# Identify split char ???
# split at splitchar (default \n) into array ???
my @aResults = split( /\n/, $result );
# replace [device:reading] or even perl expressions with replaceSetMagic
my %dummy;
Log3 $name, 4, "$name: FTUISRV_handleLoopInc ReplaceSetmagic INC before :$values:";
my ($err, @a) = ReplaceSetMagic(\%dummy, 0, ( $values ) );
if ( $err ) {
Log3 $name, 1, "$name: FTUISRV_handleLoopInc ($filename) failed on ReplaceSetmagic with :$err: on INC :$values:";
} else {
$values = join(" ", @a);
Log3 $name, 4, "$name: FTUISRV_handleLoopInc ($filename) ReplaceSetmagic INC after :".$values.":";
}
# deepcopy parhash here
my $incparhash = deepcopy( $parhash );
# parse $values + add keys to inchash
while ( $values =~ s/$FTUISRV_ftuimatch_keysegment//s ) {
my $skey = $1;
my $sval = $3;
$sval="" if ( ! defined($sval) );
Log3 $name, 4, "$name: a key :$skey: = :$sval: ";
$incparhash->{$skey} = $sval;
}
# build new filename (if not absolute already)
$incfile = $curdir.$incfile if ( substr($incfile,0,1) ne "/" );
# Loop over list of values
foreach my $loopvariable ( @aResults ) {
# deepcopy parhash here
my $loopincparhash = deepcopy( $incparhash );
# add loopvariable with current value
$loopincparhash->{$key} = $loopvariable;
Log3 $name, 1, "$name: start handling include (rec) :$incfile: with value $key = :$loopvariable:";
my $inccontent;
my $dummy;
($err, $dummy, $inccontent) = FTUISRV_handletemplatefile( $name, $incfile, $loopincparhash, $validatehash );
Log3 $name, 4, "$name: done handling include (rec) :$incfile: ".(defined($err)?"Err: ".$err:"ok");
# error will always result in stopping recursion
return ($err." (included)", $content) if ( defined($err) );
$done .= $inccontent;
# Log3 $name, 3, "$name: done handling include new content:----------------\n$content\n--------------------";
}
last if ( length($rest) == 0 );
}
$done .= $rest;
return ( undef, $done );
}
##################
#
# handle a ftui template file
@ -651,14 +941,6 @@ sub FTUISRV_handletemplatefile( $$$$ ) {
# replace [device:reading] or even perl expressions with replaceSetMagic
my %dummy;
Log3 $name, 4, "$name: FTUISRV_handletemplatefile ReplaceSetmagic HEADER before :$hvalues:";
my ($err, @a) = ReplaceSetMagic(\%dummy, 0, ( $hvalues ) );
if ( $err ) {
Log3 $name, 1, "$name: FTUISRV_handletemplatefile failed on ReplaceSetmagic with :$err: on header :$hvalues:";
} else {
$hvalues = join(" ", @a);
Log3 $name, 4, "$name: FTUISRV_handletemplatefile ReplaceSetmagic HEADER after :".$hvalues.":";
}
# grab keys for default values from header
while ( $hvalues =~ /$FTUISRV_ftuimatch_keysegment/s ) {
@ -667,6 +949,21 @@ sub FTUISRV_handletemplatefile( $$$$ ) {
if ( defined($sval) ) {
Log3 $name, 4, "$name: default value for key :$skey: = :$sval: ";
# replace escaped > (i.e. \>) by > for key replacement
$sval =~ s/\?\\>/\?>/g;
$sval = FTUISRV_replaceKeys( $name, $filename." header", $sval, $parhash );
Log3 $name, 4, "$name: FTUISRV_handletemplatefile default value for key :$skey: after replace :".$sval.":";
my ($err, @a) = ReplaceSetMagic(\%dummy, 0, ( $sval ) );
if ( $err ) {
Log3 $name, 1, "$name: FTUISRV_handletemplatefile failed on ReplaceSetmagic with :$err: on header :$sval:";
} else {
$sval = join(" ", @a);
Log3 $name, 4, "$name: FTUISRV_handletemplatefile default value for key :$skey: after setmagic :".$sval.":";
}
$parhash->{$skey} = $sval if ( ! defined($parhash->{$skey} ) )
}
$hvalues =~ s/$FTUISRV_ftuimatch_keysegment//s;
@ -677,77 +974,23 @@ sub FTUISRV_handletemplatefile( $$$$ ) {
$content =~ s/$FTUISRV_ftuimatch_header//s
}
# make replacements of keys from hash
while ( $content =~ /<\?ftui-key=([^\s]+)\s*\?>/g ) {
my $key = $1;
my $value = $parhash->{$key};
if ( ! defined( $value ) ) {
Log3 $name, 4, "$name: unmatched key in file :$filename: key :$1:";
$value = "";
}
$content =~ s/<\?ftui-key=$key\s*\?>/$value/sg;
}
# replace the keys first before loop/if
$content = FTUISRV_replaceKeys( $name, $filename, $content, $parhash );
# while ( $content =~ /$FTUISRV_ftuimatch_keygeneric/s ) {
while ( $content =~ /<\?ftui-key=([^\s]+)\s*\?>/g ) {
Log3 $name, 4, "$name: unmatched key in file :$filename: key :$1:";
}
# eval if else endif expressions
$content = FTUISRV_handleIf( $name, $filename, $content );
# Handle includes
Log3 $name, 4, "$name: look for includes :$filename:";
while ( $content =~ /$FTUISRV_ftuimatch_inc/s ) {
my $incfile = $1;
my $values = $2;
( $err, $content ) = FTUISRV_handleInc( $name, $filename, $curdir, $content, $parhash, $validatehash );
# error will always result in stopping recursion
return ($err, $validated, $content) if ( defined($err) );
Log3 $name, 4, "$name: include found :$filename: inc :$incfile: vals :$values:";
return ("$name: Empty file name in include :$filename:", $validated, $content) if ( length($incfile) == 0 );
# replace [device:reading] or even perl expressions with replaceSetMagic
my %dummy;
Log3 $name, 4, "$name: FTUISRV_handletemplatefile ReplaceSetmagic INC before :$values:";
my ($err, @a) = ReplaceSetMagic(\%dummy, 0, ( $values ) );
if ( $err ) {
Log3 $name, 1, "$name: FTUISRV_handletemplatefile failed on ReplaceSetmagic with :$err: on INC :$values:";
} else {
$values = join(" ", @a);
Log3 $name, 4, "$name: FTUISRV_handletemplatefile ReplaceSetmagic INC after :".$values.":";
}
( $err, $content ) = FTUISRV_handleLoopInc( $name, $filename, $curdir, $content, $parhash, $validatehash );
# error will always result in stopping recursion
return ($err, $validated, $content) if ( defined($err) );
# deepcopy parhash here
my $incparhash = deepcopy( $parhash );
# parse $values + add keys to inchash
# ??? check if this can not be handled in a real loop wthout midfying $values each time
while ( $values =~ /$FTUISRV_ftuimatch_keysegment/s ) {
my $skey = $1;
my $sval = $3;
$sval="" if ( ! defined($sval) );
Log3 $name, 4, "$name: a key :$skey: = :$sval: ";
$incparhash->{$skey} = $sval;
$values =~ s/$FTUISRV_ftuimatch_keysegment//s;
}
# build new filename (if not absolute already)
$incfile = $curdir.$incfile if ( substr($incfile,0,1) ne "/" );
Log3 $name, 4, "$name: start handling include (rec) :$incfile:";
my $inccontent;
my $dummy;
($err, $dummy, $inccontent) = FTUISRV_handletemplatefile( $name, $incfile, $incparhash, $validatehash );
Log3 $name, 4, "$name: done handling include (rec) :$incfile: ".(defined($err)?"Err: ".$err:"ok");
# error will always result in stopping recursion
return ($err." (included)", $validated, $content) if ( defined($err) );
$content =~ s/$FTUISRV_ftuimatch_inc/$inccontent/s;
# Log3 $name, 3, "$name: done handling include new content:----------------\n$content\n--------------------";
}
}
return ($err,$validated,$content);
@ -848,6 +1091,8 @@ sub FTUISRV_BinaryFileRead($) {
=pod
=item summary HTTP Server for tablet UI with server side includes, loops, ifs
=item summary_DE HTTP-Server für das tablet UI mit server-seitigen includes, loop, if
=begin html
<a name="FTUISRV"></a>
@ -875,6 +1120,19 @@ sub FTUISRV_BinaryFileRead($) {
<br>Example: <code>&lt;?ftui-inc="temphum-inline.ftui.part" thdev="sensorWZ" thformat="top-space-2x" thtemp="measured-temp" ?&gt;</code>
</li><br>
<li><code>&lt;?ftui-if=( expression ) ?&gt; ... [ &lt;?ftui-else ?&gt; ... ] &lt;?ftui-endif ?&gt; </code> <br>
IF statement: Allow the inclusion of a block depending on an expression that might again include also variables and expressions in fhem. The else block is optional and can contain a block that is included if the expression is not evaluated to true.
<br>
Example: <code>&lt;?ftui-if=( [tempdevice:batteryok] ) ?&gt; ... &lt;?ftui-else ?&gt; ... &lt;?ftui-endif ?&gt; </code>
</li><br>
<li><code>&lt;?ftui-loopinc="name" loopvariable=( loop-expression ) varname1="content1" ... varnameN="contentN" ?&gt;</code> <br>
LOOP-INCLUDE statement: Including other files that will be embedded in the result at the place of the include statement. The include will be executed once for every entry (line) that is returned when evaluating the loop-expression as an fhem command. So the loop expression could be a list command returning multiple devices
<br>
The quotation marks and the spaces between the variable replacements and before the final ? are significant and can not be ommitted.
<br>Example: <code>&lt;?ftui-loopinc="temphum-inline.ftui.part" thdev=( list TYPE=CUL_TX ) thformat="top-space-2x" thtemp="measured-temp" ?&gt;</code>
</li><br>
<li><code>&lt;?ftui-key=varname ?&gt;</code> <br>
VARIABLE specification: Replacement of variables with given parameters in the include statement (or the include header).
The text specified for the corresponding variable will be inserted at the place of the FTUI-Statement in parentheses.