Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 40 additions & 20 deletions homeassistant/components/climate/mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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:
Expand Down
25 changes: 23 additions & 2 deletions tests/components/climate/test_mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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'))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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'))
Expand Down