Comparing Astro binding event time with now

Hi,
I am migration from OH 2.5 to 4 but I am not a Linux, Java, etc. enthusiast! :smiley: 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

Thank you,

It helps us to tell us what exactly is failing. Are there errors in the log? It doesn’t do anything? A lot can go wrong in a rule.

What I do notice here is that you are creating a DateTime and you should be creating a ZonedDateTime. In fact, you don’t really even need to do that.

    val sunrise = (astro_dawn_end.state as DateTimeType).getZonedDateTime()
    val sunset = (astro_dusk_start.state as DateTimeType).getZonedDateTime()

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

Thank you @rlkoshak it works now. I am sorry different time variables made me confused.

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.

thank you,

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