2
0
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:
rudolfkoenig 2015-12-06 19:44:51 +00:00
parent ef64fa1017
commit 87d9bc4372
5 changed files with 673 additions and 178 deletions

495
fhem/FHEM/00_ZWCUL.pm Executable file
View 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 &lt;name&gt; ZWCUL &lt;device&gt; &lt;homeId&gt;
&lt;ctrlId&gt;</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

View File

@ -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>

View File

@ -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
View 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;

View File

@ -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);
}