client.py icon indicating copy to clipboard operation
client.py copied to clipboard

Ecovacs GOAT A1600 RTK Support

Open dcollewaert opened this issue 1 year ago • 24 comments

Checks

  • [x] I have searched the existing issues and no issue is describing my issue
  • [x] I have checked the FAQ
  • [x] I have checked the documentation
  • [x] I have installed the latest version

The problem

Hi,

Would it be possible to get GOAT A1600 RTK support so it could be used in Home Assistant?

On which deebot device (vacuum) you have the issue?

GOAT A1600 RTK

Which version of the deebot-client are you using?

2025.3.3

Country

fr

Continent

eu

Anything in the logs that might be useful for us?

25-03-16 10:59:02.785 WARNING (MainThread) [deebot_client.api_client] Device class "xmp9ds" not recognized. Please add support for it: {'did': '[REMOVED]', 'name': 'E0BA34765F09HYEE0049', 'class': 'xmp9ds', 'resource': '6lZMW5q0', 'company': 'eco-ng', 'bindTs': 1741714815430, 'service': {'jmq': 'jmq-ngiot-eu.dc.ww.ecouser.net', 'mqs': 'api-ngiot.dc-eu.ww.ecouser.net'}, 'deviceName': 'GOAT A1600 RTK', 'icon': 'https://portal-ww.ecouser.net/api/pim/file/get/677cfd9e954634917988358a', 'ota': True, 'UILogicId': 'goatr_ww_h_goat2', 'materialNo': '116-2337-0000', 'pid': '65d2bb29a1bbb50225b81776', 'product_category': 'GOATBOT', 'model': 'QingGeng-R', 'updateInfo': {'needUpdate': False, 'changeLog': ''}, 'nick': 'Wall-e', 'homeId': '67cdf8f592a75c00076aa7b5', 'homeSort': 1, 'status': 1, 'btName': 'GOAT-xmp9ds-0049', 'btMac': '28:F5:2B:A5:BE:D9', 'otaUpgrade': {}}
2025-03-16 10:59:02.786 WARNING (MainThread) [homeassistant.components.ecovacs.controller] Device "GOAT A1600 RTK" not supported. More information at https://github.com/DeebotUniverse/client.py/issues/612: {'did': 'da8aa19a-af0a-4b7f-84fe-2f9059143755', 'name': 'E0BA34765F09HYEE0049', 'class': 'xmp9ds', 'resource': '6lZMW5q0', 'company': 'eco-ng', 'bindTs': 1741714815430, 'service': {'jmq': 'jmq-ngiot-eu.dc.ww.ecouser.net', 'mqs': 'api-ngiot.dc-eu.ww.ecouser.net'}, 'deviceName': 'GOAT A1600 RTK', 'icon': 'https://portal-ww.ecouser.net/api/pim/file/get/677cfd9e954634917988358a', 'ota': True, 'UILogicId': 'goatr_ww_h_goat2', 'materialNo': '116-2337-0000', 'pid': '65d2bb29a1bbb50225b81776', 'product_category': 'GOATBOT', 'model': 'QingGeng-R', 'updateInfo': {'needUpdate': False, 'changeLog': ''}, 'nick': 'Wall-e', 'homeId': '67cdf8f592a75c00076aa7b5', 'homeSort': 1, 'status': 1, 'btName': 'GOAT-xmp9ds-0049', 'btMac': '28:F5:2B:A5:BE:D9', 'otaUpgrade': {}}
20

Additional information

No response

dcollewaert avatar Mar 16 '25 12:03 dcollewaert

Hi, please add the Goat o800 RTK, too. Thank you.

diveschumi avatar Mar 18 '25 14:03 diveschumi

Hi, please add the Goat o800 RTK, too. Thank you.

https://github.com/DeebotUniverse/client.py/discussions/853

M4tRiX92 avatar Mar 19 '25 07:03 M4tRiX92

Hi, I have copied the file 5xu9h3.py to 2px96q.py and it seems to work with the o800 RTK.

diveschumi avatar Mar 20 '25 15:03 diveschumi

@diveschumi can you create a PR or tell me how to do it? Then I will try that. This will reduce the effort of the creator. Thank you!

M4tRiX92 avatar Mar 20 '25 18:03 M4tRiX92

@diveschumi can you create a PR or tell me how to do it? Then I will try that. This will reduce the effort of the creator. Thank you!

Interested too. Would you please be able to explain how to do this? Thank you!

dcollewaert avatar Mar 20 '25 18:03 dcollewaert

When Home Assistant runs under docker:

  • docker exec -it CONTAINERNAME sh
  • cd /usr/local/lib/python3.13/site-packages/deebot_client/hardware/deebot/
  • cp 5xu9h3.py 2px96q.py

Restart home assistant

You have to do this after every update from home assistant.

For any other GOAT you have to look in the debug log files for the client-id.

diveschumi avatar Mar 21 '25 08:03 diveschumi

When Home Assistant runs under docker:

* docker exec -it CONTAINERNAME sh

* cd /usr/local/lib/python3.13/site-packages/deebot_client/hardware/deebot/

* cp 5xu9h3.py 2px96q.py

Restart home assistant

You have to do this after every update from home assistant.

For any other GOAT you have to look in the debug log files for the client-id.

Thanks for your reply. Would you happen to know how to do this on HaOS? (mine is running in a proxmox VM)

dcollewaert avatar Mar 21 '25 10:03 dcollewaert

Thanks for your reply. Would you happen to know how to do this on HaOS? (mine is running in a proxmox VM)

You have to login in with ssh to open the shell, but I don't know how.

diveschumi avatar Mar 21 '25 10:03 diveschumi

Thanks for your reply. Would you happen to know how to do this on HaOS? (mine is running in a proxmox VM)

install addon "Advanced SSH & Web terminal" configure it

start addon and join web overview Login with credentials from addon

* docker exec -it homeassistant /bin/bash
* cd /usr/local/lib/python3.13/site-packages/deebot_client/Hardware/deebot
* cp 5xu9h3.py 2px96q.py

Please be sure what you are doing, no guarantee, no help. But this is how it worked for me. I just don't know how to do a PR now.

M4tRiX92 avatar Mar 21 '25 10:03 M4tRiX92

config_entry-ecovacs-01JPWWK42ZSP92AADH26V2NNW2(1).json

home-assistant_ecovacs_2025-03-21T18-37-12.778Z.log

Thanks for your reply. Would you happen to know how to do this on HaOS? (mine is running in a proxmox VM)

install addon "Advanced SSH & Web terminal" configure it

start addon and join web overview Login with credentials from addon

* docker exec -it homeassistant /bin/bash
* cd /usr/local/lib/python3.13/site-packages/deebot_client/Hardware/deebot
* cp 5xu9h3.py 2px96q.py

Please be sure what you are doing, no guarantee, no help. But this is how it worked for me. I just don't know how to do a PR now.

It works sort of. Thanks for the help already. I can read all the data and change settings, but i cannot control (start/stop/pause) the robot. In the logs it gives an error.

Would anyone please be able to help out?

So what i did so far: I copied 5xu9h3.py to xmp9ds.py which allowed me to add the robot to HA. I've added the logs in attachment.

dcollewaert avatar Mar 21 '25 11:03 dcollewaert

I have done some more investigation, and it seems that ecovacs has changed their protocol (but i could be mistaken).

Here is some more information:

When i trace the ecovacs app, i get the following:

POST https://api-ngiot.dc-eu.ww.ecouser.net/api/iot/endpoint/control?si=xxx&ct=q&eid=xxx&et=xmp9ds&er=xxx&apn=clean&fmt=j HTTP/2.0

