HABApp - Easy automation with openHAB

Good news everyone!

I published HABApp 0.16.0! It bumps some dependencies aaaaaaaand

*drum roll*

*drum roll intensifies*

it adds support for OpenHAB 3!

To make it work “Allow basic auth” has to be activated and user/pw has to be entered in the config.yml.
There are no other changes required.

10 Likes

I have a couple of more questions:

I looked into the code of the textual thing configuration and actually came up with a similar solution for cleanup of automatically created items. I only used tags instead of metadata.
However I ran into the following issues:

  • I haven’t seen a way to query openhab items (i.e. using the /rest/items? endpoint). Instead I used a bruteforce approach and did something like:
for item in HABApp.core.Items.get_all_items():
     if isinstance(item, OpenhabItem):
           item_definition = HABApp.openhab.interface.get_item (item.name)
  • This approach resulted in a warning “HABApp.Worker] WARNING | Execution of Cleanup.do_cleanup took too long: 2.33s”. How should long running operations being done in rules?

  • Would it be a good idea to expose the /rest/items interface as a method?

The correct way would be to expose the items endpoint and iterate over all the items or use the http connector to make the query yourself. Another possibility would be to use self.run_soon and pass the item name as an argument

If this is your only long running task the warning is not nice but can be ignored. However if you have a couple more HABApp is running out of threads and there will be a significant delay for processing things. That’s why there is a warning.

What is the thread count in the pool? Is it configurable without editing the code?

The limit is currently 10 threads. Currently it’s not configurable but in the future I’ll move these to a different configuration file (e.g. habapp.internals or so).

1 Like

To make it work “Allow basic auth” has to be activated and user/pw has to be entered in the config.yml.

Alternatively you can now generate API tokens and use them to authorize access.

In the UI click on your profile in the lower-left corner, scroll down and click on the Create API Token button:

Then fill in the details (use habapp as the token name, for instance, and admin as scope):

image

The token shown is your username and the password must remain blank.
In that case there is no need to enable basic auth - you just have to use the token instead of your own username/password.
If you ever need to revoke the token you go to your profile page again and click on the red icon:

4 Likes

Hi, I am thinking on trying your HABApp but what I could not find was how to interface with Telegram (and possibly other Addons). In the openHab rules engine it seems fairly simple calling sendTelegram …

thx

HAPApp only uses the rest interface to communicate with Openhab. I.e. you can interact with items and things but can not leverage Addons directly. However you can use any python library you like to acomplish your tasks. You could e.g. use the telegram-send python module.

Dominik

Then I guess I need to learn Python too … another language in one of my +10 collections …

Currently there is no easy method to access actions from HABApp.
I’ve raised an issue about it a long time ago, but it seems it will not be implemented soon.

However As @Dominik_Bernhard described there are (most of the time) python modules available which will do all the work for you and also provide a much more flexible interface.

I typically create a new rule which does the integration and listen to a certain topic.

class NotifyOnError(HABApp.Rule):
    def __init__(self):
        super().__init__()
        
        # Listen to all errors
        self.listen_event(HABApp.core.const.topics.ERRORS, self.on_error)

    def on_error(self, event: typing.Union[HABAppError, str], priority=0):
        msg = event.to_str() if isinstance(event, HABAppError) else event
        # do stuff

NotifyOnError()

In another rule I then can

        p = self.get_rule('NotifyOnError')  # type: NotifyOnError
        p.on_error('MyMessage', priority=1)

See How to use rules from other files from the documentation.

The main selling point for HABApp is that you can use python :wink:

FYI - when I try to use Python3.9 with HABapp, I get errors and the platform fails to start.

Reverting to Python 3.8.6 and it appears to work again.

Error is:

Traceback (most recent call last):
  File "/usr/local/bin/habapp", line 5, in <module>
    from HABApp.__main__ import main
  File "/usr/local/lib/python3.9/site-packages/HABApp/__init__.py", line 14, in <module>
    import HABApp.mqtt
  File "/usr/local/lib/python3.9/site-packages/HABApp/mqtt/__init__.py", line 2, in <module>
    from . import items
  File "/usr/local/lib/python3.9/site-packages/HABApp/mqtt/items/__init__.py", line 1, in <module>
    from .mqtt_item import MqttItem
  File "/usr/local/lib/python3.9/site-packages/HABApp/mqtt/items/mqtt_item.py", line 1, in <module>
    import HABApp.mqtt.mqtt_interface
  File "/usr/local/lib/python3.9/site-packages/HABApp/mqtt/mqtt_interface.py", line 5, in <module>
    from .mqtt_connection import MqttConnection, log
  File "/usr/local/lib/python3.9/site-packages/HABApp/mqtt/mqtt_connection.py", line 9, in <module>
    from HABApp.runtime.shutdown_helper import ShutdownHelper
  File "/usr/local/lib/python3.9/site-packages/HABApp/runtime/__init__.py", line 3, in <module>
    from .runtime import Runtime
  File "/usr/local/lib/python3.9/site-packages/HABApp/runtime/runtime.py", line 7, in <module>
    import HABApp.util
  File "/usr/local/lib/python3.9/site-packages/HABApp/util/__init__.py", line 5, in <module>
    from . import multimode
  File "/usr/local/lib/python3.9/site-packages/HABApp/util/multimode/__init__.py", line 1, in <module>
    from .mode_base import BaseMode
  File "/usr/local/lib/python3.9/site-packages/HABApp/util/multimode/mode_base.py", line 32, in <module>
    from .item import MultiModeItem  # noqa: E402
  File "/usr/local/lib/python3.9/site-packages/HABApp/util/multimode/item.py", line 9, in <module>
    from HABApp.rule import get_parent_rule
  File "/usr/local/lib/python3.9/site-packages/HABApp/rule/__init__.py", line 2, in <module>
    from .rule import Rule, get_parent_rule
  File "/usr/local/lib/python3.9/site-packages/HABApp/rule/rule.py", line 13, in <module>
    import HABApp.openhab
  File "/usr/local/lib/python3.9/site-packages/HABApp/openhab/__init__.py", line 3, in <module>
    import HABApp.openhab.events
  File "/usr/local/lib/python3.9/site-packages/HABApp/openhab/events/__init__.py", line 2, in <module>
    from .item_events import ItemStateEvent, ItemStateChangedEvent, ItemCommandEvent, ItemAddedEvent,\
  File "/usr/local/lib/python3.9/site-packages/HABApp/openhab/events/item_events.py", line 4, in <module>
    from ..map_values import map_openhab_values
  File "/usr/local/lib/python3.9/site-packages/HABApp/openhab/map_values.py", line 3, in <module>
    from HABApp.openhab.definitions import HSBValue, OnOffValue, OpenClosedValue, PercentValue, QuantityValue, RawValue, \
  File "/usr/local/lib/python3.9/site-packages/HABApp/openhab/definitions/__init__.py", line 3, in <module>
    from . import rest
  File "/usr/local/lib/python3.9/site-packages/HABApp/openhab/definitions/rest/__init__.py", line 1, in <module>
    from .items import OpenhabItemDefinition
  File "/usr/local/lib/python3.9/site-packages/HABApp/openhab/definitions/rest/items.py", line 55, in <module>
    OpenhabItemDefinition.update_forward_refs()
  File "/usr/local/lib/python3.9/site-packages/pydantic/main.py", line 677, in update_forward_refs
    update_field_forward_refs(f, globalns=globalns, localns=localns)
  File "/usr/local/lib/python3.9/site-packages/pydantic/typing.py", line 233, in update_field_forward_refs
    field.type_ = evaluate_forwardref(field.type_, globalns, localns or None)
  File "/usr/local/lib/python3.9/site-packages/pydantic/typing.py", line 50, in evaluate_forwardref
    return type_._evaluate(globalns, localns)
TypeError: _evaluate() missing 1 required positional argument: 'recursive_guard'

Thanks for you hint, I’ve already updated most of the dependencies but currently I can’t test 3.9 yet.
I’ll push an update in the next couple of dates that will address the issue.

I withdrew the post as I was an idiot - so busy looking at the code, I forgot the config was still running as listen_only: true.

1 Like

Good news everyone!

I switched the build pipeline to github actions, so there are now arm builds, too (@Tomibeck).

@nick_wootton:
With the updated dependencies it should also run on python 3.9.


0.16.2

  • updated dependencies
  • added armv7 and armv6 as docker builds
  • small fixes
  • activated tests for python 3.9
2 Likes

The main selling point for HABApp is that you can use python :wink:

Besides Python 3.x vs Python 2.x, how would one pick between HABApp and the JSR 223 support for Jython?

I do not mean this in any way to disrespect Sebastian, who has put a lot of time and effort into HABapp, but scripted automation with Jython can do everything HABapp can do and a LOT more. This is due to HABApp being limited by its use of the REST API to communicate with OH, which is also what is used by the UIs. Scripted automation, which can be used with several scripting languages, uses the automation API for creating rules, but it also has full access to not only all of the OH classes, interfaces, and services, but any Java or Python2 libraries that you’d like to use. The Jython interpreter is based on Python 2.7, but when it comes to writing automation, this is not much of a limitation. Jython3 is also being developed and graal-python will be integrated.

HABApp is written in pure Python which means you can do anything you can do in python.
It’s possibly to import any library from pypi and use it together with your rule.
What initially started as an abstraction of the openhab connection has become a general purpose rule engine.
The focus of HABApp is to quickly create error free rules and to achieve this it provides full type hinting, auto complete and checks when used with an IDE and abstracts certain functionality:

  • want to use the timestamp when it was the last time an item was changed

    SwitchItem.get_item('MySwitch').last_change
    
  • want to run something on weekends at 8

    self.run_on_weekends(time(hour=8), my_func)
    
  • want to run something every two hours but only between 8 and 16 o’clock

    self.run_every(timedelta(hours=2), my_func).earliest(time(hour=8).latest(time(hour=16))
    

As Scott pointed out, it’s not possible to do everything with HABApp, because unfortunately the REST-API is in some aspects quite buggy and incomplete e.g. the maintainers don’t expose an “actions” endpoint.
For me it’s not a problem because I can always link an item to a channel and then use the item to interact with the device instead of the action. But it’s still a valid point.

While there might not be much difference and limitation for you, I wouldn’t have spent hundreds of hours developing HABApp if this were true (for me). Python 2.7 was released 2010 and many aspects of the syntax have been deprecated since.

To answer your initial question:
I guess it comes down to your personal preference! I could never go back to using jsr223 and for Scott it’s sufficient. But I guess we’re both biased so you have to try them out yourself.

Have you thought about contributing an actions endpoint to the REST API?

I do see the value of Pyhon 3.x and it would be fun to see if I could do something like hook up TensorFlow into my home automation some day. Right now I’m only at the beginning.

I don’t know any java so unfortunately I can’t help there.

Languages are easy to learn :slight_smile:. But you’re likely very busy with HABApp.