From e4bb8b044443a38d52414eb98d45571a3677428d Mon Sep 17 00:00:00 2001 From: Jan Almeroth Date: Tue, 5 Sep 2017 18:07:58 +0200 Subject: [PATCH] Introducing a media_player component for Yamaha Multicast devices (#9258) * Introducing media_player yamaha_multicast * Fix pep8_max_line_length * Revert "Fix pep8_max_line_length" This reverts commit 664c25d6571e2f49f635aea332a848655f220c36. * Revert "Introducing media_player yamaha_multicast" This reverts commit a4fb64b53a79f68966d4af80fe9304d357bcd832. * Introducing media_player for Yamaha MultiCast Devices * Add missing Docstrings * Adding Requirements * Add Geofency device tracker (#9106) * Added Geofency device tracker Added Geofency device tracker * fix pylint error * review fixes * merge coroutines * Version bump * Version bump * D210: No whitespaces allowed surrounding docstring text * Fix linting * Version bump * Revert "Add Geofency device tracker (#9106)" This reverts commit c240d907d2f1fadecf831b3d5bb4e026ce3f892d. * Fix Invalid method names * Fix update_status timer * Fix Invalid class name "mcDevice" * Fix Access to a protected members * Introducing source_list setter * Fix logging * Version bump * D400: First line should end with a period (not 'e') * Removed unnecessary logging * Minor changes Thanks to comments from @andrey-git --- .coveragerc | 1 + .../media_player/yamaha_musiccast.py | 233 ++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 237 insertions(+) create mode 100644 homeassistant/components/media_player/yamaha_musiccast.py diff --git a/.coveragerc b/.coveragerc index ecf35b8030d..2fc424e91f6 100644 --- a/.coveragerc +++ b/.coveragerc @@ -384,6 +384,7 @@ omit = homeassistant/components/media_player/vlc.py homeassistant/components/media_player/volumio.py homeassistant/components/media_player/yamaha.py + homeassistant/components/media_player/yamaha_musiccast.py homeassistant/components/mycroft.py homeassistant/components/notify/aws_lambda.py homeassistant/components/notify/aws_sns.py diff --git a/homeassistant/components/media_player/yamaha_musiccast.py b/homeassistant/components/media_player/yamaha_musiccast.py new file mode 100644 index 00000000000..88d17b4d627 --- /dev/null +++ b/homeassistant/components/media_player/yamaha_musiccast.py @@ -0,0 +1,233 @@ +"""Example for configuration.yaml. + +media_player: + - platform: yamaha_musiccast + name: "Living Room" + host: 192.168.xxx.xx + port: 5005 + +""" + +import logging +import voluptuous as vol +import homeassistant.helpers.config_validation as cv + +from homeassistant.const import ( + CONF_NAME, CONF_HOST, CONF_PORT, + STATE_UNKNOWN, STATE_ON +) +from homeassistant.components.media_player import ( + MediaPlayerDevice, MEDIA_TYPE_MUSIC, PLATFORM_SCHEMA, + SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK, + SUPPORT_TURN_ON, SUPPORT_TURN_OFF, SUPPORT_PLAY, + SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE, + SUPPORT_SELECT_SOURCE, SUPPORT_STOP +) +_LOGGER = logging.getLogger(__name__) + +SUPPORTED_FEATURES = ( + SUPPORT_PLAY | SUPPORT_PAUSE | SUPPORT_STOP | + SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | + SUPPORT_TURN_ON | SUPPORT_TURN_OFF | + SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE | + SUPPORT_SELECT_SOURCE +) + +REQUIREMENTS = ['pymusiccast==0.1.0'] + +DEFAULT_NAME = "Yamaha Receiver" +DEFAULT_PORT = 5005 + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Required(CONF_HOST): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.positive_int, +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Yamaha MusicCast platform.""" + import pymusiccast + + name = config.get(CONF_NAME) + host = config.get(CONF_HOST) + port = config.get(CONF_PORT) + + receiver = pymusiccast.McDevice(host, udp_port=port) + _LOGGER.debug("receiver: %s / Port: %d", receiver, port) + + add_devices([YamahaDevice(receiver, name)], True) + + +class YamahaDevice(MediaPlayerDevice): + """Representation of a Yamaha MusicCast device.""" + + def __init__(self, receiver, name): + """Initialize the Yamaha MusicCast device.""" + self._receiver = receiver + self._name = name + self.power = STATE_UNKNOWN + self.volume = 0 + self.volume_max = 0 + self.mute = False + self._source = None + self._source_list = [] + self.status = STATE_UNKNOWN + self.media_status = None + self._receiver.set_yamaha_device(self) + + @property + def name(self): + """Return the name of the device.""" + return self._name + + @property + def state(self): + """Return the state of the device.""" + if self.power == STATE_ON and self.status is not STATE_UNKNOWN: + return self.status + return self.power + + @property + def should_poll(self): + """Push an update after each command.""" + return True + + @property + def is_volume_muted(self): + """Boolean if volume is currently muted.""" + return self.mute + + @property + def volume_level(self): + """Volume level of the media player (0..1).""" + return self.volume + + @property + def supported_features(self): + """Flag of features that are supported.""" + return SUPPORTED_FEATURES + + @property + def source(self): + """Return the current input source.""" + return self._source + + @property + def source_list(self): + """List of available input sources.""" + return self._source_list + + @source_list.setter + def source_list(self, value): + """Set source_list attribute.""" + self._source_list = value + + @property + def media_content_type(self): + """Return the media content type.""" + return MEDIA_TYPE_MUSIC + + @property + def media_duration(self): + """Duration of current playing media in seconds.""" + return self.media_status.media_duration \ + if self.media_status else None + + @property + def media_image_url(self): + """Image url of current playing media.""" + return self.media_status.media_image_url \ + if self.media_status else None + + @property + def media_artist(self): + """Artist of current playing media, music track only.""" + return self.media_status.media_artist if self.media_status else None + + @property + def media_album(self): + """Album of current playing media, music track only.""" + return self.media_status.media_album if self.media_status else None + + @property + def media_track(self): + """Track number of current playing media, music track only.""" + return self.media_status.media_track if self.media_status else None + + @property + def media_title(self): + """Title of current playing media.""" + return self.media_status.media_title if self.media_status else None + + def update(self): + """Get the latest details from the device.""" + _LOGGER.debug("update: %s", self.entity_id) + + # call from constructor setup_platform() + if not self.entity_id: + _LOGGER.debug("First run") + self._receiver.update_status(push=False) + # call from regular polling + else: + # update_status_timer was set before + if self._receiver.update_status_timer: + _LOGGER.debug( + "is_alive: %s", + self._receiver.update_status_timer.is_alive()) + # e.g. computer was suspended, while hass was running + if not self._receiver.update_status_timer.is_alive(): + _LOGGER.debug("Reinitializing") + self._receiver.update_status() + + def turn_on(self): + """Turn on specified media player or all.""" + _LOGGER.debug("Turn device: on") + self._receiver.set_power(True) + + def turn_off(self): + """Turn off specified media player or all.""" + _LOGGER.debug("Turn device: off") + self._receiver.set_power(False) + + def media_play(self): + """Send the media player the command for play/pause.""" + _LOGGER.debug("Play") + self._receiver.set_playback("play") + + def media_pause(self): + """Send the media player the command for pause.""" + _LOGGER.debug("Pause") + self._receiver.set_playback("pause") + + def media_stop(self): + """Send the media player the stop command.""" + _LOGGER.debug("Stop") + self._receiver.set_playback("stop") + + def media_previous_track(self): + """Send the media player the command for prev track.""" + _LOGGER.debug("Previous") + self._receiver.set_playback("previous") + + def media_next_track(self): + """Send the media player the command for next track.""" + _LOGGER.debug("Next") + self._receiver.set_playback("next") + + def mute_volume(self, mute): + """Send mute command.""" + _LOGGER.debug("Mute volume: %s", mute) + self._receiver.set_mute(mute) + + def set_volume_level(self, volume): + """Set volume level, range 0..1.""" + _LOGGER.debug("Volume level: %.2f / %d", + volume, volume * self.volume_max) + self._receiver.set_volume(volume * self.volume_max) + + def select_source(self, source): + """Send the media player the command to select input source.""" + _LOGGER.debug("select_source: %s", source) + self.status = STATE_UNKNOWN + self._receiver.set_input(source) diff --git a/requirements_all.txt b/requirements_all.txt index a8353b431fd..cad3e01fd70 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -660,6 +660,9 @@ pymochad==0.1.1 # homeassistant.components.modbus pymodbus==1.3.1 +# homeassistant.components.media_player.yamaha_musiccast +pymusiccast==0.1.0 + # homeassistant.components.cover.myq pymyq==0.0.8