core/homeassistant/components/bluesound/config_flow.py

153 lines
5.0 KiB
Python

"""Config flow for bluesound."""
import logging
from typing import Any
from pyblu import Player, SyncStatus
from pyblu.errors import PlayerUnreachableError
import voluptuous as vol
from homeassistant.components import zeroconf
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
from .media_player import DEFAULT_PORT
from .utils import format_unique_id
_LOGGER = logging.getLogger(__name__)
class BluesoundConfigFlow(ConfigFlow, domain=DOMAIN):
"""Bluesound config flow."""
VERSION = 1
MINOR_VERSION = 1
def __init__(self) -> None:
"""Initialize the config flow."""
self._host: str | None = None
self._port = DEFAULT_PORT
self._sync_status: SyncStatus | None = None
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initiated by the user."""
errors: dict[str, str] = {}
if user_input is not None:
session = async_get_clientsession(self.hass)
async with Player(
user_input[CONF_HOST], user_input[CONF_PORT], session=session
) as player:
try:
sync_status = await player.sync_status(timeout=1)
except PlayerUnreachableError:
errors["base"] = "cannot_connect"
else:
await self.async_set_unique_id(
format_unique_id(sync_status.mac, user_input[CONF_PORT])
)
self._abort_if_unique_id_configured(
updates={
CONF_HOST: user_input[CONF_HOST],
}
)
return self.async_create_entry(
title=sync_status.name,
data=user_input,
)
return self.async_show_form(
step_id="user",
errors=errors,
data_schema=vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Optional(CONF_PORT, default=11000): int,
}
),
)
async def async_step_import(self, import_data: dict[str, Any]) -> ConfigFlowResult:
"""Import bluesound config entry from configuration.yaml."""
session = async_get_clientsession(self.hass)
async with Player(
import_data[CONF_HOST], import_data[CONF_PORT], session=session
) as player:
try:
sync_status = await player.sync_status(timeout=1)
except PlayerUnreachableError:
return self.async_abort(reason="cannot_connect")
await self.async_set_unique_id(
format_unique_id(sync_status.mac, import_data[CONF_PORT])
)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=sync_status.name,
data=import_data,
)
async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
) -> ConfigFlowResult:
"""Handle a flow initialized by zeroconf discovery."""
if discovery_info.port is not None:
self._port = discovery_info.port
session = async_get_clientsession(self.hass)
try:
async with Player(
discovery_info.host, self._port, session=session
) as player:
sync_status = await player.sync_status(timeout=1)
except PlayerUnreachableError:
return self.async_abort(reason="cannot_connect")
await self.async_set_unique_id(format_unique_id(sync_status.mac, self._port))
self._host = discovery_info.host
self._sync_status = sync_status
self._abort_if_unique_id_configured(
updates={
CONF_HOST: self._host,
}
)
self.context.update(
{
"title_placeholders": {"name": sync_status.name},
"configuration_url": f"http://{discovery_info.host}",
}
)
return await self.async_step_confirm()
async def async_step_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Confirm the zeroconf setup."""
assert self._sync_status is not None
assert self._host is not None
if user_input is not None:
return self.async_create_entry(
title=self._sync_status.name,
data={
CONF_HOST: self._host,
CONF_PORT: self._port,
},
)
return self.async_show_form(
step_id="confirm",
description_placeholders={
"name": self._sync_status.name,
"host": self._host,
},
)