HABApp - Easy automation with openHAB

That’s true, but you’ll spend way more time wondering about the syntax and reading the statements and t.b.h. the performance gain can be neglected.

1 Like

Hi there!

I haven’t found a way to get the label of an Openhab item type. I use labels for spoken messages because my item names are not very pronounceable. :grinning:

Well, you can use tags, metadata or parameter files. All are available without further requests.

You can however use the openhab api to request the full item definition and from that you’ll get the complete item definition (self.oh.get_item('my_not_very_pronounceable_item')).
Note that this will make a request so I’d do this once after startup.

Hi all,

currently I am working on to move from OH2.5 with Jython helper libraries without any DSL rule to OH3 with HABApp, and am a little bit in struggle with the different concepts “scripts” vs. “rules” in order to save my python code. Prohably I found a solution, so my first question is not related to this, but to logging.

First, is there is a possibility to filter out certain items from being logged the HABApp_events.log by configuration, comparable to the log4j DENY and ACCEPT stuff in OH?

And secondly, I would want to log rule stuff only on WARN level to a dedicated file. Setting up a handler and logger is not an issue. But I would want to be able to switch on INFO or DEBUG logging for certain rules scripts on demand, ideally by configuration and without restarting the rules script or even HABApp.
Can this be done?

Not with a configuration option. You can always implement it yourself with a custom logging handler but I doubt it’s worth the effort.

Yes - the logging configuration is reloaded on the fly. You can observe this in the logs

Yes - the logging configuration is reloaded on the fly. You can observe this in the logs

Because I was not able to find an explanation, after some tries this seems to work:

loggers:
  Rules:
    level: WARN
    handlers:
      - RulesLog
    propagate: no

  Rules.anyfile:
    level: DEBUG

by getting a logger with name “Rules.anyfile”. If the logger Rules.anyfile is removed from logging.yml, the Rules logger is used for Rules.anyfile.

Is this the way in doing so?

BTW: For what is “propagate” good for?

You can find the logging documentation in the official python docs and everything is explained there.

Thank you. I saw the page before, but read over the important text passage :frowning:

Some additional questions, hopefully easy to answer:

  1. How to deal with NULL and UNDEF values? Is OpenhabItem.value is set to None in such cases?
  2. Is it possible to disable and enable things through your interface?
  3. I set wait_for_openhab to true. Can I be sure that the rules are started after all items are instantiated, restored at startup and known to HABApp?
  4. Is there is definded sequence how the rule files are read and excecuted during HABApp startup, other than by configuration at the beginning of the rules files, e.g. by alphabetical order? This was very convenient using the Jython helper libraries.
  1. yes
  2. No
  3. You can be sure that at least some items are loaded (see posts above). Check the -wos command line switch to get a proper startup
  4. Alphabetical order if no dependencies are specified

Thank you.

Some further questions:

How can I be sure that a event does not trigger the same callback twice?

E.g.

rule "any"
when
        Item Value changed from 1 or
        Item Value changed to 2
then
        some stuff

which I would implement with

value_item.listen_event(callback, ValueChangeEventFilter(old_value=1))
value_item.listen_event(callback, ValueChangeEventFilter(value=2))

should call callback() only once if the Value changes from 1 to 2.

I ask out of curiosity. For remedy one could create ValueChangeEvent listener and check for the old and new value in the callback. But then this is not the same like the former rules.

The question is related to the Jython helper libraries, which gave me full control over the rules as objects. It was possible to deactivate rules, in the sense that the events do not trigger any longer, until reactivation. And it was possible to drop rules during runtime, so that the related EventListeners are deleted also.
Is this possible in your approach somehow?

You example will trigger the callback twice. There is no aggregation or logic, these are two independent event listeners.

You can cancel and recreate the event listeners, there is even a helper in the HABApp utils.
If you cancel all event listeners the rule will effectively do nothing.

Edit:
Easy workaround for the trigger twice issue:

value_item.listen_event(callback_1, ValueChangeEventFilter(old_value=1))
value_item.listen_event(callback_2, ValueChangeEventFilter(value=2))

def callback_1(event):
    if event.value == 2:
        return None
    callback_2(event)

def callback_2(event):
    # my logic

Hey All,

has anybody any hint how i can follow the ‘Getting started’ section in the docs
https://habapp.readthedocs.io/en/latest/getting_started.html

Where can i see the print() output? The log shows, that the file was updated. How and where can i see the output? When i activate the venv i also cant see any output in the terminal. I tried locally (sshed to the raspi) and i tried using vs code. Somehow i am still a noob in things like this …

Thanks a lot
Patrick

If you run it as a service then you see the output there (check status of the service).
You should start/stop habapp manually to see the print output (since it goes to stdout).
If you have setup a service try logging the output with a logger.

I wrote a wrapper around the EventListenerGroup() in order to be able to activate and deactivate listeners.

Unfortuately I get a TypeError exception from your code:

2022-01-10 13:14:46.323 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/openhab/connection_handler/sse_handler.py", line 33, in on_sse_event
2022-01-10 13:14:46.323 [ERROR] [HABApp                              ] -     EventBus.post_event(event.name, event)
2022-01-10 13:14:46.323 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/wrapper.py", line 103, in f
2022-01-10 13:14:46.323 [ERROR] [HABApp                              ] -     return func(*args, **kwargs)
2022-01-10 13:14:46.324 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/EventBus.py", line 44, in post_event
2022-01-10 13:14:46.324 [ERROR] [HABApp                              ] -     listener.notify_listeners(event)
2022-01-10 13:14:46.324 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/event_bus_listener.py", line 40, in notify_listeners
2022-01-10 13:14:46.324 [ERROR] [HABApp                              ] -     if isinstance(event, self.event_filter):
2022-01-10 13:14:46.324 [ERROR] [HABApp                              ] - 
2022-01-10 13:14:46.324 [ERROR] [HABApp                              ] - TypeError: isinstance() arg 2 must be a type or tuple of types
2022-01-10 13:14:46.331 [ERROR] [HABApp                              ] - Error isinstance() arg 2 must be a type or tuple of types in post_event:
2022-01-10 13:14:46.331 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/wrapper.py", line 103, in f
2022-01-10 13:14:46.331 [ERROR] [HABApp                              ] -     return func(*args, **kwargs)
2022-01-10 13:14:46.331 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/EventBus.py", line 44, in post_event
2022-01-10 13:14:46.331 [ERROR] [HABApp                              ] -     20   def post_event(topic: str, event):
2022-01-10 13:14:46.331 [ERROR] [HABApp                              ] -  (...)
2022-01-10 13:14:46.331 [ERROR] [HABApp                              ] -     40           pass
2022-01-10 13:14:46.331 [ERROR] [HABApp                              ] -     41   
2022-01-10 13:14:46.331 [ERROR] [HABApp                              ] -     42       # Notify all listeners
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -     43       for listener in _EVENT_LISTENERS.get(topic, []):
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] - --> 44           listener.notify_listeners(event)
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -     45   
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -      topic = 'Lst_Netzwerkschrank'
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -      event = <ItemStateChangedEvent name: Lst_Netzwerkschrank, value: 81.93499755859375, old_value: 82.75800323486328>
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -      listener = <HABApp.core.event_bus_listener.EventBusListener object at 0x7f8d65dbb7c0>
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -      listener.notify_listeners = <method 'EventBusListener.notify_listeners' of <HABApp.core.event_bus_listener.EventBusListener object at 0x7f8d65dbb7c0> event_bus_listener.py:32>
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] - 
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/event_bus_listener.py", line 40, in notify_listeners
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -     32   def notify_listeners(self, event):
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -  (...)
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     36           return None
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     37   
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     38       # single filter
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     39       if self.__is_single:
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] - --> 40           if isinstance(event, self.event_filter):
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     41               # If we have property filters wie only trigger when value is set accordingly
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -      self = <HABApp.core.event_bus_listener.EventBusListener object at 0x7f8d65dbb7c0>
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -      event = <ItemStateChangedEvent name: Lst_Netzwerkschrank, value: 81.93499755859375, old_value: 82.75800323486328>
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -      self.__is_single = # AttributeError
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -           self = <HABApp.core.event_bus_listener.EventBusListener object at 0x7f8d65dbb7c0>
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -      self.event_filter = <ValueUpdateEvent name: None, value: None>
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] - 
2022-01-10 13:14:46.334 [ERROR] [HABApp                              ] - TypeError: isinstance() arg 2 must be a type or tuple of types
2022-01-10 13:14:46.344 [ERROR] [HABApp                              ] - Error isinstance() arg 2 must be a type or tuple of types in on_sse_event:
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/openhab/connection_handler/sse_handler.py", line 66, in on_sse_event
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -     22   def on_sse_event(event_dict: dict):
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -  (...)
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -     62           if isinstance(event, (ItemAddedEvent, ItemUpdatedEvent)):
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -     63               create_task(item_event(event))
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -     64               return None
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -     65   
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] - --> 66           HABApp.core.EventBus.post_event(event.name, event)
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -     67       except Exception as e:
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -      event_dict = {'topic': 'openhab/items/Lst_Netzwerkschrank/statechanged',
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -                    'payload': '{"type":"Decimal","value":"81.93499755859375","oldType":"Decimal","oldValue":"82.75800323486328"}',
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -                    'type': 'ItemStateChangedEvent'}
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -      event = <ItemStateChangedEvent name: Lst_Netzwerkschrank, value: 81.93499755859375, old_value: 82.75800323486328>
2022-01-10 13:14:46.345 [ERROR] [HABApp                              ] -      ItemAddedEvent = <class 'HABApp.openhab.events.item_events.ItemAddedEvent'>
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -      ItemUpdatedEvent = <class 'HABApp.openhab.events.item_events.ItemUpdatedEvent'>
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -      event.name = 'Lst_Netzwerkschrank'
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -      e = TypeError('isinstance() arg 2 must be a type or tuple of types')
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] - 
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/wrapper.py", line 103, in f
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -     101  def f(*args, **kwargs):
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -  (...)
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] - --> 103          return func(*args, **kwargs)
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -      args = ('Lst_Netzwerkschrank', <ItemStateChangedEvent name: Lst_Netzwerkschrank, value: 81.93499755859375, old_value: 82.75800323486328>, )
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -      kwargs = {}
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -      func = <function 'post_event' EventBus.py:19>
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.346 [ERROR] [HABApp                              ] - 
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/EventBus.py", line 44, in post_event
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -     20   def post_event(topic: str, event):
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -  (...)
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -     40           pass
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -     41   
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -     42       # Notify all listeners
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -     43       for listener in _EVENT_LISTENERS.get(topic, []):
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] - --> 44           listener.notify_listeners(event)
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -     45   
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -      topic = 'Lst_Netzwerkschrank'
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -      event = <ItemStateChangedEvent name: Lst_Netzwerkschrank, value: 81.93499755859375, old_value: 82.75800323486328>
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -      listener = <HABApp.core.event_bus_listener.EventBusListener object at 0x7f8d65dbb7c0>
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -      listener.notify_listeners = <method 'EventBusListener.notify_listeners' of <HABApp.core.event_bus_listener.EventBusListener object at 0x7f8d65dbb7c0> event_bus_listener.py:32>
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.347 [ERROR] [HABApp                              ] - 
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/event_bus_listener.py", line 40, in notify_listeners
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -     32   def notify_listeners(self, event):
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -  (...)
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -     36           return None
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -     37   
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -     38       # single filter
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -     39       if self.__is_single:
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] - --> 40           if isinstance(event, self.event_filter):
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -     41               # If we have property filters wie only trigger when value is set accordingly
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -      self = <HABApp.core.event_bus_listener.EventBusListener object at 0x7f8d65dbb7c0>
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -      event = <ItemStateChangedEvent name: Lst_Netzwerkschrank, value: 81.93499755859375, old_value: 82.75800323486328>
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -      self.__is_single = # AttributeError
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -           self = <HABApp.core.event_bus_listener.EventBusListener object at 0x7f8d65dbb7c0>
2022-01-10 13:14:46.348 [ERROR] [HABApp                              ] -      self.event_filter = <ValueUpdateEvent name: None, value: None>
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] -     ..................................................
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] - 
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] - ---- (full traceback above) ----
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/openhab/connection_handler/sse_handler.py", line 66, in on_sse_event
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] -     HABApp.core.EventBus.post_event(event.name, event)
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/wrapper.py", line 103, in f
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] -     return func(*args, **kwargs)
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/EventBus.py", line 44, in post_event
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] -     listener.notify_listeners(event)
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/event_bus_listener.py", line 40, in notify_listeners
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] -     if isinstance(event, self.event_filter):
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] - 
2022-01-10 13:14:46.349 [ERROR] [HABApp                              ] - TypeError: isinstance() arg 2 must be a type or tuple of types

