Getting started
Install, build, log in, list devices.
Independent and unofficial
eufy-mega is not affiliated with, endorsed by, or sponsored by Anker Innovations or eufy. "eufy" and "Anker" are trademarks of their respective owners. Use it with devices on your own account.
Requirements
- Node.js ≥ 24.5.0 — the client uses
node --env-fileand native fetch-era APIs, and needs the OpenSSL 3.5.1 that 24.5.0 bundles to decode E2E camera video. See.nvmrc. - Runtime dependencies — three:
mqtt,protobufjs,werift. Everything else is Node built-ins (fetch,node:crypto,BigInt). ffmpeg— optional, onPATH. Needed only for the convenience decode/mux sinks: JPEGsnapshotLive(), the one-shotrecord(seconds)buffer, and WebRTC container output (.mp4/.mkv; falls back to raw when absent). The core paths —live(),openReadable(),recordFragments()(CMAF fMP4), and the storedsnapshot()— need no ffmpeg.
Install
npm install
npm run buildConnect
The client logs into the eufy "mega" (v6) cloud, keeps a persistent session, and models every device as a capability-driven Device. login() returns a discriminated result — no exceptions for the expected captcha / 2FA flow; step through it until authenticated. A restored session resolves straight to LoginStatus.Ok with no network.
import { EufyMega, FileSessionStore, LoginStatus } from "eufy-mega";
const eufy = new EufyMega({
email: "you@example.com",
password: "…",
countryCode: "GB", // region auto-discovers (GB → eu-pr)
phoneModel: "eufy-mega-client", // this client's device identity in your account
store: new FileSessionStore("./.eufy-session.json"), // persist + reuse session
});
let r = await eufy.login();
while (r.status !== LoginStatus.Ok) {
if (r.status === LoginStatus.Captcha) {
r = await eufy.solveCaptcha(await promptUser(r.image)); // r.retry === true after a wrong answer
} else if (r.status === LoginStatus.TwoFactor) {
r = await eufy.submitVerifyCode(await promptUser()); // code sent automatically; r.method says how
} else {
throw new Error(`unexpected login status: ${JSON.stringify(r)}`);
}
}
const devices = await eufy.getDevices();Realtime is automatic: a successful login() brings up the event channels (push + MQTT) on its own, and P2P to a camera opens on demand when a command / stream / doorbell ring needs it — no extra setup. See Connectivity & battery and Realtime transports.
Notes:
- Order: login → (captcha if demanded) → (2FA if new/changed device) → token.
- Captcha triggers after repeated failed logins on an untrusted device; the result carries a PNG
imagedata URL. Solve and callsolveCaptcha(answer). - Persistence: with a
store, the token + session key are saved and reused — later runs skip straight to ready (no re-login / 2FA) until the token expires (a 401 clears it). - Device identity: set a distinct
phoneModel/openudidso this client appears as its own trusted device rather than impersonating your phone.
Logging
The client is silent by default — it emits diagnostics only through a logger you pass. To send them to the console (the equivalent of a verbose "debug" mode), attach the built-in ConsoleLogger:
import { EufyMega, ConsoleLogger } from "eufy-mega";
const eufy = new EufyMega({ email, password, logger: new ConsoleLogger() }); // verbose
// or gate by severity:
const quiet = new EufyMega({ email, password, logger: new ConsoleLogger("warn") }); // warn + error onlyTo route logs into your own stack, pass anything that implements the Logger shape (debug/info/warn/error methods) — tslog and winston satisfy it directly:
import { Logger as TsLogger } from "tslog";
const eufy = new EufyMega({ email, password, logger: new TsLogger() });A plain object works too (and is all pino needs, via a one-line adapter):
const eufy = new EufyMega({
email,
password,
logger: {
debug: (m, ...a) => myLog.debug(m, ...a),
info: (m, ...a) => myLog.info(m, ...a),
warn: (m, ...a) => myLog.warn(m, ...a),
error: (m, ...a) => myLog.error(m, ...a),
},
});Diagnostics are separate from operational errors — always also handle the error event (eufy.on("error", …)), which fires regardless of the logger.
Full example
The minimal end-to-end — log in and print each device with its resolved capabilities:
/**
* Example 01 — log in and list devices.
*
* The minimal end-to-end: log in (captcha/2FA handled in _client.ts) and print each device with its
* resolved capabilities.
*
* EUFY_EMAIL=you@example.com EUFY_PASSWORD=… node examples/01-login-list-devices.ts
*
* Requires `npm run build` first (imports the built lib from ../dist).
*/
import { loginClient } from "./_client.ts";
async function main(): Promise<void> {
const eufy = await loginClient();
const devices = await eufy.getDevices();
for (const d of devices) {
const dev = await eufy.getDevice(d.sn);
console.log(`${d.sn} ${d.name} [${dev.codec}] caps: ${dev.capabilities.join(", ")}`);
}
await eufy.disconnect();
}
main().catch((e: unknown) => {
console.error("FATAL", e instanceof Error ? e.message : String(e));
process.exit(1);
});The examples share a
_client.tshelper that drives the login state machine fromEUFY_EMAIL/EUFY_PASSWORD(andEUFY_CAPTCHA/EUFY_2FAwhen needed). Run afternpm run build.
Next: Devices & capabilities · Events · Live media.