mow area {"body":{"data":{"act":"start","content":{"type":"spotArea","value":"1"}}},"header":{"channel":"iOS","reqid":"fTFmzJ","ts":"1742665772353","ver":"0.0.50","m":"request","pri":1,"tzm":60,"tzc":"Europe/Paris"}}

mow auto: {"body":{"data":{"act":"start","content":{"type":"auto"}}},"header":{"channel":"iOS","reqid":"UGJUnU","ts":"1742665974433","ver":"0.0.50","m":"request","pri":1,"tzm":60,"tzc":"Europe/Paris"}}

resume mowing {"body":{"data":{"act":"resume","content":{"type":"auto"}}},"header":{"channel":"iOS","reqid":"ORfMPC","ts":"1742666047520","ver":"0.0.50","m":"request","pri":1,"tzm":60,"tzc":"Europe/Paris"}}

pause: {"body":{"data":{"act":"pause","content":{"type":"auto"}}},"header":{"channel":"iOS","reqid":"tdQclK","ts":"1742666078695","ver":"0.0.50","m":"request","pri":1,"tzm":60,"tzc":"Europe/Paris"}}

STOP {"body":{"data":{"act":"stop","content":{"type":"auto"}}},"header":{"channel":"iOS","reqid":"iBZWSW","ts":"1742666237813","ver":"0.0.50","m":"request","pri":1,"tzm":60,"tzc":"Europe/Paris"}}

When i Check the Clean and CleanV2, it seems like these are different. Actually, it needs the code from "cleanV2" on the "clean" action. Doing this, it executes start and pause correctly, but i cannot resume. @edenhaus would you please be able to help me out? I can provide any information you need, and do the testing of course.

dcollewaert avatar Mar 22 '25 18:03 dcollewaert

config_entry-ecovacs-01JPWWK42ZSP92AADH26V2NNW2(1).json

home-assistant_ecovacs_2025-03-21T18-37-12.778Z.log

Thanks for your reply. Would you happen to know how to do this on HaOS? (mine is running in a proxmox VM)

install addon "Advanced SSH & Web terminal" configure it start addon and join web overview Login with credentials from addon

* docker exec -it homeassistant /bin/bash
* cd /usr/local/lib/python3.13/site-packages/deebot_client/Hardware/deebot
* cp 5xu9h3.py 2px96q.py

Please be sure what you are doing, no guarantee, no help. But this is how it worked for me. I just don't know how to do a PR now.

It works sort of. Thanks for the help already. I can read all the data and change settings, but i cannot control (start/stop/pause) the robot. In the logs it gives an error.

Would anyone please be able to help out?

So what i did so far: I copied 5xu9h3.py to xmp9ds.py which allowed me to add the robot to HA. I've added the logs in attachment.

I might be dumb, but the folder /usr/local/lib/python3.13 doesn't exist on my installation of HASS-OS... The only folder that exists in /usr/local/lib is perl5. Am I missing something obvious???

Ryeera avatar Mar 28 '25 19:03 Ryeera

Hi, please add the Goat o800 Panorama, too. It cannot be detected by the integration. Thank you.

RolfWehrli avatar Mar 29 '25 16:03 RolfWehrli

Did the same for the Goat o500 panorama (id: 300lc5). However, the actions (start, stop, home, pause) also do not work.

dgebhardt avatar Mar 30 '25 19:03 dgebhardt

How is the progress on this implementation

monsivar avatar Apr 24 '25 09:04 monsivar

I have done some more investigation, and it seems that ecovacs has changed their protocol (but i could be mistaken).

Here is some more information:

When i trace the ecovacs app, i get the following:

POST https://api-ngiot.dc-eu.ww.ecouser.net/api/iot/endpoint/control?si=xxx&ct=q&eid=xxx&et=xmp9ds&er=xxx&apn=clean&fmt=j HTTP/2.0

