mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-10 03:06:37 +00:00
00_ZWCUL.pm: first check in (Forum #44905)
git-svn-id: https://svn.fhem.de/fhem/trunk@10116 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
ef64fa1017
commit
87d9bc4372
495
fhem/FHEM/00_ZWCUL.pm
Executable file
495
fhem/FHEM/00_ZWCUL.pm
Executable file
@ -0,0 +1,495 @@
|
||||
##############################################
|
||||
# $Id$
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
|
||||
use ZWLib;
|
||||
use DevIo;
|
||||
|
||||
sub ZWCUL_Parse($$$$);
|
||||
sub ZWCUL_Read($@);
|
||||
sub ZWCUL_ReadAnswer($$$);
|
||||
sub ZWCUL_Ready($);
|
||||
sub ZWCUL_SimpleWrite($$);
|
||||
sub ZWCUL_Write($$$);
|
||||
sub ZWCUL_ProcessSendStack($);
|
||||
|
||||
use vars qw(%zwave_id2class);
|
||||
|
||||
my %sets = (
|
||||
"reopen" => "",
|
||||
"raw" => { cmd=> "%s" },
|
||||
);
|
||||
|
||||
my %gets = (
|
||||
"homeId" => { cmd=> "zi", regex => "^. [A-F0-9]{8} [A-F0-9]{2}\$" },
|
||||
"version" => { cmd=> "V", regex => "^V " },
|
||||
"raw" => { cmd=> "%s", regex => ".*" }
|
||||
);
|
||||
|
||||
sub
|
||||
ZWCUL_Initialize($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
# Provider
|
||||
$hash->{ReadFn} = "ZWCUL_Read";
|
||||
$hash->{WriteFn} = "ZWCUL_Write";
|
||||
$hash->{ReadyFn} = "ZWCUL_Ready";
|
||||
$hash->{ReadAnswerFn} = "ZWCUL_ReadAnswer";
|
||||
|
||||
# Normal devices
|
||||
$hash->{DefFn} = "ZWCUL_Define";
|
||||
$hash->{SetFn} = "ZWCUL_Set";
|
||||
$hash->{GetFn} = "ZWCUL_Get";
|
||||
$hash->{AttrFn} = "ZWCUL_Attr";
|
||||
$hash->{UndefFn} = "ZWCUL_Undef";
|
||||
$hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 model disable:0,1 ";
|
||||
"networkKey";
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
ZWCUL_Define($$)
|
||||
{
|
||||
my ($hash, $def) = @_;
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
return "wrong syntax: define <name> ZWCUL device homeId ctrlId" if(@a != 5);
|
||||
return "wrong syntax: homeId is 8 digit hex" if($a[3] !~ m/^[0-9A-F]{8}$/);
|
||||
return "wrong syntax: ctrlId is 2 digit hex" if($a[4] !~ m/^[0-9A-F]{2}$/);
|
||||
|
||||
DevIo_CloseDev($hash);
|
||||
|
||||
my $name = $a[0];
|
||||
my $dev = $a[2];
|
||||
$hash->{homeId} = $a[3];
|
||||
$hash->{homeIdSet} = $a[3];
|
||||
$hash->{nodeIdHex} = $a[4];
|
||||
|
||||
$hash->{Clients} = ":ZWave:STACKABLE_CC:";
|
||||
my %matchList = ( "1:ZWave" => ".*",
|
||||
"2:STACKABLE_CC"=>"^\\*");
|
||||
$hash->{MatchList} = \%matchList;
|
||||
|
||||
if($dev eq "none") {
|
||||
Log3 $name, 1, "$name device is none, commands will be echoed only";
|
||||
$attr{$name}{dummy} = 1;
|
||||
readingsSingleUpdate($hash, "state", "dummy", 1);
|
||||
return undef;
|
||||
|
||||
} elsif($dev !~ m/@/) {
|
||||
$def .= "\@9600"; # default baudrate
|
||||
|
||||
}
|
||||
|
||||
$hash->{DeviceName} = $dev;
|
||||
return DevIo_OpenDev($hash, 0, "ZWCUL_DoInit");
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
ZWCUL_DoInit($)
|
||||
{
|
||||
my $hash = shift;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
$hash->{PARTIAL} = "";
|
||||
|
||||
$hash->{RA_Timeout} = 0.5; # Clear the pipe
|
||||
for(;;) {
|
||||
my ($err, undef) = ZWCUL_ReadAnswer($hash, "Clear", "wontmatch");
|
||||
last if($err && ($err =~ m/^Timeout/ || $err =~ m/No FD/));
|
||||
}
|
||||
delete($hash->{RA_Timeout});
|
||||
|
||||
my ($err, $ver, $try) = ("", "", 0);
|
||||
while($try++ < 3 && $ver !~ m/^V/) {
|
||||
ZWCUL_SimpleWrite($hash, "V");
|
||||
($err, $ver) = ZWCUL_ReadAnswer($hash, "Version", "^V");
|
||||
return "$name: $err" if($err && ($err !~ m/Timeout/ || $try == 3));
|
||||
$ver = "" if(!$ver);
|
||||
}
|
||||
if($ver !~ m/^V/) {
|
||||
$attr{$name}{dummy} = 1;
|
||||
my $msg = "Not an CUL device, got for V: $ver";
|
||||
Log3 $name, 1, $msg;
|
||||
return $msg;
|
||||
}
|
||||
$ver =~ s/[\r\n]//g;
|
||||
$hash->{VERSION} = $ver;
|
||||
|
||||
ZWCUL_SimpleWrite($hash, "zi".$hash->{homeIdSet}.$hash->{nodeIdHex});
|
||||
ZWCUL_SimpleWrite($hash, $hash->{homeIdSet} =~ m/^0*$/ ? "zm" : "zr");
|
||||
|
||||
readingsSingleUpdate($hash, "state", "Initialized", 1);
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
ZWCUL_Undef($$)
|
||||
{
|
||||
my ($hash,$arg) = @_;
|
||||
ZWCUL_SimpleWrite($hash, "zx");
|
||||
DevIo_CloseDev($hash);
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
ZWCUL_cmd($$@)
|
||||
{
|
||||
my ($type, $cmdList, $hash, @a) = @_;
|
||||
my $name = shift @a;
|
||||
|
||||
return "\"$type $name\" needs at least one parameter" if(@a < 1);
|
||||
my $cmdName = shift @a;
|
||||
|
||||
if(!defined($cmdList->{$cmdName})) {
|
||||
return "Unknown argument $cmdName, choose one of " .
|
||||
join(",",sort keys %{$cmdList});
|
||||
}
|
||||
|
||||
Log3 $hash, 4, "ZWCUL $type $name $cmdName ".join(" ",@a);
|
||||
if($cmdName eq "reopen") {
|
||||
return if(AttrVal($name, "dummy",undef) || AttrVal($name, "disable",undef));
|
||||
delete $hash->{NEXT_OPEN};
|
||||
DevIo_CloseDev($hash);
|
||||
sleep(1);
|
||||
DevIo_OpenDev($hash, 0, "ZWCUL_DoInit");
|
||||
return;
|
||||
}
|
||||
|
||||
my $cmd = $cmdList->{$cmdName}{cmd};
|
||||
my @ca = split("%", $cmd, -1);
|
||||
my $nargs = int(@ca)-1;
|
||||
return "$type $name $cmdName needs $nargs arguments" if($nargs != int(@a));
|
||||
$cmd = sprintf($cmd, @a);
|
||||
ZWCUL_SimpleWrite($hash, $cmd);
|
||||
|
||||
return undef if($type eq "set");
|
||||
|
||||
my $re = $cmdList->{$cmdName}{regexp};
|
||||
my ($e, $d) = ZWCUL_ReadAnswer($hash, $cmdName, $cmdList->{$cmdName}{regexp});
|
||||
return $e if($e);
|
||||
return $d;
|
||||
}
|
||||
|
||||
sub ZWCUL_Set() { return ZWCUL_cmd("set", \%sets, @_); };
|
||||
sub ZWCUL_Get() { return ZWCUL_cmd("get", \%gets, @_); };
|
||||
|
||||
#####################################
|
||||
sub
|
||||
ZWCUL_SimpleWrite($$)
|
||||
{
|
||||
my ($hash, $msg) = @_;
|
||||
return if(!$hash);
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
Log3 $name, 1, "SW: $msg";
|
||||
$msg .= "\n";
|
||||
|
||||
$hash->{USBDev}->write($msg) if($hash->{USBDev});
|
||||
syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev});
|
||||
syswrite($hash->{DIODev}, $msg) if($hash->{DIODev});
|
||||
select(undef, undef, undef, 0.001);
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
ZWCUL_Write($$$)
|
||||
{
|
||||
my ($hash,$fn,$msg) = @_;
|
||||
|
||||
Log3 $hash, 5, "ZWCUL_Write $fn $msg";
|
||||
if($msg =~ m/0013(..)(..)(.*)(....)/) {
|
||||
my ($t,$l,$p) = ($1,$2,$3);
|
||||
my $th = $modules{ZWave}{defptr}{"$fn $t"};
|
||||
if(!$th) {
|
||||
Log 1, "ZWCUL: no device found for $fn $t";
|
||||
return;
|
||||
}
|
||||
$th->{sentIdx} = ($th->{sentIdx}++ % 16);
|
||||
$msg = sprintf("%s%s41%02x%02x%s%s",
|
||||
$fn, $hash->{nodeIdHex}, $th->{sentIdx},
|
||||
length($p)/2+10, $th->{nodeIdHex}, $p);
|
||||
$msg .= zwlib_checkSum($msg);
|
||||
ZWCUL_SimpleWrite($hash, "zs".$msg);
|
||||
}
|
||||
}
|
||||
|
||||
#####################################
|
||||
# called from the global loop, when the select for hash->{FD} reports data
|
||||
sub
|
||||
ZWCUL_Read($@)
|
||||
{
|
||||
my ($hash, $local, $regexp) = @_;
|
||||
|
||||
my $buf = (defined($local) ? $local : DevIo_SimpleRead($hash));
|
||||
return "" if(!defined($buf));
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $culdata = $hash->{PARTIAL};
|
||||
#Log3 $name, 5, "ZWCUL/RAW: $culdata/$buf";
|
||||
$culdata .= $buf;
|
||||
|
||||
while($culdata =~ m/\n/) {
|
||||
my $rmsg;
|
||||
($rmsg,$culdata) = split("\n", $culdata, 2);
|
||||
$rmsg =~ s/\r//;
|
||||
$hash->{PARTIAL} = $culdata; # for recursive calls
|
||||
return $rmsg
|
||||
if(defined($local) && (!defined($regexp) || ($rmsg =~ m/$regexp/)));
|
||||
ZWCUL_Parse($hash, $hash, $name, $rmsg) if($rmsg);
|
||||
$culdata = $hash->{PARTIAL};
|
||||
}
|
||||
$hash->{PARTIAL} = $culdata;
|
||||
}
|
||||
|
||||
sub
|
||||
ZWCUL_Parse($$$$)
|
||||
{
|
||||
my ($hash, $iohash, $name, $rmsg) = @_;
|
||||
|
||||
if($rmsg =~ m/^\*/) { # STACKABLE_CC
|
||||
Dispatch($hash, $rmsg, undef);
|
||||
return;
|
||||
}
|
||||
|
||||
$hash->{"${name}_MSGCNT"}++;
|
||||
$hash->{"${name}_TIME"} = TimeNow();
|
||||
# showtime attribute
|
||||
readingsSingleUpdate($hash, "state", $hash->{READINGS}{state}{VAL}, 0);
|
||||
$hash->{RAWMSG} = $rmsg;
|
||||
my %addvals = (RAWMSG => $rmsg);
|
||||
|
||||
$rmsg = lc($rmsg) if($rmsg =~ m/^z/);
|
||||
if($rmsg =~ m/^z(........)(..)(..)(..)(..)(..)(.*)(..)$/) {
|
||||
|
||||
my ($H, $S, $F, $f, $L, $T, $P, $C) = ($1,$2,$3,$4,$5,$6,$7,$8);
|
||||
Log3 $hash, 5, "$H S:$S F:$F f:$f L:$L T:$T P:$P C:$C";
|
||||
|
||||
$hash->{homeId} = $H;
|
||||
|
||||
if(length($P)) {
|
||||
$rmsg = sprintf("0004%s%s%02x%s", $S, $S, length($P)/2, $P);
|
||||
my $ctrlIsTgt = ($T eq $hash->{nodeIdHex});
|
||||
my $th = $modules{ZWave}{defptr}{"$H $S"};
|
||||
|
||||
if(!$ctrlIsTgt && !$th) {
|
||||
DoTrigger("global", "UNDEFINED ZWNode_$1_$2 ZWave $1 ".hex($2));
|
||||
$th = $modules{ZWave}{defptr}{"$1 $2"};
|
||||
}
|
||||
|
||||
# Auto-Add classes
|
||||
my $pcl = $zwave_id2class{substr($P, 0, 2)};
|
||||
if($th && $pcl) {
|
||||
my $cl = AttrVal($th->{NAME},"classes","");
|
||||
if($cl !~ m/\b$pcl\b/) {
|
||||
$attr{$th->{NAME}}{classes} = "$cl $pcl";
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
$rmsg = sprintf("0013%s00", $6);
|
||||
|
||||
}
|
||||
}
|
||||
Dispatch($hash, $rmsg, \%addvals);
|
||||
}
|
||||
|
||||
#####################################
|
||||
# This is a direct read for commands like get
|
||||
sub
|
||||
ZWCUL_ReadAnswer($$$)
|
||||
{
|
||||
my ($hash, $arg, $regexp) = @_;
|
||||
Log3 $hash, 4, "ZWCUL_ReadAnswer arg:$arg regexp:".($regexp ? $regexp:"");
|
||||
return ("No FD (dummy device?)", undef)
|
||||
if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD})));
|
||||
my $to = ($hash->{RA_Timeout} ? $hash->{RA_Timeout} : 1);
|
||||
|
||||
for(;;) {
|
||||
|
||||
my $buf;
|
||||
if($^O =~ m/Win/ && $hash->{USBDev}) {
|
||||
$hash->{USBDev}->read_const_time($to*1000); # set timeout (ms)
|
||||
# Read anstatt input sonst funzt read_const_time nicht.
|
||||
$buf = $hash->{USBDev}->read(999);
|
||||
return ("Timeout reading answer for get $arg", undef)
|
||||
if(length($buf) == 0);
|
||||
|
||||
} else {
|
||||
if(!$hash->{FD}) {
|
||||
Log3 $hash, 1, "ZWCUL_ReadAnswer: device lost";
|
||||
return ("Device lost when reading answer for get $arg", undef);
|
||||
}
|
||||
|
||||
my $rin = '';
|
||||
vec($rin, $hash->{FD}, 1) = 1;
|
||||
my $nfound = select($rin, undef, undef, $to);
|
||||
if($nfound < 0) {
|
||||
my $err = $!;
|
||||
Log3 $hash, 5, "ZWCUL_ReadAnswer: nfound < 0 / err:$err";
|
||||
next if ($err == EAGAIN() || $err == EINTR() || $err == 0);
|
||||
DevIo_Disconnected($hash);
|
||||
return("ZWCUL_ReadAnswer $arg: $err", undef);
|
||||
}
|
||||
|
||||
if($nfound == 0){
|
||||
Log3 $hash, 5, "ZWCUL_ReadAnswer: select timeout";
|
||||
return ("Timeout reading answer for get $arg", undef);
|
||||
}
|
||||
|
||||
$buf = DevIo_SimpleRead($hash);
|
||||
if(!defined($buf)){
|
||||
Log3 $hash, 1,"ZWCUL_ReadAnswer: no data read";
|
||||
return ("No data", undef);
|
||||
}
|
||||
}
|
||||
|
||||
my $ret = ZWCUL_Read($hash, $buf, $regexp);
|
||||
if(defined($ret)){
|
||||
Log3 $hash, 4, "ZWCUL_ReadAnswer for $arg: $ret";
|
||||
return (undef, $ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
ZWCUL_Attr($$$$)
|
||||
{
|
||||
my ($cmd, $name, $attr, $value) = @_;
|
||||
my $hash = $defs{$name};
|
||||
|
||||
if($attr eq "disable") {
|
||||
if($cmd eq "set" && ($value || !defined($value))) {
|
||||
DevIo_CloseDev($hash) if(!AttrVal($name,"dummy",undef));
|
||||
readingsSingleUpdate($hash, "state", "disabled", 1);
|
||||
|
||||
} else {
|
||||
if(AttrVal($name,"dummy",undef)) {
|
||||
readingsSingleUpdate($hash, "state", "dummy", 1);
|
||||
return;
|
||||
}
|
||||
DevIo_OpenDev($hash, 0, "ZWCUL_DoInit");
|
||||
|
||||
}
|
||||
|
||||
} elsif($attr eq "networkKey" && $cmd eq "set") {
|
||||
if(!$value || $value !~ m/^[0-9A-F]{32}$/i) {
|
||||
return "attr $name networkKey: not a hex string with a length of 32";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return undef;
|
||||
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
ZWCUL_Ready($)
|
||||
{
|
||||
my ($hash) = @_;
|
||||
|
||||
return undef if (IsDisabled($hash->{NAME}));
|
||||
|
||||
return DevIo_OpenDev($hash, 1, "ZWCUL_DoInit")
|
||||
if(ReadingsVal($hash->{NAME}, "state","") eq "disconnected");
|
||||
|
||||
# This is relevant for windows/USB only
|
||||
my $po = $hash->{USBDev};
|
||||
if($po) {
|
||||
my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
|
||||
return ($InBytes>0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=begin html
|
||||
|
||||
<a name="ZWCUL"></a>
|
||||
<h3>ZWCUL</h3>
|
||||
<ul>
|
||||
This module serves a CUL in ZWave mode (starting from culfw version 1.66),
|
||||
which is attached via USB or TCP/IP, and enables the use of ZWave devices
|
||||
(see also the <a href="#ZWave">ZWave</a> module).
|
||||
<br><br>
|
||||
<a name="ZWCULdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> ZWCUL <device> <homeId>
|
||||
<ctrlId></code>
|
||||
<br>
|
||||
<br>
|
||||
Since the DevIo module is used to open the device, you can also use devices
|
||||
connected via TCP/IP. See <a href="#CULdefine">this</a> paragraph on device
|
||||
naming details.
|
||||
<br>
|
||||
Example:
|
||||
<ul>
|
||||
<code>define ZWCUL_1 ZWCUL /dev/cu.usbmodemfa141@9600 12345678 01</code><br>
|
||||
</ul>
|
||||
If the homeId is set to 0, then culfw will enter monitor mode, i.e. no
|
||||
checksum filtering will be done, and no acks for received messages will be
|
||||
sent.
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="ZWCULset"></a>
|
||||
<b>Set</b>
|
||||
<ul>
|
||||
|
||||
<li>reopen<br>
|
||||
First close and then open the device. Used for debugging purposes.
|
||||
</li>
|
||||
|
||||
<li>raw<br>
|
||||
send a raw string to culfw
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="ZWCULget"></a>
|
||||
<b>Get</b>
|
||||
<ul>
|
||||
<li>homeId<br>
|
||||
return the homeId and the ctrlId of the controller.</li>
|
||||
|
||||
<li>raw<br>
|
||||
Send raw data to the controller.</li>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="ZWCULattr"></a>
|
||||
<b>Attributes</b>
|
||||
<ul>
|
||||
<li><a href="#dummy">dummy</a></li>
|
||||
<li><a href="#do_not_notify">do_not_notify</a></li>
|
||||
<li><a href="#model">model</a></li>
|
||||
<li><a href="#disable">disable</a></li>
|
||||
<li><a href="#networkKey">networkKey</a></li>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="ZWCULevents"></a>
|
||||
<b>Generated events: TODO</b>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
=end html
|
||||
=cut
|
@ -1,15 +1,11 @@
|
||||
##############################################
|
||||
# $Id$
|
||||
# TODO:
|
||||
# - routing commands
|
||||
# - one command to create a fhem device for all nodeList entries
|
||||
# - inclusion mode active only for a given time (pairForSec)
|
||||
# - use central readings functions
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
use ZWLib;
|
||||
|
||||
sub ZWDongle_Parse($$$);
|
||||
sub ZWDongle_Read($@);
|
||||
@ -55,124 +51,6 @@ my %gets = (
|
||||
"raw" => "%s", # hex
|
||||
);
|
||||
|
||||
# Known controller function.
|
||||
# Note: Known != implemented, see %sets & %gets for the implemented ones.
|
||||
use vars qw(%zw_func_id);
|
||||
use vars qw(%zw_type6);
|
||||
|
||||
%zw_func_id= (
|
||||
'02' => 'SERIAL_API_GET_INIT_DATA',
|
||||
'03' => 'SERIAL_API_APPL_NODE_INFORMATION',
|
||||
'04' => 'APPLICATION_COMMAND_HANDLER',
|
||||
'05' => 'ZW_GET_CONTROLLER_CAPABILITIES',
|
||||
'06' => 'SERIAL_API_SET_TIMEOUTS',
|
||||
'07' => 'SERIAL_API_GET_CAPABILITIES',
|
||||
'08' => 'SERIAL_API_SOFT_RESET',
|
||||
'10' => 'ZW_SET_R_F_RECEIVE_MODE',
|
||||
'11' => 'ZW_SET_SLEEP_MODE',
|
||||
'12' => 'ZW_SEND_NODE_INFORMATION',
|
||||
'13' => 'ZW_SEND_DATA',
|
||||
'14' => 'ZW_SEND_DATA_MULTI',
|
||||
'15' => 'ZW_GET_VERSION',
|
||||
'16' => 'ZW_SEND_DATA_ABORT',
|
||||
'17' => 'ZW_R_F_POWER_LEVEL_SET',
|
||||
'18' => 'ZW_SEND_DATA_META',
|
||||
'1c' => 'ZW_GET_RANDOM',
|
||||
'20' => 'MEMORY_GET_ID',
|
||||
'21' => 'MEMORY_GET_BYTE',
|
||||
'22' => 'MEMORY_PUT_BYTE',
|
||||
'23' => 'MEMORY_GET_BUFFER',
|
||||
'24' => 'MEMORY_PUT_BUFFER',
|
||||
'27' => 'FLASH_AUTO_PROG_SET',
|
||||
'29' => 'NVM_GET_ID',
|
||||
'2a' => 'NVM_EXT_READ_LONG_BUFFER',
|
||||
'2b' => 'NVM_EXT_WRITE_LONG_BUFFER',
|
||||
'2c' => 'NVM_EXT_READ_LONG_BYTE',
|
||||
'2d' => 'NVM_EXT_WRITE_LONG_BYTE',
|
||||
'30' => 'CLOCK_SET',
|
||||
'31' => 'CLOCK_GET',
|
||||
'32' => 'CLOCK_COMPARE',
|
||||
'33' => 'RTC_TIMER_CREATE',
|
||||
'34' => 'RTC_TIMER_READ',
|
||||
'35' => 'RTC_TIMER_DELETE',
|
||||
'36' => 'RTC_TIMER_CALL',
|
||||
'40' => 'ZW_SET_LEARN_NODE_STATE',
|
||||
'41' => 'ZW_GET_NODE_PROTOCOL_INFO',
|
||||
'42' => 'ZW_SET_DEFAULT',
|
||||
'43' => 'ZW_NEW_CONTROLLER',
|
||||
'44' => 'ZW_REPLICATION_COMMAND_COMPLETE',
|
||||
'45' => 'ZW_REPLICATION_SEND_DATA',
|
||||
'46' => 'ZW_ASSIGN_RETURN_ROUTE',
|
||||
'47' => 'ZW_DELETE_RETURN_ROUTE',
|
||||
'48' => 'ZW_REQUEST_NODE_NEIGHBOR_UPDATE',
|
||||
'49' => 'ZW_APPLICATION_UPDATE',
|
||||
'4a' => 'ZW_ADD_NODE_TO_NETWORK',
|
||||
'4b' => 'ZW_REMOVE_NODE_FROM_NETWORK',
|
||||
'4c' => 'ZW_CREATE_NEW_PRIMARY',
|
||||
'4d' => 'ZW_CONTROLLER_CHANGE',
|
||||
'50' => 'ZW_SET_LEARN_MODE',
|
||||
'51' => 'ZW_ASSIGN_SUC_RETURN_ROUTE',
|
||||
'52' => 'ZW_ENABLE_SUC',
|
||||
'53' => 'ZW_REQUEST_NETWORK_UPDATE',
|
||||
'54' => 'ZW_SET_SUC_NODE_ID',
|
||||
'55' => 'ZW_DELETE_SUC_RETURN_ROUTE',
|
||||
'56' => 'ZW_GET_SUC_NODE_ID',
|
||||
'57' => 'ZW_SEND_SUC_ID',
|
||||
'59' => 'ZW_REDISCOVERY_NEEDED',
|
||||
'5e' => 'ZW_EXPLORE_REQUEST_INCLUSION',
|
||||
'60' => 'ZW_REQUEST_NODE_INFO',
|
||||
'61' => 'ZW_REMOVE_FAILED_NODE_ID',
|
||||
'62' => 'ZW_IS_FAILED_NODE',
|
||||
'63' => 'ZW_REPLACE_FAILED_NODE',
|
||||
'70' => 'TIMER_START',
|
||||
'71' => 'TIMER_RESTART',
|
||||
'72' => 'TIMER_CANCEL',
|
||||
'73' => 'TIMER_CALL',
|
||||
'80' => 'GET_ROUTING_TABLE_LINE',
|
||||
'81' => 'GET_T_X_COUNTER',
|
||||
'82' => 'RESET_T_X_COUNTER',
|
||||
'83' => 'STORE_NODE_INFO',
|
||||
'84' => 'STORE_HOME_ID',
|
||||
'90' => 'LOCK_ROUTE_RESPONSE',
|
||||
'91' => 'ZW_SEND_DATA_ROUTE_DEMO',
|
||||
'95' => 'SERIAL_API_TEST',
|
||||
'a0' => 'SERIAL_API_SLAVE_NODE_INFO',
|
||||
'a1' => 'APPLICATION_SLAVE_COMMAND_HANDLER',
|
||||
'a2' => 'ZW_SEND_SLAVE_NODE_INFO',
|
||||
'a3' => 'ZW_SEND_SLAVE_DATA',
|
||||
'a4' => 'ZW_SET_SLAVE_LEARN_MODE',
|
||||
'a5' => 'ZW_GET_VIRTUAL_NODES',
|
||||
'a6' => 'ZW_IS_VIRTUAL_NODE',
|
||||
'b6' => 'ZW_WATCHDOG_ENABLE',
|
||||
'b7' => 'ZW_WATCHDOG_DISABLE',
|
||||
'b8' => 'ZW_WATCHDOG_CHECK',
|
||||
'b9' => 'ZW_SET_EXT_INT_LEVEL',
|
||||
'ba' => 'ZW_RF_POWERLEVEL_GET',
|
||||
'bb' => 'ZW_GET_NEIGHBOR_COUNT',
|
||||
'bc' => 'ZW_ARE_NODES_NEIGHBOURS',
|
||||
'bd' => 'ZW_TYPE_LIBRARY',
|
||||
'be' => 'ZW_SEND_TEST_FRAME',
|
||||
'bf' => 'ZW_GET_PROTOCOL_STATUS',
|
||||
'd0' => 'ZW_SET_PROMISCUOUS_MODE',
|
||||
'd2' => 'WATCHDOG_START',
|
||||
'd3' => 'WATCHDOG_STOP',
|
||||
'f2' => 'ZME_FREQ_CHANGE',
|
||||
'f4' => 'ZME_BOOTLOADER_FLASH',
|
||||
);
|
||||
|
||||
%zw_type6 = (
|
||||
'01' => 'GENERIC_CONTROLLER', '12' => 'SWITCH_REMOTE',
|
||||
'02' => 'STATIC_CONTROLLER', '13' => 'SWITCH_TOGGLE',
|
||||
'03' => 'AV_CONTROL_POINT', '20' => 'SENSOR_BINARY',
|
||||
'06' => 'DISPLAY', '21' => 'SENSOR_MULTILEVEL',
|
||||
'07' => 'GARAGE_DOOR', '22' => 'WATER_CONTROL',
|
||||
'08' => 'THERMOSTAT', '30' => 'METER_PULSE',
|
||||
'09' => 'WINDOW_COVERING', '40' => 'ENTRY_CONTROL',
|
||||
'0F' => 'REPEATER_SLAVE', '50' => 'SEMI_INTEROPERABLE',
|
||||
'10' => 'SWITCH_BINARY', 'ff' => 'NON_INTEROPERABLE',
|
||||
'11' => 'SWITCH_MULTILEVEL',
|
||||
);
|
||||
|
||||
sub
|
||||
ZWDongle_Initialize($)
|
||||
{
|
||||
@ -314,7 +192,7 @@ ZWDongle_Set($@)
|
||||
my $nargs = int(@ca)-1;
|
||||
return "set $name $type needs $nargs arguments" if($nargs != int(@a));
|
||||
|
||||
ZWDongle_Write($hash, "00", sprintf($cmd, @a));
|
||||
ZWDongle_Write($hash, "", "00".sprintf($cmd, @a));
|
||||
return undef;
|
||||
}
|
||||
|
||||
@ -357,7 +235,7 @@ ZWDongle_Get($@)
|
||||
return "No $cmd for dummies" if(IsDummy($name));
|
||||
|
||||
my $out = sprintf($gets{$cmd}, @a);
|
||||
ZWDongle_Write($hash, "00", $out);
|
||||
ZWDongle_Write($hash, "", "00".$out);
|
||||
my $re = "^01".substr($out,0,2); # Start with <01><len><01><CMD>
|
||||
my ($err, $ret) = ZWDongle_ReadAnswer($hash, $cmd, $re);
|
||||
return $err if($err);
|
||||
@ -449,7 +327,7 @@ ZWDongle_Get($@)
|
||||
|
||||
} elsif($cmd eq "neighborList") { ############################
|
||||
$msg =~ s/^....//;
|
||||
$msg = ZWDongle_parseNeighborList($hash, $msg);
|
||||
$msg = zwlib_parseNeighborList($hash, $msg);
|
||||
|
||||
}
|
||||
|
||||
@ -499,29 +377,17 @@ ZWDongle_DoInit($)
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
ZWDongle_CheckSum($)
|
||||
{
|
||||
my ($data) = @_;
|
||||
my $cs = 0xff;
|
||||
map { $cs ^= ord($_) } split("", pack('H*', $data));
|
||||
return sprintf("%02x", $cs);
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub
|
||||
ZWDongle_Write($$$)
|
||||
{
|
||||
my ($hash,$fn,$msg) = @_;
|
||||
|
||||
Log3 $hash, 5, "ZWDongle_Write $fn $msg";
|
||||
Log3 $hash, 5, "ZWDongle_Write $msg ($fn)";
|
||||
# assemble complete message
|
||||
$msg = "$fn$msg";
|
||||
$msg = sprintf("%02x%s", length($msg)/2+1, $msg);
|
||||
|
||||
$msg = "01$msg" . ZWDongle_CheckSum($msg);
|
||||
$msg = "01$msg" . zwlib_checkSum($msg);
|
||||
push @{$hash->{SendStack}}, $msg;
|
||||
|
||||
ZWDongle_ProcessSendStack($hash);
|
||||
@ -667,7 +533,7 @@ ZWDongle_Read($@)
|
||||
my $rcs = substr($data, $l+2, 2); # Received Checksum
|
||||
$data = substr($data, $l+4);
|
||||
|
||||
my $ccs = ZWDongle_CheckSum("$len$msg"); # Computed Checksum
|
||||
my $ccs = zwlib_checkSum("$len$msg"); # Computed Checksum
|
||||
if($rcs ne $ccs) {
|
||||
Log3 $name, 1,
|
||||
"$name: wrong checksum: received $rcs, computed $ccs for $len$msg";
|
||||
@ -835,32 +701,6 @@ ZWDongle_Ready($)
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub
|
||||
ZWDongle_parseNeighborList($$)
|
||||
{
|
||||
my ($iodev, $data) = @_;
|
||||
my $homeId = $iodev->{homeId};
|
||||
my $ioName = $iodev->{NAME};
|
||||
my @r = map { ord($_) } split("", pack('H*', $data));
|
||||
return "Bogus neightborList $data" if(int(@r) != 29);
|
||||
|
||||
my @list;
|
||||
my $ioId = ReadingsVal($ioName, "homeId", "");
|
||||
$ioId = $1 if($ioId =~ m/CtrlNodeId:(..)/);
|
||||
for my $byte (0..28) {
|
||||
my $bits = $r[$byte];
|
||||
for my $bit (0..7) {
|
||||
if($bits & (1<<$bit)) {
|
||||
my $dec = $byte*8+$bit+1;
|
||||
my $hex = sprintf("%02x", $dec);
|
||||
my $h = $modules{ZWave}{defptr}{"$homeId $hex"};
|
||||
push @list, ($hex eq $ioId ? $ioName :
|
||||
($h ? $h->{NAME} : "UNKNOWN_$dec"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return @list ? join(" ", @list) : "empty";
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@ -979,10 +819,10 @@ ZWDongle_parseNeighborList($$)
|
||||
<li><a href="#do_not_notify">do_not_notify</a></li>
|
||||
<li><a href="#model">model</a></li>
|
||||
<li><a href="#disable">disable</a></li>
|
||||
<li><a href="#homeId">homeId</a><br>
|
||||
<li><a name="#homeId">homeId</a><br>
|
||||
Stores the homeId of the dongle. Is a workaround for some buggy dongles,
|
||||
wich sometimes report a wrong/nonexisten homeId (Forum #35126)</li>
|
||||
<li><a href="#networkKey">networkKey</a><br>
|
||||
<li><a name="#networkKey">networkKey</a><br>
|
||||
Needed for secure inclusion, hex string with length of 32
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -9,6 +9,7 @@ use SetExtensions;
|
||||
use Compress::Zlib;
|
||||
use Time::HiRes qw( gettimeofday );
|
||||
use HttpUtils;
|
||||
use ZWLib;
|
||||
|
||||
sub ZWave_Cmd($$@);
|
||||
sub ZWave_Get($@);
|
||||
@ -20,11 +21,9 @@ sub ZWave_secStart($);
|
||||
sub ZWave_secEnd($);
|
||||
sub ZWave_configParseModel($;$);
|
||||
|
||||
use vars qw(%zw_func_id);
|
||||
use vars qw(%zw_type6);
|
||||
use vars qw($FW_ME $FW_tp $FW_ss);
|
||||
use vars qw(%zwave_id2class);
|
||||
|
||||
my %zwave_id2class;
|
||||
my %zwave_class = (
|
||||
NO_OPERATION => { id => '00' },
|
||||
BASIC => { id => '20',
|
||||
@ -525,7 +524,7 @@ ZWave_Define($$)
|
||||
$modules{ZWave}{defptr}{"$homeId $id"} = $hash;
|
||||
my $proposed;
|
||||
if($init_done) { # Use the right device while inclusion is running
|
||||
for my $p (devspec2array("TYPE=ZWDongle|FHEM2FHEM")) {
|
||||
for my $p (devspec2array("TYPE=ZWDongle|ZWCUL|FHEM2FHEM")) {
|
||||
$proposed = $p if($defs{$p}{homeId} && $defs{$p}{homeId} eq $homeId);
|
||||
}
|
||||
}
|
||||
@ -2597,7 +2596,7 @@ ZWave_wakeupTimer($$)
|
||||
my $nodeId = $hash->{nodeIdHex};
|
||||
my $cmdEf = (AttrVal($hash->{NAME},"noExplorerFrames",0)==0 ? "25":"05");
|
||||
# wakeupNoMoreInformation
|
||||
IOWrite($hash, "00", "13${nodeId}028408${cmdEf}$nodeId");
|
||||
IOWrite($hash, $hash->{homeId}, "0013${nodeId}028408${cmdEf}$nodeId");
|
||||
}
|
||||
delete $hash->{wakeupAlive};
|
||||
|
||||
@ -2634,7 +2633,7 @@ ZWave_processSendStack($)
|
||||
return;
|
||||
}
|
||||
|
||||
IOWrite($hash, "00", $ss->[0]);
|
||||
IOWrite($hash, $hash->{homeId}, "00".$ss->[0]);
|
||||
$ss->[0] = "sent:".$ss->[0];
|
||||
|
||||
$hash->{lastMsgSent} = gettimeofday();
|
||||
@ -2702,7 +2701,7 @@ ZWave_Parse($$@)
|
||||
my $hash = $modules{ZWave}{defptr}{"$homeId $id"};
|
||||
my $name = ($hash ? $hash->{NAME} : "unknown");
|
||||
|
||||
$msg = ZWDongle_parseNeighborList($iodev, $data);
|
||||
$msg = zwlib_parseNeighborList($iodev, $data);
|
||||
|
||||
readingsSingleUpdate($hash, "neighborList", $msg, 1) if($hash);
|
||||
return $msg if($srcCmd);
|
||||
|
161
fhem/FHEM/ZWLib.pm
Normal file
161
fhem/FHEM/ZWLib.pm
Normal file
@ -0,0 +1,161 @@
|
||||
##############################################
|
||||
# $Id$
|
||||
package main;
|
||||
|
||||
# Known controller function.
|
||||
# Note: Known != implemented, see %sets & %gets for the implemented ones.
|
||||
use vars qw(%zw_func_id);
|
||||
use vars qw(%zw_type6);
|
||||
|
||||
%zw_func_id= (
|
||||
'02' => 'SERIAL_API_GET_INIT_DATA',
|
||||
'03' => 'SERIAL_API_APPL_NODE_INFORMATION',
|
||||
'04' => 'APPLICATION_COMMAND_HANDLER',
|
||||
'05' => 'ZW_GET_CONTROLLER_CAPABILITIES',
|
||||
'06' => 'SERIAL_API_SET_TIMEOUTS',
|
||||
'07' => 'SERIAL_API_GET_CAPABILITIES',
|
||||
'08' => 'SERIAL_API_SOFT_RESET',
|
||||
'10' => 'ZW_SET_R_F_RECEIVE_MODE',
|
||||
'11' => 'ZW_SET_SLEEP_MODE',
|
||||
'12' => 'ZW_SEND_NODE_INFORMATION',
|
||||
'13' => 'ZW_SEND_DATA',
|
||||
'14' => 'ZW_SEND_DATA_MULTI',
|
||||
'15' => 'ZW_GET_VERSION',
|
||||
'16' => 'ZW_SEND_DATA_ABORT',
|
||||
'17' => 'ZW_R_F_POWER_LEVEL_SET',
|
||||
'18' => 'ZW_SEND_DATA_META',
|
||||
'1c' => 'ZW_GET_RANDOM',
|
||||
'20' => 'MEMORY_GET_ID',
|
||||
'21' => 'MEMORY_GET_BYTE',
|
||||
'22' => 'MEMORY_PUT_BYTE',
|
||||
'23' => 'MEMORY_GET_BUFFER',
|
||||
'24' => 'MEMORY_PUT_BUFFER',
|
||||
'27' => 'FLASH_AUTO_PROG_SET',
|
||||
'29' => 'NVM_GET_ID',
|
||||
'2a' => 'NVM_EXT_READ_LONG_BUFFER',
|
||||
'2b' => 'NVM_EXT_WRITE_LONG_BUFFER',
|
||||
'2c' => 'NVM_EXT_READ_LONG_BYTE',
|
||||
'2d' => 'NVM_EXT_WRITE_LONG_BYTE',
|
||||
'30' => 'CLOCK_SET',
|
||||
'31' => 'CLOCK_GET',
|
||||
'32' => 'CLOCK_COMPARE',
|
||||
'33' => 'RTC_TIMER_CREATE',
|
||||
'34' => 'RTC_TIMER_READ',
|
||||
'35' => 'RTC_TIMER_DELETE',
|
||||
'36' => 'RTC_TIMER_CALL',
|
||||
'40' => 'ZW_SET_LEARN_NODE_STATE',
|
||||
'41' => 'ZW_GET_NODE_PROTOCOL_INFO',
|
||||
'42' => 'ZW_SET_DEFAULT',
|
||||
'43' => 'ZW_NEW_CONTROLLER',
|
||||
'44' => 'ZW_REPLICATION_COMMAND_COMPLETE',
|
||||
'45' => 'ZW_REPLICATION_SEND_DATA',
|
||||
'46' => 'ZW_ASSIGN_RETURN_ROUTE',
|
||||
'47' => 'ZW_DELETE_RETURN_ROUTE',
|
||||
'48' => 'ZW_REQUEST_NODE_NEIGHBOR_UPDATE',
|
||||
'49' => 'ZW_APPLICATION_UPDATE',
|
||||
'4a' => 'ZW_ADD_NODE_TO_NETWORK',
|
||||
'4b' => 'ZW_REMOVE_NODE_FROM_NETWORK',
|
||||
'4c' => 'ZW_CREATE_NEW_PRIMARY',
|
||||
'4d' => 'ZW_CONTROLLER_CHANGE',
|
||||
'50' => 'ZW_SET_LEARN_MODE',
|
||||
'51' => 'ZW_ASSIGN_SUC_RETURN_ROUTE',
|
||||
'52' => 'ZW_ENABLE_SUC',
|
||||
'53' => 'ZW_REQUEST_NETWORK_UPDATE',
|
||||
'54' => 'ZW_SET_SUC_NODE_ID',
|
||||
'55' => 'ZW_DELETE_SUC_RETURN_ROUTE',
|
||||
'56' => 'ZW_GET_SUC_NODE_ID',
|
||||
'57' => 'ZW_SEND_SUC_ID',
|
||||
'59' => 'ZW_REDISCOVERY_NEEDED',
|
||||
'5e' => 'ZW_EXPLORE_REQUEST_INCLUSION',
|
||||
'60' => 'ZW_REQUEST_NODE_INFO',
|
||||
'61' => 'ZW_REMOVE_FAILED_NODE_ID',
|
||||
'62' => 'ZW_IS_FAILED_NODE',
|
||||
'63' => 'ZW_REPLACE_FAILED_NODE',
|
||||
'70' => 'TIMER_START',
|
||||
'71' => 'TIMER_RESTART',
|
||||
'72' => 'TIMER_CANCEL',
|
||||
'73' => 'TIMER_CALL',
|
||||
'80' => 'GET_ROUTING_TABLE_LINE',
|
||||
'81' => 'GET_T_X_COUNTER',
|
||||
'82' => 'RESET_T_X_COUNTER',
|
||||
'83' => 'STORE_NODE_INFO',
|
||||
'84' => 'STORE_HOME_ID',
|
||||
'90' => 'LOCK_ROUTE_RESPONSE',
|
||||
'91' => 'ZW_SEND_DATA_ROUTE_DEMO',
|
||||
'95' => 'SERIAL_API_TEST',
|
||||
'a0' => 'SERIAL_API_SLAVE_NODE_INFO',
|
||||
'a1' => 'APPLICATION_SLAVE_COMMAND_HANDLER',
|
||||
'a2' => 'ZW_SEND_SLAVE_NODE_INFO',
|
||||
'a3' => 'ZW_SEND_SLAVE_DATA',
|
||||
'a4' => 'ZW_SET_SLAVE_LEARN_MODE',
|
||||
'a5' => 'ZW_GET_VIRTUAL_NODES',
|
||||
'a6' => 'ZW_IS_VIRTUAL_NODE',
|
||||
'b6' => 'ZW_WATCHDOG_ENABLE',
|
||||
'b7' => 'ZW_WATCHDOG_DISABLE',
|
||||
'b8' => 'ZW_WATCHDOG_CHECK',
|
||||
'b9' => 'ZW_SET_EXT_INT_LEVEL',
|
||||
'ba' => 'ZW_RF_POWERLEVEL_GET',
|
||||
'bb' => 'ZW_GET_NEIGHBOR_COUNT',
|
||||
'bc' => 'ZW_ARE_NODES_NEIGHBOURS',
|
||||
'bd' => 'ZW_TYPE_LIBRARY',
|
||||
'be' => 'ZW_SEND_TEST_FRAME',
|
||||
'bf' => 'ZW_GET_PROTOCOL_STATUS',
|
||||
'd0' => 'ZW_SET_PROMISCUOUS_MODE',
|
||||
'd2' => 'WATCHDOG_START',
|
||||
'd3' => 'WATCHDOG_STOP',
|
||||
'f2' => 'ZME_FREQ_CHANGE',
|
||||
'f4' => 'ZME_BOOTLOADER_FLASH',
|
||||
);
|
||||
|
||||
%zw_type6 = (
|
||||
'01' => 'GENERIC_CONTROLLER', '12' => 'SWITCH_REMOTE',
|
||||
'02' => 'STATIC_CONTROLLER', '13' => 'SWITCH_TOGGLE',
|
||||
'03' => 'AV_CONTROL_POINT', '20' => 'SENSOR_BINARY',
|
||||
'06' => 'DISPLAY', '21' => 'SENSOR_MULTILEVEL',
|
||||
'07' => 'GARAGE_DOOR', '22' => 'WATER_CONTROL',
|
||||
'08' => 'THERMOSTAT', '30' => 'METER_PULSE',
|
||||
'09' => 'WINDOW_COVERING', '40' => 'ENTRY_CONTROL',
|
||||
'0F' => 'REPEATER_SLAVE', '50' => 'SEMI_INTEROPERABLE',
|
||||
'10' => 'SWITCH_BINARY', 'ff' => 'NON_INTEROPERABLE',
|
||||
'11' => 'SWITCH_MULTILEVEL',
|
||||
);
|
||||
|
||||
sub
|
||||
zwlib_parseNeighborList($$)
|
||||
{
|
||||
my ($iodev, $data) = @_;
|
||||
my $homeId = $iodev->{homeId};
|
||||
my $ioName = $iodev->{NAME};
|
||||
my @r = map { ord($_) } split("", pack('H*', $data));
|
||||
return "Bogus neightborList $data" if(int(@r) != 29);
|
||||
|
||||
my @list;
|
||||
my $ioId = ReadingsVal($ioName, "homeId", "");
|
||||
$ioId = $1 if($ioId =~ m/CtrlNodeId:(..)/);
|
||||
for my $byte (0..28) {
|
||||
my $bits = $r[$byte];
|
||||
for my $bit (0..7) {
|
||||
if($bits & (1<<$bit)) {
|
||||
my $dec = $byte*8+$bit+1;
|
||||
my $hex = sprintf("%02x", $dec);
|
||||
my $h = $modules{ZWave}{defptr}{"$homeId $hex"};
|
||||
push @list, ($hex eq $ioId ? $ioName :
|
||||
($h ? $h->{NAME} : "UNKNOWN_$dec"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return @list ? join(" ", @list) : "empty";
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub
|
||||
zwlib_checkSum($)
|
||||
{
|
||||
my ($data) = @_;
|
||||
my $cs = 0xff;
|
||||
map { $cs ^= ord($_) } split("", pack('H*', $data));
|
||||
return sprintf("%02x", $cs);
|
||||
}
|
||||
|
||||
|
||||
1;
|
@ -1575,9 +1575,9 @@ CommandSet($$)
|
||||
foreach my $sdev (devspec2array($a[0], $cl)) {
|
||||
|
||||
$a[0] = $sdev;
|
||||
$defs{$sdev}->{CL} = $cl;
|
||||
$defs{$sdev}->{CL} = $cl if($defs{$sdev});
|
||||
my $ret = DoSet(@a);
|
||||
delete $defs{$sdev}->{CL};
|
||||
delete $defs{$sdev}->{CL} if($defs{$sdev});
|
||||
push @rets, $ret if($ret);
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user