HTTP Binding: Tranformation from curl command

Dears,

I’m trying to “translate” the following (working!) curl command into a http-channel.

curl 'http://192.168.178.48/php/easpanelW.php' -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' -H 'X-Requested-With: XMLHttpRequest' --data-raw %2$s

What I’ve done so far:

Thing http:url:drooff "Drooff fire+ Status einlesen" [
    baseURL = "http://192.168.178.48/",
    authMode = "BASIC",
    ignoreSSLErrors = false,
    delay = 0,
    stateMethod = "GET",
    refresh = 15,
    commandMethod = "POST",
    headers = "Content-Type=application/x-www-form-urlencoded; charset=UTF-8","X-Requested-With=XMLHttpRequest", 
    timeout = 3000,
    bufferSize = 2048] {
        Channels:
          Type string            : input [stateExtension = "php/easpanel.php", mode = "READONLY"]
          Type request-date-time : last-failure
          Type string            : command [commandExtension = "php/easpanelW.php", mode = "WRITEONLY", commandExtension = "data-raw=%2$s"]
    }

The command is created in a rule:

rule "drooff: sende Befehl zu fire+"
when
    Member of gDrooff changed 
then
    if ( previousState == NULL )
      return;
    val vBetrieb    = (firePlus_Betriebsart.state as Number).intValue.toString
    val vLeistung   = (firePlus_Leistung.state as Number).intValue.toString
    val vHelligkeit = (firePlus_Helligkeit.state as Number).intValue.toString
    val vBedienung  = 1
    val vLED        = (firePlus_LED.state as Number).intValue.toString
    val vAB         = (firePlus_AB.state as Number).intValue.toString
    var strCommand  = "'Betrieb=" + vBetrieb + "&Leistung=" + vLeistung + "&Helligkeit=" + vHelligkeit + "&Bedienung=" + vBedienung + "&LED=" + vLED + "&AB=" + vAB + "'"
    firePlus_Command.sendCommand(strCommand)
end

Unfortunately, it doesn’t work. Log:

2025-03-15 09:55:16.965 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'firePlus_Command' received command 'Betrieb=3&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0'
2025-03-15 09:55:16.967 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'firePlus_Command' changed from 'Betrieb=2&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0' to 'Betrieb=3&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0'
2025-03-15 09:55:17.004 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'drooff_Daten_per_http_Last_Failure' changed from 2025-03-15T09:48:18.667046239+0100 to 2025-03-15T09:55:17.001962591+0100

So there must be sth wrong, what I cannot see. Perhaps here is an expert who can help me.

Thanks.

No expert, but to get some additional output, please set the logger for the http binding to DEBUG (or even TRACE).
This ist done via Main UI->Administration->Settings->Add-On-Settings (right column)->http-Binding
or you can do it via Karaf Console with command

log:set DEBUG org.openhab.binding.http

This way you will get way more information about what’s going on :slight_smile:

…somewhat understated :wink:

Ok, log with DEBUG:

2025-03-15 11:05:19.068 [DEBUG] [p.internal.http.HttpResponseListener] - Requesting 'http://192.168.178.48/&data-raw='Betrieb=3&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0'' (method='POST', content='org.eclipse.jetty.client.util.StringContentProvider@16666fd8') failed: 404 Not Found

It seems to ignore headers and commandExtension…

EDIT: I set commandExtension twice, combined them now. headers still missing…

2025-03-15 11:12:34.659 [DEBUG] [p.internal.http.HttpResponseListener] - Requesting 'http://192.168.178.48/php/easpanelW.php&data-raw='Betrieb=2&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0'' (method='POST', content='org.eclipse.jetty.client.util.StringContentProvider@108a49c8') failed: 404 Not Found

TRACE response:

