HABApp - Easy automation with openHAB

How about using the item watcher and watch for item changes?

def __init__(self):
    self.item = Item.get_create_item('MyWindow')
    self.item.watch_change(15 * 60).listen_event(self.window_const)

def window_const(self, event):
    if self.item.is_closed():
        return None
    # do_notification
    ...

You could iterate through the group members as you described and it will work, too.
Imho it depends how you do your abstraction.
If it’s just a simple rule that displays Window MyWindow is open! then it’s fine either way.
If you want to create more sophisticated rules and e.g. want to group window wings logically together or even want different timeouts I’d definately go the param file way because it’s easier to add/remove additional information in it. And if you attach the parameter to the rule you’ll always have the most recent values even without reloading the rule file.


The countdown is actually a rather simple implementation:
A call to .reset() will start the countdown with the specified time. It doesn’t matter if the countdown is already expired or if it’s currently running. It will always start again with the configured time.
A call do .cancel() will “destroy” the countdown object so you have to create a new one if you want to use it again.
There currently is no way to stop the countdown without letting it expire. If you create an issue maybe I’ll add it some day :wink: .

1 Like

As always thanks a lot for your answer. They give me everytime new insights.

Ok, the countdown thing - Check

The wathcer, nice idea, never tought in this way. Itvtriggers the state check every 15 minutes of stable state and my solution startes triggering only if the state changes. I try to keep that option in Mind.

The group thing. I am with you that if i need additional information. But i did not bring it to work to creat instances of a rule from a group. Checkt a group within a rule works. I do it for battery checking.

As you se from my last request it may take some time to create an issue :slight_smile:

Hi! I’m finally starting my adventure into habAPP.

I’m running the docker image from “spacemanspiff2007/habapp” on a Pi4 (bullseye).
It’s connecting to MQTT, I can publish, it’s connecting to openHAB, it’s updating the HABApp_Ping item… It adds the rule from my python file! So far so good – looking great, in fact.

But out of all the possible things I could be having problems with…

How is it that the First rule, the one that is designed to print “That was easy”, doesn’t print anything?? :slight_smile:

I started docker with sudo docker-compose up, without the -d flag, so that I can see the console output. I remember habAPP had some output that way when I first started it … but no. no output there now.
then tail -f HABApp.log. Lots of output there. The rule gets loaded. But no print output!

I figured, maybe I can print to the log like in OH2 with jsr223 and jython?
Googled that, found this page in the habapp manual and tried it. log.debug("that was not that easy") — no output.

