m8schmit / ecovacs-stack

A complete stack including backend server and frontend interface, to manage a Deebot T9.
GNU General Public License v3.0
29 stars 7 forks source link
deebot ecovacs self-hosted vacuum-robot

Ecovacs Stack

All this as only be tested on Linux, on my T9 AIVI.

alt text

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:

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

alt text

(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 moppingpreference
'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 'OnCleanInfoV2' ?
'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 onFwBuryPointare 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