Flatten media player(s) into a group card

Agree, the cards are ridiculously large. Players grouped in a single card is better (and show grouped players as a single player).


I also support the possibility of reducing media players to single card.

1 Like

I’m with stewface on this. I have a lot of media players in my house. Multiple sonos (which are a big grey empty card when paused), multiple chromecasts, plex, kodi etc with no way to see the grouping except by name on the main view.

As you can see, those paused Sonos devices take up quite a lot of room…


Same here I got about 15 media players and although it looks pretty cool that you can see what is playing on all the various chromecasts it doesn’t add an automation purpose.
I now have them all in a separate tab / page so it doesn’t take the space from the stuff I like to see. However if I want to see it it is more than fits on a page and not relevant info in most cases.

So an option to show as one line and the rest if you click on the line is preferred in my case…


An option, albeit a very manual one, – You could create custom sensors using the attributes from your media_players:

  - platform: template
        value_template: '{{ states.media_player.playbar.attributes.media_album_name }}'
        friendly_name: 'Album Title'
        unit_of_measurement: 'Album'
        value_template: '{{ states.media_player.playbar.attributes.volume_level | int * 100 }}%'
        friendly_name: 'Volume'
        unit_of_measurement: 'Volume'

and then add these custom sensors to a group. Then a group of groups to compact it to look more similar to the screenshots, maybe? Haven’t tested it but I think it should work.

Still not as pretty as the older screenshots but might give you what you want.


We should have an option for display size or have a single card with a drop down that can select different players


Flattening makes total sense, particularly for Sonos players which themselves can be dynamically grouped. Our players are almost exclusively always playing the same thing and so are in effect already a single player.

A small compromise would be to make the current media player window collapsible, say where it would close the album cover art. That would somewhat address the real estate issue.


I think that the mediaplayer is good looking but i agree, it takes up too much space. And i really want to att some of my mediaplayers to different cards together with other switches or input selects as i use for webradio.

EDIT. No need this change the way it looks today but add options and ability to move into cards.

1 Like

custom_ui does give some flexibility in terms what what exactly we want to see on the UI.
While waiting for zone2 support for media_player, i have the below DIY card for my basic usage.

Thanks @kylerw for the pointers above and here!


Awesome!!! Would love to see your code. How is the responsiveness? That’s one piece I haven’t had time to fix with my Custom Fan UI.

Well, receiver response is usually slow, and didn’t notice any lag compared to the media_player component. Also tried your buttons with light, which is very responsive – see here

My plan is to add all media_players to a generic media_player per room. So that there is 1 ‘full’ card per room. This way I will have as many cards as I have rooms on my Media view, and I can include them separately on my ‘per room’ views.

At least I figured this is the most elegant / functional solution. Having 5 media players (in the same room) show up doesn’t make sense, since there would only 1 be playing while the rest 'd be idle.


Can you share your code please as that looks awesome and just what I need!

Sure, here it is:

First i have a custom_component that acts as a sensor for the source and volume_levels.

import time, math, requests
import voluptuous as vol

from xml.etree import ElementTree

from homeassistant.components.media_player import (PLATFORM_SCHEMA, MediaPlayerDevice)
from homeassistant.const import (CONF_HOST, CONF_PORT)
import homeassistant.helpers.config_validation as cv

    vol.Required(CONF_HOST): cv.string,
    vol.Required(CONF_PORT): cv.string,

def setup_platform(hass, config, add_devices, discovery_info=None):

    denonxml = DenonSensor(config.get(CONF_HOST), config.get(CONF_PORT))

class DenonSensor(MediaPlayerDevice):

    def __init__(self, host, port):
        self._state = "off"
        self._host  = host
        self._port  = port
        self._name  = "Denon " + port

    def name(self):
        return "Denon Zone" + self._port

    def state(self):
        return self._state

    def source(self):
        return self._mediasource 

    def volume_level(self):
        return self._volume 

    def getZon(self):
        if self._port == "2":
            return "Z2"
            return "ZM"

    def turn_off(self):
        url = "http://" + self._host + "/goform/formiPhoneAppDirect.xml?" + self.getZon() + "OFF"
    def turn_on(self):
        url = "http://" + self._host + "/goform/formiPhoneAppDirect.xml?" + self.getZon() + "ON"
    def update(self):

        url = "http://" + self._host + "/goform/formMainZone_MainZoneXml.xml?ZoneName=zone" + self._port
        response = requests.get(url)
        root = ElementTree.fromstring(response.content)        

        on_off = root.find('ZonePower').find('value').text
        src    = root.find('InputFuncSelect').find('value').text
        volume = root.find('MasterVolume').find('value').text

        self._state = on_off.lower()
        self._mediasource = src
        self._volume = math.ceil(float(volume)) + 80

