Flatten media player(s) into a group card

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.

2 Likes

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.
custom_components/switch/denonxml.py

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

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    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))
    add_devices([denonxml])

class DenonSensor(MediaPlayerDevice):

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

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

    @property
    def state(self):
        return self._state

    @property
    def source(self):
        return self._mediasource 

    @property
    def volume_level(self):
        return self._volume 

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

    def turn_off(self):
        url = "http://" + self._host + "/goform/formiPhoneAppDirect.xml?" + self.getZon() + "OFF"
        requests.get(url)
        time.sleep(4)
 
    def turn_on(self):
        url = "http://" + self._host + "/goform/formiPhoneAppDirect.xml?" + self.getZon() + "ON"
        requests.get(url)
        time.sleep(4)
    
    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
custom_ui/state-card-custom_source.html

<dom-module id="state-card-custom_source">
  <template>
    <style is="custom-style" include="iron-flex iron-flex-alignment"></style>
    <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);
      }
    </style>

    <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>
    <div class='horizontal justified layout'>
      <ha-entity-toggle state-obj="[[stateObj]]" hass="[[hass]]"></ha-entity-toggle>
      <paper-button-group>
        <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>

        <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>
        </template>
      </paper-button-group>
    </div>
  </template>
</dom-module>

<script>
Polymer({
  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";
    else
      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);
    ev.stopPropagation();
  },
  
  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) {
    ev.stopPropagation();
  },
    
});
</script>

Finally, configuration.yaml

switch:               
  - platform: denonxml
    host: 192.168.1.136
    port: 1           
    scan_interval: 5  
  - platform: denonxml
    host: 192.168.1.136
    port: 2           
    scan_interval: 5
shell_command:                                  
  set_denon_volume: '/usr/bin/curl -X GET http://192.168.1.136/goform/formiPhoneAppVolume.xml?{{ zone }}+{{ volume | float - 80 }}'
  set_denon_source: '/usr/bin/curl -X GET http://192.168.1.136/goform/formiPhoneAppDirect.xml?{{ source }}'
group:
  denon:
    name: Denon AVR
    entities:
      - switch.denon_zone1
      - switch.denon_zone2
  
  customize:        
    switch.denon_zone1:
      friendly_name: Living Room
      icon: mdi:video
      custom_ui_state_card: custom_source
    switch.denon_zone2:
      friendly_name: BedRoom
      icon: mdi:volume-high
      custom_ui_state_card: custom_source

And you should get:

7 Likes

Cheers

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!

yes, me too…

Same here. The default page is pretty much cluttered up with my sonos devices :frowning:
The old small media player layout would look much nicer imho.

Would be cool to have an option (in the customize.yaml?) to set the media player layout.

2 Likes

Agree. Seems to me that it would be possible to switch view based on yaml.

If you just want to save space, you can do so by removing the picture.

homeassistant:
  customize:
    media_player.living_room:
      entity_picture: ''
``
5 Likes

Thank you very much for that tip!
But unfortunately media player can still not be added to group state cards with this trick. :frowning:
And they still do not look as nice as with the old design imho :wink:

What is the goal? A state card with media control buttons? There is no such UI implemented.

A state card that shows the state of a player? Create a template sensor or a template switch.

I figured out a slight work around that works for me (buy may not for you). I can lean toward the minimalist side so I like to keep my main page in HA as slim as possible.

I have 5 chromecast devices around my home and I really only care about them if they are playing something. So I separated them all out into their own groups, added them to my main page and then run an automation to hide and un-hide them when they play things.

The Group:

	  plankton_main:
	    entities:
	      - media_player.plankton

The Automation:

	# Plankton Visable
	- alias: plankton visable true

	  trigger:
	    platform: template
	    value_template: "{{ not is_state('media_player.plankton', 'off') }}"

	  action:
	    service: group.set_visibility
	    entity_id: group.plankton_main
	    data:
	      visible: True

	- alias: plankton visable false

	  trigger:
	    - platform: homeassistant
	      event: start
	    - platform: state
	      entity_id: media_player.plankton
	      to: 'off'

	  action:
	    service: group.set_visibility
	    entity_id: group.plankton_main
	    data:
	      visible: False
6 Likes

My goal is the possibility to add media players to other state cards and show there actual status
(if possible with Album Covers/Images, like in the old way to show media players)

Sorry if this is maybe a stupid question, but I’m completly new to this templated sensor stuff.
Can you please give me a hint, what would be the correct code, if I just want the actual state (off, paused, playing…) from a media_player, as a templated sensor?

Thanks in advance for you help :slight_smile:

I have now found a way that is quite ok for me. For a quick overview in the default_view, I created templated sensors that reflect the current state of the respective mediaplayer.

  - platform: template
    sensors:
      sonos_wohnzimmer_state:
        value_template: '{{ states("media_player.sonos_wohnzimmer") }}'
        friendly_name: 'Sonos Wohnzimmer'

For a detailed view, I then created a separate view, in which I have moved all media player entities.

This is still not as nice, comfortable and informative as the old mediaplayer representation, but at least the default_view is no longer so cluttered.

BTW: Does anyone know how I can change the color of the templated sensors similar to that of lamps? (yellow if a media player is currently playing and grey for all other states).

3 Likes

Hi.

How did you put in the custom icon the the media-sensor like Chromecast? Normally i have no issues with this kind of changes, but my sensores jut won’t change - i just got an blank icon, if i define what kind of icon i would use.

Could you please share that part, too?

I did try with both customize and the icon_template, with no luck inspired of the home-assistant page:

 media_state_tv:
    value_template: '{{ states("media_player.samsung_tv_remote") }}'
    icon_template: >
      {% if is_state('media_player.samsung_tv_remote', 'on') %}
        mdi:cast
      {% elif is_state('media_player.samsung_tv_remote', 'unknown') %}
        mdi:tv
      {% else %}
        mdi:tv
      {% endif %}

And the customize (in one of my packages):

homeassistant:
customize:
sensor.media_state_tv:
friendly_name: “Tv”
icon: mid:television

The name is viewed just fine, but not the icon.