From 82710662d1475762b904c610ef2c21bf250777cf Mon Sep 17 00:00:00 2001 From: jamesgo <> Date: Wed, 7 Oct 2015 10:54:09 +0000 Subject: [PATCH] 94_PWM.pm : Initial Version for Puls width Modulation of Heating. Define Calculation engine. git-svn-id: https://svn.fhem.de/fhem/trunk@9395 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/94_PWM.pm | 800 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 800 insertions(+) create mode 100644 fhem/contrib/94_PWM.pm diff --git a/fhem/contrib/94_PWM.pm b/fhem/contrib/94_PWM.pm new file mode 100644 index 000000000..a82debd36 --- /dev/null +++ b/fhem/contrib/94_PWM.pm @@ -0,0 +1,800 @@ +# +# +# 94_PWM.pm +# written by Andreas Goebel 2012-07-25 +# e-mail: ag at goebel-it dot de +# +# 21.09.15 GA update, use Log3 +# 07.10.15 GA initial version published +############################################## +# $Id: + + +# module for PWM (Pulse Width Modulation) calculation +# this module uses PWMR (R like room) to +# - get information (ReadRoom) +# - set actors (SetRoom) +# +# standard heating devices support 0 to 100% heating they can be driven by the PID module +# heating devices only supporing "on" of "off" can be driven by PWM +# in PWM 50% is realised by defining a timeframe (cycletime) +# and switch the defive "on" for 50% of this time +# basis for calculation of this pulse is a factor multiplied with the difference +# between desired-temp and act-temp +# +# default for cycletime is 15 minutes (900 sec) +# since the devices act very slow +# there is a parameter minonofftime to prevent "senseless" switches +# PWM recalculates the needed pulse every 60 seconds and +# then decides if the devices will be switched +# "on->off", "off->on" or stays in the current state +# + + + +package main; + +use strict; +use warnings; + +sub PWM_Get($@); +sub PWM_Set($@); +sub PWM_Define($$); +sub PWM_Calculate($); +sub PWM_Undef($$); +sub PWM_State($$$$); +sub PWM_CalcRoom(@); + +my %roomsWaitOffset = (); + +################################### +sub +PWM_Initialize($) +{ + my ($hash) = @_; + + $hash->{GetFn} = "PWM_Get"; + $hash->{SetFn} = "PWM_Set"; + $hash->{DefFn} = "PWM_Define"; + $hash->{UndefFn} = "PWM_Undef"; + #$hash->{StateFn} = "PWM_State"; + + $hash->{AttrList} = ""; + +} + +################################### +sub +PWM_Calculate($) +{ + my ($hash) = @_; + + my $name = $hash->{NAME}; + my %RoomsToSwitchOn = (); + my %RoomsToSwitchOff = (); + my %RoomsToStayOn = (); + my %RoomsToStayOff = (); + my %RoomsPulses = (); + my $roomsActive = 0; + my $newpulseSum = 0; + my $wkey = ""; + + if($hash->{INTERVAL} > 0) { + InternalTimer(gettimeofday() + $hash->{INTERVAL}, "PWM_Calculate", $hash, 0); + } + + Log3 ($hash, 3, "PWM_Calculate $name"); + + #$hash->{STATE} = "lastrun: ".TimeNow(); + #$hash->{STATE} = "calculating"; + readingsSingleUpdate ($hash, "lastrun", "calculating", 1); + $hash->{STATE} = "lastrun: ".$hash->{READINGS}{lastrun}{TIME}; + + # loop over all devices + # fetch all PWMR devices + # which are not disabled + # and are linked to me (via IODev) + + foreach my $d (sort keys %defs) { + if ( (defined ($defs{$d}{TYPE})) && $defs{$d}{TYPE} eq "PWMR" ) { # all PWMR objects + if (!defined ($attr{$d}{disable}) or $attr{$d}{disable} == 0) { # not disabled + if ($hash->{NAME} eq $defs{$d}{IODev}) { # referencing to this fb + + Log3 ($hash, 4, "PWM_Calculate calc $name, room $d"); + + ######################## + # calculate room + # $newstate is "" if state is unchanged + # $newstate is "on" or "off" if state changes + 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") { + $onoff = (1 - $newpulse) * $cycletime + } + + $wkey = $name."_".$d; + if (defined ($roomsWaitOffset{$wkey})) { + $newpulse += $roomsWaitOffset{$wkey}; + + } else { + $roomsWaitOffset{$wkey} = 0; + } + + $roomsActive++; + $RoomsPulses{$d} = $newpulse; + $newpulseSum += $newpulse; + + # $newstate ne "" -> state changed "on" -> "off" or "off" -> "on" + if ((int($hash->{MINONOFFTIME}) > 0) && + ($newstate ne "") && + ($onoff < int($hash->{MINONOFFTIME})) + ) { + + ####################### + # actor devices take 3 minutes for an open/close cycle + # this is handled by MINONOFFTIME + + Log3 ($hash, 3, "PWM_Calculate $d: F0 stay unchanged $oldstate: ". + "($onoff < $hash->{MINONOFFTIME} sec)"); + + if ($oldstate eq "off") { + $RoomsToStayOff{$d} = $newpulse; + } else { + $RoomsToStayOn{$d} = $newpulse; + } + + } else { + + # state changed and it is worth to move the device + + if ($newstate eq "on") { + $RoomsToSwitchOn{$d} = $newpulse; + + } elsif ($newstate eq "off") { + $RoomsToSwitchOff{$d} = $newpulse; + + } elsif ($newstate eq "") { + + if ($oldstate eq "on") { + $RoomsToStayOn{$d} = $newpulse; + } else { + $RoomsToStayOff{$d} = $newpulse; + } + } + + } + } + } + } + } + + + # synchronize the heating on the "off" edge of the pulse + # try to minimize the situation where all rooms are "on" at the same time + # + # algorithm: + # -> if more than 2 rooms are switched off at the same time, + # -> simply keep some on (but this will last only for one calculation cycle) + # + # assumption: 100% "on" time is not allowed (max newpulse = 85%) + # -> in the morning all rooms will be switched on at the same time + # -> and then off at the same time + + + # normally we switch off only one room at the same time + # normally we switch on only one room at the same time + my $switchOn = $hash->{MaxSwitchOnPerCycle}; # default 1 + my $switchOff = $hash->{MaxSwitchOffPerCycle}; # default 1 + + # rooms may stay on due to logic below ... + # + # switch off only (one) the room with lowest need for heating + + # sort rooms with ascending "newpulse" + foreach my $room (sort { $RoomsToSwitchOff{$a} <=> $RoomsToSwitchOff{$b} } keys %RoomsToSwitchOff) { + + # only the first room in the list will be switched off + # all others will stay on + # first room has the lowest need for heating ... it will be switched off + + $switchOff--; + + if ($switchOff >= 0) { + Log3 ($hash, 3, "PWM_Calculate $room: F99 switch off ". + "(pulse=$RoomsToSwitchOff{$room})"); + next; + } + + Log3 ($hash, 3, "PWM_Calculate $room: F99 keep room on ". + "(pulse=$RoomsToSwitchOff{$room})"); + + $RoomsToStayOn{$room} = 1; + if (defined($RoomsToSwitchOff{$room})) { + delete ($RoomsToSwitchOff{$room}); + } + } + + # try to minimize the situation where all rooms are "on" at the same time + # switch "on" only one room at the same time + + + # sort rooms with decending "newpulse" + foreach my $room (sort { $RoomsToSwitchOn{$b} <=> $RoomsToSwitchOn{$a} } keys %RoomsToSwitchOn) { + + # only the first room in the list will be switched on + # all others will stay off + # first room has the highest need for heating ... it will be switched on + + $switchOn--; + + if ($switchOn >= 0) { + Log3 ($hash, 3, "PWM_Calculate $room: F98 switch on ". + "(pulse=$RoomsToSwitchOn{$room})"); + next; + } + + Log3 ($hash, 3, "PWM_Calculate $room: F98 keep room off ". + "(pulse=$RoomsToSwitchOn{$room})"); + + my $wkey = $name."_".$room; + $roomsWaitOffset{$wkey} += 0.0001; + + $RoomsToStayOff{$room} = 1; + if (defined($RoomsToSwitchOn{$room})) { + delete ($RoomsToSwitchOn{$room}); + } + + } + + # in addition to the above max. of 85% of the active rooms may be on at the same time + # 11 * 0.8 = 8.8 ... 8 is ok ... 9, 10, 11 is not (laraEG!) + + my $roomsOn = (scalar keys %RoomsToStayOn) - (scalar keys %RoomsToSwitchOff); + + # treat less than 8 active rooms as 8 (more can get active) + # 16.01.2015 + #my $maxRoomsOn = $roomsActive * 0.7; + + # 23.09.2015 + #my $maxRoomsOn = $roomsActive * 0.6; # 11 rooms -> max 6 active + #$maxRoomsOn = (8 * 0.7) if ($roomsActive < 8); + + # HERE + my $maxRoomsOn = $roomsActive - $hash->{NoRoomsToStayOff}; + + # + # looks complicated but this will work if more than one room would be switched on + # + # prevent rooms to be switched on if maxRoomsOn is reached + # + while ( + (($roomsOn + (scalar keys %RoomsToSwitchOn)) > $maxRoomsOn) && + ((scalar keys %RoomsToSwitchOn) > 0) + ) { + + # sort rooms with ascending "newpulse" + foreach my $room (sort { $RoomsToSwitchOn{$a} <=> $RoomsToSwitchOn{$b} } keys %RoomsToSwitchOn) { + + Log3 ($hash, 3, "PWM_Calculate $room: F97 keep room off ". + "(pulse=$RoomsToSwitchOn{$room}) (max=$maxRoomsOn)"); + + + my $wkey = $name."_".$room; + $roomsWaitOffset{$wkey} += 0.001; + + $RoomsToStayOff{$room} = 1; + if (defined($RoomsToSwitchOn{$room})) { + delete ($RoomsToSwitchOn{$room}); + } + + last; # continue in while loop + } + } + + # in addition to the above try to prevent that too many rooms are off + # use $roomsActive and $newpulseSum to differentiate if heating is required + # 11 * 0.27 = 2.97 ... 3 rooms is ok ... 0,1 or 2 is not + + # 23.09.2015 + #my $minRoomsOn = $roomsActive * 0.29; + + # if overall required heating is below 0.42 ... possibly drive Vaillant into "Sperrzeit" + # 15.01.2015: adjust this from 0.42 to 0.25 (=25% Pulse needed) + # 23.09.2015 + #if ($roomsActive == 0 or $newpulseSum/$roomsActive < 0.42) { + # $minRoomsOn = 0; + #} + + # HERE + my $minRoomsOn = $hash->{NoRoomsToStayOn}; + + if ($minRoomsOn > 0) { + + my $roomsCounted = 0; + my $pulseSum = 0; + + foreach my $room (sort { $RoomsPulses{$b} <=> $RoomsPulses{$a} } keys %RoomsPulses) { + + last if ($roomsCounted == $minRoomsOn); + Log3 ($hash, 3, "PWM_Calculate: loop $roomsCounted $room $RoomsPulses{$room}"); + + $pulseSum += $RoomsPulses{$room}; + $roomsCounted++; + } + + #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; + } + + #Log3 ($hash, 3, "PWM_Calculate: newpulseSum $newpulseSum avg ".$newpulseSum/$roomsActive." minRoomsOn(".$minRoomsOn.")") if ($roomsActive > 0); + Log3 ($hash, 3, "PWM_Calculate: pulseSum $pulseSum avg ".$pulseSum/$roomsCounted." minRoomsOn(".$minRoomsOn.")") if ($roomsActive > 0); + + } + + + # + # looks complicated but this will work if more than one room would stay on + # + while ( + (((scalar keys %RoomsToStayOn) + (scalar keys %RoomsToSwitchOn)) < $minRoomsOn) && + ((scalar keys %RoomsToSwitchOff) > 0) + ) { + + # sort rooms with decending "newpulse" + foreach my $room (sort { $RoomsToSwitchOff{$b} <=> $RoomsToSwitchOff{$a} } keys %RoomsToSwitchOff) { + + my $ron = 1 + (scalar keys %RoomsToStayOn) + (scalar keys %RoomsToSwitchOn); + + Log3 ($hash, 3, "PWM_Calculate $room: F96 keep room on ". + "(pulse=$RoomsToSwitchOff{$room}) (min=$minRoomsOn) (roomsOn=$ron)"); + + my $wkey = $name."_".$room; + $roomsWaitOffset{$wkey} -= 0.001; + + $RoomsToStayOn{$room} = 1; + if (defined($RoomsToSwitchOff{$room})) { + delete ($RoomsToSwitchOff{$room}); + } + + last; # continue in while loop + } + } + + # + # now process the calculated actions + # + + foreach my $roomStay (sort keys %RoomsToStayOff) { + + PWMR_SetRoom ($defs{$roomStay}, ""); + + } + + foreach my $roomStay (sort keys %RoomsToStayOn) { + + PWMR_SetRoom ($defs{$roomStay}, ""); + + } + + foreach my $roomOff (sort keys %RoomsToSwitchOff) { + + PWMR_SetRoom ($defs{$roomOff}, "off"); + } + + foreach my $roomOn (sort keys %RoomsToSwitchOn) { + + my $wkey = $name."-".$roomOn; + $roomsWaitOffset{$wkey} = 0; + PWMR_SetRoom ($defs{$roomOn}, "on"); + + } + +# if(!$hash->{LOCAL}) { +# DoTrigger($name, undef) if($init_done); +# } + +} + +################################### +sub +PWM_CalcRoom(@) +{ + my ($hash, $room) = @_; + my $name = $hash->{NAME}; + + Log3 ($hash, 4, "PWM_CalcRoom: $name ($room->{NAME})"); + + my $cycletime = $hash->{CYCLETIME}; + + my ($temperaturV, $actorV, $factor, $oldpulse, $newpulse, $prevswitchtime, $windowV) = + PWMR_ReadRoom($room, $cycletime, $hash->{MaxPulse}); + + my $nextswitchtime; + if ($actorV eq "on") { + $nextswitchtime = int($oldpulse * $cycletime) + $prevswitchtime; + } else { + $nextswitchtime = int((1-$oldpulse) * $cycletime) + $prevswitchtime; + } + + #Log3 ($hash, 4, "PWM_CalcRoom $room->{NAME}: $cycletime ($prevswitchtime/$nextswitchtime)=".($nextswitchtime-$prevswitchtime)); + + if ($actorV eq "on") # current state is "on" + { + # decide if to change to "off" + + if ($newpulse == 1) { + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F10 stay on"); + return ("", $newpulse, $cycletime, $actorV); + } + + if ($newpulse < $oldpulse) { # on: was 80% now it is 30% + + if ( time() >= $nextswitchtime ) # F3 + { + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F3 new off"); + return ("off", $newpulse, $cycletime, $actorV); + + + # state changed and it is worth to move the device + } + else #( time() < $nextswitchtime ) # F1 + { + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F1 stay on"); + return ("", $newpulse, $cycletime, $actorV); + } + + } else { #($newpulse >= $oldpulse) # unchanged, or was 30% now 40% + + # maybe we switch off + # - because several cycles were not calculated + # - or on time is simply over + # - newpulse 0 is also handled here + + if ( time() >= $nextswitchtime) { # F4 + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F4 new off"); + return ("off", $newpulse, $cycletime, $actorV); + } else { + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F9 stay on"); + return ("", $newpulse, $cycletime, $actorV); + } + + } + + } + elsif ($actorV eq "off") # current state is "off" + { + # decide if to change to "on" + + if ($oldpulse == 0 && $newpulse > 0) { # was 0% now heating is required + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F7 new on"); + return ("on", $newpulse, $cycletime, $actorV); + } + if ($newpulse == 0) { + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F11 stay off (0)"); + return ("", $newpulse, $cycletime, $actorV); + } + + if ($newpulse > $oldpulse) { # was 30% now it is 80% + # F5 + if ( time() < $nextswitchtime ) + { + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F5 stay off"); + return ("", $newpulse, $cycletime, $actorV); + + } + else # time >= $nextswitchtime + { + # F6 + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F6 new on"); + return ("on", $newpulse, $cycletime, $actorV); + } + + } else { # unchanged, was 80% now 30% + # F2 + if ( time() >= $nextswitchtime ) { + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F2 new on"); + return ("on", $newpulse, $cycletime, $actorV); + } else { + Log3 ($hash, 3, "PWM_CalcRoom $room->{NAME}: F8 stay off"); + return ("", $newpulse, $cycletime, $actorV); + } + + + } + } + else # $actorV not "on" of "off" + { + Log3 ($hash, 3, "PWM_CalcRoom -> $name -> $room->{NAME}: invalid actor state ($actorV) try to switch off"); + return ("off", 0, $cycletime, $actorV); + + } + + return ("", $newpulse, $cycletime, $actorV); + +} + +################################### +sub +PWM_Get($@) +{ + my ($hash, @a) = @_; + + return "argument is missing" if(int(@a) != 2); + + my $msg; + + if($a[1] ne "status") { + return "Unknown argument $a[1], choose one of status"; + } + + #return $hash->{READINGS}{STATE}{VAL}; + return $hash->{STATE}; +} + +############################# +sub +PWM_Set($@) +{ + my ($hash, @a) = @_; + + my $u = "Unknown argument $a[1], choose one of recalc interval cycletime"; + + + if ( $a[1] =~ /^interval$|^cycletime$/ ) { + return $u if(int(@a) != 3); + + my $hw = uc($a[1]); + $hash->{$hw}= $a[2]; + + } elsif ( $a[1] =~ /^recalc$/ ) { + + #$hash->{LOCAL} = 1; + RemoveInternalTimer($hash); + my $v = PWM_Calculate($hash); + #delete $hash->{LOCAL}; + + } else { + + return $u; + } + + return undef; +} + + +############################# +sub +PWM_Define($$) +{ + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + my $name = $hash->{NAME}; + + return "syntax: define PWM [] [] [] [] [,] [,,]" + if(int(@a) < 2 || int(@a) > 8); + + my $interval = ((int(@a) > 2) ? $a[2] : 60); + my $cycletime = ((int(@a) > 3) ? $a[3] : 900); + my $minonofftime = ((int(@a) > 4) ? $a[4] : 120); + my $maxPulse = ((int(@a) > 5) ? min ($a[5], 1.00) : 0.85); + + $hash->{INTERVAL} = $interval; + $hash->{CYCLETIME} = $cycletime; + $hash->{MINONOFFTIME} = $minonofftime; + $hash->{MaxPulse} = $maxPulse; + + $hash->{STATE} = "defined"; + + + ########## + # [,] + + if (int(@a) > 6) { + my ($maxOn, $maxOff) = split (",", $a[6]); + $maxOff = $maxOn unless (defined($maxOff)); + + $hash->{MaxSwitchOnPerCycle} = $maxOn; + $hash->{MaxSwitchOffPerCycle} = $maxOff; + + } else { + + if ($maxPulse == 1) { + $hash->{MaxSwitchOnPerCycle} = 99; + $hash->{MaxSwitchOffPerCycle} = 99; + } else { + $hash->{MaxSwitchOnPerCycle} = 1; + $hash->{MaxSwitchOffPerCycle} = 1; + } + + } + + ########## + # [,,] + + if (int(@a) > 7) { + my ($stayOn, $stayOff, $onThreshold) = split (",", $a[7]); + + $stayOff = 1 unless (defined($stayOff)); # one room stays off + $onThreshold = 0.3 unless (defined($onThreshold)); # $stayOn is used only if average pluse is >= 0.3 + + $hash->{NoRoomsToStayOn} = $stayOn; # eg. 4 rooms stay switched on (unless average pulse is less then threshold) + $hash->{NoRoomsToStayOff} = $stayOff; # 1 room stays off to limit energy used (maxPulse should be < 1 if this is used) + $hash->{NoRoomsToStayOnThreshold} = $onThreshold; # $stayOn is used only if average pluse is >= threshold + + } else { + + $hash->{NoRoomsToStayOn} = 0; # switch off all rooms is allowd + $hash->{NoRoomsToStayOff} = 0; # switch on all rooms if allowed + $hash->{NoRoomsToStayOnThreshold} = 0; # pulse threshold to use "NoRoomsToStayOn" + + } + + AssignIoPort($hash); + + if($hash->{INTERVAL} > 0) { + InternalTimer(gettimeofday() + 10, "PWM_Calculate", $hash, 0); + } + + Log3 ($hash, 3, "PWM Define $name"); + + return undef; +} + +################################### +sub PWM_Undef($$) +{ + my ($hash, $args) = @_; + + my $name = $hash->{NAME}; + Log3 ($hash, 3, "PWM Undef $name"); + + if ( $hash->{INTERVAL} ) + { + RemoveInternalTimer($hash); + } + + return undef; + +} +################################### +sub PWM_State($$$$) +{ + my ($hash, $time, $var, $value) = @_; + + my $name = $hash->{NAME}; + Log3 ($hash, 3, "PWM States $name: $time $var $value"); + + $hash->{READINGS}{$var}{VAL} = $value; + $hash->{READINGS}{$var}{TIME} = $time; + + #if ($var =~ /INTERVAL|SCOPE|CYCLETIME/) + #{ + # Log3 ($hash, 3, "PWM States $name: set $var $value"); + # $hash->{$var} = $value; + #} + + return undef; + +} + +1; + +=pod +=begin html + + +

