appdaemon icon indicating copy to clipboard operation
appdaemon copied to clipboard

Unexpected 36-Minute Offset in run_daily Scheduler on Raspberry Pi 5 (HAOS, Python 3.12, ARM64)

Open svy123 opened this issue 6 months ago • 9 comments

What happened?

Describe the bug There is a consistent and repeatable 36-minute offset when scheduling callbacks using run_daily() in AppDaemon on Raspberry Pi 5 with Home Assistant OS (HAOS). The system time, Home Assistant time, and AppDaemon logs all show the correct time, but the callback scheduled by run_daily always fires exactly 36 minutes later than the time specified in the YAML configuration.

Environment

  • Hardware: Raspberry Pi 5 (64-bit, ARM)
  • Home Assistant OS version: 16.0
  • AppDaemon version: 4.5.11 (official addon)
  • Python version (as in AppDaemon logs): 3.12.11
  • Home Assistant Core version: 2025.7.2
  • System time: Confirmed correct with date and in AppDaemon logs

How to reproduce the issue

  1. Install AppDaemon 4.x as an official add-on on Home Assistant OS running on a Raspberry Pi 5.

  2. Create a simple app, e.g.:

       import appdaemon.plugins.hass.hassapi as hass
       import datetime
    
       class LightTimer(hass.Hass):
    
           def initialize(self):
               self.on_time = self.args.get("on_time", "10:00:00")
               parsed_on = self.parse_time(self.on_time)
               self.log(f"Parsed on_time: {parsed_on}")
               self.run_daily(self.turn_on_light, parsed_on)
    
           def turn_on_light(self, kwargs):
               self.log("Light should turn on now.")
    
  3. Configure apps.yaml:

            light_timer:
               module: light_timer
               class: LightTimer
               light: light.xyz
               on_time: "10:00:00"
    
  4. Restart AppDaemon.

  5. Observe that in the AppDaemon UI ("Scheduler Callbacks"), the scheduled time for the callback is always 10:36:00 when "10:00:00" is set, and so on (always +36 minutes).

Observed behavior

  • The scheduled callback is always +36 minutes later than the requested time, regardless of the value specified.
  • The offset is always exactly 36 minutes (e.g. 09:10:00 becomes 09:46:00, 12:12:00 becomes 12:48:00).
  • All system, Home Assistant, and AppDaemon times are correct and in sync.
  • No stubs, duplicates, or timezone inconsistencies in configuration.

Expected behavior

  • run_daily should schedule the callback at the time specified (e.g. 10:00:00).

Workaround Currently, I am manually subtracting 36 minutes in my app code:

          import datetime
          parsed_on = self.parse_time(self.on_time)
          fixed_on = (datetime.datetime.combine(datetime.date.today(), parsed_on) - datetime.timedelta(minutes=36)).time()
          self.run_daily(self.turn_on_light, fixed_on)

But this is just a workaround for a deeper issue.

Additional info

  • Full restart of the system does not resolve the issue.
  • The problem is present on Raspberry Pi 5, Home Assistant OS 16.0, Python 3.12.11.
  • The same code on older hardware or with older versions of AppDaemon/Python does not show this bug.
  • No error messages, just the consistent scheduling offset.

Version

4.5.11

Installation type

Home Assistant add-on

Relevant log output


Relevant code in the app or config file that caused the issue


Anything else?

No response

svy123 avatar Jul 16 '25 07:07 svy123

I have the same issue, but in my case it is exactly 67 minutes late.

In my case, I'm using AMD64 hardware for executing AppDaemon in a Docker container.

This was no issue with AppDaemon 4.5.10, but since updating to 4.5.11 the issue happens every time.

I have a run_every(self.my_callback, datetime.time(0, 0)) to execute every day at exactly 00:00. But the callback gets called at 01:07. The same issue with another run_every() at 21:30 which is then called at 22:37.

The issue could be caused by this change which was introduced in 4.5.11: https://github.com/AppDaemon/appdaemon/commit/d135bfe4e6346902935d5349935dea90f0f1cf70

Programie avatar Aug 04 '25 08:08 Programie

I also have the same issue

KoljaWindeler avatar Aug 18 '25 19:08 KoljaWindeler

+1 (67 minutes for me as well, docker container on raspberry pi)

bnutzer avatar Aug 25 '25 21:08 bnutzer

A temporary fix is to skip the time parsing and let AppDaemon do it for you:

So instead of:

def initialize(self):
    self.on_time = self.args.get("on_time", "10:00:00")
    parsed_on = self.parse_time(self.on_time)
    self.log(f"Parsed on_time: {parsed_on}")
    self.run_daily(self.turn_on_light, parsed_on)

use this:

def initialize(self):
    self.on_time = self.args.get("on_time", "10:00:00")
    self.run_daily(self.turn_on_light, self.on_time)

dan-danache avatar Sep 04 '25 16:09 dan-danache

This is because AppDaemon is using pytz incorrectly. The workaround is to never pass a datetime.time to run_daily (or run_hourly or any of the other methods that uses a start argument). Instead, construct a datetime.datetime object and pass it. Something like this should work:

date = datetime.datetime.now().date()
time = datetime.time(10, 0, 0)
when = self.AD.tz.localize(datetime.datetime.combine(date, time))
self.run_daily(self.turn_on_light, when)

If I'm reading the code correctly, AppDaemon tries to convert the datetime.time object to a datetime.datetime like this, when all abstractions are removed:

zone = pytz.timezone("Europe/Stockholm") # or whatever timezone you are using
if start.tzinfo is None:
    start = start.replace(tzinfo=zone)
now = datetime.datetime.now(zone)
now = now.astimezone(zone)
now = now.date()
d = datetime.datetime.combine(now, start)

It then uses d to schedule the callback. (It doesn't actually use the above variable names, though.)

But this isn't allowed. The pytz documentation says:

This library only supports two ways of building a localized time. The first is to use the localize() method provided by the pytz library. [...] The second way of building a localized time is by converting an existing localized time using the standard astimezone() method

Source: https://pypi.org/project/pytz

This web page has a lot to say about time zones in Python, and it was a very helpful resource when I tried to understand this issue: https://initialxy.com/lesson/2022/03/18/getting-the-right-time-zone-in-python

See also https://stackoverflow.com/questions/50443305/why-do-i-get-the-offset-053-for-timezone-europe-berlin

cederlys avatar Sep 10 '25 12:09 cederlys

Thanks for the analysis! We should have a fix out for this shortly.

acockburn avatar Sep 10 '25 13:09 acockburn

Maybe it's related to my problem: https://github.com/AppDaemon/appdaemon/issues/2390

brazoayeye avatar Sep 10 '25 13:09 brazoayeye

@brazoayeye - yes, we believe this is the same issue.

acockburn avatar Sep 10 '25 13:09 acockburn

This should all work as expected now

jsl12 avatar Oct 25 '25 20:10 jsl12

Should be fixed by #2515

cebtenzzre avatar Dec 28 '25 20:12 cebtenzzre