mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Move config and connections to Plex component (#26488)
* Move config and connections to component * Separate imports * Set a unique_id on sensor * Set a platforms const * Add SERVERS dict, hardcode to single server * Move to debug * Return false * More debug * Import at top to fix lint * Guard against legacy setup attempts * Refactor to add setup callback * Review comments * Log levels * Return result of callback * Store CONFIGURING in hass.data * Set up discovery if no config data * Use schema to set defaults * Remove media_player options to remove entities * Improve error handling
This commit is contained in:
parent
3c629db096
commit
30fb4ddc98
@ -36,6 +36,7 @@ SERVICE_KONNECTED = "konnected"
|
|||||||
SERVICE_MOBILE_APP = "hass_mobile_app"
|
SERVICE_MOBILE_APP = "hass_mobile_app"
|
||||||
SERVICE_NETGEAR = "netgear_router"
|
SERVICE_NETGEAR = "netgear_router"
|
||||||
SERVICE_OCTOPRINT = "octoprint"
|
SERVICE_OCTOPRINT = "octoprint"
|
||||||
|
SERVICE_PLEX = "plex_mediaserver"
|
||||||
SERVICE_ROKU = "roku"
|
SERVICE_ROKU = "roku"
|
||||||
SERVICE_SABNZBD = "sabnzbd"
|
SERVICE_SABNZBD = "sabnzbd"
|
||||||
SERVICE_SAMSUNG_PRINTER = "samsung_printer"
|
SERVICE_SAMSUNG_PRINTER = "samsung_printer"
|
||||||
@ -68,7 +69,7 @@ SERVICE_HANDLERS = {
|
|||||||
SERVICE_FREEBOX: ("freebox", None),
|
SERVICE_FREEBOX: ("freebox", None),
|
||||||
SERVICE_YEELIGHT: ("yeelight", None),
|
SERVICE_YEELIGHT: ("yeelight", None),
|
||||||
"panasonic_viera": ("media_player", "panasonic_viera"),
|
"panasonic_viera": ("media_player", "panasonic_viera"),
|
||||||
"plex_mediaserver": ("media_player", "plex"),
|
SERVICE_PLEX: ("plex", None),
|
||||||
"yamaha": ("media_player", "yamaha"),
|
"yamaha": ("media_player", "yamaha"),
|
||||||
"logitech_mediaserver": ("media_player", "squeezebox"),
|
"logitech_mediaserver": ("media_player", "squeezebox"),
|
||||||
"directv": ("media_player", "directv"),
|
"directv": ("media_player", "directv"),
|
||||||
|
@ -1 +1,201 @@
|
|||||||
"""The plex component."""
|
"""Support to embed Plex."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import plexapi.exceptions
|
||||||
|
import requests.exceptions
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components.discovery import SERVICE_PLEX
|
||||||
|
from homeassistant.components.media_player import DOMAIN as MP_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,
|
||||||
|
DEFAULT_HOST,
|
||||||
|
DEFAULT_PORT,
|
||||||
|
DEFAULT_SSL,
|
||||||
|
DEFAULT_VERIFY_SSL,
|
||||||
|
DOMAIN as PLEX_DOMAIN,
|
||||||
|
PLATFORMS,
|
||||||
|
PLEX_CONFIG_FILE,
|
||||||
|
PLEX_MEDIA_PLAYER_OPTIONS,
|
||||||
|
SERVERS,
|
||||||
|
)
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
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 = "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."""
|
||||||
|
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)
|
||||||
|
|
||||||
|
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
|
||||||
|
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:
|
||||||
|
_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:
|
||||||
|
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:
|
||||||
|
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"
|
||||||
|
server_config[CONF_URL] = f"{http_prefix}://{host_and_port}"
|
||||||
|
|
||||||
|
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(
|
||||||
|
"Connection to Plex server failed, please verify token and SSL settings: [%s]",
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
request_configuration(host_and_port)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
hass.data[PLEX_DOMAIN][SERVERS][
|
||||||
|
plex_server.machine_identifier
|
||||||
|
] = plex_server
|
||||||
|
|
||||||
|
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")
|
||||||
|
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] = MEDIA_PLAYER_SCHEMA({})
|
||||||
|
|
||||||
|
for platform in PLATFORMS:
|
||||||
|
hass.helpers.discovery.load_platform(
|
||||||
|
platform, PLEX_DOMAIN, {}, original_config
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def request_configuration(host_and_port):
|
||||||
|
"""Request configuration steps from the user."""
|
||||||
|
configurator = hass.components.configurator
|
||||||
|
if host_and_port in hass.data[PLEX_DOMAIN][CONFIGURING]:
|
||||||
|
configurator.notify_errors(
|
||||||
|
hass.data[PLEX_DOMAIN][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)
|
||||||
|
|
||||||
|
hass.data[PLEX_DOMAIN][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.
|
||||||
|
|
||||||
|
original_config = config
|
||||||
|
|
||||||
|
hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}, CONFIGURING: {}})
|
||||||
|
|
||||||
|
if hass.data[PLEX_DOMAIN][SERVERS]:
|
||||||
|
_LOGGER.debug("Plex server already configured")
|
||||||
|
return False
|
||||||
|
|
||||||
|
plex_config = config.get(PLEX_DOMAIN, {})
|
||||||
|
return setup_plex(config=plex_config)
|
||||||
|
@ -7,7 +7,11 @@ DEFAULT_PORT = 32400
|
|||||||
DEFAULT_SSL = False
|
DEFAULT_SSL = False
|
||||||
DEFAULT_VERIFY_SSL = True
|
DEFAULT_VERIFY_SSL = True
|
||||||
|
|
||||||
|
PLATFORMS = ["media_player", "sensor"]
|
||||||
|
SERVERS = "servers"
|
||||||
|
|
||||||
PLEX_CONFIG_FILE = "plex.conf"
|
PLEX_CONFIG_FILE = "plex.conf"
|
||||||
|
PLEX_MEDIA_PLAYER_OPTIONS = "plex_mp_options"
|
||||||
PLEX_SERVER_CONFIG = "server_config"
|
PLEX_SERVER_CONFIG = "server_config"
|
||||||
|
|
||||||
CONF_USE_EPISODE_ART = "use_episode_art"
|
CONF_USE_EPISODE_ART = "use_episode_art"
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import requests.exceptions
|
|
||||||
import voluptuous as vol
|
|
||||||
|
|
||||||
from homeassistant.components.media_player import MediaPlayerDevice, PLATFORM_SCHEMA
|
import plexapi.exceptions
|
||||||
|
import plexapi.playlist
|
||||||
|
import plexapi.playqueue
|
||||||
|
import requests.exceptions
|
||||||
|
|
||||||
|
from homeassistant.components.media_player import MediaPlayerDevice
|
||||||
from homeassistant.components.media_player.const import (
|
from homeassistant.components.media_player.const import (
|
||||||
MEDIA_TYPE_MOVIE,
|
MEDIA_TYPE_MOVIE,
|
||||||
MEDIA_TYPE_MUSIC,
|
MEDIA_TYPE_MUSIC,
|
||||||
@ -20,150 +23,37 @@ from homeassistant.components.media_player.const import (
|
|||||||
SUPPORT_VOLUME_SET,
|
SUPPORT_VOLUME_SET,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_HOST,
|
|
||||||
CONF_PORT,
|
|
||||||
CONF_SSL,
|
|
||||||
CONF_URL,
|
|
||||||
CONF_TOKEN,
|
|
||||||
CONF_VERIFY_SSL,
|
|
||||||
DEVICE_DEFAULT_NAME,
|
DEVICE_DEFAULT_NAME,
|
||||||
STATE_IDLE,
|
STATE_IDLE,
|
||||||
STATE_OFF,
|
STATE_OFF,
|
||||||
STATE_PAUSED,
|
STATE_PAUSED,
|
||||||
STATE_PLAYING,
|
STATE_PLAYING,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers import config_validation as cv
|
|
||||||
from homeassistant.helpers.event import track_time_interval
|
from homeassistant.helpers.event import track_time_interval
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
from homeassistant.util.json import load_json, save_json
|
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_USE_EPISODE_ART,
|
CONF_USE_EPISODE_ART,
|
||||||
CONF_SHOW_ALL_CONTROLS,
|
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,
|
DOMAIN as PLEX_DOMAIN,
|
||||||
NAME_FORMAT,
|
NAME_FORMAT,
|
||||||
PLEX_CONFIG_FILE,
|
PLEX_MEDIA_PLAYER_OPTIONS,
|
||||||
|
SERVERS,
|
||||||
)
|
)
|
||||||
from .server import PlexServer
|
|
||||||
|
|
||||||
SERVER_SETUP = "server_setup"
|
SERVER_SETUP = "server_setup"
|
||||||
|
|
||||||
_CONFIGURING = {}
|
_CONFIGURING = {}
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_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):
|
def setup_platform(hass, config, add_entities_callback, discovery_info=None):
|
||||||
"""Set up the Plex platform."""
|
"""Set up the Plex platform."""
|
||||||
plex_data = hass.data.setdefault(PLEX_DOMAIN, {})
|
if discovery_info is None:
|
||||||
server_setup = plex_data.setdefault(SERVER_SETUP, False)
|
|
||||||
if server_setup:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# get config from plex.conf
|
plexserver = list(hass.data[PLEX_DOMAIN][SERVERS].values())[0]
|
||||||
file_config = load_json(hass.config.path(PLEX_CONFIG_FILE))
|
config = hass.data[PLEX_MEDIA_PLAYER_OPTIONS]
|
||||||
|
|
||||||
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}},
|
|
||||||
)
|
|
||||||
|
|
||||||
plex_clients = {}
|
plex_clients = {}
|
||||||
plex_sessions = {}
|
plex_sessions = {}
|
||||||
@ -178,7 +68,9 @@ def setup_plexserver(
|
|||||||
return
|
return
|
||||||
except requests.exceptions.RequestException as ex:
|
except requests.exceptions.RequestException as ex:
|
||||||
_LOGGER.warning(
|
_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
|
return
|
||||||
|
|
||||||
@ -210,7 +102,9 @@ def setup_plexserver(
|
|||||||
return
|
return
|
||||||
except requests.exceptions.RequestException as ex:
|
except requests.exceptions.RequestException as ex:
|
||||||
_LOGGER.warning(
|
_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
|
return
|
||||||
|
|
||||||
@ -239,7 +133,6 @@ def setup_plexserver(
|
|||||||
_LOGGER.debug("Refreshing session: %s", machine_identifier)
|
_LOGGER.debug("Refreshing session: %s", machine_identifier)
|
||||||
plex_clients[machine_identifier].refresh(None, session)
|
plex_clients[machine_identifier].refresh(None, session)
|
||||||
|
|
||||||
clients_to_remove = []
|
|
||||||
for client in plex_clients.values():
|
for client in plex_clients.values():
|
||||||
# force devices to idle that do not have a valid session
|
# force devices to idle that do not have a valid session
|
||||||
if client.session is None:
|
if client.session is None:
|
||||||
@ -253,59 +146,10 @@ def setup_plexserver(
|
|||||||
if client not in new_plex_clients:
|
if client not in new_plex_clients:
|
||||||
client.schedule_update_ha_state()
|
client.schedule_update_ha_state()
|
||||||
|
|
||||||
if not config.get(CONF_REMOVE_UNAVAILABLE_CLIENTS) or client.available:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if (dt_util.utcnow() - client.marked_unavailable) >= (
|
|
||||||
config.get(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:
|
if new_plex_clients:
|
||||||
add_entities_callback(new_plex_clients)
|
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):
|
class PlexClient(MediaPlayerDevice):
|
||||||
"""Representation of a Plex device."""
|
"""Representation of a Plex device."""
|
||||||
|
|
||||||
@ -378,9 +222,6 @@ class PlexClient(MediaPlayerDevice):
|
|||||||
|
|
||||||
def refresh(self, device, session):
|
def refresh(self, device, session):
|
||||||
"""Refresh key device data."""
|
"""Refresh key device data."""
|
||||||
import plexapi.exceptions
|
|
||||||
|
|
||||||
# new data refresh
|
|
||||||
self._clear_media_details()
|
self._clear_media_details()
|
||||||
|
|
||||||
if session: # Not being triggered by Chrome or FireTablet Plex App
|
if session: # Not being triggered by Chrome or FireTablet Plex App
|
||||||
@ -851,8 +692,6 @@ class PlexClient(MediaPlayerDevice):
|
|||||||
src["video_name"]
|
src["video_name"]
|
||||||
)
|
)
|
||||||
|
|
||||||
import plexapi.playlist
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
media
|
media
|
||||||
and media_type == "EPISODE"
|
and media_type == "EPISODE"
|
||||||
@ -918,8 +757,6 @@ class PlexClient(MediaPlayerDevice):
|
|||||||
_LOGGER.error("Client cannot play media: %s", self.entity_id)
|
_LOGGER.error("Client cannot play media: %s", self.entity_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
import plexapi.playqueue
|
|
||||||
|
|
||||||
playqueue = plexapi.playqueue.PlayQueue.create(
|
playqueue = plexapi.playqueue.PlayQueue.create(
|
||||||
self.device.server, media, **params
|
self.device.server, media, **params
|
||||||
)
|
)
|
||||||
|
@ -1,87 +1,51 @@
|
|||||||
"""Support for Plex media server monitoring."""
|
"""Support for Plex media server monitoring."""
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import plexapi.exceptions
|
import plexapi.exceptions
|
||||||
import requests.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.helpers.entity import Entity
|
||||||
from homeassistant.util import Throttle
|
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 .const import DOMAIN as PLEX_DOMAIN, SERVERS
|
||||||
from .server import PlexServer
|
|
||||||
|
|
||||||
DEFAULT_NAME = "Plex"
|
DEFAULT_NAME = "Plex"
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1)
|
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):
|
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
"""Set up the Plex sensor."""
|
"""Set up the Plex sensor."""
|
||||||
name = config.get(CONF_NAME)
|
if discovery_info is None:
|
||||||
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
|
return
|
||||||
|
|
||||||
add_entities([PlexSensor(name, plex_server)], True)
|
plexserver = list(hass.data[PLEX_DOMAIN][SERVERS].values())[0]
|
||||||
|
add_entities([PlexSensor(plexserver)], True)
|
||||||
|
|
||||||
|
|
||||||
class PlexSensor(Entity):
|
class PlexSensor(Entity):
|
||||||
"""Representation of a Plex now playing sensor."""
|
"""Representation of a Plex now playing sensor."""
|
||||||
|
|
||||||
def __init__(self, name, plex_server):
|
def __init__(self, plex_server):
|
||||||
"""Initialize the sensor."""
|
"""Initialize the sensor."""
|
||||||
self._name = name
|
self._name = DEFAULT_NAME
|
||||||
self._state = None
|
self._state = None
|
||||||
self._now_playing = []
|
self._now_playing = []
|
||||||
self._server = plex_server
|
self._server = plex_server
|
||||||
|
self._unique_id = f"sensor-{plex_server.machine_identifier}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""Return the name of the sensor."""
|
"""Return the name of the sensor."""
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return the id of this plex client."""
|
||||||
|
return self._unique_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Shared class to maintain Plex server instances."""
|
"""Shared class to maintain Plex server instances."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import plexapi.server
|
import plexapi.server
|
||||||
from requests import Session
|
from requests import Session
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user