""" homeassistant.components.media_player.plex ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Provides an interface to the Plex API Configuration: To use Plex add something like this to your configuration: media_player: platform: plex name: plex_server user: plex password: my_secure_password Variables: name *Required The name of the backend device (Under Plex Media Server > settings > server). user *Required The Plex username password *Required The Plex password """ import logging from datetime import timedelta from homeassistant.components.media_player import ( MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO) from homeassistant.const import ( STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_UNKNOWN) import homeassistant.util as util REQUIREMENTS = ['https://github.com/adrienbrault/python-plexapi/archive/' 'df2d0847e801d6d5cda920326d693cf75f304f1a.zip' '#python-plexapi==1.0.2'] MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) _LOGGER = logging.getLogger(__name__) SUPPORT_PLEX = SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK # pylint: disable=abstract-method # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the plex platform. """ from plexapi.myplex import MyPlexUser from plexapi.exceptions import BadRequest name = config.get('name', '') user = config.get('user', '') password = config.get('password', '') plexuser = MyPlexUser.signin(user, password) plexserver = plexuser.getResource(name).connect() plex_clients = {} plex_sessions = {} @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_devices(): """ Updates the devices objects """ try: devices = plexuser.devices() except BadRequest: _LOGGER.exception("Error listing plex devices") return new_plex_clients = [] for device in devices: if (all(x not in ['client', 'player'] for x in device.provides) or 'PlexAPI' == device.product): continue if device.clientIdentifier not in plex_clients: new_client = PlexClient(device, plex_sessions, update_devices, update_sessions) plex_clients[device.clientIdentifier] = new_client new_plex_clients.append(new_client) else: plex_clients[device.clientIdentifier].set_device(device) if new_plex_clients: add_devices(new_plex_clients) @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) def update_sessions(): """ Updates the sessions objects """ try: sessions = plexserver.sessions() except BadRequest: _LOGGER.exception("Error listing plex sessions") return plex_sessions.clear() for session in sessions: plex_sessions[session.player.machineIdentifier] = session update_devices() update_sessions() class PlexClient(MediaPlayerDevice): """ Represents a Plex device. """ # pylint: disable=too-many-public-methods def __init__(self, device, plex_sessions, update_devices, update_sessions): self.plex_sessions = plex_sessions self.update_devices = update_devices self.update_sessions = update_sessions self.set_device(device) def set_device(self, device): """ Sets the device property """ self.device = device @property def session(self): """ Returns the session, if any """ if self.device.clientIdentifier not in self.plex_sessions: return None return self.plex_sessions[self.device.clientIdentifier] @property def name(self): """ Returns the name of the device. """ return self.device.name or self.device.product or self.device.device @property def state(self): """ Returns the state of the device. """ if self.session: state = self.session.player.state if state == 'playing': return STATE_PLAYING elif state == 'paused': return STATE_PAUSED elif self.device.isReachable: return STATE_IDLE else: return STATE_OFF return STATE_UNKNOWN def update(self): self.update_devices(no_throttle=True) self.update_sessions(no_throttle=True) @property def media_content_id(self): """ Content ID of current playing media. """ if self.session is not None: return self.session.ratingKey @property def media_content_type(self): """ Content type of current playing media. """ if self.session is None: return None media_type = self.session.type if media_type == 'episode': return MEDIA_TYPE_TVSHOW elif media_type == 'movie': return MEDIA_TYPE_VIDEO return None @property def media_duration(self): """ Duration of current playing media in seconds. """ if self.session is not None: return self.session.duration @property def media_image_url(self): """ Image url of current playing media. """ if self.session is not None: return self.session.thumbUrl @property def media_title(self): """ Title of current playing media. """ # find a string we can use as a title if self.session is not None: return self.session.title @property def media_season(self): """ Season of curent playing media. (TV Show only) """ from plexapi.video import Show if isinstance(self.session, Show): return self.session.seasons()[0].index @property def media_series_title(self): """ Series title of current playing media. (TV Show only)""" from plexapi.video import Show if isinstance(self.session, Show): return self.session.grandparentTitle @property def media_episode(self): """ Episode of current playing media. (TV Show only) """ from plexapi.video import Show if isinstance(self.session, Show): return self.session.index @property def supported_media_commands(self): """ Flags of media commands that are supported. """ return SUPPORT_PLEX def media_play(self): """ media_play media player. """ self.device.play({'type': 'video'}) def media_pause(self): """ media_pause media player. """ self.device.pause({'type': 'video'}) def media_next_track(self): """ Send next track command. """ self.device.skipNext({'type': 'video'}) def media_previous_track(self): """ Send previous track command. """ self.device.skipPrevious({'type': 'video'})