").
": move:$move:");
# do conversions
if ( AttrVal( $name, "positionInverse", 0 ) ) {
$newState = SOMFY_ConvertTo100To0( $newState );
$newExact = $newState;
$rounded = SOMFY_Runden( $newState );
$stateTrans = SOMFY_Translate100To0( $rounded );
} else {
$rounded = SOMFY_Runden( $newState );
$stateTrans = SOMFY_Translate( $rounded );
}
Log3($name,4,"SOMFY_UpdateState: $name after conversions newState:$newState: rounded:$rounded: stateTrans:$stateTrans:");
readingsBulkUpdate($hash,"state",$stateTrans);
$hash->{STATE} = $stateTrans;
readingsBulkUpdate($hash,"position",$rounded);
readingsBulkUpdate($hash,$addtlPosReading,$rounded) if ( defined($addtlPosReading) );
}
readingsBulkUpdate($hash,"exact",$newExact);
if ( defined( $updateState ) ) {
$hash->{updateState} = $updateState;
} else {
delete $hash->{updateState};
}
$hash->{move} = $move;
readingsEndUpdate($hash,$doTrigger);
} # end sub SOMFY_UpdateState
###################################
# Return timingvalues from attr and after correction
sub SOMFY_getTimingValues($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my $t1down100 = AttrVal($name,'drive-down-time-to-100',undef);
my $t1downclose = AttrVal($name,'drive-down-time-to-close',undef);
my $t1upopen = AttrVal($name,'drive-up-time-to-open',undef);
my $t1up100 = AttrVal($name,'drive-up-time-to-100',undef);
return (undef, undef, undef, undef) if(!defined($t1downclose) || !defined($t1down100) || !defined($t1upopen) || !defined($t1up100));
if ( ( $t1downclose < 0 ) || ( $t1down100 < 0 ) || ( $t1upopen < 0 ) || ( $t1up100 < 0 ) ) {
Log3($name,1,"SOMFY_getTimingValues: $name time values need to be positive values");
return (undef, undef, undef, undef);
}
if ( $t1downclose < $t1down100 ) {
Log3($name,1,"SOMFY_getTimingValues: $name close time needs to be higher or equal than time to pos100");
return (undef, undef, undef, undef);
} elsif ( $t1downclose == $t1down100 ) {
$t1up100 = 0;
}
if ( $t1upopen <= $t1up100 ) {
Log3($name,1,"SOMFY_getTimingValues: $name open time needs to be higher or equal than time to pos100");
return (undef, undef, undef, undef);
}
if ( $t1upopen < 1 ) {
Log3($name,1,"SOMFY_getTimingValues: $name time to open needs to be at least 1 second");
return (undef, undef, undef, undef);
}
if ( $t1downclose < 1 ) {
Log3($name,1,"SOMFY_getTimingValues: $name time to close needs to be at least 1 second");
return (undef, undef, undef, undef);
}
return ($t1down100, $t1downclose, $t1upopen, $t1up100);
}
###################################
# call with hash, translated state
sub SOMFY_CalcCurrentPos($$$$) {
my ($hash, $move, $pos, $dt) = @_;
my $name = $hash->{NAME};
my $newPos = $pos;
# Attributes for calculation
my ($t1down100, $t1downclose, $t1upopen, $t1up100) = SOMFY_getTimingValues($hash);
if(defined($t1down100) && defined($t1downclose) && defined($t1up100) && defined($t1upopen)) {
if( ( $t1downclose == $t1down100) && ( $t1up100 == 0 ) ) {
$pos = minNum( 100, $pos );
if($move eq 'on') {
$newPos = minNum( 100, $pos );
if ( $pos < 100 ) {
# calc remaining time to 100%
my $remTime = ( 100 - $pos ) * $t1down100 / 100;
if ( $remTime > $dt ) {
$newPos = $pos + ( $dt * 100 / $t1down100 );
}
}
} elsif($move eq 'off') {
$newPos = maxNum( 0, $pos );
if ( $pos > 0 ) {
$newPos = $dt * 100 / ( $t1upopen );
$newPos = maxNum( 0, ($pos - $newPos) );
}
} else {
Log3($name,1,"SOMFY_CalcCurrentPos: $name move wrong $move");
}
} else {
if($move eq 'on') {
if ( $pos >= 100 ) {
$newPos = $dt * 100 / ( $t1downclose - $t1down100 );
$newPos = minNum( 200, $pos + $newPos );
} else {
# calc remaining time to 100%
my $remTime = ( 100 - $pos ) * $t1down100 / 100;
if ( $remTime > $dt ) {
$newPos = $pos + ( $dt * 100 / $t1down100 );
} else {
$dt = $dt - $remTime;
$newPos = 100 + ( $dt * 100 / ( $t1downclose - $t1down100 ) );
}
}
} elsif($move eq 'off') {
if ( $pos <= 100 ) {
$newPos = $dt * 100 / ( $t1upopen - $t1up100 );
$newPos = maxNum( 0, $pos - $newPos );
} else {
# calc remaining time to 100%
my $remTime = ( $pos - 100 ) * $t1up100 / 100;
if ( $remTime > $dt ) {
$newPos = $pos - ( $dt * 100 / $t1up100 );
} else {
$dt = $dt - $remTime;
$newPos = 100 - ( $dt * 100 / ( $t1upopen - $t1up100 ) );
}
}
} else {
Log3($name,1,"SOMFY_CalcCurrentPos: $name move wrong $move");
}
}
} else {
### no timings set so just assume it is always moving
$newPos = $positions{'moving'};
}
return $newPos;
}
######################################################
######################################################
######################################################
1;
=pod
=item summary supporting devices using the SOMFY RTS protocol - window shades
=item summary_DE für Geräte, die das SOMFY RTS protocol unterstützen - Rolläden
=begin html
SOMFY - Somfy RTS / Simu Hz protocol
The Somfy RTS (identical to Simu Hz) protocol is used by a wide range of devices,
which are either senders or receivers/actuators.
Right now only SENDING of Somfy commands is implemented in the CULFW, so this module currently only
supports devices like blinds, dimmers, etc. through a CUL device (which must be defined first).
Reception of Somfy remotes is only supported indirectly through the usage of an FHEMduino
http://www.fhemwiki.de/wiki/FHEMduino
which can then be used to connect to the SOMFY device.
Define
define <name> SOMFY <address> [<encryption-key>] [<rolling-code>]
The address is a 6-digit hex code, that uniquely identifies a single remote control channel.
It is used to pair the remote to the blind or dimmer it should control.
Pairing is done by setting the blind in programming mode, either by disconnecting/reconnecting the power,
or by pressing the program button on an already associated remote.
Once the blind is in programming mode, send the "prog" command from within FHEM to complete the pairing.
The blind will move up and down shortly to indicate completion.
You are now able to control this blind from FHEM, the receiver thinks it is just another remote control.
<address>
is a 6 digit hex number that uniquely identifies FHEM as a new remote control channel.
You should use a different one for each device definition, and group them using a structure.
- The optional
<encryption-key>
is a 2 digit hex number (first letter should always be A)
that can be set to clone an existing remote control channel.
- The optional
<rolling-code>
is a 4 digit hex number that can be set
to clone an existing remote control channel.
If you set one of them, you need to pick the same address as an existing remote.
Be aware that the receiver might not accept commands from the remote any longer,
if you used FHEM to clone an existing remote.
This is because the code is original remote's codes are out of sync.
Examples:
define rollo_1 SOMFY 000001
define rollo_2 SOMFY 000002
define rollo_3_original SOMFY 42ABCD A5 0A1C
Set
set <name> <value> [<time>]
where value
is one of:
on
off
go-my
stop
pos value (0..100) # see note
prog # Special, see note
on-for-timer
off-for-timer
Examples:
set rollo_1 on
set rollo_1,rollo_2,rollo_3 on
set rollo_1-rollo_3 on
set rollo_1 off
set rollo_1 pos 50
Notes:
- prog is a special command used to pair the receiver to FHEM:
Set the receiver in programming mode (eg. by pressing the program-button on the original remote)
and send the "prog" command from FHEM to finish pairing.
The blind will move up and down shortly to indicate success.
- on-for-timer and off-for-timer send a stop command after the specified time,
instead of reversing the blind.
This can be used to go to a specific position by measuring the time it takes to close the blind completely.
- pos value
The position is variying between 0 completely open and 100 for covering the full window.
The position must be between 0 and 100 and the appropriate
attributes drive-down-time-to-100, drive-down-time-to-close,
drive-up-time-to-100 and drive-up-time-to-open must be set. See also positionInverse attribute.
The position reading distinuishes between multiple cases
- Without timing values (see attributes) set only generic values are used for status and position:
open, closed, moving
are used
- With timing values set but drive-down-time-to-close equal to drive-down-time-to-100 and drive-up-time-to-100 equal 0
the device is considered to only vary between 0 and 100 (100 being completely closed)
- With full timing values set the device is considerd a window shutter (Rolladen) with a difference between
covering the full window (position 100) and being completely closed (position 200)
Get
Attributes
- IODev
Set the IO or physical device which should be used for sending signals
for this "logical" device. An example for the physical device is a CUL.
Note: The IODev has to be set, otherwise no commands will be sent!
If you have both a CUL868 and CUL433, use the CUL433 as IODev for increased range.
- positionInverse
Inverse operation for positions instead of 0 to 100-200 the positions are ranging from 100 to 10 (down) and then to 0 (closed). The pos set command will point in this case to the reversed pos values. This does NOT reverse the operation of the on/off command, meaning that on always will move the shade down and off will move it up towards the initial position.
- additionalPosReading
Position of the shutter will be stored in the reading pos
as numeric value.
Additionally this attribute might specify a name for an additional reading to be updated with the same value than the pos.
- rolling-code < 4 digit hex >
Can be used to overwrite the rolling-code manually with a new value (rolling-code will be automatically increased with every command sent)
This requires also setting enc-key: only with bot attributes set the value will be accepted for the internal reading
- enc-key < 2 digit hex >
Can be used to overwrite the enc-key manually with a new value
This requires also setting rolling-code: only with bot attributes set the value will be accepted for the internal reading
- eventMap
Replace event names and set arguments. The value of this attribute
consists of a list of space separated values, each value is a colon
separated pair. The first part specifies the "old" value, the second
the new/desired value. If the first character is slash(/) or comma(,)
then split not by space but by this character, enabling to embed spaces.
Examples:
attr store eventMap on:open off:closed
attr store eventMap /on-for-timer 10:open/off:closed/
set store open
- do_not_notify
- dummy
Set the device attribute dummy to define devices which should not
output any radio signals. Associated notifys will be executed if
the signal is received. Used e.g. to react to a code from a sender, but
it will not emit radio signal if triggered in the web frontend.
- loglevel
- showtime
- model
The model attribute denotes the model type of the device.
The attributes will (currently) not be used by the fhem.pl directly.
It can be used by e.g. external programs or web interfaces to
distinguish classes of devices and send the appropriate commands
(e.g. "on" or "off" to a switch, "dim..%" to dimmers etc.).
The spelling of the model names are as quoted on the printed
documentation which comes which each device. This name is used
without blanks in all lower-case letters. Valid characters should be
a-z 0-9
and -
(dash),
other characters should be ommited.
Here is a list of "official" devices:
Receiver/Actor: somfyblinds
- ignore
Ignore this device, e.g. if it belongs to your neighbour. The device
won't trigger any FileLogs/notifys, issued commands will silently
ignored (no RF signal will be sent out, just like for the dummy attribute). The device won't appear in the
list command (only if it is explicitely asked for it), nor will it
appear in commands which use some wildcard/attribute as name specifiers
(see devspec). You still get them with the
"ignored=1" special devspec.
- drive-down-time-to-100
The time the blind needs to drive down from "open" (pos 0) to pos 100.
In this position, the lower edge touches the window frame, but it is not completely shut.
For a mid-size window this time is about 12 to 15 seconds.
- drive-down-time-to-close
The time the blind needs to drive down from "open" (pos 0) to "close", the end position of the blind.
Note: If set, this value always needs to be higher than drive-down-time-to-100
This is about 3 to 5 seonds more than the "drive-down-time-to-100" value.
- drive-up-time-to-100
The time the blind needs to drive up from "close" (endposition) to "pos 100".
This usually takes about 3 to 5 seconds.
- drive-up-time-to-open
The time the blind needs drive up from "close" (endposition) to "open" (upper endposition).
Note: If set, this value always needs to be higher than drive-down-time-to-100
This value is usually a bit higher than "drive-down-time-to-close", due to the blind's weight.
=end html
=cut