mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +00:00
Add Mqtt WebSockets support (#82078)
* Add Mqtt WebSockets support * Fix tests * Add testing websockets options * Add tests transport settings * Do not use templates for ws_headers * Use json helper - small corrections
This commit is contained in:
parent
4ea9926497
commit
32d68f375b
@ -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,
|
||||
]
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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},
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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%]"
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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",
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user