[SOLVED] Using JSONPATH inside a rule

  • Platform information:
    • Hardware: Pi4
    • OS: Raspbian buster
    • Java Runtime Environment:OpenJDK Zulu 8.25
    • openHAB version: 2.5.1
  • Issue of the topic:JSONPATH transformation behaving as if it not installed when used inside a rule
  • Please post configurations (if applicable):
    • Items configuration related to the issue
      STUCK A TRIPLE BACKTICK here to force raw format from now on !!!
      Clicking on </> has some weird effects. If anyone knows how to get rid of invisible formatting in the editor I would love to know.

Switch sonoff_one "Lounge Light" <switch> (gLounge) [ "Lighting" ]
String sonoff_one_startup "Startup"
String sonoff_one_pulse "Pulse"
Number sonoff_one_pulseWidth 
String sonoff_one_ssid "SSID"
String sonoff_one_otaUnlock "OTA Lock"

  * Sitemap configuration related to the issue
    sitemap sonoff label="sonoff_one" {
  Frame label="Lounge" {
    Switch item=sonoff_one icon="light"
    Switch item=sonoff_one_startup label="Power on State" icon="none" mappings=[on="ON",off="OFF",stay="KEEP"]
    Switch item=sonoff_one_pulse icon="none" label="Pulse/ Run timer enable" mappings=[on="ON",off="OFF"]
    Setpoint item=sonoff_one_pulseWidth icon="time" label="Pulse time max 1 hr [JS(msToTime.js):%s]" minValue=1000 maxValue=36000000 step=1000 
    Text item=sonoff_one_ssid icon="none" label="SSID [%s]"
    Text item=sonoff_one_otaUnlock icon="none" label="OTA Unlock [%s]"
  }

}
 
  * Rules code related to the issue
rule "Sonoff_one Polling"

  when
    Time cron "0/3 * * * * ?" // Run every 3 seconds
  then
  
  try {
    var CommandURL = 'http://' + Ip +':8081/zeroconf/info'
    var String CommandData = '{"deviceid": "'+ Deviceid +'", "data": {}}'
    var String UpdateResult
    var Number Attempts = 0
    var CommandExec = 'curl@@-sSH@@"Accept: */*"@@-H@@"Content-Type: text/plain"@@-X@@POST@@-d@@' + CommandData + '@@' + CommandURL
    var String JSONdata
    var String update_sswitch
    var String update_startup
    var String update_pulse
    var String update_pulseWidth
    var String update_ssid
    var String update_otaUnlock


    do {
      Attempts += 1
      UpdateResult =  "[" + executeCommandLine(CommandExec, 1500) + "]"
      logInfo("Sonoff Poll UpdateResult", UpdateResult)

      if (UpdateResult.contains('"error":0')) { 
        JSONdata = transform("JSONPATH", "$.data", UpdateResult)      
        logInfo("Sonoff Poll JSONdata", JSONdata)

        if (JSONdata != Last_update) {
          update_sswitch = transform("JSONPATH", "$.switch", JSONdata)
          update_startup = transform("JSONPATH", "$.startup", JSONdata)
          update_pulse = transform("JSONPATH", "$.pulse", JSONdata)
          update_pulseWidth = transform("JSONPATH", "$.pulseWidth", JSONdata)
          update_ssid = transform("JSONPATH", "$.ssid", JSONdata)
          update_otaUnlock = transform("JSONPATH", "$.otaUnlock", JSONdata)

          if (update_sswitch != sonoff.state.toString.toLowerCase) {
            logInfo("sonoff switch", " Syncing state to " + update_sswitch)
            sonoff.postUpdate(update_sswitch.toString.toUpperCase)
          }

          if (update_startup != startup.state.toString) {
            logInfo("sonoff switch", " Syncing startup to " + update_startup)
            startup.postUpdate(update_startup.toString)
          }

          if (update_pulse != pulse.state.toString) {
            logInfo("sonoff switch", " Syncing pulse to " + update_pulse )
            pulse.postUpdate(update_pulse.toString)
          }
 
          if (update_pulseWidth != pulseWidth.state.toString) {
            logInfo("sonoff switch", " Syncing pulseWidth to " + update_pulseWidth + "ms")
            pulseWidth.postUpdate(update_pulseWidth.toString)
          }

          if (update_ssid != ssid.state.toString) {
            logInfo("sonoff switch", " Syncing ssid")
            ssid.postUpdate(update_ssid.toString)
          }
 
          if (update_otaUnlock != otaUnlock.state.toString) {
            logInfo("sonoff switch", " Syncing otaUnlock")
            otaUnlock.postUpdate(update_otaUnlock.toString)
          }

          Last_update = JSONdata
        }     
      }    
    } while (!UpdateResult.contains('"error":0') && Attempts < 2) // Attempt to send 2 times

    if (Attempts == 2) {
      logError("Sonoff Poll Switch", "2 failures polling sonoff_one ")
    }
 
    if (UpdateResult.contains('"error":400')) {
      logError("Sonoff ID:"+ Deviceid +" Error", "The operation failed and the request was formatted incorrectly. The request body is not a valid JSON format")
    } else if  (UpdateResult.contains('"error":401')) {
      logError("Sonoff Error", "The operation failed and the request was unauthorized. Device information encryption is enabled on the device, but the request is not encrypted")
    } else if (UpdateResult.contains('"error":404')) {
      logError("Sonoff Error", "The operation failed and the device does not exist. The device does not support the requested deviceid")
    } else if (UpdateResult.contains('"error":422')) {
      logError("Sonoff Error", "The operation failed and the request parameters are invalid. For example, the device does not support setting specific device information")
    }
  }
  catch(Exception e){
    logError("Polling", "Error occured in Sonoff Poling Rule! " + e.toString)
  }


Here is a an extract of the log.
    2020-02-06 20:30:34.593 [INFO ] [internal.service.FeaturesServiceImpl] - Installing bundles:
2020-02-06 20:30:34.595 [INFO ] [internal.service.FeaturesServiceImpl] -   mvn:org.openhab.addons.bundles/org.openhab.transform.jsonpath/[2.5.0,2.6)
2020-02-06 20:30:35.232 [INFO ] [internal.service.FeaturesServiceImpl] -   mvn:org.openhab.addons.bundles/org.openhab.transform.map/[2.5.0,2.6)
2020-02-06 20:30:35.334 [INFO ] [internal.service.FeaturesServiceImpl] - Starting bundles:
2020-02-06 20:30:35.337 [INFO ] [internal.service.FeaturesServiceImpl] -   org.openhab.transform.map/2.5.1
2020-02-06 20:30:35.365 [INFO ] [internal.service.FeaturesServiceImpl] -   org.openhab.transform.jsonpath/2.5.1
2020-02-06 20:30:35.397 [INFO ] [internal.service.FeaturesServiceImpl] -   org.openhab.core.voice/2.5.0
2020-02-06 20:30:35.465 [INFO ] [internal.service.FeaturesServiceImpl] -   org.openhab.core.transform/2.5.0
2020-02-06 20:30:35.472 [INFO ] [internal.service.FeaturesServiceImpl] -   org.threeten.extra/1.4.0
2020-02-06 20:30:35.478 [INFO ] [internal.service.FeaturesServiceImpl] -   org.openhab.core.ui/2.5.0



2020-02-06 20:30:45.427 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'sonoff_one.rules'
2020-02-06 20:30:45.566 [INFO ] [org.quartz.core.QuartzScheduler     ] - JobFactory set to: org.eclipse.smarthome.model.rule.runtime.internal.engine.GuiceAwareJobFactory@1aba61a
2020-02-06 20:30:45.572 [INFO ] [org.quartz.core.QuartzScheduler     ] - Scheduler openHAB-job-scheduler_$_NON_CLUSTERED paused.
2020-02-06 20:30:48.180 [INFO ] [vice.internal.HttpServiceFactoryImpl] - Binding bundle: [org.openhab.core.boot_2.5.0 [137]] to http service
2020-02-06 20:30:48.189 [INFO ] [rg.ops4j.pax.web.utils.ClassPathUtil] - Ignoring bundle scan for /META-INF/services javax.servlet.ServletContainerInitializer.
2020-02-06 20:30:48.191 [INFO ] [ce.jetty.internal.HttpServiceContext] - registering context DefaultHttpContext [bundle=org.openhab.core.boot_2.5.0 [137], contextID=default], with context-name: 
2020-02-06 20:30:48.193 [INFO ] [ce.jetty.internal.HttpServiceContext] - registering JasperInitializer
2020-02-06 20:30:48.257 [INFO ] [.jetty.server.handler.ContextHandler] - Started HttpServiceContext{httpContext=DefaultHttpContext [bundle=org.openhab.core.boot_2.5.0 [137], contextID=default]}
2020-02-06 20:30:52.788 [INFO ] [org.quartz.core.QuartzScheduler     ] - Scheduler openHAB-job-scheduler_$_NON_CLUSTERED started.
2020-02-06 20:30:54.205 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.205 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [curl: (56) Recv failure: Connection reset by peer]
2020-02-06 20:30:54.205 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.281 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.336 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.336 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.336 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.351 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing state to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.357 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing state to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.360 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert '[{"SEQ":2,"ERROR":0,"DATA":"{\"SWITCH\":\"OFF\",\"STARTUP\":\"OFF\",\"PULSE\":\"OFF\",\"PULSEWIDTH\":500,\"SSID\":\"PEGLEGPETE\",\"OTAUNLOCK\":FALSE}"}]' to a state type which item 'sonoff_one' accepts: [OnOffType, UnDefType].
2020-02-06 20:30:54.360 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert '[{"SEQ":2,"ERROR":0,"DATA":"{\"SWITCH\":\"OFF\",\"STARTUP\":\"OFF\",\"PULSE\":\"OFF\",\"PULSEWIDTH\":500,\"SSID\":\"PEGLEGPETE\",\"OTAUNLOCK\":FALSE}"}]' to a state type which item 'sonoff_one' accepts: [OnOffType, UnDefType].
2020-02-06 20:30:54.364 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing state to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.364 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing startup to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.366 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing startup to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.367 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert '[{"SEQ":2,"ERROR":0,"DATA":"{\"SWITCH\":\"OFF\",\"STARTUP\":\"OFF\",\"PULSE\":\"OFF\",\"PULSEWIDTH\":500,\"SSID\":\"PEGLEGPETE\",\"OTAUNLOCK\":FALSE}"}]' to a state type which item 'sonoff_one' accepts: [OnOffType, UnDefType].
2020-02-06 20:30:54.370 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing startup to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.373 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing pulse to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.376 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing pulse to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.380 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing pulseWidth to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]ms
2020-02-06 20:30:54.381 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing pulseWidth to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]ms
2020-02-06 20:30:54.398 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing pulse to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:54.428 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing pulseWidth to [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]ms
2020-02-06 20:30:54.455 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert '[{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]' to a state type which item 'sonoff_one_pulseWidth' accepts: [DecimalType, QuantityType, UnDefType].
2020-02-06 20:30:54.455 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert '[{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]' to a state type which item 'sonoff_one_pulseWidth' accepts: [DecimalType, QuantityType, UnDefType].
2020-02-06 20:30:54.455 [WARN ] [rthome.model.script.actions.BusEvent] - Cannot convert '[{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]' to a state type which item 'sonoff_one_pulseWidth' accepts: [DecimalType, QuantityType, UnDefType].
2020-02-06 20:30:54.459 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing ssid
2020-02-06 20:30:54.459 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing ssid
2020-02-06 20:30:54.459 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing ssid
2020-02-06 20:30:54.465 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing otaUnlock
2020-02-06 20:30:54.465 [INFO ] [smarthome.model.script.sonoff switch] -  Syncing otaUnlock
2020-02-06 20:30:54.471 [ERROR] [home.model.script.Sonoff Poll Switch] - 2 failures polling sonoff_one 
2020-02-06 20:30:57.121 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:30:57.126 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:00.118 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:00.121 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:03.200 [INFO ] [af.deployer.kar.KarArtifactInstaller] - Found a .kar file to deploy.
2020-02-06 20:31:03.203 [INFO ] [af.deployer.kar.KarArtifactInstaller] - KAR openhab-addons-2.5.1.kar is already installed. Please uninstall it first.
2020-02-06 20:31:03.881 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:03.887 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:06.119 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:06.138 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:09.132 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:09.137 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:12.118 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:12.123 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:15.377 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]
2020-02-06 20:31:15.380 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]

