Bỏ qua đến nội dung
Show Plex Current Playing and Upcoming Movies in Home Assistant

Show Plex Current Playing and Upcoming Movies in Home Assistant

Use Tautulli, MQTT, Sonarr Upcoming Media, and a few custom Lovelace cards to turn Home Assistant into a Plex current-playing and upcoming-TV dashboard.

In my home media setup, Plex is where movies and shows are played, Sonarr tracks TV series, and Home Assistant usually handles smart-home controls. But when these pieces are pulled into one dashboard, Home Assistant becomes a genuinely useful media overview: who is watching what on Plex, playback progress, which device is streaming, and which episodes are coming soon.

This is the result on my dashboard:

Home Assistant dashboard showing Plex Current Playing and upcoming media

Overall idea

The data flow has two independent parts:

  1. Current Playing: Tautulli watches Plex, then sends poster, title, playback status, progress, and stream quality to Home Assistant through MQTT.
  2. Upcoming TV: Sonarr Upcoming Media creates a sensor with upcoming episode data, then Lovelace renders it as wide poster cards with upcoming-media-card.

Simple diagram:

Plex Media Server
    |
    v
Tautulli
    |
    v
MQTT topics
    |
    v
Home Assistant MQTT sensors
    |
    v
Current Playing card

Sonarr calendar
    |
    v
Sonarr Upcoming Media sensor
    |
    v
Upcoming Media Card

The nice part is that Home Assistant does not need to poll the Plex API constantly. Tautulli handles playback tracking, while Home Assistant only reads normalized sensor states.

Required components

You need these pieces:

  • Plex Media Server already running.
  • Tautulli connected to Plex.
  • An MQTT broker in Home Assistant, such as Mosquitto.
  • HACS for installing custom cards.
  • custom:button-card for the Current Playing card.
  • card-mod for styling the small poster inside the card.
  • upcoming-media-card for upcoming episode cards.
  • The custom integration Sonarr Upcoming Media if you want the schedule from Sonarr.

If you already have a Plex, Radarr, Sonarr, Prowlarr, and qBittorrent media stack, this is only the dashboard layer on top. For the base flow, see: Automatically download favorite movies to Plex with Watchlist, Radarr, and qBittorrent.

Part 1: Create Plex sensors from MQTT

In my current setup, Tautulli sends several small MQTT topics. Each topic carries one part of the playback data, for example:

  • homeassistant/tautulli/image: poster and movie title.
  • homeassistant/tautulli/status: status text such as (AFTMM) has stopped Jury Duty - Deliberations.
  • homeassistant/tautulli/player: app or playback device.
  • homeassistant/tautulli/episode: season and episode details.
  • homeassistant/tautulli/activity: progress percentage and stream quality.

In configuration.yaml, define MQTT sensors like this:

mqtt:
  sensor:
    - name: "Plex Media Playing"
      unique_id: "plex_media"
      state_topic: "homeassistant/tautulli/image"
      value_template: "{{ value_json.body }}"

    - name: "Plex Media Name"
      unique_id: "plex_media_name"
      state_topic: "homeassistant/tautulli/image"
      value_template: "{{ value_json.subject }}"

    - name: "Plex Activity"
      unique_id: "plex_activity"
      state_topic: "homeassistant/tautulli/status"
      value_template: "{{ value_json.subject }}"

    - name: "Plex Status"
      unique_id: "plex_status"
      state_topic: "homeassistant/tautulli/status"
      value_template: "{{ value_json.body }}"

    - name: "Plex Player"
      unique_id: "plex_player"
      state_topic: "homeassistant/tautulli/player"
      value_template: "{{ value_json.subject }}"

    - name: "Plex Player Device"
      unique_id: "plex_player_device"
      state_topic: "homeassistant/tautulli/player"
      value_template: "{{ value_json.body }}"

    - name: "Plex Playing Episode"
      unique_id: "plex_episode"
      state_topic: "homeassistant/tautulli/episode"
      value_template: "{{ value_json.body }}"

    - name: "Plex Playing Episode Name"
      unique_id: "plex_episode_name"
      state_topic: "homeassistant/tautulli/episode"
      value_template: "{{ value_json.subject }}"

    - name: "Plex Playing Progress"
      unique_id: "plex_progress"
      state_topic: "homeassistant/tautulli/activity"
      value_template: "{{ value_json.subject }}"

    - name: "Plex Resolution"
      unique_id: "plex_resolution"
      state_topic: "homeassistant/tautulli/activity"
      value_template: "{{ value_json.body }}"

