Migrating my rules to Jython

Thank you! :slight_smile:

Is there any way to replicate this in Python?
Mainly the map and reduce functions. I can get the elements from a group but I don’t know if there is a similar fast way to do this, rather then iterating through the list and append states together…

var consOn = gVacConsP.members.filter[ i | i.state <= 1 ].map[ label ].reduce[ s, label | s + ", " + label]

Haven’t you read through these yet :smile:

https://openhab-scripters.github.io/openhab-helper-libraries/Guides/But%20How%20Do%20I.html#example-with-several-functions-using-group-members

Let me know if you need help with your implementation of it!

Oh, I don’t know how but I skipped through these… only saw the first ones!

Thanks anyway :slight_smile:

How’s this?

cons_on = ", ".join([item.label for item in ir.getItem("gVacConsP").members if item.state <= DecimalType(1)])

Thanks I will try this!

@rlkoshak
I want to migrate my Time Of Day rule to Python.
Right now I want the same phases and logic like the original design pattern has (I just only changed the transition hours in your example).
I found this:
https://openhab-scripters.github.io/openhab-helper-libraries/Python/Community/Mode%20(Time%20of%20Day).html

Can you give me a further explanation how can I use this?

Mode TOD is Scott’s code and it had a different over all use case. I don’t really understand how it works but I do know it does far more than TOD as it also encapsulates what to do at each mode, not just the fact that you are in a given mode.

If you look back at the TOD DP post, you will see a Python version of the coffee (I’ve updated all of my dp posts with python versions). There is also a link to a reusable version that takes advantage of Ephemeris. It’s not merged into the Helper libraries yet but you can still copy the files to your automation folder and set up your items as described in the comments.

I have some other problems now…
I’m using a customized version of the Washing Machine State Machine (which uses persistance).

Sometimes this line:

PersistenceExtensions.averageSince(ir.getItem(power_item), DateTime.now().minusMinutes(2))

Gives me error like these:

  File "/etc/openhab2/automation/lib/python/core/log.py", line 51, in wrapper

    return fn(*args, **kwargs)

  File "<script>", line 21, in update_washing_machine_state

  File "<script>", line 38, in calculate_state

ValueError: invalid literal for float: None

Why? Because it can’t query the item? What can be wrong? Sometimes it gives these error, but in overall it works as it should…

It looks like since the update it is not working most of the time. Flooding the log… It worked better before (only a few error during a 2-3 hour run). Now every 2nd or more frequently.

The problem is not with the float parsing I mean, the problem is why the averageSince gives None. Persistance works I can view charts and write states there and query. Only these rules don’t work.

There have been other reports of various persistence calls failing over the past few months. When a call can’t completed the query it the results are not usable, it returns null.

You can filter out the errors by checking that the result isn’t null (None in this case) before trying to use it/parse it. Being that I think you’ll have to put the persistence add-on into debug logging and trying to figure it a pattern to when it does and doesn’t work.

Without seeing the whole rule, it is very difficult to troubleshoot this. The Javadoc reads differently than the actual behavior. PersistenceExtensions.averageSince will return null if the Item has not changed since the time specified. This should be changed to return the Item’s state, so I’ve submitted an issue for it and I will add a PR if the maintainers agree.

This has nothing to do with the new rule engine or scripted automation.

Thanks!
Yes since then I also figured out that this is not related to the rule engine.
Now it works as it should, I don’t know what changed during a half day.
The rule is triggered by that item when it is changed, so it should change state when this function is called…

After the changed trigger, persistence may still have not updated, causing the null. I would need to see the rule.

def calculate_state(current_power, device_item, power_item, MODE_OFF_THRESHOLD, MODE_ACTIVE_THRESHOLD, MODE_STANDBY_THRESHOLD, active_mins, standby_mins):
    mode_off_value = PersistenceExtensions.averageSince(ir.getItem(power_item), DateTime.now().minusMinutes(2))
    mode_active_value = PersistenceExtensions.averageSince(ir.getItem(power_item), DateTime.now().minusMinutes(active_mins))
    mode_standby_value = PersistenceExtensions.averageSince(ir.getItem(power_item), DateTime.now().minusMinutes(standby_mins))
    if current_power <= 0:
        events.postUpdate(device_item, str(MODE_OFF))
        return MODE_OFF
    elif mode_off_value != None and mode_off_value < MODE_OFF_THRESHOLD:
        events.postUpdate(device_item, str(MODE_OFF))
        return MODE_OFF
    elif  mode_active_value != None and mode_active_value > MODE_ACTIVE_THRESHOLD:
        events.postUpdate(device_item, str(MODE_ACTIVE))
        return MODE_ACTIVE
    elif mode_standby_value != None and mode_standby_value < MODE_STANDBY_THRESHOLD:
        if items[device_item] == NULL or int(str(items[device_item])) == MODE_OFF:
            events.postUpdate(device_item, str(MODE_STANDBY))
            return MODE_STANDBY
        elif int(str(items[device_item])) == MODE_ACTIVE:
            events.postUpdate(device_item, str(MODE_FINISHED))
            return MODE_STANDBY

This is where I use this function:

