Heating Control Basic: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
(@@ => @) |
||
Zeile 37: | Zeile 37: | ||
This means that in the end, <code>$burner_state</code> will be either <code>00</code> instead of<code>off</code> and <code>11</code> instead of <code>on</code>. | This means that in the end, <code>$burner_state</code> will be either <code>00</code> instead of<code>off</code> and <code>11</code> instead of <code>on</code>. | ||
<nowiki>my | <nowiki>my @fhts=devspec2array("TYPE=FHT");;\</nowiki> | ||
Here we create an array of all the device parameters. FHEM has a built-in function called <code>devspec2array</code> which makes this easy. The only argument we pass is <code>TYPE=FHT</code> which restricts the returned array to only contain FHT devices. | Here we create an array of all the device parameters. FHEM has a built-in function called <code>devspec2array</code> which makes this easy. The only argument we pass is <code>TYPE=FHT</code> which restricts the returned array to only contain FHT devices. | ||
<nowiki>foreach( | <nowiki>foreach(@fhts) {\ | ||
my $actuator=ReadingsVal($_, "actuator", "101%");;\ | my $actuator=ReadingsVal($_, "actuator", "101%");;\ | ||
$actuator=(substr($actuator, 0, (length($actuator)-1)));;\ | $actuator=(substr($actuator, 0, (length($actuator)-1)));;\ | ||
Zeile 66: | Zeile 66: | ||
<nowiki>else {\ | <nowiki>else {\ | ||
if ($idle_actuators == | if ($idle_actuators == @fhts) {\ | ||
Log(3,"No (more) heating needed. Previous burner status: " . $burner_state);;\ | Log(3,"No (more) heating needed. Previous burner status: " . $burner_state);;\ | ||
fhem("set heater off") if ($burner_state == 11)\ | fhem("set heater off") if ($burner_state == 11)\ | ||
} | } | ||
else {\ | else {\ | ||
Log(3,"Heating request: " . $idle_actuators . " of " . | Log(3,"Heating request: " . $idle_actuators . " of " . @fhts . " actuators are idle.")\ | ||
}\ | }\ | ||
}\ | }\ |
Aktuelle Version vom 18. November 2015, 00:02 Uhr
Die Deutsche Version ist hier verfügbar: Heizungskontrolle Einfach
The aim of this project is to control a central heating system using FHEM. The main contribution for the solution described below is from a forum post by borsti. The heating logic is to switch on heating if one or more radiators are open and switch off heating when all of them are closed.
Hardware
The system is composed of the following components:
- A central heater with a thermostat input.
- A server with FHEM installed.
- CUL USB stick connected to the server.
- FS20 AS1 radio-controlled switch connected to the thermostat input of the heater.
- 3x FHT80b thermostats for 3 rooms.
- As many FHT8V as there are radiators in the 3 rooms above.
FHEM Configuration
Once the hardware set up and FHEM running as described in the manuals, we can start configuring the heating system. The rest of the article describes the contents of the fhem.cfg
file step by step. The file can be easily edited from withing FHEM.
define CUL1 CUL /dev/CUL@9600 5643 define room1 FHT 5030 define room2 FHT 235d define room3 FHT 2d28 define heater FS20 5643 53
The configuration starts with the definition of the hardware components. The CUL is defined first, then the different FHT80b thermostats each with their house codes (which will be different in your installation of course). We then set the retrycount
which defines how many times FHEM will try to send commands to the FHT. In the last line we define the FS20 AS1 that is hooked up to the central heater.
Note that we don't have to define the actual motors (actuators) mounted on the radiator. These are controlled by the FHT thermostats and FHEM does not need to interfere.
define n_heater notify n_heater {\
This line defines a new notify called n_heater
which will hold the code. This basically represents a macro which we can later run whenever we want, using a single command. From now on we are using normal Perl commands. Since the code is embedded in an FHEM command, some special characters need to be repeated (i.e. ;;
instead of ;
and adding a \
to the end of all lines). Check the reference manual for more information about using Perl inside FHEM scripts.
my $need_heat=0;;\ my $idle_actuators=0;;\
Here we are defining two variables which we will use to track whether one of the FHT wants to heat and how many FHT agree not to heat.
my $burner_state=$fs20_c2b{ReadingsVal("heater","state","off")};;\
We define another variable representing the current state of the heater. ReadingsVal
is a function with three arguments: Device name (heater
), reading (state
) and default value which will be used if no other value is found (off
).
The function will return either on
or off
. Because programming with text strings is cumbersome, we want to use codes instead. The array fs20_c2b
is filled with text string of FS20 devices and their corresponding code. By using on
or off
as keys, we can look up the codes that corresponds to the text.
This means that in the end, $burner_state
will be either 00
instead ofoff
and 11
instead of on
.
my @fhts=devspec2array("TYPE=FHT");;\
Here we create an array of all the device parameters. FHEM has a built-in function called devspec2array
which makes this easy. The only argument we pass is TYPE=FHT
which restricts the returned array to only contain FHT devices.
foreach(@fhts) {\ my $actuator=ReadingsVal($_, "actuator", "101%");;\ $actuator=(substr($actuator, 0, (length($actuator)-1)));;\ if ($actuator > 50) {\ $need_heat=1\ }\ if ($actuator < 20) {\ $idle_actuators++\ }\ }\
This is the loop where we check all FHT for their actuator settings. The for-construct loops through the array we created earlier (@fhts
). For each of them we read the actuator setting using ReadingsVal
as described above. The first parameter $_
is a placeholder for the variable that is looped, in our case the FHT device we're currently checking.
Since the acutator value is already numeric, we don't need the $fs20_c2b
but can directly continue with the check. We do have to get rid of the percentage sign though e.g. transform 10%
to 10
. For this we use the substr
function to extract a string starting from position 0 to the second but last character (length - 1) of the orginial string. Effectively this chops off the last character, leaving only the number.
Next, we check whether the actuator value is bigger than 50 (i.e. the radiators are more than 50% open). If this is the case, the variable $need_heat
is set to 1.
If the actuator is below 20, we increment the $idle_actuators
variable.
Effectively this means that at the end of the for loop, $need_heat
will be 1 if any of the FHT needs heat, and $idle_actuators
will contain the number of FHT that don't need heat anymore.
if ($need_heat != 0) {\ Log(3,"Heating needed. Previous burner status: " . $burner_state);;\ fhem("set heater on") if ($burner_state == 00)\ }
Now we're ready for action. If we decided above that heating is needed, we write a short message to the log file indicating the previous state of the heater. The first argument of the Log
function is the level. Important messages have level 1, less important ones have higher levels. The second argument is the text to print, in our case a static sentence followed by the heater state that we saved before.
The actual order to FHEM is sent using the fhem
function. This allows to use normal FHEM commands such as set
, get
, trigger
etc. Here, we simply set the FS20 device heater
to on
. This is only done if the heater was previously off.
else {\ if ($idle_actuators == @fhts) {\ Log(3,"No (more) heating needed. Previous burner status: " . $burner_state);;\ fhem("set heater off") if ($burner_state == 11)\ } else {\ Log(3,"Heating request: " . $idle_actuators . " of " . @fhts . " actuators are idle.")\ }\ }\ }
This last block is executed if there was no need to switch on the heater. At this moment we have to check whether or not we want to switch it off. For this we use the number of FHT that indicated not needing any heat and compare it to the total number of FHT devices (@fhts
is the array of FHT devices, using it simply like this will return the size of the array, which is what we need).
If the numbers are equal this means that all FHT agree to switch off the heater. We print some log message again and then switch off the FS20 device, but again only if it was previously switched on.
The only case remaining now is when the heater should stay unchanged. We capture this with a last else statement and print a log message indicating the current vote for switching off the heater.
This concludes the notify
and we can now use it either by typing trigger n_heater
into the command box or as part of a scheduled command.
define a_heater at +*00:10:00 trigger n_heater
To schedule the command every 10 minutes we define an at
structure called a_heater
. The +
indicates that is should happen in 10 minutes (as opposed to 10 past midnight) and the *
means that it is to be repeated.
Effectively this will result in our macro defined above being executed every 10 minutes. This also defines the minimal time the heater will be turned on. While more frequent executions would improve the reactivity it is probably not a good idea to switch the heater on and off too frequently.
Result
Here's an example of the script turning the heater on and off. You can nicely see the oscillations of the temperature in the three rooms.