appdaemon
appdaemon copied to clipboard
Sunrise and Sunset may run twice
I have HA with appdaemon everything on last versions (stable).
I use an automation to activate a listen_state (movement sensor) at sunset and remove it at sunrise.
For few days I had been searching why some morning, the movement sensor was still being listened to, whereas in the logs, it was clearly printed as " deactivated " by my custom logs.
This morning, I have seen in my logs something very strange:
2021-06-01 05:51:15.019427 INFO couloir_night: Deactivating listening on binary_sensor.presence_9 for fbbabe74df9e428b9e2741c6e3836b6b
2021-06-01 05:51:15.022240 INFO couloir_night: Deactivating.
2021-06-01 05:51:15.025255 DEBUG couloir_night: Canceling listen_state for couloir_night
2021-06-01 05:51:15.146007 INFO couloir_night: Deactivating listening on binary_sensor.presence_9 for None
2021-06-01 05:51:15.148977 INFO couloir_night: Nothing to deactivate.
It seems that for no reason the deactivation at sunrise has been called two times.
My initialize is:
def initialize(self):
"""Launch the routine at each appdaemon start."""
self.log(
f"Starting our timer move scene intelligence for {self.args['scene_on']} "
f"based on movements for {self.args['presence_sensor']}. Only at night ? "
f"{self.args['only_night']}"
)
if self.args["only_night"]:
self.log("Activation will occure at sunset and deactivation at sunrise")
self.run_at_sunset(self.register_listen_state)
self.run_at_sunrise(self.deregister_listen_state)
else:
self.register_listen_state()
and the deregister_listen_state callback is:
def deregister_listen_state(self, _kwargs=None):
"""Will deregister the listen_state."""
self.log(
f"Deactivating listening on {self.args['presence_sensor']} for {self.state_listened}"
)
if self.state_listened:
self.log("Deactivating.")
self.cancel_listen_state(self.state_listened)
self.state_listened = None
else:
self.log("Nothing to deactivate.")
The fact that the activation of listen_state would have been called the other days two times would clearly explain why the deactivation was not working : I was then only deactivating once the listen_state.
I have put a control to only activate if it is not already activated, as a workaround:
def register_listen_state(self, _kwargs=None):
"""Will register the listen_state."""
self.log(
f"Activating listening on {self.args['presence_sensor']}, "
f"the scene used will be {self.args['scene_on']}"
)
if not self.state_listened:
self.state_listened = self.listen_state(
self.launch_scene_and_schedule_stop, self.args["presence_sensor"]
)
self.log("Activation complete")
else:
self.log("Nothing to do, already activated.")
Today I got the confirmation that « at_sunset » has been raised twice and that my workaround made the job to prevent a double registration :
2021-06-01 21:25:50.033536 INFO couloir_night: Activating listening on binary_sensor.presence_9, the scene used will be scene.couloir_nuit_on
2021-06-01 21:25:50.039359 INFO couloir_night: Activation complete
2021-06-01 21:25:50.471276 INFO couloir_night: Activating listening on binary_sensor.presence_9, the scene used will be scene.couloir_nuit_on
2021-06-01 21:25:50.473589 INFO couloir_night: Nothing to do, already activated.
Thanks for reporting this - it should be fixed in the dev branch and will be available for the next release. I have tested this myself but if you are able to run the dev versions it would be helpful if someone else seeing this problem could confirm the fix. Just as a point of reference, what hardware are you running? I think this was caused by a race condition that only happened on more powerful hardware - I noticed it when I upgraded from an underpowered NUC to a Mac mini M1
besides that it has been fixed, i can tell you that you dont need to register and deregister the listen_state.
for that we got constraints. just add a constraint to the listen state and you only need 1 callback.
https://appdaemon.readthedocs.io/en/latest/APPGUIDE.html#callback-constraints
there is even an example that does just what you want:
some_app:
module: some_module
class: SomeClass
constrain_start_time: sunrise
constrain_end_time: sunset
the constraints can also be used directly in the code:
self.listen_state(self.callback, self.args["presence_sensor"], constrain_start_time= "sunrise", constrain_end_time= "sunset")
Thank you ReneTode. However, from what I understand, with a constrain_start_time, the state is still listened, but the callback call is prevented. I would think that this is more consuming and that this is then not very efficient instead of activating and deactivating the listening.
acockburn, I use an RPI4. It seems there is no way in supervisor to ask for an addon to switch on a dev channel or thing like this… Sorry.
You end up with an extra un-needed callback because you have a callback running to determine if you should enable/disable another callback. It is more efficient to have 1 callback with a constraint as Rene has mentioned. It's also a lot easier to understand and read when you look at the code. Just my 2-cents.
@florck the state is listened for in AD anyway, for all entities you got. if you add 1000 listeners with a constraint you wont notice the difference in use.
but activating 1000 listeners you will notice.
and your code ends up being way more complicated then needed. if constraints were not the right way to handle things, then Andrew would never have added it ;)
and indeed if you run hassio, youll need to wait for the next release. (but then again, when you change your code to constraints, the probem is most likely gone also)