No description
Find a file
yeedi-worker 638690c184 Probe MQTT-with-JWT path: confirmed working
deebot_client.mqtt_client uses user_id + JWT (not SN + 8-char SYSINFO).
Probed mq-na.ecouser.net:443 with credentials from the user_access_token
returned by the HTTP login flow.

Confirmed:
- MQTT connect with user_id + JWT: WORKS
- Subscribed to iot/atr/+/did/class/resource/j, iot/p2p topics
- Bot was mid-clean when probed: live onMapTrace, onPos, onMinorMap,
  onStats stream flowing
- onStats area=8 time=420, mid=1394981773 -- bot is real and active
- Clean command via p2p topic was published (rc=0) but the bot was already
  in a clean cycle, so the new command is queued

Why earlier probes timed out: bot was between cleaning cycles. With the
bot actively running, MQTT is live and fast.

The 'we are missing the password' question was about the WRONG password.
8-char SYSINFO password is for the bot's own MQTT connection. A controller
(Yeedi app, Node-RED, our HA integration) uses the user's JWT.
2026-10-12 12:24:31 -07:00
probes_archive probe: iot/devmanager.do REST state queries time out for C12 PRO PLUS 2026-10-11 13:21:22 -07:00
src/yeedi_protocol fix: 950-type URL + body shape for C12 PRO PLUS map commands 2026-10-11 20:15:12 -07:00
tests fix: 950-type URL + body shape for C12 PRO PLUS map commands 2026-10-11 20:15:12 -07:00
.gitignore chore: add .gitignore 2026-10-11 12:50:41 -07:00
probe_950_url.py fix: 950-type URL + body shape for C12 PRO PLUS map commands 2026-10-11 20:15:12 -07:00
probe_1013.py fix: 950-type URL + body shape for C12 PRO PLUS map commands 2026-10-11 20:15:12 -07:00
probe_all_cmds.py fix: 950-type URL + body shape for C12 PRO PLUS map commands 2026-10-11 20:15:12 -07:00
probe_appsvr.py feat: yeedi-protocol library scaffold with live auth tests 2026-10-11 12:50:37 -07:00
probe_clean.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_clean_real.py fix: 950-type URL + body shape for C12 PRO PLUS map commands 2026-10-11 20:15:12 -07:00
probe_correct_body.py fix: 950-type URL + body shape for C12 PRO PLUS map commands 2026-10-11 20:15:12 -07:00
probe_deebot_client.py feat: refactor to 4-pillar architecture (auth/discovery/commander/listener) 2026-10-11 13:31:36 -07:00
probe_envelope.py feat: lg/log.do GetCleanLogs works; iot/devmanager.do bot-offline handled 2026-10-11 16:14:22 -07:00
probe_full_body.py fix: 950-type URL + body shape for C12 PRO PLUS map commands 2026-10-11 20:15:12 -07:00
probe_getbattery.py probe: iot/devmanager.do REST state queries time out for C12 PRO PLUS 2026-10-11 13:21:22 -07:00
probe_jmq.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
probe_jmq_8883.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
probe_jmq_tls.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
probe_jmq_v2.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
probe_lg_endpoints.py feat: lg/log.do GetCleanLogs works; iot/devmanager.do bot-offline handled 2026-10-11 16:14:22 -07:00
probe_map_endpoints.py feat: register map commands (getMajorMap, getMinorMap, getMapInfo, getMapSet) 2026-10-11 16:49:07 -07:00
probe_map_iot.py feat: register map commands (getMajorMap, getMinorMap, getMapInfo, getMapSet) 2026-10-11 16:49:07 -07:00
probe_minimal.py feat: list_user_devices works on users/user.do with minimal body 2026-10-11 13:01:32 -07:00
probe_mq_443.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
probe_mq_broker.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
probe_mqtt_du.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_mqtt_jwt.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_mqtt_minimal.py docs: add probe_950_url.py for cached-data diagnostic 2026-10-12 08:40:31 -07:00
probe_mqtt_modern.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_mqtt_passwords.py docs: add probe_950_url.py for cached-data diagnostic 2026-10-12 08:40:31 -07:00
probe_mqtt_proto.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_mqtt_stable.py docs: add probe_950_url.py for cached-data diagnostic 2026-10-12 08:40:31 -07:00
probe_mqtt_usernames.py docs: add probe_950_url.py for cached-data diagnostic 2026-10-12 08:40:31 -07:00
probe_multi_clean.py fix: 950-type URL + body shape for C12 PRO PLUS map commands 2026-10-11 20:15:12 -07:00
probe_ngiot_control.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_ngiot_final.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_ngiot_hosts.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_ngiot_sst.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_ngiot_v2.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_ngiot_verbose.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_online_check.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
probe_online_state.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
probe_online_status.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
probe_pim_map.py feat: register map commands (getMajorMap, getMinorMap, getMapInfo, getMapSet) 2026-10-11 16:49:07 -07:00
probe_reachability.py feat: lg/log.do GetCleanLogs works; iot/devmanager.do bot-offline handled 2026-10-11 16:14:22 -07:00
probe_real_clean.py docs: add probe_950_url.py for cached-data diagnostic 2026-10-12 08:40:31 -07:00
PROBE_RESULTS.md feat: list_user_devices works on users/user.do with minimal body 2026-10-11 13:01:32 -07:00
probe_sn_sst.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_soundlocate.py feat: lg/log.do GetCleanLogs works; iot/devmanager.do bot-offline handled 2026-10-11 16:14:22 -07:00
probe_sst_devmgr.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_sst_http.py Probe MQTT-with-JWT path: confirmed working 2026-10-12 12:24:31 -07:00
probe_wake.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
probe_wake_v2.py probe: MQTT wake-up path is reachable but auth unknown 2026-10-11 17:35:52 -07:00
PROTOCOL.md feat: lg/log.do GetCleanLogs works; iot/devmanager.do bot-offline handled 2026-10-11 16:14:22 -07:00
pyproject.toml fix: remove all MQTT speculation, restore verified-only state 2026-10-11 15:44:03 -07:00
pytest.ini feat: yeedi-protocol library scaffold with live auth tests 2026-10-11 12:50:37 -07:00
README.md feat: lg/log.do GetCleanLogs works; iot/devmanager.do bot-offline handled 2026-10-11 16:14:22 -07:00

