diff --git a/.coveragerc b/.coveragerc
index 9dc665d9c3cad9580e81f5bd0753d97d17119c42..d026723d500b18347279217725f709d5bc3d3146 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -550,6 +550,8 @@ omit =
     homeassistant/components/hunterdouglas_powerview/shade_data.py
     homeassistant/components/hunterdouglas_powerview/util.py
     homeassistant/components/hvv_departures/__init__.py
+    homeassistant/components/huum/__init__.py
+    homeassistant/components/huum/climate.py
     homeassistant/components/hvv_departures/binary_sensor.py
     homeassistant/components/hvv_departures/sensor.py
     homeassistant/components/ialarm/alarm_control_panel.py
diff --git a/CODEOWNERS b/CODEOWNERS
index a423bbf8f7614b4ad6475adbd57040a485a42295..9d1d2339d23523aef56cfbd1a7bb4d5e4cc47cfa 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -579,6 +579,8 @@ build.json @home-assistant/supervisor
 /tests/components/humidifier/ @home-assistant/core @Shulyaka
 /homeassistant/components/hunterdouglas_powerview/ @bdraco @kingy444 @trullock
 /tests/components/hunterdouglas_powerview/ @bdraco @kingy444 @trullock
+/homeassistant/components/huum/ @frwickst
+/tests/components/huum/ @frwickst
 /homeassistant/components/hvv_departures/ @vigonotion
 /tests/components/hvv_departures/ @vigonotion
 /homeassistant/components/hydrawise/ @dknowles2 @ptcryan
