Check, whether sun is set in if statement in a rule

I’d like to setup a rule like this:

rule "something"
when
    //trigger
then
    //if (sunset) {
        //do something
    //}
end

I already have installed the Astro binding and set up a thing with it.

Now I’d like to know what is the best way to check, whether sun is set (maybe with an offset, the action triggered would be a light) in an if statement.

Thanks. However that is a bit too complex for my usecase, so I created an own approach:

var boolean sunset = false

rule "something"
when
    //trigger
then
    if (sunset == true) {
        //do something
    }
end

rule "Sunset actions"
when
    Channel 'astro:sun:home:set#event' triggered START 
then
    sunset = true
end

rule "Sunrise actions"
when
    Channel 'astro:sun:home:rise#event' triggered START 
then
    sunset = false
end

I did not test this right now and don’t know, whether this is the best and shortest way of achieving the wanted behaviour. So if you have a better idea, please let me know.

There is no simpler solution that:

  • lets you access the time of day in any of your .rules files
  • handles the case of OH restarting between sunset and sunrise

The time of day is really not complex. It is basically almost exactly what you did with the following differences:

  • more than one time of day is handled
  • the time of day is stored in an Item rather than a global var
  • it recalculates the time of day on system restart

Disadvantages of your approach:

  • sunset is only available in that one .rules file
  • if OH restarts between sunrise and sunset, sunset will be false even though it should be true

The only thing that makes the TimeOfDay pattern look complex is that it handles four times of day instead of just the one and it does everything in one Rule instead of spreading out in a lot of rules. If you reduce it to just the one time of day the rules become:

rule "Calculate time of day state"
when
  System started or
  Channel 'astro:sun:home:rise#event' triggered START or
  Channel 'astro:sun:home:set#event' triggered START
then
  Thread::sleep(1000) // make sure we are a tad past midnight to give Astro a chance to recalculate DateTimes for today

  val long day_start = (vSunrise_Time.state as DateTimeType).calendar.timeInMillis
  val long night_start = (vSunset_Time.state as DateTimeType).calendar.timeInMillis

  var curr = "UNKNOWN"

  switch now {
        case now.isAfter(day_start) && now.isBefore(night_start):                  curr = "DAY"
        case (now.isAfter(night_start) && now.isBefore(now.withTimeAtStartOfDay.plusHours(24))) ||
             (now.isAfter(now.withTimeAtStartOfDay) && now.isBefore(day_start)):   curr = "NIGHT"
  }

  if(vTimeOfDay.state.toString != curr) {
    logInfo(logName, "Current time of day is now " + curr)
    vTimeOfDay.sendCommand(curr)
  }

end
1 Like

You are right, your approach has benefits. But I don’t like to create items especially for the use inside of rules. I like it “simpler” with having everything in one file.

Maybe I will switch to your approach on the long run. Is there a way to avoid creating this items?

DateTime vSunrise_Time "Sunrise [%1$tH:%1$tM]"
    { channel="astro:sun:home:rise#start" }

DateTime vSunset_Time "Sunset [%1$tH:%1$tM]"
    { channel="astro:sun:home:set#start" }

Isn’t it possible to retrieve this data from inside the rule?

No. If you want the date/time of sunrise and sunset you must have Items. That is the whole purpose of Items. Items are the interface between OH and the bindings which interface with the external to OH world. With two exceptions (special channel triggers for rules and the ability to query the online status of a Time which has just been added) EVERYTHING in OH is built around Items. Items are not optional. Items are not something to be avoided.

This statement makes absolutely no sense. Almost the entire reason for Rules to exist is to update and command Items. If you have an aversion to Items why use openHAB in the first place?

Items are key to the way OH operates. Everything from the Event Bus to Persistence to the UIs all operate on Items. An aversion to using Items will ultimately lead you to overly complex and difficult to maintain rules in the long run and greatly hinder your ability to use the other parts of the OH architecture.

I can’t tell you what to do, but in the long run, you would be much better off using the features of OH now even though it feels more complex than trying to “keep everything simple” by avoiding using key parts of the OH architecture.

I wasn’t saying that I don’t want to use items at all, it just seems a bit weird to create an item to use it like a variable in a rule. That makes setting it up a bit unintuitive (at least for me) as you need to keep track of several files to set up or change something. But if this is how it is designed, I will use it that way.

I think it might be a bit helpful to make a distinction between what I’ll call a variable and what I’ll call state.

A variable is something that you might use temporarily while a rule is running, or to hold a reference to something you might need a little later (e.g. Timers), and constants. The key distinction is when OH restarts or reloads the .rules file, it doesn’t matter that your variables get wiped out and reinitialized. It also doesn’t matter that they are limited to being accessed from the one .rules file, that you cannot persist, restore, chart, and that you cannot put them on any of your UIs because they are really just needed by the rules that use them. Often they are intermediate values to some calculation, a timestamp, a constant that gets reused, a reference to a running Timer, or a lambda.

What I’m calling State is something that would be used in more than one .rules file or at least across multiple rules. Something that you may want to put on your UIs to keep track of. Something you may want to have persistence restore the previous value of when OH goes down or reloads the config files. Something that you may want to use to trigger rules to fire when it changes.

