Thermostat control with openHAB

7,706 views
Skip to first unread message

Gaël L'hopital

unread,
Mar 26, 2014, 5:45:59 AM3/26/14
to ope...@googlegroups.com
Hi, wanted to share the thermostat I setup with openHAB for those interested.
Functionalities :
  - Multiple setpoints, 3 in my case, (can be changed via GUI or for example in Google Calendar)
  - Multiple zone sensors accepted (included all your sensors in a group called sensor_temp)

Prerequisites
  - Have a group called sensor_temp that contains all the possible temperature sensors used
  - In this, a switchItem called X10Chaudiere is used to turn on/off the furnace

How it works :
Depending upon the target temperature of the current setpoint , every time the sensed temperature (held by hvac_current_sensor) change,
it is evaluated if the furnace must be turned on or off. To avoid flipping around the target (frequent on/off) an hysteresis of 0.5°C (in my case, can be changed) is used.
Then if target is 19.5°C, furnace will be turned off at 20°C and on at 19°C, allowing a global mean value close to 19.5°C.

Here is the hvac.items file :
Group     hvac                     <heating>         (house_in)

Group:Number:AVG hvac_current_sensor     "Observed [%.1f °C]"     <temperature>     (hvac)    // This group holds the currently active sensor
String     hvac_sensor_name                  "Sensor Name [%s]"                        (hvac)

Group     hvac_setpoints                                                         (hvac)    // Contains all the possible setpoints
Number    hvac_setpoint_hg                "Hors-gel [%.1f °C]"    <temperature>    (hvac,hvac_setpoints)    // Mode 1
Number    hvac_setpoint_eco                "Eco [%.1f °C]"         <temperature>    (hvac,hvac_setpoints)    // Mode 2
Number    hvac_setpoint_confort            "Confort [%.1f °C]"     <temperature>    (hvac,hvac_setpoints)    // Mode 3
String     hvac_setpoint                    "Mode [%s]"             <heating>         (hvac)
Group:Number:AVG hvac_target            "Target [%.1f °C]"         <temperature>     (hvac)



Here is the hvac.rules file :
import org.openhab.core.library.types.*
import org.openhab.core.library.items.*
import java.util.*

rule hvac_init
when System started
then
   hvac_setpoints?.members.forEach(aMode|                 // Set default values for all modes, up to the user to review them in GUI
       if (aMode.state == Uninitialized) aMode.postUpdate(19.3)      // they will then be persisted       
    )
          
   hvac_sensor_name.postUpdate("temp_netatmo_in") // Replace temp_netatmo_in with the name of a temp sensor you want to be the default one
   hvac_setpoint.postUpdate("")
end

rule hvac_sensor_name_changed
when Item hvac_sensor_name received update
then
    hvac_current_sensor.members.clear
       sensor_temp.members.forEach(aSensor|
           if (hvac_sensor_name.state.toString.equalsIgnoreCase(aSensor.name)) {
               hvac_current_sensor.addMember(aSensor)
            logInfo("hvac", "Zone de chauffage basculée sur <" + hvac_sensor_name.state + ">")           
           }
       )
       if (hvac_current_sensor.members.empty) {
           logDebug("hvac","Identified sensor name not found, falling back to default")
           hvac_sensor_name.postUpdate("temp_netatmo_in")          
       }
end

rule hvac_setpoint_changed
when Item hvac_setpoint changed
then
    hvac_target.members.clear
       hvac_setpoints?.members.forEach(aMode|                
            if (hvac_setpoint.state.toString.equalsIgnoreCase(aMode.name)) {
                hvac_target.addMember(aMode)
                aMode.postUpdate(aMode.state)            // only to ensure the group value is updated to it's member value
               
logDebug("hvac", "Mode de chauffage passé sur <" + hvac_setpoint.state + ">, cible à : " + aMode.state + "°C")   
            }           
       )
       if (hvac_target.members.empty) {
           logDebug("hvac","Heating mode name not found <"+ hvac_setpoint.state + ">, falling back to default")
           hvac_setpoint.postUpdate(hvac_setpoints.members.get(1).name)                     
       }               
