Improve MQTT test coverage and remove dead code (#33584)

* Improve MQTT tests and remove dead code

* Remove useless test.

* Add more test
This commit is contained in:
Erik Montnemery 2020-04-03 18:05:58 +02:00 committed by GitHub
parent a813847f6e
commit 4e6fd19624
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 761 additions and 125 deletions

View file

@ -31,7 +31,6 @@ from homeassistant.const import (
STATE_UNKNOWN, STATE_UNKNOWN,
) )
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.typing import ConfigType, HomeAssistantType from homeassistant.helpers.typing import ConfigType, HomeAssistantType
@ -531,9 +530,6 @@ class MqttCover(
async def async_set_cover_tilt_position(self, **kwargs): async def async_set_cover_tilt_position(self, **kwargs):
"""Move the cover tilt to a specific position.""" """Move the cover tilt to a specific position."""
if ATTR_TILT_POSITION not in kwargs:
return
position = float(kwargs[ATTR_TILT_POSITION]) position = float(kwargs[ATTR_TILT_POSITION])
# The position needs to be between min and max # The position needs to be between min and max
@ -550,36 +546,31 @@ class MqttCover(
async def async_set_cover_position(self, **kwargs): async def async_set_cover_position(self, **kwargs):
"""Move the cover to a specific position.""" """Move the cover to a specific position."""
set_position_template = self._config.get(CONF_SET_POSITION_TEMPLATE) set_position_template = self._config.get(CONF_SET_POSITION_TEMPLATE)
if ATTR_POSITION in kwargs: position = kwargs[ATTR_POSITION]
position = kwargs[ATTR_POSITION] percentage_position = position
percentage_position = position if set_position_template is not None:
if set_position_template is not None: position = set_position_template.async_render(**kwargs)
try: elif (
position = set_position_template.async_render(**kwargs) self._config[CONF_POSITION_OPEN] != 100
except TemplateError as ex: and self._config[CONF_POSITION_CLOSED] != 0
_LOGGER.error(ex) ):
self._state = None position = self.find_in_range_from_percent(position, COVER_PAYLOAD)
elif (
self._config[CONF_POSITION_OPEN] != 100
and self._config[CONF_POSITION_CLOSED] != 0
):
position = self.find_in_range_from_percent(position, COVER_PAYLOAD)
mqtt.async_publish( mqtt.async_publish(
self.hass, self.hass,
self._config.get(CONF_SET_POSITION_TOPIC), self._config.get(CONF_SET_POSITION_TOPIC),
position, position,
self._config[CONF_QOS], self._config[CONF_QOS],
self._config[CONF_RETAIN], self._config[CONF_RETAIN],
)
if self._optimistic:
self._state = (
STATE_CLOSED
if percentage_position == self._config[CONF_POSITION_CLOSED]
else STATE_OPEN
) )
if self._optimistic: self._position = percentage_position
self._state = ( self.async_write_ha_state()
STATE_CLOSED
if percentage_position == self._config[CONF_POSITION_CLOSED]
else STATE_OPEN
)
self._position = percentage_position
self.async_write_ha_state()
async def async_toggle_tilt(self, **kwargs): async def async_toggle_tilt(self, **kwargs):
"""Toggle the entity.""" """Toggle the entity."""

View file

@ -6,7 +6,6 @@ import re
from homeassistant.components import mqtt from homeassistant.components import mqtt
from homeassistant.const import CONF_DEVICE, CONF_PLATFORM from homeassistant.const import CONF_DEVICE, CONF_PLATFORM
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.typing import HomeAssistantType from homeassistant.helpers.typing import HomeAssistantType
@ -35,21 +34,6 @@ SUPPORTED_COMPONENTS = [
"vacuum", "vacuum",
] ]
CONFIG_ENTRY_COMPONENTS = [
"alarm_control_panel",
"binary_sensor",
"camera",
"climate",
"cover",
"device_automation",
"fan",
"light",
"lock",
"sensor",
"switch",
"vacuum",
]
ALREADY_DISCOVERED = "mqtt_discovered_components" ALREADY_DISCOVERED = "mqtt_discovered_components"
DATA_CONFIG_ENTRY_LOCK = "mqtt_config_entry_lock" DATA_CONFIG_ENTRY_LOCK = "mqtt_config_entry_lock"
CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup" CONFIG_ENTRY_IS_SETUP = "mqtt_config_entry_is_setup"
@ -159,10 +143,6 @@ async def async_start(
_LOGGER.info("Found new component: %s %s", component, discovery_id) _LOGGER.info("Found new component: %s %s", component, discovery_id)
hass.data[ALREADY_DISCOVERED][discovery_hash] = None hass.data[ALREADY_DISCOVERED][discovery_hash] = None
if component not in CONFIG_ENTRY_COMPONENTS:
await async_load_platform(hass, component, "mqtt", payload, hass_config)
return
config_entries_key = f"{component}.mqtt" config_entries_key = f"{component}.mqtt"
async with hass.data[DATA_CONFIG_ENTRY_LOCK]: async with hass.data[DATA_CONFIG_ENTRY_LOCK]:
if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]: if config_entries_key not in hass.data[CONFIG_ENTRY_IS_SETUP]:

View file

@ -401,9 +401,6 @@ class MqttFan(
This method is a coroutine. This method is a coroutine.
""" """
if self._topic[CONF_SPEED_COMMAND_TOPIC] is None:
return
if speed == SPEED_LOW: if speed == SPEED_LOW:
mqtt_payload = self._payload["SPEED_LOW"] mqtt_payload = self._payload["SPEED_LOW"]
elif speed == SPEED_MEDIUM: elif speed == SPEED_MEDIUM:
@ -432,9 +429,6 @@ class MqttFan(
This method is a coroutine. This method is a coroutine.
""" """
if self._topic[CONF_OSCILLATION_COMMAND_TOPIC] is None:
return
if oscillating is False: if oscillating is False:
payload = self._payload["OSCILLATE_OFF_PAYLOAD"] payload = self._payload["OSCILLATE_OFF_PAYLOAD"]
else: else:

View file

@ -410,24 +410,26 @@ class MqttVacuum(
@property @property
def fan_speed_list(self): def fan_speed_list(self):
"""Return the status of the vacuum.""" """Return the status of the vacuum.
if self.supported_features & SUPPORT_FAN_SPEED == 0:
return [] No need to check SUPPORT_FAN_SPEED, this won't be called if fan_speed is None.
"""
return self._fan_speed_list return self._fan_speed_list
@property @property
def battery_level(self): def battery_level(self):
"""Return the status of the vacuum.""" """Return the status of the vacuum."""
if self.supported_features & SUPPORT_BATTERY == 0: if self.supported_features & SUPPORT_BATTERY == 0:
return return None
return max(0, min(100, self._battery_level)) return max(0, min(100, self._battery_level))
@property @property
def battery_icon(self): def battery_icon(self):
"""Return the battery icon for the vacuum cleaner.""" """Return the battery icon for the vacuum cleaner.
if self.supported_features & SUPPORT_BATTERY == 0:
return No need to check SUPPORT_BATTERY, this won't be called if battery_level is None.
"""
return icon_for_battery_level( return icon_for_battery_level(
battery_level=self.battery_level, charging=self._charging battery_level=self.battery_level, charging=self._charging

View file

@ -282,9 +282,10 @@ class MqttStateVacuum(
@property @property
def fan_speed_list(self): def fan_speed_list(self):
"""Return fan speed list of the vacuum.""" """Return fan speed list of the vacuum.
if self.supported_features & SUPPORT_FAN_SPEED == 0:
return None No need to check SUPPORT_FAN_SPEED, this won't be called if fan_speed is None.
"""
return self._fan_speed_list return self._fan_speed_list
@property @property

View file

@ -42,7 +42,8 @@ from tests.common import (
) )
from tests.components.alarm_control_panel import common from tests.components.alarm_control_panel import common
CODE = "HELLO_CODE" CODE_NUMBER = "1234"
CODE_TEXT = "HELLO_CODE"
DEFAULT_CONFIG = { DEFAULT_CONFIG = {
alarm_control_panel.DOMAIN: { alarm_control_panel.DOMAIN: {
@ -341,6 +342,34 @@ async def test_update_state_via_state_topic_template(hass, mqtt_mock):
assert state.state == STATE_ALARM_ARMED_AWAY assert state.state == STATE_ALARM_ARMED_AWAY
async def test_attributes_code_number(hass, mqtt_mock):
"""Test attributes which are not supported by the vacuum."""
config = copy.deepcopy(DEFAULT_CONFIG)
config[alarm_control_panel.DOMAIN]["code"] = CODE_NUMBER
assert await async_setup_component(hass, alarm_control_panel.DOMAIN, config)
state = hass.states.get("alarm_control_panel.test")
assert (
state.attributes.get(alarm_control_panel.ATTR_CODE_FORMAT)
== alarm_control_panel.FORMAT_NUMBER
)
async def test_attributes_code_text(hass, mqtt_mock):
"""Test attributes which are not supported by the vacuum."""
config = copy.deepcopy(DEFAULT_CONFIG)
config[alarm_control_panel.DOMAIN]["code"] = CODE_TEXT
assert await async_setup_component(hass, alarm_control_panel.DOMAIN, config)
state = hass.states.get("alarm_control_panel.test")
assert (
state.attributes.get(alarm_control_panel.ATTR_CODE_FORMAT)
== alarm_control_panel.FORMAT_TEXT
)
async def test_availability_without_topic(hass, mqtt_mock): async def test_availability_without_topic(hass, mqtt_mock):
"""Test availability without defined availability topic.""" """Test availability without defined availability topic."""
await help_test_availability_without_topic( await help_test_availability_without_topic(

View file

@ -534,6 +534,22 @@ async def test_set_hold(hass, mqtt_mock):
assert state.attributes.get("preset_mode") is None assert state.attributes.get("preset_mode") is None
async def test_set_preset_mode_twice(hass, mqtt_mock):
"""Test setting of the same mode twice only publishes once."""
assert await async_setup_component(hass, CLIMATE_DOMAIN, DEFAULT_CONFIG)
state = hass.states.get(ENTITY_CLIMATE)
assert state.attributes.get("preset_mode") is None
await common.async_set_preset_mode(hass, "hold-on", ENTITY_CLIMATE)
mqtt_mock.async_publish.assert_called_once_with("hold-topic", "hold-on", 0, False)
mqtt_mock.async_publish.reset_mock()
state = hass.states.get(ENTITY_CLIMATE)
assert state.attributes.get("preset_mode") == "hold-on"
await common.async_set_preset_mode(hass, "hold-on", ENTITY_CLIMATE)
mqtt_mock.async_publish.assert_not_called()
async def test_set_aux_pessimistic(hass, mqtt_mock): async def test_set_aux_pessimistic(hass, mqtt_mock):
"""Test setting of the aux heating in pessimistic mode.""" """Test setting of the aux heating in pessimistic mode."""
config = copy.deepcopy(DEFAULT_CONFIG) config = copy.deepcopy(DEFAULT_CONFIG)

View file

@ -1,6 +1,11 @@
"""The tests for the MQTT cover platform.""" """The tests for the MQTT cover platform."""
from homeassistant.components import cover from homeassistant.components import cover
from homeassistant.components.cover import ATTR_POSITION, ATTR_TILT_POSITION from homeassistant.components.cover import (
ATTR_CURRENT_POSITION,
ATTR_CURRENT_TILT_POSITION,
ATTR_POSITION,
ATTR_TILT_POSITION,
)
from homeassistant.components.mqtt.cover import MqttCover from homeassistant.components.mqtt.cover import MqttCover
from homeassistant.const import ( from homeassistant.const import (
ATTR_ASSUMED_STATE, ATTR_ASSUMED_STATE,
@ -335,6 +340,68 @@ async def test_optimistic_state_change(hass, mqtt_mock):
assert state.state == STATE_CLOSED assert state.state == STATE_CLOSED
async def test_optimistic_state_change_with_position(hass, mqtt_mock):
"""Test changing state optimistically."""
assert await async_setup_component(
hass,
cover.DOMAIN,
{
cover.DOMAIN: {
"platform": "mqtt",
"name": "test",
"optimistic": True,
"command_topic": "command-topic",
"position_topic": "position-topic",
"qos": 0,
}
},
)
state = hass.states.get("cover.test")
assert state.state == STATE_UNKNOWN
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(ATTR_CURRENT_POSITION) is None
await hass.services.async_call(
cover.DOMAIN, SERVICE_OPEN_COVER, {ATTR_ENTITY_ID: "cover.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "OPEN", 0, False)
mqtt_mock.async_publish.reset_mock()
state = hass.states.get("cover.test")
assert state.state == STATE_OPEN
assert state.attributes.get(ATTR_CURRENT_POSITION) == 100
await hass.services.async_call(
cover.DOMAIN, SERVICE_CLOSE_COVER, {ATTR_ENTITY_ID: "cover.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "CLOSE", 0, False)
mqtt_mock.async_publish.reset_mock()
state = hass.states.get("cover.test")
assert STATE_CLOSED == state.state
assert state.attributes.get(ATTR_CURRENT_POSITION) == 0
await hass.services.async_call(
cover.DOMAIN, SERVICE_TOGGLE, {ATTR_ENTITY_ID: "cover.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "OPEN", 0, False)
mqtt_mock.async_publish.reset_mock()
state = hass.states.get("cover.test")
assert STATE_OPEN == state.state
assert state.attributes.get(ATTR_CURRENT_POSITION) == 100
await hass.services.async_call(
cover.DOMAIN, SERVICE_TOGGLE, {ATTR_ENTITY_ID: "cover.test"}, blocking=True
)
mqtt_mock.async_publish.assert_called_once_with("command-topic", "CLOSE", 0, False)
state = hass.states.get("cover.test")
assert state.state == STATE_CLOSED
assert state.attributes.get(ATTR_CURRENT_POSITION) == 0
async def test_send_open_cover_command(hass, mqtt_mock): async def test_send_open_cover_command(hass, mqtt_mock):
"""Test the sending of open_cover.""" """Test the sending of open_cover."""
assert await async_setup_component( assert await async_setup_component(
@ -440,31 +507,31 @@ async def test_current_cover_position(hass, mqtt_mock):
) )
state_attributes_dict = hass.states.get("cover.test").attributes state_attributes_dict = hass.states.get("cover.test").attributes
assert not ("current_position" in state_attributes_dict) assert not (ATTR_CURRENT_POSITION in state_attributes_dict)
assert not ("current_tilt_position" in state_attributes_dict) assert not (ATTR_CURRENT_TILT_POSITION in state_attributes_dict)
assert not (4 & hass.states.get("cover.test").attributes["supported_features"] == 4) assert not (4 & hass.states.get("cover.test").attributes["supported_features"] == 4)
async_fire_mqtt_message(hass, "get-position-topic", "0") async_fire_mqtt_message(hass, "get-position-topic", "0")
current_cover_position = hass.states.get("cover.test").attributes[ current_cover_position = hass.states.get("cover.test").attributes[
"current_position" ATTR_CURRENT_POSITION
] ]
assert current_cover_position == 0 assert current_cover_position == 0
async_fire_mqtt_message(hass, "get-position-topic", "50") async_fire_mqtt_message(hass, "get-position-topic", "50")
current_cover_position = hass.states.get("cover.test").attributes[ current_cover_position = hass.states.get("cover.test").attributes[
"current_position" ATTR_CURRENT_POSITION
] ]
assert current_cover_position == 50 assert current_cover_position == 50
async_fire_mqtt_message(hass, "get-position-topic", "non-numeric") async_fire_mqtt_message(hass, "get-position-topic", "non-numeric")
current_cover_position = hass.states.get("cover.test").attributes[ current_cover_position = hass.states.get("cover.test").attributes[
"current_position" ATTR_CURRENT_POSITION
] ]
assert current_cover_position == 50 assert current_cover_position == 50
async_fire_mqtt_message(hass, "get-position-topic", "101") async_fire_mqtt_message(hass, "get-position-topic", "101")
current_cover_position = hass.states.get("cover.test").attributes[ current_cover_position = hass.states.get("cover.test").attributes[
"current_position" ATTR_CURRENT_POSITION
] ]
assert current_cover_position == 100 assert current_cover_position == 100
@ -490,48 +557,67 @@ async def test_current_cover_position_inverted(hass, mqtt_mock):
) )
state_attributes_dict = hass.states.get("cover.test").attributes state_attributes_dict = hass.states.get("cover.test").attributes
assert not ("current_position" in state_attributes_dict) assert not (ATTR_CURRENT_POSITION in state_attributes_dict)
assert not ("current_tilt_position" in state_attributes_dict) assert not (ATTR_CURRENT_TILT_POSITION in state_attributes_dict)
assert not (4 & hass.states.get("cover.test").attributes["supported_features"] == 4) assert not (4 & hass.states.get("cover.test").attributes["supported_features"] == 4)
async_fire_mqtt_message(hass, "get-position-topic", "100") async_fire_mqtt_message(hass, "get-position-topic", "100")
current_percentage_cover_position = hass.states.get("cover.test").attributes[ current_percentage_cover_position = hass.states.get("cover.test").attributes[
"current_position" ATTR_CURRENT_POSITION
] ]
assert current_percentage_cover_position == 0 assert current_percentage_cover_position == 0
assert hass.states.get("cover.test").state == STATE_CLOSED assert hass.states.get("cover.test").state == STATE_CLOSED
async_fire_mqtt_message(hass, "get-position-topic", "0") async_fire_mqtt_message(hass, "get-position-topic", "0")
current_percentage_cover_position = hass.states.get("cover.test").attributes[ current_percentage_cover_position = hass.states.get("cover.test").attributes[
"current_position" ATTR_CURRENT_POSITION
] ]
assert current_percentage_cover_position == 100 assert current_percentage_cover_position == 100
assert hass.states.get("cover.test").state == STATE_OPEN assert hass.states.get("cover.test").state == STATE_OPEN
async_fire_mqtt_message(hass, "get-position-topic", "50") async_fire_mqtt_message(hass, "get-position-topic", "50")
current_percentage_cover_position = hass.states.get("cover.test").attributes[ current_percentage_cover_position = hass.states.get("cover.test").attributes[
"current_position" ATTR_CURRENT_POSITION
] ]
assert current_percentage_cover_position == 50 assert current_percentage_cover_position == 50
assert hass.states.get("cover.test").state == STATE_OPEN assert hass.states.get("cover.test").state == STATE_OPEN
async_fire_mqtt_message(hass, "get-position-topic", "non-numeric") async_fire_mqtt_message(hass, "get-position-topic", "non-numeric")
current_percentage_cover_position = hass.states.get("cover.test").attributes[ current_percentage_cover_position = hass.states.get("cover.test").attributes[
"current_position" ATTR_CURRENT_POSITION
] ]
assert current_percentage_cover_position == 50 assert current_percentage_cover_position == 50
assert hass.states.get("cover.test").state == STATE_OPEN assert hass.states.get("cover.test").state == STATE_OPEN
async_fire_mqtt_message(hass, "get-position-topic", "101") async_fire_mqtt_message(hass, "get-position-topic", "101")
current_percentage_cover_position = hass.states.get("cover.test").attributes[ current_percentage_cover_position = hass.states.get("cover.test").attributes[
"current_position" ATTR_CURRENT_POSITION
] ]
assert current_percentage_cover_position == 0 assert current_percentage_cover_position == 0
assert hass.states.get("cover.test").state == STATE_CLOSED assert hass.states.get("cover.test").state == STATE_CLOSED
async def test_set_cover_position(hass, mqtt_mock): async def test_optimistic_position(hass, mqtt_mock):
"""Test setting cover position.""" """Test optimistic position is not supported."""
assert await async_setup_component(
hass,
cover.DOMAIN,
{
cover.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"set_position_topic": "set-position-topic",
}
},
)
state = hass.states.get("cover.test")
assert state is None
async def test_position_update(hass, mqtt_mock):
"""Test cover position update from received MQTT message."""
assert await async_setup_component( assert await async_setup_component(
hass, hass,
cover.DOMAIN, cover.DOMAIN,
@ -552,16 +638,16 @@ async def test_set_cover_position(hass, mqtt_mock):
) )
state_attributes_dict = hass.states.get("cover.test").attributes state_attributes_dict = hass.states.get("cover.test").attributes
assert not ("current_position" in state_attributes_dict) assert not (ATTR_CURRENT_POSITION in state_attributes_dict)
assert not ("current_tilt_position" in state_attributes_dict) assert not (ATTR_CURRENT_TILT_POSITION in state_attributes_dict)
assert 4 & hass.states.get("cover.test").attributes["supported_features"] == 4 assert 4 & hass.states.get("cover.test").attributes["supported_features"] == 4
async_fire_mqtt_message(hass, "get-position-topic", "22") async_fire_mqtt_message(hass, "get-position-topic", "22")
state_attributes_dict = hass.states.get("cover.test").attributes state_attributes_dict = hass.states.get("cover.test").attributes
assert "current_position" in state_attributes_dict assert ATTR_CURRENT_POSITION in state_attributes_dict
assert not ("current_tilt_position" in state_attributes_dict) assert not (ATTR_CURRENT_TILT_POSITION in state_attributes_dict)
current_cover_position = hass.states.get("cover.test").attributes[ current_cover_position = hass.states.get("cover.test").attributes[
"current_position" ATTR_CURRENT_POSITION
] ]
assert current_cover_position == 22 assert current_cover_position == 22
@ -629,6 +715,37 @@ async def test_set_position_untemplated(hass, mqtt_mock):
mqtt_mock.async_publish.assert_called_once_with("position-topic", 62, 0, False) mqtt_mock.async_publish.assert_called_once_with("position-topic", 62, 0, False)
async def test_set_position_untemplated_custom_percentage_range(hass, mqtt_mock):
"""Test setting cover position via template."""
assert await async_setup_component(
hass,
cover.DOMAIN,
{
cover.DOMAIN: {
"platform": "mqtt",
"name": "test",
"position_topic": "state-topic",
"command_topic": "command-topic",
"set_position_topic": "position-topic",
"position_open": 0,
"position_closed": 100,
"payload_open": "OPEN",
"payload_close": "CLOSE",
"payload_stop": "STOP",
}
},
)
await hass.services.async_call(
cover.DOMAIN,
SERVICE_SET_COVER_POSITION,
{ATTR_ENTITY_ID: "cover.test", ATTR_POSITION: 38},
blocking=True,
)
mqtt_mock.async_publish.assert_called_once_with("position-topic", 62, 0, False)
async def test_no_command_topic(hass, mqtt_mock): async def test_no_command_topic(hass, mqtt_mock):
"""Test with no command topic.""" """Test with no command topic."""
assert await async_setup_component( assert await async_setup_component(
@ -717,10 +834,10 @@ async def test_tilt_defaults(hass, mqtt_mock):
) )
state_attributes_dict = hass.states.get("cover.test").attributes state_attributes_dict = hass.states.get("cover.test").attributes
assert "current_tilt_position" in state_attributes_dict assert ATTR_CURRENT_TILT_POSITION in state_attributes_dict
current_cover_position = hass.states.get("cover.test").attributes[ current_cover_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_position == STATE_UNKNOWN assert current_cover_position == STATE_UNKNOWN
@ -770,7 +887,7 @@ async def test_tilt_via_invocation_defaults(hass, mqtt_mock):
async_fire_mqtt_message(hass, "tilt-status-topic", "0") async_fire_mqtt_message(hass, "tilt-status-topic", "0")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 0 assert current_cover_tilt_position == 0
@ -788,7 +905,7 @@ async def test_tilt_via_invocation_defaults(hass, mqtt_mock):
async_fire_mqtt_message(hass, "tilt-status-topic", "100") async_fire_mqtt_message(hass, "tilt-status-topic", "100")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 100 assert current_cover_tilt_position == 100
@ -849,7 +966,7 @@ async def test_tilt_given_value(hass, mqtt_mock):
async_fire_mqtt_message(hass, "tilt-status-topic", "25") async_fire_mqtt_message(hass, "tilt-status-topic", "25")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 25 assert current_cover_tilt_position == 25
@ -867,7 +984,7 @@ async def test_tilt_given_value(hass, mqtt_mock):
async_fire_mqtt_message(hass, "tilt-status-topic", "80") async_fire_mqtt_message(hass, "tilt-status-topic", "80")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 80 assert current_cover_tilt_position == 80
@ -913,7 +1030,7 @@ async def test_tilt_given_value_optimistic(hass, mqtt_mock):
) )
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 80 assert current_cover_tilt_position == 80
@ -928,7 +1045,7 @@ async def test_tilt_given_value_optimistic(hass, mqtt_mock):
) )
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 25 assert current_cover_tilt_position == 25
@ -969,7 +1086,7 @@ async def test_tilt_given_value_altered_range(hass, mqtt_mock):
) )
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 50 assert current_cover_tilt_position == 50
@ -984,7 +1101,7 @@ async def test_tilt_given_value_altered_range(hass, mqtt_mock):
) )
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 0 assert current_cover_tilt_position == 0
@ -999,7 +1116,7 @@ async def test_tilt_given_value_altered_range(hass, mqtt_mock):
) )
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 50 assert current_cover_tilt_position == 50
@ -1030,14 +1147,14 @@ async def test_tilt_via_topic(hass, mqtt_mock):
async_fire_mqtt_message(hass, "tilt-status-topic", "0") async_fire_mqtt_message(hass, "tilt-status-topic", "0")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 0 assert current_cover_tilt_position == 0
async_fire_mqtt_message(hass, "tilt-status-topic", "50") async_fire_mqtt_message(hass, "tilt-status-topic", "50")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 50 assert current_cover_tilt_position == 50
@ -1069,14 +1186,14 @@ async def test_tilt_via_topic_template(hass, mqtt_mock):
async_fire_mqtt_message(hass, "tilt-status-topic", "99") async_fire_mqtt_message(hass, "tilt-status-topic", "99")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 0 assert current_cover_tilt_position == 0
async_fire_mqtt_message(hass, "tilt-status-topic", "5000") async_fire_mqtt_message(hass, "tilt-status-topic", "5000")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 50 assert current_cover_tilt_position == 50
@ -1107,21 +1224,21 @@ async def test_tilt_via_topic_altered_range(hass, mqtt_mock):
async_fire_mqtt_message(hass, "tilt-status-topic", "0") async_fire_mqtt_message(hass, "tilt-status-topic", "0")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 0 assert current_cover_tilt_position == 0
async_fire_mqtt_message(hass, "tilt-status-topic", "50") async_fire_mqtt_message(hass, "tilt-status-topic", "50")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 100 assert current_cover_tilt_position == 100
async_fire_mqtt_message(hass, "tilt-status-topic", "25") async_fire_mqtt_message(hass, "tilt-status-topic", "25")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 50 assert current_cover_tilt_position == 50
@ -1155,21 +1272,21 @@ async def test_tilt_via_topic_template_altered_range(hass, mqtt_mock):
async_fire_mqtt_message(hass, "tilt-status-topic", "99") async_fire_mqtt_message(hass, "tilt-status-topic", "99")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 0 assert current_cover_tilt_position == 0
async_fire_mqtt_message(hass, "tilt-status-topic", "5000") async_fire_mqtt_message(hass, "tilt-status-topic", "5000")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 100 assert current_cover_tilt_position == 100
async_fire_mqtt_message(hass, "tilt-status-topic", "2500") async_fire_mqtt_message(hass, "tilt-status-topic", "2500")
current_cover_tilt_position = hass.states.get("cover.test").attributes[ current_cover_tilt_position = hass.states.get("cover.test").attributes[
"current_tilt_position" ATTR_CURRENT_TILT_POSITION
] ]
assert current_cover_tilt_position == 50 assert current_cover_tilt_position == 50

View file

@ -51,32 +51,32 @@ async def test_subscribing_config_topic(hass, mqtt_mock):
async def test_invalid_topic(hass, mqtt_mock): async def test_invalid_topic(hass, mqtt_mock):
"""Test sending to invalid topic.""" """Test sending to invalid topic."""
with patch( with patch(
"homeassistant.components.mqtt.discovery.async_load_platform" "homeassistant.components.mqtt.discovery.async_dispatcher_send"
) as mock_load_platform: ) as mock_dispatcher_send:
entry = MockConfigEntry( entry = MockConfigEntry(
domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"} domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}
) )
mock_load_platform.return_value = mock_coro() mock_dispatcher_send.return_value = mock_coro()
await async_start(hass, "homeassistant", {}, entry) await async_start(hass, "homeassistant", {}, entry)
async_fire_mqtt_message( async_fire_mqtt_message(
hass, "homeassistant/binary_sensor/bla/not_config", "{}" hass, "homeassistant/binary_sensor/bla/not_config", "{}"
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert not mock_load_platform.called assert not mock_dispatcher_send.called
async def test_invalid_json(hass, mqtt_mock, caplog): async def test_invalid_json(hass, mqtt_mock, caplog):
"""Test sending in invalid JSON.""" """Test sending in invalid JSON."""
with patch( with patch(
"homeassistant.components.mqtt.discovery.async_load_platform" "homeassistant.components.mqtt.discovery.async_dispatcher_send"
) as mock_load_platform: ) as mock_dispatcher_send:
entry = MockConfigEntry( entry = MockConfigEntry(
domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"} domain=mqtt.DOMAIN, data={mqtt.CONF_BROKER: "test-broker"}
) )
mock_load_platform.return_value = mock_coro() mock_dispatcher_send.return_value = mock_coro()
await async_start(hass, "homeassistant", {}, entry) await async_start(hass, "homeassistant", {}, entry)
async_fire_mqtt_message( async_fire_mqtt_message(
@ -84,19 +84,19 @@ async def test_invalid_json(hass, mqtt_mock, caplog):
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert "Unable to parse JSON" in caplog.text assert "Unable to parse JSON" in caplog.text
assert not mock_load_platform.called assert not mock_dispatcher_send.called
async def test_only_valid_components(hass, mqtt_mock, caplog): async def test_only_valid_components(hass, mqtt_mock, caplog):
"""Test for a valid component.""" """Test for a valid component."""
with patch( with patch(
"homeassistant.components.mqtt.discovery.async_load_platform" "homeassistant.components.mqtt.discovery.async_dispatcher_send"
) as mock_load_platform: ) as mock_dispatcher_send:
entry = MockConfigEntry(domain=mqtt.DOMAIN) entry = MockConfigEntry(domain=mqtt.DOMAIN)
invalid_component = "timer" invalid_component = "timer"
mock_load_platform.return_value = mock_coro() mock_dispatcher_send.return_value = mock_coro()
await async_start(hass, "homeassistant", {}, entry) await async_start(hass, "homeassistant", {}, entry)
async_fire_mqtt_message( async_fire_mqtt_message(
@ -107,7 +107,7 @@ async def test_only_valid_components(hass, mqtt_mock, caplog):
assert "Integration {} is not supported".format(invalid_component) in caplog.text assert "Integration {} is not supported".format(invalid_component) in caplog.text
assert not mock_load_platform.called assert not mock_dispatcher_send.called
async def test_correct_config_discovery(hass, mqtt_mock, caplog): async def test_correct_config_discovery(hass, mqtt_mock, caplog):

View file

@ -1,6 +1,11 @@
"""Test MQTT fans.""" """Test MQTT fans."""
from homeassistant.components import fan from homeassistant.components import fan
from homeassistant.const import ATTR_ASSUMED_STATE, STATE_OFF, STATE_ON from homeassistant.const import (
ATTR_ASSUMED_STATE,
ATTR_SUPPORTED_FEATURES,
STATE_OFF,
STATE_ON,
)
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from .test_common import ( from .test_common import (
@ -278,6 +283,54 @@ async def test_sending_mqtt_commands_and_optimistic(hass, mqtt_mock):
assert state.attributes.get(ATTR_ASSUMED_STATE) assert state.attributes.get(ATTR_ASSUMED_STATE)
async def test_on_sending_mqtt_commands_and_optimistic(hass, mqtt_mock):
"""Test on with speed."""
assert await async_setup_component(
hass,
fan.DOMAIN,
{
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic",
"speed_command_topic": "speed-command-topic",
}
},
)
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
await common.async_turn_on(hass, "fan.test")
mqtt_mock.async_publish.assert_called_once_with("command-topic", "ON", 0, False)
mqtt_mock.async_publish.reset_mock()
state = hass.states.get("fan.test")
assert state.state is STATE_ON
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) is None
assert state.attributes.get(fan.ATTR_OSCILLATING) is None
await common.async_turn_off(hass, "fan.test")
mqtt_mock.async_publish.assert_called_once_with("command-topic", "OFF", 0, False)
mqtt_mock.async_publish.reset_mock()
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
await common.async_turn_on(hass, "fan.test", speed="low")
assert mqtt_mock.async_publish.call_count == 2
mqtt_mock.async_publish.assert_any_call("command-topic", "ON", 0, False)
mqtt_mock.async_publish.assert_any_call("speed-command-topic", "low", 0, False)
mqtt_mock.async_publish.reset_mock()
state = hass.states.get("fan.test")
assert state.state is STATE_ON
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) == "low"
assert state.attributes.get(fan.ATTR_OSCILLATING) is None
async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock): async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock):
"""Test optimistic mode with state topic.""" """Test optimistic mode with state topic."""
assert await async_setup_component( assert await async_setup_component(
@ -370,6 +423,171 @@ async def test_sending_mqtt_commands_and_explicit_optimistic(hass, mqtt_mock):
assert state.state is STATE_OFF assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE) assert state.attributes.get(ATTR_ASSUMED_STATE)
await common.async_set_speed(hass, "fan.test", "cUsToM")
mqtt_mock.async_publish.assert_called_once_with(
"speed-command-topic", "cUsToM", 0, False
)
mqtt_mock.async_publish.reset_mock()
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
async def test_attributes(hass, mqtt_mock):
"""Test attributes."""
assert await async_setup_component(
hass,
fan.DOMAIN,
{
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic",
"speed_command_topic": "speed-command-topic",
}
},
)
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(fan.ATTR_SPEED_LIST) == ["off", "low", "medium", "high"]
await common.async_turn_on(hass, "fan.test")
state = hass.states.get("fan.test")
assert state.state is STATE_ON
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) is None
assert state.attributes.get(fan.ATTR_OSCILLATING) is None
await common.async_turn_off(hass, "fan.test")
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) is None
assert state.attributes.get(fan.ATTR_OSCILLATING) is None
await common.async_oscillate(hass, "fan.test", True)
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) is None
assert state.attributes.get(fan.ATTR_OSCILLATING) is True
await common.async_oscillate(hass, "fan.test", False)
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) is None
assert state.attributes.get(fan.ATTR_OSCILLATING) is False
await common.async_set_speed(hass, "fan.test", fan.SPEED_LOW)
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) == "low"
assert state.attributes.get(fan.ATTR_OSCILLATING) is False
await common.async_set_speed(hass, "fan.test", fan.SPEED_MEDIUM)
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) == "medium"
assert state.attributes.get(fan.ATTR_OSCILLATING) is False
await common.async_set_speed(hass, "fan.test", fan.SPEED_HIGH)
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) == "high"
assert state.attributes.get(fan.ATTR_OSCILLATING) is False
await common.async_set_speed(hass, "fan.test", fan.SPEED_OFF)
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) == "off"
assert state.attributes.get(fan.ATTR_OSCILLATING) is False
await common.async_set_speed(hass, "fan.test", "cUsToM")
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(ATTR_ASSUMED_STATE)
assert state.attributes.get(fan.ATTR_SPEED) == "cUsToM"
assert state.attributes.get(fan.ATTR_OSCILLATING) is False
async def test_custom_speed_list(hass, mqtt_mock):
"""Test optimistic mode without state topic."""
assert await async_setup_component(
hass,
fan.DOMAIN,
{
fan.DOMAIN: {
"platform": "mqtt",
"name": "test",
"command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic",
"oscillation_state_topic": "oscillation-state-topic",
"speed_command_topic": "speed-command-topic",
"speed_state_topic": "speed-state-topic",
"speeds": ["off", "high"],
}
},
)
state = hass.states.get("fan.test")
assert state.state is STATE_OFF
assert state.attributes.get(fan.ATTR_SPEED_LIST) == ["off", "high"]
async def test_supported_features(hass, mqtt_mock):
"""Test optimistic mode without state topic."""
assert await async_setup_component(
hass,
fan.DOMAIN,
{
fan.DOMAIN: [
{
"platform": "mqtt",
"name": "test1",
"command_topic": "command-topic",
},
{
"platform": "mqtt",
"name": "test2",
"command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic",
},
{
"platform": "mqtt",
"name": "test3",
"command_topic": "command-topic",
"speed_command_topic": "speed-command-topic",
},
{
"platform": "mqtt",
"name": "test4",
"command_topic": "command-topic",
"oscillation_command_topic": "oscillation-command-topic",
"speed_command_topic": "speed-command-topic",
},
]
},
)
state = hass.states.get("fan.test1")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == 0
state = hass.states.get("fan.test2")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_OSCILLATE
state = hass.states.get("fan.test3")
assert state.attributes.get(ATTR_SUPPORTED_FEATURES) == fan.SUPPORT_SET_SPEED
state = hass.states.get("fan.test4")
assert (
state.attributes.get(ATTR_SUPPORTED_FEATURES)
== fan.SUPPORT_OSCILLATE | fan.SUPPORT_SET_SPEED
)
async def test_availability_without_topic(hass, mqtt_mock): async def test_availability_without_topic(hass, mqtt_mock):
"""Test availability without defined availability topic.""" """Test availability without defined availability topic."""

View file

@ -9,6 +9,7 @@ import pytest
import voluptuous as vol import voluptuous as vol
from homeassistant.components import mqtt, websocket_api from homeassistant.components import mqtt, websocket_api
from homeassistant.components.mqtt import debug_info
from homeassistant.components.mqtt.discovery import async_start from homeassistant.components.mqtt.discovery import async_start
from homeassistant.const import ( from homeassistant.const import (
ATTR_DOMAIN, ATTR_DOMAIN,
@ -36,6 +37,7 @@ from tests.common import (
mock_storage, mock_storage,
threadsafe_coroutine_factory, threadsafe_coroutine_factory,
) )
from tests.testing_config.custom_components.test.sensor import DEVICE_CLASSES
@pytest.fixture @pytest.fixture
@ -363,6 +365,56 @@ class TestMQTTCallbacks(unittest.TestCase):
self.hass.block_till_done() self.hass.block_till_done()
assert len(self.calls) == 1 assert len(self.calls) == 1
def test_subscribe_deprecated(self):
"""Test the subscription of a topic using deprecated callback signature."""
calls = []
@callback
def record_calls(topic, payload, qos):
"""Record calls."""
calls.append((topic, payload, qos))
unsub = mqtt.subscribe(self.hass, "test-topic", record_calls)
fire_mqtt_message(self.hass, "test-topic", "test-payload")
self.hass.block_till_done()
assert len(calls) == 1
assert calls[0][0] == "test-topic"
assert calls[0][1] == "test-payload"
unsub()
fire_mqtt_message(self.hass, "test-topic", "test-payload")
self.hass.block_till_done()
assert len(calls) == 1
def test_subscribe_deprecated_async(self):
"""Test the subscription of a topic using deprecated callback signature."""
calls = []
@callback
async def record_calls(topic, payload, qos):
"""Record calls."""
calls.append((topic, payload, qos))
unsub = mqtt.subscribe(self.hass, "test-topic", record_calls)
fire_mqtt_message(self.hass, "test-topic", "test-payload")
self.hass.block_till_done()
assert len(calls) == 1
assert calls[0][0] == "test-topic"
assert calls[0][1] == "test-payload"
unsub()
fire_mqtt_message(self.hass, "test-topic", "test-payload")
self.hass.block_till_done()
assert len(calls) == 1
def test_subscribe_topic_not_match(self): def test_subscribe_topic_not_match(self):
"""Test if subscribed topic is not a match.""" """Test if subscribed topic is not a match."""
mqtt.subscribe(self.hass, "test-topic", self.record_calls) mqtt.subscribe(self.hass, "test-topic", self.record_calls)
@ -988,3 +1040,198 @@ async def test_mqtt_ws_get_device_debug_info(
"triggers": [], "triggers": [],
} }
assert response["result"] == expected_result assert response["result"] == expected_result
async def test_debug_info_multiple_devices(hass, mqtt_mock):
"""Test we get correct debug_info when multiple devices are present."""
devices = [
{
"domain": "sensor",
"config": {
"device": {"identifiers": ["0AFFD0"]},
"platform": "mqtt",
"state_topic": "test-topic-sensor",
"unique_id": "unique",
},
},
{
"domain": "binary_sensor",
"config": {
"device": {"identifiers": ["0AFFD1"]},
"platform": "mqtt",
"state_topic": "test-topic-binary-sensor",
"unique_id": "unique",
},
},
{
"domain": "device_automation",
"config": {
"automation_type": "trigger",
"device": {"identifiers": ["0AFFD2"]},
"platform": "mqtt",
"topic": "test-topic1",
"type": "foo",
"subtype": "bar",
},
},
{
"domain": "device_automation",
"config": {
"automation_type": "trigger",
"device": {"identifiers": ["0AFFD3"]},
"platform": "mqtt",
"topic": "test-topic2",
"type": "ikk",
"subtype": "baz",
},
},
]
entry = MockConfigEntry(domain=mqtt.DOMAIN)
entry.add_to_hass(hass)
await async_start(hass, "homeassistant", {}, entry)
registry = await hass.helpers.device_registry.async_get_registry()
for d in devices:
data = json.dumps(d["config"])
domain = d["domain"]
id = d["config"]["device"]["identifiers"][0]
async_fire_mqtt_message(hass, f"homeassistant/{domain}/{id}/config", data)
await hass.async_block_till_done()
for d in devices:
domain = d["domain"]
id = d["config"]["device"]["identifiers"][0]
device = registry.async_get_device({("mqtt", id)}, set())
assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id)
if d["domain"] != "device_automation":
assert len(debug_info_data["entities"]) == 1
assert len(debug_info_data["triggers"]) == 0
discovery_data = debug_info_data["entities"][0]["discovery_data"]
assert len(debug_info_data["entities"][0]["topics"]) == 1
topic = d["config"]["state_topic"]
assert {"topic": topic, "messages": []} in debug_info_data["entities"][0][
"topics"
]
else:
assert len(debug_info_data["entities"]) == 0
assert len(debug_info_data["triggers"]) == 1
discovery_data = debug_info_data["triggers"][0]["discovery_data"]
assert discovery_data["topic"] == f"homeassistant/{domain}/{id}/config"
assert discovery_data["payload"] == d["config"]
async def test_debug_info_multiple_entities_triggers(hass, mqtt_mock):
"""Test we get correct debug_info for a device with multiple entities and triggers."""
config = [
{
"domain": "sensor",
"config": {
"device": {"identifiers": ["0AFFD0"]},
"platform": "mqtt",
"state_topic": "test-topic-sensor",
"unique_id": "unique",
},
},
{
"domain": "binary_sensor",
"config": {
"device": {"identifiers": ["0AFFD0"]},
"platform": "mqtt",
"state_topic": "test-topic-binary-sensor",
"unique_id": "unique",
},
},
{
"domain": "device_automation",
"config": {
"automation_type": "trigger",
"device": {"identifiers": ["0AFFD0"]},
"platform": "mqtt",
"topic": "test-topic1",
"type": "foo",
"subtype": "bar",
},
},
{
"domain": "device_automation",
"config": {
"automation_type": "trigger",
"device": {"identifiers": ["0AFFD0"]},
"platform": "mqtt",
"topic": "test-topic2",
"type": "ikk",
"subtype": "baz",
},
},
]
entry = MockConfigEntry(domain=mqtt.DOMAIN)
entry.add_to_hass(hass)
await async_start(hass, "homeassistant", {}, entry)
registry = await hass.helpers.device_registry.async_get_registry()
for c in config:
data = json.dumps(c["config"])
domain = c["domain"]
# Use topic as discovery_id
id = c["config"].get("topic", c["config"].get("state_topic"))
async_fire_mqtt_message(hass, f"homeassistant/{domain}/{id}/config", data)
await hass.async_block_till_done()
device_id = config[0]["config"]["device"]["identifiers"][0]
device = registry.async_get_device({("mqtt", device_id)}, set())
assert device is not None
debug_info_data = await debug_info.info_for_device(hass, device.id)
assert len(debug_info_data["entities"]) == 2
assert len(debug_info_data["triggers"]) == 2
for c in config:
# Test we get debug info for each entity and trigger
domain = c["domain"]
# Use topic as discovery_id
id = c["config"].get("topic", c["config"].get("state_topic"))
if c["domain"] != "device_automation":
discovery_data = [e["discovery_data"] for e in debug_info_data["entities"]]
topic = c["config"]["state_topic"]
assert {"topic": topic, "messages": []} in [
t for e in debug_info_data["entities"] for t in e["topics"]
]
else:
discovery_data = [e["discovery_data"] for e in debug_info_data["triggers"]]
assert {
"topic": f"homeassistant/{domain}/{id}/config",
"payload": c["config"],
} in discovery_data
async def test_debug_info_non_mqtt(hass, device_reg, entity_reg):
"""Test we get empty debug_info for a device with non MQTT entities."""
DOMAIN = "sensor"
platform = getattr(hass.components, f"test.{DOMAIN}")
platform.init()
config_entry = MockConfigEntry(domain="test", data={})
config_entry.add_to_hass(hass)
device_entry = device_reg.async_get_or_create(
config_entry_id=config_entry.entry_id,
connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
)
for device_class in DEVICE_CLASSES:
entity_reg.async_get_or_create(
DOMAIN,
"test",
platform.ENTITIES[device_class].unique_id,
device_id=device_entry.id,
)
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {"platform": "test"}})
debug_info_data = await debug_info.info_for_device(hass, device_entry.id)
assert len(debug_info_data["entities"]) == 0
assert len(debug_info_data["triggers"]) == 0