The JSONPATH transforms behave as though it has not been installed, or the JSON is malformed.

What am I doing wrong? What have I missed?

All help gratefully received.

R.

It is unnecesary to poll the tasmotised sonoff with an http call every 3 seconds.
Change the reporting time on the sonoff to 3 seconds
And use the MQTT binding to get your values. You can do the transformation in the thing’s channel directly.

I know it is not solving your current problem but I believe you have wrong approach to getting the data you want.

1 Like

I noticed that for some reason the successful ones are in lower case and the failing ones are in upper case. Not sure why or what the exact implications are, just an observation. :wink:

Thanks vzorglub,
I am trying to use an rfr3 with the stock diy firmware, using code from here [git@github.com:JAMESBOWLER/SONOFF_DIY.git].

I suspect my problem lies elsewhere in my openhab config. I last used openhab a few years ago and things are very different now!

I think things have gone wrong before then. The first indication comes at the start of the poll rule.
if (UpdateResult.contains(’“error”:0’)) {
JSONdata = transform(“JSONPATH”, “$.data”, UpdateResult)
logInfo(“Sonoff Poll JSONdata”, JSONdata)

The log indicates that the transform has not extracted the embedded json string from the data field. So nothing works from then on.

I think the underlying issue here is that the “openHAB JSONPATH transformation service” is not JSONPATH.
It can return only a string - not arrays or lists.
So it cannot handle nested $.data in the example shown.
I think you have to $.data.switch or something like.

The other quirk is that transform failure returns the whole untransformed input string, by design.

Ooops I missed that typing stuff into the web editor was removing back slashes from my logs…

The two relevant log lines are.

2020-02-06 19:25:24.323 [INFO ] [odel.script.Sonoff Poll UpdateResult] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]

2020-02-06 19:25:24.327 [INFO ] [me.model.script.Sonoff Poll JSONdata] - [{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}]

So the input is actually valid JSON and the content of the data field is a string that just happens to contain valid JSON. I realise that if can get past the initial problem I will have to hopefully have to use JSONPATH once again to parse the data field to retrieve the values embedded in it.

I am replying to this by email to see if I can bypass the “munger”.

Roger

Edited the whole thread to tidy things up a bit.

If anyone know where there is any documentation on the online text editor. Please point me at it.

1 Like

There is this:How to use code fences
and in general it follows the MD file formating rules:

Did you not have an empty line before the code fence?

Where is Last_Update defined?

Why are you adding square brackets?

This is not valid JSON, which is one reason the JSONPATH transformation service would return the input string. Another is if there is no match. Another is if you do not have the service installed.

Thanks for the link I now understand the editor a bit better.

Last update is defined

I’m not sure why you’re not having the Sonoff post normal valid JSON to begin with

hmm. Stripped to interesting bits -
[{"seq":2,"error":0,"data":"{\"switch\":\"off\", ....}"}]
I don’t think that’s going to work because the transform will interpret as
[{"seq":2,"error":0,"data":"{"switch":"off", ....}"}]
which is invalid JSON.
The trouble is your data “string” contains " characters. Yes, they’re escaped, but the transform un-escapes them. You need to double escape.

You don’t need the either.

Demo

