Resolved - [Advice Needed] Heating rule. Thanks for the help everyone

Hi All,

I’ve created the simple rule below for my heating system. Although it all works fine I wonder if there is a more efficient way of writing the rule. So what I’m really looking for is a review of how I’ve done and any pointers at doing things better.

Thanks,

Garry

rule "Heating On"

when
	Item Living_Room_HRT4_ZW_Switch changed from 0 to 1 or
	Item Landing_HRT4_ZW_Switch changed from 0 to 1 or
	Item Living_Room_HRT4_ZW_Mode changed from 0 to 1 or
	Item Landing_HRT4_ZW_Mode changed from 0 to 1
then
	if(Kitchen_SSR_302_Radiators_Switch.state != "ON") {
		Kitchen_SSR_302_Radiators_Switch.sendCommand(ON)
	}
end

rule "Heating Off"

when
	Item Living_Room_HRT4_ZW_Switch changed from 1 to 0 or
	Item Landing_HRT4_ZW_Switch changed from 1 to 0 or
	Item Living_Room_HRT4_ZW_Mode changed from 1 to 0 or
	Item Landing_HRT4_ZW_Mode changed from 1 to 0
then
	if(Living_Room_HRT4_ZW_Switch.state == 0 && Landing_HRT4_ZW_Switch.state == 0){
		Kitchen_SSR_302_Radiators_Switch.sendCommand(OFF)
	}
end

rule "Getting Cold Outside"

when 
	Item Weather_Current_Temperature received update

then
	if(Time_Of_Day.state == "NIGHT" && Weather_Current_Temperature.state < 9.0) {
		sendCommand(Landing_HRT4_ZW_Setpoint,20.0)
	} else if(Time_Of_Day.state == "NIGHT" && Weather_Current_Temperature.state < 5.0) {
		sendCommand(Landing_HRT4_ZW_Setpoint,21.0)
	} else if(Time_Of_Day.state == "NIGHT" && Weather_Current_Temperature.state < 1.0) {
		sendCommand(Landing_HRT4_ZW_Setpoint,22.0)
	}
end

rule "Second Floor Need's Heating"

when
	Item Louises_Bedroom_ST814_Temperature received update or
	Item Joannes_Bedroom_ST814_Temperature received update
then
	var Timer Night_Heating_Timer = null
	if(Time_Of_Day.state == "NIGHT" && Second_Floor_Temperature.state < 18.0){
		Kitchen_SSR_302_Radiators_Switch.sendCommand(ON)
		Night_Heating_Timer = createTimer(now.plusMinutes(5))
                [|
                        if(Kitchen_SSR_302_Radiators_Switch.state == ON) {
                                Kitchen_SSR_302_Radiators_Switch.sendCommand(OFF)
                        }
                ]
	} else if(Time_Of_Day.state == "NIGHT" && Second_Floor_Temperature.state > 19.0){
		Kitchen_SSR_302_Radiators_Switch.sendCommand(OFF)
	}
end

I think, this could be a alternative

rule "Heating"
when
    Item Living_Room_HRT4_ZW_Switch received command or
    Item Landing_HRT4_ZW_Switch     received command or
    Item Living_Room_HRT4_ZW_Mode   received command or
    Item Landing_HRT4_ZW_Mode       received command
then
    if (receivedCommand == ON) {
        if(Kitchen_SSR_302_Radiators_Switch.state != ON) {
            Kitchen_SSR_302_Radiators_Switch.sendCommand(ON)
    	}
    } else {
	    if(Living_Room_HRT4_ZW_Switch.state == 0 && Landing_HRT4_ZW_Switch.state == 0) {
	    	Kitchen_SSR_302_Radiators_Switch.sendCommand(OFF)
    	}
    }
end

I haven’t tested.

If these are switch-items, why you don’t compare with ON/OFF

	Item Living_Room_HRT4_ZW_Switch changed from 0 to 1 or
	Item Landing_HRT4_ZW_Switch changed from 0 to 1 or
	Item Living_Room_HRT4_ZW_Mode changed from 0 to 1 or
	Item Landing_HRT4_ZW_Mode changed from 0 to 1

