Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions homeassistant/components/plex/const.py
Original file line number Diff line number Diff line change
@@ -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"
59 changes: 33 additions & 26 deletions homeassistant/components/plex/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -21,6 +20,9 @@
SUPPORT_VOLUME_SET,
)
from homeassistant.const import (
CONF_URL,
CONF_TOKEN,
CONF_VERIFY_SSL,
DEVICE_DEFAULT_NAME,
STATE_IDLE,
STATE_OFF,
Expand All @@ -32,17 +34,21 @@
from homeassistant.util import dt as dt_util
from homeassistant.util.json import load_json, save_json

_CONFIGURING = {}
_LOGGER = logging.getLogger(__name__)
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

NAME_FORMAT = "Plex {}"
PLEX_CONFIG_FILE = "plex.conf"
PLEX_DATA = "plex"
SERVER_SETUP = "server_setup"

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"
_CONFIGURING = {}
_LOGGER = logging.getLogger(__name__)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
Expand All @@ -58,8 +64,10 @@

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))
Expand Down Expand Up @@ -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,
Expand All @@ -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:
Expand All @@ -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))

Expand Down
88 changes: 28 additions & 60 deletions homeassistant/components/plex/sensor.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,39 @@
"""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)

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,
}
Expand All @@ -48,34 +43,20 @@
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,
Expand All @@ -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

Comment thread
jjlawren marked this conversation as resolved.
@property
def name(self):
Expand All @@ -145,7 +101,19 @@ def device_state_attributes(self):
@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]
Expand Down
59 changes: 59 additions & 0 deletions homeassistant/components/plex/server.py
Original file line number Diff line number Diff line change
@@ -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():
Comment thread
MartinHjelmare marked this conversation as resolved.
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