Add Plex config options support (#26870)

* Add config options support

* Actually copy dict

* Move media_player options to PlexServer class

* Handle updated config options

* Move callback out of server
pull/26930/head
jjlawren 2019-09-26 04:10:20 -05:00 committed by Martin Hjelmare
parent 62cea2b7ac
commit 9b204ad162
6 changed files with 160 additions and 19 deletions

View File

@ -77,7 +77,7 @@ 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)
hass.data.setdefault(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[
@ -96,7 +96,15 @@ async def async_setup_entry(hass, entry):
"""Set up Plex from a config entry."""
server_config = entry.data[PLEX_SERVER_CONFIG]
plex_server = PlexServer(server_config)
if MP_DOMAIN not in entry.options:
options = dict(entry.options)
options.setdefault(
MP_DOMAIN,
hass.data.get(PLEX_MEDIA_PLAYER_OPTIONS) or MEDIA_PLAYER_SCHEMA({}),
)
hass.config_entries.async_update_entry(entry, options=options)
plex_server = PlexServer(server_config, entry.options)
try:
await hass.async_add_executor_job(plex_server.connect)
except requests.exceptions.ConnectionError as error:
@ -123,14 +131,13 @@ async def async_setup_entry(hass, entry):
)
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)
)
entry.add_update_listener(async_options_updated)
return True
@ -150,3 +157,9 @@ async def async_unload_entry(hass, entry):
hass.data[PLEX_DOMAIN][SERVERS].pop(server_id)
return True
async def async_options_updated(hass, entry):
"""Triggered by config entry options updates."""
server_id = entry.data[CONF_SERVER_IDENTIFIER]
hass.data[PLEX_DOMAIN][SERVERS][server_id].options = entry.options

View File

@ -1,4 +1,5 @@
"""Config flow for Plex."""
import copy
import logging
import plexapi.exceptions
@ -6,6 +7,7 @@ import requests.exceptions
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
from homeassistant.const import (
CONF_HOST,
CONF_PORT,
@ -20,6 +22,8 @@ from homeassistant.util.json import load_json
from .const import ( # pylint: disable=unused-import
CONF_SERVER,
CONF_SERVER_IDENTIFIER,
CONF_USE_EPISODE_ART,
CONF_SHOW_ALL_CONTROLS,
DEFAULT_PORT,
DEFAULT_SSL,
DEFAULT_VERIFY_SSL,
@ -52,6 +56,12 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL
@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Get the options flow for this handler."""
return PlexOptionsFlowHandler(config_entry)
def __init__(self):
"""Initialize the Plex flow."""
self.current_login = {}
@ -214,3 +224,42 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Import from Plex configuration."""
_LOGGER.debug("Imported Plex configuration")
return await self.async_step_server_validate(import_config)
class PlexOptionsFlowHandler(config_entries.OptionsFlow):
"""Handle Plex options."""
def __init__(self, config_entry):
"""Initialize Plex options flow."""
self.options = copy.deepcopy(config_entry.options)
async def async_step_init(self, user_input=None):
"""Manage the Plex options."""
return await self.async_step_plex_mp_settings()
async def async_step_plex_mp_settings(self, user_input=None):
"""Manage the Plex media_player options."""
if user_input is not None:
self.options[MP_DOMAIN][CONF_USE_EPISODE_ART] = user_input[
CONF_USE_EPISODE_ART
]
self.options[MP_DOMAIN][CONF_SHOW_ALL_CONTROLS] = user_input[
CONF_SHOW_ALL_CONTROLS
]
return self.async_create_entry(title="", data=self.options)
return self.async_show_form(
step_id="plex_mp_settings",
data_schema=vol.Schema(
{
vol.Required(
CONF_USE_EPISODE_ART,
default=self.options[MP_DOMAIN][CONF_USE_EPISODE_ART],
): bool,
vol.Required(
CONF_SHOW_ALL_CONTROLS,
default=self.options[MP_DOMAIN][CONF_SHOW_ALL_CONTROLS],
): bool,
}
),
)

View File

@ -33,12 +33,9 @@ from homeassistant.helpers.event import track_time_interval
from homeassistant.util import dt as dt_util
from .const import (
CONF_USE_EPISODE_ART,
CONF_SHOW_ALL_CONTROLS,
CONF_SERVER_IDENTIFIER,
DOMAIN as PLEX_DOMAIN,
NAME_FORMAT,
PLEX_MEDIA_PLAYER_OPTIONS,
REFRESH_LISTENERS,
SERVERS,
)
@ -67,8 +64,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
def _setup_platform(hass, config_entry, add_entities_callback):
"""Set up the Plex media_player platform."""
server_id = config_entry.data[CONF_SERVER_IDENTIFIER]
config = hass.data[PLEX_MEDIA_PLAYER_OPTIONS]
plexserver = hass.data[PLEX_DOMAIN][SERVERS][server_id]
plex_clients = {}
plex_sessions = {}
@ -102,7 +97,7 @@ def _setup_platform(hass, config_entry, add_entities_callback):
if device.machineIdentifier not in plex_clients:
new_client = PlexClient(
config, device, None, plex_sessions, update_devices
plexserver, device, None, plex_sessions, update_devices
)
plex_clients[device.machineIdentifier] = new_client
_LOGGER.debug("New device: %s", device.machineIdentifier)
@ -141,7 +136,7 @@ def _setup_platform(hass, config_entry, add_entities_callback):
and machine_identifier is not None
):
new_client = PlexClient(
config, player, session, plex_sessions, update_devices
plexserver, player, session, plex_sessions, update_devices
)
plex_clients[machine_identifier] = new_client
_LOGGER.debug("New session: %s", machine_identifier)
@ -170,7 +165,7 @@ def _setup_platform(hass, config_entry, add_entities_callback):
class PlexClient(MediaPlayerDevice):
"""Representation of a Plex device."""
def __init__(self, config, device, session, plex_sessions, update_devices):
def __init__(self, plex_server, device, session, plex_sessions, update_devices):
"""Initialize the Plex device."""
self._app_name = ""
self._device = None
@ -191,7 +186,7 @@ class PlexClient(MediaPlayerDevice):
self._state = STATE_IDLE
self._volume_level = 1 # since we can't retrieve remotely
self._volume_muted = False # since we can't retrieve remotely
self.config = config
self.plex_server = plex_server
self.plex_sessions = plex_sessions
self.update_devices = update_devices
# General
@ -317,8 +312,9 @@ class PlexClient(MediaPlayerDevice):
def _set_media_image(self):
thumb_url = self._session.thumbUrl
if self.media_content_type is MEDIA_TYPE_TVSHOW and not self.config.get(
CONF_USE_EPISODE_ART
if (
self.media_content_type is MEDIA_TYPE_TVSHOW
and not self.plex_server.use_episode_art
):
thumb_url = self._session.url(self._session.grandparentThumb)
@ -551,7 +547,7 @@ class PlexClient(MediaPlayerDevice):
return 0
# force show all controls
if self.config.get(CONF_SHOW_ALL_CONTROLS):
if self.plex_server.show_all_controls:
return (
SUPPORT_PAUSE
| SUPPORT_PREVIOUS_TRACK

View File

@ -3,22 +3,29 @@ import plexapi.myplex
import plexapi.server
from requests import Session
from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
from homeassistant.const import CONF_TOKEN, CONF_URL, CONF_VERIFY_SSL
from .const import CONF_SERVER, DEFAULT_VERIFY_SSL
from .const import (
CONF_SERVER,
CONF_SHOW_ALL_CONTROLS,
CONF_USE_EPISODE_ART,
DEFAULT_VERIFY_SSL,
)
from .errors import NoServersFound, ServerNotSpecified
class PlexServer:
"""Manages a single Plex server connection."""
def __init__(self, server_config):
def __init__(self, server_config, options=None):
"""Initialize a Plex server instance."""
self._plex_server = None
self._url = server_config.get(CONF_URL)
self._token = server_config.get(CONF_TOKEN)
self._server_name = server_config.get(CONF_SERVER)
self._verify_ssl = server_config.get(CONF_VERIFY_SSL, DEFAULT_VERIFY_SSL)
self.options = options
def connect(self):
"""Connect to a Plex server directly, obtaining direct URL if necessary."""
@ -80,3 +87,13 @@ class PlexServer:
def url_in_use(self):
"""Return URL used for connected Plex server."""
return self._plex_server._baseurl # pylint: disable=W0212
@property
def use_episode_art(self):
"""Return use_episode_art option."""
return self.options[MP_DOMAIN][CONF_USE_EPISODE_ART]
@property
def show_all_controls(self):
"""Return show_all_controls option."""
return self.options[MP_DOMAIN][CONF_SHOW_ALL_CONTROLS]

View File

@ -41,5 +41,16 @@
"invalid_import": "Imported configuration is invalid",
"unknown": "Failed for unknown reason"
}
},
"options": {
"step": {
"plex_mp_settings": {
"description": "Options for Plex Media Players",
"data": {
"use_episode_art": "Use episode art",
"show_all_controls": "Show all controls"
}
}
}
}
}

