OH2 Astro binding - Question or feature request: calculate next day's sunrise time

Hello,

I just found the time to add the Astro binding to my home automation and I liked it so much that I created a set of dynamic icons for the moonphases as svg files.
I am just wondering a little why it only shows the sunrise and sunset time of the current day? I learned in another thread that these times are calculated shortly after midnight which sounds great as they then seem to be very accurate and up to date.
But what if I like to know the evening/day before when the sun will rise on the next day (probably to watch a romantic sunrise :wink: )?
No, honestly: Wouldn’t it make sense to have the sunrise time for tomorrow available today e.g. to plan some actions?
Depending on the season I might wake up after sunrise and the binding will only tell me what I can already see (or I need to stay awake until some time after midnight).

Would it be possible to calculate the next day’s sunrise time?
Would it be a high effort to do provide it during the current day?
Or would the calculation be so imprecise that it does not make sense?

Thanks for a response or a consideration :slight_smile:

Cheers
Justus

2 Likes

It’s an interesting idea.

One thing I would say is, you wouldn’t want it changed to “next sunrise” event. It’s pretty common to do things like test if it’s after today’s sunrise etc. That’s to say, you want today’s data even when “stale”.

But allowing some way to generate tomorrow’s data sounds reasonable.

The binding does allow generation of sets of channels with different geographical positions, specified in the Thing.
Perhaps the way to implement it would be a date specified in the Thing as well, in this case +1 day, or it could be a fixed date (birthday, solstice, etc)
Still calculates at midnight (or boot time).

1 Like

I use the python astral library to display the sun and moon

See:
https://astral.readthedocs.io/en/latest/

It should be easy to rig a quick executeCommandLine or exec node in node-red to get tomorrow’s sunrise

2 Likes

Pyephem also has this capability, one can define any number of “observers” with arbitrary location and date, and get astronomical events for those observers. I’ve fully replaced the astro binding with a pyephem script communicating with oh via mqtt.

This is not something currently supported by the Astro binding. The Astro binding has only ever worked with the times for the current day. I guess no one has come up with a compelling enough to take it upon themselves to implement it.

I see nothing that would prevent the calculation of additional start/stop times for events on previous and subsequent days. However, Astro already has SOOOOOO many Channels already. They would all have to be duplicated at least once to support something like this.

In answer to your questions though:

Depending on how close you are to one of the poles, the differences in sunrise/sunset times will not be all that different from one day to the next. If you know the general time from today, the next morning you can easily adjust your plans by the few minutes necessary to adjust to the exact minute of the sunset.

You can also use another source for the data. There are lots of websites available.

I suppose if you are in Finland or Alaska it might make more of a difference.

Not really. OH is event driven. So the Sunrise and Sunset times only need to be calculated before the event needs to take place. So the only time where having the sunrise time for tomorrow available today is if you need to do something different today based on the times for tomorrow. That is a rare requirement and I’m actually trying to come up with any practical use for that.

I’m not exactly sure what you are trying to say here. But if you want to wake up 10 minutes before dawn, for example, then configure your Astro Thing with a 10 minute offset and trigger your OH alarm rule based on that sunrise event. You don’t need to know the sunrise time the day before, you just need to know it sometime before sunrise actually occurs and a little after midnight is plenty early enough to ensure the event occurs at the right time.

Technically it is possible with changes to the binding.

It would be a lot of effort as I think it would require a complete rethinking of the entire architecture of how the Astro binding works. It really isn’t tenable to duplicate all the 50+ available Channels for tomorrow and for yesterday (what happens when someone wants two days ago?).

The offsets don’t really work like that as implemented right now. It just adds/subtracts that amount of time from today’s calculated times. So the time for tomorrow will be the same as today.

There would need to be some new element added to the Thing to tell it what day relative to today you want to calculate the value for. We can’t just do it with the offset. But I like this idea. It wouldn’t require quite as much work as I thought. We would just need to:

  • create a new optional parameter on the Thing with a calculate for parameter to tell it which day relative to today to perform the calculation for
  • add code to the binding to calculate based on this different day.

This assumes the library is able to do this.

