Note: I used Opencode and Qwen 3.6 27b / GLM 5.2 to find this bug
Version
eufy-security-client 4.1.0 (also reproducible on 4.1.1 / master). Via bropat/eufy-security-ws 3.1.0.
Regression
4.0.0 → 4.1.0. 4.0.0 logs in legacy-first (HTTPApi.login + email 2FA + addTrustDevice) and works. 4.1.0 enables the new MegaTransition (v6-first) login, which breaks for a new/trusted device.
Environment
- Standalone
bropat/eufy-security-ws Docker (not the HA add-on); WS clients drive the API directly.
- Region: CA. Fresh device (first login).
Reproduction
- Fresh data dir →
connect() (no code).
MegaTransition.loginMega requests email 2FA (v6 login: email 2FA required — call loginMega(code)), pendingChallenge = "mega".
- Submit the code via the WS
driver.set_verify_code → driver.connect({ verifyCode }) → runConnect → PHASE 1 loginMega(code):
[http] [MegaHTTPApi.login] MegaApi login ok { auth_token, token_expires_at, token_id, trust_list, ... }
[main] [MegaTransition.loginMega] v6 login: success, mega session persisted
- PHASE 2 then runs the legacy login (best-effort), and the legacy backend returns
LOGIN_NEED_CAPTCHA:
[http] [HTTPApi.login] ... endpoint: 'v2/passport/login_sec', verifyCode: undefined
[http] [HTTPApi.loginRequestCaptcha] Login - Captcha verification received
[http] [HTTPApi.loginRequestCaptcha] Please send requested captcha to proceed with authentication
- Result:
pendingChallenge = "legacy", and runConnect returns early at the PHASE-2 guard — PHASE 3 (onAPIConnect) never runs despite megaLoggedIn === true. No push connect, no Connected to station, and start_listening returns 0 stations / 0 devices.
Why devices never load (the deeper issue)
Stations/devices are fetched only from the legacy backend — HTTPApi.getStationList() / getDeviceList() (src/http/api.ts). There is no v6/mega equivalent in megaHTTPApi.ts, and MegaTransition doesn't fetch them. So when legacy is captcha-blocked, the device list is unreachable even though v6 login succeeded.
Partial fix (verified) + why it's not enough
Adding && !this.megaLoggedIn to the PHASE-2 guard in runConnect:
// src/http/megaTransition.ts
if (this.pendingChallenge === "legacy" && !this.host.api.isConnected() && !this.megaLoggedIn) return;
I tested this against the bundled build/http/megaTransition.js: with it, the app reaches PHASE 3 and Push notification connection successfully established fires (FCM token registered on the eufy_mega backend). But start_listening still returns 0 stations / 0 devices, because those are legacy-only and legacy remains captcha-blocked.
Workaround
Pin eufy-security-client 4.0.0 (bropat/eufy-security-ws 3.0.1). Legacy-first login → email 2FA → trust → devices load normally.
Question / suggestion
- Is the legacy captcha in the v6-first flow expected to be solved by the consumer via
driver.set_captcha? If so, the connected/push signal still shouldn't be gated on it (the one-liner above).
- Longer term, can stations/devices be fetched from the mega backend so the v6 flow is self-sufficient when legacy is unavailable? Currently a successful v6 login yields no devices.
Happy to turn the verified one-liner into a PR if it's useful on its own, or to test a v6 device-list path if one exists/is planned.
Note: I used Opencode and Qwen 3.6 27b / GLM 5.2 to find this bug
Version
eufy-security-client4.1.0 (also reproducible on 4.1.1 / master). Viabropat/eufy-security-ws3.1.0.Regression
4.0.0 → 4.1.0. 4.0.0 logs in legacy-first (
HTTPApi.login+ email 2FA +addTrustDevice) and works. 4.1.0 enables the newMegaTransition(v6-first) login, which breaks for a new/trusted device.Environment
bropat/eufy-security-wsDocker (not the HA add-on); WS clients drive the API directly.Reproduction
connect()(no code).MegaTransition.loginMegarequests email 2FA (v6 login: email 2FA required — call loginMega(code)),pendingChallenge = "mega".driver.set_verify_code→driver.connect({ verifyCode })→runConnect→ PHASE 1loginMega(code):LOGIN_NEED_CAPTCHA:pendingChallenge = "legacy", andrunConnectreturns early at the PHASE-2 guard — PHASE 3 (onAPIConnect) never runs despitemegaLoggedIn === true. Nopush connect, noConnected to station, andstart_listeningreturns 0 stations / 0 devices.Why devices never load (the deeper issue)
Stations/devices are fetched only from the legacy backend —
HTTPApi.getStationList()/getDeviceList()(src/http/api.ts). There is no v6/mega equivalent inmegaHTTPApi.ts, andMegaTransitiondoesn't fetch them. So when legacy is captcha-blocked, the device list is unreachable even though v6 login succeeded.Partial fix (verified) + why it's not enough
Adding
&& !this.megaLoggedInto the PHASE-2 guard inrunConnect:I tested this against the bundled
build/http/megaTransition.js: with it, the app reaches PHASE 3 andPush notification connection successfully establishedfires (FCM token registered on the eufy_mega backend). Butstart_listeningstill returns 0 stations / 0 devices, because those are legacy-only and legacy remains captcha-blocked.Workaround
Pin
eufy-security-client4.0.0 (bropat/eufy-security-ws3.0.1). Legacy-first login → email 2FA → trust → devices load normally.Question / suggestion
driver.set_captcha? If so, the connected/push signal still shouldn't be gated on it (the one-liner above).Happy to turn the verified one-liner into a PR if it's useful on its own, or to test a v6 device-list path if one exists/is planned.