Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow to configure the LCN integration."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 import pypck
9 import voluptuous as vol
10 
11 from homeassistant import config_entries
12 from homeassistant.const import (
13  CONF_BASE,
14  CONF_DEVICES,
15  CONF_ENTITIES,
16  CONF_HOST,
17  CONF_IP_ADDRESS,
18  CONF_PASSWORD,
19  CONF_PORT,
20  CONF_USERNAME,
21 )
22 from homeassistant.core import HomeAssistant
24 from homeassistant.helpers.typing import ConfigType
25 
26 from . import PchkConnectionManager
27 from .const import CONF_ACKNOWLEDGE, CONF_DIM_MODE, CONF_SK_NUM_TRIES, DIM_MODES, DOMAIN
28 
29 _LOGGER = logging.getLogger(__name__)
30 
31 CONFIG_DATA = {
32  vol.Required(CONF_IP_ADDRESS, default=""): str,
33  vol.Required(CONF_PORT, default=4114): cv.positive_int,
34  vol.Required(CONF_USERNAME, default=""): str,
35  vol.Required(CONF_PASSWORD, default=""): str,
36  vol.Required(CONF_SK_NUM_TRIES, default=0): cv.positive_int,
37  vol.Required(CONF_DIM_MODE, default="STEPS200"): vol.In(DIM_MODES),
38  vol.Required(CONF_ACKNOWLEDGE, default=False): cv.boolean,
39 }
40 
41 USER_DATA = {vol.Required(CONF_HOST, default="pchk"): str, **CONFIG_DATA}
42 
43 CONFIG_SCHEMA = vol.Schema(CONFIG_DATA)
44 USER_SCHEMA = vol.Schema(USER_DATA)
45 
46 
48  hass: HomeAssistant, data: ConfigType
49 ) -> config_entries.ConfigEntry | None:
50  """Check config entries for already configured entries based on the ip address/port."""
51  return next(
52  (
53  entry
54  for entry in hass.config_entries.async_entries(DOMAIN)
55  if entry.data[CONF_IP_ADDRESS] == data[CONF_IP_ADDRESS]
56  and entry.data[CONF_PORT] == data[CONF_PORT]
57  ),
58  None,
59  )
60 
61 
62 async def validate_connection(data: ConfigType) -> str | None:
63  """Validate if a connection to LCN can be established."""
64  error = None
65  host_name = data[CONF_HOST]
66  host = data[CONF_IP_ADDRESS]
67  port = data[CONF_PORT]
68  username = data[CONF_USERNAME]
69  password = data[CONF_PASSWORD]
70  sk_num_tries = data[CONF_SK_NUM_TRIES]
71  dim_mode = data[CONF_DIM_MODE]
72  acknowledge = data[CONF_ACKNOWLEDGE]
73 
74  settings = {
75  "SK_NUM_TRIES": sk_num_tries,
76  "DIM_MODE": pypck.lcn_defs.OutputPortDimMode[dim_mode],
77  "ACKNOWLEDGE": acknowledge,
78  }
79 
80  _LOGGER.debug("Validating connection parameters to PCHK host '%s'", host_name)
81 
82  connection = PchkConnectionManager(
83  host, port, username, password, settings=settings
84  )
85 
86  try:
87  await connection.async_connect(timeout=5)
88  _LOGGER.debug("LCN connection validated")
89  except pypck.connection.PchkAuthenticationError:
90  _LOGGER.warning('Authentication on PCHK "%s" failed', host_name)
91  error = "authentication_error"
92  except pypck.connection.PchkLicenseError:
93  _LOGGER.warning(
94  'Maximum number of connections on PCHK "%s" was '
95  "reached. An additional license key is required",
96  host_name,
97  )
98  error = "license_error"
99  except (TimeoutError, ConnectionRefusedError):
100  _LOGGER.warning('Connection to PCHK "%s" failed', host_name)
101  error = "connection_refused"
102 
103  await connection.async_close()
104  return error
105 
106 
108  """Handle a LCN config flow."""
109 
110  VERSION = 2
111  MINOR_VERSION = 1
112 
113  async def async_step_user(
114  self, user_input: dict[str, Any] | None = None
116  """Handle a flow initiated by the user."""
117  if user_input is None:
118  return self.async_show_formasync_show_formasync_show_form(step_id="user", data_schema=USER_SCHEMA)
119 
120  errors = None
121  if get_config_entry(self.hass, user_input):
122  errors = {CONF_BASE: "already_configured"}
123  elif (error := await validate_connection(user_input)) is not None:
124  errors = {CONF_BASE: error}
125 
126  if errors is not None:
127  return self.async_show_formasync_show_formasync_show_form(
128  step_id="user",
129  data_schema=self.add_suggested_values_to_schemaadd_suggested_values_to_schema(
130  USER_SCHEMA, user_input
131  ),
132  errors=errors,
133  )
134 
135  data: dict = {
136  **user_input,
137  CONF_DEVICES: [],
138  CONF_ENTITIES: [],
139  }
140 
141  return self.async_create_entryasync_create_entryasync_create_entry(title=data[CONF_HOST], data=data)
142 
144  self, user_input: dict[str, Any] | None = None
146  """Reconfigure LCN configuration."""
147  reconfigure_entry = self._get_reconfigure_entry_get_reconfigure_entry()
148  errors = None
149  if user_input is not None:
150  user_input[CONF_HOST] = reconfigure_entry.data[CONF_HOST]
151 
152  await self.hass.config_entries.async_unload(reconfigure_entry.entry_id)
153  if (error := await validate_connection(user_input)) is not None:
154  errors = {CONF_BASE: error}
155 
156  if errors is None:
157  return self.async_update_reload_and_abortasync_update_reload_and_abort(
158  reconfigure_entry, data_updates=user_input
159  )
160 
161  await self.hass.config_entries.async_setup(reconfigure_entry.entry_id)
162 
163  return self.async_show_formasync_show_formasync_show_form(
164  step_id="reconfigure",
165  data_schema=self.add_suggested_values_to_schemaadd_suggested_values_to_schema(
166  CONFIG_SCHEMA, reconfigure_entry.data
167  ),
168  errors=errors,
169  )
config_entries.ConfigFlowResult async_step_reconfigure(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:145
config_entries.ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
Definition: config_flow.py:115
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_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)
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_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
str|None validate_connection(ConfigType data)
Definition: config_flow.py:62
config_entries.ConfigEntry|None get_config_entry(HomeAssistant hass, ConfigType data)
Definition: config_flow.py:49