HABApp - Easy automation with openHAB

Yes - I only use the rules because I can easily get the state with self.get_item_state and set it with self.set_item_state.
What I sometimes do is use another rule and its functions to keep small logical units properly abstracted, e.g.:

class RuleA:
    def do_sth_a():
       pass
MyRuleA = RuleA()

class RuleB:
    def do_sth_b():
       MyRuleA.do_sth_a()
RuleB()

Another question:
If I have each rule in its own file, can I instantiate each rule in some global file?
I assume that each rule runs in the same context, right?
Would it be possible to use global variable if needed?

It is not clear for me how all my created files are related to each other and related to HapAPP.
Are the rule files (normal Python files?) just collected and concatenated or how does it work?

There is the possibility to get a rule instance by name so there is no need for global variables.

Each python file you create is loaded independently as you can see in the HABApp.log.
So if you want to share Rule instances you can use the function to get it by name or put the rules that require each other in one file as I showed in the example above :slight_smile:

Update 0.4.9

  • Reconnect on SEE event error should now work as intended
  • Caught an error when connecting to Openhab and Openhab is not ready
  • Added possibility to wait before starting HABApp
  • Reformatted error traceback in rules
  • Added example for ValueNoChangeEvent

Version 0.5.0

  • Ignore Errors when SSE event is not a valid json
  • Added possibility to load logging config first when using absolute paths
  • Added log messages when loosing connection to OpenHAB

Version 0.5.5

  • Did some refactoring - it may be necessary to fix some imports.
    Tip: run HABApp with the read_only config switch from another machine to fix them
  • Added bugfixes, documentation and tests
  • Added openHAB items (Switch, Contact, Rollershutter, Dimmer) with special functions e.g.:
    switchitem.on() or switchitem.is_on()
  • Items can be compared without having to use the state-member:
    if my_number_item < 5: ....

This looks like great work! Especially the possibility to use python libraries is great. Will definitely play with it soon. Is it also possible to use OH2 actions?

Unfortunately I have not found a way yet. :frowning:

The easiest workaround is to use a proxy-item and trigger the action on an update of the item.

@Spaceman_Spiff I really like this! I’ve started using HabApp to convert some of my rules from DSL as I am somewhat familiar with python.
Is there any chance you can do some “bridging” between jython and HabApp to make actions work?

Hi @lfs_alp5, I am really glad you like it! I’ve tried to get information how to use actions, but so fare there is no possibility. I’ve also opened an issue on github.
A workaround is to define an item and then send commands to it.
e.g.

Dimmer 	MyOutput {channel = "zwave:device:controller:node7:switch_dimmer" }

or create a rule which listens to a proxy item and then executes the action.

Uploaded a new version

Version 0.6.8

  • Added possibility to process error messages from rules
  • Added callback when rule gets unloaded
  • Added possibility to read item definitions from openhab
  • fixed a bug where created items were not put in any group
  • added possibility to check if an item already exists
  • HABApp items can be used in every rule function
  • new classmethod for getting/creating items in HABApp which provides syntax highlighting for the class functions:
from HABApp.openhab.items import SwitchItem
switch = SwitchItem.get_create_item('MySwitch')
switch.is_on()

Register a callback for errors

This example shows how to create a rule with a function which will be called when any rule throws an error. The rule function then can push the error message to an openhab item or e.g. use Pushover to send the error message to the mobile device.

import HABApp
from HABApp.core import WrappedFunction

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

        # so it get's unloaded propperly in case we make changes to this rule
        self.register_on_unload(WrappedFunction.CLEAR_ERROR_CALLBACK)

        # register function
        WrappedFunction.REGISTER_ERROR_CALLBACK(self.on_error)

    def on_error(self, error_message: str):
        print(f'Message:\n{error_message}\n')

NotifyOnError()


# this is a faulty example. Do not create this part!
class FaultyRule(HABApp.Rule):
    def __init__(self):
        super().__init__()
        self.run_soon(self.faulty_function)

    def faulty_function(self):
        1 / 0
FaultyRule()

will output:

Message:
Error in FaultyRule.faulty_function: division by zero
Traceback (most recent call last):
  File "<string>", line 32, in faulty_function
ZeroDivisionError: division by zero
1 Like

I seem to only get 0.6.6 via pip3, is there a delay before it’s “out” ?

Edit: and how do I loop a group? :worried:

Have you tried pip install --upgrade habapp?

1 Like

Thanks, my bad. I thought the install also upgraded if there was a newer version available :flushed:

By the way, is there a way to loop item members in a group? Similar to DSL “MyGroup.members.forEach” ?

i have noticed this project only now,a nd i see you ahve been developing it for quite some time!
i’ll give it a try for sure!

1 Like

I added a hint in the documentation on how to upgrade

Sure:

for item in self.openhab.get_item('MyGroup').members:
    do_something()

However, since you can get items dynamically per name i almost exclusively use naming conventions to access my items.
Example:

from HABApp.openhab.items import SwitchItem, DimmerItem
class TestClass(Rule):
    
    def __init__(self, part_name):
        super().__init__()
        
        self.switch = SwitchItem.get_create_item(f'{part_name}_Switch')
        self.dimmer = DimmerItem.get_create_item(f'{part_name}_Dimmer')


TestClass('Kitchen')
TestClass('Office')
TestClass('...')
1 Like

I’m having trouble getting that .members for loop going, it throws this:

[2019-08-04 17:20:45,948] [             HABApp.Rules]     INFO | Added rule "MyOpenhabTestRule" from test.py
[2019-08-04 17:20:45,968] [             HABApp.Rules]    ERROR | Could not load /etc/openhab2/rules/test.py: __init__() got an unexpected keyword argument 'stateDescription'!
[2019-08-04 17:20:45,969] [             HABApp.Rules]    ERROR |   File "/usr/local/lib/python3.7/site-packages/HABApp/openhab/oh_interface.py", line 52, in from_dict
[2019-08-04 17:20:45,969] [             HABApp.Rules]    ERROR |     data['members'][i] = cls.from_dict(item)
[2019-08-04 17:20:45,969] [             HABApp.Rules]    ERROR |   File "/usr/local/lib/python3.7/site-packages/HABApp/openhab/oh_interface.py", line 53, in from_dict
[2019-08-04 17:20:45,970] [             HABApp.Rules]    ERROR |     return cls(**data)
[2019-08-04 17:20:45,970] [             HABApp.Rules]    ERROR | TypeError: __init__() got an unexpected keyword argument 'stateDescription'

The rule is as follows:

 import HABApp
 import logging

log = logging.getLogger('MyRule')

class MyOpenhabTestRule(HABApp.Rule):

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

        for item in self.openhab.get_item('gTemperatureInside').members:
            log.debug(item.name)

MyOpenhabTestRule()

The group is defined like this:

Group gTemperatureInside (GAll)

Using OH 2.4.0 and HABApp 0.6.8

Ah, yes - this is a bug that is already fixed in the dev branch. I’ll upload a new version this evening.
Sorry for the inconvenience.

1 Like

Version 0.6.9

  • Scheduling async function from different threads works now as expected
  • Added type hints and fixed a bug in get_item and added tests
  • Updated documentation and tests

Btw postprocessing error messages is really nice. If there is an error/exception in one of my rules it gets pushed directly to my mobile phone :smile:

Hi @Spaceman_Spiff

I just tried to run the docker container on my RPi4 to test it out but it doesn’t look like the image is ARM-compatible. Any chance you can offer an image that will run on ARM?

Thanks