diff --git a/fhem/FHEM/98_PID20.pm b/fhem/FHEM/98_PID20.pm index 7db919526..a05e1c406 100644 --- a/fhem/FHEM/98_PID20.pm +++ b/fhem/FHEM/98_PID20.pm @@ -52,6 +52,11 @@ # V 1.0.0.6 # 12.11.2015 Fix: wrong evaluation of Sensor String, when value was 0 than sensor timeout was detected # Feature: added VERSION +# V 1.0.0.7 Feature: new attribute pidActorCallBeforeSetting for user specific callback +# before setting a new value to actor +# V 1.0.0.8 Feature: attribute handling accelerates some actions +# Fix: update of state triggers force a notify +# Fix: adjust commandref #################################################################################################### package main; @@ -63,7 +68,7 @@ use vars qw($readingFnAttributes); use vars qw(%attr); use vars qw(%modules); -my $PID20_Version = "1.0.0.6"; +my $PID20_Version = "1.0.0.8"; sub PID20_Calc($); ######################################## sub PID20_Log($$$) @@ -80,41 +85,55 @@ sub PID20_Log($$$) sub PID20_Initialize($) { my ($hash) = @_; - $hash->{DefFn} = "PID20_Define"; - $hash->{UndefFn} = "PID20_Undef"; - $hash->{SetFn} = "PID20_Set"; - $hash->{GetFn} = "PID20_Get"; - $hash->{NotifyFn} = "PID20_Notify"; + $hash->{DefFn} = 'PID20_Define'; + $hash->{UndefFn} = 'PID20_Undef'; + $hash->{SetFn} = 'PID20_Set'; + $hash->{GetFn} = 'PID20_Get'; + $hash->{NotifyFn} = 'PID20_Notify'; + $hash->{AttrFn} = 'PID20_Attr'; $hash->{AttrList} = - "pidActorValueDecPlaces:0,1,2,3,4,5 " - . "pidActorInterval " - . "pidActorTreshold " - . "pidActorErrorAction:freeze,errorPos " - . "pidActorErrorPos " - . "pidActorKeepAlive " - . "pidActorLimitLower " - . "pidActorLimitUpper " - . "pidCalcInterval " - . "pidDeltaTreshold " - . "pidDesiredName " - . "pidFactor_P " - . "pidFactor_I " - . "pidFactor_D " - . "pidMeasuredName " - . "pidSensorTimeout " - . "pidReverseAction " - . "pidUpdateInterval " - - # . "pidDebugEnable:0,1 "; - . "pidDebugSensor:0,1 " - . "pidDebugActuation:0,1 " - . "pidDebugCalc:0,1 " - . "pidDebugDelta:0,1 " - . "pidDebugUpdate:0,1 " - . "pidDebugNotify:0,1 " - . "disable:0,1 " + 'pidActorValueDecPlaces:0,1,2,3,4,5 ' + . 'pidActorInterval ' + . 'pidActorTreshold ' + . 'pidActorErrorAction:freeze,errorPos ' + . 'pidActorErrorPos ' + . 'pidActorKeepAlive ' + . 'pidActorLimitLower ' + . 'pidActorLimitUpper ' + . 'pidActorCallBeforeSetting ' + . 'pidCalcInterval ' + . 'pidDeltaTreshold ' + . 'pidDesiredName ' + . 'pidFactor_P ' + . 'pidFactor_I ' + . 'pidFactor_D ' + . 'pidMeasuredName ' + . 'pidSensorTimeout ' + . 'pidReverseAction ' + . 'pidUpdateInterval ' + . 'pidDebugSensor:0,1 ' + . 'pidDebugActuation:0,1 ' + . 'pidDebugCalc:0,1 ' + . 'pidDebugDelta:0,1 ' + . 'pidDebugUpdate:0,1 ' + . 'pidDebugNotify:0,1 ' + . 'disable:0,1 ' . $readingFnAttributes; } + +########################## +sub PID20_RestartTimer($$) +{ + my ( $hash, $seconds ) = @_; + my $name = $hash->{NAME}; + + $seconds = 1 if ( $seconds <= 0 ); + RemoveInternalTimer($name); + + my $sdNextScan = gettimeofday() + $seconds; + InternalTimer( $sdNextScan, 'PID20_Calc', $name, 0 ); + PID20_Log $hash, 5, 'name:'.$name.' seconds:'.$seconds; +} ######################################## sub PID20_TimeDiff($) { @@ -159,6 +178,7 @@ sub PID20_Define($$$) } $hash->{helper}{sensor} = $sensor; $hash->{VERSION} = $PID20_Version; + # defaults for regexp if ( !$regexp ) { @@ -335,13 +355,14 @@ sub PID20_Set($@) return 'Set start needs a parameter' if ( @a != 2 ); $hash->{helper}{stopped} = 0; + PID20_RestartTimer($hash,1); } when ('stop') { return 'Set stop needs a parameter' if ( @a != 2 ); $hash->{helper}{stopped} = 1; - PID20_Calc($name); + PID20_RestartTimer($hash,1); } when ('restart') { @@ -355,6 +376,7 @@ sub PID20_Set($@) if ( !defined($value) ); $hash->{helper}{stopped} = 0; $hash->{helper}{adjust} = $value; + PID20_RestartTimer($hash,1); PID20_Log $hash, 3, "set $name $cmd $value"; } when ("calc") # inofficial function, only for debugging purposes @@ -467,7 +489,7 @@ sub PID20_Calc($) $stateStr = "alarm - no $reading yet for $sensor" if ( $sensorStr eq '' && $stateStr eq '' ); # sensor alive - if ( $sensorStr ne '' && $sensorTS ) + if ( $sensorStr ne '' && $sensorTS ) { my $timeDiff = PID20_TimeDiff($sensorTS); $sensorIsAlive = 1 if ( $timeDiff <= $hash->{helper}{sensorTimeout} ); @@ -645,14 +667,19 @@ sub PID20_Calc($) { $readingUpdateReq = 1; # update the readings + my $actorCallBeforeSetting = AttrVal( $name, 'pidActorCallBeforeSetting', undef ); + if ( defined($actorCallBeforeSetting) && exists &$actorCallBeforeSetting ) + { + PID20_Log $hash, 5, 'start callback ' . $actorCallBeforeSetting . ' with actuation:' . $actuation; + no strict "refs"; + $actuation = &$actorCallBeforeSetting( $name, $actuation ); + use strict "refs"; + PID20_Log $hash, 5, 'return value of ' . $actorCallBeforeSetting . ':' . $actuation; + } + #build command for fhem - PID20_Log $hash, 5, - 'actor:' - . $hash->{helper}{actor} - . ' actorCommand:' - . $hash->{helper}{actorCommand} - . ' actuation:' - . $actuation; + PID20_Log $hash, 5, 'actor:' . $hash->{helper}{actor} . ' actorCommand:' . $hash->{helper}{actorCommand} + . ' actuation:' . $actuation; my $cmd = sprintf( "set %s %s %g", $hash->{helper}{actor}, $hash->{helper}{actorCommand}, $actuation ); # execute command @@ -692,9 +719,9 @@ sub PID20_Calc($) } # end while # ........ update statePID. - $stateStr = 'idle' if ( $stateStr eq '' && !$calcReq ); + $stateStr = 'idle' if ( $stateStr eq '' && !$calcReq ); $stateStr = 'processing' if ( $stateStr eq '' && $calcReq ); - readingsSingleUpdate( $hash, 'state', $stateStr, 0 ); + readingsSingleUpdate( $hash, 'state', $stateStr, 1 ); PID20_Log $hash, 2, "C1 stateStr:$stateStr calcReq:$calcReq" if ($DEBUG_Calc); #......... timer setup @@ -705,6 +732,27 @@ sub PID20_Calc($) #PID20_Log $hash, 2, "InternalTimer next:".FmtDateTime($next)." PID20_Calc name:$name DEBUG_Calc:$DEBUG_Calc"; return; } + + +######################################## +# attribute handling +sub PID20_Attr($$$$) +{ + my ( $command, $name, $attribute, $value ) = @_; + my $msg = undef; + my $hash = $defs{$name}; + my $reUINT = '^([\\+]?\\d+)$'; + + PID20_Log $hash, 5, 'name:' . $name . ' attribute:' . $attribute . ' value:' . $value . ' command:' . $command; + + if ( $attribute eq 'disable' ) + { + # PID20_Log $hash, 5, 'disable'; + PID20_RestartTimer($hash,2); + } + return $msg; +} + 1; =pod @@ -792,6 +840,21 @@ sub PID20_Calc($)
  • pidSensorTimeout - number of seconds to wait before sensor will be recognized n/a; default: 3600
  • pidReverseAction - reverse PID operation mode, possible values: 0,1; default: 0
  • pidUpdateInterval - number of seconds to wait before an update will be forced for plotting; default: 300
  • +
  • pidActorCallBeforeSetting - an optional callback-function,which can manipulate the actorValue; default: not defined +
    +        # Exampe for callback-function
    +        # 1. argument = name of PID20
    +        # 2. argument = current actor value
    +          sub PIDActorSet($$)
    +          {
    +              my ( $name, $actValue ) = @_;
    +              if ($actValue>70)
    +              {
    +                $actValue=100;
    +              }
    +              return $actValue;
    +          }
    +