Effective use of Timers in Rules

You have three options:

  1. Save the color to another Item before changing it and restore it from that Item when the Window closes.

  2. Set up persistence and use EG_Hue_bloom_Wohn_Switch.previousState(true) to get at the previous color

  3. Save the HSBType to a global var and restore from that.

Rich, can you give me more details to 1 & 3?

Color LastLightColor
case "OPEN": {
    GaesteWC_timer = createTimer(now.plusMinutes(5), [| 
        LastLightColor.postUpdate(EG_HUE_bloom_Wohnzimmer.state)
        EG_Hue_bloom_Wohnzimmer.sendCommand(HSBType::RED) 
    ])
}

...
case "CLOSED": {
    EG_Hue_bloom_Wohnzimmer.sendCommand(LastLightColor.state)
}
var HSBType lastColor = null

...
case "OPEN": {
    GaesteWC_timer = createTimer(now.plusMinutes(5), [| 
        lastColor = EG_HUE_bloom_Wohnzimmer.state
        EG_Hue_bloom_Wohnzimmer.sendCommand(HSBType::RED) 
    ])
}

...
case "CLOSED": {
    EG_Hue_bloom_Wohnzimmer.sendCommand(lastcolor)
}

It’s a shame you are not interested in 2. That would be my preferred solution. I find you get more capabilty for less code and complexity when you take advantage of the features of OH to solve your problems.

1 Like

I thought that previousState would not be a good idea, as I wanted the lamp to blink acc. to your “Alam Lights” example. Because of toggling every second ON/OFF the previousState is very uncertain.

If I am wrong, please also present solution 2! :slight_smile:

The second reason for avoiding option 2 was: I have not worked with persistence so far and the Add-ons in OH2 are still 1.9.0 so I did not want the risk to go insane.

To re-use code, do you think it would be a good idea to use your option 1 to call the Alarm_MasterEvent like this:

Color LastLightColor
case "OPEN": {
    GaesteWC_timer = createTimer(now.plusMinutes(5), [| 
        LastLightColor.postUpdate(EG_HUE_bloom_Wohnzimmer.state)
        Alarm_MasterEvent.sendCommand(ON)
    ])
}

...
case "CLOSED": {
    EG_Hue_bloom_Wohnzimmer.sendCommand(LastLightColor.state)
    Alarm_MasterEvent.sendCommand(OFF)
}

I got the following error in the log:

java.lang.RuntimeException: The name 'EG_HUE_bloom_Wohnzimmer' cannot be resolved to an item or type.

This was my code:

var HSBType lastColor = null

rule "Fensteralarm Gäste-WC Offen"
when
    Item EG_Fenster_Gaeste_WC changed
then
        GaesteWC_timer?.cancel // the ? will skip this line if GaesteWC_timer is null
        Thread::sleep(3000)
        
        logInfo("GaesteWC_timer", "Fenster ist " + EG_Fenster_Gaeste_WC.state)

        switch EG_Fenster_Gaeste_WC.state {
            case "OPEN": {
                GaesteWC_timer = createTimer(now.plusSeconds(10), [| 
                    lastColor = EG_HUE_bloom_Wohnzimmer.state
                    Alarm_MasterEvent.sendCommand(ON) 
                ])
            }
            case "TILTED": {
                GaesteWC_timer = createTimer(now.plusSeconds(10), [| 
                    lastColor = EG_HUE_bloom_Wohnzimmer.state
                    Alarm_MasterEvent.sendCommand(ON)
                ])
            }
            case "CLOSED": {
                Alarm_MasterEvent.sendCommand(OFF)
                EG_Hue_bloom_Wohnzimmer.sendCommand(lastcolor)
            }
        }
end


rule "Alam Lights"
when
    Item Alarm_MasterEvent received command ON
then
    if(lightsBlinkingTimer != null) {
        lightsBlinkingTimer.cancel
        lightsBlinkingTimer = null
    }

    lightsBlinkingTimer = createTimer(now.plusSeconds(1), [|
        EG_Hue_bloom_Wohn_Switch.sendCommand(if(EG_Hue_bloom_Wohn_Switch.state == ON) OFF else ON)
        lightsBlinkingTimer.reschedule(now.plusSeconds(1))
    ])
end

rule "Stop Alarm"
when
    Item Alarm_MasterEvent received command OFF
then
    if(lightsBlinkingTimer != null) {
        lightsBlinkingTimer.cancel
        lightsBlinkingTimer = null
    }
end

Can you see the problem?

The same happens for option 1:

java.lang.RuntimeException: The name 'EG_HUE_bloom_Wohnzimmer' cannot be resolved to an item or type.

This was the code:

In items:

Color LastLightColor

In rules:

rule "Fensteralarm Gäste-WC Offen"
when
    Item EG_Fenster_Gaeste_WC changed
then
        GaesteWC_timer?.cancel // the ? will skip this line if GaesteWC_timer is null
        Thread::sleep(3000)
        
        logInfo("GaesteWC_timer", "Fenster ist " + EG_Fenster_Gaeste_WC.state)

        switch EG_Fenster_Gaeste_WC.state {
            case "OPEN": {
                GaesteWC_timer = createTimer(now.plusSeconds(10), [| 
                    LastLightColor.postUpdate(EG_HUE_bloom_Wohnzimmer.state)
                    Alarm_MasterEvent.sendCommand(ON) 
                ])
            }
            case "TILTED": {
                GaesteWC_timer = createTimer(now.plusSeconds(10), [| 
                    LastLightColor.postUpdate(EG_HUE_bloom_Wohnzimmer.state)
                    Alarm_MasterEvent.sendCommand(ON)
                ])
            }
            case "CLOSED": {
                Alarm_MasterEvent.sendCommand(OFF)
                EG_Hue_bloom_Wohnzimmer.sendCommand(LastLightColor.state)
            }
        }
end

…and because of this error I believe the rule is not executed any further. The Alarm_MasterEvent is not changing state.

Any idea?

You might have a guess at this. It means it couldn’t find an Item of that name.
In your original rule you have
EG_Hue_bloom_Wohnzimmer
Note case of Hue not HUE

1 Like

Brilliant! It works! Thank you Rossko57!

In items:

Color LastLightColor

In rules:

rule "Fensteralarm Gäste-WC Offen"
when
    Item EG_Fenster_Gaeste_WC changed
then
        GaesteWC_timer?.cancel // the ? will skip this line if GaesteWC_timer is null
        Thread::sleep(3000)
        
        logInfo("GaesteWC_timer", "Fenster ist " + EG_Fenster_Gaeste_WC.state)

        switch EG_Fenster_Gaeste_WC.state {
            case "OPEN": {
                GaesteWC_timer = createTimer(now.plusSeconds(10), [| 
                    LastLightColor.postUpdate(EG_Hue_bloom_Wohnzimmer.state)
                    Alarm_MasterEvent.sendCommand(ON) 
                ])
            }
            case "TILTED": {
                GaesteWC_timer = createTimer(now.plusSeconds(10), [| 
                    LastLightColor.postUpdate(EG_Hue_bloom_Wohnzimmer.state)
                    Alarm_MasterEvent.sendCommand(ON)
                ])
            }
            case "CLOSED": {
                Alarm_MasterEvent.sendCommand(OFF)
                EG_Hue_bloom_Wohnzimmer.sendCommand(LastLightColor.state)
            }
        }
end


rule "Alam Lights"
when
    Item Alarm_MasterEvent received command ON
then
    if(lightsBlinkingTimer != null) {
        lightsBlinkingTimer.cancel
        lightsBlinkingTimer = null
    }
    EG_Hue_bloom_Wohnzimmer.sendCommand(HSBType::RED)
        lightsBlinkingTimer = createTimer(now.plusSeconds(1), [|
                EG_Hue_bloom_Wohn_Switch.sendCommand(if(EG_Hue_bloom_Wohn_Switch.state == ON) OFF else ON)
        lightsBlinkingTimer.reschedule(now.plusSeconds(1))
    ])
end

rule "Stop Alarm"
when
    Item Alarm_MasterEvent received command OFF