//  standard json
var JSONdata = '{"apple":100,"orange":{"pip":200}}'
logInfo("test", "valid " + JSONdata)
logInfo("test", "simple " + transform("JSONPATH", "$.apple", JSONdata))
logInfo("test", "nested " + transform("JSONPATH", "$.orange.pip", JSONdata))
// string with escaped quotes
JSONdata = '{"apple":100,"orange":{"pip":200},"banana":"{\"skin\":500}"}'
logInfo("test", "weird " + JSONdata)
logInfo("test", "broken " + transform("JSONPATH", "$.banana", JSONdata))
// string content with double escaped quotes
JSONdata = '{"apple":100,"orange":{"pip":200},"banana":"{\\"skin\\":500}"}'
logInfo("test", "double escaped " + JSONdata)
logInfo("test", "tada " + transform("JSONPATH", "$.banana", JSONdata))

results

[INFO ] [.eclipse.smarthome.model.script.test] - valid {"apple":100,"orange":{"pip":200}}
[INFO ] [.eclipse.smarthome.model.script.test] - simple 100
[INFO ] [.eclipse.smarthome.model.script.test] - nested 200

[INFO ] [.eclipse.smarthome.model.script.test] - weird {"apple":100,"orange":{"pip":200},"banana":"{"skin":500}"}
[INFO ] [.eclipse.smarthome.model.script.test] - broken {"apple":100,"orange":{"pip":200},"banana":"{"skin":500}"}

[INFO ] [.eclipse.smarthome.model.script.test] - double escaped {"apple":100,"orange":{"pip":200},"banana":"{\"skin\":500}"}
[INFO ] [.eclipse.smarthome.model.script.test] - tada {"skin":500}
2 Likes

Here is the complete rule file properly fenced.


val String Ip = '192.168.1.9'      // Ip of Sonoff or mDNS eWeLink_100098757f.local
val String Deviceid = '100098757f'              // Can get from DIY tool 

//-----------------------------------------------------Item Name
val sonoff = ScriptServiceUtil.getItemRegistry.getItem("sonoff_one")
val startup = ScriptServiceUtil.getItemRegistry.getItem("sonoff_one_startup")
val pulse = ScriptServiceUtil.getItemRegistry.getItem("sonoff_one_pulse")
val pulseWidth = ScriptServiceUtil.getItemRegistry.getItem("sonoff_one_pulseWidth")
val ssid = ScriptServiceUtil.getItemRegistry.getItem("sonoff_one_ssid")
val otaUnlock = ScriptServiceUtil.getItemRegistry.getItem("sonoff_one_otaUnlock")
var String Last_update

rule "Sonoff_one control Rule"

  when
    Item sonoff_one received command  // The Item name for your Switch 
  then
  
  try {

      var CommandState = sonoff.state.toString.toLowerCase   
      var CommandURL = 'http://' + Ip +':8081/zeroconf/switch'
      var String CommandData = '{"deviceid": "' + Deviceid + '", "data": {"switch": "' + CommandState + '"}}'
      var String UpdateResult
      var Number Attempts = 0
      var CommandExec = 'curl@@-sSH@@"Accept: */*"@@-H@@"Content-Type: text/plain"@@-X@@POST@@-d@@' + CommandData + '@@' + CommandURL
    
    do {
      Attempts += 1
      // logInfo("Sonoff command", CommandData)
      UpdateResult = executeCommandLine(CommandExec, 1500)
        
      if (UpdateResult.contains('"error":0')) { logInfo("Sonoff ID:"+ Deviceid +" Control", "IP: " + Ip + " Turned: " + CommandState) } // Success
     
  
    } while (!UpdateResult.contains('"error":0') && Attempts < 3) // Attempt to send 5 times
  
    if (Attempts == 3) {
      logError("Sonoff control Rule", "3 failures updating Sonoff")
    }
          
    if (UpdateResult.contains('"error":400')) {
      logError("Sonoff ID:"+ Deviceid +" Error", "The operation failed and the request was formatted incorrectly. The request body is not a valid JSON format")
    } else if  (UpdateResult.contains('"error":401')) {
      logError("Sonoff Error", "The operation failed and the request was unauthorized. Device information encryption is enabled on the device, but the request is not encrypted")
    } else if (UpdateResult.contains('"error":404')) {
      logError("Sonoff Error", "The operation failed and the device does not exist. The device does not support the requested deviceid")
    } else if (UpdateResult.contains('"error":422')) {
      logError("Sonoff Error", "The operation failed and the request parameters are invalid. For example, the device does not support setting specific device information")
    }
    } 
    catch(Exception e){
        logError("Control", "Error occured in Control Rule! " + e.toString)
    }
 
