Cannot get rule to Cancel Timer

Openhabian- PI 3 - openHAB 2.2.0-1 (Release Build)

I trying to get a timer based rule to work correct , logic is when Garage door is left open for longer than 2 minutes send me a message , if door is closed within that time frame (less than 2 minutes) then cancel the timer

Timer triggers successfully but does not “cancel” when door is closed … hence even if door is closed with the 2 minute time frame i still get the pushover that its open for more than 2 minutes… i have tried may iterations such as :

GarageTimer.cancel()
GarageTimer = null
GarageTimer?

Current code not working … no log errors …Please help

var Timer GarageTimer = null

rule "Garage Open Too Long"
when
  Item GGDoorSensor1 received update OPEN
then
	if (GarageTimer === null) {
 	GarageTimer = createTimer(now.plusSeconds(120)) [|
	pushover("Garage Door 1 Open for more than 2 Minutes!!!!")
    GarageTimer = null   // reset the timer
  ]
}
end

rule "Garage Door Closed - Cancel Timer"
when
    Item GGDoorSensor1 received update CLOSED
then
	GarageTimer?.cancel
	GarageTimer = null
end

I wrote a function for this:

import java.util.HashMap
var HashMap<String, Timer> tTimersWithCancel = newHashMap()

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// parameters:
// relatedItem = Item which should be changed if tiemr ends
// tTimersWithCancel = pointer to the timer variable
// timerlengthinseconds = timer time in sec.
//                        Length in sec. of the timer
//   					  0 = Timer should end now and timer end action should be done
//  					 -1 = Timer should cancel and action should not be done
// newitemstate = State of the item after timer ends
//
val Procedures$Procedure4<GenericItem, HashMap<String, Timer>, Integer, String> startTimerWithCancel=[relatedItem, tTimersWithCancel, timerlengthinseconds, newitemstate |
	// Timer should 3end before real end
	// end action should be done
	if(timerlengthinseconds == 0)
	{
		if(tTimersWithCancel.get(relatedItem.name) !== null)
			tTimersWithCancel.get(relatedItem.name).reschedule(now)
		return;
	}
	
	// Timer should be canceled
	// end action should not be done
	if(timerlengthinseconds < 0)
	{
		if(tTimersWithCancel.get(relatedItem.name) !== null)
		{
			tTimersWithCancel.get(relatedItem.name).cancel()
			tTimersWithCancel.put(relatedItem.name, null)
			tTimersWithCancel.remove(relatedItem.name)
			logInfo("TIMERCHECK", "TimerWithCancel for Item " + relatedItem.label + " canceled. No action executed.")
		}
		return;
	}
	
	// Timer should be started
	// wait a little bit, if two timers end on the same time not all of them are execute the commands
	Thread::sleep(100)

	// create the timer and do the action at the end of the timer
	if(tTimersWithCancel.get(relatedItem.name) === null || tTimersWithCancel.get(relatedItem.name).hasTerminated)
	{
		logInfo("TIMERCHECK", "TimerWithCancel for Item " + relatedItem.label + " started with " + timerlengthinseconds + " sec.")
		tTimersWithCancel.put(relatedItem.name, createTimer(now.plusSeconds(timerlengthinseconds)) [|
			tTimersWithCancel.get(relatedItem.name).cancel()
			tTimersWithCancel.put(relatedItem.name, null)
			tTimersWithCancel.remove(relatedItem.name)
			logInfo("TIMERCHECK", "TimerWithCancel for Item " + relatedItem.label + " reached end. Action " + newitemstate.toString + " executed.")
			relatedItem.sendCommand(newitemstate.toString)
		])
	}

	// timer is running, just reschedule with full value of time
	else
	{
		logInfo("TIMERCHECK", "TimerWithCancel for Item " + relatedItem.label + " rescheduled with " + timerlengthinseconds + " sec.")
		tTimersWithCancel.get(relatedItem.name).reschedule(now.plusSeconds(timerlengthinseconds))
	}
]

Can be called by using:

start timer:
startTimerWithCancel.apply(swFixedTimerSwitch01, tTimersWithCancel, 60, "OFF")

reschedule timer:
startTimerWithCancel.apply(swFixedTimerSwitch01, tTimersWithCancel, 120, "OFF")

end timer:
startTimerWithCancel.apply(swFixedTimerSwitch01, tTimersWithCancel, 0, "OFF")

cancel timer:
startTimerWithCancel.apply(swFixedTimerSwitch01, tTimersWithCancel, -1, "OFF")

maybe this will help you, sorry for some german comments.

Thanks …

Not to be unappreciative , however why is the standard Rule timer logic not working ?

You will be much better off using the expire binding

https://docs.openhab.org/addons/bindings/expire1/readme.html

and:

it is working but I prefer to use it from other rules too and do not want to recode it every time.

For a quick answer to your question
yourtimer.cancel()
is what you are searching for

or
yourtimer.reschedule(now)
to stop timer and do end action.

@vzorglub
maybe you are right, but with expire binding you cannot configure timer time at runtime of OH. You have to change the item file every time.

No you can’t, but the question was about a garage door timer going off after 2 minutes. Not about a variable period of time.

In fact, the rule should work as expected. Maybe the trigger does not work correct (e.g. contact bouncing i.e. multiple trigger).
You should insert a few lines for logging. By the way, you could use one rule:

var Timer GarageTimer = null

rule "Garage Door"
when
    Item GGDoorSensor1 changed
then
    logInfo ("garage.door","GGDoor Sensor 1 is {}",GGDoorSensor1.state)
    if (GGDoorSensor1.state == OPEN) {
        if (GarageTimer === null) 
            logInfo ("garage.door","GarageTimer is null -> create timer!")
            GarageTimer = createTimer(now.plusSeconds(120)) [|
                pushover("Garage Door 1 Open for more than 2 Minutes!!!!")
                GarageTimer = null   // reset the timer
            ]
        else
            logInfo ("garage.door","GarageTimer is not null -> do nothing!")
    }
    else {
        logInfo ("garage.door","so cancel GarageTimer!")
        GarageTimer.cancel
        GarageTimer = null
    }
end