2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-16 10:46:03 +00:00

10_ZWave.pm: rewrite processSendStack for better get handling (Forum #48962)

git-svn-id: https://svn.fhem.de/fhem/trunk@10782 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2016-02-09 20:44:59 +00:00
parent 28b49a6e90
commit 0b75b4295a

View File

@ -16,7 +16,7 @@ sub ZWave_Get($@);
sub ZWave_Parse($$@); sub ZWave_Parse($$@);
sub ZWave_Set($@); sub ZWave_Set($@);
sub ZWave_SetClasses($$$$); sub ZWave_SetClasses($$$$);
sub ZWave_addToSendStack($$); sub ZWave_addToSendStack($$$);
sub ZWave_secStart($); sub ZWave_secStart($);
sub ZWave_secEnd($); sub ZWave_secEnd($);
sub ZWave_configParseModel($;$); sub ZWave_configParseModel($;$);
@ -399,9 +399,9 @@ my %zwave_class = (
"..8506(..)" => '"assocGroups:".hex($1)' }, "..8506(..)" => '"assocGroups:".hex($1)' },
init => { ORDER=>10, CMD=> '"set $NAME associationAdd 1 $CTRLID"' } }, init => { ORDER=>10, CMD=> '"set $NAME associationAdd 1 $CTRLID"' } },
VERSION => { id => '86', VERSION => { id => '86',
set => { versionClassRequest => 'ZWave_versionClassRequest($hash,"%s")'},
get => { version => "11", get => { version => "11",
versionClass => 'ZWave_versionClassGet("%s")' }, versionClass => 'ZWave_versionClassGet("%s")',
versionClassAll => 'ZWave_versionClassAllGet($hash)'},
parse => { "028611" => "cmdGet:version", parse => { "028611" => "cmdGet:version",
"078612(..........)" => 'sprintf("version:Lib %d Prot '. "078612(..........)" => 'sprintf("version:Lib %d Prot '.
'%d.%d App %d.%d", unpack("C*",pack("H*","$1")))', '%d.%d App %d.%d", unpack("C*",pack("H*","$1")))',
@ -791,6 +791,7 @@ ZWave_Cmd($$@)
$cmdFmt = sprintf($cmdFmt, @a) if($nArg); $cmdFmt = sprintf($cmdFmt, @a) if($nArg);
my ($err, $ncmd) = eval($cmdFmt) if($cmdFmt !~ m/^\d/); my ($err, $ncmd) = eval($cmdFmt) if($cmdFmt !~ m/^\d/);
return $err if($err); return $err if($err);
return $@ if($@);
$cmdFmt = $ncmd if(defined($ncmd)); $cmdFmt = $ncmd if(defined($ncmd));
return "" if($ncmd && $ncmd eq "EMPTY"); # configAll return "" if($ncmd && $ncmd eq "EMPTY"); # configAll
} }
@ -834,13 +835,13 @@ ZWave_Cmd($$@)
} }
} }
my $r = ZWave_addToSendStack($baseHash, $data); my $r = ZWave_addToSendStack($baseHash, $type, $data);
if($r) { if($r) {
return (AttrVal($name,"verbose",3) > 2 ? $r : undef); return (AttrVal($name,"verbose",3) > 2 ? $r : undef);
} }
my $val; my $val;
if($type eq "get") { # Wait for result if($type eq "get" && $hash->{CL}) { # Wait for the result for frontend cmd
no strict "refs"; no strict "refs";
my $iohash = $hash->{IODev}; my $iohash = $hash->{IODev};
my $fn = $modules{$iohash->{TYPE}}{ReadAnswerFn}; my $fn = $modules{$iohash->{TYPE}}{ReadAnswerFn};
@ -853,9 +854,11 @@ ZWave_Cmd($$@)
$data = "$cmd $id $data" if($re); $data = "$cmd $id $data" if($re);
$val = ($data ? ZWave_Parse($iohash, $data, $type) : "no data returned"); $val = ($data ? ZWave_Parse($iohash, $data, $type) : "no data returned");
ZWave_processSendStack($hash) if($data && $cmd eq "neighborList");
} else { ZWave_processSendStack($hash, "next") # No radio traffic for neighborList
if($data && $cmd eq "neighborList");
} elsif($type ne "get") {
$cmd .= " ".join(" ", @a) if(@a); $cmd .= " ".join(" ", @a) if(@a);
readingsSingleUpdate($hash, "state", $cmd, 1); readingsSingleUpdate($hash, "state", $cmd, 1);
} }
@ -1547,48 +1550,6 @@ ZWave_meterSupportedParse($$)
" $meter_reset_text"; " $meter_reset_text";
} }
sub
ZWave_versionClassRequest($$)
{
my ($hash, $answer) = @_;
my $name = $hash->{NAME};
if($answer =~ m/^048614(..)(..)$/i) { # Parse part
my $v = $hash->{versionhash};
$v->{$zwave_id2class{lc($1)}} = $2;
foreach my $class (keys %{$v}) {
next if($v->{$class} ne "");
my $r = ZWave_Set($hash, $name, "versionClassRequest", $class);
return;
}
$attr{$hash->{NAME}}{vclasses} =
join(" ", map { "$_:$v->{$_}" } sort keys %{$v});
delete($hash->{versionhash});
}
if($answer ne "%s" && $hash->{versionhash}) { # get next
return("", sprintf('13%02x', hex($zwave_class{$answer}{id})))
if($zwave_class{$answer});
return("versionClassRequest needs no parameter", "");
}
return("versionClassRequest needs no parameter", "")
if($answer ne "%s" && !$hash->{versionhash});
return("another versionClassRequest is already running", "")
if(defined($hash->{versionhash}));
# User part: called with no parameters
my %h = map { $_ => "" }
grep { $_ !~ m/^MARK$/ && $_ !~ m/^UNKNOWN/ }
split(" ", AttrVal($name, "classes", ""));
$hash->{versionhash} = \%h;
foreach my $class (keys %h) {
next if($h{$class} ne "");
return("", sprintf('13%02x', hex($zwave_class{$class}{id})));
}
return("Should not happen", "");
}
sub sub
ZWave_versionClassGet($) ZWave_versionClassGet($)
@ -1603,6 +1564,35 @@ ZWave_versionClassGet($)
return ("Unknown class $class", ""); return ("Unknown class $class", "");
} }
sub
ZWave_versionClassAllGet($@)
{
my ($hash, $data) = @_;
my $name = $hash->{NAME};
my $hname = ".versionClassAllGet";
if(!$data) { # called by the user
$zwave_parseHook{"$hash->{nodeIdHex}:..8614"} = \&ZWave_versionClassAllGet;
delete($hash->{CL});
my @arr = split(" ", AttrVal($name, "classes", ""));
$hash->{$hname} = \@arr;
ZWave_Get($hash, $name, "versionClass", shift @{$hash->{$hname}});
$attr{$name}{vclasses} = "";
return("", "EMPTY");
}
return 0 if($data !~ m/^048614(..)(..)$/i); # ??
$attr{$name}{vclasses} .= " " if($attr{$name}{vclasses});
$attr{$name}{vclasses} .= $zwave_id2class{lc($1)}.":".hex($2);
if(!int(@{$hash->{$hname}})) {
delete($hash->{$hname});
return 1; # "veto" for parseHook
}
ZWave_Get($hash, $name, "versionClass", shift @{$hash->{$hname}});
$zwave_parseHook{"$hash->{nodeIdHex}:..8614"} = \&ZWave_versionClassAllGet;
return 1; # "veto" for parseHook
}
sub sub
ZWave_multilevelParse($$$) ZWave_multilevelParse($$$)
{ {
@ -2583,8 +2573,7 @@ ZWave_configAllGet($)
my $mc = ZWave_configGetHash($hash); my $mc = ZWave_configGetHash($hash);
return ("configAll: no model specific configs found", undef) return ("configAll: no model specific configs found", undef)
if(!$mc || !$mc->{config}); if(!$mc || !$mc->{config});
#use Data::Dumper; delete($hash->{CL});
#Log 1, Dumper $mc;
foreach my $c (sort keys %{$mc->{get}}) { foreach my $c (sort keys %{$mc->{get}}) {
ZWave_Get($hash, $hash->{NAME}, $c); ZWave_Get($hash, $hash->{NAME}, $c);
} }
@ -2598,6 +2587,7 @@ ZWave_associationAllGet($$)
if(!$data) { # called by the user if(!$data) { # called by the user
$zwave_parseHook{"$hash->{nodeIdHex}:..85"} = \&ZWave_associationAllGet; $zwave_parseHook{"$hash->{nodeIdHex}:..85"} = \&ZWave_associationAllGet;
delete($hash->{CL});
ZWave_Get($hash, $hash->{NAME}, "associationGroups"); ZWave_Get($hash, $hash->{NAME}, "associationGroups");
return("", "EMPTY"); return("", "EMPTY");
} }
@ -2879,7 +2869,7 @@ ZWave_secAddToSendStack($$)
my $len = sprintf("%02x", (length($cmd)-2)/2+1); my $len = sprintf("%02x", (length($cmd)-2)/2+1);
my $cmdEf = (AttrVal($name, "noExplorerFrames", 0) == 0 ? "25" : "05"); my $cmdEf = (AttrVal($name, "noExplorerFrames", 0) == 0 ? "25" : "05");
my $data = "13$id$len$cmd$cmdEf$id"; # 13==SEND_DATA my $data = "13$id$len$cmd$cmdEf$id"; # 13==SEND_DATA
ZWave_addToSendStack($hash, $data); ZWave_addToSendStack($hash, "set", $data);
} }
sub sub
@ -3497,14 +3487,23 @@ ZWave_isWakeUp($)
return $h->{isWakeUp}; return $h->{isWakeUp};
} }
# stack contains type:hexcode
# type is: set / sentset, get / sentget / sentackget
# sentset will be discarded after ack, sentget needs ack (->sentackget) then msg
# next discards either state.
# acktpye: next, ack or msg
sub sub
ZWave_processSendStack($) ZWave_processSendStack($$)
{ {
my ($hash) = @_; my ($hash,$ackType) = @_;
my $ss = $hash->{SendStack}; my $ss = $hash->{SendStack};
return if(!$ss); return if(!$ss);
if(index($ss->[0],"sent") == 0) { if($ss->[0] =~ m/^sent(.*?):(.*)$/) {
if($1 eq "get" && $ackType eq "ack") {
$ss->[0] = "sentackget:$2";
return;
}
shift @{$ss}; shift @{$ss};
RemoveInternalTimer($hash) if(!ZWave_isWakeUp($hash)); RemoveInternalTimer($hash) if(!ZWave_isWakeUp($hash));
} }
@ -3514,30 +3513,32 @@ ZWave_processSendStack($)
return; return;
} }
IOWrite($hash, $hash->{homeId}, "00".$ss->[0]); $ss->[0] =~ m/^([^:]*?):(.*)$/;
$ss->[0] = "sent:".$ss->[0]; my ($type, $msg) = ($1, $2);
IOWrite($hash, $hash->{homeId}, "00$msg");
$ss->[0] = "sent$type:$msg";
$hash->{lastMsgSent} = gettimeofday(); $hash->{lastMsgSent} = gettimeofday();
$zwave_lastHashSent = $hash; $zwave_lastHashSent = $hash;
if(!ZWave_isWakeUp($hash)) { if(!ZWave_isWakeUp($hash)) {
InternalTimer($hash->{lastMsgSent}+10, sub { InternalTimer($hash->{lastMsgSent}+3, sub {
Log 2, "ZWave: No ACK from $hash->{NAME} after 10s for $ss->[0]"; Log 2, "ZWave: No ACK from $hash->{NAME} after 3s for $ss->[0]";
ZWave_processSendStack($hash); ZWave_processSendStack($hash, "next");
}, $hash, 0); }, $hash, 0);
} }
} }
sub sub
ZWave_addToSendStack($$) ZWave_addToSendStack($$$)
{ {
my ($hash, $cmd) = @_; my ($hash, $type, $cmd) = @_;
if(!$hash->{SendStack}) { if(!$hash->{SendStack}) {
my @empty; my @empty;
$hash->{SendStack} = \@empty; $hash->{SendStack} = \@empty;
} }
my $ss = $hash->{SendStack}; my $ss = $hash->{SendStack};
push @{$ss}, $cmd; push @{$ss}, "$type:$cmd";
if(ZWave_isWakeUp($hash)) { if(ZWave_isWakeUp($hash)) {
# SECURITY XXX and neighborList # SECURITY XXX and neighborList
@ -3549,14 +3550,14 @@ ZWave_addToSendStack($$)
} else { # clear commands without 0113 and 0013 } else { # clear commands without 0113 and 0013
my $now = gettimeofday(); my $now = gettimeofday();
if(@{$ss} > 1 && $now-$hash->{lastMsgSent} > 10) { if(@{$ss} > 1 && $now-$hash->{lastMsgSent} > 3) {
Log3 $hash, 2, Log3 $hash, 2,
"ERROR: $hash->{NAME}: cleaning commands without ack after 10s"; "ERROR: $hash->{NAME}: cleaning commands without ack after 3s";
delete $hash->{SendStack}; delete $hash->{SendStack};
return ZWave_addToSendStack($hash, $cmd); return ZWave_addToSendStack($hash, $type, $cmd);
} }
} }
ZWave_processSendStack($hash) if(@{$ss} == 1); ZWave_processSendStack($hash, "next") if(@{$ss} == 1);
return undef; return undef;
} }
@ -3598,7 +3599,7 @@ ZWave_Parse($$@)
my $hash = $zwave_lastHashSent; my $hash = $zwave_lastHashSent;
readingsSingleUpdate($hash, "SEND_DATA", "failed:$arg", 1); readingsSingleUpdate($hash, "SEND_DATA", "failed:$arg", 1);
Log3 $ioName, 2, "ERROR: cannot SEND_DATA to $hash->{NAME}: $arg"; Log3 $ioName, 2, "ERROR: cannot SEND_DATA to $hash->{NAME}: $arg";
ZWave_processSendStack($hash); ZWave_processSendStack($hash, "next");
} else { } else {
Log3 $ioName, 2, "ERROR: cannot SEND_DATA: $arg (unknown device)"; Log3 $ioName, 2, "ERROR: cannot SEND_DATA: $arg (unknown device)";
@ -3674,7 +3675,7 @@ ZWave_Parse($$@)
if($hash) { if($hash) {
if(ZWave_isWakeUp($hash)) { if(ZWave_isWakeUp($hash)) {
ZWave_wakeupTimer($hash, 1); ZWave_wakeupTimer($hash, 1);
ZWave_processSendStack($hash); ZWave_processSendStack($hash, "next");
} }
if(!$ret) { if(!$ret) {
@ -3694,7 +3695,7 @@ ZWave_Parse($$@)
Log3 $ioName, 4, "$ioName transmit $lmsg for $callbackid"; Log3 $ioName, 4, "$ioName transmit $lmsg for $callbackid";
if($hash) { if($hash) {
readingsSingleUpdate($hash, "transmit", $lmsg, 0); readingsSingleUpdate($hash, "transmit", $lmsg, 0);
ZWave_processSendStack($hash); ZWave_processSendStack($hash, "ack");
} }
return ""; return "";
@ -3816,11 +3817,6 @@ ZWave_Parse($$@)
next; next;
} }
if($className eq "VERSION" && defined($hash->{versionhash})) {
ZWave_versionClassRequest($hash, $arg);
return "";
}
my $ptr = ZWave_getHash($hash, $className, "parse"); my $ptr = ZWave_getHash($hash, $className, "parse");
if(!$ptr) { if(!$ptr) {
push @event, "UNPARSED:$className $arg"; push @event, "UNPARSED:$className $arg";
@ -3853,7 +3849,11 @@ ZWave_Parse($$@)
if($arg =~ m/^028407/) { # wakeup:notification if($arg =~ m/^028407/) { # wakeup:notification
ZWave_wakeupTimer($hash, 1); ZWave_wakeupTimer($hash, 1);
ZWave_processSendStack($hash); ZWave_processSendStack($hash, "next");
} else {
ZWave_processSendStack($hash, "msg");
} }
return "" if(!@event); return "" if(!@event);
@ -4433,14 +4433,6 @@ s2Hex($)
available (deleted) and 1 for set (occupied). code is a hexadecimal string. available (deleted) and 1 for set (occupied). code is a hexadecimal string.
</li> </li>
<br><br><b>Class VERSION</b>
<li>versionClassRequest<br>
executes "get devicename versionClass class" for each class from the
classes attribute in the background without generating events, and sets the
vclasses attribute at the end.
</li>
<br><br><b>Class WAKE_UP</b> <br><br><b>Class WAKE_UP</b>
<li>wakeupInterval value nodeId<br> <li>wakeupInterval value nodeId<br>
Set the wakeup interval of battery operated devices to the given value in Set the wakeup interval of battery operated devices to the given value in
@ -4791,7 +4783,14 @@ s2Hex($)
</li> </li>
<li>versionClass classId or className<br> <li>versionClass classId or className<br>
return the supported command version for the requested class return the supported command version for the requested class
</li> </li>
<li>versionClassAll<br>
executes "get devicename versionClass class" for each class from the
classes attribute in the background without generating events, and sets the
vclasses attribute at the end.
</li>
<br><br><b>Class WAKE_UP</b> <br><br><b>Class WAKE_UP</b>
<li>wakeupInterval<br> <li>wakeupInterval<br>
@ -4839,7 +4838,7 @@ s2Hex($)
that are supported with SECURITY. that are supported with SECURITY.
</li> </li>
<li><a href="#vclasses">vclasses</a> <li><a href="#vclasses">vclasses</a>
This is the result of the "set DEVICE versionClassRequest" command, and This is the result of the "get DEVICE versionClassAll" command, and
contains the version information for each of the supported classes. contains the version information for each of the supported classes.
</li> </li>
<li><a href="#noExplorerFrames">noExplorerFrames</a> <li><a href="#noExplorerFrames">noExplorerFrames</a>