diff --git a/homeassistant/components/esphome/fan.py b/homeassistant/components/esphome/fan.py
index 9942498e12db43a475c15c730c3c651b5d7e9d0e..08135e1a70258d2ca9e2de0f3645157aab9b5e70 100644
--- a/homeassistant/components/esphome/fan.py
+++ b/homeassistant/components/esphome/fan.py
@@ -105,6 +105,10 @@ class EsphomeFan(EsphomeEntity[FanInfo, FanState], FanEntity):
             key=self._key, direction=_FAN_DIRECTIONS.from_hass(direction)
         )
 
+    async def async_set_preset_mode(self, preset_mode: str) -> None:
+        """Set the preset mode of the fan."""
+        await self._client.fan_command(key=self._key, preset_mode=preset_mode)
+
     @property
     @esphome_state_property
     def is_on(self) -> bool | None:
@@ -144,6 +148,17 @@ class EsphomeFan(EsphomeEntity[FanInfo, FanState], FanEntity):
         """Return the current fan direction."""
         return _FAN_DIRECTIONS.from_esphome(self._state.direction)
 
+    @property
+    @esphome_state_property
+    def preset_mode(self) -> str | None:
+        """Return the current fan preset mode."""
+        return self._state.preset_mode
+
+    @property
+    def preset_modes(self) -> list[str] | None:
+        """Return the supported fan preset modes."""
+        return self._static_info.supported_preset_modes
+
     @callback
     def _on_static_info_update(self, static_info: EntityInfo) -> None:
         """Set attrs from static info."""
@@ -156,4 +171,6 @@ class EsphomeFan(EsphomeEntity[FanInfo, FanState], FanEntity):
             flags |= FanEntityFeature.SET_SPEED
         if static_info.supports_direction:
             flags |= FanEntityFeature.DIRECTION
+        if static_info.supported_preset_modes:
+            flags |= FanEntityFeature.PRESET_MODE
         self._attr_supported_features = flags
diff --git a/tests/components/esphome/test_fan.py b/tests/components/esphome/test_fan.py
index 99f4bbc86a9fe2848877c13f1d918593fade61c3..6f383dcb6ba317f9fbe81b057ee85e7c31ad0977 100644
--- a/tests/components/esphome/test_fan.py
+++ b/tests/components/esphome/test_fan.py
@@ -16,12 +16,14 @@ from homeassistant.components.fan import (
     ATTR_DIRECTION,
     ATTR_OSCILLATING,
     ATTR_PERCENTAGE,
+    ATTR_PRESET_MODE,
     DOMAIN as FAN_DOMAIN,
     SERVICE_DECREASE_SPEED,
     SERVICE_INCREASE_SPEED,
     SERVICE_OSCILLATE,
     SERVICE_SET_DIRECTION,
     SERVICE_SET_PERCENTAGE,
+    SERVICE_SET_PRESET_MODE,
     SERVICE_TURN_OFF,
     SERVICE_TURN_ON,
     STATE_ON,
@@ -145,6 +147,7 @@ async def test_fan_entity_with_all_features_new_api(
             supports_direction=True,
             supports_speed=True,
             supports_oscillation=True,
+            supported_preset_modes=["Preset1", "Preset2"],
         )
     ]
     states = [
@@ -154,6 +157,7 @@ async def test_fan_entity_with_all_features_new_api(
             oscillating=True,
             speed_level=3,
             direction=FanDirection.REVERSE,
+            preset_mode=None,
         )
     ]
     user_service = []
@@ -270,6 +274,15 @@ async def test_fan_entity_with_all_features_new_api(
     )
     mock_client.fan_command.reset_mock()
 
+    await hass.services.async_call(
+        FAN_DOMAIN,
+        SERVICE_SET_PRESET_MODE,
+        {ATTR_ENTITY_ID: "fan.test_myfan", ATTR_PRESET_MODE: "Preset1"},
+        blocking=True,
+    )
+    mock_client.fan_command.assert_has_calls([call(key=1, preset_mode="Preset1")])
+    mock_client.fan_command.reset_mock()
+
 
 async def test_fan_entity_with_no_features_new_api(
     hass: HomeAssistant, mock_client: APIClient, mock_generic_device_entry
@@ -285,6 +298,7 @@ async def test_fan_entity_with_no_features_new_api(
             supports_direction=False,
             supports_speed=False,
             supports_oscillation=False,
+            supported_preset_modes=[],
         )
     ]
     states = [FanState(key=1, state=True)]