HABApp - Easy automation with openHAB

Good news everyone! HABApp 1.1.0 is out!

Because I’m still on 1.0.8 I ask here.

I want to get rid of a blown habapp events log (200 MB per day). Most of the power entries from several electrical devices like shellys are not needed to be logged.

So I tried to follow your description Advanced Usage — HABApp beta documentation and added a module HABAppUser.py in the directory openhab-conf/habapp/lib with code

import logging

_ignore_events = [ "power", "more power", "other stuff" ]

def event_not_to_be_ignored(record: logging.LogRecord) -> bool:
    for ign in _ignore_events:
        if ign in record.msg:
            return False
        
    return True


logging.getLogger('HABApp.EventBus').addFilter( event_not_to_be_ignored )

But after reboot the lines cotaining one of these _ignore_events strings are still logged.

What do I have to change?

This module gets loaded before the logging config is loaded.
I’m guessing the logging config overwrites the existing filter.

But you can define the filter in the logging.yml like this, there is no need to use the HABAppUser.

    "filters": {
        "warnings_and_below": {
            "()" : "my_module.filter_maker",
        }
    }

my_module.py:

def filter_maker():
    def event_not_to_be_ignored(record: logging.LogRecord) -> bool:
        for ign in _ignore_events:
            if ign in record.msg:
                return False
    
        return True

Thank you. I’ll give it a try

Is it working as expected?

Yes, it’s working well.

For those who are interested:

Create or use an already existing lib below habapp/lib (in my case called “logger.py”) and add a function filter factory like

def event_filter_factory():

    _ignore_events = [ "power", "more power", "other stuff" ]

    def event_not_to_be_ignored(record: logging.LogRecord) -> bool:
        for ign in _ignore_events:
            if ign in record.msg:
                return False
            
        return True
    
    return event_not_to_be_ignored

Then add the filter to logging.yml

levels:
  ...

formatters:
  ...

filters:
  event_filter:
    (): logger.event_filter_factory

handlers:
  ...
  
  EventFile:
    ..
    filters:
      - event_filter

Hey @Spaceman_Spiff

again after some time i have to come back with an question again. Remember you told me to recreate the configs by simply deleting them? I did so and in addition i had to change the HABApp_events.log and HABApp.log file from my user (pi) to the openhab user. It was working then.

So when i logged in today i did not see HABApp.service running but failed and had a look into it and saw, that it again throws an error on the events.log.

Error loading logging config: Unable to configure handler 'EventFile'

So after looking around i found, that the logging files are again reset to my user (pi). If i fix this again, with

sudo chown openhab:openhab HABApp* 

everything works fine. So i was thinking about what changed since then and there is nothing but some time in between and 2 or 3 reboots of the pi. Can you tell me now, why the log files are created with my normal user and not in the context of the openhab user?

Thanks in advance
Patrick

OLD

-rw-r--r-- 1 pi openhab 3.3M Jun 18 01:54 HABApp_events.log
-rw-r--r-- 1 pi openhab  41M Jun 17 23:59 HABApp_events.log.1
-rw-r--r-- 1 pi openhab  42M Jun 16 23:59 HABApp_events.log.2
-rw-r--r-- 1 pi openhab  41M Jun 15 23:59 HABApp_events.log.3
-rw-r--r-- 1 pi openhab  44K Jun 18 01:43 HABApp.log
-rw-r--r-- 1 pi openhab  44K Jun 11 21:46 HABApp.log.1
-rw-rw-r-- 1 openhab openhab 1.2K Jun 11 11:05 HABApp.log.2
-rw-rw-r-- 1 openhab openhab  36K Jun 10 20:58 HABApp.log.3
-rw-r--r-- 1 openhab openhab 133K Jun 21 21:05 openhab.log

NEW

-rw-r--r-- 1 openhab openhab 3.3M Jun 18 01:54 HABApp_events.log
-rw-r--r-- 1 openhab openhab  41M Jun 17 23:59 HABApp_events.log.1
-rw-r--r-- 1 openhab openhab  42M Jun 16 23:59 HABApp_events.log.2
-rw-r--r-- 1 openhab openhab  41M Jun 15 23:59 HABApp_events.log.3
-rw-r--r-- 1 openhab openhab  44K Jun 18 01:43 HABApp.log
-rw-r--r-- 1 openhab openhab  44K Jun 11 21:46 HABApp.log.1
-rw-rw-r-- 1 openhab openhab 1.2K Jun 11 11:05 HABApp.log.2
-rw-rw-r-- 1 openhab openhab  36K Jun 10 20:58 HABApp.log.3
-rw-r--r-- 1 openhab openhab 133K Jun 21 21:05 openhab.log

Are you using openHABian? If so the issue might be openHABian related.
HABApp is started as a certain user and creates the logfiles accordingly.
So I suspect this might be an issue with ZRAM restore or the HABApp user of the service is wrong

HABApp.Rules] ERROR | ValueError: Sun is always above the horizon on this day, at this location

