diff --git a/.strict-typing b/.strict-typing
index 4ee01b15d1a11c66586517247eef44d196b81613..a9e86b6585418c85515eb04df1b858b079cff67a 100644
--- a/.strict-typing
+++ b/.strict-typing
@@ -334,6 +334,7 @@ homeassistant.components.synology_dsm.*
 homeassistant.components.systemmonitor.*
 homeassistant.components.tag.*
 homeassistant.components.tailscale.*
+homeassistant.components.tailwind.*
 homeassistant.components.tami4.*
 homeassistant.components.tautulli.*
 homeassistant.components.tcp.*
diff --git a/CODEOWNERS b/CODEOWNERS
index 17b5909471da90a08736151e7aa5e297c75915ec..4ad2a38fa041e68010b340e4bd690e7596ca6117 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1289,6 +1289,8 @@ build.json @home-assistant/supervisor
 /tests/components/tag/ @balloob @dmulcahey
 /homeassistant/components/tailscale/ @frenck
 /tests/components/tailscale/ @frenck
+/homeassistant/components/tailwind/ @frenck
+/tests/components/tailwind/ @frenck
 /homeassistant/components/tami4/ @Guy293
 /tests/components/tami4/ @Guy293
 /homeassistant/components/tankerkoenig/ @guillempages @mib1185
diff --git a/homeassistant/components/tailwind/__init__.py b/homeassistant/components/tailwind/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2691e9c36e8dffcd5d3c0e426e44c63feb9a56f4
--- /dev/null
+++ b/homeassistant/components/tailwind/__init__.py
@@ -0,0 +1,29 @@
+"""Integration for Tailwind devices."""
+from __future__ import annotations
+
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import Platform
+from homeassistant.core import HomeAssistant
+
+from .const import DOMAIN
+from .coordinator import TailwindDataUpdateCoordinator
+
+PLATFORMS = [Platform.NUMBER]
+
+
+async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
+    """Set up Tailwind device from a config entry."""
+    coordinator = TailwindDataUpdateCoordinator(hass, entry)
+    await coordinator.async_config_entry_first_refresh()
+
+    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
+    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
+
+    return True
+
+
+async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
+    """Unload Tailwind config entry."""
+    if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
+        del hass.data[DOMAIN][entry.entry_id]
+    return unload_ok
diff --git a/homeassistant/components/tailwind/config_flow.py b/homeassistant/components/tailwind/config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9bf5ab1a44684fa3b91f43fbe6e8d3d66fdc437
--- /dev/null
+++ b/homeassistant/components/tailwind/config_flow.py
@@ -0,0 +1,79 @@
+"""Config flow to configure the Tailwind integration."""
+from __future__ import annotations
+
+from typing import Any
+
+from gotailwind import (
+    Tailwind,
+    TailwindAuthenticationError,
+    TailwindConnectionError,
+    TailwindUnsupportedFirmwareVersionError,
+)
+import voluptuous as vol
+
+from homeassistant.config_entries import ConfigFlow
+from homeassistant.const import CONF_HOST, CONF_TOKEN
+from homeassistant.data_entry_flow import FlowResult
+from homeassistant.helpers.aiohttp_client import async_get_clientsession
+from homeassistant.helpers.selector import (
+    TextSelector,
+    TextSelectorConfig,
+    TextSelectorType,
+)
+
+from .const import DOMAIN, LOGGER
+
+
+class TailwindFlowHandler(ConfigFlow, domain=DOMAIN):
+    """Handle a Tailwind config flow."""
+
+    VERSION = 1
+
+    async def async_step_user(
+        self, user_input: dict[str, Any] | None = None
+    ) -> FlowResult:
+        """Handle a flow initiated by the user."""
+        errors = {}
+
+        if user_input is not None:
+            tailwind = Tailwind(
+                host=user_input[CONF_HOST],
+                token=user_input[CONF_TOKEN],
+                session=async_get_clientsession(self.hass),
+            )
+            try:
+                status = await tailwind.status()
+            except TailwindUnsupportedFirmwareVersionError:
+                return self.async_abort(reason="unsupported_firmware")
+            except TailwindAuthenticationError:
+                errors[CONF_TOKEN] = "invalid_auth"
+            except TailwindConnectionError:
+                errors[CONF_HOST] = "cannot_connect"
+            except Exception:  # pylint: disable=broad-except
+                LOGGER.exception("Unexpected exception")
+                errors["base"] = "unknown"
+            else:
+                return self.async_create_entry(
+                    title=f"Tailwind {status.product}",
+                    data=user_input,
+                )
+        else:
+            user_input = {}
+
+        return self.async_show_form(
+            step_id="user",
+            data_schema=vol.Schema(
+                {
+                    vol.Required(
+                        CONF_HOST, default=user_input.get(CONF_HOST)
+                    ): TextSelector(TextSelectorConfig(autocomplete="off")),
+                    vol.Required(CONF_TOKEN): TextSelector(
+                        TextSelectorConfig(type=TextSelectorType.PASSWORD)
+                    ),
+                }
+            ),
+            description_placeholders={
+                "url": "https://web.gotailwind.com/client/integration/local-control-key",
+            },
+            errors=errors,
+        )
diff --git a/homeassistant/components/tailwind/const.py b/homeassistant/components/tailwind/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..99e5bb0f1bf98501e0da3e542ee4dafe5698843a
--- /dev/null
+++ b/homeassistant/components/tailwind/const.py
@@ -0,0 +1,9 @@
+"""Constants for the Tailwind integration."""
+from __future__ import annotations
+
+import logging
+from typing import Final
+
+DOMAIN: Final = "tailwind"
+
+LOGGER = logging.getLogger(__package__)
diff --git a/homeassistant/components/tailwind/coordinator.py b/homeassistant/components/tailwind/coordinator.py
new file mode 100644
index 0000000000000000000000000000000000000000..46b3d0740456e4d5fbd3542caad8d8156b633f32
--- /dev/null
+++ b/homeassistant/components/tailwind/coordinator.py
@@ -0,0 +1,39 @@
+"""Data update coordinator for Tailwind."""
+from datetime import timedelta
+
+from gotailwind import Tailwind, TailwindDeviceStatus, TailwindError
+
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import CONF_HOST, CONF_TOKEN
+from homeassistant.core import HomeAssistant
+from homeassistant.helpers.aiohttp_client import async_get_clientsession
+from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
+
+from .const import DOMAIN, LOGGER
+
+
+class TailwindDataUpdateCoordinator(DataUpdateCoordinator[TailwindDeviceStatus]):
+    """Class to manage fetching Tailwind data."""
+
+    config_entry: ConfigEntry
+
+    def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
+        """Initialize the coordinator."""
+        self.tailwind = Tailwind(
+            host=entry.data[CONF_HOST],
+            token=entry.data[CONF_TOKEN],
+            session=async_get_clientsession(hass),
+        )
+        super().__init__(
+            hass,
+            LOGGER,
+            name=f"{DOMAIN}_{entry.data[CONF_HOST]}",
+            update_interval=timedelta(seconds=5),
+        )
+
+    async def _async_update_data(self) -> TailwindDeviceStatus:
+        """Fetch data from the Tailwind device."""
+        try:
+            return await self.tailwind.status()
+        except TailwindError as err:
+            raise UpdateFailed(err) from err
diff --git a/homeassistant/components/tailwind/entity.py b/homeassistant/components/tailwind/entity.py
new file mode 100644
index 0000000000000000000000000000000000000000..1077e2eb8880fb3860ece6d235a419d7d7fb85ef
--- /dev/null
+++ b/homeassistant/components/tailwind/entity.py
@@ -0,0 +1,25 @@
+"""Base entity for the Tailwind integration."""
+from __future__ import annotations
+
+from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
+from homeassistant.helpers.update_coordinator import CoordinatorEntity
+
+from .const import DOMAIN
+from .coordinator import TailwindDataUpdateCoordinator
+
+
+class TailwindEntity(CoordinatorEntity[TailwindDataUpdateCoordinator]):
+    """Defines an Tailwind entity."""
+
+    _attr_has_entity_name = True
+
+    def __init__(self, coordinator: TailwindDataUpdateCoordinator) -> None:
+        """Initialize an Tailwind entity."""
+        super().__init__(coordinator)
+        self._attr_device_info = DeviceInfo(
+            identifiers={(DOMAIN, coordinator.data.device_id)},
+            connections={(CONNECTION_NETWORK_MAC, coordinator.data.mac_address)},
+            manufacturer="Tailwind",
+            model=coordinator.data.product,
+            sw_version=coordinator.data.firmware_version,
+        )
diff --git a/homeassistant/components/tailwind/manifest.json b/homeassistant/components/tailwind/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..4f4f5236f52835b58ebdf2b512f4327fac1582a6
--- /dev/null
+++ b/homeassistant/components/tailwind/manifest.json
@@ -0,0 +1,10 @@
+{
+  "domain": "tailwind",
+  "name": "Tailwind",
+  "codeowners": ["@frenck"],
+  "config_flow": true,
+  "documentation": "https://www.home-assistant.io/integrations/tailwind",
+  "integration_type": "device",
+  "iot_class": "local_polling",
+  "requirements": ["gotailwind==0.2.1"]
+}
diff --git a/homeassistant/components/tailwind/number.py b/homeassistant/components/tailwind/number.py
new file mode 100644
index 0000000000000000000000000000000000000000..19d234571216165d79b40226874f82da43d7b770
--- /dev/null
+++ b/homeassistant/components/tailwind/number.py
@@ -0,0 +1,86 @@
+"""Number entity platform for Tailwind."""
+from __future__ import annotations
+
+from collections.abc import Awaitable, Callable
+from dataclasses import dataclass
+from typing import Any
+
+from gotailwind import Tailwind, TailwindDeviceStatus
+
+from homeassistant.components.number import NumberEntity, NumberEntityDescription
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import PERCENTAGE, EntityCategory
+from homeassistant.core import HomeAssistant
+from homeassistant.helpers.entity_platform import AddEntitiesCallback
+
+from .const import DOMAIN
+from .coordinator import TailwindDataUpdateCoordinator
+from .entity import TailwindEntity
+
+
+@dataclass(kw_only=True)
+class TailwindNumberEntityDescription(NumberEntityDescription):
+    """Class describing Tailwind number entities."""
+
+    value_fn: Callable[[TailwindDeviceStatus], int]
+    set_value_fn: Callable[[Tailwind, float], Awaitable[Any]]
+
+
+DESCRIPTIONS = [
+    TailwindNumberEntityDescription(
+        key="brightness",
+        icon="mdi:led-on",
+        translation_key="brightness",
+        entity_category=EntityCategory.CONFIG,
+        native_step=1,
+        native_min_value=0,
+        native_max_value=100,
+        native_unit_of_measurement=PERCENTAGE,
+        value_fn=lambda data: data.led_brightness,
+        set_value_fn=lambda tailwind, brightness: tailwind.status_led(
+            brightness=int(brightness),
+        ),
+    ),
+]
+
+
+async def async_setup_entry(
+    hass: HomeAssistant,
+    entry: ConfigEntry,
+    async_add_entities: AddEntitiesCallback,
+) -> None:
+    """Set up Tailwind number based on a config entry."""
+    coordinator: TailwindDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
+    async_add_entities(
+        TailwindNumberEntity(
+            coordinator,
+            description,
+        )
+        for description in DESCRIPTIONS
+    )
+
+
+class TailwindNumberEntity(TailwindEntity, NumberEntity):
+    """Representation of a Tailwind number entity."""
+
+    entity_description: TailwindNumberEntityDescription
+
+    def __init__(
+        self,
+        coordinator: TailwindDataUpdateCoordinator,
+        description: TailwindNumberEntityDescription,
+    ) -> None:
+        """Initiate Tailwind number entity."""
+        super().__init__(coordinator)
+        self.entity_description = description
+        self._attr_unique_id = f"{coordinator.data.device_id}-{description.key}"
+
+    @property
+    def native_value(self) -> int | None:
+        """Return the number value."""
+        return self.entity_description.value_fn(self.coordinator.data)
+
+    async def async_set_native_value(self, value: float) -> None:
+        """Change to new number value."""
+        await self.entity_description.set_value_fn(self.coordinator.tailwind, value)
+        await self.coordinator.async_request_refresh()
diff --git a/homeassistant/components/tailwind/strings.json b/homeassistant/components/tailwind/strings.json
new file mode 100644
index 0000000000000000000000000000000000000000..9446b0c304c280fd60aa77a6b633e83d035eb155
--- /dev/null
+++ b/homeassistant/components/tailwind/strings.json
@@ -0,0 +1,34 @@
+{
+  "config": {
+    "step": {
+      "user": {
+        "description": "Set up your Tailwind garage door opener to integrate with Home Assistant.\n\nTo do so, you will need to get the local control key and IP address of your Tailwind device. For more details, see the description below the fields down below.",
+        "data": {
+          "host": "[%key:common::config_flow::data::host%]",
+          "token": "Local control key token"
+        },
+        "data_description": {
+          "host": "The hostname or IP address of your Tailwind device. You can find the IP address by going into the Tailwind app and selecting your Tailwind device's cog icon. The IP address is shown in the **Device Info** section.",
+          "token": "To find local control key token, browse to the [Tailwind web portal]({url}), log in with your Tailwind account, and select the [**Local Control Key**]({url}) tab. The 6-digit number shown is your local control key token."
+        }
+      }
+    },
+    "error": {
+      "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
+      "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
+      "unknown": "[%key:common::config_flow::error::unknown%]"
+    },
+    "abort": {
+      "already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
+      "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
+      "unsupported_firmware": "The firmware of your Tailwind device is not supported. Please update your Tailwind device to the latest firmware version using the Tailwind app."
+    }
+  },
+  "entity": {
+    "number": {
+      "brightness": {
+        "name": "Status LED brightness"
+      }
+    }
+  }
+}
diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py
index 5936ac01b68f14b837c6357dcca2774582c4ed3b..260efa41886b5fdf5ce064a402611a50fecae4a8 100644
--- a/homeassistant/generated/config_flows.py
+++ b/homeassistant/generated/config_flows.py
@@ -485,6 +485,7 @@ FLOWS = {
         "system_bridge",
         "tado",
         "tailscale",
+        "tailwind",
         "tami4",
         "tankerkoenig",
         "tasmota",
diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json
index af822143d50380753789d5f79b4edca149b5f353..1c7348f629c77896b9f8b67081f5261a86f29c6d 100644
--- a/homeassistant/generated/integrations.json
+++ b/homeassistant/generated/integrations.json
@@ -5685,6 +5685,12 @@
       "config_flow": true,
       "iot_class": "cloud_polling"
     },
