Files
core/homeassistant/components/switchbot/fan.py
T
2026-07-03 11:57:43 +02:00

315 lines
10 KiB
Python

"""Support for SwitchBot Fans."""
from collections.abc import Mapping
import logging
from typing import Any, override
import switchbot
from switchbot import AirPurifierMode, FanMode, StandingFanMode
from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
from .const import DOMAIN
from .coordinator import SwitchbotConfigEntry, SwitchbotDataUpdateCoordinator
from .entity import SwitchbotEntity, exception_handler
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,
entry: SwitchbotConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Switchbot fan based on a config entry."""
coordinator = entry.runtime_data
if isinstance(coordinator.device, switchbot.SwitchbotAirPurifier):
async_add_entities([SwitchBotAirPurifierEntity(coordinator)])
elif isinstance(coordinator.device, switchbot.SwitchbotStandingFan):
async_add_entities([SwitchBotStandingFanEntity(coordinator)])
else:
async_add_entities([SwitchBotFanEntity(coordinator)])
class SwitchBotFanEntity(SwitchbotEntity, FanEntity, RestoreEntity):
"""Representation of a Switchbot."""
_device: switchbot.SwitchbotFan
_attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.OSCILLATE
| FanEntityFeature.PRESET_MODE
| FanEntityFeature.TURN_OFF
| FanEntityFeature.TURN_ON
)
_attr_preset_modes = FanMode.get_modes()
_attr_translation_key = "fan"
_attr_name = None
def __init__(self, coordinator: SwitchbotDataUpdateCoordinator) -> None:
"""Initialize the switchbot."""
super().__init__(coordinator)
self._attr_is_on = False
@property
@override
def is_on(self) -> bool | None:
"""Return true if device is on."""
return self._device.is_on()
@property
@override
def percentage(self) -> int | None:
"""Return the current speed as a percentage."""
return self._device.get_current_percentage()
@property
@override
def oscillating(self) -> bool | None:
"""Return whether or not the fan is currently oscillating."""
return self._device.get_oscillating_state()
@property
@override
def preset_mode(self) -> str | None:
"""Return the current preset mode."""
return self._device.get_current_mode()
@override
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan."""
_LOGGER.debug(
"Switchbot fan to set preset mode %s %s", preset_mode, self._address
)
self._last_run_success = bool(await self._device.set_preset_mode(preset_mode))
self.async_write_ha_state()
@override
async def async_set_percentage(self, percentage: int) -> None:
"""Set the speed percentage of the fan."""
_LOGGER.debug(
"Switchbot fan to set percentage %d %s", percentage, self._address
)
self._last_run_success = bool(await self._device.set_percentage(percentage))
self.async_write_ha_state()
@override
async def async_oscillate(self, oscillating: bool) -> None:
"""Oscillate the fan."""
_LOGGER.debug(
"Switchbot fan to set oscillating %s %s", oscillating, self._address
)
self._last_run_success = bool(await self._device.set_oscillation(oscillating))
self.async_write_ha_state()
@override
async def async_turn_on(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs: Any,
) -> None:
"""Turn on the fan."""
_LOGGER.debug(
"Switchbot fan to set turn on %s %s %s",
percentage,
preset_mode,
self._address,
)
self._last_run_success = bool(await self._device.turn_on())
self.async_write_ha_state()
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the fan."""
_LOGGER.debug("Switchbot fan to set turn off %s", self._address)
self._last_run_success = bool(await self._device.turn_off())
self.async_write_ha_state()
class SwitchBotStandingFanEntity(SwitchBotFanEntity):
"""Representation of a Switchbot Standing Fan."""
_device: switchbot.SwitchbotStandingFan
_attr_preset_modes = StandingFanMode.get_modes()
_attr_translation_key = "standing_fan"
@property
@override
def extra_state_attributes(self) -> Mapping[str, Any]:
"""Return the state attributes.
Failures surface as HomeAssistantError, so last_run_success is not used.
"""
return {}
@exception_handler
@override
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the fan."""
_LOGGER.debug(
"Switchbot standing fan to set preset mode %s %s",
preset_mode,
self._address,
)
if not await self._device.set_preset_mode(preset_mode):
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="set_fan_state_failed"
)
self.async_write_ha_state()
@exception_handler
@override
async def async_set_percentage(self, percentage: int) -> None:
"""Set the speed percentage of the fan."""
_LOGGER.debug(
"Switchbot standing fan to set percentage %d %s", percentage, self._address
)
if not await self._device.set_percentage(percentage):
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="set_fan_state_failed"
)
self.async_write_ha_state()
@exception_handler
@override
async def async_oscillate(self, oscillating: bool) -> None:
"""Oscillate the fan."""
_LOGGER.debug(
"Switchbot standing fan to set oscillating %s %s",
oscillating,
self._address,
)
if not await self._device.set_oscillation(oscillating):
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="set_fan_state_failed"
)
self.async_write_ha_state()
@exception_handler
@override
async def async_turn_on(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs: Any,
) -> None:
"""Turn on the fan."""
_LOGGER.debug(
"Switchbot standing fan to set turn on %s %s %s",
percentage,
preset_mode,
self._address,
)
if not await self._device.turn_on():
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="set_fan_state_failed"
)
self.async_write_ha_state()
@exception_handler
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the fan."""
_LOGGER.debug("Switchbot standing fan to set turn off %s", self._address)
if not await self._device.turn_off():
raise HomeAssistantError(
translation_domain=DOMAIN, translation_key="set_fan_state_failed"
)
self.async_write_ha_state()
class SwitchBotAirPurifierEntity(SwitchbotEntity, FanEntity):
"""Representation of a Switchbot air purifier."""
_device: switchbot.SwitchbotAirPurifier
_attr_supported_features = (
FanEntityFeature.PRESET_MODE
| FanEntityFeature.SET_SPEED
| FanEntityFeature.TURN_OFF
| FanEntityFeature.TURN_ON
)
_attr_preset_modes = AirPurifierMode.get_modes()
_attr_translation_key = "air_purifier"
_attr_name = None
@property
@override
def is_on(self) -> bool | None:
"""Return true if device is on."""
return self._device.is_on()
@property
@override
def preset_mode(self) -> str | None:
"""Return the current preset mode."""
return self._device.get_current_mode()
@property
@override
def percentage(self) -> int | None:
"""Return the speed percentage of the air purifier."""
return self._device.get_current_percentage()
@exception_handler
@override
async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set the preset mode of the air purifier."""
_LOGGER.debug(
"Switchbot air purifier to set preset mode %s %s",
preset_mode,
self._address,
)
self._last_run_success = bool(await self._device.set_preset_mode(preset_mode))
self.async_write_ha_state()
@exception_handler
@override
async def async_set_percentage(self, percentage: int) -> None:
"""Set the speed percentage of the air purifier."""
_LOGGER.debug(
"Switchbot air purifier to set percentage %d %s", percentage, self._address
)
await self._device.set_percentage(percentage)
self.async_write_ha_state()
@exception_handler
@override
async def async_turn_on(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs: Any,
) -> None:
"""Turn on the air purifier."""
_LOGGER.debug(
"Switchbot air purifier to set turn on %s %s %s",
percentage,
preset_mode,
self._address,
)
self._last_run_success = bool(await self._device.turn_on())
self.async_write_ha_state()
@exception_handler
@override
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the air purifier."""
_LOGGER.debug("Switchbot air purifier to set turn off %s", self._address)
self._last_run_success = bool(await self._device.turn_off())
self.async_write_ha_state()