I created the regarding listener with

        self.listeners.add_listener(ohitem, self.call, ValueUpdateEventFilter(value), "uniquename")

Why - that’s literally what the EventListenerGroup is made for.

As I said before, I am coming from Jython. There I wrapped the rules stuff in order to separate my scripts from the implementation details of the “rule layer” and to add different levels of logging for debugging purposes. And now, with the need to leave Jython behind me, it shows that this was a very good decision.

You are right, the EventListenerGroup() and my OHRule class is very comparable, so the class is not rocket science.

But, independent from whether I use your EventListenerGroup directly or indirectly via my class, what do I have to do to get it working?

I don’t know. My crystal ball is still broken from the last time I had to guess problems. :wink:
Maybe see if the EventListenerGroup works as expected without your wrapper.
If that is the case then your implementation is buggy - if not it’s most likely a bug in HABApp.

You can also completely load/unload a rule file e.g. from another rule.
Just post the appropriate event on the event bus (the same that gets posted from the file watcher).
That might be an easier solution.

It is most likely a bug in your event_bus_listener.py, at least in the case that an event_filter is an event_filter and not its class:

022-01-10 13:14:46.332 [ERROR] [HABApp                              ] - File "/opt/habapp/lib/python3.9/site-packages/HABApp/core/event_bus_listener.py", line 40, in notify_listeners
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -     32   def notify_listeners(self, event):
2022-01-10 13:14:46.332 [ERROR] [HABApp                              ] -  (...)
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     36           return None
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     37   
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     38       # single filter
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     39       if self.__is_single:
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] - --> 40           if isinstance(event, self.event_filter):
2022-01-10 13:14:46.333 [ERROR] [HABApp                              ] -     41               # If we have property filters wie only trigger when value is set accordingly

So isinstance(, …self.event_filter) should be the culprit.

I’ve just migrated all my jsr223 jython scripts to HABApp and I’ve also upgraded openHAB to “version 3-something”.

Just like to say that I’m very impressed with HABApp in every aspect. It “just works” and it’s fast. I love the pure python3 environment! Again, thanks @Spaceman_Spiff for your great work! I’m really grateful for it.

3 Likes

I have to apologize. For whatever reason I got the EventListenerGroup() working now. I don’t know what I have changed to get it.

Today I found a real issue. I tried to iterate through a member list of an openHAB group item. This fails if one of the members has a NULL value.

The problem is sorted() in

def get_members(group_name: str) -> Tuple['HABApp.openhab.items.OpenhabItem', ...]:
    ret = []
    for name in MEMBERS.get(group_name, []):
        ret.append(Items.get_item(name))
    return tuple(sorted(ret))

sorted() uses

 __lt__()

, which compares the items’ values according to /HABApp/core/items/base_valueitem.py. It therefore fails on None.

Because it is not sure that all members of a group are of the same type, the list should not be sorted depending on the values. I therefore removed sorted()