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: