From e024cfca8b55b5d00ad362d46f1d0e0aa56b27a6 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 20 Sep 2025 12:17:46 +0300 Subject: [PATCH 1/6] fix battery icon --- CHANGELOG.md | 8 +++++++ .../mydolphin_plus/common/consts.py | 1 + .../common/entity_descriptions.py | 9 +++++++ .../mydolphin_plus/managers/coordinator.py | 10 ++++++++ .../mydolphin_plus/manifest.json | 3 ++- custom_components/mydolphin_plus/vacuum.py | 7 +----- requirements.txt | 24 +++++++++---------- 7 files changed, 43 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5532da9..2d1cc3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## v1.0.22 + +- Fix deprecated `battery_icon` property in vacuum entity +- Add dedicated battery sensor with proper device class (`SensorDeviceClass.BATTERY`) +- Remove hardcoded battery level from vacuum entity +- Update manifest.json with integration metadata and version bump +- Resolves deprecation warning for Home Assistant 2026.8 compatibility + ## v1.0.21 - Initialize data using `async_request_refresh` instead of `async_config_entry_first_refresh` to remove warning message diff --git a/custom_components/mydolphin_plus/common/consts.py b/custom_components/mydolphin_plus/common/consts.py index 7542b62..1fd2330 100644 --- a/custom_components/mydolphin_plus/common/consts.py +++ b/custom_components/mydolphin_plus/common/consts.py @@ -309,6 +309,7 @@ DATA_KEY_CYCLE_COUNT = "Cycle Count" DATA_KEY_ROBOT_ERROR = "Robot Error" DATA_KEY_PWS_ERROR = "Power Supply Error" +DATA_KEY_BATTERY = "Battery" TRANSLATION_KEY_ERROR_INSTRUCTIONS = "state_attributes.instructions.state" ERROR_CLEAN_CODES = [0, 255] diff --git a/custom_components/mydolphin_plus/common/entity_descriptions.py b/custom_components/mydolphin_plus/common/entity_descriptions.py index 1c1a896..5e18661 100644 --- a/custom_components/mydolphin_plus/common/entity_descriptions.py +++ b/custom_components/mydolphin_plus/common/entity_descriptions.py @@ -33,6 +33,7 @@ ) from .consts import ( DATA_KEY_AWS_BROKER, + DATA_KEY_BATTERY, DATA_KEY_CLEAN_MODE, DATA_KEY_CYCLE_COUNT, DATA_KEY_CYCLE_TIME, @@ -235,6 +236,14 @@ class MyDolphinPlusLightEntityDescription( entity_category=EntityCategory.DIAGNOSTIC, translation_key=slugify(DATA_KEY_PWS_ERROR), ), + MyDolphinPlusSensorEntityDescription( + key=slugify(DATA_KEY_BATTERY), + name=DATA_KEY_BATTERY, + device_class=SensorDeviceClass.BATTERY, + native_unit_of_measurement="%", + state_class=SensorStateClass.MEASUREMENT, + translation_key=slugify(DATA_KEY_BATTERY), + ), MyDolphinPlusSensorEntityDescription( key=slugify(DYNAMIC_DESCRIPTION_TEMPERATURE), name=DYNAMIC_DESCRIPTION_TEMPERATURE.capitalize(), diff --git a/custom_components/mydolphin_plus/managers/coordinator.py b/custom_components/mydolphin_plus/managers/coordinator.py index b0d5a0d..50e7841 100644 --- a/custom_components/mydolphin_plus/managers/coordinator.py +++ b/custom_components/mydolphin_plus/managers/coordinator.py @@ -56,6 +56,7 @@ DATA_ERROR_TURN_ON_COUNT, DATA_FILTER_BAG_INDICATION_RESET_FBI, DATA_KEY_AWS_BROKER, + DATA_KEY_BATTERY, DATA_KEY_BUSY, DATA_KEY_CLEAN_MODE, DATA_KEY_CYCLE_COUNT, @@ -366,6 +367,7 @@ def _build_data_mapping(self): slugify(DATA_KEY_AWS_BROKER): self._get_aws_broker_data, slugify(DATA_KEY_ROBOT_ERROR): self._get_robot_error_data, slugify(DATA_KEY_PWS_ERROR): self._get_pws_error_data, + slugify(DATA_KEY_BATTERY): self._get_battery_data, slugify(DYNAMIC_DESCRIPTION_TEMPERATURE): self._get_temperature_data, } @@ -707,6 +709,14 @@ def _get_pws_error_data(self, entity_description) -> dict | None: return result + def _get_battery_data(self, _entity_description) -> dict | None: + # Pool cleaning robots are always connected to power, so battery is always 100% + state = 100 + + result = {ATTR_STATE: state} + + return result + def _get_error_code(self, entity_description, data_section_key) -> dict | None: data = self.aws_data diff --git a/custom_components/mydolphin_plus/manifest.json b/custom_components/mydolphin_plus/manifest.json index aca58e6..e19018f 100644 --- a/custom_components/mydolphin_plus/manifest.json +++ b/custom_components/mydolphin_plus/manifest.json @@ -5,9 +5,10 @@ "codeowners": ["@sh00t2kill", "@lordlala", "@elad-bar"], "config_flow": true, "dependencies": ["http"], + "integration_type": "device", "documentation": "https://github.com/sh00t2kill/dolphin-robot", "iot_class": "cloud_push", "issue_tracker": "https://github.com/sh00t2kill/dolphin-robot/issues", "requirements": ["awsiotsdk"], - "version": "1.0.21" + "version": "1.0.22" } diff --git a/custom_components/mydolphin_plus/vacuum.py b/custom_components/mydolphin_plus/vacuum.py index 8a0aa59..c451024 100644 --- a/custom_components/mydolphin_plus/vacuum.py +++ b/custom_components/mydolphin_plus/vacuum.py @@ -15,7 +15,6 @@ from homeassistant.const import ATTR_MODE, ATTR_STATE, Platform from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.helpers.icon import icon_for_battery_level from .common.base_entity import MyDolphinPlusBaseEntity, async_setup_entities from .common.consts import ATTR_ATTRIBUTES, SIGNAL_DEVICE_NEW @@ -58,12 +57,8 @@ def __init__( self._attr_supported_features = entity_description.features self._attr_fan_speed_list = entity_description.fan_speed_list - self._attr_battery_level = 100 + # Battery level is now handled by a dedicated battery sensor - @property - def battery_icon(self) -> str: - """Return the battery icon for the vacuum cleaner.""" - return icon_for_battery_level(battery_level=self.battery_level, charging=True) async def async_return_to_base(self, **kwargs: Any) -> None: """Set the vacuum cleaner to return to the dock.""" diff --git a/requirements.txt b/requirements.txt index 19c3419..bae020c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,21 +1,21 @@ pre-commit -homeassistant~=2024.5.3 -aiohttp~=3.9.5 -cryptography~=42.0.5 -voluptuous~=0.13.1 +homeassistant +aiohttp +cryptography +voluptuous python-slugify awsiotsdk -awscrt~=0.20.10 +awscrt -awsiot~=0.1.3 +awsiot flatten_json -python-lokalise-api~=1.6 -python-dotenv~=0.20 -googletrans==4.0.0rc1 -translators~= 5.4 -deep-translator~=1.9 +python-lokalise-api +python-dotenv +googletrans +translators +deep-translator -aiofiles~=23.2.1 +aiofiles From acfa5ab9f5dfcef15b37f08af69c38a7368f4ee8 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 20 Sep 2025 12:23:37 +0300 Subject: [PATCH 2/6] fix deprecated constant STATE_DOCKED/CLEANING/ERROR/RETURNING --- CHANGELOG.md | 5 ++++- .../mydolphin_plus/managers/coordinator.py | 4 ++-- .../mydolphin_plus/models/system_details.py | 19 +++++++------------ 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d1cc3e..8a2baa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,11 @@ - Fix deprecated `battery_icon` property in vacuum entity - Add dedicated battery sensor with proper device class (`SensorDeviceClass.BATTERY`) - Remove hardcoded battery level from vacuum entity +- Fix deprecated vacuum state constants (STATE_DOCKED, STATE_CLEANING, STATE_ERROR, STATE_RETURNING) +- Replace deprecated constants with VacuumActivity enum for Home Assistant 2026.1 compatibility +- Update imports in coordinator.py and system_details.py to use VacuumActivity - Update manifest.json with integration metadata and version bump -- Resolves deprecation warning for Home Assistant 2026.8 compatibility +- Resolves deprecation warnings for Home Assistant 2026.8 compatibility ## v1.0.21 diff --git a/custom_components/mydolphin_plus/managers/coordinator.py b/custom_components/mydolphin_plus/managers/coordinator.py index 50e7841..a587824 100644 --- a/custom_components/mydolphin_plus/managers/coordinator.py +++ b/custom_components/mydolphin_plus/managers/coordinator.py @@ -14,7 +14,7 @@ SERVICE_SEND_COMMAND, SERVICE_SET_FAN_SPEED, SERVICE_START, - STATE_DOCKED, + VacuumActivity, ) from homeassistant.const import ( ATTR_ICON, @@ -799,7 +799,7 @@ async def _vacuum_start(self, _entity_description: EntityDescription, _state): self._aws_client.set_cleaning_mode(mode) async def _vacuum_pause(self, _entity_description: EntityDescription, state): - is_idle_state = state == STATE_DOCKED + is_idle_state = state == VacuumActivity.DOCKED _LOGGER.debug(f"Pause vacuum, State: {state}, State: {state}") if is_idle_state: diff --git a/custom_components/mydolphin_plus/models/system_details.py b/custom_components/mydolphin_plus/models/system_details.py index fe5c183..8ae4f8b 100644 --- a/custom_components/mydolphin_plus/models/system_details.py +++ b/custom_components/mydolphin_plus/models/system_details.py @@ -23,12 +23,7 @@ ) from custom_components.mydolphin_plus.common.power_supply_state import PowerSupplyState from custom_components.mydolphin_plus.common.robot_state import RobotState -from homeassistant.components.vacuum import ( - STATE_CLEANING, - STATE_DOCKED, - STATE_ERROR, - STATE_RETURNING, -) +from homeassistant.components.vacuum import VacuumActivity from homeassistant.const import ATTR_MODE @@ -54,7 +49,7 @@ def calculated_state(self) -> CalculatedState: @property def vacuum_state(self) -> str: - return self._data.get(ATTR_VACUUM_STATE, STATE_DOCKED) + return self._data.get(ATTR_VACUUM_STATE, VacuumActivity.DOCKED) @property def power_unit_state(self) -> PowerSupplyState: @@ -115,15 +110,15 @@ def _get_updated_data(aws_data: dict): mode = cleaning_mode.get(ATTR_MODE, CleanModes.REGULAR) calculated_state = CalculatedState.OFF - vacuum_state = STATE_DOCKED + vacuum_state = VacuumActivity.DOCKED if power_supply_state == PowerSupplyState.ERROR: calculated_state = CalculatedState.ERROR - vacuum_state = STATE_ERROR + vacuum_state = VacuumActivity.ERROR elif robot_state == RobotState.FAULT: calculated_state = CalculatedState.ERROR - vacuum_state = STATE_ERROR + vacuum_state = VacuumActivity.ERROR elif power_supply_state == PowerSupplyState.PROGRAMMING: if robot_state == RobotState.PROGRAMMING: @@ -140,9 +135,9 @@ def _get_updated_data(aws_data: dict): calculated_state = CalculatedState.CLEANING if mode == CleanModes.PICKUP: - vacuum_state = STATE_RETURNING + vacuum_state = VacuumActivity.RETURNING else: - vacuum_state = STATE_CLEANING + vacuum_state = VacuumActivity.CLEANING elif power_supply_state == PowerSupplyState.HOLD_DELAY: calculated_state = CalculatedState.HOLD_DELAY From 79bb9a907eb89eab21bf2d09b4ad2f511c5f6b55 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 20 Sep 2025 12:29:21 +0300 Subject: [PATCH 3/6] fix error when not able to login --- CHANGELOG.md | 2 ++ custom_components/mydolphin_plus/managers/rest_api.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a2baa6..6374daa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - Fix deprecated vacuum state constants (STATE_DOCKED, STATE_CLEANING, STATE_ERROR, STATE_RETURNING) - Replace deprecated constants with VacuumActivity enum for Home Assistant 2026.1 compatibility - Update imports in coordinator.py and system_details.py to use VacuumActivity +- Fix API response handling to prevent 'str' object has no attribute 'get' error +- Add type checking for login API response to handle string responses gracefully - Update manifest.json with integration metadata and version bump - Resolves deprecation warnings for Home Assistant 2026.8 compatibility diff --git a/custom_components/mydolphin_plus/managers/rest_api.py b/custom_components/mydolphin_plus/managers/rest_api.py index ef62d96..e0ae3e0 100644 --- a/custom_components/mydolphin_plus/managers/rest_api.py +++ b/custom_components/mydolphin_plus/managers/rest_api.py @@ -330,6 +330,14 @@ async def _service_login(self): "empty response payload of login", ) + elif isinstance(data, str): + _LOGGER.error(f"Invalid response payload of login: {data}") + + self._set_status( + ConnectivityStatus.INVALID_CREDENTIALS, + "invalid response payload of login", + ) + else: _LOGGER.info(f"Logged in to user {username}") From fa7ccb30ffcc096f2e6d4a4f9ab038b6a3450c4d Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 20 Sep 2025 12:32:33 +0300 Subject: [PATCH 4/6] implement the 'activity' property --- CHANGELOG.md | 2 ++ custom_components/mydolphin_plus/vacuum.py | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6374daa..b32c88d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - Fix deprecated vacuum state constants (STATE_DOCKED, STATE_CLEANING, STATE_ERROR, STATE_RETURNING) - Replace deprecated constants with VacuumActivity enum for Home Assistant 2026.1 compatibility - Update imports in coordinator.py and system_details.py to use VacuumActivity +- Fix vacuum entity to use activity property instead of direct state setting +- Add VacuumActivity enum support for proper vacuum state management - Fix API response handling to prevent 'str' object has no attribute 'get' error - Add type checking for login API response to handle string responses gracefully - Update manifest.json with integration metadata and version bump diff --git a/custom_components/mydolphin_plus/vacuum.py b/custom_components/mydolphin_plus/vacuum.py index c451024..9f11ded 100644 --- a/custom_components/mydolphin_plus/vacuum.py +++ b/custom_components/mydolphin_plus/vacuum.py @@ -10,6 +10,7 @@ SERVICE_SET_FAN_SPEED, SERVICE_START, StateVacuumEntity, + VacuumActivity, ) from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_MODE, ATTR_STATE, Platform @@ -58,6 +59,7 @@ def __init__( self._attr_supported_features = entity_description.features self._attr_fan_speed_list = entity_description.fan_speed_list # Battery level is now handled by a dedicated battery sensor + self._attr_activity = VacuumActivity.DOCKED async def async_return_to_base(self, **kwargs: Any) -> None: @@ -94,9 +96,10 @@ def update_component(self, data): fan_speed = attributes.get(ATTR_MODE) - self._attr_state = state + # Set activity instead of state for VacuumActivity enum compatibility + self._attr_activity = state self._attr_extra_state_attributes = attributes self._attr_fan_speed = fan_speed else: - self._attr_state = None + self._attr_activity = VacuumActivity.DOCKED From e2c7c06912c8e5e24e7ad721664c4bbaf25be42e Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 20 Sep 2025 13:01:00 +0300 Subject: [PATCH 5/6] docs --- CHANGELOG.md | 4 +-- README.md | 30 ++++++++++++++++++- .../mydolphin_plus/managers/rest_api.py | 2 +- custom_components/mydolphin_plus/vacuum.py | 1 - 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b32c88d..36a3d47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,13 @@ ## v1.0.22 -- Fix deprecated `battery_icon` property in vacuum entity +- Fix deprecated `battery_icon` property in vacuum entity (resolves [#249](https://github.com/sh00t2kill/dolphin-robot/issues/249)) - Add dedicated battery sensor with proper device class (`SensorDeviceClass.BATTERY`) - Remove hardcoded battery level from vacuum entity - Fix deprecated vacuum state constants (STATE_DOCKED, STATE_CLEANING, STATE_ERROR, STATE_RETURNING) - Replace deprecated constants with VacuumActivity enum for Home Assistant 2026.1 compatibility - Update imports in coordinator.py and system_details.py to use VacuumActivity -- Fix vacuum entity to use activity property instead of direct state setting +- Fix vacuum entity to use activity property instead of direct state setting (resolves [#250](https://github.com/sh00t2kill/dolphin-robot/issues/250)) - Add VacuumActivity enum support for proper vacuum state management - Fix API response handling to prevent 'str' object has no attribute 'get' error - Add type checking for login API response to handle string responses gracefully diff --git a/README.md b/README.md index f27884c..6635f6c 100644 --- a/README.md +++ b/README.md @@ -290,5 +290,33 @@ features: - return_home ``` +### OTP (One-Time Password) Authentication Issues -NOTE: New users will have an account created using OTP. As yet, we have not been able to reverse engineer the OTP login process. Please see this issue for further information, and a manual workaround to create an account: https://github.com/sh00t2kill/dolphin-robot/issues/199#issuecomment-2481627312 +NOTE: New users will have an account created using OTP. As yet, we have not been able to reverse engineer the OTP login process. Please see this issue for further information, and a manual workaround to create an account: https://github.com/sh00t2kill/dolphin-robot/issues/199#issuecomment-2481627312 + +#### Manual Account Creation Workaround + +If you're experiencing OTP authentication issues, you can create a new account directly using the Maytronics API: + +```bash +curl -X POST "https://mbapp18.maytronics.com/api/users/register/" \ + -H "appkey: 346BDE92-53D1-4829-8A2E-B496014B586C" \ + -H "Content-Type: application/x-www-form-urlencoded; charset=utf-8" \ + --data-urlencode "email=your-email@example.com" \ + --data-urlencode "password=your-new-password" \ + --data-urlencode "firstName=Your" \ + --data-urlencode "lastName=Name" +``` + +**Steps:** +1. Replace the placeholders with your actual information: + - `your-email@example.com` - Your email address + - `your-new-password` - A new password for your account + - `Your` - Your first name + - `Name` - Your last name + +2. Run the curl command in your terminal + +3. Use the new credentials in the Home Assistant integration configuration + +**Note:** This creates a fresh account that bypasses the OTP requirement. If you use the same email as your existing account, you may not need to re-pair your robot. diff --git a/custom_components/mydolphin_plus/managers/rest_api.py b/custom_components/mydolphin_plus/managers/rest_api.py index e0ae3e0..3b73412 100644 --- a/custom_components/mydolphin_plus/managers/rest_api.py +++ b/custom_components/mydolphin_plus/managers/rest_api.py @@ -332,7 +332,7 @@ async def _service_login(self): elif isinstance(data, str): _LOGGER.error(f"Invalid response payload of login: {data}") - + self._set_status( ConnectivityStatus.INVALID_CREDENTIALS, "invalid response payload of login", diff --git a/custom_components/mydolphin_plus/vacuum.py b/custom_components/mydolphin_plus/vacuum.py index 9f11ded..4ab570e 100644 --- a/custom_components/mydolphin_plus/vacuum.py +++ b/custom_components/mydolphin_plus/vacuum.py @@ -61,7 +61,6 @@ def __init__( # Battery level is now handled by a dedicated battery sensor self._attr_activity = VacuumActivity.DOCKED - async def async_return_to_base(self, **kwargs: Any) -> None: """Set the vacuum cleaner to return to the dock.""" await self.async_execute_device_action(SERVICE_RETURN_TO_BASE) From a809bc73fdd34849b6d4b03c19df70d3847a0523 Mon Sep 17 00:00:00 2001 From: Elad Bar Date: Sat, 20 Sep 2025 13:09:25 +0300 Subject: [PATCH 6/6] fix pre-commit errors --- .pre-commit-config.yaml | 2 +- README.md | 2 ++ bandit.yaml | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4c37306..a621c47 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,7 +41,7 @@ repos: - mccabe==0.7.0 files: ^(custom_components)/.+\.py$ - repo: https://github.com/PyCQA/bandit - rev: 1.7.4 + rev: 1.7.7 hooks: - id: bandit args: diff --git a/README.md b/README.md index 6635f6c..dafb4e0 100644 --- a/README.md +++ b/README.md @@ -309,7 +309,9 @@ curl -X POST "https://mbapp18.maytronics.com/api/users/register/" \ ``` **Steps:** + 1. Replace the placeholders with your actual information: + - `your-email@example.com` - Your email address - `your-new-password` - A new password for your account - `Your` - Your first name diff --git a/bandit.yaml b/bandit.yaml index 568f77d..46566cc 100644 --- a/bandit.yaml +++ b/bandit.yaml @@ -13,7 +13,6 @@ tests: - B318 - B319 - B320 - - B325 - B601 - B602 - B604