yeedi-protocol

A clean, dependency-light Python library that reverse-engineers the private Yeedi/Ecovacs cloud protocol, with live tests as the source of truth.

The Yeedi mobile app talks to the cloud via undocumented REST endpoints that aren't part of any public API. The behavior was pieced together from:

  • mrbungle64/ecovacs-deebot.js (the only actively maintained client)
  • bmartin5692/sucks (older Python RE work)
  • Live probing against gl-{country}-api.yeedi.com with real credentials

The Home Assistant integration at Mark/yeedi_awmbuild consumes this library. Building it as a separate package lets us prove the protocol with a clean test runner and decouple the HA scaffolding from the "what does the server actually want" work.

Layout

src/yeedi_protocol/
    auth.py      # 3-step login + 1013 device-verification recovery
    crypto.py    # authSign MD5 + RSA encrypt for verification
    discovery.py # list_user_devices() + probe helpers
    cli.py       # yeedi-probe {login,discover,recovery,full}
    errors.py
tests/
    live/        # scripts that hit the real server, gated on env vars
    unit/        # deterministic tests with mocked HTTP

Run live tests

export YEEDI_EMAIL='you@example.com'
export YEEDI_PASSWORD='your-password'
export YEEDI_COUNTRY='US'    # default if unset
uv run pytest -v

A live test only runs if both env vars are set; otherwise pytest skips it. Every live test prints the actual server response so a failure shows you exactly what came back, not a guess.

What's verified live (2026-10-09)

Step Endpoint Status
1. user/login GET gl-{cc}-api.yeedi.com/v1/private/.../user/login ✅ 0000
2. getAuthCode GET gl-{cc}-openapi.yeedi.com/v1/global/auth/getAuthCode ✅ 0000
3. loginByItToken POST api-app.dc-{cc}.ww.ecouser.net/api/users/user.do ✅ result=ok
4. GetDeviceList POST {api-app.dc-{cc}.ww or portal-{cc}}/users/user.do ✅ result=ok — returns bound bots
5. Commands POST iot/devmanager.do ✅ body shape verified, 10s timeout — times out cleanly when bot is offline
5b. GetCleanLogs POST {portal_host}/lg/log.do ✅ result=ok, works even when bot is offline

Bot-offline behavior: iot/devmanager.do holds the HTTP connection open waiting for the device to ACK. When the bot is on the dock and idle, calls time out cleanly with CommandError. Catch it and treat the device as unavailable until the next successful call. The existing HA yeedi_c12_cloud integration has the same behavior.

await async_login(session, email, password, country, device_id) returns (Auth, [Device]) with the user's actual vacuum(s). See PROTOCOL.md for the full writeup.