Skip to content

Instantly share code, notes, and snippets.

@cyclingengineer
Last active December 31, 2019 10:46
Show Gist options
  • Save cyclingengineer/9923758 to your computer and use it in GitHub Desktop.
Save cyclingengineer/9923758 to your computer and use it in GitHub Desktop.
OpenHAB Heating Control Rules
OpenHAB Heating Example
========================
This heating example shows three rooms in my house with 1 radiator valve, 1 temperature sensor per room and a single boiler. It could be adapted for different situations.
The rules allow for simple addition of items in rooms as long as they follow the naming convention and have at least a valve, a thermometer, setpoint and a demand switch.
The demand switch is purely internal and is used to indicate if the room requires heat or not. All rooms are OR'd together to define the boiler status and each room demand (On/Off) is applied to the radiator valves.
It could be more generic for multiple thermometers/valves per room - but it suits my needs for now. If I make it more generic I will update it accordingly.
/* Heating Control Items
* Contains the items used to manage the house heating system. This includes
* temperature monitors and radiators */
/* Groups */
Group gf_heating
Group gf_heating_temps (gf_heating)
Group gf_heating_humidity (gf_heating)
Group gf_heating_valves (gf_heating)
Group gf_heating_sources (gf_heating)
Group gf_heating_setpoint (gf_heating, ctrl_heating_setpoint)
Group:Switch:OR(ON, OFF) gf_heating_demand (gf_heating, ctrl_heating_demand)
Group ff_heating
Group ff_heating_temps (ff_heating)
Group ff_heating_humidity (ff_heating)
Group ff_heating_valves (ff_heating)
Group ff_heating_sources (ff_heating)
Group ff_heating_setpoint (ff_heating, ctrl_heating_setpoint)
Group:Switch:OR(ON, OFF) ff_heating_demand (ff_heating, ctrl_heating_demand)
Group:Switch:OR(ON, OFF) ctrl_heating_demand (ctrl)
Group ctrl_heating_setpoint (ctrl)
/* Chart groups */
Group chart_lounge_heating
Group chart_kitchen_heating
Group chart_bedroom1_heating
/* Boiler & heat sources */
Switch gf_utility_heating_Boiler "Boiler Status [MAP(BoilerStatus.map):%s]" <heating> (gf_heating_sources, gf_utility) { jeelinkha=">0.0:SALUSRT500RF:Command" }
/* Lounge */
Number gf_lounge_heating_RadTherm "Lounge Rad Therm [%.1f °C]" <heating> (gf_lounge, gf_heating_valves) { maxcube="XXXXXXXXX", autoupdate="false"}
Number gf_lounge_heating_temp "Lounge Temperature [%.1f °C]" <temperature> (gf_lounge, gf_heating_temps, chart_lounge_heating) { rfxcom="<47361:Temperature" }
Number gf_lounge_heating_humidity "Lounge Humidity [%.1f %%]" <temperature> (gf_lounge, gf_heating_humidity) { rfxcom="<XXXXX:Humidity" }
Number gf_lounge_heating_setpoint "Lounge Set Temp [%.1f °C]" <temperature> (gf_lounge, gf_heating_setpoint, chart_lounge_heating)
Switch gf_lounge_heating_demand "Lounge Heating Demand [MAP(BoilerStatus.map):%s]" <temperature> (gf_lounge, gf_heating_demand)
Number gf_lounge_heating_valvePos "Lounge Valve Pos [%d %%]" <heating> (gf_lounge, gf_heating_valves) { maxcube="XXXXXXXX:type=valve", autoupdate="false"}
/* Kitchen */
Number gf_kitchen_heating_RadTherm "Kitchen Rad Therm [%.1f °C]" <heating> (gf_kitchen, gf_heating_valves) { maxcube="XXXXXXX", autoupdate="false"}
Number gf_kitchen_heating_temp "Kitchen Temperature [%.1f °C]" <temperature> (gf_kitchen, gf_heating_temps, chart_kitchen_heating) { rfxcom="<XXXXXX:Temperature" }
Number gf_kitchen_heating_humidity "Kitchen Humidity [%.1f %%]" <temperature> (gf_kitchen, gf_heating_humidity) { rfxcom="<XXXXX:Humidity" }
Number gf_kitchen_heating_setpoint "Kitchen Set Temp [%.1f °C]" <temperature> (gf_kitchen, gf_heating_setpoint, chart_kitchen_heating)
Switch gf_kitchen_heating_demand "Kitchen Heating Demand [MAP(BoilerStatus.map):%s]" <temperature> (gf_kitchen, gf_heating_demand)
Number gf_kitchen_heating_valvePos "Kitchen Valve Pos [%d %%]" <heating> (gf_kitchen, gf_heating_valves) { maxcube="XXXXXXXX:type=valve", autoupdate="false"}
/* FF main front bedroom - bedroom 1 */
Number ff_bedroom1_heating_RadTherm "Bedroom Rad Therm [%.1f °C]" <heating> (ff_bedroom1, ff_heating_valves) { maxcube="XXXXXXXXX", autoupdate="false"}
Number ff_bedroom1_heating_temp "Bedroom Temperature [%.1f °C]" <temperature> (ff_bedroom1, ff_heating_temps, chart_bedroom1_heating) { rfxcom="<XXXX:Temperature" }
Number ff_bedroom1_heating_humidity "Bedroom Humidity [%.1f %%]" <temperature> (ff_bedroom1, ff_heating_humidity) { rfxcom="<XXXXX:Humidity" }
Number ff_bedroom1_heating_setpoint "Bedroom Set Temp [%.1f °C]" <temperature> (ff_bedroom1, ff_heating_setpoint, chart_bedroom1_heating)
Switch ff_bedroom1_heating_demand "Bedroom Heating Demand [MAP(BoilerStatus.map):%s]" <temperature> (ff_bedroom1, ff_heating_demand)
Number ff_bedroom1_heating_valvePos "Bedroom Valve Pos [%d %%]" <heating> (ff_bedroom1, ff_heating_valves) { maxcube="XXXXXXX:type=valve", autoupdate="false"}
import org.openhab.core.library.types.*
val Number hysteresis = 0.3
rule "Heating - demand check"
when
Item ctrl_heating_setpoint received update
or Item gf_heating_temps received update
or Item ff_heating_temps received update
then
ctrl_heating_setpoint?.allMembers.forEach[sp|
/* strip off _setpoint */
logDebug("Setpoint - ",sp.name)
val String base_name = sp.name.substring(0, sp.name.indexOf("_setpoint"))
var OnOffType oldState = house.allMembers.filter(m | m.name.equals(base_name+"_demand")).last.state as OnOffType
var OnOffType newState = oldState
/* find _temp - should only come up with 1 */
val temp = house.allMembers.filter(m | m.name.equals(base_name+"_temp")).last.state as DecimalType
logDebug(base_name+"_temp state => ",temp.toString)
logDebug(base_name+"_setpoint state => ",sp.state.toString)
/* compare _temp and _setpoint then set _demand*/
if (oldState == ON)
{
if (temp >= (sp.state as DecimalType))
{
newState = OFF
} else
{
newState = ON
}
} else {
if (temp < (sp.state as DecimalType) - hysteresis)
{
newState = ON
} else
{
newState = OFF
}
}
if (newState != oldState)
{
postUpdate(house.allMembers.filter(m | m.name.equals(base_name+"_demand")).last,newState)
sendCommand(house.allMembers.filter(m | m.name.equals(base_name+"_RadTherm")).last, newState)
}
logDebug(base_name+"_demand state => ",house.allMembers.filter(m | m.name.equals(base_name+"_demand")).last.state.toString)
]
end
rule "Heating - Boiler Control"
when
Time cron "0 * * * * ?"
then
logDebug("Demand group state", (ctrl_heating_demand.state as OnOffType).toString)
sendCommand(gf_utility_heating_Boiler, ctrl_heating_demand.state as OnOffType)
end
/* Top Level
* Contains top level items - this is to manage top level groups
*/
/* Whole house */
Group house
/* Ground floor (gf) groups */
Group gf (house)
Group gf_utility (gf)
Group gf_lounge (gf)
Group gf_kitchen (gf)
Group gf_hallway (gf)
Group gf_toilet (gf)
/* First floor (ff) groups */
Group ff (house)
Group ff_bedroom1 (ff)
Group ff_bedroom2 (ff)
Group ff_bedroom3 (ff)
Group ff_bathroom (ff)
Group ff_landing (ff)
Group ff_stairs (ff)
/* Control groups - used for rules */
Group ctrl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment