HABApp - Easy automation with openHAB

Tags: #<Tag:0x00007f7458700a20> #<Tag:0x00007f7458700930>

Hello,

since I started with it has always been puzzling to me how hard it is to write automation rules in openHAB.
Things got a little bit better with the jsr223 integration and I think @steve1 contributed some great ideas.
But with openHAB2 and the new API things got worse again and unfortunately even after long hours I did not manage to migrate my jython rules. Also jython has lots of drawbacks and is syntactically python2.
I even tried HomeAssistant because and even though it has lots of pros thanks to the great Z-Wave work of @chris I am back on the openHAB track (if someone is interested I can write a small comparison of openHAB and HomeAssistant).
But whilst trying HomeAssistant I managed to get some fresh ideas and point of views and I still really wanted to create my rules in Python.

This is why created HABApp.

It is a multithreaded, asyncIO based automation framework which allows easy and fast creation of rules.
HABApp connects to openHAB through the Rest-API and therefore allows quick and seamless integration and transition.
This approach has the great benefit that any python library can be easy loaded and integrated into the automation rules.

Links
PyPI
Github
Documentation

HABApp is currently still in beta, but I think this approach is interesting to more people.
It would be nice if I could get some feedback on the idea in general and on the current implementation status.


Version 0.10.0

  • Configured logfiles get rotated on startup instead of deleted
  • added parameter “wait_for_openhab”. If true HABApp will wait until items
    from openhab have been loaded before loading the rules
  • Param & Rule file loading is now event based:
    Reloading of both file types can be observed and triggered from within rules
  • Deprecated some functions and added deprecation warnings to the log
  • Error Processing is now event based, too. Removed WrappedFunction._ERROR_CALLBACK.
    Added an example how to process rule error messages in documentation
  • Prepared parameter file validation
  • Added some more documentation
12 Likes

Are you thinking of making a gui based rules editor that is similar to the home assistant one?

I also have feet In Both camps, it came to the conclusion it was easier to run both, and use the mqtt event bus to send and receive event from openHAB to ha, so that if you liked their automations you could write them in ha and operate oh items.

No - this is out of scope for me. There are already several very nice solutions for this (e.g. NodeRed).
Also if you look at the example it is just a simple class definition - it’s just five lines to get started.
What I might do is the ability to subscribe to MQTT and process/publish the events just like openHAB events.
This would provide some more flexibility.

I added the possibility to connect to an MQTT broker directly in HABApp:

import HABApp
from HABApp.core.events import ValueChangeEvent, ValueUpdateEvent

# MQTT example
class MyMQTTRule(HABApp.Rule):
    def __init__(self):
        super().__init__()

        # Subscribe to topic but only process changes
        self.listen_event( 'test/topic1', self.cb, ValueChangeEvent)

    def cb(self, event):
        assert isinstance(event, ValueChangeEvent)
        self.mqtt_publish( 'test/topic2', event.value + 1)

MyMQTTRule()

This looks really nice,

I have quite a few python servers all reading item states and forwarding then to various other devices, both into and out of OH.

They used to use websockets, until that was dropped, now they use complicated requests to get data from/to the REST interface.

One interface first discovers all items, then uses streaming to get updates and push the results as json to an iPad used as an interface.

All of this stuff would have been much easier with this library.

Suggestion, add a way of getting items and groups from OH, so that you can automate item discovery. Otherwise you constantly have to update item names in two locations (OH and HABapp application).

It’s fairly easy to do via REST, but parsing the massive dictionary you get back is a bit of a task.

I have 3 or 4 applications where I could use this immediately. I normally use mqtt because the REST interface is a pain. This looks like it may ease the pain.

If it uses asyncio, what version of python do you need (I use 3.6), and is it Thread safe?

Great work by the way. I will try this out tomorrow, I have a use for it right now!

Hi, first of all beware this is currently beta. Things might still change.
Currently HABApp requires Python 3.6 so you should be good to go.
Maybe it would be easier for you to get an MQTT server running so you can communicate between all your devices? HABApp works seamlessly with them.

This is already done and you can access the local cache with

self.item_exists(item_name)
self.item_state(item_name)

If I understand you correctly you want to subscribe to all changes so you can forward them.
I could add a function which just returns all items. You could use this to listen to all updates.
What do you think?

What do you mean with thread safe?

Hi,

while migrating my rules I found some bugs when creating invalid rules.
Bugfix is already available (0.0.5)

I have uploaded Version 0.1.1 which includes a couple of fixes and improvements.

Having migrated almost all of my rules I have to say I am very happy.
HABApp is really fast and easy on the system (< 35 MB RAM usage).
But most importantly writing rules is fun again! :slight_smile:

It would be really nice If someone else could try it out - I’d really love some feedback!

1 Like

Is this meant to be an equivalent to Appdaemon in the Home Assistant world? My house is primarily z wave based, so I have often thought about trying out Openhab again, but I am pretty happy with appdaemon.

Perhaps a bit off topic, but how is you impression of the difference of the two systems and what makes z wave better on openhab in your opinion?

That’s exactly what it is! Although I tried to make it better (of course :wink: ).
The nice thing is that openhab runs Z-Wave under any OS, so you can just shut down your HassIO instance and boot openhab on your main computer and try stuff out. Once you are done you plug the z-wave stick back into your HassIO instance and everything is back to the way it was.
That’s actually how I do my migraion from openhab1 at the moment :slight_smile:

Z-Wave on openhab just runs - no need to fuddle around with openzwave, entity configuration. For Z-Wave to work I had very long restart times, I don’t have with openhab. Also inclusion/exclusion of devices and setting device configuration is much easier. Having to rename the automatically created entities was a pain, too. The energy consumption of devices did often not work, since the “senser_multilevel” instead “meter_w” command class is required. Also the automatically created entities did not always work properly (I have a heating controller that did not work with Hass).
Since it is possible to do textual configuration of the devices and things the Z-Wave configuration is persistent and nice again (although not as mighty as in openhab1).

I have uploaded Version 0.1.3. It includes fixes for

self.run_on_workdays
self.run_on_weekends

I have introduced some performance fixes and migrated almost all my rules to HABApp.
V0.2.3 runs very well: on average < 1% CPU and ~2% RAM usage on the Odroid C2.

1 Like

After major refactoring I added the possibility for parameter files in V0.3.0:

        self.p1 = self.get_rule_parameter('param_file', 'key')

        self.p1 < 10         # comparison works out of the box
        self.p1.get_value()  # custom data can be obtained through this function

this will automatically create (on first) run the param_file.yml in the param directory.

key: 10

The value will always be looked up during comparison, so this adds the possibility to change boundaries without having to reload any rules. The file contents are kept in sync through a file-watcher so parameter lookup is almost instant.

Update 0.3.9

  • moved mqtt interaction to self.mqtt
  • moved openhab interaction to self.oh or self.openhab
  • more stable connection and error handling
  • items get updated from openhab after every reconnect
  • added some type hints
  • added more examples (e.g. how to mirror all openhab updates to mqtt)

Update 0.4.7

Also added a nice gif :wink:

2 Likes

@Spaceman_Spiff
Do you also have some complex ruleset for a complete house (your house? :wink: for example) ?

I am unsure how to structure the python code for a complete smart home with a lot of sensors and actuators. Temperature/Humidity, Window/Door contacts, Push Buttons, Power/Gas Metering (washing maschine, complete house, Heating), Lights, Blinds, Weather station (light, wind, temperature), Sonos, Hue, Motion Detectors, Smoke Detectors and Heating Control for each room, etc…

Ok, of course it depends on what I want to achieve…

Yes of course - I migrated all my rules.

I created subfolders with the room names and moved specific rules there (e.g. lights) and my general rules are placed directly in the rules folder.
Otherwise I log a lot of information (for debugging) and then it just grows naturally. :wink:

Ok.

Have you created python objects (e.g. with get/set) from the items and used those in the rule objects?
Or are your only objects just the rules? I hope you know what I mean…

BTW: If you can’t remember all the MQTT topics immediately, it is helpful to use a tool like MQTT.fx.
It has a builtin MQTT topics collector which scans for new topics and puts them in a list.
You can then simply right-click on the topic and copy the string to the editor where you edit your MQTT rules for HABapp.

Dream: it would be really cool, if Visual Studio Code would have an extension which autocompletes such MQTT topics or OpenHAB items while typing.

Update 0.4.8

  • Added the possibility to start external processes
  • Lots of documentation

Example

import HABApp

class MyExecutionRule(HABApp.Rule):

    def __init__(self):
        super().__init__()

        self.execute_subprocess( self.func_when_finished, 'path_to_program', 'arg1', capture_output=True)

    def func_when_finished(self, process_info):
        assert isinstance(process_info, HABApp.rule.FinishedProcessInfo)
        print(process_info)

MyExecutionRule()