Example Flows

Nine complete automations, block by block: which trigger, how the chain is wired, why it is shaped that way, and the mistake each one is avoiding.

Every flow here is built from the blocks on the Trigger Reference and Action Reference pages, plus ordinary RUAL blocks you already use. Device references are written as an exact device name (Hallway ceiling) because that reads better on a page; a platform id such as zigbee:0x00158d0007e1a2b3 works identically and is what a picker writes.

The pattern to notice across all nine: the trigger does the filtering. Its device and room pins are how you narrow the flow, not an if in the chain. A flow that fires for the whole house and then discards most events runs hundreds of times an hour for nothing.

1. Motion Lights, But Only After Dark

What it does. Somebody walks into the hallway. If the hallway is actually dark, the light comes on, warm and dimmed. If it is the middle of the afternoon, nothing happens.

BlockWired
presence detectedroom Hallway, device pin empty
get device statedevice from the trigger's device id, capability illuminance
<the state block's number pin against 30
branchflow from the state block, condition from the comparison
light onon the branch's true pin, device Hallway ceiling, brightness 40, kelvin 2700

Why it is shaped this way. Three decisions are doing work here.

The trigger's device pin is empty and its room pin is set, so this is one block for every motion sensor in the hallway rather than one flow per sensor. Add a second sensor to the room later and it works with no edit.

The illuminance reading comes from the sensor that fired, by feeding the trigger's device id straight into get device state. Most motion sensors report lux as well as occupancy, and the reading from that sensor is the one that describes the space it is watching. Hard-coding a different sensor's id would work too, and is what you want when the motion sensor has no light meter.

Brightness and kelvin ride along on light on rather than being set by a following light dim. One command means the lamp transitions once instead of visibly jumping to full white first.

The mistake it avoids. Because presence detected fires on the transition, somebody standing still in the hallway does not re-run this every thirty seconds. Without that, the flow would fight anyone who dimmed the lamp by hand.

Turning it off again is a separate flow, deliberately: presence cleared, room Hallway, into light off. Two small flows read better on the canvas than one with a branch, and they are independently editable. Remember that the delay before presence cleared fires is the sensor's own stillness timeout, usually 60 to 120 seconds, not something RUAL controls. If you want longer, put a delay block in front of the light off.

The most common automation there is. presence detected fires on the false to true transition of occupancy in the hallway, so somebody standing still does not re-run it. Its device id feeds get device state, which reads the illuminance the same sensor reports; < compares that against 30 lux and branch only lets light on run when the room is actually dark.

Studio canvas example for the presence detected block: motion, but only after dark.

2. A Door Opens While Nobody Is Home

What it does. The front door opens. If no motion sensor anywhere in the house currently reads occupied, that is worth a notification.

BlockWired
contact openeddevice Front door
get active sensorssensor type occupancy, room empty
==the query's count against 0
branchtrue pin into the notification of your choice

Why it is shaped this way. "Nobody home" is not a state RUAL tracks, and inventing one would be a worse answer than deriving it. get active sensors reads the in-memory registry, so asking the question costs nothing, and asking it at the moment the door opened is more accurate than a flag some other flow maintains.

Use the trigger's name and at pins in the message. "Front door opened at 14:32" is a useful notification; "a contact sensor changed" is not.

The honest limit. A sensor the node has not heard from at all is excluded from get active sensors rather than counted as inactive. Last-known state is restored at boot, so a restart no longer makes this flow believe the house is empty, but a device that has never reported since it was adopted is still invisible to it. Occupancy also decays: a sensor that read occupied when the node went down is restored as occupied, and stays that way until it reports again.

Occupancy is not presence. A motion sensor reads false when a person sits still, so this fires if the door opens while somebody is reading on the sofa out of view. A phone or a presence-detection radar such as an FP2 (which reports occupancy continuously rather than on motion) is the better input if false positives are unacceptable.

3. A Leak Shuts the Valve and Raises an Alarm