Finally, with log.warning("that wasn't easy at all" or log.error("really???") I do get output. In the console that’s stuck running docker-compose without the -d flag. Still nothing in the log.

So, at least I know the rules are running.
I also tried mqtt publishing, and that worked too!

So the only thing I’m missing is printing.

Surely there must be a way? What way do you recommend, @Spaceman_Spiff?

In my own projects, whether a gui-app or a service in windows or an embedded project on an ESP8266 or ESP32, I always have a telnet server i can print to and connect a console too. This works beautifully and it might be a good thing for habAPP to have, if it doesn’t already.

MQTT subscriptions…
subscribe(self, topic: str[, qos: int = None]) → int

“Subscribe to a MQTT topic. Subscriptions will be active until next disconnect. For persistent subscriptions use the configuration file”

Uh, that’s quite a limitation actually. I use MQTT a lot. It would be extremely difficult and error-prone to keep track of the MQTT subscriptions if every single one has to get its own line in the config file.

Or, am I missing something?
The example mqtt rule doesn’t use subscribe, it seems to simply add a listen event, thus it looks like it’s relying on the ‘#’ subscription in the default config.yml file… which is fine by me in that case, so maybe I’m worrying for nothing?

But, if that is the case, then I think “Subscriptions will be active until next disconnect. For persistent subscriptions use the configuration file” is misleading and could be written more clearly. :slight_smile:


I keep getting these warnings in the habapp log:

[2021-12-11 01:23:08,458] [HABApp.openhab.connection]  WARNING | Received ThingStatusInfoEvent for mqtt:homie300:main:elevator-chandelier-dev but item does not exist!
[2021-12-11 01:23:30,433] [HABApp.openhab.connection]  WARNING | Received ThingStatusInfoEvent for mqtt:homie300:main:elevatorcontrol but item does not exist!
[2021-12-11 01:23:38,557] [HABApp.openhab.connection]  WARNING | Received ThingStatusInfoEvent for mqtt:homie300:main:elevator-chandelier-dev but item does not exist!
[2021-12-11 01:24:00,532] [HABApp.openhab.connection]  WARNING | Received ThingStatusInfoEvent for mqtt:homie300:main:elevatorcontrol but item does not exist!
[2021-12-11 01:24:08,655] [HABApp.openhab.connection]  WARNING | Received ThingStatusInfoEvent for mqtt:homie300:main:elevator-chandelier-dev but item does not exist!
[2021-12-11 01:24:30,631] [HABApp.openhab.connection]  WARNING | Received ThingStatusInfoEvent for mqtt:homie300:main:elevatorcontrol but item does not exist!

Any idea what they are? These are happening continuously, the Things are mine but there is no update in status as far as I know. I don’t see any corresponding status changes in the openhab main or event logs.


Other than that:

Self-made Homie Device → OH3 Thing → Channel mapped to item → HABapp listen_event/ValueUpdateEvent, log warning(‘update {}’.format(event.value))

…WORKS. Okay, that was easy. Very, very nice work @Spaceman_Spiff, I don’t remember JSR223/Jython being this easy to get going! :slight_smile:

Hello,
i have the same problem as described in posting 426.
I followed the instructions, but I’m not able to start HabApp.

sudo python3 -m pip install --upgrade pip
Looking in indexes: Simple index, piwheels - Simple index
Requirement already satisfied: pip in /usr/local/lib/python3.7/dist-packages (21.3.1)

But if i want do install habapp:

Command “python setup.py egg_info” failed with error code 1 in /tmp/pip-install-crbnvgbf/pendulum/

Im not a pro but I have to work with habapp for a project.
Maybe you can find the mistake.

It’s an issue with the installation of pendulum. Could you try this workaround of the workaround in the next post?

This works if you start HABApp from the console or locally on your main machine. The docker container probably doesn’t catch the process output.
I just log everything to different files with separate loggers.

You have to setup an output file for your logger.
I modified the docs so it should be more clear now.

You are worrying for nothing! :wink:
Just use the mqtt wildcards or ‘#’ if you don’t have thousands of events per sec and you will be fine.

The functions are there to dynamically change the subscriptions during rule execution. For static subscription the config file should be used.
If you have a better wording I’ll gladly use it. Any improvement of the docs is very much appreciated. Just make a suggestion and I’ll add it to the docs.

Probably things that have been added after you started HABApp. Currently openhab does not provide an event when a thing has been added/changed and I haven’t created a workaround yet.
After a restart of HABApp it should be gone.

1 Like

Hello,
thank you for your quick help.
But the workaround, doesnt work :wink:

ERROR: Failed building wheel for pendulum
Failed to build pendulum
ERROR: Could not build wheels for pendulum, which is required to install pyproject.toml-based projects

Is it possible that pendulum causes this problem?

(habapp) openhabian@openhabian:/opt $ sudo systemctl start habapp.service
Failed to start habapp.service: Unit habapp.service not found.

Thanks a whole bunch!

Clear! Works, too.

That is the best kind of worry, way less stressful than when it’s justified :smiley:

Sure! How about this:

Please note that subscriptions made this way are volatile, and will only remain until the next disconnect. HABApp is already subscribed to ‘#’ (everything) by default, so rather than making a temporary subscription, you can get any topics you need simply by using listen_event, as described in the below Example MQTT rule.

Ah! Also clear.

Thanks again!

Yes - can we please continue in the topic that I linked.
Can you there please post everything you have done and the output (properly formatted with code fences).

It’ll be included in the next release

1 Like

May I just take a moment to mention how nice it is that in HABApp, when an event callback function is called, whether in response to Update or Change, and whether you check event.value or item.get_value()… the value you get in each case is the new value. None of the aforementioned combination of conditions ever returns the previous, un-updated value… meaning simple script development is not booby-trapped with race conditions.

When you’re accustomed to OH2 JSR223 Jython scripting, you learn to appreciate the little things, and not take anything for granted.

Thank you, @Spaceman_Spiff.

3 Likes

You can also check item.value or just if item == compare_value.
item.get_value() is only there if you want to specify a default in case the item.value is None and normally not needed.

You should also check out the MultiModeItem. I makes writing overlapping states really easy and they are a very powerful tool for automation!

1 Like

Thanks for the tip! I looked at MultiModeItem now, it’s going to take a while to wrap my head around it, but it seems like it can avoid having to use proxy items for certain things. Could be useful.

Also, I’m using PyCharm as you suggested in the tutorial. Great suggestion – I just installed it, and syntax highlighting and autocomplete just works automatically. That makes an incredible difference for a novice Python programmer.

I’ve written my first script! It’s working perfectly, but it’s ugly – I’ve written it as I would have in C++, not taking advantage of anything Python since I don’t know it (yet).

One common problem in home automation is asymmetrical delays.

That is:
While X is 1, do A
While X is 0, do B, and 10 seconds after that do C, as long as X didn’t become 1 again within that time.

I did that the arduino way… that is, I literally implemented a millis function and used timestamps:

def millis():
	return int(time.monotonic_ns() / 1000000)

def __init__(self):
	self.chandelier_level = 0
	self.chandelier_on = 0
	self.chandelier_on_timeout = 30000
	self.chandelier_on_millis = millis() - 9999999
	self.chandelier_power = -1

def periodicUpdate(self):
	desired_on = 0
	if self.chandelier_level > 0:
		desired_on = 1

	if desired_on:
		self.chandelier_on = 1
		self.chandelier_on_millis = millis()
	elif millis() - self.chandelier_on_millis > self.chandelier_on_timeout:
		self.chandelier_on = 0


	if self.chandelier_power != self.chandelier_on:
		self.chandelier_power = self.chandelier_on
		self.ctrl_mcu5_elchand.oh_send_command("ON" if self.chandelier_power else "OFF")

Imagine the chandelier controller is WiFi controlled, and this power to the controller is itself connected to a WiFi relay with lots of channels that control multiple things. This saves a lot of power, compared to having everything in the house on active standby, because this way only the multichannel WiFi relay stays on.

self.chandelier_level is the desired brightness of the chandelier.

When chandelier_level is greater than zero, then obviously the power to the chandelier controller needs to be on, and we need to tell it the level to be at.

When chandelier_level becomes zero again, we tell the chandelier controller to fade out, and then we give it plenty of time to fade out before we actually cut its power.

It works fine this way, but this seems like a lot of code for what is a common home automation problem!
@Spaceman_Spiff, since you’re a native Python programmer, I’ll bet you know a much more efficient way to accomplish the same thing, making use of HABApp and Python magic? :slight_smile:


I also have a question regarding HABApp logging. With the default settings it’s logging every message on my MQTT server. I will change the level to WARNING of course, but:

  • It doesn’t seem to obey the size restriction. logging.yml specifies maxBytes: 1_048_576 and yet the events.log grows to a gigabyte before it rolls over.
  • It’s nice to have detailed logging but not if it kills my SSD – is there an easy way to keep a log ONLY in RAM?

That blows my mind a little bit. As a C++ programmer, I assumed item to be a handle or pointer.
Shouldn’t item == compare_value always return false since they’re different types? What sorcery is this? :slight_smile:

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.