@rule("Washingmachine Consumption State Machine", description="Updates the virtual Item with the current state of the Washing Machine based on current Power", tags=["energy", "admin"])
@when("Item Washing_Machine_Power changed")
def update_washing_machine_state(event):
    update_washing_machine_state.log.info("Started: Washingmachine Consumption State Machine")
    MODE_OFF_THRESHOLD = 1
    MODE_ACTIVE_THRESHOLD = 8
    MODE_STANDBY_THRESHOLD = 7.5
    active_mins = 3
    standby_mins = 4
    retval = calculate_state(float(str(event.itemState)), "Washingmachine_OpState", "Washing_Machine_Power", MODE_OFF_THRESHOLD, MODE_ACTIVE_THRESHOLD, MODE_STANDBY_THRESHOLD, active_mins, standby_mins)
    if retval is not None:
        update_washing_machine_state.log.info("Set state for Washing_Machine_OpState is " + str(retval))

That seems like a pretty important piece of information. It’s also unexpected. I would have expected the most recent value to be returned since the average of one value is that value. I’ll comment as such on your PR if the maintainers give you any pushback.

Persistence can take quite a bit of time to save the new states to the database, particularly if you are using an external database like InfluxDB. I would not be surprised, given the rules as written, that sooner if the time it calls the averageSince before the new states is done being saved. A sleep(.1) would probably be sufficient.

I will try to add a sleep there and see what happens. However now it seems that the averageSince() function works, but my overall rule doesn’t works as before…
This needs deeper inspection and maybe a much more modified code…

I have some problem still these Persistence based rules (and yes I know that might not be just for NGRE).

So for example, if I want to get an avarageSince() of an Item (based on the last 2 mins), I get None.
But the same for the last 4 min, I get the value. And yes it might be true that it is null if there is no data point since then, but that’s not true. This is a power meter which changes state almost every 2nd second…
I can’t get my head around this.
I thought first that something is causing the avarageSince to not work multiple times, since I call it 3 times now when the rule starts, but I have created a test rule for it with one call, and the same happens…

Here is my rule:

@rule("Dishwasher Consumption State Machine", description="Updates the virtual Item with the current state of the Dishwasher based on current Power", tags=["energy", "admin"])
@when("Item Dishwasher_Power changed")
def update_dishwasher_state(event):
    update_dishwasher_state.log.info("Started: Dishwasher Consumption State Machine")
    MODE_OFF_THRESHOLD = 1.0
    MODE_ACTIVE_THRESHOLD = 3.5
    MODE_STANDBY_THRESHOLD = 3.0
    active_mins = 2
    standby_mins = 3
    retval, mode_off_value, mode_active_value, mode_standby_value = calculate_state(float(str(event.itemState)), "DishWasher_OpState", "Dishwasher_Power", MODE_OFF_THRESHOLD, MODE_ACTIVE_THRESHOLD, MODE_STANDBY_THRESHOLD, active_mins, standby_mins)
    if retval is not None:
        update_dishwasher_state.log.info("Set state for Dishwasher_OpState " + str(retval) + " Off value: " + str(mode_off_value) + " Standby value: " + str(mode_standby_value) + " Active value: " + str(mode_active_value))

def calculate_state(current_power, device_item, power_item, MODE_OFF_THRESHOLD, MODE_ACTIVE_THRESHOLD, MODE_STANDBY_THRESHOLD, active_mins, standby_mins):
    mode_off_value = PersistenceExtensions.averageSince(ir.getItem(power_item), DateTime.now().minusMinutes(2))
    mode_active_value = PersistenceExtensions.averageSince(ir.getItem(power_item), DateTime.now().minusMinutes(active_mins))
    mode_standby_value = PersistenceExtensions.averageSince(ir.getItem(power_item), DateTime.now().minusMinutes(standby_mins))

    if current_power <= 0:
        events.postUpdate(device_item, str(MODE_OFF))
        return MODE_OFF, mode_off_value, mode_active_value, mode_standby_value
    elif mode_off_value != None and mode_off_value < MODE_OFF_THRESHOLD:
        events.postUpdate(device_item, str(MODE_OFF))
        return MODE_OFF, mode_off_value, mode_active_value, mode_standby_value
    elif mode_active_value != None and mode_active_value > MODE_ACTIVE_THRESHOLD:
        events.postUpdate(device_item, str(MODE_ACTIVE))
        return MODE_ACTIVE, mode_off_value, mode_active_value, mode_standby_value
    elif mode_standby_value != None and mode_standby_value < MODE_STANDBY_THRESHOLD:
        if items[device_item] == NULL or int(str(items[device_item])) == MODE_OFF:
            events.postUpdate(device_item, str(MODE_STANDBY))
            return MODE_STANDBY, mode_off_value, mode_active_value, mode_standby_value
        elif int(str(items[device_item])) == MODE_ACTIVE:
            events.postUpdate(device_item, str(MODE_FINISHED))
            return MODE_STANDBY, mode_off_value, mode_active_value, mode_standby_value
    else:
        return "NaN", mode_off_value, mode_active_value, mode_standby_value

The best I can offer is file an issue. The problem is figuring out where… My first guess is it would be a problem with the database add-on and not in the core so I would probably start there.

An interesting thing to try would be to see what data you get back when you query for data for those time periods through the REST API and see if anything comes back. You can also put the data on a chart and look to see whether data is not being saved or something.

I will do, just do some tests to give a better understanding.
It looks like <=2 minusMinutes() returns null. If I specify bigger period (3 minute is enough) it works…

What do you have your default persistence set to and what is its persistence strategy?