2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-10 09:16:53 +00:00

10_SOMFY: positionInverse für homebridge + fixes

git-svn-id: https://svn.fhem.de/fhem/trunk@12388 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
viegener 2016-10-20 18:39:04 +00:00
parent e299bf80ce
commit d417df7f97
2 changed files with 383 additions and 132 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- feature: 10_SOMFY: positionInverse für homebridge + fixes
- feature: 50_TelegramBot: multibot support / markup on send text / msgEdit
- feature: 93_DbRep: get svrinfo contains SQLite database file size (MB)
- feature: 93_DbRep: get data of dbstatus, dbvars, tableinfo, svrinfo

View File

@ -1,13 +1,33 @@
######################################################
# $Id$
##############################################################################
#
# 10_SOMFY.pm
#
# This file is part of Fhem.
#
# Fhem is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# Fhem is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Fhem. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
#
# $Id$
#
# SOMFY RTS / Simu Hz protocol module for FHEM
# (c) Thomas Dankert <post@thomyd.de>
# (c) Johannes Viegener / https://github.com/viegener/Telegram-fhem/tree/master/Somfy
#
# Needs CULFW V 1.59 or higher (support for "Y" command).
#
# Published under GNU GPL License, v2
# Discussed in FHEM Forum: https://forum.fhem.de/index.php/topic,53319.msg450080.html#msg450080
#
##############################################################################
# History:
# 1.0 thomyd initial implementation
#
@ -49,6 +69,22 @@
# 2016-05-11 viegener - Handover SOMFY from thdankert/thomyd
# 2016-05-11 viegener - Cleanup Todolist
# 2016-05-11 viegener - Some additions to documentation (commandref)
# 2016-05-13 habichvergessen - Extend SOMFY module to use Signalduino as iodev
# 2016-05-13 viegener - Fix for CUL-SCC
# 2016-05-16 habichvergessen - Fixes - on newly (autocreated entries)
# 2016-05-16 habichvergessen - add rolling code / enckey for autocreate
# 2016-05-16 viegener - Minor cleanup on code
# 2016-05-16 viegener - Fix Issue#5 - autocreate preparation (code in modules pointer for address also checked for empty hash)
# 2016-05-16 viegener - Ensure Ys message length correct before analyzing
# 2016-05-29 viegener - Correct define for readingsval on rollingcode etc
# 2016-05-29 viegener - Some cleanup - translations reduced
# 2016-05-29 viegener - remove internals exact/position use only readings
# 2016-05-29 viegener - Fix value in exact not being numeric (Forum449583)
# 2016-10-06 viegener - add summary for fhem commandref
# 2016-10-06 viegener - positionInverse for inverse operation 100 open 10 down 0 closed
# 2016-10-17 viegener - positionInverse test and fixes
# 2016-10-18 viegener - positionInverse documentation and complettion (no change to set on/off logic)
#
#
#
###############################################################################
@ -59,9 +95,11 @@
###############################################################################
# Somfy Modul - OPEN
###############################################################################
#
# -
# - Autocreate
# - Complete shutter / blind as different model
# -
# -
# - Make better distinction between different IoTypes - CUL+SCC / Signalduino
# -
# -
#
@ -106,6 +144,13 @@ my %sendCommands = (
"stop" => "stop"
);
my %inverseCommands = (
"off" => "on",
"on" => "off",
"on-for-timer" => "off-for-timer",
"off-for-timer" => "on-for-timer"
);
my %somfy_c2b;
my $somfy_defsymbolwidth = 1240; # Default Somfy frame symbol width
@ -139,27 +184,25 @@ my %positions = (
my %translations = (
"0" => "open",
"10" => "10",
"20" => "20",
"30" => "30",
"40" => "40",
"50" => "50",
"60" => "60",
"70" => "70",
"80" => "80",
"90" => "90",
"100" => "100",
"150" => "down",
"200" => "closed"
);
my %translations100To0 = (
"100" => "open",
"10" => "down",
"0" => "closed"
);
##################################################
# Forward declarations
#
sub SOMFY_CalcCurrentPos($$$$);
######################################################
######################################################
@ -196,6 +239,7 @@ sub SOMFY_Initialize($) {
. " drive-up-time-to-100"
. " drive-up-time-to-open "
. " additionalPosReading "
. " positionInverse:1,0 "
. " IODev"
. " symbol-length"
. " enc-key"
@ -250,8 +294,6 @@ sub SOMFY_Define($$) {
$hash->{ADDRESS} = uc($address);
my $tn = TimeNow();
# check optional arguments for device definition
if ( int(@a) > 3 ) {
@ -261,11 +303,13 @@ sub SOMFY_Define($$) {
. "specify a 2 digits hex value (first nibble = A) "
}
# reset reading time on def to 0 seconds (1970)
my $tzero = FmtDateTime(0);
# store it as reading, so it is saved in the statefile
# only store it, if the reading does not exist yet
my $old_enc_key = uc(ReadingsVal($name, "enc_key", "invalid"));
if($old_enc_key eq "invalid") {
setReadingsVal($hash, "enc_key", uc($a[3]), $tn);
if(! defined( ReadingsVal($name, "enc_key", undef) )) {
setReadingsVal($hash, "enc_key", uc($a[3]), $tzero);
}
if ( int(@a) == 5 ) {
@ -276,9 +320,8 @@ sub SOMFY_Define($$) {
}
# store it, if old reading does not exist yet
my $old_rolling_code = uc(ReadingsVal($name, "rolling_code", "invalid"));
if($old_rolling_code eq "invalid") {
setReadingsVal($hash, "rolling_code", uc($a[4]), $tn);
if(! defined( ReadingsVal($name, "rolling_code", undef) )) {
setReadingsVal($hash, "rolling_code", uc($a[4]), $tzero);
}
}
}
@ -319,6 +362,9 @@ sub SOMFY_SendCommand($@)
my $name = $hash->{NAME};
my $numberOfArgs = int(@args);
my $io = $hash->{IODev};
my $ioType = $io->{TYPE};
Log3($name,4,"SOMFY_sendCommand: $name -> cmd :$cmd: ");
# custom control needs 2 digit hex code
@ -334,43 +380,42 @@ sub SOMFY_SendCommand($@)
. join( " ", sort keys %somfy_c2b );
}
my $io = $hash->{IODev};
## Do we need to change RFMode to SlowRF?
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"switch_rfmode"} ) )
{
if ( $attr{ $name }{"switch_rfmode"} eq "1" )
{ # do we need to change RFMode of IODev
my $ret =
CallFn( $io->{NAME}, "AttrFn", "set",
( $io->{NAME}, "rfmode", "SlowRF" ) );
# CUL specifics
if ($ioType ne "SIGNALduino") {
## Do we need to change RFMode to SlowRF?
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"switch_rfmode"} ) )
{
if ( $attr{ $name }{"switch_rfmode"} eq "1" )
{ # do we need to change RFMode of IODev
my $ret =
CallFn( $io->{NAME}, "AttrFn", "set",
( $io->{NAME}, "rfmode", "SlowRF" ) );
}
}
## Do we need to change symbol length?
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"symbol-length"} ) )
{
$message = "t" . $attr{ $name }{"symbol-length"};
IOWrite( $hash, "Y", $message );
Log GetLogLevel( $name, 4 ),
"SOMFY set symbol-length: $message for $io->{NAME}";
}
## Do we need to change frame repetition?
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"repetition"} ) )
{
$message = "r" . $attr{ $name }{"repetition"};
IOWrite( $hash, "Y", $message );
Log GetLogLevel( $name, 4 ),
"SOMFY set repetition: $message for $io->{NAME}";
}
}
## Do we need to change symbol length?
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"symbol-length"} ) )
{
$message = "t" . $attr{ $name }{"symbol-length"};
IOWrite( $hash, "Y", $message );
Log GetLogLevel( $name, 4 ),
"SOMFY set symbol-length: $message for $io->{NAME}";
}
## Do we need to change frame repetition?
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"repetition"} ) )
{
$message = "r" . $attr{ $name }{"repetition"};
IOWrite( $hash, "Y", $message );
Log GetLogLevel( $name, 4 ),
"SOMFY set repetition: $message for $io->{NAME}";
}
my $value = $name ." ". join(" ", @args);
# convert old attribute values to READINGs
my $timestamp = TimeNow();
if(defined($attr{$name}{"enc-key"} && defined($attr{$name}{"rolling-code"}))) {
@ -401,12 +446,23 @@ sub SOMFY_SendCommand($@)
. uc( $hash->{ADDRESS} );
## Log that we are going to switch Somfy
Log GetLogLevel( $name, 4 ), "SOMFY set $value: $message";
( undef, $value ) = split( " ", $value, 2 ); # Not interested in the name...
Log GetLogLevel( $name, 4 ), "SOMFY set $name " . join(" ", @args) . ": $message";
## Send Message to IODev using IOWrite
Log3($name,5,"SOMFY_sendCommand: $name -> message :$message: ");
IOWrite( $hash, "Y", $message );
if ($ioType eq "SIGNALduino") {
my $SignalRepeats = AttrVal($name,'repetition', '6');
# swap address, remove leading s
my $decData = substr($message, 1, 8) . substr($message, 13, 2) . substr($message, 11, 2) . substr($message, 9, 2);
my $check = SOMFY_RTS_Check($name, $decData);
my $encData = SOMFY_RTS_Crypt("e", $name, substr($decData, 0, 3) . $check . substr($decData, 4));
$message = 'P43#' . $encData . '#R' . $SignalRepeats;
#Log3 $hash, 4, "$hash->{IODev}->{NAME} SOMFY_sendCommand: $name -> message :$message: ";
IOWrite($hash, 'sendMsg', $message);
} else {
Log3($name,5,"SOMFY_sendCommand: $name -> message :$message: ");
IOWrite( $hash, "Y", $message );
}
# increment encryption key and rolling code
my $enc_key_increment = hex( $enckey );
@ -419,38 +475,41 @@ sub SOMFY_SendCommand($@)
setReadingsVal($hash, "enc_key", $new_enc_key, $timestamp);
setReadingsVal($hash, "rolling_code", $new_rolling_code, $timestamp);
## Do we need to change symbol length back?
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"symbol-length"} ) )
{
$message = "t" . $somfy_defsymbolwidth;
IOWrite( $hash, "Y", $message );
Log GetLogLevel( $name, 4 ),
"SOMFY set symbol-length back: $message for $io->{NAME}";
}
## Do we need to change repetition back?
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"repetition"} ) )
{
$message = "r" . $somfy_defrepetition;
IOWrite( $hash, "Y", $message );
Log GetLogLevel( $name, 4 ),
"SOMFY set repetition back: $message for $io->{NAME}";
}
## Do we need to change RFMode back to HomeMatic??
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"switch_rfmode"} ) )
{
if ( $attr{ $name }{"switch_rfmode"} eq "1" )
{ # do we need to change RFMode of IODev?
my $ret =
CallFn( $io->{NAME}, "AttrFn", "set",
( $io->{NAME}, "rfmode", "HomeMatic" ) );
# CUL specifics
if ($ioType ne "SIGNALduino") {
## Do we need to change symbol length back?
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"symbol-length"} ) )
{
$message = "t" . $somfy_defsymbolwidth;
IOWrite( $hash, "Y", $message );
Log GetLogLevel( $name, 4 ),
"SOMFY set symbol-length back: $message for $io->{NAME}";
}
## Do we need to change repetition back?
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"repetition"} ) )
{
$message = "r" . $somfy_defrepetition;
IOWrite( $hash, "Y", $message );
Log GetLogLevel( $name, 4 ),
"SOMFY set repetition back: $message for $io->{NAME}";
}
## Do we need to change RFMode back to HomeMatic??
if ( defined( $attr{ $name } )
&& defined( $attr{ $name }{"switch_rfmode"} ) )
{
if ( $attr{ $name }{"switch_rfmode"} eq "1" )
{ # do we need to change RFMode of IODev?
my $ret =
CallFn( $io->{NAME}, "AttrFn", "set",
( $io->{NAME}, "rfmode", "HomeMatic" ) );
}
}
}
##########################
# Look for all devices with the same address, and set state, enc-key, rolling-code and timestamp
my $code = "$hash->{ADDRESS}";
@ -463,6 +522,7 @@ sub SOMFY_SendCommand($@)
$lh->{READINGS}{rolling_code}{TIME} = $tn;
$lh->{READINGS}{rolling_code}{VAL} = $new_rolling_code;
}
return $ret;
} # end sub SOMFY_SendCommand
@ -489,13 +549,43 @@ sub SOMFY_Translate($) {
}
return $v
} # end sub SOMFY_Runden
}
###################################
sub SOMFY_Translate100To0($) {
my ($v) = @_;
if(exists($translations100To0{$v})) {
$v = $translations100To0{$v}
}
return $v
}
#############################
sub SOMFY_Parse($$) {
my ($hash, $msg) = @_;
my $name = $hash->{NAME};
my $ioType = $hash->{TYPE};
# return "IODev unsupported" if ((my $ioType = $hash->{TYPE}) !~ m/^(CUL|SIGNALduino)$/);
# preprocessing if IODev is SIGNALduino
if ($ioType eq "SIGNALduino") {
my $encData = substr($msg, 2);
return "Somfy RTS message format error!" if ($encData !~ m/A[0-9A-F]{13}/);
my $decData = SOMFY_RTS_Crypt("d", $name, $encData);
my $check = SOMFY_RTS_Check($name, $decData);
return "Somfy RTS checksum error!" if ($check ne substr($decData, 3, 1));
Log3 $name, 4, "$name: Somfy RTS preprocessing check: $check enc: $encData dec: $decData";
$msg = substr($msg, 0, 2) . $decData;
}
# Msg format:
# Ys AB 2C 004B 010010
# address needs bytes 1 and 3 swapped
@ -505,8 +595,12 @@ sub SOMFY_Parse($$) {
return $hash->{NAME};
}
# get address
my $address = uc(substr($msg, 14, 2).substr($msg, 12, 2).substr($msg, 10, 2));
# Check for correct length
return "SOMFY incorrect length for command (".$msg.") / length should be 16" if ( length($msg) != 16 );
# get address
my $address = uc(substr($msg, 14, 2).substr($msg, 12, 2).substr($msg, 10, 2));
# get command and set new state
my $cmd = sprintf("%X", hex(substr($msg, 4, 2)) & 0xF0);
@ -518,7 +612,7 @@ sub SOMFY_Parse($$) {
my $def = $modules{SOMFY}{defptr}{$address};
if($def) {
if ( ($def) && (keys %{ $def }) ) { # Check also for empty hash --> issue #5
my @list;
foreach my $name (keys %{ $def }) {
my $lh = $def->{$name};
@ -539,8 +633,12 @@ sub SOMFY_Parse($$) {
return @list;
} else {
Log3 $hash, 1, "SOMFY Unknown device $address, please define it";
return "UNDEFINED SOMFY_$address SOMFY $address";
# rolling code and enckey
my $rolling = substr($msg, 6, 4);
my $encKey = substr($msg, 2, 2);
Log3 $hash, 1, "SOMFY Unknown device $address ($encKey $rolling), please define it";
return "UNDEFINED SOMFY_$address SOMFY $address $encKey $rolling";
}
}
##############################
@ -553,7 +651,48 @@ sub SOMFY_Attr(@) {
# $cmd can be "del" or "set"
# $name is device name
# aName and aVal are Attribute name and value
if ($cmd eq "set") {
# Convert in case of change to positionINverse --> but only after init is done on restart this should not be recalculated
if ( ($aName eq 'positionInverse') && ( $init_done ) ) {
my $rounded;
my $stateTrans;
my $pos = ReadingsVal($name,'exact',undef);
if ( !defined($pos) ) {
$pos = ReadingsVal($name,'position',undef);
}
if ($cmd eq "set") {
if ( ( $aVal ) && ( ! AttrVal( $name, "positionInverse", 0 ) ) ) {
# set to 1 and was 0 before - convert To100To10
# first exact then round to pos
$pos = SOMFY_ConvertTo100To0( $pos );
$rounded = SOMFY_Runden( $pos );
$stateTrans = SOMFY_Translate100To0( $rounded );
} elsif ( ( ! $aVal ) && ( AttrVal( $name, "positionInverse", 0 ) ) ) {
# set to 0 and was 1 before - convert From100To10
# first exact then round to pos
$pos = SOMFY_ConvertFrom100To0( $pos );
$rounded = SOMFY_Runden( $pos );
$stateTrans = SOMFY_Translate( $rounded );
}
} elsif ($cmd eq "del") {
if ( AttrVal( $name, "positionInverse", 0 ) ) {
# delete and was 1 before - convert From100To10
# first exact then round to pos
$pos = SOMFY_ConvertFrom100To0( $pos );
$rounded = SOMFY_Runden( $pos );
$stateTrans = SOMFY_Translate( $rounded );
}
}
if ( defined( $rounded ) ) {
readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"position",$rounded);
readingsBulkUpdate($hash,"exact",$pos);
readingsBulkUpdate($hash,"state",$stateTrans);
$hash->{STATE} = $stateTrans;
readingsEndUpdate($hash,1);
}
} elsif ($cmd eq "set") {
if($aName eq 'drive-up-time-to-100') {
return "SOMFY_attr: value must be >=0 and <= 100" if($aVal < 0 || $aVal > 100);
} elsif ($aName =~/drive-(down|up)-time-to.*/) {
@ -653,6 +792,7 @@ sub SOMFY_InternalSet($@) {
$opts = $sets{$k};
if (defined($opts)) {
$opts = "100,90,80,70,60,50,40,30,20,10,0" if ( $k eq "pos" );
push(@cList,$k . ':' . $opts);
} else {
push (@cList,$k);
@ -677,18 +817,11 @@ sub SOMFY_InternalSet($@) {
if($cmd eq 'pos') {
return "SOMFY_set: No pos specification" if(!defined($arg1));
return "SOMFY_set: $arg1 must be > 0 and < 100 for pos" if($arg1 < 0 || $arg1 > 100);
return "SOMFY_set: $arg1 must be between 0 and 100 for pos" if($arg1 < 0 || $arg1 > 100);
return "SOMFY_set: Please set attr drive-down-time-to-100, drive-down-time-to-close, etc"
if(!defined($t1downclose) || !defined($t1down100) || !defined($t1upopen) || !defined($t1up100));
}
### initialize locals
my $drivetime = 0; # timings until halt command to be sent for on/off-for-timer and pos <value> -> move by time
my $updatetime = 0; # timing until update of pos to be done for any unlimited move move to endpos or go-my / stop
my $move = $cmd;
my $newState;
my $updateState;
# get current infos
my $state = $hash->{STATE};
my $pos = ReadingsVal($name,'exact',undef);
@ -696,12 +829,31 @@ sub SOMFY_InternalSet($@) {
$pos = ReadingsVal($name,'position',undef);
}
# do conversions
if ( AttrVal( $name, "positionInverse", 0 ) ) {
Log3($name,4,"SOMFY_set: $name Inverse before cmd:$cmd: arg1:$arg1: pos:$pos:");
$arg1 = SOMFY_ConvertFrom100To0( $arg1 ) if($cmd eq 'pos');
$pos = SOMFY_ConvertFrom100To0( $pos );
# $cmd = $inverseCommands{$cmd} if(exists($inverseCommands{$cmd}));
Log3($name,4,"SOMFY_set: $name Inverse after cmd:$cmd: arg1:$arg1: pos:$pos:");
}
### initialize locals
my $drivetime = 0; # timings until halt command to be sent for on/off-for-timer and pos <value> -> move by time
my $updatetime = 0; # timing until update of pos to be done for any unlimited move move to endpos or go-my / stop
my $move = $cmd;
my $newState;
my $updateState;
# translate state info to numbers - closed = 200 , open = 0 (correct missing values)
if ( !defined($pos) ) {
if(exists($positions{$state})) {
$pos = $positions{$state};
} else {
$pos = $state;
$pos = ($state ne "???" ? $state : 0); # fix runtime error
}
$pos = sprintf( "%d", $pos );
}
@ -885,9 +1037,9 @@ sub SOMFY_InternalSet($@) {
## special case close is at 100 ("markisen")
if( ( $t1downclose == $t1down100) && ( $t1up100 == 0 ) ) {
if ( defined( $updateState )) {
$updateState = min( 100, $updateState );
$updateState = minNum( 100, $updateState );
}
$newState = min( 100, $posRounded );
$newState = minNum( 100, $posRounded );
}
}
@ -962,8 +1114,75 @@ sub SOMFY_InternalSet($@) {
###
######################################################
#############################
sub SOMFY_ConvertFrom100To0($) {
my ($v) = @_;
return $v if ( ! defined($v) );
return $v if ( length($v) == 0 );
$v = minNum( 100, maxNum( 0, $v ) );
return (( $v < 10 ) ? ( 200-($v*10.0) ) : ( (100-$v)*10.0/9 ));
}
###################################
#############################
sub SOMFY_ConvertTo100To0($) {
my ($v) = @_;
return $v if ( ! defined($v) );
return $v if ( length($v) == 0 );
$v = minNum( 200, maxNum( 0, $v ) );
return ( $v > 100 ) ? ( (200-$v)/10.0 ) : ( 100-(9*$v/10.0) );
}
#############################
sub SOMFY_RTS_Crypt($$$)
{
my ($operation, $name, $data) = @_;
my $res = substr($data, 0, 2);
my $ref = ($operation eq "e" ? \$res : \$data);
for (my $idx=1; $idx < 7; $idx++)
{
my $high = hex(substr($data, $idx * 2, 2));
my $low = hex(substr(${$ref}, ($idx - 1) * 2, 2));
my $val = $high ^ $low;
$res .= sprintf("%02X", $val);
}
return $res;
}
#############################
sub SOMFY_RTS_Check($$)
{
my ($name, $data) = @_;
my $checkSum = 0;
for (my $idx=0; $idx < 7; $idx++)
{
my $val = hex(substr($data, $idx * 2, 2));
$val &= 0xF0 if ($idx == 1);
$checkSum = $checkSum ^ $val ^ ($val >> 4);
##Log3 $name, 4, "$name: Somfy RTS check: " . sprintf("%02X, %02X", $val, $checkSum);
}
$checkSum &= hex("0x0F");
return sprintf("%X", $checkSum);
}
#############################
sub SOMFY_RoundInternal($) {
my ($v) = @_;
return sprintf("%d", ($v + ($somfy_posAccuracy/2)) / $somfy_posAccuracy) * $somfy_posAccuracy;
@ -993,8 +1212,13 @@ sub SOMFY_TimedUpdate($) {
# get current infos
my $pos = ReadingsVal($hash->{NAME},'exact',undef);
if ( AttrVal( $hash->{NAME}, "positionInverse", 0 ) ) {
Log3($hash->{NAME},5,"SOMFY_TimedUpdate : pos before convert so far : $pos");
$pos = SOMFY_ConvertFrom100To0( $pos );
}
Log3($hash->{NAME},5,"SOMFY_TimedUpdate : pos so far : $pos");
my $dt = SOMFY_UpdateStartTime($hash);
my $nowt = gettimeofday();
@ -1024,7 +1248,7 @@ sub SOMFY_TimedUpdate($) {
} else {
Log3($hash->{NAME},4,"SOMFY_TimedUpdate: $hash->{NAME} -> update state in $hash->{runningtime} sec");
}
my $nstt = max($nowt+$utime-0.01, gettimeofday()+.1 );
my $nstt = maxNum($nowt+$utime-0.01, gettimeofday()+.1 );
Log3($hash->{NAME},5,"SOMFY_TimedUpdate: $hash->{NAME} -> next time to stop: $nstt");
InternalTimer($nstt,"SOMFY_TimedUpdate",$hash,0);
}
@ -1037,39 +1261,58 @@ sub SOMFY_TimedUpdate($) {
# SOMFY_UpdateState( $hash, $newState, $move, $updateState );
sub SOMFY_UpdateState($$$$$) {
my ($hash, $newState, $move, $updateState, $doTrigger) = @_;
my $name = $hash->{NAME};
my $addtlPosReading = AttrVal($hash->{NAME},'additionalPosReading',undef);
if ( defined($addtlPosReading )) {
$addtlPosReading = undef if ( ( $addtlPosReading eq "" ) or ( $addtlPosReading eq "state" ) or ( $addtlPosReading eq "position" ) or ( $addtlPosReading eq "exact" ) );
}
my $newExact = $newState;
readingsBeginUpdate($hash);
if(exists($positions{$newState})) {
readingsBulkUpdate($hash,"state",$newState);
$hash->{STATE} = $newState;
readingsBulkUpdate($hash,"position",$positions{$newState});
$hash->{position} = $positions{$newState};
readingsBulkUpdate($hash,$addtlPosReading,$positions{$newState}) if ( defined($addtlPosReading) );
$newExact = $positions{$newState};
readingsBulkUpdate($hash,"position",$newExact);
readingsBulkUpdate($hash,$addtlPosReading,$newExact) if ( defined($addtlPosReading) );
} else {
my $rounded = SOMFY_Runden( $newState );
my $stateTrans = SOMFY_Translate( $rounded );
my $rounded;
my $stateTrans;
Log3($name,4,"SOMFY_UpdateState: $name enter with newState:$newState: updatestate:$updateState: 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);
$hash->{position} = $rounded;
readingsBulkUpdate($hash,$addtlPosReading,$rounded) if ( defined($addtlPosReading) );
}
readingsBulkUpdate($hash,"exact",$newState);
$hash->{exact} = $newState;
readingsBulkUpdate($hash,"exact",$newExact);
if ( defined( $updateState ) ) {
$hash->{updateState} = $updateState;
@ -1143,9 +1386,9 @@ sub SOMFY_CalcCurrentPos($$$$) {
if(defined($t1down100) && defined($t1downclose) && defined($t1up100) && defined($t1upopen)) {
if( ( $t1downclose == $t1down100) && ( $t1up100 == 0 ) ) {
$pos = min( 100, $pos );
$pos = minNum( 100, $pos );
if($move eq 'on') {
$newPos = min( 100, $pos );
$newPos = minNum( 100, $pos );
if ( $pos < 100 ) {
# calc remaining time to 100%
my $remTime = ( 100 - $pos ) * $t1down100 / 100;
@ -1155,10 +1398,10 @@ sub SOMFY_CalcCurrentPos($$$$) {
}
} elsif($move eq 'off') {
$newPos = max( 0, $pos );
$newPos = maxNum( 0, $pos );
if ( $pos > 0 ) {
$newPos = $dt * 100 / ( $t1upopen );
$newPos = max( 0, ($pos - $newPos) );
$newPos = maxNum( 0, ($pos - $newPos) );
}
} else {
Log3($name,1,"SOMFY_CalcCurrentPos: $name move wrong $move");
@ -1167,7 +1410,7 @@ sub SOMFY_CalcCurrentPos($$$$) {
if($move eq 'on') {
if ( $pos >= 100 ) {
$newPos = $dt * 100 / ( $t1downclose - $t1down100 );
$newPos = min( 200, $pos + $newPos );
$newPos = minNum( 200, $pos + $newPos );
} else {
# calc remaining time to 100%
my $remTime = ( 100 - $pos ) * $t1down100 / 100;
@ -1183,7 +1426,7 @@ sub SOMFY_CalcCurrentPos($$$$) {
if ( $pos <= 100 ) {
$newPos = $dt * 100 / ( $t1upopen - $t1up100 );
$newPos = max( 0, $pos - $newPos );
$newPos = maxNum( 0, $pos - $newPos );
} else {
# calc remaining time to 100%
my $remTime = ( $pos - 100 ) * $t1up100 / 100;
@ -1214,6 +1457,8 @@ sub SOMFY_CalcCurrentPos($$$$) {
=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
<a name="SOMFY"></a>
@ -1312,7 +1557,7 @@ sub SOMFY_CalcCurrentPos($$$$) {
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.<br>
drive-up-time-to-100 and drive-up-time-to-open must be set. See also positionInverse attribute.<br>
</li>
</ul>
@ -1344,6 +1589,11 @@ sub SOMFY_CalcCurrentPos($$$$) {
If you have both a CUL868 and CUL433, use the CUL433 as IODev for increased range.
</li><br>
<a name="positionInverse"></a>
<li>positionInverse<br>
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.
</li><br>
<a name="additionalPosReading"></a>
<li>additionalPosReading<br>
Position of the shutter will be stored in the reading <code>pos</code> as numeric value.