Local Home Automation

A RUAL node is the gateway itself, not a client of somebody else's cloud. Zigbee, Thread/Matter, Z-Wave, Philips Hue and Daikin devices become one vocabulary that blueprints automate.

A RUAL node can run the building it sits in. Lights, sensors, buttons, plugs, blinds, locks and climate are adopted into the same document model as everything else on the node, and driven by the same blueprints you already write for pages and endpoints. Nothing goes through a vendor cloud, and nothing stops working when the internet does.

This section is the working reference for that. If you have not set the hardware up yet, start at Setting Up a RUAL Nano. If you want the shorter tour first, read the Local Home Automation tutorial; this section goes considerably deeper.

The Node Is the Gateway

The ordinary way to reach a smart device from software is to call the vendor's cloud API. Your flow talks to a server in another country, that server talks to a bridge in your hallway, and the bridge talks to a lamp two metres away. That is a round trip measured in hundreds of milliseconds on a good day, it needs an account and a rate limit, and it fails whenever any hop between you and the vendor does.

Here the node is the thing the devices talk to. Radios are attached to the machine, the protocol daemons run beside the node, and a motion event reaches your flow over a loopback socket. The distinction shows up in three places you will notice:

Vendor cloud integrationLocal, on the node
A motion event reaching a flowSensor to bridge to vendor cloud to your clusterSensor to radio to node, in process
Internet downNothing worksEverything works
Vendor retires the APIYour automation stopsUnaffected: the radio protocol has not changed
Who can see your floor planThe vendor, by constructionNobody outside the LAN

Aqara is the clearest case. Aqara devices are Zigbee devices. Reached through Aqara's cloud API, turning on a lamp is a request to Germany and back; paired to a coordinator on your own machine, they are just devices on your mesh. That is why the older aqaraconnection_* blocks are deprecated in favour of these.

What the Node Does Not Do

rual-core does not drive the radios itself, and that is a deliberate line. A native Zigbee stack means owning EZSP and Z-Stack framing, the ZCL cluster library, mesh routing, OTA updates and a device quirks database: years of work whose output would be a worse Zigbee2MQTT. So the node sits behind the reference daemon for each protocol and translates.

ProtocolRuns beside the nodeThe node reaches it over
ZigbeeZigbee2MQTT, against a USB coordinatorMQTT
Z-WaveZ-Wave JS UI, against a Z-Wave stickMQTT, gateway type ValueID topics
Thread and MatterMatter Server, plus a Thread Border Router for Thread devicesWebSocket
Philips Huethe bridge you already ownHTTPS and its event stream, on the LAN
Daikinthe BRP WiFi adapter in the unitHTTP, on the LAN, polled
UniFi Access and Protectthe UniFi console you already ownUbiquiti's integration APIs and their event streams, on the LAN

The payoff is the device knowledge base. Zigbee2MQTT publishes a complete machine-readable description of every device on your mesh: every attribute it reports, with type, unit and range, for roughly 4,000 devices, maintained by people who own the hardware. The node reads that directly, so pairing a sensor released after your RUAL version still gives you a fully described device. There is no device list inside RUAL to update.

Hue is a separate adapter from Zigbee even though a Hue bridge is a Zigbee coordinator, because the two are different deployments. If a household already has a bridge, its bulbs are paired to that bridge, and re-pairing them to your own stick breaks the Hue app, the dimmer switches and the scenes the household already uses. Talking to the bridge leaves all of that working. A bulb paired directly to your own coordinator arrives through the Zigbee adapter instead, and neither path knows about the other.

One Vocabulary, Whatever the Radio

Four vendors describe the same physical event four different ways. A motion sensor is occupancy: true on Zigbee2MQTT, a Notification command class carrying "Home Security, Motion sensor status" on Z-Wave JS, motion.motion: true on a Hue bridge, and bit 0 of an OccupancySensing bitmap on Matter. An automation author who has to know which one they own has not been given an automation platform, they have been given a protocol decoder.

So every adapter funnels into one closed set of capabilities, and a flow is written against those. A sensor activated trigger behaves identically whether the sensor is an Aqara on Zigbee, a Fibaro on Z-Wave or an Eve on Thread, because by the time the trigger sees it, all of them are occupancy = true.

The set is deliberately small and closed. A capability earns its place by being something an automation would branch on, not by being something a radio happens to transmit. Anything not listed here still survives on the device's raw map, so it is readable without being promoted into the vocabulary.

Binary Capabilities

True always means the active, noteworthy state. That single rule is why a blueprint never has to ask whether true means open or closed.

CapabilityTrue meansCounts as a sensor
occupancySomeone is thereYes
contactThe door or window is openYes
water_leakWater is presentYes
smokeSmoke detectedYes
gasGas or carbon monoxide detectedYes
vibrationMoved, tilted or droppedYes
tamperThe anti-tamper switch openedNo, device health
powerThe bulb, plug or relay is onNo, it has its own triggers
battery_lowThe device raised its own low-battery flagNo, device health

