node-red-automation

Installation
SKILL.md

Node-RED Automation

Expert guidance for Node-RED flow-based programming and automation.

When to Use This Skill

  • Building smart home automations
  • Creating API integrations
  • Processing MQTT messages
  • Building dashboards
  • Connecting services and devices
  • Data transformation and routing

Installation

# docker-compose.yml
version: '3.8'
services:
  nodered:
    image: nodered/node-red:latest
    ports:
      - "1880:1880"
    volumes:
      - ./node-red-data:/data
    environment:
      - TZ=America/New_York
    restart: unless-stopped
# Native installation
npm install -g node-red

# Start
node-red

# Access at http://localhost:1880

Settings Configuration

// settings.js
module.exports = {
    flowFile: 'flows.json',

    // Admin auth
    adminAuth: {
        type: "credentials",
        users: [{
            username: "admin",
            password: "$2b$08$...", // bcrypt hash
            permissions: "*"
        }]
    },

    // HTTP node auth
    httpNodeAuth: {
        user: "user",
        pass: "$2b$08$..."
    },

    // HTTPS
    https: {
        key: require("fs").readFileSync('privkey.pem'),
        cert: require("fs").readFileSync('cert.pem')
    },

    // Context storage
    contextStorage: {
        default: {
            module: "localfilesystem"
        }
    },

    // Function global context
    functionGlobalContext: {
        moment: require('moment')
    }
}

Core Nodes

Inject Node

// Triggered flow
{
  "payload": "Hello",
  "topic": "greeting",
  "repeat": "60",  // Every 60 seconds
  "crontab": "0 9 * * 1-5"  // 9 AM weekdays
}

Function Node

// Basic transformation
msg.payload = msg.payload.toUpperCase();
return msg;

// Multiple outputs
var msg1 = { payload: msg.payload.value1 };
var msg2 = { payload: msg.payload.value2 };
return [msg1, msg2];

// Conditional routing
if (msg.payload > 100) {
    return [msg, null];  // Output 1
} else {
    return [null, msg];  // Output 2
}

// Access context
var count = context.get('count') || 0;
count++;
context.set('count', count);
msg.payload = count;
return msg;

// Flow context (shared across flow)
var data = flow.get('sharedData');

// Global context (shared across all flows)
var config = global.get('config');

// Async operation
node.send(msg);
return null;  // Don't send twice

// Status
node.status({fill:"green", shape:"dot", text:"OK"});
node.status({fill:"red", shape:"ring", text:"Error"});

Switch Node

// Route based on property
{
  "property": "payload.type",
  "rules": [
    {"t": "eq", "v": "temperature"},
    {"t": "eq", "v": "humidity"},
    {"t": "regex", "v": "^error"},
    {"t": "else"}
  ]
}

Change Node

// Set, change, delete, move properties
{
  "rules": [
    {"t": "set", "p": "payload.processed", "to": true},
    {"t": "change", "p": "payload", "from": "foo", "to": "bar"},
    {"t": "delete", "p": "payload.temp"},
    {"t": "move", "p": "payload.old", "to": "payload.new"}
  ]
}

Template Node

Temperature: {{payload.temperature}}°C
Humidity: {{payload.humidity}}%
Time: {{timestamp}}

{{#payload.items}}
  - {{name}}: {{value}}
{{/payload.items}}

MQTT Integration

MQTT Nodes

// MQTT In
{
  "topic": "sensors/+/temperature",
  "qos": 1,
  "broker": "mqtt-broker-config"
}

// MQTT Out
{
  "topic": "commands/device1",
  "qos": 1,
  "retain": true
}

Flow: MQTT Processing

// Function: Parse MQTT message
var topic = msg.topic;
var parts = topic.split('/');
var deviceId = parts[1];
var measurement = parts[2];

msg.payload = {
    device: deviceId,
    type: measurement,
    value: parseFloat(msg.payload),
    timestamp: new Date().toISOString()
};
return msg;

Home Assistant Integration

Home Assistant Palette

# Install node-red-contrib-home-assistant-websocket
cd ~/.node-red
npm install node-red-contrib-home-assistant-websocket

HA Nodes

// Events: state
{
  "entity_id": "sensor.temperature",
  "state_type": "str",
  "halt_if": "",
  "halt_if_compare": "is"
}

// Call service
{
  "domain": "light",
  "service": "turn_on",
  "data": {
    "entity_id": "light.living_room",
    "brightness_pct": 80
  }
}

// Get current state
{
  "entity_id": "sensor.temperature",
  "state_type": "num"
}

Flow: Motion Light Automation

// When motion detected, turn on light for 5 minutes
// Events: state node -> Function -> Call service

// Function node
if (msg.payload === 'on') {
    msg.payload = {
        domain: 'light',
        service: 'turn_on',
        data: {
            entity_id: 'light.hallway',
            brightness_pct: 100
        }
    };

    // Schedule turn off
    var turnOff = {
        payload: {
            domain: 'light',
            service: 'turn_off',
            data: { entity_id: 'light.hallway' }
        }
    };
    setTimeout(() => node.send([null, turnOff]), 300000);

    return msg;
}
return null;

HTTP Integration

HTTP Nodes

// HTTP In - Create endpoint
// Method: GET, POST, etc.
// URL: /api/data

// HTTP Request - Call external API
{
  "method": "GET",
  "url": "https://api.example.com/data",
  "headers": {
    "Authorization": "Bearer {{flow.token}}"
  }
}

// HTTP Response
msg.statusCode = 200;
msg.headers = { "Content-Type": "application/json" };
msg.payload = { status: "ok" };
return msg;

Flow: REST API Endpoint

// HTTP In -> Function -> HTTP Response

// Function: Handle API request
var response = {
    success: true,
    data: [],
    timestamp: new Date().toISOString()
};

switch (msg.req.method) {
    case 'GET':
        response.data = flow.get('items') || [];
        break;
    case 'POST':
        var items = flow.get('items') || [];
        items.push(msg.payload);
        flow.set('items', items);
        response.data = msg.payload;
        break;
}

msg.payload = response;
return msg;

Dashboard (node-red-dashboard)

npm install node-red-dashboard
// Button
{
  "group": "Home",
  "name": "Toggle Light",
  "topic": "light/toggle",
  "payload": "toggle"
}

// Gauge
{
  "group": "Sensors",
  "name": "Temperature",
  "min": 0,
  "max": 50,
  "units": "°C"
}

// Chart
{
  "group": "Sensors",
  "name": "Temperature History",
  "xaxis": "time",
  "yaxis": "value"
}

// Text Input
{
  "group": "Settings",
  "name": "Set Threshold",
  "mode": "number"
}

Common Patterns

Debounce

// Prevent rapid-fire messages
var timeout = context.get('timeout');
if (timeout) {
    clearTimeout(timeout);
}
context.set('timeout', setTimeout(() => {
    node.send(msg);
}, 1000));
return null;

Rate Limiting

// Limit to 1 message per 5 seconds
var lastTime = context.get('lastTime') || 0;
var now = Date.now();

if (now - lastTime > 5000) {
    context.set('lastTime', now);
    return msg;
}
return null;

State Machine

var state = flow.get('state') || 'idle';
var event = msg.payload;

switch(state) {
    case 'idle':
        if (event === 'start') {
            flow.set('state', 'running');
            msg.payload = 'Started';
            return msg;
        }
        break;
    case 'running':
        if (event === 'stop') {
            flow.set('state', 'idle');
            msg.payload = 'Stopped';
            return msg;
        }
        break;
}
return null;

Aggregation

// Collect messages and send batch
var buffer = context.get('buffer') || [];
buffer.push(msg.payload);

if (buffer.length >= 10) {
    msg.payload = buffer;
    context.set('buffer', []);
    return msg;
}

context.set('buffer', buffer);
return null;

Error Handling

// Try-catch in function
try {
    var data = JSON.parse(msg.payload);
    msg.payload = data;
    return [msg, null];  // Success output
} catch (e) {
    msg.payload = { error: e.message };
    return [null, msg];  // Error output
}

// Catch node - catches errors from nodes
// Complete node - triggers on node completion

Subflows

// Create reusable flow components
// Right-click -> Subflow -> Create subflow
// Add inputs/outputs
// Set environment variables

// Access subflow env in function:
var threshold = env.get('THRESHOLD') || 100;

Best Practices

  1. Name all nodes for readability
  2. Use link nodes to connect distant flows
  3. Add comments with Comment nodes
  4. Use subflows for reusable logic
  5. Handle errors with Catch nodes
  6. Use context storage for persistence
  7. Organize flows in tabs
  8. Version control flows.json
  9. Add status to function nodes
  10. Test with inject nodes
Related skills
Installs
4
GitHub Stars
2
First Seen
Mar 15, 2026