Variable Wakeup-Time Sequence

Situation now:
I set my alarm clock each evening for the next morning via Alexa.
I have my heating based on fixed cron times in my rules. They will heat the rooms so they are warm at the time I normally wake-up and stop the heating I normally leave home.

Goal:
Set a wake-up time for the next day (preferable via Alexa).
Have the heaters turn on a fixed time before the set wakeup time.
Play music, switch on light, whatever at the set wakeup time.

At first, I ignore the alexa part and try to get the timer. So what is the rule fragment to have a timer at 6 o’clock in the morning executing?

import org.joda.time.*;
var Timer wakeupTimer = null
rule "wakeup"
when
   Item wakeup_time changed
then
   wakeupTimer ?.cancel
   wakeupTimer = createTimer(???) [
   // Play wakeup music
   ]
end

This would give 6AM the next day…

now.plusDays(1).withTime(6, 0, 0, 0)
1 Like

Where do I find the documentation to get “plusDays()” and “withTime()”.

These are methods of org.joda.time.Datetime… http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTime.html

BTW, you don’t need the import with OH2.

I made 2 items:

Number	wakeup_Felix_hour
Number	wakeup_Felix_minute

and the rule:

rule "wakeup"

when
	Item wakeup_Felix_hour changed or
	Item wakeup_Felix_minute changed 
then
	wakeupTimer?.cancel
	logInfo("wakeup", "Starting timer " + wakeup_Felix_hour.state + ":" + wakeup_Felix_minute.state)
	wakeupTimer = createTimer(now.plusDays(1).withTime(wakeup_Felix_hour.state, wakeup_Felix_minute.state, 0, 0))[|
		logInfo("wakeup", "Time is up")
		wakeupTimer = null
    ]
end

and I get an error:

2019-11-24 16:42:41.965 [INFO ] [clipse.smarthome.model.script.wakeup] - Starting timer 6:6
2019-11-24 16:42:41.978 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'wakeup': An error occurred during the script execution: Could not invoke method: org.joda.time.DateTime.withTime(int,int,int,int) on instance: 2019-11-25T16:42:41.968+01:00

A more common way to solve this issue:

rule "wakeup"

when
	Item wakeup_Felix_hour changed or
	Item wakeup_Felix_minute changed 
then
	wakeupTimer?.cancel
    var Integer nHour = null
    var Integer nMinute = null
    if(wakeup_Felix_hour.state instanceof Number) nHour = wakeup_Felix_hour.state as Number
    if(wakeup_Felix_minute.state instanceof Number) nMinute = wakeup_Felix_minute.state as Number
    logInfo("wakeup", "Starting timer {}:{}",nHour,nMinute)
    if(nHour === null || nMinute === null) {
        logWarn("wakeup","Time not set!")
        return;
    }
    wakeupTimer = createTimer(now.plusDays(1).withTimeAtStartOfDay.plusMinutes(nHour*60 + nMinute), [|
		logInfo("wakeup", "Time is up")
		wakeupTimer = null
    ])
end

withTimeAtStartOfDay is Midnight, plusMinutes adds the given amount of Minutes.

Please always ensure that type of data is correct to avoid null-pointer-exceptions.

1 Like

Try…

withTime(wakeup_Felix_hour.state.intValue, wakeup_Felix_minute.state.intValue, 0, 0)

Beware withTimeAtStartOfDay and daylight savings… especially for a wakeup alarm!

1 Like

I’m pretty sure, that there is no method .intValue for state. Instead you’ll have to use a Type Casting to Number, first:

withTime((wakeup_Felix_hour.state as Number).intValue, (wakeup_Felix_minute.state as Number).intValue, 0, 0)

But of course you have to ensure that the state is of type Number, before casting to Number. Otherwise you will get a null pointer exception… :wink:

1 Like

You’re right… the new rule engine handles this much better!

Well… a state is a state :slight_smile: but of course I would like to have a .toInt or .toNumber method (as there is a .toString method, too)

