|
|
|
|
@@ -62,10 +62,14 @@ 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_wname; # Web instance name
|
|
|
|
|
my $FW_wname; # Web instance
|
|
|
|
|
my $FW_cname; # Current connection
|
|
|
|
|
my @FW_zoom; # "qday", "day","week","month","year"
|
|
|
|
|
my %FW_zoom; # the same as @FW_zoom
|
|
|
|
|
my %FW_hiddenroom; # hash of hidden rooms
|
|
|
|
|
my $FW_longpoll;
|
|
|
|
|
my $FW_inform;
|
|
|
|
|
my $FW_XHR;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#####################################
|
|
|
|
|
@@ -78,10 +82,11 @@ FHEMWEB_Initialize($)
|
|
|
|
|
$hash->{AttrFn} = "FW_Attr";
|
|
|
|
|
$hash->{DefFn} = "FW_Define";
|
|
|
|
|
$hash->{UndefFn} = "FW_Undef";
|
|
|
|
|
$hash->{NotifyFn}= "FW_Notify";
|
|
|
|
|
$hash->{AttrList}= "loglevel:0,1,2,3,4,5,6 webname fwmodpath fwcompress " .
|
|
|
|
|
"plotmode:gnuplot,gnuplot-scroll,SVG plotsize refresh " .
|
|
|
|
|
"touchpad smallscreen plotfork basicAuth basicAuthMsg ".
|
|
|
|
|
"stylesheet hiddenroom HTTPS";
|
|
|
|
|
"stylesheet hiddenroom HTTPS longpoll";
|
|
|
|
|
|
|
|
|
|
###############
|
|
|
|
|
# Initialize internal structures
|
|
|
|
|
@@ -207,9 +212,9 @@ FW_Read($)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$FW_wname = $hash->{SNAME};
|
|
|
|
|
$FW_cname = $name;
|
|
|
|
|
my $ll = GetLogLevel($FW_wname,4);
|
|
|
|
|
my $c = $hash->{CD};
|
|
|
|
|
|
|
|
|
|
if(!$zlib_loaded && $try_zlib && AttrVal($FW_wname, "fwcompress", 1)) {
|
|
|
|
|
$zlib_loaded = 1;
|
|
|
|
|
eval { require Compress::Zlib; };
|
|
|
|
|
@@ -229,10 +234,10 @@ FW_Read($)
|
|
|
|
|
|
|
|
|
|
# Data from HTTP Client
|
|
|
|
|
my $buf;
|
|
|
|
|
my $ret = sysread($hash->{CD}, $buf, 1024);
|
|
|
|
|
my $ret = sysread($c, $buf, 1024);
|
|
|
|
|
|
|
|
|
|
if(!defined($ret) || $ret <= 0) {
|
|
|
|
|
my $r = CommandDelete(undef, $name);
|
|
|
|
|
CommandDelete(undef, $name);
|
|
|
|
|
Log($ll, "Connection closed for $name");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@@ -271,8 +276,9 @@ FW_Read($)
|
|
|
|
|
|
|
|
|
|
$hash->{INUSE} = 1;
|
|
|
|
|
my $cacheable = FW_AnswerCall($arg);
|
|
|
|
|
|
|
|
|
|
delete($hash->{INUSE});
|
|
|
|
|
return if($cacheable == -1); # Longpoll / inform request;
|
|
|
|
|
|
|
|
|
|
if(!$selectlist{$name}) { # removed by rereadcfg, reinsert
|
|
|
|
|
$selectlist{$name} = $hash;
|
|
|
|
|
$defs{$name} = $hash;
|
|
|
|
|
@@ -390,12 +396,31 @@ FW_AnswerCall($)
|
|
|
|
|
$FW_reldoc = "$FW_ME/commandref.html";
|
|
|
|
|
|
|
|
|
|
$FW_cmdret = $docmd ? fC($cmd) : "";
|
|
|
|
|
|
|
|
|
|
if($FW_inform) { # Longpoll header
|
|
|
|
|
$defs{$FW_cname}{inform} = $FW_room;
|
|
|
|
|
my $c = $defs{$FW_cname}{CD};
|
|
|
|
|
print $c "HTTP/1.1 200 OK\r\n",
|
|
|
|
|
"Content-Type: text/plain; charset=ISO-8859-1\r\n\r\n";
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($FW_XHR) {
|
|
|
|
|
$FW_RETTYPE = "text/plain; charset=ISO-8859-1";
|
|
|
|
|
pO $FW_cmdret;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FW_updateHashes();
|
|
|
|
|
if($cmd =~ m/^showlog /) {
|
|
|
|
|
FW_showLog($cmd);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$FW_longpoll = (AttrVal($FW_wname, "longpoll", undef) &&
|
|
|
|
|
$FW_room &&
|
|
|
|
|
!$FW_detail);
|
|
|
|
|
|
|
|
|
|
if($cmd =~ m/^toweblink (.*)$/) {
|
|
|
|
|
my @aa = split(":", $1);
|
|
|
|
|
my $max = 0;
|
|
|
|
|
@@ -433,10 +458,12 @@ FW_AnswerCall($)
|
|
|
|
|
pO "<meta http-equiv=\"refresh\" content=\"$rf\">" if($rf);
|
|
|
|
|
my $stylecss = ($FW_ss ? "style_smallscreen.css" :
|
|
|
|
|
$FW_tp ? "style_touchpad.css" : "style.css");
|
|
|
|
|
$stylecss = AttrVal($FW_wname, "stylecss", $stylecss);
|
|
|
|
|
$stylecss = AttrVal($FW_wname, "stylesheet", $stylecss);
|
|
|
|
|
pO "<link href=\"$FW_ME/$stylecss\" rel=\"stylesheet\"/>";
|
|
|
|
|
pO "<script type=\"text/javascript\" src=\"$FW_ME/svg.js\"></script>"
|
|
|
|
|
if($FW_plotmode eq "SVG");
|
|
|
|
|
pO "<script type=\"text/javascript\" src=\"$FW_ME/longpoll.js\"></script>"
|
|
|
|
|
if($FW_longpoll);
|
|
|
|
|
pO "</head>\n<body name=\"$t\">";
|
|
|
|
|
|
|
|
|
|
if($FW_cmdret) {
|
|
|
|
|
@@ -473,9 +500,11 @@ FW_digestCgi($)
|
|
|
|
|
my (%arg, %val, %dev);
|
|
|
|
|
my ($cmd, $c) = ("","","");
|
|
|
|
|
|
|
|
|
|
$FW_detail = "";
|
|
|
|
|
%FW_pos = ();
|
|
|
|
|
$FW_room = "";
|
|
|
|
|
$FW_detail = "";
|
|
|
|
|
$FW_XHR = undef;
|
|
|
|
|
$FW_inform = undef;
|
|
|
|
|
|
|
|
|
|
$arg =~ s,^[?/],,;
|
|
|
|
|
foreach my $pv (split("&", $arg)) {
|
|
|
|
|
@@ -495,6 +524,8 @@ FW_digestCgi($)
|
|
|
|
|
if($p =~ m/^cmd\.(.*)$/) { $cmd = $v; $c= $1; }
|
|
|
|
|
if($p eq "pos") { %FW_pos = split(/[=;]/, $v); }
|
|
|
|
|
if($p eq "data") { $FW_data = $v; }
|
|
|
|
|
if($p eq "XHR") { $FW_XHR = 1; }
|
|
|
|
|
if($p eq "inform") { $FW_inform = $v; }
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
$cmd.=" $dev{$c}" if(defined($dev{$c}));
|
|
|
|
|
@@ -790,22 +821,14 @@ FW_showRoom()
|
|
|
|
|
!IsIgnored($_) } keys %{$FW_types{$type}};
|
|
|
|
|
next if(!@devs);
|
|
|
|
|
|
|
|
|
|
pO " <tr><td><div class=\"devType\">$type</div></td></tr>";
|
|
|
|
|
pO "\n<tr><td><div class=\"devType\">$type</div></td></tr>";
|
|
|
|
|
pO "<tr><td>";
|
|
|
|
|
pO "<table class=\"block wide\" id=\"$type\">";
|
|
|
|
|
|
|
|
|
|
foreach my $d (sort @devs) {
|
|
|
|
|
my $type = $defs{$d}{TYPE};
|
|
|
|
|
|
|
|
|
|
my $allSets = " " . getAllSets($d) . " ";
|
|
|
|
|
my $hasOnOff = ($allSets =~ m/ on / && $allSets =~ m/ off /);
|
|
|
|
|
if(!$hasOnOff) { # Check the eventMap
|
|
|
|
|
my $em = AttrVal($d, "eventMap", "") . " ";
|
|
|
|
|
$hasOnOff = ($em =~ m/:on / && $em =~ m/:off /);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pF " <tr class=\"%s\">", ($row&1)?"odd":"even";
|
|
|
|
|
|
|
|
|
|
pF "\n<tr class=\"%s\">", ($row&1)?"odd":"even";
|
|
|
|
|
if($FW_hiddenroom{detail}) {
|
|
|
|
|
pO "<td><div class=\"col1\">$d</div></td>";
|
|
|
|
|
|
|
|
|
|
@@ -816,39 +839,14 @@ FW_showRoom()
|
|
|
|
|
|
|
|
|
|
$row++;
|
|
|
|
|
|
|
|
|
|
my $state = $defs{$d}{STATE};
|
|
|
|
|
$state = "" if(!defined($state));
|
|
|
|
|
my $txt = $state;
|
|
|
|
|
|
|
|
|
|
if(defined(AttrVal($d, "showtime", undef))) {
|
|
|
|
|
$txt = $defs{$d}{READINGS}{state}{TIME};
|
|
|
|
|
|
|
|
|
|
} elsif($allSets =~ m/ desired-temp /) {
|
|
|
|
|
$txt = ReadingsVal($d, "measured-temp", "");
|
|
|
|
|
$txt =~ s/ .*//;
|
|
|
|
|
$txt .= "°"
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
my $icon;
|
|
|
|
|
$icon = FW_dev2image($d);
|
|
|
|
|
$txt = "<img src=\"$FW_ME/icons/$icon\" alt=\"$txt\"/>" if($icon);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pO "<td>";
|
|
|
|
|
# align needed for FireFox
|
|
|
|
|
$txt = "<div align=\"center\" class=\"col2\">$txt</div>" if($FW_ss);
|
|
|
|
|
if($hasOnOff) {
|
|
|
|
|
pHPlain "cmd.$d=set $d ".($state eq "on" ? "off":"on").$rf, $txt, 0;
|
|
|
|
|
} else {
|
|
|
|
|
pO $txt;
|
|
|
|
|
}
|
|
|
|
|
my ($allSets, $hasOnOff, $txt) = FW_devState($d, $rf);
|
|
|
|
|
pO "<td id=\"$d\">$txt";
|
|
|
|
|
|
|
|
|
|
if(!$FW_ss) {
|
|
|
|
|
pO "</td>";
|
|
|
|
|
if($hasOnOff) {
|
|
|
|
|
pH "cmd.$d=set $d on$rf", ReplaceEventMap($d, "on", 1), 1, "col2";
|
|
|
|
|
pH "cmd.$d=set $d off$rf", ReplaceEventMap($d, "off", 1), 1, "col2";
|
|
|
|
|
pH "cmd.$d=set $d on$rf", ReplaceEventMap($d, "on", 1), 1, "col3";
|
|
|
|
|
pH "cmd.$d=set $d off$rf", ReplaceEventMap($d, "off", 1), 1, "col3";
|
|
|
|
|
|
|
|
|
|
} elsif($allSets =~ m/ desired-temp /) {
|
|
|
|
|
$txt = ReadingsVal($d, "measured-temp", "");
|
|
|
|
|
@@ -1510,6 +1508,7 @@ pHPlain(@)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
##################
|
|
|
|
|
# print formatted
|
|
|
|
|
sub
|
|
|
|
|
@@ -1722,4 +1721,94 @@ FW_dumpFileLog($$$)
|
|
|
|
|
}
|
|
|
|
|
return $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub
|
|
|
|
|
FW_Notify($$)
|
|
|
|
|
{
|
|
|
|
|
my ($ntfy, $dev) = @_;
|
|
|
|
|
|
|
|
|
|
my $filter = $ntfy->{inform};
|
|
|
|
|
return undef if(!$filter);
|
|
|
|
|
|
|
|
|
|
my $ln = $ntfy->{NAME};
|
|
|
|
|
|
|
|
|
|
my $dn = $dev->{NAME};
|
|
|
|
|
return undef if(AttrVal($dn, "room", "") ne $filter);
|
|
|
|
|
|
|
|
|
|
FW_ReadIcons();
|
|
|
|
|
$FW_wname = $ntfy->{SNAME};
|
|
|
|
|
$FW_ME = "/" . AttrVal($FW_wname, "webname", "fhem");
|
|
|
|
|
$FW_longpoll = 1;
|
|
|
|
|
$FW_ss = AttrVal($FW_wname, "smallscreen", 0);
|
|
|
|
|
$FW_tp = AttrVal($FW_wname, "touchpad", $FW_ss);
|
|
|
|
|
|
|
|
|
|
my ($allSet, $hasOnOff, $txt) = FW_devState($dn, "");
|
|
|
|
|
|
|
|
|
|
$ntfy->{INFORMBUF} = "" if(!defined($ntfy->{INFORMBUF}));
|
|
|
|
|
$ntfy->{INFORMBUF} .= "$dn;$dev->{STATE};$txt\n";
|
|
|
|
|
Log 0, "Sending $txt";
|
|
|
|
|
RemoveInternalTimer($ln);
|
|
|
|
|
InternalTimer(gettimeofday()+0.1, "FW_FlushInform", $ln, 0);
|
|
|
|
|
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub
|
|
|
|
|
FW_FlushInform($)
|
|
|
|
|
{
|
|
|
|
|
my ($name) = @_;
|
|
|
|
|
my $hash = $defs{$name};
|
|
|
|
|
my $c = $hash->{CD};
|
|
|
|
|
print $c $hash->{INFORMBUF};
|
|
|
|
|
|
|
|
|
|
CommandDelete(undef, $name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub
|
|
|
|
|
FW_devState($$)
|
|
|
|
|
{
|
|
|
|
|
my ($d, $rf) = @_;
|
|
|
|
|
my $allSets = " " . getAllSets($d) . " ";
|
|
|
|
|
my $hasOnOff = ($allSets =~ m/ on / && $allSets =~ m/ off /);
|
|
|
|
|
if(!$hasOnOff) { # Check the eventMap
|
|
|
|
|
my $em = AttrVal($d, "eventMap", "") . " ";
|
|
|
|
|
$hasOnOff = ($em =~ m/:on / && $em =~ m/:off /);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $state = $defs{$d}{STATE};
|
|
|
|
|
$state = "" if(!defined($state));
|
|
|
|
|
my $txt = $state;
|
|
|
|
|
|
|
|
|
|
if(defined(AttrVal($d, "showtime", undef))) {
|
|
|
|
|
$txt = $defs{$d}{READINGS}{state}{TIME};
|
|
|
|
|
|
|
|
|
|
} elsif($allSets =~ m/ desired-temp /) {
|
|
|
|
|
$txt = ReadingsVal($d, "measured-temp", "");
|
|
|
|
|
$txt =~ s/ .*//;
|
|
|
|
|
$txt .= "°"
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
my $icon;
|
|
|
|
|
$icon = FW_dev2image($d);
|
|
|
|
|
$txt = "<img src=\"$FW_ME/icons/$icon\" alt=\"$txt\"/>"
|
|
|
|
|
if($icon);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$txt = "<div id=\"$d\" align=\"center\" class=\"col2\">$txt</div>";
|
|
|
|
|
if($hasOnOff) {
|
|
|
|
|
my $link = "cmd.$d=set $d ".($state eq "on" ? "off":"on");
|
|
|
|
|
if($FW_longpoll) {
|
|
|
|
|
$txt = "<a onClick=\"cmd('$FW_ME?XHR=1&$link')\">$txt</a>";
|
|
|
|
|
|
|
|
|
|
} elsif($FW_ss || $FW_tp) {
|
|
|
|
|
$txt = "<a onClick=\"location.href='$FW_ME?$link$rf'\">$txt</a>";
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
$txt = "<a href=\"$FW_ME?$link\">$txt</a>";
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ($allSets, $hasOnOff, $txt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
1;
|
|
|
|
|
|