diff --git a/.coveragerc b/.coveragerc
index 0b73599dffa7e11c8223e35d2f57baa7721eb271..f4794b593816f112c19e4715cb311dd49d1472ee 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -676,6 +676,7 @@ omit =
     homeassistant/components/systemmonitor/sensor.py
     homeassistant/components/tado/*
     homeassistant/components/tado/device_tracker.py
+    homeassistant/components/tahoma/*
     homeassistant/components/tank_utility/sensor.py
     homeassistant/components/tapsaff/binary_sensor.py
     homeassistant/components/tautulli/sensor.py
diff --git a/CODEOWNERS b/CODEOWNERS
index 392c363c648c288ecbd14afd6db09cf4c582d6a6..4fbdca20686615750a112f75471d3da7d6fc651e 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -318,6 +318,7 @@ homeassistant/components/syncthru/* @nielstron
 homeassistant/components/synology_srm/* @aerialls
 homeassistant/components/syslog/* @fabaff
 homeassistant/components/tado/* @michaelarnauts
+homeassistant/components/tahoma/* @philklei
 homeassistant/components/tautulli/* @ludeeus
 homeassistant/components/tellduslive/* @fredrike
 homeassistant/components/template/* @PhracturedBlue
diff --git a/homeassistant/components/tahoma/__init__.py b/homeassistant/components/tahoma/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..640cc6418d0f01af52aaf0830d794abd3c4320aa
--- /dev/null
+++ b/homeassistant/components/tahoma/__init__.py
@@ -0,0 +1,140 @@
+"""Support for Tahoma devices."""
+from collections import defaultdict
+import logging
+
+from requests.exceptions import RequestException
+from tahoma_api import Action, TahomaApi
+import voluptuous as vol
+
+from homeassistant.const import CONF_EXCLUDE, CONF_PASSWORD, CONF_USERNAME
+from homeassistant.helpers import config_validation as cv, discovery
+from homeassistant.helpers.entity import Entity
+
+_LOGGER = logging.getLogger(__name__)
+
+DOMAIN = "tahoma"
+
+TAHOMA_ID_FORMAT = "{}_{}"
+
+CONFIG_SCHEMA = vol.Schema(
+    {
+        DOMAIN: vol.Schema(
+            {
+                vol.Required(CONF_USERNAME): cv.string,
+                vol.Required(CONF_PASSWORD): cv.string,
+                vol.Optional(CONF_EXCLUDE, default=[]): vol.All(
+                    cv.ensure_list, [cv.string]
+                ),
+            }
+        )
+    },
+    extra=vol.ALLOW_EXTRA,
+)
+
+TAHOMA_COMPONENTS = ["scene", "sensor", "cover", "switch", "binary_sensor"]
+
+TAHOMA_TYPES = {
+    "io:ExteriorVenetianBlindIOComponent": "cover",
+    "io:HorizontalAwningIOComponent": "cover",
+    "io:LightIOSystemSensor": "sensor",
+    "io:OnOffIOComponent": "switch",
+    "io:OnOffLightIOComponent": "switch",
+    "io:RollerShutterGenericIOComponent": "cover",
+    "io:RollerShutterUnoIOComponent": "cover",
+    "io:RollerShutterVeluxIOComponent": "cover",
+    "io:RollerShutterWithLowSpeedManagementIOComponent": "cover",
+    "io:SomfyBasicContactIOSystemSensor": "sensor",
+    "io:SomfyContactIOSystemSensor": "sensor",
+    "io:VerticalExteriorAwningIOComponent": "cover",
+    "io:VerticalInteriorBlindVeluxIOComponent": "cover",
+    "io:WindowOpenerVeluxIOComponent": "cover",
+    "io:GarageOpenerIOComponent": "cover",
+    "io:DiscreteGarageOpenerIOComponent": "cover",
+    "rtds:RTDSContactSensor": "sensor",
+    "rtds:RTDSMotionSensor": "sensor",
+    "rtds:RTDSSmokeSensor": "smoke",
+    "rts:BlindRTSComponent": "cover",
+    "rts:CurtainRTSComponent": "cover",
+    "rts:DualCurtainRTSComponent": "cover",
+    "rts:ExteriorVenetianBlindRTSComponent": "cover",
+    "rts:GarageDoor4TRTSComponent": "switch",
+    "rts:RollerShutterRTSComponent": "cover",
+    "rts:VenetianBlindRTSComponent": "cover",
+}
+
+
+def setup(hass, config):
+    """Activate Tahoma component."""
+
+    conf = config[DOMAIN]
+    username = conf.get(CONF_USERNAME)
+    password = conf.get(CONF_PASSWORD)
+    exclude = conf.get(CONF_EXCLUDE)
+    try:
+        api = TahomaApi(username, password)
+    except RequestException:
+        _LOGGER.exception("Error when trying to log in to the Tahoma API")
+        return False
+
+    try:
+        api.get_setup()
+        devices = api.get_devices()
+        scenes = api.get_action_groups()
+    except RequestException:
+        _LOGGER.exception("Error when getting devices from the Tahoma API")
+        return False
+
+    hass.data[DOMAIN] = {"controller": api, "devices": defaultdict(list), "scenes": []}
+
+    for device in devices:
+        _device = api.get_device(device)
+        if all(ext not in _device.type for ext in exclude):
+            device_type = map_tahoma_device(_device)
+            if device_type is None:
+                _LOGGER.warning(
+                    "Unsupported type %s for Tahoma device %s",
+                    _device.type,
+                    _device.label,
+                )
+                continue
+            hass.data[DOMAIN]["devices"][device_type].append(_device)
+
+    for scene in scenes:
+        hass.data[DOMAIN]["scenes"].append(scene)
+
+    for component in TAHOMA_COMPONENTS:
+        discovery.load_platform(hass, component, DOMAIN, {}, config)
+
+    return True
+
+
+def map_tahoma_device(tahoma_device):
+    """Map Tahoma device types to Home Assistant components."""
+    return TAHOMA_TYPES.get(tahoma_device.type)
+
+
+class TahomaDevice(Entity):
+    """Representation of a Tahoma device entity."""
+
+    def __init__(self, tahoma_device, controller):
+        """Initialize the device."""
+        self.tahoma_device = tahoma_device
+        self.controller = controller
+        self._name = self.tahoma_device.label
+
+    @property
+    def name(self):
+        """Return the name of the device."""
+        return self._name
+
+    @property
+    def device_state_attributes(self):
+        """Return the state attributes of the device."""
+        return {"tahoma_device_id": self.tahoma_device.url}
+
+    def apply_action(self, cmd_name, *args):
+        """Apply Action to Device."""
+
+        action = Action(self.tahoma_device.url)
+        action.add_command(cmd_name, *args)
+        self.controller.apply_actions("HomeAssistant", [action])
diff --git a/homeassistant/components/tahoma/binary_sensor.py b/homeassistant/components/tahoma/binary_sensor.py
new file mode 100644
index 0000000000000000000000000000000000000000..81078ab480babdee8cff9284628c57653a426e86
--- /dev/null
+++ b/homeassistant/components/tahoma/binary_sensor.py
@@ -0,0 +1,95 @@
+"""Support for Tahoma binary sensors."""
+from datetime import timedelta
+import logging
+
+from homeassistant.components.binary_sensor import BinarySensorDevice
+from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON
+
+from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice
+
+_LOGGER = logging.getLogger(__name__)
+
+SCAN_INTERVAL = timedelta(seconds=120)
+
+
+def setup_platform(hass, config, add_entities, discovery_info=None):
+    """Set up Tahoma controller devices."""
+    _LOGGER.debug("Setup Tahoma Binary sensor platform")
+    controller = hass.data[TAHOMA_DOMAIN]["controller"]
+    devices = []
+    for device in hass.data[TAHOMA_DOMAIN]["devices"]["smoke"]:
+        devices.append(TahomaBinarySensor(device, controller))
+    add_entities(devices, True)
+
+
+class TahomaBinarySensor(TahomaDevice, BinarySensorDevice):
+    """Representation of a Tahoma Binary Sensor."""
+
+    def __init__(self, tahoma_device, controller):
+        """Initialize the sensor."""
+        super().__init__(tahoma_device, controller)
+
+        self._state = None
+        self._icon = None
+        self._battery = None
+        self._available = False
+
+    @property
+    def is_on(self):
+        """Return the state of the sensor."""
+        return bool(self._state == STATE_ON)
+
+    @property
+    def device_class(self):
+        """Return the class of the device."""
+        if self.tahoma_device.type == "rtds:RTDSSmokeSensor":
+            return "smoke"
+        return None
+
+    @property
+    def icon(self):
+        """Icon for device by its type."""
+        return self._icon
+
+    @property
+    def device_state_attributes(self):
+        """Return the device state attributes."""
+        attr = {}
+        super_attr = super().device_state_attributes
+        if super_attr is not None:
+            attr.update(super_attr)
+
+        if self._battery is not None:
+            attr[ATTR_BATTERY_LEVEL] = self._battery
+        return attr
+
+    @property
+    def available(self):
+        """Return True if entity is available."""
+        return self._available
+
+    def update(self):
+        """Update the state."""
+        self.controller.get_states([self.tahoma_device])
+        if self.tahoma_device.type == "rtds:RTDSSmokeSensor":
+            if self.tahoma_device.active_states["core:SmokeState"] == "notDetected":
+                self._state = STATE_OFF
+            else:
+                self._state = STATE_ON
+
+        if "core:SensorDefectState" in self.tahoma_device.active_states:
+            # 'lowBattery' for low battery warning. 'dead' for not available.
+            self._battery = self.tahoma_device.active_states["core:SensorDefectState"]
+            self._available = bool(self._battery != "dead")
+        else:
+            self._battery = None
+            self._available = True
+
+        if self._state == STATE_ON:
+            self._icon = "mdi:fire"
+        elif self._battery == "lowBattery":
+            self._icon = "mdi:battery-alert"
+        else:
+            self._icon = None
+
+        _LOGGER.debug("Update %s, state: %s", self._name, self._state)
diff --git a/homeassistant/components/tahoma/cover.py b/homeassistant/components/tahoma/cover.py
new file mode 100644
index 0000000000000000000000000000000000000000..e11c2f4cdf59ce4cc3fd4e09ec7db3fb544820dd
--- /dev/null
+++ b/homeassistant/components/tahoma/cover.py
@@ -0,0 +1,249 @@
+"""Support for Tahoma cover - shutters etc."""
+from datetime import timedelta
+import logging
+
+from homeassistant.components.cover import (
+    ATTR_POSITION,
+    DEVICE_CLASS_AWNING,
+    DEVICE_CLASS_BLIND,
+    DEVICE_CLASS_CURTAIN,
+    DEVICE_CLASS_GARAGE,
+    DEVICE_CLASS_SHUTTER,
+    DEVICE_CLASS_WINDOW,
+    CoverDevice,
+)
+from homeassistant.util.dt import utcnow
+
+from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice
+
+_LOGGER = logging.getLogger(__name__)
+
+ATTR_MEM_POS = "memorized_position"
+ATTR_RSSI_LEVEL = "rssi_level"
+ATTR_LOCK_START_TS = "lock_start_ts"
+ATTR_LOCK_END_TS = "lock_end_ts"
+ATTR_LOCK_LEVEL = "lock_level"
+ATTR_LOCK_ORIG = "lock_originator"
+
+HORIZONTAL_AWNING = "io:HorizontalAwningIOComponent"
+
+TAHOMA_DEVICE_CLASSES = {
+    "io:ExteriorVenetianBlindIOComponent": DEVICE_CLASS_BLIND,
+    HORIZONTAL_AWNING: DEVICE_CLASS_AWNING,
+    "io:RollerShutterGenericIOComponent": DEVICE_CLASS_SHUTTER,
+    "io:RollerShutterUnoIOComponent": DEVICE_CLASS_SHUTTER,
+    "io:RollerShutterVeluxIOComponent": DEVICE_CLASS_SHUTTER,
+    "io:RollerShutterWithLowSpeedManagementIOComponent": DEVICE_CLASS_SHUTTER,
+    "io:VerticalExteriorAwningIOComponent": DEVICE_CLASS_AWNING,
+    "io:VerticalInteriorBlindVeluxIOComponent": DEVICE_CLASS_BLIND,
+    "io:WindowOpenerVeluxIOComponent": DEVICE_CLASS_WINDOW,
+    "io:GarageOpenerIOComponent": DEVICE_CLASS_GARAGE,
+    "io:DiscreteGarageOpenerIOComponent": DEVICE_CLASS_GARAGE,
+    "rts:BlindRTSComponent": DEVICE_CLASS_BLIND,
+    "rts:CurtainRTSComponent": DEVICE_CLASS_CURTAIN,
+    "rts:DualCurtainRTSComponent": DEVICE_CLASS_CURTAIN,
+    "rts:ExteriorVenetianBlindRTSComponent": DEVICE_CLASS_BLIND,
+    "rts:RollerShutterRTSComponent": DEVICE_CLASS_SHUTTER,
+    "rts:VenetianBlindRTSComponent": DEVICE_CLASS_BLIND,
+}
+
+
+def setup_platform(hass, config, add_entities, discovery_info=None):
+    """Set up the Tahoma covers."""
+    controller = hass.data[TAHOMA_DOMAIN]["controller"]
+    devices = []
+    for device in hass.data[TAHOMA_DOMAIN]["devices"]["cover"]:
+        devices.append(TahomaCover(device, controller))
+    add_entities(devices, True)
+
+
+class TahomaCover(TahomaDevice, CoverDevice):
+    """Representation a Tahoma Cover."""
+
+    def __init__(self, tahoma_device, controller):
+        """Initialize the device."""
+        super().__init__(tahoma_device, controller)
+
+        self._closure = 0
+        # 100 equals open
+        self._position = 100
+        self._closed = False
+        self._rssi_level = None
+        self._icon = None
+        # Can be 0 and bigger
+        self._lock_timer = 0
+        self._lock_start_ts = None
+        self._lock_end_ts = None
+        # Can be 'comfortLevel1', 'comfortLevel2', 'comfortLevel3',
+        # 'comfortLevel4', 'environmentProtection', 'humanProtection',
+        # 'userLevel1', 'userLevel2'
+        self._lock_level = None
+        # Can be 'LSC', 'SAAC', 'SFC', 'UPS', 'externalGateway', 'localUser',
+        # 'myself', 'rain', 'security', 'temperature', 'timer', 'user', 'wind'
+        self._lock_originator = None
+
+    def update(self):
+        """Update method."""
+        self.controller.get_states([self.tahoma_device])
+
+        # For vertical covers
+        self._closure = self.tahoma_device.active_states.get("core:ClosureState")
+        # For horizontal covers
+        if self._closure is None:
+            self._closure = self.tahoma_device.active_states.get("core:DeploymentState")
+
+        # For all, if available
+        if "core:PriorityLockTimerState" in self.tahoma_device.active_states:
+            old_lock_timer = self._lock_timer
+            self._lock_timer = self.tahoma_device.active_states[
+                "core:PriorityLockTimerState"
+            ]
+            # Derive timestamps from _lock_timer, only if not already set or
+            # something has changed
+            if self._lock_timer > 0:
+                _LOGGER.debug("Update %s, lock_timer: %d", self._name, self._lock_timer)
+                if self._lock_start_ts is None:
+                    self._lock_start_ts = utcnow()
+                if self._lock_end_ts is None or old_lock_timer != self._lock_timer:
+                    self._lock_end_ts = utcnow() + timedelta(seconds=self._lock_timer)
+            else:
+                self._lock_start_ts = None
+                self._lock_end_ts = None
+        else:
+            self._lock_timer = 0
+            self._lock_start_ts = None
+            self._lock_end_ts = None
+
+        self._lock_level = self.tahoma_device.active_states.get(
+            "io:PriorityLockLevelState"
+        )
+
+        self._lock_originator = self.tahoma_device.active_states.get(
+            "io:PriorityLockOriginatorState"
+        )
+
+        self._rssi_level = self.tahoma_device.active_states.get("core:RSSILevelState")
+
+        # Define which icon to use
+        if self._lock_timer > 0:
+            if self._lock_originator == "wind":
+                self._icon = "mdi:weather-windy"
+            else:
+                self._icon = "mdi:lock-alert"
+        else:
+            self._icon = None
+
+        # Define current position.
+        #   _position: 0 is closed, 100 is fully open.
+        #   'core:ClosureState': 100 is closed, 0 is fully open.
+        if self._closure is not None:
+            if self.tahoma_device.type == HORIZONTAL_AWNING:
+                self._position = self._closure
+            else:
+                self._position = 100 - self._closure
+            if self._position <= 5:
+                self._position = 0
+            if self._position >= 95:
+                self._position = 100
+            self._closed = self._position == 0
+        else:
+            self._position = None
+            if "core:OpenClosedState" in self.tahoma_device.active_states:
+                self._closed = (
+                    self.tahoma_device.active_states["core:OpenClosedState"] == "closed"
+                )
+            else:
+                self._closed = False
+
+        _LOGGER.debug("Update %s, position: %d", self._name, self._position)
+
+    @property
+    def current_cover_position(self):
+        """Return current position of cover."""
+        return self._position
+
+    def set_cover_position(self, **kwargs):
+        """Move the cover to a specific position."""
+        if self.tahoma_device.type == "io:WindowOpenerVeluxIOComponent":
+            command = "setClosure"
+        else:
+            command = "setPosition"
+
+        if self.tahoma_device.type == HORIZONTAL_AWNING:
+            self.apply_action(command, kwargs.get(ATTR_POSITION, 0))
+        else:
+            self.apply_action(command, 100 - kwargs.get(ATTR_POSITION, 0))
+
+    @property
+    def is_closed(self):
+        """Return if the cover is closed."""
+        return self._closed
+
+    @property
+    def device_class(self):
+        """Return the class of the device."""
+        return TAHOMA_DEVICE_CLASSES.get(self.tahoma_device.type)
+
+    @property
+    def device_state_attributes(self):
+        """Return the device state attributes."""
+        attr = {}
+        super_attr = super().device_state_attributes
+        if super_attr is not None:
+            attr.update(super_attr)
+
+        if "core:Memorized1PositionState" in self.tahoma_device.active_states:
+            attr[ATTR_MEM_POS] = self.tahoma_device.active_states[
+                "core:Memorized1PositionState"
+            ]
+        if self._rssi_level is not None:
+            attr[ATTR_RSSI_LEVEL] = self._rssi_level
+        if self._lock_start_ts is not None:
+            attr[ATTR_LOCK_START_TS] = self._lock_start_ts.isoformat()
+        if self._lock_end_ts is not None:
+            attr[ATTR_LOCK_END_TS] = self._lock_end_ts.isoformat()
+        if self._lock_level is not None:
+            attr[ATTR_LOCK_LEVEL] = self._lock_level
+        if self._lock_originator is not None:
+            attr[ATTR_LOCK_ORIG] = self._lock_originator
+        return attr
+
+    @property
+    def icon(self):
+        """Return the icon to use in the frontend, if any."""
+        return self._icon
+
+    def open_cover(self, **kwargs):
+        """Open the cover."""
+        self.apply_action("open")
+
+    def close_cover(self, **kwargs):
+        """Close the cover."""
+        self.apply_action("close")
+
+    def stop_cover(self, **kwargs):
+        """Stop the cover."""
+        if (
+            self.tahoma_device.type
+            == "io:RollerShutterWithLowSpeedManagementIOComponent"
+        ):
+            self.apply_action("setPosition", "secured")
+        elif self.tahoma_device.type in (
+            "rts:BlindRTSComponent",
+            "io:ExteriorVenetianBlindIOComponent",
+            "rts:VenetianBlindRTSComponent",
+            "rts:DualCurtainRTSComponent",
+            "rts:ExteriorVenetianBlindRTSComponent",
+            "rts:BlindRTSComponent",
+        ):
+            self.apply_action("my")
+        elif self.tahoma_device.type in (
+            HORIZONTAL_AWNING,
+            "io:RollerShutterGenericIOComponent",
+            "io:VerticalExteriorAwningIOComponent",
+            "io:VerticalInteriorBlindVeluxIOComponent",
+            "io:WindowOpenerVeluxIOComponent",
+        ):
+            self.apply_action("stop")
+        else:
+            self.apply_action("stopIdentify")
diff --git a/homeassistant/components/tahoma/manifest.json b/homeassistant/components/tahoma/manifest.json
new file mode 100644
index 0000000000000000000000000000000000000000..1e99d4b288d725f81b7b5ccc0ebaaa4a2822b91d
--- /dev/null
+++ b/homeassistant/components/tahoma/manifest.json
@@ -0,0 +1,12 @@
+{
+  "domain": "tahoma",
+  "name": "Tahoma",
+  "documentation": "https://www.home-assistant.io/integrations/tahoma",
+  "requirements": [
+    "tahoma-api==0.0.14"
+  ],
+  "dependencies": [],
+  "codeowners": [
+    "@philklei"
+  ]
+}
diff --git a/homeassistant/components/tahoma/scene.py b/homeassistant/components/tahoma/scene.py
new file mode 100644
index 0000000000000000000000000000000000000000..e54ff91a0f6e908f1dc1c82d6f2b05b8e8a3a0cc
--- /dev/null
+++ b/homeassistant/components/tahoma/scene.py
@@ -0,0 +1,41 @@
+"""Support for Tahoma scenes."""
+import logging
+
+from homeassistant.components.scene import Scene
+
+from . import DOMAIN as TAHOMA_DOMAIN
+
+_LOGGER = logging.getLogger(__name__)
+
+
+def setup_platform(hass, config, add_entities, discovery_info=None):
+    """Set up the Tahoma scenes."""
+    controller = hass.data[TAHOMA_DOMAIN]["controller"]
+    scenes = []
+    for scene in hass.data[TAHOMA_DOMAIN]["scenes"]:
+        scenes.append(TahomaScene(scene, controller))
+    add_entities(scenes, True)
+
+
+class TahomaScene(Scene):
+    """Representation of a Tahoma scene entity."""
+
+    def __init__(self, tahoma_scene, controller):
+        """Initialize the scene."""
+        self.tahoma_scene = tahoma_scene
+        self.controller = controller
+        self._name = self.tahoma_scene.name
+
+    def activate(self):
+        """Activate the scene."""
+        self.controller.launch_action_group(self.tahoma_scene.oid)
+
+    @property
+    def name(self):
+        """Return the name of the scene."""
+        return self._name
+
+    @property
+    def device_state_attributes(self):
+        """Return the state attributes of the scene."""
+        return {"tahoma_scene_oid": self.tahoma_scene.oid}
diff --git a/homeassistant/components/tahoma/sensor.py b/homeassistant/components/tahoma/sensor.py
new file mode 100644
index 0000000000000000000000000000000000000000..5279b160d9c5405d63831c7524efd832b4811622
--- /dev/null
+++ b/homeassistant/components/tahoma/sensor.py
@@ -0,0 +1,106 @@
+"""Support for Tahoma sensors."""
+from datetime import timedelta
+import logging
+
+from homeassistant.const import ATTR_BATTERY_LEVEL
+from homeassistant.helpers.entity import Entity
+
+from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice
+
+_LOGGER = logging.getLogger(__name__)
+
+SCAN_INTERVAL = timedelta(seconds=60)
+
+ATTR_RSSI_LEVEL = "rssi_level"
+
+
+def setup_platform(hass, config, add_entities, discovery_info=None):
+    """Set up Tahoma controller devices."""
+    controller = hass.data[TAHOMA_DOMAIN]["controller"]
+    devices = []
+    for device in hass.data[TAHOMA_DOMAIN]["devices"]["sensor"]:
+        devices.append(TahomaSensor(device, controller))
+    add_entities(devices, True)
+
+
+class TahomaSensor(TahomaDevice, Entity):
+    """Representation of a Tahoma Sensor."""
+
+    def __init__(self, tahoma_device, controller):
+        """Initialize the sensor."""
+        self.current_value = None
+        self._available = False
+        super().__init__(tahoma_device, controller)
+
+    @property
+    def state(self):
+        """Return the name of the sensor."""
+        return self.current_value
+
+    @property
+    def unit_of_measurement(self):
+        """Return the unit of measurement of this entity, if any."""
+        if self.tahoma_device.type == "Temperature Sensor":
+            return None
+        if self.tahoma_device.type == "io:SomfyContactIOSystemSensor":
+            return None
+        if self.tahoma_device.type == "io:SomfyBasicContactIOSystemSensor":
+            return None
+        if self.tahoma_device.type == "io:LightIOSystemSensor":
+            return "lx"
+        if self.tahoma_device.type == "Humidity Sensor":
+            return "%"
+        if self.tahoma_device.type == "rtds:RTDSContactSensor":
+            return None
+        if self.tahoma_device.type == "rtds:RTDSMotionSensor":
+            return None
+
+    def update(self):
+        """Update the state."""
+        self.controller.get_states([self.tahoma_device])
+        if self.tahoma_device.type == "io:LightIOSystemSensor":
+            self.current_value = self.tahoma_device.active_states["core:LuminanceState"]
+            self._available = bool(
+                self.tahoma_device.active_states.get("core:StatusState") == "available"
+            )
+        if self.tahoma_device.type == "io:SomfyContactIOSystemSensor":
+            self.current_value = self.tahoma_device.active_states["core:ContactState"]
+            self._available = bool(
+                self.tahoma_device.active_states.get("core:StatusState") == "available"
+            )
+        if self.tahoma_device.type == "io:SomfyBasicContactIOSystemSensor":
+            self.current_value = self.tahoma_device.active_states["core:ContactState"]
+            self._available = bool(
+                self.tahoma_device.active_states.get("core:StatusState") == "available"
+            )
+        if self.tahoma_device.type == "rtds:RTDSContactSensor":
+            self.current_value = self.tahoma_device.active_states["core:ContactState"]
+            self._available = True
+        if self.tahoma_device.type == "rtds:RTDSMotionSensor":
+            self.current_value = self.tahoma_device.active_states["core:OccupancyState"]
+            self._available = True
+
+        _LOGGER.debug("Update %s, value: %d", self._name, self.current_value)
+
+    @property
+    def device_state_attributes(self):
+        """Return the device state attributes."""
+        attr = {}
+        super_attr = super().device_state_attributes
+        if super_attr is not None:
+            attr.update(super_attr)
+
+        if "core:RSSILevelState" in self.tahoma_device.active_states:
+            attr[ATTR_RSSI_LEVEL] = self.tahoma_device.active_states[
+                "core:RSSILevelState"
+            ]
+        if "core:SensorDefectState" in self.tahoma_device.active_states:
+            attr[ATTR_BATTERY_LEVEL] = self.tahoma_device.active_states[
+                "core:SensorDefectState"
+            ]
+        return attr
+
+    @property
+    def available(self):
+        """Return True if entity is available."""
+        return self._available
diff --git a/homeassistant/components/tahoma/switch.py b/homeassistant/components/tahoma/switch.py
new file mode 100644
index 0000000000000000000000000000000000000000..a0a95ab47ce05632137c576282ce87ea9cf6b06b
--- /dev/null
+++ b/homeassistant/components/tahoma/switch.py
@@ -0,0 +1,109 @@
+"""Support for Tahoma switches."""
+import logging
+
+from homeassistant.components.switch import SwitchDevice
+from homeassistant.const import STATE_OFF, STATE_ON
+
+from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice
+
+_LOGGER = logging.getLogger(__name__)
+
+ATTR_RSSI_LEVEL = "rssi_level"
+
+
+def setup_platform(hass, config, add_entities, discovery_info=None):
+    """Set up Tahoma switches."""
+    controller = hass.data[TAHOMA_DOMAIN]["controller"]
+    devices = []
+    for switch in hass.data[TAHOMA_DOMAIN]["devices"]["switch"]:
+        devices.append(TahomaSwitch(switch, controller))
+    add_entities(devices, True)
+
+
+class TahomaSwitch(TahomaDevice, SwitchDevice):
+    """Representation a Tahoma Switch."""
+
+    def __init__(self, tahoma_device, controller):
+        """Initialize the switch."""
+        super().__init__(tahoma_device, controller)
+        self._state = STATE_OFF
+        self._skip_update = False
+        self._available = False
+
+    def update(self):
+        """Update method."""
+        # Postpone the immediate state check for changes that take time.
+        if self._skip_update:
+            self._skip_update = False
+            return
+
+        self.controller.get_states([self.tahoma_device])
+
+        if self.tahoma_device.type == "io:OnOffLightIOComponent":
+            if self.tahoma_device.active_states.get("core:OnOffState") == "on":
+                self._state = STATE_ON
+            else:
+                self._state = STATE_OFF
+
+        self._available = bool(
+            self.tahoma_device.active_states.get("core:StatusState") == "available"
+        )
+
+        _LOGGER.debug("Update %s, state: %s", self._name, self._state)
+
+    @property
+    def device_class(self):
+        """Return the class of the device."""
+        if self.tahoma_device.type == "rts:GarageDoor4TRTSComponent":
+            return "garage"
+        return None
+
+    def turn_on(self, **kwargs):
+        """Send the on command."""
+        _LOGGER.debug("Turn on: %s", self._name)
+        if self.tahoma_device.type == "rts:GarageDoor4TRTSComponent":
+            self.toggle()
+        else:
+            self.apply_action("on")
+            self._skip_update = True
+            self._state = STATE_ON
+
+    def turn_off(self, **kwargs):
+        """Send the off command."""
+        _LOGGER.debug("Turn off: %s", self._name)
+        if self.tahoma_device.type == "rts:GarageDoor4TRTSComponent":
+            return
+
+        self.apply_action("off")
+        self._skip_update = True
+        self._state = STATE_OFF
+
+    def toggle(self, **kwargs):
+        """Click the switch."""
+        self.apply_action("cycle")
+
+    @property
+    def is_on(self):
+        """Get whether the switch is in on state."""
+        if self.tahoma_device.type == "rts:GarageDoor4TRTSComponent":
+            return False
+        return bool(self._state == STATE_ON)
+
+    @property
+    def device_state_attributes(self):
+        """Return the device state attributes."""
+        attr = {}
+        super_attr = super().device_state_attributes
+        if super_attr is not None:
+            attr.update(super_attr)
+
+        if "core:RSSILevelState" in self.tahoma_device.active_states:
+            attr[ATTR_RSSI_LEVEL] = self.tahoma_device.active_states[
+                "core:RSSILevelState"
+            ]
+        return attr
+
+    @property
+    def available(self):
+        """Return True if entity is available."""
+        return self._available
diff --git a/requirements_all.txt b/requirements_all.txt
index 5f6e9ac78aab2c908385189c26320898e6dda3d8..c91ffeace3f6291b2bb3e16113ba3b3aee299260 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -1906,6 +1906,9 @@ swisshydrodata==0.0.3
 # homeassistant.components.synology_srm
 synology-srm==0.0.7
 
+# homeassistant.components.tahoma
+tahoma-api==0.0.14
+
 # homeassistant.components.tank_utility
 tank_utility==1.4.0