Expire Binding: On expiration set value to NULL or empty string

Tags: #<Tag:0x00007f6170e0bf90>

I need a String Item that erases its value after 20 seconds. I’ve used the Expire Binding and it works (sort of). If I assign a value to the String Item, after 20 seconds the value is purged.

This will set the value to UNDEF after 20 seconds.

String KeypadBuffer "[]" {expire="20s"}

I want to set the value to NULL so I tried this:

String KeypadBuffer "[]" {expire="20s,NULL"}

However, it sets KeypadBuffer to the string “NULL” and fails this test:

if (KeypadBuffer.state == NULL)
{
 // Do something
}

OK, if I can’t set it to NULL then an empty-string will also suit my needs. Unfortunately, I don’t know of a way of specifying an empty string for Expire. These examples will crash the Expire Binding:

String KeypadBuffer "[]" {expire="20s,"}
String KeypadBuffer "[]" {expire="20s,state="}

So how does one set a String Item to either NULL or empty-string upon expiration?

I see what you are trying to do. I would prefer null also, but could you not set it to a random value that would never exists under normal conditions. Something like xxx111@@@, then look for this string?

As the Item’s name suggests, it’s a buffer for keypad entries. The user presses key 3, key 5, key 0, and the values get appended to the buffer as “350”. If you stop pressing keys, the buffer purges itself after 20 seconds of inactivity.

Ideally, “purge” means setting itself to empty-string (""). I’ll accept NULL because when you restart OpenHAB, KeypadBuffer gets set to NULL and I already have code (in the rule) to detect that state and change it to empty-string. So the same code would be executed if Expire sets KeypadBuffer to NULL.

When the buffer is reset to empty-string, I can blindly append keypress values to it. Things get messier if I make Expire reset it to a non-numeric flag like “@”. Now I can no longer blindly append keypress values to the buffer. I have to test the buffer each time to ensure it does not contain the “purge flag” (@) before appending a keypress value to it. Sure, I can do that, but it’s a kluge compared to simply flushing the buffer.

I really hope there’s a way to do this neatly with Expire otherwise I’ll have to look into using a timer to purge the buffer.

Doesn’t it work to just check against UNDEF?

if (KeypadBuffer.state == UNDEF)

(Assuming you use expire=“20s”)

Could you not use a rule:

rule "Set KeypadBuffer to NULL"
when
    KeypadBuffer changed to "@"
then
    KeypadBuffer.postUpdate(NULL)
end

It puts a string UNDEF in KeypadBuffer (“UNDEF”). When I append values “1”, “2”, and “3” to KeypadBuffer it looks like this: “UNDEF123”.

It’s the same as checking for string “NULL” or “@” or any other ‘purge flag’. Ideally, the buffer should be reset to an empty-string and not a ‘purge flag’. If Expire can’t do that then I’ve chosen the wrong tool for the job and probably should investigate using a timer.

That’s feasible but straying away from the primary goal of setting the buffer to empty-string.

If Expire cannot set the value to empty-string, or NULL, then making it set the value to a “purge flag”, like “@”, which is then processed by a separate rule is a very roundabout way of resolving Expire’s limitation. In other words, if Expire can’t do the job then perhaps I should look for something else that can, like a timer.

Too bad Expire cannot set a value to empty-string or NULL. It would’ve made for an elegant solution to clearing the buffer.

I expect you’ve tried this variant? (but it hasn’t been mentioned so far)

String KeypadBuffer "[]" {expire="20s,state=NULL"}

I’m not sure that trying to set an Item to NULL state is actually a sensible thing to do in OpenHAB. As I understood the philosophy, NULL is a special state when an Item is uninitialized. No programmed action or binding (including expire) should be “allowed” to set NULL, because ‘doing something’ to the Item means it is not uninitialized anymore.

The UNDEF state is provided for that purpose. (and expire does obey that philosophy as it stands)

Having said all that, item.postUpdate(NULL) does I think work, and that’s essentially what expire binding does. The underlying issue is just the binding parsing out a string parameter instead of a state. I wonder if this would work differently with a non-string Item?

Yes. I’ve tried several ways but haven’t shared all of them here. Basically, you get string “NULL” and not NULL state like when you restart OpenHAB. Same thing happens if you try expire=“20s,UNDEF” or expire=“20s,state=UNDEF”.

To be clear, I don’t really want NULL, I want empty string. However, there appears to be no way to instruct Expire to set the value to an empty string. I settled on NULL because that’s the value KeypadBuffer is assigned after rebooting OpenHAB. My rule easily detects this state and reassigns KeypadBuffer to empty string.

        // KeypadBuffer is NULL on OpenHAB startup
        if (KeypadBuffer.state == NULL )
        {
            KeypadBuffer.postUpdate("")
        }

What works is a bandaid solution like this where it checks for NULL state and its string version.

        // KeypadBuffer is NULL on OpenHAB startup
        // KeypadBuffer is "NULL" on Expire expiration
        if (KeypadBuffer.state == NULL || KeypadBuffer.state == "NULL" )
        {
            KeypadBuffer.postUpdate("")
        }

When I see code like this it makes me wonder what I’m doing wrong with Expire. However, it appears it’s a limitation of the Expire binding in that it cannot set a String Item to NULL, UNDEF, or empty string.

I think the limitation that a binding should not set an Item to NULL is by design. (though I wouldn’t mind betting that it’s just chance that it doesn’t work in expire)

But expire can set state UNDEF, see your post 1 (it’s the default action).
If it’s not doing that for your String Item then I think that’s a bug.

Your rule inevitably has to deal with NULL in some way at startup, so dealing with similar UNDEF within an or-condition isn’t that much of hardship.

There might be weirdness here with the content of a String Item that has state UNDEF - if you are blindly appending characters to it, the first part of the operation is surely converting the current item.state to a string? In this case, that would be “UNDEF”. Giving you the result “UNDEF123” etc.

I’m guessing that you saw the same effect with NULL and had to code around it?

That still leaves the limitation that you can’t expire to an empty string, and it would be nice to do so. I guess we’ve run out of delimiters to define a target string within a parameter string.

Yes it can but, like I told Alpoy, it gets set to string UNDEF. Using expire=“20s” this test fails:

if (KeypadBuffer.state == UNDEF)

That’s what I thought but Expire proved me wrong. Using expire=“20s,NULL”, this test fails:

if (KeypadBuffer.state == NULL)

This works:

if (KeypadBuffer.state == "NULL")

Long story short is that I can’t specify an empty string for the Expire binding and NULL and UNDEF are reported as “NULL” and “UNDEF”. I’ve only tested it with a String Item so I don’t know if it behaves the same way with other Item types.

I can test for “NULL” but Expire is not as clean of a solution I had thought it would be (for this application).

After writing all of the below I did the test. This will work to set an Item to empty string

Try {expire="20s,state= "}. Note the space after the =.

You cannot send a NULL state as a command but you should be able to post it as an update.

Though there might be some logic in place that sees that it is working with a String and it always converts it to a String.

For the empty string one of these two might work

{expire="20s,state=\"\""}
{expire="20s,state=''"}

Or you can change your test in the Rule to if(KeypadBuffer.state == UNDEF) perhaps.

Why can’t you use changing it to “@” as a flag to reset the Item to “” just like you are with NULL now?

Or submit an issue to ask for it to be added. Or even better add this support yourself and submit a PR if you are able.

I must be missing something because if you have to check for NULL anyway I fail to see how adding checks for “NULL” or “@” to the same place is any different beyond adding a couple of || clauses to the if you already have in place.

This is something I have a pet peeve with. I get annoyed when people ask for help but insist that the solution must work they way they have preconceived it to work. Your primary goal is to be able to process key presses from a keypad. Vincent’s approach only strays from the way you have pre-decided to reach that goal. You may have reasons that have not yet been communicated but so far the only reason I can discern is because you don’t want to.

It’s like asking for a hammer to drive in a screw and rejecting anyone who suggests using a screw driver. The simile is a bit extreme for this case but not in many many others who ask for help on this forum. It’s annoying and feels like a waste of time.

Unless using persistence with restoreOnStartup. Though even there, as long as we have that timing bug Rules should still deal with NULL states.

After all of this, there are at least two issues here, one probably a bug and the other probably a feature request (assuming my escaped expire strings above do not work to set it to empty string).

  1. By default Expire binding should set String Items to UNDEF by default, not “UNDEF”. The NULL issue is trickier per rossko’s discussion of the purpose of NULL. It may not be appropriate for Expire to ever set an Item to NULL.

  2. We should be able to use Expire to go to an empty String.

After writing all of the below I did the test. This will work to set an Item to empty string
Try {expire="20s,state= "}. Note the space after the =.

Bingo! The one variation I failed to try because I thought it would simply send a space character. It indeed sets the value to empty string thereby properly flushing the string buffer.

Why can’t you use changing it to “@” as a flag to reset the Item to “” just like you are with NULL now?

Because it’s a kluge. You purge a buffer by emptying it, not by adding a ‘purge flag’ so you can empty it in a separate operation (not unless you’re forced to and thankfully you’ve shown it’s not necessary in this case).

…if you have to check for NULL anyway I fail to see how adding checks for “NULL” or “@” to the same place is any different

  • OpenHAB sets KeypadBuffer to NULL on startup. For my purposes, I’d prefer KeypadBuffer was empty - string on startup. I can’t control that so I test it for NULL.

  • I do have control over the value Expire uses and so the first choice is empty string. Second choice is NULL because I must test for it. Last choice is a ‘purge flag’.

If you were tasked to create a Design Pattern for flushing a string buffer, would your first choice be to empty it (zero-length string) or stick in a NULL or @ or some other flag to indicate the buffer needs to be emptied by a separate operation?

It’s like asking for a hammer to drive in a screw and rejecting anyone who suggests using a screw driver.

Actually, I needed a Phillips screw driver for the job. In a pinch, maybe a narrow flat screw driver. I already knew I could bash the screw in with a hammer (and many hammers were offered). You found the Phillips screw driver and I thank you for it.

I didn’t start this thread to be obstinate but to understand why things behave the way they do. Others who may wish to have Expire set an empty string now know how to do it. They’ll also know that Expire’s NULL is “NULL” and UNDEF is “UNDEF”. So, far from being a waste of time it’s potentially a time saver for others. Had I simply conceded to using a hammer, someone else would’ve puzzled over why this screw won’t turn.

1 Like

Now, that is weird. I’ve just tried this on OH 2.3.0 , a String Item with default expire action.

     String testString "stringy" {expire="20s"}

For me, it gets set to state UNDEF, and I can test it as expected with

    if (testString.state == UNDEF) {
		logInfo("test", "state really UNDEF")
    )

I’m satisfied there is no bug in expire as regards default action.

Out of curiousity, I tried

	testString.postUpdate(testString.state + "xx")

and when truly UNDEF state, this updates to string “UNDEFxx” i.e. there is an implicit .toString at work in the character appending in that case.

Yes. Also when I tried {expire=“20s,NULL”} then this would fail (because it’s actually “NULL”):

if (testString.state == NULL) 

In my view, that is working correctly, exactly as it would with say {expire=“20s, BANANAS ”}

Is the default action UNDEF not working for you ? (when no expire action is specified)
A test if (item.state == UNDEF) should work for you. It does for me.
EDIT - well you don’t care anymore, with a working empty-string solution. :slight_smile: But I mainatin expire’s UNDEF default action is working perfectly even on a String type Item.

I fear there is some confusion here - a String Item with UNDEF state is not the same as an empty string … because it’s undefined.
But if you try to handle it’s state as a string anyway … you may get the appearance that it is “UNDEF” because .state.toString (for example) correctly yields “UNDEF” and some rules code will automatically do its best to convert a state to a string…

You can if you use persistence with the restoreOnStartup you can prevent this most of the time. There will be some rare cases where the startup timing bug hits and your Rule starts running before persistence has restored the Items. And of course if you purge persistence for some reason (starting over, migrating and forgot to move the persistence, moving to a new persistence) then there won’t be a value to restore. So I wouldn’t advice removing the check for NULL.

My first choice would be driven by the specific requirements I’m writing against coupled with the limitations of the Rules DSL and utilities available.

Assuming Expire were not able to set the empty String, since I have to check for NULL anyway, I’d probably check for a reset flag there as well as it only requires adding a few dozen more characters to an if statement I need to have anyway and it doesn’t involve any other complications like Timers or a separate Rule.

And to be perfectly honest, I would probably step back and re-imagine the problem so I don’t need to clear the String in the first place. There is nothing about the ultimate goal of processing keypad events that absolutely requires the String buffer to be cleared after 20 seconds. There are other approaches which may avoid this problem entirely.

For String Items. I don’t think we know at this time what it does for all the other Item types. I can confirm that for Number and DatsTime Items it is UNDEF.

OK, that is what I would expect the behavior to be. I didn’t test that but I know it works the same with Number and DateTime Items too.

This probably goes back to rossko57’s post above. We really shouldn’t be using NULL in this way in the first place. NULL indicates uninitialized, UNDEF indicates no usable value. The expire binding should not be issuing NULL in the first place.

You can if you use persistence with the restoreOnStartup you can prevent this most of the time.

It’s the “most of the time” part that obliges me to still test for NULL. Anyway, for the limited way I’m using OpenHAB, I don’t need persistence on startup. Items get initialized another way.

My first choice would be driven by the specific requirements I’m writing against coupled with the limitations of the Rules DSL and utilities available.

Me too. I needed to flush a string buffer on timeout. It seemed odd that Expire couldn’t set empty string but, hey, I’m an OpenHAB neophyte so maybe I picked the wrong tool for the job. Fortunately, I bet on the right horse, and Expire can indeed set empty string.

And to be perfectly honest, I would probably step back and re-imagine the problem so I don’t need to clear the String in the first place. There is nothing about the ultimate goal of processing keypad events that absolutely requires the String buffer to be cleared after 20 seconds. There are other approaches which may avoid this problem entirely.

You’re a funny guy. You begin with “My first choice would be driven by the specific requirements I’m writing against” and then in the same breath contradict yourself with “There is nothing about the ultimate goal of processing keypad events that absolutely requires the String buffer to be cleared after 20 seconds.” What do you know about the “specific requirements” of my application beyond the word “keypad”? :slight_smile:

Which is why I didn’t offer an alternative approach.

But you asked

If you were tasked to create a Design Pattern for flushing a string buffer, would your first choice be to empty it (zero-length string) or stick in a NULL or @ or some other flag to indicate the buffer needs to be emptied by a separate operation?

That that is my answer. If I were to write a DP and I ran into a limitation with the Expire binding clearing the buffer, I’d look for, or at least consider and probably document a second option that didn’t require clearing the buffer at all.

Clearing the buffer after a certain amount of time isn’t a requirement. It is a solution. The requirement is to process keypad events. Clearing the buffer after 20s is one approach among many to achieve that.

Can you please share how you implemented keypad buffer filling. Sitemap, items, … , rules.

I need to do something simillar for my alarm system.

Thanks in advance.