First version for SVG / HOWTO

git-svn-id: https://svn.fhem.de/fhem/trunk@206 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig
2008-07-11 07:35:12 +00:00
parent 26c86d53ac
commit 2bf3c9da87
13 changed files with 919 additions and 322 deletions

View File

@@ -9,7 +9,7 @@ use IO::Socket;
################### ###################
# Config # Config
my $__ME; use vars qw($__ME);
my $FHEMWEBdir; my $FHEMWEBdir;
my $FHEMWEB_tmpfile = "/tmp/file.$$"; my $FHEMWEB_tmpfile = "/tmp/file.$$";
my $FHEMWEB_reldoc; my $FHEMWEB_reldoc;
@@ -27,15 +27,16 @@ sub FHEMWEB_digestCgi($);
sub FHEMWEB_doDetail($); sub FHEMWEB_doDetail($);
sub FHEMWEB_fileList($); sub FHEMWEB_fileList($);
sub FHEMWEB_makeTable($$$$$$$$); sub FHEMWEB_makeTable($$$$$$$$);
sub FHEMWEB_parseXmlList(); sub FHEMWEB_parseXmlList($);
sub FHEMWEB_showRoom(); sub FHEMWEB_showRoom();
sub FHEMWEB_showArchive($); sub FHEMWEB_showArchive($);
sub FHEMWEB_showLog($); sub FHEMWEB_showLog($);
sub FHEMWEB_showLogWrapper($); sub FHEMWEB_showLogWrapper($);
sub FHEMWEB_popup($$$); sub FHEMWEB_select($$$);
sub FHEMWEB_textfield($$); sub FHEMWEB_textfield($$);
sub FHEMWEB_submit($$); sub FHEMWEB_submit($$);
sub FHEMWEB_roomOverview(); sub FHEMWEB_style($$);
sub FHEMWEB_roomOverview($);
sub FHEMWEB_fatal($); sub FHEMWEB_fatal($);
sub pF($@); sub pF($@);
sub pO(@); sub pO(@);
@@ -45,24 +46,28 @@ sub FHEMWEB_calcWeblink($$);
######################### #########################
# As we are _not_ multithreaded, it is safe to use global variables. # As we are _not_ multithreaded, it is safe to use global variables.
my %__icons; my %__icons; # List of icons
my $__iconsread; my $__iconsread; # Timestamp of last icondir check
my %__rooms; my %__rooms; # hash of all rooms
my %__devs; my %__devs; # hash of all devices ant their attributes
my %__types; my %__types; # device types, for sorting
my $__room; my $__room; # currently selected room
my $__detail; my $__detail; # durrently selected device for detail view
my $__title; my $__title; # Page title
my $__cmdret; my $__cmdret; # Returned data by the fhem call
my $__scrolledweblinkcount; my $__scrolledweblinkcount; # Number of scrolled weblinks
my %__wlpos; my %__wlpos; # WebLink scroll position
my $__RET; my $__RET; # Returned data (html)
my $__RETTYPE; my $__RETTYPE; # image/png or the like
my $__SF; my $__SF; # Short for submit form
my $__ti; # Tabindex for all input fields my $__ti; # Tabindex for all input fields
my @__zoom; my @__zoom; # "day","week","month","year"
my %__zoom; my %__zoom; # the same as @__zoom
my $__plotmode; my $__plotmode; # Current plotmode
my $__plotsize; # Size for a plot
my $__data; # Filecontent from browser when editing a file
my $__svgloaded; # Do not load the SVG twice
my $__lastxmllist; # last time xmllist was parsed
##################################### #####################################
@@ -75,7 +80,7 @@ FHEMWEB_Initialize($)
$hash->{DefFn} = "FHEMWEB_Define"; $hash->{DefFn} = "FHEMWEB_Define";
$hash->{UndefFn} = "FHEMWEB_Undef"; $hash->{UndefFn} = "FHEMWEB_Undef";
$hash->{AttrList}= "loglevel:0,1,2,3,4,5,6 webdir webname plotmode:gnuplot,gnuplot-scroll,SVG"; $hash->{AttrList}= "loglevel:0,1,2,3,4,5,6 webname plotmode:gnuplot,gnuplot-scroll,SVG plotsize";
} }
##################################### #####################################
@@ -158,8 +163,7 @@ FHEMWEB_Read($)
my $name = $hash->{SNAME}; my $name = $hash->{SNAME};
my $ll = GetLogLevel($name,4); my $ll = GetLogLevel($name,4);
$FHEMWEBdir = ($attr{$name} && $attr{$name}{webdir}) ? $FHEMWEBdir = "$attr{global}{modpath}/FHEM";
$attr{$name}{webdir} : "$attr{global}{modpath}/FHEM";
$__ME = "/" . (($attr{$name} && $attr{$name}{webname}) ? $__ME = "/" . (($attr{$name} && $attr{$name}{webname}) ?
$attr{$name}{webname} : "fhem"); $attr{$name}{webname} : "fhem");
$FHEMWEB_reldoc = "$__ME/commandref.html"; $FHEMWEB_reldoc = "$__ME/commandref.html";
@@ -186,18 +190,20 @@ FHEMWEB_Read($)
$hash->{BUF} = ""; $hash->{BUF} = "";
Log($ll, "HTTP $hash->{NAME} GET $arg"); Log($ll, "HTTP $hash->{NAME} GET $arg");
$__plotmode = $attr{$name}{plotmode} ? $attr{$name}{plotmode} : "gnuplot"; $__plotmode = $attr{$name}{plotmode} ? $attr{$name}{plotmode} : "SVG";
$__plotsize = $attr{$name}{plotsize} ? $attr{$name}{plotsize} : "800,200";
FHEMWEB_AnswerCall($arg); my $cacheable = FHEMWEB_AnswerCall($arg);
my $c = $hash->{CD}; my $c = $hash->{CD};
my $l = length($__RET); my $l = length($__RET);
# my $exp = localtime(time()+300) . " GMT"; my $e = ($cacheable? ("Expires: ".localtime(time()+900)." GMT\r\n") : "");
# "Expires: $exp\r\n", #Log 0, "$arg / RL: $l";
print $c "HTTP/1.1 200 OK\r\n", print $c "HTTP/1.1 200 OK\r\n",
"Content-Length: $l\r\n", "Content-Length: $l\r\n",
"Content-Type: $__RETTYPE\r\n\r\n", $e,
$__RET; "Content-Type: $__RETTYPE\r\n\r\n",
$__RET;
} }
@@ -206,50 +212,55 @@ FHEMWEB_AnswerCall($)
{ {
my ($arg) = @_; my ($arg) = @_;
%__rooms = ();
%__devs = ();
%__wlpos = (); %__wlpos = ();
%__types = ();
$__room = ""; $__room = "";
$__detail = ""; $__detail = "";
$__title = "";
$__cmdret = ""; $__cmdret = "";
$__RET = ""; $__RET = "";
$__RETTYPE = "text/html; charset=ISO-8859-1"; $__RETTYPE = "text/html; charset=ISO-8859-1";
$__ti = 1; $__ti = 1;
# Lets go: # Lets go:
if($arg =~ m/^$FHEMWEB_reldoc/) { if($arg =~ m,^${__ME}/(.*html)$, || $arg =~ m,^${__ME}/(.*svg)$,) {
open(FH, "$FHEMWEBdir/commandref.html") || return; my $f = $1;
open(FH, "$FHEMWEBdir/$f") || return;
pO join("", <FH>); pO join("", <FH>);
close(FH); close(FH);
return; $__RETTYPE = "text/html; charset=ISO-8859-1" if($f =~ m/\.*html$/);
} elsif($arg =~ m,^$__ME/style.css,) { return 1;
open(FH, "$FHEMWEBdir/style.css") || return; } elsif($arg =~ m,^$__ME/(.*).css,) {
open(FH, "$FHEMWEBdir/$1.css") || return;
pO join("", <FH>); pO join("", <FH>);
close(FH); close(FH);
$__RETTYPE = "text/css"; $__RETTYPE = "text/css";
return; return 1;
} elsif($arg =~ m,^$__ME/icons/(.*)$,) { } elsif($arg =~ m,^$__ME/icons/(.*)$,) {
open(FH, "$FHEMWEBdir/$1") || return; open(FH, "$FHEMWEBdir/$1") || return;
pO join("", <FH>); pO join("", <FH>);
close(FH); close(FH);
$__RETTYPE = "image/gif"; $__RETTYPE = "image/*";
return; return 1;
} elsif($arg !~ m/^$__ME(.*)/) { } elsif($arg !~ m/^$__ME(.*)/) {
Log(5, "Unknown document $arg requested"); Log(5, "Unknown document $arg requested");
return; return 0;
} }
my $cmd = FHEMWEB_digestCgi($1); my $cmd = FHEMWEB_digestCgi($1);
my $docmd = 0;
$docmd = 1 if($cmd &&
$cmd !~ /^showlog/ &&
$cmd !~ /^toweblink/ &&
$cmd !~ /^showarchive/ &&
$cmd !~ /^style / &&
$cmd !~ /^edit/);
$__cmdret = fC($cmd) if($cmd &&
$cmd !~ /^showlog/ && $__cmdret = fC($cmd) if($docmd);
$cmd !~ /^toweblink/ && FHEMWEB_parseXmlList($docmd);
$cmd !~ /^showarchive/ && if($cmd =~ m/^showlog /) {
$cmd !~ /^edit/); FHEMWEB_showLog($cmd);
FHEMWEB_parseXmlList(); return 0;
return FHEMWEB_showLog($cmd) if($cmd =~ m/^showlog /); }
if($cmd =~ m/^toweblink (.*)$/) { if($cmd =~ m/^toweblink (.*)$/) {
my @aa = split(":", $1); my @aa = split(":", $1);
@@ -262,13 +273,13 @@ FHEMWEB_AnswerCall($)
$__cmdret = fC("define wl_$max weblink fileplot $aa[0]:$aa[1]:$aa[2]"); $__cmdret = fC("define wl_$max weblink fileplot $aa[0]:$aa[1]:$aa[2]");
if(!$__cmdret) { if(!$__cmdret) {
$__detail = "wl_$max"; $__detail = "wl_$max";
FHEMWEB_parseXmlList() FHEMWEB_parseXmlList(1);
} }
} }
pO "<html><head><title>$__title</title>"; pO "<html>\n<head>\n<title>$__title</title>\n";
pO "<link href=\"$__ME/style.css\" rel=\"stylesheet\"/>"; pO "<link href=\"$__ME/style.css\" rel=\"stylesheet\"/>\n";
pO "</head><body name=\"$__title\">\n"; pO "</head>\n<body name=\"$__title\">\n";
if($__cmdret) { if($__cmdret) {
$__detail = ""; $__detail = "";
@@ -280,12 +291,14 @@ FHEMWEB_AnswerCall($)
pO "</div>\n"; pO "</div>\n";
} }
FHEMWEB_roomOverview(); FHEMWEB_roomOverview($cmd);
FHEMWEB_style($cmd,undef) if($cmd =~ m/^style /);
FHEMWEB_doDetail($__detail) if($__detail); FHEMWEB_doDetail($__detail) if($__detail);
FHEMWEB_showRoom() if($__room && !$__detail); FHEMWEB_showRoom() if($__room && !$__detail);
FHEMWEB_showLogWrapper($cmd) if($cmd =~ /^showlogwrapper/); FHEMWEB_showLogWrapper($cmd) if($cmd =~ /^showlogwrapper/);
FHEMWEB_showArchive($cmd) if($cmd =~ m/^showarchive/); FHEMWEB_showArchive($cmd) if($cmd =~ m/^showarchive/);
pO "</body></html>"; pO "</body></html>";
return 0;
} }
@@ -303,7 +316,9 @@ FHEMWEB_digestCgi($)
$pv =~ s/\+/ /g; $pv =~ s/\+/ /g;
$pv =~ s/%(..)/chr(hex($1))/ge; $pv =~ s/%(..)/chr(hex($1))/ge;
my ($p,$v) = split("=",$pv, 2); my ($p,$v) = split("=",$pv, 2);
$v =~ s/[\r]\n/\\\n/g; # Multiline: escape the NL for fhem
# Multiline: escape the NL for fhem
$v =~ s/[\r]\n/\\\n/g if($v && $p && $p ne "data");
#Log(0, "P: $p, V: $v"); #Log(0, "P: $p, V: $v");
if($p eq "detail") { $__detail = $v; } if($p eq "detail") { $__detail = $v; }
@@ -314,7 +329,7 @@ FHEMWEB_digestCgi($)
if($p =~ m/^dev\.(.*)$/) { $dev{$1} = $v; } if($p =~ m/^dev\.(.*)$/) { $dev{$1} = $v; }
if($p =~ m/^cmd\.(.*)$/) { $cmd = $v; $c= $1; } if($p =~ m/^cmd\.(.*)$/) { $cmd = $v; $c= $1; }
if($p eq "wlpos") { %__wlpos = split(/[=;]/, $v); } if($p eq "wlpos") { %__wlpos = split(/[=;]/, $v); }
if($p eq "data") { $__data = $v; }
} }
$cmd.=" $dev{$c}" if($dev{$c}); $cmd.=" $dev{$c}" if($dev{$c});
@@ -326,9 +341,22 @@ FHEMWEB_digestCgi($)
##################### #####################
# Get the data and parse it. We are parsing XML in a non-scientific way :-) # Get the data and parse it. We are parsing XML in a non-scientific way :-)
sub sub
FHEMWEB_parseXmlList() FHEMWEB_parseXmlList($)
{ {
my $docmd = shift;
my $name; my $name;
if(!$docmd && $__lastxmllist && (time() - $__lastxmllist) < 2) {
$__room = $__devs{$__detail}{ATTR}{room}{VAL} if($__detail);
return;
}
$__lastxmllist = time();
%__rooms = ();
%__devs = ();
%__types = ();
$__title = "";
foreach my $l (split("\n", fC("xmllist"))) { foreach my $l (split("\n", fC("xmllist"))) {
####### Device ####### Device
@@ -343,7 +371,7 @@ FHEMWEB_parseXmlList()
####### INT, ATTR & STATE ####### INT, ATTR & STATE
if($l =~ m,^\t\t\t<(.*) key="(.*)" value="([^"]*)"(.*)/>,) { if($l =~ m,^\t\t\t<(.*) key="(.*)" value="([^"]*)"(.*)/>,) {
my ($t, $n, $v, $m) = ($1, $2, $3, $4); my ($t, $n, $v, $m) = ($1, $2, $3, $4);
$v =~ s/&lt;br&gt;/<br>/g; $v =~ s,&lt;br&gt;,<br/>,g;
$__devs{$name}{$t}{$n}{VAL} = $v; $__devs{$name}{$t}{$n}{VAL} = $v;
if($m) { if($m) {
$m =~ m/measured="(.*)"/; $m =~ m/measured="(.*)"/;
@@ -411,12 +439,13 @@ FHEMWEB_makeTable($$$$$$$$)
} }
pO "</tr>\n"; pO "</tr>\n";
if($clist) { if($clist) {
pO "<tr>\n";
my @al = map { s/[:;].*//;$_ } split(" ", $clist); my @al = map { s/[:;].*//;$_ } split(" ", $clist);
pO "<td>" . FHEMWEB_popup("arg.$ccmd$d",\@al,undef) . "</td>"; pO "<td>" . FHEMWEB_select("arg.$ccmd$d",\@al,undef) . "</td>";
pO "<td>" . FHEMWEB_textfield("val.$ccmd$d", 6) . "</td>"; pO "<td>" . FHEMWEB_textfield("val.$ccmd$d", 6) . "</td>";
pO "<td>" . FHEMWEB_submit("cmd.$ccmd$d", $ccmd) . "</td>"; pO "<td>" . FHEMWEB_submit("cmd.$ccmd$d", $ccmd) . "</td>";
pO FHEMWEB_hidden("dev.$ccmd$d", $d); pO FHEMWEB_hidden("dev.$ccmd$d", $d);
pO "</td></tr><tr><td>\n"; pO "</tr>\n";
} }
my $row = 1; my $row = 1;
@@ -436,13 +465,13 @@ FHEMWEB_makeTable($$$$$$$$)
} }
pO "<td>$hash->{$v}{TIM}</td>" if($hash->{$v}{TIM}); pO "<td>$hash->{$v}{TIM}</td>" if($hash->{$v}{TIM});
pO "<td><a href=\"$__ME?cmd.$d=$cmd $d $v&detail=$d\">$cmd</a></td>" pO "<td><a href=\"$__ME?cmd.$d=$cmd $d $v&amp;detail=$d\">$cmd</a></td>"
if($cmd); if($cmd);
pO "</tr>\n"; pO "</tr>\n";
} }
pO " </table>\n"; pO " </table>\n";
pO "<br>\n"; pO "<br/>\n";
} }
@@ -531,13 +560,14 @@ FHEMWEB_doDetail($)
############## ##############
# Room overview # Room overview
sub sub
FHEMWEB_roomOverview() FHEMWEB_roomOverview($)
{ {
my ($cmd) = @_;
pO $__SF; pO $__SF;
pO "<div id=\"hdr\">\n"; pO "<div id=\"hdr\">\n";
pO "<table><tr><td>"; pO "<table><tr><td>";
pO "<a href=\"$FHEMWEB_reldoc\">Cmd</a>: "; pO "<a href=\"$FHEMWEB_reldoc\">Fhem cmd</a>: ";
pO FHEMWEB_textfield("cmd", 30); pO FHEMWEB_textfield("cmd", 30);
$__scrolledweblinkcount = 0; $__scrolledweblinkcount = 0;
if($__room) { if($__room) {
@@ -558,8 +588,8 @@ FHEMWEB_roomOverview()
pO "</div>\n"; pO "</div>\n";
pO "<div id=\"left\">\n"; pO "<div id=\"left\">\n";
pO " <table><tr><td>\n"; # Need for "right" compatibility pO " <table><tr><td>\n";
pO " <table class=\"room\" summary=\"Room list\">\n"; pO " <table class=\"room\" summary=\"Room list\">\n";
$__room = "" if(!$__room); $__room = "" if(!$__room);
foreach my $r (sort keys %__rooms) { foreach my $r (sort keys %__rooms) {
next if($r eq "hidden"); next if($r eq "hidden");
@@ -567,12 +597,18 @@ FHEMWEB_roomOverview()
pO "<td><a href=\"$__ME?room=$r\">$r</a>"; pO "<td><a href=\"$__ME?room=$r\">$r</a>";
pO "</td></tr>\n"; pO "</td></tr>\n";
} }
pF " <tr%s>", "all" eq $__room ? " class=\"sel\"" : ""; pF " <tr%s>", "all" eq $__room ? " class=\"sel\"" : "";
pO "<td><a href=\"$__ME?room=all\">All together</a></td>"; pO "<td><a href=\"$__ME?room=all\">All together</a></td></tr>";
pO " </tr>\n";
pO " </table>\n"; pO " </table>\n";
pO " </td></tr>\n";
pO " <tr><td>\n";
pO " <table class=\"room\" summary=\"Help/Configuration\">\n";
pO " <tr><td><a href=\"$__ME/HOWTO.html\">Howto</a></td></tr>\n";
pO " <tr><td><a href=\"$__ME/commandref.html\">Details</a></td></tr>\n";
my $sel = ($cmd =~ m/^style/) ? " class=\"sel\"" : "";
pO " <tr$sel><td><a href=\"$__ME?cmd=style list\">Misc. files</a></td></tr>\n";
pO " </table>\n";
pO " </td></tr>\n";
pO " </table>\n"; pO " </table>\n";
pO "</div>\n"; pO "</div>\n";
pO "</form>\n"; pO "</form>\n";
@@ -580,7 +616,7 @@ FHEMWEB_roomOverview()
################# #################
# Read in the icons # Read in the icons
sub sub
FHEMWEB_checkDirs() FHEMWEB_checkDirs()
{ {
return if($__iconsread && (time() - $__iconsread) < 5); return if($__iconsread && (time() - $__iconsread) < 5);
@@ -623,7 +659,7 @@ FHEMWEB_showRoom()
next if(!$havedev); next if(!$havedev);
} }
my $rf = ($__room ? "&room=$__room" : ""); my $rf = ($__room ? "&amp;room=$__room" : "");
############################ ############################
@@ -682,6 +718,7 @@ FHEMWEB_showRoom()
$iname = $__icons{"$d"} if($__icons{"$d"}); $iname = $__icons{"$d"} if($__icons{"$d"});
$iname = $__icons{"$d.$iv"} if($__icons{"$d.$iv"}); $iname = $__icons{"$d.$iv"} if($__icons{"$d.$iv"});
} }
$v = "" if(!defined($v));
pO "<td><a href=\"$__ME?detail=$d\">$d</a></td>"; pO "<td><a href=\"$__ME?detail=$d\">$d</a></td>";
if($iname) { if($iname) {
@@ -698,6 +735,7 @@ FHEMWEB_showRoom()
} elsif($type eq "FHT") { } elsif($type eq "FHT") {
$v = $__devs{$d}{STATE}{"measured-temp"}{VAL}; $v = $__devs{$d}{STATE}{"measured-temp"}{VAL};
$v = "" if(!defined($v));
$v =~ s/ .*//; $v =~ s/ .*//;
pO "<td><a href=\"$__ME?detail=$d\">$d</a></td>"; pO "<td><a href=\"$__ME?detail=$d\">$d</a></td>";
@@ -709,7 +747,7 @@ FHEMWEB_showRoom()
pO FHEMWEB_hidden("arg.$d", "desired-temp"); pO FHEMWEB_hidden("arg.$d", "desired-temp");
pO FHEMWEB_hidden("dev.$d", $d); pO FHEMWEB_hidden("dev.$d", $d);
pO "<td>" . pO "<td>" .
FHEMWEB_popup("val.$d",\@tv,$v) . FHEMWEB_select("val.$d",\@tv,$v) .
FHEMWEB_submit("cmd.$d", "set") . "</td>"; FHEMWEB_submit("cmd.$d", "set") . "</td>";
} elsif($type eq "FileLog") { } elsif($type eq "FileLog") {
@@ -723,8 +761,10 @@ FHEMWEB_showRoom()
my %h = ("VAL" => "text"); my %h = ("VAL" => "text");
$l = \%h; $l = \%h;
} }
my @fl = FHEMWEB_fileList($__devs{$d}{INT}{logfile}{VAL});
foreach my $f (FHEMWEB_fileList($__devs{$d}{INT}{logfile}{VAL})) { foreach my $f (@fl) {
pF " </tr>";
pF " <tr class=\"%s\"><td>$f</td>", $row?"odd":"even"; pF " <tr class=\"%s\"><td>$f</td>", $row?"odd":"even";
$row = ($row+1)%2; $row = ($row+1)%2;
foreach my $ln (split(",", $l->{VAL})) { foreach my $ln (split(",", $l->{VAL})) {
@@ -732,7 +772,6 @@ FHEMWEB_showRoom()
$name = $lt if(!$name); $name = $lt if(!$name);
pO "<td><a href=\"$__ME?cmd=showlogwrapper $d $lt $f\">$name</a></td>"; pO "<td><a href=\"$__ME?cmd=showlogwrapper $d $lt $f\">$name</a></td>";
} }
pO "</tr>";
} }
} elsif($type eq "weblink") { } elsif($type eq "weblink") {
@@ -752,17 +791,25 @@ FHEMWEB_showRoom()
} }
pO "<table><tr><td>"; pO "<table><tr><td>";
my $wl = "";
$__wlpos{$va[0]} = $__wlpos{$d} if($__wlpos{$d}); $__wlpos{$va[0]} = $__wlpos{$d} if($__wlpos{$d});
$wl = "&wlpos=" . join(";", map { "$_=$__wlpos{$_}" }
grep { /(zoom|all|$va[0])/ } keys %__wlpos); my $wl = "&amp;wlpos=" . join(";", map { "$_=$__wlpos{$_}" }
grep { /(zoom|all|$va[0])/ } keys %__wlpos);
pO "<img src=\"$__ME?cmd=showlog $d $va[0] $va[1] $va[2]$wl\"/>";
my $arg="$__ME?cmd=showlog $d $va[0] $va[1] $va[2]$wl";
if($__plotmode eq "SVG") {
my ($w, $h) = split(",", $__plotsize);
pO "<embed src=\"$arg\" type=\"image/svg+xml\"" .
"width=\"$w\" height=\"$h\" name=\"$d\"/>\n";
} else {
pO "<img src=\"$arg\"/>\n";
}
pO "</td><td>"; pO "</td><td>";
FHEMWEB_zoomLink("$d=-1", "Prev.png", "prev", 1); FHEMWEB_zoomLink("$d=-1", "Prev.png", "prev", 1);
FHEMWEB_zoomLink("$d=1", "Next.png", "next", 1); FHEMWEB_zoomLink("$d=1", "Next.png", "next", 1);
pO "<a href=\"$__ME?detail=$d\">$d</a></td>"; pO "<a href=\"$__ME?detail=$d\">$d</a>";
pO "</td></tr></table>"; pO "</td></tr></table>";
} }
@@ -774,7 +821,7 @@ FHEMWEB_showRoom()
pO " </tr>\n"; pO " </tr>\n";
} }
pO " </table>\n"; pO " </table>\n";
pO " <br>\n"; # Empty line pO " <br/>\n"; # Empty line
} }
pO " </td></tr>\n</table>\n"; pO " </td></tr>\n</table>\n";
pO "</div>\n"; pO "</div>\n";
@@ -782,6 +829,7 @@ FHEMWEB_showRoom()
} }
################# #################
# return a sorted list of actual files for a given FileLog type string
sub sub
FHEMWEB_fileList($) FHEMWEB_fileList($)
{ {
@@ -827,10 +875,19 @@ FHEMWEB_showLogWrapper($)
} else { } else {
pO "<div id=\"right\">\n"; pO "<div id=\"right\">\n";
pO "<table><tr></td>\n"; pO "<table><tr><td>\n";
pO "<table><tr></td>\n"; pO "<table><tr><td>\n";
pO "<td><img src=\"$__ME?cmd=showlog undef $d $type $file\"/>"; pO "<td>";
pO "<a href=\"$__ME?cmd=toweblink $d:$type:$file\"><br>Convert to weblink</a></td>"; my $arg = "$__ME?cmd=showlog undef $d $type $file";
if($__plotmode eq "SVG") {
my ($w, $h) = split(",", $__plotsize);
pO "<embed src=\"$arg\" type=\"image/svg+xml\"" .
"width=\"$w\" height=\"$h\" name=\"$d\"/>\n";
} else {
pO "<img src=\"$arg\"/>\n";
}
pO "<a href=\"$__ME?cmd=toweblink $d:$type:$file\"><br/>Convert to weblink</a></td>";
pO "</td></tr></table>\n"; pO "</td></tr></table>\n";
pO "</td></tr></table>\n"; pO "</td></tr></table>\n";
pO "</div>\n"; pO "</div>\n";
@@ -838,6 +895,7 @@ FHEMWEB_showLogWrapper($)
} }
###################### ######################
# Generate an image from the log via gnuplot
sub sub
FHEMWEB_showLog($) FHEMWEB_showLog($)
{ {
@@ -846,42 +904,92 @@ FHEMWEB_showLog($)
my $gplot_pgm = "$FHEMWEBdir/$type.gplot"; my $gplot_pgm = "$FHEMWEBdir/$type.gplot";
return FHEMWEB_fatal("Cannot read $gplot_pgm") if(!-r $gplot_pgm); return FHEMWEB_fatal("Cannot read $gplot_pgm") if(!-r $gplot_pgm);
FHEMWEB_calcWeblink($d,$wl); FHEMWEB_calcWeblink($d,$wl);
if($__plotmode eq "gnuplot" || !$__devs{$d}{from}) {
# Looking for the logfile.... if($__plotmode =~ m/gnuplot/) {
if($__plotmode eq "gnuplot" || !$__devs{$d}{from}) {
$__devs{$d}{INT}{logfile}{VAL} =~ m,^(.*)/([^/]*)$,; # Dir and File # Looking for the logfile....
my $path = "$1/$file";
$path = $__devs{$d}{ATTR}{archivedir}{VAL} . "/$file" if(!-f $path);
return FHEMWEB_fatal("Cannot read $path") if(!-r $path);
open(FH, $gplot_pgm) || FHEMWEB_fatal("$gplot_pgm: $!"); $__devs{$d}{INT}{logfile}{VAL} =~ m,^(.*)/([^/]*)$,; # Dir and File
my $gplot_script = join("", <FH>); my $path = "$1/$file";
close(FH); $path = $__devs{$d}{ATTR}{archivedir}{VAL} . "/$file" if(!-f $path);
$gplot_script =~ s/<OUT>/$FHEMWEB_tmpfile/g; return FHEMWEB_fatal("Cannot read $path") if(!-r $path);
$gplot_script =~ s/<IN>/$path/g;
$gplot_script =~ s/<TL>/$file/g;
if($__devs{$wl} && $__devs{$wl}{ATTR}{fixedrange}) { open(FH, $gplot_pgm) || return FHEMWEB_fatal("$gplot_pgm: $!");
my $fr = $__devs{$wl}{ATTR}{fixedrange}{VAL}; my $gplot_script = join("", <FH>);
$fr =~ s/ /\":\"/; close(FH);
$fr = "set xrange [\"$fr\"]\n";
$gplot_script =~ s/(set timefmt ".*")/$1\n$fr/; $gplot_script =~ s/<OUT>/$FHEMWEB_tmpfile/g;
$gplot_script =~ s/<SIZE>/$__plotsize/g;
$gplot_script =~ s/<IN>/$path/g;
$gplot_script =~ s/<TL>/$file/g;
if($__devs{$wl} && $__devs{$wl}{ATTR}{fixedrange}) {
my $fr = $__devs{$wl}{ATTR}{fixedrange}{VAL};
$fr =~ s/ /\":\"/;
$fr = "set xrange [\"$fr\"]\n";
$gplot_script =~ s/(set timefmt ".*")/$1\n$fr/;
}
open(FH, "|gnuplot > /dev/null");# feed it to gnuplot
print FH $gplot_script;
close(FH);
} elsif($__plotmode eq "gnuplot-scroll") {
############################
# 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);
open(FH, $gplot_pgm) || return FHEMWEB_fatal("$gplot_pgm: $!");
while(my $l = <FH>) {
if($l =~ m/^#FileLog (.*)$/) {
push(@filelog, $1);
} elsif($l =~ "^plot" || $plot) {
$plot .= $l;
} else {
push(@data, $l);
}
}
close(FH);
my $gplot_script = join("", @data);
$gplot_script =~ s/<OUT>/$FHEMWEB_tmpfile/g;
$gplot_script =~ s/<SIZE>/$__plotsize/g;
$gplot_script =~ s/<TL>/$file/g;
my ($f,$t)=($__devs{$d}{from}, $__devs{$d}{to});
my @path = split(" ", fC("get $d $file $FHEMWEB_tmpfile $f $t " .
join(" ", @filelog)));
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);
}
open(FH, "|gnuplot > /dev/null");# feed it to gnuplot
print FH $gplot_script, $xrange, $plot;
close(FH);
foreach my $p (@path) {
unlink($p);
}
} }
$__RETTYPE = "image/png";
open(FH, "|gnuplot > /dev/null");# feed it to gnuplot open(FH, "$FHEMWEB_tmpfile.png"); # read in the result and send it
print FH $gplot_script; pO join("", <FH>);
close(FH); close(FH);
unlink("$FHEMWEB_tmpfile.png");
} else { # gnuplot-scroll } elsif($__plotmode eq "SVG") {
############################
# 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 (@filelog, @data, $plot);
open(FH, $gplot_pgm) || FHEMWEB_fatal("$gplot_pgm: $!"); open(FH, $gplot_pgm) || return FHEMWEB_fatal("$gplot_pgm: $!");
while(my $l = <FH>) { while(my $l = <FH>) {
if($l =~ m/^#FileLog (.*)$/) { if($l =~ m/^#FileLog (.*)$/) {
push(@filelog, $1); push(@filelog, $1);
@@ -892,41 +1000,21 @@ FHEMWEB_showLog($)
} }
} }
close(FH); close(FH);
my $gplot_script = join("", @data);
$gplot_script =~ s/<OUT>/$FHEMWEB_tmpfile/g;
$gplot_script =~ s/<TL>/$file/g;
my ($f,$t)=($__devs{$d}{from}, $__devs{$d}{to}); my ($f,$t)=($__devs{$d}{from}, $__devs{$d}{to});
$f = 0 if(!$f); # From the beginning of time...
$t = 9 if(!$t); # till the end
my @path = split(" ", fC("get $d $file $FHEMWEB_tmpfile $f $t " . my $ret = fC("get $d $file - $f $t " . join(" ", @filelog));
join(" ", @filelog))); SVG_render($file, $__plotsize, $f, $t, \@data, \$ret, $plot);
my $i = 0; $__RETTYPE = "image/svg+xml";
$plot =~ s/\".*?using 1:[^ ]+ /"\"$path[$i++]\" using 1:2 "/gse;
my $xrange = "set xrange [\"$f\":\"$t\"]\n";
foreach my $p (@path) {
next if(!-z $p);
open(FH, ">$p");
print FH "$f 0\n";
close(FH);
}
open(FH, "|gnuplot > /dev/null");# feed it to gnuplot
print FH $gplot_script, $xrange, $plot;
close(FH);
foreach my $p (@path) {
unlink($p);
}
} }
$__RETTYPE = "image/png";
open(FH, "$FHEMWEB_tmpfile.png"); # read in the result and send it
pO join("", <FH>);
close(FH);
unlink("$FHEMWEB_tmpfile.png");
} }
################## ##################
sub sub
FHEMWEB_fatal($) FHEMWEB_fatal($)
@@ -944,8 +1032,9 @@ FHEMWEB_hidden($$)
} }
################## ##################
# Generate a select field with option list
sub sub
FHEMWEB_popup($$$) FHEMWEB_select($$$)
{ {
my ($n, $va, $def) = @_; my ($n, $va, $def) = @_;
my $s = "<select name=\"$n\" tabindex=\"$__ti\">"; my $s = "<select name=\"$n\" tabindex=\"$__ti\">";
@@ -972,6 +1061,8 @@ FHEMWEB_textfield($$)
return $s; return $s;
} }
##################
# Multiline (for some types of widgets) editor with submit
sub sub
FHEMWEB_makeEdit($$$$) FHEMWEB_makeEdit($$$$)
{ {
@@ -980,7 +1071,8 @@ FHEMWEB_makeEdit($$$$)
pO "<td>"; pO "<td>";
pO "<div id=\"edit\" style=\"display:none\"><form>"; pO "<div id=\"edit\" style=\"display:none\"><form>";
my $eval = $val; my $eval = $val;
$eval =~ s/<br>/\n/g; $eval =~ s,<br/>,\n,g;
if($type eq "at" || $type eq "notify") { if($type eq "at" || $type eq "notify") {
pO "<textarea name=\"val.${cmd}$name\" cols=\"60\" rows=\"10\" ". pO "<textarea name=\"val.${cmd}$name\" cols=\"60\" rows=\"10\" ".
"tabindex=\"$__ti\">$eval</textarea>"; "tabindex=\"$__ti\">$eval</textarea>";
@@ -989,7 +1081,7 @@ FHEMWEB_makeEdit($$$$)
"tabindex=\"$__ti\" value=\"$eval\"/>"; "tabindex=\"$__ti\" value=\"$eval\"/>";
} }
$__ti++; $__ti++;
pO "<br>" . FHEMWEB_submit("cmd.${cmd}$name", "$cmd $name"); pO "<br/>" . FHEMWEB_submit("cmd.${cmd}$name", "$cmd $name");
pO "</form></div>"; pO "</form></div>";
$eval = "<pre>$eval</pre>" if($eval =~ m/\n/); $eval = "<pre>$eval</pre>" if($eval =~ m/\n/);
pO "<div id=\"disp\">$eval</div>"; pO "<div id=\"disp\">$eval</div>";
@@ -1007,6 +1099,7 @@ FHEMWEB_submit($$)
} }
################## ##################
# Generate the zoom and scroll images with links if appropriate
sub sub
FHEMWEB_zoomLink($$$$) FHEMWEB_zoomLink($$$$)
{ {
@@ -1020,7 +1113,7 @@ FHEMWEB_zoomLink($$$$)
my $val = $__wlpos{$d}; my $val = $__wlpos{$d};
$cmd = "room=$__room&wlpos="; $cmd = "room=$__room&amp;wlpos=";
if($d eq "zoom") { if($d eq "zoom") {
$val = "day" if(!$val); $val = "day" if(!$val);
@@ -1046,7 +1139,7 @@ FHEMWEB_zoomLink($$$$)
pO "<a href=\"$__ME?$cmd\">"; pO "<a href=\"$__ME?$cmd\">";
pO "<img style=\"border-color:transparent\" alt=\"$alt\" ". pO "<img style=\"border-color:transparent\" alt=\"$alt\" ".
"src=\"$__ME/icons/$img\"/></a>"; "src=\"$__ME/icons/$img\"/></a>";
pO "<br>" if($br); pO "<br/>" if($br);
} }
################## ##################
@@ -1131,6 +1224,62 @@ FHEMWEB_calcWeblink($$)
} }
################## ##################
# List/Edit/Save css and gnuplot files
sub
FHEMWEB_style($$)
{
my ($cmd, $msg) = @_;
my @a = split(" ", $cmd);
if($a[1] eq "list") {
my @fl = FHEMWEB_fileList("$FHEMWEBdir/.*.css");
push(@fl, "<br>");
push(@fl, FHEMWEB_fileList("$FHEMWEBdir/.*.gplot"));
push(@fl, "<br>");
push(@fl, FHEMWEB_fileList("$FHEMWEBdir/.*html"));
pO "<div id=\"right\">\n";
pO " <table><tr><td>\n";
pO " $msg<br/><br/>\n" if($msg);
pO " <table class=\"at\">\n";
my $row = 0;
foreach my $file (@fl) {
pO "<tr class=\"" . ($row?"odd":"even") . "\">";
pO "<td><a href=\"$__ME?cmd=style edit $file\">$file</a></td></tr>";
$row = ($row+1)%2;
}
pO " </table>\n";
pO " </td></tr></table>\n";
pO "</div>\n";
} elsif($a[1] eq "edit") {
open(FH, "$FHEMWEBdir/$a[2]") || return FHEMWEB_fatal("$a[2]: $!");
my $data = join("", <FH>);
close(FH);
pO "<div id=\"right\">\n";
pO " <form>";
pO FHEMWEB_submit("save", "Save $a[2]") . "<br/><br/>";
pO FHEMWEB_hidden("cmd", "style save $a[2]");
pO "<textarea name=\"data\" cols=\"80\" rows=\"30\">" .
"$data</textarea>";
pO "</form>";
pO "</div>\n";
} elsif($a[1] eq "save") {
open(FH, ">$FHEMWEBdir/$a[2]") || return FHEMWEB_fatal("$a[2]: $!");
print FH $__data;
close(FH);
FHEMWEB_style("style list", "Saved file $a[2]");
}
}
##################
# print formatted
sub sub
pF($@) pF($@)
{ {
@@ -1139,6 +1288,7 @@ pF($@)
} }
################## ##################
# print output
sub sub
pO(@) pO(@)
{ {
@@ -1146,12 +1296,14 @@ pO(@)
} }
################## ##################
# fhem command
sub sub
fC($) fC($)
{ {
my ($cmd) = @_; my ($cmd) = @_;
#Log 0, "Calling $cmd";
my $oll = $attr{global}{verbose}; my $oll = $attr{global}{verbose};
$attr{global}{verbose} = 0; $attr{global}{verbose} = 0 if($cmd ne "save");
my $ret = AnalyzeCommand(undef, $cmd); my $ret = AnalyzeCommand(undef, $cmd);
if($cmd !~ m/attr.*global.*verbose/) { if($cmd !~ m/attr.*global.*verbose/) {
$attr{global}{verbose} = $oll; $attr{global}{verbose} = $oll;

380
fhem/webfrontend/pgm2/98_SVG.pm Executable file
View File

@@ -0,0 +1,380 @@
##############################################
package main;
use strict;
use warnings;
use POSIX;
sub SVG_render($$$$$$$);
sub time_to_sec($);
sub fmtTime($$);
my ($__lt, $__ltstr);
#####################################
sub
SVG_Initialize($)
{
my ($hash) = @_;
}
#####################################
sub
SVG_render($$$$$$$)
{
my ($file, $wh, $from, $to, $confp, $dp, $plot) = @_;
my ($ow,$oh) = split(",", $wh); # Original width
my $th = 16; # "Font" height
my ($x, $y) = (3*$th, 1.5*$th); # Rect offset
my ($w, $h) = ($ow-2*$x, $oh-2*$y); # Rect size
my %conf; # gnuplot file settings
# Convert the configuration to a "readable" form -> array to hash
map { chomp; my @a=split(" ",$_, 3);
if($a[0] && $a[0] eq "set") { $conf{$a[1]} = $a[2]; } } @{$confp};
# Html Header
pO "<?xml version=\"1.0\" encoding=\"iso8859-1\"?>\n";
pO "<?xml-stylesheet href=\"$__ME/svg_style.css\" type=\"text/css\"?>\n";
pO "<!DOCTYPE svg>\n";
pO "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n";
# Rectangle
pO "<rect x=\"$x\" y=\"$y\" width =\"$w\" height =\"$h\"
stroke-width=\"1px\" class=\"border\"/>\n";
my ($off1,$off2) = ($ow/2, 3*$y/4);
pO "<text x=\"$off1\" y=\"$off2\"
class=\"title\" text-anchor=\"middle\">$file</text>\n";
my $t = ($conf{ylabel} ? $conf{ylabel} : "");
$t =~ s/"//g;
($off1,$off2) = (3*$th/4, $oh/2);
pO "<text x=\"$off1\" y=\"$off2\" text-anchor=\"middle\"
class=\"ylabel\" transform=\"rotate(270,$off1,$off2)\">$t</text>\n";
$t = ($conf{y2label} ? $conf{y2label} : "");
$t =~ s/"//g;
($off1,$off2) = ($ow-$th/4, $oh/2);
pO "<text x=\"$off1\" y=\"$off2\" text-anchor=\"middle\"
class=\"y2label\" transform=\"rotate(270,$off1,$off2)\">$t</text>\n";
# Digest axes/title/type from $plot (gnuplot) and draw the line-titles
my (@axes,@ltitle,@type);
my $i;
$i = 0; $plot =~ s/ axes (\w+)/$axes[$i++]=$1/gse;
$i = 0; $plot =~ s/ title '([^']*)'/$ltitle[$i++]=$1/gse;
$i = 0; $plot =~ s/ with (\w+)/$type[$i++]=$1/gse;
for my $i (0..int(@type)-1) { # axes is optional
$axes[$i] = "x1y2" if(!$axes[$i]);
}
($off1,$off2) = ($ow-$x-$th, $y+$th);
for my $i (0..int(@ltitle)-1) {
pO "<text x=\"$off1\" y=\"$off2\" text-anchor=\"end\" ".
"class=\"l$i\">$ltitle[$i]</text>\n";
$off2 += $th;
}
# Loop over the input, digest dates, calculate min/max values
my ($fromsec, $tosec);
$fromsec = time_to_sec($from) if($from ne "0"); # 0 is special
$tosec = time_to_sec($to) if($to ne "9"); # 9 is special
my $tmul;
$tmul = $w/($tosec-$fromsec) if($tosec && $fromsec);
my ($min, $max, $idx) = (99999999, -99999999, 0);
my (%hmin, %hmax, @hdx, @hdy);
my ($dxp, $dyp) = (\(), \());
my ($d, $v);
foreach my $l (split("\n", $$dp)) {
if($l =~ m/^#/) {
my $a = $axes[$idx];
$hmin{$a} = $min if(!defined($hmin{$a}) || $hmin{$a} > $min);
$hmax{$a} = $max if(!defined($hmax{$a}) || $hmax{$a} < $max);
($min, $max) = (99999999, -99999999);
$hdx[$idx] = $dxp; $hdy[$idx] = $dyp;
($dxp, $dyp) = (\(), \());
$idx++;
} else {
($d, $v) = split(" ", $l);
push @{$dxp}, ($tmul ? int((time_to_sec($d)-$fromsec)*$tmul) : $d);
push @{$dyp}, $v;
$min = $v if($min > $v);
$max = $v if($max < $v);
}
}
$dxp = $hdx[0];
if(int(@{$dxp}) < 2 && !$tosec) { # not enough data and no range...
pO "</svg>\n";
return;
}
if(!$tmul) { # recompute the x data if no range sepcified
$fromsec = time_to_sec($dxp->[0]) if(!$fromsec);
$tosec = time_to_sec($dxp->[int(@{$dxp})-1]) if(!$tosec);
$tmul = $w/($tosec-$fromsec);
for my $i (0..@hdx-1) {
$dxp = $hdx[$i];
for my $i (0..@{$dxp}-1) {
$dxp->[$i] = int((time_to_sec($dxp->[$i])-$fromsec)*$tmul);
}
}
}
# Compute & draw vertical tics, grid and labels
my $ddur = ($tosec-$fromsec)/86400;
my ($first_tag, $tag, $step, $tstep, $aligntext, $aligntics);
if($ddur <= 1) {
$first_tag=". 2 1"; $tag=": 3 4"; $step = 4*3600; $tstep = 3600;
} elsif ($ddur <= 7) {
$first_tag=". 6"; $tag=". 2 1"; $step = 24*3600; $tstep = 6*3600;
} elsif ($ddur <= 31) {
$first_tag=". 6"; $tag=". 2 1"; $step = 7*24*3600; $tstep = 24*3600;
$aligntext = 1;
} else {
$first_tag=". 6"; $tag=". 1"; $step = 28*24*3600; $tstep = 28*24*3600;
$aligntext = 2; $aligntics = 2;
}
# First the tics
$off2 = $y+4;
my ($off3, $off4) = ($y+$h-4, $y+$h);
my $initoffset = $tstep;
$initoffset = int(($tstep/2)/86400)*86400 if($aligntics);
for(my $i = $fromsec+$initoffset; $i < $tosec; $i += $tstep) {
$i = time_align($i,$aligntics);
$off1 = int($x+($i-$fromsec)*$tmul);
pO "<polyline points=\"$off1,$y $off1,$off2\"/>\n";
pO "<polyline points=\"$off1,$off3 $off1,$off4\"/>\n";
}
# then the text and the grid
$off1 = $x;
$off2 = $y+$h+$th;
$t = fmtTime($first_tag, $fromsec);
pO "<text x=\"0\" y=\"$off2\" class=\"ylabel\">$t</text>";
$initoffset = $step;
$initoffset = int(($step/2)/86400)*86400 if($aligntext);
for(my $i = $fromsec+$initoffset; $i < $tosec; $i += $step) {
$i = time_align($i,$aligntext);
$off1 = int($x+($i-$fromsec)*$tmul);
pO "<polyline points=\"$off1,$y $off1,$off4\" class=\"hgrid\"/>\n";
$t = fmtTime($tag, $i);
pO "<text x=\"$off1\" y=\"$off2\" class=\"ylabel\"
text-anchor=\"middle\">$t</text>";
}
# Left and right axis tics / text / grid
$hmin{x1y1}=$hmin{x1y2}, $hmax{x1y1}=$hmax{x1y2} if(!defined($hmin{x1y1}));
$hmin{x1y2}=$hmin{x1y1}, $hmax{x1y2}=$hmax{x1y1} if(!defined($hmin{x1y2}));
for my $axis ("x1y1", "x1y2") {
# Round values, compute a nice step
my $dh = $hmax{$axis} - $hmin{$axis};
my ($step, $mi, $ma);
my @limit = (1,2,5,10,20,50,100,200,500,1000,2000,5000,10000);
for my $li (0..int(@limit)-1) {
my $l = $limit[$li];
next if($dh > $l);
$ma = doround($hmax{$axis}, $l/10, 1);
$mi = doround($hmin{$axis}, $l/10, 0);
if(($ma-$mi)/($l/10) >= 7) { # If more then 7 steps, then choose next
$l = $limit[$li+1];
$ma = doround($hmax{$axis}, $l/10, 1);
$mi = doround($hmin{$axis}, $l/10, 0);
}
$step = $l/10;
last;
}
# yrange handling
my $yr = ($axis eq "x1y1" ? "yrange" : "y2range");
if($conf{$yr} && $conf{$yr} =~ /\[(.*):(.*)\]/) {
$mi = $1 if($1 ne "");
$ma = $2 if($2 ne "");
}
$hmax{$axis} = $ma;
$hmin{$axis} = $mi;
# Draw the horizontal values and grid
my $hmul = $h/($ma-$mi);
$off1 = ($axis eq "x1y1" ? $x-$th*0.3 : $x+$w+$th*0.3);
$off3 = ($axis eq "x1y1" ? $x : $x+$w-5);
$off4 = $off3+5;
$yr = ($axis eq "x1y1" ? "ytics" : "y2tics");
my $tic = $conf{$yr};
if($tic && $tic !~ m/mirror/) { # Tics specified in the configfile
$tic =~ s/^\((.*)\)$/$1/; # Strip ()
foreach my $onetic (split(",", $tic)) {
$onetic =~ s/^ *(.*) *$/$1/;
my ($tlabel, $tvalue) = split(" ", $onetic);
$tlabel =~ s/^"(.*)"$/$1/;
$off2 = int($y+($ma-$tvalue)*$hmul);
pO "<polyline points=\"$off3,$off2 $off4,$off2\"/>\n";
$off2 += $th/4;
my $align = ($axis eq "x1y1" ? " text-anchor=\"end\"" : "");
pO "<text x=\"$off1\" y=\"$off2\" class=\"ylabel\"$align>
$tlabel</text>";
}
} else { # Auto-tic
for(my $i = $mi; $i <= $ma; $i += $step) {
$off2 = int($y+($ma-$i)*$hmul);
pO "<polyline points=\"$off3,$off2 $off4,$off2\"/>\n";
if($axis eq "x1y2") {
my $o6 = $x+$w;
pO "<polyline points=\"$x,$off2 $o6,$off2\" class=\"vgrid\"/>\n";
}
$off2 += $th/4;
my $align = ($axis eq "x1y1" ? " text-anchor=\"end\"" : "");
pO "<text x=\"$off1\" y=\"$off2\" class=\"ylabel\"$align>$i</text>";
}
}
}
# Second loop over the data: draw the measured points
for my $idx (0..int(@hdx)-1) {
my $a = $axes[$idx];
$min = $hmin{$a};
$hmax{$a} += 1 if($min == $hmax{$a}); # Else division by 0 in the next line
my $hmul = $h/($hmax{$a}-$min);
my $ret = "";
my ($dxp, $dyp) = ($hdx[$idx], $hdy[$idx]);
if($type[$idx] eq "points" ) {
foreach my $i (0..int(@{$dxp})-1) {
my ($x1, $y1) = ($x+$dxp->[$i], $y+$h-($dyp->[$i]-$min)*$hmul);
$ret = sprintf(" %d,%d %d,%d %d,%d %d,%d %d,%d",
$x1-3,$y1, $x1,$y1-3, $x1+3,$y1, $x1,$y1+3, $x1-3,$y1);
pO "<polyline points=\"$ret\" class=\"l$idx\"/>\n";
}
} elsif($type[$idx] eq "steps" ) {
if(@{$dxp} == 1) {
my $y1 = $y+$h-($dyp->[0]-$min)*$hmul;
$ret .= sprintf(" %d,%d %d,%d %d,%d %d,%d",
$x,$y+$h, $x,$y1, $x+$w,$y1, $x+$w,$y+$h);
} else {
foreach my $i (1..int(@{$dxp})-1) {
my ($x1, $y1) = ($x+$dxp->[$i-1], $y+$h-($dyp->[$i-1]-$min)*$hmul);
my ($x2, $y2) = ($x+$dxp->[$i], $y+$h-($dyp->[$i] -$min)*$hmul);
$ret .= sprintf(" %d,%d %d,%d %d,%d", $x1,$y1, $x2,$y1, $x2,$y2);
}
}
pO "<polyline points=\"$ret\" class=\"l$idx\"/>\n";
} elsif($type[$idx] eq "histeps" ) {
if(@{$dxp} == 1) {
my $y1 = $y+$h-($dyp->[0]-$min)*$hmul;
$ret .= sprintf(" %d,%d %d,%d %d,%d %d,%d",
$x,$y+$h, $x,$y1, $x+$w,$y1, $x+$w,$y+$h);
} else {
foreach my $i (1..int(@{$dxp})-1) {
my ($x1, $y1) = ($x+$dxp->[$i-1], $y+$h-($dyp->[$i-1]-$min)*$hmul);
my ($x2, $y2) = ($x+$dxp->[$i], $y+$h-($dyp->[$i] -$min)*$hmul);
$ret .= sprintf(" %d,%d %d,%d %d,%d %d,%d",
$x1,$y1, ($x1+$x2)/2,$y1, ($x1+$x2)/2,$y2, $x2,$y2);
}
}
pO "<polyline points=\"$ret\" class=\"l$idx\"/>\n";
} else { # lines and everything else
foreach my $i (0..int(@{$dxp})-1) {
$ret .= sprintf(" %d,%d", $x + $dxp->[$i],
$y + $h-($dyp->[$i]-$min)*$hmul);
}
pO "<polyline points=\"$ret\" class=\"l$idx\"/>\n";
}
}
pO "</svg>\n";
}
sub
time_to_sec($)
{
my ($str) = @_;
my ($y,$m,$d,$h,$mi,$s) = split("[-_:]", $str);
$s = 0 if(!$s);
$mi= 0 if(!$mi);
$h = 0 if(!$h);
$d = 1 if(!$d);
$m = 1 if(!$m);
if(!$__ltstr || $__ltstr ne "$y-$m-$d") { # 2.5x faster
$__lt = mktime(0,0,0,$d,$m-1,$y-1900,0,0,-1);
$__ltstr = "$y-$m-$d";
}
return $s+$mi*60+$h*3600+$__lt;
}
sub
fmtTime($$)
{
my ($sepfmt, $sec) = @_;
my @tarr = split("[ :]+", localtime($sec));
my ($sep, $fmt) = split(" ", $sepfmt, 2);
my $ret = "";
for my $f (split(" ", $fmt)) {
$ret .= $sep if($ret);
$ret .= $tarr[$f];
}
return $ret;
}
sub
time_align($$)
{
my ($v,$align) = @_;
return $v if(!$align);
if($align == 1) { # Look for the beginning of the week
for(;;) {
my @a = localtime($v);
return $v if($a[6] == 0);
$v += 86400;
}
}
if($align == 2) { # Look for the beginning of the month
for(;;) {
my @a = localtime($v);
return $v if($a[3] == 1);
$v += 86400;
}
}
}
sub
doround($$$)
{
my ($v, $step, $isup) = @_;
if($v >= 0) {
return (int($v/$step))*$step+($isup ? $step : 0);
} else {
return (int($v/$step))*$step+($isup ? 0 : -$step);
}
}
1;

View File

@@ -0,0 +1,48 @@
<html>
<head>
<title>FHEMWEB Howto</title>
</head>
<body style="background-color: white;">
<h2>FHEMWEB Howto</h2>
<a href="#starting">Starting</a><br/>
<a name="starting"></a>
<h3>Starting</h3>
For a start create a minimal fhem config file fhem.cfg, like the
following:<br/>
<pre>
attr global logfile /tmp/fhem.log
attr global modpath /usr/local/lib
attr global port 7072 global
attr global statefile /tmp/fhem.state
define WEB FHEMWEB 8083 global</pre>
It is important that the modpath contains a directory called FHEM, where
all the necessary modules and other files are present, these modules should
<b>not</b> be found directly in modepath. Usually these are all the files
from the fhem package FHEM directory and webfrontend/pgm2 directory, and
some files from the contrib directory.<br/>
The logfile and the statefile should be changed from /tmp to a more stable directory, e.g. /var/log/FHEM or alike.<br/>
Now you can start fhem via:<pre>
perl fhem.pl fhem.cfg</pre>
The logfile should look like:
<pre>
2008.06.15 16:17:03 2: FHEMWEB port 8083 opened
2008.06.15 16:17:03 0: Server started (version ...)
</pre>
You can connect to the server either via telnet (type a return after you get
the connection):<br/><br/>
telnet localhost 7072<br/><br/>
or a little bit nicer via an internet browser:<br/><br/>
<a href="http://localhost:8083/fhem>http://localhost:8083/fhem</a><br/>
<body>
</html>

View File

@@ -4,7 +4,7 @@
#================= #=================
set terminal png transparent size 800,200 crop set terminal png transparent size <SIZE> crop
set output '<OUT>.png' set output '<OUT>.png'
set xdata time set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S" set timefmt "%Y-%m-%d_%H:%M:%S"
@@ -19,6 +19,6 @@ set y2tics
set format y "%.1f" set format y "%.1f"
set format y2 "%.1f" set format y2 "%.1f"
#FileLog 0 4:: #FileLog 4::0:
plot "<IN>" using 1:4 notitle with lines plot "<IN>" using 1:4 notitle with lines

View File

@@ -3,7 +3,7 @@
# FileLog definition: # FileLog definition:
# define FileLog fhtlog1 fht1:.*(temp|actuator).* /var/log/fht1-%Y-%U.log # define FileLog fhtlog1 fht1:.*(temp|actuator).* /var/log/fht1-%Y-%U.log
# #
set terminal png transparent size 800,200 crop set terminal png transparent size <SIZE> crop
set output '<OUT>.png' set output '<OUT>.png'
set xdata time set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S" set timefmt "%Y-%m-%d_%H:%M:%S"
@@ -17,9 +17,9 @@ set grid xtics y2tics
set y2label "temperature (Celsius)" set y2label "temperature (Celsius)"
set ylabel "Actuator (%)" set ylabel "Actuator (%)"
#FileLog 4:measured: #FileLog 4:measured:0:
##FileLog 4:desired: ##FileLog 4:desired:0:
#FileLog 4:actuator.*%:int #FileLog 4:actuator.*[0-9]+%:0:int
# "< awk '/desired/ {print $1, $4+0}' <IN>"\ # "< awk '/desired/ {print $1, $4+0}' <IN>"\
# using 1:2 axes x1y2 title 'Desired temperature' with steps,\ # using 1:2 axes x1y2 title 'Desired temperature' with steps,\

View File

@@ -3,7 +3,7 @@
# FileLog definition: # FileLog definition:
# define FileLog fs20log fs20dev /var/log/fs20dev-%Y-%U.log # define FileLog fs20log fs20dev /var/log/fs20dev-%Y-%U.log
# #
set terminal png transparent size 800,200 crop set terminal png transparent size <SIZE> crop
set output '<OUT>.png' set output '<OUT>.png'
set xdata time set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S" set timefmt "%Y-%m-%d_%H:%M:%S"
@@ -17,7 +17,7 @@ set y2range [-0.1:1.1]
set ylabel "Pumpe" set ylabel "Pumpe"
set y2label "Pumpe" set y2label "Pumpe"
#FileLog 3::$fld[2]eq"on"?1:0 #FileLog 3::0:$fld[2]eq"on"?1:0
plot "< awk '{print $1, $3==\"on\"? 1 : 0; }' <IN>"\ plot "< awk '{print $1, $3==\"on\"? 1 : 0; }' <IN>"\
using 1:2 notitle with steps using 1:2 notitle with steps

View File

@@ -3,7 +3,7 @@
# FileLog definition: # FileLog definition:
# define FileLog ks300log ks300:.*H:.* /var/log/ks300-%Y-%U.log # define FileLog ks300log ks300:.*H:.* /var/log/ks300-%Y-%U.log
# #
set terminal png transparent size 800,200 crop set terminal png transparent size <SIZE> crop
set output '<OUT>.png' set output '<OUT>.png'
set xdata time set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S" set timefmt "%Y-%m-%d_%H:%M:%S"
@@ -13,16 +13,16 @@ set y2tics
set title '<TL>' set title '<TL>'
set grid set grid
set y2label "Temperature (Celsius)" set y2label "Temperature in C<>"
set format y "%0.1f" set format y "%0.1f"
set ylabel "Rain (l/m2)" set ylabel "Rain (l/m2)"
set yrange [0:] set yrange [0:]
# Computing Rain/h and Rain/d values by accumulating the changes. # Computing Rain/h and Rain/d values by accumulating the changes.
#FileLog 4:IR: #FileLog 4:IR:0:
#FileLog 10:IR:delta-h #FileLog 10:IR:0:delta-h
#FileLog 10:IR:delta-d #FileLog 10:IR:0:delta-d
plot "<IN>" using 1:4 axes x1y2 title 'Temperature' with lines lw 2,\ plot "<IN>" using 1:4 axes x1y2 title 'Temperature' with lines lw 2,\
"<grep -v avg_ <IN> | perl -ane '\ "<grep -v avg_ <IN> | perl -ane '\

View File

@@ -3,7 +3,7 @@
# FileLog definition: # FileLog definition:
# define FileLog ks300log ks300:.*H:.* /var/log/ks300-%Y-%U.log # define FileLog ks300log ks300:.*H:.* /var/log/ks300-%Y-%U.log
# #
set terminal png transparent size 800,200 crop set terminal png transparent size <SIZE> crop
set output '<OUT>.png' set output '<OUT>.png'
set xdata time set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S" set timefmt "%Y-%m-%d_%H:%M:%S"
@@ -16,8 +16,8 @@ set grid
set ylabel "Wind (Km/h)" set ylabel "Wind (Km/h)"
set y2label "Humidity (%)" set y2label "Humidity (%)"
#FileLog 8:IR: #FileLog 8:IR:0:
#FileLog 6:IR: #FileLog 6:IR:0:
plot "<IN>" using 1:8 axes x1y1 title 'Wind' with lines,\ plot "<IN>" using 1:8 axes x1y1 title 'Wind' with lines,\
"<IN>" using 1:6 axes x1y2 title 'Rel. Humidity (%)' with lines "<IN>" using 1:6 axes x1y2 title 'Rel. Humidity (%)' with lines

View File

@@ -3,7 +3,7 @@
# FileLog definition: # FileLog definition:
# define FileLog ks300log ks300:.*H:.* /var/log/ks300-%Y-%U.log # define FileLog ks300log ks300:.*H:.* /var/log/ks300-%Y-%U.log
# #
set terminal png transparent size 800,200 crop set terminal png transparent size <SIZE> crop
set output '<OUT>.png' set output '<OUT>.png'
set xdata time set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S" set timefmt "%Y-%m-%d_%H:%M:%S"
@@ -13,11 +13,11 @@ set y2tics
set title '<TL>' set title '<TL>'
set grid set grid
set ylabel "Temperature (Celsius)" set ylabel "Temperature in C<>"
set y2label "Rain (l/m2)" set y2label "Rain (l/m2)"
#FileLog 5:avg_day: #FileLog 5:avg_day:0:
#FileLog 11:avg_day: #FileLog 11:avg_day:0:
plot "<grep avg_day <IN>" using 1:5 axes x1y1 title 'Temperature' with lines,\ plot "<grep avg_day <IN>" using 1:5 axes x1y1 title 'Temperature' with lines,\
"<grep avg_day <IN>" using 1:11 axes x1y2 title 'Rain' with histeps "<grep avg_day <IN>" using 1:11 axes x1y2 title 'Rain' with histeps

View File

@@ -3,7 +3,7 @@
# define pirilog FileLog /var/log/piri-%Y-%m-%d.log piri.* # define pirilog FileLog /var/log/piri-%Y-%m-%d.log piri.*
# The devices are called piri.sz, piri.flo, piri.flu, prir.wz1 and piri.wz2 # The devices are called piri.sz, piri.flo, piri.flu, prir.wz1 and piri.wz2
# #
set terminal png transparent size 800,200 crop set terminal png transparent size <SIZE> crop
set output '<OUT>.png' set output '<OUT>.png'
set xdata time set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S" set timefmt "%Y-%m-%d_%H:%M:%S"
@@ -14,12 +14,13 @@ set title '<TL>'
set ytics ("Sz" 0.8, "FlO" 0.6, "FlU" 0.4, "Wz1" 0.2, "Wz2" 0.0) set ytics ("Sz" 0.8, "FlO" 0.6, "FlU" 0.4, "Wz1" 0.2, "Wz2" 0.0)
set y2tics ("Sz" 0.8, "FlO" 0.6, "FlU" 0.4, "Wz1" 0.2, "Wz2" 0.0) set y2tics ("Sz" 0.8, "FlO" 0.6, "FlU" 0.4, "Wz1" 0.2, "Wz2" 0.0)
set yrange [-0.1:0.9] set yrange [-0.1:0.9]
set y2range [-0.1:0.9]
#FileLog "0.8":sz: #FileLog "0.8":sz:0.8:
#FileLog "0.6":flo: #FileLog "0.6":flo:0.6:
#FileLog "0.4":flu: #FileLog "0.4":flu:0.4:
#FileLog "0.2":wz1: #FileLog "0.2":wz1:0.2:
#FileLog "0.0":wz2: #FileLog "0.0":wz2:0.0:
plot\ plot\
"< awk '/sz/ {print $1, 0.8; }' <IN>" using 1:2 notitle with points,\ "< awk '/sz/ {print $1, 0.8; }' <IN>" using 1:2 notitle with points,\

View File

@@ -3,7 +3,7 @@
# FileLog definition: # FileLog definition:
# define FileLog fs20log fs20dev /var/log/fs20dev-%Y-%U.log # define FileLog fs20log fs20dev /var/log/fs20dev-%Y-%U.log
# #
set terminal png transparent size 800,200 crop set terminal png transparent size <SIZE> crop
set output '<OUT>.png' set output '<OUT>.png'
set xdata time set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S" set timefmt "%Y-%m-%d_%H:%M:%S"
@@ -13,7 +13,7 @@ set title '<TL>'
set grid set grid
set yrange [-0.2:1.2] set yrange [-0.2:1.2]
#FileLog "1"::: #FileLog "1"::0:
plot "< awk '{print $1, $3==\"on\"? 1 : 0; }' <IN>"\ plot "< awk '{print $1, $3==\"on\"? 1 : 0; }' <IN>"\
using 1:2 title 'On/Off' with impulses using 1:2 title 'On/Off' with impulses

View File

@@ -1,145 +1,146 @@
body { body {
color: black; color: black;
background: #FFFFD7; background: #FFFFD7;
} }
table.room { table.room {
border: solid; border: solid;
border-width: thin; border-width: thin;
width: 100%; width: 100%;
-moz-border-radius:8px; -moz-border-radius:8px;
background: #D7FFFF; background: #D7FFFF;
} }
table.room tr.sel { table.room tr.sel {
background: #A0FFFF; background: #A0FFFF;
} }
table.FS20 { table.FS20 {
border: solid; border: solid;
border-width: thin; border-width: thin;
width: 100%; width: 100%;
-moz-border-radius:8px; -moz-border-radius:8px;
background: #C0FFFF; background: #C0FFFF;
} }
table.FS20 tr.odd { table.FS20 tr.odd {
background: #D7FFFF; background: #D7FFFF;
} }
table.FHT { table.FHT {
border: solid; border: solid;
border-width: thin; border-width: thin;
width: 100%; width: 100%;
-moz-border-radius:8px; -moz-border-radius:8px;
background: #FFC0C0; background: #FFC0C0;
} }
table.FHT tr.odd { table.FHT tr.odd {
background: #FFD7D7; background: #FFD7D7;
} }
table.KS300 { table.KS300 {
border: solid; border: solid;
border-width: thin; border-width: thin;
width: 100%; width: 100%;
-moz-border-radius:8px; -moz-border-radius:8px;
background: #C0FFC0; background: #C0FFC0;
} }
table.KS300 tr.odd { table.KS300 tr.odd {
background: #A7FFA7; background: #A7FFA7;
} }
table.FileLog { table.FileLog {
border: solid; border: solid;
border-width: thin; border-width: thin;
width: 100%; width: 100%;
-moz-border-radius:8px; -moz-border-radius:8px;
background: #FFC0C0; background: #FFC0C0;
} }
table.FileLog tr.odd { table.FileLog tr.odd {
background: #FFD7D7; background: #FFD7D7;
} }
table.at { table.at {
border: solid; border: solid;
border-width: thin; border-width: thin;
width: 100%; width: 100%;
-moz-border-radius:8px; -moz-border-radius:8px;
background: #FFFFC0; background: #FFFFC0;
} }
table.at tr.odd { table.at tr.odd {
background: #FFFFD7; background: #FFFFD7;
} }
table.notify { table.notify {
border: solid; border: solid;
border-width: thin; border-width: thin;
width: 100%; width: 100%;
-moz-border-radius:8px; -moz-border-radius:8px;
background: #D7D7A0; background: #D7D7A0;
} }
table.notify tr.odd { table.notify tr.odd {
background: #FFFFC0; background: #FFFFC0;
} }
table.FHZ { table.FHZ {
border: solid; border: solid;
border-width: thin; border-width: thin;
width: 100%; width: 100%;
-moz-border-radius:8px; -moz-border-radius:8px;
background: #C0C0C0; background: #C0C0C0;
} }
table.FHZ tr.odd { table.FHZ tr.odd {
background: #D7D7D7; background: #D7D7D7;
} }
table.EM { table.EM {
border: solid; border: solid;
border-width: thin; border-width: thin;
width: 100%; width: 100%;
-moz-border-radius:8px; -moz-border-radius:8px;
background: #E0E0E0; background: #E0E0E0;
} }
table.EM tr.odd { table.EM tr.odd {
background: #F0F0F0; background: #F0F0F0;
} }
table._internal_ { table._internal_ {
border: solid; border: solid;
border-width: thin; border-width: thin;
width: 100%; width: 100%;
-moz-border-radius:8px; -moz-border-radius:8px;
background: #C0C0C0; background: #C0C0C0;
} }
table._internal_ tr.odd { table._internal_ tr.odd {
background: #D7D7D7; background: #D7D7D7;
} }
#hdr { #hdr {
position:absolute; position:absolute;
top:10px; top:10px;
left:10px; left:10px;
} }
#left { #left {
position:absolute; position:absolute;
top:50px; top:50px;
left:10px; left:10px;
width:130px; width:130px;
} }
#right { #right {
position:absolute; position:absolute;
top:50px; top:50px;
left:160px; left:160px;
bottom:10px; right:10px;
overflow:auto; bottom:10px;
} overflow:auto;
}

View File

@@ -0,0 +1,15 @@
text { font-family:Times; font-size:12px; }
text.title { font-size:16px; }
rect.border { stroke:black; stroke-width:1px; fill:none; }
polyline { stroke:black; stroke-width:1px; fill:none; }
.vgrid { stroke-dasharray:2,6; stroke:gray; }
.hgrid { stroke-dasharray:2,6; stroke:gray; }
.l0 { stroke:red; } text.l0 { stroke:none; fill:red; }
.l1 { stroke:green; } text.l1 { stroke:none; fill:green; }
.l2 { stroke:blue; } text.l2 { stroke:none; fill:blue; }
.l3 { stroke:magenta; } text.l3 { stroke:none; fill:magenta; }
.l4 { stroke:cyan; } text.l4 { stroke:none; fill:cyan; }