From 45d69b22dd6391086516618d64c47898b4295046 Mon Sep 17 00:00:00 2001 From: klaus-schauer <> Date: Thu, 30 May 2013 05:49:25 +0000 Subject: [PATCH] - profile manufProfile, 00D (Eltako Shutter): functions extended and optimized - profile roomSensorControl.[01|02|05|1F]: scaling function optimized - switch mode pushbutton: command released is now sent with a delay git-svn-id: https://svn.fhem.de/fhem/trunk@3227 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/10_EnOcean.pm | 290 ++++++++++++++++++++++++++++++---------- 1 file changed, 219 insertions(+), 71 deletions(-) diff --git a/fhem/FHEM/10_EnOcean.pm b/fhem/FHEM/10_EnOcean.pm index 1b74fa3d9..a7cf07cb0 100755 --- a/fhem/FHEM/10_EnOcean.pm +++ b/fhem/FHEM/10_EnOcean.pm @@ -175,7 +175,6 @@ my %EnO_subType = ( my @EnO_models = qw ( other - slats FSB14 FSB61 FSB70 FSM12 FSM61 FT55 @@ -197,14 +196,14 @@ EnOcean_Initialize($) $hash->{SetFn} = "EnOcean_Set"; $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 " . - "dimTime dimValueOn " . + "actualTemp angleMax:slider,-180,20,180 angleMin:slider,-180,20,180 " . + "angleTime:0,1,2,3,4,5,6 dimValueOn " . "model:" . join(",", @EnO_models) . " " . "gwCmd:" . join(",", sort @EnO_gwCmd) . " " . "manufID:" . join(",", keys %EnO_manuf) . " " . "rampTime repeatingAllowed:yes,no " . - "scaleMax scaleMin " . - "shutTime subDef subDef0 subDefI " . + "scaleDecimals:0,1,2,3,4,5,6,7,8,9 scaleMax scaleMin " . + "shutTime shutTimeCloses subDef subDef0 subDefI " . "subType:" . join(",", sort grep { !$subTypeList{$_}++ } values %EnO_subType) . " " . "subTypeSet:" . join(",", sort grep { !$subTypeSetList{$_}++ } values %EnO_subType) . " " . "switchMode:switch,pushbutton " . @@ -258,7 +257,6 @@ EnOcean_Set($@) my $switchMode = AttrVal($name, "switchMode", "switch"); my $tn = TimeNow(); my $updateState = 1; - shift @a; for(my $i = 0; $i < @a; $i++) { @@ -557,7 +555,7 @@ EnOcean_Set($@) $updateState = 0; $data = sprintf "A5%02X00%02X%02X", $gwCmdID, $controlVar, $setCmd; } else { - return "Unknown argument, choose one of teach presence energyHoldOff controllerMode controllerState"; + return "Unknown argument, choose one of teach presence:absent,present,standby energyHoldOff:holdoff,normal controllerMode:cooling,heating,off controllerState:auto,override"; } } elsif ($gwCmd eq "fanStage") { @@ -576,7 +574,7 @@ EnOcean_Set($@) } shift(@a); } else { - return "Unknown argument, choose one of teach stage"; + return "Unknown argument, choose one of teach stage:auto,0,1,2,3"; } } elsif ($gwCmd eq "blindCmd") { @@ -745,82 +743,194 @@ EnOcean_Set($@) } elsif ($st eq "manufProfile") { if ($manufID eq "00D") { # Eltako Shutter + my $angleMax = AttrVal($name, "angleMax", 90); + my $angleMin = AttrVal($name, "angleMin", -90); + my $anglePos = ReadingsVal($name, "anglePos", undef); + my $angleTime = AttrVal($name, "angleTime", 0); my $position = ReadingsVal($name, "position", undef); my $shutTime = AttrVal($name, "shutTime", 255); + my $shutTimeCloses = AttrVal($name, "shutTimeCloses", $shutTime); + $shutTimeCloses = $shutTime if ($shutTimeCloses < $shutTimeCloses); my $shutCmd = 0; + $angleMax = 90 if ($angleMax !~ m/^[+-]?\d+$/); + $angleMax = 180 if ($angleMax > 180); + $angleMax = -180 if ($angleMax < -180); + $angleMin = -90 if ($angleMin !~ m/^[+-]?\d+$/); + $angleMin = 180 if ($angleMin > 180); + $angleMin = -180 if ($angleMin < -180); + ($angleMax, $angleMin) = ($angleMin, $angleMax) if ($angleMin > $angleMax); + $angleMax ++ if ($angleMin == $angleMax); + $angleTime = 6 if ($angleTime !~ m/^[+-]?\d+$/); + $angleTime = 6 if ($angleTime > 6); + $angleTime = 0 if ($angleTime < 0); $shutTime = 255 if ($shutTime !~ m/^[+-]?\d+$/); $shutTime = 255 if ($shutTime > 255); $shutTime = 1 if ($shutTime < 1); if ($cmd eq "teach") { $data = "A5FFF80D80"; - $header = "000A0001"; - Log $ll2, "EnOcean: set $name $cmd"; } elsif ($cmd eq "stop") { + # stop + # delete readings, as they are undefined + CommandDeleteReading(undef, "$name anglePos"); + CommandDeleteReading(undef, "$name position"); + readingsSingleUpdate($hash, "state", "stop", 1); $shutCmd = 0; + } elsif ($cmd eq "opens") { + # opens >> B0 + $anglePos = 0; + readingsSingleUpdate($hash, "anglePos", $anglePos, 1); + $position = 0; + readingsSingleUpdate($hash, "position", $position, 1); + $shutTime = $shutTimeCloses; + $shutCmd = 1; + $updateState = 0; + } elsif ($cmd eq "closes") { + # closes >> BI + $anglePos = $angleMax; + readingsSingleUpdate($hash, "anglePos", $anglePos, 1); + $position = 100; + readingsSingleUpdate($hash, "position", $position, 1); + $shutTime = $shutTimeCloses; + $shutCmd = 2; + $updateState = 0; } elsif ($cmd eq "up" || $cmd eq "B0") { + # up if(defined $a[1]) { - if ($a[1] =~ m/^[+-]?\d+$/ && $a[1] > 0 && $a[1] <= 100) { - $shutTime = $shutTime / 100 * $a[1]; - $position -= $a[1]; - if($position <= 0) { $position = 0; } + if ($a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] <= 255) { + $position -= $a[1] / $shutTime * 100; + if ($angleTime) { + $anglePos -= ($angleMax - $angleMin) * $shutTime / $angleTime; + if ($anglePos < $angleMin) { + $anglePos = $angleMin; + } + } else { + $anglePos = $angleMin; + } + if ($position <= 0) { + $anglePos = 0; + $position = 0; + } + $shutTime = $a[1]; shift(@a); - } else { + } else { return "Usage: $a[1] is not numeric or out of range"; } } else { + $anglePos = 0; $position = 0; } - readingsSingleUpdate($hash,"position",$position,1); + readingsSingleUpdate($hash, "anglePos", sprintf("%d", $anglePos), 1); + readingsSingleUpdate($hash, "position", sprintf("%d", $position), 1); $shutCmd = 1; } elsif ($cmd eq "down" || $cmd eq "BI") { + # down if(defined $a[1]) { - if ($a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] < 100) { - $shutTime = $shutTime / 100 * $a[1]; - $position += $a[1]; - if($position > 100) { $position = 100; } - shift(@a); + if ($a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] < 255) { + $position += $a[1] / $shutTime * 100; + if ($angleTime) { + $anglePos += ($angleMax - $angleMin) * $shutTime / $angleTime; + if ($anglePos > $angleMax) { + $anglePos = $angleMax; + } + } else { + $anglePos = $angleMax; + } + if($position > 100) { + $anglePos = $angleMax; + $position = 100; + } + $shutTime = $a[1]; + shift(@a); } else { return "Usage: $a[1] is not numeric or out of range"; } } else { + $anglePos = $angleMax; $position = 100; } - readingsSingleUpdate($hash,"position",$position,1); + readingsSingleUpdate($hash, "anglePos", sprintf("%d", $anglePos), 1); + readingsSingleUpdate($hash, "position", sprintf("%d", $position), 1); $shutCmd = 2; } elsif ($cmd eq "position") { if (!defined $position) { - return "Position unknown, please first open the blinds completely." + return "Position unknown, please first opens the blinds completely." + } elsif ($angleTime > 0 && !defined $anglePos){ + return "Slats angle position unknown, please first opens the blinds completely." } else { + my $anglePosLast = $anglePos; + my $shutTimeSet = $shutTime; + if (defined $a[2]) { + if ($a[2] =~ m/^[+-]?\d+$/ && $a[2] >= $angleMin && $a[2] <= $angleMax) { + $anglePos = $a[2]; + } else { + return "Usage: $a[1] $a[2] is not numeric or out of range"; + } + splice(@a,2,1); + } else { + $anglePos = $angleMax; + } if (defined $a[1] && $a[1] =~ m/^[+-]?\d+$/ && $a[1] >= 0 && $a[1] <= 100) { if ($position < $a[1]) { # down - $shutTime = $shutTime / 100 * ($a[1] - $position); + ## + $angleTime = $angleTime * ($angleMax - $anglePos)/($angleMax - $angleMin); + $shutTime = $shutTime * ($a[1] - $position) / 100 + $angleTime; + $position = $a[1] + $angleTime / $shutTimeSet * 100; + if ($position >= 100) { + $position = 100; + } $shutCmd = 2; + if ($angleTime) { + my @timerCmd = ($name, "up", $angleTime); + my %par = (hash => $hash, timerCmd => \@timerCmd); + InternalTimer(gettimeofday() + $shutTime + 1, "EnOcean_TimerSet", \%par, 0); + } } elsif ($position > $a[1]) { # up - $shutTime = $shutTime / 100 * ($position - $a[1]); + ## + $angleTime = $angleTime * ($anglePos - $angleMin) /($angleMax - $angleMin); + $shutTime = $shutTime * ($position - $a[1]) / 100 + $angleTime; + $position = $a[1] - $angleTime / $shutTimeSet * 100; + if ($position <= 0) { + $position = 0; + $anglePos = 0; + } $shutCmd = 1; + if ($angleTime && $a[1] > 0) { + my @timerCmd = ($name, "down", $angleTime); + my %par = (hash => $hash, timerCmd => \@timerCmd); + InternalTimer(gettimeofday() + $shutTime + 1, "EnOcean_TimerSet", \%par, 0); + } } else { - # position ok - $shutCmd = 0; + if ($anglePosLast > $anglePos) { + # up + $shutTime = $angleTime * ($anglePosLast - $anglePos)/($angleMax - $angleMin); + $shutCmd = 1; + } elsif ($anglePosLast < $anglePos) { + # down + $shutTime = $angleTime * ($anglePos - $anglePosLast) /($angleMax - $angleMin); + $shutCmd = 2; + } else { + # position and slats angle ok + $shutCmd = 0; + } } - readingsSingleUpdate($hash,"position",$a[1],1); + readingsSingleUpdate($hash, "anglePos", sprintf("%d", $anglePos), 1); + readingsSingleUpdate($hash, "position", sprintf("%d", $position), 1); shift(@a); } else { return "Usage: $a[1] is not numeric or out of range"; } } } else { - return "Unknown argument " . $cmd . ", choose one of position:slider,0,5,100 up stop down teach" + return "Unknown argument " . $cmd . ", choose one of closes down opens position:slider,0,5,100 stop teach up" } if($shutCmd || $cmd eq "stop") { $updateState = 0; $data = sprintf "A5%02X%02X%02X%02X", 0, $shutTime, $shutCmd, 8; - $header = "000A0001"; - Log $ll2, "EnOcean: set $name $cmd"; } - } else { - return "Manufacturer Specific Application unknown. Set correct attr manufID."; + $header = "000A0001"; + Log $ll2, "EnOcean: set $name $cmd"; } } elsif ($st eq "contact") { @@ -868,7 +978,7 @@ EnOcean_Set($@) return "Wrong parameter, choose RPS [status 1 Byte hex]"; } } else { - return "Unknown argument $cmd, choose one of RPS 1BS 4BS timer"; + return "Unknown argument $cmd, choose one of RPS 1BS 4BS"; } if ($a[2]) { if ($a[2] !~ m/^[\dA-F]{2}$/) { @@ -967,6 +1077,8 @@ EnOcean_Set($@) if ($switchMode eq "pushbutton") { $data = "F600"; $status = "20"; + # next commands will be sent with a delay + select(undef, undef, undef, 0.2); Log $ll2, "EnOcean: set $name released"; $data = sprintf "%s%s%s", $data, $subDef, $status; IOWrite ($hash, $header, $data); @@ -1373,7 +1485,7 @@ EnOcean_Parse($$) push @event, "3:fan:$fspeed"; push @event, "3:switch:$switch"; push @event, "3:setpoint:$db_2"; - EnOcean_ReadingScaled($hash, "setpoint", 0, 255); + push @event, "3:setpointScaled:" . EnOcean_ReadingScaled($hash, $db_2, 0, 255); } push @event, "3:temperature:$temp"; @@ -1392,7 +1504,7 @@ EnOcean_Parse($$) push @event, "3:switch:$switch"; push @event, "3:setpoint:$db_2"; push @event, "3:temperature:$temp"; - EnOcean_ReadingScaled($hash, "setpoint", 0, 255); + push @event, "3:setpointScaled:" . EnOcean_ReadingScaled($hash, $db_2, 0, 255); } elsif($st eq "roomSensorControl.02") { # Room Sensor and Control Unit (A5-10-15 ... A5-10-17) @@ -1407,7 +1519,7 @@ EnOcean_Parse($$) push @event, "3:presence:$presence"; push @event, "3:setpoint:$setpoint"; push @event, "3:temperature:$temp"; - EnOcean_ReadingScaled($hash, "setpoint", 0, 63); + push @event, "3:setpointScaled:" . EnOcean_ReadingScaled($hash, $setpoint, 0, 63); } elsif($st eq "roomSensorControl.18") { # Room Sensor and Control Unit (A5-10-18) @@ -1644,7 +1756,7 @@ EnOcean_Parse($$) push @event, "3:setpoint:$setpoint"; push @event, "3:temperature:$temp"; push @event, "3:state:T: $temp F: $fanSpeed SP: $setpoint P: $presence"; - EnOcean_ReadingScaled($hash, "setpoint", 0, 255); + push @event, "3:setpointScaled:" . EnOcean_ReadingScaled($hash, $setpoint, 0, 255); } elsif($st eq "tempHumiSensor.02") { # Temperatur and Humidity Sensor(EEP A5-04-02) @@ -2368,19 +2480,26 @@ EnOcean_TimerSet($) sub EnOcean_ReadingScaled($$$$) { - my ($hash, $readingName, $readingMin, $readingMax) = @_; + my ($hash, $readingVal, $readingMin, $readingMax) = @_; my $name = $hash->{NAME}; - my $readingVal = ReadingsVal($hash->{NAME}, $readingName, undef); + my $valScaled; +# my $readingVal = ReadingsVal($hash->{NAME}, $readingName, undef); + my $scaleDecimals = AttrVal($name, "scaleDecimals", undef); my $scaleMin = AttrVal($name, "scaleMin", undef); my $scaleMax = AttrVal($name, "scaleMax", undef); if (defined $scaleMax && defined $scaleMin && $scaleMax =~ m/^[+-]?\d+(\.\d+)?$/ && $scaleMax =~ m/^[+-]?\d+(\.\d+)?$/) { - my $valScaled = ($readingMin*$scaleMax-$scaleMin*$readingMax)/ - ($readingMin-$readingMax)+ - ($scaleMin-$scaleMax)/($readingMin-$readingMax)*$readingVal; - readingsSingleUpdate($hash, $readingName."Scaled", $valScaled, 1); - } -} + $valScaled = ($readingMin*$scaleMax-$scaleMin*$readingMax)/ + ($readingMin-$readingMax)+ + ($scaleMin-$scaleMax)/($readingMin-$readingMax)*$readingVal; +# readingsSingleUpdate($hash, $readingName."Scaled", $valScaled, 1); + } + if (defined $scaleDecimals && $scaleDecimals =~ m/^[0-9]?$/) { + $scaleDecimals = "%0." . $scaleDecimals . "f"; + $valScaled = sprintf "$scaleDecimals", $valScaled; + } + return $valScaled; +} # Undef sub @@ -2776,17 +2895,17 @@ EnOcean_Undef($$)
  • down td/s ta/s
    issue roll down command
  • position position/% α/°
    - drive blinds to postion with angle value
  • + drive blinds to position with angle value
  • stop
    issue blinds stops command
  • runtimeSet tu/s td/s
    set runtime parameter
  • angleSet ta/s
    set angle configuration
  • -
  • positionMinMax positionMin/% positionMin/%
    - set Min, Max values for position
  • -
  • angleMinMax αs/° αo/°
    - set slat angle for Shut und Open position
  • +
  • positionMinMax positionMin/% positionMax/%
    + set min, max values for position
  • +
  • angleMinMax αo/° αs/°
    + set slat angle for open and shut position
  • positionLogic normal|inverse
    set position logic

  • @@ -2798,7 +2917,7 @@ EnOcean_Undef($$) Angle Time Range: ta = 0 s ... 25.5 s
    Runtime value for the sunblind reversion time. Select the time to revolve the sunblind from one slat angle end position to the other end position.
    - Slat Angle: α|αs|αo = -180 ° ... 180 °
    + Slat Angle: α|αo|αs = -180 ° ... 180 °
    Position Logic, normal: Blinds fully opens corresponds to Position = 0 %
    Position Logic, inverse: Blinds fully opens corresponds to Position = 100 %
    The attr subType must be gateway and gwCmd must be blindCmd. The profile @@ -2815,18 +2934,30 @@ EnOcean_Undef($$) where value is
  • teach
    initiate teach-in mode
  • -
  • up [position/%]
    +
  • opens
    + issue blinds opens command
  • +
  • up tu/s
    issue roll up command
  • -
  • down [position/%]
    +
  • closes
    + issue blinds closes command
  • +
  • down td/s
    issue roll down command
  • -
  • position position/%
    - set shutter to position
  • +
  • position position/% [α/°]
    + drive blinds to position with angle value
  • stop
    issue stop command

  • - Set attr subType to manufProfile, manufID to 00D and attr model to - FSB14|FSB61|FSB70 manually.
    - Use the sensor type "Szenentaster/PC" for Eltako devices. + Runtime Range: tu|td = 1 s ... 255 s
    + Position Range: position = 0 % ... 100 %
    + Slat Angle Range: α = -180 ° ... 180 °
    + Angle Time Range: ta = 0 s ... 6 s
    + The devive can only fully controlled if the attributes angleMax, + angleMin, angleTime, + shutTime and shutTimeCloses, + are set correctly. + Set attr subType to manufProfile, manufID to 00D and attr model to + FSB14|FSB61|FSB70 manually.
    + Use the sensor type "Szenentaster/PC" for Eltako devices.

    @@ -2855,18 +2986,26 @@ EnOcean_Undef($$) Attributes
    - The scaling of the setpoint knob is device- and vendor-specific. Set the + The scaling of the setpoint adjustment is device- and vendor-specific. Set the attributes scaleMax and scaleMin for the additional scaled reading setpointScaled. Use attribut userReadings to adjust the scaling alternatively.
    @@ -3436,7 +3584,7 @@ EnOcean_Undef($$)
  • temperature: t/°C (Sensor Range: t = 0 °C ... 40 °C)
  • state: T: t/°C F: 0|1|2|3|auto SP: 0 ... 255 P: absent|present|disabled

  • - The scaling of the setpoint knob is device- and vendor-specific. Set the + The scaling of the setpoint adjustment is device- and vendor-specific. Set the attributes scaleMax and scaleMin for the additional scaled reading setpointScaled. Use attribut userReadings to adjust the scaling alternatively.