HowTo: Climate Control at home with FHEM

As I have many houseautomation devices at home, I thought it would be a good idea to use them to tell me when I should open my windows and when to close them again.
For this little project I used the Netatmo Weatherstation with an additional Sensor for a second room to readout the temperature, dewpoint and co2 of my rooms and temperature and dewpoint outside. Additionally I use my eq-3 MAX! heating control for the actual set temperature at the radiators and the state (open/closed) of my windows in that rooms.
First thing i though about was, what do I want to have in my room, so I made a mindmap that shows what conditions the room should have in best case.

fhem_cc_room_conditions

Then I thought about what values I get from my devices:

fhem_cc_values

Based on this two things, I considered which conditions I would open the window and at what conditions I would close it:

fhem_cc_conditions

To see the relation between my values and my conditions I drew some arrows in it:

fhem_cc_conditions_with_arrows

The advantage of using the dewpoint is, that humidity is only relative humidity, whereas the dewpoint gives a clue about the absolute humidity of the air. The higher the dewpoint is, the more water is in the air. You should try to get your dewpoint as low as possible and never get with your room temperature near or under the dewpoint.

Hint: If your devices don’t have the dewpoint value but temperature and humidity, you can add the dewpoint with following command:

define dew_all dewpoint dewpoint .* temperature humidity dewpoint

The conditions to open my windows are pretty clear:
– Keep CO2 low
– Keep the air as dry as possible
– Try to get to my set temperature

So all the Script does is, if you are at home, send you a push message on your phone to open or close a certain window. Also it sets a dummy variable which you can use for visualisation in the fhem webinterface. The states are “OK” if no action is needed, “open” if you need to open a window and “close” if you should close the window again. Also it will create dummy variables for the maximum allowed co2, the dew point offset (because the air near the window and walls are a bit colder, so you need to use a lower value), the set temperature for the summer and an offset for the set temperature.

Here is the script I wrote, you need to fill in your own values at the bold written places which are also marked with brackets.

