HABApp Beta

Yes - behavior ist still the same if no uom is set. The item value is then not normalized.
E.g.: You integrate an external weather with an api key forecast and have a Number:Temperature. You get the temperature in °C because that’s how the service for your api key is configured. One day you go on your coffee break and your cat jumps on your keyboard changing the service default unit on the weather forecast website to °F. Now the item value jumps from 25 to 77 because it’s °F. Since your openHAB automation thinks an unprecedented heat wave is coming it activates all your sprinkles on full throttle flooding your basement and your whole town. Thousands of people loose their homes. :wink:
To prevent these disasters it’s recommended to set the unit in the metadata (unit=°C).
That way the incoming 77°F will automatically be normalized to 25°C, thus the shenanigans of your cat will be without consequences.
The openHAB UI addressed this, too as new items created from UI will set the metadata (if not selected) to the system default.
Some people argue it’s more of a theoretical problem but openHAB has multiple times changed the default normalization for units and it’s a problem that’s can affect you silently (e.g. you want to do some frost protection, but because it’s now in °F 0 is actually -17°C.

I think good systems should make it easy to do the right things and hard (or annoying) to do the wrong things.
That’s why I issue a warning.

3 Likes

Tomorrow is a daylight saving time transition.
It would be nice if you could pay attention if your jobs still run at the correct time.

1 Like

indeed i see some errors at midnight in the log. All at 00:00 o clock. four times this:

Exception in HABApp.scheduler:
--------------------------------------------------------------------------------
Traceback (most recent call last):
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/schedulers/async_scheduler.py", line 58, in run_jobs
    job.execute()
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/jobs/base.py", line 113, in execute
    self.update_next()
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/jobs/job_datetime.py", line 28, in update_next
    next_run = self.producer.get_next(Instant.now())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/producers/prod_time.py", line 26, in get_next
    for _ in not_infinite_loop():  # noqa: RET503
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/producers/base.py", line 60, in not_infinite_loop
    raise InfiniteLoopDetectedError()
eascheduler.errors.errors.InfiniteLoopDetectedError

And once this:

Exception in load:
Could not load /etc/openhab/habapp/rules/technik.py!
File "<frozen runpy>", line 291 in run_path
--------------------------------------------------------------------------------

File "<frozen runpy>", line 98 in _run_module_code
--------------------------------------------------------------------------------

File "<frozen runpy>", line 88 in _run_code
--------------------------------------------------------------------------------

File "/etc/openhab/habapp/rules/technik.py", line 1370 in technik.py
--------------------------------------------------------------------------------
     1366 |         if sk_tools.confirm_function(item_name, command) is False:
     1367 |             return
     1368 |         self.run.countdown(5, self.triggered_reset_openhab_body).reset()
-->  1370 | Technik()
     1373 | class WindowOpenAlert(Rule):
   ------------------------------------------------------------
     transformations.map = <MapMapTransformationFactory>
     'iSchlafzi_Tuere_Ro' + 'M' = 'iSchlafzi_Tuere_RoM'
     e_mail = <E_mail>
     range(1, 3) = range(1, 3)
     sk_tools = <SkTools>
   ------------------------------------------------------------

File "/etc/openhab/habapp/rules/technik.py", line 109 in __init__
--------------------------------------------------------------------------------
      95 | def __init__(self):
       (...)
     105 |     self.error_mailed = False                   # damit Fehlermail nur einmal geschickt wird
     106 |     self.fc_hundemelder_alarm = None
     107 |     self.items_checklist = ['iViGenOff', 'iGruenbeck_SC18_Kti', 'iAbsperrventil_Ak',
     108 |                             'iOwTemp_BetriebPufferOben', 'iViSchlafzi_Hundemelder']
-->  109 |     self.boot_initialisations()
     111 |     self.valve_time = 33              # Umschaltzeit vom Absperrventil in Sekunden
   ------------------------------------------------------------
     self = <Technik>
     self.absperrventil_was_on = False
     self.error_mailed = False
     self.fc_hundemelder_alarm = <FlashLight FlashLight.2>
     self.init_wait_curr = 0
     self.init_wait_delta = 30
     self.init_wait_max = 600
     self.items_checklist = ['iViGenOff', 'iGruenbeck_SC18_Kti', 'iAbsperrventil_Ak', 'iOwTemp_BetriebPufferOben', 'iViSchlafzi_Hundemelder']
     self.log = <Logger My_HABApp (DEBUG)>
     self.MyBad = <Item name: hBad, value: None, last_change: InstantView(2024-10-27T00:12:52.86375258+02:00), last_update: InstantView(2024-10-27T00:12:52.86375258+02:00)>
     self.MyShutterControl = <Item name: hShutterControl, value: None, last_change: InstantView(2024-10-27T00:12:52.863171512+02:00), last_update: InstantView(2024-10-27T00:12:52.863171512+02:00)>
   ------------------------------------------------------------

File "/etc/openhab/habapp/rules/technik.py", line 278 in boot_initialisations
--------------------------------------------------------------------------------
     122 | def boot_initialisations(self):
       (...)
     271 |     # Initialisierung absperrventil_was_on zur Sicherheit, NUR dann
     272 |     # True wenn 'Urlaub' UND 'Regeneration läuft' UND 'Absperrventil ist geöffnet'
     273 |     self.absperrventil_was_on = bool(NumberItem.get_item('iViGenOff').value == 12 and
     274 |     self.Gruenbeck_SC18_Kti.is_off() and
     275 |     self.Absperrventil_Ak.is_off())
     277 |     # Trigger für Feiertagscheck
-->  278 |     self.run.on_every_day(time(hour=0, minute=0), self.triggered_holiday_check)
     279 |     self.log.debug('start "triggered_holiday_check" delayes at boot '\
     280 |     'to ensure "iHeuteFeiertag" and "iMorgenFeiertag" are initialized')
   ------------------------------------------------------------
     self.run.trigger = <class 'eascheduler.builder.triggers.TriggerBuilder'>
     not self.error_mailed = True
     self = <Technik>
     self.Absperrventil_Ak = <SwitchItem name: iAbsperrventil_Ak, value: OFF, last_change: InstantView(2024-10-27T00:12:22.831250159+02:00), last_update: InstantView(2024-10-27T00:12:22.831250159+02:00)>
     self.absperrventil_was_on = False
     self.coffee_timer = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7f8c6db5d0>
     self.error_mailed = False
     self.fc_dog_watch = <FlashLight>
     self.fc_hundemelder_alarm = <FlashLight FlashLight.2>
     self.fc_kaffee = <FlashLight FlashLight.5>
     self.fc_kaffee_feedback = <FlashLight FlashLight.4>
     self.fc_sleeptimer = <FlashLight FlashLight.3>
     self.fl_gen_off_urlaub = <FlashLight FlashLight.6>
     self.Gruenbeck_SC18_Kti = <SwitchItem name: iGruenbeck_SC18_Kti, value: ON, last_change: InstantView(2024-10-27T00:12:22.812875127+02:00), last_update: InstantView(2024-10-27T00:12:22.812875127+02:00)>
     self.hundemelder_pause_timer = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7f8c607750>
     self.init_wait_curr = 0
     self.init_wait_curr >= self.init_wait_max and not self.error_mailed = False
     self.init_wait_delta = 30
     self.init_wait_max = 600
     self.iSchlafzTvMinuten = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7f8c6058d0>
     self.iSchlafzWarnMinuten = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7f8c605850>
     self.items_checklist = ['iViGenOff', 'iGruenbeck_SC18_Kti', 'iAbsperrventil_Ak', 'iOwTemp_BetriebPufferOben', 'iViSchlafzi_Hundemelder']
     self.leuchtkugel_timer = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7fa456bd10>
     self.log = <Logger My_HABApp (DEBUG)>
     self.MyBad = <Item name: hBad, value: None, last_change: InstantView(2024-10-27T00:12:52.86375258+02:00), last_update: InstantView(2024-10-27T00:12:52.86375258+02:00)>
     self.MyShutterControl = <Item name: hShutterControl, value: None, last_change: InstantView(2024-10-27T00:12:52.863171512+02:00), last_update: InstantView(2024-10-27T00:12:52.863171512+02:00)>
     self.run = <HABApp.rule.scheduler.job_builder.HABAppJobBuilder object at 0x7f8c604910>
     self.t_gen_off_urlaub = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7fa44e4850>
     self.ViAbsperrventil = <NumberItem name: iViAbsperrventil, value: 0, last_change: InstantView(2024-10-27T00:12:22.887411653+02:00), last_update: InstantView(2024-10-27T00:12:52.879173513+02:00)>
     e_mail = <E_mail>
     offline_list = []
     sk_tools = <SkTools>
     self.init_wait_curr >= self.init_wait_max = False
   ------------------------------------------------------------

File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule/scheduler/job_builder.py", line 211 in on_every_day
--------------------------------------------------------------------------------
     201 | def on_every_day(self, time, callback: HINT_CB,
     202 |                  *args: HINT_CB_P.args, **kwargs: HINT_CB_P.kwargs) -> DateTimeJobControl:
     203 |     warnings.warn(
     204 |         'self.run.on_every_day is deprecated. Use self.run.at in combination with a trigger',
     205 |         DeprecationWarning, stacklevel=2
     206 |     )
     208 |     if isinstance(time, dt_datetime):
     209 |         time = time.time()
-->  211 |     return self.at(
     212 |         TriggerBuilder.time(time),
     213 |         callback, *args, **kwargs
     214 |     )
   ------------------------------------------------------------
     callback = <bound method Technik.triggered_holiday_check of <Technik>>
     HINT_CB = collections.abc.Callable[~HINT_CB_P, typing.Any]
     kwargs = {}
     self = <HABApp.rule.scheduler.job_builder.HABAppJobBuilder object at 0x7f8c604910>
     time = datetime.time(0, 0)
   ------------------------------------------------------------

File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule/scheduler/job_builder.py", line 152 in at
--------------------------------------------------------------------------------
     128 | def at(self, trigger: TriggerObject, callback: HINT_CB,
     129 |        *args: HINT_CB_P.args,
     130 |        job_id: Hashable | None = None, **kwargs: HINT_CB_P.kwargs) -> DateTimeJobControl:
       (...)
     147 |         return self.once(trigger, callback, *args, job_id=job_id, **kwargs)
     149 |     callback = wrap_func(callback, context=self._habapp_rule_ctx)
     151 |     job = DateTimeJob(wrapped_func_executor(callback, args, kwargs), _get_producer(trigger), job_id=job_id)
-->  152 |     run_func_from_async(job.link_scheduler, self._scheduler)
     153 |     return DateTimeJobControl(job)
   ------------------------------------------------------------
     Hashable = <class 'collections.abc.Hashable'>
     self = <HABApp.rule.scheduler.job_builder.HABAppJobBuilder object at 0x7f8c604910>
     self._habapp_rule_ctx = <HABApp.rule_ctx.rule_ctx.HABAppRuleContext object at 0x7f8c604210>
     self._scheduler = <AsyncHABAppScheduler enabled=False jobs=2 next_run=None>
     _get_producer = BuilderTypeValidator(TriggerObject, DateTimeProducerBase, _producer)
     callback = <HABApp.core.internals.wrapped_function.wrapped_thread.WrappedThreadFunction object at 0x7f8c614750>
     HINT_CB = collections.abc.Callable[~HINT_CB_P, typing.Any]
     job = <DateTimeJob id=547816032336 status=created next_run=None>
     job_id = None
     kwargs = {}
     trigger = <eascheduler.builder.triggers.TriggerObject object at 0x7f8c6147d0>
   ------------------------------------------------------------

File "/opt/habapp/lib/python3.11/site-packages/HABApp/core/asyncio.py", line 102 in run_func_from_async
--------------------------------------------------------------------------------
      95 | def run_func_from_async(func: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T:
      96 |     # we already have an async context
       (...)
      98 |         return func(*args, **kwargs)
     100 |     # we are in a thread, that's why we can wait (and block) for the future
     101 |     future = _run_coroutine_threadsafe(_run_func_from_async_helper(func, *args, **kwargs), loop)
-->  102 |     return future.result()
   ------------------------------------------------------------
     _run_coroutine_threadsafe = <function run_coroutine_threadsafe at 0x7fa90efba0>
     args = (<AsyncHABAppScheduler enabled=False jobs=2 next_run=None>,)
     async_context = <ContextVar name='async_ctx' at 0x7fa72af380>
     func = <bound method JobBase.link_scheduler of <DateTimeJob id=547816032336 status=created next_run=None>>
     future = <Future at 0x7f8c614890 state=finished raised InfiniteLoopDetectedError>
     kwargs = {}
     loop = <_UnixSelectorEventLoop running=True closed=False debug=True>
   ------------------------------------------------------------

File "/usr/lib/python3.11/concurrent/futures/_base.py", line 456 in result
--------------------------------------------------------------------------------
     428 | def result(self, timeout=None):
       (...)
     453 |             if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
     454 |                 raise CancelledError()
     455 |             elif self._state == FINISHED:
-->  456 |                 return self.__get_result()
     457 |             else:
   ------------------------------------------------------------
     [CANCELLED, CANCELLED_AND_NOTIFIED] = ['CANCELLED', 'CANCELLED_AND_NOTIFIED']
     CANCELLED = 'CANCELLED'
     CANCELLED_AND_NOTIFIED = 'CANCELLED_AND_NOTIFIED'
     FINISHED = 'FINISHED'
     self = None
     timeout = None
   ------------------------------------------------------------

File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401 in __get_result
--------------------------------------------------------------------------------
     398 | def __get_result(self):
     399 |     if self._exception:
     400 |         try:
-->  401 |             raise self._exception
     402 |         finally:
     403 |             # Break a reference cycle with the exception in self._exception
   ------------------------------------------------------------
     self = None
   ------------------------------------------------------------

File "/opt/habapp/lib/python3.11/site-packages/HABApp/core/asyncio.py", line 108 in _run_func_from_async_helper
--------------------------------------------------------------------------------
     105 | async def _run_func_from_async_helper(func: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T:
     106 |     token = async_context.set('run_func_from_async')
     107 |     try:
-->  108 |         return func(*args, **kwargs)
     109 |     finally:
   ------------------------------------------------------------
     args = (<AsyncHABAppScheduler enabled=False jobs=2 next_run=None>,)
     async_context = <ContextVar name='async_ctx' at 0x7fa72af380>
     func = <bound method JobBase.link_scheduler of <DateTimeJob id=547816032336 status=created next_run=None>>
     kwargs = {}
     token = <Token used var=<ContextVar name='async_ctx' at 0x7fa72af380> at 0x7f8c614a40>
   ------------------------------------------------------------

--------------------------------------------------------------------------------
Traceback (most recent call last):
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule_manager/rule_file.py", line 79, in load
    self.create_rules(created_rules)
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule_manager/rule_file.py", line 69, in create_rules
    runpy.run_path(str(self.path), run_name=str(self.path), init_globals=rule_hook.in_dict())
  File "<frozen runpy>", line 291, in run_path
  File "<frozen runpy>", line 98, in _run_module_code
  File "<frozen runpy>", line 88, in _run_code
  File "/etc/openhab/habapp/rules/technik.py", line 1370, in technik.py
    Technik()
  File "/etc/openhab/habapp/rules/technik.py", line 109, in __init__
    self.boot_initialisations()
  File "/etc/openhab/habapp/rules/technik.py", line 278, in boot_initialisations
    self.run.on_every_day(time(hour=0, minute=0), self.triggered_holiday_check)
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule/scheduler/job_builder.py", line 211, in on_every_day
    return self.at(
           ^^^^^^^^
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule/scheduler/job_builder.py", line 152, in at
    run_func_from_async(job.link_scheduler, self._scheduler)
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/core/asyncio.py", line 102, in run_func_from_async
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 456, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/core/asyncio.py", line 108, in _run_func_from_async_helper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/jobs/base.py", line 85, in link_scheduler
    self.update_first()
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/jobs/base.py", line 105, in update_first
    self.update_next()
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/jobs/job_datetime.py", line 28, in update_next
    next_run = self.producer.get_next(Instant.now())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/producers/prod_time.py", line 26, in get_next
    for _ in not_infinite_loop():  # noqa: RET503
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/producers/base.py", line 60, in not_infinite_loop
    raise InfiniteLoopDetectedError()
eascheduler.errors.errors.InfiniteLoopDetectedErrorException in load:
Could not load /etc/openhab/habapp/rules/technik.py!
File "<frozen runpy>", line 291 in run_path
--------------------------------------------------------------------------------

File "<frozen runpy>", line 98 in _run_module_code
--------------------------------------------------------------------------------

File "<frozen runpy>", line 88 in _run_code
--------------------------------------------------------------------------------

File "/etc/openhab/habapp/rules/technik.py", line 1370 in technik.py
--------------------------------------------------------------------------------
     1366 |         if sk_tools.confirm_function(item_name, command) is False:
     1367 |             return
     1368 |         self.run.countdown(5, self.triggered_reset_openhab_body).reset()
-->  1370 | Technik()
     1373 | class WindowOpenAlert(Rule):
   ------------------------------------------------------------
     transformations.map = <MapMapTransformationFactory>
     'iSchlafzi_Tuere_Ro' + 'M' = 'iSchlafzi_Tuere_RoM'
     e_mail = <E_mail>
     range(1, 3) = range(1, 3)
     sk_tools = <SkTools>
   ------------------------------------------------------------

File "/etc/openhab/habapp/rules/technik.py", line 109 in __init__
--------------------------------------------------------------------------------
      95 | def __init__(self):
       (...)
     105 |     self.error_mailed = False                   # damit Fehlermail nur einmal geschickt wird
     106 |     self.fc_hundemelder_alarm = None
     107 |     self.items_checklist = ['iViGenOff', 'iGruenbeck_SC18_Kti', 'iAbsperrventil_Ak',
     108 |                             'iOwTemp_BetriebPufferOben', 'iViSchlafzi_Hundemelder']
-->  109 |     self.boot_initialisations()
     111 |     self.valve_time = 33              # Umschaltzeit vom Absperrventil in Sekunden
   ------------------------------------------------------------
     self = <Technik>
     self.absperrventil_was_on = False
     self.error_mailed = False
     self.fc_hundemelder_alarm = <FlashLight FlashLight.2>
     self.init_wait_curr = 0
     self.init_wait_delta = 30
     self.init_wait_max = 600
     self.items_checklist = ['iViGenOff', 'iGruenbeck_SC18_Kti', 'iAbsperrventil_Ak', 'iOwTemp_BetriebPufferOben', 'iViSchlafzi_Hundemelder']
     self.log = <Logger My_HABApp (DEBUG)>
     self.MyBad = <Item name: hBad, value: None, last_change: InstantView(2024-10-27T00:12:52.86375258+02:00), last_update: InstantView(2024-10-27T00:12:52.86375258+02:00)>
     self.MyShutterControl = <Item name: hShutterControl, value: None, last_change: InstantView(2024-10-27T00:12:52.863171512+02:00), last_update: InstantView(2024-10-27T00:12:52.863171512+02:00)>
   ------------------------------------------------------------

File "/etc/openhab/habapp/rules/technik.py", line 278 in boot_initialisations
--------------------------------------------------------------------------------
     122 | def boot_initialisations(self):
       (...)
     271 |     # Initialisierung absperrventil_was_on zur Sicherheit, NUR dann
     272 |     # True wenn 'Urlaub' UND 'Regeneration läuft' UND 'Absperrventil ist geöffnet'
     273 |     self.absperrventil_was_on = bool(NumberItem.get_item('iViGenOff').value == 12 and
     274 |     self.Gruenbeck_SC18_Kti.is_off() and
     275 |     self.Absperrventil_Ak.is_off())
     277 |     # Trigger für Feiertagscheck
-->  278 |     self.run.on_every_day(time(hour=0, minute=0), self.triggered_holiday_check)
     279 |     self.log.debug('start "triggered_holiday_check" delayes at boot '\
     280 |     'to ensure "iHeuteFeiertag" and "iMorgenFeiertag" are initialized')
   ------------------------------------------------------------
     self.run.trigger = <class 'eascheduler.builder.triggers.TriggerBuilder'>
     not self.error_mailed = True
     self = <Technik>
     self.Absperrventil_Ak = <SwitchItem name: iAbsperrventil_Ak, value: OFF, last_change: InstantView(2024-10-27T00:12:22.831250159+02:00), last_update: InstantView(2024-10-27T00:12:22.831250159+02:00)>
     self.absperrventil_was_on = False
     self.coffee_timer = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7f8c6db5d0>
     self.error_mailed = False
     self.fc_dog_watch = <FlashLight>
     self.fc_hundemelder_alarm = <FlashLight FlashLight.2>
     self.fc_kaffee = <FlashLight FlashLight.5>
     self.fc_kaffee_feedback = <FlashLight FlashLight.4>
     self.fc_sleeptimer = <FlashLight FlashLight.3>
     self.fl_gen_off_urlaub = <FlashLight FlashLight.6>
     self.Gruenbeck_SC18_Kti = <SwitchItem name: iGruenbeck_SC18_Kti, value: ON, last_change: InstantView(2024-10-27T00:12:22.812875127+02:00), last_update: InstantView(2024-10-27T00:12:22.812875127+02:00)>
     self.hundemelder_pause_timer = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7f8c607750>
     self.init_wait_curr = 0
     self.init_wait_curr >= self.init_wait_max and not self.error_mailed = False
     self.init_wait_delta = 30
     self.init_wait_max = 600
     self.iSchlafzTvMinuten = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7f8c6058d0>
     self.iSchlafzWarnMinuten = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7f8c605850>
     self.items_checklist = ['iViGenOff', 'iGruenbeck_SC18_Kti', 'iAbsperrventil_Ak', 'iOwTemp_BetriebPufferOben', 'iViSchlafzi_Hundemelder']
     self.leuchtkugel_timer = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7fa456bd10>
     self.log = <Logger My_HABApp (DEBUG)>
     self.MyBad = <Item name: hBad, value: None, last_change: InstantView(2024-10-27T00:12:52.86375258+02:00), last_update: InstantView(2024-10-27T00:12:52.86375258+02:00)>
     self.MyShutterControl = <Item name: hShutterControl, value: None, last_change: InstantView(2024-10-27T00:12:52.863171512+02:00), last_update: InstantView(2024-10-27T00:12:52.863171512+02:00)>
     self.run = <HABApp.rule.scheduler.job_builder.HABAppJobBuilder object at 0x7f8c604910>
     self.t_gen_off_urlaub = <HABApp.rule.scheduler.job_ctrl.CountdownJobControl object at 0x7fa44e4850>
     self.ViAbsperrventil = <NumberItem name: iViAbsperrventil, value: 0, last_change: InstantView(2024-10-27T00:12:22.887411653+02:00), last_update: InstantView(2024-10-27T00:12:52.879173513+02:00)>
     e_mail = <E_mail>
     offline_list = []
     sk_tools = <SkTools>
     self.init_wait_curr >= self.init_wait_max = False
   ------------------------------------------------------------

File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule/scheduler/job_builder.py", line 211 in on_every_day
--------------------------------------------------------------------------------
     201 | def on_every_day(self, time, callback: HINT_CB,
     202 |                  *args: HINT_CB_P.args, **kwargs: HINT_CB_P.kwargs) -> DateTimeJobControl:
     203 |     warnings.warn(
     204 |         'self.run.on_every_day is deprecated. Use self.run.at in combination with a trigger',
     205 |         DeprecationWarning, stacklevel=2
     206 |     )
     208 |     if isinstance(time, dt_datetime):
     209 |         time = time.time()
-->  211 |     return self.at(
     212 |         TriggerBuilder.time(time),
     213 |         callback, *args, **kwargs
     214 |     )
   ------------------------------------------------------------
     callback = <bound method Technik.triggered_holiday_check of <Technik>>
     HINT_CB = collections.abc.Callable[~HINT_CB_P, typing.Any]
     kwargs = {}
     self = <HABApp.rule.scheduler.job_builder.HABAppJobBuilder object at 0x7f8c604910>
     time = datetime.time(0, 0)
   ------------------------------------------------------------

File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule/scheduler/job_builder.py", line 152 in at
--------------------------------------------------------------------------------
     128 | def at(self, trigger: TriggerObject, callback: HINT_CB,
     129 |        *args: HINT_CB_P.args,
     130 |        job_id: Hashable | None = None, **kwargs: HINT_CB_P.kwargs) -> DateTimeJobControl:
       (...)
     147 |         return self.once(trigger, callback, *args, job_id=job_id, **kwargs)
     149 |     callback = wrap_func(callback, context=self._habapp_rule_ctx)
     151 |     job = DateTimeJob(wrapped_func_executor(callback, args, kwargs), _get_producer(trigger), job_id=job_id)
-->  152 |     run_func_from_async(job.link_scheduler, self._scheduler)
     153 |     return DateTimeJobControl(job)
   ------------------------------------------------------------
     Hashable = <class 'collections.abc.Hashable'>
     self = <HABApp.rule.scheduler.job_builder.HABAppJobBuilder object at 0x7f8c604910>
     self._habapp_rule_ctx = <HABApp.rule_ctx.rule_ctx.HABAppRuleContext object at 0x7f8c604210>
     self._scheduler = <AsyncHABAppScheduler enabled=False jobs=2 next_run=None>
     _get_producer = BuilderTypeValidator(TriggerObject, DateTimeProducerBase, _producer)
     callback = <HABApp.core.internals.wrapped_function.wrapped_thread.WrappedThreadFunction object at 0x7f8c614750>
     HINT_CB = collections.abc.Callable[~HINT_CB_P, typing.Any]
     job = <DateTimeJob id=547816032336 status=created next_run=None>
     job_id = None
     kwargs = {}
     trigger = <eascheduler.builder.triggers.TriggerObject object at 0x7f8c6147d0>
   ------------------------------------------------------------

File "/opt/habapp/lib/python3.11/site-packages/HABApp/core/asyncio.py", line 102 in run_func_from_async
--------------------------------------------------------------------------------
      95 | def run_func_from_async(func: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T:
      96 |     # we already have an async context
       (...)
      98 |         return func(*args, **kwargs)
     100 |     # we are in a thread, that's why we can wait (and block) for the future
     101 |     future = _run_coroutine_threadsafe(_run_func_from_async_helper(func, *args, **kwargs), loop)
-->  102 |     return future.result()
   ------------------------------------------------------------
     _run_coroutine_threadsafe = <function run_coroutine_threadsafe at 0x7fa90efba0>
     args = (<AsyncHABAppScheduler enabled=False jobs=2 next_run=None>,)
     async_context = <ContextVar name='async_ctx' at 0x7fa72af380>
     func = <bound method JobBase.link_scheduler of <DateTimeJob id=547816032336 status=created next_run=None>>
     future = <Future at 0x7f8c614890 state=finished raised InfiniteLoopDetectedError>
     kwargs = {}
     loop = <_UnixSelectorEventLoop running=True closed=False debug=True>
   ------------------------------------------------------------

File "/usr/lib/python3.11/concurrent/futures/_base.py", line 456 in result
--------------------------------------------------------------------------------
     428 | def result(self, timeout=None):
       (...)
     453 |             if self._state in [CANCELLED, CANCELLED_AND_NOTIFIED]:
     454 |                 raise CancelledError()
     455 |             elif self._state == FINISHED:
-->  456 |                 return self.__get_result()
     457 |             else:
   ------------------------------------------------------------
     [CANCELLED, CANCELLED_AND_NOTIFIED] = ['CANCELLED', 'CANCELLED_AND_NOTIFIED']
     CANCELLED = 'CANCELLED'
     CANCELLED_AND_NOTIFIED = 'CANCELLED_AND_NOTIFIED'
     FINISHED = 'FINISHED'
     self = None
     timeout = None
   ------------------------------------------------------------

File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401 in __get_result
--------------------------------------------------------------------------------
     398 | def __get_result(self):
     399 |     if self._exception:
     400 |         try:
-->  401 |             raise self._exception
     402 |         finally:
     403 |             # Break a reference cycle with the exception in self._exception
   ------------------------------------------------------------
     self = None
   ------------------------------------------------------------

File "/opt/habapp/lib/python3.11/site-packages/HABApp/core/asyncio.py", line 108 in _run_func_from_async_helper
--------------------------------------------------------------------------------
     105 | async def _run_func_from_async_helper(func: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T:
     106 |     token = async_context.set('run_func_from_async')
     107 |     try:
-->  108 |         return func(*args, **kwargs)
     109 |     finally:
   ------------------------------------------------------------
     args = (<AsyncHABAppScheduler enabled=False jobs=2 next_run=None>,)
     async_context = <ContextVar name='async_ctx' at 0x7fa72af380>
     func = <bound method JobBase.link_scheduler of <DateTimeJob id=547816032336 status=created next_run=None>>
     kwargs = {}
     token = <Token used var=<ContextVar name='async_ctx' at 0x7fa72af380> at 0x7f8c614a40>
   ------------------------------------------------------------

--------------------------------------------------------------------------------
Traceback (most recent call last):
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule_manager/rule_file.py", line 79, in load
    self.create_rules(created_rules)
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule_manager/rule_file.py", line 69, in create_rules
    runpy.run_path(str(self.path), run_name=str(self.path), init_globals=rule_hook.in_dict())
  File "<frozen runpy>", line 291, in run_path
  File "<frozen runpy>", line 98, in _run_module_code
  File "<frozen runpy>", line 88, in _run_code
  File "/etc/openhab/habapp/rules/technik.py", line 1370, in technik.py
    Technik()
  File "/etc/openhab/habapp/rules/technik.py", line 109, in __init__
    self.boot_initialisations()
  File "/etc/openhab/habapp/rules/technik.py", line 278, in boot_initialisations
    self.run.on_every_day(time(hour=0, minute=0), self.triggered_holiday_check)
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule/scheduler/job_builder.py", line 211, in on_every_day
    return self.at(
           ^^^^^^^^
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule/scheduler/job_builder.py", line 152, in at
    run_func_from_async(job.link_scheduler, self._scheduler)
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/core/asyncio.py", line 102, in run_func_from_async
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 456, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/opt/habapp/lib/python3.11/site-packages/HABApp/core/asyncio.py", line 108, in _run_func_from_async_helper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/jobs/base.py", line 85, in link_scheduler
    self.update_first()
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/jobs/base.py", line 105, in update_first
    self.update_next()
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/jobs/job_datetime.py", line 28, in update_next
    next_run = self.producer.get_next(Instant.now())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/producers/prod_time.py", line 26, in get_next
    for _ in not_infinite_loop():  # noqa: RET503
  File "/opt/habapp/lib/python3.11/site-packages/eascheduler/producers/base.py", line 60, in not_infinite_loop
    raise InfiniteLoopDetectedError()
eascheduler.errors.errors.InfiniteLoopDetectedError

Same here:

While tonight at midnight it was a call .next_run_datetime of a timer object that caused the exception I reproducible get an exception today If run the following:

self.run.at(self.run.trigger.time(“00:00:00”),

I.e. triggering today a timer at midnight seems to be problematic. Although this should be tomorrow and not affected by daylight saving time anymore.

Any thoughts?

I had this case with the release version HABApp and wanted to ask if this will happen also with the new version:

eascheduler.errors.errors.FirstRunInThePastError: First run can not be in the past! Now: 2024-10-27T02:49:11.837909+02:00, run: 2024-10-27T02:04:11.837909+01:00

It didn’t honor the changed time zone.

I had this issue as well and it only happened during system startup where the system was super busy. In this case a run in 1s was actually in the past when the scheduler picked it up.

In my case it was really a case of summertime/wintertime shift. The new date was in the future when looking also at the offset. Which has changed from+2 to +1.

Thanks for the replys. I’ve pushed Dev-11 which fixes all the issues.
Additionally it adds a MqttTopicInfo which makes publishing to MQTT topics from rules easier.

    from HABApp.mqtt.util import MqttTopicInfo

    topic = MqttTopicInfo('my/output/only/topic')
    topic.publish('new_value')

    topic_qos = MqttTopicInfo('my/output/only/topic', qos=2)
    topic_qos.publish('new_value')

    topic_qos_retain = topic_qos.replace(retain=True)
    topic_qos_retain.publish('new_value')
1 Like

Yes - that’s a bug and with the old datetime library (pendulum) there is no easy way to prevent this.
It’s also almost abandoned and the reason why there is still no Python 3.12 support for HABApp.
Thats why I decided to do a complete rewrite of the scheduler, even though it’s very hard and requires lots of effort.
The bug will bite you again next year during the same daylight savings time transition, so imho there is no need for a quick switch to the beta since it’s almost a year.
Of course you can also try the Beta - I’m hoping that I can finalize the release in November as I don’t expect many more breaking changes.