1 """Config flow for Kodi integration."""
3 from __future__
import annotations
8 from pykodi
import CannotConnectError, InvalidAuthError, Kodi, get_kodi_connection
9 import voluptuous
as vol
35 _LOGGER = logging.getLogger(__name__)
39 """Validate the user input allows us to connect over HTTP."""
41 host = data[CONF_HOST]
42 port = data[CONF_PORT]
43 username = data.get(CONF_USERNAME)
44 password = data.get(CONF_PASSWORD)
45 ssl = data.get(CONF_SSL)
48 _LOGGER.debug(
"Connecting to %s:%s over HTTP", host, port)
49 khc = get_kodi_connection(
50 host, port,
None, username, password, ssl, session=session
55 except CannotConnectError
as error:
56 raise CannotConnect
from error
57 except InvalidAuthError
as error:
58 raise InvalidAuth
from error
62 """Validate the user input allows us to connect over WS."""
63 if not (ws_port := data.get(CONF_WS_PORT)):
66 host = data[CONF_HOST]
67 port = data[CONF_PORT]
68 username = data.get(CONF_USERNAME)
69 password = data.get(CONF_PASSWORD)
70 ssl = data.get(CONF_SSL)
74 _LOGGER.debug(
"Connecting to %s:%s over WebSocket", host, ws_port)
75 kwc = get_kodi_connection(
76 host, port, ws_port, username, password, ssl, session=session
81 _LOGGER.warning(
"Cannot connect to %s:%s over WebSocket", host, ws_port)
85 except CannotConnectError
as error:
86 raise WSCannotConnect
from error
90 """Handle a config flow for Kodi."""
95 """Initialize flow."""
96 self.
_host_host: str |
None =
None
97 self.
_port_port: int |
None = DEFAULT_PORT
98 self.
_ws_port_ws_port: int |
None = DEFAULT_WS_PORT
99 self.
_name_name: str |
None =
None
100 self.
_username_username: str |
None =
None
101 self.
_password_password: str |
None =
None
102 self.
_ssl_ssl: bool |
None = DEFAULT_SSL
106 self, discovery_info: zeroconf.ZeroconfServiceInfo
107 ) -> ConfigFlowResult:
108 """Handle zeroconf discovery."""
109 self.
_host_host = discovery_info.host
110 self.
_port_port = discovery_info.port
or DEFAULT_PORT
111 self.
_name_name = discovery_info.hostname.removesuffix(
".local.")
112 if not (uuid := discovery_info.properties.get(
"uuid")):
120 CONF_HOST: self.
_host_host,
121 CONF_PORT: self.
_port_port,
122 CONF_NAME: self.
_name_name,
126 self.context.
update({
"title_placeholders": {CONF_NAME: self.
_name_name}})
133 except WSCannotConnect:
135 except CannotConnect:
138 _LOGGER.exception(
"Unexpected exception")
144 self, user_input: dict[str, Any] |
None =
None
145 ) -> ConfigFlowResult:
146 """Handle user-confirmation of discovered node."""
147 if user_input
is None:
148 assert self.
_name_name
is not None
150 step_id=
"discovery_confirm",
151 description_placeholders={
"name": self.
_name_name},
157 self, user_input: dict[str, Any] |
None =
None
158 ) -> ConfigFlowResult:
159 """Handle the initial step."""
162 if user_input
is not None:
163 self.
_host_host = user_input[CONF_HOST]
164 self.
_port_port = user_input[CONF_PORT]
165 self.
_ssl_ssl = user_input[CONF_SSL]
172 except WSCannotConnect:
174 except CannotConnect:
175 errors[
"base"] =
"cannot_connect"
177 _LOGGER.exception(
"Unexpected exception")
178 errors[
"base"] =
"unknown"
185 self, user_input: dict[str, Any] |
None =
None
186 ) -> ConfigFlowResult:
187 """Handle username and password input."""
190 if user_input
is not None:
198 errors[
"base"] =
"invalid_auth"
199 except WSCannotConnect:
201 except CannotConnect:
202 errors[
"base"] =
"cannot_connect"
204 _LOGGER.exception(
"Unexpected exception")
205 errors[
"base"] =
"unknown"
212 self, user_input: dict[str, Any] |
None =
None
213 ) -> ConfigFlowResult:
214 """Handle websocket port of discovered node."""
217 if user_input
is not None:
218 self.
_ws_port_ws_port = user_input.get(CONF_WS_PORT)
226 except WSCannotConnect:
227 errors[
"base"] =
"cannot_connect"
229 _LOGGER.exception(
"Unexpected exception")
230 errors[
"base"] =
"unknown"
237 """Handle import from YAML."""
243 _LOGGER.exception(
"Invalid Kodi credentials")
244 reason =
"invalid_auth"
245 except CannotConnect:
246 _LOGGER.exception(
"Cannot connect to Kodi")
247 reason =
"cannot_connect"
249 _LOGGER.exception(
"Unexpected exception")
253 title=import_data[CONF_NAME], data=import_data
260 self, errors: dict[str, str] |
None =
None
261 ) -> ConfigFlowResult:
265 CONF_USERNAME, description={
"suggested_value": self.
_username_username}
268 CONF_PASSWORD, description={
"suggested_value": self.
_password_password}
274 step_id=
"credentials", data_schema=schema, errors=errors
279 default_port = self.
_port_port
or DEFAULT_PORT
280 default_ssl = self.
_ssl_ssl
or DEFAULT_SSL
283 vol.Required(CONF_HOST, default=self.
_host_host): str,
284 vol.Required(CONF_PORT, default=default_port): int,
285 vol.Required(CONF_SSL, default=default_ssl): bool,
290 step_id=
"user", data_schema=schema, errors=errors
or {}
295 suggestion = self.
_ws_port_ws_port
or DEFAULT_WS_PORT
299 CONF_WS_PORT, description={
"suggested_value": suggestion}
305 step_id=
"ws_port", data_schema=schema, errors=errors
or {}
318 CONF_NAME: self.
_name_name,
319 CONF_HOST: self.
_host_host,
320 CONF_PORT: self.
_port_port,
321 CONF_WS_PORT: self.
_ws_port_ws_port,
324 CONF_SSL: self.
_ssl_ssl,
325 CONF_TIMEOUT: DEFAULT_TIMEOUT,
330 """Error to indicate we cannot connect."""
333 class InvalidAuth(HomeAssistantError):
334 """Error to indicate there is invalid auth."""
338 """Error to indicate we cannot connect to websocket."""
ConfigFlowResult async_step_ws_port(self, dict[str, Any]|None user_input=None)
dict[str, Any] _get_data(self)
ConfigFlowResult _show_credentials_form(self, dict[str, str]|None errors=None)
def _show_user_form(self, errors=None)
ConfigFlowResult async_step_zeroconf(self, zeroconf.ZeroconfServiceInfo discovery_info)
ConfigFlowResult async_step_credentials(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_discovery_confirm(self, dict[str, Any]|None user_input=None)
def _show_ws_port_form(self, errors=None)
ConfigFlowResult async_step_import(self, dict[str, Any] import_data)
None _abort_if_unique_id_configured(self, dict[str, Any]|None updates=None, bool reload_on_update=True, *str error="already_configured")
ConfigEntry|None async_set_unique_id(self, str|None unique_id=None, *bool raise_on_progress=True)
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_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)
_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)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
IssData update(pyiss.ISS iss)
def validate_http(HomeAssistant hass, data)
def validate_ws(HomeAssistant hass, data)
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)