So, here is my situation:
My wife uses a sleep tracking app on her iPad, the theory is that when the alarm is set, it will wake you up within 15 minutes of the set time, at the point where you are the closest to being awake. This app will also trigger a Phillips Hue bulb to turn on with the alarm. Now, since I don’t have a Hue bulb in our bedroom (light is controlled by a z-wave dimmer), I attempted to write a rule so when the hue light turns on, it will trigger the light in the bedroom to increase in brightness by 10% every minute until it is at 100% brightness.
This is the rule that I came up with, trying to keep it as simple as possible:
var Timer Timer0
var Timer Timer1
var Timer Timer2
var Timer Timer3
var Timer Timer4
var Timer Timer5
var Timer Timer6
var Timer Timer7
var Timer Timer8
rule "morning alarm"
when
Item LightDeck changed to ON
then
logInfo("WakeupStarted", "Wakeup Started")
if(Snooze.state==OFF && MorningAlarm.state==ON) {
LightMaster.sendCommand(10)
Timer0 = createTimer(now.plusMinutes(1)) [|
LightMaster.sendCommand(20)]
Timer1 = createTimer(now.plusMinutes(2)) [|
LightMaster.sendCommand(30)]
Timer2 = createTimer(now.plusMinutes(3)) [|
LightMaster.sendCommand(40)]
Timer3 = createTimer(now.plusMinutes(4)) [|
LightMaster.sendCommand(50)]
Timer4 = createTimer(now.plusMinutes(5)) [|
LightMaster.sendCommand(60)]
Timer5 = createTimer(now.plusMinutes(6)) [|
LightMaster.sendCommand(70)]
Timer6 = createTimer(now.plusMinutes(7)) [|
LightMaster.sendCommand(80)]
Timer7 = createTimer(now.plusMinutes(8)) [|
LightMaster.sendCommand(90)]
Timer8 = createTimer(now.plusMinutes(9))[|
LightMaster.sendCommand(100)]}
else if(Snooze.state==ON) {
LightMaster.sendCommand(OFF)
logInfo("WakeupEnd", "Wakeup Ended")}
end
I am not getting any errors in the MS VS Code, but in my log, I am getting this:
2018-05-15 20:52:13.983 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'MorningAlarm.rules'
2018-05-15 20:52:14.000 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'MorningAlarm.rules' is either empty or cannot be parsed correctly!
2018-05-15 20:52:16.303 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'MorningAlarm.rules'
If anyone can help out with what I am missing, I cannot seem to see where I am getting the error from. All suggestions are appreciated!
Where did you do the editing? Did you edit in a windows machine and moved over to linux? Is there any other rules in this file? Are they working fine? If not, can you try a simple rule in the same file?
Trying to see if this is because a character encoding issue.
@Anjana, I created this rule in the eclipse smart home designer and edited it in the MS VS Code. I am running Openhabian 2.2 on a Raspberry Pi 3. There were no other rules in this file, but I added a simple test rule to turn on a lamp 5 minutes ago, it worked. I am thinking it might be an issue with my wife’s app that is causing this rule not to fire… unless you see something that is messed up with my code… @rlkoshak, Thanks for the reminder, I think I have seen that before and forgotten about it.
I would suggest a more clean way to increase the brightness:
var Timer Timer0 = null
rule "morning alarm"
when
Item LightDeck changed to ON
then
logInfo("morningAlarm", "Wakeup Started")
if(Snooze.state == OFF && MorningAlarm.state == ON && Timer0 === null) {
Timer0 = createTimer(now.plusSeconds(1)) [ | // Create a Timer
if ((LightMaster.state as Number) < 100) { // already reached highest Level?
LightMaster.sendCommand((LightMaster.state as Number) + 10) // Increase Level by 10
Timer0.reschedule(now.plusMinutes(1)) // Reschedule Timer
}
else
Timer0 = null // reinitialize Timer
]
}
else if(Snooze.state==ON) {
LightMaster.sendCommand(OFF) // Switch off
Timer0.cancel // Cancel Timer if running
Timer0 = null // reinitialize Timer
logInfo("morningAlarm", "Wakeup Ended")
}
end
rule "Snooze active"
when
Item Snooze changed to ON
then
Timer0.cancel
Timer0 = null
logInfo("snoozeActive", "Wakeup Ended by Snooze")
end
Please be aware that the first string of logInfo is the logger name. You can use the karaf console to switch the logger level for this name, so it’s a good idea to use the same logger name per rule.
Is the trigger correct? (I.e.: Does the Item LightDeckchange from another state to ON?)
@Udo_Hartmann, Thank you! That is definitely much cleaner code! This is a perfect example of what I need to learn about writing rules.
The item LightDeck can have a value of 0 to 100, but I have it in my items as a switch, not a color or dimmer item just to avoid any issues, so it should have just an ON or OFF state in OpenHAB. In the Hue app it could be set it to a different brightness (but we don’t use the hue app). I would assume that if that should happen the state would be overwritten when the alarm app sends the signal to turn the light ON. I will have to test this later today when I get home from work. Again, thank you for the well written rule!
Update, @Udo_Hartmann, I just entered (copy & paste) your rule in VS Code and I get this in the log:
2018-05-16 06:28:52.191 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'TestAlarm.rules' has errors, therefore ignoring it: [17,9]: missing '}' at 'else'
Looking at it, I put the ‘}’ above the “else if” and it shows fine in the log… hopefully this is the right location.
Yes, I did the correction above in case someone finds this rule.
I’m not sure about the LightDeck Item. Either the Item is defined as
Switch LightDeck "Some Label [%s]" {hue="..."}
then this Item will only have 3 states: NULL (as long as it is uninitialized), ON and OFF. But of course the hue binding has to take care that the item gets only ON and OFF state updates.
Or it is defined as
Dimmer LightDeck "Some Label [%d]" {hue="..."}
// or
Number LightDeck "Some Label [%d]" {hue="..."}
Then the state will either be NULL (see above) or an integer 0 to 100. Then the trigger would be different, and you would need to use it like this:
when
Item LightDeck changed // without constraint
then
if ((LightDeck.state as Number) != 0) {
// LightDeck is "ON"
} else {
// LightDeck is "OFF"
}
end
I have the DeckLight set the same as your first example. Having the parameters for the second set will definitely help with other rules that I plan to write! Having this example has helped me a lot in understanding how timers can counters can be used to make cleaner code. It’s clear I need to spend more time researching tutorials on how to write code and better/cleaner ways to use Java and Xtend!
@rlkoshak, thanks for this list, I had been using some of them, but didn’t realize that there were so many published! This will help me out a great deal!
I know this is an old post, but my issue is related. Since a couple of days I own a Osram light strip which is connected to OH by DeConz which is working fine so far. I reached to create a timer rule to switch off the light after a certain time of hours and minutes. So far so good.
Then I tried to create a rule, that the light strip is waking me up in the morning on a specific hour and minute, e.g. 6:30 am. The problem is that my rule is only working, if the defined clock is one minute after I set the wakeUp.
The rule:
var Timer Schlafzimmer_WakeUp = null
var Timer Lichtweckertimer = null
rule "morning alarm"
when
Item Lichtwecker_Osram changed to ON
then
if (Lichtwecker_Osram.state == ON) {
if(Lichtweckertimer === null) {
logInfo("morningAlarm", "Wecker aktiviert")
Lichtweckertimer = createTimer(now.plusMinutes(1))[ |
var sollMinuten = (Lichtschlauch_Wecker_M.state as DecimalType).intValue
var sollStunden = (Lichtschlauch_Wecker_H.state as DecimalType).intValue
if (sollMinuten == now.getMinuteOfHour && sollStunden == now.getHourOfDay) {
logInfo("morningAlarm", "Wakeup Started")
if(Lichtwecker_Osram.state == ON && Schlafzimmer_WakeUp === null) {
Schlafzimmer_WakeUp = createTimer(now.plusSeconds(1)) [ |
logInfo("morningAlarm", "Timer erstellt " + Lightstrip_1_Dimmer.state) // Create a Timer
if ((Lightstrip_1_Dimmer.state as Number) < 91) { // already reached highest Level? to avoid getting error messages because 101 would not be possible --> error loop // maybe setting the value to 0 before starting the +10 first time..
Lightstrip_1_Dimmer.sendCommand((Lightstrip_1_Dimmer.state as Number) + 10) // Increase Level by 10
Schlafzimmer_WakeUp.reschedule(now.plusMinutes(1)) // Reschedule Timer
}
else{
Lichtwecker_Osram.sendCommand(OFF)
logInfo("morningAlarm", "Wakeup finished")
}
]
}
}
]
}
}
end
rule "Wecker deaktiviert"
when
Item Lichtwecker_Osram changed to OFF
then
if (Schlafzimmer_WakeUp !== null) {
Schlafzimmer_WakeUp.cancel
Schlafzimmer_WakeUp = null
}
if (Lichtweckertimer !==null) {
Lichtweckertimer.cancel
Lichtweckertimer = null
}
logInfo("Wecker deaktiviert", "Wecker ausgeschaltet")
end
I assume that the issue is related to the “Lichtweckertimer” wich does not loop as expected maybe. Do you have a recommendation where my bug is?
You can take that if() right out. You already know that it’s just changed to ON.
This is good, you want to ensure only one copy of the timer is running.
But you do want it to run more than once in the system’s lifetime - you need to set that variable back to null once that Timer has finished doing its thing, ready for tommorrow.
There’s your one minute. You’re doing the check (I guess it is to see if now = some set time) inside the Timer code, which runs one minute after the rule was triggered.
Don’t you want to do that test before setting up the Timer?
var Timer Lichtweckertimer = null
rule "morning alarm"
when
Item Lichtwecker_Osram changed to ON
then
if(Lichtwecker_Osram.state == ON) {
var Number sollMinuten = if(Lichtschlauch_Wecker_H.state instanceof Number) (Lichtschlauch_Wecker_H.state as Number).intValue * 60 else -61
sollMinuten = if(Lichtschlauch_Wecker_M.state instanceof Number) (Lichtschlauch_Wecker_M.state as Number).intValue + sollMinuten else -1
if (sollMinuten >= 0 ) {
Lichtweckertimer?.cancel // cancel existing timer
Lichtweckertimer = createTimer(now.withTimeAtStartOfDay.plusMinutes(sollMinuten.intValue).plusDays(if (sollMinuten <= now.getMinuteOfDay ) 1), [
if((Lightstrip_1_Dimmer.state as Number) < 91) { // already reached highest Level?
Lightstrip_1_Dimmer.sendCommand((Lightstrip_1_Dimmer.state as Number) + 10) // Increase Level by 10
Lichtweckertimer.reschedule(now.plusMinutes(1)) // Reschedule Timer
} else {
Lichtwecker_Osram.sendCommand(OFF)
logInfo("morningAlarm", "Wakeup finished")
}
])
logInfo("morningAlarm", "Timer erstellt für {}:{} Uhr",Lichtschlauch_Wecker_H.state,Lichtschlauch_Wecker_M.state)
} else {
logWarn("morningAlarm", "Stunde oder Minute haben keinen gültigen Wert!")
}
} else {
Lichtweckertimer?.cancel
logInfo("morningAlarm", "Wecker ausgeschaltet")
}
end
As I did some shortcut, here’s the long version (more readable…):
klick to show code
var Timer Lichtweckertimer = null
rule "morning alarm"
when
Item Lichtwecker_Osram changed
then
if(Lichtwecker_Osram.state == ON) {
var Number sollMinuten // define var
if(Lichtschlauch_Wecker_H.state instanceof Number) // check if value valid
sollMinuten = (Lichtschlauch_Wecker_H.state as Number).intValue * 60 // set var
else
sollMinuten = -61 // mark as invalid
if(Lichtschlauch_Wecker_M.state instanceof Number) // check if value valid
sollMinuten = sollMinuten + (Lichtschlauch_Wecker_M.state as Number).intValue // set var
else
sollMinuten = -1 // mark as invalid
if (sollMinuten >= 0 ) { // check if time valid
Lichtweckertimer?.cancel // cancel existing timer
if (sollMinuten <= now.getMinuteOfDay) // time already expired today?
sollMinuten = sollMinuten + 1440 // add a day
Lichtweckertimer = createTimer(now.withTimeAtStartOfDay.plusMinutes(sollMinuten.intValue), [ // create the timer
if((Lightstrip_1_Dimmer.state as Number) < 91) { // check Level
Lightstrip_1_Dimmer.sendCommand((Lightstrip_1_Dimmer.state as Number) + 10) // Increase Level by 10
Lichtweckertimer.reschedule(now.plusMinutes(1)) // Reschedule Timer
} else { // or
Lichtwecker_Osram.sendCommand(OFF) // switch light off
logInfo("morningAlarm", "Wakeup finished")
}
])
logInfo("morningAlarm", "Timer erstellt für {}:{} Uhr",Lichtschlauch_Wecker_H.state,Lichtschlauch_Wecker_M.state)
} else {
logWarn("morningAlarm", "Stunde oder Minute haben keinen gültigen Wert!")
}
} else {
Lichtweckertimer?.cancel
logInfo("morningAlarm", "Wecker ausgeschaltet")
}
end
The biggest change is, the timer will fire once the alarm time is reached, but not every minute. Then it will fire every minute for ten minutes, until the light gets switched off.
As both rules use the same trigger, there is no need for two rules at all
2019-08-23 05:50:00.016 [ome.event.ItemCommandEvent] - Item 'Lightstrip_1_Dimmer' received command 10
2019-08-23 05:50:00.030 [nt.ItemStatePredictedEvent] - Lightstrip_1_Dimmer predicted to become 10
2019-08-23 05:50:00.191 [vent.ItemStateChangedEvent] - Lightstrip_1_Dimmer changed from 0 to 10
2019-08-23 05:50:07.771 [vent.ItemStateChangedEvent] - Lightstrip_1_Farbe changed from 347,97,0 to 347,97,25
2019-08-23 05:50:07.774 [vent.ItemStateChangedEvent] - Lightstrip_1_Dimmer changed from 10 to 25
2019-08-23 05:50:07.777 [vent.ItemStateChangedEvent] - Lightstrip_1_Schalter changed from OFF to ON
2019-08-23 05:50:17.776 [vent.ItemStateChangedEvent] - Lightstrip_1_Farbe changed from 347,97,25 to 347,97,10
2019-08-23 05:50:17.789 [vent.ItemStateChangedEvent] - Lightstrip_1_Dimmer changed from 25 to 10
That’s it. The Dimmer remains at the level of 10%. Do you have an idea what need to be adjusted that the device is dimming up to 100%?
@rossko57 thank you, my bad, didn’t check and attached the .log information. Please find these messages below:
2019-08-23 05:50:00.024 [ERROR] [org.quartz.core.JobRunShell ] - Job DEFAULT.2019-08-23T05:50:00.000+02:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ {
org.eclipse.xtext.xbase.impl.XIfExpressionImpl@e0617a
} ] threw an unhandled Exception:
java.lang.NullPointerException: cannot invoke method public abstract boolean org.eclipse.smarthome.model.script.actions.Timer.reschedule(org.joda.time.base.AbstractInstant) on null
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1071) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1061) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1047) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:772) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:460) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:244) ~[?:?]
at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
at com.sun.proxy.$Proxy137.apply(Unknown Source) ~[?:?]
at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
2019-08-23 05:50:00.195 [ERROR] [org.quartz.core.ErrorLogger ] - Job (DEFAULT.2019-08-23T05:50:00.000+02:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ {
org.eclipse.xtext.xbase.impl.XIfExpressionImpl@e0617a
} ] threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
Caused by: java.lang.NullPointerException: cannot invoke method public abstract boolean org.eclipse.smarthome.model.script.actions.Timer.reschedule(org.joda.time.base.AbstractInstant) on null
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1071) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1061) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1047) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:772) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:460) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:244) ~[?:?]
at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
at com.sun.proxy.$Proxy137.apply(Unknown Source) ~[?:?]
at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
... 1 more