What it does. A leak sensor finds water. A smart valve closes, and somebody is told.

BlockWired
water leak detecteddevice and room both empty: any leak sensor anywhere
switch offdevice Water valve
branchcondition from the switch block's ok pin
notificationon both branches, with different text

Why it is shaped this way. The device pin is empty on purpose. A leak is a leak, wherever it is, and enumerating sensors here means the one you install next year is not covered.

Shutting the valve comes before the notification, because the valve is the part that limits the damage and the flow should not be waiting on an email API to do it.

The branch on ok is the important part, and it is what separates this from a flow that looks fine and is useless. If the valve did not respond, the notification must say so. "Leak detected in the basement, valve closed" and "Leak detected in the basement, VALVE DID NOT RESPOND" call for different human behaviour, and a flow that sends the first message in both cases is worse than one that sends neither.

Repeated firing is not a problem here. The trigger fires on the transition, so a sensor sitting in a puddle and re-reporting every thirty seconds does not send thirty notifications. It fires again only if the sensor dries out and gets wet again, which is information.

Say this out loud when you build it: this supplements a plumber and a water alarm, it does not replace them. It depends on a mesh, a broker and a node all being up.

4. A Double Press Sets a Scene

What it does. One button, three behaviours: single press toggles the lamp, double press sets a warm evening scene across several lamps, hold turns everything off.

BlockWired
button presseddevice Living room button
switchexpression from the trigger's value pin
light toggleon the single case
three light on blockson the double case, each with its own brightness and kelvin
light off blockson the hold case

Why it is shaped this way. A scene in RUAL is not an object, it is a few blocks in a row. That is more typing than picking a saved scene in a vendor app, and it buys something: the scene can read the time, check who is home, or vary by season, because it is a flow rather than a stored list of values.

Find out what your button actually sends before you write the cases. The strings on the value pin are the device's own. Aqara buttons send single, double and hold; a Hue dimmer sends initial_press, short_release and long_release; a four-button remote prefixes each with the button number. Press it once with the Zigbee2MQTT frontend open, or read get device and look at state.action, and use what it sends rather than what it ought to.

Why every press fires. Button events are momentary pulses: the device publishes double and stays double until the next press. Every other trigger in this section requires the value to have changed, which would swallow a second identical press. button pressed is the deliberate exception, so pressing the same button twice fires the flow twice.

5. A Temperature Crossing Runs the Air Conditioning

What it does. The office passes 25 °C during the day, so the air conditioner starts cooling to 22 on a quiet fan. When it drops back below 22, the unit goes off.

BlockWired
temperature changedroom Office, above 25
climate setdevice Office AC, mode cool, target 22, fan silent
and a second, separate flow:
temperature changedroom Office, below 22
climate setdevice Office AC, mode off

Why it is shaped this way. This is the flow that most obviously depends on crossing semantics. A thermometer reporting every minute on a hot afternoon produces hundreds of readings above 25, and a trigger that fired on the level would send hundreds of identical commands to the air conditioner. Because above fires on the crossing, the unit is told once.

The two thresholds are deliberately different numbers. Cooling starts at 25 and stops at 22, so the unit is not switching on and off every time the reading wobbles across a single value. That gap is hysteresis, and picking it is the actual design work in this flow; the platform will not invent it for you.

Mode, target and fan are set in one climate set rather than three. On a Daikin unit that matters more than it looks: its local API rejects partial updates and resets anything you leave out, so the adapter reads the whole control block, applies your changes and writes it back as one operation. Three separate blocks would be three read-modify-write cycles, and an occupant can hear the unit stepping through them.

Setting a mode other than off implies switching the unit on, so there is no separate power step.

The honest limit. The Daikin adapter polls: with the default 30 second interval, the temperature RUAL reacts to can be up to 30 seconds old, and "the AC turned on" fires up to an interval late. For an air conditioner that is fine. A Zigbee or Z-Wave thermostat pushes and has no such delay.