define ClimateControl at +*00:05:00 { 
 no warnings 'redefine';
 my $outTemp=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","temperature",0);
 my $outDew=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","dewpoint",100);
 
 my $sleepTemp=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","temperature",0);
 my $sleepDew=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","dewpoint",0);
 my $sleepCO2=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","co2",0);
 my $sleepWin=ReadingsVal("[MAX_XXXXXX]","state","none");
 my $sleepSetTemp=ReadingsVal("[MAX_XXXXXX]","desiredTemperature",20);
 my $sleepVar="[Sleepingroom]";

 my $livingTemp=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","temperature",0);
 my $livingDew=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","dewpoint",0);
 my $livingCO2=ReadingsNum("[netatmo_XXX_XX_XX_XX_XX_XX]","co2",0);
 my $livingWin=ReadingsVal("[MAX_XXXXXX]","state","none");
 my $livingSetTemp=ReadingsVal("[MAX_XXXXXX]","desiredTemperature",20);
 my $livingVar="[Livingroom]";

 $sleepSetTemp = SummerSetTemp($sleepVar,$sleepSetTemp);
 $livingSetTemp = SummerSetTemp($livingVar,$livingSetTemp);
 CheckRoom($outTemp, $outDew, $sleepTemp, $sleepDew, $sleepCO2, $sleepWin, $sleepSetTemp, $sleepVar);
 CheckRoom($outTemp, $outDew, $livingTemp, $livingDew, $livingCO2, $livingWin, $livingSetTemp, $livingVar);

 sub CheckRoom {
  die "Too many arguments for subroutine" unless @_ <= 8;
  die "Too few arguments for subroutine" unless @_ >= 8;

  my $outTemp=$_[0];
  my $outDew=$_[1];
  my $roomTemp=$_[2];
  my $roomDew=$_[3];
  my $roomCO2=$_[4];
  my $roomWin=$_[5];
  my $roomSetTemp=$_[6];
  my $roomVar=$_[7];
  my $roomVarState=ReadingsVal($roomVar,"state","undefined");
  my $dewOffset=ReadingsNum($roomVar . "DewOffset","state",999);
  my $setTempOffset=ReadingsNum($roomVar . "SetTempOffset","state",999);
  my $CO2max=ReadingsNum($roomVar . "maxCO2","state",9999);

  if ($roomVarState eq "undefined") {
   fhem("define " . $roomVar . " dummy");
   fhem("set " . $roomVar . " OK");
   fhem("attr " . $roomVar . " stateFormat Window: state");
  }
  if ($dewOffset eq 999) {
   fhem("define " . $roomVar . "DewOffset dummy");
   fhem("set " . $roomVar . "DewOffset 3");
   fhem("attr " . $roomVar . "DewOffset stateFormat state °C");
   $dewOffset = 3;
  }
  if ($setTempOffset eq 999) {
   fhem("define " . $roomVar . "SetTempOffset dummy");
   fhem("set " . $roomVar . "SetTempOffset 1");
   fhem("attr " . $roomVar . "SetTempOffset stateFormat state °C");
   $setTempOffset = 1;
  }
  if ($CO2max eq 9999) {
   fhem("define " . $roomVar . "maxCO2 dummy");
   fhem("set " . $roomVar . "maxCO2 1000");
   fhem("attr " . $roomVar . "maxCO2 stateFormat state ppm");
   $CO2max = 1000;
  }

  if (($outDew < $roomDew) && ($roomDew > ($roomTemp-$dewOffset))) {
   DoWinOpen($roomVar,$roomWin);
  }
  if ($roomCO2 > $CO2max) {
   DoWinOpen($roomVar,$roomWin);
  }
  if ($roomDew > $outDew) {
   if (($outTemp < $roomTemp) && ($roomSetTemp < $roomTemp)) {
    DoWinOpen($roomVar,$roomWin);
   }
   if (($outTemp > $roomTemp) && ($roomSetTemp > $roomTemp)) {
    DoWinOpen($roomVar,$roomWin);
   }
  }
  if (($roomCO2 < $CO2max) && ($roomDew < ($roomTemp-$dewOffset))) {
   if (($outTemp <= $roomTemp) && (($roomSetTemp-$setTempOffset) >= $roomTemp)) {
    DoWinClose($roomVar,$roomWin);
   }
   if (($outTemp >= $roomTemp) && ($roomSetTemp <= $roomTemp)) {
    DoWinClose($roomVar,$roomWin);
   }
  }
 } 
 sub DoWinOpen {
  die "Too many arguments for subroutine" unless @_ <= 2;
  die "Too few arguments for subroutine" unless @_ >= 2;
  my $roomVar=$_[0];
  my $winState=$_[1];
  my $roomState=ReadingsVal($roomVar,"state","none");
  if ($winState eq "opened") {
   fhem("set " . $roomVar . " OK");
  }
  if (($roomState ne "open") && ($winState eq "closed")) {
   fhem("set " . $roomVar . " open");
   if (ReadingsVal("[PRESENCE]","state","absent") ne "absent") {
    fhem("set push msg '" . $roomVar . "' 'Open the Window' '' 1 ''");
   }
  }
 }
 sub DoWinClose {
  die "Too many arguments for subroutine" unless @_ <= 2;
  die "Too few arguments for subroutine" unless @_ >= 2;
  my $roomVar=$_[0];
  my $winState=$_[1];
  my $roomState=ReadingsVal($roomVar,"state","none");
  if ($winState eq "closed") {
   fhem("set " . $roomVar . " OK");
  }
  if (($roomState ne "close") && ($winState eq "opened")) {
   fhem("set " . $roomVar . " close");
   if (ReadingsVal("[PRESENCE]","state","absent") ne "absent") {
    fhem("set push msg '" . $roomVar . "' 'Close the Window' '' 1 ''");
   }
  }
 }
 sub SummerSetTemp {
  die "Too many arguments for subroutine" unless @_ <= 2;
  die "Too few arguments for subroutine" unless @_ >= 2;
  my $roomVar=$_[0];
  my $roomSetTemp=$_[1];
  if ($roomSetTemp eq "off") {
   $roomSetTemp=ReadingsNum($roomVar . "SummerTemp","state",999);
   if ($roomSetTemp eq 999) {
    fhem("define " . $roomVar . "SummerTemp dummy");
    fhem("set " . $roomVar . "SummerTemp 20");
    fhem("attr " . $roomVar . "SummerTemp stateFormat state °C");
    $roomSetTemp=20;
   }
  }
  return $roomSetTemp;
 }
}

Simply different!