The "counts as a sensor" column is what sensor activated and get active sensors mean by a sensor: the binary capabilities that describe the world, minus the ones that describe the device. power is excluded because a bulb turning on is not a sensor activating, and folding it in would make every whole-house sensor flow fire on its own lighting changes and livelock. tamper and battery_low are excluded from the other direction: an author asking about presence does not want a flat battery in the same stream.

Numeric Capabilities

CapabilityUnitNotes
temperature°CAmbient reading
target_temperature°CThe setpoint somebody chose
humidity%Relative
pressurehPaAtmospheric
illuminanceluxHue and Matter both report a log scale on the wire; converted here
brightness0 to 100 %Vendors use 0 to 254 or 0 to 99; adapters scale
color_tempkelvinNot mireds. 2200 candle, 2700 warm white, 4000 neutral, 6500 daylight
position0 to 100 %Blinds and valves, percent open
power_wattsWInstantaneous draw
energy_kwhkWhCumulative lifetime meter
co2ppm
vocppb
pm25µg/m³
battery%Remaining charge
link_quality0 to 255The radio's own signal metric

Text and Enum Capabilities

CapabilityValues
color_hex#rrggbb
hvac_modeoff, auto, cool, heat, dry, fan
fan_modeauto, silent, low, medium, high, turbo
lock_statelocked, unlocked, jammed
actionA momentary button pulse: single, double, hold, release, and whatever else the device emits

Access Control and Cameras

These arrive with the UniFi adapter and are shaped for a different kind of device. A door controller is a lock, a position sensor, a card reader and a doorbell at once, and "the front door" is one thing people automate rather than four.

CapabilityCarriesBehaves like
door_positiontrue means the door or gate is open. The physical position, not the lock: a door can be unlocked and shut.An alarm: stored on transitions, charted as a timeline
alarmtrue means the security controller's own alarm is active. Distinct from the smoke, gas and leak sensors, which are individual detectors.An alarm
doorbellSomebody rang. The value is the moment it happened.An event: every occurrence is recorded
access_entryAn entry decision, as an object: actor, method, granted, door. Method is nfc, pin, wave, mobile, touchpass or manual.An event
nfcA card or fob was presented, whether or not it was accepted. The value is the token or actor.An event
motion_eventA camera detected motion. The value is the moment it happened.An event

nfc is separate from access_entry on purpose: "a card was swiped" and "entry was granted" are different automations, because a swipe may be refused.

motion_event is separate from occupancy for the same kind of reason. A camera reports motion as discrete detections; a PIR or radar sensor reports occupancy as a state that stays true while somebody is there. Use presence detected for the second and camera motion detected for the first.

The Two Corrections Worth Knowing

Two vendor quirks are fixed at the adapter boundary rather than left for you to discover:

  • Contact sensors read true when the door is OPEN. Zigbee's contact attribute is true when the magnet is present, which is when the door is closed, and Matter's BooleanState is the same way round. Both are inverted on the way in. This single inversion is the most common source of backwards door automation in every home automation system there is, so it is corrected once, at the boundary, and contact = true means open here and everywhere above here.
  • Colour temperature is kelvin, not mireds. Zigbee, Hue and Matter all speak mireds on the wire. "Warm white is 2700" is knowledge an automation author has; "warm white is 370" is not.

Device Types Are Derived, Not Declared

Every device is classified into one of eight types, and the classification comes from the capabilities it exposes rather than from what the vendor calls itself. Vendor device classes are inconsistent across ecosystems and frequently wrong: Zigbee plugs that meter power routinely announce themselves as lights.

TypeRecognised by
climatehvac_mode or target_temperature
doordoorbell, access_entry, nfc or door_position
cameramotion_event
locklock_state
coverposition
lightbrightness, color_temp or color_hex
sensorAny sensor capability, or any environment reading
buttonaction
switchpower, with none of the above
unknownNothing recognisable yet

The order in that table is the order the checks run, and it is not alphabetical. Capabilities overlap: a Hue bulb has power and brightness, a metering plug has power and power_watts. Testing brightness before plain power is what keeps the plug out of the lights list, and testing climate before either is what keeps a thermostat with a display backlight from being classified as a lamp.

Access control sits near the top for the same reason. A UniFi door has a lock state and a door-position sensor and usually a reader and a bell, so any access-control capability wins and the whole thing classifies as one door rather than as a lock, a sensor and a button.

Type matters more than it looks. light turned on fires only for devices classified as lights, switch turned on only for switches, and the studio's device picker offers only the right type per block.

Nothing Is In Service Until You Adopt It

Devices appear on their own: a coordinator hears everything ever paired to its network, and the node records each one as it reports. But a discovered device fires no triggers and refuses every command until you adopt it.

That gate is what makes the rest reasonable. Without it, "when any sensor activates" means "when anything within thirty metres twitches", including the sensor somebody paired to test three years ago and whatever a second-hand Z-Wave stick inherited from its previous owner. The full onboarding loop is on Discovering and Adopting Devices.

What You Need to Own

  • A machine. A RUAL Core Nano: one box in the building, wired, on an SSD. 16 GB of RAM is the comfortable number once the radio daemons are running beside the node. See RUAL Core Nano for the specifications and the reference builds.
  • A radio per protocol. Zigbee, Thread and Z-Wave are three radios on different frequency bands. Nothing bridges them and no single dongle does all three at once. The tutorial covers which to buy.
  • The daemons. Redis and an MQTT broker for the node itself, plus Zigbee2MQTT, Z-Wave JS UI or Matter Server depending on what you own. Setting Up a RUAL Nano is the runbook.
  • The devices. Hue, Aqara, IKEA, Eve, Fibaro, Aeotec, Shelly Wave, a Daikin air conditioner and a UniFi console running Access or Protect are all covered. Anything Zigbee2MQTT supports works, which is most of what is sold.

Home automation is off by default. A node without [homeassist] enabled = true starts no radio goroutine, creates no device store, and every home-automation block reports HOMEASSIST_NOT_AVAILABLE, so a cloud node pays nothing for these blocks existing. The blocks arrive with Core v15.0.0.

Two parts of this section describe work that is complete but not yet merged upstream, and say so where they appear: the UniFi Access and Protect adapter with its eight triggers and four door actions, and Daikin Onecta support, both in rual-core PR 312. Everything else describes what is on the Core master branch today.

What to Know Before You Commit

  • Restored state is a belief, not an observation. Last-known device state survives a restart (see below), but a restored value does not claim the device is reachable: available stays false and last_seen is untouched until the device actually reports. That is what keeps get all devices from announcing a fleet of confidently-reachable hardware seconds after boot.
  • Triggers are not durable. Device triggers dispatch in process rather than through the queue, which is what makes a light come on before you have finished walking through the door. The cost is that a restart drops anything in flight, and that is the right behaviour anyway: replaying a motion event from before the restart would light a room somebody left ten minutes ago.
  • Smoke, gas and leak triggers are a supplement, not a certified alarm. They depend on a mesh, a broker and a node all being up.
  • Daikin is polled. Its local API has no event channel, so poll_interval (30s by default) is the resolution of every climate trigger.
  • Matter endpoints collapse. A Matter device with two endpoints, such as a two-gang switch, currently appears as one device whose power state is whichever endpoint reported last.
  • The UniFi adapter is not verified against hardware yet. Its transport follows Ubiquiti's documented Access and Protect integration APIs, but the change that introduced it could not confirm the field decoding end to end against a live console. Treat the translated fields as best-effort; every payload is also carried untranslated on the device's raw map, so nothing is lost when a field name differs.
  • Daikin Onecta temperatures are best-effort. Power on and off is reliable; the temperature and mode decoding follows community reverse engineering, the hex encoding varies by model, and it is unverified against a live unit. The full raw /dsiot tree is on the device's raw map.
  • History is sampled by design. It answers trends and totals well and forensic questions poorly. See Device History and Energy.

What a Restart Looks Like

Worth understanding before you write a flow that runs at boot, because the naive design of this feature has a memorable failure mode: a window that was open all night announces itself as "just opened" at 03:00, the alarm flow runs, and the lights come on.

Two mechanisms prevent that.

An unknown previous value seeds silently rather than firing. When the node has never seen a capability before, an incoming reading is recorded without counting as a transition. The exceptions are the three where a missed event is worse than a repeated one: smoke, gas and water_leak still fire when they arrive active, so a node that comes back up to an alarm already sounding says so. Buttons also still fire, because a press is an event with no persistent state to seed.

Last-known state is persisted and restored before the adapters connect, so the first real report has something to compare against and an unchanged re-announce stays unchanged. It lives in Redis rather than the document store, because it is hot, small, rewritten constantly and worthless once stale: the exact profile of a cache, and the exact opposite of the identity data. Writes coalesce per device on a two-second flush (one Zigbee message carrying four capabilities is one write), a clean shutdown flushes, and entries expire after 30 days so a node restored from a backup does not seed year-old beliefs.

Momentary capabilities are excluded from the persisted map. Replaying the last button press as current state would make it look freshly pressed.

Next Steps

Setting Up a RUAL Nano The runbook: broker, Zigbee2MQTT against a ZBT-2, config.toml and first boot. Discovering and Adopting Devices Pairing, adoption, rooms and naming. Example Flows Eight worked automations, block by block.

Frequently asked

Can RUAL control smart home devices without a cloud?

Yes. A RUAL node is the gateway itself: the radios are attached to the machine, the protocol daemons run beside the node, and a motion event reaches a blueprint over a loopback socket. Zigbee, Thread and Matter, Z-Wave, a Philips Hue bridge and Daikin air conditioning are all reached on the local network, so the automations keep working when the internet does not.

Does a RUAL flow have to know whether a sensor is Zigbee or Z-Wave?

No. Every adapter translates into one closed set of capabilities, so a motion sensor is occupancy whether it is an Aqara on Zigbee, a Fibaro on Z-Wave or an Eve on Thread. A flow written against one works against the others without an edit.

Does a RUAL contact sensor report true when the door is open or closed?

True means OPEN. Zigbee and Matter both report the opposite on the wire, because their contact attribute is true when the magnet is present, and RUAL inverts it at the adapter so that true always means the active, noteworthy state.