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.