HABApp 1.1

i am pretty sure what you want like in dsl it was the received command is in habapp the event.value

for example if you define trigger

OpenhabItem.get_item(your_item).listen_event(self.to_call, EventFilter(ItemCommandEvent))

than you will receive there

    def to_call(self, event):
        '''
        called when "your_item" recieves a command
        '''
        item_name = event.name
        item_value = event.value

in event.value the recieved command that caused the trigger and in event.name the name of the item (good if more items trigger the same rule).

but you have to pay attention with

OpenhabItem.get_item('your_item').value

because it takes some time until the item recieved the new value and it could report (still) the old value

1 Like

Instead of EventFilter(ItemCommandEvent)) you can also use the ItemCommandEventFilter().
The event.value holds the command.

Can you post an example because I’m saying otherwise. :wink:

Just some small note:
The item always holds the correct value. If you send a command to the item the value has not changed yet (because you just sent the command). So the “old” value is still correct.
Once the device changes it will issue an ItemStateUpdatedEvent which holds the new value and when the event happens the item and the HABApp item will already have the correct state.

1 Like

yes, thats what i wanted to say but you explained it much better, thank you :slightly_smiling_face:

to be honest i dont understand the difference between the two variants. is there a use case when i should use one or when the other or can i just edit all my code to the second variant that is easier to read?

They are absolutely equivalent

Yes - I implemented the most used filters that way so it’s more convenient to use them.
Also you get type hints if you want to filter the values even further that way.

1 Like

I stand corrected! I was recalling this post and incorrectly thought you were talking about ItemCommandEvent().

Here is my code in full:

class System_Volume(HABApp.Rule):
    # --- Member of System_Volume Received Command ---
    def __init__(self):
        super().__init__()

        self.items = self.get_items(groups='System_Volume')         # Get all group items
        # Listen to each item for received command events using a for loop
        for i in self.items: i.listen_event(self.System_Volume, ItemCommandEventFilter())   # Command
        log.info(f'RULE LOADED: {self}')  
         
    def System_Volume(self, event): 
        # Rule triggered by a member of System_Volume receiving a command

        tName  = event.name                     # Get the triggering item name
        tItem  = OpenhabItem.get_item(tName)    # Get the triggering OpenHAB item object    
        tType  = type(tItem).__name__           # Get the triggering item type
        tState = event.value                    # Get the triggering item state (value)
        if tState == None: return               # Return if the item's state is reset to NULL

        log.info(f'RULE FIRED: {tItem.name} ({tItem.label}) --> {tItem.value}')

Well, in working through this, I’ve figured out my problem… event.value does indeed do as you say. The problem is that in my log.info above I incorrectly left tItem.value.

However, I’ve uncovered another issue which I’m sure has to do with something I’m doing wrong. With the above code (I think it’s this rule causing it, but may be something deeper), if I very rapidly turn a switch in the “System_Volume” group on and off, I get the following error:

Exception ignored When destroying _lsprof profiler:
Traceback (most recent call last):
  File "C:\opt\habapp\Lib\site-packages\HABApp\core\internals\wrapped_function\wrapped_thread.py", line 91, in run
    pr.enable()
RuntimeError: Cannot install a profile function while another profile function is being installed

I’d appreciate any thoughts on things I’m not doing most efficiently in my rule code above, from your perspective, and any thoughts on this error, as I’m about ready to apply this code to thousands of lines of jython rules! Thank you again!

Why do you

  • Not use the GroupItem to get all members?
  • Use OpenhabItem in the event block instead of the proper item type (e.g. NumberItem)
  • Use the item registry to get the item instead of a local dict in the __init__ block for the items that you are using?
  • Try getting the event type - it’s always ItemCommandEvent
  • Don’t use a type hint for event?
  • Don’t use newlines after if and for

Can you create a github issue with a short snippet from the event log and the complete error message please?

[why do you] Not use the GroupItem to get all members?

Is this what you mean:

    def __init__(self):
        super().__init__()
        self.group = GroupItem.get_item('System_Volume')
        for i in self.group.members: 
            i.listen_event(self.System_Volume, ItemCommandEventFilter())   # Received Command
        log.info(f'RULE LOADED: {self}')

[why do you] Use OpenhabItem in the event block instead of the proper item type (e.g. NumberItem)

I didn’t post the full rule. The Group has more than one type of item in it, so it would fail if I used a specific type. Below the log.info I go into several if/then’s of if isinstance(tItem, DimmerItem) etc… What is the advantage of doing this other than I can then use the type specific helpers like tItem.on?