end

rule "Sonoff_one Power ON state Rule"

  when
    Item sonoff_one_startup received command 
  then

  try {
    
      var CommandState = sonoff_one_startup.state 
      var CommandURL = 'http://' + Ip +':8081/zeroconf/startup'
      var String CommandData = '{"deviceid": "' + Deviceid + '", "data": {"startup": "' + CommandState + '"}}'
      var String UpdateResult
      var Number Attempts = 0
      var CommandExec = 'curl@@-sSH@@"Accept: */*"@@-H@@"Content-Type: text/plain"@@-X@@POST@@-d@@' + CommandData + '@@' + CommandURL

 
    do {
      Attempts += 1
      // logInfo("Sonoff command", CommandData)
      UpdateResult = executeCommandLine(CommandExec, 1500)
        
      if (UpdateResult.contains('"error":0')) { logInfo("Sonoff ID:"+ Deviceid +" Control", "IP: " + Ip + " Turned: " + CommandState) } // Success
     
  
    } while (!UpdateResult.contains('"error":0') && Attempts < 3) // Attempt to send 5 times
  
    if (Attempts == 3) {
      logError("Sonoff Power ON state Rule", "3 failures updating Sonoff")
    }
          
    if (UpdateResult.contains('"error":400')) {
      logError("Sonoff ID:"+ Deviceid +" Error", "The operation failed and the request was formatted incorrectly. The request body is not a valid JSON format")
    } else if  (UpdateResult.contains('"error":401')) {
      logError("Sonoff Error", "The operation failed and the request was unauthorized. Device information encryption is enabled on the device, but the request is not encrypted")
    } else if (UpdateResult.contains('"error":404')) {
      logError("Sonoff Error", "The operation failed and the device does not exist. The device does not support the requested deviceid")
    } else if (UpdateResult.contains('"error":422')) {
      logError("Sonoff Error", "The operation failed and the request parameters are invalid. For example, the device does not support setting specific device information")
    }
    } 
    catch(Exception e){
        logError("state", "Error occured in on state Rule! " + e.toString)
    }
 
end

rule "Sonoff_one Pulse Rule"

  when
    Item sonoff_one_pulse received command or 
    Item sonoff_one_pulseWidth received command
  then

  try {

      var CommandState = pulse.state 
      var CommandTime = pulseWidth.state.toString 
      var CommandURL = 'http://' + Ip +':8081/zeroconf/pulse'
      var String CommandData = '{"deviceid": "' + Deviceid + '", "data": {"pulse": "' + CommandState + '", "pulseWidth":' + CommandTime +'}}'
      var String UpdateResult
      var Number Attempts = 0
      var CommandExec = 'curl@@-sSH@@"Accept: */*"@@-H@@"Content-Type: text/plain"@@-X@@POST@@-d@@' + CommandData + '@@' + CommandURL

    do {
      Attempts += 1
      // logInfo("Sonoff command", CommandData)
      UpdateResult = executeCommandLine(CommandExec, 1500)
        
      if (UpdateResult.contains('"error":0')) { logInfo("Sonoff ID:"+ Deviceid +" Pulse", "IP: " + Ip + " State: " + CommandState + "  Time  :" + CommandTime) } // Success
     
  
    } while (!UpdateResult.contains('"error":0') && Attempts < 3) // Attempt to send 5 times
  
    if (Attempts == 3) {
      logError("Sonoff Pulse Rule", "3 failures updating Sonoff")
    }
          
    if (UpdateResult.contains('"error":400')) {
      logError("Sonoff ID:"+ Deviceid +" Error", "The operation failed and the request was formatted incorrectly. The request body is not a valid JSON format")
    } else if  (UpdateResult.contains('"error":401')) {
      logError("Sonoff Error", "The operation failed and the request was unauthorized. Device information encryption is enabled on the device, but the request is not encrypted")
    } else if (UpdateResult.contains('"error":404')) {
      logError("Sonoff Error", "The operation failed and the device does not exist. The device does not support the requested deviceid")
    } else if (UpdateResult.contains('"error":422')) {
      logError("Sonoff Error", "The operation failed and the request parameters are invalid. For example, the device does not support setting specific device information")
    }
    } 
    catch(Exception e){
       logError("pulse", "Error occured in Pulse Rule! " + e.toString)
    }

