Rule behaviour problem

Hello,

I have a working rule that controls my heating, which was developed with the help of the community here for v2. I’ll include my items and the rule below, however, with OH3 the rule does not always work as expected, because it leaves the basement heating on even if there is no need for it and I’d like to ask for your opinion why that happens. The basement heating controls the boiler, so if any of the floors need heating, basement needs to be turned on - it seems when the heat need goes away, for some reason the relay isn’t turned off and I can’t figure out why; it was working flawlessly under v2. The items, the old rule and the new rules from the OH3 interface are below:

Items:

Switch			GF_Heating_Pump				"Groundfloor heating pump"		<faucet>			(GF, gFaucet)			["Switch","Switchable"]			{autoupdate="false", channel="souliss:t11:77:0-15:onoff"}
Switch			FF_Heating_Pump				"First floor heating pump"		<faucet>			(FF, gFaucet)			["Switch","Switchable"]			{autoupdate="false", channel="souliss:t11:77:0-16:onoff"}
Switch			BF_Heating_Pump				"Basement heating pump"			<faucet>			(BF, gFaucet)			["Switch","Switchable"]			{autoupdate="false", channel="souliss:t11:77:0-17:onoff"}

Switch	BF_Heat_Need	"Basement heat need"		<heating>	(gHneed)
Switch	GF_Heat_Need	"Ground floor heat need"	<heating>	(gHneed)
Switch	FF_Heat_Need	"First floor heat need"		<heating>	(gHneed)

Switch	Heating_Mode_Auto	"Automata fűtésmód"

Group:Number:AVG gHeatingTargetTemp (gHeating)
Number	BF_Target_Temp	"Pince célhőm. [%.1f °C]" (gHeatingTargetTemp)
Number	GF_Target_Temp	"Földszint célhőm. [%.1f °C]" (gHeatingTargetTemp)
Number	FF_Target_Temp	"Emelet célhőm. [%.1f °C]" (gHeatingTargetTemp)

Group Heating_PresetNormal_Group (gHeating)
Number BF_Heating_PresetTempNormal "Pince alap [%.1f °C]" <heating> (Heating_PresetNormal_Group)
Number GF_Heating_PresetTempNormal "Fszt. alap [%.1f °C]" <heating> (Heating_PresetNormal_Group)
Number FF_Heating_PresetTempNormal "Emelet alap [%.1f °C]" <heating> (Heating_PresetNormal_Group)

Number	hysteresis	"Hiszterézis"

Group:Switch:OR(ON, OFF)	gHneed	"Heat need"	<heating>	(Home)

Old rule:

val String filename = "heating_mode.rules"
val Number hysteresis = 0.2

rule "Initialize uninitialized virtual Items"
when
    System started
then
	createTimer(now.plusSeconds(60)) [ |
		logInfo(filename, "Executing 'System started' rule for Heating")
		Heating_PresetNormal_Group.members.filter[item | item.state == NULL].forEach[item | item.postUpdate(22.0)]
		BF_Heating_PresetTempNormal.postUpdate(20.0)
		gHneed.members.forEach[item | item.postUpdate("OFF")]
		if (Heating_Mode_Auto.state == NULL) Heating_Mode_Auto.postUpdate("ON")
	]
end

rule "React on heating mode switch, send target temperatures"
when
    Item Heating_Mode_Auto received update
then
	BF_Target_Temp.sendCommand(BF_Heating_PresetTempNormal.state as Number)
	GF_Target_Temp.sendCommand(GF_Heating_PresetTempNormal.state as Number)
	FF_Target_Temp.sendCommand(FF_Heating_PresetTempNormal.state as Number)
end

rule "Check heating actuators every 15 minutes"
when
	Time cron "* 0/15 * * * ? *" or
	Member of gHeatingTargetTemp received update
then
	//logInfo("heatcheck.tdebug", "Heatcheck started ")
	if (Heating_Mode_Auto.state == ON) { // automatic heating enabled
		var Number hylow = 0
		var Number hyhigh = 0
		gHeatingTargetTemp.members.forEach[ temp |
			if (temp.state !== NULL || temp.state !== UNDEF)
			{
				hylow = (temp.state as Number) - hysteresis
				hyhigh  = (temp.state as Number) + hysteresis
				var String GroupName = temp.name.substring(0, 3) // set groupname based on first 3 bytes
				val avgtemp = org.openhab.core.model.script.ScriptServiceUtil.getItemRegistry.getItem(GroupName + "AvgTemp")
				val needheat = org.openhab.core.model.script.ScriptServiceUtil.getItemRegistry.getItem(GroupName + "Heat_Need")
				if ((avgtemp.state as Number) <= hylow && needheat.state == OFF) { //if average temp lower than preset-hysteresis and heating is not working
					needheat.sendCommand(ON) //turn heating on
					logInfo("ghneedrule.debug", "Heating turned on for: {}", needheat)
				}
				if ((avgtemp.state as Number) >= hyhigh && needheat.state == ON) { //if average temp higher than preset+hysteresis and heating is working
					needheat.sendCommand(OFF) // turn heating off
					logInfo("ghneedrule.debug", "Heating turned off for: {}", needheat)
				}
			}
		]
	}
end


rule "Process heating need"
when
	Member of gHneed received update
