Skip to content
Merged
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
24 changes: 20 additions & 4 deletions homeassistant/components/mqtt/fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ async def _subscribe_topics(self): # noqa: C901
def state_received(msg):
"""Handle new received MQTT message."""
payload = self._value_templates[CONF_STATE](msg.payload)
if not payload:
_LOGGER.debug("Ignoring empty state from '%s'", msg.topic)
return
if payload == self._payload["STATE_ON"]:
self._state = True
elif payload == self._payload["STATE_OFF"]:
Expand All @@ -362,22 +365,27 @@ def state_received(msg):
def percentage_received(msg):
"""Handle new received MQTT message for the percentage."""
numeric_val_str = self._value_templates[ATTR_PERCENTAGE](msg.payload)
if not numeric_val_str:
_LOGGER.debug("Ignoring empty speed from '%s'", msg.topic)
return
try:
percentage = ranged_value_to_percentage(
self._speed_range, int(numeric_val_str)
)
except ValueError:
_LOGGER.warning(
"'%s' received on topic %s is not a valid speed within the speed range",
"'%s' received on topic %s. '%s' is not a valid speed within the speed range",
msg.payload,
msg.topic,
numeric_val_str,
)
return
if percentage < 0 or percentage > 100:
_LOGGER.warning(
"'%s' received on topic %s is not a valid speed within the speed range",
"'%s' received on topic %s. '%s' is not a valid speed within the speed range",
msg.payload,
msg.topic,
numeric_val_str,
)
return
self._percentage = percentage
Expand All @@ -396,11 +404,15 @@ def percentage_received(msg):
def preset_mode_received(msg):
"""Handle new received MQTT message for preset mode."""
preset_mode = self._value_templates[ATTR_PRESET_MODE](msg.payload)
if not preset_mode:
_LOGGER.debug("Ignoring empty preset_mode from '%s'", msg.topic)
return
if preset_mode not in self.preset_modes:
_LOGGER.warning(
"'%s' received on topic %s is not a valid preset mode",
"'%s' received on topic %s. '%s' is not a valid preset mode",
msg.payload,
msg.topic,
preset_mode,
)
return

Expand Down Expand Up @@ -436,9 +448,10 @@ def speed_received(msg):
self._speed = speed
else:
_LOGGER.warning(
"'%s' received on topic %s is not a valid speed",
"'%s' received on topic %s. '%s' is not a valid speed",
msg.payload,
msg.topic,
speed,
)
return

Expand All @@ -464,6 +477,9 @@ def speed_received(msg):
def oscillation_received(msg):
"""Handle new received MQTT message for the oscillation."""
payload = self._value_templates[ATTR_OSCILLATING](msg.payload)
if not payload:
_LOGGER.debug("Ignoring empty oscillation from '%s'", msg.topic)
return
if payload == self._payload["OSCILLATE_ON_PAYLOAD"]:
self._oscillation = True
elif payload == self._payload["OSCILLATE_OFF_PAYLOAD"]:
Expand Down
97 changes: 97 additions & 0 deletions tests/components/mqtt/test_fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,10 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, cap
state = hass.states.get("fan.test")
assert state.attributes.get(fan.ATTR_PERCENTAGE) == 100

async_fire_mqtt_message(hass, "percentage-state-topic", '{"otherval": 100}')
assert "Ignoring empty speed from" in caplog.text
caplog.clear()

async_fire_mqtt_message(hass, "preset-mode-state-topic", '{"val": "low"}')
assert "not a valid preset mode" in caplog.text
caplog.clear()
Expand All @@ -424,6 +428,99 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, cap
state = hass.states.get("fan.test")
assert state.attributes.get("preset_mode") == "silent"

async_fire_mqtt_message(hass, "preset-mode-state-topic", '{"otherval": 100}')
assert "Ignoring empty preset_mode from" in caplog.text
caplog.clear()


async def test_controlling_state_via_topic_and_json_message_shared_topic(
hass, mqtt_mock, caplog
):
"""Test the controlling state via topic and JSON message using a shared topic."""
assert await async_setup_component(
hass,
fan.DOMAIN,
{
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"state_topic": "shared-state-topic",
"command_topic": "command-topic",
"oscillation_state_topic": "shared-state-topic",
"oscillation_command_topic": "oscillation-command-topic",
"percentage_state_topic": "shared-state-topic",
"percentage_command_topic": "percentage-command-topic",
"preset_mode_state_topic": "shared-state-topic",
"preset_mode_command_topic": "preset-mode-command-topic",
"preset_modes": [
"auto",
"smart",
"whoosh",
"eco",
"breeze",
"silent",
],
"state_value_template": "{{ value_json.state }}",
"oscillation_value_template": "{{ value_json.oscillation }}",
"percentage_value_template": "{{ value_json.percentage }}",
"preset_mode_value_template": "{{ value_json.preset_mode }}",
"speed_range_min": 1,
"speed_range_max": 100,
}
},
)
await hass.async_block_till_done()

state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert not state.attributes.get(ATTR_ASSUMED_STATE)

async_fire_mqtt_message(
hass,
"shared-state-topic",
'{"state":"ON","preset_mode":"eco","oscillation":"oscillate_on","percentage": 50}',
)
state = hass.states.get("fan.test")
assert state.state == STATE_ON
assert state.attributes.get("oscillating") is True
assert state.attributes.get(fan.ATTR_PERCENTAGE) == 50
assert state.attributes.get("preset_mode") == "eco"

async_fire_mqtt_message(
hass,
"shared-state-topic",
'{"state":"ON","preset_mode":"auto","oscillation":"oscillate_off","percentage": 10}',
)
state = hass.states.get("fan.test")
assert state.state == STATE_ON
assert state.attributes.get("oscillating") is False
assert state.attributes.get(fan.ATTR_PERCENTAGE) == 10
assert state.attributes.get("preset_mode") == "auto"

async_fire_mqtt_message(
hass,
"shared-state-topic",
'{"state":"OFF","preset_mode":"auto","oscillation":"oscillate_off","percentage": 0}',
)
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.attributes.get("oscillating") is False
assert state.attributes.get(fan.ATTR_PERCENTAGE) == 0
assert state.attributes.get("preset_mode") == "auto"

async_fire_mqtt_message(
hass,
"shared-state-topic",
'{"percentage": 100}',
)
state = hass.states.get("fan.test")
assert state.attributes.get(fan.ATTR_PERCENTAGE) == 100
assert state.attributes.get("preset_mode") == "auto"
assert "Ignoring empty preset_mode from" in caplog.text
assert "Ignoring empty state from" in caplog.text
assert "Ignoring empty oscillation from" in caplog.text
caplog.clear()


async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock, caplog):
"""Test optimistic mode without state topic."""
Expand Down