From 0ba5110cce48a99cf8327d63a76fed8d0cd5af45 Mon Sep 17 00:00:00 2001 From: rudolfkoenig Date: Sun, 2 Oct 2011 12:26:43 +0000 Subject: [PATCH] longpoll git-svn-id: svn://svn.code.sf.net/p/fhem/code/trunk@1039 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/docs/commandref.html | 7 +- fhem/docs/fhem.html | 2 - fhem/webfrontend/pgm2/01_FHEMWEB.pm | 239 ++++++++++++++++------- fhem/webfrontend/pgm2/longpoll.js | 39 ++++ fhem/webfrontend/pgm2/style_touchpad.css | 2 +- 5 files changed, 210 insertions(+), 79 deletions(-) create mode 100644 fhem/webfrontend/pgm2/longpoll.js diff --git a/fhem/docs/commandref.html b/fhem/docs/commandref.html index e039bf531..6364b5602 100644 --- a/fhem/docs/commandref.html +++ b/fhem/docs/commandref.html @@ -6375,7 +6375,12 @@ href="http://www.elv.de/output/controller.aspx?cid=74&detail=10&detail2=29870">U

- + +
  • longpoll
    + Affects devices states in the room overview only.
    + In this mode status update is refreshed more or less instantaneously, + and state change (on/off only) is done without requesting a complete + refresh from the server. diff --git a/fhem/docs/fhem.html b/fhem/docs/fhem.html index ed5bc2706..4b9d27243 100644 --- a/fhem/docs/fhem.html +++ b/fhem/docs/fhem.html @@ -120,8 +120,6 @@ iPhone frontends: Fhemobile (native app), - - fhemgw, dhs-computertechnik or phyfhem diff --git a/fhem/webfrontend/pgm2/01_FHEMWEB.pm b/fhem/webfrontend/pgm2/01_FHEMWEB.pm index fe185e4d7..b4ecaecf7 100755 --- a/fhem/webfrontend/pgm2/01_FHEMWEB.pm +++ b/fhem/webfrontend/pgm2/01_FHEMWEB.pm @@ -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 "" 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 ""; pO "" if($FW_plotmode eq "SVG"); + pO "" + if($FW_longpoll); pO "\n"; 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})); @@ -537,13 +568,13 @@ FW_makeTable($$@) my($name, $hash, $cmd) = (@_); return if(!$hash || !int(keys %{$hash})); - pO " "; + pO "
    "; my $row = 1; foreach my $n (sort keys %{$hash}) { my $r = ref($hash->{$n}); next if($r && ($r ne "HASH" || !defined($hash->{$n}{VAL}))); - pF " ", ($row&1)?"odd":"even"; + pF "", ($row&1)?"odd":"even"; $row++; my $val = $hash->{$n}; @@ -578,7 +609,7 @@ FW_makeTable($$@) pO ""; } - pO "
    "; + pO ""; pO "
    "; } @@ -622,9 +653,9 @@ FW_doDetail($) FW_makeTable($d, $attr{$d}, "deleteattr"); if($t eq "FileLog" ) { - pO " "; + pO "
    "; FW_dumpFileLog($d, 0, 1); - pO "
    "; + pO ""; } pO ""; @@ -728,25 +759,25 @@ FW_roomOverview($) pO "
    "; pO ""; if($FW_ss) { # Make a selection sensitive dropdown list - pO " "; + pO ""; } else { foreach(my $idx = 0; $idx < @list1; $idx++) { my ($l1, $l2) = ($list1[$idx], $list2[$idx]); if(!$l1) { - pO "
    " if($idx); - pO " " + pO "
    " if($idx); + pO "" if($idx", $l1 eq $FW_room ? " class=\"sel\"" : ""; + pF "", $l1 eq $FW_room ? " class=\"sel\"" : ""; if($l2 =~ m/.html$/ || $l2 =~ m/^http/) { pO ""; } else { @@ -774,7 +805,7 @@ FW_showRoom() pO ""; pO "
    "; - pO "
    $l1
    "; # Need for equal width of subtables + pO "
    "; # Need for equal width of subtables my $rf = ($FW_room ? "&room=$FW_room" : ""); # stay in the room @@ -790,22 +821,14 @@ FW_showRoom() !IsIgnored($_) } keys %{$FW_types{$type}}; next if(!@devs); - pO " "; - pO " "; } - pO "
    $type
    "; - pO " "; + pO "\n"; + pO ""; + pO "
    $type
    "; + pO ""; 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 " ", ($row&1)?"odd":"even"; - + pF "\n", ($row&1)?"odd":"even"; if($FW_hiddenroom{detail}) { pO ""; @@ -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 = "\"$txt\"/" if($icon); - - } - - pO ""; 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", ""); @@ -872,10 +870,10 @@ FW_showRoom() } pO ""; } - pO "
    $d
    "; - # align needed for FireFox - $txt = "
    $txt
    " 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 "
    $txt"; if(!$FW_ss) { pO "
    "; - pO "
    "; + pO "

    "; + pO "
    "; # Now the weblinks my $buttons = 1; @@ -1384,9 +1382,9 @@ FW_style($$) push(@fl, FW_fileList("$FW_dir/.*html")); pO "
    "; - pO "
    "; - pO " $msg

    " if($msg); - pO " "; + pO "
    "; + pO "$msg

    " if($msg); + pO ""; my $row = 0; foreach my $file (@fl) { pO ""; @@ -1398,17 +1396,17 @@ FW_style($$) pO ""; $row = ($row+1)%2; } - pO "
    "; - pO "
    "; + pO "
    "; + pO ""; pO "
    "; } elsif($a[1] eq "examples") { my @fl = FW_fileList("$FW_dir/example.*"); pO "
    "; - pO "
    "; - pO " $msg

    " if($msg); - pO " "; + pO "
    "; + pO "$msg

    " if($msg); + pO ""; my $row = 0; foreach my $file (@fl) { pO ""; @@ -1416,8 +1414,8 @@ FW_style($$) pO ""; $row = ($row+1)%2; } - pO "
    "; - pO "
    "; + pO "
    "; + pO ""; pO "
    "; } elsif($a[1] eq "edit") { @@ -1434,7 +1432,7 @@ FW_style($$) my $ncols = $FW_ss ? 40 : 80; pO "
    "; - pO " "; + pO ""; $f =~ s,^.*/,,; pO FW_submit("save", "Save $f") . "

    "; pO FW_hidden("cmd", "style save $a[2]"); @@ -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 = "\"$txt\"/" + if($icon); + } + + $txt = "
    $txt
    "; + if($hasOnOff) { + my $link = "cmd.$d=set $d ".($state eq "on" ? "off":"on"); + if($FW_longpoll) { + $txt = "$txt"; + + } elsif($FW_ss || $FW_tp) { + $txt = "$txt"; + + } else { + $txt = "$txt"; + + } + } + return ($allSets, $hasOnOff, $txt); +} + 1; diff --git a/fhem/webfrontend/pgm2/longpoll.js b/fhem/webfrontend/pgm2/longpoll.js new file mode 100644 index 000000000..1f5294444 --- /dev/null +++ b/fhem/webfrontend/pgm2/longpoll.js @@ -0,0 +1,39 @@ +var pollConn; + +function +cmd(arg) +{ + var req = new XMLHttpRequest(); + req.open("GET", arg, true); + req.send(null); +} + +function +doUpdate() +{ + if(pollConn.readyState != 4) + return; + var lines = pollConn.responseText.split("\n"); + for(var i=0; i < lines.length; i++) { + var d = lines[i].split(";", 3); // Complete arg + if(d.length != 3) + continue; + var el = document.getElementById(d[0]); + if(el) + el.innerHTML=d[2]; + } + pollConn.abort(); + longpoll(); +} + +function +longpoll() +{ + pollConn = new XMLHttpRequest(); + pollConn.open("GET", document.location.pathname+document.location.search+ + "&XHR=1&inform=1", true); + pollConn.onreadystatechange = doUpdate; + pollConn.send(null); +} + +window.onload = longpoll; diff --git a/fhem/webfrontend/pgm2/style_touchpad.css b/fhem/webfrontend/pgm2/style_touchpad.css index ca506ec29..0cada02e4 100644 --- a/fhem/webfrontend/pgm2/style_touchpad.css +++ b/fhem/webfrontend/pgm2/style_touchpad.css @@ -17,7 +17,7 @@ table.block tr.odd { background: #F0F0D8; } table.block tr.sel { background: #F0F0D8; } table { -moz-border-radius:8px; border-radius:8px; } -.col1, .col2 { padding: 8px; } +.col1, .col2, .col3 { padding: 8px; } .dname, .dval { padding: 0px; font-size: 14px; } table#room { border:1px solid gray; width: 100%; background: #D7FFFF; }