Add Mqtt Fan unknown state support (#65301)

* Add Mqtt Fan unknown state support

* Update homeassistant/components/mqtt/fan.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Adjust default state in tests

Co-authored-by: Erik Montnemery <erik@montnemery.com>
pull/65717/head
Jan Bouwhuis 2022-02-03 16:48:03 +01:00 committed by GitHub
parent 3d434dffc7
commit 711bd7169e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 25 additions and 12 deletions

View File

@ -94,6 +94,8 @@ DEFAULT_SPEED_RANGE_MAX = 100
OSCILLATE_ON_PAYLOAD = "oscillate_on"
OSCILLATE_OFF_PAYLOAD = "oscillate_off"
PAYLOAD_NONE = "None"
MQTT_FAN_ATTRIBUTES_BLOCKED = frozenset(
{
fan.ATTR_DIRECTION,
@ -243,7 +245,7 @@ class MqttFan(MqttEntity, FanEntity):
def __init__(self, hass, config, config_entry, discovery_data):
"""Initialize the MQTT fan."""
self._state = False
self._state = None
self._percentage = None
self._preset_mode = None
self._oscillation = None
@ -367,6 +369,8 @@ class MqttFan(MqttEntity, FanEntity):
self._state = True
elif payload == self._payload["STATE_OFF"]:
self._state = False
elif payload == PAYLOAD_NONE:
self._state = None
self.async_write_ha_state()
if self._topic[CONF_STATE_TOPIC] is not None:
@ -493,7 +497,7 @@ class MqttFan(MqttEntity, FanEntity):
return self._optimistic
@property
def is_on(self):
def is_on(self) -> bool | None:
"""Return true if device is on."""
return self._state

View File

@ -27,6 +27,7 @@ from homeassistant.const import (
ATTR_SUPPORTED_FEATURES,
STATE_OFF,
STATE_ON,
STATE_UNKNOWN,
)
from homeassistant.setup import async_setup_component
@ -119,7 +120,7 @@ async def test_controlling_state_via_topic(hass, mqtt_mock, caplog):
await hass.async_block_till_done()
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.state == STATE_UNKNOWN
assert not state.attributes.get(ATTR_ASSUMED_STATE)
async_fire_mqtt_message(hass, "state-topic", "StAtE_On")
@ -194,6 +195,10 @@ async def test_controlling_state_via_topic(hass, mqtt_mock, caplog):
assert state.attributes.get(fan.ATTR_PERCENTAGE) is None
assert state.attributes.get(fan.ATTR_SPEED) is None
async_fire_mqtt_message(hass, "state-topic", "None")
state = hass.states.get("fan.test")
assert state.state == STATE_UNKNOWN
async def test_controlling_state_via_topic_with_different_speed_range(
hass, mqtt_mock, caplog
@ -285,7 +290,7 @@ async def test_controlling_state_via_topic_no_percentage_topics(
await hass.async_block_till_done()
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.state == STATE_UNKNOWN
assert not state.attributes.get(ATTR_ASSUMED_STATE)
async_fire_mqtt_message(hass, "preset-mode-state-topic", "smart")
@ -349,13 +354,17 @@ async def test_controlling_state_via_topic_and_json_message(hass, mqtt_mock, cap
await hass.async_block_till_done()
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.state == STATE_UNKNOWN
assert not state.attributes.get(ATTR_ASSUMED_STATE)
async_fire_mqtt_message(hass, "state-topic", '{"val":"ON"}')
state = hass.states.get("fan.test")
assert state.state == STATE_ON
async_fire_mqtt_message(hass, "state-topic", '{"val": null}')
state = hass.states.get("fan.test")
assert state.state == STATE_UNKNOWN
async_fire_mqtt_message(hass, "state-topic", '{"val":"OFF"}')
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
@ -449,7 +458,7 @@ async def test_controlling_state_via_topic_and_json_message_shared_topic(
await hass.async_block_till_done()
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.state == STATE_UNKNOWN
assert not state.attributes.get(ATTR_ASSUMED_STATE)
async_fire_mqtt_message(
@ -527,7 +536,7 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock, caplog):
await hass.async_block_till_done()
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.state == STATE_UNKNOWN
assert state.attributes.get(ATTR_ASSUMED_STATE)
await common.async_turn_on(hass, "fan.test")
@ -748,7 +757,7 @@ async def test_sending_mqtt_commands_and_optimistic_no_legacy(hass, mqtt_mock, c
await hass.async_block_till_done()
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.state == STATE_UNKNOWN
assert state.attributes.get(ATTR_ASSUMED_STATE)
await common.async_turn_on(hass, "fan.test")
@ -883,7 +892,7 @@ async def test_sending_mqtt_command_templates_(hass, mqtt_mock, caplog):
await hass.async_block_till_done()
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.state == STATE_UNKNOWN
assert state.attributes.get(ATTR_ASSUMED_STATE)
await common.async_turn_on(hass, "fan.test")
@ -1022,7 +1031,7 @@ async def test_sending_mqtt_commands_and_optimistic_no_percentage_topic(
await hass.async_block_till_done()
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.state == STATE_UNKNOWN
assert state.attributes.get(ATTR_ASSUMED_STATE)
await common.async_set_preset_mode(hass, "fan.test", "medium")
@ -1086,7 +1095,7 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock, ca
await hass.async_block_till_done()
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.state == STATE_UNKNOWN
assert state.attributes.get(ATTR_ASSUMED_STATE)
await common.async_turn_on(hass, "fan.test")
@ -1344,7 +1353,7 @@ async def test_attributes(hass, mqtt_mock, caplog):
await hass.async_block_till_done()
state = hass.states.get("fan.test")
assert state.state == STATE_OFF
assert state.state == STATE_UNKNOWN
await common.async_turn_on(hass, "fan.test")
state = hass.states.get("fan.test")