Vacuums & mowers
The eufy Clean line covers robot vacuums (RoboVac / X-series, e.g. the Clean X10 Pro Omni) and robot mowers. Vacuums are driven through the vacuumClean and suction capabilities. Like every capability they resolve dynamically, so any robot on the Clean line exposes the same fluent API; nothing is hardcoded per model.
Mowers are part of the same Clean family but a distinct device type, so their control surface is a later addition — the sections below cover vacuums today.
const dev = await eufy.getDevice(sn);
const robo = dev.vacuumClean(); // present only on a Clean-line deviceThe accessor returns undefined on a device without the capability, so guard it (dev.vacuumClean()?.…) or assert once up front. Each individual getter is present only when the device reports that value, so read them defensively.
A robot's state appears only after it reports
The cloud device record does not carry a robot's live state — its reported parameters hold none of the activity/suction/battery values. Confirmed on two unrelated models, so treat it as how the line behaves rather than a quirk of one robot. Those values arrive only over the robot's realtime feed, which the SDK subscribes for you.
The consequence for a host: a robot reports on change, not on request, and an idle docked one may stay silent for a long time. Because every getter is evidence-gated, vacuumClean() / suction() resolve with no state getters at all until the first report lands — activity, battery, power, volume and suction are absent, not stale. They appear once the robot has something to say.
So don't treat a missing getter as an error, and don't block startup waiting for one. Listen for deviceState and re-read dev.vacuumClean() when it fires, rather than sampling once at bind time. There is no way to ask a robot for its state on demand — the vendor cloud exposes no such read.
Re-read through the accessor (dev.vacuumClean()?.activity), not through an object you kept from an earlier call: the report that first creates the reads installs them on a fresh object, and a cached one never grows them. deviceState fires again once they exist, so a handler that re-reads each time sees them on the first report.
State
dev.vacuumClean()?.activity; // what the robot is doing (see below)
dev.vacuumClean()?.battery; // 0–100
dev.vacuumClean()?.power; // boolean — powered on
dev.vacuumClean()?.volume; // speaker volume, 0–100
dev.vacuumClean()?.cleanType; // "sweep" | "mop" | "sweepAndMop" | "sweepThenMop"activity is readable only through this typed getter. The robot reports it inside a structured payload rather than as a plain value, and the getter is what unpacks it, so the low-level dev.getProperty("activity") escape hatch hands back that raw payload rather than "docked". Use the typed getter.
activity is the robot's high-level state:
activity | meaning |
|---|---|
cleaning | actively cleaning (see caveat) |
returning | heading back to the dock |
docked | on the dock (idle or charging) |
paused | a task is paused (see caveat) |
idle | standby / asleep |
error | a fault is active |
unknown | a state the SDK can't classify yet |
Caveat:
cleaningis currently broader than "actively vacuuming" — a robot that is paused mid-clean, or parked on the dock running a wash/dry cycle, also reportscleaningtoday. As a result the standalonepausedstate may not appear in practice. Treatcleaningas "a job is in progress" rather than "the brushes are spinning right now". This narrows as more states are decoded.
Cleaning type
dev.vacuumClean()?.cleanType; // "sweep" | "mop" | "sweepAndMop" | "sweepThenMop"What the robot is set to do with a surface, not what the job in progress is doing — the two differ while a setting is changing, and this is the one the app's own screen shows. mop and sweepAndMop are verified against a real robot; sweepThenMop comes from the vendor's enum and has not been seen on a device yet.
sweep also means "not stated"
The protocol omits zero-valued fields, so a robot that states no cleaning type is byte-identical to one set to sweep-only. Both read as "sweep". If your host distinguishes "sweeping" from "unconfigured", it can't rely on this getter to do it.
Suction
import { suctionLevelName } from "@mega-yfue/eufy-sdk";
const level = dev.suction()?.level; // raw integer
suctionLevelName(level); // "Quiet" | "Standard" | "Turbo" | "Max" | "BoostIQ" | "MaxPro" | undefined
dev.suction()?.boostIq; // boolean — BoostIQ auto-suctionlevel is the raw suction level the device reports. The level → name mapping is fixed across models (0 Quiet · 1 Standard · 2 Turbo · 3 Max · 4 BoostIQ · 5 MaxPro) — name it with suctionLevelName(). What differs per model is only which levels a device supports (some expose a narrower range), which is why the SDK returns the integer rather than constraining it. BoostIQ is a level here; the separate boostIq boolean is the independent auto-suction toggle, and a device can report both.
Refreshing state + controls
The robot's state does not come down on the cloud device record; it arrives on the realtime feed, which the SDK subscribes and merges into device state for you. A getter built once stays current — the same object reflects each new report, so there is nothing to re-fetch and no poll to schedule.
What you cannot do is pull state: a robot reports on change, and the vendor cloud offers no read that returns its current values. So the honest pattern is event-driven — react to deviceState rather than sampling. A robot that has not reported since you connected simply has no getters yet.
A report also announces each value it moved as propertyChanged, naming the property rather than the whole device — so you update one reading instead of re-reading the object. That includes the session and lifetime counters (clearTime, clearArea, lifetimeCleanTime, lifetimeCleanArea), which advance throughout a run: since the realtime feed is the only place a robot's state comes from, this is also the only way to follow a clean in progress. They are payload-backed on the AIoT line, so those announcements name the property with no value — re-read it through the accessor.
Controls — start / pause / return-to-dock, suction, volume — are not implemented; the robot surface is read-only today.
See Devices & capabilities for how capability resolution works, and the device gallery for the Clean line.