Custom Component: Tautulli Active Streams

Had a quick scan thorugh but couldnt see anything similar…so here goes…

No changes on my system other than the usual system updates.

So Im on

Core 2025.85.3
Supervisor 2025.08.3
HASSOS 16.1

And recently I’ve noticed an oddity on the active stream info. as below

it starts off as usual…like this

then after a second or two, it changes to this and stays like it

I’ve not changed anything yet or reinstalled…as above, nothing has changed so wondering if anyone is seeing the same…so to clarify, all the requiredinfo is there,…theres just this big red errorbox throwing a spanner in the works

I am having the same issue on

  • Installation method Home Assistant OS
  • Core 2025.8.3
  • Supervisor 2025.08.3
  • Operating System 16.1
  • Frontend 20250811.1
1 Like

So it looks like the issue is that the Custom:Bar Card element, having been removed from HACS has broken things.

Removing that element from the config of the card element removes the config error but then you can’t see how far through the viewing is.

The original has been forked here

add it as a custom depository as the dashboard option and then it should work as expected

Hope this helps someone

All working again

1 Like

you sir, are gentleman and a scholar. Thought i was losing my mine with this and silly me was looking at the gitrepo not this conversation. so thank you

New to HA so apologies if I’m not understanding basic things. I have installed auto-entities, button-card, bar-card and card-mod from HACS, checked they’re all running in resources and added the yaml to my dashboard (I assume I do this through raw configuation editor?) Tautalli I believe is set up properly, however, nothing shows up when I play something on Plex. The sensor is showing that it’s working but the card doesn’t show up.

If anyone could help me understand what I’m doing wrong I would be appreciative.

EDIT: I have fixed it, I didn’t realise I had to add it as a card and paste the yaml there.

New problem though, the card keeps refreshing and looks like it’s wiggling? Not sure what’s causing that.

It appears to be changing to this layout before loading the card as normal

I found an issue with this new repo that causes the playing bar not clipped to the rounded corner correctly or in @jouster’s screenshot the blue bar’s corners are not rounded. Luckily it’s an easier fix, just add !important as below:

bar-card-currentbar, bar-card-backgroundbar {
  border-radius: 8px !important;
  left: 0;
}

Heya all
I’m new to HA, but love this card, however, od love to be able to replace the % text with a timer, displaying the time left instead of the % played. I think the progress bar is great for showing the percentage left.
Is there a way to change the text from % to duration left??

Thanks very much in advance.

Cheers
gskips

give this one a try. Merry Christmas!