Hi Rich, thanks for the explanation!
I agree that OH is event driven and you are right that normally it might be enough to schedule an event shortly before an occurance - meaning in this context that it is enough to calculate the sunrise time ten minutes before or as you stated in another thread “shortly after midnight”. But even with a high degree of automation there are still human decisions to interact with the automation :wink: … And one of those could be to know tomorrow’s sunrise time before the end of the day to make a decision for the next day.
But if the “architecture” of the Astro binding does not allow this flexibility then it is worthless to discuss. My assumption was that the underlying calculation gets a date and a time stamp passed and that it might be easy possible to create a new channel like:

DateTime    Sunrise_Time_Today         "Sunrise Today [%1$tH:%1$tM]"     (Astro) {channel="astro:sun:home:rise_today#end"}
DateTime    Sunrise_Time_Tomorrow      "Sunrise Tomorrow [%1$tH:%1$tM]"  (Astro) {channel="astro:sun:home:rise_tomorrow#end"}

or

DateTime    Sunrise_Time_Today         "Sunrise Today [%1$tH:%1$tM]"     (Astro) {channel="astro:sun:home:today:rise#end"}
DateTime    Sunrise_Time_Tomorrow      "Sunrise Tomorrow [%1$tH:%1$tM]"  (Astro) {channel="astro:sun:home:tomorrow:rise#end"}

I agree that this would open the door for every indvidual request to calculate and desired day … and this is not desirable for you :wink:

But what does this minus90 mean in the item definition:

DateTime    Evening_Time         "Evening [%1$tH:%1$tM]"       (Astro) {channel="astro:sun:minus90:set#start"}

???

Wouldn’t it be possible/easy to create a plus1440 or plus24h ???

Again, I understand that this might open the box of the Pandora for any desired date. But to know the sunrise for the next day sounds at least to me very reasonable as it just provides the information that you are calculating anyway earlier to be visible during normal day activity times. The “Evening time” looks also already very individual to me :wink:

But anyways - I understand and sadly accept if this is a major change that does not provide the value compared to the huge effort of necessary changes.

The problem is that tomorrow’s sunrise isn’t 24hrs after today’s. The daily shift depends on both your location and the date in the year. If you don’t need or want that level of precision, the 24hr offset will do fine for you, and i think you can do that already now, by defining a second thing withe the same location and 24hr offset.

1 Like

Calculate the sunset time for today and subtract 90 minutes.

So if you did plus24h you would get the sunset time for today plus 24 hours. But that isn’t what you want.

It sounds reasonable yet as far as I know no one has mentioned it in the four(ish) years I’ve used OH and certainly no one has felt strongly enough to implement it themselves. If you have the skills please do feel free to add this feature to the binding. If not you can open a bounty on bountysource or try to find a developer who is otherwise willing to implement it.

The value of the change versus the effort is best assessed by the person who needs the feature and the person who will be making the change. This is one of the challenges with open source development because usually those two need to be the same person.

1 Like

A limitation of doing it that way would be that you are stuck with pre-selecting the supposed +1 day offset (or whatever we have) in the Thing definition.
Same as geo-location currently is, of course.

Someone’s bound to want a way to feed it a variable - send a command to a channel with datetime content perhaps. Might as well allow geo-location to be updated in similar way. Then you’d need a way to trigger a calculation - maybe implementing a REFRESH command would do there.

All sounds do-able - who’s got the inclination though :wink:

Perhaps, but that really doesn’t go well with the OH architecture.

You may have noticed that as old 1.x versions of bindings get rewritten as 2.x version bindings there is no longer an Action. For example, the Astro Action only works with Astro 1.x. The MQTT publish Action will only work with MQTT 1.x. There is no way to have an Action that works with a 2.x version binding like that.

The assumption among the devs is that we should be creating a model of everything that we use in our home automation. So if we need the sunset time for tomorrow then we should have a Thing and Item for that.

Hi Rivi,
as it seems that there is no chance to get the Astro Binding enhanced (as I unfortunately do not have the skills to do so) a short tutorial or maybe sharing your script would be kindly appreciated to see how an alternative could look like.

Would you mind sharing your experience?

Thank you
Justus

Hi Vincent,

same suggestion to you. Would you consider sharing your setup to help finding an alternative to import these events into OH as it seems that there is no one being able to enhance the Astro binding and I am also no programmer to try myself doing this :wink: .

This information would be greatly appreciated …

Cheers
Justus

Perhaps clarify what you want? “Today + 24hrs” is easy to calculate and accurate to within a few minutes.

