diff --git a/homeassistant/components/climate/mqtt.py b/homeassistant/components/climate/mqtt.py
new file mode 100644
index 0000000000000000000000000000000000000000..2f7bba74185c13ee9b5de6f4a82bcf999a3f12cb
--- /dev/null
+++ b/homeassistant/components/climate/mqtt.py
@@ -0,0 +1,483 @@
+"""
+Support for MQTT climate devices.
+
+For more details about this platform, please refer to the documentation
+https://home-assistant.io/components/climate.mqtt/
+"""
+import asyncio
+import logging
+
+import voluptuous as vol
+
+from homeassistant.core import callback
+import homeassistant.components.mqtt as mqtt
+
+from homeassistant.components.climate import (
+    STATE_HEAT, STATE_COOL, STATE_DRY, STATE_FAN_ONLY, ClimateDevice,
+    PLATFORM_SCHEMA as CLIMATE_PLATFORM_SCHEMA, STATE_AUTO,
+    ATTR_OPERATION_MODE)
+from homeassistant.const import (
+    STATE_ON, STATE_OFF, ATTR_TEMPERATURE, CONF_NAME)
+from homeassistant.components.mqtt import (CONF_QOS, CONF_RETAIN)
+import homeassistant.helpers.config_validation as cv
+from homeassistant.components.fan import (SPEED_LOW, SPEED_MEDIUM,
+                                          SPEED_HIGH)
+
+_LOGGER = logging.getLogger(__name__)
+
+DEPENDENCIES = ['mqtt']
+
+DEFAULT_NAME = 'MQTT HVAC'
+
+CONF_POWER_COMMAND_TOPIC = 'power_command_topic'
+CONF_POWER_STATE_TOPIC = 'power_state_topic'
+CONF_MODE_COMMAND_TOPIC = 'mode_command_topic'
+CONF_MODE_STATE_TOPIC = 'mode_state_topic'
+CONF_TEMPERATURE_COMMAND_TOPIC = 'temperature_command_topic'
+CONF_TEMPERATURE_STATE_TOPIC = 'temperature_state_topic'
+CONF_FAN_MODE_COMMAND_TOPIC = 'fan_mode_command_topic'
+CONF_FAN_MODE_STATE_TOPIC = 'fan_mode_state_topic'
+CONF_SWING_MODE_COMMAND_TOPIC = 'swing_mode_command_topic'
+CONF_SWING_MODE_STATE_TOPIC = 'swing_mode_state_topic'
+CONF_AWAY_MODE_COMMAND_TOPIC = 'away_mode_command_topic'
+CONF_AWAY_MODE_STATE_TOPIC = 'away_mode_state_topic'
+CONF_HOLD_COMMAND_TOPIC = 'hold_command_topic'
+CONF_HOLD_STATE_TOPIC = 'hold_state_topic'
+CONF_AUX_COMMAND_TOPIC = 'aux_command_topic'
+CONF_AUX_STATE_TOPIC = 'aux_state_topic'
+
+CONF_CURRENT_TEMPERATURE_TOPIC = 'current_temperature_topic'
+
+CONF_PAYLOAD_ON = 'payload_on'
+CONF_PAYLOAD_OFF = 'payload_off'
+
+CONF_FAN_MODE_LIST = 'fan_modes'
+CONF_MODE_LIST = 'modes'
+CONF_SWING_MODE_LIST = 'swing_modes'
+CONF_INITIAL = 'initial'
+CONF_SEND_IF_OFF = 'send_if_off'
+
+PLATFORM_SCHEMA = CLIMATE_PLATFORM_SCHEMA.extend({
+    vol.Optional(CONF_POWER_COMMAND_TOPIC): mqtt.valid_publish_topic,
+    vol.Optional(CONF_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
+    vol.Optional(CONF_TEMPERATURE_COMMAND_TOPIC): mqtt.valid_publish_topic,
+    vol.Optional(CONF_FAN_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
+    vol.Optional(CONF_SWING_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
+    vol.Optional(CONF_AWAY_MODE_COMMAND_TOPIC): mqtt.valid_publish_topic,
+    vol.Optional(CONF_HOLD_COMMAND_TOPIC): mqtt.valid_publish_topic,
+    vol.Optional(CONF_AUX_COMMAND_TOPIC): mqtt.valid_publish_topic,
+    vol.Optional(CONF_POWER_STATE_TOPIC): mqtt.valid_subscribe_topic,
+    vol.Optional(CONF_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
+    vol.Optional(CONF_TEMPERATURE_STATE_TOPIC): mqtt.valid_subscribe_topic,
+    vol.Optional(CONF_FAN_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
+    vol.Optional(CONF_SWING_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
+    vol.Optional(CONF_AWAY_MODE_STATE_TOPIC): mqtt.valid_subscribe_topic,
+    vol.Optional(CONF_HOLD_STATE_TOPIC): mqtt.valid_subscribe_topic,
+    vol.Optional(CONF_AUX_STATE_TOPIC): mqtt.valid_subscribe_topic,
+    vol.Optional(CONF_CURRENT_TEMPERATURE_TOPIC):
+        mqtt.valid_subscribe_topic,
+    vol.Optional(CONF_FAN_MODE_LIST,
+                 default=[STATE_AUTO, SPEED_LOW,
+                          SPEED_MEDIUM, SPEED_HIGH]): cv.ensure_list,
+    vol.Optional(CONF_SWING_MODE_LIST,
+                 default=[STATE_ON, STATE_OFF]): cv.ensure_list,
+    vol.Optional(CONF_MODE_LIST,
+                 default=[STATE_AUTO, STATE_OFF, STATE_COOL, STATE_HEAT,
+                          STATE_DRY, STATE_FAN_ONLY]): cv.ensure_list,
+    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
+    vol.Optional(CONF_INITIAL, default=21): cv.positive_int,
+    vol.Optional(CONF_SEND_IF_OFF, default=True): cv.boolean,
+    vol.Optional(CONF_PAYLOAD_ON, default="ON"): cv.string,
+    vol.Optional(CONF_PAYLOAD_OFF, default="OFF"): cv.string,
+})
+
+
+@asyncio.coroutine
+def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
+    """Set up the MQTT climate devices."""
+    async_add_devices([
+        MqttClimate(
+            hass,
+            config.get(CONF_NAME),
+            {
+                key: config.get(key) for key in (
+                    CONF_POWER_COMMAND_TOPIC,
+                    CONF_MODE_COMMAND_TOPIC,
+                    CONF_TEMPERATURE_COMMAND_TOPIC,
+                    CONF_FAN_MODE_COMMAND_TOPIC,
+                    CONF_SWING_MODE_COMMAND_TOPIC,
+                    CONF_AWAY_MODE_COMMAND_TOPIC,
+                    CONF_HOLD_COMMAND_TOPIC,
+                    CONF_AUX_COMMAND_TOPIC,
+                    CONF_POWER_STATE_TOPIC,
+                    CONF_MODE_STATE_TOPIC,
+                    CONF_TEMPERATURE_STATE_TOPIC,
+                    CONF_FAN_MODE_STATE_TOPIC,
+                    CONF_SWING_MODE_STATE_TOPIC,
+                    CONF_AWAY_MODE_STATE_TOPIC,
+                    CONF_HOLD_STATE_TOPIC,
+                    CONF_AUX_STATE_TOPIC,
+                    CONF_CURRENT_TEMPERATURE_TOPIC
+                )
+            },
+            config.get(CONF_QOS),
+            config.get(CONF_RETAIN),
+            config.get(CONF_MODE_LIST),
+            config.get(CONF_FAN_MODE_LIST),
+            config.get(CONF_SWING_MODE_LIST),
+            config.get(CONF_INITIAL),
+            False, None, SPEED_LOW,
+            STATE_OFF, STATE_OFF, False,
+            config.get(CONF_SEND_IF_OFF),
+            config.get(CONF_PAYLOAD_ON),
+            config.get(CONF_PAYLOAD_OFF))
+    ])
+
+
+class MqttClimate(ClimateDevice):
+    """Representation of a demo climate device."""
+
+    def __init__(self, hass, name, topic, qos, retain, mode_list,
+                 fan_mode_list, swing_mode_list, target_temperature, away,
+                 hold, current_fan_mode, current_swing_mode,
+                 current_operation, aux, send_if_off, payload_on,
+                 payload_off):
+        """Initialize the climate device."""
+        self.hass = hass
+        self._name = name
+        self._topic = topic
+        self._qos = qos
+        self._retain = retain
+        self._target_temperature = target_temperature
+        self._unit_of_measurement = hass.config.units.temperature_unit
+        self._away = away
+        self._hold = hold
+        self._current_temperature = None
+        self._current_fan_mode = current_fan_mode
+        self._current_operation = current_operation
+        self._aux = aux
+        self._current_swing_mode = current_swing_mode
+        self._fan_list = fan_mode_list
+        self._operation_list = mode_list
+        self._swing_list = swing_mode_list
+        self._target_temperature_step = 1
+        self._send_if_off = send_if_off
+        self._payload_on = payload_on
+        self._payload_off = payload_off
+
+    def async_added_to_hass(self):
+        """Handle being added to home assistant."""
+        @callback
+        def handle_current_temp_received(topic, payload, qos):
+            """Handle current temperature coming via MQTT."""
+            try:
+                self._current_temperature = float(payload)
+                self.async_schedule_update_ha_state()
+            except ValueError:
+                _LOGGER.error("Could not parse temperature from %s", payload)
+
+        if self._topic[CONF_CURRENT_TEMPERATURE_TOPIC] is not None:
+            yield from mqtt.async_subscribe(
+                self.hass, self._topic[CONF_CURRENT_TEMPERATURE_TOPIC],
+                handle_current_temp_received, self._qos)
+
+        @callback
+        def handle_mode_received(topic, payload, qos):
+            """Handle receiving mode via MQTT."""
+            if payload not in self._operation_list:
+                _LOGGER.error("Invalid mode: %s", payload)
+            else:
+                self._current_operation = payload
+                self.async_schedule_update_ha_state()
+
+        if self._topic[CONF_MODE_STATE_TOPIC] is not None:
+            yield from mqtt.async_subscribe(
+                self.hass, self._topic[CONF_MODE_STATE_TOPIC],
+                handle_mode_received, self._qos)
+
+        @callback
+        def handle_temperature_received(topic, payload, qos):
+            """Handle target temperature coming via MQTT."""
+            try:
+                self._target_temperature = float(payload)
+                self.async_schedule_update_ha_state()
+            except ValueError:
+                _LOGGER.error("Could not parse temperature from %s", payload)
+
+        if self._topic[CONF_TEMPERATURE_STATE_TOPIC] is not None:
+            yield from mqtt.async_subscribe(
+                self.hass, self._topic[CONF_TEMPERATURE_STATE_TOPIC],
+                handle_temperature_received, self._qos)
+
+        @callback
+        def handle_fan_mode_received(topic, payload, qos):
+            """Handle receiving fan mode via MQTT."""
+            if payload not in self._fan_list:
+                _LOGGER.error("Invalid fan mode: %s", payload)
+            else:
+                self._current_fan_mode = payload
+                self.async_schedule_update_ha_state()
+
+        if self._topic[CONF_FAN_MODE_STATE_TOPIC] is not None:
+            yield from mqtt.async_subscribe(
+                self.hass, self._topic[CONF_FAN_MODE_STATE_TOPIC],
+                handle_fan_mode_received, self._qos)
+
+        @callback
+        def handle_swing_mode_received(topic, payload, qos):
+            """Handle receiving swing mode via MQTT."""
+            if payload not in self._swing_list:
+                _LOGGER.error("Invalid swing mode: %s", payload)
+            else:
+                self._current_swing_mode = payload
+                self.async_schedule_update_ha_state()
+
+        if self._topic[CONF_SWING_MODE_STATE_TOPIC] is not None:
+            yield from mqtt.async_subscribe(
+                self.hass, self._topic[CONF_SWING_MODE_STATE_TOPIC],
+                handle_swing_mode_received, self._qos)
+
+        @callback
+        def handle_away_mode_received(topic, payload, qos):
+            """Handle receiving away mode via MQTT."""
+            if payload == self._payload_on:
+                self._away = True
+            elif payload == self._payload_off:
+                self._away = False
+            else:
+                _LOGGER.error("Invalid away mode: %s", payload)
+
+            self.async_schedule_update_ha_state()
+
+        if self._topic[CONF_AWAY_MODE_STATE_TOPIC] is not None:
+            yield from mqtt.async_subscribe(
+                self.hass, self._topic[CONF_AWAY_MODE_STATE_TOPIC],
+                handle_away_mode_received, self._qos)
+
+        @callback
+        def handle_aux_mode_received(topic, payload, qos):
+            """Handle receiving aux mode via MQTT."""
+            if payload == self._payload_on:
+                self._aux = True
+            elif payload == self._payload_off:
+                self._aux = False
+            else:
+                _LOGGER.error("Invalid aux mode: %s", payload)
+
+            self.async_schedule_update_ha_state()
+
+        if self._topic[CONF_AUX_STATE_TOPIC] is not None:
+            yield from mqtt.async_subscribe(
+                self.hass, self._topic[CONF_AUX_STATE_TOPIC],
+                handle_aux_mode_received, self._qos)
+
+        @callback
+        def handle_hold_mode_received(topic, payload, qos):
+            """Handle receiving hold mode via MQTT."""
+            self._hold = payload
+            self.async_schedule_update_ha_state()
+
+        if self._topic[CONF_HOLD_STATE_TOPIC] is not None:
+            yield from mqtt.async_subscribe(
+                self.hass, self._topic[CONF_HOLD_STATE_TOPIC],
+                handle_hold_mode_received, self._qos)
+
+    @property
+    def should_poll(self):
+        """Return the polling state."""
+        return False
+
+    @property
+    def name(self):
+        """Return the name of the climate device."""
+        return self._name
+
+    @property
+    def temperature_unit(self):
+        """Return the unit of measurement."""
+        return self._unit_of_measurement
+
+    @property
+    def current_temperature(self):
+        """Return the current temperature."""
+        return self._current_temperature
+
+    @property
+    def target_temperature(self):
+        """Return the temperature we try to reach."""
+        return self._target_temperature
+
+    @property
+    def current_operation(self):
+        """Return current operation ie. heat, cool, idle."""
+        return self._current_operation
+
+    @property
+    def operation_list(self):
+        """Return the list of available operation modes."""
+        return self._operation_list
+
+    @property
+    def target_temperature_step(self):
+        """Return the supported step of target temperature."""
+        return self._target_temperature_step
+
+    @property
+    def is_away_mode_on(self):
+        """Return if away mode is on."""
+        return self._away
+
+    @property
+    def current_hold_mode(self):
+        """Return hold mode setting."""
+        return self._hold
+
+    @property
+    def is_aux_heat_on(self):
+        """Return true if away mode is on."""
+        return self._aux
+
+    @property
+    def current_fan_mode(self):
+        """Return the fan setting."""
+        return self._current_fan_mode
+
+    @property
+    def fan_list(self):
+        """Return the list of available fan modes."""
+        return self._fan_list
+
+    @asyncio.coroutine
+    def async_set_temperature(self, **kwargs):
+        """Set new target temperatures."""
+        if kwargs.get(ATTR_OPERATION_MODE) is not None:
+            operation_mode = kwargs.get(ATTR_OPERATION_MODE)
+            yield from self.async_set_operation_mode(operation_mode)
+
+        if kwargs.get(ATTR_TEMPERATURE) is not None:
+            if self._topic[CONF_TEMPERATURE_STATE_TOPIC] is None:
+                # optimistic mode
+                self._target_temperature = kwargs.get(ATTR_TEMPERATURE)
+
+            if self._send_if_off or self._current_operation != STATE_OFF:
+                mqtt.async_publish(
+                    self.hass, self._topic[CONF_TEMPERATURE_COMMAND_TOPIC],
+                    kwargs.get(ATTR_TEMPERATURE), self._qos, self._retain)
+
+        self.async_schedule_update_ha_state()
+
+    @asyncio.coroutine
+    def async_set_swing_mode(self, swing_mode):
+        """Set new swing mode."""
+        if self._send_if_off or self._current_operation != STATE_OFF:
+            mqtt.async_publish(
+                self.hass, self._topic[CONF_SWING_MODE_COMMAND_TOPIC],
+                swing_mode, self._qos, self._retain)
+
+        if self._topic[CONF_SWING_MODE_STATE_TOPIC] is None:
+            self._current_swing_mode = swing_mode
+            self.async_schedule_update_ha_state()
+
+    @asyncio.coroutine
+    def async_set_fan_mode(self, fan):
+        """Set new target temperature."""
+        if self._send_if_off or self._current_operation != STATE_OFF:
+            mqtt.async_publish(
+                self.hass, self._topic[CONF_FAN_MODE_COMMAND_TOPIC],
+                fan, self._qos, self._retain)
+
+        if self._topic[CONF_FAN_MODE_STATE_TOPIC] is None:
+            self._current_fan_mode = fan
+            self.async_schedule_update_ha_state()
+
+    @asyncio.coroutine
+    def async_set_operation_mode(self, operation_mode) -> None:
+        """Set new operation mode."""
+        if self._topic[CONF_POWER_COMMAND_TOPIC] is not None:
+            if (self._current_operation == STATE_OFF and
+                    operation_mode != STATE_OFF):
+                mqtt.async_publish(
+                    self.hass, self._topic[CONF_POWER_COMMAND_TOPIC],
+                    self._payload_on, self._qos, self._retain)
+            elif (self._current_operation != STATE_OFF and
+                  operation_mode == STATE_OFF):
+                mqtt.async_publish(
+                    self.hass, self._topic[CONF_POWER_COMMAND_TOPIC],
+                    self._payload_off, self._qos, self._retain)
+
+        if self._topic[CONF_MODE_COMMAND_TOPIC] is not None:
+            mqtt.async_publish(
+                self.hass, self._topic[CONF_MODE_COMMAND_TOPIC],
+                operation_mode, self._qos, self._retain)
+
+        if self._topic[CONF_MODE_STATE_TOPIC] is None:
+            self._current_operation = operation_mode
+            self.async_schedule_update_ha_state()
+
+    @property
+    def current_swing_mode(self):
+        """Return the swing setting."""
+        return self._current_swing_mode
+
+    @property
+    def swing_list(self):
+        """List of available swing modes."""
+        return self._swing_list
+
+    @asyncio.coroutine
+    def async_turn_away_mode_on(self):
+        """Turn away mode on."""
+        if self._topic[CONF_AWAY_MODE_COMMAND_TOPIC] is not None:
+            mqtt.async_publish(self.hass,
+                               self._topic[CONF_AWAY_MODE_COMMAND_TOPIC],
+                               self._payload_on, self._qos, self._retain)
+
+        if self._topic[CONF_AWAY_MODE_STATE_TOPIC] is None:
+            self._away = True
+            self.async_schedule_update_ha_state()
+
+    @asyncio.coroutine
+    def async_turn_away_mode_off(self):
+        """Turn away mode off."""
+        if self._topic[CONF_AWAY_MODE_COMMAND_TOPIC] is not None:
+            mqtt.async_publish(self.hass,
+                               self._topic[CONF_AWAY_MODE_COMMAND_TOPIC],
+                               self._payload_off, self._qos, self._retain)
+
+        if self._topic[CONF_AWAY_MODE_STATE_TOPIC] is None:
+            self._away = False
+            self.async_schedule_update_ha_state()
+
+    @asyncio.coroutine
+    def async_set_hold_mode(self, hold):
+        """Update hold mode on."""
+        if self._topic[CONF_HOLD_COMMAND_TOPIC] is not None:
+            mqtt.async_publish(self.hass,
+                               self._topic[CONF_HOLD_COMMAND_TOPIC],
+                               hold, self._qos, self._retain)
+
+        if self._topic[CONF_HOLD_STATE_TOPIC] is None:
+            self._hold = hold
+            self.async_schedule_update_ha_state()
+
+    @asyncio.coroutine
+    def async_turn_aux_heat_on(self):
+        """Turn auxillary heater on."""
+        if self._topic[CONF_AUX_COMMAND_TOPIC] is not None:
+            mqtt.async_publish(self.hass, self._topic[CONF_AUX_COMMAND_TOPIC],
+                               self._payload_on, self._qos, self._retain)
+
+        if self._topic[CONF_AUX_STATE_TOPIC] is None:
+            self._aux = True
+            self.async_schedule_update_ha_state()
+
+    @asyncio.coroutine
+    def async_turn_aux_heat_off(self):
+        """Turn auxillary heater off."""
+        if self._topic[CONF_AUX_COMMAND_TOPIC] is not None:
+            mqtt.async_publish(self.hass, self._topic[CONF_AUX_COMMAND_TOPIC],
+                               self._payload_off, self._qos, self._retain)
+
+        if self._topic[CONF_AUX_STATE_TOPIC] is None:
+            self._aux = False
+            self.async_schedule_update_ha_state()
diff --git a/tests/components/climate/test_mqtt.py b/tests/components/climate/test_mqtt.py
new file mode 100644
index 0000000000000000000000000000000000000000..9b70138908d90c3f7000177203fc222ec8697bdd
--- /dev/null
+++ b/tests/components/climate/test_mqtt.py
@@ -0,0 +1,420 @@
+"""The tests for the mqtt climate component."""
+import unittest
+import copy
+
+from homeassistant.util.unit_system import (
+    METRIC_SYSTEM
+)
+from homeassistant.setup import setup_component
+from homeassistant.components import climate
+from homeassistant.const import STATE_OFF
+
+from tests.common import (get_test_home_assistant, mock_mqtt_component,
+                          fire_mqtt_message, mock_component)
+
+ENTITY_CLIMATE = 'climate.test'
+
+DEFAULT_CONFIG = {
+    'climate': {
+        'platform': 'mqtt',
+        'name': 'test',
+        'mode_command_topic': 'mode-topic',
+        'temperature_command_topic': 'temperature-topic',
+        'fan_mode_command_topic': 'fan-mode-topic',
+        'swing_mode_command_topic': 'swing-mode-topic',
+        'away_mode_command_topic': 'away-mode-topic',
+        'hold_command_topic': 'hold-topic',
+        'aux_command_topic': 'aux-topic'
+    }}
+
+
+class TestMQTTClimate(unittest.TestCase):
+    """Test the mqtt climate hvac."""
+
+    def setUp(self):  # pylint: disable=invalid-name
+        """Setup things to be run when tests are started."""
+        self.hass = get_test_home_assistant()
+        self.mock_publish = mock_mqtt_component(self.hass)
+        self.hass.config.units = METRIC_SYSTEM
+
+    def tearDown(self):  # pylint: disable=invalid-name
+        """Stop down everything that was started."""
+        self.hass.stop()
+
+    def test_setup_params(self):
+        """Test the initial parameters."""
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(21, state.attributes.get('temperature'))
+        self.assertEqual("low", state.attributes.get('fan_mode'))
+        self.assertEqual("off", state.attributes.get('swing_mode'))
+        self.assertEqual("off", state.attributes.get('operation_mode'))
+
+    def test_get_operation_modes(self):
+        """Test that the operation list returns the correct modes."""
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        modes = state.attributes.get('operation_list')
+        self.assertEqual([
+            climate.STATE_AUTO, STATE_OFF, climate.STATE_COOL,
+            climate.STATE_HEAT, climate.STATE_DRY, climate.STATE_FAN_ONLY
+        ], modes)
+
+    def test_set_operation_bad_attr_and_state(self):
+        """Test setting operation mode without required attribute.
+
+        Also check the state.
+        """
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("off", state.attributes.get('operation_mode'))
+        self.assertEqual("off", state.state)
+        climate.set_operation_mode(self.hass, None, ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("off", state.attributes.get('operation_mode'))
+        self.assertEqual("off", state.state)
+
+    def test_set_operation(self):
+        """Test setting of new operation mode."""
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("off", state.attributes.get('operation_mode'))
+        self.assertEqual("off", state.state)
+        climate.set_operation_mode(self.hass, "cool", ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("cool", state.attributes.get('operation_mode'))
+        self.assertEqual("cool", state.state)
+        self.assertEqual(('mode-topic', 'cool', 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+
+    def test_set_operation_pessimistic(self):
+        """Test setting operation mode in pessimistic mode."""
+        config = copy.deepcopy(DEFAULT_CONFIG)
+        config['climate']['mode_state_topic'] = 'mode-state'
+        assert setup_component(self.hass, climate.DOMAIN, config)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("off", state.attributes.get('operation_mode'))
+        self.assertEqual("off", state.state)
+
+        climate.set_operation_mode(self.hass, "cool", ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("off", state.attributes.get('operation_mode'))
+        self.assertEqual("off", state.state)
+
+        fire_mqtt_message(self.hass, 'mode-state', 'cool')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("cool", state.attributes.get('operation_mode'))
+        self.assertEqual("cool", state.state)
+
+        fire_mqtt_message(self.hass, 'mode-state', 'bogus mode')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("cool", state.attributes.get('operation_mode'))
+        self.assertEqual("cool", state.state)
+
+    def test_set_fan_mode_bad_attr(self):
+        """Test setting fan mode without required attribute."""
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("low", state.attributes.get('fan_mode'))
+        climate.set_fan_mode(self.hass, None, ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("low", state.attributes.get('fan_mode'))
+
+    def test_set_fan_mode_pessimistic(self):
+        """Test setting of new fan mode in pessimistic mode."""
+        config = copy.deepcopy(DEFAULT_CONFIG)
+        config['climate']['fan_mode_state_topic'] = 'fan-state'
+        assert setup_component(self.hass, climate.DOMAIN, config)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("low", state.attributes.get('fan_mode'))
+
+        climate.set_fan_mode(self.hass, 'high', ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("low", state.attributes.get('fan_mode'))
+
+        fire_mqtt_message(self.hass, 'fan-state', 'high')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('high', state.attributes.get('fan_mode'))
+
+        fire_mqtt_message(self.hass, 'fan-state', 'bogus mode')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('high', state.attributes.get('fan_mode'))
+
+    def test_set_fan_mode(self):
+        """Test setting of new fan mode."""
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("low", state.attributes.get('fan_mode'))
+        climate.set_fan_mode(self.hass, 'high', ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        self.assertEqual(('fan-mode-topic', 'high', 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('high', state.attributes.get('fan_mode'))
+
+    def test_set_swing_mode_bad_attr(self):
+        """Test setting swing mode without required attribute."""
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("off", state.attributes.get('swing_mode'))
+        climate.set_swing_mode(self.hass, None, ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("off", state.attributes.get('swing_mode'))
+
+    def test_set_swing_pessimistic(self):
+        """Test setting swing mode in pessimistic mode."""
+        config = copy.deepcopy(DEFAULT_CONFIG)
+        config['climate']['swing_mode_state_topic'] = 'swing-state'
+        assert setup_component(self.hass, climate.DOMAIN, config)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("off", state.attributes.get('swing_mode'))
+
+        climate.set_swing_mode(self.hass, 'on', ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("off", state.attributes.get('swing_mode'))
+
+        fire_mqtt_message(self.hass, 'swing-state', 'on')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("on", state.attributes.get('swing_mode'))
+
+        fire_mqtt_message(self.hass, 'swing-state', 'bogus state')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("on", state.attributes.get('swing_mode'))
+
+    def test_set_swing(self):
+        """Test setting of new swing mode."""
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("off", state.attributes.get('swing_mode'))
+        climate.set_swing_mode(self.hass, 'on', ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        self.assertEqual(('swing-mode-topic', 'on', 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual("on", state.attributes.get('swing_mode'))
+
+    def test_set_target_temperature(self):
+        """Test setting the target temperature."""
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(21, state.attributes.get('temperature'))
+        climate.set_operation_mode(self.hass, 'heat', ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        self.assertEqual(('mode-topic', 'heat', 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+        climate.set_temperature(self.hass, temperature=47,
+                                entity_id=ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(47, state.attributes.get('temperature'))
+        self.assertEqual(('temperature-topic', 47, 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+
+    def test_set_target_temperature_pessimistic(self):
+        """Test setting the target temperature."""
+        config = copy.deepcopy(DEFAULT_CONFIG)
+        config['climate']['temperature_state_topic'] = 'temperature-state'
+        assert setup_component(self.hass, climate.DOMAIN, config)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(21, state.attributes.get('temperature'))
+        climate.set_operation_mode(self.hass, 'heat', ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        climate.set_temperature(self.hass, temperature=47,
+                                entity_id=ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(21, state.attributes.get('temperature'))
+
+        fire_mqtt_message(self.hass, 'temperature-state', '1701')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(1701, state.attributes.get('temperature'))
+
+        fire_mqtt_message(self.hass, 'temperature-state', 'not a number')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(1701, state.attributes.get('temperature'))
+
+    def test_receive_mqtt_temperature(self):
+        """Test getting the current temperature via MQTT."""
+        config = copy.deepcopy(DEFAULT_CONFIG)
+        config['climate']['current_temperature_topic'] = 'current_temperature'
+        mock_component(self.hass, 'mqtt')
+        assert setup_component(self.hass, climate.DOMAIN, config)
+
+        fire_mqtt_message(self.hass, 'current_temperature', '47')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(47, state.attributes.get('current_temperature'))
+
+    def test_set_away_mode_pessimistic(self):
+        """Test setting of the away mode."""
+        config = copy.deepcopy(DEFAULT_CONFIG)
+        config['climate']['away_mode_state_topic'] = 'away-state'
+        assert setup_component(self.hass, climate.DOMAIN, config)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('away_mode'))
+
+        climate.set_away_mode(self.hass, True, ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('away_mode'))
+
+        fire_mqtt_message(self.hass, 'away-state', 'ON')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('on', state.attributes.get('away_mode'))
+
+        fire_mqtt_message(self.hass, 'away-state', 'OFF')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('away_mode'))
+
+        fire_mqtt_message(self.hass, 'away-state', 'nonsense')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('away_mode'))
+
+    def test_set_away_mode(self):
+        """Test setting of the away mode."""
+        config = copy.deepcopy(DEFAULT_CONFIG)
+        config['climate']['payload_on'] = 'AN'
+        config['climate']['payload_off'] = 'AUS'
+
+        assert setup_component(self.hass, climate.DOMAIN, config)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('away_mode'))
+        climate.set_away_mode(self.hass, True, ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        self.assertEqual(('away-mode-topic', 'AN', 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('on', state.attributes.get('away_mode'))
+
+        climate.set_away_mode(self.hass, False, ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        self.assertEqual(('away-mode-topic', 'AUS', 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('away_mode'))
+
+    def test_set_hold_pessimistic(self):
+        """Test setting the hold mode in pessimistic mode."""
+        config = copy.deepcopy(DEFAULT_CONFIG)
+        config['climate']['hold_state_topic'] = 'hold-state'
+        assert setup_component(self.hass, climate.DOMAIN, config)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(None, state.attributes.get('hold_mode'))
+
+        climate.set_hold_mode(self.hass, 'on', ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(None, state.attributes.get('hold_mode'))
+
+        fire_mqtt_message(self.hass, 'hold-state', 'on')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('on', state.attributes.get('hold_mode'))
+
+        fire_mqtt_message(self.hass, 'hold-state', 'off')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('hold_mode'))
+
+    def test_set_hold(self):
+        """Test setting the hold mode."""
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual(None, state.attributes.get('hold_mode'))
+        climate.set_hold_mode(self.hass, 'on', ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        self.assertEqual(('hold-topic', 'on', 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('on', state.attributes.get('hold_mode'))
+
+        climate.set_hold_mode(self.hass, 'off', ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        self.assertEqual(('hold-topic', 'off', 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('hold_mode'))
+
+    def test_set_aux_pessimistic(self):
+        """Test setting of the aux heating in pessimistic mode."""
+        config = copy.deepcopy(DEFAULT_CONFIG)
+        config['climate']['aux_state_topic'] = 'aux-state'
+        assert setup_component(self.hass, climate.DOMAIN, config)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('aux_heat'))
+
+        climate.set_aux_heat(self.hass, True, ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('aux_heat'))
+
+        fire_mqtt_message(self.hass, 'aux-state', 'ON')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('on', state.attributes.get('aux_heat'))
+
+        fire_mqtt_message(self.hass, 'aux-state', 'OFF')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('aux_heat'))
+
+        fire_mqtt_message(self.hass, 'aux-state', 'nonsense')
+        self.hass.block_till_done()
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('aux_heat'))
+
+    def test_set_aux(self):
+        """Test setting of the aux heating."""
+        assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG)
+
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('aux_heat'))
+        climate.set_aux_heat(self.hass, True, ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        self.assertEqual(('aux-topic', 'ON', 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('on', state.attributes.get('aux_heat'))
+
+        climate.set_aux_heat(self.hass, False, ENTITY_CLIMATE)
+        self.hass.block_till_done()
+        self.assertEqual(('aux-topic', 'OFF', 0, False),
+                         self.mock_publish.mock_calls[-2][1])
+        state = self.hass.states.get(ENTITY_CLIMATE)
+        self.assertEqual('off', state.attributes.get('aux_heat'))