HABApp - Easy automation with openHAB

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

Me neither, but the “ding” tells me to look at the logfiles :wink:

1 Like

Wait, your phone has notifications sounds enabled? How do you focus? :slight_smile:

No, everything is silent except for calls and pushover notifications.
Because what could be more urgent than an error in one of my scripts :wink:

1 Like

Thank you @Spaceman_Spiff for all of your time, effort, patience while providing the HABApp framework to us. I’ve started to migrate all my JSR223/Jython scripts now. It will take a while.

I have a python file with some handy functions that I use to import from my rule python files. Those are not class methods, just stand-alone functions hence they can not interact through the self.openhab object that’s available in a typical rule class.

I wonder if it would be possible in such a function to access an openHAB group item and loop through all the members extraction the member item names. Below is my old jython function that can serve as an example:

def sayHello():
    hour = datetime.datetime.now().hour
    if (hour < 7 or hour > 21):
        return
    greetings = ['Hello', 'Good day', 'Cheers']
    people_at_home = []
    for member in scope.itemRegistry.getItem('G_Presence_Family').getAllMembers():
        if member.state in [scope.OPEN, scope.ON]:
            people_at_home.append(member.label)
    random.shuffle(people_at_home)
    msg = random.choice(greetings)
    for i in range(len(people_at_home)):
        person = people_at_home[i]
        msg += ' '+person
        if i+2 == len(people_at_home):
            msg +=' and'
        elif i+1 == len(people_at_home):
            msg +='.'
        elif i+2 < len(people_at_home):
            msg +=','
    tts(msg)
    notifyLaMetric(msg, icon='none')

I hope that it’s possible to understand what I’m trying to do. :grinning: Thanks in advance for any help.