Update Plex via websockets (#28158)

* Save client identifier from auth for future use

* Use websocket events to update Plex

* Handle websocket disconnections

* Use aiohttp, shut down socket cleanly

* Bad rebase fix

* Don't connect websocket during config_flow validation, fix tests

* Move websocket handling to external library

* Close websocket session on HA stop

* Use external library, revert unnecessary test change

* Async & lint fixes

* Clean up websocket stopper on entry unload

* Setup websocket in component, pass actual needed object to library
This commit is contained in:
jjlawren 2019-10-25 11:37:50 -05:00 committed by Paulus Schoutsen
parent 0a5cde7ac3
commit 05ee15c28c
7 changed files with 52 additions and 13 deletions

View file

@ -514,6 +514,7 @@ omit =
homeassistant/components/plex/media_player.py homeassistant/components/plex/media_player.py
homeassistant/components/plex/sensor.py homeassistant/components/plex/sensor.py
homeassistant/components/plex/server.py homeassistant/components/plex/server.py
homeassistant/components/plex/websockets.py
homeassistant/components/plugwise/* homeassistant/components/plugwise/*
homeassistant/components/plum_lightpad/* homeassistant/components/plum_lightpad/*
homeassistant/components/pocketcasts/sensor.py homeassistant/components/pocketcasts/sensor.py

View file

@ -1,9 +1,9 @@
"""Support to embed Plex.""" """Support to embed Plex."""
import asyncio import asyncio
from datetime import timedelta
import logging import logging
import plexapi.exceptions import plexapi.exceptions
from plexwebsocket import PlexWebsocket
import requests.exceptions import requests.exceptions
import voluptuous as vol import voluptuous as vol
@ -16,9 +16,14 @@ from homeassistant.const import (
CONF_TOKEN, CONF_TOKEN,
CONF_URL, CONF_URL,
CONF_VERIFY_SSL, CONF_VERIFY_SSL,
EVENT_HOMEASSISTANT_STOP,
) )
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.event import async_track_time_interval from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from .const import ( from .const import (
CONF_USE_EPISODE_ART, CONF_USE_EPISODE_ART,
@ -33,8 +38,9 @@ from .const import (
PLATFORMS, PLATFORMS,
PLEX_MEDIA_PLAYER_OPTIONS, PLEX_MEDIA_PLAYER_OPTIONS,
PLEX_SERVER_CONFIG, PLEX_SERVER_CONFIG,
REFRESH_LISTENERS, PLEX_UPDATE_PLATFORMS_SIGNAL,
SERVERS, SERVERS,
WEBSOCKETS,
) )
from .server import PlexServer from .server import PlexServer
@ -67,9 +73,7 @@ _LOGGER = logging.getLogger(__package__)
def setup(hass, config): def setup(hass, config):
"""Set up the Plex component.""" """Set up the Plex component."""
hass.data.setdefault( hass.data.setdefault(PLEX_DOMAIN, {SERVERS: {}, DISPATCHERS: {}, WEBSOCKETS: {}})
PLEX_DOMAIN, {SERVERS: {}, REFRESH_LISTENERS: {}, DISPATCHERS: {}}
)
plex_config = config.get(PLEX_DOMAIN, {}) plex_config = config.get(PLEX_DOMAIN, {})
if plex_config: if plex_config:
@ -136,7 +140,6 @@ async def async_setup_entry(hass, entry):
) )
server_id = plex_server.machine_identifier server_id = plex_server.machine_identifier
hass.data[PLEX_DOMAIN][SERVERS][server_id] = plex_server hass.data[PLEX_DOMAIN][SERVERS][server_id] = plex_server
hass.data[PLEX_DOMAIN][DISPATCHERS][server_id] = []
for platform in PLATFORMS: for platform in PLATFORMS:
hass.async_create_task( hass.async_create_task(
@ -145,9 +148,29 @@ async def async_setup_entry(hass, entry):
entry.add_update_listener(async_options_updated) entry.add_update_listener(async_options_updated)
hass.data[PLEX_DOMAIN][REFRESH_LISTENERS][server_id] = async_track_time_interval( unsub = async_dispatcher_connect(
hass, lambda now: plex_server.update_platforms(), timedelta(seconds=10) hass,
PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id),
plex_server.update_platforms,
) )
hass.data[PLEX_DOMAIN][DISPATCHERS].setdefault(server_id, [])
hass.data[PLEX_DOMAIN][DISPATCHERS][server_id].append(unsub)
def update_plex():
async_dispatcher_send(hass, PLEX_UPDATE_PLATFORMS_SIGNAL.format(server_id))
session = async_get_clientsession(hass)
websocket = PlexWebsocket(plex_server.plex_server, update_plex, session)
hass.loop.create_task(websocket.listen())
hass.data[PLEX_DOMAIN][WEBSOCKETS][server_id] = websocket
def close_websocket_session(_):
websocket.close()
unsub = hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP, close_websocket_session
)
hass.data[PLEX_DOMAIN][DISPATCHERS][server_id].append(unsub)
return True return True
@ -156,8 +179,8 @@ async def async_unload_entry(hass, entry):
"""Unload a config entry.""" """Unload a config entry."""
server_id = entry.data[CONF_SERVER_IDENTIFIER] server_id = entry.data[CONF_SERVER_IDENTIFIER]
cancel = hass.data[PLEX_DOMAIN][REFRESH_LISTENERS].pop(server_id) websocket = hass.data[PLEX_DOMAIN][WEBSOCKETS].pop(server_id)
cancel() websocket.close()
dispatchers = hass.data[PLEX_DOMAIN][DISPATCHERS].pop(server_id) dispatchers = hass.data[PLEX_DOMAIN][DISPATCHERS].pop(server_id)
for unsub in dispatchers: for unsub in dispatchers:

View file

@ -10,8 +10,8 @@ DEFAULT_VERIFY_SSL = True
DISPATCHERS = "dispatchers" DISPATCHERS = "dispatchers"
PLATFORMS = ["media_player", "sensor"] PLATFORMS = ["media_player", "sensor"]
REFRESH_LISTENERS = "refresh_listeners"
SERVERS = "servers" SERVERS = "servers"
WEBSOCKETS = "websockets"
PLEX_CONFIG_FILE = "plex.conf" PLEX_CONFIG_FILE = "plex.conf"
PLEX_MEDIA_PLAYER_OPTIONS = "plex_mp_options" PLEX_MEDIA_PLAYER_OPTIONS = "plex_mp_options"
@ -19,6 +19,7 @@ PLEX_SERVER_CONFIG = "server_config"
PLEX_NEW_MP_SIGNAL = "plex_new_mp_signal.{}" PLEX_NEW_MP_SIGNAL = "plex_new_mp_signal.{}"
PLEX_UPDATE_MEDIA_PLAYER_SIGNAL = "plex_update_mp_signal.{}" PLEX_UPDATE_MEDIA_PLAYER_SIGNAL = "plex_update_mp_signal.{}"
PLEX_UPDATE_PLATFORMS_SIGNAL = "plex_update_platforms_signal.{}"
PLEX_UPDATE_SENSOR_SIGNAL = "plex_update_sensor_signal.{}" PLEX_UPDATE_SENSOR_SIGNAL = "plex_update_sensor_signal.{}"
CONF_CLIENT_IDENTIFIER = "client_id" CONF_CLIENT_IDENTIFIER = "client_id"

View file

@ -5,7 +5,8 @@
"documentation": "https://www.home-assistant.io/integrations/plex", "documentation": "https://www.home-assistant.io/integrations/plex",
"requirements": [ "requirements": [
"plexapi==3.0.6", "plexapi==3.0.6",
"plexauth==0.0.5" "plexauth==0.0.5",
"plexwebsocket==0.0.1"
], ],
"dependencies": [ "dependencies": [
"http" "http"

View file

@ -103,6 +103,8 @@ class PlexServer:
def update_platforms(self): def update_platforms(self):
"""Update the platform entities.""" """Update the platform entities."""
_LOGGER.debug("Updating devices")
available_clients = {} available_clients = {}
new_clients = set() new_clients = set()
@ -164,6 +166,11 @@ class PlexServer:
sessions, sessions,
) )
@property
def plex_server(self):
"""Return the plexapi PlexServer instance."""
return self._plex_server
@property @property
def friendly_name(self): def friendly_name(self):
"""Return name of connected Plex server.""" """Return name of connected Plex server."""

View file

@ -973,6 +973,9 @@ plexapi==3.0.6
# homeassistant.components.plex # homeassistant.components.plex
plexauth==0.0.5 plexauth==0.0.5
# homeassistant.components.plex
plexwebsocket==0.0.1
# homeassistant.components.plum_lightpad # homeassistant.components.plum_lightpad
plumlightpad==0.0.11 plumlightpad==0.0.11

View file

@ -345,6 +345,9 @@ plexapi==3.0.6
# homeassistant.components.plex # homeassistant.components.plex
plexauth==0.0.5 plexauth==0.0.5
# homeassistant.components.plex
plexwebsocket==0.0.1
# homeassistant.components.mhz19 # homeassistant.components.mhz19
# homeassistant.components.serial_pm # homeassistant.components.serial_pm
pmsensor==0.4 pmsensor==0.4