2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-02-28 15:04:51 +00:00

10_ZWave.pm: add openzwave deviceconfig xmls

git-svn-id: https://svn.fhem.de/fhem/trunk@6691 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
rudolfkoenig 2014-10-05 17:28:09 +00:00
parent 659f1a10cf
commit d0f5cb7d2f
3 changed files with 236 additions and 16 deletions

@ -6,6 +6,7 @@ package main;
use strict;
use warnings;
use SetExtensions;
use Compress::Zlib;
sub ZWave_Parse($$@);
sub ZWave_Set($@);
@ -191,8 +192,9 @@ my %zwave_class = (
parse => { "..7105(..)(..)" => '"alarm_type_$1:level $2"',}, },
MANUFACTURER_SPECIFIC => { id => '72',
get => { model => "04", },
parse => { "087205(....)(....)(....)" => 'ZWave_mfsParse($1,$2,$3)',
"087205(....)(....)(.{4})" => '"modelId:$1-$2-$3"', }},
parse => { "087205(....)(....)(....)" => 'ZWave_mfsParse($1,$2,$3,0)',
"087205(....)(....)(.{4})" => 'ZWave_mfsParse($1,$2,$3,1)',
"087205(....)(.{4})(.{4})" => '"modelId:$1-$2-$3"', }},
POWERLEVEL => { id => '73', },
PROTECTION => { id => '75',
set => { protectionOff => "0100",
@ -250,7 +252,7 @@ my %zwave_class = (
MULTI_CHANNEL_ASSOCIATION=> { id => '8e', }, # aka MULTI_INSTANCE_ASSOCIATION
MULTI_CMD => { id => '8f', }, # Handled in Parse
ENERGY_PRODUCTION => { id => '90', },
MANUFACTURER_PROPRIETARY => { id => '91', }, # see manuf_proprietary below
MANUFACTURER_PROPRIETARY => { id => '91', }, # see zwave_manuf_proprietary
SCREEN_MD => { id => '92', },
SCREEN_ATTRIBUTES => { id => '93', },
SIMPLE_AV_CONTROL => { id => '94', },
@ -273,12 +275,20 @@ my %zwave_class = (
);
my %zwave_cmdArgs = (
dim => "slider,0,1,99",
indicatorDim => "slider,0,1,99",
set => {
dim => "slider,0,1,99",
indicatorDim => "slider,0,1,99",
},
get => {
},
parse => {
}
);
my %modelIdAlias = ( "010f-0301-1001" => "Fibaro_FGRM222" );
my %manuf_proprietary = ( # MANUFACTURER_PROPRIETARY ist model dependent
use vars qw(%zwave_modelConfig);
#my %zwave_modelConfig;
my %zwave_modelIdAlias = ( "010f-0301-1001" => "Fibaro_FGRM222" );
my %zwave_manuf_proprietary = ( # MANUFACTURER_PROPRIETARY ist model dependent
Fibaro_FGRM222 => {
set => { positionSlat=>"010f26010100%02x",
positionBlinds=>"010f260102%02x00",},
@ -300,6 +310,8 @@ ZWave_Initialize($)
$hash->{AttrList} = "IODev do_not_notify:1,0 ".
"ignore:1,0 dummy:1,0 showtime:1,0 classes $readingFnAttributes";
map { $zwave_id2class{lc($zwave_class{$_}{id})} = $_ } keys %zwave_class;
$hash->{FW_detailFn} = "ZWave_fhemwebFn";
}
@ -355,7 +367,6 @@ sub
ZWave_Cmd($$@)
{
my ($type, $hash, @a) = @_;
my $ret = undef;
return "no $type argument specified" if(int(@a) < 2);
my $name = shift(@a);
my $cmd = shift(@a);
@ -379,8 +390,8 @@ ZWave_Cmd($$@)
if(!$cmdList{$cmd}) {
my @list;
foreach my $cmd (sort keys %cmdList) {
if($zwave_cmdArgs{$cmd}) {
push @list, "$cmd:$zwave_cmdArgs{$cmd}";
if($zwave_cmdArgs{$type}{$cmd}) {
push @list, "$cmd:$zwave_cmdArgs{$type}{$cmd}";
} elsif($cmdList{$cmd}{fmt} !~ m/%/) {
push @list, "$cmd:noArg";
} else {
@ -422,7 +433,14 @@ ZWave_Cmd($$@)
} else {
return "$type $cmd needs $parTxt" if($nArg != int(@a));
}
$cmdFmt = sprintf($cmdFmt, @a) if($nArg);
if($cmd =~ m/^config/) {
my ($err, $cmd) = ZWave_checkConfigParam($hash, $type, $cmd, $cmdFmt, @a);
return $err if($err);
$cmdFmt = $cmd;
} else {
$cmdFmt = sprintf($cmdFmt, @a) if($nArg);
}
my ($baseClasses, $baseHash) = ($classes, $hash);
if($id =~ m/(..)(..)/) { # Multi-Channel, encapsulate
@ -625,9 +643,9 @@ ZWave_mcCapability($$)
}
sub
ZWave_mfsParse($$$)
ZWave_mfsParse($$$$)
{
my ($mf, $prod, $id) = @_;
my ($mf, $prod, $id, $config) = @_;
my $xml = $attr{global}{modpath}.
"/FHEM/lib/openzwave_manufacturer_specific.xml";
($mf, $prod, $id) = (lc($mf), lc($prod), lc($id)); # Just to make it sure
@ -642,7 +660,12 @@ ZWave_mfsParse($$$)
if($l =~ m/<Product type="([^"]*)".*id="([^"]*)".*name="([^"]*)"/) {
if($mf eq $lastMf && $prod eq lc($1) && $id eq lc($2)) {
$ret = "model:$mName $3";
if($config) {
$ret = "modelConfig:$1" if($l =~ m/config="([^"]*)"/);
return $ret;
} else {
$ret = "model:$mName $3";
}
last;
}
}
@ -657,6 +680,112 @@ ZWave_mfsParse($$$)
return sprintf("model:0x%s 0x%s 0x%s", $mf, $prod, $id);
}
sub
ZWave_cleanString($)
{
my ($c) = @_;
$c =~ s/[.,].*$//g;
$c =~ s/[^A-Z]+(.)/uc($1)/gei;
$c =~ s/[^A-Z]//i;
while(length($c) > 32) { # might be endless loop
$c =~ s/[A-Z][^A-Z]*$//;
}
return $c;
}
# Poor mans XML-Parser
sub
ZWave_parseConfig($)
{
my ($cfg) = @_;
my $fn = $attr{global}{modpath}."/FHEM/lib/openzwave_deviceconfig.xml.gz";
my $gz = gzopen($fn, "rb");
if(!$gz) {
Log 3, "Can't open $fn: $!";
return;
}
my ($line, $class, %hash, $cmdName);
while($gz->gzreadline($line)) { # Search the "file" entry
last if($line =~ m/^<Product sourceFile="$cfg">$/);
}
while($gz->gzreadline($line)) {
last if($line =~ m+^</Product>+);
$class = $1 if($line =~ m/^<CommandClass.*id="([^"]*)"/);
next if(!$class || $class ne "112");
if($line =~ m/^<Value /) {
my %h;
$h{type} = $1 if($line =~ m/type="([^"]*)"/i);
$h{genre} = $1 if($line =~ m/genre="([^"]*)"/i); # config, user
$h{label} = $1 if($line =~ m/label="([^"]*)"/i);
$h{min} = $1 if($line =~ m/min="([^"]*)"/i);
$h{max} = $1 if($line =~ m/max="([^"]*)"/i);
$h{value} = $1 if($line =~ m/value="([^"]*)"/i);
$h{index} = $1 if($line =~ m/index="([^"]*)"/i); # 1, 2, etc
$h{read_only} = $1 if($line =~ m/read_only="([^"]*)"/i); # true,false
$h{write_only} = $1 if($line =~ m/write_only="([^"]*)"/i); # true,false
$cmdName = "config".ZWave_cleanString($h{label});
$hash{$cmdName} = \%h;
}
$hash{$cmdName}{Help} = $1 if($line =~ m+^<Help>(.*)</Help>$+);
if($line =~ m/^<Item/) {
my $label = $1 if($line =~ m/label="([^"]*)"/i);
my $value = $1 if($line =~ m/value="([^"]*)"/i);
$label = ZWave_cleanString($label);
$hash{$cmdName}{Item}{$label} = $value;
}
}
$gz->gzclose();
my %mc;
foreach my $cmd (keys %hash) {
my $h = $hash{$cmd};
my $arg = ($h->{type} eq "button" ? "a" : "a%b");
$mc{set}{$cmd} = $arg if(!$h->{read_only} || $h->{read_only} ne "true");
$mc{get}{$cmd} ="noArg" if(!$h->{write_only} || $h->{write_only} ne "true");
$mc{config}{$cmd} = $h;
$zwave_cmdArgs{set}{$cmd} = join(",", keys %{$h->{Item}}) if($h->{Item});
$zwave_cmdArgs{set}{$cmd} = "noArg" if($h->{type} eq "button");
$zwave_cmdArgs{get}{$cmd} = "noArg";
}
$zwave_modelConfig{$cfg} = \%mc;
}
sub
ZWave_checkConfigParam($$$$@)
{
my ($hash, $type, $cmd, $fmt, @arg) = @_;
my $mc = ReadingsVal($hash->{NAME}, "modelConfig", "");
return ("", sprintf($fmt, @arg)) if(!$mc || !$zwave_modelConfig{$mc});
my $h = $zwave_modelConfig{$mc}{config};
$h = $h->{$cmd};
return ("", sprintf($fmt, @arg)) if(!$h);
return ("", sprintf("05%02x", $h->{index})) if($type eq "get");
my $t = $h->{type};
if($t eq "list") {
my $v = $h->{Item}{$arg[0]};
return ("Unknown parameter $arg[0] for $cmd, use one of ".
join(",", keys %{$h->{Item}}), "") if(!$v);
return ("", sprintf("04%02x01%02x", $h->{index}, $v));
}
if($t eq "button") {
return ("", sprintf("04%02x01%02x", $h->{index}, $h->{value}));
}
return ("Parameter is not decimal", "") if($arg[0] !~ m/^[0-9]+$/);
if($t eq "short") {
return ("", sprintf("04%02x02%04x", $h->{index}, $arg[0]));
}
if($t eq "byte") {
return ("", sprintf("04%02x01%02x", $h->{index}, $arg[0]));
}
return ("", sprintf("04%02x01%02x", $h->{index}, $arg[0]));
}
sub
ZWave_getHash($$$)
{
@ -665,10 +794,25 @@ ZWave_getHash($$$)
my $ptr = $zwave_class{$cl}{$type}
if($zwave_class{$cl} && $zwave_class{$cl}{$type});
if($cl eq "CONFIGURATION" && $type ne "parse") {
my $mc = ReadingsVal($hash->{NAME}, "modelConfig", "");
ZWave_parseConfig($mc) if($mc && !$zwave_modelConfig{$mc});
my $mch = $zwave_modelConfig{$mc};
if($mch) {
my $mcp = $mch->{$type};
if($mcp) {
my %nptr = ();
map({$nptr{$_} = $ptr->{$_}} keys %{$ptr});
map({$nptr{$_} = $mcp->{$_}} keys %{$mcp});
$ptr = \%nptr;
}
}
}
if($cl eq "MANUFACTURER_PROPRIETARY") {
my $modelId = ReadingsVal($hash->{NAME}, "modelId", "");
$modelId = $modelIdAlias{$modelId} if($modelIdAlias{$modelId});
my $p = $manuf_proprietary{$modelId};
$modelId = $zwave_modelIdAlias{$modelId} if($zwave_modelIdAlias{$modelId});
my $p = $zwave_manuf_proprietary{$modelId};
$ptr = $p->{$type} if($p && $p->{$type});
}
@ -867,6 +1011,47 @@ ZWave_Undef($$)
return undef;
}
sub
ZWave_helpFn($$)
{
my ($d,$cmd) = @_;
my $mc = ReadingsVal($d, "modelConfig", "");
return "" if(!$mc || !$zwave_modelConfig{$mc});
my $h = $zwave_modelConfig{$mc}{config};
$h = $h->{$cmd};
return "" if(!$h || !$h->{Help});
return "Help for $cmd:<br>".$h->{Help};
}
sub
ZWave_fhemwebFn($$$$)
{
my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn.
return "<div id=\"Help_$d\" class=\"help\"></div>".<<JSEND
<script type="text/javascript">
var oldHelp={}
function helpSet(val) { document.querySelector("#Help_$d").innerHTML=val; }
function
helpCheck(name)
{
var sel = document.querySelector("select."+name);
if(!sel)
return;
var newVal = sel.options[sel.selectedIndex].value;
if(oldHelp[name] && oldHelp[name] != newVal) {
FW_queryValue('{ZWave_helpFn("$d","'+newVal+'")}', 'helpSet("%")', '');
}
oldHelp[name] = newVal;
}
setInterval(function() {
helpCheck("set");
helpCheck("get");
}, 300);
</script>
JSEND
}
#####################################
# 2-byte signed hex

Binary file not shown.

@ -0,0 +1,35 @@
#!/usr/bin/perl
# Usage: contrib/zwave_configconvert.pl priv/zwave/open-zwave-read-only/config |
# gzip > FHEM/lib/openzwave_deviceconfig.xml.gz
#
print '<?xml version="1.0" encoding="utf-8"?>', "\n";
print "<ProductList>\n";
foreach my $file (`find $ARGV[0] -name \*.xml`) {
chomp($file);
my $name = $file;
$name =~ s+.*config/++;
next if($name !~ m+/+); # Only files from subdirs
open(FH, $file) || die("$file:$!\n");
my $buffer="";
while(my $l = <FH>) {
next if($l =~ m/^<\?xml/);
chomp($l);
$l =~ s/^<Product .*/<Product sourceFile="$name">/;
$l =~ s/\r//g;
$l =~ s/^[ \t]*//g;
$l =~ s/[ \t]*$//g;
next if($l eq "");
if($l !~ m/>$/ || $l =~ m/^<Help>$/) { $buffer .= " ".$l; next; }
if($buffer && $l =~ m/>$/) { $l = "$buffer $l"; $buffer=""; }
$l =~ s/<!--.*-->//g;
$l =~ s/^[ \t]*//g;
$l =~ s/[ \t]*$//g;
print "$l\n" if($l);
}
close(FH);
print $buffer if($buffer);
print "\n"; # Some files are not NL terminated!
}
print "</ProductList>\n";