diff --git a/homeassistant/components/huum/__init__.py b/homeassistant/components/huum/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a5daf471a2d2728d36ab167ccbd44a9d1f42d3c9
--- /dev/null
+++ b/homeassistant/components/huum/__init__.py
@@ -0,0 +1,46 @@
+"""The Huum integration."""
+from __future__ import annotations
+
+import logging
+
+from huum.exceptions import Forbidden, NotAuthenticated
+from huum.huum import Huum
+
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
+from homeassistant.core import HomeAssistant
+from homeassistant.exceptions import ConfigEntryNotReady
+from homeassistant.helpers.aiohttp_client import async_get_clientsession
+
+from .const import DOMAIN, PLATFORMS
+
+_LOGGER = logging.getLogger(__name__)
+
+
+async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
+    """Set up Huum from a config entry."""
+    username = entry.data[CONF_USERNAME]
+    password = entry.data[CONF_PASSWORD]
+
+    huum = Huum(username, password, session=async_get_clientsession(hass))
+
+    try:
+        await huum.status()
+    except (Forbidden, NotAuthenticated) as err:
+        _LOGGER.error("Could not log in to Huum with given credentials")
+        raise ConfigEntryNotReady(
+            "Could not log in to Huum with given credentials"
+        ) from err
+
+    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = huum
+
+    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
+    return True
+
+
+async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
+    """Unload a config entry."""
+    if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
+        hass.data[DOMAIN].pop(entry.entry_id)
+
+    return unload_ok
diff --git a/homeassistant/components/huum/climate.py b/homeassistant/components/huum/climate.py
new file mode 100644
index 0000000000000000000000000000000000000000..dcf025082cc1811fb11ad86862be16a0aeb284e7
--- /dev/null
+++ b/homeassistant/components/huum/climate.py
@@ -0,0 +1,128 @@
+"""Support for Huum wifi-enabled sauna."""
+from __future__ import annotations
+
+import logging
+from typing import Any
+
+from huum.const import SaunaStatus
+from huum.exceptions import SafetyException
+from huum.huum import Huum
+from huum.schemas import HuumStatusResponse
+
+from homeassistant.components.climate import (
+    ClimateEntity,
+    ClimateEntityFeature,
+    HVACMode,
+)
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
+from homeassistant.core import HomeAssistant
+from homeassistant.exceptions import HomeAssistantError
+from homeassistant.helpers.device_registry import DeviceInfo
+from homeassistant.helpers.entity_platform import AddEntitiesCallback
+
+from .const import DOMAIN
+
+_LOGGER = logging.getLogger(__name__)
+
+
+async def async_setup_entry(
+    hass: HomeAssistant,
+    entry: ConfigEntry,
+    async_add_entities: AddEntitiesCallback,
+) -> None:
+    """Set up the Huum sauna with config flow."""
+    huum_handler = hass.data.setdefault(DOMAIN, {})[entry.entry_id]
+
+    async_add_entities([HuumDevice(huum_handler, entry.entry_id)], True)
+
+
+class HuumDevice(ClimateEntity):
+    """Representation of a heater."""
+
+    _attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
+    _attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
+    _attr_target_temperature_step = PRECISION_WHOLE
+    _attr_temperature_unit = UnitOfTemperature.CELSIUS
+    _attr_max_temp = 110
+    _attr_min_temp = 40
+    _attr_has_entity_name = True
+    _attr_name = None
+
+    _target_temperature: int | None = None
+    _status: HuumStatusResponse | None = None
+
+    def __init__(self, huum_handler: Huum, unique_id: str) -> None:
+        """Initialize the heater."""
+        self._attr_unique_id = unique_id
+        self._attr_device_info = DeviceInfo(
+            identifiers={(DOMAIN, unique_id)},
+            name="Huum sauna",
+            manufacturer="Huum",
+        )
+
+        self._huum_handler = huum_handler
+
+    @property
+    def hvac_mode(self) -> HVACMode:
+        """Return hvac operation ie. heat, cool mode."""
+        if self._status and self._status.status == SaunaStatus.ONLINE_HEATING:
+            return HVACMode.HEAT
+        return HVACMode.OFF
+
+    @property
+    def icon(self) -> str:
+        """Return nice icon for heater."""
+        if self.hvac_mode == HVACMode.HEAT:
+            return "mdi:radiator"
+        return "mdi:radiator-off"
+
+    @property
+    def current_temperature(self) -> int | None:
+        """Return the current temperature."""
+        if (status := self._status) is not None:
+            return status.temperature
+        return None
+
+    @property
+    def target_temperature(self) -> int:
+        """Return the temperature we try to reach."""
+        return self._target_temperature or int(self.min_temp)
+
+    async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
+        """Set hvac mode."""
+        if hvac_mode == HVACMode.HEAT:
+            await self._turn_on(self.target_temperature)
+        elif hvac_mode == HVACMode.OFF:
+            await self._huum_handler.turn_off()
+
+    async def async_set_temperature(self, **kwargs: Any) -> None:
+        """Set new target temperature."""
+        temperature = kwargs.get(ATTR_TEMPERATURE)
+        if temperature is None:
+            return
+        self._target_temperature = temperature
+
+        if self.hvac_mode == HVACMode.HEAT:
+            await self._turn_on(temperature)
+
+    async def async_update(self) -> None:
+        """Get the latest status data.
+
+        We get the latest status first from the status endpoints of the sauna.
+        If that data does not include the temperature, that means that the sauna
+        is off, we then call the off command which will in turn return the temperature.
+        This is a workaround for getting the temperature as the Huum API does not
+        return the target temperature of a sauna that is off, even if it can have
+        a target temperature at that time.
+        """
+        self._status = await self._huum_handler.status_from_status_or_stop()
+        if self._target_temperature is None or self.hvac_mode == HVACMode.HEAT:
+            self._target_temperature = self._status.target_temperature
+
+    async def _turn_on(self, temperature: int) -> None:
+        try:
+            await self._huum_handler.turn_on(temperature)
+        except (ValueError, SafetyException) as err:
+            _LOGGER.error(str(err))
+            raise HomeAssistantError(f"Unable to turn on sauna: {err}") from err
diff --git a/homeassistant/components/huum/config_flow.py b/homeassistant/components/huum/config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..31f4c9a137cd8d8701709a788386d6ccb1dafb96
--- /dev/null
+++ b/homeassistant/components/huum/config_flow.py
@@ -0,0 +1,63 @@
+"""Config flow for huum integration."""
+from __future__ import annotations
+
+import logging
+from typing import Any
+
+from huum.exceptions import Forbidden, NotAuthenticated
+from huum.huum import Huum
+import voluptuous as vol
+
+from homeassistant import config_entries
+from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
+from homeassistant.data_entry_flow import FlowResult
+from homeassistant.helpers.aiohttp_client import async_get_clientsession
+
+from .const import DOMAIN
+
+_LOGGER = logging.getLogger(__name__)
+
+STEP_USER_DATA_SCHEMA = vol.Schema(
+    {
+        vol.Required(CONF_USERNAME): str,
+        vol.Required(CONF_PASSWORD): str,
+    }
+)
+
+
+class HuumConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
+    """Handle a config flow for huum."""
+
+    VERSION = 1
+
+    async def async_step_user(
+        self, user_input: dict[str, Any] | None = None
+    ) -> FlowResult:
+        """Handle the initial step."""
+        errors = {}
+        if user_input is not None:
+            try:
+                huum_handler = Huum(
+                    user_input[CONF_USERNAME],
+                    user_input[CONF_PASSWORD],
+                    session=async_get_clientsession(self.hass),
+                )
+                await huum_handler.status()
+            except (Forbidden, NotAuthenticated):
+                # Most likely Forbidden as that is what is returned from `.status()` with bad creds
+                _LOGGER.error("Could not log in to Huum with given credentials")
+                errors["base"] = "invalid_auth"
+            except Exception:  # pylint: disable=broad-except
+                _LOGGER.exception("Unknown error")
+                errors["base"] = "unknown"
+            else:
+                self._async_abort_entries_match(
+                    {CONF_USERNAME: user_input[CONF_USERNAME]}
+                )
+                return self.async_create_entry(
+                    title=user_input[CONF_USERNAME], data=user_input
+                )
+
+        return self.async_show_form(
+            step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
+        )
diff --git a/homeassistant/components/huum/const.py b/homeassistant/components/huum/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..69dea45b2184ca64727c4cd61fcd4d8c8d1b4e52
--- /dev/null
+++ b/homeassistant/components/huum/const.py
@@ -0,0 +1,7 @@
+"""Constants for the huum integration."""
+
+from homeassistant.const import Platform
+
+DOMAIN = "huum"
+
+PLATFORMS = [Platform.CLIMATE]
diff --git a/homeassistant/components/huum/manifest.json b/homeassistant/components/huum/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..46256d1534775ef0c19053d7add80ff34252d443
--- /dev/null
+++ b/homeassistant/components/huum/manifest.json
@@ -0,0 +1,9 @@
+{
+  "domain": "huum",
+  "name": "Huum",
+  "codeowners": ["@frwickst"],
+  "config_flow": true,
+  "documentation": "https://www.home-assistant.io/integrations/huum",
+  "iot_class": "cloud_polling",
+  "requirements": ["huum==0.7.9"]
+}
diff --git a/homeassistant/components/huum/strings.json b/homeassistant/components/huum/strings.json
new file mode 100644
index 0000000000000000000000000000000000000000..68ab1adde6f7203e15fd682e754b52af193dfa25
--- /dev/null
+++ b/homeassistant/components/huum/strings.json
@@ -0,0 +1,22 @@
+{
+  "config": {
+    "step": {
+      "user": {
+        "title": "Connect to the Huum",
+        "description": "Log in with the same username and password that is used in the Huum mobile app.",
+        "data": {
+          "username": "[%key:common::config_flow::data::username%]",
+          "password": "[%key:common::config_flow::data::password%]"
+        }
+      }
+    },
+    "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%]"
+    }
+  }
+}
diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py
index 3f26b6f907b20cd93acb1f98c3059a0d1c14f58b..d63bdc23b124da0b98eefe4a22987ae1af53b551 100644
--- a/homeassistant/generated/config_flows.py
+++ b/homeassistant/generated/config_flows.py
@@ -225,6 +225,7 @@ FLOWS = {
         "hue",
         "huisbaasje",
         "hunterdouglas_powerview",
+        "huum",
         "hvv_departures",
         "hydrawise",
         "hyperion",
diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json
index f188201f84722d283405ea08b351186fb3aad513..0e9b46ea15294cfdda1cfa497db8499716353c58 100644
--- a/homeassistant/generated/integrations.json
+++ b/homeassistant/generated/integrations.json
@@ -2596,6 +2596,12 @@
       "integration_type": "virtual",
       "supported_by": "motion_blinds"
     },
