mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 06:39:11 +00:00
weblink fileplot/dbplot converted to SVG device + FHEMWEB cleanup
git-svn-id: https://svn.fhem.de/fhem/trunk@3697 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
bb808092ad
commit
81e67a9258
@ -1,5 +1,6 @@
|
||||
# Add changes at the top of the list. Keep it in ASCII
|
||||
- SVN
|
||||
- change: weblink fileplot/dbplot converted to SVG device (+FHEMWEB cleanup)
|
||||
- change: VIERA: Added support for get dropdown
|
||||
- change: EnOcean: Manufacturer Specific Applications (EEP A5-3F-7F), Shutter:
|
||||
readings position and anglePos are updated automatically, if command
|
||||
|
@ -13,7 +13,6 @@ sub FW_IconURL($);
|
||||
sub FW_iconName($);
|
||||
sub FW_iconPath($);
|
||||
sub FW_answerCall($);
|
||||
sub FW_calcWeblink($$);
|
||||
sub FW_dev2image($;$);
|
||||
sub FW_devState($$@);
|
||||
sub FW_digestCgi($);
|
||||
@ -36,26 +35,20 @@ sub FW_returnFileAsStream($$$$$);
|
||||
sub FW_roomOverview($);
|
||||
sub FW_select($$$$$@);
|
||||
sub FW_serveSpecial($$$$);
|
||||
sub FW_showLog($);
|
||||
sub FW_showRoom();
|
||||
sub FW_style($$);
|
||||
sub FW_submit($$@);
|
||||
sub FW_substcfg($$$$$$);
|
||||
sub FW_textfield($$$);
|
||||
sub FW_textfieldv($$$$);
|
||||
sub FW_updateHashes();
|
||||
sub FW_zoomLink($$$);
|
||||
|
||||
use vars qw($FW_dir); # base directory for web server: the first available
|
||||
# from $modpath/www, $modpath/FHEM
|
||||
use vars qw($FW_icondir); # icon base directory for web server: the first
|
||||
# available from $FW_dir/icons, $FW_dir
|
||||
use vars qw($FW_cssdir); # css directory for web server: the first available
|
||||
# from $FW_dir/css, $FW_dir
|
||||
use vars qw($FW_gplotdir);# gplot directory for web server: the first
|
||||
# available from $FW_dir/gplot,$FW_dir
|
||||
use vars qw($FW_dir); # base directory for web server
|
||||
use vars qw($FW_icondir); # icon base directory
|
||||
use vars qw($FW_cssdir); # css directory
|
||||
use vars qw($FW_gplotdir);# gplot directory
|
||||
use vars qw($MW_dir); # moddir (./FHEM), needed by edit Files in new
|
||||
# structure
|
||||
|
||||
use vars qw($FW_ME); # webname (default is fhem), used by 97_GROUP/weblink
|
||||
use vars qw($FW_ss); # is smallscreen, needed by 97_GROUP/95_VIEW
|
||||
use vars qw($FW_tp); # is touchpad (iPad / etc)
|
||||
@ -64,6 +57,7 @@ use vars qw($FW_sp); # stylesheetPrefix
|
||||
# global variables, also used by 97_GROUP/95_VIEW/95_FLOORPLAN
|
||||
use vars qw(%FW_types); # device types,
|
||||
use vars qw($FW_RET); # Returned data (html)
|
||||
use vars qw($FW_RETTYPE); # image/png or the like
|
||||
use vars qw($FW_wname); # Web instance
|
||||
use vars qw($FW_subdir); # Sub-path in URL, used by FLOORPLAN/weblink
|
||||
use vars qw(%FW_pos); # scroll position
|
||||
@ -73,6 +67,9 @@ use vars qw($FW_plotmode);# Global plot mode (WEB attribute), used by weblink
|
||||
use vars qw($FW_plotsize);# Global plot size (WEB attribute), used by weblink
|
||||
use vars qw(%FW_webArgs); # all arguments specified in the GET
|
||||
use vars qw(@FW_fhemwebjs);# List of fhemweb*js scripts to load
|
||||
use vars qw($FW_detail); # currently selected device for detail view
|
||||
use vars qw($FW_cmdret); # Returned data by the fhem call
|
||||
use vars qw($FW_room); # currently selected room
|
||||
|
||||
my $FW_zlib_checked;
|
||||
my $FW_use_zlib = 1;
|
||||
@ -84,14 +81,10 @@ my $FW_formmethod = "post";
|
||||
# Note: for delivering SVG plots we fork
|
||||
my @FW_httpheader; # HTTP header, line by line
|
||||
my @FW_enc; # Accepted encodings (browser header)
|
||||
my $FW_cmdret; # Returned data by the fhem call
|
||||
my $FW_data; # Filecontent from browser when editing a file
|
||||
my $FW_detail; # currently selected device for detail view
|
||||
my %FW_devs; # hash of from/to entries per device
|
||||
my %FW_icons; # List of icons
|
||||
my @FW_iconDirs; # Directory search order for icons
|
||||
my $FW_RETTYPE; # image/png or the like
|
||||
my $FW_room; # currently selected room
|
||||
my %FW_rooms; # hash of all rooms
|
||||
my %FW_types; # device types, for sorting
|
||||
my %FW_hiddengroup;# hash of hidden groups
|
||||
@ -214,7 +207,6 @@ FW_Read($)
|
||||
$FW_cname = $name;
|
||||
$FW_subdir = "";
|
||||
|
||||
my $ll = GetLogLevel($FW_wname,4);
|
||||
my $c = $hash->{CD};
|
||||
if(!$FW_zlib_checked) {
|
||||
$FW_zlib_checked = 1;
|
||||
@ -223,18 +215,15 @@ FW_Read($)
|
||||
eval { require Compress::Zlib; };
|
||||
if($@) {
|
||||
$FW_use_zlib = 0;
|
||||
Log 1, $@;
|
||||
Log 1, "$FW_wname: Can't load Compress::Zlib, deactivating compression";
|
||||
Log3 $FW_wname, 1, $@;
|
||||
Log3 $FW_wname, 1,
|
||||
"$FW_wname: Can't load Compress::Zlib, deactivating compression";
|
||||
$attr{$FW_wname}{fwcompress} = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# This is a hack... Dont want to do it each time after a fork.
|
||||
if(!$modules{SVG}{LOADED} && -f "$attr{global}{modpath}/FHEM/98_SVG.pm") {
|
||||
my $ret = CommandReload(undef, "98_SVG");
|
||||
Log 1, $ret if($ret);
|
||||
}
|
||||
|
||||
|
||||
# Data from HTTP Client
|
||||
my $buf;
|
||||
@ -242,7 +231,7 @@ FW_Read($)
|
||||
|
||||
if(!defined($ret) || $ret <= 0) {
|
||||
CommandDelete(undef, $name);
|
||||
Log($ll, "Connection closed for $name");
|
||||
Log3 $FW_wname, 4, "Connection closed for $name";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -282,12 +271,12 @@ FW_Read($)
|
||||
if($secret && $basicAuth =~ m/^{.*}$/ || $headerOptions[0]) {
|
||||
eval "use MIME::Base64";
|
||||
if($@) {
|
||||
Log 1, $@;
|
||||
Log3 $FW_wname, 1, $@;
|
||||
|
||||
} else {
|
||||
my ($user, $password) = split(":", decode_base64($secret));
|
||||
$pwok = eval $basicAuth;
|
||||
Log 1, "basicAuth expression: $@" if($@);
|
||||
Log3 $FW_wname, 1, "basicAuth expression: $@" if($@);
|
||||
}
|
||||
}
|
||||
if($headerOptions[0]) {
|
||||
@ -321,7 +310,7 @@ FW_Read($)
|
||||
$hash->{LASTACCESS} = $now;
|
||||
|
||||
$arg = "" if(!defined($arg));
|
||||
Log $ll, "HTTP $name GET $arg";
|
||||
Log3 $FW_wname, 4, "HTTP $name GET $arg";
|
||||
my $pid;
|
||||
if(AttrVal($FW_wname, "plotfork", undef)) {
|
||||
# Process SVG rendering as a parallel process
|
||||
@ -344,7 +333,7 @@ FW_Read($)
|
||||
my $length = length($FW_RET);
|
||||
my $expires = ($cacheable?
|
||||
("Expires: ".localtime($now+900)." GMT\r\n") : "");
|
||||
Log $ll, "$arg / RL: $length / $FW_RETTYPE / $compressed / $expires";
|
||||
Log3 $FW_wname, 4, "$arg / RL:$length / $FW_RETTYPE / $compressed / $expires";
|
||||
print $c "HTTP/1.1 200 OK\r\n",
|
||||
"Content-Length: $length\r\n",
|
||||
$expires, $compressed, $FW_headercors,
|
||||
@ -423,7 +412,7 @@ FW_answerCall($)
|
||||
|
||||
} else {
|
||||
my $c = $me->{CD};
|
||||
Log 4, "$FW_wname: redirecting $arg to $FW_ME";
|
||||
Log3 $FW_wname, 4, "$FW_wname: redirecting $arg to $FW_ME";
|
||||
print $c "HTTP/1.1 302 Found\r\n",
|
||||
"Content-Length: 0\r\n", $FW_headercors,
|
||||
"Location: $FW_ME\r\n\r\n";
|
||||
@ -453,8 +442,6 @@ FW_answerCall($)
|
||||
my $docmd = 0;
|
||||
$docmd = 1 if($cmd &&
|
||||
$cmd !~ /^showlog/ &&
|
||||
$cmd !~ /^logwrapper/ &&
|
||||
$cmd !~ /^toweblink/ &&
|
||||
$cmd !~ /^style / &&
|
||||
$cmd !~ /^edit/);
|
||||
|
||||
@ -474,28 +461,31 @@ FW_answerCall($)
|
||||
}
|
||||
|
||||
##############################
|
||||
# Axels FHEMWEB modules...
|
||||
# FHEMWEB extensions (FLOORPLOAN, SVG_WriteGplot, etc)
|
||||
my $FW_contentFunc;
|
||||
if(defined($data{FWEXT})) {
|
||||
foreach my $k (sort keys %{$data{FWEXT}}) {
|
||||
my $h = $data{FWEXT}{$k};
|
||||
next if($arg !~ m/^$k/ || $h !~ m/HASH/ || !$h->{FUNC});
|
||||
next if($arg !~ m/^$k/);
|
||||
$FW_contentFunc = $h->{CONTENTFUNC};
|
||||
next if($h !~ m/HASH/ || !$h->{FUNC});
|
||||
#Returns undef as FW_RETTYPE if it already sent a HTTP header
|
||||
no strict "refs";
|
||||
#Returns undef if it already sent a HTTP header
|
||||
my $localType;
|
||||
($localType, $FW_RET) = &{$h->{FUNC}}($arg);
|
||||
($FW_RETTYPE, $FW_RET) = &{$h->{FUNC}}($arg);
|
||||
use strict "refs";
|
||||
if($FW_RET && $FW_RET eq "continue") { # Continue displaying the data
|
||||
$FW_RET="";
|
||||
last;
|
||||
}
|
||||
$FW_RETTYPE = $localType;
|
||||
return defined($FW_RETTYPE) ? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#Now execute the command
|
||||
$FW_cmdret = $docmd ? FW_fC($cmd, $cmddev) : "";
|
||||
$FW_cmdret = "";
|
||||
if($docmd) {
|
||||
$FW_cmdret = FW_fC($cmd, $cmddev);
|
||||
if($cmd =~ m/^define +([^ ]+) /) { # "redirect" after define to details
|
||||
$FW_detail = $1;
|
||||
}
|
||||
}
|
||||
|
||||
# Redirect after a command, to clean the browser URL window
|
||||
if($docmd && !$FW_cmdret && AttrVal($FW_wname, "redirectCmds", 1)) {
|
||||
@ -511,25 +501,6 @@ FW_answerCall($)
|
||||
}
|
||||
|
||||
FW_updateHashes();
|
||||
if($cmd =~ m/^showlog /) {
|
||||
FW_showLog($cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if($cmd =~ m/^toweblink (.*)$/) {
|
||||
my @aa = split(":", $1);
|
||||
my $max = 0;
|
||||
for my $d (keys %defs) {
|
||||
$max = ($1+1) if($d =~ m/^wl_(\d+)$/ && $1 >= $max);
|
||||
}
|
||||
$defs{$aa[0]}{currentlogfile} =~ m,([^/]*)$,;
|
||||
$aa[2] = "CURRENT" if($1 eq $aa[2]);
|
||||
$FW_cmdret = FW_fC("define wl_$max weblink fileplot $aa[0]:$aa[1]:$aa[2]");
|
||||
if(!$FW_cmdret) {
|
||||
$FW_detail = "wl_$max";
|
||||
FW_updateHashes();
|
||||
}
|
||||
}
|
||||
|
||||
my $t = AttrVal("global", "title", "Home, Sweet Home");
|
||||
|
||||
@ -605,11 +576,19 @@ FW_answerCall($)
|
||||
}
|
||||
|
||||
FW_roomOverview($cmd);
|
||||
if($FW_contentFunc) {
|
||||
no strict "refs";
|
||||
my $ret = &{$FW_contentFunc}($arg);
|
||||
use strict "refs";
|
||||
return $ret if($ret);
|
||||
}
|
||||
|
||||
if($cmd =~ m/^style /) { FW_style($cmd,undef); }
|
||||
elsif($cmd =~ /^logwrapper/) { return FW_logWrapper($cmd); }
|
||||
elsif($FW_detail) { FW_doDetail($FW_detail); }
|
||||
elsif($FW_room) { FW_showRoom(); }
|
||||
elsif(!$FW_cmdret && AttrVal("global", "motd", "none") ne "none") {
|
||||
elsif(!$FW_cmdret &&
|
||||
!$FW_contentFunc &&
|
||||
AttrVal("global", "motd", "none") ne "none") {
|
||||
my $motd = AttrVal("global","motd",undef);
|
||||
$motd =~ s/\n/<br>/g;
|
||||
FW_pO "<div id=\"content\">$motd</div>";
|
||||
@ -671,6 +650,7 @@ FW_digestCgi($)
|
||||
}
|
||||
|
||||
#####################
|
||||
# create FW_rooms && FW_types
|
||||
sub
|
||||
FW_updateHashes()
|
||||
{
|
||||
@ -991,7 +971,8 @@ FW_roomOverview($)
|
||||
if($defs{$lfn}) { # Add the current Logfile to the list if defined
|
||||
my @l = FW_fileList($defs{$lfn}{logfile});
|
||||
my $fn = pop @l;
|
||||
splice @list, 4,0, ("Logfile","$FW_ME?cmd=logwrapper%20$lfn%20text%20$fn");
|
||||
splice @list, 4,0, ("Logfile",
|
||||
"$FW_ME/FileLog_logWrapper?dev=$lfn&type=text&file=$fn");
|
||||
}
|
||||
|
||||
my @me = split(",", AttrVal($FW_wname, "menuEntries", ""));
|
||||
@ -1243,7 +1224,7 @@ FW_returnFileAsStream($$$$$)
|
||||
}
|
||||
|
||||
if(!open(FH, $path)) {
|
||||
Log 2, "FHEMWEB $FW_wname $path: $!";
|
||||
Log3 $FW_wname, 2, "FHEMWEB $FW_wname $path: $!";
|
||||
FW_pO "<div id=\"content\">$path: $!</div>";
|
||||
return 0;
|
||||
}
|
||||
@ -1279,329 +1260,6 @@ FW_returnFileAsStream($$$$$)
|
||||
return -1;
|
||||
}
|
||||
|
||||
######################
|
||||
# Show the content of the log (plain text), or an image and offer a link
|
||||
# to convert it to a weblink
|
||||
# If text and no reverse required, try to return the data as a stream;
|
||||
sub
|
||||
FW_logWrapper($)
|
||||
{
|
||||
my ($cmd) = @_;
|
||||
my (undef, $d, $type, $file) = split(" ", $cmd, 4);
|
||||
if(defined($type) && $type eq "text") {
|
||||
$defs{$d}{logfile} =~ m,^(.*)/([^/]*)$,; # Dir and File
|
||||
my $path = "$1/$file";
|
||||
$path =~ s/%L/$attr{global}{logdir}/g
|
||||
if($path =~ m/%/ && $attr{global}{logdir});
|
||||
$path = AttrVal($d,"archivedir","") . "/$file" if(!-f $path);
|
||||
|
||||
FW_pO "<div id=\"content\">";
|
||||
FW_pO "<div class=\"tiny\">" if($FW_ss);
|
||||
FW_pO "<pre class=\"log\">";
|
||||
my $suffix = "</pre>".($FW_ss ? "</div>" : "")."</div>";
|
||||
|
||||
my $reverseLogs = AttrVal($FW_wname, "reverseLogs", 0);
|
||||
if(!$reverseLogs) {
|
||||
$suffix .= "</body></html>";
|
||||
return FW_returnFileAsStream($path, $suffix, "text/html", 1, 0);
|
||||
}
|
||||
|
||||
if(!open(FH, $path)) {
|
||||
FW_pO "<div id=\"content\">$path: $!</div></body></html>";
|
||||
return 0;
|
||||
}
|
||||
my $cnt = join("", reverse <FH>);
|
||||
close(FH);
|
||||
$cnt = FW_htmlEscape($cnt);
|
||||
FW_pO $cnt;
|
||||
FW_pO $suffix;
|
||||
|
||||
} else {
|
||||
FW_pO "<div id=\"content\">";
|
||||
FW_pO "<br>";
|
||||
FW_pO FW_zoomLink("cmd=$cmd;zoom=-1", "Zoom-in", "zoom in");
|
||||
FW_pO FW_zoomLink("cmd=$cmd;zoom=1", "Zoom-out","zoom out");
|
||||
FW_pO FW_zoomLink("cmd=$cmd;off=-1", "Prev", "prev");
|
||||
FW_pO FW_zoomLink("cmd=$cmd;off=1", "Next", "next");
|
||||
FW_pO "<table><tr><td>";
|
||||
FW_pO "<td>";
|
||||
my $logtype = $defs{$d}{TYPE};
|
||||
my $wl = "&pos=" . join(";", map {"$_=$FW_pos{$_}"} keys %FW_pos);
|
||||
my $arg = "$FW_ME?cmd=showlog $logtype $d $type $file$wl";
|
||||
if(AttrVal($d,"plotmode",$FW_plotmode) eq "SVG") {
|
||||
my ($w, $h) = split(",", AttrVal($d,"plotsize",$FW_plotsize));
|
||||
FW_pO "<embed src=\"$arg\" type=\"image/svg+xml\" " .
|
||||
"width=\"$w\" height=\"$h\" name=\"$d\"/>\n";
|
||||
|
||||
} else {
|
||||
FW_pO "<img src=\"$arg\"/>";
|
||||
}
|
||||
|
||||
FW_pO "<br>";
|
||||
FW_pH "cmd=toweblink $d:$type:$file", "Convert to weblink";
|
||||
FW_pO "</td>";
|
||||
FW_pO "</td></tr></table>";
|
||||
FW_pO "</div>";
|
||||
|
||||
}
|
||||
FW_pO "</body></html>";
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub
|
||||
FW_readgplotfile($$$)
|
||||
{
|
||||
my ($wl, $gplot_pgm, $file) = @_;
|
||||
|
||||
############################
|
||||
# Read in the template gnuplot file. Digest the #FileLog lines. Replace
|
||||
# the plot directive with our own, as we offer a file for each line
|
||||
my (@filelog, @data, $plot);
|
||||
|
||||
my $wltype = "";
|
||||
$wltype = $defs{$wl}{WLTYPE} if($defs{$wl} && $defs{$wl}{WLTYPE});
|
||||
|
||||
open(FH, $gplot_pgm) || return (FW_fatal("$gplot_pgm: $!"), undef);
|
||||
while(my $l = <FH>) {
|
||||
$l =~ s/\r//g;
|
||||
my $plotfn = undef;
|
||||
if($l =~ m/^#FileLog (.*)$/ &&
|
||||
($wltype eq "fileplot" || $wl eq "FileLog")) {
|
||||
$plotfn = $1;
|
||||
} elsif ($l =~ m/^#DbLog (.*)$/ &&
|
||||
($wltype eq "dbplot" || $wl eq "DbLog")) {
|
||||
$plotfn = $1;
|
||||
} elsif($l =~ "^plot" || $plot) {
|
||||
$plot .= $l;
|
||||
} else {
|
||||
push(@data, $l);
|
||||
}
|
||||
|
||||
if($plotfn) {
|
||||
my $specval = AttrVal($wl, "plotfunction", undef);
|
||||
if ($specval) {
|
||||
my @spec = split(" ",$specval);
|
||||
my $spec_count=1;
|
||||
foreach (@spec) {
|
||||
$plotfn =~ s/<SPEC$spec_count>/$_/g;
|
||||
$spec_count++;
|
||||
}
|
||||
}
|
||||
push(@filelog, $plotfn);
|
||||
}
|
||||
}
|
||||
close(FH);
|
||||
|
||||
return (undef, \@data, $plot, \@filelog);
|
||||
}
|
||||
|
||||
sub
|
||||
FW_substcfg($$$$$$)
|
||||
{
|
||||
my ($splitret, $wl, $cfg, $plot, $file, $tmpfile) = @_;
|
||||
|
||||
# interpret title and label as a perl command and make
|
||||
# to all internal values e.g. $value.
|
||||
|
||||
my $oll = $attr{global}{verbose};
|
||||
$attr{global}{verbose} = 0; # Else the filenames will be Log'ged
|
||||
|
||||
if($file eq "CURRENT") {
|
||||
my @a = split(":", $defs{$wl}{LINK});
|
||||
$file = $defs{$a[0]}{currentlogfile};
|
||||
$file =~ s+.*/++;
|
||||
}
|
||||
my $fileesc = $file;
|
||||
$fileesc =~ s/\\/\\\\/g; # For Windows, by MarkusRR
|
||||
my $title = AttrVal($wl, "title", "\"$fileesc\"");
|
||||
|
||||
$title = AnalyzeCommand($FW_chash, "{ $title }");
|
||||
my $label = AttrVal($wl, "label", undef);
|
||||
my @g_label;
|
||||
if ($label) {
|
||||
@g_label = split("::",$label);
|
||||
foreach (@g_label) {
|
||||
$_ = AnalyzeCommand($FW_chash, "{ $_ }");
|
||||
}
|
||||
}
|
||||
$attr{global}{verbose} = $oll;
|
||||
|
||||
my $gplot_script = join("", @{$cfg});
|
||||
$gplot_script .= $plot if(!$splitret);
|
||||
|
||||
$gplot_script =~ s/<OUT>/$tmpfile/g;
|
||||
$gplot_script =~ s/<IN>/$file/g;
|
||||
|
||||
my $ps = AttrVal($wl,"plotsize",$FW_plotsize);
|
||||
$gplot_script =~ s/<SIZE>/$ps/g;
|
||||
|
||||
$gplot_script =~ s/<TL>/$title/g;
|
||||
my $g_count=1;
|
||||
if ($label) {
|
||||
foreach (@g_label) {
|
||||
$gplot_script =~ s/<L$g_count>/$_/g;
|
||||
$plot =~ s/<L$g_count>/$_/g;
|
||||
$g_count++;
|
||||
}
|
||||
}
|
||||
|
||||
$plot =~ s/\r//g; # For our windows friends...
|
||||
$gplot_script =~ s/\r//g;
|
||||
|
||||
if($splitret == 1) {
|
||||
my @ret = split("\n", $gplot_script);
|
||||
return (\@ret, $plot);
|
||||
} else {
|
||||
return $gplot_script;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
######################
|
||||
# Generate an image from the log via gnuplot or SVG
|
||||
sub
|
||||
FW_showLog($)
|
||||
{
|
||||
my ($cmd) = @_;
|
||||
my (undef, $wl, $d, $type, $file) = split(" ", $cmd, 5);
|
||||
|
||||
my $pm = AttrVal($wl,"plotmode",$FW_plotmode);
|
||||
|
||||
my $gplot_pgm = "$FW_gplotdir/$type.gplot";
|
||||
|
||||
if(!-r $gplot_pgm) {
|
||||
my $msg = "Cannot read $gplot_pgm";
|
||||
Log 1, $msg;
|
||||
|
||||
if($pm =~ m/SVG/) { # FW_fatal for SVG:
|
||||
$FW_RETTYPE = "image/svg+xml";
|
||||
FW_pO '<svg xmlns="http://www.w3.org/2000/svg">';
|
||||
FW_pO '<text x="20" y="20">'.$msg.'</text>';
|
||||
FW_pO '</svg>';
|
||||
return;
|
||||
|
||||
} else {
|
||||
return FW_fatal($msg);
|
||||
|
||||
}
|
||||
}
|
||||
FW_calcWeblink($d,$wl);
|
||||
|
||||
if($pm =~ m/gnuplot/) {
|
||||
|
||||
my $tmpfile = "/tmp/file.$$";
|
||||
my $errfile = "/tmp/gnuplot.err";
|
||||
|
||||
if($pm eq "gnuplot" || !$FW_devs{$d}{from}) {
|
||||
|
||||
# Looking for the logfile....
|
||||
$defs{$d}{logfile} =~ m,^(.*)/([^/]*)$,; # Dir and File
|
||||
my $path = "$1/$file";
|
||||
$path = AttrVal($d,"archivedir","") . "/$file" if(!-f $path);
|
||||
return FW_fatal("Cannot read $path") if(!-r $path);
|
||||
|
||||
my ($err, $cfg, $plot, undef) = FW_readgplotfile($wl, $gplot_pgm, $file);
|
||||
return $err if($err);
|
||||
my $gplot_script = FW_substcfg(0, $wl, $cfg, $plot, $file,$tmpfile);
|
||||
|
||||
my $fr = AttrVal($wl, "fixedrange", undef);
|
||||
if($fr) {
|
||||
$fr =~ s/ /\":\"/;
|
||||
$fr = "set xrange [\"$fr\"]\n";
|
||||
$gplot_script =~ s/(set timefmt ".*")/$1\n$fr/;
|
||||
}
|
||||
|
||||
open(FH, "|gnuplot >> $errfile 2>&1");# feed it to gnuplot
|
||||
print FH $gplot_script;
|
||||
close(FH);
|
||||
|
||||
} elsif($pm eq "gnuplot-scroll") {
|
||||
|
||||
|
||||
my ($err, $cfg, $plot, $flog) = FW_readgplotfile($wl, $gplot_pgm, $file);
|
||||
return $err if($err);
|
||||
|
||||
|
||||
# Read the data from the filelog
|
||||
my ($f,$t)=($FW_devs{$d}{from}, $FW_devs{$d}{to});
|
||||
my $oll = $attr{global}{verbose};
|
||||
$attr{global}{verbose} = 0; # Else the filenames will be Log'ged
|
||||
my @path = split(" ", FW_fC("get $d $file $tmpfile $f $t " .
|
||||
join(" ", @{$flog})));
|
||||
$attr{global}{verbose} = $oll;
|
||||
|
||||
|
||||
# replace the path with the temporary filenames of the filelog output
|
||||
my $i = 0;
|
||||
$plot =~ s/\".*?using 1:[^ ]+ /"\"$path[$i++]\" using 1:2 "/gse;
|
||||
my $xrange = "set xrange [\"$f\":\"$t\"]\n";
|
||||
foreach my $p (@path) { # If the file is empty, write a 0 line
|
||||
next if(!-z $p);
|
||||
open(FH, ">$p");
|
||||
print FH "$f 0\n";
|
||||
close(FH);
|
||||
}
|
||||
|
||||
my $gplot_script = FW_substcfg(0, $wl, $cfg, $plot, $file, $tmpfile);
|
||||
|
||||
open(FH, "|gnuplot >> $errfile 2>&1");# feed it to gnuplot
|
||||
print FH $gplot_script, $xrange, $plot;
|
||||
close(FH);
|
||||
foreach my $p (@path) {
|
||||
unlink($p);
|
||||
}
|
||||
}
|
||||
$FW_RETTYPE = "image/png";
|
||||
open(FH, "$tmpfile.png"); # read in the result and send it
|
||||
binmode (FH); # necessary for Windows
|
||||
FW_pO join("", <FH>);
|
||||
close(FH);
|
||||
unlink("$tmpfile.png");
|
||||
|
||||
} elsif($pm eq "SVG") {
|
||||
|
||||
my ($err, $cfg, $plot, $flog) = FW_readgplotfile($wl, $gplot_pgm, $file);
|
||||
return $err if($err);
|
||||
|
||||
my ($f,$t)=($FW_devs{$d}{from}, $FW_devs{$d}{to});
|
||||
$f = 0 if(!$f); # From the beginning of time...
|
||||
$t = 9 if(!$t); # till the end
|
||||
|
||||
my $ret;
|
||||
if(!$modules{SVG}{LOADED}) {
|
||||
$ret = CommandReload(undef, "98_SVG");
|
||||
Log 1, $ret if($ret);
|
||||
}
|
||||
Log 5, "plotcommand: get $d $file INT $f $t " . join(" ", @{$flog});
|
||||
|
||||
$FW_RETTYPE = "image/svg+xml";
|
||||
|
||||
(my $cachedate = TimeNow()) =~ s/ /_/g;
|
||||
my $SVGcache = (AttrVal($FW_wname, "SVGcache", undef) && $t lt $cachedate);
|
||||
my $cDir = "$FW_dir/SVGcache";
|
||||
my $cName = "$cDir/$wl-$f-$t.svg";
|
||||
if($SVGcache && open(CFH, $cName)) {
|
||||
FW_pO join("", <CFH>);
|
||||
close(CFH);
|
||||
|
||||
} else {
|
||||
FW_fC("get $d $file INT $f $t " . join(" ", @{$flog}), 1);
|
||||
($cfg, $plot) = FW_substcfg(1, $wl, $cfg, $plot, $file, "<OuT>");
|
||||
$ret = SVG_render($wl, $f, $t, $cfg,
|
||||
$internal_data, $plot, $FW_wname, $FW_cssdir, $flog);
|
||||
FW_pO $ret;
|
||||
if($SVGcache) {
|
||||
mkdir($cDir) if(! -d $cDir);
|
||||
if(open(CFH, ">$cName")) {
|
||||
print CFH $ret;
|
||||
close(CFH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
##################
|
||||
sub
|
||||
@ -1667,176 +1325,6 @@ FW_submit($$@)
|
||||
return $s;
|
||||
}
|
||||
|
||||
##################
|
||||
# Generate the zoom and scroll images with links if appropriate
|
||||
sub
|
||||
FW_zoomLink($$$)
|
||||
{
|
||||
my ($cmd, $img, $alt) = @_;
|
||||
|
||||
my $prf;
|
||||
$cmd =~ m/^(.*);([^;]*)$/;
|
||||
($prf, $cmd) = ($1, $2) if($2);
|
||||
my ($d,$off) = split("=", $cmd, 2);
|
||||
|
||||
my $val = $FW_pos{$d};
|
||||
$cmd = ($FW_detail ? "detail=$FW_detail":
|
||||
($prf ? $prf : "room=$FW_room")) . "&pos=";
|
||||
|
||||
if($d eq "zoom") {
|
||||
|
||||
my $n = 0;
|
||||
my @FW_zoom = ("hour","qday","day","week","month","year");
|
||||
my %FW_zoom = map { $_, $n++ } @FW_zoom;
|
||||
|
||||
$val = "day" if(!$val);
|
||||
$val = $FW_zoom{$val};
|
||||
return "" if(!defined($val) || $val+$off < 0 || $val+$off >= int(@FW_zoom));
|
||||
$val = $FW_zoom[$val+$off];
|
||||
return "" if(!$val);
|
||||
|
||||
# Approximation of the next offset.
|
||||
my $w_off = $FW_pos{off};
|
||||
$w_off = 0 if(!$w_off);
|
||||
|
||||
if ($val eq "hour") {
|
||||
$w_off = $w_off*6;
|
||||
} elsif($val eq "qday") {
|
||||
$w_off = ($off < 0) ? $w_off*4 : int($w_off/6);
|
||||
} elsif($val eq "day") {
|
||||
$w_off = ($off < 0) ? $w_off*7 : int($w_off/4);
|
||||
} elsif($val eq "week") {
|
||||
$w_off = ($off < 0) ? $w_off*4 : int($w_off/7);
|
||||
} elsif($val eq "month") {
|
||||
$w_off = ($off < 0) ? $w_off*12: int($w_off/4);
|
||||
} elsif($val eq "year") {
|
||||
$w_off = int($w_off/12);
|
||||
}
|
||||
$cmd .= "zoom=$val;off=$w_off";
|
||||
|
||||
} else {
|
||||
|
||||
return "" if((!$val && $off > 0) || ($val && $val+$off > 0)); # no future
|
||||
$off=($val ? $val+$off : $off);
|
||||
my $zoom=$FW_pos{zoom};
|
||||
$zoom = 0 if(!$zoom);
|
||||
$cmd .= "zoom=$zoom;off=$off";
|
||||
|
||||
}
|
||||
|
||||
return " ".FW_pHPlain("$cmd", FW_makeImage($img, $alt));
|
||||
}
|
||||
|
||||
##################
|
||||
# Calculate either the number of scrollable weblinks (for $d = undef) or
|
||||
# for the device the valid from and to dates for the given zoom and offset
|
||||
sub
|
||||
FW_calcWeblink($$)
|
||||
{
|
||||
my ($d,$wl) = @_;
|
||||
|
||||
my $pm = AttrVal($d,"plotmode",$FW_plotmode);
|
||||
return if($pm eq "gnuplot");
|
||||
|
||||
my $frx;
|
||||
if($defs{$wl}) {
|
||||
my $fr = AttrVal($wl, "fixedrange", undef);
|
||||
if($fr) {
|
||||
#klaus fixed range day, week, month or year
|
||||
if($fr eq "day" || $fr eq "week" || $fr eq "month" || $fr eq "year" ) {
|
||||
$frx=$fr;
|
||||
|
||||
} else {
|
||||
my @range = split(" ", $fr);
|
||||
my @t = localtime;
|
||||
$FW_devs{$d}{from} = ResolveDateWildcards($range[0], @t);
|
||||
$FW_devs{$d}{to} = ResolveDateWildcards($range[1], @t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $off = $FW_pos{$d};
|
||||
$off = 0 if(!$off);
|
||||
$off += $FW_pos{off} if($FW_pos{off});
|
||||
|
||||
my $now = time();
|
||||
my $zoom = $FW_pos{zoom};
|
||||
$zoom = "day" if(!$zoom);
|
||||
$zoom = $frx if ($frx); #for fixedrange {day|week|...} klaus
|
||||
|
||||
|
||||
if($zoom eq "hour") {
|
||||
my $t = $now + $off*3600;
|
||||
my @l = localtime($t);
|
||||
$FW_devs{$d}{from}
|
||||
= sprintf("%04d-%02d-%02d_%02d:00:00",$l[5]+1900,$l[4]+1,$l[3],$l[2]);
|
||||
@l = localtime($t+3600);
|
||||
$FW_devs{$d}{to}
|
||||
= sprintf("%04d-%02d-%02d_%02d:00:01",$l[5]+1900,$l[4]+1,$l[3],$l[2]);
|
||||
|
||||
} elsif($zoom eq "qday") {
|
||||
my $t = $now + $off*21600;
|
||||
my @l = localtime($t);
|
||||
$l[2] = int($l[2]/6)*6;
|
||||
$FW_devs{$d}{from} =
|
||||
sprintf("%04d-%02d-%02d_%02d:00:00",$l[5]+1900,$l[4]+1,$l[3],$l[2]);
|
||||
@l = localtime($t+21600);
|
||||
$l[2] = int($l[2]/6)*6;
|
||||
$FW_devs{$d}{to} =
|
||||
sprintf("%04d-%02d-%02d_%02d:00:01",$l[5]+1900,$l[4]+1,$l[3],$l[2]);
|
||||
|
||||
} elsif($zoom eq "day") {
|
||||
my $t = $now + $off*86400;
|
||||
my @l = localtime($t);
|
||||
$FW_devs{$d}{from} =
|
||||
sprintf("%04d-%02d-%02d_00:00:00",$l[5]+1900,$l[4]+1,$l[3]);
|
||||
@l = localtime($t+86400);
|
||||
$FW_devs{$d}{to} =
|
||||
sprintf("%04d-%02d-%02d_00:00:01",$l[5]+1900,$l[4]+1,$l[3]);
|
||||
|
||||
} elsif($zoom eq "week") {
|
||||
my @l = localtime($now);
|
||||
my $start = (AttrVal($FW_wname, "endPlotToday", undef) ? 6 : $l[6]);
|
||||
my $t = $now - ($start*86400) + ($off*86400)*7;
|
||||
@l = localtime($t);
|
||||
$FW_devs{$d}{from} =
|
||||
sprintf("%04d-%02d-%02d_00:00:00",$l[5]+1900,$l[4]+1,$l[3]);
|
||||
@l = localtime($t+7*86400);
|
||||
$FW_devs{$d}{to} =
|
||||
sprintf("%04d-%02d-%02d_00:00:01",$l[5]+1900,$l[4]+1,$l[3]);
|
||||
|
||||
} elsif($zoom eq "month") {
|
||||
my ($endDay, @l);
|
||||
if(AttrVal($FW_wname, "endPlotToday", undef)) {
|
||||
@l = localtime($now+86400);
|
||||
$endDay = $l[3];
|
||||
$off--;
|
||||
} else {
|
||||
@l = localtime($now);
|
||||
$endDay = 1;
|
||||
}
|
||||
while($off < -12) { # Correct the year
|
||||
$off += 12; $l[5]--;
|
||||
}
|
||||
$l[4] += $off;
|
||||
$l[4] += 12, $l[5]-- if($l[4] < 0);
|
||||
$FW_devs{$d}{from} =
|
||||
sprintf("%04d-%02d-%02d_00:00:00", $l[5]+1900, $l[4]+1,$endDay);
|
||||
$l[4]++;
|
||||
$l[4] = 0, $l[5]++ if($l[4] == 12);
|
||||
$FW_devs{$d}{to} =
|
||||
sprintf("%04d-%02d-%02d_00:00:01", $l[5]+1900, $l[4]+1,$endDay);
|
||||
|
||||
} elsif($zoom eq "year") {
|
||||
my @l = localtime($now);
|
||||
$l[5] += $off;
|
||||
$FW_devs{$d}{from} = sprintf("%04d-01-01_00:00:00", $l[5]+1900);
|
||||
$FW_devs{$d}{to} = sprintf("%04d-01-01_00:00:01", $l[5]+1901);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
##################
|
||||
sub
|
||||
FW_displayFileList($@)
|
||||
@ -2077,12 +1565,13 @@ FW_pHPlain(@)
|
||||
{
|
||||
my ($link, $txt, $td) = @_;
|
||||
|
||||
$link = "?$link" if($link !~ m+^/+);
|
||||
my $ret = "";
|
||||
$ret .= "<td>" if($td);
|
||||
if($FW_ss || $FW_tp) {
|
||||
$ret .= "<a onClick=\"location.href='$FW_ME$FW_subdir?$link'\">$txt</a>";
|
||||
$ret .= "<a onClick=\"location.href='$FW_ME$FW_subdir$link'\">$txt</a>";
|
||||
} else {
|
||||
$ret .= "<a href=\"$FW_ME$FW_subdir?$link\">$txt</a>";
|
||||
$ret .= "<a href=\"$FW_ME$FW_subdir$link\">$txt</a>";
|
||||
}
|
||||
$ret .= "</td>" if($td);
|
||||
return $ret;
|
||||
@ -2301,7 +1790,7 @@ FW_dev2image($;$)
|
||||
my $devStateIcon = AttrVal($name, "devStateIcon", undef);
|
||||
if(defined($devStateIcon) && $devStateIcon =~ m/^{.*}$/) {
|
||||
my ($html, $link) = eval $devStateIcon;
|
||||
Log 1, "devStateIcon $name: $@" if($@);
|
||||
Log3 $FW_wname, 1, "devStateIcon $name: $@" if($@);
|
||||
return ($html, $link, 1) if(defined($html) && $html =~ m/^<.*>$/s);
|
||||
$devStateIcon = $html;
|
||||
}
|
||||
@ -2632,7 +2121,7 @@ FW_closeOldClients()
|
||||
next if(!$defs{$dev}{TYPE} || $defs{$dev}{TYPE} ne "FHEMWEB" ||
|
||||
!$defs{$dev}{LASTACCESS} || $defs{$dev}{inform} ||
|
||||
($now - $defs{$dev}{LASTACCESS}) < 60);
|
||||
Log 4, "Closing connection $dev";
|
||||
Log3 $FW_wname, 4, "Closing connection $dev";
|
||||
FW_Undef($defs{$dev}, "");
|
||||
delete $defs{$dev};
|
||||
}
|
||||
|
@ -5,9 +5,21 @@ package main;
|
||||
use strict;
|
||||
use warnings;
|
||||
use IO::File;
|
||||
#use Devel::Size qw(size total_size);
|
||||
use vars qw($FW_ss); # is smallscreen
|
||||
|
||||
# This block is only needed when FileLog is loaded bevore FHEMWEB
|
||||
sub FW_pO(@);
|
||||
sub FW_pH(@);
|
||||
use vars qw($FW_ME); # webname (default is fhem), needed by 97_GROUP
|
||||
use vars qw($FW_RET); # Returned data (html)
|
||||
use vars qw($FW_RETTYPE);
|
||||
use vars qw($FW_cmdret); # error msg forwarding from toSVG
|
||||
use vars qw($FW_detail); # for redirect after toSVG
|
||||
use vars qw($FW_plotmode);# Global plot mode (WEB attribute), used by weblink
|
||||
use vars qw($FW_plotsize);# Global plot size (WEB attribute), used by weblink
|
||||
use vars qw($FW_ss); # is smallscreen
|
||||
use vars qw($FW_wname); # Web instance
|
||||
use vars qw(%FW_pos); # scroll position
|
||||
use vars qw(%FW_webArgs); # all arguments specified in the GET
|
||||
|
||||
sub seekTo($$$$);
|
||||
|
||||
@ -30,6 +42,8 @@ FileLog_Initialize($)
|
||||
|
||||
$hash->{FW_summaryFn} = "FileLog_fhemwebFn";
|
||||
$hash->{FW_detailFn} = "FileLog_fhemwebFn";
|
||||
$data{FWEXT}{"/FileLog_toSVG"}{CONTENTFUNC} = "FileLog_toSVG";
|
||||
$data{FWEXT}{"/FileLog_logWrapper"}{CONTENTFUNC} = "FileLog_logWrapper";
|
||||
}
|
||||
|
||||
|
||||
@ -254,6 +268,15 @@ FileLog_Set($@)
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
FileLog_loadSVG()
|
||||
{
|
||||
if(!$modules{SVG}{LOADED} && -f "$attr{global}{modpath}/FHEM/98_SVG.pm") {
|
||||
my $ret = CommandReload(undef, "98_SVG");
|
||||
Log 1, $ret if($ret);
|
||||
}
|
||||
}
|
||||
|
||||
#########################
|
||||
sub
|
||||
FileLog_fhemwebFn($$$$)
|
||||
@ -277,7 +300,7 @@ FileLog_fhemwebFn($$$$)
|
||||
}
|
||||
my ($lt, $name) = split(":", $ln);
|
||||
$name = $lt if(!$name);
|
||||
$ret .= FW_pH("cmd=logwrapper $d $lt $f",
|
||||
$ret .= FW_pH("$FW_ME/FileLog_logWrapper&dev=$d&type=$lt&file=$f",
|
||||
"<div class=\"dval\">$name</div>", 1, "dval", 1);
|
||||
}
|
||||
$ret .= "</tr>";
|
||||
@ -337,17 +360,118 @@ FileLog_fhemwebFn($$$$)
|
||||
$ret .= "</table>";
|
||||
|
||||
my $newIdx=1;
|
||||
while($defs{"wl_${d}_$newIdx"}) {
|
||||
while($defs{"SVG_${d}_$newIdx"}) {
|
||||
$newIdx++;
|
||||
}
|
||||
my $name = "wl_${d}_$newIdx";
|
||||
$ret .= FW_pH("cmd=define $name weblink fileplot $d:template:CURRENT;".
|
||||
my $name = "SVG_${d}_$newIdx";
|
||||
$ret .= FW_pH("cmd=define $name SVG $d:template:CURRENT;".
|
||||
"set $name copyGplotFile&detail=$name",
|
||||
"<div class=\"dval\">Create new SVG plot</div>", 0, "dval", 1);
|
||||
"<div class=\"dval\">Create SVG plot</div>", 0, "dval", 1);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
###################################
|
||||
sub
|
||||
FileLog_toSVG($)
|
||||
{
|
||||
my ($arg) = @_;
|
||||
FW_digestCgi($arg);
|
||||
return("text/html;", "bad url: cannot create SVG def")
|
||||
if(!defined($FW_webArgs{arg}));
|
||||
|
||||
my @aa = split(":", $FW_webArgs{arg});
|
||||
my $max = 0;
|
||||
for my $d (keys %defs) {
|
||||
$max = ($1+1) if($d =~ m/^SVG_(\d+)$/ && $1 >= $max);
|
||||
}
|
||||
$defs{$aa[0]}{currentlogfile} =~ m,([^/]*)$,;
|
||||
$aa[2] = "CURRENT" if($1 eq $aa[2]);
|
||||
$FW_cmdret = FW_fC("define SVG_$max SVG $aa[0]:$aa[1]:$aa[2]");
|
||||
$FW_detail = "SVG_$max" if(!$FW_cmdret);
|
||||
return;
|
||||
}
|
||||
|
||||
######################
|
||||
# Show the content of the log (plain text), or an image and offer a link
|
||||
# to convert it to an SVG instance
|
||||
# If text and no reverse required, try to return the data as a stream;
|
||||
sub
|
||||
FileLog_logWrapper($)
|
||||
{
|
||||
my ($cmd) = @_;
|
||||
|
||||
my $d = $FW_webArgs{dev};
|
||||
my $type = $FW_webArgs{type};
|
||||
my $file = $FW_webArgs{file};
|
||||
my $ret = "";
|
||||
|
||||
if(!$d || !$type || !$file) {
|
||||
FW_pO '<div id="content">FileLog_logWrapper: bad arguments</div>';
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(defined($type) && $type eq "text") {
|
||||
$defs{$d}{logfile} =~ m,^(.*)/([^/]*)$,; # Dir and File
|
||||
my $path = "$1/$file";
|
||||
$path =~ s/%L/$attr{global}{logdir}/g
|
||||
if($path =~ m/%/ && $attr{global}{logdir});
|
||||
$path = AttrVal($d,"archivedir","") . "/$file" if(!-f $path);
|
||||
|
||||
FW_pO "<div id=\"content\">";
|
||||
FW_pO "<div class=\"tiny\">" if($FW_ss);
|
||||
FW_pO "<pre class=\"log\">";
|
||||
my $suffix = "</pre>".($FW_ss ? "</div>" : "")."</div>";
|
||||
|
||||
my $reverseLogs = AttrVal($FW_wname, "reverseLogs", 0);
|
||||
if(!$reverseLogs) {
|
||||
$suffix .= "</body></html>";
|
||||
return FW_returnFileAsStream($path, $suffix, "text/html", 1, 0);
|
||||
}
|
||||
|
||||
if(!open(FH, $path)) {
|
||||
FW_pO "<div id=\"content\">$path: $!</div></body></html>";
|
||||
return 0;
|
||||
}
|
||||
my $cnt = join("", reverse <FH>);
|
||||
close(FH);
|
||||
$cnt = FW_htmlEscape($cnt);
|
||||
FW_pO $cnt;
|
||||
FW_pO $suffix;
|
||||
|
||||
} else {
|
||||
FileLog_loadSVG();
|
||||
FW_pO "<div id=\"content\">";
|
||||
FW_pO "<br>";
|
||||
FW_pO SVG_zoomLink("$cmd;zoom=-1", "Zoom-in", "zoom in");
|
||||
FW_pO SVG_zoomLink("$cmd;zoom=1", "Zoom-out","zoom out");
|
||||
FW_pO SVG_zoomLink("$cmd;off=-1", "Prev", "prev");
|
||||
FW_pO SVG_zoomLink("$cmd;off=1", "Next", "next");
|
||||
FW_pO "<table><tr><td>";
|
||||
FW_pO "<td>";
|
||||
my $logtype = $defs{$d}{TYPE};
|
||||
my $wl = "&pos=" . join(";", map {"$_=$FW_pos{$_}"} keys %FW_pos);
|
||||
my $arg = "$FW_ME/SVG_showLog&dev=$logtype&logdev=$d".
|
||||
"&gplotfile=$type&logfile=$file$wl";
|
||||
if(AttrVal($d,"plotmode",$FW_plotmode) eq "SVG") {
|
||||
my ($w, $h) = split(",", AttrVal($d,"plotsize",$FW_plotsize));
|
||||
FW_pO "<embed src=\"$arg\" type=\"image/svg+xml\" " .
|
||||
"width=\"$w\" height=\"$h\" name=\"$d\"/>\n";
|
||||
|
||||
} else {
|
||||
FW_pO "<img src=\"$arg\"/>";
|
||||
}
|
||||
|
||||
FW_pO "<br>";
|
||||
FW_pH "$FW_ME/FileLog_toSVG&arg=$d:$type:$file", "Create SVG instance";
|
||||
FW_pO "</td>";
|
||||
FW_pO "</td></tr></table>";
|
||||
FW_pO "</div>";
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
###################################
|
||||
# We use this function to be able to scroll/zoom in the plots created from the
|
||||
|
@ -7,24 +7,819 @@ use warnings;
|
||||
use POSIX;
|
||||
#use Devel::Size qw(size total_size);
|
||||
|
||||
# This block is only needed when SVG is loaded bevore FHEMWEB
|
||||
sub FW_pO(@);
|
||||
use vars qw($FW_ME); # webname (default is fhem), needed by 97_GROUP
|
||||
use vars qw($FW_RET); # Returned data (html)
|
||||
use vars qw($FW_RETTYPE); # image/png or the like
|
||||
use vars qw($FW_cssdir); # css directory
|
||||
use vars qw($FW_detail); # currently selected device for detail view
|
||||
use vars qw($FW_dir); # base directory for web server
|
||||
use vars qw($FW_gplotdir);# gplot directory for web server: the first
|
||||
use vars qw($FW_plotmode);# Global plot mode (WEB attribute), used by weblink
|
||||
use vars qw($FW_plotsize);# Global plot size (WEB attribute), used by weblink
|
||||
use vars qw($FW_room); # currently selected room
|
||||
use vars qw($FW_subdir); # Sub-path in URL, used by FLOORPLAN/weblink
|
||||
use vars qw($FW_wname); # Web instance
|
||||
use vars qw(%FW_hiddenroom); # hash of hidden rooms, used by weblink
|
||||
use vars qw(%FW_pos); # scroll position
|
||||
use vars qw(%FW_webArgs); # all arguments specified in the GET
|
||||
|
||||
|
||||
sub SVG_render($$$$$$$$$);
|
||||
sub SVG_time_to_sec($);
|
||||
sub SVG_fmtTime($$);
|
||||
sub SVG_time_align($$);
|
||||
sub SVG_doround($$$);
|
||||
sub SVG_pO($);
|
||||
my $SVG_RET; # Returned data (SVG)
|
||||
|
||||
sub SVG_calcWeblink($$);
|
||||
sub SVG_doround($$$);
|
||||
sub SVG_fmtTime($$);
|
||||
sub SVG_pO($);
|
||||
sub SVG_readgplotfile($$$);
|
||||
sub SVG_render($$$$$$$$$);
|
||||
sub SVG_showLog($);
|
||||
sub SVG_substcfg($$$$$$);
|
||||
sub SVG_time_align($$);
|
||||
sub SVG_time_to_sec($);
|
||||
|
||||
my ($SVG_lt, $SVG_ltstr);
|
||||
my %SVG_devs; # hash of from/to entries per device
|
||||
|
||||
#####################################
|
||||
sub
|
||||
SVG_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{DefFn} = "SVG_Define";
|
||||
$hash->{AttrList} = "fixedrange plotsize label title plotfunction";
|
||||
$hash->{SetFn} = "SVG_Set";
|
||||
$hash->{FW_summaryFn} = "SVG_FwFn";
|
||||
$hash->{FW_detailFn} = "SVG_FwFn";
|
||||
$hash->{FW_atPageEnd} = 1;
|
||||
$data{FWEXT}{"/SVG_WriteGplot"}{CONTENTFUNC} = "SVG_WriteGplot";
|
||||
$data{FWEXT}{"/SVG_showLog"}{FUNC} = "SVG_showLog";
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
SVG_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my ($name, $type, $arg) = split("[ \t]+", $def, 3);
|
||||
|
||||
if(!$arg || $arg !~ m/^(.*):(.*):(.*)$/) {
|
||||
return "Usage: define <name> SVG <logdevice>:<gnuplot-file>:<logfile>";
|
||||
}
|
||||
|
||||
$hash->{LOGDEVICE} = $1;
|
||||
$hash->{GPLOTFILE} = $2;
|
||||
$hash->{LOGFILE} = $3;
|
||||
$hash->{STATE} = "initialized";
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
##################
|
||||
sub
|
||||
SVG_Set($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
my $me = $hash->{NAME};
|
||||
return "no set argument specified" if(int(@a) < 2);
|
||||
|
||||
my $cmd = $a[1];
|
||||
return "Unknown argument $cmd, choose one of copyGplotFile:noArg"
|
||||
if($cmd ne "copyGplotFile");
|
||||
|
||||
my $srcName = "$FW_gplotdir/$hash->{GPLOTFILE}.gplot";
|
||||
$hash->{GPLOTFILE} = $hash->{NAME};
|
||||
my $dstName = "$FW_gplotdir/$hash->{GPLOTFILE}.gplot";
|
||||
return "this is already a unique gplot file" if($srcName eq $dstName);
|
||||
$hash->{DEF} = $hash->{LOGDEVICE} . ":".
|
||||
$hash->{GPLOTFILE} . ":".
|
||||
$hash->{LOGFILE};
|
||||
|
||||
open(SFH, $srcName) || return "Can't open $srcName: $!";
|
||||
open(DFH, ">$dstName") || return "Can't open $dstName: $!";
|
||||
while(my $l = <SFH>) {
|
||||
print DFH $l;
|
||||
}
|
||||
close(SFH); close(DFH);
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
##################
|
||||
sub
|
||||
SVG_FwDetail($@)
|
||||
{
|
||||
my ($d, $text, $nobr)= @_;
|
||||
return "" if(AttrVal($d, "group", ""));
|
||||
my $alias= AttrVal($d, "alias", $d);
|
||||
|
||||
my $ret = ($nobr ? "" : "<br>");
|
||||
$ret .= "$text " if($text);
|
||||
$ret .= FW_pHPlain("detail=$d", $alias) if(!$FW_subdir);
|
||||
$ret .= "<br>";
|
||||
return $ret;
|
||||
}
|
||||
|
||||
##################
|
||||
sub
|
||||
SVG_FwFn($$$$)
|
||||
{
|
||||
my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
|
||||
my $hash = $defs{$d};
|
||||
my $ld = $defs{$hash->{LOGDEVICE}};
|
||||
my $ret = "";
|
||||
|
||||
# plots navigation buttons
|
||||
if((!$pageHash || !$pageHash->{buttons}) &&
|
||||
AttrVal($d, "fixedrange", "x") !~ m/^[ 0-9:-]*$/) {
|
||||
|
||||
$ret .= SVG_zoomLink("zoom=-1", "Zoom-in", "zoom in");
|
||||
$ret .= SVG_zoomLink("zoom=1", "Zoom-out","zoom out");
|
||||
$ret .= SVG_zoomLink("off=-1", "Prev", "prev");
|
||||
$ret .= SVG_zoomLink("off=1", "Next", "next");
|
||||
$pageHash->{buttons} = 1 if($pageHash);
|
||||
$ret .= "<br>";
|
||||
}
|
||||
|
||||
my $arg="$FW_ME/SVG_showLog?dev=$d".
|
||||
"&logdev=$hash->{LOGDEVICE}".
|
||||
"&gplotfile=$hash->{GPLOTFILE}".
|
||||
"&logfile=$hash->{LOGFILE}".
|
||||
"&pos=" . join(";", map {"$_=$FW_pos{$_}"} keys %FW_pos);
|
||||
|
||||
if(AttrVal($d,"plotmode",$FW_plotmode) eq "SVG") {
|
||||
my ($w, $h) = split(",", AttrVal($d,"plotsize",$FW_plotsize));
|
||||
$ret .= "<div class=\"SVGplot\">";
|
||||
$ret .= "<embed src=\"$arg\" type=\"image/svg+xml\" " .
|
||||
"width=\"$w\" height=\"$h\" name=\"$d\"/>\n";
|
||||
$ret .= "</div>";
|
||||
|
||||
} else {
|
||||
$ret .= "<img src=\"$arg\"/>";
|
||||
}
|
||||
|
||||
if(!$pageHash) {
|
||||
if($FW_plotmode eq "SVG") {
|
||||
$ret .= SVG_PEdit($FW_wname,$d,$room,$pageHash) . "<br>";
|
||||
}
|
||||
|
||||
} else {
|
||||
$ret .= SVG_FwDetail($d, "", 1) if(!$FW_hiddenroom{detail});
|
||||
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub
|
||||
SVG_cb($$$)
|
||||
{
|
||||
my ($v,$t,$c) = @_;
|
||||
$c = ($c ? " checked" : "");
|
||||
return "<td>$t <input type=\"checkbox\" name=\"$v\" value=\"$v\"$c></td>";
|
||||
}
|
||||
|
||||
sub
|
||||
SVG_txt($$$$)
|
||||
{
|
||||
my ($v,$t,$c,$sz) = @_;
|
||||
$c = "" if(!defined($c));
|
||||
$c =~ s/"/\"/g;
|
||||
return "$t <input type=\"text\" name=\"$v\" size=\"$sz\" ".
|
||||
"value=\"$c\"/>";
|
||||
}
|
||||
|
||||
sub
|
||||
SVG_sel($$$@)
|
||||
{
|
||||
my ($v,$l,$c,$fnData) = @_;
|
||||
my @al = split(",",$l);
|
||||
$c =~ s/\\x3a/:/g if($c);
|
||||
return FW_select($v,$v,\@al,$c, "set", $fnData);
|
||||
}
|
||||
|
||||
sub
|
||||
SVG_getRegFromFile($)
|
||||
{
|
||||
my ($fName) = @_;
|
||||
my $fh = new IO::File $fName;
|
||||
if(!$fh) {
|
||||
Log 1, "$fName: $!";
|
||||
return (3, "NoFile", "NoFile");
|
||||
}
|
||||
$fh->seek(0, 2); # Go to the end
|
||||
my $sz = $fh->tell;
|
||||
$fh->seek($sz > 65536 ? $sz-65536 : 0, 0);
|
||||
my $data;
|
||||
$data = <$fh> if($sz > 65536); # discard the first/partial line
|
||||
my $maxcols = 0;
|
||||
my %h;
|
||||
while($data = <$fh>) {
|
||||
my @cols = split(" ", $data);
|
||||
next if(@cols < 3);
|
||||
$maxcols = @cols if(@cols > $maxcols);
|
||||
$cols[2] = "*" if($cols[2] =~ m/^[-\.\d]+$/);
|
||||
$h{"$cols[1].$cols[2]"} = $data;
|
||||
$h{"$cols[1].*"} = "" if($cols[2] ne "*");
|
||||
}
|
||||
$fh->close();
|
||||
return ($maxcols+1,
|
||||
join(",", sort keys %h),
|
||||
join("<br>", grep /.+/,map { $h{$_} } sort keys %h)),
|
||||
close(FH);
|
||||
}
|
||||
|
||||
sub
|
||||
SVG_addTics($$)
|
||||
{
|
||||
my ($in, $p) = @_;
|
||||
return if(!$in || $in !~ m/^\((.*)\)$/);
|
||||
map { $p->{"\"$2\""}=1 if(m/^ *([^ ]+) ([^ ]+) */); } split(",",$1);
|
||||
}
|
||||
|
||||
############################
|
||||
# gnuplot file "editor"
|
||||
sub
|
||||
SVG_PEdit($$$$)
|
||||
{
|
||||
my ($FW_wname,$d,$room,$pageHash) = @_;
|
||||
my $gp = "$FW_gplotdir/$defs{$d}{GPLOTFILE}.gplot";
|
||||
my $file = $defs{$defs{$d}{LOGDEVICE}}{currentlogfile};
|
||||
|
||||
my ($err, $cfg, $plot, $flog) = SVG_readgplotfile($d, $gp, $file);
|
||||
my %conf = SVG_digestConf($cfg, $plot);
|
||||
|
||||
my $ret .= "<br><form autocomplete=\"off\" action=\"$FW_ME/SVG_WriteGplot\">";
|
||||
$ret .= FW_hidden("detail", $d);
|
||||
$ret .= FW_hidden("gplotName", $gp);
|
||||
$ret .= "<table class=\"block wide plotEditor\">";
|
||||
$ret .= "<tr class=\"even\">";
|
||||
$ret .= "<td>Plot title</td>";
|
||||
$ret .= "<td>".SVG_txt("title", "", $conf{title}, 32)."</td>";
|
||||
$ret .= "</tr>";
|
||||
$ret .= "<tr class=\"odd\">";
|
||||
$ret .= "<td>Y-Axis label</td>";
|
||||
$conf{ylabel} =~ s/"//g if($conf{ylabel});
|
||||
$ret .= "<td>".SVG_txt("ylabel", "left", $conf{ylabel}, 16)."</td>";
|
||||
$conf{y2label} =~ s/"//g if($conf{y2label});
|
||||
$ret .= "<td>".SVG_txt("y2label","right", $conf{y2label}, 16)."</td>";
|
||||
$ret .= "</tr>";
|
||||
$ret .= "<tr class=\"even\">";
|
||||
$ret .= "<td>Grid aligned</td>";
|
||||
$ret .= SVG_cb("gridy", "left", $conf{hasygrid});
|
||||
$ret .= SVG_cb("gridy2","right",$conf{hasy2grid});
|
||||
$ret .= "</tr>";
|
||||
$ret .= "<tr class=\"odd\">";
|
||||
$ret .= "<td>Range as [min:max]</td>";
|
||||
$ret .= "<td>".SVG_txt("yrange", "left", $conf{yrange}, 16)."</td>";
|
||||
$ret .= "<td>".SVG_txt("y2range", "right", $conf{y2range}, 16)."</td>";
|
||||
$ret .= "</tr>";
|
||||
$ret .= "<tr class=\"even\">";
|
||||
$ret .= "<td>Tics as (\"Txt\" val, ...)</td>";
|
||||
$ret .= "<td>".SVG_txt("ytics", "left", $conf{ytics}, 16)."</td>";
|
||||
$ret .= "<td>".SVG_txt("y2tics","right", $conf{y2tics}, 16)."</td>";
|
||||
$ret .= "</tr>";
|
||||
|
||||
$ret .= "<tr class=\"odd\"><td>Diagramm label</td>";
|
||||
$ret .= "<td>Input:Column,Regexp,DefaultValue,Function</td>";
|
||||
$ret .=" <td>Y-Axis,Plot-Type,Style,Width</td></tr>";
|
||||
|
||||
my ($colnums, $colregs, $coldata) = SVG_getRegFromFile($file);
|
||||
$colnums = join(",", 3..$colnums);
|
||||
|
||||
my %tickh;
|
||||
SVG_addTics($conf{ytics}, \%tickh);
|
||||
SVG_addTics($conf{y2tics}, \%tickh);
|
||||
$colnums = join(",", sort keys %tickh).",$colnums" if(%tickh);
|
||||
|
||||
my $max = @{$conf{lType}}+1;
|
||||
$max = 8 if($max > 8);
|
||||
my $r = 0;
|
||||
for($r=0; $r < $max; $r++) {
|
||||
$ret .= "<tr class=\"".(($r&1)?"odd":"even")."\"><td>";
|
||||
$ret .= SVG_txt("title_${r}", "", !$conf{lTitle}[$r]&&$r<($max-1) ?
|
||||
"notitle" : $conf{lTitle}[$r], 12);
|
||||
$ret .= "</td><td>";
|
||||
my @f = split(":", ($flog->[$r] ? $flog->[$r] : ":::"), 4);
|
||||
$ret .= SVG_sel("cl_${r}", $colnums, $f[0]);
|
||||
$ret .= SVG_sel("re_${r}", $colregs, $f[1]);
|
||||
$ret .= SVG_txt("df_${r}", "", $f[2], 1);
|
||||
$ret .= SVG_txt("fn_${r}", "", $f[3], 6);
|
||||
|
||||
$ret .= "</td><td>";
|
||||
my $v = $conf{lAxis}[$r];
|
||||
$ret .= SVG_sel("axes_${r}", "left,right",
|
||||
($v && $v eq "x1y1") ? "left" : "right");
|
||||
$ret .= SVG_sel("type_${r}", "lines,points,steps,fsteps,histeps,bars",
|
||||
$conf{lType}[$r]);
|
||||
my $ls = $conf{lStyle}[$r];
|
||||
if($ls) {
|
||||
$ls =~ s/class=//g;
|
||||
$ls =~ s/"//g;
|
||||
}
|
||||
$ret .= SVG_sel("style_${r}", "l0,l1,l2,l3,l4,l5,l6,l7,l8,".
|
||||
"l0fill,l1fill,l2fill,l3fill,l4fill,l5fill,l6fill", $ls);
|
||||
my $lw = $conf{lWidth}[$r];
|
||||
if($lw) {
|
||||
$lw =~ s/.*stroke-width://g;
|
||||
$lw =~ s/"//g;
|
||||
}
|
||||
$ret .= SVG_sel("width_${r}", "0.2,0.5,1,1.5,2,3,4", ($lw ? $lw : 1));
|
||||
$ret .= "</td></tr>";
|
||||
}
|
||||
$ret .= "<tr class=\"".(($r++&1)?"odd":"even")."\"><td colspan=\"3\">";
|
||||
$ret .= "Example lines for input:<br>$coldata</td></tr>";
|
||||
|
||||
$ret .= "<tr class=\"".(($r++&1)?"odd":"even")."\"><td colspan=\"3\">";
|
||||
$ret .= FW_submit("submit", "Write .gplot file")."</td></tr>";
|
||||
|
||||
$ret .= "</table></form>";
|
||||
}
|
||||
|
||||
##################
|
||||
# Generate the zoom and scroll images with links if appropriate
|
||||
sub
|
||||
SVG_zoomLink($$$)
|
||||
{
|
||||
my ($cmd, $img, $alt) = @_;
|
||||
|
||||
my $prf;
|
||||
$cmd =~ m/^(.*);([^;]*)$/;
|
||||
if($2) {
|
||||
($prf, $cmd) = ($1, $2);
|
||||
$prf =~ s/&pos=.*//;
|
||||
}
|
||||
my ($d,$off) = split("=", $cmd, 2);
|
||||
|
||||
my $val = $FW_pos{$d};
|
||||
$cmd = ($FW_detail ? "detail=$FW_detail":
|
||||
($prf ? $prf : "room=$FW_room")) . "&pos=";
|
||||
if($d eq "zoom") {
|
||||
|
||||
my $n = 0;
|
||||
my @FW_zoom = ("hour","qday","day","week","month","year");
|
||||
my %FW_zoom = map { $_, $n++ } @FW_zoom;
|
||||
|
||||
$val = "day" if(!$val);
|
||||
$val = $FW_zoom{$val};
|
||||
return "" if(!defined($val) || $val+$off < 0 || $val+$off >= int(@FW_zoom));
|
||||
$val = $FW_zoom[$val+$off];
|
||||
return "" if(!$val);
|
||||
|
||||
# Approximation of the next offset.
|
||||
my $w_off = $FW_pos{off};
|
||||
$w_off = 0 if(!$w_off);
|
||||
|
||||
if ($val eq "hour") {
|
||||
$w_off = $w_off*6;
|
||||
} elsif($val eq "qday") {
|
||||
$w_off = ($off < 0) ? $w_off*4 : int($w_off/6);
|
||||
} elsif($val eq "day") {
|
||||
$w_off = ($off < 0) ? $w_off*7 : int($w_off/4);
|
||||
} elsif($val eq "week") {
|
||||
$w_off = ($off < 0) ? $w_off*4 : int($w_off/7);
|
||||
} elsif($val eq "month") {
|
||||
$w_off = ($off < 0) ? $w_off*12: int($w_off/4);
|
||||
} elsif($val eq "year") {
|
||||
$w_off = int($w_off/12);
|
||||
}
|
||||
$cmd .= "zoom=$val;off=$w_off";
|
||||
|
||||
} else {
|
||||
|
||||
return "" if((!$val && $off > 0) || ($val && $val+$off > 0)); # no future
|
||||
$off=($val ? $val+$off : $off);
|
||||
my $zoom=$FW_pos{zoom};
|
||||
$zoom = 0 if(!$zoom);
|
||||
$cmd .= "zoom=$zoom;off=$off";
|
||||
|
||||
}
|
||||
|
||||
return " ".FW_pHPlain("$cmd", FW_makeImage($img, $alt));
|
||||
}
|
||||
|
||||
|
||||
sub
|
||||
SVG_WriteGplot($)
|
||||
{
|
||||
my ($arg) = @_;
|
||||
FW_digestCgi($arg);
|
||||
|
||||
if(!defined($FW_webArgs{cl_0})) {
|
||||
FW_pO "missing data in logfile: won't write incomplete .gplot definition";
|
||||
return 0;
|
||||
}
|
||||
|
||||
my $hasTl;
|
||||
for(my $i=0; $i <= 8; $i++) {
|
||||
$hasTl = 1 if($FW_webArgs{"title_$i"});
|
||||
}
|
||||
return 0 if(!$hasTl);
|
||||
|
||||
my $fName = $FW_webArgs{gplotName};
|
||||
return if(!$fName);
|
||||
if(!open(FH, ">$fName")) {
|
||||
FW_pO "SVG_WriteGplot: Can't write $fName";
|
||||
return 0;
|
||||
}
|
||||
print FH "# Created by FHEMWEB, ".TimeNow()."\n";
|
||||
print FH "set terminal png transparent size <SIZE> crop\n";
|
||||
print FH "set output '<OUT>.png'\n";
|
||||
print FH "set xdata time\n";
|
||||
print FH "set timefmt \"%Y-%m-%d_%H:%M:%S\"\n";
|
||||
print FH "set xlabel \" \"\n";
|
||||
print FH "set title '$FW_webArgs{title}'\n";
|
||||
print FH "set ytics ".$FW_webArgs{ytics}."\n";
|
||||
print FH "set y2tics ".$FW_webArgs{y2tics}."\n";
|
||||
print FH "set grid".($FW_webArgs{gridy} ? " ytics" :"").
|
||||
($FW_webArgs{gridy2} ? " y2tics":"")."\n";
|
||||
print FH "set ylabel \"$FW_webArgs{ylabel}\"\n";
|
||||
print FH "set y2label \"$FW_webArgs{y2label}\"\n";
|
||||
print FH "set yrange $FW_webArgs{yrange}\n" if($FW_webArgs{yrange});
|
||||
print FH "set y2range $FW_webArgs{y2range}\n" if($FW_webArgs{y2range});
|
||||
print FH "\n";
|
||||
|
||||
my @plot;
|
||||
for(my $i=0; $i <= 8; $i++) {
|
||||
next if(!$FW_webArgs{"title_$i"});
|
||||
my $re = $FW_webArgs{"re_$i"};
|
||||
$re = "" if(!defined($re));
|
||||
$re =~ s/:/\\x3a/g;
|
||||
print FH "#FileLog ". $FW_webArgs{"cl_$i"} .":$re:".
|
||||
$FW_webArgs{"df_$i"} .":".
|
||||
$FW_webArgs{"fn_$i"} ."\n";
|
||||
push @plot, "\"<IN>\" using 1:2 axes ".
|
||||
($FW_webArgs{"axes_$i"} eq "right" ? "x1y2" : "x1y1").
|
||||
($FW_webArgs{"title_$i"} eq "notitle" ? " notitle" :
|
||||
" title '".$FW_webArgs{"title_$i"} ."'").
|
||||
" ls " .$FW_webArgs{"style_$i"} .
|
||||
" lw " .$FW_webArgs{"width_$i"} .
|
||||
" with " .$FW_webArgs{"type_$i"};
|
||||
}
|
||||
print FH "\n";
|
||||
print FH "plot ".join(",\\\n ", @plot)."\n";
|
||||
close(FH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub
|
||||
SVG_readgplotfile($$$)
|
||||
{
|
||||
my ($wl, $gplot_pgm, $file) = @_;
|
||||
|
||||
############################
|
||||
# Read in the template gnuplot file. Digest the #FileLog lines. Replace
|
||||
# the plot directive with our own, as we offer a file for each line
|
||||
my (@filelog, @data, $plot);
|
||||
|
||||
my $ldType = $wl;
|
||||
$ldType = $defs{$defs{$wl}{LOGDEVICE}}{TYPE}
|
||||
if($defs{$wl}{LOGDEVICE} && $defs{$defs{$wl}{LOGDEVICE}});
|
||||
|
||||
open(FH, $gplot_pgm) || return (FW_fatal("$gplot_pgm: $!"), undef);
|
||||
while(my $l = <FH>) {
|
||||
$l =~ s/\r//g;
|
||||
my $plotfn = undef;
|
||||
if($l =~ m/^#$ldType (.*)$/) {
|
||||
$plotfn = $1;
|
||||
} elsif($l =~ "^plot" || $plot) {
|
||||
$plot .= $l;
|
||||
} else {
|
||||
push(@data, $l);
|
||||
}
|
||||
|
||||
if($plotfn) {
|
||||
my $specval = AttrVal($wl, "plotfunction", undef);
|
||||
if ($specval) {
|
||||
my @spec = split(" ",$specval);
|
||||
my $spec_count=1;
|
||||
foreach (@spec) {
|
||||
$plotfn =~ s/<SPEC$spec_count>/$_/g;
|
||||
$spec_count++;
|
||||
}
|
||||
}
|
||||
push(@filelog, $plotfn);
|
||||
}
|
||||
}
|
||||
close(FH);
|
||||
|
||||
return (undef, \@data, $plot, \@filelog);
|
||||
}
|
||||
|
||||
sub
|
||||
SVG_substcfg($$$$$$)
|
||||
{
|
||||
my ($splitret, $wl, $cfg, $plot, $file, $tmpfile) = @_;
|
||||
|
||||
# interpret title and label as a perl command and make
|
||||
# to all internal values e.g. $value.
|
||||
|
||||
my $oll = $attr{global}{verbose};
|
||||
$attr{global}{verbose} = 0; # Else the filenames will be Log'ged
|
||||
|
||||
if($file eq "CURRENT") {
|
||||
$file = $defs{$defs{$wl}{LOGDEVICE}}{currentlogfile};
|
||||
$file =~ s+.*/++;
|
||||
}
|
||||
my $fileesc = $file;
|
||||
$fileesc =~ s/\\/\\\\/g; # For Windows, by MarkusRR
|
||||
my $title = AttrVal($wl, "title", "\"$fileesc\"");
|
||||
|
||||
$title = AnalyzeCommand(undef, "{ $title }");
|
||||
my $label = AttrVal($wl, "label", undef);
|
||||
my @g_label;
|
||||
if ($label) {
|
||||
@g_label = split("::",$label);
|
||||
foreach (@g_label) {
|
||||
$_ = AnalyzeCommand(undef, "{ $_ }");
|
||||
}
|
||||
}
|
||||
$attr{global}{verbose} = $oll;
|
||||
|
||||
my $gplot_script = join("", @{$cfg});
|
||||
$gplot_script .= $plot if(!$splitret);
|
||||
|
||||
$gplot_script =~ s/<OUT>/$tmpfile/g;
|
||||
$gplot_script =~ s/<IN>/$file/g;
|
||||
|
||||
my $ps = AttrVal($wl,"plotsize",$FW_plotsize);
|
||||
$gplot_script =~ s/<SIZE>/$ps/g;
|
||||
|
||||
$gplot_script =~ s/<TL>/$title/g;
|
||||
my $g_count=1;
|
||||
if ($label) {
|
||||
foreach (@g_label) {
|
||||
$gplot_script =~ s/<L$g_count>/$_/g;
|
||||
$plot =~ s/<L$g_count>/$_/g;
|
||||
$g_count++;
|
||||
}
|
||||
}
|
||||
|
||||
$plot =~ s/\r//g; # For our windows friends...
|
||||
$gplot_script =~ s/\r//g;
|
||||
|
||||
if($splitret == 1) {
|
||||
my @ret = split("\n", $gplot_script);
|
||||
return (\@ret, $plot);
|
||||
} else {
|
||||
return $gplot_script;
|
||||
}
|
||||
}
|
||||
|
||||
##################
|
||||
# Calculate either the number of scrollable weblinks (for $d = undef) or
|
||||
# for the device the valid from and to dates for the given zoom and offset
|
||||
sub
|
||||
SVG_calcWeblink($$)
|
||||
{
|
||||
my ($d,$wl) = @_;
|
||||
|
||||
my $pm = AttrVal($d,"plotmode",$FW_plotmode);
|
||||
return if($pm eq "gnuplot");
|
||||
|
||||
my $frx;
|
||||
if($defs{$wl}) {
|
||||
my $fr = AttrVal($wl, "fixedrange", undef);
|
||||
if($fr) {
|
||||
#klaus fixed range day, week, month or year
|
||||
if($fr eq "day" || $fr eq "week" || $fr eq "month" || $fr eq "year" ) {
|
||||
$frx=$fr;
|
||||
|
||||
} else {
|
||||
my @range = split(" ", $fr);
|
||||
my @t = localtime;
|
||||
$SVG_devs{$d}{from} = ResolveDateWildcards($range[0], @t);
|
||||
$SVG_devs{$d}{to} = ResolveDateWildcards($range[1], @t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $off = $FW_pos{$d};
|
||||
$off = 0 if(!$off);
|
||||
$off += $FW_pos{off} if($FW_pos{off});
|
||||
|
||||
my $now = time();
|
||||
my $zoom = $FW_pos{zoom};
|
||||
$zoom = "day" if(!$zoom);
|
||||
$zoom = $frx if ($frx); #for fixedrange {day|week|...} klaus
|
||||
|
||||
|
||||
if($zoom eq "hour") {
|
||||
my $t = $now + $off*3600;
|
||||
my @l = localtime($t);
|
||||
$SVG_devs{$d}{from}
|
||||
= sprintf("%04d-%02d-%02d_%02d:00:00",$l[5]+1900,$l[4]+1,$l[3],$l[2]);
|
||||
@l = localtime($t+3600);
|
||||
$SVG_devs{$d}{to}
|
||||
= sprintf("%04d-%02d-%02d_%02d:00:01",$l[5]+1900,$l[4]+1,$l[3],$l[2]);
|
||||
|
||||
} elsif($zoom eq "qday") {
|
||||
my $t = $now + $off*21600;
|
||||
my @l = localtime($t);
|
||||
$l[2] = int($l[2]/6)*6;
|
||||
$SVG_devs{$d}{from} =
|
||||
sprintf("%04d-%02d-%02d_%02d:00:00",$l[5]+1900,$l[4]+1,$l[3],$l[2]);
|
||||
@l = localtime($t+21600);
|
||||
$l[2] = int($l[2]/6)*6;
|
||||
$SVG_devs{$d}{to} =
|
||||
sprintf("%04d-%02d-%02d_%02d:00:01",$l[5]+1900,$l[4]+1,$l[3],$l[2]);
|
||||
|
||||
} elsif($zoom eq "day") {
|
||||
my $t = $now + $off*86400;
|
||||
my @l = localtime($t);
|
||||
$SVG_devs{$d}{from} =
|
||||
sprintf("%04d-%02d-%02d_00:00:00",$l[5]+1900,$l[4]+1,$l[3]);
|
||||
@l = localtime($t+86400);
|
||||
$SVG_devs{$d}{to} =
|
||||
sprintf("%04d-%02d-%02d_00:00:01",$l[5]+1900,$l[4]+1,$l[3]);
|
||||
|
||||
} elsif($zoom eq "week") {
|
||||
my @l = localtime($now);
|
||||
my $start = (AttrVal($FW_wname, "endPlotToday", undef) ? 6 : $l[6]);
|
||||
my $t = $now - ($start*86400) + ($off*86400)*7;
|
||||
@l = localtime($t);
|
||||
$SVG_devs{$d}{from} =
|
||||
sprintf("%04d-%02d-%02d_00:00:00",$l[5]+1900,$l[4]+1,$l[3]);
|
||||
@l = localtime($t+7*86400);
|
||||
$SVG_devs{$d}{to} =
|
||||
sprintf("%04d-%02d-%02d_00:00:01",$l[5]+1900,$l[4]+1,$l[3]);
|
||||
|
||||
} elsif($zoom eq "month") {
|
||||
my ($endDay, @l);
|
||||
if(AttrVal($FW_wname, "endPlotToday", undef)) {
|
||||
@l = localtime($now+86400);
|
||||
$endDay = $l[3];
|
||||
$off--;
|
||||
} else {
|
||||
@l = localtime($now);
|
||||
$endDay = 1;
|
||||
}
|
||||
while($off < -12) { # Correct the year
|
||||
$off += 12; $l[5]--;
|
||||
}
|
||||
$l[4] += $off;
|
||||
$l[4] += 12, $l[5]-- if($l[4] < 0);
|
||||
$SVG_devs{$d}{from} =
|
||||
sprintf("%04d-%02d-%02d_00:00:00", $l[5]+1900, $l[4]+1,$endDay);
|
||||
$l[4]++;
|
||||
$l[4] = 0, $l[5]++ if($l[4] == 12);
|
||||
$SVG_devs{$d}{to} =
|
||||
sprintf("%04d-%02d-%02d_00:00:01", $l[5]+1900, $l[4]+1,$endDay);
|
||||
|
||||
} elsif($zoom eq "year") {
|
||||
my @l = localtime($now);
|
||||
$l[5] += $off;
|
||||
$SVG_devs{$d}{from} = sprintf("%04d-01-01_00:00:00", $l[5]+1900);
|
||||
$SVG_devs{$d}{to} = sprintf("%04d-01-01_00:00:01", $l[5]+1901);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
######################
|
||||
# Generate an image from the log via gnuplot or SVG
|
||||
sub
|
||||
SVG_showLog($)
|
||||
{
|
||||
my ($cmd) = @_;
|
||||
my $wl = $FW_webArgs{dev};
|
||||
my $d = $FW_webArgs{logdev};
|
||||
my $type = $FW_webArgs{gplotfile};
|
||||
my $file = $FW_webArgs{logfile};
|
||||
my $pm = AttrVal($wl,"plotmode",$FW_plotmode);
|
||||
|
||||
my $gplot_pgm = "$FW_gplotdir/$type.gplot";
|
||||
|
||||
if(!-r $gplot_pgm) {
|
||||
my $msg = "Cannot read $gplot_pgm";
|
||||
Log3 $FW_wname, 1, $msg;
|
||||
|
||||
if($pm =~ m/SVG/) { # FW_fatal for SVG:
|
||||
$FW_RETTYPE = "image/svg+xml";
|
||||
FW_pO '<svg xmlns="http://www.w3.org/2000/svg">';
|
||||
FW_pO '<text x="20" y="20">'.$msg.'</text>';
|
||||
FW_pO '</svg>';
|
||||
return ($FW_RETTYPE, $FW_RET);
|
||||
|
||||
} else {
|
||||
return ($FW_RETTYPE, $msg);
|
||||
|
||||
}
|
||||
}
|
||||
SVG_calcWeblink($d,$wl);
|
||||
|
||||
if($pm =~ m/gnuplot/) {
|
||||
|
||||
my $tmpfile = "/tmp/file.$$";
|
||||
my $errfile = "/tmp/gnuplot.err";
|
||||
|
||||
if($pm eq "gnuplot" || !$SVG_devs{$d}{from}) {
|
||||
# Looking for the logfile....
|
||||
$defs{$d}{logfile} =~ m,^(.*)/([^/]*)$,; # Dir and File
|
||||
my $path = "$1/$file";
|
||||
$path = AttrVal($d,"archivedir","") . "/$file" if(!-f $path);
|
||||
return ($FW_RETTYPE, "Cannot read $path") if(!-r $path);
|
||||
|
||||
my ($err, $cfg, $plot, undef) = SVG_readgplotfile($wl, $gplot_pgm, $file);
|
||||
return ($FW_RETTYPE, $err) if($err);
|
||||
my $gplot_script = SVG_substcfg(0, $wl, $cfg, $plot, $file,$tmpfile);
|
||||
|
||||
my $fr = AttrVal($wl, "fixedrange", undef);
|
||||
if($fr) {
|
||||
$fr =~ s/ /\":\"/;
|
||||
$fr = "set xrange [\"$fr\"]\n";
|
||||
$gplot_script =~ s/(set timefmt ".*")/$1\n$fr/;
|
||||
}
|
||||
|
||||
open(FH, "|gnuplot >> $errfile 2>&1");# feed it to gnuplot
|
||||
print FH $gplot_script;
|
||||
close(FH);
|
||||
|
||||
} elsif($pm eq "gnuplot-scroll") {
|
||||
my ($err, $cfg, $plot, $flog) = SVG_readgplotfile($wl, $gplot_pgm, $file);
|
||||
return ($FW_RETTYPE, $err) if($err);
|
||||
|
||||
# Read the data from the filelog
|
||||
my ($f,$t)=($SVG_devs{$d}{from}, $SVG_devs{$d}{to});
|
||||
my $oll = $attr{global}{verbose};
|
||||
$attr{global}{verbose} = 0; # Else the filenames will be Log'ged
|
||||
my @path = split(" ", FW_fC("get $d $file $tmpfile $f $t " .
|
||||
join(" ", @{$flog})));
|
||||
$attr{global}{verbose} = $oll;
|
||||
|
||||
# replace the path with the temporary filenames of the filelog output
|
||||
my $i = 0;
|
||||
$plot =~ s/\".*?using 1:[^ ]+ /"\"$path[$i++]\" using 1:2 "/gse;
|
||||
my $xrange = "set xrange [\"$f\":\"$t\"]\n";
|
||||
foreach my $p (@path) { # If the file is empty, write a 0 line
|
||||
next if(!-z $p);
|
||||
open(FH, ">$p");
|
||||
print FH "$f 0\n";
|
||||
close(FH);
|
||||
}
|
||||
|
||||
my $gplot_script = SVG_substcfg(0, $wl, $cfg, $plot, $file, $tmpfile);
|
||||
|
||||
open(FH, "|gnuplot >> $errfile 2>&1");# feed it to gnuplot
|
||||
print FH $gplot_script, $xrange, $plot;
|
||||
close(FH);
|
||||
foreach my $p (@path) {
|
||||
unlink($p);
|
||||
}
|
||||
}
|
||||
$FW_RETTYPE = "image/png";
|
||||
open(FH, "$tmpfile.png"); # read in the result and send it
|
||||
binmode (FH); # necessary for Windows
|
||||
FW_pO join("", <FH>);
|
||||
close(FH);
|
||||
unlink("$tmpfile.png");
|
||||
|
||||
} elsif($pm eq "SVG") {
|
||||
my ($err, $cfg, $plot, $flog) = SVG_readgplotfile($wl, $gplot_pgm, $file);
|
||||
return ($FW_RETTYPE, $err) if($err);
|
||||
|
||||
my ($f,$t)=($SVG_devs{$d}{from}, $SVG_devs{$d}{to});
|
||||
$f = 0 if(!$f); # From the beginning of time...
|
||||
$t = 9 if(!$t); # till the end
|
||||
|
||||
my $ret;
|
||||
if(!$modules{SVG}{LOADED}) {
|
||||
$ret = CommandReload(undef, "98_SVG");
|
||||
Log3 $FW_wname, 1, $ret if($ret);
|
||||
}
|
||||
Log3 $FW_wname, 5,
|
||||
"plotcommand: get $d $file INT $f $t " . join(" ", @{$flog});
|
||||
|
||||
$FW_RETTYPE = "image/svg+xml";
|
||||
|
||||
(my $cachedate = TimeNow()) =~ s/ /_/g;
|
||||
my $SVGcache = (AttrVal($FW_wname, "SVGcache", undef) && $t lt $cachedate);
|
||||
my $cDir = "$FW_dir/SVGcache";
|
||||
my $cName = "$cDir/$wl-$f-$t.svg";
|
||||
if($SVGcache && open(CFH, $cName)) {
|
||||
FW_pO join("", <CFH>);
|
||||
close(CFH);
|
||||
|
||||
} else {
|
||||
FW_fC("get $d $file INT $f $t " . join(" ", @{$flog}), 1);
|
||||
($cfg, $plot) = SVG_substcfg(1, $wl, $cfg, $plot, $file, "<OuT>");
|
||||
$ret = SVG_render($wl, $f, $t, $cfg,
|
||||
$internal_data, $plot, $FW_wname, $FW_cssdir, $flog);
|
||||
FW_pO $ret;
|
||||
if($SVGcache) {
|
||||
mkdir($cDir) if(! -d $cDir);
|
||||
if(open(CFH, ">$cName")) {
|
||||
print CFH $ret;
|
||||
close(CFH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return ($FW_RETTYPE, $FW_RET);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -680,3 +1475,144 @@ SVG_pO($)
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=begin html
|
||||
|
||||
<a name="SVG"></a>
|
||||
<h3>SVG</h3>
|
||||
<ul>
|
||||
<a name="SVGlinkdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> SVG
|
||||
<logDevice>:<gplotfile>:<logfile></code>
|
||||
<br><br>
|
||||
This is the Plotting/Charting device of FHEMWEB
|
||||
Examples:
|
||||
<ul>
|
||||
<code>define MyPlot SVG inlog:temp4hum4:CURRENT</code><br>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
Notes:
|
||||
<ul>
|
||||
<li>Normally you won't define an SVG device manually, as
|
||||
FHEMWEB makes it easy for you, just plot a logfile (see <a
|
||||
href="#logtype">logtype</a>) and click on "Create SVG instance".
|
||||
Specifying CURRENT as a logfilename will always access the current
|
||||
logfile, even if its name changes regularly.</li>
|
||||
<li>For historic reasons this module uses a Gnuplot file description
|
||||
to store different attributes. Some special commands (beginning with
|
||||
#FileLog or #DbLog) are used additionally, and not all gnuplot
|
||||
attribtues are implemented.</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<a name="SVGset"></a>
|
||||
<b>Set</b>
|
||||
<ul>
|
||||
<li>copyGplotFile<br>
|
||||
Copy the currently specified gplot file to a new file, which is named
|
||||
after the SVG device, existing files will be overwritten.
|
||||
This operation is needed in order to use the plot editor (see below)
|
||||
without affecting other SVG instances using the same gplot file.
|
||||
Creating the SVG instance from the FileLog detail menu will also
|
||||
create a unique gplot file, in this case this operation is not needed.
|
||||
</li>
|
||||
</ul><br>
|
||||
|
||||
<a name="SVGget"></a>
|
||||
<b>Get</b> <ul>N/A</ul><br>
|
||||
|
||||
<a name="SVGattr"></a>
|
||||
<b>Attributes</b>
|
||||
<ul>
|
||||
<li><a href="#fixedrange">fixedrange</a></li>
|
||||
<li><a href="#plotsize">plotsize</a></li>
|
||||
<li><a href="#plotmode">plotmode</a></li>
|
||||
<a name="label"></a>
|
||||
<li>label<br>
|
||||
Double-Colon separated list of values. The values will be used to replace
|
||||
<L#> type of strings in the .gplot file, with # beginning at 1
|
||||
(<L1>, <L2>, etc.). Each value will be evaluated as a perl
|
||||
expression, so you have access e.g. to the Value functions.<br><br>
|
||||
|
||||
If the plotmode is gnuplot-scroll or SVG, you can also use the min, max,
|
||||
avg, cnt, sum, currval (last value) and currdate (last date) values of
|
||||
the individual curves, by accessing the corresponding values from the
|
||||
data hash, see the example below:<br>
|
||||
|
||||
<ul>
|
||||
<li>Fixed text for the right and left axis:<br>
|
||||
<ul>
|
||||
<li>Fhem config:<br>
|
||||
attr wl_1 label "Temperature"::"Humidity"</li>
|
||||
<li>.gplot file entry:<br>
|
||||
set ylabel <L1><br>
|
||||
set y2label <L2></li>
|
||||
</ul></li>
|
||||
<li>Title with maximum and current values of the 1st curve (FileLog)
|
||||
<ul>
|
||||
<li>Fhem config:<br>
|
||||
attr wl_1 label "Max $data{max1}, Current $data{currval1}"</li>
|
||||
<li>.gplot file entry:<br>
|
||||
set title <L1><br></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<a name="title"></a>
|
||||
<li>title<br>
|
||||
A special form of label (see above), which replaces the string <TL>
|
||||
in the .gplot file. It defaults to the filename of the logfile.
|
||||
</li>
|
||||
|
||||
<a name="plotfunction"></a>
|
||||
<li>plotfunction<br>
|
||||
Space value separated list of values. The value will be used to replace
|
||||
<SPEC#> type of strings in the .gplot file, with # beginning at 1
|
||||
(<SPEC1>, <SPEC2>, etc.) in the #FileLog or #DbLog directive.
|
||||
With this attribute you can use the same .gplot file for multiple devices
|
||||
with the same logdevice.
|
||||
<ul><b>Example:</b><br>
|
||||
<li>#FileLog <SPEC1><br>
|
||||
with: attr <SVGdevice> plotfunction "4:IR\x3a:0:"<br>
|
||||
instead of<br>
|
||||
#FileLog 4:IR\x3a:0:
|
||||
</li>
|
||||
<li>#DbLog <SPEC1><br>
|
||||
with: attr <SVGdevice> plotfunction
|
||||
"Garage_Raumtemp:temperature::"<br> instead of<br>
|
||||
#DbLog Garage_Raumtemp:temperature::
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="plotEditor"></a>
|
||||
<b>Plot-Editor</b>
|
||||
<ul>
|
||||
This editor is visible on the detail screen of the SVG instance.
|
||||
Most features are obvious here, up to some exceptions:
|
||||
<li>if you want to omit the title for a Diagram label, enter notitle in the
|
||||
input field.</li>
|
||||
<li>if you want to specify a fixed value (not taken from a column) if a
|
||||
string found (e.g. 1 of the FS20 switch is on 0 if it off), then you have
|
||||
to specify the Tics first, and write the .gplot file, before you can
|
||||
select this value from the dropdown.<br>
|
||||
Example:
|
||||
<ul>
|
||||
Enter in the Tics field: ("On" 1, "Off" 0)<br>
|
||||
Write .gplot file<br>
|
||||
Select "1" from the column dropdown (note the double quote!) for the
|
||||
regexp switch.on, and "0" for the regexp switch.off.<br>
|
||||
Write .gplot file again<br>
|
||||
</ul></li>
|
||||
</ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
=cut
|
||||
|
@ -5,14 +5,6 @@ package main;
|
||||
use strict;
|
||||
use warnings;
|
||||
use vars qw($FW_subdir); # Sub-path in URL for extensions, e.g. 95_FLOORPLAN
|
||||
use vars qw($FW_ME); # webname (default is fhem), needed by 97_GROUP
|
||||
use vars qw(%FW_hiddenroom); # hash of hidden rooms, used by weblink
|
||||
use vars qw($FW_plotmode);# Global plot mode (WEB attribute), used by weblink
|
||||
use vars qw($FW_plotsize);# Global plot size (WEB attribute), used by weblink
|
||||
use vars qw(%FW_pos); # scroll position
|
||||
use vars qw($FW_gplotdir);# gplot directory for web server: the first
|
||||
use vars qw(%FW_webArgs); # all arguments specified in the GET
|
||||
|
||||
use IO::File;
|
||||
|
||||
#####################################
|
||||
@ -22,9 +14,7 @@ weblink_Initialize($)
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{DefFn} = "weblink_Define";
|
||||
$hash->{AttrList} = "fixedrange plotmode plotsize label ".
|
||||
"title htmlattr plotfunction";
|
||||
$hash->{SetFn} = "weblink_Set";
|
||||
$hash->{AttrList} = "htmlattr";
|
||||
$hash->{FW_summaryFn} = "weblink_FwFn";
|
||||
$hash->{FW_detailFn} = "weblink_FwFn";
|
||||
$hash->{FW_atPageEnd} = 1;
|
||||
@ -37,52 +27,31 @@ sub
|
||||
weblink_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my ($type, $name, $wltype, $link) = split("[ \t]+", $def, 4);
|
||||
my %thash = ( link=>1, fileplot=>1, dbplot=>1, image=>1, iframe=>1, htmlCode=>1, cmdList=>1, readings=>1 );
|
||||
my ($name, $type, $wltype, $link) = split("[ \t]+", $def, 4);
|
||||
my %thash = ( link=>1, image=>1, iframe=>1, htmlCode=>1,
|
||||
cmdList=>1, readings=>1,
|
||||
fileplot=>1, dbplot=>1);
|
||||
|
||||
if(!$link || !$thash{$wltype}) {
|
||||
return "Usage: define <name> weblink [" .
|
||||
join("|",sort keys %thash) . "] <arg>";
|
||||
}
|
||||
|
||||
if($wltype eq "fileplot" || $wltype eq "dbplot") {
|
||||
Log 1, "Converting weblink $name ($wltype) to SVG";
|
||||
my $newm = LoadModule("SVG");
|
||||
return "Cannot load module SVG" if($newm eq "UNDEFINED");
|
||||
$hash->{TYPE} = "SVG";
|
||||
$hash->{DEF} = $link;
|
||||
return CallFn($name, "DefFn", $hash, "$name $type $link");
|
||||
}
|
||||
|
||||
$hash->{WLTYPE} = $wltype;
|
||||
$hash->{LINK} = $link;
|
||||
$hash->{STATE} = "initial";
|
||||
$hash->{STATE} = "initialized";
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub
|
||||
weblink_Set($@)
|
||||
{
|
||||
my ($hash, @a) = @_;
|
||||
my $me = $hash->{NAME};
|
||||
return "no set argument specified" if(int(@a) < 2);
|
||||
my %sets = (copyGplotFile=>0);
|
||||
|
||||
my $cmd = $a[1];
|
||||
return "Unknown argument $cmd, choose one of ".join(" ",sort keys %sets)
|
||||
if(!defined($sets{$cmd}));
|
||||
return "$cmd needs $sets{$cmd} parameter(s)" if(@a-$sets{$cmd} != 2);
|
||||
|
||||
if($cmd eq "copyGplotFile") {
|
||||
return "type is not fileplot" if($hash->{WLTYPE} ne "fileplot");
|
||||
my @a = split(":", $hash->{LINK});
|
||||
my $srcName = "$FW_gplotdir/$a[1].gplot";
|
||||
$a[1] = $hash->{NAME};
|
||||
my $dstName = "$FW_gplotdir/$a[1].gplot";
|
||||
$hash->{LINK} = join(":", @a);
|
||||
return "this is already a unique gplot file" if($srcName eq $dstName);
|
||||
$hash->{DEF} = "$hash->{WLTYPE} $hash->{LINK}";
|
||||
open(SFH, $srcName) || return "Can't open $srcName: $!";
|
||||
open(DFH, ">$dstName") || return "Can't open $dstName: $!";
|
||||
while(my $l = <SFH>) {
|
||||
print DFH $l;
|
||||
}
|
||||
close(SFH); close(DFH);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
# FLOORPLAN compat
|
||||
sub
|
||||
@ -155,8 +124,6 @@ weblink_FwFn($$$$)
|
||||
$ret .= "</table></td></tr>";
|
||||
$ret .= "</table><br>";
|
||||
|
||||
return $ret;
|
||||
|
||||
} elsif($wltype eq "readings") {
|
||||
my @params = split(" ", $link);
|
||||
|
||||
@ -232,295 +199,11 @@ weblink_FwFn($$$$)
|
||||
$ret .= "</table>";
|
||||
$ret .= "</br>";
|
||||
|
||||
return $ret;
|
||||
|
||||
} elsif($wltype eq "fileplot" || $wltype eq "dbplot" ) {
|
||||
|
||||
# plots navigation buttons
|
||||
if((!$pageHash || !$pageHash->{buttons}) &&
|
||||
($wltype eq "fileplot" || $wltype eq "dbplot") &&
|
||||
AttrVal($d, "fixedrange", "x") !~ m/^[ 0-9:-]*$/) {
|
||||
|
||||
$ret .= FW_zoomLink("zoom=-1", "Zoom-in", "zoom in");
|
||||
$ret .= FW_zoomLink("zoom=1", "Zoom-out","zoom out");
|
||||
$ret .= FW_zoomLink("off=-1", "Prev", "prev");
|
||||
$ret .= FW_zoomLink("off=1", "Next", "next");
|
||||
$pageHash->{buttons} = 1 if($pageHash);
|
||||
$ret .= "<br>";
|
||||
}
|
||||
|
||||
my @va = split(":", $link, 3);
|
||||
if($wltype eq "fileplot" &&
|
||||
(@va != 3 || !$defs{$va[0]} || !$defs{$va[0]}{currentlogfile})) {
|
||||
$ret .= weblink_FwDetail($d, "Broken definition ");
|
||||
|
||||
} elsif ($wltype eq "dbplot" && (@va != 2 || !$defs{$va[0]})) {
|
||||
$ret .= weblink_FwDetail($d, "Broken definition ");
|
||||
|
||||
} else {
|
||||
if ($wltype eq "dbplot") {
|
||||
$va[2] = "-";
|
||||
}
|
||||
|
||||
my $wl = "&pos=" . join(";", map {"$_=$FW_pos{$_}"} keys %FW_pos);
|
||||
|
||||
my $arg="$FW_ME?cmd=showlog $d $va[0] $va[1] $va[2]$wl";
|
||||
if(AttrVal($d,"plotmode",$FW_plotmode) eq "SVG") {
|
||||
my ($w, $h) = split(",", AttrVal($d,"plotsize",$FW_plotsize));
|
||||
$ret .= "<div class=\"SVGplot\">";
|
||||
$ret .= "<embed src=\"$arg\" type=\"image/svg+xml\" " .
|
||||
"width=\"$w\" height=\"$h\" name=\"$d\"/>\n";
|
||||
$ret .= "</div>";
|
||||
|
||||
} else {
|
||||
$ret .= "<img src=\"$arg\"/>";
|
||||
}
|
||||
|
||||
if(!$pageHash) {
|
||||
$ret .= wl_PEdit($FW_wname,$d,$room,$pageHash)
|
||||
if($wltype eq "fileplot" && $FW_plotmode eq "SVG");
|
||||
$ret .= "<br>";
|
||||
|
||||
} else {
|
||||
$ret .= weblink_FwDetail($d, "", 1) if(!$FW_hiddenroom{detail});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub
|
||||
wl_cb($$$)
|
||||
{
|
||||
my ($v,$t,$c) = @_;
|
||||
$c = ($c ? " checked" : "");
|
||||
return "<td>$t <input type=\"checkbox\" name=\"$v\" value=\"$v\"$c></td>";
|
||||
}
|
||||
|
||||
sub
|
||||
wl_txt($$$$)
|
||||
{
|
||||
my ($v,$t,$c,$sz) = @_;
|
||||
$c = "" if(!defined($c));
|
||||
$c =~ s/"/\"/g;
|
||||
return "$t <input type=\"text\" name=\"$v\" size=\"$sz\" ".
|
||||
"value=\"$c\"/>";
|
||||
}
|
||||
|
||||
sub
|
||||
wl_sel($$$@)
|
||||
{
|
||||
my ($v,$l,$c,$fnData) = @_;
|
||||
my @al = split(",",$l);
|
||||
$c =~ s/\\x3a/:/g if($c);
|
||||
return FW_select($v,$v,\@al,$c, "set", $fnData);
|
||||
}
|
||||
|
||||
sub
|
||||
wl_getRegFromFile($)
|
||||
{
|
||||
my ($fName) = @_;
|
||||
my $fh = new IO::File $fName;
|
||||
if(!$fh) {
|
||||
Log 1, "$fName: $!";
|
||||
return (3, "NoFile", "NoFile");
|
||||
}
|
||||
$fh->seek(0, 2); # Go to the end
|
||||
my $sz = $fh->tell;
|
||||
$fh->seek($sz > 65536 ? $sz-65536 : 0, 0);
|
||||
my $data;
|
||||
$data = <$fh> if($sz > 65536); # discard the first/partial line
|
||||
my $maxcols = 0;
|
||||
my %h;
|
||||
while($data = <$fh>) {
|
||||
my @cols = split(" ", $data);
|
||||
next if(@cols < 3);
|
||||
$maxcols = @cols if(@cols > $maxcols);
|
||||
$cols[2] = "*" if($cols[2] =~ m/^[-\.\d]+$/);
|
||||
$h{"$cols[1].$cols[2]"} = $data;
|
||||
$h{"$cols[1].*"} = "" if($cols[2] ne "*");
|
||||
}
|
||||
$fh->close();
|
||||
return ($maxcols+1,
|
||||
join(",", sort keys %h),
|
||||
join("<br>", grep /.+/,map { $h{$_} } sort keys %h)),
|
||||
close(FH);
|
||||
}
|
||||
|
||||
sub
|
||||
wl_addTics($$)
|
||||
{
|
||||
my ($in, $p) = @_;
|
||||
return if(!$in || $in !~ m/^\((.*)\)$/);
|
||||
map { $p->{"\"$2\""}=1 if(m/^ *([^ ]+) ([^ ]+) */); } split(",",$1);
|
||||
}
|
||||
|
||||
############################
|
||||
# gnuplot file "editor"
|
||||
sub
|
||||
wl_PEdit($$$$)
|
||||
{
|
||||
my ($FW_wname,$d,$room,$pageHash) = @_;
|
||||
my @a = split(":", $defs{$d}{LINK});
|
||||
my $gp = "$FW_gplotdir/$a[1].gplot";
|
||||
my $file = $defs{$a[0]}{currentlogfile};
|
||||
|
||||
my ($err, $cfg, $plot, $flog) = FW_readgplotfile($d, $gp, $file);
|
||||
my %conf = SVG_digestConf($cfg, $plot);
|
||||
|
||||
my $ret .= "<br><form autocomplete=\"off\" action=\"$FW_ME/weblinkDetails\">";
|
||||
$ret .= FW_hidden("detail", $d);
|
||||
$ret .= FW_hidden("gplotName", $gp);
|
||||
$ret .= "<table class=\"block wide plotEditor\">";
|
||||
$ret .= "<tr class=\"even\">";
|
||||
$ret .= "<td>Plot title</td>";
|
||||
$ret .= "<td>".wl_txt("title", "", $conf{title}, 32)."</td>";
|
||||
$ret .= "</tr>";
|
||||
$ret .= "<tr class=\"odd\">";
|
||||
$ret .= "<td>Y-Axis label</td>";
|
||||
$conf{ylabel} =~ s/"//g if($conf{ylabel});
|
||||
$ret .= "<td>".wl_txt("ylabel", "left", $conf{ylabel}, 16)."</td>";
|
||||
$conf{y2label} =~ s/"//g if($conf{y2label});
|
||||
$ret .= "<td>".wl_txt("y2label","right", $conf{y2label}, 16)."</td>";
|
||||
$ret .= "</tr>";
|
||||
$ret .= "<tr class=\"even\">";
|
||||
$ret .= "<td>Grid aligned</td>";
|
||||
$ret .= wl_cb("gridy", "left", $conf{hasygrid});
|
||||
$ret .= wl_cb("gridy2","right",$conf{hasy2grid});
|
||||
$ret .= "</tr>";
|
||||
$ret .= "<tr class=\"odd\">";
|
||||
$ret .= "<td>Range as [min:max]</td>";
|
||||
$ret .= "<td>".wl_txt("yrange", "left", $conf{yrange}, 16)."</td>";
|
||||
$ret .= "<td>".wl_txt("y2range", "right", $conf{y2range}, 16)."</td>";
|
||||
$ret .= "</tr>";
|
||||
$ret .= "<tr class=\"even\">";
|
||||
$ret .= "<td>Tics as (\"Txt\" val, ...)</td>";
|
||||
$ret .= "<td>".wl_txt("ytics", "left", $conf{ytics}, 16)."</td>";
|
||||
$ret .= "<td>".wl_txt("y2tics","right", $conf{y2tics}, 16)."</td>";
|
||||
$ret .= "</tr>";
|
||||
|
||||
$ret .= "<tr class=\"odd\"><td>Diagramm label</td>";
|
||||
$ret .= "<td>Input:Column,Regexp,DefaultValue,Function</td>";
|
||||
$ret .=" <td>Y-Axis,Plot-Type,Style,Width</td></tr>";
|
||||
|
||||
my ($colnums, $colregs, $coldata) = wl_getRegFromFile($file);
|
||||
$colnums = join(",", 3..$colnums);
|
||||
|
||||
my %tickh;
|
||||
wl_addTics($conf{ytics}, \%tickh);
|
||||
wl_addTics($conf{y2tics}, \%tickh);
|
||||
$colnums = join(",", sort keys %tickh).",$colnums" if(%tickh);
|
||||
|
||||
my $max = @{$conf{lType}}+1;
|
||||
$max = 8 if($max > 8);
|
||||
my $r = 0;
|
||||
for($r=0; $r < $max; $r++) {
|
||||
$ret .= "<tr class=\"".(($r&1)?"odd":"even")."\"><td>";
|
||||
$ret .= wl_txt("title_${r}", "", !$conf{lTitle}[$r]&&$r<($max-1) ?
|
||||
"notitle" : $conf{lTitle}[$r], 12);
|
||||
$ret .= "</td><td>";
|
||||
my @f = split(":", ($flog->[$r] ? $flog->[$r] : ":::"), 4);
|
||||
$ret .= wl_sel("cl_${r}", $colnums, $f[0]);
|
||||
$ret .= wl_sel("re_${r}", $colregs, $f[1]);
|
||||
$ret .= wl_txt("df_${r}", "", $f[2], 1);
|
||||
$ret .= wl_txt("fn_${r}", "", $f[3], 6);
|
||||
|
||||
$ret .= "</td><td>";
|
||||
my $v = $conf{lAxis}[$r];
|
||||
$ret .= wl_sel("axes_${r}", "left,right",
|
||||
($v && $v eq "x1y1") ? "left" : "right");
|
||||
$ret .= wl_sel("type_${r}", "lines,points,steps,fsteps,histeps,bars",
|
||||
$conf{lType}[$r]);
|
||||
my $ls = $conf{lStyle}[$r];
|
||||
if($ls) {
|
||||
$ls =~ s/class=//g;
|
||||
$ls =~ s/"//g;
|
||||
}
|
||||
$ret .= wl_sel("style_${r}", "l0,l1,l2,l3,l4,l5,l6,l7,l8,".
|
||||
"l0fill,l1fill,l2fill,l3fill,l4fill,l5fill,l6fill", $ls);
|
||||
my $lw = $conf{lWidth}[$r];
|
||||
if($lw) {
|
||||
$lw =~ s/.*stroke-width://g;
|
||||
$lw =~ s/"//g;
|
||||
}
|
||||
$ret .= wl_sel("width_${r}", "0.2,0.5,1,1.5,2,3,4", ($lw ? $lw : 1));
|
||||
$ret .= "</td></tr>";
|
||||
}
|
||||
$ret .= "<tr class=\"".(($r++&1)?"odd":"even")."\"><td colspan=\"3\">";
|
||||
$ret .= "Example lines for input:<br>$coldata</td></tr>";
|
||||
|
||||
$ret .= "<tr class=\"".(($r++&1)?"odd":"even")."\"><td colspan=\"3\">";
|
||||
$ret .= FW_submit("submit", "Write .gplot file")."</td></tr>";
|
||||
|
||||
$ret .= "</table></form>";
|
||||
}
|
||||
|
||||
sub
|
||||
weblink_WriteGplot($)
|
||||
{
|
||||
my ($arg) = @_;
|
||||
FW_digestCgi($arg);
|
||||
|
||||
if(!defined($FW_webArgs{cl_0})) {
|
||||
return("text/html;",
|
||||
"missing data in logfile: won't write incomplete .gplot definition");
|
||||
}
|
||||
|
||||
my $hasTl;
|
||||
for(my $i=0; $i <= 8; $i++) {
|
||||
$hasTl = 1 if($FW_webArgs{"title_$i"});
|
||||
}
|
||||
return (undef, "continue") if(!$hasTl);
|
||||
|
||||
my $fName = $FW_webArgs{gplotName};
|
||||
return (undef, "continue") if(!$fName);
|
||||
if(!open(FH, ">$fName")) {
|
||||
Log 1, "weblink_WriteGplot: Can't write $fName";
|
||||
return (undef, "continue");
|
||||
}
|
||||
print FH "# Created by FHEMWEB, ".TimeNow()."\n";
|
||||
print FH "set terminal png transparent size <SIZE> crop\n";
|
||||
print FH "set output '<OUT>.png'\n";
|
||||
print FH "set xdata time\n";
|
||||
print FH "set timefmt \"%Y-%m-%d_%H:%M:%S\"\n";
|
||||
print FH "set xlabel \" \"\n";
|
||||
print FH "set title '$FW_webArgs{title}'\n";
|
||||
print FH "set ytics ".$FW_webArgs{ytics}."\n";
|
||||
print FH "set y2tics ".$FW_webArgs{y2tics}."\n";
|
||||
print FH "set grid".($FW_webArgs{gridy} ? " ytics" :"").
|
||||
($FW_webArgs{gridy2} ? " y2tics":"")."\n";
|
||||
print FH "set ylabel \"$FW_webArgs{ylabel}\"\n";
|
||||
print FH "set y2label \"$FW_webArgs{y2label}\"\n";
|
||||
print FH "set yrange $FW_webArgs{yrange}\n" if($FW_webArgs{yrange});
|
||||
print FH "set y2range $FW_webArgs{y2range}\n" if($FW_webArgs{y2range});
|
||||
print FH "\n";
|
||||
|
||||
my @plot;
|
||||
for(my $i=0; $i <= 8; $i++) {
|
||||
next if(!$FW_webArgs{"title_$i"});
|
||||
my $re = $FW_webArgs{"re_$i"};
|
||||
$re = "" if(!defined($re));
|
||||
$re =~ s/:/\\x3a/g;
|
||||
print FH "#FileLog ". $FW_webArgs{"cl_$i"} .":$re:".
|
||||
$FW_webArgs{"df_$i"} .":".
|
||||
$FW_webArgs{"fn_$i"} ."\n";
|
||||
push @plot, "\"<IN>\" using 1:2 axes ".
|
||||
($FW_webArgs{"axes_$i"} eq "right" ? "x1y2" : "x1y1").
|
||||
($FW_webArgs{"title_$i"} eq "notitle" ? " notitle" :
|
||||
" title '".$FW_webArgs{"title_$i"} ."'").
|
||||
" ls " .$FW_webArgs{"style_$i"} .
|
||||
" lw " .$FW_webArgs{"width_$i"} .
|
||||
" with " .$FW_webArgs{"type_$i"};
|
||||
}
|
||||
print FH "\n";
|
||||
print FH "plot ".join(",\\\n ", @plot)."\n";
|
||||
close(FH);
|
||||
|
||||
return (undef, "continue");
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
@ -532,13 +215,11 @@ weblink_WriteGplot($)
|
||||
<a name="weblinkdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> weblink [link|fileplot|dbplot|image|iframe|htmlCode|cmdList|readings]
|
||||
<code>define <name> weblink [link|image|iframe|htmlCode|cmdList|readings]
|
||||
<argument></code>
|
||||
<br><br>
|
||||
This is a placeholder used with webpgm2 to be able to integrate links
|
||||
into it, and to be able to put more than one gnuplot/SVG picture on one
|
||||
page. It has no set or get methods.
|
||||
|
||||
This is a placeholder device used with FHEMWEB to be able to add user
|
||||
defined links.
|
||||
Examples:
|
||||
<ul>
|
||||
<code>define homepage weblink link http://www.fhem.de</code><br>
|
||||
@ -546,8 +227,6 @@ weblink_WriteGplot($)
|
||||
<code>define interactive_webcam weblink iframe http://w.x.y.z/webcam.cgi</code><br>
|
||||
<code>define hr weblink htmlCode <hr></code><br>
|
||||
<code>define w_Frlink weblink htmlCode { WeatherAsHtml("w_Frankfurt") }</code><br>
|
||||
<code>define MyPlot weblink fileplot <logdevice>:<gnuplot-file>:<logfile></code><br>
|
||||
<code>define MyPlot weblink dbplot <logdevice>:<gnuplot-file></code><br>
|
||||
<code>define systemCommands weblink cmdList pair:Pair:set+cul2+hmPairForSec+60 restart:Restart:shutdown+restart update:UpdateCheck:update+check</code><br>
|
||||
<code>define wl_SystemStatus weblink readings sysstat *nostate *notime {{ 'load' => 'Systemauslastung', 'temperature' => 'Systemtemperatur in &deg;;C'}}</code><br>
|
||||
<code>define wlHeizung weblink readings t1:temperature t2:temperature t3:temperature *notime {{ 't1.temperature' => 'Vorlauf', 't2.temperature' => 'R&uuml;;cklauf', 't3.temperature' => 'Zirkulation'}}</code>
|
||||
@ -556,15 +235,8 @@ weblink_WriteGplot($)
|
||||
|
||||
Notes:
|
||||
<ul>
|
||||
<li>Normally you won't have to define fileplot weblinks manually, as
|
||||
FHEMWEB makes it easy for you, just plot a logfile (see
|
||||
<a href="#logtype">logtype</a>) and convert it to weblink. Now you
|
||||
can group these weblinks by putting them into rooms. If you convert
|
||||
the current logfile to a weblink, it will always refer to the current
|
||||
file, even if its name changes regularly (and not the one you
|
||||
originally specified).</li>
|
||||
<li>For cmdList <argument> consist of a list of space separated icon:label:cmd triples.</li>
|
||||
<li>For redings <argument> consist of one or more <device>[:regex] pairs,
|
||||
<li>For readings <argument> consist of one or more <device>[:regex] pairs,
|
||||
zero or more of the modifiers *noheading, *notime and *nostate and as the last argument an optional {} expression that should return
|
||||
a perl hash to map reading names to display names.
|
||||
<ul>
|
||||
@ -583,16 +255,7 @@ weblink_WriteGplot($)
|
||||
</ul>
|
||||
|
||||
<a name="weblinkset"></a>
|
||||
<b>Set</b>
|
||||
<ul>
|
||||
<li>copyGplotFile<br>
|
||||
Only applicable to fileplot type weblinks.<br>
|
||||
Copy the currently specified gplot file to a new file, which is named
|
||||
after the weblink (existing files will be overwritten), in order to be
|
||||
able to modify it locally without the problem of being overwritten by
|
||||
update. The weblink definition will be updated.
|
||||
</li>
|
||||
</ul><br>
|
||||
<b>Set</b> <ul>N/A</ul><br>
|
||||
|
||||
<a name="weblinkget"></a>
|
||||
<b>Get</b> <ul>N/A</ul><br>
|
||||
@ -610,91 +273,9 @@ weblink_WriteGplot($)
|
||||
attr yw weblink htmlattr width="480" height="560"<br>
|
||||
</code>
|
||||
</ul></li>
|
||||
<li><a href="#fixedrange">fixedrange</a></li>
|
||||
<li><a href="#plotsize">plotsize</a></li>
|
||||
<li><a href="#plotmode">plotmode</a></li>
|
||||
<a name="label"></a>
|
||||
<li>label<br>
|
||||
Double-Colon separated list of values. The values will be used to replace
|
||||
<L#> type of strings in the .gplot file, with # beginning at 1
|
||||
(<L1>, <L2>, etc.). Each value will be evaluated as a perl
|
||||
expression, so you have access e.g. to the Value functions.<br><br>
|
||||
|
||||
If the plotmode is gnuplot-scroll or SVG, you can also use the min, max,
|
||||
avg, cnt, sum, currval (last value) and currdate (last date) values of
|
||||
the individual curves, by accessing the corresponding values from the
|
||||
data hash, see the example below:<br>
|
||||
|
||||
<ul>
|
||||
<li>Fixed text for the right and left axis:<br>
|
||||
<ul>
|
||||
<li>Fhem config:<br>
|
||||
attr wl_1 label "Temperature"::"Humidity"</li>
|
||||
<li>.gplot file entry:<br>
|
||||
set ylabel <L1><br>
|
||||
set y2label <L2></li>
|
||||
</ul></li>
|
||||
<li>Title with maximum and current values of the 1st curve (FileLog)
|
||||
<ul>
|
||||
<li>Fhem config:<br>
|
||||
attr wl_1 label "Max $data{max1}, Current $data{currval1}"</li>
|
||||
<li>.gplot file entry:<br>
|
||||
set title <L1><br></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<a name="title"></a>
|
||||
<li>title<br>
|
||||
A special form of label (see above), which replaces the string <TL>
|
||||
in the .gplot file. It defaults to the filename of the logfile.
|
||||
</li>
|
||||
|
||||
<a name="plotfunction"></a>
|
||||
<li>plotfunction<br>
|
||||
Space value separated list of values. The value will be used to replace
|
||||
<SPEC#> type of strings in the .gplot file, with # beginning at 1
|
||||
(<SPEC1>, <SPEC2>, etc.) in the #FileLog or #DbLog directive.
|
||||
With this attribute you can use the same .gplot file for multiple devices
|
||||
with the same logdevice.
|
||||
<ul><b>Example:</b><br>
|
||||
<li>#FileLog <SPEC1><br>
|
||||
with: attr <weblinkdevice> plotfunction "4:IR\x3a:0:"<br>
|
||||
instead of<br>
|
||||
#FileLog 4:IR\x3a:0:
|
||||
</li>
|
||||
<li>#DbLog <SPEC1><br>
|
||||
with: attr <weblinkdevice> plotfunction
|
||||
"Garage_Raumtemp:temperature::"<br> instead of<br>
|
||||
#DbLog Garage_Raumtemp:temperature::
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="weblinkEditor"></a>
|
||||
<b>Plot-Editor specialities</b>
|
||||
<ul>
|
||||
If the weblink type is set to fileplot, a weblink editor is displayed in
|
||||
the detail view of the weblink. Most features are obvious here, up to some
|
||||
exceptions:
|
||||
<li>if you want to omit the title for a Diagram label, enter notitle in the
|
||||
input field.</li>
|
||||
<li>if you want to specify a fixed value (not taken from a column) if a
|
||||
string found (e.g. 1 of the FS20 switch is on 0 if it off), then you have
|
||||
to specify the Tics first, and write the .gplot file, before you can
|
||||
select this value from the dropdown.<br>
|
||||
Example:
|
||||
<ul>
|
||||
Enter in the Tics field: ("On" 1, "Off" 0)<br>
|
||||
Write .gplot file<br>
|
||||
Select "1" from the column dropdown (note the double quote!) for the
|
||||
regexp switch.on, and "0" for the regexp switch.off.<br>
|
||||
Write .gplot file again<br>
|
||||
</ul></li>
|
||||
</ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
@ -104,6 +104,7 @@
|
||||
<a href="#sequence">sequence</a>
|
||||
<a href="#speedtest">speedtest</a>
|
||||
<a href="#structure">structure</a>
|
||||
<a href="#SVG">SVG</a>
|
||||
<a href="#telnet">telnet</a>
|
||||
<a href="#Twilight">Twilight</a>
|
||||
<a href="#THRESHOLD">THRESHOLD</a>
|
||||
|
@ -102,6 +102,7 @@
|
||||
<a href="#sequence">sequence</a>
|
||||
<a href="#speedtest">speedtest</a>
|
||||
<a href="#structure">structure</a>
|
||||
<a href="#SVG">SVG</a>
|
||||
<a href="#telnet">telnet</a>
|
||||
<a href="#Twilight">Twilight</a>
|
||||
<a href="#THRESHOLD">THRESHOLD</a>
|
||||
|
Loading…
Reference in New Issue
Block a user