Improve roon integraton (#66000)

* Update to new library, revise discovery to work with new library, specify port to work with new library.

* Move user gui to fallback.

* Revise tests.

* Handle old config.

* Improve debugging, refresh faster on load.

* Remove duplicate.

* Bump library version.

* Fix docstring per review.

* Review suggestion

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Review suggestion

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Add check for duplicate host.

* Add error message to strings.

* Tidy.

* Review changes.

* Remove default.

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Greg Dowling 2022-04-18 15:27:14 +01:00 committed by GitHub
parent c3108b3899
commit 23264c8fd4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 142 additions and 40 deletions

View file

@ -6,11 +6,13 @@ from roonapi import RoonApi, RoonDiscovery
import voluptuous as vol
from homeassistant import config_entries, core, exceptions
from homeassistant.const import CONF_API_KEY, CONF_HOST
from homeassistant.const import CONF_API_KEY, CONF_HOST, CONF_PORT
import homeassistant.helpers.config_validation as cv
from .const import (
AUTHENTICATE_TIMEOUT,
CONF_ROON_ID,
CONF_ROON_NAME,
DEFAULT_NAME,
DOMAIN,
ROON_APPINFO,
@ -18,7 +20,12 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
DATA_SCHEMA = vol.Schema({vol.Required("host"): str})
DATA_SCHEMA = vol.Schema(
{
vol.Required("host"): cv.string,
vol.Required("port", default=9330): cv.port,
}
)
TIMEOUT = 120
@ -45,7 +52,7 @@ class RoonHub:
_LOGGER.debug("Servers = %s", servers)
return servers
async def authenticate(self, host, servers):
async def authenticate(self, host, port, servers):
"""Authenticate with one or more roon servers."""
def stop_apis(apis):
@ -54,6 +61,7 @@ class RoonHub:
token = None
core_id = None
core_name = None
secs = 0
if host is None:
apis = [
@ -61,7 +69,7 @@ class RoonHub:
for server in servers
]
else:
apis = [RoonApi(ROON_APPINFO, None, host, blocking_init=False)]
apis = [RoonApi(ROON_APPINFO, None, host, port, blocking_init=False)]
while secs <= TIMEOUT:
# Roon can discover multiple devices - not all of which are proper servers, so try and authenticate with them all.
@ -71,6 +79,7 @@ class RoonHub:
secs += AUTHENTICATE_TIMEOUT
if auth_api:
core_id = auth_api[0].core_id
core_name = auth_api[0].core_name
token = auth_api[0].token
break
@ -78,7 +87,7 @@ class RoonHub:
await self._hass.async_add_executor_job(stop_apis, apis)
return (token, core_id)
return (token, core_id, core_name)
async def discover(hass):
@ -90,15 +99,21 @@ async def discover(hass):
return servers
async def authenticate(hass: core.HomeAssistant, host, servers):
async def authenticate(hass: core.HomeAssistant, host, port, servers):
"""Connect and authenticate home assistant."""
hub = RoonHub(hass)
(token, core_id) = await hub.authenticate(host, servers)
(token, core_id, core_name) = await hub.authenticate(host, port, servers)
if token is None:
raise InvalidAuth
return {CONF_HOST: host, CONF_ROON_ID: core_id, CONF_API_KEY: token}
return {
CONF_HOST: host,
CONF_PORT: port,
CONF_ROON_ID: core_id,
CONF_ROON_NAME: core_name,
CONF_API_KEY: token,
}
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
@ -109,33 +124,45 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
def __init__(self):
"""Initialize the Roon flow."""
self._host = None
self._port = None
self._servers = []
async def async_step_user(self, user_input=None):
"""Handle getting host details from the user."""
"""Get roon core details via discovery."""
errors = {}
self._servers = await discover(self.hass)
# We discovered one or more roon - so skip to authentication
if self._servers:
return await self.async_step_link()
return await self.async_step_fallback()
async def async_step_fallback(self, user_input=None):
"""Get host and port details from the user."""
errors = {}
if user_input is not None:
self._host = user_input["host"]
self._port = user_input["port"]
return await self.async_step_link()
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors
step_id="fallback", data_schema=DATA_SCHEMA, errors=errors
)
async def async_step_link(self, user_input=None):
"""Handle linking and authenticting with the roon server."""
errors = {}
if user_input is not None:
# Do not authenticate if the host is already configured
self._async_abort_entries_match({CONF_HOST: self._host})
try:
info = await authenticate(self.hass, self._host, self._servers)
info = await authenticate(
self.hass, self._host, self._port, self._servers
)
except InvalidAuth:
errors["base"] = "invalid_auth"
except Exception: # pylint: disable=broad-except