diff --git a/homeassistant/components/climate/mqtt.py b/homeassistant/components/climate/mqtt.py index d571ebd39e4351..953ead4a5197d7 100644 --- a/homeassistant/components/climate/mqtt.py +++ b/homeassistant/components/climate/mqtt.py @@ -96,6 +96,14 @@ }) +def unquote_from_mqtt(payload): + """Return a list of possible payloads after unquoting.""" + if payload[0] == '"' and payload[-1] == '"': + return (payload[1:-1], payload) + else: + return (payload,) + + @asyncio.coroutine def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Set up the MQTT climate devices.""" @@ -188,11 +196,13 @@ def handle_current_temp_received(topic, payload, 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() + for unquoted_payload in unquote_from_mqtt(payload): + if unquoted_payload in self._operation_list: + self._current_operation = unquoted_payload + self.async_schedule_update_ha_state() + return + + _LOGGER.error("Invalid mode: %s", payload) if self._topic[CONF_MODE_STATE_TOPIC] is not None: yield from mqtt.async_subscribe( @@ -216,11 +226,13 @@ def handle_temperature_received(topic, payload, 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() + for unquoted_payload in unquote_from_mqtt(payload): + if unquoted_payload in self._fan_list: + self._current_fan_mode = unquoted_payload + self.async_schedule_update_ha_state() + return + + _LOGGER.error("Invalid fan mode: %s", payload) if self._topic[CONF_FAN_MODE_STATE_TOPIC] is not None: yield from mqtt.async_subscribe( @@ -230,11 +242,13 @@ def handle_fan_mode_received(topic, payload, 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() + for unquoted_payload in unquote_from_mqtt(payload): + if unquoted_payload in self._swing_list: + self._current_swing_mode = unquoted_payload + self.async_schedule_update_ha_state() + return + + _LOGGER.error("Invalid swing mode: %s", payload) if self._topic[CONF_SWING_MODE_STATE_TOPIC] is not None: yield from mqtt.async_subscribe( @@ -244,9 +258,9 @@ def handle_swing_mode_received(topic, payload, qos): @callback def handle_away_mode_received(topic, payload, qos): """Handle receiving away mode via MQTT.""" - if payload == self._payload_on: + if self._payload_on in unquote_from_mqtt(payload): self._away = True - elif payload == self._payload_off: + elif self._payload_off in unquote_from_mqtt(payload): self._away = False else: _LOGGER.error("Invalid away mode: %s", payload) @@ -261,9 +275,9 @@ def handle_away_mode_received(topic, payload, qos): @callback def handle_aux_mode_received(topic, payload, qos): """Handle receiving aux mode via MQTT.""" - if payload == self._payload_on: + if self._payload_on in unquote_from_mqtt(payload): self._aux = True - elif payload == self._payload_off: + elif self._payload_off in unquote_from_mqtt(payload): self._aux = False else: _LOGGER.error("Invalid aux mode: %s", payload) @@ -278,7 +292,13 @@ def handle_aux_mode_received(topic, payload, qos): @callback def handle_hold_mode_received(topic, payload, qos): """Handle receiving hold mode via MQTT.""" - self._hold = payload + # Since we don't have a fixed list of possible hold modes, we can't + # determine for sure whether a quoted payload should be unquoted + # or not. Unquoted seems very much more likely, though. + if payload[0] == '"' and payload[-1] == '"': + self._hold = payload[1:-1] + else: + self._hold = payload self.async_schedule_update_ha_state() if self._topic[CONF_HOLD_STATE_TOPIC] is not None: diff --git a/tests/components/climate/test_mqtt.py b/tests/components/climate/test_mqtt.py index 43f90eeee2076b..2b646a03ae5f4a 100644 --- a/tests/components/climate/test_mqtt.py +++ b/tests/components/climate/test_mqtt.py @@ -135,6 +135,12 @@ def test_set_operation_pessimistic(self): self.assertEqual("cool", state.attributes.get('operation_mode')) self.assertEqual("cool", state.state) + fire_mqtt_message(self.hass, 'mode-state', '"heat"') + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_CLIMATE) + self.assertEqual("heat", state.attributes.get('operation_mode')) + self.assertEqual("heat", 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) @@ -170,6 +176,11 @@ def test_set_fan_mode_pessimistic(self): state = self.hass.states.get(ENTITY_CLIMATE) self.assertEqual('high', state.attributes.get('fan_mode')) + fire_mqtt_message(self.hass, 'fan-state', '"low"') + 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(self): """Test setting of new fan mode.""" assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG) @@ -218,6 +229,11 @@ def test_set_swing_pessimistic(self): state = self.hass.states.get(ENTITY_CLIMATE) self.assertEqual("on", state.attributes.get('swing_mode')) + fire_mqtt_message(self.hass, 'swing-state', '"off"') + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_CLIMATE) + self.assertEqual("off", 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) @@ -306,7 +322,7 @@ def test_set_away_mode_pessimistic(self): state = self.hass.states.get(ENTITY_CLIMATE) self.assertEqual('on', state.attributes.get('away_mode')) - fire_mqtt_message(self.hass, 'away-state', 'OFF') + 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')) @@ -364,6 +380,11 @@ def test_set_hold_pessimistic(self): state = self.hass.states.get(ENTITY_CLIMATE) self.assertEqual('off', state.attributes.get('hold_mode')) + fire_mqtt_message(self.hass, 'hold-state', '"quoted"') + self.hass.block_till_done() + state = self.hass.states.get(ENTITY_CLIMATE) + self.assertEqual('quoted', state.attributes.get('hold_mode')) + def test_set_hold(self): """Test setting the hold mode.""" assert setup_component(self.hass, climate.DOMAIN, DEFAULT_CONFIG) @@ -403,7 +424,7 @@ def test_set_aux_pessimistic(self): state = self.hass.states.get(ENTITY_CLIMATE) self.assertEqual('on', state.attributes.get('aux_heat')) - fire_mqtt_message(self.hass, 'aux-state', 'OFF') + 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'))