From 473eb70940235dc31257e04d8b6cc90c22f0b84b Mon Sep 17 00:00:00 2001 From: jamesgo <> Date: Mon, 11 Jul 2016 07:49:34 +0000 Subject: [PATCH] 94_PWM.pm : add Attribute valveProtectIdlePeriod, implementiert Ventilschutz nach n Tagen git-svn-id: https://svn.fhem.de/fhem/trunk@11781 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/93_PWMR.pm | 89 ++++++++++++++++++++++++- fhem/contrib/94_PWM.pm | 139 +++++++++++++++++++++++++++++++++------- 2 files changed, 205 insertions(+), 23 deletions(-) diff --git a/fhem/contrib/93_PWMR.pm b/fhem/contrib/93_PWMR.pm index 458c16f4f..fb7dc38dd 100644 --- a/fhem/contrib/93_PWMR.pm +++ b/fhem/contrib/93_PWMR.pm @@ -29,6 +29,8 @@ # 27.01.16 GA add attribute desiredTempFrom to take desiredTemp from another object # 04.02.16 GA add DLookBackCnt, buffer holding previouse temperatures used for PID D-Part calculation # 08.02.16 GA add ILookBackCnt, buffer holding previouse temperatures used for PID I-Part calculation +# 08.02.16 GA add valueFormat attribute +# 29.06.16 GA add "set frostProtect on|off" # module for PWM (Pulse Width Modulation) calculation @@ -88,6 +90,7 @@ sub PWMR_SetRoom(@); sub PWMR_ReadRoom(@); sub PWMR_Attr(@); sub PWMR_Boost(@); +sub PWMR_valueFormat(@); ################################### sub @@ -115,6 +118,7 @@ PWMR_Initialize($) "tempRule3 ". "tempRule4 ". "tempRule5 ". + "valueFormat:textField-long ". ""; } @@ -366,7 +370,7 @@ PWMR_Set($@) $valList .= ",30.0"; #my $u = "Unknown argument $a[1], choose one of factor actor:off,on desired-temp:knob,min:6,max:26,step:0.5,linecap:round interval manualTempDuration:slider,60,60,600"; #my $u = "Unknown argument $a[1], choose one of factor actor:off,on desired-temp:uzsuDropDown:$valList interval manualTempDuration:slider,60,60,600"; - my $u = "Unknown argument $a[1], choose one of factor actor:off,on desired-temp:$valList interval manualTempDuration:slider,60,60,600"; + my $u = "Unknown argument $a[1], choose one of factor actor:off,on desired-temp:$valList interval manualTempDuration:slider,60,60,600 frostProtect:off,on"; $valList = "slider,6,0.5,30,0.5"; @@ -441,6 +445,24 @@ PWMR_Set($@) } } + ############## + # frostProtect + + if ( $cmd eq "frostProtect" ) { + my $val = $a[2]; + if ( $val eq "on" ) { + $hash->{c_frostProtect} = 1; + $attr{$name}{frostProtect} = 1; + return undef; + } elsif ( $val eq "off" ) { + $hash->{c_frostProtect} = 0; + $attr{$name}{frostProtect} = 0; + return undef; + } else { + return "Unknow argument for $cmd, choose on|off"; + } + } + ############## # others @@ -814,6 +836,8 @@ PWMR_ReadRoom(@) $temperaturT = $defs{$sensor}->{READINGS}{$reading}{TIME}; $temperaturV =~ s/$t_regexp/$1/; + + $temperaturV = PWMR_valueFormat ($room, "temperature", $temperaturV); } if ($room->{actor}) @@ -1161,7 +1185,12 @@ PWMR_Attr(@) } elsif ($attr eq "autoCalcTemp") { $hash->{c_autoCalcTemp} = 1; $hash->{STATE} = "Calculating"; + } + + if ($attr eq "valueFormat" and defined ($hash->{helper}{$attr})) { + delete ($hash->{helper}{$attr}); } + } @@ -1240,6 +1269,32 @@ PWMR_Attr(@) } + if ($attr eq "valueFormat") { + my $attrVal = $val; + if( $attrVal =~ m/^{.*}$/s && $attrVal =~ m/=>/ && $attrVal !~ m/\$/ ) { + my $av = eval $attrVal; + if( $@ ) { + Log3 ($hash->{NAME}, 3, $hash->{NAME} ." $attr: ". $@); + } else { + $attrVal = $av if( ref($av) eq "HASH" ); + } + $hash->{helper}{$attr} = $attrVal; + + foreach my $key (keys $hash->{helper}{$attr}) { + Log3 ($hash->{NAME}, 3, $hash->{NAME} ." $key ".$hash->{helper}{$attr}{$key}); + } + + #return "$attr set to $attrVal"; + + } else { + # if valueFormat is not verified sucessfully ... the helper is deleted (=not used) + delete $hash->{helper}{$attr}; + } + return undef; + } + + + return undef; } @@ -1311,6 +1366,27 @@ PWMR_Boost(@) return undef; } +sub +PWMR_valueFormat(@) +{ + my ($hash, $reading, $value) = @_; + + return $value unless (defined ($reading)); + + if (ref($hash->{helper}{valueFormat}) eq 'HASH') + { + + if (exists($hash->{helper}{valueFormat}->{$reading})) { + + my $vf = $hash->{helper}{valueFormat}->{$reading}; + return sprintf ("$vf", $value); + } + } + + return $value; + +} + 1; =pod @@ -1418,6 +1494,11 @@ PWMR_Boost(@) Temporary change INTERVAL which defines how often desired-temp is calculated in autoCalcMode. Default is 300 seconds (5:00 Minutes).
+
  • frostProtect
    + Sets attribute frostProtect to 1 (on) or 0 (off). +

  • + +
    @@ -1479,6 +1560,12 @@ PWMR_Boost(@) Calculation of desired-temp is (like when using tempRules) based on the interval specified for this device (default is 300 seconds).
    +
  • valueFormat
    + Defines a map to format values within PWMR.
    + The following reading can be formated using syntax of sprinf: temperature +
    + Example: { "temperature" => "%0.2f" } +


  • diff --git a/fhem/contrib/94_PWM.pm b/fhem/contrib/94_PWM.pm index f0dfd23b8..598aef120 100644 --- a/fhem/contrib/94_PWM.pm +++ b/fhem/contrib/94_PWM.pm @@ -15,6 +15,7 @@ # 30.11.15 GA add new followUpTime can now delay switching of OverallHeatingSwitch from "on" to "off" # 26.01.16 GA fix don't call AssignIoPort # 26.01.16 GA fix IODev from PWMR object is now a reference to PWM object +# 29.06.16 GA add attribute valveProtectIdlePeriod ############################################## # $Id: @@ -66,8 +67,9 @@ PWM_Initialize($) $hash->{SetFn} = "PWM_Set"; $hash->{DefFn} = "PWM_Define"; $hash->{UndefFn} = "PWM_Undef"; + $hash->{AttrFn} = "PWM_Attr"; - $hash->{AttrList} = "event-on-change-reading"; + $hash->{AttrList} = "event-on-change-reading valveProtectIdlePeriod"; } @@ -82,6 +84,7 @@ PWM_Calculate($) my %RoomsToSwitchOff = (); my %RoomsToStayOn = (); my %RoomsToStayOff = (); + my %RoomsValveProtect = (); my %RoomsPulses = (); my $roomsActive = 0; my $newpulseSum = 0; @@ -119,16 +122,23 @@ PWM_Calculate($) # calculate room # $newstate is "" if state is unchanged # $newstate is "on" or "off" if state changes + # $newstate may be "on_vp" or "off_vp" if valve protection is active my ($newstate, $newpulse, $cycletime, $oldstate) = PWM_CalcRoom($hash, $defs{$d}); $defs{$d}->{READINGS}{oldpulse}{TIME} = TimeNow(); $defs{$d}->{READINGS}{oldpulse}{VAL} = $newpulse; - + my $onoff = $newpulse * $cycletime; - if ($newstate eq "off") { + if ($newstate =~ "off.*") { $onoff = (1 - $newpulse) * $cycletime } + if ($newstate eq "on_vp") { + $RoomsValveProtect{$d} = "on"; + } elsif ($newstate eq "off_vp") { + $RoomsValveProtect{$d} = "off"; + } + $wkey = $name."_".$d; if (defined ($roomsWaitOffset{$wkey})) { $newpulse += $roomsWaitOffset{$wkey}; @@ -144,7 +154,7 @@ PWM_Calculate($) # $newstate ne "" -> state changed "on" -> "off" or "off" -> "on" if ((int($hash->{MINONOFFTIME}) > 0) && - ($newstate ne "") && + (($newstate eq "on") or ($newstate eq "off")) && ($onoff < int($hash->{MINONOFFTIME})) ) { @@ -341,11 +351,9 @@ PWM_Calculate($) } $minRoomsOnList =~ s/,$//; - #if ($roomsActive == 0 or $hash->{NoRoomsToStayOnThreshold} == 0 or $newpulseSum/$roomsActive < $hash->{NoRoomsToStayOnThreshold}) - - if ($roomsActive == 0 or $hash->{NoRoomsToStayOnThreshold} == 0 or $pulseSum/$roomsCounted < $hash->{NoRoomsToStayOnThreshold}) { $minRoomsOn = 0; + $minRoomsOnList = ""; } #Log3 ($hash, 3, "PWM_Calculate: newpulseSum $newpulseSum avg ".$newpulseSum/$roomsActive." minRoomsOn(".$minRoomsOn.")") if ($roomsActive > 0); @@ -427,6 +435,26 @@ PWM_Calculate($) $pulseRoomsOn += $RoomsPulses{$roomOn}; } + + foreach my $roomVP (sort keys %RoomsValveProtect) { + + my $wkey = $name."-".$roomVP; + $roomsWaitOffset{$wkey} = 0; + + if ( $RoomsValveProtect{$roomVP} eq "on") { + + PWMR_SetRoom ($defs{$roomVP}, "on"); + $cntRoomsOn++; + $pulseRoomsOn += $RoomsPulses{$roomVP}; + + } else { + + PWMR_SetRoom ($defs{$roomVP}, "off"); + $cntRoomsOff++; + $pulseRoomsOff += $RoomsPulses{$roomVP}; + } + + } readingsBulkUpdate ($hash, "roomsActive", $roomsActive); @@ -445,33 +473,33 @@ PWM_Calculate($) if ( defined ($hash->{OverallHeatingSwitch}) ) { if ( $hash->{OverallHeatingSwitch} ne "") { - my $newstate = "on"; + my $newstateOHS = "on"; if ( $hash->{OverallHeatingSwitch_threshold} > 0) { # threshold based - $newstate = ($newpulseMax > $hash->{OverallHeatingSwitch_threshold}) ? "on" : "off"; + $newstateOHS = ($newpulseMax > $hash->{OverallHeatingSwitch_threshold}) ? "on" : "off"; } else { # room based - $newstate = ($cntRoomsOn > 0) ? "on" : "off"; + $newstateOHS = ($cntRoomsOn > 0) ? "on" : "off"; } - my $actor = $hash->{OverallHeatingSwitch}; - my $actstate = ($defs{$actor}{STATE} =~ $hash->{OverallHeatingSwitch_regexp_on}) ? "on" : "off"; + my $actor = $hash->{OverallHeatingSwitch}; + my $actstateOHS = ($defs{$actor}{STATE} =~ $hash->{OverallHeatingSwitch_regexp_on}) ? "on" : "off"; if ($hash->{OverallHeatingSwitch_followUpTime} > 0) { - if ($actstate eq "on" and $newstate eq "off") { + if ($actstateOHS eq "on" and $newstateOHS eq "off") { if ($hash->{READINGS}{OverallHeatingSwitchWaitUntil}{VAL} eq "") { - $newstate = "on"; + $newstateOHS = "on"; Log3 ($name, 2, "PWM_Calculate: $name: OverallHeatingSwitch wait for followUpTime before switching off (init timestamp)"); readingsBulkUpdate ($hash, "OverallHeatingSwitchWaitUntil", FmtDateTime(time() + $hash->{OverallHeatingSwitch_followUpTime})); } elsif ($hash->{READINGS}{OverallHeatingSwitchWaitUntil}{VAL} ge TimeNow()) { - $newstate = "on"; + $newstateOHS = "on"; Log3 ($name, 2, "PWM_Calculate: $name: OverallHeatingSwitch wait for followUpTime before switching off"); } else { readingsBulkUpdate ($hash, "OverallHeatingSwitchWaitUntil", ""); @@ -482,19 +510,19 @@ PWM_Calculate($) } } - if ($newstate ne $actstate or $hash->{READINGS}{OverallHeatingSwitch}{VAL} ne $actstate) { + if ($newstateOHS ne $actstateOHS or $hash->{READINGS}{OverallHeatingSwitch}{VAL} ne $actstateOHS) { - my $ret = fhem sprintf ("set %s %s", $hash->{OverallHeatingSwitch}, $newstate); + my $ret = fhem sprintf ("set %s %s", $hash->{OverallHeatingSwitch}, $newstateOHS); if (!defined($ret)) { # sucessfull - Log3 ($name, 4, "PWMR_SetRoom: $name: set $actor $newstate"); + Log3 ($name, 4, "PWMR_SetRoom: $name: set $actor $newstateOHS"); - readingsBulkUpdate ($hash, "OverallHeatingSwitch", $newstate); + readingsBulkUpdate ($hash, "OverallHeatingSwitch", $newstateOHS); -# push @{$room->{CHANGED}}, "actor $newstate"; +# push @{$room->{CHANGED}}, "actor $newstateOHS"; # DoTrigger($name, undef); } else { - Log3 ($name, 4, "PWMR_SetRoom $name: set $actor $newstate failed ($ret)"); + Log3 ($name, 4, "PWMR_SetRoom $name: set $actor $newstateOHS failed ($ret)"); } } } @@ -534,6 +562,22 @@ PWM_CalcRoom(@) if ($actorV eq "on") # current state is "on" { + # ---------------- + # check if valve protection is active, keep this state for 5 minutes + + if (defined ($room->{helper}{valveProtectLastSwitch})) { + if ( $room->{helper}{valveProtectLastSwitch} + 300 > time()) { + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F13 valveProtect continue"); + return ("", $newpulse, $cycletime, $actorV); + } else { + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F14 valveProtect off"); + delete ($room->{helper}{valveProtectLastSwitch}); + return ("off_vp", $newpulse, $cycletime, $actorV); + } + + } + + # ---------------- # decide if to change to "off" if ($newpulse == 1) { @@ -577,6 +621,20 @@ PWM_CalcRoom(@) } elsif ($actorV eq "off") # current state is "off" { + # ---------------- + # check if valve protection is activated (attribute valveProtectIdlePeriod is set) + + if (defined ($attr{$name}{"valveProtectIdlePeriod"})) { + # period is defined in days (*86400) + if ($room->{READINGS}{lastswitch}{VAL} + ($attr{$name}{"valveProtectIdlePeriod"} * 86400) < time()) { + + $room->{helper}{valveProtectLastSwitch} = time(); + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F12 valve protect"); + return ("on_vp", $newpulse, $cycletime, $actorV); + } + } + + # ---------------- # decide if to change to "on" if ($oldpulse == 0 && $newpulse > 0) { # was 0% now heating is required @@ -615,6 +673,7 @@ PWM_CalcRoom(@) } + } else # $actorV not "on" of "off" { @@ -806,6 +865,37 @@ sub PWM_Undef($$) } +sub +PWM_Attr(@) +{ + my @a = @_; + my ($action, $name, $attrname, $attrval) = @a; + + my $hash = $defs{$name}; + + $attrval = "" unless defined ($attrval); + + if ($action eq "del") + { + if (defined $attr{$name}{$attrname}) { + delete ($attr{$name}{$attrname}); + } + + return undef; + } + elsif ($action eq "set") + { + if (defined $attr{$name}{$attrname}) + { + } + } + + Log3 (undef, 2, "called PWM_Attr($a[0],$a[1],$a[2],<$a[3]>)"); + + return undef; +} + +################################### 1; =pod @@ -874,7 +964,7 @@ sub PWM_Undef($$)
    -
  • overallHeatingSwitch>[,<pulseMaxThreshold>[,<followUpTime>[,<regexp_on>]]]
    +
  • <overallHeatingSwitch>[,<pulseMaxThreshold>[,<followUpTime>[,<regexp_on>]]]
    Universal switch to controll eg. pumps or the heater itself. It will be set to "off" if no heating is required and otherwise "on".
    pulseMaxThreshold defines a threshold which is applied to reading maxPulse of the PWM object to decide if heating is required. If (calculated maxPulse > threshold) then actor is set to "on", otherwise "off".
    If pulseMaxThreshold is set to 0 (or is not defined) then the decision is based on roomsOn. If (roomsOn > 0) then actor is set to "on", otherwise "off".
    @@ -926,6 +1016,11 @@ sub PWM_Undef($$) Attributes