end

rule "Sonoff_one Polling"

  when
    Time cron "0/3 * * * * ?" // Run every 3 seconds
  then
  
  try {

        var CommandURL = 'http://' + Ip +':8081/zeroconf/info'
        var String CommandData = '{"deviceid": "'+ Deviceid +'", "data": {}}'
        var String UpdateResult
        var Number Attempts = 0
        var CommandExec = 'curl@@-sSH@@"Accept: */*"@@-H@@"Content-Type: text/plain"@@-X@@POST@@-d@@' + CommandData + '@@' + CommandURL
        var String JSONdata
        var String update_sswitch
        var String update_startup
        var String update_pulse
        var String update_pulseWidth
        var String update_ssid
        var String update_otaUnlock


    do {
        Attempts += 1
        // logInfo("Sonoff command", CommandData)
        UpdateResult = executeCommandLine(CommandExec, 1500)

      if (UpdateResult.contains('"error":0')) { 
        JSONdata = transform("JSONPATH", "$.data", UpdateResult)      
        // logInfo("Sonoff Poll JSONdata", JSONdata)


      if (JSONdata != Last_update) {
       
          update_sswitch = transform("JSONPATH", "$.switch", JSONdata)
          update_startup = transform("JSONPATH", "$.startup", JSONdata)
          update_pulse = transform("JSONPATH", "$.pulse", JSONdata)
          update_pulseWidth = transform("JSONPATH", "$.pulseWidth", JSONdata)
          update_ssid = transform("JSONPATH", "$.ssid", JSONdata)
          update_otaUnlock = transform("JSONPATH", "$.otaUnlock", JSONdata)

          if (update_sswitch != sonoff.state.toString.toLowerCase) {
            logInfo("sonoff switch", " Syncing state to " + update_sswitch)
            sonoff.postUpdate(update_sswitch.toString.toUpperCase)
          }

          if (update_startup != startup.state.toString) {
            logInfo("sonoff switch", " Syncing startup to " + update_startup)
            startup.postUpdate(update_startup.toString)
          }
          
          if (update_pulse != pulse.state.toString) {
            logInfo("sonoff switch", " Syncing pulse to " + update_pulse )
            pulse.postUpdate(update_pulse.toString)
          }
          
          if (update_pulseWidth != pulseWidth.state.toString) {
            logInfo("sonoff switch", " Syncing pulseWidth to " + update_pulseWidth + "ms")
            pulseWidth.postUpdate(update_pulseWidth.toString)
          }
          
          if (update_ssid != ssid.state.toString) {
            logInfo("sonoff switch", " Syncing ssid")
            ssid.postUpdate(update_ssid.toString)
          }
          
          if (update_otaUnlock != otaUnlock.state.toString) {
            logInfo("sonoff switch", " Syncing otaUnlock")
            otaUnlock.postUpdate(update_otaUnlock.toString)
          }
          
          Last_update = JSONdata
          }     
        }    
  
      } while (!UpdateResult.contains('"error":0') && Attempts < 2) // Attempt to send 2 times

    if (Attempts == 2) {
      logError("Sonoff Poll Switch", "2 failures polling sonoff_one ")
    }
      
        if (UpdateResult.contains('"error":400')) {
          logError("Sonoff ID:"+ Deviceid +" Error", "The operation failed and the request was formatted incorrectly. The request body is not a valid JSON format")
        } else if  (UpdateResult.contains('"error":401')) {
          logError("Sonoff Error", "The operation failed and the request was unauthorized. Device information encryption is enabled on the device, but the request is not encrypted")
        } else if (UpdateResult.contains('"error":404')) {
          logError("Sonoff Error", "The operation failed and the device does not exist. The device does not support the requested deviceid")
        }    else if (UpdateResult.contains('"error":422')) {
          logError("Sonoff Error", "The operation failed and the request parameters are invalid. For example, the device does not support setting specific device information")
        }
    
    }
    catch(Exception e){
      logError("Polling", "Error occured in Sonoff Poling Rule! " + e.toString)
    }

