From 410f0e36049b20fbae5fff8a5e4f52ab64d58b21 Mon Sep 17 00:00:00 2001 From: Martin Hjelmare Date: Tue, 20 Apr 2021 18:21:38 +0200 Subject: [PATCH] Fix mysensors mqtt integration setup guard (#49423) --- .../components/mysensors/config_flow.py | 15 ++++++++-- homeassistant/components/mysensors/const.py | 1 - homeassistant/components/mysensors/gateway.py | 8 ++++-- .../components/mysensors/strings.json | 5 ++-- .../components/mysensors/translations/en.json | 1 + tests/components/mysensors/conftest.py | 10 +++++++ .../components/mysensors/test_config_flow.py | 28 ++++++++++++++++--- tests/components/mysensors/test_init.py | 4 ++- 8 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 tests/components/mysensors/conftest.py diff --git a/homeassistant/components/mysensors/config_flow.py b/homeassistant/components/mysensors/config_flow.py index bdf1b9392a8..4fd52f29bf3 100644 --- a/homeassistant/components/mysensors/config_flow.py +++ b/homeassistant/components/mysensors/config_flow.py @@ -14,7 +14,11 @@ from awesomeversion import ( import voluptuous as vol from homeassistant import config_entries -from homeassistant.components.mqtt import valid_publish_topic, valid_subscribe_topic +from homeassistant.components.mqtt import ( + DOMAIN as MQTT_DOMAIN, + valid_publish_topic, + valid_subscribe_topic, +) from homeassistant.components.mysensors import ( CONF_DEVICE, DEFAULT_BAUD_RATE, @@ -135,18 +139,23 @@ class MySensorsConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Create a config entry from frontend user input.""" schema = {vol.Required(CONF_GATEWAY_TYPE): vol.In(CONF_GATEWAY_TYPE_ALL)} schema = vol.Schema(schema) + errors = {} if user_input is not None: gw_type = self._gw_type = user_input[CONF_GATEWAY_TYPE] input_pass = user_input if CONF_DEVICE in user_input else None if gw_type == CONF_GATEWAY_TYPE_MQTT: - return await self.async_step_gw_mqtt(input_pass) + # Naive check that doesn't consider config entry state. + if MQTT_DOMAIN in self.hass.config.components: + return await self.async_step_gw_mqtt(input_pass) + + errors["base"] = "mqtt_required" if gw_type == CONF_GATEWAY_TYPE_TCP: return await self.async_step_gw_tcp(input_pass) if gw_type == CONF_GATEWAY_TYPE_SERIAL: return await self.async_step_gw_serial(input_pass) - return self.async_show_form(step_id="user", data_schema=schema) + return self.async_show_form(step_id="user", data_schema=schema, errors=errors) async def async_step_gw_serial(self, user_input: dict[str, str] | None = None): """Create config entry for a serial gateway.""" diff --git a/homeassistant/components/mysensors/const.py b/homeassistant/components/mysensors/const.py index 7a9027d9b72..1bd071be9a9 100644 --- a/homeassistant/components/mysensors/const.py +++ b/homeassistant/components/mysensors/const.py @@ -29,7 +29,6 @@ CONF_GATEWAY_TYPE_ALL: list[str] = [ CONF_GATEWAY_TYPE_TCP, ] - DOMAIN: str = "mysensors" MYSENSORS_GATEWAY_START_TASK: str = "mysensors_gateway_start_task_{}" MYSENSORS_GATEWAYS: str = "mysensors_gateways" diff --git a/homeassistant/components/mysensors/gateway.py b/homeassistant/components/mysensors/gateway.py index dc6caa93949..0d800e0215e 100644 --- a/homeassistant/components/mysensors/gateway.py +++ b/homeassistant/components/mysensors/gateway.py @@ -13,6 +13,7 @@ import async_timeout from mysensors import BaseAsyncGateway, Message, Sensor, mysensors import voluptuous as vol +from homeassistant.components.mqtt import DOMAIN as MQTT_DOMAIN from homeassistant.config_entries import ConfigEntry from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.core import Event, callback @@ -163,9 +164,10 @@ async def _get_gateway( persistence_file = hass.config.path(persistence_file) if device == MQTT_COMPONENT: - # what is the purpose of this? - # if not await async_setup_component(hass, MQTT_COMPONENT, entry): - # return None + # Make sure the mqtt integration is set up. + # Naive check that doesn't consider config entry state. + if MQTT_DOMAIN not in hass.config.components: + return None mqtt = hass.components.mqtt def pub_callback(topic, payload, qos, retain): diff --git a/homeassistant/components/mysensors/strings.json b/homeassistant/components/mysensors/strings.json index 43a68f61e24..54821877b4f 100644 --- a/homeassistant/components/mysensors/strings.json +++ b/homeassistant/components/mysensors/strings.json @@ -41,7 +41,7 @@ "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", - "invalid_subscribe_topic": "Invalid subscribe topic", + "invalid_subscribe_topic": "Invalid subscribe topic", "invalid_publish_topic": "Invalid publish topic", "duplicate_topic": "Topic already in use", "same_topic": "Subscribe and publish topics are the same", @@ -52,6 +52,7 @@ "invalid_serial": "Invalid serial port", "invalid_device": "Invalid device", "invalid_version": "Invalid MySensors version", + "mqtt_required": "The MQTT integration is not set up", "not_a_number": "Please enter a number", "port_out_of_range": "Port number must be at least 1 and at most 65535", "unknown": "[%key:common::config_flow::error::unknown%]" @@ -60,7 +61,7 @@ "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_auth": "[%key:common::config_flow::error::invalid_auth%]", - "invalid_subscribe_topic": "Invalid subscribe topic", + "invalid_subscribe_topic": "Invalid subscribe topic", "invalid_publish_topic": "Invalid publish topic", "duplicate_topic": "Topic already in use", "same_topic": "Subscribe and publish topics are the same", diff --git a/homeassistant/components/mysensors/translations/en.json b/homeassistant/components/mysensors/translations/en.json index 63af85488f0..7ca3516e50d 100644 --- a/homeassistant/components/mysensors/translations/en.json +++ b/homeassistant/components/mysensors/translations/en.json @@ -33,6 +33,7 @@ "invalid_serial": "Invalid serial port", "invalid_subscribe_topic": "Invalid subscribe topic", "invalid_version": "Invalid MySensors version", + "mqtt_required": "The MQTT integration is not set up", "not_a_number": "Please enter a number", "port_out_of_range": "Port number must be at least 1 and at most 65535", "same_topic": "Subscribe and publish topics are the same", diff --git a/tests/components/mysensors/conftest.py b/tests/components/mysensors/conftest.py new file mode 100644 index 00000000000..7a4733e8ce2 --- /dev/null +++ b/tests/components/mysensors/conftest.py @@ -0,0 +1,10 @@ +"""Provide common mysensors fixtures.""" +import pytest + +from homeassistant.components.mqtt import DOMAIN as MQTT_DOMAIN + + +@pytest.fixture(name="mqtt") +async def mock_mqtt_fixture(hass): + """Mock the MQTT integration.""" + hass.config.components.add(MQTT_DOMAIN) diff --git a/tests/components/mysensors/test_config_flow.py b/tests/components/mysensors/test_config_flow.py index e4c4016d11a..a91159e4395 100644 --- a/tests/components/mysensors/test_config_flow.py +++ b/tests/components/mysensors/test_config_flow.py @@ -50,7 +50,7 @@ async def get_form( return result -async def test_config_mqtt(hass: HomeAssistantType): +async def test_config_mqtt(hass: HomeAssistantType, mqtt: None) -> None: """Test configuring a mqtt gateway.""" step = await get_form(hass, CONF_GATEWAY_TYPE_MQTT, "gw_mqtt") flow_id = step["flow_id"] @@ -88,6 +88,24 @@ async def test_config_mqtt(hass: HomeAssistantType): assert len(mock_setup_entry.mock_calls) == 1 +async def test_missing_mqtt(hass: HomeAssistantType) -> None: + """Test configuring a mqtt gateway without mqtt integration setup.""" + await setup.async_setup_component(hass, "persistent_notification", {}) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert not result["errors"] + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT}, + ) + assert result["step_id"] == "user" + assert result["type"] == "form" + assert result["errors"] == {"base": "mqtt_required"} + + async def test_config_serial(hass: HomeAssistantType): """Test configuring a gateway via serial.""" step = await get_form(hass, CONF_GATEWAY_TYPE_SERIAL, "gw_serial") @@ -348,12 +366,13 @@ async def test_fail_to_connect(hass: HomeAssistantType): ) async def test_config_invalid( hass: HomeAssistantType, + mqtt: config_entries.ConfigEntry, gateway_type: ConfGatewayType, expected_step_id: str, user_input: dict[str, any], err_field, err_string, -): +) -> None: """Perform a test that is expected to generate an error.""" step = await get_form(hass, gateway_type, expected_step_id) flow_id = step["flow_id"] @@ -421,7 +440,7 @@ async def test_config_invalid( }, ], ) -async def test_import(hass: HomeAssistantType, user_input: dict): +async def test_import(hass: HomeAssistantType, mqtt: None, user_input: dict) -> None: """Test importing a gateway.""" await setup.async_setup_component(hass, "persistent_notification", {}) @@ -713,10 +732,11 @@ async def test_import(hass: HomeAssistantType, user_input: dict): ) async def test_duplicate( hass: HomeAssistantType, + mqtt: None, first_input: dict, second_input: dict, expected_result: tuple[str, str] | None, -): +) -> None: """Test duplicate detection.""" await setup.async_setup_component(hass, "persistent_notification", {}) diff --git a/tests/components/mysensors/test_init.py b/tests/components/mysensors/test_init.py index c85c627df9f..780621112ab 100644 --- a/tests/components/mysensors/test_init.py +++ b/tests/components/mysensors/test_init.py @@ -227,12 +227,14 @@ from homeassistant.setup import async_setup_component ) async def test_import( hass: HomeAssistantType, + mqtt: None, config: ConfigType, expected_calls: int, expected_to_succeed: bool, expected_config_flow_user_input: dict[str, any], -): +) -> None: """Test importing a gateway.""" + await async_setup_component(hass, "persistent_notification", {}) with patch("sys.platform", "win32"), patch( "homeassistant.components.mysensors.config_flow.try_connect", return_value=True ), patch(