Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions homeassistant/components/media_player/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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'

Expand Down Expand Up @@ -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({
Expand All @@ -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,
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -194,6 +205,8 @@
ATTR_APP_NAME,
ATTR_INPUT_SOURCE,
ATTR_INPUT_SOURCE_LIST,
ATTR_SOUND_MODE,
ATTR_SOUND_MODE_LIST,
ATTR_MEDIA_SHUFFLE,
]

Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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."""
Expand Down
80 changes: 71 additions & 9 deletions homeassistant/components/media_player/denonavr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand All @@ -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
Expand All @@ -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()
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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':
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

continuation line over-indented for visual indent

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()
Expand All @@ -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():
Expand Down
10 changes: 10 additions & 0 deletions homeassistant/components/media_player/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
35 changes: 32 additions & 3 deletions homeassistant/components/media_player/songpal.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from homeassistant.components.media_player import (
PLATFORM_SCHEMA, SUPPORT_SELECT_SOURCE, SUPPORT_TURN_OFF,
SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_SET,
SUPPORT_TURN_ON, MediaPlayerDevice, DOMAIN)
SUPPORT_TURN_ON, SUPPORT_SELECT_SOUND_MODE, MediaPlayerDevice, DOMAIN)
from homeassistant.const import (
CONF_NAME, STATE_ON, STATE_OFF, ATTR_ENTITY_ID)
from homeassistant.exceptions import PlatformNotReady
Expand All @@ -21,7 +21,8 @@

SUPPORT_SONGPAL = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_STEP | \
SUPPORT_VOLUME_MUTE | SUPPORT_SELECT_SOURCE | \
SUPPORT_TURN_ON | SUPPORT_TURN_OFF
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | \
SUPPORT_SELECT_SOUND_MODE

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -171,6 +172,15 @@ async def async_update(self):
_LOGGER.debug("Got ins: %s", inputs)
self._sources = inputs


try:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too many blank lines (2)

soundfields = await self.dev.get_soundfield()
self._soundmodes = {x.value:x.title for x in soundfields.candidate}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing whitespace after ':'
line too long (83 > 79 characters)

self._active_soundmode = self._soundmodes[soundfields.currentValue]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (83 > 79 characters)

except SongpalException:
self._soundmodes = None
self._active_soundmode = None

self._available = True
except SongpalException as ex:
# if we were available, print out the exception
Expand All @@ -192,6 +202,22 @@ def source_list(self):
"""Return list of available sources."""
return [x.title for x in self._sources]

async def async_select_sound_mode(self, sound_mode):
"""Select sound mode."""
reversed = {v: k for k, v in self._soundmodes.items()}
return await self.dev.set_soundfield(reversed[sound_mode])

@property
def sound_mode(self):
"""Return currently active sound mode."""
return self._active_soundmode

@property
def sound_mode_list(self):
"""Return supported sound modes."""
if self._soundmodes is not None:
return list(self._soundmodes.keys())

@property
def state(self):
"""Return current state."""
Expand Down Expand Up @@ -249,4 +275,7 @@ def is_volume_muted(self):
@property
def supported_features(self):
"""Return supported features."""
return SUPPORT_SONGPAL
supported = SUPPORT_SONGPAL
supported |= (self._soundmodes is not None and
SUPPORT_SELECT_SOUND_MODE)
return supported
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down