diff --git a/.coveragerc b/.coveragerc
index 50bf08b0279482df55b5d9b6dc5870385bb9ab32..820c53d81eed237b90792b40f03b42abfab85ae7 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -132,6 +132,9 @@ omit =
     homeassistant/components/zabbix.py
     homeassistant/components/*/zabbix.py
 
+    homeassistant/components/maxcube.py
+    homeassistant/components/*/maxcube.py
+
     homeassistant/components/alarm_control_panel/alarmdotcom.py
     homeassistant/components/alarm_control_panel/concord232.py
     homeassistant/components/alarm_control_panel/nx584.py
diff --git a/homeassistant/components/binary_sensor/maxcube.py b/homeassistant/components/binary_sensor/maxcube.py
new file mode 100644
index 0000000000000000000000000000000000000000..77448fd6adc391e6f44ab94ddfc1931511ed1793
--- /dev/null
+++ b/homeassistant/components/binary_sensor/maxcube.py
@@ -0,0 +1,76 @@
+"""
+Support for MAX! Window Shutter via MAX! Cube.
+
+For more details about this platform, please refer to the documentation
+https://home-assistant.io/components/maxcube/
+"""
+
+import logging
+
+from homeassistant.components.binary_sensor import BinarySensorDevice
+from homeassistant.components.maxcube import MAXCUBE_HANDLE
+from homeassistant.const import STATE_UNKNOWN
+
+_LOGGER = logging.getLogger(__name__)
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Iterate through all MAX! Devices and add window shutters to HASS."""
+    cube = hass.data[MAXCUBE_HANDLE].cube
+
+    # List of devices
+    devices = []
+
+    for device in cube.devices:
+        # Create device name by concatenating room name + device name
+        name = "%s %s" % (cube.room_by_id(device.room_id).name, device.name)
+
+        # Only add Window Shutters
+        if cube.is_windowshutter(device):
+            # add device to HASS
+            devices.append(MaxCubeShutter(hass, name, device.rf_address))
+
+    if len(devices) > 0:
+        add_devices(devices)
+
+
+class MaxCubeShutter(BinarySensorDevice):
+    """MAX! Cube BinarySensor device."""
+
+    def __init__(self, hass, name, rf_address):
+        """Initialize MAX! Cube BinarySensorDevice."""
+        self._name = name
+        self._sensor_type = 'opening'
+        self._rf_address = rf_address
+        self._cubehandle = hass.data[MAXCUBE_HANDLE]
+        self._state = STATE_UNKNOWN
+
+    @property
+    def should_poll(self):
+        """Polling is required."""
+        return True
+
+    @property
+    def name(self):
+        """Return the name of the BinarySensorDevice."""
+        return self._name
+
+    @property
+    def device_class(self):
+        """Return the class of this sensor."""
+        return self._sensor_type
+
+    @property
+    def is_on(self):
+        """Return true if the binary sensor is on/open."""
+        return self._state
+
+    def update(self):
+        """Get latest data from MAX! Cube."""
+        self._cubehandle.update()
+
+        # Get the device we want to update
+        device = self._cubehandle.cube.device_by_rf(self._rf_address)
+
+        # Update our internal state
+        self._state = device.is_open
diff --git a/homeassistant/components/climate/maxcube.py b/homeassistant/components/climate/maxcube.py
new file mode 100644
index 0000000000000000000000000000000000000000..a04a547f534e0be3fb7ccddc8edbc0ea8c278f8e
--- /dev/null
+++ b/homeassistant/components/climate/maxcube.py
@@ -0,0 +1,216 @@
+"""
+Support for MAX! Thermostats via MAX! Cube.
+
+For more details about this platform, please refer to the documentation
+https://home-assistant.io/components/maxcube/
+"""
+
+import socket
+import logging
+
+from homeassistant.components.climate import ClimateDevice, STATE_AUTO
+from homeassistant.components.maxcube import MAXCUBE_HANDLE
+from homeassistant.const import TEMP_CELSIUS, ATTR_TEMPERATURE
+from homeassistant.const import STATE_UNKNOWN
+
+_LOGGER = logging.getLogger(__name__)
+
+STATE_MANUAL = "manual"
+STATE_BOOST = "boost"
+STATE_VACATION = "vacation"
+
+
+def setup_platform(hass, config, add_devices, discovery_info=None):
+    """Iterate through all MAX! Devices and add thermostats to HASS."""
+    cube = hass.data[MAXCUBE_HANDLE].cube
+
+    # List of devices
+    devices = []
+
+    for device in cube.devices:
+        # Create device name by concatenating room name + device name
+        name = "%s %s" % (cube.room_by_id(device.room_id).name, device.name)
+
+        # Only add thermostats and wallthermostats
+        if cube.is_thermostat(device) or cube.is_wallthermostat(device):
+            # Add device to HASS
+            devices.append(MaxCubeClimate(hass, name, device.rf_address))
+
+    # Add all devices at once
+    if len(devices) > 0:
+        add_devices(devices)
+
+
+class MaxCubeClimate(ClimateDevice):
+    """MAX! Cube ClimateDevice."""
+
+    def __init__(self, hass, name, rf_address):
+        """Initialize MAX! Cube ClimateDevice."""
+        self._name = name
+        self._unit_of_measurement = TEMP_CELSIUS
+        self._operation_list = [STATE_AUTO, STATE_MANUAL, STATE_BOOST,
+                                STATE_VACATION]
+        self._rf_address = rf_address
+        self._cubehandle = hass.data[MAXCUBE_HANDLE]
+
+    @property
+    def should_poll(self):
+        """Polling is required."""
+        return True
+
+    @property
+    def name(self):
+        """Return the name of the ClimateDevice."""
+        return self._name
+
+    @property
+    def min_temp(self):
+        """Return the minimum temperature."""
+        # Get the device we want (does not do any IO, just reads from memory)
+        device = self._cubehandle.cube.device_by_rf(self._rf_address)
+
+        # Map and return minimum temperature
+        return self.map_temperature_max_hass(device.min_temperature)
+
+    @property
+    def max_temp(self):
+        """Return the maximum temperature."""
+        # Get the device we want (does not do any IO, just reads from memory)
+        device = self._cubehandle.cube.device_by_rf(self._rf_address)
+
+        # Map and return maximum temperature
+        return self.map_temperature_max_hass(device.max_temperature)
+
+    @property
+    def temperature_unit(self):
+        """Return the unit of measurement."""
+        return self._unit_of_measurement
+
+    @property
+    def current_temperature(self):
+        """Return the current temperature."""
+        # Get the device we want (does not do any IO, just reads from memory)
+        device = self._cubehandle.cube.device_by_rf(self._rf_address)
+
+        # Map and return current temperature
+        return self.map_temperature_max_hass(device.actual_temperature)
+
+    @property
+    def current_operation(self):
+        """Return current operation (auto, manual, boost, vacation)."""
+        # Get the device we want (does not do any IO, just reads from memory)
+        device = self._cubehandle.cube.device_by_rf(self._rf_address)
+
+        # Mode Mapping
+        return self.map_mode_max_hass(device.mode)
+
+    @property
+    def operation_list(self):
+        """List of available operation modes."""
+        return self._operation_list
+
+    @property
+    def target_temperature(self):
+        """Return the temperature we try to reach."""
+        # Get the device we want (does not do any IO, just reads from memory)
+        device = self._cubehandle.cube.device_by_rf(self._rf_address)
+
+        # Map and return target temperature
+        return self.map_temperature_max_hass(device.target_temperature)
+
+    def set_temperature(self, **kwargs):
+        """Set new target temperatures."""
+        # Fail is target temperature has not been supplied as argument
+        if kwargs.get(ATTR_TEMPERATURE) is None:
+            return False
+
+        # Determine the new target temperature
+        target_temperature = kwargs.get(ATTR_TEMPERATURE)
+
+        # Write the target temperature to the MAX! Cube.
+        device = self._cubehandle.cube.device_by_rf(self._rf_address)
+
+        cube = self._cubehandle.cube
+
+        with self._cubehandle.mutex:
+            try:
+                cube.set_target_temperature(device, target_temperature)
+            except (socket.timeout, socket.error):
+                _LOGGER.error("Setting target temperature failed")
+                return False
+
+    def set_operation_mode(self, operation_mode):
+        """Set new operation mode."""
+        # Get the device we want to update
+        device = self._cubehandle.cube.device_by_rf(self._rf_address)
+
+        # Mode Mapping
+        mode = self.map_mode_hass_max(operation_mode)
+
+        # Write new mode to thermostat
+        if mode is None:
+            return False
+
+        with self._cubehandle.mutex:
+            try:
+                self._cubehandle.cube.set_mode(device, mode)
+            except (socket.timeout, socket.error):
+                _LOGGER.error("Setting operation mode failed")
+                return False
+
+    def update(self):
+        """Get latest data from MAX! Cube."""
+        # Update the CubeHandle
+        self._cubehandle.update()
+
+    @staticmethod
+    def map_temperature_max_hass(temperature):
+        """Map Temperature from MAX! to HASS."""
+        if temperature is None:
+            return STATE_UNKNOWN
+
+        return temperature
+
+    @staticmethod
+    def map_mode_hass_max(operation_mode):
+        """Map HASS Operation Modes to MAX! Operation Modes."""
+        from maxcube.device import \
+            MAX_DEVICE_MODE_AUTOMATIC, \
+            MAX_DEVICE_MODE_MANUAL, \
+            MAX_DEVICE_MODE_VACATION, \
+            MAX_DEVICE_MODE_BOOST
+
+        if operation_mode == STATE_AUTO:
+            mode = MAX_DEVICE_MODE_AUTOMATIC
+        elif operation_mode == STATE_MANUAL:
+            mode = MAX_DEVICE_MODE_MANUAL
+        elif operation_mode == STATE_VACATION:
+            mode = MAX_DEVICE_MODE_VACATION
+        elif operation_mode == STATE_BOOST:
+            mode = MAX_DEVICE_MODE_BOOST
+        else:
+            mode = None
+
+        return mode
+
+    @staticmethod
+    def map_mode_max_hass(mode):
+        """Map MAX! Operation Modes to HASS Operation Modes."""
+        from maxcube.device import \
+            MAX_DEVICE_MODE_AUTOMATIC, \
+            MAX_DEVICE_MODE_MANUAL, \
+            MAX_DEVICE_MODE_VACATION, \
+            MAX_DEVICE_MODE_BOOST
+
+        if mode == MAX_DEVICE_MODE_AUTOMATIC:
+            operation_mode = STATE_AUTO
+        elif mode == MAX_DEVICE_MODE_MANUAL:
+            operation_mode = STATE_MANUAL
+        elif mode == MAX_DEVICE_MODE_VACATION:
+            operation_mode = STATE_VACATION
+        elif mode == MAX_DEVICE_MODE_BOOST:
+            operation_mode = STATE_BOOST
+        else:
+            operation_mode = None
+
+        return operation_mode
diff --git a/homeassistant/components/maxcube.py b/homeassistant/components/maxcube.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc201825e83e0faca2399716972e2cc2497ee667
--- /dev/null
+++ b/homeassistant/components/maxcube.py
@@ -0,0 +1,94 @@
+"""
+Platform for the MAX! Cube LAN Gateway.
+
+For more details about this component, please refer to the documentation
+https://home-assistant.io/components/maxcube/
+"""
+
+from socket import timeout
+import logging
+import time
+from threading import Lock
+
+from homeassistant.components.discovery import load_platform
+from homeassistant.const import CONF_HOST, CONF_PORT
+import homeassistant.helpers.config_validation as cv
+import voluptuous as vol
+
+REQUIREMENTS = ['maxcube-api==0.1.0']
+
+_LOGGER = logging.getLogger(__name__)
+
+DOMAIN = 'maxcube'
+MAXCUBE_HANDLE = 'maxcube'
+
+DEFAULT_PORT = 62910
+
+CONFIG_SCHEMA = vol.Schema({
+    DOMAIN: vol.Schema({
+        vol.Required(CONF_HOST): cv.string,
+        vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
+    }),
+}, extra=vol.ALLOW_EXTRA)
+
+
+def setup(hass, config):
+    """Establish connection to MAX! Cube."""
+    from maxcube.connection import MaxCubeConnection
+    from maxcube.cube import MaxCube
+
+    # Read Config
+    host = config.get(DOMAIN).get(CONF_HOST)
+    port = config.get(DOMAIN).get(CONF_PORT)
+
+    # Assign Cube Handle to global variable
+    try:
+        cube = MaxCube(MaxCubeConnection(host, port))
+    except timeout:
+        _LOGGER.error("Connection to Max!Cube could not be established")
+        cube = None
+        return False
+
+    hass.data[MAXCUBE_HANDLE] = MaxCubeHandle(cube)
+
+    # Load Climate (for Thermostats)
+    load_platform(hass, 'climate', DOMAIN)
+
+    # Load BinarySensor (for Window Shutter)
+    load_platform(hass, 'binary_sensor', DOMAIN)
+
+    # Initialization successfull
+    return True
+
+
+class MaxCubeHandle(object):
+    """Keep the cube instance in one place and centralize the update."""
+
+    def __init__(self, cube):
+        """Initialize the Cube Handle."""
+        # Cube handle
+        self.cube = cube
+
+        # Instantiate Mutex
+        self.mutex = Lock()
+
+        # Update Timestamp
+        self._updatets = time.time()
+
+    def update(self):
+        """Pull the latest data from the MAX! Cube."""
+        # Acquire mutex to prevent simultaneous update from multiple threads
+        with self.mutex:
+            # Only update every 60s
+            if (time.time() - self._updatets) >= 60:
+                _LOGGER.debug("UPDATE: Updating")
+
+                try:
+                    self.cube.update()
+                except timeout:
+                    _LOGGER.error("Max!Cube connection failed")
+                    return False
+
+                self._updatets = time.time()
+            else:
+                _LOGGER.debug("UPDATE: Skipping")
diff --git a/requirements_all.txt b/requirements_all.txt
index 9f8ba5d2bcc32361a712ace17fe049f0bcb95e8e..2fe7c9046fafb6b7b20fbfd09cf94ea95e92bf90 100755
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -336,6 +336,9 @@ liveboxplaytv==1.4.9
 # homeassistant.components.notify.matrix
 matrix-client==0.0.5
 
+# homeassistant.components.maxcube
+maxcube-api==0.1.0
+
 # homeassistant.components.notify.message_bird
 messagebird==1.2.0