[webhook] New, very simple binding for listening incomming http requests

“req” object used in webhook expressions is a java object. This is why passing req.parameters directly to a channel value produces strange random results.

You can elaborate how to improve this, maybe a channel which only passes req object as a json to its value bypassing its own jexl expression?

Having a good design feature and same spare time / funds - I would add it my binding.

I don‘t think so.

var value = JSON.parse(event.payload).event

and

var itemName = JSON.parse(event.payload).channel

seems to be easier to me than your jexl script.

But as far as I can see with your code I’d still need the same JEXL script, wouldn’t I? The point of the JEXL script is to pack multiple parameters into one JSON string, to be able to pass them to the JavaScript without having to create multiple channels. So the “event” in the payload is not just “ON” but {“action”:“toggle”,“itemName”:“xxxx”,“command”:“ON”}. Also with your code, the JEXL code is still required to generate this JSON, isn’t it?

Having all parameters in one string allows me to work without an Item but only a trigger channel that is linked to a script. That’s less configuration work, and also when doing it with multiple string channels, I’d worry about thread-safety (though this is just a gut feeling, I don’t know openHAB well enough to know if that could be an issue).

My jexl script just returns the parameter:

return req.parameters.trigger[0];

But I see the point now. You are trying to submit three parameters to a triggering channel instead of one.

Ok, but I’d like to pass multiple values, see my previous post …

funny, I updated my post at the same time.
How about submitting all three variables with a delimiter as one variable?

something like

oh:8080/webhook/xxxxxxxx?trigger={“action”:“toggle”,“itemName”:“xxxx”,“command”:“ON”}

(but json string needs to be URL encoded)
or

oh:8080/webhook/xxxxxxxx?trigger=“toggle_itemName_ON”

Yes, that could be a good alternative to the JEXL code generating the JSON. Depending on how often you have to alter or generate URLs I think one or the other will be more efficient/convenient. However, for a generic and reusable solution I wanted to harness all the capabilities of the Webhook binding, so not only multiple GET parameters but also the ability to maybe later include POST body content into the JSON passed on to the script. So I think in that way the JSON generated by JEXL is still the way to go.

Ideally I’d still like to figure out a way to generically turn all GET parameters into a JSON without having to name them individually, but not sure if it can be done in JEXL.

Exactly, so my ideal implementation would be a trigger channel that passes a JSON containing all the data you have from the request (parameters, body and method). Maybe the script could also be part of the Thing configuration, and be called synchronously so that it can return a HTTP response. Because I think that is a weakness of the binding, the caller does not really know if the actions were successful or not.

I now figured out how to do the iteration of req.parameters. The improved code below puts all items in req.parameters in the JSON, so it is generic and does not need to be modified if new parameters are introduced.

Edit: improved the JEXL further by using Gson to generate the JSON code. This simplifies the code a bit and also handles encoding/escaping of strings in case they contain special characters not allowed in the JSON.

Edit again: actually req.parameters can be directly converted to JSON, so I now skipped the iteration. And I now also added the other request values (body and method), so now it is completely generic, everything that is available is passed on via the trigger channel to the rule, and the rule can act upon it as it sees fit.

UID: webhook:Webhook:xxxxxxxxxx
label: Webhook
thingTypeUID: webhook:Webhook
configuration:
  expression: resp.status=200
channels:
  - id: trigger
    channelTypeUID: webhook:trigger-channel
    label: Trigger
    description: Trigger fires when the webhook is called.
    configuration:
      expression: |-
        {
          // This expression takes all data from the request and puts it into a JSON string
          var gson = new( "com.google.gson.Gson" );
          var jsonRoot = {:}; var jsonBody = {:};
          // If text/json are not present put returns an error, so we need to check first
          if( ! empty( req.body.text ) ) jsonBody.put( "text", req.body.text );
          if( ! empty( req.body.json ) ) jsonBody.put( "json", req.body.json );
          jsonRoot.put( "parameters", req.parameters );
          jsonRoot.put( "body", jsonBody );
          jsonRoot.put( "method", req.method );
          return gson.toJson( jsonRoot );
        }
var input = JSON.parse( event.event );

// Multiple values could be submitted with the same name,
// we only take the first one.
var action = input["parameters"]["action"] ? input["parameters"]["action"][0] : null;
var itemName = input["parameters"]["itemName"] ? input["parameters"]["itemName"][0] : null;
var command = input["parameters"]["command"] ? input["parameters"]["command"][0] : null;

if( action == "sendCommand" )
  {
    if( itemName && command ) items.getItem( itemName ).sendCommand( command )
    else console.warn( "Webhook: sendCommand was called but itemName or command was missing (itemName='" + itemName + "', command='" + command + "')." );
  }
else if( action == "toggle" )
  {
    if( itemName )
      {
        var item = items.getItem( itemName );
        var state = item.state;
        
        if( state == "ON" ) item.sendCommand( "OFF" )
        else if( state == "OFF" ) item.sendCommand( "ON" )
        else console.warn( "Webhook: toggle was called for itemName='" + itemName + "', but state was '" + state + "'." );
      }
    else console.warn( "Webhook: toggle was called but itemName was missing (itemName='" + itemName + "')." );
  }
else
  console.warn( "Webhook: action is empty or invalid (action='" + action + "')." );

If you are not interested in body or method, you can also go for a much simpler expression, but would also need to adapt the script accordingly:

      expression: |-
        {
          return gson.toJson( req.parameters );
        }

@Piotr_Bojko: so basically that is already what I thought is the “ideal” implementation. No need to change anything in the binding. One thing I noted: regardless of whether I sent a content-type “text/plain” or “application/json”, both req.body.text and req.body.json will contain the data sent. Is that a bug? I think it would be good to either provide only one of those two depending on the content-type, or provide the content-type separately and the actual content in just one field.

The code was working till opehab 4..1.2 . But not working after update to 4.1.3

I have updated to 4.1.3 and the binding is running here.
But I had to reinstall it from the Add-on store.
Check under SettingsAdd-On settings if it is available

The add-on is available. I have also configured the trigger. But there’s no log or anything when the webhook url is used. The rule is also not called

After a reinstallation of openhab it works . I think I did something wrong while updating.

Hi all,

Would like to ask if this is the same add-on as the following?

I’m not very technical but would like to configure a webhook for Emby to send POST request with JSON payload, and I plan to write a DSL rule to parse the JSON and control the items with different values based on the JSON message, the rule should be triggered when the webhook is called. I’m a bit lost where to start, can somebody give me a hand?
Many thanks

If emby is able to send an http POST request, then you don‘t need this webhook binding. You can send the request directly to your local openhab’s rest API.
You need to provide the following:

host = 'ipaddress:8080'
path = '/rest/items/youritem'
message = {your json string}
contentType = "application/json"
headers = {'Accept': '*/*','Authorization': 'Bearer oh.xxxxxxxx'}

xxxxxx is an API-token which you generate in MainUI. Click on your account name at the bottom of the menu and click on “Create new token”

Thank you, unfortunately Emby is very limited and I cannot choose header/payload etc.

Hi. This is the same. As far I understand you can use this binding to your use case.

Thank you I have managed to make it work with a triggering channel.
Is there a way to define the Thing with channels in .things file or the thing has to be created by UI?

Strange issue: after upgrading from 4.2.1 to 4.2.2 I need to reinstall the binding (for some reason it’s not there after upgrade)