From a89a8cb74c359f4a3ecec0f6c997fff467de5216 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Fri, 6 Sep 2019 23:25:59 -0500 Subject: [PATCH 01/19] Move config and connections to component --- .../components/discovery/__init__.py | 3 +- homeassistant/components/plex/__init__.py | 202 +++++++++++++++++- homeassistant/components/plex/const.py | 3 + homeassistant/components/plex/media_player.py | 177 ++------------- homeassistant/components/plex/sensor.py | 59 +---- 5 files changed, 227 insertions(+), 217 deletions(-) diff --git a/homeassistant/components/discovery/__init__.py b/homeassistant/components/discovery/__init__.py index 5f1fd335d45d89..827e05a424be85 100644 --- a/homeassistant/components/discovery/__init__.py +++ b/homeassistant/components/discovery/__init__.py @@ -36,6 +36,7 @@ SERVICE_MOBILE_APP = "hass_mobile_app" SERVICE_NETGEAR = "netgear_router" SERVICE_OCTOPRINT = "octoprint" +SERVICE_PLEX = "plex_mediaserver" SERVICE_ROKU = "roku" SERVICE_SABNZBD = "sabnzbd" SERVICE_SAMSUNG_PRINTER = "samsung_printer" @@ -68,7 +69,7 @@ SERVICE_FREEBOX: ("freebox", None), SERVICE_YEELIGHT: ("yeelight", None), "panasonic_viera": ("media_player", "panasonic_viera"), - "plex_mediaserver": ("media_player", "plex"), + SERVICE_PLEX: ("plex", None), "yamaha": ("media_player", "yamaha"), "logitech_mediaserver": ("media_player", "squeezebox"), "directv": ("media_player", "directv"), diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 6e4e02026abff8..4a5d01e5ec8ede 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -1 +1,201 @@ -"""The plex component.""" +"""Support to embed Plex.""" +import logging +import plexapi.exceptions +import voluptuous as vol + +from homeassistant.components.discovery import SERVICE_PLEX +from homeassistant.components.media_player import DOMAIN as MP_DOMAIN +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.const import ( + CONF_HOST, + CONF_PORT, + CONF_SSL, + CONF_TOKEN, + CONF_URL, + 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_USE_EPISODE_ART, + CONF_SHOW_ALL_CONTROLS, + CONF_REMOVE_UNAVAILABLE_CLIENTS, + CONF_CLIENT_REMOVE_INTERVAL, + DEFAULT_HOST, + DEFAULT_PORT, + DEFAULT_SSL, + DEFAULT_VERIFY_SSL, + DOMAIN as PLEX_DOMAIN, + PLEX_CONFIG_FILE, + PLEX_MEDIA_PLAYER_OPTIONS, + SHARED_SERVER, +) +from .server import PlexServer + +MEDIA_PLAYER_SCHEMA = vol.Schema( + { + vol.Optional(CONF_USE_EPISODE_ART, default=False): cv.boolean, + vol.Optional(CONF_SHOW_ALL_CONTROLS, default=False): cv.boolean, + vol.Optional(CONF_REMOVE_UNAVAILABLE_CLIENTS, default=True): cv.boolean, + vol.Optional(CONF_CLIENT_REMOVE_INTERVAL, default=600): cv.positive_int, + } +) + +SERVER_CONFIG_SCHEMA = vol.Schema( + { + vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, + vol.Optional(CONF_TOKEN): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, + vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, + vol.Optional(MP_DOMAIN, default={}): MEDIA_PLAYER_SCHEMA, + } +) + +CONFIG_SCHEMA = vol.Schema({PLEX_DOMAIN: SERVER_CONFIG_SCHEMA}, extra=vol.ALLOW_EXTRA) + +_CONFIGURING = {} +_LOGGER = logging.getLogger(__package__) + + +def setup(hass, config): + """Set up the Plex component.""" + + def server_discovered(service, info): + """Pass back discovered Plex server details.""" + _LOGGER.info("Discovered Plex server: %s:%s", info["host"], info["port"]) + info["discovered_plex"] = True + setup(hass, info) + + def connect_plex_server(plex_server): + """Create shared PlexServer instance.""" + try: + plex_server.connect() + except ( + plexapi.exceptions.BadRequest, + plexapi.exceptions.Unauthorized, + plexapi.exceptions.NotFound, + ) as error: + _LOGGER.info(error) + return False + else: + server_id = plex_server.machine_identifier + hass.data[PLEX_DOMAIN][server_id] = plex_server + hass.data[PLEX_DOMAIN][SHARED_SERVER] = server_id + + host_and_port = plex_server.url_in_use.split("/")[-1] + if host_and_port in _CONFIGURING: + request_id = _CONFIGURING.pop(host_and_port) + configurator = hass.components.configurator + configurator.request_done(request_id) + _LOGGER.info("Discovery configuration done") + + return True + + if PLEX_DOMAIN not in hass.data: + hass.data[PLEX_DOMAIN] = {} + + if SHARED_SERVER in hass.data[PLEX_DOMAIN]: + _LOGGER.debug("Plex server already configured") + return + + # Prefer configuration + plex_config = config.get(PLEX_DOMAIN, {}) + # Otherwise use plex.conf + file_config = load_json(hass.config.path(PLEX_CONFIG_FILE)) + # Fallback to discovery/configurator + discovered = config.pop("discovered_plex", False) + if not plex_config and discovered: + plex_config = config + + if plex_config: + hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = plex_config.get(MP_DOMAIN) + host = plex_config[CONF_HOST] + port = plex_config[CONF_PORT] + token = plex_config.get(CONF_TOKEN) + has_ssl = plex_config.get(CONF_SSL) + verify_ssl = plex_config.get(CONF_VERIFY_SSL) + elif file_config: + host_and_port, host_config = file_config.popitem() + host, port = host_and_port.split(":") + token = host_config[CONF_TOKEN] + has_ssl = host_config[CONF_SSL] + verify_ssl = host_config["verify"] + else: + discovery.listen(hass, SERVICE_PLEX, server_discovered) + return True + + http_prefix = "https" if has_ssl else "http" + url = f"{http_prefix}://{host}:{port}" + + server_config = {CONF_URL: url, CONF_TOKEN: token, CONF_VERIFY_SSL: verify_ssl} + + if not hass.data.get(PLEX_MEDIA_PLAYER_OPTIONS): + hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = { + CONF_USE_EPISODE_ART: False, + CONF_SHOW_ALL_CONTROLS: False, + CONF_REMOVE_UNAVAILABLE_CLIENTS: True, + CONF_CLIENT_REMOVE_INTERVAL: 600, + } + + plex_server = PlexServer(server_config) + if connect_plex_server(plex_server): + if discovered: + # Write plex.conf if created via discovery/configurator + save_json( + hass.config.path(PLEX_CONFIG_FILE), + { + f"{host}:{port}": { + "token": token, + "ssl": has_ssl, + "verify": verify_ssl, + } + }, + ) + + platforms = [MP_DOMAIN, SENSOR_DOMAIN] + for platform in platforms: + hass.helpers.discovery.load_platform(platform, PLEX_DOMAIN, {}, config) + else: + request_configuration(hass, f"{host}:{port}") + + return True + + +def request_configuration(hass, host_and_port): + """Request configuration steps from the user.""" + configurator = hass.components.configurator + if host_and_port in _CONFIGURING: + configurator.notify_errors( + _CONFIGURING[host_and_port], "Failed to register, please try again." + ) + + return + + def plex_configuration_callback(data): + """Handle configuration changes.""" + host, port = host_and_port.split(":") + callback_config = { + CONF_HOST: host, + CONF_PORT: port, + CONF_TOKEN: data.get("token"), + CONF_SSL: cv.boolean(data.get("has_ssl")), + CONF_VERIFY_SSL: cv.boolean(data.get("verify_ssl")), + "discovered_plex": True, + } + setup(hass, callback_config) + + _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": "has_ssl", "name": "Use SSL", "type": ""}, + {"id": "verify_ssl", "name": "Verify SSL", "type": ""}, + ], + ) diff --git a/homeassistant/components/plex/const.py b/homeassistant/components/plex/const.py index 4495b9a8c838ba..1bb5ca680d678c 100644 --- a/homeassistant/components/plex/const.py +++ b/homeassistant/components/plex/const.py @@ -8,8 +8,11 @@ DEFAULT_VERIFY_SSL = True PLEX_CONFIG_FILE = "plex.conf" +PLEX_MEDIA_PLAYER_OPTIONS = "plex_mp_options" PLEX_SERVER_CONFIG = "server_config" +SHARED_SERVER = "shared_plex_server" + CONF_USE_EPISODE_ART = "use_episode_art" CONF_SHOW_ALL_CONTROLS = "show_all_controls" CONF_REMOVE_UNAVAILABLE_CLIENTS = "remove_unavailable_clients" diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index 6005321310d79a..0045f7457849cd 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -2,10 +2,10 @@ from datetime import timedelta import json import logging +import plexapi.exceptions import requests.exceptions -import voluptuous as vol -from homeassistant.components.media_player import MediaPlayerDevice, PLATFORM_SCHEMA +from homeassistant.components.media_player import MediaPlayerDevice from homeassistant.components.media_player.const import ( MEDIA_TYPE_MOVIE, MEDIA_TYPE_MUSIC, @@ -20,150 +20,37 @@ SUPPORT_VOLUME_SET, ) from homeassistant.const import ( - CONF_HOST, - CONF_PORT, - CONF_SSL, - CONF_URL, - CONF_TOKEN, - CONF_VERIFY_SSL, DEVICE_DEFAULT_NAME, STATE_IDLE, STATE_OFF, STATE_PAUSED, STATE_PLAYING, ) -from homeassistant.helpers import config_validation as cv from homeassistant.helpers.event import track_time_interval from homeassistant.util import dt as dt_util -from homeassistant.util.json import load_json, save_json from .const import ( CONF_USE_EPISODE_ART, CONF_SHOW_ALL_CONTROLS, CONF_REMOVE_UNAVAILABLE_CLIENTS, CONF_CLIENT_REMOVE_INTERVAL, - DEFAULT_HOST, - DEFAULT_PORT, - DEFAULT_SSL, - DEFAULT_VERIFY_SSL, DOMAIN as PLEX_DOMAIN, NAME_FORMAT, - PLEX_CONFIG_FILE, + PLEX_MEDIA_PLAYER_OPTIONS, + SHARED_SERVER, ) -from .server import PlexServer SERVER_SETUP = "server_setup" _CONFIGURING = {} _LOGGER = logging.getLogger(__name__) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, - vol.Optional(CONF_TOKEN): cv.string, - vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, - vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, - vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, - vol.Optional(CONF_USE_EPISODE_ART, default=False): cv.boolean, - vol.Optional(CONF_SHOW_ALL_CONTROLS, default=False): cv.boolean, - vol.Optional(CONF_REMOVE_UNAVAILABLE_CLIENTS, default=True): cv.boolean, - vol.Optional( - CONF_CLIENT_REMOVE_INTERVAL, default=timedelta(seconds=600) - ): vol.All(cv.time_period, cv.positive_timedelta), - } -) - def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up the Plex platform.""" - plex_data = hass.data.setdefault(PLEX_DOMAIN, {}) - server_setup = plex_data.setdefault(SERVER_SETUP, False) - if server_setup: - return - - # get config from plex.conf - file_config = load_json(hass.config.path(PLEX_CONFIG_FILE)) - - if file_config: - # Setup a configured PlexServer - host, host_config = file_config.popitem() - token = host_config["token"] - try: - has_ssl = host_config["ssl"] - except KeyError: - has_ssl = False - try: - verify_ssl = host_config["verify"] - except KeyError: - verify_ssl = True - - # Via discovery - elif discovery_info is not None: - # Parse discovery data - host = discovery_info.get("host") - port = discovery_info.get("port") - host = f"{host}:{port}" - _LOGGER.info("Discovered PLEX server: %s", host) - - if host in _CONFIGURING: - return - token = None - has_ssl = False - verify_ssl = True - else: - host = config[CONF_HOST] - port = config[CONF_PORT] - host = f"{host}:{port}" - token = config.get(CONF_TOKEN) - has_ssl = config[CONF_SSL] - verify_ssl = config[CONF_VERIFY_SSL] - - setup_plexserver( - host, token, has_ssl, verify_ssl, hass, config, add_entities_callback - ) - - -def setup_plexserver( - host, token, has_ssl, verify_ssl, hass, config, add_entities_callback -): - """Set up a plexserver based on host parameter.""" - import plexapi.exceptions - - http_prefix = "https" if has_ssl else "http" - - server_config = { - CONF_URL: f"{http_prefix}://{host}", - CONF_TOKEN: token, - CONF_VERIFY_SSL: verify_ssl, - } - - try: - plexserver = PlexServer(server_config) - plexserver.connect() - except ( - plexapi.exceptions.BadRequest, - plexapi.exceptions.Unauthorized, - plexapi.exceptions.NotFound, - ) as error: - _LOGGER.info(error) - # No token or wrong token - request_configuration(host, hass, config, add_entities_callback) - return - else: - hass.data[PLEX_DOMAIN][SERVER_SETUP] = True - - # If we came here and configuring this host, mark as done - if host in _CONFIGURING: - request_id = _CONFIGURING.pop(host) - configurator = hass.components.configurator - configurator.request_done(request_id) - _LOGGER.info("Discovery configuration done") - - # Save config - save_json( - hass.config.path(PLEX_CONFIG_FILE), - {host: {"token": token, "ssl": has_ssl, "verify": verify_ssl}}, - ) + server_id = hass.data[PLEX_DOMAIN][SHARED_SERVER] + plexserver = hass.data[PLEX_DOMAIN][server_id] + config = hass.data[PLEX_MEDIA_PLAYER_OPTIONS] plex_clients = {} plex_sessions = {} @@ -178,7 +65,9 @@ def update_devices(): return except requests.exceptions.RequestException as ex: _LOGGER.warning( - "Could not connect to plex server at http://%s (%s)", host, ex + "Could not connect to Plex server: %s (%s)", + plexserver.friendly_name, + ex, ) return @@ -210,7 +99,9 @@ def update_devices(): return except requests.exceptions.RequestException as ex: _LOGGER.warning( - "Could not connect to plex server at http://%s (%s)", host, ex + "Could not connect to Plex server: %s (%s)", + plexserver.friendly_name, + ex, ) return @@ -257,7 +148,7 @@ def update_devices(): continue if (dt_util.utcnow() - client.marked_unavailable) >= ( - config.get(CONF_CLIENT_REMOVE_INTERVAL) + timedelta(seconds=config[CONF_CLIENT_REMOVE_INTERVAL]) ): hass.add_job(client.async_remove()) clients_to_remove.append(client.machine_identifier) @@ -269,43 +160,6 @@ def update_devices(): add_entities_callback(new_plex_clients) -def request_configuration(host, hass, config, add_entities_callback): - """Request configuration steps from the user.""" - configurator = hass.components.configurator - # We got an error if this method is called while we are configuring - if host in _CONFIGURING: - configurator.notify_errors( - _CONFIGURING[host], "Failed to register, please try again." - ) - - return - - def plex_configuration_callback(data): - """Handle configuration changes.""" - setup_plexserver( - host, - data.get("token"), - cv.boolean(data.get("has_ssl")), - cv.boolean(data.get("do_not_verify_ssl")), - hass, - config, - add_entities_callback, - ) - - _CONFIGURING[host] = 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": "has_ssl", "name": "Use SSL", "type": ""}, - {"id": "do_not_verify_ssl", "name": "Do not verify SSL", "type": ""}, - ], - ) - - class PlexClient(MediaPlayerDevice): """Representation of a Plex device.""" @@ -378,9 +232,6 @@ def _clear_media_details(self): def refresh(self, device, session): """Refresh key device data.""" - import plexapi.exceptions - - # new data refresh self._clear_media_details() if session: # Not being triggered by Chrome or FireTablet Plex App diff --git a/homeassistant/components/plex/sensor.py b/homeassistant/components/plex/sensor.py index bece6274af69dc..c16f1a100da611 100644 --- a/homeassistant/components/plex/sensor.py +++ b/homeassistant/components/plex/sensor.py @@ -3,76 +3,31 @@ import logging import plexapi.exceptions import requests.exceptions -import voluptuous as vol - -from homeassistant.components.switch import PLATFORM_SCHEMA -from homeassistant.const import ( - CONF_NAME, - CONF_HOST, - CONF_PORT, - CONF_TOKEN, - CONF_SSL, - CONF_URL, - CONF_VERIFY_SSL, -) + from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -import homeassistant.helpers.config_validation as cv -from .const import DEFAULT_HOST, DEFAULT_PORT, DEFAULT_SSL, DEFAULT_VERIFY_SSL -from .server import PlexServer +from .const import DOMAIN as PLEX_DOMAIN, SHARED_SERVER DEFAULT_NAME = "Plex" _LOGGER = logging.getLogger(__name__) MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1) -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( - { - vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, - vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, - vol.Optional(CONF_TOKEN): cv.string, - vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, - vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean, - vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, - } -) - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Plex sensor.""" - name = config.get(CONF_NAME) - plex_host = config.get(CONF_HOST) - plex_port = config.get(CONF_PORT) - plex_token = config.get(CONF_TOKEN) - verify_ssl = config.get(CONF_VERIFY_SSL) - - plex_url = "{}://{}:{}".format( - "https" if config.get(CONF_SSL) else "http", plex_host, plex_port - ) - - try: - plex_server = PlexServer( - {CONF_URL: plex_url, CONF_TOKEN: plex_token, CONF_VERIFY_SSL: verify_ssl} - ) - plex_server.connect() - except ( - plexapi.exceptions.BadRequest, - plexapi.exceptions.Unauthorized, - plexapi.exceptions.NotFound, - ) as error: - _LOGGER.error(error) - return - - add_entities([PlexSensor(name, plex_server)], True) + server_id = hass.data[PLEX_DOMAIN][SHARED_SERVER] + plexserver = hass.data[PLEX_DOMAIN][server_id] + add_entities([PlexSensor(plexserver)], True) class PlexSensor(Entity): """Representation of a Plex now playing sensor.""" - def __init__(self, name, plex_server): + def __init__(self, plex_server): """Initialize the sensor.""" - self._name = name + self._name = DEFAULT_NAME self._state = None self._now_playing = [] self._server = plex_server From 9033c0a2c88c4b1bd542702fa99639f23547ed25 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Sat, 7 Sep 2019 11:59:47 -0500 Subject: [PATCH 02/19] Separate imports --- homeassistant/components/plex/__init__.py | 1 + homeassistant/components/plex/media_player.py | 1 + homeassistant/components/plex/sensor.py | 1 + homeassistant/components/plex/server.py | 1 + 4 files changed, 4 insertions(+) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 4a5d01e5ec8ede..b5a70d6ff68c1a 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -1,5 +1,6 @@ """Support to embed Plex.""" import logging + import plexapi.exceptions import voluptuous as vol diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index 0045f7457849cd..8ab80bfa5aa283 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -2,6 +2,7 @@ from datetime import timedelta import json import logging + import plexapi.exceptions import requests.exceptions diff --git a/homeassistant/components/plex/sensor.py b/homeassistant/components/plex/sensor.py index c16f1a100da611..5e2f2a36943768 100644 --- a/homeassistant/components/plex/sensor.py +++ b/homeassistant/components/plex/sensor.py @@ -1,6 +1,7 @@ """Support for Plex media server monitoring.""" from datetime import timedelta import logging + import plexapi.exceptions import requests.exceptions diff --git a/homeassistant/components/plex/server.py b/homeassistant/components/plex/server.py index 6647b81714f3d5..c778588752a3ad 100644 --- a/homeassistant/components/plex/server.py +++ b/homeassistant/components/plex/server.py @@ -1,5 +1,6 @@ """Shared class to maintain Plex server instances.""" import logging + import plexapi.server from requests import Session From d73264d1aea1fb420dda9086e308447c33c43e25 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Sun, 8 Sep 2019 09:25:30 -0500 Subject: [PATCH 03/19] Set a unique_id on sensor --- homeassistant/components/plex/sensor.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/homeassistant/components/plex/sensor.py b/homeassistant/components/plex/sensor.py index 5e2f2a36943768..93145d4692a5bc 100644 --- a/homeassistant/components/plex/sensor.py +++ b/homeassistant/components/plex/sensor.py @@ -32,12 +32,18 @@ def __init__(self, plex_server): self._state = None self._now_playing = [] self._server = plex_server + self._unique_id = f"sensor-{plex_server.machine_identifier}" @property def name(self): """Return the name of the sensor.""" return self._name + @property + def unique_id(self): + """Return the id of this plex client.""" + return self._unique_id + @property def state(self): """Return the state of the sensor.""" From c50fa62d486442dc76e7ff757f290899d058d64a Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Sun, 8 Sep 2019 09:29:12 -0500 Subject: [PATCH 04/19] Set a platforms const --- homeassistant/components/plex/__init__.py | 5 ++--- homeassistant/components/plex/const.py | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index b5a70d6ff68c1a..765f52ad5e5116 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -6,7 +6,6 @@ from homeassistant.components.discovery import SERVICE_PLEX from homeassistant.components.media_player import DOMAIN as MP_DOMAIN -from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.const import ( CONF_HOST, CONF_PORT, @@ -29,6 +28,7 @@ DEFAULT_SSL, DEFAULT_VERIFY_SSL, DOMAIN as PLEX_DOMAIN, + PLATFORMS, PLEX_CONFIG_FILE, PLEX_MEDIA_PLAYER_OPTIONS, SHARED_SERVER, @@ -156,8 +156,7 @@ def connect_plex_server(plex_server): }, ) - platforms = [MP_DOMAIN, SENSOR_DOMAIN] - for platform in platforms: + for platform in PLATFORMS: hass.helpers.discovery.load_platform(platform, PLEX_DOMAIN, {}, config) else: request_configuration(hass, f"{host}:{port}") diff --git a/homeassistant/components/plex/const.py b/homeassistant/components/plex/const.py index 1bb5ca680d678c..80286bb8ed6016 100644 --- a/homeassistant/components/plex/const.py +++ b/homeassistant/components/plex/const.py @@ -7,6 +7,8 @@ DEFAULT_SSL = False DEFAULT_VERIFY_SSL = True +PLATFORMS = ["media_player", "sensor"] + PLEX_CONFIG_FILE = "plex.conf" PLEX_MEDIA_PLAYER_OPTIONS = "plex_mp_options" PLEX_SERVER_CONFIG = "server_config" From 013642949f9ffb31de60ac71399df2bed6883673 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Sun, 8 Sep 2019 09:58:37 -0500 Subject: [PATCH 05/19] Add SERVERS dict, hardcode to single server --- homeassistant/components/plex/__init__.py | 13 ++++++------- homeassistant/components/plex/const.py | 3 +-- homeassistant/components/plex/media_player.py | 5 ++--- homeassistant/components/plex/sensor.py | 5 ++--- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 765f52ad5e5116..0a92cd6c5dbd6d 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -31,7 +31,7 @@ PLATFORMS, PLEX_CONFIG_FILE, PLEX_MEDIA_PLAYER_OPTIONS, - SHARED_SERVER, + SERVERS, ) from .server import PlexServer @@ -82,9 +82,9 @@ def connect_plex_server(plex_server): _LOGGER.info(error) return False else: - server_id = plex_server.machine_identifier - hass.data[PLEX_DOMAIN][server_id] = plex_server - hass.data[PLEX_DOMAIN][SHARED_SERVER] = server_id + hass.data[PLEX_DOMAIN][SERVERS][ + plex_server.machine_identifier + ] = plex_server host_and_port = plex_server.url_in_use.split("/")[-1] if host_and_port in _CONFIGURING: @@ -95,10 +95,9 @@ def connect_plex_server(plex_server): return True - if PLEX_DOMAIN not in hass.data: - hass.data[PLEX_DOMAIN] = {} + hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}}) - if SHARED_SERVER in hass.data[PLEX_DOMAIN]: + if hass.data[PLEX_DOMAIN][SERVERS]: _LOGGER.debug("Plex server already configured") return diff --git a/homeassistant/components/plex/const.py b/homeassistant/components/plex/const.py index 80286bb8ed6016..bf8c2387e4de3a 100644 --- a/homeassistant/components/plex/const.py +++ b/homeassistant/components/plex/const.py @@ -8,13 +8,12 @@ DEFAULT_VERIFY_SSL = True PLATFORMS = ["media_player", "sensor"] +SERVERS = "servers" PLEX_CONFIG_FILE = "plex.conf" PLEX_MEDIA_PLAYER_OPTIONS = "plex_mp_options" PLEX_SERVER_CONFIG = "server_config" -SHARED_SERVER = "shared_plex_server" - CONF_USE_EPISODE_ART = "use_episode_art" CONF_SHOW_ALL_CONTROLS = "show_all_controls" CONF_REMOVE_UNAVAILABLE_CLIENTS = "remove_unavailable_clients" diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index 8ab80bfa5aa283..9dc28fb8f8d251 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -38,7 +38,7 @@ DOMAIN as PLEX_DOMAIN, NAME_FORMAT, PLEX_MEDIA_PLAYER_OPTIONS, - SHARED_SERVER, + SERVERS, ) SERVER_SETUP = "server_setup" @@ -49,8 +49,7 @@ def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up the Plex platform.""" - server_id = hass.data[PLEX_DOMAIN][SHARED_SERVER] - plexserver = hass.data[PLEX_DOMAIN][server_id] + plexserver = hass.data[PLEX_DOMAIN][SERVERS].values()[0] config = hass.data[PLEX_MEDIA_PLAYER_OPTIONS] plex_clients = {} diff --git a/homeassistant/components/plex/sensor.py b/homeassistant/components/plex/sensor.py index 93145d4692a5bc..a404cf517e35d0 100644 --- a/homeassistant/components/plex/sensor.py +++ b/homeassistant/components/plex/sensor.py @@ -8,7 +8,7 @@ from homeassistant.helpers.entity import Entity from homeassistant.util import Throttle -from .const import DOMAIN as PLEX_DOMAIN, SHARED_SERVER +from .const import DOMAIN as PLEX_DOMAIN, SERVERS DEFAULT_NAME = "Plex" _LOGGER = logging.getLogger(__name__) @@ -18,8 +18,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Plex sensor.""" - server_id = hass.data[PLEX_DOMAIN][SHARED_SERVER] - plexserver = hass.data[PLEX_DOMAIN][server_id] + plexserver = hass.data[PLEX_DOMAIN][SERVERS].values()[0] add_entities([PlexSensor(plexserver)], True) From cae33d7a3b714b07ac4e6815d68e71f40d1790c6 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Sun, 8 Sep 2019 10:01:26 -0500 Subject: [PATCH 06/19] Move to debug --- homeassistant/components/plex/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 0a92cd6c5dbd6d..92986f23517313 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -91,7 +91,7 @@ def connect_plex_server(plex_server): request_id = _CONFIGURING.pop(host_and_port) configurator = hass.components.configurator configurator.request_done(request_id) - _LOGGER.info("Discovery configuration done") + _LOGGER.debug("Discovery configuration done") return True From 79e74922d3f75ec0d35c4fe36c5e01e2f8c8a0b5 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Sun, 8 Sep 2019 10:02:00 -0500 Subject: [PATCH 07/19] Return false --- homeassistant/components/plex/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 92986f23517313..21a3bb6a22da90 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -99,7 +99,7 @@ def connect_plex_server(plex_server): if hass.data[PLEX_DOMAIN][SERVERS]: _LOGGER.debug("Plex server already configured") - return + return False # Prefer configuration plex_config = config.get(PLEX_DOMAIN, {}) From f56beecab1f5289e779c7cb255d79c7f63532931 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Sun, 8 Sep 2019 10:19:50 -0500 Subject: [PATCH 08/19] More debug --- homeassistant/components/plex/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 21a3bb6a22da90..6a58cf62747750 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -66,7 +66,7 @@ def setup(hass, config): def server_discovered(service, info): """Pass back discovered Plex server details.""" - _LOGGER.info("Discovered Plex server: %s:%s", info["host"], info["port"]) + _LOGGER.debug("Discovered Plex server: %s:%s", info["host"], info["port"]) info["discovered_plex"] = True setup(hass, info) From f4e4cf5f0b3900468b6a09863c91ae3c44de612d Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Sun, 8 Sep 2019 10:47:27 -0500 Subject: [PATCH 09/19] Import at top to fix lint --- homeassistant/components/plex/media_player.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index 9dc28fb8f8d251..e23f7dcb669c55 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -4,6 +4,8 @@ import logging import plexapi.exceptions +import plexapi.playlist +import plexapi.playqueue import requests.exceptions from homeassistant.components.media_player import MediaPlayerDevice @@ -702,8 +704,6 @@ def play_media(self, media_type, media_id, **kwargs): src["video_name"] ) - import plexapi.playlist - if ( media and media_type == "EPISODE" @@ -769,8 +769,6 @@ def _client_play_media(self, media, delete=False, **params): _LOGGER.error("Client cannot play media: %s", self.entity_id) return - import plexapi.playqueue - playqueue = plexapi.playqueue.PlayQueue.create( self.device.server, media, **params ) From 630a67e543d037eb5572dc109714910f948b9d5b Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Sun, 8 Sep 2019 10:49:47 -0500 Subject: [PATCH 10/19] Guard against legacy setup attempts --- homeassistant/components/plex/media_player.py | 3 +++ homeassistant/components/plex/sensor.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index e23f7dcb669c55..5282237d55af3a 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -51,6 +51,9 @@ def setup_platform(hass, config, add_entities_callback, discovery_info=None): """Set up the Plex platform.""" + if discovery_info is None: + return + plexserver = hass.data[PLEX_DOMAIN][SERVERS].values()[0] config = hass.data[PLEX_MEDIA_PLAYER_OPTIONS] diff --git a/homeassistant/components/plex/sensor.py b/homeassistant/components/plex/sensor.py index a404cf517e35d0..a495fc3643ff22 100644 --- a/homeassistant/components/plex/sensor.py +++ b/homeassistant/components/plex/sensor.py @@ -18,6 +18,9 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Plex sensor.""" + if discovery_info is None: + return + plexserver = hass.data[PLEX_DOMAIN][SERVERS].values()[0] add_entities([PlexSensor(plexserver)], True) From 288940b848109e416817e42c920c3635e66e45bc Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Mon, 9 Sep 2019 10:37:20 -0500 Subject: [PATCH 11/19] Refactor to add setup callback --- homeassistant/components/plex/__init__.py | 191 +++++++++--------- homeassistant/components/plex/media_player.py | 2 +- homeassistant/components/plex/sensor.py | 2 +- 3 files changed, 95 insertions(+), 100 deletions(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 6a58cf62747750..5479a025825164 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -67,11 +67,36 @@ def setup(hass, config): def server_discovered(service, info): """Pass back discovered Plex server details.""" _LOGGER.debug("Discovered Plex server: %s:%s", info["host"], info["port"]) - info["discovered_plex"] = True - setup(hass, info) + 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) + + if config: + server_config = config.get(PLEX_DOMAIN, {}) + host_and_port = f"{server_config[CONF_HOST]}:{server_config[CONF_PORT]}" + if MP_DOMAIN in server_config: + hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = server_config.pop(MP_DOMAIN) + elif file_config: + _LOGGER.info("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: + return False + + 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}" - def connect_plex_server(plex_server): - """Create shared PlexServer instance.""" + plex_server = PlexServer(server_config) try: plex_server.connect() except ( @@ -80,20 +105,80 @@ def connect_plex_server(plex_server): plexapi.exceptions.NotFound, ) as error: _LOGGER.info(error) + request_configuration(host_and_port) return False else: hass.data[PLEX_DOMAIN][SERVERS][ plex_server.machine_identifier ] = plex_server - host_and_port = plex_server.url_in_use.split("/")[-1] if host_and_port in _CONFIGURING: request_id = _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] = { + CONF_USE_EPISODE_ART: False, + CONF_SHOW_ALL_CONTROLS: False, + CONF_REMOVE_UNAVAILABLE_CLIENTS: True, + CONF_CLIENT_REMOVE_INTERVAL: 600, + } + + for platform in PLATFORMS: + hass.helpers.discovery.load_platform( + platform, PLEX_DOMAIN, {}, original_config + ) + + def request_configuration(host_and_port): + """Request configuration steps from the user.""" + configurator = hass.components.configurator + if host_and_port in _CONFIGURING: + configurator.notify_errors( + _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) + + _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": ""}, + ], + ) + + # End of inner functions. - return True + if config: + original_config = config hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}}) @@ -101,100 +186,10 @@ def connect_plex_server(plex_server): _LOGGER.debug("Plex server already configured") return False - # Prefer configuration - plex_config = config.get(PLEX_DOMAIN, {}) - # Otherwise use plex.conf - file_config = load_json(hass.config.path(PLEX_CONFIG_FILE)) - # Fallback to discovery/configurator - discovered = config.pop("discovered_plex", False) - if not plex_config and discovered: - plex_config = config - - if plex_config: - hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = plex_config.get(MP_DOMAIN) - host = plex_config[CONF_HOST] - port = plex_config[CONF_PORT] - token = plex_config.get(CONF_TOKEN) - has_ssl = plex_config.get(CONF_SSL) - verify_ssl = plex_config.get(CONF_VERIFY_SSL) - elif file_config: - host_and_port, host_config = file_config.popitem() - host, port = host_and_port.split(":") - token = host_config[CONF_TOKEN] - has_ssl = host_config[CONF_SSL] - verify_ssl = host_config["verify"] - else: + if PLEX_DOMAIN not in config: discovery.listen(hass, SERVICE_PLEX, server_discovered) return True - http_prefix = "https" if has_ssl else "http" - url = f"{http_prefix}://{host}:{port}" - - server_config = {CONF_URL: url, CONF_TOKEN: token, CONF_VERIFY_SSL: verify_ssl} - - if not hass.data.get(PLEX_MEDIA_PLAYER_OPTIONS): - hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = { - CONF_USE_EPISODE_ART: False, - CONF_SHOW_ALL_CONTROLS: False, - CONF_REMOVE_UNAVAILABLE_CLIENTS: True, - CONF_CLIENT_REMOVE_INTERVAL: 600, - } - - plex_server = PlexServer(server_config) - if connect_plex_server(plex_server): - if discovered: - # Write plex.conf if created via discovery/configurator - save_json( - hass.config.path(PLEX_CONFIG_FILE), - { - f"{host}:{port}": { - "token": token, - "ssl": has_ssl, - "verify": verify_ssl, - } - }, - ) - - for platform in PLATFORMS: - hass.helpers.discovery.load_platform(platform, PLEX_DOMAIN, {}, config) - else: - request_configuration(hass, f"{host}:{port}") + setup_plex(config=config) return True - - -def request_configuration(hass, host_and_port): - """Request configuration steps from the user.""" - configurator = hass.components.configurator - if host_and_port in _CONFIGURING: - configurator.notify_errors( - _CONFIGURING[host_and_port], "Failed to register, please try again." - ) - - return - - def plex_configuration_callback(data): - """Handle configuration changes.""" - host, port = host_and_port.split(":") - callback_config = { - CONF_HOST: host, - CONF_PORT: port, - CONF_TOKEN: data.get("token"), - CONF_SSL: cv.boolean(data.get("has_ssl")), - CONF_VERIFY_SSL: cv.boolean(data.get("verify_ssl")), - "discovered_plex": True, - } - setup(hass, callback_config) - - _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": "has_ssl", "name": "Use SSL", "type": ""}, - {"id": "verify_ssl", "name": "Verify SSL", "type": ""}, - ], - ) diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index 5282237d55af3a..bfbd049c832132 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -54,7 +54,7 @@ def setup_platform(hass, config, add_entities_callback, discovery_info=None): if discovery_info is None: return - plexserver = hass.data[PLEX_DOMAIN][SERVERS].values()[0] + plexserver = list(hass.data[PLEX_DOMAIN][SERVERS].values())[0] config = hass.data[PLEX_MEDIA_PLAYER_OPTIONS] plex_clients = {} diff --git a/homeassistant/components/plex/sensor.py b/homeassistant/components/plex/sensor.py index a495fc3643ff22..f469e95da808e0 100644 --- a/homeassistant/components/plex/sensor.py +++ b/homeassistant/components/plex/sensor.py @@ -21,7 +21,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): if discovery_info is None: return - plexserver = hass.data[PLEX_DOMAIN][SERVERS].values()[0] + plexserver = list(hass.data[PLEX_DOMAIN][SERVERS].values())[0] add_entities([PlexSensor(plexserver)], True) From 7ca959201c2b084227ebfeff6372bd51bc314b2a Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Mon, 9 Sep 2019 12:04:24 -0500 Subject: [PATCH 12/19] Review comments --- homeassistant/components/plex/__init__.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 5479a025825164..b12ee066cef45f 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -66,6 +66,9 @@ def setup(hass, config): 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) @@ -75,8 +78,10 @@ def setup_plex(config=None, discovery_info=None, configurator_info=None): file_config = load_json(json_file) if config: - server_config = config.get(PLEX_DOMAIN, {}) - host_and_port = f"{server_config[CONF_HOST]}:{server_config[CONF_PORT]}" + server_config = 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: @@ -177,8 +182,7 @@ def plex_configuration_callback(data): # End of inner functions. - if config: - original_config = config + original_config = config hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}}) @@ -186,10 +190,9 @@ def plex_configuration_callback(data): _LOGGER.debug("Plex server already configured") return False - if PLEX_DOMAIN not in config: - discovery.listen(hass, SERVICE_PLEX, server_discovered) - return True + discovery.listen(hass, SERVICE_PLEX, server_discovered) - setup_plex(config=config) + plex_config = config.get(PLEX_DOMAIN, {}) + setup_plex(config=plex_config) return True From c26fc579caeeac668e47fd9755ff3fe9f17abf4a Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Mon, 9 Sep 2019 12:07:25 -0500 Subject: [PATCH 13/19] Log levels --- homeassistant/components/plex/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index b12ee066cef45f..91c6bf28d12e99 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -85,7 +85,7 @@ def setup_plex(config=None, discovery_info=None, configurator_info=None): if MP_DOMAIN in server_config: hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = server_config.pop(MP_DOMAIN) elif file_config: - _LOGGER.info("Loading config from %s", json_file) + _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: @@ -109,7 +109,7 @@ def setup_plex(config=None, discovery_info=None, configurator_info=None): plexapi.exceptions.Unauthorized, plexapi.exceptions.NotFound, ) as error: - _LOGGER.info(error) + _LOGGER.error(error) request_configuration(host_and_port) return False else: From 9d5dc916f0ca21e77ff79967ef3b237f2931ca9a Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Mon, 9 Sep 2019 12:10:08 -0500 Subject: [PATCH 14/19] Return result of callback --- homeassistant/components/plex/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 91c6bf28d12e99..e2da1753eed50c 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -148,6 +148,8 @@ def setup_plex(config=None, discovery_info=None, configurator_info=None): platform, PLEX_DOMAIN, {}, original_config ) + return True + def request_configuration(host_and_port): """Request configuration steps from the user.""" configurator = hass.components.configurator @@ -193,6 +195,4 @@ def plex_configuration_callback(data): discovery.listen(hass, SERVICE_PLEX, server_discovered) plex_config = config.get(PLEX_DOMAIN, {}) - setup_plex(config=plex_config) - - return True + return setup_plex(config=plex_config) From 08fd236fe2e662d3421420449c9587d2afd6940c Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Mon, 9 Sep 2019 12:23:46 -0500 Subject: [PATCH 15/19] Store CONFIGURING in hass.data --- homeassistant/components/plex/__init__.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index e2da1753eed50c..2940077afc3a5d 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -57,7 +57,7 @@ CONFIG_SCHEMA = vol.Schema({PLEX_DOMAIN: SERVER_CONFIG_SCHEMA}, extra=vol.ALLOW_EXTRA) -_CONFIGURING = {} +CONFIGURING = "configuring" _LOGGER = logging.getLogger(__package__) @@ -117,8 +117,8 @@ def setup_plex(config=None, discovery_info=None, configurator_info=None): plex_server.machine_identifier ] = plex_server - if host_and_port in _CONFIGURING: - request_id = _CONFIGURING.pop(host_and_port) + 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") @@ -153,9 +153,10 @@ def setup_plex(config=None, discovery_info=None, configurator_info=None): def request_configuration(host_and_port): """Request configuration steps from the user.""" configurator = hass.components.configurator - if host_and_port in _CONFIGURING: + if host_and_port in hass.data[PLEX_DOMAIN][CONFIGURING]: configurator.notify_errors( - _CONFIGURING[host_and_port], "Failed to register, please try again." + hass.data[PLEX_DOMAIN][CONFIGURING][host_and_port], + "Failed to register, please try again.", ) return @@ -169,7 +170,9 @@ def plex_configuration_callback(data): } setup_plex(configurator_info=config) - _CONFIGURING[host_and_port] = configurator.request_config( + hass.data[PLEX_DOMAIN][CONFIGURING][ + host_and_port + ] = configurator.request_config( "Plex Media Server", plex_configuration_callback, description="Enter the X-Plex-Token", @@ -186,7 +189,7 @@ def plex_configuration_callback(data): original_config = config - hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}}) + hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}, CONFIGURING: {}}) if hass.data[PLEX_DOMAIN][SERVERS]: _LOGGER.debug("Plex server already configured") From 37b5be348e7ea6564b0d7e8e7bbda6c4870474cc Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Mon, 9 Sep 2019 12:24:23 -0500 Subject: [PATCH 16/19] Set up discovery if no config data --- homeassistant/components/plex/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 2940077afc3a5d..e89a36ce7d2fc1 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -95,7 +95,8 @@ def setup_plex(config=None, discovery_info=None, configurator_info=None): server_config = configurator_info host_and_port = server_config["host_and_port"] else: - return False + discovery.listen(hass, SERVICE_PLEX, server_discovered) + return True use_ssl = server_config.get(CONF_SSL, DEFAULT_SSL) http_prefix = "https" if use_ssl else "http" @@ -195,7 +196,5 @@ def plex_configuration_callback(data): _LOGGER.debug("Plex server already configured") return False - discovery.listen(hass, SERVICE_PLEX, server_discovered) - plex_config = config.get(PLEX_DOMAIN, {}) return setup_plex(config=plex_config) From bab5f66405234b580f3dd4ff661281ed2d83d5db Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Mon, 9 Sep 2019 12:28:41 -0500 Subject: [PATCH 17/19] Use schema to set defaults --- homeassistant/components/plex/__init__.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index e89a36ce7d2fc1..f762515110c30c 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -137,12 +137,7 @@ def setup_plex(config=None, discovery_info=None, configurator_info=None): ) if not hass.data.get(PLEX_MEDIA_PLAYER_OPTIONS): - hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = { - CONF_USE_EPISODE_ART: False, - CONF_SHOW_ALL_CONTROLS: False, - CONF_REMOVE_UNAVAILABLE_CLIENTS: True, - CONF_CLIENT_REMOVE_INTERVAL: 600, - } + hass.data[PLEX_MEDIA_PLAYER_OPTIONS] = MEDIA_PLAYER_SCHEMA({}) for platform in PLATFORMS: hass.helpers.discovery.load_platform( From 893508851889c5f3a6345b677681daf1df1a7de9 Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Mon, 9 Sep 2019 13:11:11 -0500 Subject: [PATCH 18/19] Remove media_player options to remove entities --- homeassistant/components/plex/__init__.py | 4 ---- homeassistant/components/plex/media_player.py | 15 --------------- 2 files changed, 19 deletions(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index f762515110c30c..892b318789439d 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -21,8 +21,6 @@ from .const import ( CONF_USE_EPISODE_ART, CONF_SHOW_ALL_CONTROLS, - CONF_REMOVE_UNAVAILABLE_CLIENTS, - CONF_CLIENT_REMOVE_INTERVAL, DEFAULT_HOST, DEFAULT_PORT, DEFAULT_SSL, @@ -39,8 +37,6 @@ { vol.Optional(CONF_USE_EPISODE_ART, default=False): cv.boolean, vol.Optional(CONF_SHOW_ALL_CONTROLS, default=False): cv.boolean, - vol.Optional(CONF_REMOVE_UNAVAILABLE_CLIENTS, default=True): cv.boolean, - vol.Optional(CONF_CLIENT_REMOVE_INTERVAL, default=600): cv.positive_int, } ) diff --git a/homeassistant/components/plex/media_player.py b/homeassistant/components/plex/media_player.py index bfbd049c832132..cfc63948bee80c 100644 --- a/homeassistant/components/plex/media_player.py +++ b/homeassistant/components/plex/media_player.py @@ -35,8 +35,6 @@ from .const import ( CONF_USE_EPISODE_ART, CONF_SHOW_ALL_CONTROLS, - CONF_REMOVE_UNAVAILABLE_CLIENTS, - CONF_CLIENT_REMOVE_INTERVAL, DOMAIN as PLEX_DOMAIN, NAME_FORMAT, PLEX_MEDIA_PLAYER_OPTIONS, @@ -135,7 +133,6 @@ def update_devices(): _LOGGER.debug("Refreshing session: %s", machine_identifier) plex_clients[machine_identifier].refresh(None, session) - clients_to_remove = [] for client in plex_clients.values(): # force devices to idle that do not have a valid session if client.session is None: @@ -149,18 +146,6 @@ def update_devices(): if client not in new_plex_clients: client.schedule_update_ha_state() - if not config.get(CONF_REMOVE_UNAVAILABLE_CLIENTS) or client.available: - continue - - if (dt_util.utcnow() - client.marked_unavailable) >= ( - timedelta(seconds=config[CONF_CLIENT_REMOVE_INTERVAL]) - ): - hass.add_job(client.async_remove()) - clients_to_remove.append(client.machine_identifier) - - while clients_to_remove: - del plex_clients[clients_to_remove.pop()] - if new_plex_clients: add_entities_callback(new_plex_clients) From f13adf3faa1d47976d4d2d2b805d9273103809ca Mon Sep 17 00:00:00 2001 From: Jason Lawrence Date: Mon, 9 Sep 2019 14:20:43 -0500 Subject: [PATCH 19/19] Improve error handling --- homeassistant/components/plex/__init__.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/plex/__init__.py b/homeassistant/components/plex/__init__.py index 892b318789439d..846f3e3f53c325 100644 --- a/homeassistant/components/plex/__init__.py +++ b/homeassistant/components/plex/__init__.py @@ -2,6 +2,7 @@ import logging import plexapi.exceptions +import requests.exceptions import voluptuous as vol from homeassistant.components.discovery import SERVICE_PLEX @@ -101,12 +102,21 @@ def setup_plex(config=None, discovery_info=None, configurator_info=None): 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(error) + _LOGGER.error( + "Connection to Plex server failed, please verify token and SSL settings: [%s]", + error, + ) request_configuration(host_and_port) return False else: