core/homeassistant/components/lg_soundbar/config_flow.py

112 lines
3.5 KiB
Python

"""Config flow to configure the LG Soundbar integration."""
import logging
from queue import Empty, Full, Queue
import socket
import temescal
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.data_entry_flow import FlowResult
from .const import DEFAULT_PORT, DOMAIN
DATA_SCHEMA = {
vol.Required(CONF_HOST): str,
}
_LOGGER = logging.getLogger(__name__)
QUEUE_TIMEOUT = 10
def test_connect(host, port):
"""LG Soundbar config flow test_connect."""
uuid_q = Queue(maxsize=1)
name_q = Queue(maxsize=1)
def check_msg_response(response, msgs, attr):
msg = response["msg"]
if msg == msgs or msg in msgs:
if "data" in response and attr in response["data"]:
return True
_LOGGER.debug(
"[%s] msg did not contain expected attr [%s]: %s", msg, attr, response
)
return False
def queue_add(attr_q, data):
try:
attr_q.put_nowait(data)
except Full:
_LOGGER.debug("attempted to add [%s] to full queue", data)
def msg_callback(response):
if check_msg_response(response, ["MAC_INFO_DEV", "PRODUCT_INFO"], "s_uuid"):
queue_add(uuid_q, response["data"]["s_uuid"])
if check_msg_response(response, "SPK_LIST_VIEW_INFO", "s_user_name"):
queue_add(name_q, response["data"]["s_user_name"])
details = {}
try:
connection = temescal.temescal(host, port=port, callback=msg_callback)
connection.get_info()
connection.get_mac_info()
if uuid_q.empty():
connection.get_product_info()
details["name"] = name_q.get(timeout=QUEUE_TIMEOUT)
details["uuid"] = uuid_q.get(timeout=QUEUE_TIMEOUT)
except Empty:
pass
except socket.timeout as err:
raise ConnectionError(f"Connection timeout with server: {host}:{port}") from err
except OSError as err:
raise ConnectionError(f"Cannot resolve hostname: {host}") from err
return details
class LGSoundbarConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""LG Soundbar config flow."""
VERSION = 1
async def async_step_user(self, user_input=None) -> FlowResult:
"""Handle a flow initiated by the user."""
if user_input is None:
return self._show_form()
errors = {}
try:
details = await self.hass.async_add_executor_job(
test_connect, user_input[CONF_HOST], DEFAULT_PORT
)
except ConnectionError:
errors["base"] = "cannot_connect"
else:
if len(details) != 0:
info = {
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: DEFAULT_PORT,
}
if "uuid" in details:
unique_id = details["uuid"]
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
else:
self._async_abort_entries_match(info)
return self.async_create_entry(title=details["name"], data=info)
errors["base"] = "no_data"
return self._show_form(errors)
def _show_form(self, errors=None):
"""Show the form to the user."""
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(DATA_SCHEMA),
errors=errors if errors else {},
)