HA Webhook Node Red integration

Hi all,

I’ve been using MyStrom buttons which make a HTTP GET or POST to a specific URL, where one can only can configure the GET params or the POST body. Thus I had to rethink my whole integration after the api_password URL parameter support was removed in v0.101.

The easiest way is to create a webhook and let it do a POST request there. However, this only works for HA automation.

What I wanted was the Node RED automation thing. As I found no solution how to let a HA webhook trigger something in HA, I looked for a solution which I want to show here. Of course I could’ve simply opened the Node RED port and use this as kind of a webhook endpoint, but why adding another possible security hole.

So first I created a rest_command like this, which simply sends all incoming data to a Node Red HTTP endpoint. I’ve used the same secret vars for the Hass.io node username+password, so if you’ve configured it differently, you need to set your own user+pass of the Node RED HTTP node:

        url: http://localhost:1880/endpoint/inbound
        method: POST
        username: !secret node_red_http_node_username
        password: !secret node_red_http_node_password
        payload: '{{ data | tojson }}'
        content_type:  'application/json; charset=utf-8'

Secondly, I’ve added the following automation which sends the webhook id, the request data or json as well as the query params.
There are 2 things to note here:

  1. data+query are of type MultiDictProxy, that’s why I need to convert it to JSON manually. This is not fool proof, so if you have some quotes in your keys or values, it’s not encoded
  2. the “json” content is no valid JSON. It uses single quotes instead of double quotes around keys and values. I’ve opened an issue for this and you’ll see later, that I do a stupid but simple “replace single quotes with double quotes” string replacement.
- alias: Node Red
  - platform: webhook
  - data_template:
        data: '{ {% for key, value in trigger.data.items() %} {% if not loop.first
          %} , {% endif %} "{{ key|e }}": "{{ value|e }}" {% endfor %} }'
        webhook_id: '{{trigger.webhook_id}}'
        json: '{{trigger.json}}'
        query: '{ {% for key, value in trigger.query.items() %} {% if not loop.first
          %} , {% endif %} "{{ key|e }}": "{{ value|e }}" {% endfor %} }'
    service: rest_command.node_red

Last but not least, I’ve added a subflow in Node Red. Please find a screenshot as well as the flow JSON below.

[{"id":"3c3edf43.a7172","type":"subflow","name":"HA webhook","info":"# Payload:\n\n* json (object containing body json)\n* query (object containing GET parameters)\n* webhook_id (string)\n* data (object containing form data body)","category":"","in":[],"out":[{"x":540,"y":80,"wires":[{"id":"a39b3afc.3ba0f8","port":0}]}],"env":[],"color":"#DDAA99"},{"id":"a412c92e.cac678","type":"http in","z":"3c3edf43.a7172","name":"","url":"/inbound","method":"post","upload":false,"swaggerDoc":"","x":200,"y":80,"wires":[["a39b3afc.3ba0f8","4e6bf1c.a50661"]]},{"id":"a39b3afc.3ba0f8","type":"function","z":"3c3edf43.a7172","name":"parse JSON","func":"[\"json\",\"query\", \"data\"].forEach(key => {\n    if(msg.payload[key] && msg.payload[key].length > 1) {\n        // the replacement is necessary as no valid JSON is received from HA\n        const jsonStr = msg.payload[key].replace(/'/g, '\"');\n        msg.payload[key] = JSON.parse(jsonStr);\n    }\n});\nreturn msg;","outputs":1,"noerr":0,"x":410,"y":80,"wires":[[]]},{"id":"4e6bf1c.a50661","type":"http response","z":"3c3edf43.a7172","name":"","statusCode":"204","headers":{},"x":580,"y":140,"wires":[]},{"id":"f99841cd.e6753","type":"subflow:3c3edf43.a7172","z":"df65cebf.0ae0e","name":"HA Webhook","env":[],"x":210,"y":760,"wires":[["48b9895.1da0c78"]]},{"id":"48b9895.1da0c78","type":"debug","z":"df65cebf.0ae0e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":380,"y":760,"wires":[]}]

Subflow screenshot:

So then your payload includes the id and some objects, containing the query (key: value) as well as the data with form data (key: value) or the json content (please note again that single quotes might’ve been replaced by double quotes due to the HA issue).

I hope it also helps one or the other to migrate away from api_password.

Let me know if you have suggestions about how to improve the code and/or share it below :slight_smile:

hi Sebastian,

it seems to me for some reason that using Node-RED companion plugin might be easier, at least for newbies.

See Companion Component to node-red-contrib-home-assistant-websocket to integrate Node-RED with Home Assistant

This works just by creating a node which corresponds to a switch or sensor in Home Assistant. The switch or sensor then function the usual way and may be referenced and used both in Node-RED and in Home Assistant.

MfG Alex.

Yes, a similar solution should be possible using this plugin, since they added the custom integration feature a few weeks after my post :slight_smile:

1 Like

I was looking for a solution like this. Reading the idea above triggered an alternative that’s nearly the same idea, but allows me to skip posting to node red.

You can send the message from the hassio automation over mqtt to node red.

Only easier if you already use mqtt, and I use it for as much as I can to give me the most flexibility of where things run. Thanks for putting together your solution and helping spark another!

Also I didn’t fully understand how you fixed the single quote problem and the bug is still opened. I solved it with a “function” node in node-red with the following few lines:

var originalPayload = msg.payload;
var fixedPayloadJson = originalPayload.replace(/'/g, '"');
var payload = JSON.parse(fixedPayloadJson);

What I’m trying to figure out is if you already have a webhook in HA will this websocket work with it?

Webhook in HA
When webhook called start node-red node

If so I’m still trying to figure that out.

1 Like