Skip to content

Conversation

JNickson
Copy link

No description provided.

@Copilot Copilot AI review requested due to automatic review settings May 23, 2025 11:51
@JNickson JNickson closed this May 23, 2025
@JNickson JNickson reopened this May 23, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces an initial API endpoint for IoT control and updates the existing command infrastructure to support the new changes.

  • Added a new constant PATH_API_IOT_CONTROL to define the new endpoint.
  • Introduced CleanV3 and GetCleanInfoV3 command classes to use the new API endpoint.
  • Updated the API request execution logic in command.py to include a new branch for the IoT control endpoint.

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
deebot_client/const.py Added new constant for the IoT control API endpoint.
deebot_client/commands/json/clean.py Added new command classes (CleanV3, GetCleanInfoV3) and updated CleanAreaV3 accordingly.
deebot_client/commands/json/init.py Updated exports to include the new CleanV3 and GetCleanInfoV3 commands.
deebot_client/command.py Updated API request logic to branch based on the new PATH_API_IOT_CONTROL endpoint.

headers=REQUEST_HEADERS,
)

elif self._api_path == PATH_API_IOT_CONTROL:
Copy link

Copilot AI May 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The branch for handling PATH_API_IOT_CONTROL contains placeholder values ('body = ...' and 'query_params = ...'). Please implement the actual payload and query parameters to ensure correct functionality.

Copilot uses AI. Check for mistakes.

@JNickson JNickson marked this pull request as draft May 23, 2025 11:52
@JNickson JNickson closed this May 23, 2025
@JNickson JNickson reopened this May 23, 2025
@JNickson JNickson changed the title added initial api endpoint execute request change Ecovacs GOAT - RTK O800 updated api endpoint request changes May 23, 2025
@edenhaus
Copy link
Member

edenhaus commented Aug 7, 2025

I maybe found better solution, I need to test it next week

