Add init type hints to XiaomiCoordinatedMiioEntity derived entities (#145612)

This commit is contained in:
epenet 2025-05-26 14:45:55 +02:00 committed by GitHub
parent 970359c6a0
commit 2d2e0d0fb9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 328 additions and 137 deletions

View File

@ -5,7 +5,9 @@ from __future__ import annotations
from collections.abc import Callable, Iterable
from dataclasses import dataclass
import logging
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from miio import Device as MiioDevice
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
@ -15,6 +17,7 @@ from homeassistant.components.binary_sensor import (
from homeassistant.const import CONF_DEVICE, CONF_MODEL, EntityCategory
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import VacuumCoordinatorDataAttributes
from .const import (
@ -213,12 +216,21 @@ async def async_setup_entry(
async_add_entities(entities)
class XiaomiGenericBinarySensor(XiaomiCoordinatedMiioEntity, BinarySensorEntity):
class XiaomiGenericBinarySensor(
XiaomiCoordinatedMiioEntity[DataUpdateCoordinator[Any]], BinarySensorEntity
):
"""Representation of a Xiaomi Humidifier binary sensor."""
entity_description: XiaomiMiioBinarySensorDescription
def __init__(self, device, entry, unique_id, coordinator, description):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str,
coordinator: DataUpdateCoordinator[Any],
description: XiaomiMiioBinarySensorDescription,
) -> None:
"""Initialize the entity."""
super().__init__(device, entry, unique_id, coordinator)

View File

@ -3,7 +3,9 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from miio import Device as MiioDevice
from miio.integrations.vacuum.roborock.vacuum import Consumable
from homeassistant.components.button import (
@ -14,6 +16,7 @@ from homeassistant.components.button import (
from homeassistant.const import CONF_MODEL, EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import MODEL_AIRFRESH_A1, MODEL_AIRFRESH_T2017, MODELS_VACUUM
from .entity import XiaomiCoordinatedMiioEntity
@ -148,14 +151,23 @@ async def async_setup_entry(
async_add_entities(entities)
class XiaomiGenericCoordinatedButton(XiaomiCoordinatedMiioEntity, ButtonEntity):
class XiaomiGenericCoordinatedButton(
XiaomiCoordinatedMiioEntity[DataUpdateCoordinator[Any]], ButtonEntity
):
"""A button implementation for Xiaomi."""
entity_description: XiaomiMiioButtonDescription
_attr_device_class = ButtonDeviceClass.RESTART
def __init__(self, device, entry, unique_id, coordinator, description):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str,
coordinator: DataUpdateCoordinator[Any],
description: XiaomiMiioButtonDescription,
) -> None:
"""Initialize the plug switch."""
super().__init__(device, entry, unique_id, coordinator)
self.entity_description = description

View File

@ -6,7 +6,7 @@ from functools import partial
import logging
from typing import TYPE_CHECKING, Any
from miio import DeviceException
from miio import Device as MiioDevice, DeviceException
from miio.gateway.devices import SubDevice
from homeassistant.const import ATTR_CONNECTIONS, CONF_MAC, CONF_MODEL
@ -70,7 +70,13 @@ class XiaomiCoordinatedMiioEntity[_T: DataUpdateCoordinator[Any]](
_attr_has_entity_name = True
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: _T,
) -> None:
"""Initialize the coordinated Xiaomi Miio Device."""
super().__init__(coordinator)
self._device = device
@ -88,6 +94,8 @@ class XiaomiCoordinatedMiioEntity[_T: DataUpdateCoordinator[Any]](
@property
def device_info(self) -> DeviceInfo:
"""Return the device info."""
if TYPE_CHECKING:
assert self._device_id is not None
device_info = DeviceInfo(
identifiers={(DOMAIN, self._device_id)},
manufacturer="Xiaomi",

View File

@ -8,6 +8,7 @@ import logging
import math
from typing import Any
from miio import Device as MiioDevice
from miio.fan_common import (
MoveDirection as FanMoveDirection,
OperationMode as FanOperationMode,
@ -34,6 +35,7 @@ from homeassistant.const import ATTR_ENTITY_ID, CONF_DEVICE, CONF_MODEL
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util.percentage import (
percentage_to_ranged_value,
ranged_value_to_percentage,
@ -293,22 +295,30 @@ async def async_setup_entry(
async_add_entities(entities)
class XiaomiGenericDevice(XiaomiCoordinatedMiioEntity, FanEntity):
class XiaomiGenericDevice(
XiaomiCoordinatedMiioEntity[DataUpdateCoordinator[Any]], FanEntity
):
"""Representation of a generic Xiaomi device."""
_attr_name = None
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the generic Xiaomi device."""
super().__init__(device, entry, unique_id, coordinator)
self._available_attributes = {}
self._state = None
self._mode = None
self._fan_level = None
self._state_attrs = {}
self._available_attributes: dict[str, Any] = {}
self._state: bool | None = None
self._mode: str | None = None
self._fan_level: int | None = None
self._state_attrs: dict[str, Any] = {}
self._device_features = 0
self._preset_modes = []
self._preset_modes: list[str] = []
@property
@abstractmethod
@ -343,7 +353,8 @@ class XiaomiGenericDevice(XiaomiCoordinatedMiioEntity, FanEntity):
) -> None:
"""Turn the device on."""
result = await self._try_command(
"Turning the miio device on failed.", self._device.on
"Turning the miio device on failed.",
self._device.on, # type: ignore[attr-defined]
)
# If operation mode was set the device must not be turned on.
@ -359,7 +370,8 @@ class XiaomiGenericDevice(XiaomiCoordinatedMiioEntity, FanEntity):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the device off."""
result = await self._try_command(
"Turning the miio device off failed.", self._device.off
"Turning the miio device off failed.",
self._device.off, # type: ignore[attr-defined]
)
if result:
@ -370,7 +382,13 @@ class XiaomiGenericDevice(XiaomiCoordinatedMiioEntity, FanEntity):
class XiaomiGenericAirPurifier(XiaomiGenericDevice):
"""Representation of a generic AirPurifier device."""
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the generic AirPurifier device."""
super().__init__(device, entry, unique_id, coordinator)
@ -417,7 +435,13 @@ class XiaomiAirPurifier(XiaomiGenericAirPurifier):
REVERSE_SPEED_MODE_MAPPING = {v: k for k, v in SPEED_MODE_MAPPING.items()}
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the plug switch."""
super().__init__(device, entry, unique_id, coordinator)
@ -528,7 +552,7 @@ class XiaomiAirPurifier(XiaomiGenericAirPurifier):
if speed_mode:
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
self.operation_mode_class(self.SPEED_MODE_MAPPING[speed_mode]),
)
@ -539,7 +563,7 @@ class XiaomiAirPurifier(XiaomiGenericAirPurifier):
"""
if await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
self.operation_mode_class[preset_mode],
):
self._mode = self.operation_mode_class[preset_mode].value
@ -552,7 +576,7 @@ class XiaomiAirPurifier(XiaomiGenericAirPurifier):
await self._try_command(
"Setting the extra features of the miio device failed.",
self._device.set_extra_features,
self._device.set_extra_features, # type: ignore[attr-defined]
features,
)
@ -599,7 +623,7 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
return
if await self._try_command(
"Setting fan level of the miio device failed.",
self._device.set_fan_level,
self._device.set_fan_level, # type: ignore[attr-defined]
fan_level,
):
self._fan_level = fan_level
@ -609,7 +633,13 @@ class XiaomiAirPurifierMiot(XiaomiAirPurifier):
class XiaomiAirPurifierMB4(XiaomiGenericAirPurifier):
"""Representation of a Xiaomi Air Purifier MB4."""
def __init__(self, device, entry, unique_id, coordinator) -> None:
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize Air Purifier MB4."""
super().__init__(device, entry, unique_id, coordinator)
@ -659,7 +689,7 @@ class XiaomiAirPurifierMB4(XiaomiGenericAirPurifier):
return
if await self._try_command(
"Setting fan level of the miio device failed.",
self._device.set_favorite_rpm,
self._device.set_favorite_rpm, # type: ignore[attr-defined]
favorite_rpm,
):
self._favorite_rpm = favorite_rpm
@ -673,7 +703,7 @@ class XiaomiAirPurifierMB4(XiaomiGenericAirPurifier):
if await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
self.operation_mode_class[preset_mode],
):
self._mode = self.operation_mode_class[preset_mode].value
@ -712,7 +742,13 @@ class XiaomiAirFresh(XiaomiGenericAirPurifier):
"Interval": AirfreshOperationMode.Interval,
}
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the miio device."""
super().__init__(device, entry, unique_id, coordinator)
@ -764,7 +800,7 @@ class XiaomiAirFresh(XiaomiGenericAirPurifier):
if speed_mode:
if await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
AirfreshOperationMode(self.SPEED_MODE_MAPPING[speed_mode]),
):
self._mode = AirfreshOperationMode(
@ -779,7 +815,7 @@ class XiaomiAirFresh(XiaomiGenericAirPurifier):
"""
if await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
self.operation_mode_class[preset_mode],
):
self._mode = self.operation_mode_class[preset_mode].value
@ -792,7 +828,7 @@ class XiaomiAirFresh(XiaomiGenericAirPurifier):
await self._try_command(
"Setting the extra features of the miio device failed.",
self._device.set_extra_features,
self._device.set_extra_features, # type: ignore[attr-defined]
features,
)
@ -810,10 +846,16 @@ class XiaomiAirFresh(XiaomiGenericAirPurifier):
class XiaomiAirFreshA1(XiaomiGenericAirPurifier):
"""Representation of a Xiaomi Air Fresh A1."""
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the miio device."""
super().__init__(device, entry, unique_id, coordinator)
self._favorite_speed = None
self._favorite_speed: int | None = None
self._device_features = FEATURE_FLAGS_AIRFRESH_A1
self._preset_modes = PRESET_MODES_AIRFRESH_A1
self._attr_supported_features = (
@ -857,7 +899,7 @@ class XiaomiAirFreshA1(XiaomiGenericAirPurifier):
return
if await self._try_command(
"Setting fan level of the miio device failed.",
self._device.set_favorite_speed,
self._device.set_favorite_speed, # type: ignore[attr-defined]
favorite_speed,
):
self._favorite_speed = favorite_speed
@ -867,7 +909,7 @@ class XiaomiAirFreshA1(XiaomiGenericAirPurifier):
"""Set the preset mode of the fan. This method is a coroutine."""
if await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
self.operation_mode_class[preset_mode],
):
self._mode = self.operation_mode_class[preset_mode].value
@ -885,7 +927,13 @@ class XiaomiAirFreshA1(XiaomiGenericAirPurifier):
class XiaomiAirFreshT2017(XiaomiAirFreshA1):
"""Representation of a Xiaomi Air Fresh T2017."""
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the miio device."""
super().__init__(device, entry, unique_id, coordinator)
self._device_features = FEATURE_FLAGS_AIRFRESH_T2017
@ -897,7 +945,13 @@ class XiaomiGenericFan(XiaomiGenericDevice):
_attr_translation_key = "generic_fan"
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the fan."""
super().__init__(device, entry, unique_id, coordinator)
@ -922,9 +976,9 @@ class XiaomiGenericFan(XiaomiGenericDevice):
)
if self._model != MODEL_FAN_1C:
self._attr_supported_features |= FanEntityFeature.DIRECTION
self._preset_mode = None
self._oscillating = None
self._percentage = None
self._preset_mode: str | None = None
self._oscillating: bool | None = None
self._percentage: int | None = None
@property
def preset_mode(self) -> str | None:
@ -953,7 +1007,7 @@ class XiaomiGenericFan(XiaomiGenericDevice):
"""Set oscillation."""
await self._try_command(
"Setting oscillate on/off of the miio device failed.",
self._device.set_oscillate,
self._device.set_oscillate, # type: ignore[attr-defined]
oscillating,
)
self._oscillating = oscillating
@ -966,7 +1020,7 @@ class XiaomiGenericFan(XiaomiGenericDevice):
await self._try_command(
"Setting move direction of the miio device failed.",
self._device.set_rotate,
self._device.set_rotate, # type: ignore[attr-defined]
FanMoveDirection(FAN_DIRECTIONS_MAP[direction]),
)
@ -974,7 +1028,13 @@ class XiaomiGenericFan(XiaomiGenericDevice):
class XiaomiFan(XiaomiGenericFan):
"""Representation of a Xiaomi Fan."""
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the fan."""
super().__init__(device, entry, unique_id, coordinator)
@ -1018,13 +1078,13 @@ class XiaomiFan(XiaomiGenericFan):
if preset_mode == ATTR_MODE_NATURE:
await self._try_command(
"Setting natural fan speed percentage of the miio device failed.",
self._device.set_natural_speed,
self._device.set_natural_speed, # type: ignore[attr-defined]
self._percentage,
)
else:
await self._try_command(
"Setting direct fan speed percentage of the miio device failed.",
self._device.set_direct_speed,
self._device.set_direct_speed, # type: ignore[attr-defined]
self._percentage,
)
@ -1041,13 +1101,13 @@ class XiaomiFan(XiaomiGenericFan):
if self._nature_mode:
await self._try_command(
"Setting fan speed percentage of the miio device failed.",
self._device.set_natural_speed,
self._device.set_natural_speed, # type: ignore[attr-defined]
percentage,
)
else:
await self._try_command(
"Setting fan speed percentage of the miio device failed.",
self._device.set_direct_speed,
self._device.set_direct_speed, # type: ignore[attr-defined]
percentage,
)
self._percentage = percentage
@ -1061,7 +1121,13 @@ class XiaomiFan(XiaomiGenericFan):
class XiaomiFanP5(XiaomiGenericFan):
"""Representation of a Xiaomi Fan P5."""
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the fan."""
super().__init__(device, entry, unique_id, coordinator)
@ -1089,7 +1155,7 @@ class XiaomiFanP5(XiaomiGenericFan):
"""Set the preset mode of the fan."""
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
self.operation_mode_class[preset_mode],
)
self._preset_mode = preset_mode
@ -1104,7 +1170,7 @@ class XiaomiFanP5(XiaomiGenericFan):
await self._try_command(
"Setting fan speed percentage of the miio device failed.",
self._device.set_speed,
self._device.set_speed, # type: ignore[attr-defined]
percentage,
)
self._percentage = percentage
@ -1145,7 +1211,7 @@ class XiaomiFanMiot(XiaomiGenericFan):
"""Set the preset mode of the fan."""
await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
self.operation_mode_class[preset_mode],
)
self._preset_mode = preset_mode
@ -1160,7 +1226,7 @@ class XiaomiFanMiot(XiaomiGenericFan):
result = await self._try_command(
"Setting fan speed percentage of the miio device failed.",
self._device.set_speed,
self._device.set_speed, # type: ignore[attr-defined]
percentage,
)
if result:
@ -1184,7 +1250,13 @@ class XiaomiFanZA5(XiaomiFanMiot):
class XiaomiFan1C(XiaomiFanMiot):
"""Representation of a Xiaomi Fan 1C (Standing Fan 2 Lite)."""
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize MIOT fan with speed count."""
super().__init__(device, entry, unique_id, coordinator)
self._speed_count = 3
@ -1221,7 +1293,7 @@ class XiaomiFan1C(XiaomiFanMiot):
result = await self._try_command(
"Setting fan speed percentage of the miio device failed.",
self._device.set_speed,
self._device.set_speed, # type: ignore[attr-defined]
speed,
)

View File

@ -4,6 +4,7 @@ import logging
import math
from typing import Any
from miio import Device as MiioDevice
from miio.integrations.humidifier.deerma.airhumidifier_mjjsq import (
OperationMode as AirhumidifierMjjsqOperationMode,
)
@ -23,6 +24,7 @@ from homeassistant.components.humidifier import (
from homeassistant.const import ATTR_MODE, CONF_DEVICE, CONF_MODEL
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util.percentage import percentage_to_ranged_value
from .const import (
@ -108,26 +110,35 @@ async def async_setup_entry(
async_add_entities(entities)
class XiaomiGenericHumidifier(XiaomiCoordinatedMiioEntity, HumidifierEntity):
class XiaomiGenericHumidifier(
XiaomiCoordinatedMiioEntity[DataUpdateCoordinator[Any]], HumidifierEntity
):
"""Representation of a generic Xiaomi humidifier device."""
_attr_device_class = HumidifierDeviceClass.HUMIDIFIER
_attr_supported_features = HumidifierEntityFeature.MODES
_attr_name = None
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the generic Xiaomi device."""
super().__init__(device, entry, unique_id, coordinator=coordinator)
self._attributes = {}
self._mode = None
self._attributes: dict[str, Any] = {}
self._mode: str | int | None = None
self._humidity_steps = 100
self._target_humidity: float | None = None
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""
result = await self._try_command(
"Turning the miio device on failed.", self._device.on
"Turning the miio device on failed.",
self._device.on, # type: ignore[attr-defined]
)
if result:
self._attr_is_on = True
@ -136,7 +147,8 @@ class XiaomiGenericHumidifier(XiaomiCoordinatedMiioEntity, HumidifierEntity):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the device off."""
result = await self._try_command(
"Turning the miio device off failed.", self._device.off
"Turning the miio device off failed.",
self._device.off, # type: ignore[attr-defined]
)
if result:
@ -159,7 +171,13 @@ class XiaomiAirHumidifier(XiaomiGenericHumidifier, HumidifierEntity):
available_modes: list[str]
def __init__(self, device, entry, unique_id, coordinator):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[Any],
) -> None:
"""Initialize the plug switch."""
super().__init__(device, entry, unique_id, coordinator)
@ -228,7 +246,7 @@ class XiaomiAirHumidifier(XiaomiGenericHumidifier, HumidifierEntity):
_LOGGER.debug("Setting the target humidity to: %s", target_humidity)
if await self._try_command(
"Setting target humidity of the miio device failed.",
self._device.set_target_humidity,
self._device.set_target_humidity, # type: ignore[attr-defined]
target_humidity,
):
self._target_humidity = target_humidity
@ -243,7 +261,7 @@ class XiaomiAirHumidifier(XiaomiGenericHumidifier, HumidifierEntity):
_LOGGER.debug("Setting the operation mode to: Auto")
if await self._try_command(
"Setting operation mode of the miio device to MODE_AUTO failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
AirhumidifierOperationMode.Auto,
):
self._mode = AirhumidifierOperationMode.Auto.value
@ -261,7 +279,7 @@ class XiaomiAirHumidifier(XiaomiGenericHumidifier, HumidifierEntity):
_LOGGER.debug("Setting the operation mode to: %s", mode)
if await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
AirhumidifierOperationMode[mode],
):
self._mode = mode.lower()
@ -306,7 +324,7 @@ class XiaomiAirHumidifierMiot(XiaomiAirHumidifier):
_LOGGER.debug("Setting the humidity to: %s", target_humidity)
if await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_target_humidity,
self._device.set_target_humidity, # type: ignore[attr-defined]
target_humidity,
):
self._target_humidity = target_humidity
@ -320,7 +338,7 @@ class XiaomiAirHumidifierMiot(XiaomiAirHumidifier):
_LOGGER.debug("Setting the operation mode to: Auto")
if await self._try_command(
"Setting operation mode of the miio device to MODE_AUTO failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
AirhumidifierMiotOperationMode.Auto,
):
self._mode = 0
@ -339,7 +357,7 @@ class XiaomiAirHumidifierMiot(XiaomiAirHumidifier):
if self.is_on:
if await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
self.REVERSE_MODE_MAPPING[mode],
):
self._mode = self.REVERSE_MODE_MAPPING[mode].value
@ -381,7 +399,7 @@ class XiaomiAirHumidifierMjjsq(XiaomiAirHumidifier):
_LOGGER.debug("Setting the humidity to: %s", target_humidity)
if await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_target_humidity,
self._device.set_target_humidity, # type: ignore[attr-defined]
target_humidity,
):
self._target_humidity = target_humidity
@ -395,7 +413,7 @@ class XiaomiAirHumidifierMjjsq(XiaomiAirHumidifier):
_LOGGER.debug("Setting the operation mode to: Humidity")
if await self._try_command(
"Setting operation mode of the miio device to MODE_HUMIDITY failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
AirhumidifierMjjsqOperationMode.Humidity,
):
self._mode = 3
@ -411,7 +429,7 @@ class XiaomiAirHumidifierMjjsq(XiaomiAirHumidifier):
if self.is_on:
if await self._try_command(
"Setting operation mode of the miio device failed.",
self._device.set_mode,
self._device.set_mode, # type: ignore[attr-defined]
self.MODE_MAPPING[mode],
):
self._mode = self.MODE_MAPPING[mode].value

View File

@ -4,8 +4,9 @@ from __future__ import annotations
import dataclasses
from dataclasses import dataclass
from typing import Any
from miio import Device
from miio import Device as MiioDevice
from homeassistant.components.number import (
DOMAIN as PLATFORM_DOMAIN,
@ -350,17 +351,19 @@ async def async_setup_entry(
async_add_entities(entities)
class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity):
class XiaomiNumberEntity(
XiaomiCoordinatedMiioEntity[DataUpdateCoordinator[Any]], NumberEntity
):
"""Representation of a generic Xiaomi attribute selector."""
entity_description: XiaomiMiioNumberDescription
def __init__(
self,
device: Device,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str,
coordinator: DataUpdateCoordinator,
coordinator: DataUpdateCoordinator[Any],
description: XiaomiMiioNumberDescription,
) -> None:
"""Initialize the generic Xiaomi attribute selector."""
@ -402,7 +405,7 @@ class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity):
"""Set the target motor speed."""
return await self._try_command(
"Setting the target motor speed of the miio device failed.",
self._device.set_speed,
self._device.set_speed, # type: ignore[attr-defined]
motor_speed,
)
@ -410,7 +413,7 @@ class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity):
"""Set the favorite level."""
return await self._try_command(
"Setting the favorite level of the miio device failed.",
self._device.set_favorite_level,
self._device.set_favorite_level, # type: ignore[attr-defined]
level,
)
@ -418,7 +421,7 @@ class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity):
"""Set the fan level."""
return await self._try_command(
"Setting the fan level of the miio device failed.",
self._device.set_fan_level,
self._device.set_fan_level, # type: ignore[attr-defined]
level,
)
@ -426,21 +429,23 @@ class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity):
"""Set the volume."""
return await self._try_command(
"Setting the volume of the miio device failed.",
self._device.set_volume,
self._device.set_volume, # type: ignore[attr-defined]
volume,
)
async def async_set_oscillation_angle(self, angle: int) -> bool:
"""Set the volume."""
return await self._try_command(
"Setting angle of the miio device failed.", self._device.set_angle, angle
"Setting angle of the miio device failed.",
self._device.set_angle, # type: ignore[attr-defined]
angle,
)
async def async_set_delay_off_countdown(self, delay_off_countdown: int) -> bool:
"""Set the delay off countdown."""
return await self._try_command(
"Setting delay off miio device failed.",
self._device.delay_off,
self._device.delay_off, # type: ignore[attr-defined]
delay_off_countdown,
)
@ -448,7 +453,7 @@ class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity):
"""Set the led brightness level."""
return await self._try_command(
"Setting the led brightness level of the miio device failed.",
self._device.set_led_brightness_level,
self._device.set_led_brightness_level, # type: ignore[attr-defined]
level,
)
@ -456,7 +461,7 @@ class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity):
"""Set the led brightness level."""
return await self._try_command(
"Setting the led brightness level of the miio device failed.",
self._device.set_led_brightness,
self._device.set_led_brightness, # type: ignore[attr-defined]
level,
)
@ -464,6 +469,6 @@ class XiaomiNumberEntity(XiaomiCoordinatedMiioEntity, NumberEntity):
"""Set the target motor speed."""
return await self._try_command(
"Setting the favorite rpm of the miio device failed.",
self._device.set_favorite_rpm,
self._device.set_favorite_rpm, # type: ignore[attr-defined]
rpm,
)

