createTimer in temperature control

Hi,

i’m running a solar powered heating system as descriped here. (see page 14). I’m controlling the fan with a sonoff sv.

This works fine, but the fan is switching quite often from off to on. See the graph below:

Due to this, i want to implement a time delay in my current rule. The fan should run minimum 5 minutes after he switched on.

I’ve already took a look at createTimer, but i have no idea, how to implement it in my code:

rule “Solarheizung Temperaturautomatik”

when
Item HeizmodusSolarheizung received update or
Item ThermostatTemp changed or
Item SolarheizungKollektorTemp changed
then
var SolarheizungKollektorTemp = SolarheizungKollektorTemp.state as Number
var ThermostatTemp = ThermostatTemp.state as Number
var Differenztemperatur = Differenztemperatur.state as Number
var RaumtempSolarheizung = RaumtempSolarheizung.state as Number
var timer = null
val Number hysteresis = 2

    switch HeizmodusSolarheizung.state {
            case "AUTOMATIK": {
                    if(ThermostatTemp < (RaumtempSolarheizung - hysteresis) && ThermostatTemp < (SolarheizungKollektorTemp - Differenztemperatur)){
                            if(Solarheizung.state != ON){
                                    Solarheizung.sendCommand(ON)
                                    logInfo("Solargebläse EIN")
                            }
                    }
                    else if(ThermostatTemp > (RaumtempSolarheizung + hysteresis)) {
                            if(Solarheizung.state !=OFF) {
                                    Solarheizung.sendCommand(OFF)
                                    logInfo("Solargebläse AUS")
                            }
                    }
                    else if(Solarheizung.state !=OFF) {
                            Solarheizung.sendCommand(OFF)
                            logInfo("Solargebläse AUS")
                    }
    }

}
end

See if the expire binding with a virtual switch can help.

I knew the expire-binding. But why is it not possible, to store the current time in variable and deal with it?

For example the function millis() in the arduino documentation.

If by “deal with it” you mean, perform some action at the appointed time - it is, but the only method to do that is to use createTimer().

There’s lots of examples of timer use in these forums. You should find the techniques used here are what you want for “runtime stretching”

Hi,

I’m not a 100% sure about what your rule does but I think you could try this:



    var Timer SolarheizungTimer = null
    
    rule “Solarheizung Temperaturautomatik”
    when
        Item HeizmodusSolarheizung received update or
        Item ThermostatTemp changed or
        Item SolarheizungKollektorTemp changed
    then
        var SolarheizungKollektorTemp = SolarheizungKollektorTemp.state as Number
        var ThermostatTemp = ThermostatTemp.state as Number
        var Differenztemperatur = Differenztemperatur.state as Number
        var RaumtempSolarheizung = RaumtempSolarheizung.state as Number
        var timer = null
        val Number hysteresis = 2

        switch HeizmodusSolarheizung.state {
            case "AUTOMATIK": {
                if(ThermostatTemp < (RaumtempSolarheizung - hysteresis) && ThermostatTemp < (SolarheizungKollektorTemp - Differenztemperatur)){
                    if(Solarheizung.state != ON){
                        Solarheizung.sendCommand(ON)
                        logInfo("Solargebläse EIN")
                    }
                    if(SolarheizungTimer === null || SolarheizungTimer.hasTerminated()) {
                        SolarheizungTimer = createTimer(now.plusMinutes(5), [|
                            Solarheizung.sendCommand(OFF)
                            logInfo("Solargebläse AUS")
                            SolarheizungTimer = null
                        ])
                    } else {
                        SolarheizungTimer.reschedule(now.plusMinutes(5))
                    }
                }
            }
        }
    end

Basically if the “turn on” conditions are met, it will start a timer which turns the heating? off after 5 minutes… If the temperature gets updated while the timer is running and the “turn on” conditions are still true or are true again, it will reschedule the timer for another 5 minutes.

[edit]
There is only one case in your code “AUTOMATIK”, if there are more, like “ON” (on all the time) you should cancel the running timer like so:

case "ON": {
    if(SolarheizungTimer !== null){
        SolarheizungTimer.cancel();
    }   
}

otherwise it could potentially turn the heating off after 5-x minutes even if the mode is ON.