On the Tautulli side, configure an MQTT Notification Agent to publish simple JSON:

{
  "subject": "Jury Duty - Deliberations",
  "body": "https://your-plex-image-proxy.example/pms_image_proxy?img=/library/metadata/13289/thumb/1780883689"
}

Depending on the topic, subject and body mean different things. The image topic uses subject as the title and body as the poster URL. The activity topic uses subject as progress and body as a stream-quality string such as 1080p -> Direct Play -> 1080.

After reloading MQTT or restarting Home Assistant, check Developer Tools. These entities should have data:

sensor.plex_media_playing
sensor.plex_media_name
sensor.plex_status
sensor.plex_playing_progress
sensor.plex_resolution

If sensor.plex_media_playing returns an image URL, the poster part is working.

Part 2: Create the Current Playing card

This card uses custom:button-card because it needs a flexible layout: poster on the left, text on the right, and a small HTML progress bar.

type: heading
heading: Current Playing
heading_style: title
icon: mdi:disc-player

The main card goes right under the heading:

type: custom:button-card
custom_fields:
  picture:
    card:
      type: custom:button-card
      show_entity_picture: true
      entity_picture: |
        [[[
          return states['sensor.plex_media_playing'].state
        ]]]
      card_mod:
        style: |
          ha-card {
            box-shadow: none;
            border: none;
            padding: 0;
            margin: 0;
            width: 100px;
            height: 100px;
            border-radius: 12px;
            overflow: hidden;
          }
          img#icon {
            width: auto !important;
            height: 100% !important;
            object-fit: cover;
            border-radius: 12px;
          }
  intro: |
    [[[
      return states['sensor.plex_status'].state
    ]]]
  title: |
    [[[
      return states['sensor.plex_media_name'].state
    ]]]
  bar: |
    [[[
      let val = states['sensor.plex_playing_progress'].state;
      return `
        <div style="width: 100%; height: 18px; background: #2e2e2e; border-radius: 10px; overflow: hidden;">
          <div style="width: ${val}%; height: 100%; background: #e49f29; display: flex; align-items: center; justify-content: flex-end; padding-right: 8px; color: #fff; font-weight: bold; font-size: 12px;">
            ${val} %
          </div>
        </div>
      `;
    ]]]
  stream: |
    [[[
      return "Streaming at " + states['sensor.plex_resolution'].state
    ]]]
tap_action:
  action: more-info
  entity: sensor.plex_activity
styles:
  card:
    - height: auto
    - padding: 12px
    - border-radius: 16px
  custom_fields:
    picture:
      - grid-area: picture
      - margin-right: 12px
    intro:
      - text-align: left
      - font-size: 14px
      - font-weight: bold
      - color: "#e49f29"
    title:
      - text-align: left
      - font-size: 16px
      - margin-top: 4px
      - margin-bottom: 4px
    bar:
      - margin-top: 6px
      - margin-bottom: 6px
    stream:
      - text-align: left
      - font-size: 12px
      - color: "#cccccc"
  grid:
    - grid-template-areas: |
        "picture intro intro"
        "picture title title"
        "picture bar bar"
        "picture stream stream"
    - grid-template-columns: 100px 1fr
    - grid-template-rows: auto

The key part is custom_fields.picture.card.entity_picture. It reads the poster URL directly from sensor.plex_media_playing, so the poster changes with the current or most recent playback item.

The progress bar does not need a separate card. As long as sensor.plex_playing_progress is a percentage number, button-card can render a small HTML bar.

Part 3: Get upcoming episodes from Sonarr

