Is there a way to detect reload of items - changed from NULL to

Some of my code reacts on items changes triggered by a group change. When an item file is reloaded due to save all items were reinitialized

swFixedTimerSwitch01 changed from NULL to OFF
swFixedTimerSwitch02 changed from NULL to OFF

This causes some of my rule to get triggered because groups will recalculated. Is there a way to detect this in a rule?

I tried to get last state of item to detect change from NULL to …

item.historicState(now.minusMillis(100))
item.changedSince(now.minusMillis(200))

But I think this interim state NULL is not stored anywhere. Not in the item history nor in the persistance file.

At the moment my workaround is to create a switch item maintanancemode and switch it to ON manually which blocks all rules to run and after reload of item file I change it back OFF manually.

if (maintanancemode.state == ON)
 return;

rule syntax is here

https://docs.openhab.org/configuration/rules-dsl.html#implicit-variables#

previousState - will be implicitly available in every rule that has at least one status change event trigger.

This is then valid for the item which triggered the rule.

rule "Test"
    when
        Item ItemTest changed // has to be changed i think
    then 
        if (previousState == NULL) { ... }
end
1 Like

Yes, I know this, but my rule is triggered by a group change and the group itself is changed due reload of item file. The only way to fix/detect this is to check which item triggers the group change and check if this item is changed from NULL to something else. This part is what I want to ask for.

    when
        Item gContactStatus changed
    then
        val item = gContactStatus.members.filter[l | l.lastUpdate !== null].sortBy[lastUpdate].last
       // item.historicState(now.minusMillis(100) does not fix it
       // item.changedSince(now.minusMillis(200)) does not fix it
        logInfo("gContact","########## changed: "+item.name+": "+item.state.toString)

I played around a little bit and it looks that item reload is a special event which does not trigger a rule.

This rule will never triggered:

rule "detect item change from NULL"
    when
        Item swReloadItemFile changed from NULL to ON or
        Item swReloadItemFile changed from NULL to OFF
    then
        logInfo("logger","detect swReloadItemFile changed from NULL: ")
end

Log entry which does not trigger rule, I think because of item file reload and not a real change of state.

2018-02-20 12:39:36.027 [vent.ItemStateChangedEvent] - swReloadItemFile changed from NULL to OFF

Also not when restarting openhab?
Somehow this is the opposite of what you stated earlier?

Sorry for misleading you. On restart of OH it is the same, this event changed from NULL to … do not trigger any rule.

This makes sense because if it is different, a lot of rules must be rewritten.

maybe not solvable at all.

Actually, some people do report that a rule like this will trigger. It all depends on when your Rules get loaded in relation to when the restoreOnStartup initializes the Items.

Honestly, in cases like this, I will list all the contact items separately as triggers to the Rule and use triggeringItem to determine which Item triggered the Rule. Hopefully, soon we will have the ability to determine which Item in a Group triggered the Rule to trigger (it will be a new trigger type) but until that happens, in my opinion, it is worth having a slightly more involved rule trigger to avoid problems like this and avoid the persistence hack to get the Item.

I also notice that in your example code above where you try to get the Item from the Group using lastUpdate you probably need a short Thread::sleep to give persistence time to save the update to the database. Also, your persistence must be configured with everyChange or everyUpdate or else the restored state will not be saved to the DB and lastUpdate won’t work.

Hi Rich,

I think i lead you to the wrong destination. My problem ist not onsystemstartup. My problem is on reload of item file due to edition this at runtime of OH.

When I try to implement new ideas in my config I mostly have to edit item files. And this leads to reload of items and a repersistance from NULL to the value before load of item file. All is good but some of my rules are fired because I cannot detect this unnormal behaviour (reinit of items on running) of the working system.

It is not so a big thing. I tell my wife to not care about some alarm within the next two hours because of my editing of configs :wink:

But on a productive system I want to fix it with something like a maintanance switch or an auto detect.

It is solved for me by now because I implement a maintanance switch and add this to every related rule but thinking of a better way. Maybe this way does not exist.

So you said, a group Item triggers the group. But when a items file is save all items get reseted.
If you make sure that the group is in the same item file this should also be reseted and you should be able to abort with previousState as the group triggers the rule.

