Finally stuck: TypeError keeps rule from being loaded by HABApp

Over the past weeks I have succeded in installing (easy: openHabian) HABApp, configuring a development environment (VS Code on my macos: hard - took me days to understand virtualenvironments, pyenv etc), and even a rule to shut off my espresso machine after being unused for 1,5 hours.
I also got my screens working (auto up and down based on sun_azimuth and elevation).
However, while making the rule smarter I also introduced errors. Up until now I have been able to correct these myself. This time I am stuck however.
Help would be appreciated to get me going again.

This is my script:

import HABApp
from HABApp.core.events import ValueUpdateEventFilter, ValueUpdateEvent
from HABApp.core.items import Item
from HABApp.openhab.items import SwitchItem, NumberItem, GroupItem
import statistics
import logging

log = logging.getLogger("RuleLog")

class ScreenAutomation(HABApp.Rule):
    def __init__(self):
        super().__init__()

        self.sceneScreensZdicht=SwitchItem.get_item('Zuidscreens_preset_0_omlaag_Scene')    
        self.sceneScreensZopen=SwitchItem.get_item('Zone_Zuidscreens_Group_gray_Scene_preset_2_Scene')
        self.sceneScreensWdicht=SwitchItem.get_item('Zone_Westscreens_Group_preset_0_Scene')
        self.sceneScreensWopen=SwitchItem.get_item('Zone_Westscreens_Group_gray_Scene_preset_2_Scene')

        self.bewolking0H=NumberItem.get_item('One_Call_API_Bewolking_plus_0H').value
        self.bewolking1H=NumberItem.get_item('One_Call_API_Bewolking_plus_1H').value                 
        self.bewolking2H=NumberItem.get_item('One_Call_API_Bewolking_plus_2H').value                  
        self.bewolking3H=NumberItem.get_item('One_Call_API_Bewolking_plus_3H').value                 
        self.gem_bewolking=statistics.mean([self.bewolking0H, self.bewolking1H, self.bewolking2H, self.bewolking3H])

        self.buienhoeveelheid=GroupItem.get_item('Buienradar_eerste_kwartier')
        self.buien=(GroupItem.get_item('Buienradar_komende_2_uur'))

        self.run.at(self.run.trigger.sun_azimuth(90),self.screensSouthDown)
        self.run.at(self.run.trigger.sun_azimuth(140),self.screensWestDown)
        self.run.at(self.run.trigger.sun_azimuth(250),self.screensSouthUp)
        self.run.at(self.run.trigger.sun_elevation(15,'setting'),self.screensWestUp)


        self.buienhoeveelheid.listen_event(self.alleScreensOmhoog,ValueUpdateEventFilter())
        
    def screensSouthDown(self) -> None:
        log.info('Tijd om de screens in het zuiden te sluiten.')
        if self.gem_bewolking<45:
            log.debug(f'"Verwacht bewolkingspercentage: "{self.gem_bewolking}". Boven 45% gaan screens niet dicht."')
            if self.buien.value<1:
                log.debug(f'Verwachte buienhoeveelheid komende twee uur: {self.buien.value}. Boven 1mm gaan screens niet dicht.')
                if self.sceneScreensZdicht.is_off():
                    log.debug(f'"Status van screen scene "dicht": "{self.sceneScreensZopen}')
                    self.sceneScreensZdicht.on()
                    log.info('Commando "DICHT" gegeven.')
                    log.info(f'{self.sceneScreensZdicht.name}", "{self.sceneScreensZdicht.command_value}')
                else:
                    log.info('Geen actie: de screens zijn al dicht.')
                    log.debug(f'{self.sceneScreensZdicht.name}", "{self.sceneScreensZdicht.command_value}')
            else:
                log.info(f'"Buienkans is meer dan "{self.buien.value}" mm in de komende 2 uur, screens dichtlaten."')
        else:
            log.info(f'"De bewolking is meer dan "{self.gem_bewolking}" %. Geen screens nodig."')


    def screensSouthUp(self):
        log.info('Tijd om de screens in het zuiden te openen')
        if self.sceneScreensZopen.is_off():
            self.sceneScreensZopen.on()
            log.info('Commando "OPEN" gegeven.')
            log.info(f'{self.sceneScreensZopen.name}", "{self.sceneScreensZopen.command_value}')
        else:    
            log.info('Geen actie: de screens staan al omhoog.')

    def screensWestDown(self):
        log.info('Tijd om de screens in het westen te sluiten.')
        if self.gem_bewolking.value<45:
            log.debug(f'"Verwacht bewolkingspercentage: "{self.gem_bewolking}". Boven 45% gaan screens niet dicht."')
            if self.buien.value<1:
                log.debug(f'"Verwachte buienhoeveelheid komende twee uur: "{self.buien.value}". Boven 1mm gaan screens niet dicht."')
                if self.sceneScreensWdicht.is_off():
                    self.sceneScreensWdicht.on()
                    log.info('Commando "DICHT" gegeven.')
                    log.info(f'{self.sceneScreensWdicht.name}", "{self.sceneScreensWdicht.command_value}')
                else:
                    log.info('Geen actie: de screens zijn al dicht.')
                    log.debug(f'{self.sceneScreensZdicht.name}", "{self.sceneScreensZdicht.command_value}')
            else:
                log.info(f'"Buienkans is meer dan "{self.buien.value}" mm in de komende 2 uur, screens dichtlaten"')
        else:
            log.info(f'"De gem_bewolking is meer dan "{self.gem_bewolking}"%. Geen screens nodig."')



    def screensWestUp(self):
        log.info('Tijd om de screens in het westen te openen')
        if self.sceneScreensWopen.is_off():
            self.sceneScreensWopen.on()
            log.info('Commando "OPEN" gegeven.')
            log.info(f'{self.sceneScreensWopen.name}", "{self.sceneScreensWopen.command_value}')
        else:
            log.info('Geen actie: de screens staan al omhoog.')

    def alleScreensOmhoog(self, event: ValueUpdateEvent):
        if self.buienhoeveelheid.value>0.0:
            if self.buienhoeveelheid.value <0.5 and (self.sceneScreensZdicht.is_on or self.sceneScreensWdicht.is_on):
                log.info(f'"De hoeveelheid regen de komende 15 minuten valt mee: "{self.buienhoeveelheid.value}" mm, de screens kunnen omlaag blijven."')
            elif self.sceneScreensWopen.is_off:
                log.info('Regen in de komende 15 minuten: screens op het westen omhoog.')
                self.sceneScreensWopen.on
            elif self.sceneScreensZopen.is_off:
                log.info('Regen in de komende 15 minuten: screens op het zuiden omhoog.')
                self.sceneScreensZopen.on


