Add to protocol.md
12 KiB
Ecovacs Protocol
Depending on the device there are a few different protocols involved in the communication between the client and Ecovacs systems. There are a series of HTTPS requests used to log in and find devices. Once logged in, you get a token that is used for connecting to different services. In many cases this involves connecting to an XMPP server, which mediates communication with the vacuum. That's right, your robot housecleaner, like an errant teen, spends all its free time hanging out in an internet chat room.
This is all taken from MITMing the Android app. The iOS app appears to follow the same protocol conventions.
Location
It appears that Ecovacs have broken up their API servers by location. Some are designated by country, others by continent. All appear to use the two-letter ISO codes, but at this time it doesn't look like all codes map to valid servers.
The HTTPS and XMPP servers do not appear to be following the same convention. For example, a Canadian user must authenticate on country-specific HTTPS server, but XMPP commands work both on the worldwide server msg-ww.ecouser.net) and the North America server (msg-na.ecouser.net)
HTTPS
There are two sorts of URLs in the basic login flow. The first set use a format like this:
https://eco-{country}-api.ecovacs.com/v1/private/{country}/{lang}/{deviceId}/{appCode}/{appVersion}/{channel}/{deviceType}
They also have a complicated API request signature that seems overelaborate to me. See the Python code for more details.
-
GET eco-us-api.ecovacs.com ... common/checkVersion - appears to just check the app version
-
GET eco-us-api.ecovacs.com ... user/login - Sends encrypted versions of the username and password. The response is some json containing a uid and access token.
-
GET eco-us-api.ecovacs.com ... user/getAuthCode - sends uid, accessToken; gets back an auth code
Under mysterious circumstances, for some people the getAuthCode call will return a different userId than is passed in. In that case, apparently the new userId should be used for future calls, or an Auth 1004 error results.
Now we switch to posting to a different server, and the request and response style change substantially. I think of this at the user server, or perhaps the XMPP/device server.
https://portal-{continent}.ecouser.net/api
There are a few different endpoints within the API that have been seen and are used in the library:
| Endpoint | Description |
|---|---|
| /users/user.do | Handles user / account functions |
| /iot/devmanager.do | Handles sending commands to "IOT" devices |
| /pim/product/getProductIotMap | Provides the "IOT" Product map |
- POST portal-na.ecouser.net/api/users/user.do loginByItToken - trades the authCode from the previous call for yet another token
- POST ne-na.ecouser.net:8018/notify_engine.do - not sure what this is for; my script skips this and seems to work fine
- POST portal-na.ecouser.net/api/users/user.do GetDeviceList - Using the token from step 4, gets the list of devices; that's needed for talking to the vacuum via XMPP
- POST
portal-na.ecouser.net/api/pim/product/getProductIotMap
getProductIotMap - Provides a list of "IOT" products, the devices are referenced in the table below and these are assumed to be "IOT" devices within the library.
IOT Products DEEBOT 600 Series DEEBOT OZMO Slim10 Series DEEBOT OZMO 900 DEEBOT 711 DEEBOT 710 DEEBOT 900 Series
At this point depending on your device you will connect to either an XMPP server, or an MQTT server. This is believed to be based on the "IOT Products" vs "Non-IOT" products.
| "Non-IOT" Products | "IOT" Products |
|---|---|
| Connect to an XMPP server to send commands to devices and receive status results | Connect to an MQTT server to subscribe to status messages and results. A Rest API is utilized to send commands to devices, but can also be used to obtain statuses. |
XMPP - ("Non-IOT")
The app establishes a connection to an XMPP server and logs in using a secret that comes from the earlier HTTPS calls. It then sends XMPP IQ commands. It describes them as queries, but they all contain "ctl" elements that appear to be commands.
The Android App uses the following XMPP messaging servers:
| Country | URL |
|---|---|
| CH | msg.ecouser.net |
| TW, MY, JP, SG, TH, HK, IN, KR | msg-as.ecouser.net |
| US | msg-na.ecouser.net |
| FR, ES, UK, NO, MX, DE, PT, CH, AU, IT, NL, SE, BE, DK | msg-eu.ecouser.net |
| Any other | msg-ww.ecouser.net |
MQTT - ("IOT")
The app establishes a connection to an MQTT server and logs in using a secret that comes from the earlier HTTPS calls.
It then subscribes to a topic where various status and result messages are published by the device. The topic looks like this:
iot/atr/+/{deviceID}/{deviceClass}/{deviceResource}/+
It is believed the MQTT servers mirror the XMPP servers, but only the NA and WW have been tested so far.
| Country | URL |
|---|---|
| US | mq-na.ecouser.net |
| "World-wide" | mq-ww.ecouser.net |
Rest API - ("IOT")
For IOT devices the app sends commands to the device over a Rest API utilizing the secret that comes from the earlier HTTPS calls. This API has only been tested from an "IOT" device, but could possibly work for "Non-IOT" devices as well.
The Rest API utilizes the same portal URL as used previously, but with the iot/devmanager endpoint:
https://portal-{continent}.ecouser.net/api/iot/devmanager.do
Commands are sent via POST in the format of:
{
"auth": {
"realm": EcoVacsAPI.REALM,
"resource": self.resource,
"token": self.secret,
"userid": self.uid,
"with": "users",
},
"cmdName": cmd.name,
"payload": cmd.args_to_xml(),
"payloadType": "x",
"td": "q",
"toId": recipient,
"toRes": self.vacuum['resource'],
"toType": self.vacuum['class']
}
Cleaning
Command
<ctl td="Clean"><clean type="auto" speed="standard"/></ctl>
State
- Request
<ctl td="GetCleanState" /> - Response
<ctl td="CleanReport"><clean type="stop" speed="standard" /></ctl>- type
autoautomatic cleaning program - type
borderedge cleaning program - type
spotspot cleaning program - type
singleroomcleaning a single room - type
stopbot at full stop - type
SpotAreacleaning a mapped room - speed
standardregular fan speed (suction) - speed
stronghigh fan speed (suction)
- type
Charging
Command
<query xmlns="com:ctl"><ctl td="Charge"><charge type="go"/></ctl>goorder bot to return to charger
State
- Request
<query xmlns="com:ctl"><ctl td="GetChargeState" /> - Response
<ctl td="ChargeState"><charge type="SlotCharging" /></ctl>Idlenot trying to chargeGoingtrying to return to chargerSlotChargingcurrently charging in dockWireChargingcurrently charging by cable
Battery State
Battery charge level. 080 = 80% charged. State is broadcast continously when the robot is running och charging, but can also be requested manually.
- Request
<ctl td="GetBatteryInfo" /> - Response
<ctl td="BatteryInfo"><battery power="080" /></ctl>
Component lifespan
The remaining lifespan of components. Based on an internal counters that can be reset with command ResetLifeSpan (untested).
It's presumed that the timers need to be reset manually.
- Request
<ctl td="GetLifeSpan" type="Brush" /> - Response
<ctl td="LifeSpan" type="Brush" val="095" total="365" />- Brush
- SideBrush
- DustCaseHeap
Manually moving around
| Command | Control |
|---|---|
| Move forward | <ctl td="Move"><move action="forward"/></ctl> |
| Move backward | <ctl td="Move"><move action="backward"/></ctl> |
| Spin left 360 degrees | <ctl td="Move"><move action="SpinLeft"/></ctl> |
| Spin right 360 degrees | <ctl td="Move"><move action="SpinRight"/></ctl> |
| Turn 180 degrees | <ctl td="Move"><move action="TurnAround"/></ctl> |
| Stop the ongoing action | <ctl td="Move"><move action="stop"/></ctl> |
Configuration
Set/get robot internal clock
<ctl td="SetTime"><time t="1509622697" tz="+8"/></ctl><ctl td="GetTime"></ctl>- Time is specified as a UNIX timestamp and timezone + or - UTC offset.
Get firmware version
<ctl td="GetVersion" name="FW"/>
Get robot logs
<ctl td="GetLog"></ctl>
Errors
The bot broadcasts error codes for a number of cases.
<ctl td="error" error="BatteryLow" errno="101"></ctl>
The latest error can be requested like so:
- Request
<ctl td="GetError" /> - Response
<ctl ret="ok" errs="100"/>
However in some cases the robot sends to code 100 shortly after an error has occurred, meaning that we cannot trust the GetError request to contain the last relevant error. For example, if the robot gets stuck it broadcasts 102 HostHang, then proceeds to stop and broadcasts 100 NoError.
Known error codes
| Code | Description |
|---|---|
| 100 | NoError: Robot is operational |
| 101 | BatteryLow: Low battery |
| 102 | HostHang: Robot is stuck |
| 103 | WheelAbnormal: Wheels are not moving as expected |
| 104 | DownSensorAbnormal: Down sensor is getting abnormal values |
| 110 | NoDustBox: Dust Bin Not installed |
These codes are taken from model M81 Pro. Error codes may differ between models.
Sounds
Different sid "Sound IDs" will play different sounds. If the vacuum has Voice Report disabled, these won't play.
<ctl td="PlaySound" sid="1" />
| SID | Description |
|---|---|
| 0 | Startup Music Chime |
| 3 | I Am Suspended |
| 4 | Check Driving Wheels |
| 5 | Please Help Me Out |
| 6 | Please Install Dust Bin |
| 17 | Chime / Beep |
| 18 | My Battery Is Low |
| 29 | Please power me on before charging |
| 30 | I Am Here |
| 31 | Brush is tangled please clean my brush |
| 35 | Please clean my antidrop sensors |
| 48 | Brush is tangled |
| 55 | I am relocating |
| 56 | Upgrade succeeded |
| 63 | I am returning to the charging dock |
| 65 | Cleaning paused |
| 69 | Connected please go back to ecovacs app to continue setup |
| 71 | I am restoring the map please do not stand beside me |
| 73 | My battery is low returning to the charging dock |
| 74 | Difficult to locate I am starting a new cleaning cycle |
| 75 | I am resuming the clean |
| 76 | Upgrade failed please try again |
| 77 | Please place me on the charging dock |
| 79 | Resume the clean |
| 80 | I am starting the clean |
| 81 | I am starting the clean |
| 82 | I am starting the clean |
| 84 | I am ready for mopping |
| 85 | Please remove the mopping plate when I am building the map |
| 86 | Cleaning is complete returning to the charging dock |
| 89 | LVS Malfunction please try to tap the LVS |
| 90 | I am upgrading please wait |
Untested commands
<ctl td="SetOnOff" t="b" on="1"/>
<ctl td="GetOnOff" />
<ctl id="12351409" td="PlaySound" sid="0"/>
<ctl id="30800321" td="GetSched"/>
It appears that it adds an extra id when it cares to receive a specific response. This is a little odd in that the iq blocks already contain ids, but perhaps one is more a server id and the other is used by the robot itself.