Extract number from a string

I have a string with a number enclosed in square brackets that I would like to turn into a standard number so I can use it with operators like <=> . Is there an easy way to do this? The square brackets aren’t in the original JSON information and have appeared when extracting the value using
var String transformtest = (transform(“JSONPATH”, “$.result…temperature”, temperaturetest))
Using var Number doesn’t work, so I’m guessing there is another way

A quick and dirty approach could be, in your rule:

val num = Integer::parseInt(transformtest.replace('[', '').replace(']', ''))

I’m assuming the value is an integer. If it isn’t, use Double::parseDouble

Finally, I don’t think <=> is a valid operator. See the Infix Operators and Operator Overloading of the Xtend documentation for the full list of valid comparison operators.

1 Like

Thanks for the quick reply, quick and dirty is all I need in this case as all I want to do is extract a temperature from my AC JSON string to use in a “if temperature is greater than x at time y, turn AC on cooling” type rule
Is there some trick into getting the log to display a numeric variable? The designer doesn’t seem happy.
val num = Integer::parseInt(transformtest.replace(’[’, ‘’).replace(’]’, ‘’)) is OK.
logInfo(“Testing”, num) throws an expecting java.lang.string but was int error.
I have added import java.lang,Integer to the top of the rules file

You don’t need to import anything in java.lang. Those are imported automatically.

Try num.toString. If that doesn’t work try 'num + “”. One of those should work. Even better, try"The temperature is " + num` so you have some context for the otherwise disembodied number in your logs.

This is getting odd - tried all those suggestions and they didn’t work and in the log I got
[22.8] which is the output of the value before your suggestion from the previous log entry then

Error during the execution of rule ‘test’: For input string: “22.8”

which suggests its doing what I want as the brackets are gone.
So I tried posting it into a numeric item with

postUpdate(SensiboOfficeTemperature, (num))
logInfo(“Testing”, "the value is " +SensiboOfficeTemperature.state)

this gave me

Error during the execution of rule ‘test’: For input string: “22.8”

So I tried
var test = num + 1
postUpdate(SensiboOfficeTemperature, (num))
logInfo(“Testing”, "the value is " +SensiboOfficeTemperature.state)

which got me

Error during the execution of rule ‘test’: The name ‘’ cannot be resolved to an item or type.

Finally I tried

val num = Double::parseDouble(transformtest.replace(’[’, ‘’).replace(’]’, ‘’))
var test1 = num + 1
postUpdate(SensiboOfficeTemperature, (test1))
logInfo(“Testing”, "the value is " +SensiboOfficeTemperature.state)

which gave me

Error during the execution of rule ‘test’: The name ‘(,,)’ cannot be resolved to an item or type.

Could it be doing a find / replace whilst remaining a string?

Firstly postUpdate() takes two Strings so even though SensiboOfficeTemperature is a Number Item, you still need to send it a String as a value. So you aren’t actually solving anything by posting num to it. Also, I don’t know if it is guaranteed to get the new value immediately meaning that when you do the log statement you might be getting the old value.

Also, get rid of the extraneous parens in the call to postUpdate. I think they mean something in XTend and that can be causing your errors.

So lets go all the way back to the original version without posting num to some Item and trying to log that Item’s state.

Add some logging statements to figure out exactly which line the error is being generated on, the Double::parseDouble line or the log line.

Another thing you can try:

val Number num = new Number(Double::parseDouble(transformtest.replace('[','').replace(']',''))

Then at least you will be dealing with a Number. You might need to do BigDecimal instead of Number.

Then in your logStatement (and calls to postUpdate or sendCommand) you can use num.toString.

Will try that tomorrow and let you know, thanks again!

val Number num = new Number(Double::parseDouble(transformtest.replace(’[’,’’).replace(’]’,’’))
gives
Invalid number of arguments. Expected () in the designer,
changing “Number” to “BigDecimal” changes the errors to
Multiple markers at this line

  • The value of the local variable num is not used
  • Couldn’t resolve reference to JvmConstructor ‘BigDecimal’.

So after much googling and experimenting including random stack dumps that didn’t clear until I rebooted I ended up with something that works

var String test22 = (transformtest.replace(’[’, ‘’).replace(’]’, ‘’))
val Number test23 = new Double(test22)
val test24 = test23 + 1
logInfo(“Testing”, "test24 is " +test24)

I added the +1 to make sure that I was dealing with a real number rather than something that looked like it in the log.

I wonder why it didn’t like doing it in one operation?

Its missing a closing parenthesis.

It should be:

val Number num = new Number(Double::parseDouble(transformtest.replace('[','').replace(']','')))

That still gives
Multiple markers at this line

  • Cannot instantiate abstract class
  • Invalid number of arguments. Expected ()
    in the designer.
    Hovering over the new Number bit gives
    Cannot instantiate abstract class
    as a popup

OK, then that means you can’t instantiate a new Number so BigDecimal or Double would be the way to go. Which is what you did to make it work above.

val Number num = new Double(transformtest.replace(']','').replace(']',''))

Also, be careful with the extraneous parens like on your line:

It works here and it may work most places, but they actually mean something to XTend so using them willy-nilly like this could cause your trouble down the line.

Thanks for the tip ref the parens, can you answer this one so I know what to do in future?
In my rules I make use of the state of items repeatedly and put them in Parens,
is enclosing each ((item.state == OFF) && (otheritem.state == ON)) in parens incorrect?
should I use (item.state == OFF && otheritem,state == ON) instead?
My statements all work as expected so this is really a future if it doesn’t work try this question

In this particular case that is fine, though it does nothing. In the context of an if statement, adding extra parens is a way to control the order of operations. For example:

if ( foo == ON && bar == ON && boo == ON)

This will evaluate the foo == ON first, the bar == ON second (assuming foo == ON is true), and the boo == ON last (assuming that both foo and bar are equal to ON.

if ( foo == ON && (bar == ON && boo == ON) )

This will evaluate the bar == ON first, if that is true evaluate the boo == ON next. If both of those are true it will then evaluate the foo == ON.

However

if ( (foo == ON) && (bar == ON) && (boo == ON) )

Is equivalent to the first example. The extra parens add nothing functionally.

Being able to control the order of operations can be important. For example, if you want to do something like if foo is ON and bar or boo are ON you would use the parens to evaluate the || first:

if ( foo == ON && ( bar == ON || boo == ON) )

In this case the bar == ON is evaluated first, if it is true the parens clause evaluates to true. If it is false then boo == ON is evaluated. If that is true then the parens clause evaluates to true. Otherwise it evaluates to false. Whatever the parens clause evaluates to will be compared to foo == ON.

Thanks, that explains the occasional stack dump on startup. I have an item that is turned off a few minutes after startup that is checked by any rules that need things to initialise before they can work.

Leaving this here for completness.

I just now realized that I misspoke when I said that

if ( foo == ON && bar == ON && boo == ON )

is functionally equivalent to

if( (foo == ON) && (bar == ON) && (boo == ON) )

There is one important distinction. In the first one it will evaluate from left to right until the equals evaluates to false. Then it stops. For example, if foo is actually OFF, it won’t even try to evaluate bar or boo because the expression can only ever be false in this case. However, when you add the parens it will evaluate all of the == because it works from the inside to the outside (i.e. it will evaluate everything in the inner parens before evaluating stuff in outer parens).

Therefore, if foo is OFF, bar is ON and boo is null, the first one will work without any problem whereas the second one will throw a null pointer exception.

1 Like

Thanks Rich,
one further case, going back to

if (foo == ON && (bar == ON || boo == ON))

How can it be rewritten to make sure foo is always evaluated first? In my case “foo” is only ON when the system has started and finished restoring everything, if a rule depends on the system being up and in a stable state I have to use it to avoid stack dumps and other nastiness.

You would need to split this one into two separate if clauses.

if(foo == ON)
    if(bar == ON || boo == ON)

As written it gets evaluated in the following order:
First the bar is evaluated. If bar is ON the parans gets set to true and boo is never evaluated because no matter what the || will evaluate to true. If bar is OFF the boo is evaluated. If boo is ON then the parens gets set to true, otherwise the parens gets set to false. Then foo is evaluated. Finally the && gets evaluated. There is no short circuiting because the stuff in parens has already been evaluated.

If you put parens around foo, that means that foo will get evaluated first, then the bar/boo parens will be evaluated, and finally the && will get evaluated. But again, because it works from the inside out there is no chance for the && to short circuit and avoid the evaluation of the bar/boo clause.

One thing to note, in your actual rules you need to use foo.state == ON. I didn’t above because I was speaking more generally but I want to make sure you and future readers know that to compare an Item, you have to grab its state.

That’s interesting as last time I did
when Time Cron (a time)
then
if(foo.state == ON)
if((bar == ON) || (boo == ON))
it stack dumped whenever foo was OFF and Cron fired, maybe it was my extra parenthesis which I’ve now edited out.
As a workaround would this work for making foo the first item to be evaluated? My reasoning is the parens promotes foo to the same level as the others and it appears first
if((foo == ON) && (bar == ON || boo == ON)

See my paragraph that starts with "if you put parents around foo in the previous post.

Your reasoning doesn’t pan out because it is the evaluation of the && that causes the short circuit but because of the parents it is the last thing to be evaluated.

And just to be clear, when you did the separate if statements, did you use .state for the second if and did you use curly brackets?

if(foo.state ==ON) {
    if( bar.state == ON || boo.state == ON) {
        // do stuff
    }
}

And it occurs to me if you are using this as a test to see if everything has loaded before allowing rules to execute, why don’t you set the polling periods at the top of openhab.cfg so the rules files are loaded last, significantly later than everything else. I set mine to load ten seconds after items and persistence. That is how I make sure all items are defined and populated before a rule can execute.

OK, that kind of makes sense, I was thinking the parents was promoting foo into being evaluated first… The rule I had the problem with was in the order of your example above, and it only failed and stack dumped when bar was OFF (the rule was something like
when Time Cron (a time)
then
if(startedup.state == ON) && (bar.state OFF)) {
// logInfo(something useful", “another description”)
if((new1.state == ON) && (new2.state == OFF) && (heaps of other stuff)
{ do stuff }
}
If I remember correctly, and it only failed if I disabled the logInfo (as above) so nothing was happening after If statement 1 before if statement 2, AND if bar was ON

As it’s all working I’ll keep everything you said at the back of my mind in case any stack dumps reappear in future.