Add setting to change carpet clean mode (#1458)
Some checks failed
CI / Perform linting checks (3.13) (push) Has been cancelled
CodeQL checks / Analyze (python) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, macos-latest, 3.13) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, ubuntu-latest, 3.13) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (false, windows-latest, 3.13) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.11) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.12) (push) Has been cancelled
CI / Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (extras)", ""]')[matrix.extras == ''] }} (true, ubuntu-latest, 3.13) (push) Has been cancelled

Add new setting to control carpet clean mode:
```
== Configuration ==
Carpet clean mode (carpet_clean_mode): Normal *Boost*
```
This commit is contained in:
Teemu R. 2025-01-15 20:35:41 +01:00 committed by GitHub
parent d27697c50f
commit 773801cad5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 94 additions and 2 deletions

View File

@ -437,6 +437,7 @@ COMPONENT_REQUESTS = {
"overheat_protection": [], "overheat_protection": [],
# Vacuum components # Vacuum components
"clean": [ "clean": [
SmartRequest.get_raw_request("getCarpetClean"),
SmartRequest.get_raw_request("getCleanRecords"), SmartRequest.get_raw_request("getCleanRecords"),
SmartRequest.get_raw_request("getVacStatus"), SmartRequest.get_raw_request("getVacStatus"),
SmartRequest.get_raw_request("getAreaUnit"), SmartRequest.get_raw_request("getAreaUnit"),

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import logging import logging
from datetime import timedelta from datetime import timedelta
from enum import IntEnum from enum import IntEnum, StrEnum
from typing import Annotated, Literal from typing import Annotated, Literal
from ...feature import Feature from ...feature import Feature
@ -56,6 +56,13 @@ class FanSpeed(IntEnum):
Ultra = 5 Ultra = 5
class CarpetCleanMode(StrEnum):
"""Carpet clean mode."""
Normal = "normal"
Boost = "boost"
class AreaUnit(IntEnum): class AreaUnit(IntEnum):
"""Area unit.""" """Area unit."""
@ -143,7 +150,6 @@ class Clean(SmartModule):
type=Feature.Type.Sensor, type=Feature.Type.Sensor,
) )
) )
self._add_feature( self._add_feature(
Feature( Feature(
self._device, self._device,
@ -171,6 +177,20 @@ class Clean(SmartModule):
type=Feature.Type.Number, type=Feature.Type.Number,
) )
) )
self._add_feature(
Feature(
self._device,
id="carpet_clean_mode",
name="Carpet clean mode",
container=self,
attribute_getter="carpet_clean_mode",
attribute_setter="set_carpet_clean_mode",
icon="mdi:rug",
choices_getter=lambda: list(CarpetCleanMode.__members__),
category=Feature.Category.Config,
type=Feature.Type.Choice,
)
)
self._add_feature( self._add_feature(
Feature( Feature(
self._device, self._device,
@ -234,6 +254,7 @@ class Clean(SmartModule):
return { return {
"getVacStatus": {}, "getVacStatus": {},
"getCleanInfo": {}, "getCleanInfo": {},
"getCarpetClean": {},
"getAreaUnit": {}, "getAreaUnit": {},
"getBatteryInfo": {}, "getBatteryInfo": {},
"getCleanStatus": {}, "getCleanStatus": {},
@ -342,6 +363,24 @@ class Clean(SmartModule):
_LOGGER.warning("Got unknown status code: %s (%s)", status_code, self.data) _LOGGER.warning("Got unknown status code: %s (%s)", status_code, self.data)
return Status.UnknownInternal return Status.UnknownInternal
@property
def carpet_clean_mode(self) -> Annotated[str, FeatureAttribute()]:
"""Return carpet clean mode."""
return CarpetCleanMode(self.data["getCarpetClean"]["carpet_clean_prefer"]).name
async def set_carpet_clean_mode(
self, mode: str
) -> Annotated[dict, FeatureAttribute()]:
"""Set carpet clean mode."""
name_to_value = {x.name: x.value for x in CarpetCleanMode}
if mode not in name_to_value:
raise ValueError(
"Invalid carpet clean mode %s, available %s", mode, name_to_value
)
return await self.call(
"setCarpetClean", {"carpet_clean_prefer": name_to_value[mode]}
)
@property @property
def area_unit(self) -> AreaUnit: def area_unit(self) -> AreaUnit:
"""Return area unit.""" """Return area unit."""

View File

@ -162,6 +162,9 @@
"getBatteryInfo": { "getBatteryInfo": {
"battery_percentage": 75 "battery_percentage": 75
}, },
"getCarpetClean": {
"carpet_clean_prefer": "boost"
},
"getCleanAttr": { "getCleanAttr": {
"cistern": 2, "cistern": 2,
"clean_number": 1, "clean_number": 1,

View File

@ -21,6 +21,7 @@ clean = parametrize("clean module", component_filter="clean", protocol_filter={"
("vacuum_status", "status", Status), ("vacuum_status", "status", Status),
("vacuum_error", "error", ErrorCode), ("vacuum_error", "error", ErrorCode),
("vacuum_fan_speed", "fan_speed_preset", str), ("vacuum_fan_speed", "fan_speed_preset", str),
("carpet_clean_mode", "carpet_clean_mode", str),
("battery_level", "battery", int), ("battery_level", "battery", int),
], ],
) )
@ -69,6 +70,13 @@ async def test_features(dev: SmartDevice, feature: str, prop_name: str, type: ty
{"suction": 1, "type": "global"}, {"suction": 1, "type": "global"},
id="vacuum_fan_speed", id="vacuum_fan_speed",
), ),
pytest.param(
"carpet_clean_mode",
"Boost",
"setCarpetClean",
{"carpet_clean_prefer": "boost"},
id="carpet_clean_mode",
),
pytest.param( pytest.param(
"clean_count", "clean_count",
2, 2,
@ -151,3 +159,44 @@ async def test_unknown_status(
assert clean.status is Status.UnknownInternal assert clean.status is Status.UnknownInternal
assert "Got unknown status code: 123" in caplog.text assert "Got unknown status code: 123" in caplog.text
@clean
@pytest.mark.parametrize(
("setting", "value", "exc", "exc_message"),
[
pytest.param(
"vacuum_fan_speed",
"invalid speed",
ValueError,
"Invalid fan speed",
id="vacuum_fan_speed",
),
pytest.param(
"carpet_clean_mode",
"invalid mode",
ValueError,
"Invalid carpet clean mode",
id="carpet_clean_mode",
),
],
)
async def test_invalid_settings(
dev: SmartDevice,
mocker: MockerFixture,
setting: str,
value: str,
exc: type[Exception],
exc_message: str,
):
"""Test invalid settings."""
clean = next(get_parent_and_child_modules(dev, Module.Clean))
# Not using feature.set_value() as it checks for valid values
setter_name = dev.features[setting].attribute_setter
assert isinstance(setter_name, str)
setter = getattr(clean, setter_name)
with pytest.raises(exc, match=exc_message):
await setter(value)