type: custom:auto-entities
filter:
  exclude:
    - options: {}
      state: unknown
    - options: {}
      state: unavailable
    - options: {}
      state: "off"
    - options: {}
      state: "on"
  include:
    - entity_id: "*plex_session_*"
      options:
        type: custom:button-card
        tap_action:
          action: none
        variables:
          entity: this.entity_id
        custom_fields:
          picture:
            card:
              type: picture
              image: |
                [[[
                  return states[variables.entity].attributes.image_url
                    ? states[variables.entity].attributes.image_url
                    : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAHCCAIAAADaUWPQAAABoElEQVR4nO3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgL8BMCQAAX/csOsAAAAASUVORK5CYII=";
                ]]]
              card_mod:
                style: |
                  ha-card {
                    box-shadow: 0;
                    border-radius: 5px;
                    margin: 1px 3px -4px 3px;
                  }
                  ha-card img {
                    min-height: 80px;
                    min-width: 80px;
                  }
          bar: |
            [[[
              const s = states[variables.entity];
              if (!s) return '';

              const st = (s.state || '').toLowerCase();
              const pct = Number(s.attributes.progress_percent ?? 0);

              // Remaining formatting: 0:45:50 -> 45:50
              let remaining = s.attributes.stream_remaining ?? '';
              if (typeof remaining === 'string') {
                remaining = remaining.trim();
                if (/^0:\d{2}:\d{2}$/.test(remaining)) remaining = remaining.slice(2);
              }

              // Paused duration (Tautulli gives "0m 0s" style)
              const pausedDur = (s.attributes.stream_paused_duration ?? '').toString().trim();

              // LEFT label
              let left = s.state ?? '';
              if (st === 'playing') {
                left = `Playing - ${isFinite(pct) ? Math.round(pct) : pct}%`;
              } else if (st === 'paused') {
                left = pausedDur ? `Paused - ${pausedDur}` : 'Paused';
              } else {
                // fallback for buffering/idle/etc
                left = `${s.state ?? ''}${isFinite(pct) ? ` - ${Math.round(pct)}%` : ''}`;
              }

              // RIGHT label (remaining)
              const right = remaining ? `Remaining: ${remaining}` : '';

              const cls = (st === 'paused') ? 'ddmbar paused' : 'ddmbar';

              return `
                <div class="${cls}">
                  <div class="fill" style="width:${Math.max(0, Math.min(100, pct))}%;"></div>
                  <div class="labels">
                    <div class="l">${left}</div>
                    <div class="r">${right}</div>
                  </div>
                </div>
              `;
            ]]]
          user: |
            [[[
              return "<b>" + (states[variables.entity].attributes.user ?? '') + "</b>";
            ]]]
          title: |
            [[[
              const s = states[variables.entity];
              const full = s.attributes.full_title ?? '';
              if (s.state == 'playing') {
                return "<ha-icon icon='mdi:play' style='width: 15px; height: 15px; position: relative; top: -2px;'></ha-icon> " + full;
              } else if (s.state == 'paused') {
                return "<ha-icon icon='mdi:pause' style='width: 15px; height: 15px; position: relative; top: -2px;'></ha-icon> " + full;
              }
              return full;
            ]]]
          stream: |
            [[[
              const a = states[variables.entity].attributes;
              return (a.transcode_decision ?? '') + " - " + (a.stream_video_resolution ?? '');
            ]]]
          product: |
            [[[
              const a = states[variables.entity].attributes;
              const player = a.player ?? '';
              const product = a.product ?? '';
              return product + ' - ' + '<i>' + player + '</i>';
            ]]]
          media_detail: |
            [[[
              const a = states[variables.entity].attributes;
              if (a.media_type == 'movie') {
                return "<ha-icon icon='mdi:filmstrip' style='width: 15px; height: 15px; position: relative; top: -2px;'></ha-icon> (" + (a.year ?? '') + ")";
              }
              return "<ha-icon icon='mdi:television-classic' style='width: 15px; height: 15px; position: relative; top: -2px;'></ha-icon> S" + (a.parent_media_index ?? '') + " • E" + (a.media_index ?? '');
            ]]]
          bandwidth: |
            [[[
              const bw = Number(states[variables.entity].attributes.bandwidth ?? 0) * 1000;
              const sizes = ['Bytes', 'Kbps', 'Mbps', 'Gbps', 'Tbps'];
              if (!bw) return 'Bandwidth: n/a';
              const i = parseInt(Math.floor(Math.log(bw) / Math.log(1000)));
              if (i === 0) return 'Bandwidth: ' + bw + ' ' + sizes[i];
              return 'Bandwidth: ' + (bw / Math.pow(1000, i)).toFixed(1) + ' ' + sizes[i];
            ]]]
        card_mod:
          style: |
            ha-card {
              padding: 0;
              margin: 0;
              border: 0.01px solid rgba(70, 130, 180, 0.5);
              box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.5);
              background: rgba(0.40, 1, 0, 0.5) !important;
            }
            ha-card #container {
              margin: 5px 0 0 0;
            }
            #name { display: none; }

            /* ✅ Progress bar styles */
            .ddmbar {
              position: relative;
              height: 19px;
              border-radius: 8px;
              overflow: hidden;
              background: rgba(0,0,0,0.55);
              border: 0.02px solid rgba(70, 130, 180, 0.3);
            }
            .ddmbar .fill {
              position: absolute;
              left: 0;
              top: 0;
              bottom: 0;
              border-radius: 8px;
              background: rgba(41, 134, 204, 0.95); /* playing color default */
            }
            /* paused color + blink */
            .ddmbar.paused .fill {
              background: rgba(228, 159, 41, 0.95);
              animation: ddmblink 1.5s linear infinite;
            }
            @keyframes ddmblink {
              50% { opacity: 0; }
            }
            .ddmbar .labels {
              position: absolute;
              inset: 0;
              display: flex;
              align-items: center;
              justify-content: space-between;
              padding: 0 8px;
              pointer-events: none;
              font-size: 11px;
              letter-spacing: 0.4px;
              opacity: 0.9;
            }

            .ddmbar .labels .l {
              text-transform: capitalize;
              font-weight: 500;
            }

            .ddmbar .labels .r {
              font-weight: 400;
              font-size: 9.5px;
              opacity: 0.75;
            }
        styles:
          card:
            - height: 100x
            - padding: 0
          custom_fields:
            bar:
              - padding-top: 2px
              - padding-bottom: 0px
            user:
              - text-transform: capitalize
              - text-align: end
              - font-size: 12px
              - font-family: Arial, sans-serif
              - font-style: italic
              - letter-spacing: 2px
              - margin-left: "-60px;"
            title:
              - text-transform: capitalize
              - text-align: start
              - font-size: 26px
              - margin-top: "-5px"
              - margin-bottom: 2px
            stream:
              - text-transform: capitalize
              - text-align: start
              - font-size: 12px
            product:
              - text-transform: capitalize
              - text-align: start
              - font-size: 12px
            media_detail:
              - text-transform: uppercase
              - text-align: start
              - font-size: 15px
            bandwidth:
              - text-transform: capitalize
              - text-align: end
              - font-size: 12px
              - margin-left: "-60px;"
          grid:
            - grid-template-areas: |
                "picture product user"
                "picture title title"
                "picture media_detail media_detail"
                "picture bar bar"
                "picture stream bandwidth"
            - grid-template-columns: 1fr 200px 3fr
            - grid-gap: 5px 3px