The state of a NumberItem is DecimalType, which does have an intValue method (and floatValue, longValue, doubleValue, etc.)…

… but in the old rule engine, you need to cast the state first. In the new rule engine, this is not necessary. I’m getting a little rusty with the old stuff :wink:!

For example…

Rules DSL

logWarn("Old Rules", "{}", (Virtual_Number_1.state as Number).intValue)

Scripted Automation (Jython)

from core.actions import LogAction
LogAction.logWarn("New Rules", "{}", items["Virtual_Number_1"].intValue())
1 Like

I now get error messages, I don’t understand

==> /var/log/openhab2/openhab.log <==
2019-11-25 20:00:59.566 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'wakeup': An error occurred during the script execution: Couldn't invoke 'assignValueTo' for feature JvmVoid:  (eProxyURI: timemachine.rules#|::0.2.1.2.0.1.1::0::/1)

Full code of the rule:

var Timer wakeupTimer = null

rule "initWakeup"

when 
	System started
then
	postUpdate(wakeup_Felix_hour, 0)
	postUpdate(wakeup_Felix_minute, 0)
	logInfo("wakeup", "System start")
end

rule "wakeup"

when
	Item wakeup_Felix_hour changed or
	Item wakeup_Felix_minute changed 
then
	wakeupTimer?.cancel
	if(wakeup_Felix_hour.state instanceof Number) nHour = wakeup_Felix_hour.state as Number
	if(wakeup_Felix_minute.state instanceof Number) nMinute = wakeup_Felix_minute.state as Number
	logInfo("wakeup", "Starting timer " + nHour  + ":" + nMinute )
	wakeupTimer = createTimer(now.plusDays(1).withTime(nHour, nMinute , 0, 0))[|
		logInfo("wakeup", "Time is up")
		wakeupTimer = null
    ]
end

the Vars aren’t defined before setting the value.
nHour and nMinute have to be of Type Integer. Both values have to have a default value and you have to ensure that both values are valid. Please take a look at the code in my posting Variable Wakeup-Time Sequence

How is this?

rule "wakeup"
when
    Item wakeup_Felix_hour received update
    or
    Item wakeup_Felix_minute received update 
then
    wakeupTimer?.cancel
    if (wakeup_Felix_hour.state instanceof Number && wakeup_Felix_minute.state instanceof Number) {
        var nHour = (wakeup_Felix_hour.state as Number).intValue
        var nMinute = (wakeup_Felix_hour.state as Number).intValue
        logInfo("wakeup", "Starting timer {}:{}", nHour, nMinute)
        wakeupTimer = createTimer(now.plusDays(1).withTime(nHour, nMinute , 0, 0))[|
            logInfo("wakeup", "Time is up")
            wakeupTimer = null
        ]
    } else {
        logWarn("wakeup", "Could not start timer due to missing numbers: wakeup_Felix_hour={}, wakeup_Felix_minute={}", wakeup_Felix_hour.state, wakeup_Felix_minute.state)
    }
end

Ok, I have it running. Thank you, both of you.
I have the echo dots waiting for me to pick them up at post office. Then I’ll come back…

Have it running with an echo dot.
Now I’m dealing with DateTime as data type, but this is brilliant:


The issue I face now: When I cancel the alarm or the alarm is executed:

2019-11-27 09:40:15.216 [vent.ItemStateChangedEvent] - Echo_Next_Alarm changed from 2019-11-27T09:40:00.000+0100 to UNDEF

I try to catch it in the rule:

	if (Echo_Next_Alarm.state == 'UNDEF') {
		logInfo("wakeup", "Timer cancelled")
	} else {

but get an error:

2019-11-27 09:40:15.227 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'wakeup': d != java.lang.String

Remove the quotes (UNDEF is UnDefType, not StringType)…

if (Echo_Next_Alarm.state == UNDEF) {

Works. Thank you!

1 Like