Skip to content

Commit

Permalink
Merge branch 'pr/1643' into dev
Browse files Browse the repository at this point in the history
Conflicts:
	requirements_all.txt
  • Loading branch information
balloob committed Mar 30, 2016
2 parents a4ffec3 + 86199c8 commit 5b00919
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 4 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ omit =
homeassistant/components/media_player/snapcast.py
homeassistant/components/media_player/sonos.py
homeassistant/components/media_player/squeezebox.py
homeassistant/components/media_player/onkyo.py
homeassistant/components/media_player/yamaha.py
homeassistant/components/notify/free_mobile.py
homeassistant/components/notify/googlevoice.py
Expand Down
49 changes: 49 additions & 0 deletions homeassistant/components/media_player/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
}

SERVICE_PLAY_MEDIA = 'play_media'
SERVICE_SELECT_SOURCE = 'select_source'

ATTR_MEDIA_VOLUME_LEVEL = 'volume_level'
ATTR_MEDIA_VOLUME_MUTED = 'is_volume_muted'
Expand All @@ -55,6 +56,7 @@
ATTR_APP_ID = 'app_id'
ATTR_APP_NAME = 'app_name'
ATTR_SUPPORTED_MEDIA_COMMANDS = 'supported_media_commands'
ATTR_INPUT_SOURCE = 'source'

MEDIA_TYPE_MUSIC = 'music'
MEDIA_TYPE_TVSHOW = 'tvshow'
Expand All @@ -74,6 +76,7 @@
SUPPORT_TURN_OFF = 256
SUPPORT_PLAY_MEDIA = 512
SUPPORT_VOLUME_STEP = 1024
SUPPORT_SELECT_SOURCE = 2048

SERVICE_TO_METHOD = {
SERVICE_TURN_ON: 'turn_on',
Expand All @@ -87,6 +90,7 @@
SERVICE_MEDIA_NEXT_TRACK: 'media_next_track',
SERVICE_MEDIA_PREVIOUS_TRACK: 'media_previous_track',
SERVICE_PLAY_MEDIA: 'play_media',
SERVICE_SELECT_SOURCE: 'select_source',
}

ATTR_TO_PROPERTY = [
Expand All @@ -108,6 +112,7 @@
ATTR_APP_ID,
ATTR_APP_NAME,
ATTR_SUPPORTED_MEDIA_COMMANDS,
ATTR_INPUT_SOURCE,
]


Expand Down Expand Up @@ -220,6 +225,16 @@ def play_media(hass, media_type, media_id, entity_id=None):
hass.services.call(DOMAIN, SERVICE_PLAY_MEDIA, data)


def select_source(hass, source, entity_id=None):
"""Send the media player the command to select input source."""
data = {ATTR_INPUT_SOURCE: source}

if entity_id:
data[ATTR_ENTITY_ID] = entity_id

hass.services.call(DOMAIN, SERVICE_SELECT_SOURCE, data)


def setup(hass, config):
"""Track states and offer events for media_players."""
component = EntityComponent(
Expand Down Expand Up @@ -302,6 +317,26 @@ def media_seek_service(service):
hass.services.register(DOMAIN, SERVICE_MEDIA_SEEK, media_seek_service,
descriptions.get(SERVICE_MEDIA_SEEK))

def select_source_service(service):
"""Change input to selected source."""
input_source = service.data.get(ATTR_INPUT_SOURCE)

if input_source is None:
_LOGGER.error(
'Received call to %s without attribute %s',
service.service, ATTR_INPUT_SOURCE)
return

for player in component.extract_from_service(service):
player.select_source(input_source)

if player.should_poll:
player.update_ha_state(True)

hass.services.register(DOMAIN, SERVICE_SELECT_SOURCE,
select_source_service,
descriptions.get(SERVICE_SELECT_SOURCE))

def play_media_service(service):
"""Play specified media_id on the media player."""
media_type = service.data.get(ATTR_MEDIA_CONTENT_TYPE)
Expand Down Expand Up @@ -430,6 +465,11 @@ def app_name(self):
"""Name of the current running app."""
return None

@property
def source(self):
"""Name of the current input source."""
return None

@property
def supported_media_commands(self):
"""Flag media commands that are supported."""
Expand Down Expand Up @@ -475,6 +515,10 @@ def play_media(self, media_type, media_id):
"""Play a piece of media."""
raise NotImplementedError()

def select_source(self, source):
"""Select input source."""
raise NotImplementedError()

# No need to overwrite these.
@property
def support_pause(self):
Expand Down Expand Up @@ -511,6 +555,11 @@ def support_play_media(self):
"""Boolean if play media command supported."""
return bool(self.supported_media_commands & SUPPORT_PLAY_MEDIA)

@property
def support_select_source(self):
"""Boolean if select source command supported."""
return bool(self.supported_media_commands & SUPPORT_SELECT_SOURCE)

def toggle(self):
"""Toggle the power on the media player."""
if self.state in [STATE_OFF, STATE_IDLE]:
Expand Down
32 changes: 30 additions & 2 deletions homeassistant/components/media_player/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
MEDIA_TYPE_MUSIC, MEDIA_TYPE_TVSHOW, MEDIA_TYPE_VIDEO, SUPPORT_NEXT_TRACK,
SUPPORT_PAUSE, SUPPORT_PLAY_MEDIA, SUPPORT_PREVIOUS_TRACK,
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
MediaPlayerDevice)
SUPPORT_SELECT_SOURCE, MediaPlayerDevice)
from homeassistant.const import STATE_OFF, STATE_PAUSED, STATE_PLAYING