To show items such as Widow's Bay, Cape Fear, and Star City, I use this sensor:

sensor.tv_sonarr_upcoming_media

This sensor comes from the custom integration Sonarr Upcoming Media. After installing it through HACS, configure the Sonarr URL and API key. The integration reads Sonarr's calendar and creates data that works with upcoming-media-card.

When the sensor works, its state is usually Online, while the data attribute contains upcoming episodes. Each item has fields like:

{
  "title": "FROM",
  "episode": "Heavy Is the Head",
  "number": "S04E08",
  "airdate": "2026-06-15T01:00:00Z",
  "rating": "★ 7.8",
  "genres": "Drama, Horror, Mystery"
}

Part 4: Display Upcoming Media Card

The Lovelace config for the upcoming schedule is compact:

type: custom:upcoming-media-card
entity: sensor.tv_sonarr_upcoming_media
title: null
image_style: fanart
sort_by: number
enable_tooltips: true
hide_empty: true
title_text: "$title"
line1_text: "$episode"
line2_text: "$number"
line3_text: "$day, $date $time"
line4_text: "$empty"
max: 5

A few useful options:

  • image_style: fanart: uses wide images, which fit a dashboard column well.
  • hide_empty: true: hides the card if there are no upcoming episodes.
  • max: 5: shows only the nearest five episodes so the dashboard does not get too long.
  • line1_text, line2_text, line3_text: control the text shown over the poster.

If you also want upcoming movies from Radarr, the flow is similar: install Radarr Upcoming Media, create a separate sensor, then add another custom:upcoming-media-card pointing to the Radarr sensor.

Suggested dashboard layout

In my dashboard, the media section sits in its own column:

  1. Current Playing heading.
  2. Main Plex current-playing card.
  3. Small media-player controls for speakers or TVs.
  4. A recent Plex/Jellyfin log card.
  5. A right column with bandwidth and upcoming-media-card.

On a tablet or wide wall panel, putting Current Playing and Upcoming side by side is easier to scan. On a phone, put Current Playing first and Upcoming Media below it because the upcoming card takes more vertical space.

Common issues

Poster does not show

Check the state of:

sensor.plex_media_playing

It must be an image URL that the Home Assistant dashboard browser can access. If the URL is an internal Plex address with a token or a private HTTPS domain, check certificate and access from the device opening the dashboard.

Progress bar does not move

Check sensor.plex_playing_progress. This sensor should be a number from 0 to 100. If Tautulli sends a string with %, clean the payload or template so only the number remains.

Upcoming card has no data

Check:

sensor.tv_sonarr_upcoming_media

If the state is not Online, check the Sonarr URL, API key, and Sonarr calendar. If the state is Online but the card is empty, inspect whether the data attribute has any item.

Card says custom element does not exist

This usually means a HACS resource is missing. Check Home Assistant:

Settings -> Dashboards -> Resources

You need resources for:

  • button-card
  • card-mod
  • upcoming-media-card

Conclusion

This setup does not turn Home Assistant into a media server. Plex, Tautulli, and Sonarr still do their own jobs. Home Assistant only collects their states into a readable dashboard.

But on a tablet or wall control panel, the experience feels very different: you can see what is playing, playback progress, the device being used, and which TV episodes are coming this week. For a homelab media server, this is one of the most satisfying dashboard cards to build.

Bạn thấy bài viết hữu ích?

Đăng ký để nhận thông báo khi có bài viết mới.

Kiểm tra hộp thư để xác nhận email!
Bạn đã đăng ký thành công vào Geek Playground
Tuyệt vời! Tiếp theo, hoàn tất thanh toán để có quyền truy cập đầy đủ vào Geek Playground
Chào mừng trở lại! Bạn đã đăng nhập thành công.
Thành công! Tài khoản của bạn đã được kích hoạt đầy đủ, bạn hiện có quyền truy cập vào tất cả nội dung.
Thành công! Thông tin thanh toán của bạn đã được cập nhật.
Cập nhật thông tin thanh toán không thành công.