First I know there is a way around this using a schedule_handler. Please hear me out first. Two items.
Sunup and Sundown are not schedules. They are events. run_at_sunrise and run_at_sunset a nice helper functions, but not exactly right. Setting an event callback on sun doesn’t work either because it returns every minute with the degrees above/below horizon. What would be nice is the ability to use listen_state to only get notified when the state changes not every time an attribute of the target changes.
For example
listen_state(self.sunup_down_handler,“sun.sun”)
This should fire when the state changes from above_horizon to below_horizon and vice versa. Instead it triggers ever minute, using up cpu cycles unnecessarily.
run_at_sunrise/sunset run at specific points in time but use a scheduler callback. I’m trying to reduce complexity in my apps by eliminating unnecessary if blocks and handlers that in the end just call a general function.
Another thing that would be nice would be a “run_if” function. This would run if a certain block evaluates to true. For Example.
An event callback function.
self.listen_if(self.run_if_callback,“sensor.office_temp”,">=self.trigger_temp") Something like that…
Found this old message and it’s still kind of relevant.
I have an app that has a run_at_sunset command in it. It calls a routine that normalizes various sensor states to on/off. So it checks the state of the sun to basically convert above_horizon to on and below_horizon to off. However it seems that sunset doesn’t necessarily mean that the sun is below the horizon. So my callback for the run_at_sunset event, tries to normalize the state of the sun to on/off and finds out that it’s still above the horizon so it doesn’t turn on the light.
I know there are a thousand different ways I could code around this. But it still seems like checking the state of the sun after run_at_sunset is triggered, should return below_horizon. Right???
For now I just put in a 5 minute offset and that should take care of it. My gut feeling is that the run_at_sunrise and run_at_sunset are looking at the sunrise/sunset times of the day they are called instead of the next day. So after run at sunset triggers, and I re-create it, I think it’s re-creating at the same same time instead of looking at tomorrow’s sunset time. That’s just my gut feeling. Probably just gas so take it with a couple of pepto-bismol tablets.
This is the schedule event handler. The only thing that has changed since I had the problem was adding in the offset parameter in the function. It seems to be working now with the offset in there, but I’ve had to restart things a couple of times (I upgraded today (thanks again for the logs)) so it may not have had time for a real test yet.
########
#
# process_sun - handler for sun schedule
#
def process_sun(self,kwargs):
self.log("target={} trigger={}".format(kwargs["target"],"sunrise" if self.sun_up() else "sunset"))
# a trigger based on sunup or sunset fired so check the target entity it was associated with.
self.process_light_state(kwargs["target"]) # something changed so go evaluate the state of everything
# either run_at_sunrise or run_at_sunset was triggered so re-register it.
if self.sun_up():
self.run_at_sunrise(self.process_sun,offset=5*60,target=kwargs["target"])
else:
self.run_at_sunset(self.process_sun,offset=5*60,target=kwargs["target"])
So the first thing is that run_at_sunset() and run_at_sunrise() are repeating functions, you don’t need to re-register them every day
With that in mind, over the course of days you are probably ending up with multiple scheduler entries all calling your callback at the same time - I’ll also make sure the docs are clearer on that point.
Even so, when called at sunrise or sunset, sun_up() abd sun_down() should be valid - I worked hard on that specific point while I was building AppDaemon. I started by using HASS for the sun state but there was a race condition at sunrise and sunset in which the event would fire before the state update and I had the exact same problem, until I moved the sun processing right into AD then it worked.
I dug up my test Apps for this area and ran them - all seems to be fine.
The callback:
def sun(self, kwargs):
self.log("{}".format(kwargs["title"]))
self.log("Current sun state: Up = {}, Down = {}".format(self.sun_up(), self.sun_down()))
self.log("Next sunrise: {}".format(self.sunrise()))
self.log("Next sunset: {}".format(self.sunset()))
And the setup:
self.run_at_sunrise(self.sun, title = "sunrise")
self.run_at_sunset(self.sun, title = "sunset")
Output:
2017-08-10 19:58:44 INFO sun: sunset
2017-08-10 19:58:44 INFO sun: Current sun state: Up = False, Down = True
2017-08-10 19:58:44 INFO sun: Next sunrise: 2017-08-11 05:59:59
2017-08-10 19:58:44 INFO sun: Next sunset: 2017-08-11 19:57:24
...
2017-08-11 05:59:59 INFO sun: sunrise
2017-08-11 05:59:59 INFO sun: Current sun state: Up = True, Down = False
2017-08-11 05:59:59 INFO sun: Next sunrise: 2017-08-12 06:00:59
2017-08-11 05:59:59 INFO sun: Next sunset: 2017-08-11 19:57:24
The only caveat is that I tested this using time travel.
I suggest you fix the multiple scheduling, and if you still have problems, give my code a try.
How do you guys actually use sunrise/sunset?
For me they are almost useless beacuse in the summer its light beyond sunset and before sunrise, and using time offset fails since it gets darker faster in the winter.
What I use is twighlight (actually civil dawn/dusk) , calculated by angle offset from sunrise/sunset. So if you are considering adding helper functions…
i use sunset with an offset and the lights go on when i like them to go on.
except for dark (cloudy) days but for that i have a lightsensor outside and that switches the lights on if it gets to dark outside.
For me it’s been close enough although I bet that varies based on latitude.
I’m always willing to accept a Pull Request Actually, I am using a library to calculate all that and it probably has support for azimuth stuff - I’ll take a look.