Trying to use .on_sunrise and on_sunset
 and fair enough, where I live, at this time of year, the sun is always up, but not in the spring/autumn/winter. Should HABApp throw an “ERROR” on this? :thinking:

1 Like

That’s interesting - here you you live (just an estimate)?

I think there should be no error but HABApp should select the next day where there is a a sunrise (e.g. the first day in autumn. Is that what you would have expected, too?

1 Like

I live in northern part of Norway

Yes I agree, that would make sense :slight_smile:

Finally, after a good amount of work and hours I’ve migrated all my jython code to HabApp and just wanna share a little of my experience.
It took some time to understand how HabApp works so I migrated some of my simpler rules first just to get the feeling of it. Even though HabApp can’t use “actions” because of the Rest API limitations it was never really a problem since most of my actions are notification services like mail/pushover etc. and could easily be solved by using 3rd party libraries or just write them myself.
The biggest challenge for me was Persistence, which I wrote my own “library”, to make a simple drop-in replacement for functions like averageSince, maximumSince etc. for Influxdb (and that’s the beauty of python :wink: )
Estimate I migrated perhaps 70% of my code “as-is” and only had to rewrite the functions like getting item value etc. The rest needed some changes on a bigger level but also gave me the opportunity to optimize the code. :slight_smile:

I’m sure there are other ways to Rome but this was the easiest for me and also makes my code more inline of how I want it to work.
Huge thanks to @Spaceman_Spiff for all the effort in creating and maintaining HabApp! :smiley:

PS: The HabApp documentation is very good but if I could come with a suggestion it would’ve been nice to add more examples of using the different scheduler functions, datetime, looping groupmembers etc. :innocent:

4 Likes

Could you share your implementations of persistence? I would need to implement the delt since functionality myself.

Just to reply to me:

here is the simple and fast implementation of delta_since:

from influxdb import InfluxDBClient
import yaml
from datetime import datetime, timedelta 
import os

class InfluxDBHelper:
    
    def __init__(self, config_file=os.path.join(os.environ.get('HABAPP_CONF'), "influxdb.yml")):
        self.config = self.load_config(config_file)
        self.client = InfluxDBClient(
            host=self.config['host'], 
            port=self.config['port'], 
            username=self.config['username'], 
            password=self.config['password'], 
            database=self.config['database']
        )

    @staticmethod
    def load_config(filename):
        with open(filename, 'r') as file:
            config = yaml.safe_load(file)
            return config['influxdb']

    def get_delta_since(self, item: str, period: timedelta) -> float:

        # Convert the timedelta to an InfluxDB-compatible time string
        minutes = int(period.total_seconds() / 60)
        
        # Define the query
        query = f'SELECT last("value") - first("value") AS "delta" FROM "{item}" WHERE time >= now() - {minutes}m'

        # Execute the query
        result = self.client.query(query)

        # Process the result
        points = list(result.get_points())
        
        if not points:
            return 0
        else:
            return points[0]['delta']

Howdy! I haven’t been here for a while but I am very much still enjoying HABApp, definitely not idle. My rules folder is now 600 kilobytes :-).

Question. Is there a way to get the openhab item description rather than just the identifier?

Example: .items file definition:

Switch EM_AC_Woodshop  "Woodshop A/C"

SwitchItem.get_item(“EM_AC_Woodshop”).name == “EM_AC_Woodshop”
I would like to get the “Woodshop A/C” string for UI purposes.

I’ve looked through the HABApp openHAB item types documentation with no luck.

Wow! :+1:

Try .label

That was quick!! Thank you – that worked!

Some time I will shoot a video of my house and show you some of where all that code went :smiley:

My next question was going to be “so why didn’t I see that when I print the item object” but of course I forgot about the python dir() command.

I did see “label” in the the documentation, but misunderstood it as an “input” parameter when creating the object.

Anyway, problem solved! Thank you again.

I see how you could think that and it’s misleading. But if you look closely it’s under Variables and not in the constructor. But I agree that it’s very easy to miss.
Unfortunately there is no way to change that.
Maybe I can strip the constructor arguments - do you think this would make it more clear?

I would really love that.

@leif Do you think this is better?

I am new at HABApp. I am using this for a very performance-sensitive rule that is basically a numerical optimization algorithm. It ran great in Jython and was easy to port to HABApp. Thank you for the great extension!

I have a “beginner” question that is rather about elegance of the code. I have a rule that is supposed to run every hour twice, on 27 min and 57 min. I ended up in this:

nowDate = datetime.now()
startTime = nowDate.replace(minute=27,second=0,microsecond=0)
if (nowDate > startTime): startTime = startTime + timedelta(minutes=30)
if (nowDate > startTime): startTime = startTime + timedelta(minutes=30)
self.run.every(startTime,timedelta(minutes=30), self.execute, 'arg 1', asdf='kwarg 1')

While this is working without a problem, I somehow felt this is not so elegant as a Cron trigger is. Is there a better way to do that?