end

Last update is defined at the top of the file.

The square brackets were me cluttching at straws. The result is the same wether they are there or not.

This is what the the sonoff actually returns.

{"seq":2,"error":0,"data":"{\"switch\":\"off\",\"startup\":\"off\",\"pulse\":\"off\",\"pulseWidth\":500,\"ssid\":\"peglegpete\",\"otaUnlock\":false}"}

I think that is valid.

So after.

        JSONdata = transform("JSONPATH", "$.data", UpdateResult)      

This what I would expect to see in JSONdata

{"switch":"off","startup":"off","pulse":"off","pulseWidth":500,"ssid":"peglegpete","otaUnlock":false}"}

The JSONPATH transform is installed. It should match the “data” value. I would expect it to return the value of the string with the escape() characters removed and the quote chracters retained.

Thanks for spending time on this guys. I am somewhat out of depth on this.

Just another reminder, the sonoff is not running tasmota.

Cheers Roger

Did you see my previous post?

I gave up this morning and decided to flash tasmota on the sonoff I was testing on. I somehow managed to partially brick it. So I decided make a simple rule that did not require an actual device to work. Here it is.

import org.eclipse.smarthome.model.script.ScriptServiceUtil

//-----------------------------------------------------Item Name

rule "Sonoff_one Polling"

  when
    Time cron "0/60 * * * * ?" // Run every 60 seconds
  then
  
  try {
    var String UpdateResult
    var String JSONdata

    UpdateResult =  "{\"seq\":2,\"error\":0,\"data\":\"{\\\"switch\\\":\\\"off\\\",\\\"startup\\\":\\\"off\\\",\\\"pulse\\\":\\\"off\\\",\\\"pulseWidth\\\":500,\\\"ssid\\\":\\\"peglegpete\\\",\\\"otaUnlock\\\":true}\"}"
    logInfo("Sonoff Poll UpdateResult", UpdateResult)

    JSONdata = transform("JSONPATH", "$.data", UpdateResult)      
    logInfo("Sonoff Poll JSONdata", JSONdata)
  }
  catch(Exception e){
    logError("Polling", "Error occured in Sonoff Poling Rule! " + e.toString)
  }

end 

Lo and behold this worked as expected.

Unfortunately my openhab.logs dont go back as far as the point where it was failing on raw string from the device without my added [].

The the input string literal that initialises UpdateResult is should be the string that the sonoff is sending with additional escapes added because they are needed in string literals. But they should not be needed in the actual content of a string object.

So I am putting this on hold for a while. However I still want to pursue my original intention, which was to create a binding for sonoff devices in diy mode that uses mdns to find and connect to them. I am surprised that one does not exist already given the number of these things that are out there. Maybe I missed one.

Thanks once again for your help and patience.

Roger

Resurrected a sonoff!

Found the problem in the rule. The curl command was missing a --raw parameter.

I got this code from https://github.com/JAMESBOWLER/SONOFF_DIY.git

I cannot see how this code could have worked as it is in the repository. I will raise a pull request on it. Here is a snippet of the poll rule. There are number of other places where this needs to be changed

rule "Sonoff_one Polling"

  when
    Time cron "0/60 * * * * ?" // Run every 60 seconds

  then
  
  try {
    var CommandURL = 'http://' + Ip +':8081/zeroconf/info'
    var String CommandData = '{"deviceid": "'+ Deviceid +'", "data": {}}'
    var String UpdateResult
    var Number Attempts = 0
    var CommandExec = 'curl@@--raw@@-sSH@@"Accept: */*"@@-H@@"Content-Type: text/plain"@@-X@@POST@@-d@@' + CommandData + '@@' + CommandURL

Roger