Files
HomeAssistantVS/custom_components/midea_ac_lan/fan.py
T

376 lines
13 KiB
Python

"""Midea Fan entries."""
import logging
from typing import Any, cast
from homeassistant.components.fan import FanEntity, FanEntityFeature
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_DEVICE_ID,
CONF_SWITCHES,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from midealocal.device import DeviceType
from midealocal.devices.ac import DeviceAttributes as ACAttributes
from midealocal.devices.ac import MideaACDevice
from midealocal.devices.b6 import MideaB6Device
from midealocal.devices.ce import DeviceAttributes as CEAttributes
from midealocal.devices.ce import MideaCEDevice
from midealocal.devices.fa import MideaFADevice
from midealocal.devices.x40 import DeviceAttributes as X40Attributes
from midealocal.devices.x40 import MideaX40Device
from .const import DEVICES, DOMAIN
from .midea_devices import (
FRESH_AIR_EXHAUST,
FRESH_AIR_EXHAUST_MODE,
FRESH_AIR_EXHAUST_POWER,
FRESH_AIR_EXHAUST_SPEED,
MIDEA_DEVICES,
)
from .midea_entity import MideaEntity
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up fan entries."""
device_id = config_entry.data.get(CONF_DEVICE_ID)
device = hass.data[DOMAIN][DEVICES].get(device_id)
extra_switches = config_entry.options.get(CONF_SWITCHES, [])
devs: list[
MideaFAFan | MideaB6Fan | MideaACFreshAirFan | MideaCEFan | MideaX40Fan
] = []
for entity_key, config in cast(
"dict",
MIDEA_DEVICES[device.device_type]["entities"],
).items():
required_attribute = config.get("required_attribute")
if (
required_attribute is not None
and required_attribute not in device.attributes
):
continue
if config["type"] == Platform.FAN and (
config.get("default") or entity_key in extra_switches
):
if device.device_type == DeviceType.FA:
devs.append(MideaFAFan(device, entity_key))
elif device.device_type == DeviceType.B6:
devs.append(MideaB6Fan(device, entity_key))
elif device.device_type == DeviceType.AC:
devs.append(MideaACFreshAirFan(device, entity_key))
elif device.device_type == DeviceType.CE:
devs.append(MideaCEFan(device, entity_key))
elif device.device_type == DeviceType.X40:
devs.append(MideaX40Fan(device, entity_key))
async_add_entities(devs)
# HA version >= 2024,8 support TURN_ON | TURN_OFF,for future changes, ref PR #285
try:
FAN_FEATURE_TURN_ON_OFF = FanEntityFeature["TURN_ON"] | FanEntityFeature["TURN_OFF"]
except KeyError:
FAN_FEATURE_TURN_ON_OFF = FanEntityFeature(0)
class MideaFan(MideaEntity, FanEntity):
"""Midea Fan Entries Base Class."""
_enable_turn_on_off_backwards_compatibility = False # 2024.8~2025.1
@property
def preset_modes(self) -> list[str] | None:
"""Midea Fan preset modes."""
return (
self._device.preset_modes if hasattr(self._device, "preset_modes") else None
)
@property
def is_on(self) -> bool:
"""Midea Fan is on."""
return cast("bool", self._device.get_attribute("power"))
@property
def oscillating(self) -> bool:
"""Midea Fan oscillating."""
return cast("bool", self._device.get_attribute("oscillate"))
@property
def preset_mode(self) -> str | None:
"""Midea Fan preset mode."""
return cast("str", self._device.get_attribute("mode"))
@property
def fan_speed(self) -> int | None:
"""Midea Fan fan speed."""
return cast("int", self._device.get_attribute("fan_speed"))
def turn_off(self, **kwargs: Any) -> None: # ruff:ignore[any-type, unused-method-argument]
"""Midea Fan turn off."""
self._device.set_attribute(attr="power", value=False)
def oscillate(self, oscillating: bool) -> None:
"""Midea Fan oscillate."""
self._device.set_attribute(attr="oscillate", value=oscillating)
def set_preset_mode(self, preset_mode: str) -> None:
"""Midea Fan set preset mode."""
self._device.set_attribute(attr="mode", value=preset_mode.capitalize())
@property
def percentage(self) -> int | None:
"""Midea Fan percentage."""
if not self.fan_speed:
return None
return int(round(self.fan_speed * self.percentage_step)) # ruff:ignore[unnecessary-cast-to-int]
def set_percentage(self, percentage: int) -> None:
"""Midea Fan set percentage."""
fan_speed = round(percentage / self.percentage_step)
self._device.set_attribute(attr="fan_speed", value=fan_speed)
async def async_set_percentage(self, percentage: int) -> None:
"""Midea Fan async set percentage."""
if percentage == 0:
await self.async_turn_off()
else:
await self.hass.async_add_executor_job(self.set_percentage, percentage)
def update_state(self, status: Any) -> None: # ruff:ignore[any-type, unused-method-argument]
"""Midea Fan update state."""
if not self.hass:
_LOGGER.warning(
"Fan update_state skipped for %s [%s]: HASS is None",
self.name,
type(self),
)
return
self.schedule_update_if_running()
class MideaFAFan(MideaFan):
"""Midea FA Fan Entries."""
_device: MideaFADevice
def __init__(self, device: MideaFADevice, entity_key: str) -> None:
"""Midea FA Fan entity init."""
super().__init__(device, entity_key)
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.OSCILLATE
| FanEntityFeature.PRESET_MODE
| FAN_FEATURE_TURN_ON_OFF
)
self._attr_speed_count = self._device.speed_count
def turn_on(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs: Any, # ruff:ignore[any-type, unused-method-argument]
) -> None:
"""Midea FA Fan turn on."""
fan_speed = int(percentage / self.percentage_step + 0.5) if percentage else None
self._device.turn_on(fan_speed=fan_speed, mode=preset_mode)
class MideaB6Fan(MideaFan):
"""Midea B6 Fan Entries."""
_device: MideaB6Device
def __init__(self, device: MideaB6Device, entity_key: str) -> None:
"""Midea B6 Fan entity init."""
super().__init__(device, entity_key)
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.PRESET_MODE
| FAN_FEATURE_TURN_ON_OFF
)
self._attr_speed_count = self._device.speed_count
def turn_on(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs: Any, # ruff:ignore[any-type, unused-method-argument]
) -> None:
"""Midea B6 Fan turn on."""
fan_speed = int(percentage / self.percentage_step + 0.5) if percentage else None
self._device.turn_on(fan_speed=fan_speed, mode=preset_mode)
class MideaACFreshAirFan(MideaFan):
"""Midea AC Fresh Air Fan Entries."""
_device: MideaACDevice
def __init__(self, device: MideaACDevice, entity_key: str) -> None:
"""Midea AC Fresh Air Fan entity init."""
super().__init__(device, entity_key)
self._is_exhaust = entity_key == FRESH_AIR_EXHAUST
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.PRESET_MODE
| FAN_FEATURE_TURN_ON_OFF
)
self._attr_speed_count = 100
@property
def preset_modes(self) -> list[str] | None:
"""Midea AC Fan preset modes."""
if self._is_exhaust:
return cast(
"list[str]",
getattr(self._device, "fresh_air_exhaust_fan_speeds", []),
)
return cast("list", self._device.fresh_air_fan_speeds)
@property
def is_on(self) -> bool:
"""Midea AC Fan is on."""
attribute = (
FRESH_AIR_EXHAUST_POWER
if self._is_exhaust
else ACAttributes.fresh_air_power
)
return cast("bool", self._device.get_attribute(attribute))
@property
def fan_speed(self) -> int:
"""Midea AC Fan fan speed."""
attribute = (
FRESH_AIR_EXHAUST_SPEED
if self._is_exhaust
else ACAttributes.fresh_air_fan_speed
)
return cast("int", self._device.get_attribute(attribute))
def turn_on(
self,
percentage: int | None = None,
preset_mode: str | None = None,
**kwargs: Any, # ruff:ignore[any-type, unused-method-argument]
) -> None:
"""Midea AC Fan tun on."""
if preset_mode is not None:
self.set_preset_mode(preset_mode)
return
if percentage is not None:
self.set_percentage(percentage)
return
attribute = (
FRESH_AIR_EXHAUST_POWER
if self._is_exhaust
else ACAttributes.fresh_air_power
)
self._device.set_attribute(attr=attribute, value=True)
def turn_off(self, **kwargs: Any) -> None: # ruff:ignore[any-type, unused-method-argument]
"""Midea AC Fan turn off."""
attribute = (
FRESH_AIR_EXHAUST_POWER
if self._is_exhaust
else ACAttributes.fresh_air_power
)
self._device.set_attribute(attr=attribute, value=False)
def set_percentage(self, percentage: int) -> None:
"""Midea AC Fan set percentage."""
fan_speed = int(percentage / self.percentage_step + 0.5)
attribute = (
FRESH_AIR_EXHAUST_SPEED
if self._is_exhaust
else ACAttributes.fresh_air_fan_speed
)
self._device.set_attribute(
attr=attribute,
value=fan_speed,
)
def set_preset_mode(self, preset_mode: str) -> None:
"""Midea AC Fan set preset mode."""
attribute = (
FRESH_AIR_EXHAUST_MODE if self._is_exhaust else ACAttributes.fresh_air_mode
)
self._device.set_attribute(attr=attribute, value=preset_mode)
@property
def preset_mode(self) -> str | None:
"""Midea AC Fan preset mode."""
attribute = (
FRESH_AIR_EXHAUST_MODE if self._is_exhaust else ACAttributes.fresh_air_mode
)
return cast("str", self._device.get_attribute(attr=attribute))
class MideaCEFan(MideaFan):
"""Midea CE Fan Entries."""
_device: MideaCEDevice
def __init__(self, device: MideaCEDevice, entity_key: str) -> None:
"""Midea CE Fan entity init."""
super().__init__(device, entity_key)
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.PRESET_MODE
| FAN_FEATURE_TURN_ON_OFF
)
self._attr_speed_count = self._device.speed_count
def turn_on(
self,
percentage: int | None = None, # ruff:ignore[unused-method-argument]
preset_mode: str | None = None, # ruff:ignore[unused-method-argument]
**kwargs: Any, # ruff:ignore[any-type, unused-method-argument]
) -> None:
"""Midea CE Fan turn on."""
self._device.set_attribute(attr=CEAttributes.power, value=True)
async def async_set_percentage(self, percentage: int) -> None:
"""Midea CE Fan async set percentage."""
await self.hass.async_add_executor_job(self.set_percentage, percentage)
class MideaX40Fan(MideaFan):
"""Midea X40 Fan Entries."""
_device: MideaX40Device
def __init__(self, device: MideaX40Device, entity_key: str) -> None:
"""Midea X40 Fan entity init."""
super().__init__(device, entity_key)
self._attr_supported_features = (
FanEntityFeature.SET_SPEED
| FanEntityFeature.OSCILLATE
| FAN_FEATURE_TURN_ON_OFF
)
self._attr_speed_count = 2
@property
def is_on(self) -> bool:
"""Midea X40 Fan is on."""
return cast("int", self._device.get_attribute(attr=X40Attributes.fan_speed)) > 0
def turn_on(
self,
percentage: int | None = None, # ruff:ignore[unused-method-argument]
preset_mode: str | None = None, # ruff:ignore[unused-method-argument]
**kwargs: Any, # ruff:ignore[any-type, unused-method-argument]
) -> None:
"""Midea X40 Fan turn on."""
self._device.set_attribute(attr=X40Attributes.fan_speed, value=1)
def turn_off(self, **kwargs: Any) -> None: # ruff:ignore[any-type, unused-method-argument]
"""Midea X40 Fan turn off."""
self._device.set_attribute(attr=X40Attributes.fan_speed, value=0)