+    "huum": {
+      "name": "Huum",
+      "integration_type": "hub",
+      "config_flow": true,
+      "iot_class": "cloud_polling"
+    },
     "hvv_departures": {
       "name": "HVV Departures",
       "integration_type": "hub",
diff --git a/requirements_all.txt b/requirements_all.txt
index f70bd86f29c198aaf13c5a7706d07e474985d780..bdc51d6c204aa7c495221604773f6073920fb339 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1073,6 +1073,9 @@ httplib2==0.20.4
 # homeassistant.components.huawei_lte
 huawei-lte-api==1.7.3
 
+# homeassistant.components.huum
+huum==0.7.9
+
 # homeassistant.components.hyperion
 hyperion-py==0.7.5
 
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 479d84bf57a6b6742731fac10aad45ad0928baee..cb81754923240f9b4e699c9875a300fb0cb7f0fe 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -863,6 +863,9 @@ httplib2==0.20.4
 # homeassistant.components.huawei_lte
 huawei-lte-api==1.7.3
 
+# homeassistant.components.huum
+huum==0.7.9
+
 # homeassistant.components.hyperion
 hyperion-py==0.7.5
 
diff --git a/tests/components/huum/__init__.py b/tests/components/huum/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..443cbd52c36dbb7af02371618863dadeb7699a38
--- /dev/null
+++ b/tests/components/huum/__init__.py
@@ -0,0 +1 @@
+"""Tests for the huum integration."""
diff --git a/tests/components/huum/test_config_flow.py b/tests/components/huum/test_config_flow.py
new file mode 100644
index 0000000000000000000000000000000000000000..7163521b446fcc0597284bb6a1b3ddfdaa56354b
--- /dev/null
+++ b/tests/components/huum/test_config_flow.py
@@ -0,0 +1,135 @@
+"""Test the huum config flow."""
+from unittest.mock import patch
+
+from huum.exceptions import Forbidden
+import pytest
+
+from homeassistant import config_entries
+from homeassistant.components.huum.const import DOMAIN
+from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
+from homeassistant.core import HomeAssistant
+from homeassistant.data_entry_flow import FlowResultType
+
+from tests.common import MockConfigEntry
+
+TEST_USERNAME = "test-username"
+TEST_PASSWORD = "test-password"
+
+
+async def test_form(hass: HomeAssistant) -> None:
+    """Test we get the form."""
+
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+    assert result["type"] == FlowResultType.FORM
+    assert result["errors"] == {}
+
+    with patch(
+        "homeassistant.components.huum.config_flow.Huum.status",
+        return_value=True,
+    ), patch(
+        "homeassistant.components.huum.async_setup_entry",
+        return_value=True,
+    ) as mock_setup_entry:
+        result2 = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            {
+                CONF_USERNAME: TEST_USERNAME,
+                CONF_PASSWORD: TEST_PASSWORD,
+            },
+        )
+        await hass.async_block_till_done()
+
+    assert result2["type"] == FlowResultType.CREATE_ENTRY
+    assert result2["title"] == TEST_USERNAME
+    assert result2["data"] == {
+        CONF_USERNAME: TEST_USERNAME,
+        CONF_PASSWORD: TEST_PASSWORD,
+    }
+    assert len(mock_setup_entry.mock_calls) == 1
+
+
+async def test_signup_flow_already_set_up(hass: HomeAssistant) -> None:
+    """Test that we handle already existing entities with same id."""
+    mock_config_entry = MockConfigEntry(
+        title="Huum Sauna",
+        domain=DOMAIN,
+        unique_id=TEST_USERNAME,
+        data={
+            CONF_USERNAME: TEST_USERNAME,
+            CONF_PASSWORD: TEST_PASSWORD,
+        },
+    )
+    mock_config_entry.add_to_hass(hass)
+
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+
+    with patch(
+        "homeassistant.components.huum.config_flow.Huum.status",
+        return_value=True,
+    ), patch(
+        "homeassistant.components.huum.async_setup_entry",
+        return_value=True,
+    ):
+        result2 = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            {
+                CONF_USERNAME: TEST_USERNAME,
+                CONF_PASSWORD: TEST_PASSWORD,
+            },
+        )
+        await hass.async_block_till_done()
+        assert result2["type"] == FlowResultType.ABORT
+
+
+@pytest.mark.parametrize(
+    (
+        "raises",
+        "error_base",
+    ),
+    [
+        (Exception, "unknown"),
+        (Forbidden, "invalid_auth"),
+    ],
+)
+async def test_huum_errors(
+    hass: HomeAssistant, raises: Exception, error_base: str
+) -> None:
+    """Test we handle cannot connect error."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+
+    with patch(
+        "homeassistant.components.huum.config_flow.Huum.status",
+        side_effect=raises,
+    ):
+        result2 = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            {
+                CONF_USERNAME: TEST_USERNAME,
+                CONF_PASSWORD: TEST_PASSWORD,
+            },
+        )
+
+    assert result2["type"] == FlowResultType.FORM
+    assert result2["errors"] == {"base": error_base}
+
+    with patch(
+        "homeassistant.components.huum.config_flow.Huum.status",
+        return_value=True,
+    ), patch(
+        "homeassistant.components.huum.async_setup_entry",
+        return_value=True,
+    ):
+        result2 = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            {
+                CONF_USERNAME: TEST_USERNAME,
+                CONF_PASSWORD: TEST_PASSWORD,
+            },
+        )
+        assert result2["type"] == FlowResultType.CREATE_ENTRY