2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 12:49:34 +00:00

fhem.pl: async get capability, if supported by the module (Forum #43771)

git-svn-id: https://svn.fhem.de/fhem/trunk@9927 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2015-11-18 18:53:43 +00:00
parent 67755a6f5b
commit 9d7c346d13
4 changed files with 134 additions and 21 deletions

View File

@ -106,6 +106,7 @@ my %FW_types; # device types, for sorting
my %FW_hiddengroup;# hash of hidden groups
my $FW_inform;
my $FW_XHR; # Data only answer, no HTML
my $FW_id; # id of current page
my $FW_jsonp; # jasonp answer (sending function calls to the client)
my $FW_headercors; #
my $FW_chash; # client fhem hash
@ -125,6 +126,7 @@ FHEMWEB_Initialize($)
$hash->{DefFn} = "FW_Define";
$hash->{UndefFn} = "FW_Undef";
$hash->{NotifyFn}= ($init_done ? "FW_Notify" : "FW_SecurityCheck");
$hash->{AsyncOutputFn} = "FW_AsyncOutput";
$hash->{ActivateInformFn} = "FW_ActivateInform";
no warnings 'qw';
my @attrList = qw(
@ -475,6 +477,43 @@ FW_Read($$)
}
}
sub
FW_AsyncOutput($$)
{
my ($hash, $ret) = @_;
if( $ret =~ m/^<html>(.*)<\/html>$/s ) {
$ret = $1;
} else {
$ret =~ s/&/&amp;/g;
$ret =~ s/'/&apos;/g;
$ret =~ s/</&lt;/g;
$ret =~ s/>/&gt;/g;
$ret = "<pre>$ret</pre>" if($ret =~ m/\n/ );
$ret =~ s/\n/<br>/g;
}
# find the longpoll connection with the same fw_id as the page that was the
# origin of the get command
my $found = 0;
my $data = FW_longpollInfo('JSON',
"#FHEMWEB:$FW_wname","FW_okDialog('$ret')","");
foreach my $d (keys %defs ) {
my $chash = $defs{$d};
next if( $chash->{TYPE} ne 'FHEMWEB' );
next if( !$chash->{inform} );
next if( !$chash->{FW_ID} || $chash->{FW_ID} ne $hash->{FW_ID} );
addToWritebuffer($chash, $data."\n");
$found = 1;
last;
}
$defs{$FW_wname}{asyncOutput}{$hash->{FW_ID}} = $data if( !$found );
return undef;
}
sub
FW_closeConn($)
{
@ -601,6 +640,11 @@ FW_answerCall($)
}
}
if( $FW_id ) {
$me->{FW_ID} = $FW_id;
$me->{canAsyncOutput} = 1;
}
if($FW_inform) { # Longpoll header
if($FW_inform =~ /type=/) {
foreach my $kv (split(";", $FW_inform)) {
@ -632,6 +676,12 @@ FW_answerCall($)
$FW_headercors.
"Content-Type: application/octet-stream; charset=$FW_encoding\r\n\r\n".
FW_roomStatesForInform($me, $sinceTimestamp));
if( my $data = $defs{$FW_wname}{asyncOutput}{$FW_id} ) {
addToWritebuffer($me, $data."\n");
delete $defs{$FW_wname}{asyncOutput}{$FW_id};
}
return -1;
}
@ -692,8 +742,9 @@ FW_answerCall($)
# Redirect after a command, to clean the browser URL window
if($docmd && !$FW_cmdret && AttrVal($FW_wname, "redirectCmds", 1)) {
my $tgt = $FW_ME;
if($FW_detail) { $tgt .= "?detail=$FW_detail" }
elsif($FW_room) { $tgt .= "?room=$FW_room" }
if($FW_detail) { $tgt .= "?detail=$FW_detail&fw_id=$FW_id" }
elsif($FW_room) { $tgt .= "?room=$FW_room&fw_id=$FW_id" }
else { $tgt .= "?fw_id=$FW_id" }
TcpServer_WriteBlocking($me,
"HTTP/1.1 302 Found\r\n".
"Content-Length: 0\r\n". $FW_headercors.
@ -782,7 +833,8 @@ FW_answerCall($)
my $csrf= ($FW_CSRF ? "fwcsrf='$defs{$FW_wname}{CSRFTOKEN}'" : "");
my $gen = 'generated="'.(time()-1).'"';
my $lp = 'longpoll="'.AttrVal($FW_wname,"longpoll",1).'"';
FW_pO "</head>\n<body name=\"$t\" $gen $lp $csrf>";
$FW_id = $FW_chash->{NR} if( !$FW_id );
FW_pO "</head>\n<body name=\"$t\" fw_id=\"$FW_id\" $gen $lp $csrf>";
if($FW_activateInform) {
$cmd = "style eventMonitor $FW_activateInform";
@ -874,6 +926,7 @@ FW_digestCgi($)
$FW_room = "";
$FW_detail = "";
$FW_XHR = undef;
$FW_id = undef;
$FW_jsonp = undef;
$FW_inform = undef;
@ -900,6 +953,7 @@ FW_digestCgi($)
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 "fw_id") { $FW_id = $v; }
if($p eq "jsonp") { $FW_jsonp = $v; }
if($p eq "inform") { $FW_inform = $v; }
@ -1363,7 +1417,8 @@ FW_roomOverview($)
FW_pO "<div id=\"hdr\">";
FW_pO '<table border="0" class="header"><tr><td style="padding:0">';
FW_pO "<form method=\"$FW_formmethod\" action=\"$FW_ME\">";
FW_pO FW_hidden("room", "$FW_room") if($FW_room);
FW_pO FW_hidden("fw_id", $FW_id) if($FW_id);
FW_pO FW_hidden("room", $FW_room) if($FW_room);
FW_pO FW_hidden("fwcsrf", $defs{$FW_wname}{CSRFTOKEN}) if($FW_CSRF);
FW_pO FW_textfield("cmd", $FW_ss ? 25 : 40, "maininput");
FW_pO "</form>";

View File

@ -17,6 +17,7 @@ telnet_Initialize($)
$hash->{DefFn} = "telnet_Define";
$hash->{ReadFn} = "telnet_Read";
$hash->{AsyncOutputFn} = "telnet_Output";
$hash->{UndefFn} = "telnet_Undef";
$hash->{AttrFn} = "telnet_Attr";
$hash->{NotifyFn}= "telnet_SecurityCheck";
@ -187,6 +188,7 @@ telnet_Read($)
if($hash->{SERVERSOCKET}) { # Accept and create a child
my $chash = TcpServer_Accept($hash, "telnet");
return if(!$chash);
$chash->{canAsyncOutput} = 1;
$chash->{encoding} = AttrVal($name, "encoding", "utf8");
$chash->{prompt} = AttrVal($name, "prompt", "fhem>");
syswrite($chash->{CD}, sprintf("%c%c%c", 255, 253, 0) )
@ -203,6 +205,7 @@ telnet_Read($)
if($hash->{isClient}) {
telnet_ClientDisconnect($hash, 0);
} else {
delete $hash->{canAsyncOutput};
CommandDelete(undef, $name);
}
return;
@ -284,17 +287,10 @@ telnet_Read($)
$ret .= (join("\n", @ret) . "\n") if(@ret);
$ret .= ($hash->{prevlines} ? "> " : $hash->{prompt}." ")
if($gotCmd && $hash->{showPrompt} && !$hash->{rcvdQuit});
if($ret) {
$ret = utf8ToLatin1($ret) if( $hash->{encoding} eq "latin1" );
$ret =~ s/\n/\r\n/g if($pw); # only for DOS telnet
for(;;) {
my $l = syswrite($hash->{CD}, $ret);
last if(!$l || $l == length($ret));
$ret = substr($ret, $l);
}
$hash->{CD}->flush();
}
$ret =~ s/\n/\r\n/g if($pw); # only for DOS telnet
telnet_Output($hash,$ret);
if($hash->{rcvdQuit}) {
if($hash->{isClient}) {
delete($hash->{rcvdQuit});
@ -304,6 +300,24 @@ telnet_Read($)
}
}
}
sub
telnet_Output($$)
{
my ($hash,$ret) = @_;
if($ret) {
$ret = utf8ToLatin1($ret) if( $hash->{encoding} eq "latin1" );
for(;;) {
my $l = syswrite($hash->{CD}, $ret);
last if(!$l || $l == length($ret));
$ret = substr($ret, $l);
}
$hash->{CD}->flush();
}
return undef;
}
##########################
sub

View File

@ -1601,12 +1601,33 @@ CommandGet($$)
}
$a[0] = $sdev;
$defs{$sdev}->{CL} = $cl;
my $ret = CallFn($sdev, "GetFn", $defs{$sdev}, @a);
delete $defs{$sdev}->{CL};
push @rets, $ret if(defined($ret) && $ret ne "");
}
return join("\n", @rets);
}
sub
asyncOutput($$)
{
my ($cl,$ret) = @_;
return undef if( !$cl );
if( !$defs{$cl->{NAME}}
|| $defs{$cl->{NAME}}->{NR} != $cl->{NR}
|| $defs{$cl->{NAME}}->{NAME} ne $cl->{NAME} ) {
Log3 $cl->{NAME},3,"$cl->{NAME} asyncOutput: device gone, output was: $ret";
return undef;
}
$ret = CallFn($cl->{NAME}, "AsyncOutputFn", $defs{$cl->{NAME}}, $ret);
return $ret;
}
#####################################
sub
LoadModule($;$)

