diff --git a/.coveragerc b/.coveragerc
index a218c812df43f28944b3d7ce1446db6dc9a79c3c..14a731498b9b6cbe080719b8892a439c9b6e93da 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -609,6 +609,7 @@ omit =
     homeassistant/components/saj/sensor.py
     homeassistant/components/salt/device_tracker.py
     homeassistant/components/satel_integra/*
+    homeassistant/components/schluter/*
     homeassistant/components/scrape/sensor.py
     homeassistant/components/scsgate/*
     homeassistant/components/scsgate/cover.py
diff --git a/CODEOWNERS b/CODEOWNERS
index 1fda4d6f44b35b3b5f18e8a5be8f0c3339fbc0ab..62e6f0d8e6683b35c039b23ae1b27f2cbd144741 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -313,6 +313,7 @@ homeassistant/components/saj/* @fredericvl
 homeassistant/components/salt/* @bjornorri
 homeassistant/components/samsungtv/* @escoand
 homeassistant/components/scene/* @home-assistant/core
+homeassistant/components/schluter/* @prairieapps
 homeassistant/components/scrape/* @fabaff
 homeassistant/components/script/* @home-assistant/core
 homeassistant/components/search/* @home-assistant/core
diff --git a/homeassistant/components/schluter/__init__.py b/homeassistant/components/schluter/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a78730d775f416d5ff27f43bd7c04a600e1b8ca
--- /dev/null
+++ b/homeassistant/components/schluter/__init__.py
@@ -0,0 +1,73 @@
+"""The Schluter DITRA-HEAT integration."""
+import logging
+
+from requests import RequestException, Session
+from schluter.api import Api
+from schluter.authenticator import AuthenticationState, Authenticator
+import voluptuous as vol
+
+from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
+from homeassistant.helpers import discovery
+import homeassistant.helpers.config_validation as cv
+
+from .const import DOMAIN
+
+_LOGGER = logging.getLogger(__name__)
+DATA_SCHLUTER_SESSION = "schluter_session"
+DATA_SCHLUTER_API = "schluter_api"
+SCHLUTER_CONFIG_FILE = ".schluter.conf"
+API_TIMEOUT = 10
+
+CONFIG_SCHEMA = vol.Schema(
+    {
+        vol.Required(DOMAIN): vol.Schema(
+            {
+                vol.Required(CONF_USERNAME): cv.string,
+                vol.Required(CONF_PASSWORD): cv.string,
+            }
+        )
+    },
+    extra=vol.ALLOW_EXTRA,
+)
+
+
+def setup(hass, config):
+    """Set up the Schluter component."""
+    _LOGGER.debug("Starting setup of schluter")
+
+    conf = config[DOMAIN]
+    api_http_session = Session()
+    api = Api(timeout=API_TIMEOUT, http_session=api_http_session)
+
+    authenticator = Authenticator(
+        api,
+        conf.get(CONF_USERNAME),
+        conf.get(CONF_PASSWORD),
+        session_id_cache_file=hass.config.path(SCHLUTER_CONFIG_FILE),
+    )
+
+    authentication = None
+    try:
+        authentication = authenticator.authenticate()
+    except RequestException as ex:
+        _LOGGER.error("Unable to connect to Schluter service: %s", ex)
+        return
+
+    state = authentication.state
+
+    if state == AuthenticationState.AUTHENTICATED:
+        hass.data[DOMAIN] = {
+            DATA_SCHLUTER_API: api,
+            DATA_SCHLUTER_SESSION: authentication.session_id,
+        }
+        discovery.load_platform(hass, "climate", DOMAIN, {}, config)
+        return True
+    if state == AuthenticationState.BAD_PASSWORD:
+        _LOGGER.error("Invalid password provided")
+        return False
+    if state == AuthenticationState.BAD_EMAIL:
+        _LOGGER.error("Invalid email provided")
+        return False
+
+    _LOGGER.error("Unknown set up error: %s", state)
+    return False
diff --git a/homeassistant/components/schluter/climate.py b/homeassistant/components/schluter/climate.py
new file mode 100644
index 0000000000000000000000000000000000000000..99dc5b0d495ca42d2873583b5020b8d066d34322
--- /dev/null
+++ b/homeassistant/components/schluter/climate.py
@@ -0,0 +1,169 @@
+"""Support for Schluter thermostats."""
+import logging
+
+from requests import RequestException
+import voluptuous as vol
+
+from homeassistant.components.climate import (
+    PLATFORM_SCHEMA,
+    SCAN_INTERVAL,
+    ClimateDevice,
+)
+from homeassistant.components.climate.const import (
+    CURRENT_HVAC_HEAT,
+    CURRENT_HVAC_IDLE,
+    HVAC_MODE_HEAT,
+    SUPPORT_TARGET_TEMPERATURE,
+)
+from homeassistant.const import ATTR_TEMPERATURE, CONF_SCAN_INTERVAL
+from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
+
+from . import DATA_SCHLUTER_API, DATA_SCHLUTER_SESSION, DOMAIN
+
+_LOGGER = logging.getLogger(__name__)
+PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
+    {vol.Optional(CONF_SCAN_INTERVAL): vol.All(vol.Coerce(int), vol.Range(min=1))}
+)
+
+
+async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
+    """Set up the Schluter thermostats."""
+    if discovery_info is None:
+        return
+    session_id = hass.data[DOMAIN][DATA_SCHLUTER_SESSION]
+    api = hass.data[DOMAIN][DATA_SCHLUTER_API]
+    temp_unit = hass.config.units.temperature_unit
+
+    async def async_update_data():
+        try:
+            thermostats = await hass.async_add_executor_job(
+                api.get_thermostats, session_id
+            )
+        except RequestException as err:
+            raise UpdateFailed(f"Error communicating with Schluter API: {err}")
+
+        if thermostats is None:
+            return {}
+
+        return {thermo.serial_number: thermo for thermo in thermostats}
+
+    coordinator = DataUpdateCoordinator(
+        hass,
+        _LOGGER,
+        name="schluter",
+        update_method=async_update_data,
+        update_interval=SCAN_INTERVAL,
+    )
+
+    await coordinator.async_refresh()
+
+    async_add_entities(
+        SchluterThermostat(coordinator, serial_number, temp_unit, api, session_id)
+        for serial_number, thermostat in coordinator.data.items()
+    )
+
+
+class SchluterThermostat(ClimateDevice):
+    """Representation of a Schluter thermostat."""
+
+    def __init__(self, coordinator, serial_number, temp_unit, api, session_id):
+        """Initialize the thermostat."""
+        self._unit = temp_unit
+        self._coordinator = coordinator
+        self._serial_number = serial_number
+        self._api = api
+        self._session_id = session_id
+        self._support_flags = SUPPORT_TARGET_TEMPERATURE
+
+    @property
+    def available(self):
+        """Return if thermostat is available."""
+        return self._coordinator.last_update_success
+
+    @property
+    def should_poll(self):
+        """Return if platform should poll."""
+        return False
+
+    @property
+    def supported_features(self):
+        """Return the list of supported features."""
+        return self._support_flags
+
+    @property
+    def unique_id(self):
+        """Return unique ID for this device."""
+        return self._serial_number
+
+    @property
+    def name(self):
+        """Return the name of the thermostat."""
+        return self._coordinator.data[self._serial_number].name
+
+    @property
+    def temperature_unit(self):
+        """Return the unit of measurement."""
+        return self._unit
+
+    @property
+    def current_temperature(self):
+        """Return the current temperature."""
+        return self._coordinator.data[self._serial_number].temperature
+
+    @property
+    def hvac_mode(self):
+        """Return current mode. Only heat available for floor thermostat."""
+        return HVAC_MODE_HEAT
+
+    @property
+    def hvac_action(self):
+        """Return current operation. Can only be heating or idle."""
+        return (
+            CURRENT_HVAC_HEAT
+            if self._coordinator.data[self._serial_number].is_heating
+            else CURRENT_HVAC_IDLE
+        )
+
+    @property
+    def target_temperature(self):
+        """Return the temperature we try to reach."""
+        return self._coordinator.data[self._serial_number].set_point_temp
+
+    @property
+    def hvac_modes(self):
+        """List of available operation modes."""
+        return [HVAC_MODE_HEAT]
+
+    @property
+    def min_temp(self):
+        """Identify min_temp in Schluter API."""
+        return self._coordinator.data[self._serial_number].min_temp
+
+    @property
+    def max_temp(self):
+        """Identify max_temp in Schluter API."""
+        return self._coordinator.data[self._serial_number].max_temp
+
+    async def async_set_hvac_mode(self, hvac_mode):
+        """Mode is always heating, so do nothing."""
+
+    def set_temperature(self, **kwargs):
+        """Set new target temperature."""
+        target_temp = None
+        target_temp = kwargs.get(ATTR_TEMPERATURE)
+        serial_number = self._coordinator.data[self._serial_number].serial_number
+        _LOGGER.debug("Setting thermostat temperature: %s", target_temp)
+
+        try:
+            if target_temp is not None:
+                self._api.set_temperature(self._session_id, serial_number, target_temp)
+        except RequestException as ex:
+            _LOGGER.error("An error occurred while setting temperature: %s", ex)
+
+    async def async_added_to_hass(self):
+        """When entity is added to hass."""
+        self._coordinator.async_add_listener(self.async_write_ha_state)
+
+    async def async_will_remove_from_hass(self):
+        """When entity will be removed from hass."""
+        self._coordinator.async_remove_listener(self.async_write_ha_state)
diff --git a/homeassistant/components/schluter/const.py b/homeassistant/components/schluter/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..e09c8cf66a91641ae09a827a9dd8d9cab1b67b4a
--- /dev/null
+++ b/homeassistant/components/schluter/const.py
@@ -0,0 +1,3 @@
+"""Constants for the Schluter DITRA-HEAT integration."""
+
+DOMAIN = "schluter"
diff --git a/homeassistant/components/schluter/manifest.json b/homeassistant/components/schluter/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..1a7cebcf06af0166f512e212c02c930cfec921cc
--- /dev/null
+++ b/homeassistant/components/schluter/manifest.json
@@ -0,0 +1,8 @@
+{
+  "domain": "schluter",
+  "name": "Schluter",
+  "documentation": "https://www.home-assistant.io/integrations/schluter",
+  "requirements": ["py-schluter==0.1.7"],
+  "dependencies": [],
+  "codeowners": ["@prairieapps"]
+}
diff --git a/requirements_all.txt b/requirements_all.txt
index d1d6e2447eda585843abe5488975d3617be41ba3..f0ba017f5740ae59572d0c4c3d6911ed07360625 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1111,6 +1111,9 @@ py-cpuinfo==5.0.0
 # homeassistant.components.melissa
 py-melissa-climate==2.0.0
 
+# homeassistant.components.schluter
+py-schluter==0.1.7
+
 # homeassistant.components.synology
 py-synology==0.2.0