mow area {"body":{"data":{"act":"start","content":{"type":"spotArea","value":"1"}}},"header":{"channel":"iOS","reqid":"fTFmzJ","ts":"1742665772353","ver":"0.0.50","m":"request","pri":1,"tzm":60,"tzc":"Europe/Paris"}}

mow auto: {"body":{"data":{"act":"start","content":{"type":"auto"}}},"header":{"channel":"iOS","reqid":"UGJUnU","ts":"1742665974433","ver":"0.0.50","m":"request","pri":1,"tzm":60,"tzc":"Europe/Paris"}}

resume mowing {"body":{"data":{"act":"resume","content":{"type":"auto"}}},"header":{"channel":"iOS","reqid":"ORfMPC","ts":"1742666047520","ver":"0.0.50","m":"request","pri":1,"tzm":60,"tzc":"Europe/Paris"}}

pause: {"body":{"data":{"act":"pause","content":{"type":"auto"}}},"header":{"channel":"iOS","reqid":"tdQclK","ts":"1742666078695","ver":"0.0.50","m":"request","pri":1,"tzm":60,"tzc":"Europe/Paris"}}

STOP {"body":{"data":{"act":"stop","content":{"type":"auto"}}},"header":{"channel":"iOS","reqid":"iBZWSW","ts":"1742666237813","ver":"0.0.50","m":"request","pri":1,"tzm":60,"tzc":"Europe/Paris"}}

When i Check the Clean and CleanV2, it seems like these are different. Actually, it needs the code from "cleanV2" on the "clean" action. Doing this, it executes start and pause correctly, but i cannot resume. @edenhaus would you please be able to help me out? I can provide any information you need, and do the testing of course.

Would you mind terribly, explaining which files you changed to get it to start and what you actually changed? I'm by no means a coder.

Right now, all I care about is getting my mower to start on a schedule using home assistant instead of relying on the Ecovacs-app.

yeyeoke avatar Apr 29 '25 15:04 yeyeoke

@dcollewaert Sorry for the delay but I missed your question. Feel free to write me a PM on Discord. My username is edenhaus

@yeyeoke dcollewaert is analyzing the traffic between the app and the ecovacs servers. This is required as the first step to get it working later in HA. Please be patient and wait if you don't have the technical skills to analyze the traffic

edenhaus avatar Apr 29 '25 22:04 edenhaus

Hello,

I copied 5xu9h3.py to xmp9ds.py and I see A1600, but some features are disabled.

I would love to be able to automate A1600, there is one feature missing in the app, which is crucial for me - mow direction for each zone - that wuld be possible with HA.

Can I help somehow?

Thank you

MilanDasek avatar May 03 '25 16:05 MilanDasek

I extract some additional request/response for the A1600 RTK firmware 1.7.80:

{"body":{"data":["getCutEfficiency","getObstacleHeight","getCutHeight","getCutDirection","getAutoCutDirection","getRainDelay","getAnimProtect","getTimeZone","getCustomCutMode","getBorderSwitch"]}
{"header":{"tzm":120,"ts":"1746483121900561666","fwVer":"1.7.80"},"body":{"code":0,"msg":"ok","data":{"getCutEfficiency":{"data":{"level":2},"code":0,"msg":"ok"},"getObstacleHeight":{"data":{"level":2},"code":0,"msg":"ok"},"getCutHeight":{"data":{"level":4},"code":0,"msg":"ok"},"getCutDirection":{"data":{"angle":270,"set":0},"code":0,"msg":"ok"},"getAutoCutDirection":{"data":{"enable":1},"code":0,"msg":"ok"},"getRainDelay":{"data":{"enable":1,"delay":180},"code":0,"msg":"ok"},"getAnimProtect":{"data":{"enable":1,"start":"19:0","end":"7:0"},"code":0,"msg":"ok"},"getTimeZone":{"data":{"tzm":120,"isReset":1},"code":0,"msg":"ok"},"getCustomCutMode":{"data":{"enable":1},"code":0,"msg":"ok"},"getBorderSwitch":{"data":{"enable":1,"mode":1},"code":0,"msg":"ok"}}}}
{"body":{"data":["getError","getRTK","getRTKOta"]}
{"header":{"tzm":120,"ts":"1746483108116909076","fwVer":"1.7.80"},"body":{"code":0,"msg":"ok","data":{"getError":{"code":0,"msg":"","data":{"code":[0]}},"getRTK":{"data":{"result":0,"rtks":[{"x":10,"y":53,"sn":"XXXXX","state":0,"mode":0,"star":29,"version":"XXXXX 1.3.1"}],"observations":{"solStat":0,"poseType":50,"roverId":"XXXXX","roverSvs":32,"roverSolnSvs":29,"roverSignalRate":42,"roverSignalScore":86,"roverOcclusionRate":26,"baseStnId":"\"2318\"","baseSolnSvs":33,"baseSignalRate":44,"baseSignalScore":92,"baseOcclusionRate":23}},"code":0,"msg":"ok"},"getRTKOta":{"code":0,"msg":"ok","data":{"needUpdate":0,"poleState":0,"movState":2,"poleProgress":0,"movProgress":100,"totalProgress":100,"rtkOtaResult":2,"forceUpdate":0,"otaMinute":0,"otaMethod":0,"currentVersion":"","newVersion":""}}}}}

comtel2000 avatar May 05 '25 22:05 comtel2000

This is auto mow on for RTK O800 https://api-ngiot.dc-eu.ww.ecouser.net/api/iot/endpoint/control?si=xxx&ct=q&eid=xxx&et=2px96q&er=xxx&apn=clean&fmt=j

{
  "body": {
    "data": {
      "act": "start",
      "content": {
        "type": "auto"
      }
    }
  },
  "header": {
    "channel": "Android",
    "m": "request",
    "pri": 2,
    "reqid": "iw7ETJ",
    "ts": "1747742911408",
    "tzc": "Europe/London",
    "tzm": 60,
    "ver": "0.0.22"
  }
}

I can get more if needed

alec-pinson avatar May 20 '25 12:05 alec-pinson

It looks like my robot vacuum also uses the endpoint /iot/endpoint/control rather than iot/devmanager.do within the app (my vacuum can be controlled fine by the integration).

I'm guessing endpoint/control is a new endpoint that will takeover from devmanager.do.

Also I think that these are the same:- https://api-ngiot.dc-eu.ww.ecouser.net/api/iot/endpoint/control https://portal-eu.ecouser.net/api/iot/endpoint/control

alec-pinson avatar May 20 '25 15:05 alec-pinson

some more notes...

    async def _execute_api_request(
        self, authenticator: Authenticator, device_info: ApiDeviceInfo
    ) -> dict[str, Any]:
        payload = {
            "cmdName": self.NAME,                # doesn't exist
            "payload": self._get_payload(),
            "payloadType": self.DATA_TYPE.value, # http param renamed to fmt
            "td": "q",                           # http param renamed to ct
            "toId": device_info["did"],          # http param renamed to eid
            "toRes": device_info["resource"],    # http param renamed to er
            "toType": device_info["class"],      # http param renamed to et
                                                 # new http param si (some random id which matches request header X-ECO-REQUEST-ID)
                                                 # new http param apn (clean|charge)
        }

alec-pinson avatar May 20 '25 16:05 alec-pinson

Instead comment code snippets here, can you open a PR for your changes :) We can than discuss your changes directly in the PR and it's easier for me to follow :)

edenhaus avatar May 21 '25 07:05 edenhaus

Instead comment code snippets here, can you open a PR for your changes :) We can than discuss your changes directly in the PR and it's easier for me to follow :)

Sorry I wish I could but my python skills don't exist, I understand APIs but as for trying to make this work in python I don't really know where to start

alec-pinson avatar May 21 '25 07:05 alec-pinson