From 8437591dc19fc5cd73fce7c14a296909d8a88677 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 18 Jan 2018 12:05:12 +0100 Subject: [PATCH 01/12] Add sound mode support (media_player __init__.py) Add sound mode support as a general feature. implemented in the denonavr component --- .../components/media_player/__init__.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 89686c312bd47..f2a80d43e9dcd 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -57,6 +57,7 @@ SERVICE_PLAY_MEDIA = 'play_media' SERVICE_SELECT_SOURCE = 'select_source' +SERVICE_SELECT_SOUND_MODE = 'select_sound_mode' SERVICE_CLEAR_PLAYLIST = 'clear_playlist' ATTR_MEDIA_VOLUME_LEVEL = 'volume_level' @@ -81,6 +82,8 @@ ATTR_APP_NAME = 'app_name' ATTR_INPUT_SOURCE = 'source' ATTR_INPUT_SOURCE_LIST = 'source_list' +ATTR_SOUND_MODE = 'sound_mode' +ATTR_SOUND_MODE_LIST = 'sound_mode_list' ATTR_MEDIA_ENQUEUE = 'enqueue' ATTR_MEDIA_SHUFFLE = 'shuffle' @@ -107,6 +110,7 @@ SUPPORT_CLEAR_PLAYLIST = 8192 SUPPORT_PLAY = 16384 SUPPORT_SHUFFLE_SET = 32768 +SUPPORT_SELECT_SOUND_MODE = 65536 # Service call validation schemas MEDIA_PLAYER_SCHEMA = vol.Schema({ @@ -130,6 +134,10 @@ vol.Required(ATTR_INPUT_SOURCE): cv.string, }) +MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({ + vol.Required(ATTR_SOUND_MODE): cv.string, +}) + MEDIA_PLAYER_PLAY_MEDIA_SCHEMA = MEDIA_PLAYER_SCHEMA.extend({ vol.Required(ATTR_MEDIA_CONTENT_TYPE): cv.string, vol.Required(ATTR_MEDIA_CONTENT_ID): cv.string, @@ -165,6 +173,9 @@ SERVICE_SELECT_SOURCE: { 'method': 'async_select_source', 'schema': MEDIA_PLAYER_SELECT_SOURCE_SCHEMA}, + SERVICE_SELECT_SOUND_MODE: { + 'method': 'async_select_sound_mode', + 'schema': MEDIA_PLAYER_SELECT_SOUND_MODE_SCHEMA}, SERVICE_PLAY_MEDIA: { 'method': 'async_play_media', 'schema': MEDIA_PLAYER_PLAY_MEDIA_SCHEMA}, @@ -195,6 +206,8 @@ ATTR_APP_NAME, ATTR_INPUT_SOURCE, ATTR_INPUT_SOURCE_LIST, + ATTR_SOUND_MODE, + ATTR_SOUND_MODE_LIST, ATTR_MEDIA_SHUFFLE, ] @@ -344,6 +357,17 @@ def select_source(hass, source, entity_id=None): hass.services.call(DOMAIN, SERVICE_SELECT_SOURCE, data) +@bind_hass +def select_sound_mode(hass, sound_mode, entity_id=None): + """Send the media player the command to select sound mode.""" + data = {ATTR_SOUND_MODE: sound_mode} + + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + hass.services.call(DOMAIN, SERVICE_SELECT_SOUND_MODE, data) + + @bind_hass def clear_playlist(hass, entity_id=None): """Send the media player the command for clear playlist.""" @@ -392,6 +416,8 @@ def async_service_handler(service): params['position'] = service.data.get(ATTR_MEDIA_SEEK_POSITION) elif service.service == SERVICE_SELECT_SOURCE: params['source'] = service.data.get(ATTR_INPUT_SOURCE) + elif service.service == SERVICE_SELECT_SOUND_MODE: + params['sound_mode'] = service.data.get(ATTR_SOUND_MODE) elif service.service == SERVICE_PLAY_MEDIA: params['media_type'] = \ service.data.get(ATTR_MEDIA_CONTENT_TYPE) @@ -574,6 +600,16 @@ def source_list(self): """List of available input sources.""" return None + @property + def sound_mode(self): + """Name of the current sound mode.""" + return None + + @property + def sound_mode_list(self): + """List of available sound modes.""" + return None + @property def shuffle(self): """Boolean if shuffle is enabled.""" @@ -717,6 +753,17 @@ def async_select_source(self, source): """ return self.hass.async_add_job(self.select_source, source) + def select_sound_mode(self, sound_mode): + """Select sound mode.""" + raise NotImplementedError() + + def async_select_sound_mode(self, sound_mode): + """Select sound mode. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.async_add_job(self.select_sound_mode, sound_mode) + def clear_playlist(self): """Clear players playlist.""" raise NotImplementedError() @@ -790,6 +837,11 @@ def support_select_source(self): """Boolean if select source command supported.""" return bool(self.supported_features & SUPPORT_SELECT_SOURCE) + @property + def support_select_sound_mode(self): + """Boolean if select sound mode command supported.""" + return bool(self.supported_features & SUPPORT_SELECT_SOUND_MODE) + @property def support_clear_playlist(self): """Boolean if clear playlist command supported.""" From 11963fca9a7c0d999298702c367e605004fa8ffe Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 18 Jan 2018 12:07:24 +0100 Subject: [PATCH 02/12] add sound mode support (media_player services.yaml) Add sound mode support (media_player services.yaml) --- homeassistant/components/media_player/services.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index b2f98d378cf27..1ed6620499f01 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -130,6 +130,16 @@ select_source: description: Name of the source to switch to. Platform dependent. example: 'video1' +select_sound_mode: + description: Send the media player the command to change sound mode. + fields: + entity_id: + description: Name(s) of entities to change sound mode on. + example: 'media_player.media_player.txnr535_0009b0d81f82' + sound_mode: + description: Name of the sound mode to switch to. Platform dependent. + example: 'Music' + clear_playlist: description: Send the media player the command to clear players playlist. fields: From 75f5475ef5225199270e90a08390d64778c730ac Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 18 Jan 2018 12:09:36 +0100 Subject: [PATCH 03/12] add sound mode support (denonavr platform) Implement the sound mode support for the denonavr platform using the new general sound mode support. --- .../components/media_player/denonavr.py | 48 ++++++++++++++++--- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/media_player/denonavr.py index 0a03af0e1bf03..e6138cd6dccf3 100644 --- a/homeassistant/components/media_player/denonavr.py +++ b/homeassistant/components/media_player/denonavr.py @@ -8,13 +8,16 @@ import logging from collections import namedtuple import voluptuous as vol +import urllib +import xml.etree.ElementTree as ET from homeassistant.components.media_player import ( SUPPORT_PAUSE, SUPPORT_NEXT_TRACK, SUPPORT_PREVIOUS_TRACK, SUPPORT_TURN_OFF, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP, - SUPPORT_SELECT_SOURCE, SUPPORT_PLAY_MEDIA, MEDIA_TYPE_CHANNEL, - MediaPlayerDevice, PLATFORM_SCHEMA, SUPPORT_TURN_ON, - MEDIA_TYPE_MUSIC, SUPPORT_VOLUME_SET, SUPPORT_PLAY) + SUPPORT_SELECT_SOURCE,SUPPORT_SELECT_SOUND_MODE, + SUPPORT_PLAY_MEDIA, MEDIA_TYPE_CHANNEL, MediaPlayerDevice, + PLATFORM_SCHEMA, SUPPORT_TURN_ON, MEDIA_TYPE_MUSIC, + SUPPORT_VOLUME_SET, SUPPORT_PLAY) from homeassistant.const import ( CONF_HOST, STATE_OFF, STATE_PLAYING, STATE_PAUSED, CONF_NAME, STATE_ON, CONF_ZONE, CONF_TIMEOUT) @@ -35,7 +38,8 @@ SUPPORT_DENON = SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | \ SUPPORT_TURN_ON | SUPPORT_TURN_OFF | \ - SUPPORT_SELECT_SOURCE | SUPPORT_VOLUME_SET + SUPPORT_SELECT_SOURCE | SUPPORT_SELECT_SOUND_MODE | \ + SUPPORT_VOLUME_SET SUPPORT_MEDIA_MODES = SUPPORT_PLAY_MEDIA | \ SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | \ @@ -66,6 +70,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Initialize list with receivers to be started receivers = [] + hosts = [] cache = hass.data.get(KEY_DENON_CACHE) if cache is None: @@ -117,7 +122,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): show_all_inputs=show_all_sources, timeout=timeout, add_zones=add_zones) for new_zone in new_device.zones.values(): - receivers.append(DenonDevice(new_zone)) + receivers.append(DenonDevice(new_zone, host)) cache.add(host) _LOGGER.info("Denon receiver at host %s initialized", host) @@ -129,14 +134,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class DenonDevice(MediaPlayerDevice): """Representation of a Denon Media Player Device.""" - def __init__(self, receiver): + def __init__(self, receiver, host): """Initialize the device.""" self._receiver = receiver self._name = self._receiver.name + self._host = host self._muted = self._receiver.muted self._volume = self._receiver.volume self._current_source = self._receiver.input_func self._source_list = self._receiver.input_func_list + self._current_sound_mode = None + dict = {'MUSIC':'PLII MUSIC', 'MOVIE':'PLII MOVIE', 'GAME':'PLII GAME', 'PURE DIRECT':'DIRECT', 'AUTO':'None', 'DOLBY DIGITAL':'DOLBY DIGITAL', 'MCH STEREO':'MULTI CH STEREO', 'STEREO':'STEREO'} + self._sound_mode_list = list(dict) + self._sound_mode_dict = dict self._state = self._receiver.state self._power = self._receiver.power self._media_image_url = self._receiver.image_url @@ -165,6 +175,16 @@ def update(self): self._frequency = self._receiver.frequency self._station = self._receiver.station + url = 'http://' + str(self._host) + '/goform/formMainZone_MainZoneXml.xml' + XML_data = urllib.request.urlopen(url) + parsed_data = ET.parse(XML_data).getroot() + sound_mode_raw = parsed_data.find('selectSurround/value').text.rstrip() + try: + sound_mode = list(self._sound_mode_dict.keys())[list(self._sound_mode_dict.values()).index(sound_mode_raw.upper())] + self._current_sound_mode = sound_mode + except ValueError: + self._current_sound_mode = sound_mode_raw + @property def name(self): """Return the name of the device.""" @@ -197,6 +217,16 @@ def source_list(self): """Return a list of available input sources.""" return self._source_list + @property + def sound_mode(self): + """Return the current sound mode.""" + return self._current_sound_mode + + @property + def sound_mode_list(self): + """Return a list of available sound modes.""" + return self._sound_mode_list + @property def supported_features(self): """Flag media player features that are supported.""" @@ -292,6 +322,12 @@ def select_source(self, source): """Select input source.""" return self._receiver.set_input_func(source) + def select_sound_mode(self, sound_mode): + """Select sound mode.""" + url = 'http://' + str(self._host) + '/MainZone/index.put.asp?cmd0=PutSurroundMode%2F' + sound_mode.upper().replace(" ", "+") + urllib.request.urlopen(url) + return sound_mode + def turn_on(self): """Turn on media player.""" if self._receiver.power_on(): From 6dcafbce94c5788681d2f447c81a6d58c8c46a02 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 18 Jan 2018 17:56:12 +0100 Subject: [PATCH 04/12] Add sound mode support - improved code a bit - added the option to disable sound mode support in the configuration.yaml file - added the option to specify a custom list of sound modes in the configuration.yaml file --- .../components/media_player/denonavr.py | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/media_player/denonavr.py index e6138cd6dccf3..b7eabfed9f46a 100644 --- a/homeassistant/components/media_player/denonavr.py +++ b/homeassistant/components/media_player/denonavr.py @@ -6,7 +6,7 @@ """ import logging -from collections import namedtuple +from collections import (namedtuple, OrderedDict) import voluptuous as vol import urllib import xml.etree.ElementTree as ET @@ -30,16 +30,19 @@ DEFAULT_NAME = None DEFAULT_SHOW_SOURCES = False DEFAULT_TIMEOUT = 2 +DEFAULT_SOUND_MODE = True +DEFAULT_SOUND_MODE_DICT = OrderedDict([('MUSIC', 'PLII MUSIC'), ('MOVIE', 'PLII MOVIE'), ('GAME', 'PLII GAME'), ('PURE DIRECT', 'DIRECT'), ('AUTO', 'None'), ('DOLBY DIGITAL', 'DOLBY DIGITAL'), ('MCH STEREO', 'MULTI CH STEREO'), ('STEREO', 'STEREO')]) CONF_SHOW_ALL_SOURCES = 'show_all_sources' CONF_ZONES = 'zones' +CONF_SOUND_MODE = 'sound_mode' +CONF_SOUND_MODE_DICT = 'sound_mode_dict' CONF_VALID_ZONES = ['Zone2', 'Zone3'] CONF_INVALID_ZONES_ERR = 'Invalid Zone (expected Zone2 or Zone3)' KEY_DENON_CACHE = 'denonavr_hosts' SUPPORT_DENON = SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | \ SUPPORT_TURN_ON | SUPPORT_TURN_OFF | \ - SUPPORT_SELECT_SOURCE | SUPPORT_SELECT_SOUND_MODE | \ - SUPPORT_VOLUME_SET + SUPPORT_SELECT_SOURCE | SUPPORT_VOLUME_SET SUPPORT_MEDIA_MODES = SUPPORT_PLAY_MEDIA | \ SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | \ @@ -53,6 +56,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOST): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_SOUND_MODE, default=DEFAULT_SOUND_MODE): cv.boolean, + vol.Optional(CONF_SOUND_MODE_DICT, default=DEFAULT_SOUND_MODE_DICT): vol.Schema({str: str}), vol.Optional(CONF_SHOW_ALL_SOURCES, default=DEFAULT_SHOW_SOURCES): cv.boolean, vol.Optional(CONF_ZONES): @@ -79,6 +84,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Get config option for show_all_sources and timeout show_all_sources = config.get(CONF_SHOW_ALL_SOURCES) timeout = config.get(CONF_TIMEOUT) + sound_mode_support = config.get(CONF_SOUND_MODE) + sound_mode_dict = config.get(CONF_SOUND_MODE_DICT) # Get config option for additional zones zones = config.get(CONF_ZONES) @@ -122,7 +129,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): show_all_inputs=show_all_sources, timeout=timeout, add_zones=add_zones) for new_zone in new_device.zones.values(): - receivers.append(DenonDevice(new_zone, host)) + receivers.append(DenonDevice(new_zone, host, sound_mode_support, sound_mode_dict)) cache.add(host) _LOGGER.info("Denon receiver at host %s initialized", host) @@ -134,7 +141,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class DenonDevice(MediaPlayerDevice): """Representation of a Denon Media Player Device.""" - def __init__(self, receiver, host): + def __init__(self, receiver, host, sound_mode_support, sound_mode_dict): """Initialize the device.""" self._receiver = receiver self._name = self._receiver.name @@ -144,9 +151,9 @@ def __init__(self, receiver, host): self._current_source = self._receiver.input_func self._source_list = self._receiver.input_func_list self._current_sound_mode = None - dict = {'MUSIC':'PLII MUSIC', 'MOVIE':'PLII MOVIE', 'GAME':'PLII GAME', 'PURE DIRECT':'DIRECT', 'AUTO':'None', 'DOLBY DIGITAL':'DOLBY DIGITAL', 'MCH STEREO':'MULTI CH STEREO', 'STEREO':'STEREO'} - self._sound_mode_list = list(dict) - self._sound_mode_dict = dict + self._sound_mode_list = list(sound_mode_dict) + self._sound_mode_dict = sound_mode_dict + self._sound_mode_support = sound_mode_support self._state = self._receiver.state self._power = self._receiver.power self._media_image_url = self._receiver.image_url @@ -157,6 +164,9 @@ def __init__(self, receiver, host): self._frequency = self._receiver.frequency self._station = self._receiver.station + self._supported_features_base = SUPPORT_DENON + self._supported_features_base |= (sound_mode_support and SUPPORT_SELECT_SOUND_MODE) + def update(self): """Get the latest status information from device.""" self._receiver.update() @@ -175,15 +185,19 @@ def update(self): self._frequency = self._receiver.frequency self._station = self._receiver.station - url = 'http://' + str(self._host) + '/goform/formMainZone_MainZoneXml.xml' - XML_data = urllib.request.urlopen(url) - parsed_data = ET.parse(XML_data).getroot() - sound_mode_raw = parsed_data.find('selectSurround/value').text.rstrip() - try: - sound_mode = list(self._sound_mode_dict.keys())[list(self._sound_mode_dict.values()).index(sound_mode_raw.upper())] - self._current_sound_mode = sound_mode - except ValueError: - self._current_sound_mode = sound_mode_raw + if self._sound_mode_support: + try: + url = 'http://' + str(self._host) + '/goform/formMainZone_MainZoneXml.xml' + XML_data = urllib.request.urlopen(url) + except ConnectTimeoutError: + return + parsed_data = ET.parse(XML_data).getroot() + sound_mode_raw = parsed_data.find('selectSurround/value').text.rstrip() + try: + sound_mode = list(self._sound_mode_dict.keys())[list(self._sound_mode_dict.values()).index(sound_mode_raw.upper())] + self._current_sound_mode = sound_mode + except ValueError: + self._current_sound_mode = sound_mode_raw @property def name(self): @@ -231,8 +245,8 @@ def sound_mode_list(self): def supported_features(self): """Flag media player features that are supported.""" if self._current_source in self._receiver.netaudio_func_list: - return SUPPORT_DENON | SUPPORT_MEDIA_MODES - return SUPPORT_DENON + return self._supported_features_base | SUPPORT_MEDIA_MODES + return self._supported_features_base @property def media_content_id(self): From b4b5c25f8a3420bb421874ee044fb3d541ac5a60 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 18 Jan 2018 20:37:11 +0100 Subject: [PATCH 05/12] Added sound mode support - fixed some too long lines and other houndci-bot issues --- .../components/media_player/denonavr.py | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/media_player/denonavr.py index b7eabfed9f46a..0b576bdb6914c 100644 --- a/homeassistant/components/media_player/denonavr.py +++ b/homeassistant/components/media_player/denonavr.py @@ -14,7 +14,7 @@ from homeassistant.components.media_player import ( SUPPORT_PAUSE, SUPPORT_NEXT_TRACK, SUPPORT_PREVIOUS_TRACK, SUPPORT_TURN_OFF, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP, - SUPPORT_SELECT_SOURCE,SUPPORT_SELECT_SOUND_MODE, + SUPPORT_SELECT_SOURCE, SUPPORT_SELECT_SOUND_MODE, SUPPORT_PLAY_MEDIA, MEDIA_TYPE_CHANNEL, MediaPlayerDevice, PLATFORM_SCHEMA, SUPPORT_TURN_ON, MEDIA_TYPE_MUSIC, SUPPORT_VOLUME_SET, SUPPORT_PLAY) @@ -31,7 +31,14 @@ DEFAULT_SHOW_SOURCES = False DEFAULT_TIMEOUT = 2 DEFAULT_SOUND_MODE = True -DEFAULT_SOUND_MODE_DICT = OrderedDict([('MUSIC', 'PLII MUSIC'), ('MOVIE', 'PLII MOVIE'), ('GAME', 'PLII GAME'), ('PURE DIRECT', 'DIRECT'), ('AUTO', 'None'), ('DOLBY DIGITAL', 'DOLBY DIGITAL'), ('MCH STEREO', 'MULTI CH STEREO'), ('STEREO', 'STEREO')]) +DEFAULT_SOUND_MODE_DICT = OrderedDict([('MUSIC', 'PLII MUSIC'), + ('MOVIE', 'PLII MOVIE'), + ('GAME', 'PLII GAME'), + ('PURE DIRECT', 'DIRECT'), + ('AUTO', 'None'), + ('DOLBY DIGITAL', 'DOLBY DIGITAL'), + ('MCH STEREO', 'MULTI CH STEREO'), + ('STEREO', 'STEREO')]) CONF_SHOW_ALL_SOURCES = 'show_all_sources' CONF_ZONES = 'zones' CONF_SOUND_MODE = 'sound_mode' @@ -57,7 +64,8 @@ vol.Optional(CONF_HOST): cv.string, vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, vol.Optional(CONF_SOUND_MODE, default=DEFAULT_SOUND_MODE): cv.boolean, - vol.Optional(CONF_SOUND_MODE_DICT, default=DEFAULT_SOUND_MODE_DICT): vol.Schema({str: str}), + vol.Optional(CONF_SOUND_MODE_DICT, + default=DEFAULT_SOUND_MODE_DICT): vol.Schema({str: str}), vol.Optional(CONF_SHOW_ALL_SOURCES, default=DEFAULT_SHOW_SOURCES): cv.boolean, vol.Optional(CONF_ZONES): @@ -75,7 +83,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Initialize list with receivers to be started receivers = [] - hosts = [] cache = hass.data.get(KEY_DENON_CACHE) if cache is None: @@ -129,7 +136,9 @@ def setup_platform(hass, config, add_devices, discovery_info=None): show_all_inputs=show_all_sources, timeout=timeout, add_zones=add_zones) for new_zone in new_device.zones.values(): - receivers.append(DenonDevice(new_zone, host, sound_mode_support, sound_mode_dict)) + receivers.append(DenonDevice(new_zone, host, + sound_mode_support, + sound_mode_dict)) cache.add(host) _LOGGER.info("Denon receiver at host %s initialized", host) @@ -165,7 +174,8 @@ def __init__(self, receiver, host, sound_mode_support, sound_mode_dict): self._station = self._receiver.station self._supported_features_base = SUPPORT_DENON - self._supported_features_base |= (sound_mode_support and SUPPORT_SELECT_SOUND_MODE) + self._supported_features_base |= (sound_mode_support and + SUPPORT_SELECT_SOUND_MODE) def update(self): """Get the latest status information from device.""" @@ -187,14 +197,18 @@ def update(self): if self._sound_mode_support: try: - url = 'http://' + str(self._host) + '/goform/formMainZone_MainZoneXml.xml' + url = ('http://' + str(self._host) + + '/goform/formMainZone_MainZoneXml.xml') XML_data = urllib.request.urlopen(url) - except ConnectTimeoutError: + except URLError: return parsed_data = ET.parse(XML_data).getroot() - sound_mode_raw = parsed_data.find('selectSurround/value').text.rstrip() + sound_mode_raw = parsed_data.find('selectSurround/value') + sound_mode_raw = sound_mode_raw.text.rstrip() try: - sound_mode = list(self._sound_mode_dict.keys())[list(self._sound_mode_dict.values()).index(sound_mode_raw.upper())] + mode_list = list(self._sound_mode_dict.values()) + Index = mode_list.index(sound_mode_raw.upper()) + sound_mode = list(self._sound_mode_dict.keys())[Index] self._current_sound_mode = sound_mode except ValueError: self._current_sound_mode = sound_mode_raw @@ -338,7 +352,9 @@ def select_source(self, source): def select_sound_mode(self, sound_mode): """Select sound mode.""" - url = 'http://' + str(self._host) + '/MainZone/index.put.asp?cmd0=PutSurroundMode%2F' + sound_mode.upper().replace(" ", "+") + url = ('http://' + str(self._host) + + '/MainZone/index.put.asp?cmd0=PutSurroundMode%2F' + + sound_mode.upper().replace(" ", "+")) urllib.request.urlopen(url) return sound_mode From 463e889ab7451f36d241079c08dbc87257c3084d Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 18 Jan 2018 20:39:29 +0100 Subject: [PATCH 06/12] Add sound mode support fixed issue with except URLError --- homeassistant/components/media_player/denonavr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/media_player/denonavr.py index 0b576bdb6914c..54b2017aad1fb 100644 --- a/homeassistant/components/media_player/denonavr.py +++ b/homeassistant/components/media_player/denonavr.py @@ -200,7 +200,7 @@ def update(self): url = ('http://' + str(self._host) + '/goform/formMainZone_MainZoneXml.xml') XML_data = urllib.request.urlopen(url) - except URLError: + except urllib.error.URLError: return parsed_data = ET.parse(XML_data).getroot() sound_mode_raw = parsed_data.find('selectSurround/value') From 29d687c0cc448c913589fc4a056646e86cab2c8d Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 18 Jan 2018 21:38:02 +0100 Subject: [PATCH 07/12] Add sound mode support - fixed some naming issues - fixed import order --- homeassistant/components/media_player/denonavr.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/media_player/denonavr.py index 54b2017aad1fb..61f7007d5232b 100644 --- a/homeassistant/components/media_player/denonavr.py +++ b/homeassistant/components/media_player/denonavr.py @@ -7,9 +7,9 @@ import logging from collections import (namedtuple, OrderedDict) -import voluptuous as vol import urllib import xml.etree.ElementTree as ET +import voluptuous as vol from homeassistant.components.media_player import ( SUPPORT_PAUSE, SUPPORT_NEXT_TRACK, SUPPORT_PREVIOUS_TRACK, @@ -199,16 +199,16 @@ def update(self): try: url = ('http://' + str(self._host) + '/goform/formMainZone_MainZoneXml.xml') - XML_data = urllib.request.urlopen(url) + xml_data = urllib.request.urlopen(url) except urllib.error.URLError: return - parsed_data = ET.parse(XML_data).getroot() + parsed_data = ET.parse(xml_data).getroot() sound_mode_raw = parsed_data.find('selectSurround/value') sound_mode_raw = sound_mode_raw.text.rstrip() try: mode_list = list(self._sound_mode_dict.values()) - Index = mode_list.index(sound_mode_raw.upper()) - sound_mode = list(self._sound_mode_dict.keys())[Index] + mode_index = mode_list.index(sound_mode_raw.upper()) + sound_mode = list(self._sound_mode_dict.keys())[mode_index] self._current_sound_mode = sound_mode except ValueError: self._current_sound_mode = sound_mode_raw From c6ddfd61fc6854ce047d11b118fc46d66625ce9f Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Sat, 20 Jan 2018 20:35:01 +0100 Subject: [PATCH 08/12] Add sound mode support (service) changed the discription of the service. --- homeassistant/components/media_player/services.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index 1ed6620499f01..5e0784376f6f5 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -135,9 +135,9 @@ select_sound_mode: fields: entity_id: description: Name(s) of entities to change sound mode on. - example: 'media_player.media_player.txnr535_0009b0d81f82' + example: 'media_player.marantz' sound_mode: - description: Name of the sound mode to switch to. Platform dependent. + description: Name of the sound mode to switch to. example: 'Music' clear_playlist: From 95bab1223498f3217a48c862efdf17c27c1ef691 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Sat, 20 Jan 2018 20:44:32 +0100 Subject: [PATCH 09/12] Added sound mode support added a log message if the url get request fails to receive the XML file containing the sound mode. --- homeassistant/components/media_player/denonavr.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/media_player/denonavr.py index 61f7007d5232b..2a4cc5cf955f8 100644 --- a/homeassistant/components/media_player/denonavr.py +++ b/homeassistant/components/media_player/denonavr.py @@ -201,6 +201,7 @@ def update(self): '/goform/formMainZone_MainZoneXml.xml') xml_data = urllib.request.urlopen(url) except urllib.error.URLError: + _LOGGER.info("Denon receiver failed to get sound mode, URL-Error") return parsed_data = ET.parse(xml_data).getroot() sound_mode_raw = parsed_data.find('selectSurround/value') From d3494bf3abba56487959c9fee026579fffbe4bf7 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Sat, 20 Jan 2018 23:28:05 +0100 Subject: [PATCH 10/12] Add sound mode support fixed line too long changed loger.info to loger.error for the error message --- homeassistant/components/media_player/denonavr.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/media_player/denonavr.py index 2a4cc5cf955f8..a009ece9f4b2b 100644 --- a/homeassistant/components/media_player/denonavr.py +++ b/homeassistant/components/media_player/denonavr.py @@ -201,7 +201,8 @@ def update(self): '/goform/formMainZone_MainZoneXml.xml') xml_data = urllib.request.urlopen(url) except urllib.error.URLError: - _LOGGER.info("Denon receiver failed to get sound mode, URL-Error") + err = "Denon receiver failed to get sound mode, URL-Error" + _LOGGER.error(err) return parsed_data = ET.parse(xml_data).getroot() sound_mode_raw = parsed_data.find('selectSurround/value') From 0903efbfd2fb68178d57c6942c5ec1e32a752123 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Sat, 17 Feb 2018 16:30:18 +0100 Subject: [PATCH 11/12] Compatible with Home Assistant v0.63.2 implement some changes that were done in HASS v0.63.2 --- .../components/media_player/__init__.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index f2a80d43e9dcd..34092b4cea493 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -10,7 +10,6 @@ import collections import hashlib import logging -import os from random import SystemRandom from aiohttp import web @@ -19,7 +18,6 @@ import voluptuous as vol from homeassistant.components.http import KEY_AUTHENTICATED, HomeAssistantView -from homeassistant.config import load_yaml_config_file from homeassistant.const import ( STATE_OFF, STATE_IDLE, STATE_PLAYING, STATE_UNKNOWN, ATTR_ENTITY_ID, SERVICE_TOGGLE, SERVICE_TURN_ON, SERVICE_TURN_OFF, SERVICE_VOLUME_UP, @@ -93,6 +91,7 @@ MEDIA_TYPE_EPISODE = 'episode' MEDIA_TYPE_CHANNEL = 'channel' MEDIA_TYPE_PLAYLIST = 'playlist' +MEDIA_TYPE_URL = 'url' SUPPORT_PAUSE = 1 SUPPORT_SEEK = 2 @@ -392,14 +391,10 @@ def async_setup(hass, config): component = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) - hass.http.register_view(MediaPlayerImageView(component.entities)) + hass.http.register_view(MediaPlayerImageView(component)) yield from component.async_setup(config) - descriptions = yield from hass.async_add_job( - load_yaml_config_file, os.path.join( - os.path.dirname(__file__), 'services.yaml')) - @asyncio.coroutine def async_service_handler(service): """Map services to methods on MediaPlayerDevice.""" @@ -444,7 +439,7 @@ def async_service_handler(service): 'schema', MEDIA_PLAYER_SCHEMA) hass.services.async_register( DOMAIN, service, async_service_handler, - descriptions.get(service), schema=schema) + schema=schema) return True @@ -987,14 +982,14 @@ class MediaPlayerImageView(HomeAssistantView): url = '/api/media_player_proxy/{entity_id}' name = 'api:media_player:image' - def __init__(self, entities): + def __init__(self, component): """Initialize a media player view.""" - self.entities = entities + self.component = component @asyncio.coroutine def get(self, request, entity_id): """Start a get request.""" - player = self.entities.get(entity_id) + player = self.component.get_entity(entity_id) if player is None: status = 404 if request[KEY_AUTHENTICATED] else 401 return web.Response(status=status) From c192350111191ef21bb698ca197f317722121622 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Sat, 17 Feb 2018 16:31:30 +0100 Subject: [PATCH 12/12] Compatible with Home Assistant v0.63.2 implemented the changes made in HASS v0.63.2 --- .../components/media_player/services.yaml | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index 5e0784376f6f5..7d5700b0c2759 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -107,6 +107,20 @@ media_seek: description: Position to seek to. The format is platform dependent. example: 100 +monoprice_snapshot: + description: Take a snapshot of the media player zone. + fields: + entity_id: + description: Name(s) of entities that will be snapshot. Platform dependent. + example: 'media_player.living_room' + +monoprice_restore: + description: Restore a snapshot of the media player zone. + fields: + entity_id: + description: Name(s) of entities that will be restored. Platform dependent. + example: 'media_player.living_room' + play_media: description: Send the media player the command for playing media. fields: @@ -303,3 +317,30 @@ kodi_call_method: method: description: Name of the Kodi JSONRPC API method to be called. example: 'VideoLibrary.GetRecentlyAddedEpisodes' + +squeezebox_call_method: + description: 'Call a Squeezebox JSON/RPC API method.' + fields: + entity_id: + description: Name(s) of the Squeexebox entities where to run the API method. + example: 'media_player.squeezebox_radio' + command: + description: Name of the Squeezebox command. + example: 'playlist' + parameters: + description: Optional array of parameters to be appended to the command. See 'Command Line Interface' official help page from Logitech for details. + example: '["loadtracks", "track.titlesearch=highway to hell"]' + +yamaha_enable_output: + description: Enable or disable an output port + + fields: + entity_id: + description: Name(s) of entites to enable/disable port on. + example: 'media_player.yamaha' + port: + description: Name of port to enable/disable. + example: 'hdmi1' + enabled: + description: Boolean indicating if port should be enabled or not. + example: true