Next, comes the custom_ui

<dom-module id="state-card-custom_source">
    <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
      :host {
        line-height: 1.5;
      ha-entity-toggle {
        margin-left: 55px;
      paper-button.nml {
        min-width: 35px;
      paper-button.sel {
        min-width: 35px;
        color: #03a9f4;
        font-weight: 800;
      paper-slider {
        margin: 4px 0;
        max-width: 100%;
        min-width: 100px;
        width: var(--ha-paper-slider-width, 200px);

    <div class='horizontal justified layout'>
      <state-info state-obj="[[stateObj]]"></state-info>
      <paper-slider min="10" max="70" value="[[vol_num]]" pin on-change="volChanged" on-tap="stopPropagation"></paper-slider>
    <div class='horizontal justified layout'>
      <ha-entity-toggle state-obj="[[stateObj]]" hass="[[hass]]"></ha-entity-toggle>
        <template is='dom-if' if='[[_showControl(stateObj,"1")]]'>
          <paper-button class$='[[_clsbtn(stateObj,"Game")]]' on-tap="btntap11">XBOX</paper-button>
          <paper-button class$='[[_clsbtn(stateObj,"CBL/SAT")]]' on-tap="btntap12">TATASKY</paper-button>
          <paper-button class$='[[_clsbtn(stateObj,"Media Player")]]' on-tap="btntap13">KODI</paper-button>

        <template is='dom-if' if='[[_showControl(stateObj,"2")]]'>
          <paper-button class$='[[_clsbtn(stateObj,"Online Music")]]' on-tap="btntap21">AIRPLAY</paper-button>
          <paper-button class$='[[_clsbtn(stateObj,"AUX")]]' on-tap="btntap22">KODI</paper-button>

  is: 'state-card-custom_source',
  properties: {
    hass: {
      type: Object,
    stateObj: {
      type: Object,
    vol_num: {
      type: String,
      computed: 'computeVol(stateObj)',

  _showControl: function (stateObj,zone) {
    return (this.stateObj.entity_id.slice(-1) === zone);

  _clsbtn: function (stateObj,btn_src) {
    if (btn_src === this.stateObj.attributes.source)
      return "sel";
      return "nml";

  computeVol: function(stateObj) {
    return stateObj.attributes.volume_level;

  btntap11: function (ev) {
    this.setSource('SIGAME', ev);
  btntap12: function (ev) {
    this.setSource('SISAT/CBL', ev);
  btntap13: function (ev) {
    this.setSource('SIMPLAY', ev);
  btntap21: function (ev) {
    this.setSource('Z2NET', ev);
  btntap22: function (ev) {
    this.setSource('Z2AUX1', ev);
  setSource(src, ev) {
    var serviceData = {source: src} || {};
    this.hass.callService('shell_command', 'set_denon_source', serviceData);
  volChanged: function(ev) {
    var serviceData = {zone: this.stateObj.entity_id.slice(-1), volume: ev.target.value} || {};
    this.hass.callService('shell_command', 'set_denon_volume', serviceData);

  stopPropagation: function (ev) {

Finally, configuration.yaml

  - platform: denonxml
    port: 1           
    scan_interval: 5  
  - platform: denonxml
    port: 2           
    scan_interval: 5
  set_denon_volume: '/usr/bin/curl -X GET{{ zone }}+{{ volume | float - 80 }}'
  set_denon_source: '/usr/bin/curl -X GET{{ source }}'
    name: Denon AVR
      - switch.denon_zone1
      - switch.denon_zone2
      friendly_name: Living Room
      icon: mdi:video
      custom_ui_state_card: custom_source
      friendly_name: BedRoom
      icon: mdi:volume-high
      custom_ui_state_card: custom_source

And you should get:



Bit late reply but didn’t found time to do something with it but today I had and got the same card as you have now. Just need to customize it so it reflect my Denon config :slight_smile:

1 Like

i have 6 sonos, and my Homescreen looks very bad with this extremly large covers in HASS.IO . Want the old group behavior back, but wont downgrade to HA again :frowning:

1 Like

Hass.io and HA are the same HomeAssistant, only the operating system is different. So this is not a Hass.io thing.

I do like to see this feature however!

Yeah that’s a much nicer view, the new way just takes up too much room, especially on a mobile device.

Would love to see this return, Hate having my media players all over the place!