[why do you] Use the item registry to get the item instead of a local dict in the __init__ block for the items that you are using?

So, the code I posted just above, using GroupItem.get_item() works, but how do I create the ‘dict’ you speak of that I can then access the item object from my event block using event.name? What’s the advantage? Is using a local dict faster than using the registry?

I’ve tried: item = self.group.members.get_item(event.name) in the event block, along with a few other things, but they doesn’t work. Or, asked another way, how do I get an “get an item” from “self.group.members” based on the item’s name? Seems like there would be a built-in way to do this?

[why do you] Try getting the event type - it’s always ItemCommandEvent

Actually that gets the triggering item type, not the event type (DimmerItem, SwitchItem, etc). I previously used that to get the item’s type, but now I’m using the isinstance() method to do the same because it seemed like that’s what people were doing, and that there wasn’t a really nice looking way of getting the item’s type like there was in jython or rules dsl like item.type.

[why you] Don’t use a type hint for event?

What is the advantage with doing this? Does it affect the code somehow or just the IDE experience. I have never done anything type hints in my code before, and from reading it seems like something to do with PyCharm. I’m a cheap ba$%^$# and saw that it only had a 30 day trial period so I’m just sticking with VS code. Is there a way I can get type hints in VS code? Should I stop being cheap and buy PyCharm?

[why you] Don’t use newlines after if and for

Well, because if I can get it on one line, sort of like a list comprehension, in a manageable width, that’s what I do. Other than Basic in Highschool and Ada95 (yes) in my freshman year of college, I have no code training. I’ll stop doing it if you tell me I should!

Can you create a github issue with a short snippet from the event log and the complete error message please?

YES!

Yes - exactly.

Then it makes sense

I mean put the used items in a local dicts so you have them handy and can look them up in the event handler

It’s just for IDE but it will help you catch errors.
Imho you just have to install HABApp in the venv your IDE is using and you will get the type hints automatically.

If it would be any other language I wouldn’t mind but in python nobody does this and it’s very confusing to read. If you use a list comprehension a oneliner is indeed fine.

Wow! Thank you, all is updated and working great. Additionally, I can no longer get the error above

no matter how fast I try to switch on/off a switch item. I am wondering if it was due to me accessing the registry so rapidly, which is no longer happening since I’m just accessing my local dict? If you’d still like me to make the github issue, I can, I’ll just go back to my old code and see if I can make it happen. let me know.

Here is my final code for this rule, with the whole rule included:

# ------------------------ RULE ------------------------  
class System_Volume(HABApp.Rule):
    # --- Member of System_Volume Received Command ---
    def __init__(self):
        super().__init__()

        self.gItems = {}                                    # Init a class dict 
        self.group = GroupItem.get_item('System_Volume')    # Get the group
        # Listen to each item for received command events using a for loop
        for i in self.group.members:                
            self.gItems[i.name] = i                         # Add this item to the dict
            i.listen_event(self.System_Volume, ItemCommandEventFilter())   # Received Command
        log.info(f'RULE LOADED: {self}')  
         
    def System_Volume(self, event): 
        # Rule triggered by a member of System_Volume receiving a command

        item = self.gItems[event.name]              # Get the item from the dict    
        # --- Use 'event.value' for 'receivedCommand' rules ---
        # --- Use 'item.value'  for 'changed' or 'updated' rules ---

        log.info(f'RULE FIRED: {item.name} ({item.label}) --> {event.value}')

        output = item.split('_')[0]

        if isinstance(item, DimmerItem):           # If this is a VOLUME item (dimmer)
            vol = xyz # Do some external stuff with 'output' to get 'vol'
            item.oh_post_update(vol)

        elif isinstance(item, SwitchItem):         # If this is a MUTE item (switch)
            mute = xyz # Do some external stuff with 'output' to get 'mute'
            item.oh_post_update(mute)

I truncated the if/elif code to make it more readable.

Just some more hints:
You can get items by naming structure (e.g. self.get_items('_Volume') will return all items with “_Volume” in them. That way you don’t have to use groups.
Another option is to create one rule per item and pass the item name into it.
That’s something I’d prefer. That way you can create two rule classes, one SystemVolumeDimmerRule and one SystemVolumeSwitchRule which should simplify the logic even more.

1 Like

Sebastian,

Thanks for the time taken in your replies. I will try to implement all of your suggestions. Some of them will take some time. My item naming convention is not quite ready for using naming structure to help with the event handling, but I’m going to work on it because I like that simplicity, and organization.

Have a great weekend!
Phil