Whats wrong with my timer

I have a set of timers, depending on my wake-up time (alarm of alexa), the thermostats are controlled. Bad is never getting the call to switch to daytime-cold - so I don’t see the log entry “Timer Bad fertig”. Rule:

rule "wakeup"

when
	Item Echo_Next_Alarm changed 
then
	wakeupTimer?.cancel
	HeatTimer_Bad_SetWarm?.cancel
	HeatTimer_Bad_SetCold?.cancel
	HeatTimer_Arbeitszimmer_SetWarm?.cancel
	HeatTimer_Arbeitszimmer_SetCold?.cancel
	if (Echo_Next_Alarm.state == UNDEF) {
		logInfo("wakeup", "Timer cancelled")
	} else {
        logInfo("wakeup", Echo_Next_Alarm.state.format("%1$td.%1$tm.%1$ty %1$tH:%1$tM"))
        wakeupTimer = createTimer(new DateTime((Echo_Next_Alarm.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli))[|
            logInfo("wakeup", "Time is up")
            wakeupTimer = null
        ]
		HeatTimer_Bad_SetWarm = createTimer(new DateTime((Echo_Next_Alarm.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli).minusHours(2))[|
			logInfo("wakeup", "Timer Bad aufheizen")
			Solltemperatur_Bad.sendCommand(22)
            HeatTimer_Bad_SetWarm = null
        ]
		HeatTimer_Bad_SetCold = createTimer(new DateTime((Echo_Next_Alarm.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli).plusHours(1))[|
			logInfo("wakeup", "Timer Bad fertig")
			Solltemperatur_Bad.sendCommand(19)
            HeatTimer_Bad_SetCold = null
        ]
		HeatTimer_Arbeitszimmer_SetWarm = createTimer(new DateTime((Echo_Next_Alarm.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli).minusHours(3))[|
			logInfo("wakeup", "Timer Arbeitszimmer aufheizen")
			Solltemperatur_Arbeitszimmer.sendCommand(22)
            HeatTimer_Arbeitszimmer_SetWarm = null
        ]
		HeatTimer_Arbeitszimmer_SetCold = createTimer(new DateTime((Echo_Next_Alarm.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli).plusHours(1))[|
			logInfo("wakeup", "Timer Arbeitszimmer fertig")
			Solltemperatur_Arbeitszimmer.sendCommand(19)
            HeatTimer_Arbeitszimmer_SetCold = null
        ]
	}
end

Logfile:

2019-12-09 22:04:11.728 [INFO ] [clipse.smarthome.model.script.wakeup] - 10.12.19 05:50
2019-12-10 02:50:00.006 [INFO ] [clipse.smarthome.model.script.wakeup] - Timer Arbeitszimmer aufheizen
2019-12-10 03:50:00.005 [INFO ] [clipse.smarthome.model.script.wakeup] - Timer Bad aufheizen
2019-12-10 05:50:00.005 [INFO ] [clipse.smarthome.model.script.wakeup] - Time is up
2019-12-10 05:50:20.600 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'wakeup': 'cancel' is not a member of 'java.lang.Object'; line 25, column 2, length 39
2019-12-10 06:50:00.005 [INFO ] [clipse.smarthome.model.script.wakeup] - Timer Arbeitszimmer fertig

Line 25 is:

 wakeupTimer?.cancel

Is wakeupTimer declared outside of the rule? It does need to be.

Yes, declaration:

var Timer wakeupTimer = null
var Timer HeatTimer_Bad_SetWarm = null
var Timer HeatTimer_Bad_SetCold = null
var HeatTimer_Arbeitszimmer_SetWarm = null
var HeatTimer_Arbeitszimmer_SetCold = null

Feels like you have another rule interfering with wakeupTimer.
You declared it as a Timer object. It will get set to null, and later by createTimer, and later still null again.
So
wakeupTimer?.cancel
should operate on either a Timer object or be omitted by the ? if null
Rule 'wakeup': 'cancel' is not a member of 'java.lang.Object'
seems to imply that it is neither at that moment.

Are you expecting this rule to be run again while some of its child timers are still active? It would have cancelled the Arbeitszimmer +1 timer had the rule not errored.

Maybe consider to change the rule:

// Always define global vars on top of file
var Timer tWakeup = null
var Integer iWakeup = 0

rule "wakeup"

when
    Item Echo_Next_Alarm changed 
then
    tWakeup?.cancel
    if (Echo_Next_Alarm.state == UNDEF) {
        logInfo("wakeup", "Timer cancelled")
    } else {
        logInfo("wakeup", Echo_Next_Alarm.state.format("%1$td.%1$tm.%1$ty %1$tH:%1$tM"))
        val dtAlarm = new DateTime((Echo_Next_Alarm.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
        iWakeup = 0
        tWakeup = createTimer(dtAlarm.minusHours(3), [ |
            iWakeup += 1
            switch(iWakeup) {
                case 1: {
                    logInfo("wakeup", "Timer Arbeitszimmer aufheizen")
                    Solltemperatur_Arbeitszimmer.sendCommand(22)
                    tWakeup.reschedule(now.plusHours(1))
                }
                case 2: {
                    logInfo("wakeup", "Timer Bad aufheizen")
                    Solltemperatur_Bad.sendCommand(22)
                    tWakeup.reschedule(now.plusHours(2))
                }
                case 3: {
                    logInfo("wakeup", "Time is up")
                    tWakeup.reschedule(now.plusHours(1))
                }
                case 4: {
                    logInfo("wakeup", "Timer Bad fertig")
                    Solltemperatur_Bad.sendCommand(19)
                    logInfo("wakeup", "Timer Arbeitszimmer fertig")
                    Solltemperatur_Arbeitszimmer.sendCommand(19)
                    tWakeup = null
                }
            }
        ])
    }
end

Please be aware that there is no check if dtAlarm is more than 3 hours in future.

Missing key word Timer

1 Like

I have to explain the code a bit: In the evening I tell alexa what time I want to wake up, this triggers the “Echo_Next_Alarm” change. - Log entry at 22:04

First I clear any timer that is running. Then I create 5 timers.
The wakeupTimer is printing the “Time is up” log message exactly at the right time.
20 seconds after that I get the error message. Maybe alexa is executing an “Echo_Next_Alarm” change to “UNDEF”? Which I should catch, but the error should prevent that “HeatTimer_Bad_SetCold” is cancelled, as “HeatTimer_Arbeitszimmer_SetCold” is executing see below.
The Heater in “Arbeitszimmer” is turned on 3 hours ahead of time.
The Heater in “Bad” is turned on 2 hours ahead of time.
The Heater in “Arbeitszimmer” is turned off 1 hour after the wakeup call.
But - the heater in “Bad” does not turn off 1 hour after the wakeup call.

Find out - look in your events.log
Something triggered your rule.

Yes

2019-12-10 05:50:20.582 [vent.ItemStateChangedEvent] - Echo_Next_Alarm changed from 2019-12-10T05:50:00.000+0100 to UNDEF

But still why does the heater in the bath “Bad” not turn off but the heater in “Arbeitszimmer”?
And whats wrong with

wakeupTimer?.cancel

As the log shows :

2019-12-10 05:50:00.005 [INFO ] [clipse.smarthome.model.script.wakeup] - Time is up

This section was executed:

            logInfo("wakeup", "Time is up")
            wakeupTimer = null

So
null?.cancel shoul do nothing!

Because you cancelled it.
At 05:50:20, your Echo_Next_Alarm is changed.
Your HeatTimer_Bad_SetCold still has an hour to go yet
but
the change makes your rule run
and
HeatTimer_Bad_SetCold?.cancel
cancels the Bad timer

I think that is the big clue. The error message about cancel is not coming from
wakeupTimer?.cancel
but from one of
HeatTimer_Arbeitszimmer_SetWarm?.cancel
HeatTimer_Arbeitszimmer_SetCold?.cancel
You could prove that by adding progress logInfo()

Why, what is different about those? Udo has already spotted that -

You are going to need to rethink your strategy to deal with Alexa “announcing” alarm time has passed.

Now I see it, yes! I changed that, so tomorrow I should run into the situation where the two timers HeatTimer_Bad_SetCold and HeatTimer_Arbeitszimmer_SetCold are cancelled before they execute - so temperature will stay at 22 during the day.

var Timer wakeupTimer = null
var Timer HeatTimer_Bad_SetWarm = null
var Timer HeatTimer_Bad_SetCold = null
var Timer HeatTimer_Arbeitszimmer_SetWarm = null
var Timer HeatTimer_Arbeitszimmer_SetCold = null

Be interesting to see. So far as I can see, at the moment of failure the last thing that happened to that variable was assignment = to createTimer. I’d have thought that would force it into a Timer type object.

Result:
As expected:
HeatTimer_Bad_SetCold and HeatTimer_Arbeitszimmer_SetCold are cancelled before they execute - so temperature will stay at 22 during the day.

Still I get the error:

2019-12-11 05:50:00.005 [INFO ] [clipse.smarthome.model.script.wakeup] - Time is up
2019-12-11 05:50:05.467 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'wakeup': 'cancel' is not a member of 'java.lang.Object'; line 25, column 2, length 39

There is no other code calling this timer. I also did expect the behaviour explained in post 2:

You declared it as a Timer object. It will get set to null , and later by createTimer, and later still null again.
So
wakeupTimer?.cancel
should operate on either a Timer object or be omitted by the ? if null

Add logInfo() to where you think the error is. Might as well log out object type.

	logInfo("diag", "timer1 {}", wakeupTimer)
	wakeupTimer?.cancel
	logInfo("diag", "timer2 {}", HeatTimer_Bad_SetWarm)
	HeatTimer_Bad_SetWarm?.cancel
	logInfo("diag", "timer3 {}", HeatTimer_Bad_SetCold)
	HeatTimer_Bad_SetCold?.cancel
	logInfo("diag", "timer4 {}", HeatTimer_Arbeitszimmer_SetWarm)
	HeatTimer_Arbeitszimmer_SetWarm?.cancel
	logInfo("diag", "timer5 {}", HeatTimer_Arbeitszimmer_SetCold)
	HeatTimer_Arbeitszimmer_SetCold?.cancel

I’m still suspicious something else is using one of those variable names.
someVariable?.cancel
will break in with the error you get if it is neither null nor a Timer object.

It is a Heisenbug!
A Heisenbug is a bug that disappears when you try to observe it.
So this morning all the log mesages and no error message.

The logInfo propably forced a type check/type cast? I’m not familiar with the behaviour of such interpreters, having used strongly typed languages only.