2024-06-22 09:58:23 +01:00
""" Config flow for Squeezebox integration. """
2024-03-08 19:15:59 +01:00
2024-09-03 13:19:30 -04:00
from __future__ import annotations
2020-06-22 09:29:01 -05:00
import asyncio
2021-10-23 21:49:04 +03:00
from http import HTTPStatus
2020-06-22 09:29:01 -05:00
import logging
2024-08-01 09:19:16 +02:00
from typing import TYPE_CHECKING , Any
2020-06-22 09:29:01 -05:00
from pysqueezebox import Server , async_discover
import voluptuous as vol
2021-11-21 23:33:44 +01:00
from homeassistant . components import dhcp
2021-08-06 12:29:52 -04:00
from homeassistant . components . media_player import DOMAIN as MP_DOMAIN
2024-02-29 22:16:14 +01:00
from homeassistant . config_entries import ConfigFlow , ConfigFlowResult
2021-10-23 21:49:04 +03:00
from homeassistant . const import CONF_HOST , CONF_PASSWORD , CONF_PORT , CONF_USERNAME
2024-02-29 22:16:14 +01:00
from homeassistant . data_entry_flow import AbortFlow
2024-05-27 12:50:11 +02:00
from homeassistant . helpers import entity_registry as er
2020-06-22 09:29:01 -05:00
from homeassistant . helpers . aiohttp_client import async_get_clientsession
2021-08-06 12:29:52 -04:00
from homeassistant . helpers . device_registry import format_mac
2020-06-22 09:29:01 -05:00
2023-12-08 09:51:19 -05:00
from . const import CONF_HTTPS , DEFAULT_PORT , DOMAIN
2020-06-22 09:29:01 -05:00
_LOGGER = logging . getLogger ( __name__ )
TIMEOUT = 5
2024-09-03 13:19:30 -04:00
def _base_schema (
discovery_info : dict [ str , Any ] | None = None ,
) - > vol . Schema :
2020-06-22 09:29:01 -05:00
""" Generate base schema. """
2024-09-03 13:19:30 -04:00
base_schema : dict [ Any , Any ] = { }
2020-06-22 09:29:01 -05:00
if discovery_info and CONF_HOST in discovery_info :
base_schema . update (
{
vol . Required (
CONF_HOST ,
description = { " suggested_value " : discovery_info [ CONF_HOST ] } ,
) : str ,
}
)
else :
base_schema . update ( { vol . Required ( CONF_HOST ) : str } )
if discovery_info and CONF_PORT in discovery_info :
base_schema . update (
{
vol . Required (
CONF_PORT ,
default = DEFAULT_PORT ,
description = { " suggested_value " : discovery_info [ CONF_PORT ] } ,
) : int ,
}
)
else :
base_schema . update ( { vol . Required ( CONF_PORT , default = DEFAULT_PORT ) : int } )
2023-12-08 09:51:19 -05:00
2020-06-22 09:29:01 -05:00
base_schema . update (
2023-12-08 09:51:19 -05:00
{
vol . Optional ( CONF_USERNAME ) : str ,
vol . Optional ( CONF_PASSWORD ) : str ,
vol . Optional ( CONF_HTTPS , default = False ) : bool ,
}
2020-06-22 09:29:01 -05:00
)
2023-12-08 09:51:19 -05:00
2020-06-22 09:29:01 -05:00
return vol . Schema ( base_schema )
2024-02-29 22:16:14 +01:00
class SqueezeboxConfigFlow ( ConfigFlow , domain = DOMAIN ) :
2024-06-22 09:58:23 +01:00
""" Handle a config flow for Squeezebox. """
2020-06-22 09:29:01 -05:00
VERSION = 1
2023-02-07 15:55:14 +01:00
def __init__ ( self ) - > None :
2020-06-22 09:29:01 -05:00
""" Initialize an instance of the squeezebox config flow. """
self . data_schema = _base_schema ( )
2024-09-03 13:19:30 -04:00
self . discovery_info : dict [ str , Any ] | None = None
2020-06-22 09:29:01 -05:00
2024-09-03 13:19:30 -04:00
async def _discover ( self , uuid : str | None = None ) - > None :
2020-06-22 09:29:01 -05:00
""" Discover an unconfigured LMS server. """
self . discovery_info = None
discovery_event = asyncio . Event ( )
2024-09-03 13:19:30 -04:00
def _discovery_callback ( server : Server ) - > None :
2020-06-22 09:29:01 -05:00
if server . uuid :
# ignore already configured uuids
for entry in self . _async_current_entries ( ) :
if entry . unique_id == server . uuid :
return
self . discovery_info = {
CONF_HOST : server . host ,
2021-03-13 01:34:20 -06:00
CONF_PORT : int ( server . port ) ,
2020-06-22 09:29:01 -05:00
" uuid " : server . uuid ,
}
_LOGGER . debug ( " Discovered server: %s " , self . discovery_info )
discovery_event . set ( )
discovery_task = self . hass . async_create_task (
async_discover ( _discovery_callback )
)
await discovery_event . wait ( )
discovery_task . cancel ( ) # stop searching as soon as we find server
# update with suggested values from discovery
self . data_schema = _base_schema ( self . discovery_info )
2024-08-01 09:19:16 +02:00
async def _validate_input ( self , data : dict [ str , Any ] ) - > str | None :
2023-02-03 23:08:48 +01:00
""" Validate the user input allows us to connect.
2020-06-22 09:29:01 -05:00
Retrieve unique id and abort if already configured .
"""
server = Server (
async_get_clientsession ( self . hass ) ,
data [ CONF_HOST ] ,
data [ CONF_PORT ] ,
data . get ( CONF_USERNAME ) ,
data . get ( CONF_PASSWORD ) ,
2023-12-08 09:51:19 -05:00
https = data [ CONF_HTTPS ] ,
2020-06-22 09:29:01 -05:00
)
try :
status = await server . async_query ( " serverstatus " )
if not status :
2021-10-23 21:49:04 +03:00
if server . http_status == HTTPStatus . UNAUTHORIZED :
2020-06-22 09:29:01 -05:00
return " invalid_auth "
return " cannot_connect "
2024-05-07 14:00:27 +02:00
except Exception : # noqa: BLE001
2020-06-22 09:29:01 -05:00
return " unknown "
if " uuid " in status :
await self . async_set_unique_id ( status [ " uuid " ] )
self . _abort_if_unique_id_configured ( )
2024-08-01 09:19:16 +02:00
return None
2024-08-21 22:42:58 +02:00
async def async_step_user (
self , user_input : dict [ str , Any ] | None = None
) - > ConfigFlowResult :
2020-06-22 09:29:01 -05:00
""" Handle a flow initialized by the user. """
errors = { }
if user_input and CONF_HOST in user_input :
# update with host provided by user
self . data_schema = _base_schema ( user_input )
return await self . async_step_edit ( )
# no host specified, see if we can discover an unconfigured LMS server
try :
2023-08-15 14:34:18 +02:00
async with asyncio . timeout ( TIMEOUT ) :
2023-02-24 14:13:03 +01:00
await self . _discover ( )
2020-06-22 09:29:01 -05:00
return await self . async_step_edit ( )
2024-02-05 12:14:37 +01:00
except TimeoutError :
2020-06-22 09:29:01 -05:00
errors [ " base " ] = " no_server_found "
# display the form
return self . async_show_form (
step_id = " user " ,
data_schema = vol . Schema ( { vol . Optional ( CONF_HOST ) : str } ) ,
errors = errors ,
)
2024-09-03 13:19:30 -04:00
async def async_step_edit (
self , user_input : dict [ str , Any ] | None = None
) - > ConfigFlowResult :
2020-06-22 09:29:01 -05:00
""" Edit a discovered or manually inputted server. """
errors = { }
if user_input :
error = await self . _validate_input ( user_input )
2020-06-24 12:04:17 -05:00
if not error :
2020-06-22 09:29:01 -05:00
return self . async_create_entry (
title = user_input [ CONF_HOST ] , data = user_input
)
2020-06-24 12:04:17 -05:00
errors [ " base " ] = error
2020-06-22 09:29:01 -05:00
return self . async_show_form (
step_id = " edit " , data_schema = self . data_schema , errors = errors
)
2024-09-03 13:19:30 -04:00
async def async_step_integration_discovery (
self , discovery_info : dict [ str , Any ]
) - > ConfigFlowResult :
2021-08-06 12:29:52 -04:00
""" Handle discovery of a server. """
_LOGGER . debug ( " Reached server discovery flow with info: %s " , discovery_info )
2020-06-22 09:29:01 -05:00
if " uuid " in discovery_info :
await self . async_set_unique_id ( discovery_info . pop ( " uuid " ) )
self . _abort_if_unique_id_configured ( )
else :
2021-05-26 03:30:15 -05:00
# attempt to connect to server and determine uuid. will fail if
# password required
2020-06-22 09:29:01 -05:00
error = await self . _validate_input ( discovery_info )
if error :
2021-08-06 12:29:52 -04:00
await self . _async_handle_discovery_without_unique_id ( )
2020-06-22 09:29:01 -05:00
# update schema with suggested values from discovery
self . data_schema = _base_schema ( discovery_info )
self . context . update ( { " title_placeholders " : { " host " : discovery_info [ CONF_HOST ] } } )
return await self . async_step_edit ( )
2021-08-06 12:29:52 -04:00
2021-11-21 23:33:44 +01:00
async def async_step_dhcp (
self , discovery_info : dhcp . DhcpServiceInfo
2024-02-29 22:16:14 +01:00
) - > ConfigFlowResult :
2021-08-06 12:29:52 -04:00
""" Handle dhcp discovery of a Squeezebox player. """
_LOGGER . debug (
" Reached dhcp discovery of a player with info: %s " , discovery_info
)
2021-12-01 17:52:29 +01:00
await self . async_set_unique_id ( format_mac ( discovery_info . macaddress ) )
2021-08-06 12:29:52 -04:00
self . _abort_if_unique_id_configured ( )
_LOGGER . debug ( " Configuring dhcp player with unique id: %s " , self . unique_id )
2024-05-27 12:50:11 +02:00
registry = er . async_get ( self . hass )
2021-08-06 12:29:52 -04:00
2021-11-21 23:33:44 +01:00
if TYPE_CHECKING :
assert self . unique_id
2021-08-06 12:29:52 -04:00
# if we have detected this player, do nothing. if not, there must be a server out there for us to configure, so start the normal user flow (which tries to autodetect server)
if registry . async_get_entity_id ( MP_DOMAIN , DOMAIN , self . unique_id ) is not None :
# this player is already known, so do nothing other than mark as configured
2024-02-29 22:16:14 +01:00
raise AbortFlow ( " already_configured " )
2021-08-06 12:29:52 -04:00
# if the player is unknown, then we likely need to configure its server
return await self . async_step_user ( )