From 7506569db920fc5505d2a529eb27156f7700a3b6 Mon Sep 17 00:00:00 2001 From: Philipp Schmitt Date: Fri, 3 Feb 2017 08:52:52 +0100 Subject: [PATCH] Support for the Orange Livebox Play TV appliance (#5533) * Support for the Orage Livebox Play TV appliance * Add liveboxplaytv to coveragerc * Minor refactoring * Update requirements * Adjust comments * Fix alignment * Fix some coding-style issues highlighted by Travis CI * The livebox play TV does not support playing media * Lint: shorten line * Remove unused callback function * Remove redundant backslash * Implement changes requested by balloob * Don't error out if channel name or media url could not be retrieved * Support current program (media title property) * Remove unnecessary check * Clean up: Remove another unnecessary check, _CONFIGURING variable and _playing attribute * Update liveboxplaytv dependency to version 1.4.4 * Fix liveboxplaytv requirement * Improve media state (support for playing and pause state) * Update liveboxplaytv.py --- .coveragerc | 1 + .../components/media_player/liveboxplaytv.py | 207 ++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 211 insertions(+) create mode 100644 homeassistant/components/media_player/liveboxplaytv.py diff --git a/.coveragerc b/.coveragerc index 093b0e4d526..be2f12aa434 100644 --- a/.coveragerc +++ b/.coveragerc @@ -226,6 +226,7 @@ omit = homeassistant/components/media_player/itunes.py homeassistant/components/media_player/kodi.py homeassistant/components/media_player/lg_netcast.py + homeassistant/components/media_player/liveboxplaytv.py homeassistant/components/media_player/mpchc.py homeassistant/components/media_player/mpd.py homeassistant/components/media_player/nad.py diff --git a/homeassistant/components/media_player/liveboxplaytv.py b/homeassistant/components/media_player/liveboxplaytv.py new file mode 100644 index 00000000000..7386f51df4c --- /dev/null +++ b/homeassistant/components/media_player/liveboxplaytv.py @@ -0,0 +1,207 @@ +""" +Support for interface with an Orange Livebox Play TV appliance. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/media_player.liveboxplaytv/ +""" +import logging +from datetime import timedelta + +import requests +import voluptuous as vol + +import homeassistant.util as util +from homeassistant.components.media_player import ( + SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_PLAY, + SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, + SUPPORT_VOLUME_STEP, SUPPORT_VOLUME_MUTE, SUPPORT_SELECT_SOURCE, + MEDIA_TYPE_CHANNEL, MediaPlayerDevice, PLATFORM_SCHEMA) +from homeassistant.const import ( + CONF_HOST, CONF_PORT, STATE_ON, STATE_OFF, STATE_PLAYING, + STATE_PAUSED, STATE_UNKNOWN, CONF_NAME) +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['liveboxplaytv==1.4.7'] + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'Livebox Play TV' +DEFAULT_PORT = 8080 + +SUPPORT_LIVEBOXPLAYTV = SUPPORT_TURN_OFF | SUPPORT_TURN_ON | \ + SUPPORT_NEXT_TRACK | SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | \ + SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | SUPPORT_SELECT_SOURCE | \ + SUPPORT_PLAY + +MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10) +MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=1) + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_HOST): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string +}) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the Orange Livebox Play TV platform.""" + host = config.get(CONF_HOST) + port = config.get(CONF_PORT) + name = config.get(CONF_NAME) + + add_devices([LiveboxPlayTvDevice(host, port, name)], True) + + +class LiveboxPlayTvDevice(MediaPlayerDevice): + """Representation of an Orange Livebox Play TV.""" + + def __init__(self, host, port, name): + """Initialize the Livebox Play TV device.""" + from liveboxplaytv import LiveboxPlayTv + self._client = LiveboxPlayTv(host, port) + # Assume that the appliance is not muted + self._muted = False + # Assume that the TV is in Play mode + self._name = name + self._current_source = None + self._state = STATE_UNKNOWN + self._channel_list = {} + self._current_channel = None + self._current_program = None + self._media_image_url = None + + @util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS) + def update(self): + """Retrieve the latest data.""" + try: + self._state = self.refresh_state() + # Update current channel + channel = self._client.get_current_channel() + if channel is not None: + self._current_program = self._client.program + self._current_channel = channel.get('name', None) + self._media_image_url = \ + self._client.get_current_channel_image(img_size=300) + self.refresh_channel_list() + except requests.ConnectionError: + self._state = STATE_OFF + + @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._state + + @property + def is_volume_muted(self): + """Boolean if volume is currently muted.""" + return self._muted + + @property + def source(self): + """Return the current input source.""" + return self._current_channel + + @property + def source_list(self): + """List of available input sources.""" + # Sort channels by tvIndex + return [self._channel_list[c] for c in + sorted(self._channel_list.keys())] + + @property + def media_content_type(self): + """Content type of current playing media.""" + # return self._client.media_type + return MEDIA_TYPE_CHANNEL + + @property + def media_image_url(self): + """Image url of current playing media.""" + return self._media_image_url + + @property + def media_title(self): + """Title of current playing media.""" + if self._current_channel: + return '{}: {}'.format(self._current_channel, + self._current_program) + + @property + def supported_media_commands(self): + """Flag of media commands that are supported.""" + return SUPPORT_LIVEBOXPLAYTV + + def refresh_channel_list(self): + """Refresh the list of available channels.""" + new_channel_list = {} + # update channels + for channel in self._client.get_channels(): + new_channel_list[int(channel['index'])] = channel['name'] + self._channel_list = new_channel_list + + def refresh_state(self): + """Refresh the current media state.""" + state = self._client.media_state + if state == 'PLAY': + return STATE_PLAYING + elif state == 'PAUSE': + return STATE_PAUSED + else: + return STATE_ON if self._client.is_on else STATE_OFF + return STATE_UNKNOWN + + def turn_off(self): + """Turn off media player.""" + self._state = STATE_OFF + self._client.turn_off() + + def turn_on(self): + """Turn on the media player.""" + self._state = STATE_ON + self._client.turn_on() + + def volume_up(self): + """Volume up the media player.""" + self._client.volume_up() + + def volume_down(self): + """Volume down media player.""" + self._client.volume_down() + + def mute_volume(self, mute): + """Send mute command.""" + self._muted = mute + self._client.mute() + + def media_play_pause(self): + """Simulate play pause media player.""" + self._client.play_pause() + + def select_source(self, source): + """Select input source.""" + self._current_source = source + self._client.set_channel(source) + + def media_play(self): + """Send play command.""" + self._state = STATE_PLAYING + self._client.play() + + def media_pause(self): + """Send media pause command to media player.""" + self._state = STATE_PAUSED + self._client.pause() + + def media_next_track(self): + """Send next track command.""" + self._client.channel_up() + + def media_previous_track(self): + """Send the previous track command.""" + self._client.channel_down() diff --git a/requirements_all.txt b/requirements_all.txt index f48a900dc91..693785b9ae0 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -312,6 +312,9 @@ liffylights==0.9.4 # homeassistant.components.light.limitlessled limitlessled==1.0.2 +# homeassistant.components.media_player.liveboxplaytv +liveboxplaytv==1.4.7 + # homeassistant.components.notify.matrix matrix-client==0.0.5