Yes that is all I want. I simple want to be able to see the “next” sunrise before it happens :slight_smile:

Hi Justus,

that would be it, just a very crude piece of python. Run on a Linux terminal with e.g.
$ nohup local.py > log.txt &

and it should stay running until the machine is shut down. All you need then is a mqtt broker. I added the “tomorrow” computation without running it, so please make sure I did not make a typo or so. As I said before the difference between today and tomorrow is typically only a few minutes away from 24hrs.

The script local.py:

#!/usr/bin/python3
import ephem
import datetime, time
import paho.mqtt.client as mqtt

mqtt_broker="127.0.0.1" 
mqttc = mqtt.Client()
mqttc.connect(mqtt_broker, 1883, 60)

# define location Local and start of today
local = ephem.Observer()
local.lat = '<lat>' 
local.lon = '<lon>' 
local.elevation = 500
tomorrow = local

sun = ephem.Sun()

old = 0
sun_alt_old = 0
sun_az_old = 0
moon_alt_old = 0
moon_az_old = 0
moon_fli_old = 0

mqttc.loop_start()

while True:
    now = ephem.Date(datetime.datetime.utcnow())
    # Make sure we have today's date and times:
    if (now - old) > 1.0:
        # IMPORTANT: This is in UTC. If you live significantly away from lon=0, adjust for your timezone by subtracting TZ-offset
        # This is to make sure the computation is done after dusk and before dawn. Otherwise no effect, so needs not be precise.
        # E.g. subtracting "lon/360" will do just fine, unless you live in arctic regions.
        local.date = ephem.Date(datetime.datetime.utcnow().date()) 
        old =  local.date
        # Compute the relevant time points:
        sunrise = local.next_rising(sun)
        sunset = local.next_setting(sun)
        transit = local.next_transit(sun)
        # Modify "horizon" altitude for dusk and dawn. Common values are 6,12,18 (civil, nautical, astro)
        local.horizon = '-12'
        dawn = local.next_rising(sun)
        dusk = local.next_setting(sun)
        local.horizon = '0'
        #
        mqttc.publish('local/sun/dawn', payload=int(dawn.datetime().replace(tzinfo=datetime.timezone.utc).timestamp()), qos=0, retain=True)
        mqttc.publish('local/sun/rise', payload=int(sunrise.datetime().replace(tzinfo=datetime.timezone.utc).timestamp()), qos=0, retain=True)
        mqttc.publish('local/sun/transit', payload=int(transit.datetime().replace(tzinfo=datetime.timezone.utc).timestamp()), qos=0, retain=True)
        mqttc.publish('local/sun/set', payload=int(sunset.datetime().replace(tzinfo=datetime.timezone.utc).timestamp()), qos=0, retain=True)
        mqttc.publish('local/sun/dusk', payload=int(dusk.datetime().replace(tzinfo=datetime.timezone.utc).timestamp()), qos=0, retain=True)
        tomorrow.date = local.date + 1
       # Compute the relevant time points tomorrow:
        sunrise_t = tomorrow.next_rising(sun)
        sunset_t = tomorrow.next_setting(sun)
        transit_t = tomorrow.next_transit(sun)
        # Modify "horizon" altitude for dusk and dawn. 
        tomorrow.horizon = '-12'
        dawn_t = tomorrow.next_rising(sun)
        dusk_t = tomorrow.next_setting(sun)
        tomorrow.horizon = '0'
        mqttc.publish('tomorrow/sun/dawn', payload=int(dawn_t.datetime().replace(tzinfo=datetime.timezone.utc).timestamp()), qos=0, retain=True)
        mqttc.publish('tomorrow/sun/rise', payload=int(sunrise_t.datetime().replace(tzinfo=datetime.timezone.utc).timestamp()), qos=0, retain=True)
        mqttc.publish('tomorrow/sun/transit', payload=int(transit_t.datetime().replace(tzinfo=datetime.timezone.utc).timestamp()), qos=0, retain=True)
        mqttc.publish('tomorrow/sun/set', payload=int(sunset_t.datetime().replace(tzinfo=datetime.timezone.utc).timestamp()), qos=0, retain=True)
        mqttc.publish('tomorrow/sun/dusk', payload=int(dusk_t.datetime().replace(tzinfo=datetime.timezone.utc).timestamp()), qos=0, retain=True)



    # Compute Sun and Moon for now and publish if changed
    local.date = now
    sol = ephem.Sun(local)
    luna = ephem.Moon(local)
    sun_alt = int(sol.alt*180/ephem.pi)
    if sun_alt != sun_alt_old:
        mqttc.publish('local/sun/alt', payload=sun_alt, qos=0, retain=True)
        sun_alt_old = sun_alt
    sun_az = int(sol.az*180/ephem.pi)
    if sun_az != sun_az_old:
        mqttc.publish('local/sun/az', payload=sun_az, qos=0, retain=True)
        sun_az_old = sun_az
    moon_alt = int(luna.alt*180/ephem.pi)
    if moon_alt != moon_alt_old:
        mqttc.publish('local/moon/alt', payload=moon_alt, qos=0, retain=True)
        moon_alt_old = moon_alt
    moon_az = int(luna.az*180/ephem.pi)
    if moon_az != moon_az_old:
        mqttc.publish('local/moon/az', payload=moon_az, qos=0, retain=True)
        moon_az_old = moon_az
    moon_fli = int(luna.phase)
    if moon_fli != moon_fli_old:
        mqttc.publish('local/moon/fli', payload=moon_fli, qos=0, retain=True)
        moon_fli_old = moon_fli

    time.sleep(10)