ScreenAutomation()

What am I trying to do?

  1. Bring down the sunscreens as soon as the sun is going to shine into the windows (sun_azimuth).
  2. Do not bring the screens down if cloudiness is going to be more than 45% or if there is a chance of rain of more than 1 mm/hr.
  3. Bring up the screens as soon as the sun goes beyond a certain azimuth (does not shine on window) or if it is so far down (elevation) it’s ray’s stop having enough power to warm up the window.
  4. Bring up the screens if there is a chance of rain > 1.5 mm in the coming quarter of an hour.

The error seems to be from this code:

self.gem_bewolking=statistics.mean([self.bewolking0H, self.bewolking1H, self.bewolking2H, self.bewolking3H])

In stead of using the value of an avaraged group in openhab I thought I would calculate the avarage cloudiness (bewolking) for the coming 4 hours.
The cloudiness items are One_Call_API dimensionless numbered channels (openweather). Somehow python wants a unit (but does not see one)? But from documentation I understand that statistics.mean() only expects numbers.

Websearches at this point fail to help me with usefull hits. I think I need hints from knowledgable Python programmers.

System used:
RPi 5 , 16GB
openhabian
openhab 4.3.5
habapp 25.5.0

This is the traceback from my HABApp.log:

2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      self.run.trigger = <class 'eascheduler.builder.triggers.TriggerBuilder'>
2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      [self.bewolking0H, self.bewolking1H, self.bewolking2H, self.bewolking3H] = [100, 100, 100, 100]
2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      self = <ScreenAutomation>
2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      self.bewolking0H = 100
2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      self.bewolking1H = 100
2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      self.bewolking2H = 100
2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      self.bewolking3H = 100
2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      self.run = <HABApp.rule.scheduler.job_builder.HABAppJobBuilder object at 0x7fff3289b790>
2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      self.sceneScreensWdicht = <SwitchItem name: Zone_Westscreens_Group_preset_0_Scene, value: OFF, last_change: InstantView(2025-06-01T16:43:02.378733221+02:00), last_update: InstantView(2025-06-01T16:43:02.378733221+02:00)>
2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      self.sceneScreensWopen = <SwitchItem name: Zone_Westscreens_Group_gray_Scene_preset_2_Scene, value: ON, last_change: InstantView(2025-06-01T16:43:02.378279089+02:00), last_update: InstantView(2025-06-01T16:43:02.378279089+02:00)>
2025-06-01 16:43:03.791 [ERROR] [HABApp.Rules                        ] -      self.sceneScreensZdicht = <SwitchItem name: Zuidscreens_preset_0_omlaag_Scene, value: OFF, last_change: InstantView(2025-06-01T16:43:02.380255245+02:00), last_update: InstantView(2025-06-01T16:43:02.380255245+02:00)>
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -      self.sceneScreensZopen = <SwitchItem name: Zone_Zuidscreens_Group_gray_Scene_preset_2_Scene, value: ON, last_change: InstantView(2025-06-01T16:43:02.378682646+02:00), last_update: InstantView(2025-06-01T16:43:02.378682646+02:00)>
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -    ------------------------------------------------------------
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] - --------------------------------------------------------------------------------
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] - Traceback (most recent call last):
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -   File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule_manager/rule_file.py", line 79, in load
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -     self.create_rules(created_rules)
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -   File "/opt/habapp/lib/python3.11/site-packages/HABApp/rule_manager/rule_file.py", line 69, in create_rules
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -     runpy.run_path(str(self.path), run_name=str(self.path), init_globals=rule_hook.in_dict())
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -   File "<frozen runpy>", line 291, in run_path
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -   File "<frozen runpy>", line 98, in _run_module_code
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -   File "<frozen runpy>", line 88, in _run_code
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -   File "/etc/openhab/habapp/rules/ScreenAutomation.py", line 106, in ScreenAutomation.py
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -     ScreenAutomation()
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -   File "/etc/openhab/habapp/rules/ScreenAutomation.py", line 23, in __init__
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -     self.gem_bewolking=statistics.mean([self.bewolking0H, self.bewolking1H, self.bewolking2H, self.bewolking3H])
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -   File "/usr/lib/python3.11/statistics.py", line 433, in mean
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -     return _convert(total / n, T)
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -            ^^^^^^^^^^^^^^^^^^^^^^
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -   File "/usr/lib/python3.11/statistics.py", line 343, in _convert
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -     return T(value)
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] -            ^^^^^^^^
2025-06-01 16:43:03.792 [ERROR] [HABApp.Rules                        ] - TypeError: QuantityInt.__new__() missing 1 required positional argument: 'unit'
2025-06-01 16:43:03.792 [WARN ] [HABApp.Rule                         ] - /opt/habapp/lib/python3.11/site-packages/HABApp/rule_manager/rule_file.py:86: RuntimeWarning:coroutine 'HABAppRuleContext.unload_rule' was never awaited
2025-06-01 16:43:03.792 [WARN ] [HABApp.Rules                        ] - Failed to load /etc/openhab/habapp/rules/ScreenAutomation.py!

