Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow for PrusaLink integration."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 import logging
7 from typing import Any
8 
9 from awesomeversion import AwesomeVersion, AwesomeVersionException
10 from httpx import HTTPError, InvalidURL
11 from pyprusalink import PrusaLink
12 from pyprusalink.types import InvalidAuth, VersionInfo
13 import voluptuous as vol
14 
15 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
16 from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
17 from homeassistant.core import HomeAssistant
18 from homeassistant.exceptions import HomeAssistantError
19 from homeassistant.helpers.httpx_client import get_async_client
20 
21 from .const import DOMAIN
22 
23 _LOGGER = logging.getLogger(__name__)
24 
25 
26 STEP_USER_DATA_SCHEMA = vol.Schema(
27  {
28  vol.Required(CONF_HOST): str,
29  # "maker" is currently hardcoded in the firmware
30  # https://github.com/prusa3d/Prusa-Firmware-Buddy/blob/bfb0ffc745ee6546e7efdba618d0e7c0f4c909cd/lib/WUI/wui_api.h#L19
31  vol.Required(CONF_USERNAME, default="maker"): str,
32  vol.Required(CONF_PASSWORD): str,
33  }
34 )
35 
36 
37 def ensure_printer_is_supported(version: VersionInfo) -> None:
38  """Raise NotSupported exception if the printer is not supported."""
39 
40  try:
41  if AwesomeVersion("2.0.0") <= AwesomeVersion(version["api"]):
42  return
43 
44  # Workaround to allow PrusaLink 0.7.2 on MK3 and MK2.5 that supports
45  # the 2.0.0 API, but doesn't advertise it yet
46  if version.get("original", "").startswith(
47  ("PrusaLink I3MK3", "PrusaLink I3MK2")
48  ) and AwesomeVersion("0.7.2") <= AwesomeVersion(version["server"]):
49  return
50 
51  except AwesomeVersionException as err:
52  raise NotSupported from err
53 
54  raise NotSupported
55 
56 
57 async def validate_input(hass: HomeAssistant, data: dict[str, str]) -> dict[str, str]:
58  """Validate the user input allows us to connect.
59 
60  Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
61  """
62  api = PrusaLink(
63  get_async_client(hass),
64  data[CONF_HOST],
65  data[CONF_USERNAME],
66  data[CONF_PASSWORD],
67  )
68 
69  try:
70  async with asyncio.timeout(5):
71  version = await api.get_version()
72 
73  except (TimeoutError, HTTPError, InvalidURL) as err:
74  _LOGGER.error("Could not connect to PrusaLink: %s", err)
75  raise CannotConnect from err
76 
78 
79  return {"title": version["hostname"] or version["text"]}
80 
81 
82 class PrusaLinkConfigFlow(ConfigFlow, domain=DOMAIN):
83  """Handle a config flow for PrusaLink."""
84 
85  VERSION = 1
86  MINOR_VERSION = 2
87 
88  async def async_step_user(
89  self, user_input: dict[str, Any] | None = None
90  ) -> ConfigFlowResult:
91  """Handle the initial step."""
92  if user_input is None:
93  return self.async_show_formasync_show_formasync_show_form(
94  step_id="user", data_schema=STEP_USER_DATA_SCHEMA
95  )
96 
97  host = user_input[CONF_HOST].rstrip("/")
98  if not host.startswith(("http://", "https://")):
99  host = f"http://{host}"
100 
101  data = {
102  CONF_HOST: host,
103  CONF_USERNAME: user_input[CONF_USERNAME],
104  CONF_PASSWORD: user_input[CONF_PASSWORD],
105  }
106  errors = {}
107 
108  try:
109  info = await validate_input(self.hass, data)
110  except CannotConnect:
111  errors["base"] = "cannot_connect"
112  except NotSupported:
113  errors["base"] = "not_supported"
114  except InvalidAuth:
115  errors["base"] = "invalid_auth"
116  except Exception:
117  _LOGGER.exception("Unexpected exception")
118  errors["base"] = "unknown"
119  else:
120  return self.async_create_entryasync_create_entryasync_create_entry(title=info["title"], data=data)
121 
122  return self.async_show_formasync_show_formasync_show_form(
123  step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
124  )
125 
126 
128  """Error to indicate we cannot connect."""
129 
130 
132  """Error to indicate we cannot connect."""
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_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)
httpx.AsyncClient get_async_client(HomeAssistant hass, bool verify_ssl=True)
Definition: httpx_client.py:41