Show Remaining Time of a Timer

So I have been trying this for hours and I cant wrap my head around it.
I am setting up an automated irrigation, which is working very well, especially through the help of @rlkoshak’s design patterns. It takes my hours to understand them (no computer savvy background whatsoever) but I got there. A Post in the solutions area will follow, once everything works to my satisfaction.

I have a Number Item (Setpoint in Sitemap) where I can set the IrrigationDuration in Minutes.
When Automated Irrigation is started, I want to show the RemainingMinutes of the Duration.

I found:

rule "Countdown"
    Time cron "0 0 * * * ?"
    var Number remain = MinutesRemainingOnTimer.state as DecimalType
    if((myTimer == null || myTimer.hasTerminated) && remain != 0) {
    MinutesRemainingOnTimer.postUpdate(remain - 1)

But I don’t really like it, because that rule would fire every Minute, no matter if the Irrigation is running or not. So I want something like this in plain text:

rule "Remaining Time"
AutoIrrigation changed to ON
Every Minute:

How on earth do I implement this?
Another approach I thought of is to calculate the End time and then subtract currentTime from the EndTime

So something like:

rule "Remaining Time"
When AutoIrrigation changed to ON
For Every minute calculate 
Stop When RemainingTime=0

Best regards from an eager student…

It would look something like

    Item AutoIrrigation changed to ON
    timer = createTimer(now.plusMinutes(1), [ |
        var remain = MinutesRemainingOnTimer.state - 1
        if( remain < 0 ) remain = 0
        if( remain > 0) timer.reschedule(now.plusMinutes(1))
1 Like

Would the if need {} ?
and Would I have to use

var remain = MinutesRemainingOnTimer.state as Number -1

Or actually

var remain = MinutesRemainingOnDripTimer.state as Number  
countdownDripTimer = createTimer(now.plusMinutes(1), [ |
remain = remain - 1
if( remain < 0 ) {remain = 0}
if( remain > 0){ countdownDripTimer.reschedule(now.plusMinutes(1))

something like that?

If there is only one statement that needs to run when the condition is true the {} is optional.

The formatting got a little mangled but as far as I can tell the cover you posted looks ok.

I fixed the formatting.
I implemented as above, but for some reason, it throws an error, when trying the timer expired (so when

remain = remain -1

comes up.
I`ll look into it again tonight and then post the complete look. Also I’ll do some reading into Xtend to better understand the syntax. Is there any good tutorial available?

What is the error that gets generated now?

Start with the Rules docs:

Then move on to (Note only the expressions page fully applies to OH Rules)

Then review the Design Patterns on this forum.

1 Like

Hey guys.
I want to share my working solution:

var Timer irrigationDripTimer = null
var Timer countdownDripTimer 

rule "Reset Irrigation"
    System started 

    createTimer(now.plusSeconds(80)) [|
    gDrip.members.forEach[ i | i.sendCommand(OFF)]
    Garden_Irrigation_DripSwitch.sendCommand(OFF) ]

Above code turns off Irrigation at restart of the system.

rule "Start Automatic Drip Irrigation"
    Item Garden_Irrigation_DripSwitch received command ON
 gDrip.members.forEach[ i | i.sendCommand(ON)]
   val currValveDripMins = Garden_Irrigation_DripDuration.state as Number
irrigationDripTimer = createTimer(now.plusMinutes(currValveDripMins.intValue), [|
gDrip.members.forEach[ i | i.sendCommand(OFF)]

Above code for the actual timer, next is defining a cancel action:

rule "Cancel Drip Irrigation"
    Item Garden_Irrigation_DripSwitch received command OFF    

    // Cancel the timer if there is one, the ? will cause the line to be skipped if timer is null
    irrigationDripTimer = null
    countdownDripTimer = null

    gDrip.members.forEach[ i | i.sendCommand(OFF)]

Now comes the rule for the Timer which shows the remaining Timer.

rule "Remaining"
when Item Garden_Irrigation_DripSwitch changed to ON 
val currValveDripMinutes = Garden_Irrigation_DripDuration.state as Number
var remain = currValveDripMinutes
countdownDripTimer = createTimer(now.plusMinutes(1), [|
remain = remain - 1
if( remain < 0 ) remain = 0
if( remain > 0){ countdownDripTimer.reschedule(now.plusMinutes(1))
    }    ])

By trial and error I learned some things about vals and vars and the declaratioion thereof.
When using timers I have to declare
var timer myTimer = null in the beginning?
Doing that for the timer used for the remain messed up the timer (errors came when I used Timer.reschedule(now.PlusMinutes(1)
Also I have some general questions Are var and val defined per Rule or globally? So can I just reuse them in different rules (that would make my rules a littler shorter and easier). Does it matter in which order the rules are in the rules file?

1 Like
var xx = 1      // a global, can be seen in all rules (in this rules file)
                      // but not by rules in other files

rule xxx
    var zz = 1   // only accessible in the context of this rule
    createTimer( ...  [  |
           // code executed here is in global context
           // globals cannot 'see' each other
           // code cannot see 'xx'
           // nor can code see 'zz' which was in rule context

I think you are beginning to run into the difficulties described in Rik’s post below, in particular
“lambdas do not have access to the global vals and vars in their context so all of those must be passed as arguments.”