2025-03-15 11:16:28.229 [TRACE] [nding.http.internal.HttpThingHandler] - Sending to 'http://192.168.178.48/php/easpanelW.php&data-raw=Betrieb=3&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0': Method = {POST}, Headers = {Accept-Encoding: gzip, User-Agent: Jetty/9.4.54.v20240208}, Content = {Betrieb=3&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0}
2025-03-15 11:16:28.243 [TRACE] [p.internal.http.HttpResponseListener] - Received from 'http://192.168.178.48/php/easpanelW.php&data-raw=Betrieb=3&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0': Code = {404}, Headers = {Content-Type: text/html, Content-Length: 345, Date: Sat, 15 Mar 2025 10:16:28 GMT, Server: lighttpd/1.4.35}, Content = {<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <title>404 - Not Found</title>
 </head>
 <body>
  <h1>404 - Not Found</h1>
 </body>
</html>
}

I’m not sure if data-raw should be part of the url at all.
As said, I’m no expert (at least for http, but also many other bindings…)

I tried it without data-raw.
I also tried GET, PUT and POST.

But response remains a ‘404’

2025-03-15 12:11:04.671 [DEBUG] [p.internal.http.HttpResponseListener] - Requesting 'http://192.168.178.48/php/easpanelW.php&Betrieb=4&Leistung=4&Helligkeit=40&Bedienung=1&LED=0&AB=0' (method='PUT', content='org.eclipse.jetty.client.util.StringContentProvider@28c9e2a7') failed: 404 Not Found

As you use http and not https my suggestion would be to use a network sniffer like wireshark.
Record the traffic when you execute your curl command and record it again when you use the http binding implementation.
Then you should be able to see the differences in header and body of the POST requests.

And even in case you should find an item that is not configurable in the http binding setup ( just in case there for example is a header that cannot be changed ) you then can use the curl command to reconstruct the http binding setup and check if the then get the same error behavior.

Here’s the wireshark recording.

uhh, that’s beyond my expertise… :face_with_peeking_eye:

How did you start the request ? Wondering why User-Agent is neither curl nor jetty but a list of browser engines.
You can see all the components of the request:
the host it was sent to
the request method ( POST )
the other header parameters and also the content of post request ( bottom part ).

I assume that this is not the recording base on the curl request.
Do it with curl again and then compare the content. What are the differences ?

You’re right.

I don’t see neither the cURL not the http request from the openHAB-Pi (IP… .60) in Wireshark protocol.
Only communication between PC (IP…10) and the corresponding IoT drooff (IP …48)

So the first Wireshark screenshot was done while operating the Drooff from its webpage on my PC.

As stated in

it seems that data is sent through the url, so the thing should look like this:

Thing http:url:drooff2 "Drooff fire+ Status einlesen" [
    baseURL = "http://192.168.178.48/",
    authMode = "BASIC",
    ignoreSSLErrors = false,
    delay = 0,
    stateMethod = "GET",
    refresh = 15,
    commandMethod = "POST",
    headers = "Content-Type=application/x-www-form-urlencoded; charset=UTF-8","X-Requested-With=XMLHttpRequest", 
    contentType = "application/xml",
    timeout = 3000,
    bufferSize = 2048] {
        Channels:
          Type string : input   "Rückmeldung" [stateExtension = "php/easpanel.php", mode = "READONLY"]
          Type string : command "Befehl"      [commandExtension = "php/easpanelW.php?%2$s", mode = "WRITEONLY"]
    }
1 Like

Thanks for your investigations. Getting closer to the goal.

In the meantime I was trying some modifications. I also removed the quotation marks, which were embracing the command string.

First of all, from your approch the questionmark after .php instead of my “&” is an improvement.

Moreover, using your complete code leads to a return code 400 instead of 404.