then
    if(lightsBlinkingTimer != null) {
        lightsBlinkingTimer.cancel
        lightsBlinkingTimer = null
    }
end

I learned a lot!

Still some ideas to option 2?

That does make it more complicated. You could use historicState(now.minusMinutes(X)) where X is how long ago the lights started blinking. But once you do that you really are not gaining much by using Persistence.

If I understand correctly, I actually wrote that up as a Design Pattern (Separation of Behaviors).

Dear Rich,
dear rossko57,
dear Forum,

thank you very much. I learned a lot and would like to share some of my learnings:

As my window has three states (TILTED, OPEN, CLOSED) I had the issue, if I moved from OPEN to TILTED, it lost the origin status of the light. First I had the following solution. I added

if(Alarm_MasterEvent.state == OFF) Alarm_MasterEvent.sendCommand(ON)

in the Timer. Then I realized to put

    Item Alarm_MasterEvent changed to ON

instead of

    Item Alarm_MasterEvent received command ON

in the when section of the ’rule "Alam Lights". This seems to have the same function.

Furthermore I realized that

var HSBType lastColor = null

does not work. It gave me a mismatch error message in the Eclipse SmartHome Designer. I tried it, it practically did not work. By not defining the type of variable, it worked. I have no explanation for that.

My code looks like that in the moment and my next step will be to add more windows. :slight_smile:

var lastColor = null

rule "Fensteralarm Gäste-WC Offen"
when
    Item EG_Fenster_Gaeste_WC changed
then
        GaesteWC_timer?.cancel // the ? will skip this line if GaesteWC_timer is null
        Thread::sleep(3000)
        
        logInfo("GaesteWC_timer", "Fenster ist " + EG_Fenster_Gaeste_WC.state)

        switch EG_Fenster_Gaeste_WC.state {
            case "OPEN": {
                GaesteWC_timer = createTimer(now.plusSeconds(5), [|Alarm_MasterEvent.sendCommand(ON)])
            }
            case "TILTED": {
                GaesteWC_timer = createTimer(now.plusSeconds(7), [|Alarm_MasterEvent.sendCommand(ON)])
            }
            case "CLOSED": {
        		Alarm_MasterEvent.sendCommand(OFF)               
            }
        }
end


rule "Alam Lights"
when
    Item Alarm_MasterEvent changed to ON
then
    if(lightsBlinkingTimer != null) {
        lightsBlinkingTimer.cancel
        lightsBlinkingTimer = null
    }
    lastColor = EG_Hue_bloom_Wohnzimmer.state
    Thread::sleep(500)
    EG_Hue_bloom_Wohnzimmer.sendCommand(HSBType::RED)
        lightsBlinkingTimer = createTimer(now.plusSeconds(1), [|
                EG_Hue_bloom_Wohn_Switch.sendCommand(if(EG_Hue_bloom_Wohn_Switch.state == ON) OFF else ON)
        lightsBlinkingTimer.reschedule(now.plusSeconds(1))
    ])
end

rule "Stop Alarm"
when
    Item Alarm_MasterEvent received command OFF
then
	EG_Hue_bloom_Wohnzimmer.sendCommand(lastColor)
    if(lightsBlinkingTimer != null) {
        lightsBlinkingTimer.cancel
        lightsBlinkingTimer = null
    }
end

If you should have anything to add, I am always happy to learn from you!

Greetings,
Timo

You may shorten your code

as Rich has already suggested by

lightsBlinkingTimer?.cancel
1 Like

@rlkoshak your timerName?.cancel is a great tip! Saved me so much hassle! Thank you!

Does anybody have a clue how to update the rule?

It seems the HUE-Binding has changed a lot. I’m running on openHAB 2.3.0 now.

Hi, when I use the

timerName?.cancel command in a rule I get the following error message in openhab.log and the rule doesn*t work at all.

2019-08-09 22:35:53.739 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'WZ_Alle_Rollos': 'cancel' is not a member of 'Object'; line 31, column 2, length 18

What is the reason for that and how can it be solved?

You probably haven’t declared the timerName as a Timer variable and it is interpreted as an instance of Object instead.