then
	logInfo("ghneed.debug", "gHneed state is: {}", gHneed.state)
	logInfo("ghneed.debug", "BF_Heat_need state is: {}", BF_Heat_Need.state)
	logInfo("ghneed.debug", "BF_Heating_Pump state is: {}", BF_Heating_Pump.state)
	logInfo("ghneed.debug", "GF_Heat_need state is: {}", GF_Heat_Need.state)
	logInfo("ghneed.debug", "GF_Heating_Pump state is: {}", GF_Heating_Pump.state)
	logInfo("ghneed.debug", "FF_Heat_need state is: {}", FF_Heat_Need.state)
	logInfo("ghneed.debug", "FF_Heating_Pump state is: {}", FF_Heating_Pump.state)
	BF_Heating_Pump.sendCommand(gHneed.state.toString)
	GF_Heating_Pump.sendCommand(GF_Heat_Need.state.toString)
	FF_Heating_Pump.sendCommand(FF_Heat_Need.state.toString)
end

New rules from OH3:

// Triggers:
// - When the system has reached start level 20

// context: heating_mode-1
createTimer(now.plusSeconds(60)) [ |
	logInfo(filename, "Executing 'System started' rule for Heating")
	Heating_PresetNormal_Group.members.filter[item | item.state == NULL].forEach[item | item.postUpdate(22.0)]
	BF_Heating_PresetTempNormal.postUpdate(20.0)
	gHneed.members.forEach[item | item.postUpdate("OFF")]
	if (Heating_Mode_Auto.state == NULL) Heating_Mode_Auto.postUpdate("ON")
]
// Triggers:
// - When Heating_Mode_Auto was updated

// context: heating_mode-2
BF_Target_Temp.sendCommand(BF_Heating_PresetTempNormal.state as Number)
GF_Target_Temp.sendCommand(GF_Heating_PresetTempNormal.state as Number)
FF_Target_Temp.sendCommand(FF_Heating_PresetTempNormal.state as Number)
// Triggers:
// - Every second, every 15 minutes
// - When a member of gHeatingTargetTemp was updated

// context: heating_mode-3
//logInfo("heatcheck.tdebug", "Heatcheck started ")
if (Heating_Mode_Auto.state == ON) { // automatic heating enabled
	var Number hylow = 0
	var Number hyhigh = 0
	gHeatingTargetTemp.members.forEach[ temp | 
		if (temp.state !== NULL || temp.state !== UNDEF) 
		{
			hylow = (temp.state as Number) - hysteresis 
			hyhigh  = (temp.state as Number) + hysteresis
			var String GroupName = temp.name.substring(0, 3) // set groupname based on first 3 bytes
			val avgtemp = org.openhab.core.model.script.ScriptServiceUtil.getItemRegistry.getItem(GroupName + "AvgTemp")
			val needheat = org.openhab.core.model.script.ScriptServiceUtil.getItemRegistry.getItem(GroupName + "Heat_Need")
			if ((avgtemp.state as Number) <= hylow && needheat.state == OFF) { //if average temp lower than preset-hysteresis and heating is not working
				needheat.sendCommand(ON) //turn heating on
				logInfo("ghneedrule.debug", "Heating turned on for: {}", needheat)
			}
			if ((avgtemp.state as Number) >= hyhigh && needheat.state == ON) { //if average temp higher than preset+hysteresis and heating is working
				needheat.sendCommand(OFF) // turn heating off
				logInfo("ghneedrule.debug", "Heating turned off for: {}", needheat)
			}
		}
	]
}

// Triggers:
// - When a member of gHneed was updated

// context: heating_mode-4
logInfo("ghneed.debug", "gHneed state is: {}", gHneed.state)
logInfo("ghneed.debug", "BF_Heat_need state is: {}", BF_Heat_Need.state)
logInfo("ghneed.debug", "BF_Heating_Pump state is: {}", BF_Heating_Pump.state)
logInfo("ghneed.debug", "GF_Heat_need state is: {}", GF_Heat_Need.state)
logInfo("ghneed.debug", "GF_Heating_Pump state is: {}", GF_Heating_Pump.state)
logInfo("ghneed.debug", "FF_Heat_need state is: {}", FF_Heat_Need.state)
logInfo("ghneed.debug", "FF_Heating_Pump state is: {}", FF_Heating_Pump.state)
BF_Heating_Pump.sendCommand(gHneed.state.toString)
GF_Heating_Pump.sendCommand(GF_Heat_Need.state.toString)
FF_Heating_Pump.sendCommand(FF_Heat_Need.state.toString)

Maybe some of your temperature Items now come as quantities, with units. These need to be handled carefully in rules.

Thanks for the idea, but I’m casting them all to Number. I can add some more logs to see the actual values, but I’m suspecting either a timing issue (the rule to adjust the pump states runs in parallel with the rule that decides if an adjustment is needed and therefor the heating need group - gHneed - may still have a state ON) or that OH3 evaluates some of the constraints differently compared to OH2 (most likely due to the rule conversion).

I was looking for documentation on the new rule structure but couldn’t find any; if you can point me to a guide or example to build up this same rule from scratch to work on OH3, I’m OK with that as well, because at this point I’m not sure what’s more effort, to troubleshoot the old one or write anew.

So? Number variables will hold a Quantity e.g. 20 °C
Comparing 20°C with 21 is comparing apples and oranges, and will fail.

This may well not be the cause, it all depends on your Items, but it would be far from the first time that improvements in bindings have an unexpected knock-on effect on users rules.

Yep, let’s find out.

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.