[SOLVED] Astro binding - hours until / after sunrise

Folks,

I have been using Rich’s astro example for a while now.


rule "Update NightState"
when
    Item SunElevation changed
//    or Item SunElevation_proxy changed
then
	if (AutoDayNight.state == OFF){
		return
	}
    if(SunElevation.state >  0){ //it's daytime.
        if(NightState.state != OFF){
            //logInfo("Sun Elevation", "Setting Night state OFF")
            NightState.sendCommand(OFF)
        }
    }
    else {
        if(NightState.state != ON){
            //logInfo("Sun Elevation", "Setting Night state ON")
            NightState.sendCommand(ON)
        }
    }
end

I want to have another item which gives me the hours until the event.
This would allow me to have a number of rules which fire when “NightStatePeriod” changed to -2 (there are two hours until night time.

I have a lot of stuff firing on the change and want to spread it around a bit. Winter in the UK makes night time 4:30pm but I don’t really want to close all the blinds. I do though want to turn some lights on at 3:30 because, while it’s not night, it’s darker.

Having a “hours until” number item seems like a good idea but the maths escapes me :frowning:

Any options or help?

C

Hi,

There are several ways this could be done.

Ive used timers for something similar. To begin you need a trigger that occurs before your event. You could use a Cron trigger at midday (or anytime) or you could use your sunelevation>0 trigger.

Once you have an event triggered, you calculate how many minutes there are between that time and sunset (as defined by astro). You then set a timer for 60 minutes (or whatever) less.

Here’s one way…

Let’s say you have the ntp and astro bindings installed.

And that you have items defined for the current time (ntp) and sunset time (astro).

And you have a Number item defined called MinutesUntilSunset

Trigger a rule when the ntp item changes.

    val long sunsetInMillis = (SunsetStartTime.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli
    val long nowInMillis = now.getMillis

    var long minutesUntilSunset = (sunsetInMillis - nowInMillis) / (1000 * 60)
    if (minutesUntilSunset > 0) {
        logInfo("test", "Minutes until sunset: {}", minutesUntilSunset)
        MinutesUntilSunset.postUpdate(minutesUntilSunset)
    }

Now you can trigger another rule on MinutesToSunset changed. MinutesToSunset.state contains the number of minutes until sunset.

BTW, I did minutes until sunset because it allows you to be more granular.

@rlkoshak probably has a more efficient design… :smiley:

Have you seen the offset events the Astro binding allows you to create?

0[quote=“Crispin, post:1, topic:59426”]
Having a “hours until” number item seems like a good idea but the maths escapes me :frowning:
[/quote]

This is a good way to handle this. Instead of using named states like the Time of Day Design Pattern, have a Number state representing the number of hours until sunset. For completeness you might need a number of hours until sunrise as well.

One thing I will note though is this is the exact reason why I have a mix of Astro events and statically defined times in the Time of Day DP. Some things happen at a given time no matter the season and others happen based on when sunset occurs. And it works out that as the sunset/sunrise changes and ends up happening before/after sometimes the fixed time one gets skipped.

For example, I have a lamp I want to turn on at 06:30 in the morning. But only if 06:30 is before sunrise. When sunrise occurs before 06:30, the lamp doesn’t turn on because the sunrise event happens instead.

But using minutes like this can be a bit more flexible in some situations. I like it.

Anyway, you need to have an Item linked to the sunset Channel of the Astro Thing. Let’s call it SunSet.

Periodically have a Rule run, maybe once per hour. In this Rule calculate the number of hours until sunset. Actually, I would use minutes instead of hours to give yourself more leeway to schedule things at smaller increments.

The calculation in the Rule is pretty straight forward.

rule "Minutes to sunset"
when
    Time cron "30 * * * * ? *" // avoid 0 0 0 to give Astro time to calculate the new times for the new day
then
    val sunsetTime = new DateTime(Sunset.state.toString) // convert to a DateTime
    val minsToSunset = sunsetTime.minutesOfDay - now.minutesOfDay // minsToSun set will be negative after sunset to midnight
    MinsToSunset.postUpdate(minsToSunset)
end

How often does NTP change? That’s an interesting idea for a way to drive the calculations instead of using a cron trigger.

Probably less efficient in runtime but more efficient in typing time. :slight_smile:

1 Like

The default is every minute, but you can set it to whatever you want.

And here I thought my idea was silly…

There is a way to calculate the hour based on the elevation on the sun. It says so in Wikipedia :slight_smile: So, based on this I was thinking of doing it in the same rule as working out the night state.

I’m trying to move away from cron rules which run frequently. I feel states should drive states.

There is an example here which is correct: https://keisan.casio.com/exec/system/1224682277
I know my elevation because the binding tells me so.
I bunked the day in maths when we learned about this… :frowning:

If the answer was in minutes I’d still be happy; hours is perfectly granular for now if the solution easier.
It could be baked into the binding as well.

C

Sometimes time drives states.

Given you have two solutions, one that’s all of 8 lines of code and one that is all of three lines of code it is up to you if you want to make this more complicated. I prefer short and easy to read and understand Rules and am willing to give a little like having a Rule that runs every minute to achieve that.

And, if you don’t want it the rule to run every minute, just change the ntp refresh interval to have it run every 5 minutes, 10 minutes, or whatever you want.

You’re right :slight_smile: There is a hard way and an easy way…
I took the middle of the road (although, I don’t actually agree with it and will change it… :slight_smile: )

I bundled your idea into my night-state calc. That way there is change enough to keep it updated but not to frequent to be “wasteful” :slight_smile:


rule "Update NightState"
when
    Item SunElevation changed
//    or Item SunElevation_proxy changed
then

    val sunsetTime = new DateTime(SunsetTime.state.toString) // convert to a DateTime
    val minsToSunset = sunsetTime.minutesOfDay - now.minutesOfDay // minsToSun set will be negative after sunset to midnight
    MinsToSunset.postUpdate(minsToSunset)

    val sunriseTime = new DateTime(SunriseTime.state.toString) // convert to a DateTime
    val minsToSunrise = sunriseTime.minutesOfDay - now.minutesOfDay // minsToSun set will be negative after sunset to midnight
    MinsToSunrise.postUpdate(minsToSunrise)

    

Thanks for the ideas!

I could be wrong, but unless you set the interval on the Astro Thing to something else the default is 60 meaning this Rule runs exactly every 60 seconds since the elevation is recalculated every 60 seconds.

I’m still a little puzzled why not using Astro event offset here. Is there a need for a variable offset or something?

Two hours before sunset

astro:sun:timed [ geolocation=".... ] {
    Channels:
        Type rangeEvent : set#event [
            offset=-120
        ]
}
rule "example trigger rule"
when
    Channel 'astro:sun:timed:set#event' triggered START
then
    // send solar lawnmower to bed
end

I could but then for each different offset I would need a new channel?
If I have a number of + or - minutes until sunset / sunrise then I can pick and chose what I want in rules.

1 Like

You would need to create separate Thing for each offset.

The code doesn’t work for OH3 anymore. I read in another thread that the DateTime conversion changed with OH3 but I have no idea how to implement those changes, I’m still new to the rules & scripts of OH. Could someone please tell me what I need to change to make it work again?
Thanks & best regards
Thorsten