Thermostat Rule that compares current temperature to a upper and lower limit

Hi

Thanks to the work already done by a lot of users, I’ve created a Thermostat rule, that works from a One-Wire slave thermocouple, using a DigiTemp command line and a relay / switch.

The requirement is to keep a water tank within a temperature range, but be able to adjust both the target temperature and the offset.

The offset is there is prevent the heater oscillating too much, once the system has been commissioned and monitored for a few weeks, I doubt the offset will be adjusted, so I will copy the user set value to the system start part of the rule.

(And yes, there is a mechanical thermostat on the tank to prevent it boiling if the rule fails)

The topics that I referenced for the piece are :–

https://docs.openhab.org/configuration/rules-dsl.html

The following rule relies on these items, all confirgured in PaperUI, so I don’t have a .items file to share :frowning_face:

Exec Command : DigiTemp, with polling
One-Wire temperature value, as String (Because that’s what it has to be for ExecCommand Output) // This caused a big headache as I had to learn how to change it from a String to a usable number type.
One-Wire command last updated, as date/time

Virtual items:
Target temperature, as Number
Offset value, as Number

A switch item linked to the relay of choice

Optional…

Velbus OLED glass panels have an option to send a text string to the display, so I added that in as a sanity check and to show the tank temperature.

I hope this rule helps someone save a few hours of development.

FYI, I’m not a coder, so I know this could probably be done in a much neater way, but it works, which is good enough for me :slight_smile:
You’ll also notice my liberal use of the Say command, which is just my quick way of debugging while standing next to the tank, so that I don’t have to look away at a screen :wink:

var int offset = 10
var int target = 25
var int High = 26
var int Low = 23
var int temp = 30


rule "reset thermostat"
when
    System started
then
	target = 50
	offset = 3
	temp = 130
	OneWire_Thermostat_Offset.sendCommand(3)
	OneWire_Thermostat_SetPoint.sendCommand(50)
	OneWire_Temperature_Output.sendCommand(120)
	say("Hello. This is the One Wire thermostat rule", "voicerss:enGB", "webaudio")
	OneWire_Thermostat_Switch.sendCommand(OFF)
	
end


rule "One-Wire Thermostat"

when
     Item OneWire_Temperature_LastExecution changed or Item OneWire_Thermostat_Offset changed or Item OneWire_Thermostat_SetPoint changed
then
	//	say("Updated", "voicerss:enGB", "webaudio")
	offset = (OneWire_Thermostat_Offset.state as Number)

	target = (OneWire_Thermostat_SetPoint.state as Number)
	temp = Float::parseFloat(String::format("%s",OneWire_Temperature_Output.state))
	
	//say("Offset at " + offset + "Target at " + target + "Temperature at " + temp, "voicerss:enGB", "webaudio")
	High = (target as DecimalType) + (offset  as DecimalType)
	Low = (target as DecimalType) - (offset  as DecimalType)
	//say("Offset at " + offset + "Target at " + target + "Temperature at " + temp +"High at " + High + "Low at " + Low, "voicerss:enGB", "webaudio")



 			if ( (temp as Number) < (Low as Number) ){
			OneWire_Thermostat_Switch.sendCommand(ON)
			Cabin_Memo_Text.sendCommand("Temp is "+ temp + "Target is " + target + " " + "Heat is " + OneWire_Thermostat_Switch.state)
			say("Temp is"+ temp + "Target is " + target + " " + "Heat is " +OneWire_Thermostat_Switch.state, "voicerss:enGB", "webaudio")
 			}
		
			else if( (temp as Number) > (High as Number)){
			OneWire_Thermostat_Switch.sendCommand(OFF)
			Cabin_Memo_Text.sendCommand("Temp is "+ temp + "Target is " + target + " " + "Heat is " + OneWire_Thermostat_Switch.state)
			say("Temp is"+ temp + "Target is" + target + " " + "Heat is " +OneWire_Thermostat_Switch.state, "voicerss:enGB", "webaudio")
 			
 			}


end
2 Likes

Good work!!
I am glad it works for you.

You are using a lot of type casting such as as DecimalType, as Number

I propose the following:
Create an item:

Number OneWire_Temperature

And a rule when the String item is changed then you automatically convert to a number item:

rule "Change String Temp to Number"
when
    Item OneWire_Temperature_Output changed
then
    var temperature = Float::parseFloat(String::format("%s",OneWire_Temperature_Output.state))
    OneWire_Temperature.postUpdate(temperature as Number)
end

In your main rules get rid of the int type and only use Number all the way through.
Also note that your startup rule will trigger the main rule 3 times so it may be worth adding some NULL testing at the start of the main rule:

var Number offset = 10
var Number target = 25
var Number High = 26
var Number Low = 23
var Number temp = 30

rule "reset thermostat"
when
    System started
then
	target = 50
	offset = 3
	temp = 130
	OneWire_Thermostat_Offset.postUpdate(offset)
	OneWire_Thermostat_SetPoint.postUpdate(target)
	OneWire_Temperature_Output.postUpdate(temp)
	OneWire_Thermostat_Switch.sendCommand(OFF)
end

rule "One-Wire Thermostat"
when
     Item OneWire_Temperature changed or
     Item OneWire_Thermostat_Offset changed or
     Item OneWire_Thermostat_SetPoint changed
then
    logInfo("One Wire Thermostat", "Rule Triggered")
    if (OneWire_Thermostat_Offset == NULL || OneWire_Thermostat_SetPoint == NULL || OneWire_Temperature == NULL) return; //Do nothing and exit the rule
    offset = OneWire_Thermostat_Offset.state as Number
    target = OneWire_Thermostat_SetPoint.state as Number
    temp = OneWire_Temperature.state as Number
    High = target + offset
    Low = target - offset
    if (temp < Low) {
        logInfo("One Wire Thermostat", "Switched ON")
        OneWire_Thermostat_Switch.sendCommand(ON)
        Cabin_Memo_Text.sendCommand("Temp is "+ temp.toString + "Target is " + target.toString + " " + "Heat is " + OneWire_Thermostat_Switch.state.toString)
    }
    else if (temp > High) {
        logInfo("One Wire Thermostat", "Switched OFF")
        OneWire_Thermostat_Switch.sendCommand(OFF)
        Cabin_Memo_Text.sendCommand("Temp is "+ temp.toString + "Target is " + target.toString + " " + "Heat is " + OneWire_Thermostat_Switch.state.toString)
    }
end
2 Likes