2025-03-16 09:48:54.912 [TRACE] [nding.http.internal.HttpThingHandler] - Sending to 'http://192.168.178.48/php/easpanelW.php?Betrieb=2&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0': Method = {POST}, Headers = {Accept-Encoding: gzip, Content-Type: application/xml, X-Requested-With: XMLHttpRequest, User-Agent: Jetty/9.4.54.v20240208, Content-Type: application/x-www-form-urlencoded; charset=UTF-8}, Content = {Betrieb=2&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0}
2025-03-16 09:48:54.921 [TRACE] [p.internal.http.HttpResponseListener] - Received from 'http://192.168.178.48/php/easpanelW.php?Betrieb=2&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0': Code = {400}, Headers = {Content-Type: text/html, Content-Length: 349, Connection: close, Date: Sun, 16 Mar 2025 08:48:54 GMT, Server: lighttpd/1.4.35}, Content = {<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <title>400 - Bad Request</title>
 </head>
 <body>
  <h1>400 - Bad Request</h1>
 </body>
</html>
}
2025-03-16 09:48:54.921 [DEBUG] [p.internal.http.HttpResponseListener] - Requesting 'http://192.168.178.48/php/easpanelW.php?Betrieb=2&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0' (method='POST', content='org.eclipse.jetty.client.util.StringContentProvider@44f82e') failed: 400 Bad Request
==> /var/log/openhab/events.log <==
2025-03-16 09:48:54.926 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'drooff_Daten_per_http_Last_Failure' changed from 2025-03-15T23:50:20.245392575+0100 to 2025-03-16T09:48:54.923206857+0100

Next try was to remove the header, which leads to a return code 200. Should be a success code.

2025-03-16 09:55:23.300 [TRACE] [nding.http.internal.HttpThingHandler] - Sending to 'http://192.168.178.48/php/easpanelW.php?Betrieb=2&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0': Method = {POST}, Headers = {Accept-Encoding: gzip, Content-Type: application/xml, User-Agent: Jetty/9.4.54.v20240208}, Content = {Betrieb=2&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0}
2025-03-16 09:55:23.324 [TRACE] [p.internal.http.HttpResponseListener] - Received from 'http://192.168.178.48/php/easpanelW.php?Betrieb=2&Leistung=4&Helligkeit=40&Bedienung=1&LED=1&AB=0': Code = {200}, Headers = {Content-Type: text/html; charset=UTF-8, Transfer-Encoding: chunked, Date: Sun, 16 Mar 2025 08:55:23 GMT, Server: lighttpd/1.4.35}, Content = {}
==> /var/log/openhab/events.log <==
2025-03-16 09:55:23.328 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Drooff_fire_Status_einlesen_Last_Success' changed from 2025-03-16T09:55:20.770393378+0100 to 2025-03-16T09:55:23.325015861+0100

But the Drooff unit is still not applying the changes…

yes, 200 means request is ok and server accepts its syntax. But still can lead an empty result.
Run the curl command again and record the TRACE log like you did for the one with the return code 200. Then you should see the difference in the request.
To capture network traffic you can run tcpdump on the openhab server. Record it to a file and open that file with wireshark. Then do a right mouse button click on the traffic and do follow http stream.

Result then will show the request as well as the repsone e.g.


GET /api/review?limit=10&severity=alert HTTP/1.1
Host: quark:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
X-CSRF-TOKEN: 1
X-CACHE-BYPASS: 1
Connection: keep-alive
Referer: http://quark:5000/

HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: Sun, 16 Mar 2025 09:19:52 GMT
Content-Type: application/json
Content-Length: 2550

Connection: keep-alive
Cache-Control: no-store
X-Cache-Status: BYPASS
.....
1 Like

Ok, didn’t expect, but I managed to do so.

This is the result from tcpdump/wireshark corresponding to the cURL command.