end

rule hvac_Temperature_Changed                        // when either the target or the sensed temp
when Item hvac_target changed or                     // changes, we check if we must update the furnace temp
     Item hvac_current_sensor changed
then
    if ( !hvac_current_sensor.members.empty && !hvac_target.members.empty && (hvac_refill.state != ON)) {
        val hysteresis = 0.5                        // Adjust the hysteresis to your system/home/needs
        var Number current = hvac_current_sensor.members.get(0).state as DecimalType
        var Number maximum = (hvac_target.members.get(0).state as DecimalType) + hysteresis
        var Number minimum = maximum - (2*hysteresis)
   
        if ((current > maximum) && (x10Chaudiere.state != OFF)) {
            x10Chaudiere.send(OFF)
           
logDebug("hvac","Turning furnace off")   
        }
        else if ((current < minimum) && (x10Chaudiere.state != ON)) {
                x10Chaudiere.send(ON)
               
logDebug("hvac","Turning furnace on")
        }               
    }
end


db4o.persist file :

Strategies {
    everyDay     : "0 0 0 * * ?"
    everyHour     : "0 0 * * * ?"
    everyMinute : "0 * * * * ?"

    default = everyChange
}

Items { 
       hvac_setpoint : strategy = everyChange, everyHour, restoreOnStartup
       hvac_setpoint_hg : strategy = everyChange, restoreOnStartup   
    hvac_setpoint_eco : strategy = everyChange, restoreOnStartup   
    hvac_setpoint_confort : strategy = everyChange, restoreOnStartup
   
    hvac_setpoint : strategy = everyChange, restoreOnStartup
    hvac_sensor_name : strategy = everyChange, restoreOnStartup
    x10Chaudiere : strategy = everyChange, everyHour, restoreOnStartup  
}

rrd4j.persist file :

Strategies {
    everyMinute : "0 * * * * ?"                                            // rrd4j needs persistence at minute level for graphing   
    default = everyChange
}

Items {
    hvac_target : strategy = everyChange, everyMinute, restoreOnStartup
    hvac_current_sensor : strategy = everyChange, everyMinute, restoreOnStartup
}

hvac.sitemap file :

sitemap hvac label="Thermostat"
{
    Frame label="Thermostat" {
        Text item=hvac_current_sensor
        Text item=hvac_target
        Selection item=hvac_setpoint label="Mode" mappings=[hvac_setpoint_hg=Horsgel,hvac_setpoint_eco=Eco,hvac_setpoint_confort=Confort]
        Switch item=x10Chaudiere
    }
   
    Frame label="Settings" {
        Switch item=hvac_started
        Group label="Sensors" item=sensor_temp
        Text item=hvac_sensor_name
        Group label="T° target" icon= "status" {
                Setpoint item=hvac_setpoint_hg minValue=12 maxValue=28 step=0.1
                   Setpoint item=hvac_setpoint_eco minValue=12 maxValue=28 step=0.1
                   Setpoint item=hvac_setpoint_confort minValue=12 maxValue=28 step=0.1
        }               
    }
}

hvac.map file :

1=Hors-Gel
2=Eco
3=Confort



Hope this will help. Comments and ideas welcome

Frank Olaf Sem-jacobsen

unread,
Mar 26, 2014, 7:47:55 AM3/26/14
to ope...@googlegroups.com
Looks very interesting.

A couple of questions. Do you have an example of how you use the Google calendar functionality to change the active setpoint?

It looks like it only supports one room/heating zone, correct? If I wanted to support multiple rooms, each with its own sensor and heater I guess I would have to add an extra group hierarchy on top in order to be able to create a single reel sets that covers all the rooms. Does this make sense? Do you have any ideas as to what that would look like?

In other words, it should be possible to loop through all the rooms and make the heating decisions in a single function.

gael.l...@augure.com

unread,
Mar 26, 2014, 8:57:09 AM3/26/14
to ope...@googlegroups.com
Hello, in order to pilot zones and setpoint with Google Calendar, just create event in gCal, having a description like :
"update hvac_sensor_name temp_parents" to change the sensor or
"update hvac_setpoint hvac_mode_confort" to change the setpoint

