ecovacs-stack
ecovacs-stack copied to clipboard
A complete stack including backend server and frontend interface, to manage a Deebot T9.
Ecovacs Stack
All this as only be tested on Linux, on my T9 AIVI.
Bot make request on port 8883 (MQTTs) and 443 (MQTTs too, but, I've put an HTTPS server with a listener to be sure) Frontend as difficulties with MQTT protocol, so it listen to the events trought a WS server on port 3000, finally there a DB on port 3306 to uniformise the way we display events, errors and lifespan.
How to install
It still in early development so it's a little bit complicated:
- Add a new rule file in your
Dsnmasq
to redirect all the Ecovacs urls to your local ip
address=/ecouser.net/your-local-ip-here
address=/ecovacs.com/your-local-ip-here
address=/ecovacs.net/your-local-ip-here
address=/aliyuncs.com/your-local-ip-here
-
In the
backend
folder make a copy of the.env.example
file to.env
and add you Vacuum botid
,class
andressources
, and localip
. -
In the
frontend
folder make a copy of the.env.example
file to.env
and add your localip
. -
run
UID="$(id -u)" GID="$(id -g)" docker-compose up
, you should be able to access tohttp://localhost:4200/
-
restart your vacuum bot, it should connect to the MQTTs server and you should see something like:
(Since the main objective is to hide my personal information, the map is intentionally truncated)
Topics
Vacuum bot subscribe to
iot/p2p/+/+/+/+/${bot_id}/${bot_class}/${bot_resource}/q/+/+
to listen request
iot/cfg/${bot_id}/${bot_class}/${bot_resource}/+/+
to... ?
iot/dtcfg/${bot_class}/222/+/+
to... ?
iot/dtgcfg/${bot_class}/${bot_id}/${bot_resource}/+/+
to... ?
Vacuum bot will send messages to
iot/p2p/${command}/${bot_id}/${bot_class}/${bot_resource}/${requester_name}/${env /*'ecosys'*/)/${4_char_id} /*'1234'*//p /*for resPonse*//${request_id}/${j /* for Json */}
to answer to requests.
iot/atr/${command}/${bot_id}/${bot_class}/${bot_resource}/${request_id}/${j /* for Json */}
to send status update.
Commands
(almost) All response will contain an object looking like:
{ code: int (0,),
msg: string ('ok',),
data?: any,
}
Command name | Payload | utility | response data | Comment |
---|---|---|---|---|
'playSound' | { sid: int } |
play a sound. | na | T9 seems to always return the same sounds dependinf of his current state |
'getWKVer' | na | return a version number. | {"ret": string ('ok' todo),"ver": string} |
"ver":"0.25.16", doens't seems to be the firmware or app version number, the response doesn't contain the classic code msg data properties |
'getBattery' | na | return the battery level and a isLow boolean. |
{"value":int,"isLow":int boolean} |
the isLow boolean is an int 0 or 1 |
'getCleanInfo' | na | return a trigger value and the state . |
{"trigger": string ('workComplete', todo),"state":string ('idle , todo)} |
na |
'getChargeState' | na | return the charge state. | {"isCharging":int boolean,"mode":string ('slot', todo)}} |
na |
'getWaterInfo' | {"id": int (length 8)} |
return info relative to the sweep module (Ozmo) | {"enable": int boolean,"amount": int (3, todo),"type": int (0, todo),"sweepType": int (2,todo)} |
amount is the water flow level, sweepType is the mopping_preference |
'getSleep' | {"id": int (length 8)} |
return 0 or 1 if the bot is on standby. |
{"enable":int boolean} |
na |
'getAdvancedMode' | {"id": int (length 8)} |
return 0 or 1 . |
{"enable":int boolean} |
Not sure yet what is the 'advanced mode'. |
'getVolume' | {"id": int (length 8)} |
return the volume total and current value | {"total":int,"volume": int} |
na |
'clean_V2' | {"act": string ('start', 'pause', ),"content":{"total":int (0,),"donotClean":int (0,),"count": int (0,),"type": string ('auto', )},"bdTaskID": string (length 16)}} |
start or pause the cleaning process | na | how it's define total , donotClean , or count ? What's the utility of bdTaskID ? For logs and stats maybe. |
'getStats' | na | ?? | {"area": int (25,),"time": int (length 4),"cid": int (lenght 9),"start": int (timestamp),"type": string ('customArea',),"enablePowerMop": boolean in,"powerMopType": int (2),"aiopen": int (boolean int ? 1),"aitypes": int[] ([5,3,6,4,9]) ,"avoidCount": int (24)} |
need more info for this one |
'appping' | na | no idea | na | na |
'charge' | {"act": string ('go'), bdTaskID": string (length 16)} |
send to charge dock | na | na |
'setRelocationState' | {"mode": string ('manu', ),"bdTaskID": string (length 16)} |
ask to relocate the bot | na | answer when the command as been receive not when the relocation is done with success or not |
'getAudioCallState' | ||||
'getMapSet' | ||||
'getMajorMap' | ||||
'getMapSubSet' | {"msid": string (length 9) ,"values":{},"count": int (length 3),"name":"","mid": string (length 8),"seqIndex": int,"totalCount":int (length 3),"type":string,"mssid":string,"seq":int,"bdTaskID":int (length 16)} |
to get all the zones, virtual wall (vw) , no mop zone (mw) and room (ar) |
||
'getMinorMap' | ||||
'getMapTrace' | {"traceStart":number,"pointCount":number,"bdTaskID": string (length 16)} |
to get the cleaning trace | ||
'getCleanInfo_V2' | Don't return anything, activate 'OnCleanInfo_V2' ? | |||
'getMapInfo_V2' | {"mid": string (length 16),"type": string ("1, 4"),"bdTaskID": string (length 16)} |
|||
onEvt | na | triggered by the bot on event | {"code": number} |
|
SetTime | {"ts": number (timestamp), "tsInSed": number (timestamp in secondes)} |
To set the time of the bot? | {"ret": string"ok"/"fail"} |
On my side event with this time is still inverted 8pm start at 8am |
Event | code |
---|---|
relocate success | 1071 |
Ozmo pro plugged | 1007 |
task type did not support | 20003 |
?? | 1015 |
change the mop reminded | 1052 |
HandleDealMsg_setSched_packageSchedule fail | 20011 |
get pointCount outof range | 20012 |
location failed | 1088 |
unable to locate, returning to charge | 1068 |
App subscribe to these channel, but it's not a complete list, onRosNodeReady
or onFwBuryPoint
are missing.
The initial onMajorMap
and onRosNodeReady
seems to be triggered by the binary script (dln_drawer
) sended to bot.
App atr Channel |
---|
getPos_V2 |
setMapSubSet |
onLiveLaunchPwdState |
onSched_V2 |
onPos |
onVolume |
onBreakPoint |
onBattery |
onMajorMap |
onMapSubSetError |
onRecognize |
onSleep |
onMinorMap |
onDModule |
onSched |
getMapTrace |
onBreakPointStatus |
onEvt |
onCleanPreference |
onSpeed |
onDusterRemind |
onWarning |
onCleanDataUpdate |
onError |
onMapState |
onCachedMapInfo |
onAudioCallState |
onMapSet |
getMapInfo |
onAIMapAndMapSet |
onBlock |
onResetLiveLaunchPwd |
onOta |
setMapSet |
onRelocationState |
onVoice |
batchSetMapSubSet |
onWaterInfo |
onAutoEmpty |
onStats |
getMapSet |
onNextSched |
getMapSubSet |
onLiveState |
onCleanCount |
onMapInfo_V2 |
onAvoidObject |
onChargeState |
onCleanInfo |
onAdvancedMode |
onNextVideoSched |
onMapTrace |
onMapInfo |
onCleanInfo_V2 |
getCachedMapInfo |
setCachedMapInfo |
onCarpertPressure |
Video
Obviously another protocol, the bot send some "Feiyan Info" then call https://iot-auth-global.aliyuncs.com and https://public.iot-as-mqtt.cn-shanghai.aliyuncs.com/. Ecovacs servers are based on https://github.com/alibaba/tengine with Tomcat.
http server
After each clean, the bot send a report to
iotin.ecouser.net/data_collect/upload/generalData?auth.with=device&auth.name={PARAMS}&auth.did={PARAMS}&auth.mid={PARAMS}&auth.res={PARAMS}&auth.ts={PARAMS}&auth.sign={PARAMS}&rn=CleanResult&meta={PARAMS}&fmt=j&dType=string
Once a day the bot call
portal.ecouser.net/api/ota/products/wukong/class/{CLASS}/firmware/latest.json?sn={???}&ver=1.4.5&mac={MAC ADDRESS}&plat={???}&module=fw0
Thanks to
https://github.com/mrbungle64/ecovacs-deebot.js
https://github.com/And3rsL/Deebot-for-Home-Assistant
https://deebot.readthedocs.io/
https://github.com/bmartin5692/bumper
https://github.com/kushagharahi/ecovacs-privacy-control