text: """Support for VeSync fans."""
  from __future__ import annotations
  
  import logging
  import math
  from typing import Any
  
  from homeassistant.components.fan import FanEntity, FanEntityFeature
  from homeassistant.config_entries import ConfigEntry
  from homeassistant.core import HomeAssistant, callback
  from homeassistant.helpers.dispatcher import async_dispatcher_connect
  from homeassistant.helpers.entity_platform import AddEntitiesCallback
  from homeassistant.util.percentage import (
      int_states_in_range,
      percentage_to_ranged_value,
      ranged_value_to_percentage,
  )
  
  from .common import VeSyncDevice
  from .const import DOMAIN, SKU_TO_BASE_DEVICE, VS_DISCOVERY, VS_FANS
  
  _LOGGER = logging.getLogger(__name__)
  
  DEV_TYPE_TO_HA = {
      "LV-PUR131S": "fan",
      "Core200S": "fan",
      "Core300S": "fan",
      "Core400S": "fan",
      "Core600S": "fan",
  }
  
  FAN_MODE_AUTO = "auto"
  FAN_MODE_SLEEP = "sleep"
  
  PRESET_MODES = {
      "LV-PUR131S": [FAN_MODE_AUTO, FAN_MODE_SLEEP],
      "Core200S": [FAN_MODE_SLEEP],
      "Core300S": [FAN_MODE_AUTO, FAN_MODE_SLEEP],
      "Core400S": [FAN_MODE_AUTO, FAN_MODE_SLEEP],
      "Core600S": [FAN_MODE_AUTO, FAN_MODE_SLEEP],
  }
  SPEED_RANGE = {  # off is not included
      "LV-PUR131S": (1, 3),
      "Core200S": (1, 3),
      "Core300S": (1, 3),
      "Core400S": (1, 4),
      "Core600S": (1, 4),
  }
  
  
  async def async_setup_entry(
      hass: HomeAssistant,
      config_entry: ConfigEntry,
      async_add_entities: AddEntitiesCallback,
  ) -> None:
      """Set up the VeSync fan platform."""
  
      @callback
      def discover(devices):
          """Add new devices to platform."""
          _setup_entities(devices, async_add_entities)
  
      config_entry.async_on_unload(
          async_dispatcher_connect(hass, VS_DISCOVERY.format(VS_FANS), discover)
      )
  
      _setup_entities(hass.data[DOMAIN][VS_FANS], async_add_entities)
  
  
  @callback
  def _setup_entities(devices, async_add_entities):
      """Check if device is online and add entity."""
      entities = []
      for dev in devices:
          if DEV_TYPE_TO_HA.get(SKU_TO_BASE_DEVICE.get(dev.device_type)) == "fan":
              entities.append(VeSyncFanHA(dev))
          else:
              _LOGGER.warning(
                  "%s - Unknown device type - %s", dev.device_name, dev.device_type
              )
              continue
  
      async_add_entities(entities, update_before_add=True)
  
  
  class VeSyncFanHA(VeSyncDevice, FanEntity):
      """Representation of a VeSync fan."""
  
      _attr_supported_features = FanEntityFeature.SET_SPEED
  
      def __init__(self, fan):
          """Initialize the VeSync fan device."""
          super().__init__(fan)
          self.smartfan = fan
  
      @property
      def percentage(self) -> int | None:
          """Return the current speed."""
          if (
              self.smartfan.mode == "manual"
              and (current_level := self.smartfan.fan_level) is not None
          ):
              return ranged_value_to_percentage(
                  SPEED_RANGE[SKU_TO_BASE_DEVICE[self.device.device_type]], current_level
              )
          return None
  
      @property
      def speed_count(self) -> int:
          """Return the number of speeds the fan supports."""
          return int_states_in_range(
              SPEED_RANGE[SKU_TO_BASE_DEVICE[self.device.device_type]]
          )
  
      @property
      def preset_modes(self) -> list[str]:
          """Get the list of available preset modes."""
          return PRESET_MODES[SKU_TO_BASE_DEVICE[self.device.device_type]]
  
      @property
      def preset_mode(self) -> str | None:
          """Get the current preset mode."""
          if self.smartfan.mode in (FAN_MODE_AUTO, FAN_MODE_SLEEP):
              return self.smartfan.mode
          return None
  
      @property
      def unique_info(self):
          """Return the ID of this fan."""
          return self.smartfan.uuid
  
      @property
      def extra_state_attributes(self):
          """Return the state attributes of the fan."""
          attr = {}
  
          if hasattr(self.smartfan, "active_time"):
              attr["active_time"] = self.smartfan.active_time
  
          if hasattr(self.smartfan, "screen_status"):
              attr["screen_status"] = self.smartfan.screen_status
  
          if hasattr(self.smartfan, "child_lock"):
              attr["child_lock"] = self.smartfan.child_lock
  
          if hasattr(self.smartfan, "night_light"):
              attr["night_light"] = self.smartfan.night_light
  
          if hasattr(self.smartfan, "mode"):
              attr["mode"] = self.smartfan.mode
  
          return attr
  
      def set_percentage(self, percentage: int) -> None:
          """Set the speed of the device."""
          if percentage == 0:
              self.smartfan.turn_off()
              return
  
          if not self.smartfan.is_on:
              self.smartfan.turn_on()
  
          self.smartfan.manual_mode()
          self.smartfan.change_fan_speed(
              math.ceil(
                  percentage_to_ranged_value(
                      SPEED_RANGE[SKU_TO_BASE_DEVICE[self.device.device_type]], percentage
                  )
              )
          )
          self.schedule_update_ha_state()
  
      def set_preset_mode(self, preset_mode: str) -> None:
          """Set the preset mode of device."""
          if preset_mode not in self.preset_modes:
              raise ValueError(
                  f"{preset_mode} is not one of the valid preset modes: "
                  f"{self.preset_modes}"
              )
  
          if not self.smartfan.is_on:
              self.smartfan.turn_on()
  
          if preset_mode == FAN_MODE_AUTO:
              self.smartfan.auto_mode()
          elif preset_mode == FAN_MODE_SLEEP:
              self.smartfan.sleep_mode()
  
          self.schedule_update_ha_state()
  
      def turn_on(
          self,
          percentage: int | None = None,
          preset_mode: str | None = None,
          **kwargs: Any,
      ) -> None:
          """Turn the device on."""
          if preset_mode:
              self.set_preset_mode(preset_mode)
              return
          if percentage is None:
              percentage = 50
          self.set_percentage(percentage)
  