2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-25 09:49:20 +00:00

MAX: Rewrite faking of WallThermostat/ShutterContact

git-svn-id: https://svn.fhem.de/fhem/trunk@2731 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
mgehre 2013-02-15 03:20:39 +00:00
parent 7c8aa8240f
commit 5e9f17e51b
2 changed files with 109 additions and 16 deletions

View File

@ -87,6 +87,7 @@ MAX_Define($$)
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
my $name = $hash->{NAME};
return "name \"$name\" is reserved for internal use" if($name eq "fakeWallThermostat" or $name eq "fakeShutterContact");
return "wrong syntax: define <name> MAX addr"
if(int(@a)!=4 || $a[3] !~ m/^[A-F0-9]{6}$/i);
@ -368,14 +369,30 @@ MAX_Set($@)
} elsif($setting ~~ ["associate", "deassociate"]) {
my $dest = $args[0];
if(exists($defs{$dest})) {
return "Destination is not a MAX device" if($defs{$dest}{TYPE} ne "MAX");
$dest = $defs{$dest}{addr};
my $destType;
if($dest eq "fakeWallThermostat") {
return "IODev is not CUL_MAX" if($hash->{IODev}->{TYPE} ne "CUL_MAX");
$dest = AttrVal($hash->{IODev}->{NAME}, "fakeWTaddr", "111111");
return "Invalid fakeWTaddr attribute set (must not be 000000)" if($dest eq "000000");
$destType = MAX_TypeToTypeId("WallMountedThermostat");
} elsif($dest eq "fakeShutterContact") {
return "IODev is not CUL_MAX" if($hash->{IODev}->{TYPE} ne "CUL_MAX");
$dest = AttrVal($hash->{IODev}->{NAME}, "fakeSCaddr", "222222");
return "Invalid fakeSCaddr attribute set (must not be 000000)" if($dest eq "000000");
$destType = MAX_TypeToTypeId("ShutterContact");
} else {
return "No MAX device with address $dest" if(!exists($modules{MAX}{defptr}{$dest}));
if(exists($defs{$dest})) {
return "Destination is not a MAX device" if($defs{$dest}{TYPE} ne "MAX");
$dest = $defs{$dest}{addr};
} else {
return "No MAX device with address $dest" if(!exists($modules{MAX}{defptr}{$dest}));
}
my $destType = MAX_TypeToTypeId($modules{MAX}{defptr}{$dest}{type});
Log 2, "Warning: Device do not have same groupid" if($hash->{groupid} != $modules{MAX}{defptr}{$dest}{groupid});
}
my $destType = MAX_TypeToTypeId($modules{MAX}{defptr}{$dest}{type});
Log 2, "Warning: Device do not have same groupid" if($hash->{groupid} != $modules{MAX}{defptr}{$dest}{groupid});
Log GetLogLevel($hash->{NAME}, 5), "Using dest $dest, destType $destType";
if($setting eq "associate") {
return ($hash->{IODev}{Send})->($hash->{IODev},"AddLinkPartner",$hash->{addr},sprintf("%s%02x", $dest, $destType));
@ -446,7 +463,11 @@ MAX_Set($@)
my $assoclist;
#Build list of devices which this device can be associated to
if($hash->{type} =~ /HeatingThermostat.*/) {
$assoclist = join(",", map { defined($_->{type}) && $_->{type} ~~ ["HeatingThermostat", "HeatingThermostatPlus", "WallMountedThermostat", "ShutterContact"] ? $_->{NAME} : () } values %{$modules{MAX}{defptr}});
$assoclist = join(",", map { defined($_->{type}) && $_->{type} ~~ ["HeatingThermostat", "HeatingThermostatPlus", "WallMountedThermostat", "ShutterContact"] && $_ != $hash ? $_->{NAME} : () } values %{$modules{MAX}{defptr}});
if($hash->{IODev}->{TYPE} eq "CUL_MAX") {
$assoclist .= "," if(length($assoclist));
$assoclist .= "fakeWallThermostat,fakeShutterContact";
}
} elsif($hash->{type} ~~ ["ShutterContact", "WallMountedThermostat"]) {
$assoclist = join(",", map { defined($_->{type}) && $_->{type} =~ /HeatingThermostat.*/ ? $_->{NAME} : () } values %{$modules{MAX}{defptr}});
}

View File

@ -38,6 +38,7 @@ CUL_MAX_Initialize($)
$hash->{UndefFn} = "CUL_MAX_Undef";
$hash->{ParseFn} = "CUL_MAX_Parse";
$hash->{SetFn} = "CUL_MAX_Set";
$hash->{AttrFn} = "CUL_MAX_Attr";
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 " .
"showtime:1,0 loglevel:0,1,2,3,4,5,6 ".
$readingFnAttributes;
@ -66,12 +67,18 @@ CUL_MAX_Define($$)
$hash->{sendQueue} = [];
AssignIoPort($hash);
if(CUL_MAX_Check($hash)) {
if(CUL_MAX_Check($hash) >= 152) {
#Doing this on older firmware disables MAX mode
IOWrite($hash, "", "Za". $hash->{addr});
#Append to initString, so this is resend if cul disappears and then reappears
$hash->{IODev}{initString} .= "\nZa". $hash->{addr};
}
if(CUL_MAX_Check($hash) >= 153) {
#Doing this on older firmware disables MAX mode
my $cmd = "Zw". CUL_MAX_fakeWTaddr($hash);
IOWrite($hash, "", $cmd);
$hash->{IODev}{initString} .= "\n".$cmd;
}
#This interface is shared with 00_MAXLAN.pm
$hash->{Send} = \&CUL_MAX_Send;
@ -111,11 +118,33 @@ CUL_MAX_Check($@)
#Looks like "V 1.49 CUL868"
$version =~ m/V (.*)\.(.*) .*/;
my ($major_version,$minorversion) = ($1, $2);
if($major_version == 1 and $minorversion < 52) {
Log 2, "The current firmware of the CUL has known bugs with respect to MAX! support. Please update.";
return 0;
my $version = 100*$major_version + $minorversion;
if($version < 153) {
Log 2, "You are using an old version of the CUL firmware, which has known bugs with respect to MAX! support. Please update.";
}
return 1;
return $version;
}
sub
CUL_MAX_Attr(@)
{
my ($hash, $action, $name, $attr, $value) = @_;
if ($action eq "set") {
return "No such attribute" if($attr ne "fakeWTaddr" && $attr ne "fakeSCaddr");
return "Invalid value" if($value !~ /^[0-9a-fA-F]{6}$/);
}
}
sub
CUL_MAX_fakeWTaddr($)
{
return AttrVal(@_[0]->{NAME}, "fakeWTaddr", "111111");
}
sub
CUL_MAX_fakeSCaddr($)
{
return AttrVal(@_[0]->{NAME}, "fakeSCaddr", "222222");
}
sub
@ -132,6 +161,41 @@ CUL_MAX_Set($@)
} elsif($setting eq "broadcastTime") {
CUL_MAX_BroadcastTime($hash, 1);
} elsif($setting ~~ ["fakeSC", "fakeWT"]) {
return "Invalid number of arguments" if(@args == 0);
my $dest = $args[0];
#$dest may be either a name or an address
if(exists($defs{$dest})) {
return "Destination is not a MAX device" if($defs{$dest}{TYPE} ne "MAX");
$dest = $defs{$dest}{addr};
} else {
return "No MAX device with address $dest" if(!exists($modules{MAX}{defptr}{$dest}));
}
if($setting eq "fakeSC") {
return "Invalid number of arguments" if(@args != 2);
return "Invalid fakeSCaddr attribute set (must not be 000000)" if(CUL_MAX_fakeSCaddr($hash) eq "000000");
my $state = $args[1] ? "12" : "10";
return CUL_MAX_Send($hash, "ShutterContactState",$dest,$state, flags => "06", src => CUL_MAX_fakeSCaddr($hash));
} elsif($setting eq "fakeWT") {
return "Invalid number of arguments" if(@args != 3);
return "desiredTemperature is invalid" if($args[1] < 4.5 || $args[2] > 30.5);
return "Invalid fakeWTaddr attribute set (must not be 000000)" if(CUL_MAX_fakeWTaddr($hash) eq "000000");
$args[2] = 0 if($args[2] < 0); #Clamp temperature to minimum of 0 degree
#Encode into binary form
my $arg2 = int(10*$args[2]);
#First bit is 9th bit of temperature, rest is desiredTemperature
my $arg1 = (($arg2&0x100)>>1) | (int(2*$args[1])&0x7F);
$arg2 &= 0xFF; #only take the lower 8 bits
return CUL_MAX_Send($hash,"WallThermostatControl",$dest,
sprintf("%02x%02x",$arg1,$arg2),flags => "04", src => CUL_MAX_fakeWTaddr($hash));
}
} else {
return "Unknown argument $setting, choose one of pairmode broadcastTime";
}
@ -172,16 +236,20 @@ CUL_MAX_Parse($$)
if(exists($msgId2Cmd{$msgTypeRaw})) {
if($msgType eq "Ack") {
return $shash->{NAME} if($src eq $shash->{addr}); #Ignore packets generated by culfw's auto-Ack
#Ignore packets generated by culfw's auto-Ack
return $shash->{NAME} if($src eq $shash->{addr});
return $shash->{NAME} if($src eq CUL_MAX_fakeWTaddr($hash));
return $shash->{NAME} if($src eq CUL_MAX_fakeSCaddr($hash));
Dispatch($shash, "MAX,$isToMe,Ack,$src,$payload", {RAWMSG => $rmsg});
return $shash->{NAME} if(!$isToMe);
return $shash->{NAME} if(!@{$shash->{sendQueue}}); #we are not waiting for any Ack
my $packet = $shash->{sendQueue}[0];
if($packet->{dst} eq $src and $packet->{cnt} == hex($msgcnt)) {
if($packet->{src} eq $dst and $packet->{dst} eq $src and $packet->{cnt} == hex($msgcnt)) {
Log $ll5, "Got matching ack";
$packet->{sent} = 2;
my $isnak = unpack("C",pack("H*",$payload)) & 0x80;
$packet->{sent} = $isnak ? 3 : 2;
return $shash->{NAME};
}
@ -273,6 +341,7 @@ CUL_MAX_Send(@)
my $timeout = gettimeofday()+$ackTimeout;
my $aref = $hash->{sendQueue};
push(@{$aref}, { "packet" => $packet,
"src" => $src,
"dst" => $dst,
"cnt" => hex($msgcnt),
"time" => $timeout,
@ -348,6 +417,9 @@ CUL_MAX_SendQueueHandler($)
Dispatch($hash, "MAX,1,Ack$packet->{cmd},$packet->{dst},$packet->{callbackParam}", {RAWMSG => ""});
}
splice @{$hash->{sendQueue}}, 0, 1; #Remove from array
} elsif( $packet->{sent} == 3 ) { #Got nack
splice @{$hash->{sendQueue}}, 0, 1; #Remove from array
}
return if(!@{$hash->{sendQueue}}); #everything done