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 serverpull/26930/head
parent
62cea2b7ac
commit
9b204ad162
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue