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
+ - valveProtectIdlePeriod
+ Protect Valve by switching on actor for 300 seconds.
+ After valveProtectIdlePeriod number of days without switching the valve the actor is set to "on" for 300 seconds.
+ overallHeatingSwitch is not affected.
+