HABApp - Easy automation with openHAB

Of course! Create a NoChangeWatcher for chandelier_level item and if the value is 0 just switch it off.
Create a value_change_watcher and if value is != 0 switch on.
It’s almost a three-liner :wink:

def __init__(self):
    ...
    self.my_switch = SwitchItem.get_item('my_switch')
    self.item = Item.get_create_item('my_item_name')
    self.item.watch_change(60).listen_event(self.value_const)
    self.item.listen_event(self.value_change, ValueChangeEvent)

def value_change(self, event):
    if event.value and self.my_switch.is_off():
        self.my_switch.on()

def value_const(self, event):
    if self.item.value != 0:
        return None
   self.my_switch.off()

Think of it as a class instance object. Then it’s clear that I overloaded the compare operators.


Check out the BufferEventFile in the log. The event file will written every x messages and the BufferEventFile defines the capacity of the in memory buffer.
Under handlers you can use for example the
class: logging.handlers.RotatingFileHandler since
class: HABApp.core.lib.handler.MidnightRotatingFileHandler
will only rotate on midnight (who would have thought :wink: ).
A list of all available handlers (additional to the MidnightRotatingFileHandler) and the description are in the python docs

Just configure the log folder / log file so it points to a ram disk.

I however would modify the mqtt topics that HABApp subscribes to and just pick those that are from interest.
Less messages to log → less written

Okay now you’re just embarrassing me. You’re enjoying this aren’t you :slight_smile: :grinning_face_with_smiling_eyes:
Seriously, thank you. That is elegant. I will try it shortly – that looks so much better.

New question.
In OH2.5 Basic UI, I wanted to display the online status of devices so I could see it at a glance.

Here’s what it looked like:

And here’s how I did it using JSR223/Jython in OH2.5

I wrote a python object that monitors the status of the Thing and sets a text item, and optionally sets some MQTT topics when an item goes offline.
This way, when for example the Garage Speaker Node goes offline, the volume controls of all the speakers it controls is set to zero, so that they’re shown at 0 when the system is off.
Then I simply instantiate a copy of this object for each Thing I want to monitor, and add more items of course.

The question is, what’s the best way to do this with HABApp? Is ThingStatusInfoChangedEvent the best way (essentially the equivalent of what I did before) or is there a better way?
I figured I better just ask. :slight_smile:

Ha! Since you put it that way :slight_smile:
I actually thought it would rotate when maxBytes is reached as well as at midnight no matter what… otherwise the maxBytes line, which you do include in the default configuration example along with the handler MidnightRotatingFileHandler, doesn’t actually have any meaning. So there. :wink:

I like idea of a RAM disk, I will try that. Modifying the MQTT subscription, less so… it would be cumbersome to have to maintain that. My homie tree is about 50 messages per second, hardly thousands. Most of that is from my energy meters. If there was a way to exclude those, I would, but I think subscriptions are inclusive, not exclusive, right?

Yep - this one is the same. Create a rule for every thing or a rule which monitors all the things.
As you like.

I think I need to be more precise:
The MidnightRotatingFileHandler will rotate on Midnight if the file size is > max_bytes.

For me the logfile helps to see what kind of states have been processed.
If it’s too much (as you said gigs) to even look at the messages then just set the level to WARNING.
Or you can set the buffer to 100 or 1000 entries then it’s only written every 5-10 secs.

correct

1 Like

Following your example, this works to monitor the state of ALL things.

        for thing in self.get_items(Thing):
            thing.listen_event(self.thing_status_changed, ThingStatusInfoChangedEvent)
            log.info(f'{thing.name}: {thing.status} {repr(type(thing))}')

But, how do I get just one Thing by the UID?

self.get_items(Thing) returns a list of HABApp.openhab.items.thing_item.Thing which is what I need for the listen event

But HABApp.openhab.interface.get_thing(self.thing_id) returns a HABApp.openhab.definitions.rest.things.OpenhabThingDefinition

I could let the for loop run and do string matching but would love a better way. :slight_smile:

Do you mean an existing Thing? Thing.get_item(name)

Be aware that there is

  • a new function self.get_items which should be used to iterate over items
  • self.oh.get_thing returns the OpenhabThingDefinition which is the raw information from openhab.
    It’s done with a sync request to OH.
  • Thing.get_item() returns the HABApp - Thing object (which is something different).

Thank you! That is precisely what I was looking for.

I looked back on the documentation page, which I spent quite a bit of time on already, to try to figure out why I wasn’t able to discover this answer myself.

I did find HABApp.openhab.interface.get_item(item_name, metadata=None, all_metadata=False) which is quite early on the page, and that is what I tried to use.
Thing.get_item is muuuuch further down the page, behind get_items for all the different Item types… I never got that far, it was a needle in a haystack. I wonder if it might make sense with a remark in the HABApp.openhab.interface.get_item description that Thing.get_item also exists, with a link to the bookmark?

Small question:

