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

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
This commit is contained in:
jamesgo 2016-07-11 07:49:34 +00:00
parent 1344193092
commit 473eb70940
2 changed files with 205 additions and 23 deletions

View File

@ -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 <i>INTERVAL</i> which defines how often <i>desired-temp</i> is calculated in autoCalcMode. Default is 300 seconds (5:00 Minutes).
</li><br>
<li>frostProtect<br>
Sets attribute frostProtect to 1 (on) or 0 (off).
</li><br>
</ul>
<br>
@ -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).
</li><br>
<li>valueFormat<br>
Defines a map to format values within PWMR.<br>
The following reading can be formated using syntax of sprinf: temperature
<br>
Example: { "temperature" => "%0.2f" }
</li><br>
</ul>
<br>

View File

@ -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($$)
<br>
</li>
<li>overallHeatingSwitch&gt[,&lt;pulseMaxThreshold&gt[,&lt;followUpTime&gt;[,&lt;regexp_on&gt]]]<br>
<li>&lt;overallHeatingSwitch&gt[,&lt;pulseMaxThreshold&gt[,&lt;followUpTime&gt;[,&lt;regexp_on&gt]]]<br>
Universal switch to controll eg. pumps or the heater itself. It will be set to "off" if no heating is required and otherwise "on".<br>
<i>pulseMaxThreshold</i> defines a threshold which is applied to reading <i>maxPulse</i> of the PWM object to decide if heating is required. If (calculated maxPulse > threshold) then actor is set to "on", otherwise "off".<br>
If <i>pulseMaxThreshold</i> is set to 0 (or is not defined) then the decision is based on <i>roomsOn</i>. If (roomsOn > 0) then actor is set to "on", otherwise "off".<br>
@ -926,6 +1016,11 @@ sub PWM_Undef($$)
<b>Attributes</b>
<ul>
<li>valveProtectIdlePeriod<br>
Protect Valve by switching on actor for 300 seconds.<br>
After <i>valveProtectIdlePeriod</i> number of days without switching the valve the actor is set to "on" for 300 seconds.
overallHeatingSwitch is not affected.
</li><br>
</ul>
<br>
</ul>