diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 615c758cd1a739..50cb9535877fef 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -54,6 +54,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' @@ -78,6 +79,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' @@ -106,6 +109,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({ @@ -129,6 +133,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, @@ -164,6 +172,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}, @@ -194,6 +205,8 @@ ATTR_APP_NAME, ATTR_INPUT_SOURCE, ATTR_INPUT_SOURCE_LIST, + ATTR_SOUND_MODE, + ATTR_SOUND_MODE_LIST, ATTR_MEDIA_SHUFFLE, ] @@ -343,6 +356,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.""" @@ -387,6 +411,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) @@ -569,6 +595,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.""" @@ -712,6 +748,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() @@ -785,6 +832,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.""" diff --git a/homeassistant/components/media_player/denonavr.py b/homeassistant/components/media_player/denonavr.py index fe8fc46c24b260..c253c5cc1a3e25 100644 --- a/homeassistant/components/media_player/denonavr.py +++ b/homeassistant/components/media_player/denonavr.py @@ -12,27 +12,32 @@ 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) import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['denonavr==0.6.1'] +REQUIREMENTS = ['denonavr==0.7.0'] _LOGGER = logging.getLogger(__name__) -DEFAULT_NAME = None DEFAULT_SHOW_SOURCES = False DEFAULT_TIMEOUT = 2 +DEFAULT_SOUND_MODE = True 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' +ATTR_SOUND_MODE_RAW = 'sound_mode_raw' + SUPPORT_DENON = SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | \ SUPPORT_TURN_ON | SUPPORT_TURN_OFF | \ SUPPORT_SELECT_SOURCE | SUPPORT_VOLUME_SET @@ -49,6 +54,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOST): cv.string, vol.Optional(CONF_NAME): cv.string, + vol.Optional(CONF_SOUND_MODE, default=DEFAULT_SOUND_MODE): cv.boolean, + vol.Optional(CONF_SOUND_MODE_DICT): vol.Schema({str: list}), vol.Optional(CONF_SHOW_ALL_SOURCES, default=DEFAULT_SHOW_SOURCES): cv.boolean, vol.Optional(CONF_ZONES): @@ -84,6 +91,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): else: add_zones = None + # Get config option for sound mode + sound_mode_support = config.get(CONF_SOUND_MODE) + sound_mode_dict = config.get(CONF_SOUND_MODE_DICT) + # Start assignment of host and name new_hosts = [] # 1. option: manual setting @@ -117,7 +128,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)) + receivers.append(DenonDevice(new_zone, + sound_mode_support, + sound_mode_dict)) cache.add(host) _LOGGER.info("Denon receiver at host %s initialized", host) @@ -129,7 +142,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): + def __init__(self, receiver, sound_mode_support, sound_mode_dict): """Initialize the device.""" self._receiver = receiver self._name = self._receiver.name @@ -147,6 +160,24 @@ def __init__(self, receiver): self._frequency = self._receiver.frequency self._station = self._receiver.station + self._sound_mode_support = sound_mode_support + if sound_mode_support: + self._sound_mode = self._receiver.sound_mode + self._sound_mode_raw = self._receiver.sound_mode_raw + if sound_mode_dict is None: + self._sound_mode_list = self._receiver.sound_mode_list + else: + self._receiver.set_sound_mode_dict(sound_mode_dict) + self._sound_mode_list = list(sound_mode_dict) + else: + self._sound_mode = None + self._sound_mode_raw = None + self._sound_mode_list = None + + 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() @@ -164,6 +195,9 @@ def update(self): self._band = self._receiver.band self._frequency = self._receiver.frequency self._station = self._receiver.station + if self._sound_mode_support: + self._sound_mode = self._receiver.sound_mode + self._sound_mode_raw = self._receiver.sound_mode_raw @property def name(self): @@ -197,12 +231,27 @@ def source_list(self): """Return a list of available input sources.""" return self._source_list + @property + def sound_mode(self): + """Return the current matched sound mode.""" + return self._sound_mode + + @property + def sound_mode_raw(self): + """Return the current raw sound mode.""" + return self._sound_mode_raw + + @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.""" 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): @@ -276,6 +325,15 @@ def media_episode(self): """Episode of current playing media, TV show only.""" return None + @property + def device_state_attributes(self): + """Return device specific state attributes.""" + attributes = {} + if self._sound_mode_raw is not None and self._sound_mode_support\ + and self._power == 'ON': + attributes[ATTR_SOUND_MODE_RAW] = self._sound_mode_raw + return attributes + def media_play_pause(self): """Simulate play pause media player.""" return self._receiver.toggle_play_pause() @@ -292,6 +350,10 @@ 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.""" + return self._receiver.set_sound_mode(sound_mode) + def turn_on(self): """Turn on media player.""" if self._receiver.power_on(): diff --git a/homeassistant/components/media_player/services.yaml b/homeassistant/components/media_player/services.yaml index 95072f0270c22f..3739a9a35c82b5 100644 --- a/homeassistant/components/media_player/services.yaml +++ b/homeassistant/components/media_player/services.yaml @@ -144,6 +144,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.marantz' + sound_mode: + description: Name of the sound mode to switch to. + example: 'Music' + clear_playlist: description: Send the media player the command to clear players playlist. fields: diff --git a/requirements_all.txt b/requirements_all.txt index 9a0613211da916..6c2b9f1878c0d4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -239,7 +239,7 @@ defusedxml==0.5.0 deluge-client==1.0.5 # homeassistant.components.media_player.denonavr -denonavr==0.6.1 +denonavr==0.7.0 # homeassistant.components.media_player.directv directpy==0.2