Use

if(Kitchen_SSR_302_Radiators_Switch.state != ON) {

I think, this couldn’t work

if(Time_Of_Day.state == "NIGHT" && Weather_Current_Temperature.state < 9.0) {
		sendCommand(Landing_HRT4_ZW_Setpoint,20.0)
	} else if(Time_Of_Day.state == "NIGHT" && Weather_Current_Temperature.state < 5.0) {
		sendCommand(Landing_HRT4_ZW_Setpoint,21.0)
	} else if(Time_Of_Day.state == "NIGHT" && Weather_Current_Temperature.state < 1.0) {
		sendCommand(Landing_HRT4_ZW_Setpoint,22.0)
	}

and has to be

    if (Time_Of_Day.state == "NIGHT") {
        if      (Weather_Current_Temperature.state < 1.0) {sendCommand(Landing_HRT4_ZW_Setpoint,22.0)}
        else if (Weather_Current_Temperature.state < 5.0) {sendCommand(Landing_HRT4_ZW_Setpoint,21.0)}
        else if (Weather_Current_Temperature.state < 9.0) {sendCommand(Landing_HRT4_ZW_Setpoint,20.0)}
    }

You could put your two rules into one:

rule "Heating On"

when
	Item Living_Room_HRT4_ZW_Switch changed or
	Item Landing_HRT4_ZW_Switch changed or
	Item Living_Room_HRT4_ZW_Mode changed or
	Item Landing_HRT4_ZW_Mode changed
then
    if (triggeringItem.state == 1) {
        if(Kitchen_SSR_302_Radiators_Switch.state != "ON") {
            Kitchen_SSR_302_Radiators_Switch.sendCommand(ON)
        }
    } else if (triggeringItem.state == 0) {
        if(Living_Room_HRT4_ZW_Switch.state == 0 && Landing_HRT4_ZW_Switch.state == 0) {
            Kitchen_SSR_302_Radiators_Switch.sendCommand(OFF)
        }
    }
end

For your last rule “Second floor need heating”
How often do you receive the temp updates triggers
If it is less than 5 minutes you will keep creating timers on top of each others
You could add a check that the radiator is OFF to avoid that

rule "Second Floor Need's Heating"

when
    Item Louises_Bedroom_ST814_Temperature received update or
    Item Joannes_Bedroom_ST814_Temperature received update
then
    var Timer Night_Heating_Timer = null
    if (Time_Of_Day.state == "NIGHT" && Second_Floor_Temperature.state < 18.0) {
        if (Kitchen_SSR_302_Radiators_Switch.state == OFF) {
            Kitchen_SSR_302_Radiators_Switch.sendCommand(ON)
            Night_Heating_Timer = createTimer(now.plusMinutes(5)) [|
                if(Kitchen_SSR_302_Radiators_Switch.state == ON) {
                    Kitchen_SSR_302_Radiators_Switch.sendCommand(OFF)
                }
            ]
        }
    } else if (Time_Of_Day.state == "NIGHT" && Second_Floor_Temperature.state > 19.0) {
        Kitchen_SSR_302_Radiators_Switch.sendCommand(OFF)
    }
end

Thanks for the responses so far, I knew there would be some great advise. I’ll put these ideas into practice and see if I get the same results as I’m getting at present.

I’m probably going to repeat a lot of the same advice already provided.

  • I agree with hr_2, if the only values are 0 or 1 these Items should be Switches or Contacts

  • I also agree with Vincent, you can merge the frist two rules

  • I recommend structuring Rules as follows: calculate what to do and then do it. If you have lines that cause a side effect, they should only appear once in a Rule.

  • Use the methods, not the Actions for sendCommand and postUpdate. https://docs.openhab.org/configuration/rules-dsl.html#sendcommand-method-vs-action

I would rewrite your first two rules as follows:

rule "Heating"
when
    Item Living_Room_HRT4_ZW_Switch changed or
    Item Landing_HRT4_ZW_Switch changed or
    Item Living_Room_HRT4_ZW_Mode changed or
    Item Landing_HRT4_ZW_Mode changed
then
    // ignore state changes that are not 1 to 0 or 0 to 1
    if(previousState != 0 && previousState != 1) return;

    // Calculate what to do

    // by default we will turn on Kitchen radiators
    var newState = ON
    if(triggeringItem.state == 0 &&
       Living_Room_HRT4_ZW_Switch.state == 0 && 
       Landing_HRT4_ZW_Switch.state == 0) {
        newState = OFF
    }

    // Do it
    if(Kitchen_SSR_302_Radiators_Switch.state != newState) {
        Kitchen_SSR_302_Radiators_Switch.sendCommand(newState)
    }
end

Not only does this cut out one of your Rules but by following this format if there is some more work you want to do before sending the command to the radiator you only have to do it in one place.

For the “Getting Cold Outside” rule I would restructure it as above.

rule "Getting Cold Outside"
when
    Item Weather_Current_Temperature changed or // do you need to run this if it is the same temp?
    Item Time_Of_Day changed // so you can change the SP when it is night immediately
then
    if(Time_Of_Day.state != "NIGHT") return;

    var newTemp = -1

    val outsideTemp = Weather_Current_Temperature.state as Number
    switch  outsideTemp{
        case outsideTemp < 1: newTemp = 22
        case outsideTemp < 5: newTemp = 21
        case outsideTemp < 9: newTemp = 22
    }
    
    if(newTemp > -1) Landing_HRT4_ZW_Setpoint.sendCommand(newTemp)
end

The setpoint is only changed when the outside temp is below 9 and it only makes the call to change the setpoint once. Note this also corrects the problem hr_2 identified that makes your original code as written not work as you intend.

Finally, with your last rule again I would restructure it a bit. I agree with Vincent that this rule runs the risk of generating all sorts of Timers one on top of the other. Also, it makes no sense to store the Timer in a variable indie the Rule because that variable goes away as soon as the Rule exits. Night_Heating_Timer should either be a global variable or call createTimer without assigning it to anything.

It is not clear to me exactly what you want the behavior to be. I’m going to assume you want to turn the heater on for no more than 15 minutes at a time. After that it will turn OFF and then not turn on again until one of the temps changes and the Second_Floor_Temperature calls for heat.

Here is how I would write it:

var Timer nightHeatingTimer = null

rule "Second Floor Need's Heating"
when
    Item Second_Floor_Temperature changed // we only need to run if the temp actually changes
then
    // Only do something if it's NIGHT
    if(Time_Of_Day.state != "NIGHT") return;

    // Calculate whether we need to turn on or off the heater
    val createTimer = if(nightHeatingTimer != null && Second_Floor_Temperature.state < 18) true else false
    val cancelHeat = if(Second_Floor_Temperature.state > 19) true else false

    if(createTimer) {
        nightHeatingTimer = createTimer(now.plusMinutes(15), [ |
            if(Kitchen_SSR_302_Radiators_Switch.state != OFF) Kitchen_SSR_302_Radiators_Switch.sendCommand(OFF)
            nightHeatingTimer = null
        ])
    }
    if(cancelHeat) {
        nightHeatingTimer?.cancel
        if(Kitchen_SSR_302_Radiators_Switch.state != OFF) Kitchen_SSR_302_Radiators_Switch.sendCommand(OFF)
    }
end

I tried to stick as close as possible to the behavior you actually coded but there is an edge case that is not handled. If the heat is on when morning starts, if there is a timer running it will turn off the heat automatically no matter what the temperature is.

Thanks @rlkoshak your explanation it has been very helpful as always. With all the suggestions above I now have a better understanding and will look at all of my other rules. So may ask for further advice when I get it completely wrong once again.