1 """Config flow for MQTT."""
3 from __future__
import annotations
6 from collections
import OrderedDict
7 from collections.abc
import Callable, Mapping
10 from ssl
import PROTOCOL_TLS_CLIENT, SSLContext, SSLError
11 from types
import MappingProxyType
12 from typing
import TYPE_CHECKING, Any
14 from cryptography.hazmat.primitives.serialization
import load_pem_private_key
15 from cryptography.x509
import load_pem_x509_certificate
16 import voluptuous
as vol
59 from .addon
import get_addon_manager
60 from .client
import MqttClientSetup
71 CONF_DISCOVERY_PREFIX,
94 async_create_certificate_temp_files,
100 _LOGGER = logging.getLogger(__name__)
102 ADDON_SETUP_TIMEOUT = 5
103 ADDON_SETUP_TIMEOUT_ROUNDS = 5
107 ADVANCED_OPTIONS =
"advanced_options"
108 SET_CA_CERT =
"set_ca_cert"
109 SET_CLIENT_CERT =
"set_client_cert"
114 PORT_SELECTOR = vol.All(
119 QOS_SELECTOR = vol.All(
123 KEEPALIVE_SELECTOR = vol.All(
126 mode=NumberSelectorMode.BOX, min=15, step=
"any", unit_of_measurement=
"sec"
133 options=SUPPORTED_PROTOCOLS,
134 mode=SelectSelectorMode.DROPDOWN,
137 SUPPORTED_TRANSPORTS = [
143 options=SUPPORTED_TRANSPORTS,
144 mode=SelectSelectorMode.DROPDOWN,
150 CA_VERIFICATION_MODES = [
157 options=CA_VERIFICATION_MODES,
158 mode=SelectSelectorMode.DROPDOWN,
159 translation_key=SET_CA_CERT,
172 REAUTH_SCHEMA = vol.Schema(
174 vol.Required(CONF_USERNAME): TEXT_SELECTOR,
175 vol.Required(CONF_PASSWORD): PASSWORD_SELECTOR,
178 PWD_NOT_CHANGED =
"__**password_not_changed**__"
183 entry_password: str |
None, user_input: dict[str, Any]
185 """Update the password if the entry has been updated.
187 As we want to avoid reflecting the stored password in the UI,
188 we replace the suggested value in the UI with a sentitel,
189 and we change it back here if it was changed.
191 substituted_used_data =
dict(user_input)
193 user_password: str |
None = substituted_used_data.pop(CONF_PASSWORD,
None)
197 password_changed = user_password
is not None and user_password != PWD_NOT_CHANGED
198 password = user_password
if password_changed
else entry_password
199 if password
is not None:
200 substituted_used_data[CONF_PASSWORD] = password
201 return substituted_used_data
205 """Handle a config flow."""
209 _hassio_discovery: dict[str, Any] |
None =
None
210 _addon_manager: AddonManager
213 """Set up flow instance."""
214 self.
install_taskinstall_task: asyncio.Task |
None =
None
215 self.
start_taskstart_task: asyncio.Task |
None =
None
220 config_entry: ConfigEntry,
221 ) -> MQTTOptionsFlowHandler:
222 """Get the options flow for this handler."""
226 """Install the Mosquitto Mqtt broker add-on."""
228 await addon_manager.async_schedule_install_addon()
231 self, user_input: dict[str, Any] |
None =
None
232 ) -> ConfigFlowResult:
233 """Add-on installation failed."""
235 reason=
"addon_install_failed",
236 description_placeholders={
"addon": self.
_addon_manager_addon_manager.addon_name},
240 self, user_input: dict[str, Any] |
None =
None
241 ) -> ConfigFlowResult:
242 """Install Mosquitto Broker add-on."""
248 step_id=
"install_addon",
249 progress_action=
"install_addon",
255 except AddonError
as err:
264 self, user_input: dict[str, Any] |
None =
None
265 ) -> ConfigFlowResult:
266 """Add-on start failed."""
268 reason=
"addon_start_failed",
269 description_placeholders={
"addon": self.
_addon_manager_addon_manager.addon_name},
273 self, user_input: dict[str, Any] |
None =
None
274 ) -> ConfigFlowResult:
275 """Start Mosquitto Broker add-on."""
280 step_id=
"start_addon",
281 progress_action=
"start_addon",
286 except AddonError
as err:
295 """Get the MQTT add-on discovery info and try the connection."""
300 addon_discovery_config = (
301 await addon_manager.async_get_addon_discovery_info()
303 config: dict[str, Any] = {
304 CONF_BROKER: addon_discovery_config[CONF_HOST],
305 CONF_PORT: addon_discovery_config[CONF_PORT],
306 CONF_USERNAME: addon_discovery_config.get(CONF_USERNAME),
307 CONF_PASSWORD: addon_discovery_config.get(CONF_PASSWORD),
308 CONF_DISCOVERY: DEFAULT_DISCOVERY,
313 if await self.hass.async_add_executor_job(
322 """Start the Mosquitto Broker add-on."""
324 await addon_manager.async_schedule_start_addon()
327 for _
in range(ADDON_SETUP_TIMEOUT_ROUNDS):
328 await asyncio.sleep(ADDON_SETUP_TIMEOUT)
334 f
"Failed to correctly start {addon_manager.addon_name} add-on"
338 self, user_input: dict[str, Any] |
None =
None
339 ) -> ConfigFlowResult:
340 """Handle a flow initialized by the user."""
346 menu_options=[
"addon",
"broker"],
347 description_placeholders={
"addon": self.
_addon_manager_addon_manager.addon_name},
354 self, user_input: dict[str, Any] |
None =
None
355 ) -> ConfigFlowResult:
356 """Set up mqtt entry from discovery info."""
364 "addon_connection_failed",
365 description_placeholders={
"addon": self.
_addon_manager_addon_manager.addon_name},
369 self, user_input: dict[str, Any] |
None =
None
370 ) -> ConfigFlowResult:
371 """Install and start MQTT broker add-on."""
375 addon_info = await addon_manager.async_get_addon_info()
376 except AddonError
as err:
379 description_placeholders={
"addon": self.
_addon_manager_addon_manager.addon_name},
382 if addon_info.state == AddonState.RUNNING:
386 if addon_info.state == AddonState.NOT_RUNNING:
393 self, entry_data: Mapping[str, Any]
394 ) -> ConfigFlowResult:
395 """Handle re-authentication with MQTT broker."""
400 addon_discovery_config = (
401 await addon_manager.async_get_addon_discovery_info()
411 entry_data[CONF_BROKER] == addon_discovery_config[CONF_HOST]
412 and entry_data[CONF_PORT] == addon_discovery_config[CONF_PORT]
413 and entry_data.get(CONF_USERNAME)
414 == (username := addon_discovery_config.get(CONF_USERNAME))
415 and entry_data.get(CONF_PASSWORD)
416 != (password := addon_discovery_config.get(CONF_PASSWORD))
419 "Executing autorecovery %s add-on secrets",
420 addon_manager.addon_name,
423 user_input={CONF_USERNAME: username, CONF_PASSWORD: password}
429 self, user_input: dict[str, Any] |
None =
None
430 ) -> ConfigFlowResult:
431 """Confirm re-authentication with MQTT broker."""
432 errors: dict[str, str] = {}
437 reauth_entry.data.get(CONF_PASSWORD), user_input
439 new_entry_data = {**reauth_entry.data, **substituted_used_data}
440 if await self.hass.async_add_executor_job(
445 reauth_entry, data=new_entry_data
448 errors[
"base"] =
"invalid_auth"
453 CONF_USERNAME: reauth_entry.data.get(CONF_USERNAME),
454 CONF_PASSWORD: PWD_NOT_CHANGED,
458 step_id=
"reauth_confirm",
464 self, user_input: dict[str, Any] |
None =
None
465 ) -> ConfigFlowResult:
466 """Confirm the setup."""
467 errors: dict[str, str] = {}
468 fields: OrderedDict[Any, Any] = OrderedDict()
469 validated_user_input: dict[str, Any] = {}
475 validated_user_input,
478 can_connect = await self.hass.async_add_executor_job(
480 validated_user_input,
484 validated_user_input[CONF_DISCOVERY] = DEFAULT_DISCOVERY
486 title=validated_user_input[CONF_BROKER],
487 data=validated_user_input,
490 errors[
"base"] =
"cannot_connect"
493 step_id=
"broker", data_schema=vol.Schema(fields), errors=errors
497 self, discovery_info: HassioServiceInfo
498 ) -> ConfigFlowResult:
499 """Receive a Hass.io discovery or process setup after addon install."""
507 self, user_input: dict[str, Any] |
None =
None
508 ) -> ConfigFlowResult:
509 """Confirm a Hass.io discovery."""
510 errors: dict[str, str] = {}
514 if user_input
is not None:
516 data[CONF_BROKER] = data.pop(CONF_HOST)
517 can_connect = await self.hass.async_add_executor_job(
526 CONF_BROKER: data[CONF_BROKER],
527 CONF_PORT: data[CONF_PORT],
528 CONF_USERNAME: data.get(CONF_USERNAME),
529 CONF_PASSWORD: data.get(CONF_PASSWORD),
530 CONF_DISCOVERY: DEFAULT_DISCOVERY,
534 errors[
"base"] =
"cannot_connect"
537 step_id=
"hassio_confirm",
538 description_placeholders={
"addon": self.
_hassio_discovery_hassio_discovery[
"addon"]},
544 """Handle MQTT options."""
547 """Initialize MQTT options flow."""
548 self.broker_config: dict[str, str | int] = {}
551 """Manage the MQTT options."""
555 self, user_input: dict[str, Any] |
None =
None
556 ) -> ConfigFlowResult:
557 """Manage the MQTT broker configuration."""
558 errors: dict[str, str] = {}
559 fields: OrderedDict[Any, Any] = OrderedDict()
560 validated_user_input: dict[str, Any] = {}
566 validated_user_input,
569 self.broker_config.
update(
574 can_connect = await self.hass.async_add_executor_job(
582 errors[
"base"] =
"cannot_connect"
586 data_schema=vol.Schema(fields),
592 self, user_input: dict[str, Any] |
None =
None
593 ) -> ConfigFlowResult:
594 """Manage the MQTT options."""
597 options_config: dict[str, Any] = {}
598 bad_input: bool =
False
600 def _birth_will(birt_or_will: str) -> dict[str, Any]:
601 """Return the user input for birth or will."""
605 ATTR_TOPIC: user_input[f
"{birt_or_will}_topic"],
606 ATTR_PAYLOAD: user_input.get(f
"{birt_or_will}_payload",
""),
607 ATTR_QOS: user_input[f
"{birt_or_will}_qos"],
608 ATTR_RETAIN: user_input[f
"{birt_or_will}_retain"],
613 values: dict[str, Any],
615 schema: Callable[[Any], Any],
617 """Validate the user input."""
620 option_values = schema(values)
621 options_config[field] = option_values
623 errors[
"base"] = error_code
626 if user_input
is not None:
628 options_config[CONF_DISCOVERY] = user_input[CONF_DISCOVERY]
630 CONF_DISCOVERY_PREFIX,
631 user_input[CONF_DISCOVERY_PREFIX],
632 "bad_discovery_prefix",
635 if "birth_topic" in user_input:
638 _birth_will(
"birth"),
642 if not user_input[
"birth_enable"]:
643 options_config[CONF_BIRTH_MESSAGE] = {}
645 if "will_topic" in user_input:
652 if not user_input[
"will_enable"]:
653 options_config[CONF_WILL_MESSAGE] = {}
657 updated_config.update(self.broker_config)
658 updated_config.update(options_config)
659 self.hass.config_entries.async_update_entry(
662 title=
str(self.broker_config[CONF_BROKER]),
668 **current_config.get(CONF_BIRTH_MESSAGE, {}),
672 **current_config.get(CONF_WILL_MESSAGE, {}),
674 discovery = current_config.get(CONF_DISCOVERY, DEFAULT_DISCOVERY)
675 discovery_prefix = current_config.get(CONF_DISCOVERY_PREFIX, DEFAULT_PREFIX)
678 fields: OrderedDict[vol.Marker, Any] = OrderedDict()
679 fields[vol.Optional(CONF_DISCOVERY, default=discovery)] = BOOLEAN_SELECTOR
680 fields[vol.Optional(CONF_DISCOVERY_PREFIX, default=discovery_prefix)] = (
681 PUBLISH_TOPIC_SELECTOR
688 default=CONF_BIRTH_MESSAGE
not in current_config
689 or current_config[CONF_BIRTH_MESSAGE] != {},
694 "birth_topic", description={
"suggested_value": birth[ATTR_TOPIC]}
696 ] = PUBLISH_TOPIC_SELECTOR
699 "birth_payload", description={
"suggested_value": birth[CONF_PAYLOAD]}
702 fields[vol.Optional(
"birth_qos", default=birth[ATTR_QOS])] = QOS_SELECTOR
703 fields[vol.Optional(
"birth_retain", default=birth[ATTR_RETAIN])] = (
711 default=CONF_WILL_MESSAGE
not in current_config
712 or current_config[CONF_WILL_MESSAGE] != {},
717 "will_topic", description={
"suggested_value": will[ATTR_TOPIC]}
719 ] = PUBLISH_TOPIC_SELECTOR
722 "will_payload", description={
"suggested_value": will[CONF_PAYLOAD]}
725 fields[vol.Optional(
"will_qos", default=will[ATTR_QOS])] = QOS_SELECTOR
726 fields[vol.Optional(
"will_retain", default=will[ATTR_RETAIN])] = (
732 data_schema=vol.Schema(fields),
739 """Get file content from uploaded file."""
741 def _proces_uploaded_file() -> str:
743 return file_path.read_text(encoding=DEFAULT_ENCODING)
745 return await hass.async_add_executor_job(_proces_uploaded_file)
749 flow: ConfigFlow | OptionsFlow,
750 fields: OrderedDict[Any, Any],
751 entry_config: MappingProxyType[str, Any] |
None,
752 user_input: dict[str, Any] |
None,
753 validated_user_input: dict[str, Any],
754 errors: dict[str, str],
756 """Build the config flow schema to collect the broker settings.
758 Shows advanced options if one or more are configured
759 or when the advanced_broker_options checkbox was selected.
760 Returns True when settings are collected successfully.
763 advanced_broker_options: bool =
False
764 user_input_basic: dict[str, Any] = {}
765 current_config: dict[str, Any] = (
766 entry_config.copy()
if entry_config
is not None else {}
769 async
def _async_validate_broker_settings(
770 config: dict[str, Any],
771 user_input: dict[str, Any],
772 validated_user_input: dict[str, Any],
773 errors: dict[str, str],
775 """Additional validation on broker settings for better error messages."""
778 certificate: str |
None = (
780 if user_input.get(SET_CA_CERT,
"off") ==
"auto"
781 else config.get(CONF_CERTIFICATE)
782 if user_input.get(SET_CA_CERT,
"off") ==
"custom"
785 client_certificate: str |
None = (
786 config.get(CONF_CLIENT_CERT)
if user_input.get(SET_CLIENT_CERT)
else None
788 client_key: str |
None = (
789 config.get(CONF_CLIENT_KEY)
if user_input.get(SET_CLIENT_CERT)
else None
793 validated_user_input.update(user_input)
794 client_certificate_id: str |
None = user_input.get(CONF_CLIENT_CERT)
795 client_key_id: str |
None = user_input.get(CONF_CLIENT_KEY)
797 client_certificate_id
798 and not client_key_id
799 or not client_certificate_id
802 errors[
"base"] =
"invalid_inclusion"
804 certificate_id: str |
None = user_input.get(CONF_CERTIFICATE)
810 not client_certificate
811 and user_input.get(SET_CLIENT_CERT)
812 and not client_certificate_id
814 and user_input.get(SET_CA_CERT,
"off") ==
"custom"
815 and not certificate_id
816 or user_input.get(CONF_TRANSPORT) == TRANSPORT_WEBSOCKETS
817 and CONF_WS_PATH
not in user_input
821 if client_certificate_id:
826 certificate_data: dict[str, Any] = {}
828 certificate_data[CONF_CERTIFICATE] = certificate
829 if client_certificate:
830 certificate_data[CONF_CLIENT_CERT] = client_certificate
831 certificate_data[CONF_CLIENT_KEY] = client_key
833 validated_user_input.update(certificate_data)
835 if error := await hass.async_add_executor_job(
836 check_certicate_chain,
838 errors[
"base"] = error
841 if SET_CA_CERT
in validated_user_input:
842 del validated_user_input[SET_CA_CERT]
843 if SET_CLIENT_CERT
in validated_user_input:
844 del validated_user_input[SET_CLIENT_CERT]
845 if validated_user_input.get(CONF_TRANSPORT, TRANSPORT_TCP) == TRANSPORT_TCP:
846 if CONF_WS_PATH
in validated_user_input:
847 del validated_user_input[CONF_WS_PATH]
848 if CONF_WS_HEADERS
in validated_user_input:
849 del validated_user_input[CONF_WS_HEADERS]
852 validated_user_input[CONF_WS_HEADERS] =
json_loads(
853 validated_user_input.get(CONF_WS_HEADERS,
"{}")
855 schema = vol.Schema({cv.string: cv.template})
856 schema(validated_user_input[CONF_WS_HEADERS])
857 except (*JSON_DECODE_EXCEPTIONS, vol.MultipleInvalid):
858 errors[
"base"] =
"bad_ws_headers"
863 user_input_basic = user_input.copy()
864 advanced_broker_options = user_input_basic.get(ADVANCED_OPTIONS,
False)
865 if ADVANCED_OPTIONS
not in user_input
or advanced_broker_options
is False:
866 if await _async_validate_broker_settings(
869 validated_user_input,
874 current_broker = user_input_basic.get(CONF_BROKER)
875 current_port = user_input_basic.get(CONF_PORT, DEFAULT_PORT)
876 current_user = user_input_basic.get(CONF_USERNAME)
877 current_pass = user_input_basic.get(CONF_PASSWORD)
880 current_broker = current_config.get(CONF_BROKER)
881 current_port = current_config.get(CONF_PORT, DEFAULT_PORT)
882 current_user = current_config.get(CONF_USERNAME)
884 current_entry_pass = current_config.get(CONF_PASSWORD)
885 current_pass = PWD_NOT_CHANGED
if current_entry_pass
else None
889 current_config.update(user_input_basic)
892 current_client_id = current_config.get(CONF_CLIENT_ID)
893 current_keepalive = current_config.get(CONF_KEEPALIVE, DEFAULT_KEEPALIVE)
894 current_ca_certificate = current_config.get(CONF_CERTIFICATE)
895 current_client_certificate = current_config.get(CONF_CLIENT_CERT)
896 current_client_key = current_config.get(CONF_CLIENT_KEY)
897 current_tls_insecure = current_config.get(CONF_TLS_INSECURE,
False)
898 current_protocol = current_config.get(CONF_PROTOCOL, DEFAULT_PROTOCOL)
899 current_transport = current_config.get(CONF_TRANSPORT, DEFAULT_TRANSPORT)
900 current_ws_path = current_config.get(CONF_WS_PATH, DEFAULT_WS_PATH)
901 current_ws_headers = (
902 json_dumps(current_config.get(CONF_WS_HEADERS))
903 if CONF_WS_HEADERS
in current_config
906 advanced_broker_options |= bool(
908 or current_keepalive != DEFAULT_KEEPALIVE
909 or current_ca_certificate
910 or current_client_certificate
911 or current_client_key
912 or current_tls_insecure
913 or current_protocol != DEFAULT_PROTOCOL
914 or current_config.get(SET_CA_CERT,
"off") !=
"off"
915 or current_config.get(SET_CLIENT_CERT)
916 or current_transport == TRANSPORT_WEBSOCKETS
920 fields[vol.Required(CONF_BROKER, default=current_broker)] = TEXT_SELECTOR
921 fields[vol.Required(CONF_PORT, default=current_port)] = PORT_SELECTOR
925 description={
"suggested_value": current_user},
931 description={
"suggested_value": current_pass},
933 ] = PASSWORD_SELECTOR
937 if not advanced_broker_options:
938 if not flow.show_advanced_options:
949 description={
"suggested_value": current_client_id},
955 description={
"suggested_value": current_keepalive},
957 ] = KEEPALIVE_SELECTOR
961 default=current_client_certificate
is not None
962 or current_config.get(SET_CLIENT_CERT)
is True,
966 current_client_certificate
is not None
967 or current_config.get(SET_CLIENT_CERT)
is True
972 description={
"suggested_value": user_input_basic.get(CONF_CLIENT_CERT)},
974 ] = CERT_UPLOAD_SELECTOR
978 description={
"suggested_value": user_input_basic.get(CONF_CLIENT_KEY)},
980 ] = KEY_UPLOAD_SELECTOR
981 verification_mode = current_config.get(SET_CA_CERT)
or (
983 if current_ca_certificate
is None
985 if current_ca_certificate ==
"auto"
991 default=verification_mode,
993 ] = BROKER_VERIFICATION_SELECTOR
994 if current_ca_certificate
is not None or verification_mode ==
"custom":
998 user_input_basic.get(CONF_CERTIFICATE),
1000 ] = CA_CERT_UPLOAD_SELECTOR
1004 description={
"suggested_value": current_tls_insecure},
1006 ] = BOOLEAN_SELECTOR
1010 description={
"suggested_value": current_protocol},
1012 ] = PROTOCOL_SELECTOR
1016 description={
"suggested_value": current_transport},
1018 ] = TRANSPORT_SELECTOR
1019 if current_transport == TRANSPORT_WEBSOCKETS:
1021 vol.Optional(CONF_WS_PATH, description={
"suggested_value": current_ws_path})
1025 CONF_WS_HEADERS, description={
"suggested_value": current_ws_headers}
1027 ] = WS_HEADERS_SELECTOR
1034 user_input: dict[str, Any],
1036 """Test if we can connect to an MQTT broker."""
1039 import paho.mqtt.client
as mqtt
1042 mqtt_client_setup.setup()
1043 client = mqtt_client_setup.client
1045 result: queue.Queue[bool] = queue.Queue(maxsize=1)
1048 client_: mqtt.Client,
1050 flags: dict[str, Any],
1052 properties: mqtt.Properties |
None =
None,
1054 """Handle connection result."""
1055 result.put(result_code == mqtt.CONNACK_ACCEPTED)
1057 client.on_connect = on_connect
1059 client.connect_async(user_input[CONF_BROKER], user_input[CONF_PORT])
1063 return result.get(timeout=MQTT_TIMEOUT)
1072 """Check the MQTT certificates."""
1075 with open(client_certificate,
"rb")
as client_certificate_file:
1076 load_pem_x509_certificate(client_certificate_file.read())
1078 return "bad_client_cert"
1082 with open(private_key,
"rb")
as client_key_file:
1083 load_pem_private_key(client_key_file.read(), password=
None)
1084 except (TypeError, ValueError):
1085 return "bad_client_key"
1087 context = SSLContext(PROTOCOL_TLS_CLIENT)
1088 if client_certificate
and private_key:
1090 context.load_cert_chain(client_certificate, private_key)
1092 return "bad_client_cert_key"
1098 context.load_verify_locations(ca_cert)
1100 return "bad_certificate"
ConfigFlowResult async_step_hassio(self, HassioServiceInfo discovery_info)
ConfigFlowResult async_step_start_addon(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_broker(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
ConfigFlowResult async_step_addon(self, dict[str, Any]|None user_input=None)
MQTTOptionsFlowHandler async_get_options_flow(ConfigEntry config_entry)
dict[str, Any]|None _async_get_config_and_try(self)
ConfigFlowResult async_step_setup_entry_from_discovery(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_start_failed(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
None _async_start_addon(self)
None _async_install_addon(self)
ConfigFlowResult async_step_install_failed(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_reauth_confirm(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_install_addon(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_hassio_confirm(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_broker(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_init(self, None user_input=None)
ConfigFlowResult async_step_options(self, dict[str, Any]|None user_input=None)
None _async_handle_discovery_without_unique_id(self)
ConfigEntry _get_reauth_entry(self)
ConfigFlowResult async_create_entry(self, *str title, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None, Mapping[str, Any]|None options=None)
ConfigFlowResult async_update_reload_and_abort(self, ConfigEntry entry, *str|None|UndefinedType unique_id=UNDEFINED, str|UndefinedType title=UNDEFINED, Mapping[str, Any]|UndefinedType data=UNDEFINED, Mapping[str, Any]|UndefinedType data_updates=UNDEFINED, Mapping[str, Any]|UndefinedType options=UNDEFINED, str|UndefinedType reason=UNDEFINED, bool reload_even_if_entry_is_unchanged=True)
ConfigFlowResult async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
ConfigFlowResult async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
ConfigEntry config_entry(self)
None config_entry(self, ConfigEntry value)
vol.Schema add_suggested_values_to_schema(self, vol.Schema data_schema, Mapping[str, Any]|None suggested_values)
_FlowResultT async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_show_progress(self, *str|None step_id=None, str progress_action, Mapping[str, str]|None description_placeholders=None, asyncio.Task[Any]|None progress_task=None)
_FlowResultT async_show_menu(self, *str|None step_id=None, Container[str] menu_options, Mapping[str, str]|None description_placeholders=None)
_FlowResultT async_show_progress_done(self, *str next_step_id)
_FlowResultT async_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
Iterator[Path] process_uploaded_file(HomeAssistant hass, str file_id)
bool is_hassio(HomeAssistant hass)
IssData update(pyiss.ISS iss)
str _get_uploaded_file(HomeAssistant hass, str id)
dict[str, Any] update_password_from_user_input(str|None entry_password, dict[str, Any] user_input)
str|None check_certicate_chain()
bool async_get_broker_settings(ConfigFlow|OptionsFlow flow, OrderedDict[Any, Any] fields, MappingProxyType[str, Any]|None entry_config, dict[str, Any]|None user_input, dict[str, Any] validated_user_input, dict[str, str] errors)
bool try_connection(dict[str, Any] user_input)
None async_create_certificate_temp_files(HomeAssistant hass, ConfigType config)
str|None get_file_path(str option, str|None default=None)
None open(self, **Any kwargs)
AddonManager get_addon_manager(HomeAssistant hass, str slug)
None _validate(Wallbox wallbox)