diff --git a/CODEOWNERS b/CODEOWNERS
index f43cdf457c87bea1485358974d532a1ef165c5dd..4ef40a79bd1840e4620ce7e14f1764aedd925df0 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -637,6 +637,8 @@ build.json @home-assistant/supervisor
 /tests/components/homeassistant_sky_connect/ @home-assistant/core
 /homeassistant/components/homeassistant_yellow/ @home-assistant/core
 /tests/components/homeassistant_yellow/ @home-assistant/core
+/homeassistant/components/homee/ @Taraman17
+/tests/components/homee/ @Taraman17
 /homeassistant/components/homekit/ @bdraco
 /tests/components/homekit/ @bdraco
 /homeassistant/components/homekit_controller/ @Jc2k @bdraco
diff --git a/homeassistant/components/homee/__init__.py b/homeassistant/components/homee/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed5dd69767f8f8c533b21382cb21da15e89ea058
--- /dev/null
+++ b/homeassistant/components/homee/__init__.py
@@ -0,0 +1,85 @@
+"""The Homee integration."""
+
+import logging
+
+from pyHomee import Homee, HomeeAuthFailedException, HomeeConnectionFailedException
+
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform
+from homeassistant.core import HomeAssistant
+from homeassistant.exceptions import ConfigEntryNotReady
+from homeassistant.helpers import device_registry as dr
+
+from .const import DOMAIN
+
+_LOGGER = logging.getLogger(__name__)
+
+PLATFORMS = [Platform.COVER]
+
+type HomeeConfigEntry = ConfigEntry[Homee]
+
+
+async def async_setup_entry(hass: HomeAssistant, entry: HomeeConfigEntry) -> bool:
+    """Set up homee from a config entry."""
+    # Create the Homee api object using host, user,
+    # password & pyHomee instance from the config
+    homee = Homee(
+        host=entry.data[CONF_HOST],
+        user=entry.data[CONF_USERNAME],
+        password=entry.data[CONF_PASSWORD],
+        device="HA_" + hass.config.location_name,
+        reconnect_interval=10,
+        max_retries=100,
+    )
+
+    # Start the homee websocket connection as a new task
+    # and wait until we are connected
+    try:
+        await homee.get_access_token()
+    except HomeeConnectionFailedException as exc:
+        raise ConfigEntryNotReady(
+            f"Connection to Homee failed: {exc.__cause__}"
+        ) from exc
+    except HomeeAuthFailedException as exc:
+        raise ConfigEntryNotReady(
+            f"Authentication to Homee failed: {exc.__cause__}"
+        ) from exc
+
+    hass.loop.create_task(homee.run())
+    await homee.wait_until_connected()
+
+    entry.runtime_data = homee
+    entry.async_on_unload(homee.disconnect)
+
+    async def _connection_update_callback(connected: bool) -> None:
+        """Call when the device is notified of changes."""
+        if connected:
+            _LOGGER.warning("Reconnected to Homee at %s", entry.data[CONF_HOST])
+        else:
+            _LOGGER.warning("Disconnected from Homee at %s", entry.data[CONF_HOST])
+
+    await homee.add_connection_listener(_connection_update_callback)
+
+    # create device register entry
+    device_registry = dr.async_get(hass)
+    device_registry.async_get_or_create(
+        config_entry_id=entry.entry_id,
+        connections={
+            (dr.CONNECTION_NETWORK_MAC, dr.format_mac(homee.settings.mac_address))
+        },
+        identifiers={(DOMAIN, homee.settings.uid)},
+        manufacturer="homee",
+        name=homee.settings.homee_name,
+        model="homee",
+        sw_version=homee.settings.version,
+    )
+
+    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
+
+    return True
+
+
+async def async_unload_entry(hass: HomeAssistant, entry: HomeeConfigEntry) -> bool:
+    """Unload a homee config entry."""
+    # Unload platforms
+    return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
diff --git a/homeassistant/components/homee/config_flow.py b/homeassistant/components/homee/config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..61d2a3f25a50d8c8d79346b2d5c7cfaa5f821dc3
--- /dev/null
+++ b/homeassistant/components/homee/config_flow.py
@@ -0,0 +1,85 @@
+"""Config flow for homee integration."""
+
+import logging
+from typing import Any
+
+from pyHomee import (
+    Homee,
+    HomeeAuthFailedException as HomeeAuthenticationFailedException,
+    HomeeConnectionFailedException,
+)
+import voluptuous as vol
+
+from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
+from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
+
+from .const import DOMAIN
+
+_LOGGER = logging.getLogger(__name__)
+
+AUTH_SCHEMA = vol.Schema(
+    {
+        vol.Required(CONF_HOST): str,
+        vol.Required(CONF_USERNAME): str,
+        vol.Required(CONF_PASSWORD): str,
+    }
+)
+
+
+class HomeeConfigFlow(ConfigFlow, domain=DOMAIN):
+    """Handle a config flow for homee."""
+
+    VERSION = 1
+
+    homee: Homee
+
+    async def async_step_user(
+        self, user_input: dict[str, Any] | None = None
+    ) -> ConfigFlowResult:
+        """Handle the initial user step."""
+
+        errors = {}
+        if user_input is not None:
+            self.homee = Homee(
+                user_input[CONF_HOST],
+                user_input[CONF_USERNAME],
+                user_input[CONF_PASSWORD],
+            )
+
+            try:
+                await self.homee.get_access_token()
+            except HomeeConnectionFailedException:
+                errors["base"] = "cannot_connect"
+            except HomeeAuthenticationFailedException:
+                errors["base"] = "invalid_auth"
+            except Exception:  # pylint: disable=broad-except
+                _LOGGER.exception("Unexpected exception")
+                errors["base"] = "unknown"
+            else:
+                _LOGGER.info("Got access token for homee")
+                self.hass.loop.create_task(self.homee.run())
+                _LOGGER.debug("Homee task created")
+                await self.homee.wait_until_connected()
+                _LOGGER.info("Homee connected")
+                self.homee.disconnect()
+                _LOGGER.debug("Homee disconnecting")
+                await self.homee.wait_until_disconnected()
+                _LOGGER.info("Homee config successfully tested")
+
+                await self.async_set_unique_id(self.homee.settings.uid)
+
+                self._abort_if_unique_id_configured()
+
+                _LOGGER.info(
+                    "Created new homee entry with ID %s", self.homee.settings.uid
+                )
+
+                return self.async_create_entry(
+                    title=f"{self.homee.settings.homee_name} ({self.homee.host})",
+                    data=user_input,
+                )
+        return self.async_show_form(
+            step_id="user",
+            data_schema=AUTH_SCHEMA,
+            errors=errors,
+        )
diff --git a/homeassistant/components/homee/const.py b/homeassistant/components/homee/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..c96165ead81d8e3b7efb0071b66b9450583210e4
--- /dev/null
+++ b/homeassistant/components/homee/const.py
@@ -0,0 +1,4 @@
+"""Constants for the homee integration."""
+
+# General
+DOMAIN = "homee"
diff --git a/homeassistant/components/homee/cover.py b/homeassistant/components/homee/cover.py
new file mode 100644
index 0000000000000000000000000000000000000000..c6546596fa7476e4516cac47e3fee6dc9813ba3b
--- /dev/null
+++ b/homeassistant/components/homee/cover.py
@@ -0,0 +1,261 @@
+"""The homee cover platform."""
+
+import logging
+from typing import Any, cast
+
+from pyHomee.const import AttributeType, NodeProfile
+from pyHomee.model import HomeeAttribute, HomeeNode
+
+from homeassistant.components.cover import (
+    ATTR_POSITION,
+    ATTR_TILT_POSITION,
+    CoverDeviceClass,
+    CoverEntity,
+    CoverEntityFeature,
+)
+from homeassistant.core import HomeAssistant
+from homeassistant.helpers.entity_platform import AddEntitiesCallback
+
+from . import HomeeConfigEntry
+from .entity import HomeeNodeEntity
+
+_LOGGER = logging.getLogger(__name__)
+
+OPEN_CLOSE_ATTRIBUTES = [
+    AttributeType.OPEN_CLOSE,
+    AttributeType.SLAT_ROTATION_IMPULSE,
+    AttributeType.UP_DOWN,
+]
+POSITION_ATTRIBUTES = [AttributeType.POSITION, AttributeType.SHUTTER_SLAT_POSITION]
+
+
+def get_open_close_attribute(node: HomeeNode) -> HomeeAttribute:
+    """Return the attribute used for opening/closing the cover."""
+    # We assume, that no device has UP_DOWN and OPEN_CLOSE, but only one of them.
+    if (open_close := node.get_attribute_by_type(AttributeType.UP_DOWN)) is None:
+        open_close = node.get_attribute_by_type(AttributeType.OPEN_CLOSE)
+
+    return open_close
+
+
+def get_cover_features(
+    node: HomeeNode, open_close_attribute: HomeeAttribute
+) -> CoverEntityFeature:
+    """Determine the supported cover features of a homee node based on the available attributes."""
+    features = CoverEntityFeature(0)
+
+    if open_close_attribute.editable:
+        features |= (
+            CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP
+        )
+
+    # Check for up/down position settable.
+    attribute = node.get_attribute_by_type(AttributeType.POSITION)
+    if attribute is not None:
+        if attribute.editable:
+            features |= CoverEntityFeature.SET_POSITION
+
+    if node.get_attribute_by_type(AttributeType.SLAT_ROTATION_IMPULSE) is not None:
+        features |= CoverEntityFeature.OPEN_TILT | CoverEntityFeature.CLOSE_TILT
+
+    if node.get_attribute_by_type(AttributeType.SHUTTER_SLAT_POSITION) is not None:
+        features |= CoverEntityFeature.SET_TILT_POSITION
+
+    return features
+
+
+def get_device_class(node: HomeeNode) -> CoverDeviceClass | None:
+    """Determine the device class a homee node based on the node profile."""
+    COVER_DEVICE_PROFILES = {
+        NodeProfile.GARAGE_DOOR_OPERATOR: CoverDeviceClass.GARAGE,
+        NodeProfile.SHUTTER_POSITION_SWITCH: CoverDeviceClass.SHUTTER,
+    }
+
+    return COVER_DEVICE_PROFILES.get(node.profile)
+
+
+async def async_setup_entry(
+    hass: HomeAssistant,
+    config_entry: HomeeConfigEntry,
+    async_add_devices: AddEntitiesCallback,
+) -> None:
+    """Add the homee platform for the cover integration."""
+
+    async_add_devices(
+        HomeeCover(node, config_entry)
+        for node in config_entry.runtime_data.nodes
+        if is_cover_node(node)
+    )
+
+
+def is_cover_node(node: HomeeNode) -> bool:
+    """Determine if a node is controllable as a homee cover based on its profile and attributes."""
+    return node.profile in [
+        NodeProfile.ELECTRIC_MOTOR_METERING_SWITCH,
+        NodeProfile.ELECTRIC_MOTOR_METERING_SWITCH_WITHOUT_SLAT_POSITION,
+        NodeProfile.GARAGE_DOOR_OPERATOR,
+        NodeProfile.SHUTTER_POSITION_SWITCH,
+    ]
+
+
+class HomeeCover(HomeeNodeEntity, CoverEntity):
+    """Representation of a homee cover device."""
+
+    _attr_name = None
+
+    def __init__(self, node: HomeeNode, entry: HomeeConfigEntry) -> None:
+        """Initialize a homee cover entity."""
+        super().__init__(node, entry)
+        self._open_close_attribute = get_open_close_attribute(node)
+        self._attr_supported_features = get_cover_features(
+            node, self._open_close_attribute
+        )
+        self._attr_device_class = get_device_class(node)
+
+        self._attr_unique_id = f"{self._attr_unique_id}-{self._open_close_attribute.id}"
+
+    @property
+    def current_cover_position(self) -> int | None:
+        """Return the cover's position."""
+        # Translate the homee position values to HA's 0-100 scale
+        if self.has_attribute(AttributeType.POSITION):
+            attribute = self._node.get_attribute_by_type(AttributeType.POSITION)
+            homee_min = attribute.minimum
+            homee_max = attribute.maximum
+            homee_position = attribute.current_value
+            position = ((homee_position - homee_min) / (homee_max - homee_min)) * 100
+
+            return 100 - position
+
+        return None
+
+    @property
+    def current_cover_tilt_position(self) -> int | None:
+        """Return the cover's tilt position."""
+        # Translate the homee position values to HA's 0-100 scale
+        if self.has_attribute(AttributeType.SHUTTER_SLAT_POSITION):
+            attribute = self._node.get_attribute_by_type(
+                AttributeType.SHUTTER_SLAT_POSITION
+            )
+            homee_min = attribute.minimum
+            homee_max = attribute.maximum
+            homee_position = attribute.current_value
+            position = ((homee_position - homee_min) / (homee_max - homee_min)) * 100
+
+            return 100 - position
+
+        return None
+
+    @property
+    def is_opening(self) -> bool | None:
+        """Return the opening status of the cover."""
+        if self._open_close_attribute is not None:
+            return (
+                self._open_close_attribute.get_value() == 3
+                if not self._open_close_attribute.is_reversed
+                else self._open_close_attribute.get_value() == 4
+            )
+
+        return None
+
+    @property
+    def is_closing(self) -> bool | None:
+        """Return the closing status of the cover."""
+        if self._open_close_attribute is not None:
+            return (
+                self._open_close_attribute.get_value() == 4
+                if not self._open_close_attribute.is_reversed
+                else self._open_close_attribute.get_value() == 3
+            )
+
+        return None
+
+    @property
+    def is_closed(self) -> bool | None:
+        """Return if the cover is closed."""
+        if self.has_attribute(AttributeType.POSITION):
+            attribute = self._node.get_attribute_by_type(AttributeType.POSITION)
+            return attribute.get_value() == attribute.maximum
+
+        if self._open_close_attribute is not None:
+            if not self._open_close_attribute.is_reversed:
+                return self._open_close_attribute.get_value() == 1
+
+            return self._open_close_attribute.get_value() == 0
+
+        # If none of the above is present, it might be a slat only cover.
+        if self.has_attribute(AttributeType.SHUTTER_SLAT_POSITION):
+            attribute = self._node.get_attribute_by_type(
+                AttributeType.SHUTTER_SLAT_POSITION
+            )
+            return attribute.get_value() == attribute.minimum
+
+        return None
+
+    async def async_open_cover(self, **kwargs: Any) -> None:
+        """Open the cover."""
+        if not self._open_close_attribute.is_reversed:
+            await self.async_set_value(self._open_close_attribute, 0)
+        else:
+            await self.async_set_value(self._open_close_attribute, 1)
+
+    async def async_close_cover(self, **kwargs: Any) -> None:
+        """Close cover."""
+        if not self._open_close_attribute.is_reversed:
+            await self.async_set_value(self._open_close_attribute, 1)
+        else:
+            await self.async_set_value(self._open_close_attribute, 0)
+
+    async def async_set_cover_position(self, **kwargs: Any) -> None:
+        """Move the cover to a specific position."""
+        if CoverEntityFeature.SET_POSITION in self.supported_features:
+            position = 100 - cast(int, kwargs[ATTR_POSITION])
+
+            # Convert position to range of our entity.
+            attribute = self._node.get_attribute_by_type(AttributeType.POSITION)
+            homee_min = attribute.minimum
+            homee_max = attribute.maximum
+            homee_position = (position / 100) * (homee_max - homee_min) + homee_min
+
+            await self.async_set_value(AttributeType.POSITION, homee_position)
+
+    async def async_stop_cover(self, **kwargs: Any) -> None:
+        """Stop the cover."""
+        await self.async_set_value(self._open_close_attribute, 2)
+
+    async def async_open_cover_tilt(self, **kwargs: Any) -> None:
+        """Open the cover tilt."""
+        slat_attribute = self._node.get_attribute_by_type(
+            AttributeType.SLAT_ROTATION_IMPULSE
+        )
+        if not slat_attribute.is_reversed:
+            await self.async_set_value(AttributeType.SLAT_ROTATION_IMPULSE, 2)
+        else:
+            await self.async_set_value(AttributeType.SLAT_ROTATION_IMPULSE, 1)
+
+    async def async_close_cover_tilt(self, **kwargs: Any) -> None:
+        """Close the cover tilt."""
+        slat_attribute = self._node.get_attribute_by_type(
+            AttributeType.SLAT_ROTATION_IMPULSE
+        )
+        if not slat_attribute.is_reversed:
+            await self.async_set_value(AttributeType.SLAT_ROTATION_IMPULSE, 1)
+        else:
+            await self.async_set_value(AttributeType.SLAT_ROTATION_IMPULSE, 2)
+
+    async def async_set_cover_tilt_position(self, **kwargs: Any) -> None:
+        """Move the cover tilt to a specific position."""
+        if CoverEntityFeature.SET_TILT_POSITION in self.supported_features:
+            position = 100 - cast(int, kwargs[ATTR_TILT_POSITION])
+
+            # Convert position to range of our entity.
+            attribute = self._node.get_attribute_by_type(
+                AttributeType.SHUTTER_SLAT_POSITION
+            )
+            homee_min = attribute.minimum
+            homee_max = attribute.maximum
+            homee_position = (position / 100) * (homee_max - homee_min) + homee_min
+
+            await self.async_set_value(
+                AttributeType.SHUTTER_SLAT_POSITION, homee_position
+            )
diff --git a/homeassistant/components/homee/entity.py b/homeassistant/components/homee/entity.py
new file mode 100644
index 0000000000000000000000000000000000000000..c3c2d860cc0f2d7d395bebe39220b9bd33087cd3
--- /dev/null
+++ b/homeassistant/components/homee/entity.py
@@ -0,0 +1,88 @@
+"""Base Entities for Homee integration."""
+
+from pyHomee.const import AttributeType, NodeProfile, NodeState
+from pyHomee.model import HomeeNode
+
+from homeassistant.helpers.device_registry import DeviceInfo
+from homeassistant.helpers.entity import Entity
+
+from . import HomeeConfigEntry
+from .const import DOMAIN
+from .helpers import get_name_for_enum
+
+
+class HomeeNodeEntity(Entity):
+    """Representation of an Entity that uses more than one HomeeAttribute."""
+
+    _attr_has_entity_name = True
+    _attr_should_poll = False
+
+    def __init__(self, node: HomeeNode, entry: HomeeConfigEntry) -> None:
+        """Initialize the wrapper using a HomeeNode and target entity."""
+        self._node = node
+        self._attr_unique_id = f"{entry.runtime_data.settings.uid}-{node.id}"
+        self._entry = entry
+
+        self._attr_device_info = DeviceInfo(
+            identifiers={(DOMAIN, str(node.id))},
+            name=node.name,
+            model=get_name_for_enum(NodeProfile, node.profile),
+            sw_version=self._get_software_version(),
+            via_device=(DOMAIN, entry.runtime_data.settings.uid),
+        )
+        self._host_connected = entry.runtime_data.connected
+
+    async def async_added_to_hass(self) -> None:
+        """Add the homee binary sensor device to home assistant."""
+        self.async_on_remove(self._node.add_on_changed_listener(self._on_node_updated))
+        self.async_on_remove(
+            await self._entry.runtime_data.add_connection_listener(
+                self._on_connection_changed
+            )
+        )
+
+    @property
+    def available(self) -> bool:
+        """Return the availability of the underlying node."""
+        return self._node.state == NodeState.AVAILABLE and self._host_connected
+
+    async def async_update(self) -> None:
+        """Fetch new state data for this node."""
+        # Base class requests the whole node, if only a single attribute is needed
+        # the platform will overwrite this method.
+        homee = self._entry.runtime_data
+        await homee.update_node(self._node.id)
+
+    def _get_software_version(self) -> str | None:
+        """Return the software version of the node."""
+        if self.has_attribute(AttributeType.FIRMWARE_REVISION):
+            return self._node.get_attribute_by_type(
+                AttributeType.FIRMWARE_REVISION
+            ).get_value()
+        if self.has_attribute(AttributeType.SOFTWARE_REVISION):
+            return self._node.get_attribute_by_type(
+                AttributeType.SOFTWARE_REVISION
+            ).get_value()
+        return None
+
+    def has_attribute(self, attribute_type: AttributeType) -> bool:
+        """Check if an attribute of the given type exists."""
+        return attribute_type in self._node.attribute_map
+
+    async def async_set_value(self, attribute_type: int, value: float) -> None:
+        """Set an attribute value on the homee node."""
+        await self.async_set_value_by_id(
+            self._node.get_attribute_by_type(attribute_type).id, value
+        )
+
+    async def async_set_value_by_id(self, attribute_id: int, value: float) -> None:
+        """Set an attribute value on the homee node."""
+        homee = self._entry.runtime_data
+        await homee.set_value(self._node.id, attribute_id, value)
+
+    def _on_node_updated(self, node: HomeeNode) -> None:
+        self.schedule_update_ha_state()
+
+    async def _on_connection_changed(self, connected: bool) -> None:
+        self._host_connected = connected
+        self.schedule_update_ha_state()
diff --git a/homeassistant/components/homee/helpers.py b/homeassistant/components/homee/helpers.py
new file mode 100644
index 0000000000000000000000000000000000000000..30826d7f47cd8ce168cece98fe8905918679eb8e
--- /dev/null
+++ b/homeassistant/components/homee/helpers.py
@@ -0,0 +1,16 @@
+"""Helper functions for the homee custom component."""
+
+import logging
+
+_LOGGER = logging.getLogger(__name__)
+
+
+def get_name_for_enum(att_class, att_id) -> str:
+    """Return the enum item name for a given integer."""
+    try:
+        attribute_name = att_class(att_id).name
+    except ValueError:
+        _LOGGER.warning("Value %s does not exist in %s", att_id, att_class.__name__)
+        return "Unknown"
+
+    return attribute_name
diff --git a/homeassistant/components/homee/manifest.json b/homeassistant/components/homee/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..5869a9760eacfc4c4f33cee70c95dbcc633440ba
--- /dev/null
+++ b/homeassistant/components/homee/manifest.json
@@ -0,0 +1,12 @@
+{
+  "domain": "homee",
+  "name": "Homee",
+  "codeowners": ["@Taraman17"],
+  "config_flow": true,
+  "documentation": "https://www.home-assistant.io/integrations/homee",
+  "integration_type": "hub",
+  "iot_class": "local_push",
+  "loggers": ["homee"],
+  "quality_scale": "bronze",
+  "requirements": ["pyHomee==1.2.0"]
+}
diff --git a/homeassistant/components/homee/quality_scale.yaml b/homeassistant/components/homee/quality_scale.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..96d4678b4201bdfe5a84176c84202dea413e429b
--- /dev/null
+++ b/homeassistant/components/homee/quality_scale.yaml
@@ -0,0 +1,68 @@
+rules:
+  # Bronze
+  action-setup:
+    status: exempt
+    comment: |
+      The integration does not provide any additional actions.
+  appropriate-polling:
+    status: exempt
+    comment: Integration is push based.
+  brands: done
+  common-modules: done
+  config-flow-test-coverage: done
+  config-flow: done
+  dependency-transparency: done
+  docs-actions:
+    status: exempt
+    comment: |
+      The integration does not provide any additional actions.
+  docs-high-level-description: done
+  docs-installation-instructions: done
+  docs-removal-instructions: done
+  entity-event-setup: done
+  entity-unique-id: done
+  has-entity-name: done
+  runtime-data: done
+  test-before-configure: done
+  test-before-setup: done
+  unique-config-entry: done
+
+  # Silver
+  action-exceptions: todo
+  config-entry-unloading: done
+  docs-configuration-parameters: todo
+  docs-installation-parameters: todo
+  entity-unavailable: done
+  integration-owner: done
+  log-when-unavailable: done
+  parallel-updates: todo
+  reauthentication-flow: todo
+  test-coverage: todo
+
+  # Gold
+  devices: done
+  diagnostics: todo
+  discovery-update-info: todo
+  discovery: todo
+  docs-data-update: todo
+  docs-examples: todo
+  docs-known-limitations: todo
+  docs-supported-devices: todo
+  docs-supported-functions: todo
+  docs-troubleshooting: todo
+  docs-use-cases: todo
+  dynamic-devices: todo
+  entity-category: todo
+  entity-device-class: todo
+  entity-disabled-by-default: todo
+  entity-translations: todo
+  exception-translations: todo
+  icon-translations: todo
+  reconfiguration-flow: todo
+  repair-issues: todo
+  stale-devices: todo
+
+  # Platinum
+  async-dependency: todo
+  inject-websession: todo
+  strict-typing: todo
diff --git a/homeassistant/components/homee/strings.json b/homeassistant/components/homee/strings.json
new file mode 100644
index 0000000000000000000000000000000000000000..54f80ba297744efa388ab9e821cbccb80a84f295
--- /dev/null
+++ b/homeassistant/components/homee/strings.json
@@ -0,0 +1,28 @@
+{
+  "config": {
+    "flow_title": "Homee {name} ({host})",
+    "abort": {
+      "already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
+    },
+    "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%]"
+    },
+    "step": {
+      "user": {
+        "title": "Configure homee",
+        "data": {
+          "host": "[%key:common::config_flow::data::host%]",
+          "password": "[%key:common::config_flow::data::password%]",
+          "username": "[%key:common::config_flow::data::username%]"
+        },
+        "data_description": {
+          "host": "The IP address of your Homee.",
+          "username": "The username for your Homee.",
+          "password": "The password for your Homee."
+        }
+      }
+    }
+  }
+}
diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py
index f3e82d4d085adce3457f543edc6f5c58c6f7d274..14061d2e9602403ae3dedd5b0e1774436693ae48 100644
--- a/homeassistant/generated/config_flows.py
+++ b/homeassistant/generated/config_flows.py
@@ -255,6 +255,7 @@ FLOWS = {
         "holiday",
         "home_connect",
         "homeassistant_sky_connect",
+        "homee",
         "homekit",
         "homekit_controller",
         "homematicip_cloud",
diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json
index af5b510b222c61dc25d1bb4dcdf81e3f961031b4..96ca8a9f7669c395fa4108a6ffdbfa7e9848965e 100644
--- a/homeassistant/generated/integrations.json
+++ b/homeassistant/generated/integrations.json
@@ -2600,6 +2600,12 @@
       "integration_type": "virtual",
       "supported_by": "netatmo"
     },