rule "Test"
    when
        Item GroupItemTest changed // has to be changed
    then 
        if (previousState == NULL) { return; }
end

Correct, but when you reload a .items file it is the same as when you restart OH. Items all get initialized to NULL. Then restoreOnStartup runs and the Items get populated with their last saved value. Or the Item remains NULL until the binding sets it to a new state. Given the behavior described I assumed restoreOnStartup. This is normal behavior.

Well, to me it is less work to just change the Rule trigger away from the Group and back to listing each Item.

when
    Item Contact1 changed from OPEN to CLOSED or
    Item Contact1 changed from CLOSED to OPEN or
    ...
then
    logInfo("gContact", "The triggering item is " + triggeringItem.name + " and it is now " + triggeringItem.state

Or

when
    Item MyContact1 changed or
    Item MyContact2 changed or
    ...
then
    if(previousState == NULL) return; // ignore changes from NULL to anything
    logInfo("gContact", "The triggering item is " + triggeringItem.name + " and it is now " + triggeringItem.state

To me, that is a lot less complex and a lot less work than setting up a maintenance switch or trying to detect when the .items files are reloaded or any of the many other workarounds one could think of.

The members of gContactStatus are unlikely to change very often so maintaining the list of Rule triggers will not require a lot of extra work. And the above approach will be foolproof (if you have a manual maintenance switch you may forget to flip it) and much simpler to implement than most other options I can think of.

When I save a items file where the group triggering the rule is also in the file the rule gets triggered.
If the group is not in the group file it does not trigger.

[INFO ] [marthome.model.script.Test Save Items] - Item:"GTemps" state:"UNDEF" previousState:"NULL"
1 Like

I am experiencing a similar issue to this. It seems the cause in my case is when I edited an item in my .items file and wrote a wrong syntax, the item will be unloaded from openhab, and the link to the channel is removed.

Then after I fixed the syntax error and saved the .items file, openhab will recreate the item, and re-link it to the channel. At this time, I will receive an update “item XX changed from NULL to YY” with YY being the actual value held by the thing’s channel, prior to the item deletion above.

My trigger type is “received update” so I cannot just change it to a specific “changed from X to Y” as suggested.

My current workaround, as ugly as it may be, and it’s in jython, is to change my trigger type from “received update” to “changed”. This is done purely so I have access to “event.oldItemState”.

def myrule(event):

# If the new / current state is NULL, just return
    if isinstance(event.itemState, UnDefType):
        return

###### This part is purely because I want a "received update" trigger, not "changed" trigger
    state = event.itemState.toString()
    postUpdate(event.itemName, '')
    if state == '' or event.oldItemState.toString() != '':
        return
###### end of "received update" hack 

# This is the KEY: if the oldItemState == NULL then just return
    if isinstance(event.oldItemState, UnDefType):
        return

In this case, every time I received an update, I would read the state value and then update the item’s value to a blank string. This would ensure that I would keep receiving “changed” event, even though the item received updates of the same value, e.g. “click”, “click”, “click”, which was originally caught by the “received update” trigger. This approach would not work if you do not wish to do this.

I hope there’s a better solution to this. Specifically, if “event.oldItemState” can be made available in “received update” events, then I won’t have to resort to updating the item state to an empty string.

Note that in RulesDSL this is called previousState but afaik it only applies to “changed” triggers

It’s usually better to open a new thread rather than reopening two year old threads. A lot has changed over the past two years.

When you do, please provide a little more detail about specifically what you are trying to accomplish to avoid the XY problem. You do not provide enough information to understand why you can’t use changed from as the trigger, for example.

You are correct, previousState/event.oldItemState only exists for Rules triggered by a changed trigger.

I’ve opened a new topic.

Yes, but as hinted at by Scotts’ reply, you still haven’t provided enough information.

Maybe show your full Rule as it exists now as a start.

You are still asking how to solve the problem the way you think it needs to be solved (access the previous state with an update trigger). Tell us what you are really trying to accomplish (e.g. “I have a temperature sensor that reports every five minutes that triggers a Rule. I need to process every update even when the temp is the same because … but I don’t want to process the update if the change is from NULL.”) and perhaps we can tell you a better and different overall approach.

Thanks. I will explain it again in the other topic.