Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e898297
Add config flow support
jjlawren Sep 10, 2019
09f8cc7
Log error on failed connection
jjlawren Sep 10, 2019
0d86d67
Review comments
jjlawren Sep 10, 2019
1513c6e
Unused errors
jjlawren Sep 10, 2019
28922e5
Move form to step
jjlawren Sep 10, 2019
63d6752
Use instance var instead of passing argument
jjlawren Sep 10, 2019
4384f53
Only share servers created by component
jjlawren Sep 10, 2019
efe1d7c
Return errors early to avoid try:else
jjlawren Sep 10, 2019
97b7fc2
Separate debug for validation vs setup
jjlawren Sep 10, 2019
84bfc3c
Unnecessary
jjlawren Sep 10, 2019
137afd0
Unnecessary checks
jjlawren Sep 10, 2019
9011d46
Combine import flows, move logic to component
jjlawren Sep 10, 2019
cbcdc2a
Use config entry discovery handler
jjlawren Sep 10, 2019
828aa6d
Temporary lint fix
jjlawren Sep 10, 2019
304667a
Filter out servers already configured
jjlawren Sep 11, 2019
bb8e67e
Remove manual config flow
jjlawren Sep 11, 2019
b9813c9
Skip discovery if a config exists
jjlawren Sep 11, 2019
220fb07
Swap conditional to reduce indenting
jjlawren Sep 11, 2019
3a809ff
Only discover when no configs created or creating
jjlawren Sep 11, 2019
7592f84
Un-nest function
jjlawren Sep 11, 2019
c5fdfa2
Proper async use
jjlawren Sep 11, 2019
da0d61d
Move legacy file import to discovery
jjlawren Sep 12, 2019
2694fea
Fix, bad else
jjlawren Sep 12, 2019
825d2dc
Separate validate step
jjlawren Sep 12, 2019
aa8a030
Unused without manual setup step
jjlawren Sep 12, 2019
10cd2bd
Async oops
jjlawren Sep 12, 2019
7dbdc98
First attempt at tests
jjlawren Sep 17, 2019
0e112d2
Test cleanup
jjlawren Sep 17, 2019
5e71311
Full test coverage for config_flow, enable tests
jjlawren Sep 17, 2019
37b249b
Lint
jjlawren Sep 18, 2019
b980fb0
Fix lint vs black
jjlawren Sep 18, 2019
c64eae2
Add test init
jjlawren Sep 18, 2019
9286ade
Add test package requirement
jjlawren Sep 18, 2019
06c18e5
Actually run script
jjlawren Sep 18, 2019
2a19a9e
Use 'not None' convention
jjlawren Sep 18, 2019
64fee5e
Group exceptions by result
jjlawren Sep 18, 2019
7c31e71
Improve logic, add new error and test
jjlawren Sep 18, 2019
cf47a4d
Test cleanup
jjlawren Sep 18, 2019
c461e6d
Add more asserts
jjlawren Sep 18, 2019
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
5 changes: 4 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,10 @@ omit =
homeassistant/components/pioneer/media_player.py
homeassistant/components/pjlink/media_player.py
homeassistant/components/plaato/*
homeassistant/components/plex/*
homeassistant/components/plex/__init__.py
homeassistant/components/plex/media_player.py
homeassistant/components/plex/sensor.py
homeassistant/components/plex/server.py
homeassistant/components/plugwise/*
homeassistant/components/plum_lightpad/*
homeassistant/components/pocketcasts/sensor.py
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/discovery/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
SERVICE_DAIKIN: "daikin",
SERVICE_TELLDUSLIVE: "tellduslive",
SERVICE_IGD: "upnp",
SERVICE_PLEX: "plex",
}

SERVICE_HANDLERS = {
Expand All @@ -69,7 +70,6 @@
SERVICE_FREEBOX: ("freebox", None),
SERVICE_YEELIGHT: ("yeelight", None),
"panasonic_viera": ("media_player", "panasonic_viera"),
SERVICE_PLEX: ("plex", None),
"yamaha": ("media_player", "yamaha"),
"logitech_mediaserver": ("media_player", "squeezebox"),
"directv": ("media_player", "directv"),
Expand Down
205 changes: 64 additions & 141 deletions homeassistant/components/plex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import requests.exceptions
import voluptuous as vol

from homeassistant.components.discovery import SERVICE_PLEX
from homeassistant import config_entries
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
from homeassistant.const import (
CONF_HOST,
Expand All @@ -16,20 +16,18 @@
CONF_VERIFY_SSL,
)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import discovery
from homeassistant.util.json import load_json, save_json

from .const import (
CONF_SERVER,
CONF_USE_EPISODE_ART,
CONF_SHOW_ALL_CONTROLS,
CONF_SERVER,
DEFAULT_PORT,
DEFAULT_SSL,
DEFAULT_VERIFY_SSL,
DOMAIN as PLEX_DOMAIN,
PLATFORMS,
PLEX_CONFIG_FILE,
PLEX_MEDIA_PLAYER_OPTIONS,
PLEX_SERVER_CONFIG,
SERVERS,
)
from .server import PlexServer
Expand Down Expand Up @@ -58,151 +56,76 @@

CONFIG_SCHEMA = vol.Schema({PLEX_DOMAIN: SERVER_CONFIG_SCHEMA}, extra=vol.ALLOW_EXTRA)

CONFIGURING = "configuring"
_LOGGER = logging.getLogger(__package__)


def setup(hass, config):
"""Set up the Plex component."""
hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}})

def server_discovered(service, info):
"""Pass back discovered Plex server details."""
if hass.data[PLEX_DOMAIN][SERVERS]:
_LOGGER.debug("Plex server already configured, ignoring discovery.")
return
_LOGGER.debug("Discovered Plex server: %s:%s", info["host"], info["port"])
setup_plex(discovery_info=info)

def setup_plex(config=None, discovery_info=None, configurator_info=None):
"""Return assembled server_config dict."""
json_file = hass.config.path(PLEX_CONFIG_FILE)
file_config = load_json(json_file)
host_and_port = None

if config:
server_config = config
if CONF_HOST in server_config:
host_and_port = (
f"{server_config.pop(CONF_HOST)}:{server_config.pop(CONF_PORT)}"
)
if MP_DOMAIN in server_config:
hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = server_config.pop(MP_DOMAIN)
elif file_config:
_LOGGER.debug("Loading config from %s", json_file)
host_and_port, server_config = file_config.popitem()
server_config[CONF_VERIFY_SSL] = server_config.pop("verify")
elif discovery_info:
server_config = {}
host_and_port = f"{discovery_info[CONF_HOST]}:{discovery_info[CONF_PORT]}"
elif configurator_info:
server_config = configurator_info
host_and_port = server_config["host_and_port"]
else:
discovery.listen(hass, SERVICE_PLEX, server_discovered)
return True

if host_and_port:
use_ssl = server_config.get(CONF_SSL, DEFAULT_SSL)
http_prefix = "https" if use_ssl else "http"
server_config[CONF_URL] = f"{http_prefix}://{host_and_port}"

plex_server = PlexServer(server_config)
try:
plex_server.connect()
except requests.exceptions.ConnectionError as error:
_LOGGER.error(
"Plex server could not be reached, please verify host and port: [%s]",
error,
)
return False
except (
plexapi.exceptions.BadRequest,
plexapi.exceptions.Unauthorized,
plexapi.exceptions.NotFound,
) as error:
_LOGGER.error(
"Connection to Plex server failed, please verify token and SSL settings: [%s]",
error,
)
request_configuration(host_and_port)
return False
else:
hass.data[PLEX_DOMAIN][SERVERS][
plex_server.machine_identifier
] = plex_server

if host_and_port in hass.data[PLEX_DOMAIN][CONFIGURING]:
request_id = hass.data[PLEX_DOMAIN][CONFIGURING].pop(host_and_port)
configurator = hass.components.configurator
configurator.request_done(request_id)
_LOGGER.debug("Discovery configuration done")
if configurator_info:
# Write plex.conf if created via discovery/configurator
save_json(
hass.config.path(PLEX_CONFIG_FILE),
{
host_and_port: {
CONF_TOKEN: server_config[CONF_TOKEN],
CONF_SSL: use_ssl,
"verify": server_config[CONF_VERIFY_SSL],
}
},
)

if not hass.data.get(PLEX_MEDIA_PLAYER_OPTIONS):
hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = MEDIA_PLAYER_SCHEMA({})

for platform in PLATFORMS:
hass.helpers.discovery.load_platform(
platform, PLEX_DOMAIN, {}, original_config
)

return True

def request_configuration(host_and_port):
"""Request configuration steps from the user."""
configurator = hass.components.configurator
if host_and_port in hass.data[PLEX_DOMAIN][CONFIGURING]:
configurator.notify_errors(
hass.data[PLEX_DOMAIN][CONFIGURING][host_and_port],
"Failed to register, please try again.",
)
return

def plex_configuration_callback(data):
"""Handle configuration changes."""
config = {
"host_and_port": host_and_port,
CONF_TOKEN: data.get("token"),
CONF_SSL: cv.boolean(data.get("ssl")),
CONF_VERIFY_SSL: cv.boolean(data.get("verify_ssl")),
}
setup_plex(configurator_info=config)

hass.data[PLEX_DOMAIN][CONFIGURING][
host_and_port
] = configurator.request_config(
"Plex Media Server",
plex_configuration_callback,
description="Enter the X-Plex-Token",
entity_picture="/static/images/logo_plex_mediaserver.png",
submit_caption="Confirm",
fields=[
{"id": "token", "name": "X-Plex-Token", "type": ""},
{"id": "ssl", "name": "Use SSL", "type": ""},
{"id": "verify_ssl", "name": "Verify SSL", "type": ""},
],
plex_config = config.get(PLEX_DOMAIN, {})
if plex_config:
_setup_plex(hass, plex_config)

return True


def _setup_plex(hass, config):
"""Pass configuration to a config flow."""
server_config = dict(config)
if MP_DOMAIN in server_config:
hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = server_config.pop(MP_DOMAIN)
if CONF_HOST in server_config:
prefix = "https" if server_config.pop(CONF_SSL) else "http"
server_config[
CONF_URL
] = f"{prefix}://{server_config.pop(CONF_HOST)}:{server_config.pop(CONF_PORT)}"
hass.async_create_task(
hass.config_entries.flow.async_init(
PLEX_DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=server_config,
)
)

# End of inner functions.

original_config = config

hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}, CONFIGURING: {}})
async def async_setup_entry(hass, entry):
"""Set up Plex from a config entry."""
server_config = entry.data[PLEX_SERVER_CONFIG]

if hass.data[PLEX_DOMAIN][SERVERS]:
_LOGGER.debug("Plex server already configured")
plex_server = PlexServer(server_config)
try:
await hass.async_add_executor_job(plex_server.connect)
except requests.exceptions.ConnectionError as error:
_LOGGER.error(
"Plex server (%s) could not be reached: [%s]",
server_config[CONF_URL],
error,
)
return False
except (
plexapi.exceptions.BadRequest,
plexapi.exceptions.Unauthorized,
plexapi.exceptions.NotFound,
) as error:
_LOGGER.error(
"Login to %s failed, verify token and SSL settings: [%s]",
server_config[CONF_SERVER],
error,
)
return False

plex_config = config.get(PLEX_DOMAIN, {})
return setup_plex(config=plex_config)
_LOGGER.debug(
"Connected to: %s (%s)", plex_server.friendly_name, plex_server.url_in_use
)
hass.data[PLEX_DOMAIN][SERVERS][plex_server.machine_identifier] = plex_server

if not hass.data.get(PLEX_MEDIA_PLAYER_OPTIONS):
hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = MEDIA_PLAYER_SCHEMA({})

for platform in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, platform)
)

return True
Comment thread
jjlawren marked this conversation as resolved.
Loading