diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 0a2ee1eea9d..1c0df640c08 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -76,7 +76,10 @@ from .const import ( # noqa: F401 CONF_TLS_INSECURE, CONF_TLS_VERSION, CONF_TOPIC, + CONF_TRANSPORT, CONF_WILL_MESSAGE, + CONF_WS_HEADERS, + CONF_WS_PATH, DATA_MQTT, DEFAULT_ENCODING, DEFAULT_QOS, @@ -134,6 +137,9 @@ CONFIG_ENTRY_CONFIG_KEYS = [ CONF_PORT, CONF_PROTOCOL, CONF_TLS_INSECURE, + CONF_TRANSPORT, + CONF_WS_PATH, + CONF_WS_HEADERS, CONF_USERNAME, CONF_WILL_MESSAGE, ] diff --git a/homeassistant/components/mqtt/client.py b/homeassistant/components/mqtt/client.py index c94b5d5ec51..66f1e130ff7 100644 --- a/homeassistant/components/mqtt/client.py +++ b/homeassistant/components/mqtt/client.py @@ -51,14 +51,19 @@ from .const import ( CONF_CLIENT_KEY, CONF_KEEPALIVE, CONF_TLS_INSECURE, + CONF_TRANSPORT, CONF_WILL_MESSAGE, + CONF_WS_HEADERS, + CONF_WS_PATH, DEFAULT_ENCODING, DEFAULT_PROTOCOL, DEFAULT_QOS, + DEFAULT_TRANSPORT, MQTT_CONNECTED, MQTT_DISCONNECTED, PROTOCOL_5, PROTOCOL_31, + TRANSPORT_WEBSOCKETS, ) from .models import ( AsyncMessageCallbackType, @@ -284,7 +289,8 @@ class MqttClientSetup: # PAHO MQTT relies on the MQTT server to generate random client IDs. # However, that feature is not mandatory so we generate our own. client_id = mqtt.base62(uuid.uuid4().int, padding=22) - self._client = mqtt.Client(client_id, protocol=proto) + transport = config.get(CONF_TRANSPORT, DEFAULT_TRANSPORT) + self._client = mqtt.Client(client_id, protocol=proto, transport=transport) # Enable logging self._client.enable_logger() @@ -302,6 +308,10 @@ class MqttClientSetup: client_key = get_file_path(CONF_CLIENT_KEY, config.get(CONF_CLIENT_KEY)) client_cert = get_file_path(CONF_CLIENT_CERT, config.get(CONF_CLIENT_CERT)) tls_insecure = config.get(CONF_TLS_INSECURE) + if transport == TRANSPORT_WEBSOCKETS: + ws_path = config.get(CONF_WS_PATH) + ws_headers = config.get(CONF_WS_HEADERS) + self._client.ws_set_options(ws_path, ws_headers) if certificate is not None: self._client.tls_set( certificate, diff --git a/homeassistant/components/mqtt/config_flow.py b/homeassistant/components/mqtt/config_flow.py index 3d5ab99f340..b79ff30f111 100644 --- a/homeassistant/components/mqtt/config_flow.py +++ b/homeassistant/components/mqtt/config_flow.py @@ -27,6 +27,8 @@ from homeassistant.const import ( ) from homeassistant.core import HomeAssistant, callback from homeassistant.data_entry_flow import FlowResult +from homeassistant.helpers import config_validation as cv +from homeassistant.helpers.json import JSON_DECODE_EXCEPTIONS, json_dumps, json_loads from homeassistant.helpers.selector import ( BooleanSelector, FileSelector, @@ -58,7 +60,10 @@ from .const import ( CONF_DISCOVERY_PREFIX, CONF_KEEPALIVE, CONF_TLS_INSECURE, + CONF_TRANSPORT, CONF_WILL_MESSAGE, + CONF_WS_HEADERS, + CONF_WS_PATH, DEFAULT_BIRTH, DEFAULT_DISCOVERY, DEFAULT_ENCODING, @@ -66,9 +71,14 @@ from .const import ( DEFAULT_PORT, DEFAULT_PREFIX, DEFAULT_PROTOCOL, + DEFAULT_TRANSPORT, DEFAULT_WILL, + DEFAULT_WS_PATH, DOMAIN, SUPPORTED_PROTOCOLS, + SUPPORTED_TRANSPORTS, + TRANSPORT_TCP, + TRANSPORT_WEBSOCKETS, ) from .util import ( async_create_certificate_temp_files, @@ -109,6 +119,15 @@ PROTOCOL_SELECTOR = SelectSelector( mode=SelectSelectorMode.DROPDOWN, ) ) +TRANSPORT_SELECTOR = SelectSelector( + SelectSelectorConfig( + options=SUPPORTED_TRANSPORTS, + mode=SelectSelectorMode.DROPDOWN, + ) +) +WS_HEADERS_SELECTOR = TextSelector( + TextSelectorConfig(type=TextSelectorType.TEXT, multiline=True) +) CA_VERIFICATION_MODES = [ SelectOptionDict(value="off", label="Off"), SelectOptionDict(value="auto", label="Auto"), @@ -493,6 +512,8 @@ async def async_get_broker_settings( or not certificate and user_input.get(SET_CA_CERT, "off") == "custom" and not certificate_id + or user_input.get(CONF_TRANSPORT) == TRANSPORT_WEBSOCKETS + and CONF_WS_PATH not in user_input ): return False @@ -526,6 +547,23 @@ async def async_get_broker_settings( del validated_user_input[SET_CA_CERT] if SET_CLIENT_CERT in validated_user_input: del validated_user_input[SET_CLIENT_CERT] + if validated_user_input.get(CONF_TRANSPORT, TRANSPORT_TCP) == TRANSPORT_TCP: + if CONF_WS_PATH in validated_user_input: + del validated_user_input[CONF_WS_PATH] + if CONF_WS_HEADERS in validated_user_input: + del validated_user_input[CONF_WS_HEADERS] + return True + try: + validated_user_input[CONF_WS_HEADERS] = json_loads( + validated_user_input.get(CONF_WS_HEADERS, "{}") + ) + schema = vol.Schema({cv.string: cv.template}) + schema(validated_user_input[CONF_WS_HEADERS]) + except JSON_DECODE_EXCEPTIONS + ( # pylint: disable=wrong-exception-operation + vol.MultipleInvalid, + ): + errors["base"] = "bad_ws_headers" + return False return True if user_input: @@ -562,6 +600,13 @@ async def async_get_broker_settings( current_client_key = current_config.get(CONF_CLIENT_KEY) current_tls_insecure = current_config.get(CONF_TLS_INSECURE, False) current_protocol = current_config.get(CONF_PROTOCOL, DEFAULT_PROTOCOL) + current_transport = current_config.get(CONF_TRANSPORT, DEFAULT_TRANSPORT) + current_ws_path = current_config.get(CONF_WS_PATH, DEFAULT_WS_PATH) + current_ws_headers = ( + json_dumps(current_config.get(CONF_WS_HEADERS)) + if CONF_WS_HEADERS in current_config + else None + ) advanced_broker_options |= bool( current_client_id or current_keepalive != DEFAULT_KEEPALIVE @@ -572,6 +617,7 @@ async def async_get_broker_settings( or current_protocol != DEFAULT_PROTOCOL or current_config.get(SET_CA_CERT, "off") != "off" or current_config.get(SET_CLIENT_CERT) + or current_transport == TRANSPORT_WEBSOCKETS ) # Build form @@ -665,6 +711,21 @@ async def async_get_broker_settings( description={"suggested_value": current_protocol}, ) ] = PROTOCOL_SELECTOR + fields[ + vol.Optional( + CONF_TRANSPORT, + description={"suggested_value": current_transport}, + ) + ] = TRANSPORT_SELECTOR + if current_transport == TRANSPORT_WEBSOCKETS: + fields[ + vol.Optional(CONF_WS_PATH, description={"suggested_value": current_ws_path}) + ] = TEXT_SELECTOR + fields[ + vol.Optional( + CONF_WS_HEADERS, description={"suggested_value": current_ws_headers} + ) + ] = WS_HEADERS_SELECTOR # Show form return False diff --git a/homeassistant/components/mqtt/config_integration.py b/homeassistant/components/mqtt/config_integration.py index 9319a48ac3d..d487f1b382f 100644 --- a/homeassistant/components/mqtt/config_integration.py +++ b/homeassistant/components/mqtt/config_integration.py @@ -45,15 +45,21 @@ from .const import ( CONF_KEEPALIVE, CONF_TLS_INSECURE, CONF_TLS_VERSION, + CONF_TRANSPORT, CONF_WILL_MESSAGE, + CONF_WS_HEADERS, + CONF_WS_PATH, DEFAULT_BIRTH, DEFAULT_DISCOVERY, DEFAULT_KEEPALIVE, DEFAULT_PORT, DEFAULT_PREFIX, DEFAULT_PROTOCOL, + DEFAULT_TRANSPORT, DEFAULT_WILL, SUPPORTED_PROTOCOLS, + TRANSPORT_TCP, + TRANSPORT_WEBSOCKETS, ) from .util import valid_birth_will, valid_publish_topic @@ -66,6 +72,7 @@ DEFAULT_VALUES = { CONF_PORT: DEFAULT_PORT, CONF_PROTOCOL: DEFAULT_PROTOCOL, CONF_TLS_VERSION: DEFAULT_TLS_PROTOCOL, + CONF_TRANSPORT: DEFAULT_TRANSPORT, CONF_WILL_MESSAGE: DEFAULT_WILL, CONF_KEEPALIVE: DEFAULT_KEEPALIVE, } @@ -160,6 +167,11 @@ CONFIG_SCHEMA_ENTRY = vol.Schema( # discovery_prefix must be a valid publish topic because if no # state topic is specified, it will be created with the given prefix. vol.Optional(CONF_DISCOVERY_PREFIX): valid_publish_topic, + vol.Optional(CONF_TRANSPORT, default=DEFAULT_TRANSPORT): vol.All( + cv.string, vol.In([TRANSPORT_TCP, TRANSPORT_WEBSOCKETS]) + ), + vol.Optional(CONF_WS_PATH, default="/"): cv.string, + vol.Optional(CONF_WS_HEADERS, default={}): {cv.string: cv.string}, } ) diff --git a/homeassistant/components/mqtt/const.py b/homeassistant/components/mqtt/const.py index cbcc23bb1a9..7b4c7378b9e 100644 --- a/homeassistant/components/mqtt/const.py +++ b/homeassistant/components/mqtt/const.py @@ -23,6 +23,9 @@ CONF_SCHEMA = "schema" CONF_STATE_TOPIC = "state_topic" CONF_STATE_VALUE_TEMPLATE = "state_value_template" CONF_TOPIC = "topic" +CONF_TRANSPORT = "transport" +CONF_WS_PATH = "ws_path" +CONF_WS_HEADERS = "ws_headers" CONF_WILL_MESSAGE = "will_message" CONF_CERTIFICATE = "certificate" @@ -42,15 +45,21 @@ DEFAULT_PAYLOAD_AVAILABLE = "online" DEFAULT_PAYLOAD_NOT_AVAILABLE = "offline" DEFAULT_PORT = 1883 DEFAULT_RETAIN = False +DEFAULT_WS_PATH = "/" PROTOCOL_31 = "3.1" PROTOCOL_311 = "3.1.1" PROTOCOL_5 = "5" SUPPORTED_PROTOCOLS = [PROTOCOL_31, PROTOCOL_311, PROTOCOL_5] +TRANSPORT_TCP = "tcp" +TRANSPORT_WEBSOCKETS = "websockets" +SUPPORTED_TRANSPORTS = [TRANSPORT_TCP, TRANSPORT_WEBSOCKETS] + DEFAULT_PORT = 1883 DEFAULT_KEEPALIVE = 60 DEFAULT_PROTOCOL = PROTOCOL_311 +DEFAULT_TRANSPORT = TRANSPORT_TCP DEFAULT_BIRTH = { ATTR_TOPIC: DEFAULT_BIRTH_WILL_TOPIC, diff --git a/homeassistant/components/mqtt/strings.json b/homeassistant/components/mqtt/strings.json index 5788597e9a9..30cb57bb85a 100644 --- a/homeassistant/components/mqtt/strings.json +++ b/homeassistant/components/mqtt/strings.json @@ -27,7 +27,10 @@ "tls_insecure": "Ignore broker certificate validation", "protocol": "MQTT protocol", "set_ca_cert": "Broker certificate validation", - "set_client_cert": "Use a client certificate" + "set_client_cert": "Use a client certificate", + "transport": "MQTT transport", + "ws_headers": "WebSocket headers in JSON format", + "ws_path": "WebSocket path" } }, "hassio_confirm": { @@ -50,6 +53,7 @@ "bad_client_cert": "Invalid client certificate, ensure a PEM coded file is supplied", "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_client_cert_key": "Client certificate and private are no valid pair", + "bad_ws_headers": "Supply valid HTTP headers as a JSON object", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_inclusion": "The client certificate and private key must be configurered together" } @@ -95,7 +99,10 @@ "tls_insecure": "[%key:component::mqtt::config::step::broker::data::tls_insecure%]", "protocol": "[%key:component::mqtt::config::step::broker::data::protocol%]", "set_ca_cert": "[%key:component::mqtt::config::step::broker::data::set_ca_cert%]", - "set_client_cert": "[%key:component::mqtt::config::step::broker::data::set_client_cert%]" + "set_client_cert": "[%key:component::mqtt::config::step::broker::data::set_client_cert%]", + "transport": "[%key:component::mqtt::config::step::broker::data::transport%]", + "ws_headers": "[%key:component::mqtt::config::step::broker::data::ws_headers%]", + "ws_path": "[%key:component::mqtt::config::step::broker::data::ws_path%]" } }, "options": { @@ -125,6 +132,7 @@ "bad_client_cert": "[%key:component::mqtt::config::error::bad_client_cert%]", "bad_client_key": "[%key:component::mqtt::config::error::bad_client_key%]", "bad_client_cert_key": "[%key:component::mqtt::config::error::bad_client_cert_key%]", + "bad_ws_headers": "[%key:component::mqtt::config::error::bad_ws_headers%]", "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", "invalid_inclusion": "[%key:component::mqtt::config::error::invalid_inclusion%]" } diff --git a/homeassistant/components/mqtt/translations/en.json b/homeassistant/components/mqtt/translations/en.json index 6ab75c6a30d..51e7918b5cc 100644 --- a/homeassistant/components/mqtt/translations/en.json +++ b/homeassistant/components/mqtt/translations/en.json @@ -12,6 +12,7 @@ "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_discovery_prefix": "Invalid discovery prefix", "bad_will": "Invalid will topic", + "bad_ws_headers": "Supply valid HTTP headers as a JSON object", "cannot_connect": "Failed to connect", "invalid_inclusion": "The client certificate and private key must be configurered together" }, @@ -32,7 +33,10 @@ "set_ca_cert": "Broker certificate validation", "set_client_cert": "Use a client certificate", "tls_insecure": "Ignore broker certificate validation", - "username": "Username" + "transport": "MQTT transport", + "username": "Username", + "ws_path": "WebSocket path", + "ws_headers": "WebSocket headers" }, "description": "Please enter the connection information of your MQTT broker." }, @@ -86,6 +90,7 @@ "bad_client_key": "Invalid private key, ensure a PEM coded file is supplied without password", "bad_discovery_prefix": "Invalid discovery prefix", "bad_will": "Invalid will topic", + "bad_ws_headers": "Supply valid HTTP headers as a JSON object", "cannot_connect": "Failed to connect", "invalid_inclusion": "The client certificate and private key must be configurered together" }, @@ -105,7 +110,10 @@ "set_ca_cert": "Broker certificate validation", "set_client_cert": "Use a client certificate", "tls_insecure": "Ignore broker certificate validation", - "username": "Username" + "transport": "MQTT transport", + "username": "Username", + "ws_path": "WebSocket path", + "ws_headers": "WebSocket headers" }, "description": "Please enter the connection information of your MQTT broker.", "title": "Broker options" diff --git a/tests/components/mqtt/test_config_flow.py b/tests/components/mqtt/test_config_flow.py index b9fdf0b9c26..818cdcf33a6 100644 --- a/tests/components/mqtt/test_config_flow.py +++ b/tests/components/mqtt/test_config_flow.py @@ -1140,7 +1140,6 @@ async def test_options_bad_will_message_fails(hass, mock_try_connection): async def test_try_connection_with_advanced_parameters( hass, - mqtt_mock_entry_with_yaml_config, mock_try_connection_success, tmp_path, mock_ssl_context, @@ -1171,6 +1170,9 @@ async def test_try_connection_with_advanced_parameters( mqtt.CONF_PORT: 1234, mqtt.CONF_USERNAME: "user", mqtt.CONF_PASSWORD: "pass", + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/path/", + mqtt.CONF_WS_HEADERS: {"h1": "v1", "h2": "v2"}, mqtt.CONF_KEEPALIVE: 30, mqtt.CONF_DISCOVERY: True, mqtt.CONF_BIRTH_MESSAGE: { @@ -1205,6 +1207,9 @@ async def test_try_connection_with_advanced_parameters( mqtt.CONF_PASSWORD: "pass", mqtt.CONF_TLS_INSECURE: True, mqtt.CONF_PROTOCOL: "3.1.1", + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/path/", + mqtt.CONF_WS_HEADERS: '{"h1":"v1","h2":"v2"}', } for k, v in defaults.items(): assert get_default(result["data_schema"].schema, k) == v @@ -1220,7 +1225,7 @@ async def test_try_connection_with_advanced_parameters( ) assert config_entry.data[mqtt.CONF_CERTIFICATE] == "auto" - # test we can chante username and password + # test we can change username and password # as it was configured as auto in configuration.yaml is is migrated now mock_try_connection_success.reset_mock() result = await hass.config_entries.options.async_configure( @@ -1233,6 +1238,9 @@ async def test_try_connection_with_advanced_parameters( "set_ca_cert": "auto", "set_client_cert": True, mqtt.CONF_TLS_INSECURE: True, + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/new/path", + mqtt.CONF_WS_HEADERS: '{"h3": "v3"}', }, ) assert result["type"] == data_entry_flow.FlowResultType.FORM @@ -1256,6 +1264,12 @@ async def test_try_connection_with_advanced_parameters( "keyfile" ] == mqtt.util.get_file_path(mqtt.CONF_CLIENT_KEY) + # check if websockets options are set + assert mock_try_connection_success.ws_set_options.mock_calls[0][1] == ( + "/new/path", + {"h3": "v3"}, + ) + # Accept default option result = await hass.config_entries.options.async_configure( result["flow_id"], @@ -1305,6 +1319,7 @@ async def test_setup_with_advanced_settings( assert result["data_schema"].schema["set_ca_cert"] assert result["data_schema"].schema[mqtt.CONF_TLS_INSECURE] assert result["data_schema"].schema[mqtt.CONF_PROTOCOL] + assert result["data_schema"].schema[mqtt.CONF_TRANSPORT] assert mqtt.CONF_CLIENT_CERT not in result["data_schema"].schema assert mqtt.CONF_CLIENT_KEY not in result["data_schema"].schema @@ -1320,6 +1335,8 @@ async def test_setup_with_advanced_settings( "set_ca_cert": "auto", "set_client_cert": True, mqtt.CONF_TLS_INSECURE: True, + mqtt.CONF_PROTOCOL: "3.1.1", + mqtt.CONF_TRANSPORT: "websockets", }, ) assert result["type"] == "form" @@ -1333,8 +1350,11 @@ async def test_setup_with_advanced_settings( assert result["data_schema"].schema[mqtt.CONF_PROTOCOL] assert result["data_schema"].schema[mqtt.CONF_CLIENT_CERT] assert result["data_schema"].schema[mqtt.CONF_CLIENT_KEY] + assert result["data_schema"].schema[mqtt.CONF_TRANSPORT] + assert result["data_schema"].schema[mqtt.CONF_WS_PATH] + assert result["data_schema"].schema[mqtt.CONF_WS_HEADERS] - # third iteration, advanced settings with client cert and key set + # third iteration, advanced settings with client cert and key set and bad json payload result = await hass.config_entries.options.async_configure( result["flow_id"], user_input={ @@ -1348,6 +1368,34 @@ async def test_setup_with_advanced_settings( mqtt.CONF_CLIENT_CERT: file_id[mqtt.CONF_CLIENT_CERT], mqtt.CONF_CLIENT_KEY: file_id[mqtt.CONF_CLIENT_KEY], mqtt.CONF_TLS_INSECURE: True, + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/custom_path/", + mqtt.CONF_WS_HEADERS: '{"header_1": "content_header_1", "header_2": "content_header_2"', + }, + ) + + assert result["type"] == "form" + assert result["step_id"] == "broker" + assert result["errors"]["base"] == "bad_ws_headers" + + # fourth iteration, advanced settings with client cert and key set + # and correct json payload for ws_headers + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + mqtt.CONF_BROKER: "test-broker", + mqtt.CONF_PORT: 2345, + mqtt.CONF_USERNAME: "user", + mqtt.CONF_PASSWORD: "secret", + mqtt.CONF_KEEPALIVE: 30, + "set_ca_cert": "auto", + "set_client_cert": True, + mqtt.CONF_CLIENT_CERT: file_id[mqtt.CONF_CLIENT_CERT], + mqtt.CONF_CLIENT_KEY: file_id[mqtt.CONF_CLIENT_KEY], + mqtt.CONF_TLS_INSECURE: True, + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/custom_path/", + mqtt.CONF_WS_HEADERS: '{"header_1": "content_header_1", "header_2": "content_header_2"}', }, ) @@ -1362,3 +1410,80 @@ async def test_setup_with_advanced_settings( }, ) assert result["type"] == "create_entry" + + # Check config entry result + assert config_entry.data == { + mqtt.CONF_BROKER: "test-broker", + mqtt.CONF_PORT: 2345, + mqtt.CONF_USERNAME: "user", + mqtt.CONF_PASSWORD: "secret", + mqtt.CONF_KEEPALIVE: 30, + mqtt.CONF_CLIENT_CERT: "## mock client certificate file ##", + mqtt.CONF_CLIENT_KEY: "## mock key file ##", + "tls_insecure": True, + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_PATH: "/custom_path/", + mqtt.CONF_WS_HEADERS: { + "header_1": "content_header_1", + "header_2": "content_header_2", + }, + mqtt.CONF_CERTIFICATE: "auto", + mqtt.CONF_DISCOVERY: True, + mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test", + } + + +async def test_change_websockets_transport_to_tcp( + hass, mock_try_connection, tmp_path, mock_ssl_context, mock_process_uploaded_file +): + """Test option flow setup with websockets transport settings.""" + config_entry = MockConfigEntry(domain=mqtt.DOMAIN) + config_entry.add_to_hass(hass) + config_entry.data = { + mqtt.CONF_BROKER: "test-broker", + mqtt.CONF_PORT: 1234, + mqtt.CONF_TRANSPORT: "websockets", + mqtt.CONF_WS_HEADERS: {"header_1": "custom_header1"}, + mqtt.CONF_WS_PATH: "/some_path", + } + + mock_try_connection.return_value = True + + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == "form" + assert result["step_id"] == "broker" + assert result["data_schema"].schema["transport"] + assert result["data_schema"].schema["ws_path"] + assert result["data_schema"].schema["ws_headers"] + + # Change transport to tcp + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + mqtt.CONF_BROKER: "test-broker", + mqtt.CONF_PORT: 1234, + mqtt.CONF_TRANSPORT: "tcp", + mqtt.CONF_WS_HEADERS: '{"header_1": "custom_header1"}', + mqtt.CONF_WS_PATH: "/some_path", + }, + ) + assert result["type"] == "form" + assert result["step_id"] == "options" + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={ + mqtt.CONF_DISCOVERY: True, + mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test", + }, + ) + assert result["type"] == "create_entry" + + # Check config entry result + assert config_entry.data == { + mqtt.CONF_BROKER: "test-broker", + mqtt.CONF_PORT: 1234, + mqtt.CONF_TRANSPORT: "tcp", + mqtt.CONF_DISCOVERY: True, + mqtt.CONF_DISCOVERY_PREFIX: "homeassistant_test", + } diff --git a/tests/components/mqtt/test_diagnostics.py b/tests/components/mqtt/test_diagnostics.py index 49d3fe0b4b6..7512a46802f 100644 --- a/tests/components/mqtt/test_diagnostics.py +++ b/tests/components/mqtt/test_diagnostics.py @@ -23,6 +23,9 @@ default_config = { "port": 1883, "protocol": "3.1.1", "tls_version": "auto", + "transport": "tcp", + "ws_headers": {}, + "ws_path": "/", "will_message": { "payload": "offline", "qos": 0, diff --git a/tests/components/mqtt/test_discovery.py b/tests/components/mqtt/test_discovery.py index 77d7093830e..48c1d88a9a8 100644 --- a/tests/components/mqtt/test_discovery.py +++ b/tests/components/mqtt/test_discovery.py @@ -1179,7 +1179,10 @@ ABBREVIATIONS_WHITE_LIST = [ "CONF_KEEPALIVE", "CONF_TLS_INSECURE", "CONF_TLS_VERSION", + "CONF_TRANSPORT", "CONF_WILL_MESSAGE", + "CONF_WS_PATH", + "CONF_WS_HEADERS", # Undocumented device configuration "CONF_DEPRECATED_VIA_HUB", "CONF_VIA_DEVICE",