Expand All @@ -20,7 +20,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
'Living Room', 'eyU3bRy2x44',
'♥♥ The Best Fireplace Video (3 hours)'),
DemoYoutubePlayer('Bedroom', 'kxopViU98Xo', 'Epic sax guy 10 hours'),
DemoMusicPlayer(), DemoTVShowPlayer(),
DemoMusicPlayer(), DemoTVShowPlayer(), DemoReceiver(),
])


Expand All @@ -37,6 +37,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
NETFLIX_PLAYER_SUPPORT = \
SUPPORT_PAUSE | SUPPORT_TURN_ON | SUPPORT_TURN_OFF

RECEIVER_SUPPORT = SUPPORT_SELECT_SOURCE


class AbstractDemoPlayer(MediaPlayerDevice):
"""A demo media players."""
Expand Down Expand Up @@ -337,3 +339,29 @@ def media_next_track(self):
if self._cur_episode < self._episode_count:
self._cur_episode += 1
self.update_ha_state()


class DemoReceiver(AbstractDemoPlayer):
"""A Demo receiver that only supports changing source input."""

# We only implement the methods that we support
# pylint: disable=abstract-method
def __init__(self):
"""Initialize the demo device."""
super().__init__('receiver')
self._source = 'dvd'

@property
def source(self):
"""Return the current input source."""
return self._source

def select_source(self, source):
"""Set the input source."""
self._source = source
self.update_ha_state()

@property
def supported_media_commands(self):
"""Flag of media commands that are supported."""
return RECEIVER_SUPPORT
111 changes: 111 additions & 0 deletions homeassistant/components/media_player/onkyo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""
Support for Onkyo Receivers.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.onkyo/
"""
import logging

from homeassistant.components.media_player import (
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
SUPPORT_SELECT_SOURCE, MediaPlayerDevice)
from homeassistant.const import STATE_OFF, STATE_ON

REQUIREMENTS = ['https://github.com/danieljkemp/onkyo-eiscp/archive/'
'python3.zip#onkyo-eiscp==0.9.2']
_LOGGER = logging.getLogger(__name__)

SUPPORT_ONKYO = SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | \
SUPPORT_TURN_ON | SUPPORT_TURN_OFF | SUPPORT_SELECT_SOURCE


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Onkyo platform."""
from eiscp import eISCP
add_devices(OnkyoDevice(receiver)
for receiver in eISCP.discover())


class OnkyoDevice(MediaPlayerDevice):
"""Representation of a Onkyo device."""

# pylint: disable=too-many-public-methods, abstract-method
def __init__(self, receiver):
"""Initialize the Onkyo Receiver."""
self._receiver = receiver
self._muted = False
self._volume = 0
self._pwstate = STATE_OFF
self.update()
self._name = '{}_{}'.format(
receiver.info['model_name'], receiver.info['identifier'])
self._current_source = None

def update(self):
"""Get the latest details from the device."""
status = self._receiver.command('system-power query')
if status[1] == 'on':
self._pwstate = STATE_ON
else:
self._pwstate = STATE_OFF
return
volume_raw = self._receiver.command('volume query')
mute_raw = self._receiver.command('audio-muting query')
current_source_raw = self._receiver.command('input-selector query')
self._current_source = '_'.join('_'.join(
[i for i in current_source_raw[1]]))
self._muted = bool(mute_raw[1] == 'on')
self._volume = int(volume_raw[1], 16)/80.0

@property
def name(self):
"""Return the name of the device."""
return self._name

@property
def state(self):
"""Return the state of the device."""
return self._pwstate

@property
def volume_level(self):
"""Volume level of the media player (0..1)."""
return self._volume

@property
def is_volume_muted(self):
"""Boolean if volume is currently muted."""
return self._muted

@property
def supported_media_commands(self):
"""Flag of media commands that are supported."""
return SUPPORT_ONKYO

@property
def source(self):
""""Return the current input source of the device."""
return self._current_source

def turn_off(self):
"""Turn off media player."""
self._receiver.command('system-power standby')