View File

@ -4,8 +4,9 @@ from __future__ import annotations
from dataclasses import dataclass, field
import logging
from typing import NamedTuple
from typing import Any, NamedTuple
from miio import Device as MiioDevice
from miio.fan_common import LedBrightness as FanLedBrightness
from miio.integrations.airpurifier.dmaker.airfresh_t2017 import (
DisplayOrientation as AirfreshT2017DisplayOrientation,
@ -32,6 +33,7 @@ from homeassistant.components.select import SelectEntity, SelectEntityDescriptio
from homeassistant.const import CONF_DEVICE, CONF_MODEL, EntityCategory
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import (
CONF_FLOW_TYPE,
@ -87,7 +89,7 @@ class AttributeEnumMapping(NamedTuple):
enum_class: type
MODEL_TO_ATTR_MAP: dict[str, list] = {
MODEL_TO_ATTR_MAP: dict[str, list[AttributeEnumMapping]] = {
MODEL_AIRFRESH_T2017: [
AttributeEnumMapping(ATTR_DISPLAY_ORIENTATION, AirfreshT2017DisplayOrientation),
AttributeEnumMapping(ATTR_PTC_LEVEL, AirfreshT2017PtcLevel),
@ -232,10 +234,21 @@ async def async_setup_entry(
)
class XiaomiSelector(XiaomiCoordinatedMiioEntity, SelectEntity):
class XiaomiSelector(
XiaomiCoordinatedMiioEntity[DataUpdateCoordinator[Any]], SelectEntity
):
"""Representation of a generic Xiaomi attribute selector."""
def __init__(self, device, entry, unique_id, coordinator, description):
entity_description: XiaomiMiioSelectDescription
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str,
coordinator: DataUpdateCoordinator[Any],
description: XiaomiMiioSelectDescription,
) -> None:
"""Initialize the generic Xiaomi attribute selector."""
super().__init__(device, entry, unique_id, coordinator)
self.entity_description = description
@ -244,9 +257,15 @@ class XiaomiSelector(XiaomiCoordinatedMiioEntity, SelectEntity):
class XiaomiGenericSelector(XiaomiSelector):
"""Representation of a Xiaomi generic selector."""
entity_description: XiaomiMiioSelectDescription
def __init__(self, device, entry, unique_id, coordinator, description, enum_class):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str,
coordinator: DataUpdateCoordinator[Any],
description: XiaomiMiioSelectDescription,
enum_class: type,
) -> None:
"""Initialize the generic Xiaomi attribute selector."""
super().__init__(device, entry, unique_id, coordinator, description)
self._current_attr = enum_class(
@ -257,10 +276,10 @@ class XiaomiGenericSelector(XiaomiSelector):
if description.options_map:
self._options_map = {}
for key, val in enum_class._member_map_.items():
for key, val in enum_class._member_map_.items(): # type: ignore[attr-defined]
self._options_map[description.options_map[key]] = val
else:
self._options_map = enum_class._member_map_
self._options_map = enum_class._member_map_ # type: ignore[attr-defined]
self._reverse_map = {val: key for key, val in self._options_map.items()}
self._enum_class = enum_class

View File

@ -5,7 +5,7 @@ from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass
import logging
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from miio import AirQualityMonitor, Device as MiioDevice, DeviceException
from miio.gateway.devices import SubDevice
@ -854,12 +854,21 @@ async def async_setup_entry(
async_add_entities(entities)
class XiaomiGenericSensor(XiaomiCoordinatedMiioEntity, SensorEntity):
class XiaomiGenericSensor(
XiaomiCoordinatedMiioEntity[DataUpdateCoordinator[Any]], SensorEntity
):
"""Representation of a Xiaomi generic sensor."""
entity_description: XiaomiMiioSensorDescription
def __init__(self, device, entry, unique_id, coordinator, description):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str,
coordinator: DataUpdateCoordinator[Any],
description: XiaomiMiioSensorDescription,
) -> None:
"""Initialize the entity."""
super().__init__(device, entry, unique_id, coordinator)
self.entity_description = description

View File

@ -8,7 +8,13 @@ from functools import partial
import logging
from typing import Any
from miio import AirConditioningCompanionV3, ChuangmiPlug, DeviceException, PowerStrip
from miio import (
AirConditioningCompanionV3,
ChuangmiPlug,
Device as MiioDevice,
DeviceException,
PowerStrip,
)
from miio.gateway.devices import SubDevice
from miio.gateway.devices.switch import Switch
from miio.powerstrip import PowerMode
@ -520,12 +526,21 @@ async def async_setup_other_entry(
async_add_entities(entities)
class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
class XiaomiGenericCoordinatedSwitch(
XiaomiCoordinatedMiioEntity[DataUpdateCoordinator[Any]], SwitchEntity
):
"""Representation of a Xiaomi Plug Generic."""
entity_description: XiaomiMiioSwitchDescription
def __init__(self, device, entry, unique_id, coordinator, description):
def __init__(
self,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str,
coordinator: DataUpdateCoordinator[Any],
description: XiaomiMiioSwitchDescription,
) -> None:
"""Initialize the plug switch."""
super().__init__(device, entry, unique_id, coordinator)
@ -574,7 +589,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the buzzer on."""
return await self._try_command(
"Turning the buzzer of the miio device on failed.",
self._device.set_buzzer,
self._device.set_buzzer, # type: ignore[attr-defined]
True,
)
@ -582,7 +597,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the buzzer off."""
return await self._try_command(
"Turning the buzzer of the miio device off failed.",
self._device.set_buzzer,
self._device.set_buzzer, # type: ignore[attr-defined]
False,
)
@ -590,7 +605,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the child lock on."""
return await self._try_command(
"Turning the child lock of the miio device on failed.",
self._device.set_child_lock,
self._device.set_child_lock, # type: ignore[attr-defined]
True,
)
@ -598,7 +613,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the child lock off."""
return await self._try_command(
"Turning the child lock of the miio device off failed.",
self._device.set_child_lock,
self._device.set_child_lock, # type: ignore[attr-defined]
False,
)
@ -606,7 +621,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the display on."""
return await self._try_command(
"Turning the display of the miio device on failed.",
self._device.set_display,
self._device.set_display, # type: ignore[attr-defined]
True,
)
@ -614,7 +629,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the display off."""
return await self._try_command(
"Turning the display of the miio device off failed.",
self._device.set_display,
self._device.set_display, # type: ignore[attr-defined]
False,
)
@ -622,7 +637,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the dry mode on."""
return await self._try_command(
"Turning the dry mode of the miio device on failed.",
self._device.set_dry,
self._device.set_dry, # type: ignore[attr-defined]
True,
)
@ -630,7 +645,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the dry mode off."""
return await self._try_command(
"Turning the dry mode of the miio device off failed.",
self._device.set_dry,
self._device.set_dry, # type: ignore[attr-defined]
False,
)
@ -638,7 +653,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the dry mode on."""
return await self._try_command(
"Turning the clean mode of the miio device on failed.",
self._device.set_clean_mode,
self._device.set_clean_mode, # type: ignore[attr-defined]
True,
)
@ -646,7 +661,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the dry mode off."""
return await self._try_command(
"Turning the clean mode of the miio device off failed.",
self._device.set_clean_mode,
self._device.set_clean_mode, # type: ignore[attr-defined]
False,
)
@ -654,7 +669,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the led on."""
return await self._try_command(
"Turning the led of the miio device on failed.",
self._device.set_led,
self._device.set_led, # type: ignore[attr-defined]
True,
)
@ -662,7 +677,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the led off."""
return await self._try_command(
"Turning the led of the miio device off failed.",
self._device.set_led,
self._device.set_led, # type: ignore[attr-defined]
False,
)
@ -670,7 +685,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the learn mode on."""
return await self._try_command(
"Turning the learn mode of the miio device on failed.",
self._device.set_learn_mode,
self._device.set_learn_mode, # type: ignore[attr-defined]
True,
)
@ -678,7 +693,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn the learn mode off."""
return await self._try_command(
"Turning the learn mode of the miio device off failed.",
self._device.set_learn_mode,
self._device.set_learn_mode, # type: ignore[attr-defined]
False,
)
@ -686,7 +701,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn auto detect on."""
return await self._try_command(
"Turning auto detect of the miio device on failed.",
self._device.set_auto_detect,
self._device.set_auto_detect, # type: ignore[attr-defined]
True,
)
@ -694,7 +709,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn auto detect off."""
return await self._try_command(
"Turning auto detect of the miio device off failed.",
self._device.set_auto_detect,
self._device.set_auto_detect, # type: ignore[attr-defined]
False,
)
@ -702,7 +717,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn ionizer on."""
return await self._try_command(
"Turning ionizer of the miio device on failed.",
self._device.set_ionizer,
self._device.set_ionizer, # type: ignore[attr-defined]
True,
)
@ -710,7 +725,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn ionizer off."""
return await self._try_command(
"Turning ionizer of the miio device off failed.",
self._device.set_ionizer,
self._device.set_ionizer, # type: ignore[attr-defined]
False,
)
@ -718,7 +733,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn ionizer on."""
return await self._try_command(
"Turning ionizer of the miio device on failed.",
self._device.set_anion,
self._device.set_anion, # type: ignore[attr-defined]
True,
)
@ -726,7 +741,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn ionizer off."""
return await self._try_command(
"Turning ionizer of the miio device off failed.",
self._device.set_anion,
self._device.set_anion, # type: ignore[attr-defined]
False,
)
@ -734,7 +749,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn ionizer on."""
return await self._try_command(
"Turning ionizer of the miio device on failed.",
self._device.set_ptc,
self._device.set_ptc, # type: ignore[attr-defined]
True,
)
@ -742,7 +757,7 @@ class XiaomiGenericCoordinatedSwitch(XiaomiCoordinatedMiioEntity, SwitchEntity):
"""Turn ionizer off."""
return await self._try_command(
"Turning ionizer of the miio device off failed.",
self._device.set_ptc,
self._device.set_ptc, # type: ignore[attr-defined]
False,
)

View File

@ -6,7 +6,7 @@ from functools import partial
import logging
from typing import Any
from miio import DeviceException
from miio import Device as MiioDevice, DeviceException
import voluptuous as vol
from homeassistant.components.vacuum import (
@ -196,9 +196,9 @@ class MiroboVacuum(
def __init__(
self,
device,
entry,
unique_id,
device: MiioDevice,
entry: XiaomiMiioConfigEntry,
unique_id: str | None,
coordinator: DataUpdateCoordinator[VacuumCoordinatorData],
) -> None:
"""Initialize the Xiaomi vacuum cleaner robot handler."""
@ -281,16 +281,23 @@ class MiroboVacuum(
async def async_start(self) -> None:
"""Start or resume the cleaning task."""
await self._try_command(
"Unable to start the vacuum: %s", self._device.resume_or_start
"Unable to start the vacuum: %s",
self._device.resume_or_start, # type: ignore[attr-defined]
)
async def async_pause(self) -> None:
"""Pause the cleaning task."""
await self._try_command("Unable to set start/pause: %s", self._device.pause)
await self._try_command(
"Unable to set start/pause: %s",
self._device.pause, # type: ignore[attr-defined]
)
async def async_stop(self, **kwargs: Any) -> None:
"""Stop the vacuum cleaner."""
await self._try_command("Unable to stop: %s", self._device.stop)
await self._try_command(
"Unable to stop: %s",
self._device.stop, # type: ignore[attr-defined]
)
async def async_set_fan_speed(self, fan_speed: str, **kwargs: Any) -> None:
"""Set fan speed."""
@ -307,22 +314,31 @@ class MiroboVacuum(
)
return
await self._try_command(
"Unable to set fan speed: %s", self._device.set_fan_speed, fan_speed_int
"Unable to set fan speed: %s",
self._device.set_fan_speed, # type: ignore[attr-defined]
fan_speed_int,
)
async def async_return_to_base(self, **kwargs: Any) -> None:
"""Set the vacuum cleaner to return to the dock."""
await self._try_command("Unable to return home: %s", self._device.home)
await self._try_command(
"Unable to return home: %s",
self._device.home, # type: ignore[attr-defined]
)
async def async_clean_spot(self, **kwargs: Any) -> None:
"""Perform a spot clean-up."""
await self._try_command(
"Unable to start the vacuum for a spot clean-up: %s", self._device.spot
"Unable to start the vacuum for a spot clean-up: %s",
self._device.spot, # type: ignore[attr-defined]
)
async def async_locate(self, **kwargs: Any) -> None:
"""Locate the vacuum cleaner."""
await self._try_command("Unable to locate the botvac: %s", self._device.find)
await self._try_command(
"Unable to locate the botvac: %s",
self._device.find, # type: ignore[attr-defined]
)
async def async_send_command(
self,
@ -341,13 +357,15 @@ class MiroboVacuum(
async def async_remote_control_start(self) -> None:
"""Start remote control mode."""
await self._try_command(
"Unable to start remote control the vacuum: %s", self._device.manual_start
"Unable to start remote control the vacuum: %s",
self._device.manual_start, # type: ignore[attr-defined]
)
async def async_remote_control_stop(self) -> None:
"""Stop remote control mode."""
await self._try_command(
"Unable to stop remote control the vacuum: %s", self._device.manual_stop
"Unable to stop remote control the vacuum: %s",
self._device.manual_stop, # type: ignore[attr-defined]
)
async def async_remote_control_move(
@ -356,7 +374,7 @@ class MiroboVacuum(
"""Move vacuum with remote control mode."""
await self._try_command(
"Unable to move with remote control the vacuum: %s",
self._device.manual_control,
self._device.manual_control, # type: ignore[attr-defined]
velocity=velocity,
rotation=rotation,
duration=duration,
@ -368,7 +386,7 @@ class MiroboVacuum(
"""Move vacuum one step with remote control mode."""
await self._try_command(
"Unable to remote control the vacuum: %s",
self._device.manual_control_once,
self._device.manual_control_once, # type: ignore[attr-defined]
velocity=velocity,
rotation=rotation,
duration=duration,
@ -378,7 +396,7 @@ class MiroboVacuum(
"""Goto the specified coordinates."""
await self._try_command(
"Unable to send the vacuum cleaner to the specified coordinates: %s",
self._device.goto,
self._device.goto, # type: ignore[attr-defined]
x_coord=x_coord,
y_coord=y_coord,
)
@ -390,7 +408,7 @@ class MiroboVacuum(
await self._try_command(
"Unable to start cleaning of the specified segments: %s",
self._device.segment_clean,
self._device.segment_clean, # type: ignore[attr-defined]
segments=segments,
)
@ -400,7 +418,10 @@ class MiroboVacuum(
_zone.append(repeats)
_LOGGER.debug("Zone with repeats: %s", zone)
try:
await self.hass.async_add_executor_job(self._device.zoned_clean, zone)
await self.hass.async_add_executor_job(
self._device.zoned_clean, # type: ignore[attr-defined]
zone,
)
await self.coordinator.async_refresh()
except (OSError, DeviceException) as exc:
_LOGGER.error("Unable to send zoned_clean command to the vacuum: %s", exc)