Delay for item update with threshold check

  • Platform information:
    • Hardware: AMD Phenom II X4, 12GB Ram
    • OS: Ubuntu 16.04
    • Java Runtime Environment: Oracle Java 1.8.0_151
    • openHAB version: 2.2.0 stable

Dear all,

I tried to write a rule today with a topic that I thought was quite easy. But actually it drives me nuts. I cancelled several approaches because each of them became a little complex in my opinion. So I was not sure if this is the right way or if there is a best practice for this. Maybe the rule experts among you have some hints.

Here’s the situation:

  • I have a Number item without binding -> sWzHelligkeit_State
  • This item represents the status (3 different states possible) of the light level in a room
  • There are 2 thresholds used to calculate the state out of a light sensor value

With the following items and rule this works fine

Number sWzHelligkeit { <some binding for lux sensor> }
Number sWzHelligkeit_State      
Number sWzHelligkeit_Treshold1
Number sWzHelligkeit_Treshold2
rule "WzHelligkeitStatus"
when
  Item sWzHelligkeit changed
then
  val newValue = sWzHelligkeit.state as Number
  val currState = sWzHelligkeit_State.state as Number
  val threshold1 = sWzHelligkeit_Threshold1.state as Number
  val threshold2 = sWzHelligkeit_Threshold2.state as Number
  
  var newState
  if (newValue < threshold2)                          { newState = 2 }
  if (newValue > threshold2 && newValue < threshold1) { newState = 1 }
  if (newValue > threshold1)                          { newState = 0 }
  
  if (newState != currState) {
      sWzHelligkeit_State.sendCommand(newState)
  }
end

And now comes the headache

I want to expand this rule with a time delay to filter some short light peaks hitting the sensor (e.g. a car light)

  • Only update the state item if sensor value is in the defined area for let’s say 30 sec
  • if sensor value shows just a short peak -> do nothing

1. attempt -> timer:

  • Start a timer instead of updating the state item
if (newState != currState) {
    timerVar = createTimer(now.plusSeconds(30)) [|]
      sWzHelligkeit_State.sendCommand(newState)
    ]
}
  • Here I ran into issues with the variable scope -> newState is defined in the rule what leads to errors
  • Does it make sense to define the var newState as global in the ruiles file?
  • Or use a proxy item?
  • And what’s about cancelling the timer when the sensor value drops back to where it came from?

2. attempt -> sleep:

  • Use Thread::sleep before updating the state item
  • And then? Check all conditions again? (Copy&Paste same code?)
  • Or calculate average or minimum with persistence?
if (newState != currState) {
  Thread::sleep(30000)
  // What to do here?
  sWzHelligkeit_State.sendCommand(newState)
}

I think with a little bit more tweaking both attempts can do the thing. Just wanted to check what’s your solution for this. Maybe I’m missing something that makes it less complicated and/or w/o additional proxy items.

Looking forward to your suggestions.

Greetings
Sebastian

Just for reference if somebody discovers this thread -> Here is the solution I ended up with:

I decided to go on with approach 1 (timer) and to introduce an additional proxy item

Number sWzHelligkeit { <some binding for lux sensor> }
Number sWzHelligkeit_State      
Number sWzHelligkeit_StateProxy
Number sWzHelligkeit_Treshold1
Number sWzHelligkeit_Treshold2
var Timer timerDelayWzLuxState = null

rule "WzHelligkeitStatus"
when
  Item sWzHelligkeit changed
then
  val newValue = sWzHelligkeit.state as Number
  val currState = sWzHelligkeit_State.state as Number
  val threshold1 = sWzHelligkeit_Threshold1.state as Number
  val threshold2 = sWzHelligkeit_Threshold2.state as Number
  
  var newState
  if (newValue < threshold2)                          { newState = 2 }
  if (newValue > threshold2 && newValue < threshold1) { newState = 1 }
  if (newValue > threshold1)                          { newState = 0 }
  
  if (newState != currState) {
    sWzHelligkeit_StateProxy.sendCommand(newState)  // Update Proxy
    if (timerDelayWzLuxState === null) {            // no timer running -> start timer and update state when finished
      timerDelayWzLuxState = createTimer(now.plusSeconds(30)) [|
        sWzHelligkeit_State.sendCommand(sWzHelligkeit_StateProxy.state as Number)
        timerDelayWzLuxState = null
      ]
    }
    else { 
      // do something else if timer already running, I just log this as info
    }
  }
  else {  // cancel timer if falling back to current state
    if (timerDelayWzLuxState !== null) {
      timerDelayWzLuxState.cancel
      timerDelayWzLuxState = null
    }
  }
end