How To: API for Docker Node-RED to Send Commands to Host System

To enable Node-RED to issue commands to the host OS, an API is needed on the host side (outside of Docker) to receive those commands. The API in this example will listen on Port 5001. and is a simple one that reboots the Edge PC.

NOTICE: This example does not address any security issues that may arise from Port 5001 being open to outside commands.

This example assumes that Node-RED is already installed and running on your system and that Python3 is already installed (which should be the case for the Edge PC).

Example project:

1) Install Flask:

apt install python3-flask

2) Create Python API program that will run on the host (outside of Docker) called host_listener.py.

from flask import Flask, request
import subprocess
import os

app = Flask(__name__)

@app.route('/execute', methods=['POST'])
def execute_command():
    data = request.get_json()
    command = data.get('command', '')
    if not command:
        return {"error": "No command provided"}, 400
    
    try:
        result = subprocess.check_output(command, shell=True, text=True, stderr=subprocess.STDOUT)
        return {"output": result, "status": "success"}, 200
    except subprocess.CalledProcessError as e:
        return {"output": e.output, "status": "error"}, 400
    except Exception as e:
        return {"error": str(e)}, 500

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001, debug=True)

3) Run the python script

python3 host_listener.py

4) Create a Flow in Node-RED:

o Inject Node:
Payload: String, e.g., reboot (or any command you want to test).
Set to trigger once or repeatable for testing.
o Function Node:
Name: Prepare Command
Code:
msg.payload = {
    "command": msg.payload
};
return msg;

o HTTP Request Node:
Method: POST
URL: http://localhost:5001/execute (or http://host.docker.internal:5001/execute for Docker Desktop, or your host IP)
Return: Parsed JSON
Name: Send to Host

If localhost:5001 doesn’t work, use the IP address of the Edge PC: 192.168.xxx.xxx:5001

o Debug Node:
Output: msg.payload
Name: Response

Here is the JSON for the Node-RED flow is you just want to import (change the IP address to match your project):

[
    {
        "id": "f6f2187d.f17ca8",
        "type": "tab",
        "label": "Flow 1",
        "disabled": false,
        "info": ""
    },
    {
        "id": "ba7defd8fdcc877c",
        "type": "inject",
        "z": "f6f2187d.f17ca8",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "reboot",
        "payloadType": "str",
        "x": 150,
        "y": 100,
        "wires": [
            [
                "8c8d1d2f813c9c5e"
            ]
        ]
    },
    {
        "id": "8c8d1d2f813c9c5e",
        "type": "function",
        "z": "f6f2187d.f17ca8",
        "name": "Prepare Command",
        "func": "msg.payload = {\"command\": msg.payload };\n\nreturn msg;\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 350,
        "y": 100,
        "wires": [
            [
                "04a4210843555f0d"
            ]
        ]
    },
    {
        "id": "b0de6f6e5be63d17",
        "type": "debug",
        "z": "f6f2187d.f17ca8",
        "name": "response",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 760,
        "y": 100,
        "wires": []
    },
    {
        "id": "04a4210843555f0d",
        "type": "http request",
        "z": "f6f2187d.f17ca8",
        "name": "Send to Host",
        "method": "POST",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "192.168.1.222:5001/execute",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "",
        "senderr": false,
        "headers": [],
        "x": 570,
        "y": 100,
        "wires": [
            [
                "b0de6f6e5be63d17"
            ]
        ]
    }
]

Give it a try!

2 Likes