Hypertext Transfer Protocol
    POST /php/easpanelW.php HTTP/1.1\r\n
        Request Method: POST
        Request URI: /php/easpanelW.php
        Request Version: HTTP/1.1
    Host: 192.168.178.48\r\n
    User-Agent: curl/7.88.1\r\n
    Accept: */*\r\n
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n
    X-Requested-With: XMLHttpRequest\r\n
    Content-Length: 57\r\n
        [Content length: 57]
    \r\n
    [Response in frame: 1139]
    [Full request URI: http://192.168.178.48/php/easpanelW.php]
    File Data: 57 bytes
HTML Form URL Encoded: application/x-www-form-urlencoded
    Form item: "Betrieb" = "3"
        Key: Betrieb
        Value: 3
    Form item: "Leistung" = "4"
        Key: Leistung
        Value: 4
    Form item: "Helligkeit" = "30"
        Key: Helligkeit
        Value: 30
    Form item: "Bedienung" = "1"
        Key: Bedienung
        Value: 1
    Form item: "LED" = "0"
        Key: LED
        Value: 0
    Form item: "AB" = "1"
        Key: AB
        Value: 1

Does it help?

and in comparison the result from the http binding command: (based on Udo’s proposal, with code 400 response)

Hypertext Transfer Protocol
    POST /php/easpanelW.php?Betrieb=2&Leistung=4&Helligkeit=60&Bedienung=1&LED=0&AB=0 HTTP/1.1\r\n
        Request Method: POST
        Request URI: /php/easpanelW.php?Betrieb=2&Leistung=4&Helligkeit=60&Bedienung=1&LED=0&AB=0
            Request URI Path: /php/easpanelW.php
            Request URI Query: Betrieb=2&Leistung=4&Helligkeit=60&Bedienung=1&LED=0&AB=0
                Request URI Query Parameter: Betrieb=2
                Request URI Query Parameter: Leistung=4
                Request URI Query Parameter: Helligkeit=60
                Request URI Query Parameter: Bedienung=1
                Request URI Query Parameter: LED=0
                Request URI Query Parameter: AB=0
        Request Version: HTTP/1.1
    Accept-Encoding: gzip\r\n
    Content-Type: application/xml\r\n
    User-Agent: Jetty/9.4.54.v20240208\r\n
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n
    Host: 192.168.178.48\r\n
    Content-Length: 57\r\n
        [Content length: 57]
    \r\n
    [Response in frame: 629]
    [Full request URI: http://192.168.178.48/php/easpanelW.php?Betrieb=2&Leistung=4&Helligkeit=60&Bedienung=1&LED=0&AB=0]
    File Data: 57 bytes
HTML Form URL Encoded: application/x-www-form-urlencoded
    Form item: "Betrieb" = "2"
        Key: Betrieb
        Value: 2
    Form item: "Leistung" = "4"
        Key: Leistung
        Value: 4
    Form item: "Helligkeit" = "60"
        Key: Helligkeit
        Value: 60
    Form item: "Bedienung" = "1"
        Key: Bedienung
        Value: 1
    Form item: "LED" = "0"
        Key: LED
        Value: 0
    Form item: "AB" = "0"
        Key: AB
        Value: 0

Looks a bit different. But tbh, I don’t know how to adjust it…

Heureka, suddenly it works!

Thank you both very much for your help. I learned alot again.

so, finally the http thing has to look like this:

Thing http:url:drooff "Drooff fire+" [
    baseURL = "http://192.168.178.48/",
    authMode = "BASIC",
    ignoreSSLErrors = false,
    delay = 0,
    stateMethod = "GET",
    refresh = 15,
    commandMethod = "POST",
    headers = "Content-Type=application/x-www-form-urlencoded; charset=UTF-8", "X-Requested-With=XMLHttpRequest",
    timeout = 3000,
    bufferSize = 2048] {
        Channels:
          Type string            : input [stateExtension = "php/easpanel.php", mode = "READONLY"]
          Type string            : command [commandExtension = "php/easpanelW.php?%2$s",  mode = "WRITEONLY"]
    }

Necessary are the two headers, but without the line contentType = "application/xml",

In the end it looks quite simple but it was a long journey without knowing why it did’nt work.
Done! :grinning_face:

2 Likes