python-kasa/kasa/smart/modules/vacuum.py

247 lines
6.8 KiB
Python
Raw Normal View History

2024-11-30 15:06:00 +00:00
"""Implementation of vacuum."""
from __future__ import annotations
import logging
from enum import IntEnum
from typing import TYPE_CHECKING
from ...feature import Feature
from ..smartmodule import SmartModule
if TYPE_CHECKING:
2025-01-03 18:46:20 +00:00
pass
_LOGGER = logging.getLogger(__name__)
class Status(IntEnum):
"""Status of vacuum."""
Idle = 0
Cleaning = 1
GoingHome = 4
Charging = 5
Charged = 6
Paused = 7
Error = 100
Unknown = 101
2025-01-03 18:46:20 +00:00
class ErrorCode(IntEnum):
"""Error codes for vacuum."""
Ok = 0
DustBinRemoved = 14
Unknown = -1000
2024-12-02 13:27:25 +00:00
class FanSpeed(IntEnum):
"""Fan speed level."""
Quiet = 1
Standard = 2
Turbo = 3
Max = 4
class Vacuum(SmartModule):
2024-11-30 15:06:00 +00:00
"""Implementation of vacuum support."""
REQUIRED_COMPONENT = "clean"
2025-01-03 18:46:20 +00:00
_error_code = ErrorCode.Ok
2025-01-03 18:46:20 +00:00
def _initialize_features(self) -> None:
"""Initialize features."""
self._add_feature(
Feature(
2025-01-03 18:46:20 +00:00
self._device,
2024-11-30 15:17:20 +00:00
id="vacuum_return_home",
name="Return home",
container=self,
attribute_setter="return_home",
category=Feature.Category.Primary,
type=Feature.Action,
)
)
self._add_feature(
Feature(
2025-01-03 18:46:20 +00:00
self._device,
2024-11-30 15:17:20 +00:00
id="vacuum_start",
name="Start cleaning",
container=self,
attribute_setter="start",
category=Feature.Category.Primary,
type=Feature.Action,
)
)
self._add_feature(
Feature(
2025-01-03 18:46:20 +00:00
self._device,
2024-11-30 15:17:20 +00:00
id="vacuum_pause",
name="Pause",
container=self,
attribute_setter="pause",
category=Feature.Category.Primary,
type=Feature.Action,
)
)
self._add_feature(
Feature(
2025-01-03 18:46:20 +00:00
self._device,
2024-11-30 15:17:20 +00:00
id="vacuum_status",
name="Vacuum status",
container=self,
attribute_getter="status",
category=Feature.Category.Primary,
type=Feature.Type.Sensor,
)
)
2025-01-03 18:46:20 +00:00
self._add_feature(
Feature(
self._device,
id="vacuum_error",
name="Error",
container=self,
attribute_getter="error",
category=Feature.Category.Info,
type=Feature.Type.Sensor,
)
)
2024-11-30 16:26:56 +00:00
self._add_feature(
Feature(
self._device,
2024-12-02 13:27:25 +00:00
id="battery_level",
name="Battery level",
2024-11-30 16:26:56 +00:00
container=self,
attribute_getter="battery",
icon="mdi:battery",
unit_getter=lambda: "%",
category=Feature.Category.Info,
type=Feature.Type.Sensor,
)
)
2024-12-02 13:27:25 +00:00
self._add_feature(
Feature(
self._device,
id="vacuum_fan_speed",
name="Fan speed",
container=self,
2025-01-03 18:14:22 +00:00
attribute_getter="fan_speed_preset",
attribute_setter="set_fan_speed_preset",
2024-12-02 13:27:25 +00:00
icon="mdi:fan",
2025-01-03 18:28:35 +00:00
choices_getter=lambda: list(FanSpeed.__members__),
2024-12-02 13:27:25 +00:00
category=Feature.Category.Primary,
type=Feature.Type.Choice,
)
)
2025-01-03 18:46:20 +00:00
async def _post_update_hook(self) -> None:
"""Set error code after update."""
errors = self._vac_status.get("err_status")
if errors is None:
2025-01-03 18:49:09 +00:00
self._error_code = ErrorCode.Ok
2025-01-03 18:46:20 +00:00
return
if len(errors) > 1:
_LOGGER.warning(
"Multiple error codes, using the first one only: %s", errors
)
error = errors.pop(0)
try:
2025-01-03 18:49:09 +00:00
self._error_code = ErrorCode(error)
2025-01-03 18:46:20 +00:00
except ValueError:
2025-01-03 18:49:09 +00:00
self._error_code = ErrorCode.Unknown
2025-01-03 18:46:20 +00:00
def query(self) -> dict:
"""Query to execute during the update cycle."""
2024-12-02 13:27:25 +00:00
return {
"getVacStatus": None,
"getBatteryInfo": None,
"getCleanStatus": None,
"getCleanAttr": {"type": "global"},
}
2024-11-30 15:06:00 +00:00
async def start(self) -> dict:
"""Start cleaning."""
# If we are paused, do not restart cleaning
if self.status == Status.Paused:
return await self.resume()
# TODO: we need to create settings for clean_modes
2024-06-02 16:52:30 +00:00
return await self.call(
"setSwitchClean",
{
"clean_mode": 0,
"clean_on": True,
"clean_order": True,
"force_clean": False,
},
)
2024-11-30 15:06:00 +00:00
async def pause(self) -> dict:
"""Pause cleaning."""
return await self.set_pause(True)
2024-11-30 15:06:00 +00:00
async def resume(self) -> dict:
"""Resume cleaning."""
return await self.set_pause(False)
2024-11-30 15:06:00 +00:00
async def set_pause(self, enabled: bool) -> dict:
"""Pause or resume cleaning."""
2024-06-02 16:52:30 +00:00
return await self.call("setRobotPause", {"pause": enabled})
2024-11-30 15:06:00 +00:00
async def return_home(self) -> dict:
"""Return home."""
return await self.set_return_home(True)
2024-11-30 15:06:00 +00:00
async def set_return_home(self, enabled: bool) -> dict:
"""Return home / pause returning."""
2024-06-02 16:52:30 +00:00
return await self.call("setSwitchCharge", {"switch_charge": enabled})
2025-01-03 18:46:20 +00:00
@property
def error(self) -> ErrorCode:
"""Return error."""
2025-01-03 18:49:09 +00:00
return self._error_code
2025-01-03 18:46:20 +00:00
2024-12-02 13:27:25 +00:00
@property
def fan_speed_preset(self) -> str:
"""Return fan speed preset."""
return FanSpeed(self.data["getCleanAttr"]["suction"]).name
async def set_fan_speed_preset(self, speed: str) -> dict:
"""Set fan speed preset."""
name_to_value = {x.name.lower(): x.value for x in FanSpeed}
if speed not in name_to_value:
raise ValueError("Invalid fan speed %s, available %s", speed, name_to_value)
return await self.call(
"setCleanAttr", {"suction": name_to_value[speed], "type": "global"}
)
2024-11-30 16:26:56 +00:00
@property
def battery(self) -> int:
"""Return battery level."""
return self.data["getBatteryInfo"]["battery_percentage"]
@property
def _vac_status(self) -> dict:
"""Return vac status container."""
return self.data["getVacStatus"]
@property
def status(self) -> Status:
"""Return current status."""
2025-01-03 18:46:20 +00:00
if self._error_code is not ErrorCode.Ok:
return Status.Error
2024-11-30 15:06:00 +00:00
2024-11-30 16:26:56 +00:00
status_code = self._vac_status["status"]
try:
return Status(status_code)
2024-11-30 15:06:00 +00:00
except ValueError:
_LOGGER.warning("Got unknown status code: %s (%s)", status_code, self.data)
return Status.Unknown