mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Standardize Plex server connections (#26444)
* Common connection class * Omit tests for new Plex files * Oops * Add missing properties * Remove redundant log message * Stopgap to avoid duplicate setups * Cleaner check for server setup Co-Authored-By: Martin Hjelmare <marhje52@kth.se> * Cleaner check for server setup Co-Authored-By: Martin Hjelmare <marhje52@kth.se> * Not needed with previous setup check * Remove username/password support * Reduce log level Co-Authored-By: Martin Hjelmare <marhje52@kth.se> * Don't do setup in __init__ * Oops * Committing too fast... * Connect after init * Catch update exceptions like media_player * Pass in validated PlexServer instance * Remove unnecessary check * Counter should be unknown on init * Remove servername config option
This commit is contained in:
parent
a000125729
commit
2cd845fb25
@ -470,8 +470,7 @@ omit =
|
||||
homeassistant/components/pioneer/media_player.py
|
||||
homeassistant/components/pjlink/media_player.py
|
||||
homeassistant/components/plaato/*
|
||||
homeassistant/components/plex/media_player.py
|
||||
homeassistant/components/plex/sensor.py
|
||||
homeassistant/components/plex/*
|
||||
homeassistant/components/plugwise/*
|
||||
homeassistant/components/plum_lightpad/*
|
||||
homeassistant/components/pocketcasts/sensor.py
|
||||
|
15
homeassistant/components/plex/const.py
Normal file
15
homeassistant/components/plex/const.py
Normal file
@ -0,0 +1,15 @@
|
||||
"""Constants for the Plex component."""
|
||||
DOMAIN = "plex"
|
||||
NAME_FORMAT = "Plex {}"
|
||||
|
||||
DEFAULT_PORT = 32400
|
||||
DEFAULT_SSL = False
|
||||
DEFAULT_VERIFY_SSL = True
|
||||
|
||||
PLEX_CONFIG_FILE = "plex.conf"
|
||||
PLEX_SERVER_CONFIG = "server_config"
|
||||
|
||||
CONF_USE_EPISODE_ART = "use_episode_art"
|
||||
CONF_SHOW_ALL_CONTROLS = "show_all_controls"
|
||||
CONF_REMOVE_UNAVAILABLE_CLIENTS = "remove_unavailable_clients"
|
||||
CONF_CLIENT_REMOVE_INTERVAL = "client_remove_interval"
|
@ -2,8 +2,7 @@
|
||||
from datetime import timedelta
|
||||
import json
|
||||
import logging
|
||||
|
||||
import requests
|
||||
import requests.exceptions
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import MediaPlayerDevice, PLATFORM_SCHEMA
|
||||
@ -21,6 +20,9 @@ from homeassistant.components.media_player.const import (
|
||||
SUPPORT_VOLUME_SET,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_URL,
|
||||
CONF_TOKEN,
|
||||
CONF_VERIFY_SSL,
|
||||
DEVICE_DEFAULT_NAME,
|
||||
STATE_IDLE,
|
||||
STATE_OFF,
|
||||
@ -32,18 +34,22 @@ 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,
|
||||
DOMAIN as PLEX_DOMAIN,
|
||||
NAME_FORMAT,
|
||||
PLEX_CONFIG_FILE,
|
||||
)
|
||||
from .server import PlexServer
|
||||
|
||||
SERVER_SETUP = "server_setup"
|
||||
|
||||
_CONFIGURING = {}
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
NAME_FORMAT = "Plex {}"
|
||||
PLEX_CONFIG_FILE = "plex.conf"
|
||||
PLEX_DATA = "plex"
|
||||
|
||||
CONF_USE_EPISODE_ART = "use_episode_art"
|
||||
CONF_SHOW_ALL_CONTROLS = "show_all_controls"
|
||||
CONF_REMOVE_UNAVAILABLE_CLIENTS = "remove_unavailable_clients"
|
||||
CONF_CLIENT_REMOVE_INTERVAL = "client_remove_interval"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_USE_EPISODE_ART, default=False): cv.boolean,
|
||||
@ -58,8 +64,10 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
|
||||
def setup_platform(hass, config, add_entities_callback, discovery_info=None):
|
||||
"""Set up the Plex platform."""
|
||||
if PLEX_DATA not in hass.data:
|
||||
hass.data[PLEX_DATA] = {}
|
||||
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))
|
||||
@ -102,20 +110,19 @@ def setup_plexserver(
|
||||
host, token, has_ssl, verify_ssl, hass, config, add_entities_callback
|
||||
):
|
||||
"""Set up a plexserver based on host parameter."""
|
||||
import plexapi.server
|
||||
import plexapi.exceptions
|
||||
|
||||
cert_session = None
|
||||
http_prefix = "https" if has_ssl else "http"
|
||||
if has_ssl and (verify_ssl is False):
|
||||
_LOGGER.info("Ignoring SSL verification")
|
||||
cert_session = requests.Session()
|
||||
cert_session.verify = False
|
||||
|
||||
server_config = {
|
||||
CONF_URL: f"{http_prefix}://{host}",
|
||||
CONF_TOKEN: token,
|
||||
CONF_VERIFY_SSL: verify_ssl,
|
||||
}
|
||||
|
||||
try:
|
||||
plexserver = plexapi.server.PlexServer(
|
||||
f"{http_prefix}://{host}", token, cert_session
|
||||
)
|
||||
_LOGGER.info("Discovery configuration done (no token needed)")
|
||||
plexserver = PlexServer(server_config)
|
||||
plexserver.connect()
|
||||
except (
|
||||
plexapi.exceptions.BadRequest,
|
||||
plexapi.exceptions.Unauthorized,
|
||||
@ -125,6 +132,8 @@ def setup_plexserver(
|
||||
# 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:
|
||||
@ -139,9 +148,7 @@ def setup_plexserver(
|
||||
{host: {"token": token, "ssl": has_ssl, "verify": verify_ssl}},
|
||||
)
|
||||
|
||||
_LOGGER.info("Connected to: %s://%s", http_prefix, host)
|
||||
|
||||
plex_clients = hass.data[PLEX_DATA]
|
||||
plex_clients = {}
|
||||
plex_sessions = {}
|
||||
track_time_interval(hass, lambda now: update_devices(), timedelta(seconds=10))
|
||||
|
||||
|
@ -1,32 +1,30 @@
|
||||
"""Support for Plex media server monitoring."""
|
||||
from datetime import timedelta
|
||||
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_USERNAME,
|
||||
CONF_PASSWORD,
|
||||
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
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_SERVER = "server"
|
||||
from .const import DEFAULT_PORT, DEFAULT_SSL, DEFAULT_VERIFY_SSL
|
||||
from .server import PlexServer
|
||||
|
||||
DEFAULT_HOST = "localhost"
|
||||
DEFAULT_NAME = "Plex"
|
||||
DEFAULT_PORT = 32400
|
||||
DEFAULT_SSL = False
|
||||
DEFAULT_VERIFY_SSL = True
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1)
|
||||
|
||||
@ -34,11 +32,8 @@ 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_PASSWORD): cv.string,
|
||||
vol.Optional(CONF_TOKEN): cv.string,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||
vol.Optional(CONF_SERVER): cv.string,
|
||||
vol.Optional(CONF_USERNAME): cv.string,
|
||||
vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
|
||||
vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean,
|
||||
}
|
||||
@ -48,34 +43,20 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Plex sensor."""
|
||||
name = config.get(CONF_NAME)
|
||||
plex_user = config.get(CONF_USERNAME)
|
||||
plex_password = config.get(CONF_PASSWORD)
|
||||
plex_server = config.get(CONF_SERVER)
|
||||
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
|
||||
)
|
||||
|
||||
import plexapi.exceptions
|
||||
|
||||
try:
|
||||
add_entities(
|
||||
[
|
||||
PlexSensor(
|
||||
name,
|
||||
plex_url,
|
||||
plex_user,
|
||||
plex_password,
|
||||
plex_server,
|
||||
plex_token,
|
||||
config.get(CONF_VERIFY_SSL),
|
||||
)
|
||||
],
|
||||
True,
|
||||
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,
|
||||
@ -84,43 +65,18 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
_LOGGER.error(error)
|
||||
return
|
||||
|
||||
add_entities([PlexSensor(name, plex_server)], True)
|
||||
|
||||
|
||||
class PlexSensor(Entity):
|
||||
"""Representation of a Plex now playing sensor."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
plex_url,
|
||||
plex_user,
|
||||
plex_password,
|
||||
plex_server,
|
||||
plex_token,
|
||||
verify_ssl,
|
||||
):
|
||||
def __init__(self, name, plex_server):
|
||||
"""Initialize the sensor."""
|
||||
from plexapi.myplex import MyPlexAccount
|
||||
from plexapi.server import PlexServer
|
||||
from requests import Session
|
||||
|
||||
self._name = name
|
||||
self._state = 0
|
||||
self._state = None
|
||||
self._now_playing = []
|
||||
|
||||
cert_session = None
|
||||
if not verify_ssl:
|
||||
_LOGGER.info("Ignoring SSL verification")
|
||||
cert_session = Session()
|
||||
cert_session.verify = False
|
||||
|
||||
if plex_token:
|
||||
self._server = PlexServer(plex_url, plex_token, cert_session)
|
||||
elif plex_user and plex_password:
|
||||
user = MyPlexAccount(plex_user, plex_password)
|
||||
server = plex_server if plex_server else user.resources()[0].name
|
||||
self._server = user.resource(server).connect()
|
||||
else:
|
||||
self._server = PlexServer(plex_url, None, cert_session)
|
||||
self._server = plex_server
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -145,7 +101,19 @@ class PlexSensor(Entity):
|
||||
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
||||
def update(self):
|
||||
"""Update method for Plex sensor."""
|
||||
sessions = self._server.sessions()
|
||||
try:
|
||||
sessions = self._server.sessions()
|
||||
except plexapi.exceptions.BadRequest:
|
||||
_LOGGER.error(
|
||||
"Error listing current Plex sessions on %s", self._server.friendly_name
|
||||
)
|
||||
return
|
||||
except requests.exceptions.RequestException as ex:
|
||||
_LOGGER.warning(
|
||||
"Temporary error connecting to %s (%s)", self._server.friendly_name, ex
|
||||
)
|
||||
return
|
||||
|
||||
now_playing = []
|
||||
for sess in sessions:
|
||||
user = sess.usernames[0]
|
||||
|
59
homeassistant/components/plex/server.py
Normal file
59
homeassistant/components/plex/server.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""Shared class to maintain Plex server instances."""
|
||||
import logging
|
||||
import plexapi.server
|
||||
from requests import Session
|
||||
|
||||
from homeassistant.const import CONF_TOKEN, CONF_URL, CONF_VERIFY_SSL
|
||||
|
||||
from .const import DEFAULT_VERIFY_SSL
|
||||
|
||||
_LOGGER = logging.getLogger(__package__)
|
||||
|
||||
|
||||
class PlexServer:
|
||||
"""Manages a single Plex server connection."""
|
||||
|
||||
def __init__(self, server_config):
|
||||
"""Initialize a Plex server instance."""
|
||||
self._plex_server = None
|
||||
self._url = server_config.get(CONF_URL)
|
||||
self._token = server_config.get(CONF_TOKEN)
|
||||
self._verify_ssl = server_config.get(CONF_VERIFY_SSL, DEFAULT_VERIFY_SSL)
|
||||
|
||||
def connect(self):
|
||||
"""Connect to a Plex server directly, obtaining direct URL if necessary."""
|
||||
|
||||
def _connect_with_url():
|
||||
session = None
|
||||
if self._url.startswith("https") and not self._verify_ssl:
|
||||
session = Session()
|
||||
session.verify = False
|
||||
self._plex_server = plexapi.server.PlexServer(
|
||||
self._url, self._token, session
|
||||
)
|
||||
_LOGGER.debug("Connected to: %s (%s)", self.friendly_name, self.url_in_use)
|
||||
|
||||
_connect_with_url()
|
||||
|
||||
def clients(self):
|
||||
"""Pass through clients call to plexapi."""
|
||||
return self._plex_server.clients()
|
||||
|
||||
def sessions(self):
|
||||
"""Pass through sessions call to plexapi."""
|
||||
return self._plex_server.sessions()
|
||||
|
||||
@property
|
||||
def friendly_name(self):
|
||||
"""Return name of connected Plex server."""
|
||||
return self._plex_server.friendlyName
|
||||
|
||||
@property
|
||||
def machine_identifier(self):
|
||||
"""Return unique identifier of connected Plex server."""
|
||||
return self._plex_server.machineIdentifier
|
||||
|
||||
@property
|
||||
def url_in_use(self):
|
||||
"""Return URL used for connected Plex server."""
|
||||
return self._plex_server._baseurl # pylint: disable=W0212
|
Loading…
x
Reference in New Issue
Block a user