@edenhaus edenhaus changed the title Ecovacs GOAT - RTK O800 updated api endpoint request changes Add new API endpoint via DeviceAuthenticator Aug 11, 2025
@edenhaus edenhaus added the pr: enhancement PR with Improve something label Aug 11, 2025
"Calling api(%d/%d): %s",
i + 1,
MAX_RETRIES,
logger_request_params,

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (password)
as clear text.
_LOGGER.debug(
"Error calling api %s, response=%s", logger_request_params, res
"Success calling api %s, response=%s",
logger_request_params,

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (password)
as clear text.
return response_data

_LOGGER.debug(
"Error calling api %s, response=%s", logger_request_params, res

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (password)
as clear text.
_LOGGER.debug("Timeout (%d) reached on path: %s", _TIMEOUT, url)
raise ApiTimeoutError(path=url, timeout=_TIMEOUT) from ex
except ClientResponseError as ex:
_LOGGER.debug("Error: %s", logger_request_params, exc_info=True)

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (password)
as clear text.
Copy link

codecov bot commented Aug 11, 2025

Codecov Report

❌ Patch coverage is 57.01754% with 49 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.26%. Comparing base (21c79c7) to head (9928d2e).
⚠️ Report is 1 commits behind head on dev.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
deebot_client/authentication.py 38.75% 49 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev     #987      +/-   ##
==========================================
- Coverage   93.78%   93.26%   -0.53%     
==========================================
  Files         132      133       +1     
  Lines        5072     5140      +68     
  Branches      332      332              
==========================================
+ Hits         4757     4794      +37     
- Misses        252      284      +32     
+ Partials       63       62       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@edenhaus edenhaus force-pushed the CleanV3-new-api-endpoint branch from 23f1880 to da7a42c Compare August 11, 2025 20:02
Copy link

codspeed-hq bot commented Aug 11, 2025

CodSpeed Performance Report

Merging #987 will not alter performance

Comparing JNickson:CleanV3-new-api-endpoint (9928d2e) with dev (21c79c7)

Summary

✅ 6 untouched

@edenhaus edenhaus force-pushed the CleanV3-new-api-endpoint branch from fb5bc0b to e582689 Compare September 15, 2025 19:20
@edenhaus
Copy link
Member

Can you test if your mower is working with this PR?

@edenhaus edenhaus added the pr: Breaking Change Pull request with braking changes label Sep 16, 2025
@alec-pinson
Copy link

Can you test if your mower is working with this PR?

im not sure if I did something wrong copying deebot_client over

Logger: homeassistant.setup
Source: setup.py:343
First occurred: 08:24:22 (1 occurrence)
Last logged: 08:24:22

Setup failed for 'ecovacs': Unable to import component: No module named 'deebot_client.rs.map'
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/setup.py", line 343, in _async_setup_component
    component = await integration.async_get_component()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/loader.py", line 1023, in async_get_component
    self._component_future.result()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/usr/src/homeassistant/homeassistant/loader.py", line 1003, in async_get_component
    comp = await self.hass.async_add_import_executor_job(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        self._get_component, True
        ^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/usr/local/lib/python3.13/concurrent/futures/thread.py", line 59, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/usr/src/homeassistant/homeassistant/loader.py", line 1063, in _get_component
    ComponentProtocol, importlib.import_module(self.pkg_path)
                       ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/util/loop.py", line 201, in protected_loop_func
    return func(*args, **kwargs)
  File "/usr/local/lib/python3.13/importlib/__init__.py", line 88, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 1026, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/usr/src/homeassistant/homeassistant/components/ecovacs/__init__.py", line 9, in <module>
    from .controller import EcovacsController
  File "/usr/src/homeassistant/homeassistant/components/ecovacs/controller.py", line 14, in <module>
    from deebot_client.device import Device
  File "/usr/local/lib/python3.13/site-packages/deebot_client/device.py", line 13, in <module>
    from deebot_client.mqtt_client import MqttClient, SubscriberInfo
  File "/usr/local/lib/python3.13/site-packages/deebot_client/mqtt_client.py", line 19, in <module>
    from .commands import COMMANDS_WITH_MQTT_P2P_HANDLING
  File "/usr/local/lib/python3.13/site-packages/deebot_client/commands/__init__.py", line 11, in <module>
    from .json import COMMANDS as JSON_COMMANDS
  File "/usr/local/lib/python3.13/site-packages/deebot_client/commands/json/__init__.py", line 7, in <module>
    from . import auto_empty, station_action, station_state
  File "/usr/local/lib/python3.13/site-packages/deebot_client/commands/json/auto_empty.py", line 8, in <module>
    from deebot_client.messages.json.auto_empty import OnAutoEmpty
  File "/usr/local/lib/python3.13/site-packages/deebot_client/messages/__init__.py", line 12, in <module>
    from .xml import MESSAGES as XML_MESSAGES
  File "/usr/local/lib/python3.13/site-packages/deebot_client/messages/xml/__init__.py", line 9, in <module>
    from deebot_client.messages.xml.clean import (
    ...<4 lines>...
    )
  File "/usr/local/lib/python3.13/site-packages/deebot_client/messages/xml/clean.py", line 18, in <module>
    from deebot_client.messages.xml.pos import Pos
  File "/usr/local/lib/python3.13/site-packages/deebot_client/messages/xml/pos.py", line 10, in <module>
    from deebot_client.rs.map import PositionType
ModuleNotFoundError: No module named 'deebot_client.rs.map'

I replaced the following 3 with deebot_client from this PR
/config/deps/lib/python3.11/site-packages/deebot_client
/config/deps/lib/python3.12/site-packages/deebot_client
/usr/local/lib/python3.13/site-packages/deebot_client

@edenhaus
Copy link
Member

Please delete the following folders completely:

  • /config/deps/lib/python3.11/
  • /config/deps/lib/python3.12/

For a few months, HA has required Python 3.13, so you can delete any dependencies installed for 3.11 and 3.12

Copying over will not work as you need to properly install it as this repo does also include native code (rust)
You can do it with uv pip install git+https://github.com/JNickson/client.py@CleanV3-new-api-endpoint
Also please edit the manifest.json file of the ecovacs component and replace the deebot client dependency with
git+https://github.com/JNickson/client.py@CleanV3-new-api-endpoint#deebot-client==0.0.0

@alec-pinson
Copy link

alec-pinson commented Sep 18, 2025

changed from

# cat /usr/src/homeassistant/homeassistant/components/ecovacs/manifest.json
{
  "domain": "ecovacs",
  "name": "Ecovacs",
  "codeowners": ["@mib1185", "@edenhaus", "@Augar"],
  "config_flow": true,
  "documentation": "https://www.home-assistant.io/integrations/ecovacs",
  "iot_class": "cloud_push",
  "loggers": ["sleekxmppfs", "sucks", "deebot_client"],
  "requirements": ["py-sucks==0.9.11", "deebot-client==13.7.0"]
}

changed to

# cat /usr/src/homeassistant/homeassistant/components/ecovacs/manifest.json
{
  "domain": "ecovacs",
  "name": "Ecovacs",
  "codeowners": ["@mib1185", "@edenhaus", "@Augar"],
  "config_flow": true,
  "documentation": "https://www.home-assistant.io/integrations/ecovacs",
  "iot_class": "cloud_push",
  "loggers": ["sleekxmppfs", "sucks", "deebot_client"],
  "requirements": ["py-sucks==0.9.11", "deebot-client==git+https://github.com/JNickson/client.py@CleanV3-new-api-endpoint#deebot-client==0.0.0"]
}
/config # uv pip install git+https://github.com/JNickson/client.py@CleanV3-new-api-endpoint
Using Python 3.13.7 environment at: /usr/local
Resolved 14 packages in 589ms
    Updated https://github.com/JNickson/client.py (9928d2ea20fbfcc19e7ebe09095921c37307b5e2)
      Built deebot-client @ git+https://github.com/JNickson/client.py@9928d2ea20fbfcc19e7ebe09095921c37307b5e2
Prepared 1 package in 1m 43s
Uninstalled 1 package in 21ms
Installed 1 package in 6ms
 - deebot-client==13.7.0
 + deebot-client==0.0.0 (from git+https://github.com/JNickson/client.py@9928d2ea20fbfcc19e7ebe09095921c37307b5e2)
image
2025-09-18 10:50:03.699 ERROR (MainThread) [homeassistant.config_entries] Error setting up entry [email protected] for ecovacs
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 751, in __async_setup_with_context
    result = await component.async_setup_entry(hass, self)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/ecovacs/__init__.py", line 28, in async_setup_entry
    controller = EcovacsController(hass, entry.data)
  File "/usr/src/homeassistant/homeassistant/components/ecovacs/controller.py", line 50, in __init__
    self._authenticator = Authenticator(
                          ~~~~~~~~~~~~~^
        create_rest_config(
        ^^^^^^^^^^^^^^^^^^^
    ...<6 lines>...
        md5(config[CONF_PASSWORD]),
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
TypeError: Can't instantiate abstract class Authenticator without an implementation for abstract methods 'execute_command_request', 'post_authenticated'

has something changed?

@edenhaus
Copy link
Member

Shit forgotten that you need also HA changes. Will send them to you soon as I'm currently out

@alec-pinson
Copy link

Shit forgotten that you need also HA changes. Will send them to you soon as I'm currently out

Don't forget to send when you can :)

@edenhaus
Copy link
Member

@alec-pinson Sorry for the delay but my life is currently chaotic with a broken leg

diff --git a/homeassistant/components/ecovacs/config_flow.py b/homeassistant/components/ecovacs/config_flow.py
index 2637dbbddf8..25911c8a810 100644
--- a/homeassistant/components/ecovacs/config_flow.py
+++ b/homeassistant/components/ecovacs/config_flow.py
@@ -9,7 +9,7 @@ from typing import Any
 from urllib.parse import urlparse
 
 from aiohttp import ClientError
-from deebot_client.authentication import Authenticator, create_rest_config
+from deebot_client.authentication import UserAuthenticator, create_rest_config
 from deebot_client.const import UNDEFINED, UndefinedType
 from deebot_client.exceptions import InvalidAuthenticationError, MqttError
 from deebot_client.mqtt_client import MqttClient, create_mqtt_config
@@ -77,7 +77,7 @@ async def _validate_input(
         override_rest_url=rest_url,
     )
 
-    authenticator = Authenticator(
+    authenticator = UserAuthenticator(
         rest_config,
         user_input[CONF_USERNAME],
         md5(user_input[CONF_PASSWORD]),
diff --git a/homeassistant/components/ecovacs/controller.py b/homeassistant/components/ecovacs/controller.py
index 69dd0f0813f..0225d6e2876 100644
--- a/homeassistant/components/ecovacs/controller.py
+++ b/homeassistant/components/ecovacs/controller.py
@@ -9,7 +9,7 @@ import ssl
 from typing import Any
 
 from deebot_client.api_client import ApiClient
-from deebot_client.authentication import Authenticator, create_rest_config
+from deebot_client.authentication import UserAuthenticator, create_rest_config
 from deebot_client.const import UNDEFINED, UndefinedType
 from deebot_client.device import Device
 from deebot_client.exceptions import DeebotError, InvalidAuthenticationError
@@ -47,7 +47,7 @@ class EcovacsController:
         country = config[CONF_COUNTRY]
         self._continent = get_continent(country)
 
-        self._authenticator = Authenticator(
+        self._authenticator = UserAuthenticator(
             create_rest_config(
                 aiohttp_client.async_get_clientsession(self._hass),
                 device_id=self._device_id,
diff --git a/homeassistant/components/ecovacs/strings.json b/homeassistant/components/ecovacs/strings.json
index 1be81ab1292..04d7ee3d5f7 100644
--- a/homeassistant/components/ecovacs/strings.json
+++ b/homeassistant/components/ecovacs/strings.json
@@ -152,8 +152,10 @@
       "station_state": {
         "name": "Station state",
         "state": {
+          "drying": "Drying mop",
           "idle": "[%key:common::state::idle%]",
-          "emptying_dustbin": "Emptying dustbin"
+          "emptying_dustbin": "Emptying dustbin",
+          "washing": "Washing mop"
         }
       },
       "stats_area": {

@alec-pinson
Copy link

alec-pinson commented Sep 22, 2025

image
2025-09-22 18:33:21.912 WARNING (MainThread) [deebot_client.command] Command "getStats" was not successfully.
2025-09-22 18:33:21.914 WARNING (MainThread) [deebot_client.command] Command "getNetInfo" was not successfully.
2025-09-22 18:33:21.915 WARNING (MainThread) [deebot_client.command] Command "getBattery" was not successfully.
2025-09-22 18:33:22.011 WARNING (MainThread) [deebot_client.command] Command "getTotalStats" was not successfully.
2025-09-22 18:33:22.015 WARNING (MainThread) [deebot_client.command] Command "getBattery" was not successfully.
2025-09-22 18:33:22.017 WARNING (MainThread) [deebot_client.command] Command "getLifeSpan" was not successfully.
2025-09-22 18:33:22.100 WARNING (MainThread) [deebot_client.command] Command "getChargeState" was not successfully.
2025-09-22 18:33:42.053 WARNING (MainThread) [deebot_client.command] Could not parse response for getCleanInfo_V2: None
Traceback (most recent call last):
  File "/usr/local/lib/python3.13/site-packages/deebot_client/command.py", line 174, in __handle_response
    result = self._handle_response(event_bus, response)
  File "/usr/local/lib/python3.13/site-packages/deebot_client/command.py", line 226, in _handle_response
    if response.get("ret") == "ok":
       ^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'get'
2025-09-22 18:33:42.055 WARNING (MainThread) [deebot_client.command] Could not parse getCleanInfo_V2 for 2px96q: None

@edenhaus edenhaus force-pushed the dev branch 2 times, most recently from bf94983 to ed03dbd Compare October 10, 2025 09:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr: Breaking Change Pull request with braking changes pr: enhancement PR with Improve something

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants