Node-RED for the Rest of Us: Automations Without a Cloud
A visual flow editor that turns fiddly home-automation logic into something you can actually read

Contents
Every home-automation platform eventually asks you to write logic, and every one of them makes the simple things easy and the interesting things awful. “Turn the porch light on at sunset” is a one-liner anywhere. “Turn the hall light on when someone comes home after dark, but only if it isn’t already on, and dim it instead of blazing if it’s past midnight, and don’t touch it at all if the film-night scene is active” is where the wheels come off. In a YAML automation that logic becomes a nest of condition: blocks and templating that you cannot read a fortnight later, let alone hand to anyone else in the house.
Node-RED is the tool I reach for when the logic outgrows a tidy automation. It’s a visual flow editor — you drag nodes onto a canvas and wire them together — and its great trick is that a complicated automation stops being a wall of indentation and becomes a diagram you can follow with your finger. It runs entirely on your own hardware, talks to Home Assistant over a local WebSocket, and never phones home. Let me explain why the visual model earns its keep before I show you how to wire it up.
Why a flow editor beats text for this particular job
Home-automation logic has an awkward shape. It’s event-driven, it’s full of branching, and it’s stateful — the right action often depends on what time it is, who’s home, and what happened thirty seconds ago. Text-based automation formats handle the linear case beautifully and then fall apart exactly when the branching gets interesting, because a branch in text is an indented block, and three levels of nested branches is unreadable no matter how disciplined you are.
A flow makes the branching physical. A decision node has two output ports, and you can see the true path and the false path leave it as separate wires going to separate places. When something misbehaves you don’t read code and hold state in your head; you watch a message travel the wires. Node-RED even lights up the node that’s currently firing and lets you drop a debug node onto any wire to see exactly what’s passing through it. For the class of automation where the question is always “why did it do that?”, being able to watch the message flow is worth more than any amount of clever syntax.
The honest counterpoint: for simple, linear rules, a flow is more clicking than a three-line automation deserves, and a canvas of forty flows can become its own kind of spaghetti if you’re undisciplined. Node-RED rewards you when the logic is genuinely branchy and punishes you when it isn’t. I keep the trivial stuff in Home Assistant automations and reach for Node-RED when I catch myself fighting the YAML.
What you’re actually installing
Node-RED is a Node.js application. If you run Home Assistant OS, the easiest route is the official community add-on, which handles authentication and the local connection for you. If you run Home Assistant Container or a separate box, run Node-RED as its own container and point it at Home Assistant over the network. Either way the thing to install on top is the node-red-contrib-home-assistant-websocket palette, which is the set of nodes that lets flows listen to and control Home Assistant entities.
Here’s a minimal standalone deployment with the Home Assistant nodes baked in:
| |
The ./data volume is the whole of your Node-RED brain — flows, credentials, installed palettes. Back it up and a lost container costs you nothing. Lose it and you rebuild every flow by hand, so this is the first thing to add to your backup routine.
Wiring it to Home Assistant
The connection is a WebSocket to Home Assistant’s local API, authenticated with a long-lived access token. In Home Assistant, go to your user profile, scroll to the bottom and create a long-lived access token, then paste it into the Node-RED server configuration along with the local address of your Home Assistant instance — something like http://homeassistant.local:8123 or the container’s address on your Docker network. Keep it on the local address. There is no reason for this traffic to leave your LAN, and every reason for it not to.
Once connected, four node types cover most of what you’ll ever do:
- events: state — fires when an entity changes state. This is your trigger for “the motion sensor went active” or “the door opened”.
- current state — reads an entity’s state on demand, for conditions like “only if the sun is below the horizon”.
- call service — the action end: turn a light on, set a brightness, activate a scene.
- trigger: state — like events, but with built-in “for X seconds” timing, which saves you a pile of delay plumbing.
That small set plus a switch node (routing) and a change node (setting values) is genuinely enough to express most household logic. The presence and motion events you feed in are only as good as the sensors behind them, which is why it’s worth pairing Node-RED with proper mmWave presence sensors rather than cheap PIR — the flow can only reason about what the sensor bothers to report.
A flow worth building: the “don’t blind me at 3am” light
Let me make the earlier example concrete, because it shows why the visual model pays off. The goal: when the hall motion sensor goes active, turn on the hall light, but dim and warm at night, full brightness by day, and never override an active scene.
In Node-RED that’s an events: state node watching the motion sensor, feeding a current state check on the scene helper (route to a dead end if the film-night scene is on), into a switch node that reads the current hour. Before 07:00 or after 23:00 the message takes the “night” wire to a call service node that sets brightness to 15% and a warm colour temperature; otherwise it takes the “day” wire to a node that sets full brightness. A separate trigger: state node watches the same sensor and, after two minutes of no motion, sends the off command.
Written out in prose that’s a mouthful. On the canvas it’s six nodes and you can see the night path and the day path diverge. When your partner asks why the light came on dim, you point at the wire. That legibility is the entire argument for the tool.
Subflows and context: the two features that make it scale
Two Node-RED features turn a canvas from a novelty into something you’d trust with a whole house, and both are easy to miss when you start.
The first is the subflow. If you find yourself building the same five-node pattern for every room — motion in, time-of-day check, brightness decision, light out — you can collapse that pattern into a single reusable subflow node, parameterised so each room passes its own sensor and light entity. Now the logic lives in one place. Fix a bug once and every room inherits the fix, instead of hunting the same mistake through fifteen near-identical copies. This is the ordinary discipline of not repeating yourself, and Node-RED supports it properly once you know to look.
The second is context storage, which is how a flow remembers things between messages. By default context lives in memory and evaporates on restart, which is fine for “was the last motion event more than a minute ago” but useless for state you want to survive a reboot. Node-RED lets you configure a file-backed context store in settings.js, so a flag like “holiday mode is on” or “the last known house state” persists across restarts. Getting this wrong is a common source of the “my automation forgot everything after an update” complaint. Decide deliberately which state is disposable and which needs the file store, and label it so future-you knows why.
There’s a performance note worth internalising too: a flow that fires on every state change of a chatty entity can generate a surprising amount of work. Filter early — put a check right after the trigger that drops messages you don’t care about — so the expensive nodes downstream only run when they should. A flow that does nothing ninety-nine times to act once should spend almost no effort on the ninety-nine.
Keeping it local and keeping it honest
The reason I like Node-RED in a homelab is that it keeps the intelligence on hardware you own. The flows run on your box, the Home Assistant connection is a LAN WebSocket, and no automation depends on a vendor’s servers staying up or a subscription staying paid. If your internet drops, your lights still respond to motion, because the whole loop — sensor to mesh to Home Assistant to Node-RED and back — never leaves the building.
A few habits keep it maintainable. Give every flow tab a clear name and every important node a label, because a canvas full of “function 12” nodes ages badly. Use link nodes to jump between tabs instead of dragging wires across the whole workspace. And export your flows to the git repository that holds the rest of your infrastructure config — a flow is just JSON, it diffs cleanly, and having it in version control means a bad edit is one git checkout away from undone.
One security note that catches people: if you expose the Node-RED editor, lock it down. The editor can call any Home Assistant service, so an unauthenticated editor on your network is a skeleton key to the whole house. The add-on handles this; a standalone container does not by default. Set editor authentication in settings.js and never map port 1880 to the public internet.
Troubleshooting the usual snags
The Home Assistant nodes show “disconnected” or “connecting” forever. The token or the address is wrong. Long-lived tokens don’t expire, but they’re revoked if you delete them in the profile, and it’s easy to paste one with a trailing space. Re-create the token, re-enter the server URL as the local address (not a Nabu Casa cloud URL), and confirm Node-RED can actually reach Home Assistant on the network — a container on a different Docker network can’t see homeassistant.local.
A flow deploys but nothing happens. Drop a debug node on the wire straight after your trigger and watch the sidebar. If nothing arrives when the sensor changes, the events: state entity ID is wrong or the sensor isn’t reporting. If a message arrives but no action fires, the fault is downstream — a switch node with no matching rule silently swallows the message, which is the single most common “why is this flow dead” cause.
Everything worked, then a restart lost my flows. Your /data volume isn’t persistent, or you edited flows inside a container whose filesystem is ephemeral. Confirm the volume mount, and get that directory into backups today.
Timing is off — the light turns off while I’m still in the room. You’re using a fixed delay instead of retriggering. Use the trigger: state node’s “reset” behaviour so that fresh motion restarts the countdown, rather than a plain delay that fires regardless. This is the classic bug that also plagues plain PIR setups, and it’s the exact problem better presence sensing solves.
The verdict
Node-RED earns its place the moment your automation logic gets branchy enough that you’re squinting at YAML. For linear rules it’s overkill and the built-in automations are faster to write, so don’t rip those out. For the tangled, stateful, “only if, unless, except when” logic that makes a home feel genuinely smart rather than merely scheduled, a flow you can watch messages travel through will save you hours of debugging over its life.
It’s local, it’s version-controllable, it survives vendors going under, and it makes the hardest part of home automation legible. Install it the day you first swear at a nested condition block. You’ll wonder why you waited.



