Creating a 20 timer to act as a Delay

  • Platform information:
    • Hardware: Raspberry Pi 4 4GB

    • OS: Openhabian 3.2_

    • openHAB version:3.2
      So I created a rule, included below, to turn on a grain bin fan when several parameters are within a certain range and turn it back of when those parameters move outside their set ranges. That all appears to be working fine, so now I would like to add a “delay” after the fan turns on for 20 minutes so it either A- doesn’t check to see if it needs to turn off for 20 minutes, or B - the parameters have to be outside their ranges for 20 minutes before the fan will turn off. That way when the parameters are right on the edge, the fan doesn’t turn off and on repeatedly in a short amount of time. Either one works for me, although I prefer method B if possible. I accomplished this previously with an esp32 and Arduino IDE before transitioned to openhab, and I got it to work there, but the code is obviously a little different and I am having trouble wrapping my head around what I need to do… Thank you in advance!

rule "EMC Moisture Control"

when

    Item EMCCornDrying received update

then

   

    var Number temp = BigChief_SHT3XTemperature.state as Number

    var emc = EMCCornDrying.state

   

    if (emc > 14.5 && emc < 15 && temp > 0 && temp <32) //check to see if the fan needs to turn on

    ESP32Temp_TestSwitch.sendCommand(ON)//Turn fan on

else

    ESP32Temp_TestSwitch.sendCommand(OFF)//turn fan off

    end

There are many ways of achieving what you want. The easiest I can think of is using the expire profile. You set ESP32Temp_TestSwitch with expire="20m,command=OFF"

Then you just remove the code that turn it off in your rule, so your rule would look like this:

rule "EMC Moisture Control"
when
    Item EMCCornDrying received update
then
    var Number temp = BigChief_SHT3XTemperature.state as Number
    var emc = EMCCornDrying.state

    if (emc > 14.5 && emc < 15 && temp > 0 && temp <32) //check to see if the fan needs to turn on
        ESP32Temp_TestSwitch.sendCommand(ON)//Turn fan on
    end
end

Wouldn’t that shut it off after the 20 minutes are up though? I just want it to run for a minimum of 20 minutes, and then check to see if it needs to turn off.

If EMCCornDrying received more updates in the mean time and it falls within your parameters, your rule then sends ESP32Temp_TestSwitch.sendCommand(ON) which resets the 20 minute timer again and again.

It will switch off only when that command hasn’t been sent within the last 20 minutes.

The expiration timer is started or restarted every time an item receives an update or a command other than the specified “expire” update/command. Any future expiring update or command is cancelled, if the item receives an update or command that matches the “expire” update/command.

Oh perfect! That didn’t sink in when I read that, thank you!

@JimT’s solution is probably the simplest. I wanted to outline a few other approaches because this is one of those problems that has a lot of different potential solutions.

Hysteresis

This is a technique where one turns on the fan at a given set of readings and then wait for the readings to drop below a different reading to turn it back off (or vise versa). For example, turn on the heater when the temp first falls below 60 °F and turn it off only when it gets to 62 °F. when the temp is between 60 and 62, do nothing.

Thus any flapping behavior caused when the reading is jumping above and below the threshold quickly is smoothed out but it doesn’t require timers.

There exists an Hysteresis profile you could use to drive this, though it’s not hard to implement in a rule if that’s what you prefer.

This is a great choice when it’s the sensor readings instead of time that is most important.

UI Rule Conditions

This is how you would implement “doesn’t check to see if it needs to turn off for 20 minutes.” Though it could also be used for other approaches like to implement the hysteresis above. A rule’s condition extracts the common case where one has an if statement to check some states and only if that if is true does the rule do something.

This doesn’t seem like a big deal at first but in practice it opens up a whole lot of new ways to think about your rules. And by separating the condition from the actions both become simpler code and simpler is easier to understand and maintain in the long run.

So if we take Jim’s example, the UI rule would have a Script Condition (using Rules DSL here for consistency)

val temp = BigChief_SHT3XTemperature.state as Number
val emc = EMCCornDryingh.state as Number
emc > 14.5 && emc < 15 && temp > 0 && temp < 32

Then your script action would just be

ESP32Temp_TestSwich.sendCommand(ON)

or you wouldn’t even need to use a script at all since the UI has simple actions like this built in.

Another thing one could do is create a timer in the script action and then check for that in the script condition. But that requires the ability to store the timer outside of the scripts themselves and as far as I know JS Scripting is the only language that supports that in UI rules right now. But the condition could check for the existence of the Timer and not run the actions as long as it exists.

Rule Templates

There is a rule template on the marketplace that could be used here: Threshold Alert. This rule template will call another one of your rules when one or more of the members of a Group of Items exceeds a configured threshold, with some additional configuration to control how often it can call your rule and a do not disturb time range (which we don’t need here).

You would instantiate two instances of this rule, one for each of your sensors. Set the limit to “20m” so it won’t call your rule more than once every 20 minutes. Then in the rule that you write you’d just have a script action

    var Number temp = BigChief_SHT3XTemperature.state as Number
    var emc = EMCCornDrying.state

    if (emc > 14.5 && emc < 15 && temp > 0 && temp <32) //check to see if the fan needs to turn on
        ESP32Temp_TestSwitch.sendCommand(ON)//Turn fan on

This would be a more complicated way to implement this but is a good choice when you have, for example, a period of time throughout the day where you don’t want the rule to run regardless of the sensor readings.

You could also modify the rule generated by the template to make it handle both sensors in the same rule without too much trouble.

None of these are better solutions for these specific requirements, but I wanted to present the options for future readers who may have a similar problem but slightly different requirements.

Thank you for the additional ideas! I and trying the expire currently but I’ll play with these as well!

Another option would be to use cron to run the rule once every 20 minutes. The rule would say if the criteria is met send an ON command, else send an OFF command. This might cause a redundant command to be sent every 20 minutes, but that shouldn’t be a problem.

JRuby’s instance variables persist between rule executions including in UI rules. One can save a value (e.g. a timer handle) and access it in subsequent executions.

JRuby scripting library comes with a built in timer manager so tracking the timer handle is not necessary in most cases, although it can be done using an instance variable.

Persisting between executions is not sufficient. Each Script Action and Script Condition is its own environment. Unless you have something like Js Scripting’s cache which lives outside of the rules themselves (it actually lives in the add-on), there is no way to share variables between scripts. You might be able to access that Timer created in the Script Action that created it, but you couldn’t access that Timer in the Script Condition, even if it’s the same rule.

Interesting feature!