[SOLVED] How to split a string in a rule

Hi. I’m new to openhab. I have configured an MQTT feed from my emonpi to openhab and got it working OK.
What my item is returned, is a string of values like “2307,28,0,2253,241.22,300,300,300,300,300,300,1”.
I want to be able to pick out one of the values and display just that value (eg. 2253).
I was trying to do that in a rule with a string array and toString.split(",") - that seems to work OK but when I reference one of the array variables (array[0]) it says it is NULL.
Can anyone tell me how to get this to work (or perhaps a better way to achieve the same result)?

Thanks.

You could use a JavaScript transform in the MQTT binding string so the item it is bound to gets the substring directly without needing a rule. Consider the item definition:

Number SomeNumberItem "Value [%d]" { mqtt="<[broker:/my/topic:state:JS(get3rdcolumn.js)]" }

transform/get3rdcolumn.js:

(function(csv) { 
  var arr = csv.split(",");
  return arr[2];
})(input);

If the CSV string is formatted correctly, and the 3rd (at index 2) element is a valid number, the above should update the item correctly. Under OH2, make sure you have the JavaScript transformation service installed.

@watou’s solution is the best one. But to answer the direct question, you can split a String using the split method as you describe, but the Rules DSL (based on Xtend) doesn’t support array syntax. So instead of using array[0] you need to use array.get(0).

Thanks @watou and @rlkoshak for the replies.
I have the JS version working great but one more question if I may.
I have three different items which get data from the same MQTT topic. The first wants array item 0, the second wants array item 3 and the third wants array item 4.
Using the JS transform option, I have created three separate JS functions, each one returning the correct array item. Is it possible to have one JS transform function which takes an argument which is ‘which array index to return’? Not sure how to specify the JS function argument in the item definition. Thanks again.

There is no way to pass an argument to the transform, so you need the individual files as you have them.

Its easier with an EMONPI to switch to the new MQTT format that gives one topic per value, that way no translations are needed and you can set up one item for each value you are interested in.
.
See “1. New MQTT Topic Format” from the Open Energy Monitor wiki http://guide.openenergymonitor.org/technical/mqtt/

@watou I’m trying to use the same javascript transform principle with the globalcache binding

String A44VOL "A44 volume" { channel="globalcache:itachFlex:ebc4de10:sl-m1#c1-receive:JS(A44-STA-VOL.js)" }

where A44-STA-VOL is basically the same javascript as yours returning the 3rd column

(function(csv) { 
    var arr = csv.split("%2C");
    return arr[2];
  })(input);

but I get the following error message.

Any idea what might be wrong ?

2017-10-22 09:35:21.440 [ERROR] [el.item.internal.GenericItemProvider] - Binding configuration of type 'channel' of item 'A44VOL' could not be parsed correctly.
org.eclipse.smarthome.model.item.BindingConfigParseException: UID segment 'sl-m1#c1-receive' contains invalid characters. Each segment of the UID must match the pattern [A-Za-z0-9_-]*.
	at org.eclipse.smarthome.model.thing.internal.GenericItemChannelLinkProvider.createItemChannelLink(GenericItemChannelLinkProvider.java:77)[133:org.eclipse.smarthome.model.thing:0.9.0.b5]
	at org.eclipse.smarthome.model.thing.internal.GenericItemChannelLinkProvider.processBindingConfiguration(GenericItemChannelLinkProvider.java:67)[133:org.eclipse.smarthome.model.thing:0.9.0.b5]
	at org.eclipse.smarthome.model.item.internal.GenericItemProvider.internalDispatchBindings(GenericItemProvider.java:325)[123:org.eclipse.smarthome.model.item:0.9.0.b5]
	at org.eclipse.smarthome.model.item.internal.GenericItemProvider.internalDispatchBindings(GenericItemProvider.java:297)[123:org.eclipse.smarthome.model.item:0.9.0.b5]
	at org.eclipse.smarthome.model.item.internal.GenericItemProvider.processBindingConfigsFromModel(GenericItemProvider.java:182)[123:org.eclipse.smarthome.model.item:0.9.0.b5]
	at org.eclipse.smarthome.model.item.internal.GenericItemProvider.modelChanged(GenericItemProvider.java:367)[123:org.eclipse.smarthome.model.item:0.9.0.b5]
	at org.eclipse.smarthome.model.core.internal.ModelRepositoryImpl.notifyListeners(ModelRepositoryImpl.java:286)[122:org.eclipse.smarthome.model.core:0.9.0.b5]
	at org.eclipse.smarthome.model.core.internal.ModelRepositoryImpl.addOrRefreshModel(ModelRepositoryImpl.java:136)[122:org.eclipse.smarthome.model.core:0.9.0.b5]
	at org.eclipse.smarthome.model.core.internal.folder.FolderObserver.checkFile(FolderObserver.java:234)[122:org.eclipse.smarthome.model.core:0.9.0.b5]
	at org.eclipse.smarthome.model.core.internal.folder.FolderObserver.processWatchEvent(FolderObserver.java:297)[122:org.eclipse.smarthome.model.core:0.9.0.b5]
	at org.eclipse.smarthome.core.service.WatchQueueReader.run(WatchQueueReader.java:206)[98:org.eclipse.smarthome.core:0.9.0.b5]
	at java.lang.Thread.run(Thread.java:745)[:1.8.0_121]

Transforms are not generic. You can’t just apply one to any channel you want. To apply a transform like this it would need to be defined on the Thing, assuming the itachFlex binding sorts transforms. If it doesn’t, you will need to do the transform in a rule.

Thanks, that explains. Will go the rule wat then :slight_smile:

I would also like to split a string within a rule.

My Powermax binding gives a status from the ITEM here.

String Powermax_LCD “System LCD Display [%s]” (GPowerMax) {powermax=“partition_status”}

this returns a value like.

Disarmed, Ready, Violated (Motion) in Zone 2

Wanted to split this into 3 Items

String AlarmLCD1 “System LCD Line 1” (GPowerMax)
String AlarmLCD2 “System LCD Line 2” (GPowerMax)
String AlarmLCD3 “System LCD Line 3” (GPowerMax)

I have tried using the following within a rule.

val LCDLines = Powermax_LCD.state.split(",")
postUpdate(AlarmLCD1, LCDLines[0])

but this generates errors.

Thanks.

1 Like

There are no arrays in the Rules DSL. You have to use LCDLines.get(0)