Yes, it is designed to pilot one furnace with multiple sensing zone - you could have multiple furnaces.
The idea could be to associate one actuator to each sensor - but it must be developped a bit.

Regards

mdc....@gmail.com

unread,
Mar 31, 2014, 9:30:51 AM3/31/14
to ope...@googlegroups.com
Thank you for sharing, it is interesting indeed. I am starting experimenting with openHAB, and the thermostat is one of the first objects that gave me some problems ;-)

Paul Hampson

unread,
Mar 31, 2014, 12:41:45 PM3/31/14
to ope...@googlegroups.com
Hi all,

I have a setup that controls a single boiler, multiple radiators (1 per room) and has associated temperature sensors. I have just moved into a new house so I am slowly getting things set up again, but it gives me the opportunity to do it nicely using groups and cleverly named items so I can do rules that don't require me to copy and paste rules for every single room. I'll share them when I have them working.

What I would also like to do is implement some kind of 'intelligent' heating control that is able to turn on heating earlier or later depending on internal room temperature/external temperature etc. I have done some reading about this and I see there is some research already done about neural networks and other forward model control techniques that can be used. Anyone else done any investigation into this?

Paul

Paul Hampson

unread,
Apr 1, 2014, 5:51:08 PM4/1/14
to ope...@googlegroups.com
Here are my rules and  example items for anyone who is interested - https://gist.github.com/cyclingengineer/9923758

Ben Jones

unread,
Apr 1, 2014, 9:36:25 PM4/1/14
to ope...@googlegroups.com
Very nice rules. Here are mine, much simpler as I only have a controllable heating source in my living room - so just one heatpump (Daikin), one temp sensor, and one setpoint. Thought it might be useful for those with simple setups like mine;

ITEMS
// living room setpoint
Number  Heating_LivingSetpoint    "Living Setpoint [%.1f °C]" <degreesc>      (HeatingSetpoint)

// Daikin online controller
String  Heating_DaikinMode        "Daikin Mode"               <heating>       (Heating)         { daikin="living:mode" }
Number  Heating_DaikinTemp        "Daikin Temp [%.0f °C]"     <degreesc>      (Heating)         { daikin="living:temp" }
Switch  Heating_DaikinPower       "Daikin Power"              <fire>          (HeatingSource)   { daikin="living:power" }

// Daikin temp sensors
Number  Heating_DaikinTempIn      "Living Room [%.1f °C]"     <temperature>   (SensorTemperature,TemperatureChart)  { daikin="living:tempin" }

RULES
rule "Living room heating"
when
    Item Heating_LivingSetpoint changed or
    Item Heating_DaikinTempIn changed
then
    // only activate heating if someone home and not in bed
    if (Scene_Presence.state == ON && Scene_Bed.state == OFF) {
    // get the current setpoint for the living room
        var Number setpoint = Heating_LivingSetpoint.state as DecimalType
        var Number hysteresis = 1 

        // get the current temperature in the living room
        var Number tempIn = Heating_DaikinTempIn.state as DecimalType

        // check if the current temp is below the setpoint (+ hysteresis)
        if (tempIn < (setpoint - hysteresis)) {
            // make sure the heatpump is set to a temp higher than the setpoint
            var Number daikinTemp = Heating_DaikinTemp.state as DecimalType
            if (daikinTemp <= setpoint) 
           Heating_DaikinTemp.sendCommand(setpoint + 1)
       // make sure the heatpump is in 'heat' mode
       if (Heating_DaikinMode.state != "Heat")
           Heating_DaikinMode.sendCommand("Heat")
       // turn on the heatpump
            if (Heating_DaikinPower.state == OFF)
                Heating_DaikinPower.sendCommand(ON);
        } else if (tempIn >= setpoint) {
            // setpoint has been reached so switch off the heatpump
            if (Heating_DaikinPower.state == ON)
                Heating_DaikinPower.sendCommand(OFF);
        }
    }
end

Reply all
Reply to author
Forward
0 new messages