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!