[OH4] Hue Motion Sensor - Reschedule Timer - Can't seem to figure it out

This is a straight forward [Deprecated] Design Pattern: Motion Sensor Timer problem.

I see nothing wrong with the Timer code itself. There must be something wrong with the if/else logic. Make sure you log out the values of everything.

Well, you could use the Threshold Alert and Open Reminder [4.0.0.0;4.9.9.9] to handle the timer and rescheduling the timer and then you’d just need to write a rule that checks the presence and light level and turn on/off the light as required.

The configuration would be something like this:

  • Write a rule. File based rules van work but a UI rule will work better. The following is a UI rule (let’s say the UID is “kitchenLight”). Notice there are no triggers, and there is only one line of code in the script Action and no code is written for the rule conditions, just configuration and that the condition for the light level uses units directly (I just compare to < 19 lx rather than subtracting 18 lx and comparing < 1 lx which is the same thing).
configuration: {}
triggers: []
conditions:
  - inputs: {}
    id: "1"
    configuration:
      itemName: gPresence
      state: ON
      operator: =
    type: core.ItemStateCondition
  - inputs: {}
    id: "2"
    configuration:
      itemName: HUE_KITCHEN_MD_LIGHT_LVL
      state: 19 lx
      operator: <
    type: core.ItemStateCondition
actions:
  - inputs: {}
    id: "3"
    configuration:
      type: application/javascript
      script: "items.gHUE_KITCHEN.sendCommand((isInitialAlert) ? 'ON' : 'OFF');"
    type: script.ScriptAction
  • put HUE_KITCHEN_MD Item into a Group and use that Group as “Triggering Group”
  • Threshold State: ON
  • Comparison operator: ==
  • Alert Delay: PT2M
  • Alert Rule: kitchenLight
  • Initial Alert Rule: kitchenLight
  • leave the rest of the properties set to the defaults, though some may be of interest like the DND period.

With a just a tiny bit of work you could use this one template rule with all you motion sensors. You’ll just need to use the alertItem’s name to get the name of the light to control in your rule.

If you coded this in the UI without the rule template, you can move the checks to the rule conditions (“but only if…”) so your script action would only need to handle the timer stuff. That would look something like this (note I’ve switched to Rules DSL for the script action below):

configuration: {}
triggers:
  - id: "3"
    configuration:
      itemName: HUE_KITCHEN_MD
    type: core.ItemCommandTrigger
conditions:
  - inputs: {}
    id: "1"
    configuration:
      itemName: gPresence
      state: ON
      operator: =
    type: core.ItemStateCondition
  - inputs: {}
    id: "2"
    configuration:
      itemName: HUE_KITCHEN_MD_LIGHT_LVL
      state: 19 lx
      operator: <
    type: core.ItemStateCondition
actions:
  - inputs: {}
    id: "4"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: |-
        var timer = privateCache.get('timer');

        if(timer === null || timer.hasTerminated()){
          gHUE_KITCHEN.sendCommand(ON)
          privateCache.put('timer', createTimer(now.plusMinutes(2), [ |
                                      gHUE_KITCHEN.sendCommand(OFF)
                                      privateCache.put()
                                    ]))
        }
        else {
          timer.reschedule(now.plusMinute(2))
        }
    type: script.ScriptAction

Notice how much simpler the code becomes. Also see how we use the cache, which I strongly recommend over global variables whether you are working in UI rules (where there are no global variables) or files.

You can also simplify this code by failing fast.

rule "HUE - KITCHEN: LIGHTS ON WHEN DETECTION IS REGISTERED"
when
        Item HUE_KITCHEN_MD received update ON
then
    // Exit if no one is home or it's too bright already
    if(gPresence.state != ON || HUE_KITCHEN_MD_LIGHT_LVL.state as QuantityType< Illuminance> < 19|lx) {
        return;
    }

    var timer = privateCache.get('timer');
    if(timer === null || timer.hasTerminated) {
        gHUE_KITCHEN.sendCommand(ON)
        privateCache.put('timer', createTimer(now.plusMinutes(2), [ |
            gHUE_KITCHEN.sendCommand(OFF)
            privateCache.put('timer', null)
        ])
    }
    else {
        timer.reschedule(now.plusMinutes(2))
    }
end

By failing fast by returning up front the whole code becomes more compact and easy to read and therefore analyze and maintain. Note I use the privateCache here as well.

I just typed in the above. There may be errors and typos.

That won’t show how to do it in Rules DSL though. That shows JS Scripting.

1 Like