[SOLVED] Sending Post with a JSON payload?

Tags: #<Tag:0x00007f7455145818> #<Tag:0x00007f7455145750>

I need to send a Post command with a payload, and see that the HTTP binding doesn’t support payloads but maybe a Curl can do it with the Exec binding.
Can someone talk me through how to do this please?
I have it working from a chrome addon but my knowledge of Curl is zero
The payload is:
{
“acState”: {
“on”: false,
“mode”: “heat”,
“fanLevel”: “auto”,
“targetTemperature”: 22
}
}

If the server you’re posting to has simple needs, then you can use the built in sendHttpPostRequest without jumping into curl.

A call looks like:

    sendHttpPostRequest(MY_URL, "application/x-www-form-urlencoded", myData)

Where the url is in the variable MY_URL, and the post data you want to send is in the variable myData.

There are cases where the server is more rigid on the caller, such as checking specific Headers, Authentication, (etc), in which case you’d need to switch to curl and executeCommandLine:

If you to go that route, there are a ton of examples posted here, so searching on “executeCommandLine” and “curl” will pay dividends (esp under Google, with criteria like):

    openhab executecommandline curl site:community.openhab.org

Thanks for the reply, I had to enclose the URL in quotes to keep the designer happy but I’m not sure how to set the payload into the variable myData. I think the {{ }} may conflict with the rest of the rules? -

@kevin,
It’ll take this general form, wrapped in single-quotes (') to avoid escaping the double-quotes (") in the JSON value itself .

You can also inline the value instead of using a variable.

val String MY_URL = 'http://...'
var String myData = '{
    "acState": {
    "on": false,
    "mode": "heat",
    "fanLevel": "auto",
    "targetTemperature": 22
    }
}'

sendHttpPostRequest(MY_URL, "application/x-www-form-urlencoded", myData)
1 Like

Thanks, that has at least kept the designer happy. By experimentation I discovered that the server would accept
{“acState”:{“on”:true,“mode”:“heat”,“fanLevel”:“auto”,“targetTemperature”:24}} so I set that into the string.

However now I get "14:24:40.900 [ERROR] [g.openhab.io.net.http.HttpUtil:233 ] - Fatal transport error: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

So I think something is wrong with HTTPS?

Yup, there’s a bunch of cases that can cause this including:

  • Bad/expired site cert
  • Self-signed cert, with no trust relationship and/or;
  • No mechanism to validate the cert’s CA

At this point you’ve entered into the realm of “There are cases where the server is more rigid on the caller…”, and the options for disabling cert validation (etc) are wider when using CURL.

If you run the Google search criteria above it’ll give you a great set of pointers for next steps.

Oh dear… further into the rabbit hole we go
Is there some obscure hidden away procedure for adding / editing / general playing with certificates that has to be done before openhab will talk SSL? I seem to remember I had a similar problem somewhere else in the deep and distant past taking to the IFTTT maker channel and got around it by dropping the SSL which I can’t do with this one.

Yes, the rabbit hole is fronted by Java CLI tools like keytool.

Here’s a quick pointer that shows it in use:

I’ve routinely scratched my head with (browser) SSL issues only to find that it’s happening because my clock is out of whack… And the list of those gotchas is long :smile:

I take it I don’t need to add an SSL import into the top of the rules file? Something like

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.OutputStream;

Not surprisingly it works fine in Windows 10

The HttpUtil.java code from openHAB is doing the import for you, so they’re not needed in your Rule.

For SSL/TLS, the connection establishment sequence validates the [HTTPS Server] Certificate in a number of ways. If any of these fail, then you’ll get the type of error your seeing.

A few quick checks:

  • ensure the clock on your openHAB device is current.
  • check the Root CA (the issuer Organization) and validate that it’s in the list of Root CA’s Java is configured for.

This latter list can be extracted with keytool using something akin to:

keytool -list -v -keystore /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts

(adjust for your OS, and installation paths)

If these things sounds foreign, then you’re not alone, it’s a complex subject :wink:

This is why some people play very loose security-wise, and end up disabling certificate validation. In tools like curl this is a simple command line option (eg. --insecure).

You may also find that running curl, at the command line, will give you more details on the Certificate issue (although it validates against a different list of CA’s, not the Java list referenced above)

As an example, here’s what curl says about the certificate shipped with my Router:

root@openhab:~$ curl https://192.xxx.xxx.xxx
curl: (60) SSL certificate problem: self signed certificate
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
 of Certificate Authority (CA) public keys (CA certs). If the default
 bundle file isn't adequate, you can specify an alternate file
 using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
 the bundle, the certificate verification probably failed due to a
 problem with the certificate (it might be expired, or the name might
 not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
 the -k (or --insecure) option.

It sounds like avoiding all the SSL complication is the way to go, if this goes to the wrong place my AC doesn’t turn on, hardly the end of the world!
I’ll take a look tomorrow, apologies in advance for any dumb questions, I know absolutely nothing about Curl which is why so far I’ve used IFTTT to do this. Unfortunately a couple of complimentary bugs mean I need to go direct to get the reliability I need.

I just entered that and got

keytool -list -v -keystore /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts
keytool error: java.lang.Exception: Keystore file does not exist: /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts
java.lang.Exception: Keystore file does not exist: /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts
at sun.security.tools.keytool.Main.doCommands(Main.java:742)
at sun.security.tools.keytool.Main.run(Main.java:340)
at sun.security.tools.keytool.Main.main(Main.java:333)

So I guess that’s a clue!

If you do want to diagnose it, this SO contains the specifics on how to do that, using an option added to Java prior to startup:

It may be in a different location, or using a different Java version, on your machine. It will be present.

keytool -list -v -keystore /usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/jre/lib/security/cacerts
found it - its a Pi2 with Raspbian

I’ve given up on getting SSL to work, the debug flag did nothing that I could find even after turning on the debug logging so I’m now trying to get Curl to work in the hope of translating this into a commandline.

Needless to say it doesn’t work so I guess a fresh set of obscure hoops is in order. Can you give me a clue as to what options I need to add?

From the google chrome Advanced Web Client I put in the URL:
https://myurl?apiKey=myapikey
click the post dot
put in payload {“acState”:{“on”:true,“mode”:“heat”,“fanLevel”:“auto”,“targetTemperature”:20}}
and leave the content-type set as the default application/x-www-form-urlencoded

After clicking send this gives me back a status of 200 OK and turns the AC On which is what I’m after.

After a lucky google strike it appears I may have answered my own question. This seems to work
curl -H “Content-Type: application/x-www-form-urlencoded” -X POST -d ‘{“acState”:{“on”:true,“mode”:“heat”,“fanLevel”:“auto”,“targetTemperature”:21}}’ https://myURL?apiKey=myAPI
Which in a rule becomes:
executeCommandLine('curl@@-H@@Content-Type: application/x-www-form-urlencoded@@-X@@POST@@-d@@{“acState”:{“on”:false,“mode”:“heat”,“fanLevel”:“auto”,“targetTemperature”:22}}@@https://myURL’@apiKey=myAPI)

Now I need to work out how I can link this to an Switch item using whether I got back a status of 200 or not as its status.

There is a missing ’ at the end …myAPI’)

Also I do not get the " , they are stripped away. I really need them

How '@ becomes ?

I tried calling a .bat (windows OS) with the payload as parameter and works fine but not with Unicode/Cyrillic letters.

Can’t speak for Windows, but my working solution under Raspbian Jessie on a Pi is

executeCommandLine('curl@@-H@@Content-Type: application/x-www-form-urlencoded@@-X@@POST@@-d@@{"acState":{"on":true,"mode":"cool","fanLevel":"auto","targetTemperature":20}}@@https://home.sensibo.com/api/v2/pods/MYPODID/acStates?apiKey=MYAPIKEY&fields=acState', 5000)

This sends a JSON string. MYPODID is replaced with the correct POD ID and MYAPIKEY is replaced with the correct API Key. The command above I think does send the “”

From memory the whole command line needs to be enclosed in (’ ') and @@ is used in place of a space on the command line so to run in a command prompt the command “Ping MyHost” would be written as

executeCommandline('Ping@@MyHost')

assuming it behaves the same in Windows as it does in Linux.

Looking further in my rules, to move a file in Linux is written as

executeCommandLine('"mv" "FileToBeMovedIncludingPath" "PathToDestinationFolder/"', 2000)

which is slightly different as it doesn’t have “@@” in place of a command line space and there are no brackets around it all, so perhaps both are interchangable in a rule file. This example is a simple command line with no feedback from the command.

In your case it may be simpler to just call the windows batch file with a timelimit on the end to ensure nothing is left hanging if something unexpected happens.