Hi,
I am migration from OH 2.5 to 4 but I am not a Linux, Java, etc. enthusiast! I read several topics here and about time variables and conversions, etc. but I can’t make my rule work in OH4. I can’t make Astro events time comparable with Now. Could you please help me to compare Astro events time with Now properly? This is my OH2.5 rule what worked well:
rule "Garden Gate"
when
Item os_garden_door changed to ON
then
val sunrise = new DateTime(astro_dawn_end.state.toString)
val sunset = new DateTime(astro_dusk_start.state.toString)
if( (now.isAfter(sunset) && Outside_walk_lamps.state == OFF) || (now.isBefore(sunrise) && Outside_walk_lamps.state == OFF ) ){
Outside_walk_lamps.sendCommand(ON)
createTimer(now.plusSeconds(900), [|
Outside_walk_lamps.sendCommand(OFF)
])
}
end
I know you’re just upgrading so you’d probably prefer to keep changes to a minimum. However, openHAB 4 brings modern automation languages such as JS Scripting and JRuby that you should check out because it offers many advantages over RulesDSL.
Here’s three versions of your rule implemented in JRuby. The second one uses the sun elevation from the astro binding which is my preferred method of tracking sunrise/sunset.
Note that JRuby has handy built in timers, in this case, implicitly created by the for: 15.minutes argument. It saves you from having to manually create a timer and keeping track of it yourself. It will auto reset the timer should the garden door closed and reopened before the timer had expired. There are other handy little things that help you deal with timers and other things more easily.
rule "Garden Gate" do
changed os_garden_door, to: ON
run do
if Time.now.between?(astro_dusk_start.state, astro_dawn_end.state)
Outside_walk_lamps.ensure.on, for: 15.minutes
end
end
end
# Option 2, use the between guard
rule "Garden Gate" do
changed os_garden_door, to: ON
between astro_dusk_start.state..astro_dawn_end.state
run do
Outside_walk_lamps.ensure.on, for: 15.minutes
end
end
# Alternative implementation
rule "Garden Gate" do
changed os_garden_door, to: ON
run do
if things["astro:sun:home"].get_elevation(nil).negative?
Outside_walk_lamps.ensure.on, for: 15.minutes
end
end
end
# Using only_if guard
rule "Garden Gate" do
changed os_garden_door, to: ON
only_if { things["astro:sun:home"].get_elevation(nil).negative? }
run do
Outside_walk_lamps.ensure.on, for: 15.minutes
end
end
Dear @JimT,
I really appreciate your detailed answer. Maybe your solution is more advanced then a simple rule based one, but since I have experience with rules I stick to that solution.
The problem with your rule is that the timer doesn’t restart.
Say you open the gate, then in 14.5 minutes, you close it, then open it again. You would expect the light to stay on for 15 minutes after the last time you opened it, but the first timer would kick in and turn it off after only 30 seconds.
You need to cancel the previous timer.
This is the type of bookeeping that you don’t need to do with jruby’s library, although in this particular care it’s not hard to do in rulesdsl.
In your rulesdsl it would look like this.
var Timer timer = null
rule "Garden Gate"
when
Item os_garden_door changed to ON
then
val sunrise = (astro_dawn_end.state as DateTimeType).zonedDateTime
val sunset = (astro_dusk_start.state as DateTimeType).zonedDateTime
if (Outside_walk_lamps.state == OFF && (now.isAfter(sunset) || now.isBefore(sunrise))) {
Outside_walk_lamps.sendCommand(ON)
timer?.cancel
timer = createTimer(now.plusSeconds(900), [|
Outside_walk_lamps.sendCommand(OFF)
])
}
end