[JS UI rule] How to check if item state changed with "ItemStateUpdatedEvent"

  • Platform information:
    • Hardware: Raspberry Pi 4
    • OS: Raspbian Bullseye 11.8
    • Java Runtime Environment: OpenJDK (build 17.0.7+7-Raspbian-1deb11u1rpt1)
    • openHAB version: 4.1.0.M2

Context:
I have default persistence configuration in SQL database for every item on every change. But for few items I would like to store values also when the item’s value is updated (e.g. for Water meter flow rate or Rain rate)

If I extend the persistence configuration for those items then I get those item’s values stored twice (one for default strategy, second for particular item strategy). And also I get stored zero values for most of the time

So I wanted to write simple javascript rule which executes everytime the item is updated and check if the old and new values are the same and if yes then persist the item value manually.

My current simple test rule:

configuration: {}
triggers:
  - id: "3"
    configuration:
      itemName: TestNumberItem
    type: core.ItemStateUpdateTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >
        console.log("Persist unchanged values rule: eventType = %s, oldItemState
        = %s, itemState = %s", event.type, event.oldItemState,
        event.itemState);  


        if (event.oldItemState && event.oldItemState == event.itemState && Float.parseFloat(event.itemState) > 0) {
          items.getItem(event.itemName).history.persist();
        }
    type: script.ScriptAction

Console output:

 Persist unchanged values rule: eventType = ItemStateUpdatedEvent, oldItemState = undefined, itemState = 0
 Persist unchanged values rule: eventType = ItemStateUpdatedEvent, oldItemState = undefined, itemState = 2.4

With this rule the event is always of type ItemStateUpdatedEvent no matter if the state has changed or is still the same. And as it is stated in the documentation the event.oldItemState is only available when item’s value has changed.

Is there any other way how to check the previous value in ItemStateUpdateTrigger event? Or could be the event object extended with this oldItemState state also for this type of trigger?

The way you would work around that in the .persist file would be to do away with the default and configure these Items independently so they only save on updates and not changes. If you save on both you will get two entries because an update and a change are two separate events and you’ve configured both to be saved.

I don’t think it’s because the Item is configured twice in the file.

This has the potential to be a pain though so if it’s just a couple of Items a rule might be the way to go. But that’s going to be a pain there too so it might be worth while fixing this in the persistence config in the end. See Design Pattern: Group Based Persistence for one way to make this easier.

Yes, because the ItemStateChangedEvent is a different event and a different rule trigger. The variables stored in the event Object (or implicit variables in Rules DSL) are dictated by the rule trigger. You are not triggering the rule with an ItemStateChangedEvent so the rule never sees anything associated with that change. It only sees the updates.

Not really. You’ll have to get the previous state in other ways. Luckily you have a couple options:

  • pull it from persistence, though timing might become an issue
  • store the old value in the cache each time the rule triggers.

This last approach would look something like this:

var previousState = cache.private.get("prev", () => event.itemState);

if(event.itemState == previousState) {
  items[event.itemName].history.persist();
}

cache.private.put("prev", event.itemState);

This script is pretty simple and it makes sense to keep it all in the script action. But if it were more complicated, you could split the test into the script condition.

Script Condition

var previousState = cache.private.get("prev", () => event.itemState);
var isUnchanged = (event.itemState == previousState);
cache.private.put("prev", event.itemState);
isUnchanged;

And then the Script Action becomes

items[event.itemName].history.persist();

It doesn’t really make anything better here, but for more complicated rules it can cause a lot of simplification of the code.

However, given the overhead of running a rule, if this Item updates a whole lot you might need to go back to the persistence configuration approach. Just make sure these Items are only stored on updates, not updates and changes.

Thanks for your quick reply Rich.

I have this in my to-do list for some time already (+ migrate the config from .persist file to UI configuration to have it much easier to change the config). So I’m going to move that point up into higher priority.

In the meantime I use your proposal with the cache storage. And BTW thanks for pointing me to the possiblility of using Script condition, I’ve not noticed this option yet.

I have already tried to get the previous value from persistence, but that didn’t work much. Maybe it is because of timing issue as you’ve mentioned.
When I used items.getItem(event.itemName).history.previousState() I actually got the new state value. Probably because it has been meanwhile stored into database.