card:
  type: vertical-stack
card_param: cards
2 Likes

Sorry late getting to this… but THANK YOU sooo much… this is genius and exactly what I was looking for… really appreciated. Happy New Year!!

Though not a big deal, but, what is it that controls the update of this? It seems to do it every 10 secs or so… is there an easy way of increasing or reducing this update time… as I say, not a big deal, just wondering.

Thanks again!!!

the card update would be from the integration,
the active streams “api update frequency” this is how often you will get updates.
i have mine set at every 3 seconds,

1 Like

not sure if anyone noticed but i have updated the integrations around a week back and the movie/tv streams card in the repo and also added music support with its own card, hope you like these updates

2 Likes

I just came across this integration as I recently installed Tautulli for my Plex server. Installation of the integration itself went swimmingly, but as you say the movie/tv cards are in the repo, I’m unsure what you mean? Are there supposed to be new custom cards available for the dashboard somewhere, because I don’t see any.

Or am I just missing something?

Cheers!

Edit: Wow, I’m not bright today. Nevermind, I found it and figured it out! Thanks for this, by the way. Absolutely love this.

1 Like

Checkout the new Custom card I’ve been working on, make sure to update to the latest Tautulli_Active_Streams integration to use this

Richardvaio/tautulli-active-streams-card: Responsive Home Assistant dashboard card for Tautulli Active Streams

ANyone else been experiencing this with the 5-month old YAML card? Started with HA 2026.8.0 and been broken since. I have the latest 2.7.0 of the integration, but the card yaml in the repo is 5 months old and no longer formats correctly.

Thanks
Ross

the card yaml should work fine still, check out the new custom card i have released, this now comes with loads of options and hopefully over time this will progress

The old yaml wasn’t working, but I installed the new card via HACS and it works just fine. Thanks!

Ive been using this add in for ages now and it’s great, so thank you

But recently (after updating to the latest version) I was getting an error where the cards displayed like this for any playback

I even tried going back one or two releases but the same happens…it might be that I hadnt upgrdaed for ages before that so I could kkeep going back till i find one that works but I thiought Id mention it as the latest version definitely does it

odd thing is if I refresh the page, it displays as it should (and always did) but stopping and starting playback again gives the same result unless I refresh…this is for playback on all devices

Any simple way to fault-find on this

if you need any more info, let me know, im currently on Version 2.7.0

have you tired the latest integration with the new release of our custom card this now comes with configurable editor and loads more features

I had but that aslo gave me these same initial issues…ill give it another go though

New card has resolved issues…so have moved over to that…thanks for the nudge

Out of interest, is there a way to set the ETA/finish time to 24 hours… haven’t found one yet…

I’ve found how to turn it off..but not change it from 12 hour format

Oddly adding in the new card seems to have also fixed the old card too…but the new card is much better