To me, a “sunset” switch like you are looking for meets almost all the criteria of a state and not a variable.

  • I know of no home automation setup that only has one set of behaviors that care about what time of day it is. Even if you only have one rule now, you will soon have several.
  • While putting the time of day on your sitemap, it can be useful to turn on or off the visibility of some lines (e.g. only show certain controls when it is night time)
  • Rather than triggering rules based on raw cron triggers or Astro events, when you centralize your time of day logic in one place you can let the time of day Item trigger the rules that need to fire at certain times of day. This also lets you easily add, remove, or change how the times of day are calculated in one place, leaving the rest of your Rules unchanged.

That is true and the reason, why I will switch to your solution in the future. For now it just seemed like much overhead, since I only needed it for one rule. But as you said, on the long run I will probably need this information in many rules.

I have a similar rule, but I just check if the sun elevation is below 0 (below the horizon), which means the sun is set :wink:

Item

Number   Sun_Elevation  "Sun Elevation"   { channel="astro:sun:local:position#elevation" }

Rule

rule "sunset check"
when
    // some trigger
then

    if(Sun_Elevation.state < 0) {

        // the sun is currently set
    
    } else {

        // the sun is currently not set

    }

end
4 Likes

Thanks for sharing your example.

OK so similar, but different…

I used to run Misterhouse about 15 years ago, and had that system turn on lights based on a PIR trigger… But only when it was after Sunset and before a time of night (01:00).

Is something similar with the Rules set?

Something like;

rule "lights on morning"
when
Time cron “0 0 7 ? * *” && EARLIER THAN Channel ‘astro:sun:home:rise#event’ triggered START
then
gHallLight.sendCommand(ON)
end

So the light comes on at 07:00 but only if it is dark at 07:00… If the sun:rise has occurred then keep the light off…

I am having a brain fart, trying to think of the way around it can be expressed in the Rules syntax…

First, How to use code fences

As demonstrated in post 4 and post 10, you use an if statement.

Rules are triggered by events. && makes no sense.

Assign the sun rise to an Item and check in the rule whether now is after that Item.

Apologies, being a bit thick… Have you got an example? And I also got the && being pointless thing from the above, hence the question on just how you overcome that…
so

DateTime    Sunrise_Time   "Sunrise [%1$tH:%1$tM]"  <sun> (Astro) {channel="astro:sun:home:rise#end"}
rule "lights on morning"
    when
         This is the bit I am struggling with.  If Sunrise_time is before 07:00 then do nothing else turn on the light.
    end

The next rule would then turn off the light at Sunrise_time

effectively I am trying to turn on the light in the morning at 07:00 but not if the Sun has risen…

The next bit of fun will be to turn on the lights based on an event, but not if the Sun HAS’NT set. Therefore keeping the lights off during the day if an even happens, but if its dark make that event turn them on… I can imagine that if we are talking finite times, but struggling when the time bit is moving about with the sun… :frowning: Brain hurts… Not understanding Object Orientation isnt helping much either, never have been able to get my head around that…

Brought up on too much basis and scripting languages…

lightbulb moment… Would something like this work?

rule "lights on morning"
   when
     Time cron “0 0 7 ? * *”
   then
      if(now.millis  > Sunrise_time){
         gHallLight.sendCommand(OFF)
      }else{
         gHallLight.sendCommand(ON)
      }
end

Post 4 and post 10 in this thread both have examples. Post 10 is about as simple of an example of using and if statement as it gets.

Please see the Rules Docs for some of the basics:

https://docs.openhab.org/configuration/rules-dsl.html

As documented:

  • Rules start with rule "Unique name for rule"
  • when
  • list of events that cause the rule to trigger
  • then
  • code to run when the rule triggers
  • end

The fact that your “lights on morning” rule does not conform to this and your inability to apply the example in posting 10 leads me to question whether you understand the basics well enough yet to proceed. You might benefit from going though one of the many online beginning programming courses just to get some basics in programming concepts understood. It doesn’t matter what language. I personally like the Python ones.

There is very little object oriented in the Rules DSL, and in the examples above there is basically nothing object oriented.

What you need to understand is basic programming concepts:

  • if , else
  • boolean logic
  • accessing methods or data on objects // this is the closes we get to OO concepts
  • how to look at and follow examples

Yes, with a few little corrections.

rule "lights on morning"
   when
     Time cron “0 0 7 ? * *”
   then
      // get the sunrise time as epoch millis
      val sunrise_millis = (Sunrise_Time.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli

      // use the isAfter method rather than comparing millis
      if(now.isAfter(sunrise_millis)){
         gHallLight.sendCommand(OFF)
      }else{
         gHallLight.sendCommand(ON)
      }
end

Changes:

  • I put on a separate line the extraction of the sunrise time in epoch milliseconds, this is the main thing you were missing
  • Case matters. You must match the Item name exactly. Above you defined your Item as Sunrise_Time.
  • Comparing the millis is fine, but I find the code to be more self documenting and often more flexible, to use the methods (e.g. now.isAfter, now.isBefore)

Glad you got past this hurdle. I’m sure you will be just fine.