core/homeassistant/components/songpal/config_flow.py

155 lines
5.2 KiB
Python
Raw Normal View History

"""Config flow to configure songpal component."""
import logging
from typing import Optional
from urllib.parse import urlparse
from songpal import Device, SongpalException
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components import ssdp
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.core import callback
from .const import CONF_ENDPOINT, DOMAIN # pylint: disable=unused-import
_LOGGER = logging.getLogger(__name__)
class SongpalConfig:
"""Device Configuration."""
def __init__(self, name, host, endpoint):
"""Initialize Configuration."""
self.name = name
self.host = host
self.endpoint = endpoint
class SongpalConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Songpal configuration flow."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH
def __init__(self):
"""Initialize the flow."""
self.conf: Optional[SongpalConfig] = None
async def async_step_user(self, user_input=None):
"""Handle a flow initiated by the user."""
if user_input is None:
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({vol.Required(CONF_ENDPOINT): str}),
)
# Validate input
endpoint = user_input[CONF_ENDPOINT]
parsed_url = urlparse(endpoint)
# Try to connect and get device name
try:
device = Device(endpoint)
await device.get_supported_methods()
interface_info = await device.get_interface_information()
name = interface_info.modelName
except SongpalException as ex:
_LOGGER.debug("Connection failed: %s", ex)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(
CONF_ENDPOINT, default=user_input.get(CONF_ENDPOINT, "")
): str,
}
),
errors={"base": "cannot_connect"},
)
self.conf = SongpalConfig(name, parsed_url.hostname, endpoint)
return await self.async_step_init(user_input)
async def async_step_init(self, user_input=None):
"""Handle a flow start."""
# Check if already configured
if self._async_endpoint_already_configured():
return self.async_abort(reason="already_configured")
if user_input is None:
return self.async_show_form(
step_id="init",
description_placeholders={
CONF_NAME: self.conf.name,
CONF_HOST: self.conf.host,
},
)
await self.async_set_unique_id(self.conf.endpoint)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=self.conf.name,
data={CONF_NAME: self.conf.name, CONF_ENDPOINT: self.conf.endpoint},
)
async def async_step_ssdp(self, discovery_info):
"""Handle a discovered Songpal device."""
await self.async_set_unique_id(discovery_info[ssdp.ATTR_UPNP_UDN])
self._abort_if_unique_id_configured()
_LOGGER.debug("Discovered: %s", discovery_info)
friendly_name = discovery_info[ssdp.ATTR_UPNP_FRIENDLY_NAME]
parsed_url = urlparse(discovery_info[ssdp.ATTR_SSDP_LOCATION])
scalarweb_info = discovery_info["X_ScalarWebAPI_DeviceInfo"]
endpoint = scalarweb_info["X_ScalarWebAPI_BaseURL"]
service_types = scalarweb_info["X_ScalarWebAPI_ServiceList"][
"X_ScalarWebAPI_ServiceType"
]
# Ignore Bravia TVs
if "videoScreen" in service_types:
return self.async_abort(reason="not_songpal_device")
self.context["title_placeholders"] = {
CONF_NAME: friendly_name,
CONF_HOST: parsed_url.hostname,
}
self.conf = SongpalConfig(friendly_name, parsed_url.hostname, endpoint)
return await self.async_step_init()
async def async_step_import(self, user_input=None):
"""Import a config entry."""
name = user_input.get(CONF_NAME)
endpoint = user_input.get(CONF_ENDPOINT)
parsed_url = urlparse(endpoint)
# Try to connect to test the endpoint
try:
device = Device(endpoint)
await device.get_supported_methods()
# Get name
if name is None:
interface_info = await device.get_interface_information()
name = interface_info.modelName
except SongpalException as ex:
_LOGGER.error("Import from yaml configuration failed: %s", ex)
return self.async_abort(reason="cannot_connect")
self.conf = SongpalConfig(name, parsed_url.hostname, endpoint)
return await self.async_step_init(user_input)
@callback
def _async_endpoint_already_configured(self):
"""See if we already have an endpoint matching user input configured."""
for entry in self._async_current_entries():
if entry.data.get(CONF_ENDPOINT) == self.conf.endpoint:
return True
return False