View file

@ -14,6 +14,7 @@ from homeassistant.components.vacuum import (
ATTR_BATTERY_ICON, ATTR_BATTERY_ICON,
ATTR_BATTERY_LEVEL, ATTR_BATTERY_LEVEL,
ATTR_FAN_SPEED, ATTR_FAN_SPEED,
ATTR_FAN_SPEED_LIST,
ATTR_STATUS, ATTR_STATUS,
) )
from homeassistant.const import CONF_NAME, CONF_PLATFORM, STATE_OFF, STATE_ON from homeassistant.const import CONF_NAME, CONF_PLATFORM, STATE_OFF, STATE_ON
@ -223,10 +224,20 @@ async def test_attributes_without_supported_features(hass, mqtt_mock):
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config}) assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
message = """{
"battery_level": 54,
"cleaning": true,
"docked": false,
"charging": false,
"fan_speed": "max"
}"""
async_fire_mqtt_message(hass, "vacuum/state", message)
state = hass.states.get("vacuum.mqtttest") state = hass.states.get("vacuum.mqtttest")
assert state.state == STATE_OFF assert state.state == STATE_ON
assert state.attributes.get(ATTR_BATTERY_LEVEL) is None assert state.attributes.get(ATTR_BATTERY_LEVEL) is None
assert state.attributes.get(ATTR_BATTERY_ICON) is None assert state.attributes.get(ATTR_BATTERY_ICON) is None
assert state.attributes.get(ATTR_FAN_SPEED) is None
assert state.attributes.get(ATTR_FAN_SPEED_LIST) is None
async def test_status(hass, mqtt_mock): async def test_status(hass, mqtt_mock):
@ -353,6 +364,36 @@ async def test_status_fan_speed(hass, mqtt_mock):
assert state.attributes.get(ATTR_FAN_SPEED) == "max" assert state.attributes.get(ATTR_FAN_SPEED) == "max"
async def test_status_fan_speed_list(hass, mqtt_mock):
"""Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG)
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
ALL_SERVICES, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
state = hass.states.get("vacuum.mqtttest")
assert state.attributes.get(ATTR_FAN_SPEED_LIST) == ["min", "medium", "high", "max"]
async def test_status_no_fan_speed_list(hass, mqtt_mock):
"""Test status updates from the vacuum.
If the vacuum doesn't support fan speed, fan speed list should be None.
"""
config = deepcopy(DEFAULT_CONFIG)
services = ALL_SERVICES - mqttvacuum.SUPPORT_FAN_SPEED
config[mqttvacuum.CONF_SUPPORTED_FEATURES] = services_to_strings(
services, SERVICE_TO_STRING
)
assert await async_setup_component(hass, vacuum.DOMAIN, {vacuum.DOMAIN: config})
state = hass.states.get("vacuum.mqtttest")
assert state.attributes.get(ATTR_FAN_SPEED_LIST) is None
async def test_status_error(hass, mqtt_mock): async def test_status_error(hass, mqtt_mock):
"""Test status updates from the vacuum.""" """Test status updates from the vacuum."""
config = deepcopy(DEFAULT_CONFIG) config = deepcopy(DEFAULT_CONFIG)