Home Assistant Unofficial Reference 2024.12.1
config_flow.py
Go to the documentation of this file.
1 """Config flow to configure the LG Soundbar integration."""
2 
3 import logging
4 from queue import Empty, Full, Queue
5 
6 import temescal
7 import voluptuous as vol
8 
9 from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
10 from homeassistant.const import CONF_HOST, CONF_PORT
11 
12 from .const import DEFAULT_PORT, DOMAIN
13 
14 DATA_SCHEMA = {
15  vol.Required(CONF_HOST): str,
16 }
17 
18 _LOGGER = logging.getLogger(__name__)
19 
20 QUEUE_TIMEOUT = 10
21 
22 
23 def test_connect(host, port):
24  """LG Soundbar config flow test_connect."""
25  uuid_q = Queue(maxsize=1)
26  name_q = Queue(maxsize=1)
27 
28  def check_msg_response(response, msgs, attr):
29  msg = response["msg"]
30  if msg == msgs or msg in msgs:
31  if "data" in response and attr in response["data"]:
32  return True
33  _LOGGER.debug(
34  "[%s] msg did not contain expected attr [%s]: %s", msg, attr, response
35  )
36  return False
37 
38  def queue_add(attr_q, data):
39  try:
40  attr_q.put_nowait(data)
41  except Full:
42  _LOGGER.debug("attempted to add [%s] to full queue", data)
43 
44  def msg_callback(response):
45  if check_msg_response(response, ["MAC_INFO_DEV", "PRODUCT_INFO"], "s_uuid"):
46  queue_add(uuid_q, response["data"]["s_uuid"])
47  if check_msg_response(response, "SPK_LIST_VIEW_INFO", "s_user_name"):
48  queue_add(name_q, response["data"]["s_user_name"])
49 
50  details = {}
51 
52  try:
53  connection = temescal.temescal(host, port=port, callback=msg_callback)
54  connection.get_info()
55  connection.get_mac_info()
56  if uuid_q.empty():
57  connection.get_product_info()
58  details["name"] = name_q.get(timeout=QUEUE_TIMEOUT)
59  details["uuid"] = uuid_q.get(timeout=QUEUE_TIMEOUT)
60  except Empty:
61  pass
62  except TimeoutError as err:
63  raise ConnectionError(f"Connection timeout with server: {host}:{port}") from err
64  except OSError as err:
65  raise ConnectionError(f"Cannot resolve hostname: {host}") from err
66 
67  return details
68 
69 
70 class LGSoundbarConfigFlow(ConfigFlow, domain=DOMAIN):
71  """LG Soundbar config flow."""
72 
73  VERSION = 1
74 
75  async def async_step_user(self, user_input=None) -> ConfigFlowResult:
76  """Handle a flow initiated by the user."""
77  if user_input is None:
78  return self._show_form_show_form()
79 
80  errors = {}
81  try:
82  details = await self.hass.async_add_executor_job(
83  test_connect, user_input[CONF_HOST], DEFAULT_PORT
84  )
85  except ConnectionError:
86  errors["base"] = "cannot_connect"
87  else:
88  if len(details) != 0:
89  info = {
90  CONF_HOST: user_input[CONF_HOST],
91  CONF_PORT: DEFAULT_PORT,
92  }
93  if "uuid" in details:
94  unique_id = details["uuid"]
95  await self.async_set_unique_idasync_set_unique_id(unique_id)
96  self._abort_if_unique_id_configured_abort_if_unique_id_configured()
97  else:
98  self._async_abort_entries_match_async_abort_entries_match(info)
99  return self.async_create_entryasync_create_entryasync_create_entry(title=details["name"], data=info)
100  errors["base"] = "no_data"
101 
102  return self._show_form_show_form(errors)
103 
104  def _show_form(self, errors=None):
105  """Show the form to the user."""
106  return self.async_show_formasync_show_formasync_show_form(
107  step_id="user",
108  data_schema=vol.Schema(DATA_SCHEMA),
109  errors=errors if errors else {},
110  )
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)
None _async_abort_entries_match(self, dict[str, Any]|None match_dict=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)