mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 18:57:06 +00:00
Improve denon media_player (#4836)
* Add debug level logging of messages in denon * Added media stop for Denon AVR Media Player * Sort source list * Rework input selection for Denon AVR This reworks the input selection, adding more modes and making it so that the media controls are only announced in modes where they actually makes sense. * Added real media info for Denon AVR Media modes * Read more configuration from denon devices This reads network name, and overrides the local name with that. This also reads the source names and reconfigures the input list to those names, and also reads the source deleted list and removes the inputs that are set to deleted in the device. * Discover and handle max volume in Denon media player * Rework source discovery in Denon media player This uses SSFUN as authorative source for which sources that we should present.
This commit is contained in:
parent
b156ae7812
commit
04aa4e898a
@ -13,7 +13,7 @@ from homeassistant.components.media_player import (
|
||||
PLATFORM_SCHEMA, SUPPORT_NEXT_TRACK, SUPPORT_SELECT_SOURCE,
|
||||
SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_TURN_OFF,
|
||||
SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
|
||||
MediaPlayerDevice)
|
||||
SUPPORT_STOP, MediaPlayerDevice)
|
||||
from homeassistant.const import (
|
||||
CONF_HOST, CONF_NAME, STATE_OFF, STATE_ON, STATE_UNKNOWN)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@ -22,16 +22,32 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = 'Music station'
|
||||
|
||||
SUPPORT_DENON = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | \
|
||||
SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK | \
|
||||
SUPPORT_SELECT_SOURCE | SUPPORT_NEXT_TRACK | \
|
||||
SUPPORT_TURN_ON | SUPPORT_TURN_OFF
|
||||
SUPPORT_DENON = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
|
||||
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE \
|
||||
|
||||
SUPPORT_MEDIA_MODES = SUPPORT_PAUSE | SUPPORT_STOP | \
|
||||
SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
})
|
||||
|
||||
NORMAL_INPUTS = {'Cd': 'CD', 'Dvd': 'DVD', 'Blue ray': 'BD', 'TV': 'TV',
|
||||
'Satelite / Cable': 'SAT/CBL', 'Game': 'GAME',
|
||||
'Game2': 'GAME2', 'Video Aux': 'V.AUX', 'Dock': 'DOCK'}
|
||||
|
||||
MEDIA_MODES = {'Tuner': 'TUNER', 'Media server': 'SERVER',
|
||||
'Ipod dock': 'IPOD', 'Net/USB': 'NET/USB',
|
||||
'Rapsody': 'RHAPSODY', 'Napster': 'NAPSTER',
|
||||
'Pandora': 'PANDORA', 'LastFM': 'LASTFM',
|
||||
'Flickr': 'FLICKR', 'Favorites': 'FAVORITES',
|
||||
'Internet Radio': 'IRADIO', 'USB/IPOD': 'USB/IPOD'}
|
||||
|
||||
# Sub-modes of 'NET/USB'
|
||||
# {'USB': 'USB', 'iPod Direct': 'IPD', 'Internet Radio': 'IRP',
|
||||
# 'Favorites': 'FVP'}
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Denon platform."""
|
||||
@ -53,14 +69,39 @@ class DenonDevice(MediaPlayerDevice):
|
||||
self._host = host
|
||||
self._pwstate = 'PWSTANDBY'
|
||||
self._volume = 0
|
||||
self._source_list = {'TV': 'SITV', 'Tuner': 'SITUNER',
|
||||
'Internet Radio': 'SIIRP', 'Favorites': 'SIFVP'}
|
||||
# Initial value 60dB, changed if we get a MVMAX
|
||||
self._volume_max = 60
|
||||
self._source_list = NORMAL_INPUTS.copy()
|
||||
self._source_list.update(MEDIA_MODES)
|
||||
self._muted = False
|
||||
self._mediasource = ''
|
||||
self._mediainfo = ''
|
||||
|
||||
self._should_setup_sources = True
|
||||
|
||||
def _setup_sources(self, telnet):
|
||||
# NSFRN - Network name
|
||||
self._name = self.telnet_request(telnet, 'NSFRN ?')[len('NSFRN '):]
|
||||
|
||||
# SSFUN - Configured sources with names
|
||||
self._source_list = {}
|
||||
for line in self.telnet_request(telnet, 'SSFUN ?', all_lines=True):
|
||||
source, configured_name = line[len('SSFUN'):].split(" ", 1)
|
||||
self._source_list[configured_name] = source
|
||||
|
||||
# SSSOD - Deleted sources
|
||||
for line in self.telnet_request(telnet, 'SSSOD ?', all_lines=True):
|
||||
source, status = line[len('SSSOD'):].split(" ", 1)
|
||||
if status == 'DEL':
|
||||
for pretty_name, name in self._source_list.items():
|
||||
if source == name:
|
||||
del self._source_list[pretty_name]
|
||||
break
|
||||
|
||||
@classmethod
|
||||
def telnet_request(cls, telnet, command):
|
||||
def telnet_request(cls, telnet, command, all_lines=False):
|
||||
"""Execute `command` and return the response."""
|
||||
_LOGGER.debug('Sending: "%s"', command)
|
||||
telnet.write(command.encode('ASCII') + b'\r')
|
||||
lines = []
|
||||
while True:
|
||||
@ -68,12 +109,16 @@ class DenonDevice(MediaPlayerDevice):
|
||||
if not line:
|
||||
break
|
||||
lines.append(line.decode('ASCII').strip())
|
||||
_LOGGER.debug('Recived: "%s"', line)
|
||||
|
||||
if all_lines:
|
||||
return lines
|
||||
return lines[0]
|
||||
|
||||
def telnet_command(self, command):
|
||||
"""Establish a telnet connection and sends `command`."""
|
||||
telnet = telnetlib.Telnet(self._host)
|
||||
_LOGGER.debug('Sending: "%s"', command)
|
||||
telnet.write(command.encode('ASCII') + b'\r')
|
||||
telnet.read_very_eager() # skip response
|
||||
telnet.close()
|
||||
@ -85,12 +130,30 @@ class DenonDevice(MediaPlayerDevice):
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
if self._should_setup_sources:
|
||||
self._setup_sources(telnet)
|
||||
self._should_setup_sources = False
|
||||
|
||||
self._pwstate = self.telnet_request(telnet, 'PW?')
|
||||
volume_str = self.telnet_request(telnet, 'MV?')[len('MV'):]
|
||||
self._volume = int(volume_str) / 60
|
||||
for line in self.telnet_request(telnet, 'MV?', all_lines=True):
|
||||
if line.startswith('MVMAX '):
|
||||
# only grab two digit max, don't care about any half digit
|
||||
self._volume_max = int(line[len('MVMAX '):len('MVMAX XX')])
|
||||
continue
|
||||
if line.startswith('MV'):
|
||||
self._volume = int(line[len('MV'):])
|
||||
self._muted = (self.telnet_request(telnet, 'MU?') == 'MUON')
|
||||
self._mediasource = self.telnet_request(telnet, 'SI?')[len('SI'):]
|
||||
|
||||
if self._mediasource in MEDIA_MODES.values():
|
||||
self._mediainfo = ""
|
||||
answer_codes = ["NSE0", "NSE1X", "NSE2X", "NSE3X", "NSE4", "NSE5",
|
||||
"NSE6", "NSE7", "NSE8"]
|
||||
for line in self.telnet_request(telnet, 'NSE', all_lines=True):
|
||||
self._mediainfo += line[len(answer_codes.pop()):] + '\n'
|
||||
else:
|
||||
self._mediainfo = self.source
|
||||
|
||||
telnet.close()
|
||||
return True
|
||||
|
||||
@ -112,7 +175,7 @@ class DenonDevice(MediaPlayerDevice):
|
||||
@property
|
||||
def volume_level(self):
|
||||
"""Volume level of the media player (0..1)."""
|
||||
return self._volume
|
||||
return self._volume / self._volume_max
|
||||
|
||||
@property
|
||||
def is_volume_muted(self):
|
||||
@ -122,17 +185,27 @@ class DenonDevice(MediaPlayerDevice):
|
||||
@property
|
||||
def source_list(self):
|
||||
"""List of available input sources."""
|
||||
return list(self._source_list.keys())
|
||||
return sorted(list(self._source_list.keys()))
|
||||
|
||||
@property
|
||||
def media_title(self):
|
||||
"""Current media source."""
|
||||
return self._mediasource
|
||||
"""Current media info."""
|
||||
return self._mediainfo
|
||||
|
||||
@property
|
||||
def supported_media_commands(self):
|
||||
"""Flag of media commands that are supported."""
|
||||
return SUPPORT_DENON
|
||||
if self._mediasource in MEDIA_MODES.values():
|
||||
return SUPPORT_DENON | SUPPORT_MEDIA_MODES
|
||||
else:
|
||||
return SUPPORT_DENON
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
"""Return the current input source."""
|
||||
for pretty_name, name in self._source_list.items():
|
||||
if self._mediasource == name:
|
||||
return pretty_name
|
||||
|
||||
def turn_off(self):
|
||||
"""Turn off media player."""
|
||||
@ -148,8 +221,8 @@ class DenonDevice(MediaPlayerDevice):
|
||||
|
||||
def set_volume_level(self, volume):
|
||||
"""Set volume level, range 0..1."""
|
||||
# 60dB max
|
||||
self.telnet_command('MV' + str(round(volume * 60)).zfill(2))
|
||||
self.telnet_command('MV' +
|
||||
str(round(volume * self._volume_max)).zfill(2))
|
||||
|
||||
def mute_volume(self, mute):
|
||||
"""Mute (true) or unmute (false) media player."""
|
||||
@ -163,6 +236,10 @@ class DenonDevice(MediaPlayerDevice):
|
||||
"""Pause media player."""
|
||||
self.telnet_command('NS9B')
|
||||
|
||||
def media_stop(self):
|
||||
"""Pause media player."""
|
||||
self.telnet_command('NS9C')
|
||||
|
||||
def media_next_track(self):
|
||||
"""Send the next track command."""
|
||||
self.telnet_command('NS9D')
|
||||
@ -177,4 +254,4 @@ class DenonDevice(MediaPlayerDevice):
|
||||
|
||||
def select_source(self, source):
|
||||
"""Select input source."""
|
||||
self.telnet_command(self._source_list.get(source))
|
||||
self.telnet_command('SI' + self._source_list.get(source))
|
||||
|
Loading…
x
Reference in New Issue
Block a user