Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 49 additions & 4 deletions homeassistant/components/plex/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,22 @@
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import CONF_URL, CONF_TOKEN, CONF_SSL, CONF_VERIFY_SSL
from homeassistant.const import (
CONF_HOST,
CONF_PORT,
CONF_URL,
CONF_TOKEN,
CONF_SSL,
CONF_VERIFY_SSL,
)
from homeassistant.core import callback
from homeassistant.util.json import load_json

from .const import ( # pylint: disable=unused-import
CONF_SERVER,
CONF_SERVER_IDENTIFIER,
DEFAULT_PORT,
DEFAULT_SSL,
DEFAULT_VERIFY_SSL,
DOMAIN,
PLEX_CONFIG_FILE,
Expand All @@ -21,7 +30,9 @@
from .errors import NoServersFound, ServerNotSpecified
from .server import PlexServer

USER_SCHEMA = vol.Schema({vol.Required(CONF_TOKEN): str})
USER_SCHEMA = vol.Schema(
{vol.Optional(CONF_TOKEN): str, vol.Optional("manual_setup"): bool}
)

_LOGGER = logging.getLogger(__package__)

Expand All @@ -44,14 +55,22 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
def __init__(self):
"""Initialize the Plex flow."""
self.current_login = {}
self.discovery_info = {}
self.available_servers = None

async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
errors = {}
if user_input is not None:
return await self.async_step_server_validate(user_input)
if user_input.pop("manual_setup", False):
return await self.async_step_manual_setup(user_input)
if CONF_TOKEN in user_input:
Comment thread
jjlawren marked this conversation as resolved.
return await self.async_step_server_validate(user_input)
errors[CONF_TOKEN] = "no_token"

return self.async_show_form(step_id="user", data_schema=USER_SCHEMA, errors={})
return self.async_show_form(
step_id="user", data_schema=USER_SCHEMA, errors=errors
)

async def async_step_server_validate(self, server_config):
"""Validate a provided configuration."""
Expand Down Expand Up @@ -114,6 +133,30 @@ async def async_step_server_validate(self, server_config):
},
)

async def async_step_manual_setup(self, user_input=None):
"""Begin manual configuration."""
if len(user_input) > 1:
host = user_input.pop(CONF_HOST)
port = user_input.pop(CONF_PORT)
prefix = "https" if user_input.pop(CONF_SSL) else "http"
user_input[CONF_URL] = f"{prefix}://{host}:{port}"
return await self.async_step_server_validate(user_input)

data_schema = vol.Schema(
{
vol.Required(
CONF_HOST, default=self.discovery_info.get(CONF_HOST)
): str,
vol.Required(
CONF_PORT, default=self.discovery_info.get(CONF_PORT, DEFAULT_PORT)
): int,
vol.Optional(CONF_SSL, default=DEFAULT_SSL): bool,
vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): bool,
vol.Optional(CONF_TOKEN, default=user_input.get(CONF_TOKEN, "")): str,
}
)
return self.async_show_form(step_id="manual_setup", data_schema=data_schema)

async def async_step_select_server(self, user_input=None):
"""Use selected Plex server."""
config = dict(self.current_login)
Expand Down Expand Up @@ -148,6 +191,8 @@ async def async_step_discovery(self, discovery_info):
# Skip discovery if a config already exists or is in progress.
return self.async_abort(reason="already_configured")

discovery_info[CONF_PORT] = int(discovery_info[CONF_PORT])
self.discovery_info = discovery_info
json_file = self.hass.config.path(PLEX_CONFIG_FILE)
file_config = await self.hass.async_add_executor_job(load_json, json_file)

Expand Down
18 changes: 15 additions & 3 deletions homeassistant/components/plex/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
"config": {
"title": "Plex",
"step": {
"manual_setup": {
"title": "Plex server",
"data": {
"host": "Host",
"port": "Port",
"ssl": "Use SSL",
"verify_ssl": "Verify SSL certificate",
"token": "Token (if required)"
}
},
"select_server": {
"title": "Select Plex server",
"description": "Multiple servers available, select one:",
Expand All @@ -11,16 +21,18 @@
},
"user": {
"title": "Connect Plex server",
"description": "Enter a Plex token for automatic setup.",
"description": "Enter a Plex token for automatic setup or manually configure a server.",
"data": {
"token": "Plex token"
"token": "Plex token",
"manual_setup": "Manual setup"
}
}
},
"error": {
"faulty_credentials": "Authorization failed",
"no_servers": "No servers linked to account",
"not_found": "Plex server not found"
"not_found": "Plex server not found",
"no_token": "Provide a token or select manual setup"
},
"abort": {
"all_configured": "All linked servers already configured",
Expand Down
84 changes: 76 additions & 8 deletions tests/components/plex/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
import requests.exceptions

from homeassistant.components.plex import config_flow
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TOKEN, CONF_URL
from homeassistant.const import (
CONF_HOST,
CONF_PORT,
CONF_SSL,
CONF_VERIFY_SSL,
CONF_TOKEN,
CONF_URL,
)

from tests.common import MockConfigEntry

Expand Down Expand Up @@ -44,7 +51,8 @@ async def test_bad_credentials(hass):
):

result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={CONF_TOKEN: MOCK_TOKEN}
result["flow_id"],
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
)

assert result["type"] == "form"
Expand Down Expand Up @@ -196,7 +204,7 @@ async def test_unknown_exception(hass):
result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN,
context={"source": "user"},
data={CONF_TOKEN: MOCK_TOKEN},
data={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
)

assert result["type"] == "abort"
Expand All @@ -219,7 +227,8 @@ async def test_no_servers_found(hass):
with patch("plexapi.myplex.MyPlexAccount", return_value=mm_plex_account):

result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={CONF_TOKEN: MOCK_TOKEN}
result["flow_id"],
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
)

assert result["type"] == "form"
Expand Down Expand Up @@ -257,7 +266,8 @@ async def test_single_available_server(hass):
)._baseurl = PropertyMock(return_value=mock_connections.connections[0].httpuri)

result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={CONF_TOKEN: MOCK_TOKEN}
result["flow_id"],
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
)

assert result["type"] == "create_entry"
Expand Down Expand Up @@ -303,7 +313,8 @@ async def test_multiple_servers_with_selection(hass):
)._baseurl = PropertyMock(return_value=mock_connections.connections[0].httpuri)

result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={CONF_TOKEN: MOCK_TOKEN}
result["flow_id"],
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
)

assert result["type"] == "form"
Expand Down Expand Up @@ -364,7 +375,8 @@ async def test_adding_last_unconfigured_server(hass):
)._baseurl = PropertyMock(return_value=mock_connections.connections[0].httpuri)

result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={CONF_TOKEN: MOCK_TOKEN}
result["flow_id"],
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
)

assert result["type"] == "create_entry"
Expand Down Expand Up @@ -447,8 +459,64 @@ async def test_all_available_servers_configured(hass):
with patch("plexapi.myplex.MyPlexAccount", return_value=mm_plex_account):

result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={CONF_TOKEN: MOCK_TOKEN}
result["flow_id"],
user_input={CONF_TOKEN: MOCK_TOKEN, "manual_setup": False},
)

assert result["type"] == "abort"
assert result["reason"] == "all_configured"


async def test_manual_config(hass):
"""Test creating via manual configuration."""

result = await hass.config_entries.flow.async_init(
config_flow.DOMAIN, context={"source": "user"}
)

assert result["type"] == "form"
assert result["step_id"] == "user"

result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={CONF_TOKEN: "", "manual_setup": True}
)

assert result["type"] == "form"
assert result["step_id"] == "manual_setup"

mock_connections = MockConnections(ssl=True)

with patch("plexapi.server.PlexServer") as mock_plex_server:
type(mock_plex_server.return_value).machineIdentifier = PropertyMock(
return_value=MOCK_SERVER_1.clientIdentifier
)
type(mock_plex_server.return_value).friendlyName = PropertyMock(
return_value=MOCK_SERVER_1.name
)
type( # pylint: disable=protected-access
mock_plex_server.return_value
)._baseurl = PropertyMock(return_value=mock_connections.connections[0].httpuri)

result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={
CONF_HOST: MOCK_HOST_1,
CONF_PORT: int(MOCK_PORT_1),
CONF_SSL: True,
CONF_VERIFY_SSL: True,
CONF_TOKEN: MOCK_TOKEN,
},
)

assert result["type"] == "create_entry"
assert result["title"] == MOCK_SERVER_1.name
assert result["data"][config_flow.CONF_SERVER] == MOCK_SERVER_1.name
assert (
result["data"][config_flow.CONF_SERVER_IDENTIFIER]
== MOCK_SERVER_1.clientIdentifier
)
assert (
result["data"][config_flow.PLEX_SERVER_CONFIG][CONF_URL]
== mock_connections.connections[0].httpuri
)
assert result["data"][config_flow.PLEX_SERVER_CONFIG][CONF_TOKEN] == MOCK_TOKEN