In openhab then have the items from mqtt defined like this (vers 2.3 or lower, I’ve heard 2.4 may look different, but I don’t run it):

//
// Solar and lunar data f. Local
DateTime  Local_Dawn    {mqtt="<[raspi-serverMQTT:local/sun/dawn:state:JS(timestamp.js)]"}
DateTime  Local_Rise    {mqtt="<[raspi-serverMQTT:local/sun/rise:state:JS(timestamp.js)]"}
DateTime  Local_Transit {mqtt="<[raspi-serverMQTT:local/sun/transit:state:JS(timestamp.js)]"}
DateTime  Local_Set     {mqtt="<[raspi-serverMQTT:local/sun/set:state:JS(timestamp.js)]"}
DateTime  Local_Dusk    {mqtt="<[raspi-serverMQTT:local/sun/dusk:state:JS(timestamp.js)]"}

DateTime  Tomorrow_Dawn    {mqtt="<[raspi-serverMQTT:tomorrow/sun/dawn:state:JS(timestamp.js)]"}
DateTime  Tomorrow_Rise    {mqtt="<[raspi-serverMQTT:tomorrow/sun/rise:state:JS(timestamp.js)]"}
DateTime  Tomorrow_Transit {mqtt="<[raspi-serverMQTT:tomorrow/sun/transit:state:JS(timestamp.js)]"}
DateTime  Tomorrow_Set     {mqtt="<[raspi-serverMQTT:tomorrow/sun/set:state:JS(timestamp.js)]"}
DateTime  Tomorrow_Dusk    {mqtt="<[raspi-serverMQTT:tomorrow/sun/dusk:state:JS(timestamp.js)]"}

Number  Local_SunAlt     {mqtt="<[raspi-serverMQTT:local/sun/alt:state:default]"}
Number  Local_SunAz      {mqtt="<[raspi-serverMQTT:local/sun/az:state:default]"}
Number  Local_MoonAlt    {mqtt="<[raspi-serverMQTT:local/moon/alt:state:default]"}
Number  Local_MoonAz     {mqtt="<[raspi-serverMQTT:local/moon/az:state:default]"}
Number  Local_MoonFLI    {mqtt="<[raspi-serverMQTT:local/moon/fli:state:default]"}

and the timestamp.js transformation is just:

(function(i) {
    var date = new Date(i * 1000.);
    return date.toISOString();
})(input)

Thanks Rivi, I will look into that later … I am not using mqtt at the moment and I am thinking about leveraging the REST-API. The interesting stuff happens around the calculation with the ephem library.
I currently also got a feedback on leveraging the astral library python library. Before I can estimate what is better for me, I will have to understand both and I will start with the astral lib as this looks easier. And updating via REST-API looks also easier for me as I am only looking for one value and the python script runs on the same box! Hence the mqtt setup looks a little bit too sophisticated for my experience and for my purpose.

But as soon as I understood the one path I will look into your description as well.

Thank you for sharing!!! That was already a great help for me and probably for other who are looking for workarounds !!!