diff --git a/fhem/FHEM/10_EnOcean.pm b/fhem/FHEM/10_EnOcean.pm index 9bf2561a2..f1b697d28 100755 --- a/fhem/FHEM/10_EnOcean.pm +++ b/fhem/FHEM/10_EnOcean.pm @@ -12,20 +12,23 @@ sub EnOcean_Initialize($); sub EnOcean_Parse($$); sub EnOcean_Set($@); sub EnOcean_MD15Cmd($$$); +sub EnOcean_CheckSenderID($$$); sub EnOcean_SndRadio($$$$$$$); sub EnOcean_ReadingScaled($$$$); sub EnOcean_TimerSet($); sub EnOcean_Undef($$); my %EnO_rorgname = ("F6" => "switch", # RPS, org 05 - "D5" =>" contact", # 1BS, org 06 + "D5" => "contact", # 1BS, org 06 "A5" => "sensor", # 4BS, org 07 - "D2" => "vld", # VLD + "D1" => "MSC", # MSC + "D2" => "VLD", # VLD + "D4" => "UTE", # UTE ); my @EnO_ptm200btn = ("AI", "A0", "BI", "B0", "CI", "C0", "DI", "D0"); my %EnO_ptm200btn; -# Gateway Commands +# Gateway commands my @EnO_gwCmd = ("switching", "dimming", "setpointShift", "setpointBasic", "controlVar", "fanStage", "blindCmd"); my %EnO_gwCmd = ( "switching" => 1, @@ -37,8 +40,7 @@ my %EnO_gwCmd = ( "blindCmd" => 7, ); -# Some Manufacturers (e.g. Jaeger Direkt) also sell EnOcean products without an -# entry in the table below. This table is only needed for 4BS category devices. +# Some Manufacturers (e.g. Jaeger Direkt) also sell EnOcean products without an entry in the table below. my %EnO_manuf = ( "001" => "Peha", "002" => "Thermokon", @@ -173,14 +175,14 @@ my %EnO_subType = ( "F6.10.00" => "windowHandle", 1 => "switch", 2 => "sensor", - 3 => "vld", - 4 => "FRW", - 5 => "PM101", - 6 => "raw", + 3 => "FRW", + 4 => "PM101", + 5 => "raw", ); my @EnO_models = qw ( other + FAE14 FHK14 FSB14 FSB61 FSB70 FSM12 FSM61 FT55 @@ -203,21 +205,23 @@ EnOcean_Initialize($) $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 dummy:0,1 " . "showtime:1,0 loglevel:0,1,2,3,4,5,6 " . "actualTemp angleMax:slider,-180,20,180 angleMin:slider,-180,20,180 " . - "angleTime:0,1,2,3,4,5,6 destinationID dimValueOn " . + "angleTime:0,1,2,3,4,5,6 comMode:biDir,uniDir destinationID " . + "devChannel dimValueOn " . + "gwCmd:" . join(",", sort @EnO_gwCmd) . " humidityRefDev " . + "manufID:" . join(",", sort keys %EnO_manuf) . " " . "model:" . join(",", @EnO_models) . " " . - "gwCmd:" . join(",", sort @EnO_gwCmd) . " " . - "manufID:" . join(",", keys %EnO_manuf) . " " . "rampTime repeatingAllowed:yes,no " . "scaleDecimals:0,1,2,3,4,5,6,7,8,9 scaleMax scaleMin " . - "securityLevel:unencrypted " . - "shutTime shutTimeCloses subDef subDef0 subDefI " . + "securityLevel:unencrypted sensorMode:switch,pushbutton " . + "shutTime shutTimeCloses subDef " . + "subDef0 subDefI " . "subType:" . join(",", sort grep { !$subTypeList{$_}++ } values %EnO_subType) . " " . "subTypeSet:" . join(",", sort grep { !$subTypeSetList{$_}++ } values %EnO_subType) . " " . "switchMode:switch,pushbutton " . - "switchType:direction,universal,central " . + "switchType:direction,universal,central temperatureRefDev " . $readingFnAttributes; - for(my $i=0; $i<@EnO_ptm200btn;$i++) { + for (my $i = 0; $i < @EnO_ptm200btn; $i++) { $EnO_ptm200btn{$EnO_ptm200btn[$i]} = "$i:30"; } $EnO_ptm200btn{released} = "0:20"; @@ -232,7 +236,7 @@ EnOcean_Define($$) my @a = split("[ \t][ \t]*", $def); my $name = $hash->{NAME}; return "wrong syntax: define EnOcean 8-digit-hex-code" - if(int(@a)!=3 || $a[2] !~ m/^[A-F0-9]{8}$/i); + if(int(@a) != 3 || $a[2] !~ m/^[A-F0-9]{8}$/i); $modules{EnOcean}{defptr}{uc($a[2])} = $hash; AssignIoPort($hash); @@ -271,17 +275,308 @@ EnOcean_Set($@) if ($subDef !~ m/^[\dA-F]{8}$/) {return "SenderID $subDef wrong, choose <8-digit-hex-code>.";} my $switchMode = AttrVal($name, "switchMode", "switch"); my $tn = TimeNow(); + # control set actions + # $updateState = -1: no set commands available e. g. sensors + # 0: execute set commands + # 1: execute set commands and and update reading state + # 2: execute set commands delayed my $updateState = 1; shift @a; - for(my $i = 0; $i < @a; $i++) { + for (my $i = 0; $i < @a; $i++) { my $cmd = $a[$i]; - if($st eq "MD15") { - # Battery Powered Actuator (EEP A5-20-01) - # [Kieback&Peter MD15-FTL-xx] - # See also http://www.oscat.de/community/index.php/topic,985.30.html - # Maintenance commands (runInit, liftSet, valveOpen, valveClosed) + if ($st eq "roomSensorControl.05") { + # Room Sensor and Control Unit (EEP A5-10-01 ... A5-10-0D) + # [Eltako FTF55D, FTF55H, Thermokon SR04 *, Thanos SR *, untested] + # $db[3] is the fan speed or night reduction for Eltako + # $db[2] is the setpoint where 0x00 = min ... 0xFF = max or + # reference temperature for Eltako where 0x00 = 0°C ... 0xFF = 40°C + # $db[1] is the temperature where 0x00 = +40°C ... 0xFF = 0°C + # $db[1]_bit_1 is blocking the aditional Room Sensor and Control Unit for Eltako FVS + # $db[0]_bit_0 is the slide switch + $rorg = "A5"; + # primarily temperature from the reference device then the attribute actualTemp is read + my $temperatureRefDev = AttrVal($name, "temperatureRefDev", undef); + my $actualTemp = AttrVal($name, "actualTemp", 20); + $actualTemp = ReadingsVal($temperatureRefDev, "temperature", 20) if (defined $temperatureRefDev); + $actualTemp = 20 if ($actualTemp !~ m/^[+-]?\d+(\.\d+)?$/); + $actualTemp = 0 if ($actualTemp < 0); + $actualTemp = 40 if ($actualTemp > 40); + readingsSingleUpdate($hash, "temperature", $actualTemp, 1); + my $setCmd = 8; + if ($manufID eq "00D") { + # EEP A5-10-06 plus DB3 [Eltako FVS] + my $setpointTemp = ReadingsVal($name, "setpointTemp", 20); + my $nightReduction = ReadingsVal($name, "nightReduction", 0); + my $block = ReadingsVal($name, "block", "unlock"); + if ($cmd eq "teach") { + # teach-in EEP A5-10-06 plus "FVS", Manufacturer "Eltako" + $data = "40370D85"; + CommandDeleteReading(undef, "$name .*"); + } elsif ($cmd eq "desired-temp" || $cmd eq "setpointTemp") { + # + if (defined $a[1]) { + if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= 0) && ($a[1] <= 40)) { + $setpointTemp = $a[1]; + shift(@a); + } else { + return "Usage: $a[1] is not numeric or out of range"; + } + } + if (defined $a[1]) { + if (($a[1] =~ m/^(lock|unlock)$/) ) { + $block = $a[1]; + shift(@a); + } else { + return "Usage: $a[1] is unknown"; + } + } + readingsSingleUpdate($hash, "setpointTemp", $setpointTemp, 1); + readingsSingleUpdate($hash, "nightReduction", $nightReduction, 1); + readingsSingleUpdate($hash, "block", $block, 1); + readingsSingleUpdate($hash, "state", "T: $actualTemp SPT: $setpointTemp NR: $nightReduction", 1); + if ($nightReduction == 5) { + $nightReduction = 31; + } elsif ($nightReduction == 4) { + $nightReduction = 25; + } elsif ($nightReduction == 3) { + $nightReduction = 19; + } elsif ($nightReduction == 2) { + $nightReduction = 12; + } elsif ($nightReduction == 1) { + $nightReduction = 6; + } else { + $nightReduction = 0; + } + $actualTemp = (40 - $actualTemp) / 40 * 255; + $setpointTemp = $setpointTemp * 255 / 40; + # control of the aditional Room Sensor and Control Unit + if ($block eq "lock") { + # temperature setting is locked + $setCmd = 0x0D; + } else { + # setpointTemp may be subject to change at +/-3 K + $setCmd = 0x0F; + } + $updateState = 0; + $data = sprintf "%02X%02X%02X%02X", $nightReduction, $setpointTemp, $actualTemp, $setCmd; + + } elsif ($cmd eq "nightReduction") { + # + if (defined $a[1]) { + if ($a[1] =~ m/^[0-5]$/) { + $nightReduction = $a[1]; + shift(@a); + } else { + return "Usage: $a[1] is not numeric or out of range"; + } + } + if (defined $a[1]) { + if (($a[1] =~ m/^(lock|unlock)$/) ) { + $block = $a[1]; + shift(@a); + } else { + return "Usage: $a[1] is unknown"; + } + } + readingsSingleUpdate($hash, "setpointTemp", $setpointTemp, 1); + readingsSingleUpdate($hash, "nightReduction", $nightReduction, 1); + readingsSingleUpdate($hash, "block", $block, 1); + readingsSingleUpdate($hash, "state", "T: $actualTemp SPT: $setpointTemp NR: $nightReduction", 1); + if ($nightReduction == 5) { + $nightReduction = 31; + } elsif ($nightReduction == 4) { + $nightReduction = 25; + } elsif ($nightReduction == 3) { + $nightReduction = 19; + } elsif ($nightReduction == 2) { + $nightReduction = 12; + } elsif ($nightReduction == 1) { + $nightReduction = 6; + } else { + $nightReduction = 0; + } + $actualTemp = (40 - $actualTemp) / 40 * 255; + $setpointTemp = $setpointTemp * 255 / 40; + # control of the aditional Room Sensor and Control Unit + if ($block eq "lock") { + # temperature setting is locked + $setCmd = 0x0D; + } else { + # setpointTemp may be subject to change at +/-3 K + $setCmd = 0x0F; + } + $updateState = 0; + $data = sprintf "%02X%02X%02X%02X", $nightReduction, $setpointTemp, $actualTemp, $setCmd; + + } else { + return "Unknown argument " . $cmd . ", choose one of desired-temp nightReduction:0,1,2,3,4,5 setpointTemp teach" + } + + } else { + # EEP A5-10-02 + my $setpoint = ReadingsVal($name, "setpoint", 128); + my $setpointScaled = ReadingsVal($name, "setpointScaled", undef); + my $fanStage = ReadingsVal($name, "fanStage", "auto"); + my $switch = ReadingsVal($name, "switch", "off"); + $setCmd |= 1 if ($switch eq "on"); + if ($cmd eq "teach") { + # teach-in EEP A5-10-02, Manufacturer "Multi user Manufacturer ID" + $data = "4017FF80"; + CommandDeleteReading(undef, "$name .*"); + } elsif ($cmd eq "fanStage") { + # + if (defined $a[1] && ($a[1] =~ m/^[0-3]$/ || $a[1] eq "auto")) { + $fanStage = $a[1]; + shift(@a); + readingsSingleUpdate($hash, "setpointScaled", $setpointScaled, 1) if (defined $setpointScaled); + readingsSingleUpdate($hash, "setpoint", $setpoint, 1); + readingsSingleUpdate($hash, "fanStage", $fanStage, 1); + readingsSingleUpdate($hash, "switch", $switch, 1); + readingsSingleUpdate($hash, "state", "T: $actualTemp SP: $setpoint F: $fanStage SW: $switch", 1); + if ($fanStage eq "auto"){ + $fanStage = 255; + } elsif ($fanStage == 0) { + $fanStage = 209; + } elsif ($fanStage == 1) { + $fanStage = 189; + } elsif ($fanStage == 2) { + $fanStage = 164; + } else { + $fanStage = 144; + } + } else { + return "Usage: $a[1] is not numeric, out of range or unknown"; + } + $actualTemp = (40 - $actualTemp) / 40 * 255; + $updateState = 0; + $data = sprintf "%02X%02X%02X%02X", $fanStage, $setpoint, $actualTemp, $setCmd; + + } elsif ($cmd eq "setpoint") { + # + if (defined $a[1]) { + if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= 0) && ($a[1] <= 255)) { + $setpoint = $a[1]; + shift(@a); + if (defined $setpointScaled) { + $setpointScaled = EnOcean_ReadingScaled($hash, $setpoint, 0, 255); + } + } else { + return "Usage: $a[1] is not numeric or out of range"; + } + + } + readingsSingleUpdate($hash, "setpointScaled", $setpointScaled, 1) if (defined $setpointScaled); + readingsSingleUpdate($hash, "setpoint", $setpoint, 1); + readingsSingleUpdate($hash, "fanStage", $fanStage, 1); + readingsSingleUpdate($hash, "switch", $switch, 1); + readingsSingleUpdate($hash, "state", "T: $actualTemp SP: $setpoint F: $fanStage SW: $switch", 1); + if ($fanStage eq "auto"){ + $fanStage = 255; + } elsif ($fanStage == 0) { + $fanStage = 209; + } elsif ($fanStage == 1) { + $fanStage = 189; + } elsif ($fanStage == 2) { + $fanStage = 164; + } else { + $fanStage = 144; + } + $actualTemp = (40 - $actualTemp) / 40 * 255; + $updateState = 0; + $data = sprintf "%02X%02X%02X%02X", $fanStage, $setpoint, $actualTemp, $setCmd; + + } elsif ($cmd eq "setpointScaled") { + # + if (defined $a[1]) { + my $scaleMin = AttrVal($name, "scaleMin", undef); + my $scaleMax = AttrVal($name, "scaleMax", undef); + my ($rangeMin, $rangeMax); + if (defined $scaleMax && defined $scaleMin && + $scaleMax =~ m/^[+-]?\d+(\.\d+)?$/ && $scaleMin =~ m/^[+-]?\d+(\.\d+)?$/) { + if ($scaleMin > $scaleMax) { + ($rangeMin, $rangeMax)= ($scaleMax, $scaleMin); + } else { + ($rangeMin, $rangeMax)= ($scaleMin, $scaleMax); + } + } else { + return "Usage: Attributes scaleMin and/or scaleMax not defined or not numeric."; + } + if ($a[1] =~ m/^[+-]?\d+(\.\d+)?$/ && $a[1] >= $rangeMin && $a[1] <= $rangeMax) { + $setpointScaled = $a[1]; + shift(@a); + $setpoint = sprintf "%d", 255 * $scaleMin/($scaleMin-$scaleMax) - 255/($scaleMin-$scaleMax) * $setpointScaled; + } else { + return "Usage: $a[1] is not numeric or out of range"; + } + } + readingsSingleUpdate($hash, "setpointScaled", $setpointScaled, 1); + readingsSingleUpdate($hash, "setpoint", $setpoint, 1); + readingsSingleUpdate($hash, "fanStage", $fanStage, 1); + readingsSingleUpdate($hash, "switch", $switch, 1); + readingsSingleUpdate($hash, "state", "T: $actualTemp SP: $setpoint F: $fanStage SW: $switch", 1); + if ($fanStage eq "auto"){ + $fanStage = 255; + } elsif ($fanStage == 0) { + $fanStage = 209; + } elsif ($fanStage == 1) { + $fanStage = 189; + } elsif ($fanStage == 2) { + $fanStage = 164; + } else { + $fanStage = 144; + } + $actualTemp = (40 - $actualTemp) / 40 * 255; + $updateState = 0; + $data = sprintf "%02X%02X%02X%02X", $fanStage, $setpoint, $actualTemp, $setCmd; + + } elsif ($cmd eq "switch") { + # + if (defined $a[1]) { + if ($a[1] eq "on") { + $switch = $a[1]; + $setCmd |= 1; + shift(@a); + } elsif ($a[1] eq "off"){ + $switch = $a[1]; + shift(@a); + } else { + return "Usage: $a[1] is unknown"; + } + } + readingsSingleUpdate($hash, "setpointScaled", $setpointScaled, 1) if (defined $setpointScaled); + readingsSingleUpdate($hash, "setpoint", $setpoint, 1); + readingsSingleUpdate($hash, "fanStage", $fanStage, 1); + readingsSingleUpdate($hash, "switch", $switch, 1); + readingsSingleUpdate($hash, "state", "T: $actualTemp SP: $setpoint F: $fanStage SW: $switch", 1); + if ($fanStage eq "auto"){ + $fanStage = 255; + } elsif ($fanStage == 0) { + $fanStage = 209; + } elsif ($fanStage == 1) { + $fanStage = 189; + } elsif ($fanStage == 2) { + $fanStage = 164; + } else { + $fanStage = 144; + } + $actualTemp = (40 - $actualTemp) / 40 * 255; + $updateState = 0; + $data = sprintf "%02X%02X%02X%02X", $fanStage, $setpoint, $actualTemp, $setCmd; + + } else { + return "Unknown argument " . $cmd . ", choose one of fanStage:auto,0,1,2,3 setpoint setpointScaled switch:on,off teach" + } + + } + Log $ll2, "EnOcean: set $name $cmd"; + + } elsif ($st eq "MD15") { + # Battery Powered Actuator (EEP A5-20-01) + # [Kieback&Peter MD15-FTL-xx] + # See also http://www.oscat.de/community/index.php/topic,985.30.html + # Maintenance commands (runInit, liftSet, valveOpen, valveClosed) $rorg = "A5"; my %sets = ( "desired-temp" => "\\d+(\\.\\d)?", @@ -295,17 +590,17 @@ EnOcean_Set($@) ); my $re = $sets{$a[0]}; return "Unknown argument $cmd, choose one of ".join(" ", sort keys %sets) - if(!defined($re)); + if (!defined($re)); return "Need a parameter" if ($re && @a < 2); - return "Argument $a[1] is incorrect (expect $re)" - if ($re && $a[1] !~ m/^$re$/); + return "Argument $a[1] is incorrect (expect $re)" if ($re && $a[1] !~ m/^$re$/); + $updateState = 2; $hash->{CMD} = $cmd; $hash->{READINGS}{CMD}{TIME} = $tn; $hash->{READINGS}{CMD}{VAL} = $cmd; my $arg = "true"; - if($re) { + if ($re) { $arg = $a[1]; shift(@a); } @@ -340,7 +635,9 @@ EnOcean_Set($@) # Switching $gwCmdID = 1; if($cmd eq "teach") { - $data = sprintf "%02X000000", $gwCmdID; + # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID" + #$data = sprintf "%02X000000", $gwCmdID; + $data = "E047FF80"; } elsif ($cmd eq "on" || $cmd eq "B0") { $setCmd = 9; if ($a[1]) { @@ -374,8 +671,8 @@ EnOcean_Set($@) my $sendDimCmd = 0; $setCmd = 9; if ($cmd eq "teach") { - $setCmd = 0; - $data = sprintf "%02X000000", $gwCmdID; + # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID" + $data = "E047FF80"; } elsif ($cmd eq "dim") { return "Usage: $cmd dim/% [rampTime/s lock|unlock]" if(@a < 2 || $a[1] < 0 || $a[1] > 100 || $a[1] !~ m/^[+-]?\d+$/); @@ -458,11 +755,11 @@ EnOcean_Set($@) my $cmdList = "dim:slider,0,1,100 B0 BI teach"; return SetExtensions ($hash, $cmdList, $name, @a); } - if($sendDimCmd) { + if ($sendDimCmd) { if (defined $a[1]) { return "Usage: $cmd dim/% [rampTime/s lock|unlock]" if (($a[1] ne "lock") && ($a[1] ne "unlock")); if ($manufID eq "OOD") { - # Eltako devices: block dimming value + # Eltako devices: lock dimming value if ($a[1] eq "lock") { $setCmd = $setCmd | 4; } } else { # Dimming value relative @@ -483,7 +780,8 @@ EnOcean_Set($@) } elsif ($gwCmd eq "setpointShift") { $gwCmdID = 3; if ($cmd eq "teach") { - $data = sprintf "%02X000000", $gwCmdID; + # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID" + $data = "E047FF80"; } elsif ($cmd eq "shift") { if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= -12.7) && ($a[1] <= 12.8)) { $updateState = 0; @@ -499,7 +797,8 @@ EnOcean_Set($@) } elsif ($gwCmd eq "setpointBasic") { $gwCmdID = 4; if($cmd eq "teach") { - $data = sprintf "A5%02X000000", $gwCmdID; + # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID" + $data = "E047FF80"; } elsif ($cmd eq "basic") { if (($a[1] =~ m/^[+-]?\d+(\.\d+)?$/) && ($a[1] >= 0) && ($a[1] <= 51.2)) { $updateState = 0; @@ -516,7 +815,8 @@ EnOcean_Set($@) $gwCmdID = 5; my $controlVar = ReadingsVal($name, "controlVar", 0); if($cmd eq "teach") { - $data = printf "A5%02X000000", $gwCmdID; + # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID" + $data = "E047FF80"; } elsif ($cmd eq "presence") { if ($a[1] eq "standby") { $setCmd = 0x0A; @@ -577,7 +877,8 @@ EnOcean_Set($@) } elsif ($gwCmd eq "fanStage") { $gwCmdID = 6; if($cmd eq "teach") { - $data = sprintf "%02X000000", $gwCmdID; + # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID" + $data = "E047FF80"; } elsif ($cmd eq "stage") { if ($a[1] eq "auto") { $updateState = 0; @@ -621,8 +922,12 @@ EnOcean_Set($@) $setCmd = $blindFuncID << 4 | 8; if($blindFuncID == 255) { - # teach - $setCmd = 0; + # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID" + $gwCmdID = 0xE0; + $blindParam1 = 0x47; + $blindParam2 = 0xFF; + $setCmd = 0x80; + #$setCmd = 0; } elsif ($blindFuncID == 0) { # status $updateState = 0; @@ -680,7 +985,6 @@ EnOcean_Set($@) } else { return "Usage: $cmd variable is not numeric or out of range."; } - ## readingsSingleUpdate($hash, "runTimeUp", $blindParam1, 1); readingsSingleUpdate($hash, "runTimeDown", $blindParam2, 1); $updateState = 0; @@ -710,7 +1014,6 @@ EnOcean_Set($@) return "Usage: $cmd variable is not numeric or out of range."; } if ($blindParam1 > $blindParam2) {($blindParam1, $blindParam2) = ($blindParam2, $blindParam1);} - ## readingsSingleUpdate($hash, "positionMin", $blindParam1, 1); readingsSingleUpdate($hash, "positionMax", $blindParam2, 1); $updateState = 0; @@ -728,7 +1031,6 @@ EnOcean_Set($@) } else { return "Usage: $cmd variable is not numeric or out of range."; } - ## readingsSingleUpdate($hash, "angleMin", $a[1], 1); readingsSingleUpdate($hash, "angleMax", $a[2], 1); splice (@a, 0, 2); @@ -751,7 +1053,7 @@ EnOcean_Set($@) } else { return "Unknown Gateway command " . $cmd . ", choose one of ". join(" ", sort keys %EnO_gwCmd); } - Log $ll2, "EnOcean: set $name $cmd $setCmd"; + Log $ll2, "EnOcean: set $name $cmd"; } elsif ($st eq "manufProfile") { if ($manufID eq "00D") { @@ -781,12 +1083,15 @@ EnOcean_Set($@) $shutTime = 255 if ($shutTime > 255); $shutTime = 1 if ($shutTime < 1); if ($cmd eq "teach") { + # teach-in EEP A5-3F-7F, Manufacturer "Eltako" + CommandDeleteReading(undef, "$name .*"); $data = "FFF80D80"; } elsif ($cmd eq "stop") { # stop # delete readings, as they are undefined CommandDeleteReading(undef, "$name anglePos"); CommandDeleteReading(undef, "$name position"); + readingsSingleUpdate($hash, "endPosition", "not_reached", 1); readingsSingleUpdate($hash, "state", "stop", 1); $shutCmd = 0; } elsif ($cmd eq "opens") { @@ -795,6 +1100,8 @@ EnOcean_Set($@) readingsSingleUpdate($hash, "anglePos", $anglePos, 1); $position = 0; readingsSingleUpdate($hash, "position", $position, 1); + readingsSingleUpdate($hash, "endPosition", "open", 1); + $cmd = "open"; $shutTime = $shutTimeCloses; $shutCmd = 1; $updateState = 0; @@ -804,12 +1111,14 @@ EnOcean_Set($@) readingsSingleUpdate($hash, "anglePos", $anglePos, 1); $position = 100; readingsSingleUpdate($hash, "position", $position, 1); + readingsSingleUpdate($hash, "endPosition", "closed", 1); + $cmd = "closed"; $shutTime = $shutTimeCloses; $shutCmd = 2; $updateState = 0; - } elsif ($cmd eq "up" || $cmd eq "B0") { + } elsif ($cmd eq "up") { # up - if(defined $a[1]) { + if (defined $a[1]) { if ($a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] <= 255) { $position -= $a[1] / $shutTime * 100; if ($angleTime) { @@ -823,7 +1132,11 @@ EnOcean_Set($@) if ($position <= 0) { $anglePos = 0; $position = 0; + readingsSingleUpdate($hash, "endPosition", "open", 1); + $cmd = "open"; } + readingsSingleUpdate($hash, "endPosition", "not_reached", 1); + $cmd = "not_reached"; $shutTime = $a[1]; shift(@a); } else { @@ -832,13 +1145,15 @@ EnOcean_Set($@) } else { $anglePos = 0; $position = 0; + readingsSingleUpdate($hash, "endPosition", "open", 1); + $cmd = "open"; } readingsSingleUpdate($hash, "anglePos", sprintf("%d", $anglePos), 1); readingsSingleUpdate($hash, "position", sprintf("%d", $position), 1); $shutCmd = 1; - } elsif ($cmd eq "down" || $cmd eq "BI") { + } elsif ($cmd eq "down") { # down - if(defined $a[1]) { + if (defined $a[1]) { if ($a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] < 255) { $position += $a[1] / $shutTime * 100; if ($angleTime) { @@ -852,15 +1167,21 @@ EnOcean_Set($@) if($position > 100) { $anglePos = $angleMax; $position = 100; + readingsSingleUpdate($hash, "endPosition", "closed", 1); + $cmd = "closed"; } - $shutTime = $a[1]; - shift(@a); + readingsSingleUpdate($hash, "endPosition", "not_reached", 1); + $cmd = "not_reached"; + $shutTime = $a[1]; + shift(@a); } else { return "Usage: $a[1] is not numeric or out of range"; } } else { $anglePos = $angleMax; $position = 100; + readingsSingleUpdate($hash, "endPosition", "closed", 1); + $cmd = "closed"; } readingsSingleUpdate($hash, "anglePos", sprintf("%d", $anglePos), 1); readingsSingleUpdate($hash, "position", sprintf("%d", $position), 1); @@ -871,6 +1192,17 @@ EnOcean_Set($@) } elsif ($angleTime > 0 && !defined $anglePos){ return "Slats angle position unknown, please first opens the blinds completely." } else { + # check actual shutter position + my $actualState = ReadingsVal($name, "state", undef); + if (defined $actualState) { + if ($actualState eq "open") { + $position = 0; + $anglePos = 0; + } elsif ($actualState eq "closed") { + $position = 100; + $anglePos = $angleMax; + } + } my $anglePosLast = $anglePos; my $shutTimeSet = $shutTime; if (defined $a[2]) { @@ -927,6 +1259,16 @@ EnOcean_Set($@) $shutCmd = 0; } } + if ($position == 0) { + readingsSingleUpdate($hash, "endPosition", "open", 1); + $cmd = "open"; + } elsif ($position == 100) { + readingsSingleUpdate($hash, "endPosition", "closed", 1); + $cmd = "closed"; + } else { + readingsSingleUpdate($hash, "endPosition", "not_reached", 1); + $cmd = "not_reached"; + } readingsSingleUpdate($hash, "anglePos", sprintf("%d", $anglePos), 1); readingsSingleUpdate($hash, "position", sprintf("%d", $position), 1); shift(@a); @@ -937,7 +1279,7 @@ EnOcean_Set($@) } else { return "Unknown argument " . $cmd . ", choose one of closes down opens position:slider,0,5,100 stop teach up" } - if($shutCmd || $cmd eq "stop") { + if ($shutCmd || $cmd eq "stop") { $updateState = 0; $data = sprintf "%02X%02X%02X%02X", 0, $shutTime, $shutCmd, 8; } @@ -963,7 +1305,6 @@ EnOcean_Set($@) } elsif ($st eq "raw") { # sent raw data - # to do: optional data if ($cmd eq "4BS"){ # 4BS Telegram if ($a[1] && $a[1] =~ m/^[\dA-F]{8}$/) { @@ -988,6 +1329,23 @@ EnOcean_Set($@) } else { return "Wrong parameter, choose RPS [status 1 Byte hex]"; } + } elsif ($cmd eq "MSC") { + # MSC Telegram + if ($a[1] && $a[1] =~ m/^[\dA-F]{2,28}$/ && !(length($a[1]) % 2)) { + $data = $a[1]; + $rorg = "D1"; + } else { + return "Wrong parameter, choose MSC [status 1 Byte hex]"; + } + } elsif ($cmd eq "UTE") { + # UTE Telegram + if ($a[1] && $a[1] =~ m/^[\dA-F]{7}$/) { + $data = $a[1]; + $rorg = "D4"; + } else { + return "Wrong parameter, choose UTE [status 1 Byte hex]"; + } + } elsif ($cmd eq "VLD") { # VLD Telegram if ($a[1] && $a[1] =~ m/^[\dA-F]{2,28}$/ && !(length($a[1]) % 2)) { @@ -996,23 +1354,9 @@ EnOcean_Set($@) } else { return "Wrong parameter, choose VLD [status 1 Byte hex]"; } - } elsif ($cmd eq "timer") { - ### test - if ($a[1] && $a[1] =~ m/^[\d]{2}$/) { - $data = "09"; - $rorg = "F6"; - - readingsSingleUpdate($hash, "test", 127, 1); - readingsSingleUpdate($hash, "testScaled", EnOcean_ReadingScaled($hash, 127, 0, 255), 1) ; - my @timerCmd = ($name, "RPS", "08"); - my %par = (hash => $hash, timerCmd => \@timerCmd); - InternalTimer(gettimeofday() + $a[1], "EnOcean_TimerSet", \%par, 0); - } else { - return "Wrong parameter, choose timer