mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-30 18:12:28 +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$
|
# $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;
|
package main;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use Time::HiRes qw(gettimeofday);
|
use Time::HiRes qw(gettimeofday);
|
||||||
|
use ZWLib;
|
||||||
|
|
||||||
sub ZWDongle_Parse($$$);
|
sub ZWDongle_Parse($$$);
|
||||||
sub ZWDongle_Read($@);
|
sub ZWDongle_Read($@);
|
||||||
@ -55,124 +51,6 @@ my %gets = (
|
|||||||
"raw" => "%s", # hex
|
"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
|
sub
|
||||||
ZWDongle_Initialize($)
|
ZWDongle_Initialize($)
|
||||||
{
|
{
|
||||||
@ -314,7 +192,7 @@ ZWDongle_Set($@)
|
|||||||
my $nargs = int(@ca)-1;
|
my $nargs = int(@ca)-1;
|
||||||
return "set $name $type needs $nargs arguments" if($nargs != int(@a));
|
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;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,7 +235,7 @@ ZWDongle_Get($@)
|
|||||||
return "No $cmd for dummies" if(IsDummy($name));
|
return "No $cmd for dummies" if(IsDummy($name));
|
||||||
|
|
||||||
my $out = sprintf($gets{$cmd}, @a);
|
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 $re = "^01".substr($out,0,2); # Start with <01><len><01><CMD>
|
||||||
my ($err, $ret) = ZWDongle_ReadAnswer($hash, $cmd, $re);
|
my ($err, $ret) = ZWDongle_ReadAnswer($hash, $cmd, $re);
|
||||||
return $err if($err);
|
return $err if($err);
|
||||||
@ -449,7 +327,7 @@ ZWDongle_Get($@)
|
|||||||
|
|
||||||
} elsif($cmd eq "neighborList") { ############################
|
} elsif($cmd eq "neighborList") { ############################
|
||||||
$msg =~ s/^....//;
|
$msg =~ s/^....//;
|
||||||
$msg = ZWDongle_parseNeighborList($hash, $msg);
|
$msg = zwlib_parseNeighborList($hash, $msg);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,29 +377,17 @@ ZWDongle_DoInit($)
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
#####################################
|
|
||||||
sub
|
|
||||||
ZWDongle_CheckSum($)
|
|
||||||
{
|
|
||||||
my ($data) = @_;
|
|
||||||
my $cs = 0xff;
|
|
||||||
map { $cs ^= ord($_) } split("", pack('H*', $data));
|
|
||||||
return sprintf("%02x", $cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
sub
|
sub
|
||||||
ZWDongle_Write($$$)
|
ZWDongle_Write($$$)
|
||||||
{
|
{
|
||||||
my ($hash,$fn,$msg) = @_;
|
my ($hash,$fn,$msg) = @_;
|
||||||
|
|
||||||
Log3 $hash, 5, "ZWDongle_Write $fn $msg";
|
Log3 $hash, 5, "ZWDongle_Write $msg ($fn)";
|
||||||
# assemble complete message
|
# assemble complete message
|
||||||
$msg = "$fn$msg";
|
|
||||||
$msg = sprintf("%02x%s", length($msg)/2+1, $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;
|
push @{$hash->{SendStack}}, $msg;
|
||||||
|
|
||||||
ZWDongle_ProcessSendStack($hash);
|
ZWDongle_ProcessSendStack($hash);
|
||||||
@ -667,7 +533,7 @@ ZWDongle_Read($@)
|
|||||||
my $rcs = substr($data, $l+2, 2); # Received Checksum
|
my $rcs = substr($data, $l+2, 2); # Received Checksum
|
||||||
$data = substr($data, $l+4);
|
$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) {
|
if($rcs ne $ccs) {
|
||||||
Log3 $name, 1,
|
Log3 $name, 1,
|
||||||
"$name: wrong checksum: received $rcs, computed $ccs for $len$msg";
|
"$name: wrong checksum: received $rcs, computed $ccs for $len$msg";
|
||||||
@ -835,32 +701,6 @@ ZWDongle_Ready($)
|
|||||||
return 0;
|
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;
|
1;
|
||||||
|
|
||||||
@ -979,10 +819,10 @@ ZWDongle_parseNeighborList($$)
|
|||||||
<li><a href="#do_not_notify">do_not_notify</a></li>
|
<li><a href="#do_not_notify">do_not_notify</a></li>
|
||||||
<li><a href="#model">model</a></li>
|
<li><a href="#model">model</a></li>
|
||||||
<li><a href="#disable">disable</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,
|
Stores the homeId of the dongle. Is a workaround for some buggy dongles,
|
||||||
wich sometimes report a wrong/nonexisten homeId (Forum #35126)</li>
|
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
|
Needed for secure inclusion, hex string with length of 32
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -9,6 +9,7 @@ use SetExtensions;
|
|||||||
use Compress::Zlib;
|
use Compress::Zlib;
|
||||||
use Time::HiRes qw( gettimeofday );
|
use Time::HiRes qw( gettimeofday );
|
||||||
use HttpUtils;
|
use HttpUtils;
|
||||||
|
use ZWLib;
|
||||||
|
|
||||||
sub ZWave_Cmd($$@);
|
sub ZWave_Cmd($$@);
|
||||||
sub ZWave_Get($@);
|
sub ZWave_Get($@);
|
||||||
@ -20,11 +21,9 @@ sub ZWave_secStart($);
|
|||||||
sub ZWave_secEnd($);
|
sub ZWave_secEnd($);
|
||||||
sub ZWave_configParseModel($;$);
|
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($FW_ME $FW_tp $FW_ss);
|
||||||
|
use vars qw(%zwave_id2class);
|
||||||
|
|
||||||
my %zwave_id2class;
|
|
||||||
my %zwave_class = (
|
my %zwave_class = (
|
||||||
NO_OPERATION => { id => '00' },
|
NO_OPERATION => { id => '00' },
|
||||||
BASIC => { id => '20',
|
BASIC => { id => '20',
|
||||||
@ -525,7 +524,7 @@ ZWave_Define($$)
|
|||||||
$modules{ZWave}{defptr}{"$homeId $id"} = $hash;
|
$modules{ZWave}{defptr}{"$homeId $id"} = $hash;
|
||||||
my $proposed;
|
my $proposed;
|
||||||
if($init_done) { # Use the right device while inclusion is running
|
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);
|
$proposed = $p if($defs{$p}{homeId} && $defs{$p}{homeId} eq $homeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2597,7 +2596,7 @@ ZWave_wakeupTimer($$)
|
|||||||
my $nodeId = $hash->{nodeIdHex};
|
my $nodeId = $hash->{nodeIdHex};
|
||||||
my $cmdEf = (AttrVal($hash->{NAME},"noExplorerFrames",0)==0 ? "25":"05");
|
my $cmdEf = (AttrVal($hash->{NAME},"noExplorerFrames",0)==0 ? "25":"05");
|
||||||
# wakeupNoMoreInformation
|
# wakeupNoMoreInformation
|
||||||
IOWrite($hash, "00", "13${nodeId}028408${cmdEf}$nodeId");
|
IOWrite($hash, $hash->{homeId}, "0013${nodeId}028408${cmdEf}$nodeId");
|
||||||
}
|
}
|
||||||
delete $hash->{wakeupAlive};
|
delete $hash->{wakeupAlive};
|
||||||
|
|
||||||
@ -2634,7 +2633,7 @@ ZWave_processSendStack($)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IOWrite($hash, "00", $ss->[0]);
|
IOWrite($hash, $hash->{homeId}, "00".$ss->[0]);
|
||||||
$ss->[0] = "sent:".$ss->[0];
|
$ss->[0] = "sent:".$ss->[0];
|
||||||
|
|
||||||
$hash->{lastMsgSent} = gettimeofday();
|
$hash->{lastMsgSent} = gettimeofday();
|
||||||
@ -2702,7 +2701,7 @@ ZWave_Parse($$@)
|
|||||||
my $hash = $modules{ZWave}{defptr}{"$homeId $id"};
|
my $hash = $modules{ZWave}{defptr}{"$homeId $id"};
|
||||||
my $name = ($hash ? $hash->{NAME} : "unknown");
|
my $name = ($hash ? $hash->{NAME} : "unknown");
|
||||||
|
|
||||||
$msg = ZWDongle_parseNeighborList($iodev, $data);
|
$msg = zwlib_parseNeighborList($iodev, $data);
|
||||||
|
|
||||||
readingsSingleUpdate($hash, "neighborList", $msg, 1) if($hash);
|
readingsSingleUpdate($hash, "neighborList", $msg, 1) if($hash);
|
||||||
return $msg if($srcCmd);
|
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)) {
|
foreach my $sdev (devspec2array($a[0], $cl)) {
|
||||||
|
|
||||||
$a[0] = $sdev;
|
$a[0] = $sdev;
|
||||||
$defs{$sdev}->{CL} = $cl;
|
$defs{$sdev}->{CL} = $cl if($defs{$sdev});
|
||||||
my $ret = DoSet(@a);
|
my $ret = DoSet(@a);
|
||||||
delete $defs{$sdev}->{CL};
|
delete $defs{$sdev}->{CL} if($defs{$sdev});
|
||||||
push @rets, $ret if($ret);
|
push @rets, $ret if($ret);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user