+    "tailwind": {
+      "name": "Tailwind",
+      "integration_type": "device",
+      "config_flow": true,
+      "iot_class": "local_polling"
+    },
     "tami4": {
       "name": "Tami4 Edge / Edge+",
       "integration_type": "hub",
diff --git a/mypy.ini b/mypy.ini
index cf590b5391884feabdf0b14d54a9f1092eeffdbc..6dc2542347d7cecff0bd7e6d929382801753ce04 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -3102,6 +3102,16 @@ disallow_untyped_defs = true
 warn_return_any = true
 warn_unreachable = true
 
+[mypy-homeassistant.components.tailwind.*]
+check_untyped_defs = true
+disallow_incomplete_defs = true
+disallow_subclassing_any = true
+disallow_untyped_calls = true
+disallow_untyped_decorators = true
+disallow_untyped_defs = true
+warn_return_any = true
+warn_unreachable = true
+
 [mypy-homeassistant.components.tami4.*]
 check_untyped_defs = true
 disallow_incomplete_defs = true
diff --git a/requirements_all.txt b/requirements_all.txt
index 2b3b06db1f6a89224b8f133e665a82ddd6c2b493..ec23c9d1b80eeb4c392fefd36b19ea2efc08bca0 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -933,6 +933,9 @@ googlemaps==2.5.1
 # homeassistant.components.slide
 goslide-api==0.5.1
 
+# homeassistant.components.tailwind
+gotailwind==0.2.1
+
 # homeassistant.components.govee_ble
 govee-ble==0.24.0
 
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 607a9a37b06f7c9829e95108aaa6a9fed0f53922..bcffdfcc7acbd3d2679f247ecff28714a5809f0f 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -744,6 +744,9 @@ google-nest-sdm==3.0.3
 # homeassistant.components.google_travel_time
 googlemaps==2.5.1
 
+# homeassistant.components.tailwind
+gotailwind==0.2.1
+
 # homeassistant.components.govee_ble
 govee-ble==0.24.0
 
diff --git a/tests/components/tailwind/__init__.py b/tests/components/tailwind/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..48c1de3d421dd5d7e4d6f0c58e35e1886255baff
--- /dev/null
+++ b/tests/components/tailwind/__init__.py
@@ -0,0 +1 @@
+"""Integration tests for the Tailwind integration."""
diff --git a/tests/components/tailwind/conftest.py b/tests/components/tailwind/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..b39a3598a3ee90007d42e3b83dfc159d408e73f4
--- /dev/null
+++ b/tests/components/tailwind/conftest.py
@@ -0,0 +1,74 @@
+"""Fixtures for the Tailwind integration tests."""
+from __future__ import annotations
+
+from collections.abc import Generator
+from unittest.mock import AsyncMock, MagicMock, patch
+
+from gotailwind import TailwindDeviceStatus
+import pytest
+
+from homeassistant.components.tailwind.const import DOMAIN
+from homeassistant.const import CONF_HOST, CONF_TOKEN
+from homeassistant.core import HomeAssistant
+
+from tests.common import MockConfigEntry, load_fixture
+
+
+@pytest.fixture
+def device_fixture() -> str:
+    """Return the device fixtures for a specific device."""
+    return "iq3"
+
+
+@pytest.fixture
+def mock_config_entry() -> MockConfigEntry:
+    """Return the default mocked config entry."""
+    return MockConfigEntry(
+        title="Tailwind iQ3",
+        domain=DOMAIN,
+        data={
+            CONF_HOST: "127.0.0.127",
+            CONF_TOKEN: "123456",
+        },
+        unique_id="3c:e9:0e:6d:21:84",
+    )
+
+
+@pytest.fixture
+def mock_setup_entry() -> Generator[AsyncMock, None, None]:
+    """Mock setting up a config entry."""
+    with patch(
+        "homeassistant.components.tailwind.async_setup_entry", return_value=True
+    ):
+        yield
+
+
+@pytest.fixture
+def mock_tailwind(device_fixture: str) -> Generator[MagicMock, None, None]:
+    """Return a mocked Tailwind client."""
+    with patch(
+        "homeassistant.components.tailwind.coordinator.Tailwind", autospec=True
+    ) as tailwind_mock, patch(
+        "homeassistant.components.tailwind.config_flow.Tailwind",
+        new=tailwind_mock,
+    ):
+        tailwind = tailwind_mock.return_value
+        tailwind.status.return_value = TailwindDeviceStatus.from_json(
+            load_fixture(f"{device_fixture}.json", DOMAIN)
+        )
+        yield tailwind
+
+
+@pytest.fixture
+async def init_integration(
+    hass: HomeAssistant,
+    mock_config_entry: MockConfigEntry,
+    mock_tailwind: MagicMock,
+) -> MockConfigEntry:
+    """Set up the Tailwind integration for testing."""
+    mock_config_entry.add_to_hass(hass)
+
+    await hass.config_entries.async_setup(mock_config_entry.entry_id)
+    await hass.async_block_till_done()
+
+    return mock_config_entry
diff --git a/tests/components/tailwind/fixtures/iq3.json b/tests/components/tailwind/fixtures/iq3.json
new file mode 100644
index 0000000000000000000000000000000000000000..1c8b2d5e0d43af09b17c6958fe90efcb38cc6d9e
--- /dev/null
+++ b/tests/components/tailwind/fixtures/iq3.json
@@ -0,0 +1,24 @@
+{
+  "result": "OK",
+  "product": "iQ3",
+  "dev_id": "_3c_e9_e_6d_21_84_",
+  "proto_ver": "0.1",
+  "door_num": 2,
+  "night_mode_en": 0,
+  "fw_ver": "10.10",
+  "led_brightness": 100,
+  "data": {
+    "door1": {
+      "index": 0,
+      "status": "open",
+      "lockup": 0,
+      "disabled": 0
+    },
+    "door2": {
+      "index": 1,
+      "status": "open",
+      "lockup": 0,
+      "disabled": 0
+    }
+  }
+}
diff --git a/tests/components/tailwind/snapshots/test_config_flow.ambr b/tests/components/tailwind/snapshots/test_config_flow.ambr
new file mode 100644
index 0000000000000000000000000000000000000000..fc0572c2e42cc83c898a775ee8cd9308d63d7c26
--- /dev/null
+++ b/tests/components/tailwind/snapshots/test_config_flow.ambr
@@ -0,0 +1,40 @@
+# serializer version: 1
+# name: test_user_flow
+  FlowResultSnapshot({
+    'context': dict({
+      'source': 'user',
+    }),
+    'data': dict({
+      'host': '127.0.0.1',
+      'token': '987654',
+    }),
+    'description': None,
+    'description_placeholders': None,
+    'flow_id': <ANY>,
+    'handler': 'tailwind',
+    'minor_version': 1,
+    'options': dict({
+    }),
+    'result': ConfigEntrySnapshot({
+      'data': dict({
+        'host': '127.0.0.1',
+        'token': '987654',
+      }),
+      'disabled_by': None,
+      'domain': 'tailwind',
+      'entry_id': <ANY>,
+      'minor_version': 1,
+      'options': dict({
+      }),
+      'pref_disable_new_entities': False,
+      'pref_disable_polling': False,
+      'source': 'user',
+      'title': 'Tailwind iQ3',
+      'unique_id': None,
+      'version': 1,
+    }),
+    'title': 'Tailwind iQ3',
+    'type': <FlowResultType.CREATE_ENTRY: 'create_entry'>,
+    'version': 1,
+  })
+# ---
diff --git a/tests/components/tailwind/snapshots/test_number.ambr b/tests/components/tailwind/snapshots/test_number.ambr
new file mode 100644
index 0000000000000000000000000000000000000000..1d1444461ff2796d3484a61c5c664ecb08f414ec
--- /dev/null
+++ b/tests/components/tailwind/snapshots/test_number.ambr
@@ -0,0 +1,87 @@
+# serializer version: 1
+# name: test_number_entities
+  StateSnapshot({
+    'attributes': ReadOnlyDict({
+      'friendly_name': 'Tailwind iQ3 Status LED brightness',
+      'icon': 'mdi:led-on',
+      'max': 100,
+      'min': 0,
+      'mode': <NumberMode.AUTO: 'auto'>,
+      'step': 1,
+      'unit_of_measurement': '%',
+    }),
+    'context': <ANY>,
+    'entity_id': 'number.tailwind_iq3_status_led_brightness',
+    'last_changed': <ANY>,
+    'last_updated': <ANY>,
+    'state': '100',
+  })
+# ---
+# name: test_number_entities.1
+  EntityRegistryEntrySnapshot({
+    'aliases': set({
+    }),
+    'area_id': None,
+    'capabilities': dict({
+      'max': 100,
+      'min': 0,
+      'mode': <NumberMode.AUTO: 'auto'>,
+      'step': 1,
+    }),
+    'config_entry_id': <ANY>,
+    'device_class': None,
+    'device_id': <ANY>,
+    'disabled_by': None,
+    'domain': 'number',
+    'entity_category': <EntityCategory.CONFIG: 'config'>,
+    'entity_id': 'number.tailwind_iq3_status_led_brightness',
+    'has_entity_name': True,
+    'hidden_by': None,
+    'icon': None,
+    'id': <ANY>,
+    'name': None,
+    'options': dict({
+    }),
+    'original_device_class': None,
+    'original_icon': 'mdi:led-on',
+    'original_name': 'Status LED brightness',
+    'platform': 'tailwind',
+    'previous_unique_id': None,
+    'supported_features': 0,
+    'translation_key': 'brightness',
+    'unique_id': '_3c_e9_e_6d_21_84_-brightness',
+    'unit_of_measurement': '%',
+  })
+# ---
+# name: test_number_entities.2
+  DeviceRegistryEntrySnapshot({
+    'area_id': None,
+    'config_entries': <ANY>,
+    'configuration_url': None,
+    'connections': set({
+      tuple(
+        'mac',
+        '3c:e9:0e:6d:21:84',
+      ),
+    }),
+    'disabled_by': None,
+    'entry_type': None,
+    'hw_version': None,
+    'id': <ANY>,
+    'identifiers': set({
+      tuple(
+        'tailwind',
+        '_3c_e9_e_6d_21_84_',
+      ),
+    }),
+    'is_new': False,
+    'manufacturer': 'Tailwind',
+    'model': 'iQ3',
+    'name': 'Tailwind iQ3',
+    'name_by_user': None,
+    'serial_number': None,
+    'suggested_area': None,
+    'sw_version': '10.10',
+    'via_device_id': None,
+  })
+# ---
diff --git a/tests/components/tailwind/test_config_flow.py b/tests/components/tailwind/test_config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..186e1464e14dcef1b054c02b86af49bb58c0f00f
--- /dev/null
+++ b/tests/components/tailwind/test_config_flow.py
@@ -0,0 +1,103 @@
+"""Configuration flow tests for the Tailwind integration."""
+from unittest.mock import MagicMock
+
+from gotailwind import (
+    TailwindAuthenticationError,
+    TailwindConnectionError,
+    TailwindUnsupportedFirmwareVersionError,
+)
+import pytest
+from syrupy.assertion import SnapshotAssertion
+
+from homeassistant.components.tailwind.const import DOMAIN
+from homeassistant.config_entries import SOURCE_USER
+from homeassistant.const import CONF_HOST, CONF_TOKEN
+from homeassistant.core import HomeAssistant
+from homeassistant.data_entry_flow import FlowResultType
+
+pytestmark = pytest.mark.usefixtures("mock_setup_entry")
+
+
+@pytest.mark.usefixtures("mock_tailwind")
+async def test_user_flow(
+    hass: HomeAssistant,
+    snapshot: SnapshotAssertion,
+) -> None:
+    """Test the full happy path user flow from start to finish."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN,
+        context={"source": SOURCE_USER},
+    )
+
+    assert result.get("type") == FlowResultType.FORM
+    assert result.get("step_id") == "user"
+
+    result2 = await hass.config_entries.flow.async_configure(
+        result["flow_id"],
+        user_input={
+            CONF_HOST: "127.0.0.1",
+            CONF_TOKEN: "987654",
+        },
+    )
+
+    assert result2.get("type") == FlowResultType.CREATE_ENTRY
+    assert result2 == snapshot
+
+
+@pytest.mark.parametrize(
+    ("side_effect", "expected_error"),
+    [
+        (TailwindConnectionError, {CONF_HOST: "cannot_connect"}),
+        (TailwindAuthenticationError, {CONF_TOKEN: "invalid_auth"}),
+        (Exception, {"base": "unknown"}),
+    ],
+)
+async def test_user_flow_errors(
+    hass: HomeAssistant,
+    mock_tailwind: MagicMock,
+    side_effect: Exception,
+    expected_error: dict[str, str],
+) -> None:
+    """Test we show user form on a connection error."""
+    mock_tailwind.status.side_effect = side_effect
+
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN,
+        context={"source": SOURCE_USER},
+        data={
+            CONF_HOST: "127.0.0.1",
+            CONF_TOKEN: "987654",
+        },
+    )
+
+    assert result.get("type") == FlowResultType.FORM
+    assert result.get("step_id") == "user"
+    assert result.get("errors") == expected_error
+
+    mock_tailwind.status.side_effect = None
+    result2 = await hass.config_entries.flow.async_configure(
+        result["flow_id"],
+        user_input={
+            CONF_HOST: "127.0.0.2",
+            CONF_TOKEN: "123456",
+        },
+    )
+    assert result2.get("type") == FlowResultType.CREATE_ENTRY
+
+
+async def test_unsupported_firmware_version(
+    hass: HomeAssistant, mock_tailwind: MagicMock
+) -> None:
+    """Test configuration flow aborts when the firmware version is not supported."""
+    mock_tailwind.status.side_effect = TailwindUnsupportedFirmwareVersionError
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN,
+        context={"source": SOURCE_USER},
+        data={
+            CONF_HOST: "127.0.0.1",
+            CONF_TOKEN: "987654",
+        },
+    )
+
+    assert result.get("type") == FlowResultType.ABORT
+    assert result.get("reason") == "unsupported_firmware"
diff --git a/tests/components/tailwind/test_init.py b/tests/components/tailwind/test_init.py
new file mode 100644
index 0000000000000000000000000000000000000000..c15646f44596f5be6b8946fac79db6ebae0f7856
--- /dev/null
+++ b/tests/components/tailwind/test_init.py
@@ -0,0 +1,46 @@
+"""Integration tests for the Tailwind integration."""
+from unittest.mock import MagicMock
+
+from gotailwind import TailwindConnectionError
+
+from homeassistant.components.tailwind.const import DOMAIN
+from homeassistant.config_entries import ConfigEntryState
+from homeassistant.core import HomeAssistant
+
+from tests.common import MockConfigEntry
+
+
+async def test_load_unload_config_entry(
+    hass: HomeAssistant,
+    mock_config_entry: MockConfigEntry,
+    mock_tailwind: MagicMock,
+) -> None:
+    """Test the Tailwind configuration entry loading/unloading."""
+    mock_config_entry.add_to_hass(hass)
+    await hass.config_entries.async_setup(mock_config_entry.entry_id)
+    await hass.async_block_till_done()
+
+    assert mock_config_entry.state is ConfigEntryState.LOADED
+    assert len(mock_tailwind.status.mock_calls) == 1
+
+    await hass.config_entries.async_unload(mock_config_entry.entry_id)
+    await hass.async_block_till_done()
+
+    assert not hass.data.get(DOMAIN)
+    assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
+
+
+async def test_config_entry_not_ready(
+    hass: HomeAssistant,
+    mock_config_entry: MockConfigEntry,
+    mock_tailwind: MagicMock,
+) -> None:
+    """Test the Tailwind configuration entry not ready."""
+    mock_tailwind.status.side_effect = TailwindConnectionError
+
+    mock_config_entry.add_to_hass(hass)
+    await hass.config_entries.async_setup(mock_config_entry.entry_id)
+    await hass.async_block_till_done()
+
+    assert len(mock_tailwind.status.mock_calls) == 1
+    assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
diff --git a/tests/components/tailwind/test_number.py b/tests/components/tailwind/test_number.py
new file mode 100644
index 0000000000000000000000000000000000000000..b67af3d0e6230740336739d489b57325ea404a15
--- /dev/null
+++ b/tests/components/tailwind/test_number.py
@@ -0,0 +1,46 @@
+"""Tests for number entities provided by the Tailwind integration."""
+from unittest.mock import MagicMock
+
+import pytest
+from syrupy.assertion import SnapshotAssertion
+
+from homeassistant.components import number
+from homeassistant.components.number import ATTR_VALUE, SERVICE_SET_VALUE
+from homeassistant.const import ATTR_ENTITY_ID
+from homeassistant.core import HomeAssistant
+from homeassistant.helpers import device_registry as dr, entity_registry as er
+
+pytestmark = pytest.mark.usefixtures("init_integration")
+
+
+async def test_number_entities(
+    hass: HomeAssistant,
+    device_registry: dr.DeviceRegistry,
+    entity_registry: er.EntityRegistry,
+    mock_tailwind: MagicMock,
+    snapshot: SnapshotAssertion,
+) -> None:
+    """Test number entities provided by the Tailwind integration."""
+    assert (state := hass.states.get("number.tailwind_iq3_status_led_brightness"))
+    assert snapshot == state
+
+    assert (entity_entry := entity_registry.async_get(state.entity_id))
+    assert snapshot == entity_entry
+
+    assert entity_entry.device_id
+    assert (device_entry := device_registry.async_get(entity_entry.device_id))
+    assert snapshot == device_entry
+
+    assert len(mock_tailwind.status_led.mock_calls) == 0
+    await hass.services.async_call(
+        number.DOMAIN,
+        SERVICE_SET_VALUE,
+        {
+            ATTR_ENTITY_ID: state.entity_id,
+            ATTR_VALUE: 42,
+        },
+        blocking=True,
+    )
+
+    assert len(mock_tailwind.status_led.mock_calls) == 1
+    mock_tailwind.status_led.assert_called_with(brightness=42)