Concatenate inside exec command

If you can’t use single quotes ', use escape characters
.... select(.value==\"" + WebOS_state.state.toString + "\") ...

The " should embed a " character in your string
EDIT - used the right slashes now, duh

executeCommandLine("/usr/bin/curl -s http://209.12.145.41:8080/rest/items/LG_TV0_Channel | /usr/bin/jq '.stateDescription.options[] | select(.value==\"" + WebOS_state.state.toString + "\") | .label'", 1000)

Still not working…
Is there a way to see the command itself?

I modified the rule like this:

rule "Get Channel from rest/items"
when Item WebOS_state received update
then
    myText = "sudo -u openhab /usr/bin/curl -s http://209.0.0.0:8080/rest/items/LG_TV0_Channel | /usr/bin/jq ‘.stateDescription.options[] | select(.value==\"3_26_20_20_230_143_1\") | .label’"
    val newValue = executeCommandLine(myText, 1000)
    logInfo("test", "exec string = " + newValue)
    WebOS_label.postUpdate( newValue )
end

and I get this error:
19:46:58.799 [ERROR] [untime.internal.engine.RuleEngineImpl] - Rule 'Get Channel from rest/items': An error occurred during the script execution: Couldn't invoke 'assignValueTo' for feature JvmVoid: (eProxyURI: test.rules#|::0.2.0.2.0.0::0::/1)

Note that I used the actual string in the value parameter and not the item value.

EDIT: the error was because I was missing “val” in front of “myText”

I modified the rule, again.
I’m pretty sure that the command string is ok

rule "Get Channel from rest/items"
when Item WebOS_state received update
then
logInfo("test", "WebOS_state received: " + WebOS_state.state.toString)
val myText = "sudo -u openhab /usr/bin/curl -s http://209.0.0.0:8080/rest/items/LG_TV0_Channel | /usr/bin/jq '.stateDescription.options[] | select(.value==\"" 
+ WebOS_state.state.toString + "\") | .label'
val newValue = executeCommandLine(myText, 1000)
logInfo("test", "command for terminal = " + myText)  
logInfo("test", "exec string = " + newValue)
WebOS_label.postUpdate( newValue )
end

The log looks like this:

20:14:35.476 [INFO ] [g.eclipse.smarthome.model.script.test] - WebOS_state received: 3_26_20_20_230_143_1
20:14:35.890 [INFO ] [g.eclipse.smarthome.model.script.test] - command for terminal = sudo -u openhab /usr/bin/curl -s http://209.0.0.0:8080/rest/items/LG_TV0_Channel | /usr/bin/jq '.stateDescription.options[] | select(.value=="3_26_20_20_230_143_1") | .label'
20:14:35.900 [INFO ] [g.eclipse.smarthome.model.script.test] - exec string = {"link":"http://209.0.0.0:8080/rest/items/LG_TV0_Channel","state":"3_26_20_20_230_143_1","stateDescri............... and a lot of JSON

If I copy the “command for terminal” output to terminal the command is running ok.
But the exec is returning the entire JSON.

Okay, why do you expect it not to return the entire JSON?
I presume you are using the pipe | to pass the output to something else (jq?).
That works in a shell environment (the console)
It doesn’t work if there is no shell (executeCommandLine)

What are you actually trying to achieve here? It looks like something you could do in a rule.

My bad, I was thinking that the executeCommandLine runs like any command in a linux terminal.

What I’m trying to do is to get the name of the current TV channel from the webos binding.
Actually this is my problem LGWebOS Binding (for LG WebOS TVs)

The
String LG_TV0_Channel "Channel [%s]" { channel="lgwebos:WebOSTV:tv1:channel" }

Returns this
image

But I need the “label” not the “value”, that’s why I was using jq

I see.
I guess the binding populates that with “discovered” data from TV.
Yup, those Item options are not directly accessible from DSL rules.

You could re-jig your idea to use HTTP Action to grab the target Items JSON, then use a JSONPATH transform to recover the text you want, all in a DSL rule.

Alternatively, there may be a way to get direct access to Item option properties via new rule engine, let’s consult @5iver on that?

Using Jython, you can use something like this…

from core.log import logging, LOG_PREFIX#, log_traceback
LOG = logging.getLogger("{}.TEST_3".format(LOG_PREFIX))

# get a list of all state description options for an Item
LOG.warn("stateDescription.options: '{}'".format(itemRegistry.getItem("Lock_EntranceFront").stateDescription.options))

# get the state description option that matches the current state of an Item
LOG.warn("stateDescription.options that match current state: '{}'".format([option for option in itemRegistry.getItem("Lock_EntranceFront").stateDescription.options if option.value == items["Lock_EntranceFront"].toString()][0]))

# get the label of the state description option that matches the current state of an Item
LOG.warn("label: '{}'".format([option for option in itemRegistry.getItem("Lock_EntranceFront").stateDescription.options if option.value == items["Lock_EntranceFront"].toString()][0].label))
2020-10-04 15:23:49.195 [WARN ] [jython.TEST_3] - stateDescription.options: '[StateOption [value=ON, label=Locked], StateOption [value=OFF, label=Unlocked]]'
2020-10-04 15:23:49.195 [WARN ] [jython.TEST_3] - stateDescription.options that match current state: 'StateOption [value=ON, label=Locked]'
2020-10-04 15:23:49.196 [WARN ] [jython.TEST_3] - label: 'Locked'

I’ll add this to the helper library docs.

1 Like

That was the problem. After some googleing I found that passing pipe to exec is not working so I made a script:
#!/bin/bash
curl -s ‘http://209.0.0.0:8080/rest/items/LG_TV0_Channel’ | jq ‘.stateDescription.options | select(.value=="’$1’") | .label’

The rule now becomes:

rule “Get Channel from rest/items”
when Item WebOS_state received update
then
logInfo(“test”, "WebOS_state received: " + WebOS_state.state.toString)
val myText = "/etc/openhab2/scripts/webOs_channel.sh " + “"” + WebOS_state.state.toString + “"”
val newValue = executeCommandLine(myText, 6000)
logInfo(“test”, "command = " + myText)
logInfo(“test”, "exec = " + newValue)
WebOS_label.postUpdate( newValue )
end

End the log:
22:51:19.092 [INFO ] [g.eclipse.smarthome.model.script.test] - WebOS_state received: 3_3_22_22_233_153_1
22:51:19.255 [INFO ] [g.eclipse.smarthome.model.script.test] - command = /etc/openhab2/scripts/webOs_channel.sh “3_3_22_22_233_153_1”
22:51:19.263 [INFO ] [g.eclipse.smarthome.model.script.test] - exec = “22 - DIGI Sport 2 HD”

EDIT: It looks like some backslashes do not appear in the rule above

2 Likes

Thank you Scott, but I have no ideea on how to implement this :roll_eyes:

jq is notorious for problem with quote and parsing from one liners like this without a full shell. jq solves this by allowing you to define your arguments and variables before wrapping them in your query.

some examples here

you can look up the --arg and --argjson functions of jq. I’ve battled this myself in the past.

You could set up Jython and the helper libraries manually…

https://openhab-scripters.github.io/openhab-helper-libraries/index.html

… or copy an add-on to your $OPENHAB_HOME/addons/ directory…

If you have any questions, just ask!

1 Like

I begin to wonder if you can get hold of this info directly in DSL.
I don’t have a suitable Item to try it out on, with real stateDescription options … but try this and see what you get

var sd = LG_TV0_Channel.getStateDescription
logInfo("test", "object  {}", sd)
logInfo("test", "options list  {}", sd.getOptions)

I thought you were asking for an NGRE example :slightly_smiling_face:. Here is how you could get an option using the rules DSL…

rule "Test DSL rule"
when
    System started
then
    logWarn("Rules", "Test: Start")

    var current_label
    val state_description_options = Lock_EntranceFront.stateDescription.options

    state_description_options.forEach[option|
        if (option.value == Lock_EntranceFront.state.toString()) {
            current_label = option.label
        }
    ]
    logWarn("Rules", "current_label: '{}'", current_label)

    logWarn("Rules", "Test: End")
end
1 Like

It’s also possible to run it in a shell if you pass the command to a shell. For example:

    val myText = "bash -c 'sudo -u openhab /usr/bin/curl... | /usr/bin/jq \..."

Though I would still recommend using a shell script like you have done already. As mentioned, getting the quotes right and all the escaping becomes challenging. But I wanted to mention that it is possible to run commands in a shell so you can get pipes and redirects and such.

NOTE: There is no reason to sudo -u openhab, it’ll already be running as user openhab.

I don’t know how to use your example.
I tried

rule "Test DSL rule"
when
    System started
then
    logWarn("Rules", "Test: Start")

    var current_label
    val state_description_options = WebOS.stateDescription.options

    state_description_options.forEach[option|
        if (option.value == WebOS_state.state.toString()) {
            current_label = option.label
        }
    ]
    logWarn("Rules", "current_label: '{}'", current_label)

    logWarn("Rules", "Test: End")
end

where WebOS is like this, but with a lot more value/ label lines

{
  "link": "http://209.0.0.0:8080/rest/items/LG_TV0_Channel",
  "state": "3_24_57_57_212_321_1",
  "stateDescription": {
    "pattern": "%s",
    "readOnly": false,
    "options": [
      {
        "value": "3_14_56_56_103_320_1",
        "label": "56 - Travel"
      },
      {
        "value": "3_24_57_57_212_321_1",
        "label": "57 - Travel Channel HD"
      }
    ]
  },
  "commandDescription": {
    "commandOptions": [
      {
        "command": "3_14_56_56_103_320_1",
        "label": "56 - Travel"
      },
      {
        "command": "3_24_57_57_212_321_1",
        "label": "57 - Travel Channel HD"
      }
    ]
  },
  "editable": false,
  "type": "String",
  "name": "LG_TV0_Channel",
  "label": "Channel",
  "tags": [
  ],
  "groupNames": [ 
  ]
}

And WebOS_state is 3_24_57_57_212_321_1 so the output should be “57 - Travel Channel HD”

log says
14:20:14.218 [INFO ] [smarthome.event.ItemStateChangedEvent] - WebOS_state changed from 3_12_95_95_101_412_1 to 3_24_57_57_212_321_1
14:20:15.020 [WARN ] [.eclipse.smarthome.model.script.Rules] - Test: Start
14:20:15.023 [INFO ] [g.eclipse.smarthome.model.script.test] - WebOS_state received: 3_24_57_57_212_321_1
14:20:15.072 [WARN ] [.eclipse.smarthome.model.script.Rules] - current_label: '{}'
14:20:15.082 [WARN ] [.eclipse.smarthome.model.script.Rules] - Test: End

How about this?

rule "Test DSL rule"
when
    Item WebOS_state changed
then
    logWarn("Rules", "Test: Start")
    logWarn("Rules", "WebOS_state changed: {}", WebOS_state.state)

    var current_label
    val state_description_options = WebOS_state.stateDescription.options

    state_description_options.forEach[option|
        if (option.value == WebOS_state.state.toString()) {
            current_label = option.label
        }
    ]
    logWarn("Rules", "current_label: '{}'", current_label)

    logWarn("Rules", "Test: End")
end

Still not working

16:38:57.942 [WARN ] [.eclipse.smarthome.model.script.Rules] - Test: Start
16:38:57.965 [WARN ] [.eclipse.smarthome.model.script.Rules] - WebOS_state changed: 3_3_22_22_233_153_1
16:38:57.982 [WARN ] [.eclipse.smarthome.model.script.Rules] - current_label: '{}'
16:38:57.994 [WARN ] [.eclipse.smarthome.model.script.Rules] - Test: End

I don’t think you want the quotes around the curly braces in the logInfo