def set_volume_level(self, volume):
"""Set volume level, input is range 0..1. Onkyo ranges from 1-80."""
self._receiver.command('volume {}'.format(int(volume*80)))

def mute_volume(self, mute):
"""Mute (true) or unmute (false) media player."""
if mute:
self._receiver.command('audio-muting on')
else:
self._receiver.command('audio-muting off')

def turn_on(self):
"""Turn the media player on."""
self._receiver.power_on()

def select_source(self, source):
"""Set the input source."""
self._receiver.command('input-selector {}'.format(source))
11 changes: 11 additions & 0 deletions homeassistant/components/media_player/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,14 @@ play_media:
media_content_type:
description: The type of the content to play. Must be one of MUSIC, TVSHOW, VIDEO, EPISODE, CHANNEL or PLAYLIST
example: 'MUSIC'

select_source:
description: Send the media player the command to change input source.

fields:
entity_id:
description: Name(s) of entites to change source on
example: 'media_player.media_player.txnr535_0009b0d81f82'
source:
description: Name of the source to switch to. Platform dependent.
example: 'video1'
16 changes: 15 additions & 1 deletion homeassistant/components/media_player/universal.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED,
ATTR_SUPPORTED_MEDIA_COMMANDS, DOMAIN, SERVICE_PLAY_MEDIA,
SUPPORT_TURN_OFF, SUPPORT_TURN_ON, SUPPORT_VOLUME_MUTE, SUPPORT_VOLUME_SET,
SUPPORT_VOLUME_STEP, MediaPlayerDevice)
SUPPORT_VOLUME_STEP, SUPPORT_SELECT_SOURCE, ATTR_INPUT_SOURCE,
SERVICE_SELECT_SOURCE, MediaPlayerDevice)
from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_PICTURE, CONF_NAME, SERVICE_MEDIA_NEXT_TRACK,
SERVICE_MEDIA_PAUSE, SERVICE_MEDIA_PLAY, SERVICE_MEDIA_PLAY_PAUSE,
Expand Down Expand Up @@ -321,6 +322,11 @@ def app_name(self):
"""Name of the current running app."""
return self._child_attr(ATTR_APP_NAME)

@property
def current_source(self):
""""Return the current input source of the device."""
return self._child_attr(ATTR_INPUT_SOURCE)

@property
def supported_media_commands(self):
"""Flag media commands that are supported."""
Expand All @@ -340,6 +346,9 @@ def supported_media_commands(self):
ATTR_MEDIA_VOLUME_MUTED in self._attrs:
flags |= SUPPORT_VOLUME_MUTE

if SUPPORT_SELECT_SOURCE in self._cmds:
flags |= SUPPORT_SELECT_SOURCE

return flags

@property
Expand Down Expand Up @@ -406,6 +415,11 @@ def media_play_pause(self):
"""Play or pause the media player."""
self._call_service(SERVICE_MEDIA_PLAY_PAUSE)

def select_source(self, source):
"""Set the input source."""
data = {ATTR_INPUT_SOURCE: source}
self._call_service(SERVICE_SELECT_SOURCE, data)

def update(self):
"""Update state in HA."""
for child_name in self._children:
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/helpers/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import homeassistant.util.dt as dt_util
from homeassistant.components.media_player import (
ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_CONTENT_TYPE, ATTR_MEDIA_SEEK_POSITION,
ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, SERVICE_PLAY_MEDIA)
ATTR_MEDIA_VOLUME_LEVEL, ATTR_MEDIA_VOLUME_MUTED, SERVICE_PLAY_MEDIA,
SERVICE_SELECT_SOURCE, ATTR_INPUT_SOURCE)
from homeassistant.components.notify import (
ATTR_MESSAGE, SERVICE_NOTIFY)
from homeassistant.components.sun import (
Expand Down Expand Up @@ -42,6 +43,7 @@
SERVICE_SET_AWAY_MODE: [ATTR_AWAY_MODE],
SERVICE_SET_FAN_MODE: [ATTR_FAN],
SERVICE_SET_TEMPERATURE: [ATTR_TEMPERATURE],
SERVICE_SELECT_SOURCE: [ATTR_INPUT_SOURCE],
}

# Update this dict when new services are added to HA.
Expand Down
3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ https://github.com/Xorso/pyalarmdotcom/archive/0.1.1.zip#pyalarmdotcom==0.1.1
# homeassistant.components.modbus
https://github.com/bashwork/pymodbus/archive/d7fc4f1cc975631e0a9011390e8017f64b612661.zip#pymodbus==1.2.0

# homeassistant.components.media_player.onkyo
https://github.com/danieljkemp/onkyo-eiscp/archive/python3.zip#onkyo-eiscp==0.9.2

# homeassistant.components.sensor.sabnzbd
https://github.com/jamespcole/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1

Expand Down
Loading

0 comments on commit 5b00919

Please sign in to comment.