View File

@ -52,8 +52,8 @@ function
FW_jqueryReadyFn()
{
FW_docReady = true;
FW_serverGenerated = document.body.getAttribute("generated");
if(document.body.getAttribute("longpoll"))
FW_serverGenerated = $("body").attr("generated");
if($("body").attr("longpoll"))
setTimeout("FW_longpoll()", 100);
$("a").each(function() { FW_replaceLink(this); })
@ -128,6 +128,7 @@ FW_jqueryReadyFn()
});
FW_cmd(FW_root+"?"+cmd+"&XHR=1&addLinks=1", function(data) {
if(!data.match(/^[\r\n]*$/)) // ignore empty answers
data = data.replace( '<', '&lt;' );
FW_okDialog('<pre>'+data+'</pre>', el);
});
});
@ -146,12 +147,32 @@ FW_jqueryReadyFn()
var input = $(this).find("input.maininput");
if(!input.length)
return;
$(this).on("submit", function() {
if($(input).val().match(/^\s*shutdown/)) {
FW_cmd(FW_root+"?XHR=1&cmd="+$(input).val());
$(this).on("submit", function(e) {
var val = $(input).val();
if(val.match(/^\s*shutdown/)) {
FW_cmd(FW_root+"?XHR=1&cmd="+val);
$(input).val("");
return false;
} else if(val.match(/^\s*get\s+/)) {
// make get use xhr instead of reload
//return true;
FW_cmd(FW_root+"?cmd="+val+"&XHR=1", function(data) {
if( !data.match( /^<html>.*<\/html>/ ) ) {
data = data.replace( '<', '&lt;' );
data = '<pre>'+data+'</pre>';
}
if( location.href.indexOf('?') === -1 )
$('#content').html(data);
else
FW_okDialog(data);
});
e.preventDefault();
$(input).val("");
return false;
}
return true;
});
});
@ -215,7 +236,7 @@ log(txt)
function
addcsrf(arg)
{
var csrf = document.body.getAttribute('fwcsrf');
var csrf = $("body").attr('fwcsrf');
if(csrf && arg.indexOf('fwcsrf') < 0)
arg += '&fwcsrf='+csrf;
return arg;
@ -226,6 +247,7 @@ FW_cmd(arg, callback)
{
log("FW_cmd:"+arg);
arg = addcsrf(arg);
arg += '&fw_id='+$("body").attr('fw_id');
var req = new XMLHttpRequest();
req.open("POST", arg, true);
req.send(null);
@ -509,7 +531,7 @@ FW_longpoll()
filter="room="+room;
}
}
var iP = document.body.getAttribute("iconPath");
var iP = $("body").attr("iconPath");
if(iP != null)
filter = filter +";iconPath="+iP;
@ -519,6 +541,7 @@ FW_longpoll()
var query = location.pathname+"?XHR=1"+
"&inform=type=status;filter="+filter+";since="+since+";fmt=JSON"+
'&fw_id='+$("body").attr('fw_id')+
"&timestamp="+new Date().getTime();
query = addcsrf(query);
FW_pollConn.open("GET", query, true);