+    "homee": {
+      "name": "Homee",
+      "integration_type": "hub",
+      "config_flow": true,
+      "iot_class": "local_push"
+    },
     "homematic": {
       "name": "Homematic",
       "integrations": {
diff --git a/requirements_all.txt b/requirements_all.txt
index 49e407b212e87e5e4c2340b39e4627871fd00607..42dd293043b6aa0f3407fb5597234411c15e85a0 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1759,6 +1759,9 @@ pyEmby==1.10
 # homeassistant.components.hikvision
 pyHik==0.3.2
 
+# homeassistant.components.homee
+pyHomee==1.2.0
+
 # homeassistant.components.rfxtrx
 pyRFXtrx==0.31.1
 
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 77530c77f051e97e88d0f8d476c5c0a1fb9da4c7..2f129b8c4e73b6b47a6930737e3e191270ef4b65 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -1448,6 +1448,9 @@ pyDuotecno==2024.10.1
 # homeassistant.components.electrasmart
 pyElectra==1.2.4
 
+# homeassistant.components.homee
+pyHomee==1.2.0
+
 # homeassistant.components.rfxtrx
 pyRFXtrx==0.31.1
 
diff --git a/tests/components/homee/__init__.py b/tests/components/homee/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..03095aca7df9001801d16df11e5a90a2fee57715
--- /dev/null
+++ b/tests/components/homee/__init__.py
@@ -0,0 +1 @@
+"""Tests for the homee component."""
diff --git a/tests/components/homee/conftest.py b/tests/components/homee/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..881a24656f3ddcf54ca7daeba97a6248a0b45cc8
--- /dev/null
+++ b/tests/components/homee/conftest.py
@@ -0,0 +1,68 @@
+"""Fixtures for Homee integration tests."""
+
+from unittest.mock import AsyncMock, MagicMock, patch
+
+import pytest
+from typing_extensions import Generator
+
+from homeassistant.components.homee.const import DOMAIN
+from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
+
+from tests.common import MockConfigEntry
+
+HOMEE_ID = "00055511EECC"
+HOMEE_IP = "192.168.1.11"
+HOMEE_NAME = "TestHomee"
+TESTUSER = "testuser"
+TESTPASS = "testpass"
+
+
+@pytest.fixture
+def mock_config_entry() -> MockConfigEntry:
+    """Return the default mocked config entry."""
+    return MockConfigEntry(
+        title=f"{HOMEE_NAME} ({HOMEE_IP})",
+        domain=DOMAIN,
+        data={
+            CONF_HOST: HOMEE_IP,
+            CONF_USERNAME: TESTUSER,
+            CONF_PASSWORD: TESTPASS,
+        },
+        unique_id=HOMEE_ID,
+    )
+
+
+@pytest.fixture
+def mock_setup_entry() -> Generator[AsyncMock]:
+    """Mock setting up a config entry."""
+    with patch(
+        "homeassistant.components.homee.async_setup_entry", return_value=True
+    ) as mock_setup:
+        yield mock_setup
+
+
+@pytest.fixture
+def mock_homee() -> Generator[AsyncMock]:
+    """Return a mock Homee instance."""
+    with (
+        patch(
+            "homeassistant.components.homee.config_flow.Homee", autospec=True
+        ) as mocked_homee,
+        patch(
+            "homeassistant.components.homee.Homee",
+            autospec=True,
+        ),
+    ):
+        homee = mocked_homee.return_value
+
+        homee.host = HOMEE_IP
+        homee.user = TESTUSER
+        homee.password = TESTPASS
+        homee.settings = MagicMock()
+        homee.settings.uid = HOMEE_ID
+        homee.settings.homee_name = HOMEE_NAME
+        homee.reconnect_interval = 10
+
+        homee.get_access_token.return_value = "test_token"
+
+        yield homee
diff --git a/tests/components/homee/test_config_flow.py b/tests/components/homee/test_config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..4dfe8226d16132e3dc47e2ef39254e5eb58f7b70
--- /dev/null
+++ b/tests/components/homee/test_config_flow.py
@@ -0,0 +1,132 @@
+"""Test the Homee config flow."""
+
+from unittest.mock import AsyncMock
+
+from pyHomee import HomeeAuthFailedException, HomeeConnectionFailedException
+import pytest
+
+from homeassistant.components.homee.const import DOMAIN
+from homeassistant.config_entries import SOURCE_USER
+from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
+from homeassistant.core import HomeAssistant
+from homeassistant.data_entry_flow import FlowResultType
+
+from .conftest import HOMEE_ID, HOMEE_IP, HOMEE_NAME, TESTPASS, TESTUSER
+
+from tests.common import MockConfigEntry
+
+
+@pytest.mark.usefixtures("mock_homee", "mock_config_entry", "mock_setup_entry")
+async def test_config_flow(
+    hass: HomeAssistant,
+) -> None:
+    """Test the complete config flow."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": SOURCE_USER}
+    )
+
+    assert result["step_id"] == "user"
+    assert result["type"] is FlowResultType.FORM
+    assert result["errors"] == {}
+
+    result = await hass.config_entries.flow.async_configure(
+        result["flow_id"],
+        user_input={
+            CONF_HOST: HOMEE_IP,
+            CONF_USERNAME: TESTUSER,
+            CONF_PASSWORD: TESTPASS,
+        },
+    )
+
+    assert result["type"] is FlowResultType.CREATE_ENTRY
+
+    assert result["data"] == {
+        "host": HOMEE_IP,
+        "username": TESTUSER,
+        "password": TESTPASS,
+    }
+    assert result["title"] == f"{HOMEE_NAME} ({HOMEE_IP})"
+    assert result["result"].unique_id == HOMEE_ID
+
+
+@pytest.mark.parametrize(
+    ("side_eff", "error"),
+    [
+        (
+            HomeeConnectionFailedException("connection timed out"),
+            {"base": "cannot_connect"},
+        ),
+        (
+            HomeeAuthFailedException("wrong username or password"),
+            {"base": "invalid_auth"},
+        ),
+        (
+            Exception,
+            {"base": "unknown"},
+        ),
+    ],
+)
+async def test_config_flow_errors(
+    hass: HomeAssistant,
+    mock_homee: AsyncMock,
+    side_eff: Exception,
+    error: dict[str, str],
+) -> None:
+    """Test the config flow fails as expected."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": SOURCE_USER}
+    )
+    assert result["type"] == FlowResultType.FORM
+    flow_id = result["flow_id"]
+
+    mock_homee.get_access_token.side_effect = side_eff
+    result = await hass.config_entries.flow.async_configure(
+        flow_id,
+        user_input={
+            CONF_HOST: HOMEE_IP,
+            CONF_USERNAME: TESTUSER,
+            CONF_PASSWORD: TESTPASS,
+        },
+    )
+
+    assert result["type"] == FlowResultType.FORM
+    assert result["errors"] == error
+
+    mock_homee.get_access_token.side_effect = None
+
+    result = await hass.config_entries.flow.async_configure(
+        flow_id,
+        user_input={
+            CONF_HOST: HOMEE_IP,
+            CONF_USERNAME: TESTUSER,
+            CONF_PASSWORD: TESTPASS,
+        },
+    )
+
+    assert result["type"] == FlowResultType.CREATE_ENTRY
+
+
+@pytest.mark.usefixtures("mock_homee")
+async def test_flow_already_configured(
+    hass: HomeAssistant,
+    mock_config_entry: MockConfigEntry,
+) -> None:
+    """Test config flow aborts when already configured."""
+    mock_config_entry.add_to_hass(hass)
+
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": SOURCE_USER}
+    )
+
+    assert result["type"] is FlowResultType.FORM
+
+    result = await hass.config_entries.flow.async_configure(
+        result["flow_id"],
+        user_input={
+            CONF_HOST: HOMEE_IP,
+            CONF_USERNAME: TESTUSER,
+            CONF_PASSWORD: TESTPASS,
+        },
+    )
+    assert result["type"] is FlowResultType.ABORT
+    assert result["reason"] == "already_configured"