The ThingStatusInfoChangedEvent includes both status and detail which is great. You need detail to get the “PENDING” status.

The HABApp - Thing includes status but does not include detail. Not the end of the world (unlikely that an item will be pending right when my script starts) but is there a way to get the detail outside of an event?

Additionally I could move the item item description before the interface description. Wdyt?

Maybe with a sync request to get the item directly from openhab but that’s not worth the effort.
Could you open an issue on git with the item information and the raw event (you can set the event log to DEBUG and it’ll log the raw message from openhab). It should be easy to add if I have sample data.

Thanks a lot for a really great app. It made my life a bit easier.
I have one question though. Let’s imagine, I want to write rather big python project with a complex logic, lots of imports (such as calDAV, telegram-bot, raspberry gpio, etc) and some sophisticated classes. What is the best way to make it seamlessly work with habapp? Should I make it (just another) habapp rule? Then, what is the logic of rule reloading? Should I break my script to pieces and/or use your event bus?
In short, what to do if I have my own idea of a smart home, and openHAB is only a small part of it?

Good news everyone!

I just release HABApp 0.31.2!


Depends as always. Since your stuff will be tied together somehow you’ll need some kind of event bus.
Either you use openhab, HABApp, MQTT or simple REST calls.

What you always can do is call python scripts in a new process from HABApp and then parse the output.
Or use an e.g. GPIO2MQTT service to interact with the GPIOs.

Imports are generally not the problem. Long running function calls are problematic since they block the worker threads So avoid those - e.g. through asyncio code.
You can always use your own libraries with the lib-folder (see HABApp config), but be aware that in order to pick up changes you have to reload HABApp.

To summarize:
It depends and without knowing what you want to do and what your architecture is it’s impossible to say!

I think you mean 0.31.2 :wink:

1 Like

You’re right! I guess it’s time for Christmas holidays! :wink:

1 Like

Hello again Sebastian! :slight_smile:

This might be more of a Python question than a HABApp question, but:

Where do I put my own “library” type functions or classes that I want to be able to use from multiple other rules? (Equivalent of OH2 JSR223’s automation\lib\python\personal)

Depends.
E.g. I have a pushover notification service set up which I trigger through a rule.
The rule does only listen to the event bus for errors and then notify me.
But sometimes I want a notification e.g. if the battery is low.
Then I call the rule functions directly.
If you want to write real libraries (e.g. classes you want to derive from) then you can use the lib folder.
Imho you should be fine with the first one, because with the second one you have to restart HABApp every time you make changes.

Using stuff from other files / self written libs can be tricky and lead to hard to track down errors because rules can be dynamically reloaded. So make sure to follow the docs to the letter!

If you don’t need a return value you can always create an HABApp internal item and post a value to it.
In the implementation you can listen to updates and start your logic.

I don’t particularly need to derive from but I would like to be able to instantiate a class from one file into another file. Not sure if that’s a distinction without a difference.

Speaking of restarting HABApp… It is rather a pain to have to restart HABApp every time I add a new item in OH, especially since I also then have to tail -f another logfile. Have you considered having a magic item (you already have HABApp_Ping, why not a HABApp_Restart or HABApp_Refresh switch) which tells HABApp to re-initialize itself?

As for libraries and reloading, in OH2 with Jython, when using a personal library, it was standard practice to:

import personal.superbuttons
reload (personal.superbuttons)

…to ensure that that you got the latest version of the library, without having to restart anything else.

There are many, many functions that I will need to write and use for many different items.

Here’s one example of something that I need to be in a library.
The superbuttons library above references my own 433 MHz remote parser, which distills the raw code values rc-switch puts out. I use wall-mounted 433 MHz remotes everywhere – that is my primary day-to-control method.

So, pushing a remote button, rc-switch spits out 20f4a1 for every code it sees. One does not want to post that raw to MQTT (message rate), and in any case you’ve gotta distill it down somewhere, so I do that with my superbuttons library inside the esp8266 or esp32 that has the 433 MHz receiver.

Then I post the following to the remote receiver MQTT topic:

20f4a1*TALLY,1,0
20f4a1*SOLID,0,1
20f4a1*MEDIUMPRESS,0,3
20f4a1*LONGPRESS,0,7
20f4a1*RELEASE,1,7
20f4a1*DONE,1,7

If pressed three times in quick succession, the following gets posted:

20f4a1*TALLY,1,0
20f4a1*SOLID,0,1
20f4a1*RELEASE,1,1
20f4a1*TALLY,2,1
20f4a1*RELEASE,2,1
20f4a1*TALLY,3,1
20f4a1*RELEASE,3,1
20f4a1*DONE,3,1

This allows me to write whatever logic I want at the destination side, without the remote receiver needing to be aware of any remotes.

For example, if you wanted to have a separate action for one, two or three pushes, you’d simply trap the DONE message and look at the first parameter which is the number of distinct keypresses (this is determined by timing). If you wanted a self-repeating control while holding the button down, you’d simply trap TALLY and RELEASE while ignoring the parameters.