There is an Issue in your code, which causes the erratic behaviour. It’s the last else if() which will switch off the heating if the first and the second clause does not apply.
The first clause applies if temperature is too low and the Collector temperature is high enough.
The second clause applies if the temperature is too high.
The third clause applies if the motor is on. -> always switch off if the temperature is not too low.

So please as a first step delete this part as it prevents correct switching.
In question of a minimum on time, setup a switch Item with expire binding.

Switch SolarheizungLock "Motorsperre [%s]" { expire="5m,state=OFF" }

In the rule use this code snippet:

case "AUTOMATIK": {
    if(SolarheizungLock.state == OFF) {
        if(ThermostatTemp < (RaumtempSolarheizung - hysteresis) && ThermostatTemp < (SolarheizungKollektorTemp - Differenztemperatur)){
            if(Solarheizung.state != ON){
                Solarheizung.sendCommand(ON)
                SolarheizungLock.postUpdate(ON)
                logInfo("Solargebläse EIN")
            }
        }
        else if(ThermostatTemp > (RaumtempSolarheizung + hysteresis)) {
            if(Solarheizung.state !=OFF) {
                Solarheizung.sendCommand(OFF)
                SolarheizungLock.postUpdate(ON)
                logInfo("Solargebläse AUS")
            }
        }
    }
}

The item is only used to ensure, the motor is not cycled more than once in 5 Minutes. You may need to add a trigger SolarheizungLock changed to OFF to ensure the motor is switched once the time is expired.

If there are several other clauses, it may be better to split the code. First, define a OnOffType var with state NULL.
second, use the clauses to decide whether the motor should be switched on or off (only set the var) or to do nothing at all.
third, if the var is not NULL anymore, switch to the state of the var (symbolic code):

var OnOffType ootNewState = NULL
case auto
if(tempIs < low && tempIs < heat) ootNewState = ON
if(tempIs > high) ootNewState = OFF
...
case on
ootNewState = ON
case off
ootNewState = OFF
...
if(ootNewState != NULL && lock.state == OFF) {
    if(motor.state != ootNewState) {
        motor.sendCommand(ootNewState)
        lock.postUpdate(ON)
        logInfo("heat","Switch Motor {}",ootNewState)
    }
}
   

this way there is only one “big” code block to actually switch the motor

Thanks a lot for your work! It seems to work, excepting one thing. In the log occurs the following error:

Rule ‘Solarheizung Temperaturautomatik’: An error occurred during the script execution: index=1, size=1

What is the reason for this issue?

BW Maximilian

The subpackage argument is missing from the logInfo actions…

1 Like

Argh… Yes, I forgot to change this part…

I have got an issue open for that unhelpful error message, I’m sure we’ve all made that syntax mistake. I doubt it’ll get fixed in 2.x though.

In the evening another issue come up: The motor doesn’t switch of when SolarzheizungKollektorTemp drops below ThermostatTemp. Therefore i added another else if loop:

 switch HeizmodusSolarheizung.state {
                case "AUTOMATIK": {
                    if(SolarheizungLock.state == OFF) {
                        if(ThermostatTemp < (RaumtempSolarheizung - hysteresis) && ThermostatTemp < (SolarheizungKollektorTemp - Differenztemperatur)){
                            if(Solarheizung.state != ON){
                                Solarheizung.sendCommand(ON)
                                SolarheizungLock.postUpdate(ON)
                                logInfo("Solarheizung.rules", "Solargebläse EIN")
                            }
                        }
                        else if(ThermostatTemp > (RaumtempSolarheizung + hysteresis)) {
                            if(Solarheizung.state !=OFF) {
                                Solarheizung.sendCommand(OFF)
                                SolarheizungLock.postUpdate(ON)
                                logInfo("Solarheizung.rules", "Solargebläse AUS")
                            }
                        }
                        else if(ThermostatTemp > (SolarheizungKollektorTemp - Differenztemperatur)) {
                            if(Solarheizung.state !=OFF) {
                                Solarheizung.sendCommand(OFF)
                                SolarheizungLock.postUpdate(ON)
                                logInfo("Solarheizung.rules", "Solargebläse AUS")
                            }
                        }
                    }
                }