text: """Support to interface with the Emby API."""
  import logging
  
  from pyemby import EmbyServer
  import voluptuous as vol
  
  from homeassistant.components.media_player import PLATFORM_SCHEMA, MediaPlayerDevice
  from homeassistant.components.media_player.const import (
      MEDIA_TYPE_CHANNEL,
      MEDIA_TYPE_MOVIE,
      MEDIA_TYPE_MUSIC,
      MEDIA_TYPE_TVSHOW,
      SUPPORT_NEXT_TRACK,
      SUPPORT_PAUSE,
      SUPPORT_PLAY,
      SUPPORT_PREVIOUS_TRACK,
      SUPPORT_SEEK,
      SUPPORT_STOP,
  )
  from homeassistant.const import (
      CONF_API_KEY,
      CONF_HOST,
      CONF_PORT,
      CONF_SSL,
      DEVICE_DEFAULT_NAME,
      EVENT_HOMEASSISTANT_START,
      EVENT_HOMEASSISTANT_STOP,
      STATE_IDLE,
      STATE_OFF,
      STATE_PAUSED,
      STATE_PLAYING,
  )
  from homeassistant.core import callback
  import homeassistant.helpers.config_validation as cv
  import homeassistant.util.dt as dt_util
  
  _LOGGER = logging.getLogger(__name__)
  
  CONF_AUTO_HIDE = "auto_hide"
  
  MEDIA_TYPE_TRAILER = "trailer"
  MEDIA_TYPE_GENERIC_VIDEO = "video"
  
  DEFAULT_HOST = "localhost"
  DEFAULT_PORT = 8096
  DEFAULT_SSL_PORT = 8920
  DEFAULT_SSL = False
  DEFAULT_AUTO_HIDE = False
  
  _LOGGER = logging.getLogger(__name__)
  
  SUPPORT_EMBY = (
      SUPPORT_PAUSE
      | SUPPORT_PREVIOUS_TRACK
      | SUPPORT_NEXT_TRACK
      | SUPPORT_STOP
      | SUPPORT_SEEK
      | SUPPORT_PLAY
  )
  
  PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
      {
          vol.Required(CONF_API_KEY): cv.string,
          vol.Optional(CONF_AUTO_HIDE, default=DEFAULT_AUTO_HIDE): cv.boolean,
          vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
          vol.Optional(CONF_PORT): cv.port,
          vol.Optional(CONF_SSL, default=DEFAULT_SSL): cv.boolean,
      }
  )
  
  
  async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
      """Set up the Emby platform."""
  
      host = config.get(CONF_HOST)
      key = config.get(CONF_API_KEY)
      port = config.get(CONF_PORT)
      ssl = config.get(CONF_SSL)
      auto_hide = config.get(CONF_AUTO_HIDE)
  
      if port is None:
          port = DEFAULT_SSL_PORT if ssl else DEFAULT_PORT
  
      _LOGGER.debug("Setting up Emby server at: %s:%s", host, port)
  
      emby = EmbyServer(host, key, port, ssl, hass.loop)
  
      active_emby_devices = {}
      inactive_emby_devices = {}
  
      @callback
      def device_update_callback(data):
          """Handle devices which are added to Emby."""
          new_devices = []
          active_devices = []
          for dev_id in emby.devices:
              active_devices.append(dev_id)
              if (
                  dev_id not in active_emby_devices
                  and dev_id not in inactive_emby_devices
              ):
                  new = EmbyDevice(emby, dev_id)
                  active_emby_devices[dev_id] = new
                  new_devices.append(new)
  
              elif dev_id in inactive_emby_devices:
                  if emby.devices[dev_id].state != "Off":
                      add = inactive_emby_devices.pop(dev_id)
                      active_emby_devices[dev_id] = add
                      _LOGGER.debug("Showing %s, item: %s", dev_id, add)
                      add.set_available(True)
                      add.set_hidden(False)
  
          if new_devices:
              _LOGGER.debug("Adding new devices: %s", new_devices)
              async_add_entities(new_devices, True)
  
      @callback
      def device_removal_callback(data):
          """Handle the removal of devices from Emby."""
          if data in active_emby_devices:
              rem = active_emby_devices.pop(data)
              inactive_emby_devices[data] = rem
              _LOGGER.debug("Inactive %s, item: %s", data, rem)
              rem.set_available(False)
              if auto_hide:
                  rem.set_hidden(True)
  
      @callback
      def start_emby(event):
          """Start Emby connection."""
          emby.start()
  
      async def stop_emby(event):
          """Stop Emby connection."""
          await emby.stop()
  
      emby.add_new_devices_callback(device_update_callback)
      emby.add_stale_devices_callback(device_removal_callback)
  
      hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_emby)
      hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_emby)
  
  
  class EmbyDevice(MediaPlayerDevice):
      """Representation of an Emby device."""
  
      def __init__(self, emby, device_id):
          """Initialize the Emby device."""
          _LOGGER.debug("New Emby Device initialized with ID: %s", device_id)
          self.emby = emby
          self.device_id = device_id
          self.device = self.emby.devices[self.device_id]
  
          self._hidden = False
          self._available = True
  
          self.media_status_last_position = None
          self.media_status_received = None
  
      async def async_added_to_hass(self):
          """Register callback."""
          self.emby.add_update_callback(self.async_update_callback, self.device_id)
  
      @callback
      def async_update_callback(self, msg):
          """Handle device updates."""
          # Check if we should update progress
          if self.device.media_position:
              if self.device.media_position != self.media_status_last_position:
                  self.media_status_last_position = self.device.media_position
                  self.media_status_received = dt_util.utcnow()
          elif not self.device.is_nowplaying:
              # No position, but we have an old value and are still playing
              self.media_status_last_position = None
              self.media_status_received = None
  
          self.async_schedule_update_ha_state()
  
      @property
      def hidden(self):
          """Return True if entity should be hidden from UI."""
          return self._hidden
  
      def set_hidden(self, value):
          """Set hidden property."""
          self._hidden = value
  
      @property
      def available(self):
          """Return True if entity is available."""
          return self._available
  
      def set_available(self, value):
          """Set available property."""
          self._available = value
  
      @property
      def unique_id(self):
          """Return the id of this emby client."""
          return self.device_id
  
      @property
      def supports_remote_control(self):
          """Return control ability."""
          return self.device.supports_remote_control
  
      @property
      def name(self):
          """Return the name of the device."""
          return (
              f"Emby - {self.device.client} - {self.device.name}" or DEVICE_DEFAULT_NAME
          )
  
      @property
      def should_poll(self):
          """Return True if entity has to be polled for state."""
          return False
  
      @property
      def state(self):
          """Return the state of the device."""
          state = self.device.state
          if state == "Paused":
              return STATE_PAUSED
          if state == "Playing":
              return STATE_PLAYING
          if state == "Idle":
              return STATE_IDLE
          if state == "Off":
              return STATE_OFF
  
      @property
      def app_name(self):
          """Return current user as app_name."""
          # Ideally the media_player object would have a user property.
          return self.device.username
  
      @property
      def media_content_id(self):
          """Content ID of current playing media."""
          return self.device.media_id
  
      @property
      def media_content_type(self):
          """Content type of current playing media."""
          media_type = self.device.media_type
          if media_type == "Episode":
              return MEDIA_TYPE_TVSHOW
          if media_type == "Movie":
              return MEDIA_TYPE_MOVIE
          if media_type == "Trailer":
              return MEDIA_TYPE_TRAILER
          if media_type == "Music":
              return MEDIA_TYPE_MUSIC
          if media_type == "Video":
              return MEDIA_TYPE_GENERIC_VIDEO
          if media_type == "Audio":
              return MEDIA_TYPE_MUSIC
          if media_type == "TvChannel":
              return MEDIA_TYPE_CHANNEL
          return None
  
      @property
      def media_duration(self):
          """Return the duration of current playing media in seconds."""
          return self.device.media_runtime
  
      @property
      def media_position(self):
          """Return the position of current playing media in seconds."""
          return self.media_status_last_position
  
      @property
      def media_position_updated_at(self):
          """
          When was the position of the current playing media valid.
  
          Returns value from homeassistant.util.dt.utcnow().
          """
          return self.media_status_received
  
      @property
      def media_image_url(self):
          """Return the image URL of current playing media."""
          return self.device.media_image_url
  
      @property
      def media_title(self):
          """Return the title of current playing media."""
          return self.device.media_title
  
      @property
      def media_season(self):
          """Season of current playing media (TV Show only)."""
          return self.device.media_season
  
      @property
      def media_series_title(self):
          """Return the title of the series of current playing media (TV)."""
          return self.device.media_series_title
  
      @property
      def media_episode(self):
          """Return the episode of current playing media (TV only)."""
          return self.device.media_episode
  
      @property
      def media_album_name(self):
          """Return the album name of current playing media (Music only)."""
          return self.device.media_album_name
  
      @property
      def media_artist(self):
          """Return the artist of current playing media (Music track only)."""
          return self.device.media_artist
  
      @property
      def media_album_artist(self):
          """Return the album artist of current playing media (Music only)."""
          return self.device.media_album_artist
  
      @property
      def supported_features(self):
          """Flag media player features that are supported."""
          if self.supports_remote_control:
              return SUPPORT_EMBY
          return None
  
      def async_media_play(self):
          """Play media.
  
          This method must be run in the event loop and returns a coroutine.
          """
          return self.device.media_play()
  
      def async_media_pause(self):
          """Pause the media player.
  
          This method must be run in the event loop and returns a coroutine.
          """
          return self.device.media_pause()
  
      def async_media_stop(self):
          """Stop the media player.
  
          This method must be run in the event loop and returns a coroutine.
          """
          return self.device.media_stop()
  
      def async_media_next_track(self):
          """Send next track command.
  
          This method must be run in the event loop and returns a coroutine.
          """
          return self.device.media_next()
  
      def async_media_previous_track(self):
          """Send next track command.
  
          This method must be run in the event loop and returns a coroutine.
          """
          return self.device.media_previous()
  
      def async_media_seek(self, position):
          """Send seek command.
  
          This method must be run in the event loop and returns a coroutine.
          """
          return self.device.media_seek(position)
  