@mega-yfue/eufy-sdk / Device
Class: Device
A eufy device: one class, capability-driven. Construct from a resolved record (or a raw CloudRecord via Device.fromRecord), then feed it param updates.
Extends
Constructors
Constructor
new Device(
sn,
resolved,
logger?
): Device;Parameters
sn
string
resolved
logger?
Logger = noopLogger
Returns
Device
Properties
arming?
arming?: () => ArmingActions | undefined;Guard mode: setMode(ArmingMode) + setAlarmDelayConfig(mode, config). Of the 9 modes a station reports, only away/home/custom1/disarmed are confirmed as writes (ArmingMode); the alarm-delay write takes the narrower byte-captured AlarmDelayMode.
Returns
ArmingActions | undefined
audio?
audio?: () => AudioActions | undefined;Audio/volume (family-gated): mic, speaker + volume, in-video recording, doorbell ringtone, HomeBase alarm/prompt.
Returns
AudioActions | undefined
battery?
battery?: () => BatteryActions | undefined;Battery/power: power source, working mode, and the custom-mode clip/interval/auto-stop settings.
Returns
BatteryActions | undefined
camera?
camera?: () => CameraActions | undefined;Camera: on/off, privacy, status LED; snapshot/live/record when bound to a live client.
Returns
CameraActions | undefined
capabilities
capabilities: readonly Capability[];Resolved capability set. Widens if the device later reports evidence for more.
co?
co?: () => CoActions | undefined;CO-detector reads (read-only): coDetected, lastSeen.
Returns
CoActions | undefined
codec
codec: Codec;Resolved command-codec family.
contact?
contact?: () => ContactActions | undefined;Entry (door/window) sensor: reads open, lastSeen, rssi, alarmSoundType, alarmVolume; writes setAlarmSoundType, setAlarmVolume (present when the sensor reports the alarm params).
Returns
ContactActions | undefined
display?
display?: () => DisplayActions | undefined;Smart Display (read-only): battery. No display write is captured.
Returns
DisplayActions | undefined
doorbell?
doorbell?: () => DoorbellActions | undefined;Doorbell: playQuickResponse(voiceId) (the canned voice replies).
Returns
DoorbellActions | undefined
info?
info?: () => DeviceInfo | undefined;Identity metadata (read-only): { manufacturer, model, serialNumber, name, deviceType?, firmwareVersion?, hardwareVersion? }.
Returns
DeviceInfo | undefined
keypad?
keypad?: () => KeypadActions | undefined;Security-keypad reads (read-only): charging, rssi.
Returns
KeypadActions | undefined
leak?
leak?: () => LeakActions | undefined;Water-leak / freeze sensor reads (read-only): leakDetected, lastSeen.
Returns
LeakActions | undefined
light?
light?: () => LightActions | undefined;Floodlight/spotlight: on/off/set, setBrightness/setColorTemp/setEnabled, setAutoSpotlight.
Returns
LightActions | undefined
locate?
locate?: () => LocateActions | undefined;RoboVac locate (find-robot beep): locating; locate(on?).
Returns
LocateActions | undefined
lock?
lock?: () => LockActions | undefined;Smart lock: lock()/unlock() + verified setRainMode/setAutoLock. The 6 source-confirmed-but-uncaptured setting toggles are ABSENT until captured (present method ⇒ wire verified).
Returns
LockActions | undefined
model?
model?: string;Model / T-code from the record ("T8410"), when known.
modelName
modelName: string;The model's display name ("Indoor Cam Pan & Tilt") — the product this unit is one of.
motion?
motion?: () => MotionActions | undefined;Motion/PIR: setDetection(on) (sensitivity is read-only until its write wire is captured).
Returns
MotionActions | undefined
mower?
mower?: () => MowerActions | undefined;Robot mower (read-only): battery, cutHeight, network, wifiSignal, wear timers and settings. No write is captured.
Returns
MowerActions | undefined
name
name: string;What the user named this device in the app (device_name), falling back to modelName when the record carries none.
properties
properties: readonly PropertySpec[];Merged property schema (one entry per known property this device exposes).
ptz?
ptz?: () => PtzActions | undefined;Pan/tilt/zoom control: rotate(dir, step) + left/right/up/down, zoom, preset() namespace (goto/preview/save/setDefault/delete/list/image).
Returns
PtzActions | undefined
rtsp?
rtsp?: () => RtspActions | undefined;RTSP publish for a NAS/NVR: publish/withdraw + published, requireAuth/allowAnonymous, recordingMode/setRecordingMode. One camera at a time per station.
Returns
RtspActions | undefined
siren?
siren?: () => SirenActions | undefined;Siren: reads active, volume, alarmDuration, doNotDisturb; writes setVolume, setAlarmDuration, test, stop (config setters present when the param is reported). No direct "sound the alarm" wire — a real alarm is driven by the arming system; test is the on-demand trigger.
Returns
SirenActions | undefined
smartDrop?
smartDrop?: () => SmartDropActions | undefined;SmartDrop parcel box: openLid().
Returns
SmartDropActions | undefined
smartLight?
smartLight?: () => SmartLightActions | undefined;eufy_life smart light (T8L0x): on/off, setBrightness, T8L02 setColor, setEffect(lightId).
Returns
SmartLightActions | undefined
smoke?
smoke?: () => SmokeActions | undefined;Smoke-detector reads (read-only): smokeDetected, lastSeen.
Returns
SmokeActions | undefined
sn
readonly sn: string;Serial number (station/device SN).
source
source: "category" | "model" | "inferred";Which resolver tier produced the codec/caps.
stationSn
stationSn: string;The station this device's traffic belongs to: its parent HomeBase, or its own sn when it has none.
Set from the record's parentSn, which is present only for a device that hangs off a base. A record that states none leaves the last known value, as every other identity field here does, so it starts at this device's own serial and every device therefore has a station.
suction?
suction?: () => SuctionActions | undefined;RoboVac suction: level, boostIq, supportedLevels; setSuctionLevel, setBoostIq.
Returns
SuctionActions | undefined
vacuumClean?
vacuumClean?: () =>
| VacuumCleanActions
| undefined;RoboVac core state and controls: power, activity (WorkStatus), volume, battery, cleanType; setPower, startCleaning, returnToDock, pauseCleaning.
Returns
| VacuumCleanActions | undefined
vacuumDock?
vacuumDock?: () => VacuumDockActions | undefined;RoboVac Omni dock controls: emptyDust, washMops, dryMops; raw dockState unexposed until wire confirmed.
Returns
VacuumDockActions | undefined
Methods
announcements()
announcements(changed): PropertyChange[];Which of these changed property names are worth ANNOUNCING, each with the value getProperty now serves for it — the second half of an applyParams call, and the input a facade turns into a property-change event.
Only a name in this device's own schema survives, and EVERY name in it does. The schema is what the SDK published and getProperty serves every entry of, so announcing one is honest; a dictionary-named param and an unknown_<paramType> passthrough are things the SDK makes no claim about, and announcing either would promise a value it never agreed to serve. Diagnostics reach those through inspectParams.
Nothing is withheld for being uninteresting, here or in a capability's own table. Which of a device's truths a host acts on is the host's call: a withheld value cannot be recovered, where an unwanted one costs a caller one comparison on the name.
The value comes out of live state — written microseconds earlier by the same call that produced changed — through the same narrow the capability getters use, which is what makes an announcement and the getter beside it one answer rather than two. Not from the raw wire value: that is a second conversion and a second answer, which is exactly how a payload comes to disagree with its getter. And not by invoking the installed getter, which has read side effects (a scheduled background refresh, a codec call) an announcement must not trigger — and which an unexposed schema property does not have at all.
Kept beside the state and the schema rather than in a caller, because both are here; a caller doing the join would be re-deriving what this object already holds. Says nothing about the previous value: a caller that needs the delta already holds it, because it was told last time.
Parameters
changed
readonly string[]
Returns
applyParams()
applyParams(params, ts?): string[];Apply a raw param map (cloud record or P2P notification). Known params update their named property; unrecognised params are retained as unknown_<paramType> so nothing is lost.
Naming precedence: this device's own PropertySpec (curated), then the param dictionary for its namespace, then the unknown_<paramType> passthrough. The dictionary def is consulted even where a spec exists, because encoding lives there.
A param a resolved capability's gate WITHHELD takes the passthrough instead of its dictionary name. The gate decided the read does not describe this device, the dictionary names it what the member would have, and republishing it there hands a caller a reading indistinguishable from one the device really answered. A capability that never resolved withholds nothing: a param arriving before its capability is still the device's own, and keeps its dictionary name.
Parameters
params
param_type → raw value.
ts?
number = ...
observation time (epoch ms); defaults to Date.now().
Returns
string[]
the list of property names whose value changed.
bindActions()
bindActions(
ctx,
sink,
media?,
ff09Settings?,
rawDp?
): void;Wire this device to a CommandSink so its semantic action objects become live (device.light?.on()). ctx carries the evidence a capability uses to resolve the right command variant. Called by EufyMega.getDevice; a raw model object left unbound simply has no action objects (all accessors return undefined). ff09Settings reads that frame family's settings over whichever transport the device has; rawDp reads the structured payloads a few params carry in place of a scalar. Both are optional and both are named for the job, not the caller — a read needing one returns undefined without it. See CapabilityModule.actions's doc before threading a third. The final buildActions arg is the live-state reader backing the capabilities' typed read getters (dev.battery()?.level): it closes over this.getProperty, so a getter built once here stays current as realtime/poll updates land in this.state.
Parameters
ctx
sink
media?
ff09Settings?
rawDp?
Returns
void
describe()
describe(): DeviceManifest;What this device exposes, as data — every installed read with what its value MEANS, every offerable action with what it accepts, and every event each capability emits.
Beside toJSON rather than folded into it, because the two answer different questions and toJSON fires on every implicit JSON.stringify (an event payload, a log line) where the shape is not wanted. This one carries shape only — no values; read those through the capability getters it names.
The reads and actions are the ones this device actually installed, so a caller can offer everything listed: a write the device gave no evidence for, or one whose wire is not confirmed, is absent rather than described. An unbound device (no live client) has nothing bound to enumerate and answers bound: false with empty details. details is resolved against this device's own facts (codec, model, capabilities), so a per-device enum domain (e.g. workingMode) carries the same options the property schema does.
Returns
getProperties()
getProperties(): Record<string, PropertyValue>;Snapshot of all current property values (named + unknown passthrough) — the untyped bulk read, for diagnostics / discovery. The typed fluent capability getters (dev.battery()?.level, …) answer one specific known property; this loose map answers every observed value at once. Under a freshness policy, schedules a background refresh when the OLDEST observed value is stale (one fetch covers every param) and returns the current snapshot immediately.
Returns
Record<string, PropertyValue>
getProperty()
getProperty(name): PropertyValue | undefined;Low-level property read by name — the untyped escape hatch. The typed fluent capability getters (dev.battery()?.level, dev.contact()?.open) answer the value already narrowed to its declared type where the property is exposed by a capability; this answers the loose PropertyValue.value (boolean|number|string|object). It is the only read for an unbound model object (no live client → no dev.<cap>()) and for a param not yet surfaced on a capability. Returns undefined if never observed.
Parameters
name
string
Returns
PropertyValue | undefined
has()
has(cap): boolean;Does this device have the given capability?
Parameters
cap
Returns
boolean
hasProperty()
hasProperty(name): boolean;Is the given property name part of this device's schema?
Parameters
name
string
Returns
boolean
reresolve()
reresolve(rec): Capability[];Re-resolve against a fresher record and adopt the result if the capability set grew.
A capability is granted on evidence the device reports, so a device that hadn't reported a param when it was first resolved lacks the capability that param proves — and would keep lacking it for the object's whole lifetime, even as the value itself started arriving. Re-resolving on fresh evidence closes that: the accessor appears, already bound if the device is bound.
Only ever widens. A param the device stops reporting does not retract a capability, because the cloud record is a snapshot that can lose a field for reasons that have nothing to do with the hardware, and revoking an accessor a caller already holds is worse than keeping a quiet one.
Returns the capabilities gained, empty when nothing changed — so a caller can skip re-binding.
Parameters
rec
Returns
setFreshnessPolicy()
setFreshnessPolicy(policy): void;Wire a read-through freshness policy. When a cached property is older than staleAfterMs, a getProperty/getProperties read schedules ONE coalesced background refresh() (the facade supplies it, choosing the cheapest live transport) and returns the last-known value immediately — reads never block. Push / P2P realtime updates refresh ts themselves via applyParams, so a device kept fresh by realtime never re-fetches (a fresh entry is never stale). Keeps model/ transport-free: the device only calls the injected callback.
Parameters
policy
refresh
() => Promise<void>
staleAfterMs
number
Returns
void
toJSON()
toJSON(): Record<string, unknown>;Plain-object view: serial, name, resolved codec/source, capabilities, and every current property value.
Returns
Record<string, unknown>
fromRecord()
static fromRecord(
sn,
rec,
logger?
): Device;Build a Device from a raw cloud record (runs the 3-tier resolver).
Parameters
sn
string
rec
logger?
Logger = noopLogger
Returns
Device