View File

@ -28,6 +28,13 @@ MOCK_FILE_CONTENTS = {
MOCK_SERVER_1 = MockAvailableServer(MOCK_NAME_1, MOCK_ID_1)
MOCK_SERVER_2 = MockAvailableServer(MOCK_NAME_2, MOCK_ID_2)
DEFAULT_OPTIONS = {
config_flow.MP_DOMAIN: {
config_flow.CONF_USE_EPISODE_ART: False,
config_flow.CONF_SHOW_ALL_CONTROLS: False,
}
}
def init_config_flow(hass):
"""Init a configuration flow."""
@ -520,3 +527,51 @@ async def test_manual_config(hass):
== mock_connections.connections[0].httpuri
)
assert result["data"][config_flow.PLEX_SERVER_CONFIG][CONF_TOKEN] == MOCK_TOKEN
async def test_no_token(hass):
"""Test failing when no token provided."""
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={"manual_setup": False}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
assert result["errors"][CONF_TOKEN] == "no_token"
async def test_option_flow(hass):
"""Test config flow selection of one of two bridges."""
entry = MockConfigEntry(domain=config_flow.DOMAIN, data={}, options=DEFAULT_OPTIONS)
entry.add_to_hass(hass)
result = await hass.config_entries.options.flow.async_init(
entry.entry_id, context={"source": "test"}, data=None
)
assert result["type"] == "form"
assert result["step_id"] == "plex_mp_settings"
result = await hass.config_entries.options.flow.async_configure(
result["flow_id"],
user_input={
config_flow.CONF_USE_EPISODE_ART: True,
config_flow.CONF_SHOW_ALL_CONTROLS: True,
},
)
assert result["type"] == "create_entry"
assert result["data"] == {
config_flow.MP_DOMAIN: {
config_flow.CONF_USE_EPISODE_ART: True,
config_flow.CONF_SHOW_ALL_CONTROLS: True,
}
}