1 Like

Does anybody have an idea how to transfer the rules into Openhab 3.0?

The main rule checks for the change of the window item and starts a timer based on the three cases. After the timer has expired, it sends the command ON to the virtual item Alarm_MasterEvent:

rule "Fensteralarm Gäste-WC Offen"

when

    Item EG_Fenster_Gaeste_WC changed

then

        GaesteWC_timer?.cancel // the ? will skip this line if GaesteWC_timer is null

        Thread::sleep(3000)

        

        logInfo("GaesteWC_timer", "Fenster ist " + EG_Fenster_Gaeste_WC.state)

        switch EG_Fenster_Gaeste_WC.state {

            case "OPEN": {

                GaesteWC_timer = createTimer(now.plusMinutes(5), [|Alarm_MasterEvent.sendCommand(ON)])

            }

            case "TILTED": {

                GaesteWC_timer = createTimer(now.plusMinutes(7), [|Alarm_MasterEvent.sendCommand(ON)])

            }

            case "CLOSED": {

                Alarm_MasterEvent.sendCommand(OFF)               

            }

        }

end

Changes of the item Alarm_MasterEvent will be handled in these two rules:

rule "Alam Lights"

when

    Item Alarm_MasterEvent changed to ON

then

    if(lightsBlinkingTimer != null) {

        lightsBlinkingTimer.cancel

        lightsBlinkingTimer = null

    }

    EG_Hue_bloom_Wohn_LastColor.postUpdate(EG_Hue_bloom_Wohnzimmer.state)       //tracking state by item

    EG_Hue_bloom_Wein_LastColor.postUpdate(EG_Hue_bloom_Weinschrank.state)

    Thread::sleep(500)

    EG_Hue_bloom_Wohnzimmer.sendCommand(HSBType::RED)

    EG_Hue_bloom_Weinschrank.sendCommand(HSBType::RED)

        lightsBlinkingTimer = createTimer(now.plusSeconds(1), [|

                EG_Hue_bloom_Wohn_Switch.sendCommand(if(EG_Hue_bloom_Wohn_Switch.state == ON) OFF else ON)

                EG_Hue_bloom_Wein_Switch.sendCommand(if(EG_Hue_bloom_Wein_Switch.state == ON) OFF else ON)

        lightsBlinkingTimer.reschedule(now.plusSeconds(1))

    ])

end

and

rule "Stop Alarm"

when

    Item Alarm_MasterEvent received command OFF

then

    EG_Hue_bloom_Wohnzimmer.sendCommand(EG_Hue_bloom_Wohn_LastColor.state)  // posting state of lastColor item

    EG_Hue_bloom_Weinschrank.sendCommand(EG_Hue_bloom_Wein_LastColor.state)

    if(lightsBlinkingTimer != null) {

        lightsBlinkingTimer.cancel

        lightsBlinkingTimer = null

    }

end

I have no clue how to set this up in OH3. The start is easy:

Then I tried this code:

And checked for the status change of item Alarm_MasterEvent:

Analyse tracks no status change, the event log shows no changes and the rule delivers no errors.

Can anybody help? I would love to do it with Blockly but it seems not to have timers at all.

Thanks,
Timo

There are two problems with the code you tried.

  1. GaesteWC_timer is a global variable. I know of no way to define a global variable like that in Rules DSL in the UI. If you have rules that use a global variable like this, you are better off keeping it in .rules files. This is even more the case if that global variable is used by more than one Rule.

  2. The “then” on the first line renders this script syntactically invalid. Script Actions should not include “rule”, “when”, the triggers, “then” and “end”.

And if you are creating Timers anyway, why the sleep for three seconds?

Hi friends!
I would like to visualize the created timer to know how much is left until the process is complete. I have OpenHAB3.
Thanks to everyone who responds!

You cannot do that directly with createTimer().

You might get some ideas from here

or here

You forgot your design pattern. :smiley: Design Pattern : Expire Binding based Countdown timer

Note that I’ve a JavaScript library implementation for this DP now too. openhab-rules-tools/countdown_timer at main · rkoshak/openhab-rules-tools · GitHub