First congratulations on your journey! You learned really complex things and can be proud of your achievement.

The reason for the error is that you are dealing with a UoM item which requires a unit.
A quick fix would be e.g.

self.gem_bewolking=statistics.mean([
  float(self.bewolking0H), float(self.bewolking1H), float(self.bewolking2H), float(self.bewolking3H)
])

I think the behavior you are seeing is very unexpected and I’ll add a way how this will work out of the box with the next version of HABApp.

Also be aware that with your current rule the mean is calculated only once, when the rule is created (since it’s in __init__). You’ll probably want to update self.gem_bewolking every time self.bewolking0H updates/changes

PS:
I see that you are missing the brackets in your code snippet quite a couple of times.
Without the brackets it’s just the function, e.g. the receipe how to do something. With brackets you execute the function, e.g. do it.
So it should be:

<item>.is_on  -> <item>.is_on()
<item>.is_off -> <item>.is_off()
<item>.on     -> <item>.is_on()

Edit: The next version of HABApp which will be released this week will work accordingly.

That did the trick! Thank you.

I was wondering why making these values floats solves the problem? Is it because now the number item is converted into a new (Python) float object that is UoM unaware?

The value of a UoM Number item is a QuantityType which is essentially a float/int with a .unit.
statistics.mean does not provide a unit so creating a QuantityType fails with the above error message.
If you cast this to a float before you get a new plain float object that works with statistics.mean as expected.