Jython: how to detect state change from NULL within a received update trigger?

I wish to avoid my rules (which has trigger type of “received update”) from getting triggered when the item changed from NULL to XXX. If I could check the event.oldItemState that would solve this issue. However, event.oldItemState is only available in “changed” triggers.

My current workaround is to use “changed” trigger, but hack it in a way that it would receive update even though the updated value is the same, which normally would’ve have triggered the changed trigger. It’s an ugly hack though, and I’d rather not have to do it.


I’m still in the process of migrating my rules, so I’m by far not an expert, but this occurs to me

@when(“item myItem changed from ON”)
@when(“item myItem changed from OFF”)

Replace on and off with the appropriate states


How about using both the ItemStateUpdateTrigger and ItemStateChangeTrigger in the rule and check to see which event objects are available? Or maybe two separate rules? Your question is rather vague, so without seeing your rule, I’m just guessing. :slightly_smiling_face:

@5iver OK this issue came up in many different scenarios, but here’s one example. I have an item:

String Button_Click { channel="mqtt:topic:mosquitto:mybutton:click" }

This button click can receive an update of single, double, hold, release (via mqtt). So if I press the button it will send an update “single”. If I press it again the second time, it will send another update “single”.

So in this instance, I cannot use the trigger “changed” and need to use “received update” instead.

Hence my rule would be:

@when("Item Button_Click received update")
def handle_button(event):
  if event.itemState.toString() == 'single':
    if items.MyLight == OFF:
      sendCommand('MyLight', 'ON')
      sendCommand('MyLight', 'OFF')
  elif event.itemState.toString() == 'double':
    # do something else here

The problem is sometimes I fiddle with the .items file and cause a syntax error. When this happens, the item “Button_Click” gets wiped off from openhab, and its link to the channel is removed by openhab.

Then when I fixed the syntax error and saved the .items file, OpenHAB would load the item and re-link it with the channel.

At this point, I will see in Karaf log: Item Button_Click changed from NULL to single - because the channel still holds the value ‘single’ from the last time I pressed the button, so it sets the item to that value. This does not represent an actual button press though.

However, this does trigger my rule, as a result it thinks a button is pressed, and it will toggle the light - remember that this is caused by the reload of the item, not because the button is actually pressed.

Suffice to say that this caused havoc around the house whenever I played around with the items file past midnight.

I don’t think I can change my trigger to “Item Button_Click changed” because if I press the button again and again, the value of “Button_Click” item doesn’t change. The value remains as “single” so the changed trigger would not fire.

My current workaround is changing the trigger type to “change” instead of “received update” but then I would update the item state to a blank string, just so that I can detect the “change” if another “single” is received.

So this is my hack / workaround:

@when("Item Button_Click changed")
def handle_button(event):
  command = event.itemState.toString()
  postUpdate(event.itemName, '')
  if isinstance(event.oldItemState, UnDefType):
    return # ignore if the oldstate is null
  if command == 'single':
    if items.MyLight == OFF:
      sendCommand('MyLight', 'ON')
      sendCommand('MyLight', 'OFF')
  elif command == 'double':
    # do something else here

In this case, I suggest using persistence to check the previousState of the Item in order to ignore the trigger if it was NULL…

if PersistenceExtensions.previousState(itemRegistry.getItem("Button_Click")).state != NULL:

Which persistence backend would you recommend?

I understand that mapdb will only store the current state, so it won’t have previousState, and I can use influxdb (which I am already using for charts), but I will have to specifically set the retention for these button items to just retain 2 values at most?

I would hate to have to use mysql or something else just to solve this issue. A simple file based database (like mapdb) would be ideal.

Is there a persistence backend that would store just one previousState without having to do too much configuration?


I’m also wondering though, whether in my case above, the NULL value will be stored by the persistence, seeing that the item never actually received an update / changed to NULL. It became null simply because it was removed by openhab, and recreated, i.e. NULL (or is it supposed to be UNDEF?) is the initial state of any newly created item.

Yes, you are correct… persistence does save NULL states! Sorry for that! But you could persist the previous state in the rule…

previous_state = None

@when("Item Button_Click received update")
def handle_button(event):
  global previous_state
  if previous_state != NULL:
    if event.itemState.toString() == 'single':
      if items.MyLight == OFF:
        sendCommand('MyLight', 'ON')
        sendCommand('MyLight', 'OFF')
    elif event.itemState.toString() == 'double':
      # do something else here
  previous_state = event.itemState

In thinking about this further, I also have a rule like this for my Aeon Minimotes and I do not recall ever running into a problem like you describe… but I persist all Items on everyChange. Perhaps persisting your Items will avoid them being set to NULL. Perhaps using VS Code would catch the error before saving the .items file? :wink:

Did you mean “does NOT save NULL states”?

The problem: I don’t know if the rule gets triggered when the item is set to NULL (when the item gets nuked by openhab, or when it reappears), so the previous_state variable will probably never be NULL.

LOLOL! I ned to slow it down! Yes… it does not!

The ItemStateUpdateTrigger does trigger when an Item receives an update of NULL, so I expect this will work.

@5iver I just came across a bizarre thing, a bit unrelated to the main issue at hand.

So I have an existing item that’s linked to a thing (via openhab channel):

String Garage_Light_Button   (ItemRule) { channel="mqtt:topic:mosquitto:garage-switch:button1", autoupdate="false", ItemRule="on" [ SINGLE="(? items.Garage_Light_Power != ON ?)Garage_Light_Dimmer=100,Garage_Light_Power=TOGGLE" ] }

Then I have a rule:

@rule("Item Rule")
@when("Member of ItemRule received update")
def rule(event):
  ## blah blah

So you would expect that when the item Garage_Light_Button received update, the “Item Rule” gets triggered, right?

So far so good.

I created another item, which links to the same thing / channel

String Test1 { channel="mqtt:topic:mosquitto:garage-switch:button1" }

Notice that Test1 is not a member of “ItemRule” group!

However, I saw this in the log when Test1 changed when I commented it out and uncommented it:

02:06:29.178 [INFO ] [del.core.internal.ModelRepositoryImpl] - Refreshing model 'test.items'
02:06:29.179 [INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'Test1-mqtt:topic:mosquitto:garage-switch:button1' has been added.
02:06:29.183 [INFO ] [smarthome.event.ItemStateChangedEvent] - Test1 changed from NULL to SINGLE
02:06:29.184 [INFO ] [jsr223.jython.Item Rule              ] - Performing: 'Garage_Light_Dimmer' '=' '100'
02:06:29.186 [INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'Garage_Light_Dimmer' received command 100
02:06:29.186 [INFO ] [jsr223.jython.Item Rule              ] - Performing: 'Garage_Light_Power' '=' 'ON'
02:06:29.190 [INFO ] [arthome.event.ItemStatePredictedEvent] - Garage_Light_Dimmer predicted to become 100
02:06:29.192 [INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'Garage_Light_Power' received command ON
02:06:29.536 [INFO ] [smarthome.event.ItemStateChangedEvent] - Garage_Light_Power changed from OFF to ON

In other words, the changing value in Test1 (due to it being loaded), and despite openhab not registering any change in value in Garage_Light_Button, still triggers the rule that was only meant to trigger when Garage_Light_Button is changed.


Your callback function is named ‘rule’, which will likely collide with core.rules.rule and cause all sorts of strangeness. Try changing the identifier of the callback function…

sorry the actual callback name is “simple_rule”. I just typed “rule” as a pseudo code sorry.


Anyhow, back to the original topic,

I created

String Test1 { channel="mqtt:topic:mosquitto:garage-switch:button1" }
previous_state = None

@when("Item Test1 received update")
def test1(event):
    global previous_state
    test1.log.info("new state: {}, prev_state: {}".format(event.itemState, previous_state))
    previous_state = event.itemState

Then I commented out String Test1 ...., saved the file

02:24:54.228 [INFO ] [del.core.internal.ModelRepositoryImpl] - Refreshing model 'test.items'
02:24:54.230 [INFO ] [ome.event.ItemChannelLinkRemovedEvent] - Link 'Test1 => mqtt:topic:mosquitto:garage-switch:button1' has been removed.

Then I uncommented it and saved the file:

02:24:57.436 [INFO ] [del.core.internal.ModelRepositoryImpl] - Refreshing model 'test.items'
02:24:57.439 [INFO ] [smarthome.event.ItemStateChangedEvent] - Test1 changed from NULL to SINGLE
02:24:57.440 [INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'Test1-mqtt:topic:mosquitto:garage-switch:button1' has been added.

My rule Test11 never got triggered, which means the solution of saving the previous_state and checking it against NULL wouldn’t work.

Oops sorry, in my haste, I forgot to save the new rule file lol.

However, now that it’s saved, when the Test1 item got uncommented and saved, this is all I got:

02:28:44.273 [INFO ] [thome.event.ItemChannelLinkAddedEvent] - Link 'Test1-mqtt:topic:mosquitto:garage-switch:button1' has been added.
02:28:44.274 [INFO ] [smarthome.event.ItemStateChangedEvent] - Test1 changed from NULL to SINGLE
02:28:44.272 [INFO ] [jsr223.jython.Test11                 ] - new state: SINGLE, prev_state: SINGLE

And when it was commented out, the rule didn’t get triggered, so it never saved the value NULL