Anyway, the OH2 JSR223/Jython superbuttons counterpart simply contains the following:

class ParseEvent(object):
	def __init__(self, payload):
		self.valid = 0
		self.code = 0
		self.event = 0
		self.count = 0
		self.solid = 0
		self.medium = 0
		self.long = 0
		self.verylong = 0
		self.flags = 0
		
		if payload.find("*") < 0: return

		splitevent = payload.split("*")
		
		self.code = int(splitevent[0],base=16)

		splitdata = splitevent[1].split(",")
		
		if len(splitdata)<3: return
		
		self.event = splitdata[0]
		self.count = int(splitdata[1])
		self.flags = int(splitdata[2])
		
		self.solid=1 if (self.flags & 1) else 0
		self.medium=1 if (self.flags & 2) else 0
		self.long=1 if (self.flags & 4) else 0
		self.verylong=1 if (self.flags & 8) else 0
	
		self.valid = 1

And then, here’s the rule that triggers on the 433 MHz receiver item (also OH2 Jython):

		sb=superbuttons.ParseEvent(event.getEvent())
		if not sb.valid: return

		if sb.code == 0x19697d:
			if sb.event == "SOLID":
				toggle("BedsideLight")

And by the way, the toggle function is in another library:

from core.jsr223.scope import itemRegistry, OnOffType 

def toggle(strItem):
	item=itemRegistry.getItem(strItem)
	item.send(OnOffType.OFF if item.getState()==OnOffType.ON else OnOffType.ON)

Where would those things go if not in libraries? :slight_smile:

Issues arise if you instantiate Rules/Items/OtherHABAppStuff in library files.
If you just import things and instantiate them in a rule it’s fine (at least most of the time).

Why would you do that? HABApp picks up Item changes without issue.
It’s Thing changes that don’t get picked up since openHAB does not propagate them.

Don’t do that. There is no need to and the official python docs state that you should explicitly not do that. I know it’s common practice with jsr223 but it can (and will) cause hard to track down problems once things get a little bit more complicated.

Your superbuttons is a good example for something that can and should be put in the library.
It’s more or less static and has no dependencies (e.g. does not depend on items/rules/oh).

As of the toggle:
You should always work with the HABApp item anyway so this is a one-liner:

item.oh_send_command('ON' if item.is_off() else 'OFF')

For many things that you used to write library functions you can just instantiate rules with parameters/args.

1 Like

That is exactly why I would do that. Even if openHAB does not propagate Thing changes, it’d be nice if I could tell HABApp to refresh the Things, without having to restart the HABApp docker in putty console 1 and ctrl-c the tail -f /data/habapp/log/HABApp.log in putty console 2. Remember, I have to keep the tail -f open in order to see the log.info output, and it has to be HABApp.log or I won’t see python errors. Print does not go anywhere when you’re running the docker container version…
Anyway, just having a switch (or something) would be a lot fewer steps for something I find myself needing to do very often while setting things up.

There is also the fact that just to rename a property on a homie device, OH forces me to delete the whole Thing and re-add it, in order for it to detect the new channel, or get rid of the old one. At least the items remain valid once I’ve re-added it again. Yes, this is an OH MQTT binding issue and not a HA issue but having to go through multiple steps to restart HA isn’t helping.
I opened an issue two weeks ago but nobody has even looked at it, so I do not have high hopes that this will be fixed in a timely manner – and in the end I need to just make this work so that I can move on to the next in the lost list of house construction related duties. :slight_smile:

Okay definitely easier in HA than OH JSR223, cool. Although, to be honest toggle(item) is only half a line :wink:

So, where do I put the py files with things i want to be able to import in other files?

Do I just put it in the normal rules folder and add a dummy HABApp.Rule inheritance item to squelch the Found no instances of HABApp.Rule in /config/rules/hello.py warning?

Edit: You already answered this earlier! Sorry about that. lib folder, in the configuration file.

You can always serve the log folder through smb and watch the logfile with a log viewer from your main machine. That’s how I do it. And if there are errors I get a notification on my smartphone. In the docs you’ll find an example on how to process errors.

You asked me four posts above and my answer shall remain the same :wink: :
The lib folder. Otherwise the imports won’t work.

Yea - sure. But tbh there are still so many things to do so I don’t know when I’ll have the time to implement it.

1 Like

Thank you so much again! It’s working great.

My superbuttons library is working wonderfully in HABApp now. Currently writing a rule to turn speaker volume up and down.

I am serving the log folder but I’ve found that sometimes it takes a while for smb to notice file updates… different issue. I will try to set up frontail at some point, that might work too. I definitely would not want to have to read my python development errors (there are plenty since I just barely know python!) on my phone screen, which I am of course not holding while writing code. :wink:
But I will read the documentation about processing errors regardless.

Hey, did I mention I love not having to create openHAB items to interact with MQTT? :heart:

1 Like