6. Energy Over a Threshold Raises a Warning

What it does. A metering plug reports power draw. When something on that circuit pulls more than 2400 W for more than a minute, warn.

BlockWired
device state changeddevice Utility plug
==the trigger's capability pin against power_watts
>the trigger's value pin against 2400
ANDboth conditions
branch, delay, get device state, >, branchwait 60 seconds, re-read power_watts, and only warn if it is still high

Why it is shaped this way. There is no power changed trigger. Only temperature and humidity have dedicated threshold triggers, so anything else numeric goes through device state changed with a check on the capability pin. The catch-all is high volume across the whole house, which is exactly why the device pin is set here: narrowed to one plug, it fires only for that plug's reports.

The delay-then-recheck is the point of the flow. A kettle pulls 2400 W for ninety seconds every morning and that is not news. What you want to know about is a load that is still drawing when you look again. Doing that with a second read rather than with a stateful counter keeps the flow readable and stateless: the registry already knows the current value, so re-asking is free.

Watch the concurrency budget. Device triggers run at most 16 flows at once, and beyond that events are dropped with a log line rather than queued. A delay inside a device flow holds one of those 16 slots for its whole duration. One minute is fine. If you find yourself wanting ten, move the waiting into the queue or a repeating event, and keep the device flow short.

For totals rather than spikes, do not accumulate in a flow at all. The node already records energy at a fixed interval and can hand you consumption per hour or per day: see Device History and Energy.

7. The House Empties, Every Light Goes Off

What it does. The last occupancy sensor clears. Every light that is currently on is switched off, without naming any of them.

BlockWired
presence cleareddevice and room empty
get active sensorssensor type occupancy
== and branchcount against 0: nobody anywhere
get lights that are onroom empty
select field valuesthe devices array, field id
mapthat array of ids
execute and light offthe map's function pin drives the execute block, whose value pin is one device id, into the light off device pin

Why it is shaped this way. This is the flow the query blocks exist for. get lights that are on reads the in-memory registry with no network call, so iterating it costs nothing, and the result is derived from the live state rather than from a list of light names you maintain by hand. Install a lamp next year and it is covered.

select field values sits between the query and the map because the query returns device objects and light off wants a device reference. Pulling the id field out first gives the map an array of plain ids, which is exactly what the execute block's value pin then carries into each command.

Only lights that are actually on are touched, which is the difference between this and looping over every light. Lamps whose state the node does not know are excluded rather than assumed off, so the flow never blasts commands at the whole house on a guess.

The re-check on the trigger matters. presence cleared with an empty device pin fires when any sensor clears, which happens constantly in an occupied house as people move between rooms. Asking get active sensors whether anything is still occupied, and stopping if it is, is what turns "a sensor cleared" into "the house is empty".

For a per-room version, set the room pin on both the trigger and the query, and drop the whole-house check.

get lights that are on reads the in-memory device registry, so it makes no network call and is cheap enough to run in a loop. It returns only lights whose power state is KNOWN and true: a lamp the node has not heard from since it started is left out rather than assumed off. The count pin feeds > and branch, which is the shape of "if anything is still on, do something about it".

Studio canvas example for the get lights that are on block: asking the registry which lights are on.

8. Auto-Adopting a New Device Into a Room

What it does. While you are commissioning a floor, every device that pairs is adopted, named after its model and placed in the room you are working in, without a trip to the API for each one.

BlockWired
device discovereddevice and room empty
get value variablea variable holding the room you are commissioning, for example commissioning_room
device adoptdevice from the trigger's device id, room from that variable, name from the trigger's name pin
branchon the adopt block's newly pin, into a notification or a log document

Why it is shaped this way. device discovered is the only trigger that fires for unadopted devices, which is what makes this possible at all. It fires once per device, the first time it is ever seen.

device adopt takes name and room pins so the whole onboarding is one block rather than an adopt followed by a rename. Internally it does adopt first and rename second, and if the rename fails the adoption still stands and is reported honestly: ok stays true, and the error pin carries the rename failure. That ordering exists precisely so a failed rename does not leave a half-onboarded device behind.

Branch on newly, not on ok. Adoption is idempotent, so calling it on an already-adopted device succeeds with newly = false. Notifying on ok would announce devices that were already yours.

Prefer this to auto_adopt = true. The config flag adopts everything the radio can hear, forever, including whatever a neighbour paired to a mesh that overlaps yours. This flow is a switch you control: keep it deactivated, activate it while you are physically commissioning, and deactivate it when you are done. The trigger index rebuilds when you save, so activating and deactivating takes effect without a restart.

A safer variant: instead of adopting, notify. Wire device discovered into a notification carrying the trigger's name and device id, and adopt from the studio when you recognise it. That is the right default for a mesh that is not brand new, because the devices a coordinator hears are not all yours.

9. An Unknown Card at Three in the Morning

What it does. Somebody is refused at the front door. During the day that is a colleague with an expired badge and not worth a message; at night it is worth waking up for. Meanwhile every successful entry is logged with who and how.

BlockWired
access denieddevice empty: any door
date now, branchoutside working hours
notificationwith the trigger's name, actor and method pins
and a second flow, for the log:
access granteddevice empty
create documenta storage document carrying actor, method, name and at

Why it is shaped this way. The actor, method and granted pins are on the trigger itself rather than inside the device object, because "someone came in" is only useful with who and how. A notification that says "Access denied at Front door, unknown card, nfc" is actionable; "a door reported something" is not.

Denied and swiped are different triggers. access denied fires on the decision. If you want to react to a specific card regardless of what the door decided, for example turning on the porch light when a particular fob is tapped, use NFC card swiped with its card pin, which fires when the card is presented and before the decision.

Write the log with a flow, not with history. Access events are recorded in device history as events, so they are not lost, but "who came in last month" is a table you will want to query, filter and export. A storage document per entry gives you that; the analytics store is shaped for charts.

The gate variant: doorbell rang into gate open is one wire and a genuinely bad idea. Put a branch in between that checks something: a delivery window, a person at home, a specific NFC token. A doorbell is an unauthenticated button anyone can press.

Patterns Worth Reusing

When you wantReach for
One flow covering a whole room or the whole houseLeave the trigger's device pin empty, set the room pin
A condition about the world right nowA query block: they read memory and cost nothing
To know whether a command actually landedBranch on ok, not on the returned device state
To act on a device the trigger identifiedFeed the trigger's device id into the next block's device pin
To act on many devices at onceQuery, select field values for id, map, command
A numeric capability with no dedicated triggerdevice state changed narrowed to one device, then check the capability pin
To avoid reacting to a momentary spikedelay, then re-read with get device state
To answer a question about the pastget device history, not a variable you maintain

Next Steps

Device History and Energy Questions about the past: what is recorded, and how to read it back. Troubleshooting When one of these does not fire, this is the checklist. Common Blueprint Patterns The general-purpose shapes these flows are built from.

Frequently asked

How do I turn on a light only when it is dark in RUAL?

Wire presence detected into get device state reading the illuminance capability of the same sensor, compare that number against a lux threshold, and put a branch in front of light on. Most motion sensors report lux as well as occupancy, and the reading from the sensor that fired describes the space it is watching.

How do I turn off every light in RUAL without naming them?

Feed get lights that are on into select field values for the id field, then into map, and drive light off from the execute block inside the map. The query returns only lights whose power state is known and true, so lamps the node has not heard from are left alone rather than blasted with commands.

How do I automatically adopt new smart home devices in RUAL?

Wire the device discovered trigger into the device adopt block, which takes optional name and room pins so onboarding is a single block. Keep that flow deactivated and activate it only while you are physically commissioning hardware, which is safer than the auto_adopt config flag because that adopts everything the radio can hear, forever.