PWM

+
    + + + +
    + The PMW module implements temperature regulation for heating systems only capeable of switching on/off.

    + PWM is based on Pulse Width Modulation which means valve position 70% is implemented in switching the device on for 70% and off for 30% in a given timeframe.
    + PWM defines a calculation unit and depents on objects based on PWMR which define the rooms to be heated.
    +
    +
    + + Define +
      + define <name> PWM [<interval>] [<cycletime>] [<minonofftime>] [<maxPulse>] [<maxSwitchOnPerCycle>,<maxSwitchOffPerCycle>] [<roomStayOn>,<roomStayOff>,<stayOnThreshold>]
      +
      + Define a calculation object with the following parameters:
      +
        +
      • interval
        + Calculate the pulses every interval seconds. Default is 60 seconds.
        +
      • + +
      • cycletime
        + Timeframe to which the pulses refere to. Default is 900 seconds (=15 Minutes). "valve position" of 100% calculates to "on" for this period.
        +
      • + +
      • minonofftime
        + Default is 120 seconds. + Floor heating systems are driven by thermomechanic elements which react very slow. on/off status changes for lower periods are ignored.
        +
      • + +
      • maxPulse
        + Default is 1, which means that a device can be switched on for the full cylcetime period.
        + For energy saving reasons it may be wanted to prevent situations were all rooms are switched on (high energy usage) and afterwards off.
        + In this case maxPulse is set to 0.85 (=12:45 minutes) which forces a room with a pulse of 1 (=100%) to be switched off after 12:45 minutes to give another + room the chance to be switched on. +
        +
      • + +
      • maxSwitchOnPerCycle,maxSwitchoffPerCycle
        + Defaults are 99 for both values. This means that 99 PWMR object can be switched on or off at the same time.
        + To prevent energy usage peaks followend by "no energy consumption" situations set both values to "1".
        + This means after the room the the least energy required is switched off the next will be switched off.
        + Rooms are switched on or off one after the other (in cycles) and not all at one time.
        + Waiting times are honored by a addon to the pulse.
        +
        +
      • + +
      • roomStayOn,roomStayOff,stayOnThreshold
        + Defauts:
        + roomStayOn = 0 ... all rooms can be switched off at the same time.
        + roomStayOff = 0 ... all rooms can be switched on at the same time.
        + stayOnThreshold = 0 ... no impact.
        + For energy saving reasons the following may be set: "4,1,0.25". This means:
        + The room with the least pulse will be kept off (roomsStayOff=1)
        + If the average pulse for the (roomsStayOn=4) rooms with the most heating required is greater than (stayOnThreshold=0.25) then maxRoomStayOn will be kept in state "on", even it the time for the current pulse is reached. + If the threshold is not reached (not so much heating required) then all rooms can be switched off at the same time.
        +
        +
      • +
      + +
      + Example:
      +
      + define fh PWM +
      which is equal to
      + define fh PWM 60 900 120 1 99,99 0,0,0 +
      Energy saving definition might be
      + define fh PWM 60 900 120 0.85 1,1 4,1,0.25 +

      + + +
    +
    + + Set +
      +
    • cycletime
      + Temporary change of parameter cycletime. +

    • + +
    • interval
      + Temporary change of parameter interval. +

    • + +
    • recalc
      + Cause recalculation that normally appeary every interval seconds. +

    • + +
    + + Get +
      +
    • status
      + Retrieve content of variable STATE. +

    • + +
    +
    + + Attributes +
      +
    +
    +
+ +=end html +=cut