From 996322a2d30f21a419b126c859a012540ddae276 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 31 May 2015 12:14:45 +0200 Subject: [PATCH 1/9] add date --- homeassistant/components/media_player/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 2b555faa503..923b76d259f 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -40,6 +40,7 @@ ATTR_MEDIA_ALBUM = 'media_album' ATTR_MEDIA_IMAGE_URL = 'media_image_url' ATTR_MEDIA_VOLUME = 'media_volume' ATTR_MEDIA_DURATION = 'media_duration' +ATTR_MEDIA_DATE = 'media_date' MEDIA_STATE_UNKNOWN = 'unknown' MEDIA_STATE_PLAYING = 'playing' From 453b5a3e8cb9e9058664bbe7caf9a575ffcca6b8 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 31 May 2015 12:15:05 +0200 Subject: [PATCH 2/9] add initial mpd --- homeassistant/components/media_player/mpd.py | 158 +++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 homeassistant/components/media_player/mpd.py diff --git a/homeassistant/components/media_player/mpd.py b/homeassistant/components/media_player/mpd.py new file mode 100644 index 00000000000..83cba24325e --- /dev/null +++ b/homeassistant/components/media_player/mpd.py @@ -0,0 +1,158 @@ +""" +homeassistant.components.media_player.mpd +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Provides functionality to interact with a Music Player Daemon. + +Configuration: + +To use MPD you will need to add something like the following to your +config/configuration.yaml + +media_player: + platform: mpd + server: 127.0.0.1 + port: 6600 + +""" +import logging + +from homeassistant.components.media_player import ( + MediaPlayerDevice, STATE_NO_APP, ATTR_MEDIA_STATE, + ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_TITLE, ATTR_MEDIA_ARTIST, + ATTR_MEDIA_ALBUM, ATTR_MEDIA_DATE, ATTR_MEDIA_DURATION, + ATTR_MEDIA_VOLUME, MEDIA_STATE_PLAYING, MEDIA_STATE_STOPPED) + +_LOGGER = logging.getLogger(__name__) + + +# pylint: disable=unused-argument +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the MPD platform. """ + + daemon = config.get('server', None) + port = config.get('port', 6600) + + try: + from mpd import MPDClient, ConnectionError + + except ImportError: + _LOGGER.exception( + "Unable to import mpd2. " + "Did you maybe not install the 'python-mpd2' package?") + + return None + + mpd = [] + if daemon is not None and port is not None: + mpd.append(MpdDevice(daemon, port)) + + add_devices(mpd) + + +class MpdDevice(MediaPlayerDevice): + """ Represents a MPD server. """ + + def __init__(self, server, port): + from mpd import MPDClient + + self.server = server + self.port = port + self._name = 'MPD' + self.state_attr = {ATTR_MEDIA_STATE: MEDIA_STATE_STOPPED} + + self.client = MPDClient() + self.client.timeout = 10 + self.client.idletimeout = None + self.client.connect(self.server, self.port) + + @property + def name(self): + """ Returns the name of the device. """ + return self._name + + # pylint: disable=no-member + @property + def state(self): + """ Returns the state of the device. """ + status = self.client.status() + + if status is None: + return STATE_NO_APP + else: + return self.client.currentsong()['artist'] + + # pylint: disable=no-member + @property + def state_attributes(self): + """ Returns the state attributes. """ + status = self.client.status() + current_song = self.client.currentsong() + + if not status and not current_song: + state_attr = {} + + if status['state'] == 'play': + state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_PLAYING + else: + state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_STOPPED + + if current_song['id']: + state_attr[ATTR_MEDIA_CONTENT_ID] = current_song['id'] + + if current_song['date']: + state_attr[ATTR_MEDIA_DATE] = current_song['date'] + + if current_song['title']: + state_attr[ATTR_MEDIA_TITLE] = current_song['title'] + + if current_song['time']: + state_attr[ATTR_MEDIA_DURATION] = current_song['time'] + + if current_song['artist']: + state_attr[ATTR_MEDIA_ARTIST] = current_song['artist'] + + if current_song['album']: + state_attr[ATTR_MEDIA_ALBUM] = current_song['album'] + + state_attr[ATTR_MEDIA_VOLUME] = status['volume'] + + return state_attr + + def turn_off(self): + """ Service to exit the running MPD. """ + self.client.close() + + def volume_up(self): + """ Service to send the MPD the command for volume up. """ + current_volume = self.client.status()['volume'] + + if current_volume != '-1': + self.client.setvol(int(current_volume) + 5) + + def volume_down(self): + """ Service to send the MPD the command for volume down. """ + current_volume = self.client.status()['volume'] + + if current_volume != '-1': + self.client.setvol(int(current_volume) - 5) + + def media_play_pause(self): + """ Service to send the MPD the command for play/pause. """ + self.client.stop() + + def media_play(self): + """ Service to send the MPD the command for play/pause. """ + self.client.start() + + def media_pause(self): + """ Service to send the MPD the command for play/pause. """ + self.client.pause() + + def media_next_track(self): + """ Service to send the MPD the command for next track. """ + self.client.next() + + def media_prev_track(self): + """ Service to send the MPD the command for previous track. """ + self.client.previous() From 4a25b59d333b4462836d5535979e201f1b344fe2 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 31 May 2015 14:20:58 +0200 Subject: [PATCH 3/9] add period --- homeassistant/components/media_player/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 923b76d259f..1f647abe9fe 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -2,7 +2,7 @@ homeassistant.components.media_player ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Component to interface with various media players +Component to interface with various media players. """ import logging From 88523e6ffbd461dea026a41942ed39fc5a08afc2 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 31 May 2015 15:03:30 +0200 Subject: [PATCH 4/9] add mpd bindings --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index 624cd3797cd..da169624276 100644 --- a/requirements.txt +++ b/requirements.txt @@ -58,3 +58,6 @@ sleekxmpp>=1.3.1 # Blockchain (sensor.bitcoin) blockchain>=1.1.2 + +# MPD Bindings (media_player.mpd) +python-mpd2>=0.5.4 From bd561dea6e70693cb248fee40a697c0e3ff0fce0 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 31 May 2015 15:18:14 +0200 Subject: [PATCH 5/9] add mpd --- .coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/.coveragerc b/.coveragerc index 4029b48d47c..00e10da8110 100644 --- a/.coveragerc +++ b/.coveragerc @@ -31,6 +31,7 @@ omit = homeassistant/components/keyboard.py homeassistant/components/light/hue.py homeassistant/components/media_player/cast.py + homeassistant/components/media_player/mpd.py homeassistant/components/notify/instapush.py homeassistant/components/notify/nma.py homeassistant/components/notify/pushbullet.py From cb9fe8d9ff6b425c7a9acb1835256147d2191439 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 31 May 2015 15:18:25 +0200 Subject: [PATCH 6/9] update format --- requirements.txt | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/requirements.txt b/requirements.txt index da169624276..24e1f8ff921 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,56 +1,56 @@ -# required for Home Assistant core +# Required for Home Assistant core requests>=2.0 pyyaml>=3.11 pytz>=2015.2 -# optional, needed for specific components +# Optional, needed for specific components -# discovery +# Discovery platform (discovery) zeroconf>=0.16.0 -# sun +# Sun (sun) pyephem>=3.7 -# lights.hue +# Philips Hue library (lights.hue) phue>=0.8 -# lights.limitlessled +# Limitlessled/Easybulb/Milight library (lights.limitlessled) ledcontroller>=1.0.7 -# media_player.cast +# Chromecast bindings (media_player.cast) pychromecast>=0.6.2 -# keyboard +# Keyboard (keyboard) pyuserinput>=0.1.9 -# switch.tellstick, sensor.tellstick +# Tellstick bindings (*.tellstick) tellcore-py>=1.0.4 -# device_tracker.nmap +# Nmap bindings (device_tracker.nmap) python-libnmap>=0.6.2 -# notify.pushbullet +# PushBullet bindings (notify.pushbullet) pushbullet.py>=0.7.1 -# thermostat.nest +# Nest Thermostat bindings (thermostat.nest) python-nest>=2.1 -# z-wave +# Z-Wave (*.zwave) pydispatcher>=2.0.5 -# isy994 +# ISY994 bindings (*.isy994 PyISY>=1.0.2 -# sensor.systemmonitor +# PSutil (sensor.systemmonitor) psutil>=2.2.1 -# pushover notifications +# Pushover bindings (notify.pushover) python-pushover>=0.2 -# Transmission Torrent Client +# Transmission Torrent Client (*.transmission) transmissionrpc>=0.11 -# OpenWeatherMap Web API +# OpenWeatherMap Web API (sensor.openweathermap) pyowm>=2.2.0 # XMPP Bindings (notify.xmpp) From ab4ed2e032f2ebff2cb9182f0c9edab8d709c9a0 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 31 May 2015 20:52:28 +0200 Subject: [PATCH 7/9] add check --- homeassistant/components/media_player/mpd.py | 53 ++++++++++++++------ 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/media_player/mpd.py b/homeassistant/components/media_player/mpd.py index 83cba24325e..37ddf1b3769 100644 --- a/homeassistant/components/media_player/mpd.py +++ b/homeassistant/components/media_player/mpd.py @@ -11,17 +11,20 @@ config/configuration.yaml media_player: platform: mpd + location: bedroom server: 127.0.0.1 port: 6600 """ import logging +import socket from homeassistant.components.media_player import ( MediaPlayerDevice, STATE_NO_APP, ATTR_MEDIA_STATE, ATTR_MEDIA_CONTENT_ID, ATTR_MEDIA_TITLE, ATTR_MEDIA_ARTIST, ATTR_MEDIA_ALBUM, ATTR_MEDIA_DATE, ATTR_MEDIA_DURATION, - ATTR_MEDIA_VOLUME, MEDIA_STATE_PLAYING, MEDIA_STATE_STOPPED) + ATTR_MEDIA_VOLUME, MEDIA_STATE_PAUSED, MEDIA_STATE_PLAYING, + MEDIA_STATE_STOPPED, MEDIA_STATE_UNKNOWN) _LOGGER = logging.getLogger(__name__) @@ -32,9 +35,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None): daemon = config.get('server', None) port = config.get('port', 6600) + location = config.get('location', 'MPD') + # pylint: disable=unused-argument try: - from mpd import MPDClient, ConnectionError + from mpd import MPDClient except ImportError: _LOGGER.exception( @@ -43,9 +48,20 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return None + # pylint: disable=unused-argument + try: + mpd_client = MPDClient() + mpd_client.connect(daemon, port) + except socket.error: + _LOGGER.error( + "Unable to connect to MPD. " + "Please check your settings") + + return None + mpd = [] if daemon is not None and port is not None: - mpd.append(MpdDevice(daemon, port)) + mpd.append(MpdDevice(daemon, port, location)) add_devices(mpd) @@ -53,12 +69,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class MpdDevice(MediaPlayerDevice): """ Represents a MPD server. """ - def __init__(self, server, port): + def __init__(self, server, port, location): from mpd import MPDClient self.server = server self.port = port - self._name = 'MPD' + self._name = location self.state_attr = {ATTR_MEDIA_STATE: MEDIA_STATE_STOPPED} self.client = MPDClient() @@ -82,6 +98,20 @@ class MpdDevice(MediaPlayerDevice): else: return self.client.currentsong()['artist'] + @property + def media_state(self): + """ Returns the media state. """ + media_controller = self.client.status() + + if media_controller['state'] == 'play': + return MEDIA_STATE_PLAYING + elif media_controller['state'] == 'pause': + return MEDIA_STATE_PAUSED + elif media_controller['state'] == 'stop': + return MEDIA_STATE_STOPPED + else: + return MEDIA_STATE_UNKNOWN + # pylint: disable=no-member @property def state_attributes(self): @@ -92,11 +122,6 @@ class MpdDevice(MediaPlayerDevice): if not status and not current_song: state_attr = {} - if status['state'] == 'play': - state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_PLAYING - else: - state_attr[ATTR_MEDIA_STATE] = MEDIA_STATE_STOPPED - if current_song['id']: state_attr[ATTR_MEDIA_CONTENT_ID] = current_song['id'] @@ -121,25 +146,25 @@ class MpdDevice(MediaPlayerDevice): def turn_off(self): """ Service to exit the running MPD. """ - self.client.close() + self.client.stop() def volume_up(self): """ Service to send the MPD the command for volume up. """ current_volume = self.client.status()['volume'] - if current_volume != '-1': + if int(current_volume) <= 100: self.client.setvol(int(current_volume) + 5) def volume_down(self): """ Service to send the MPD the command for volume down. """ current_volume = self.client.status()['volume'] - if current_volume != '-1': + if int(current_volume) >= 0: self.client.setvol(int(current_volume) - 5) def media_play_pause(self): """ Service to send the MPD the command for play/pause. """ - self.client.stop() + self.client.pause() def media_play(self): """ Service to send the MPD the command for play/pause. """ From 26c8d81dbf5811d05ae1b1b08d85f4d1920cd68f Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 31 May 2015 22:21:11 +0200 Subject: [PATCH 8/9] add configuration desc --- homeassistant/components/media_player/mpd.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/mpd.py b/homeassistant/components/media_player/mpd.py index 37ddf1b3769..0905588cfef 100644 --- a/homeassistant/components/media_player/mpd.py +++ b/homeassistant/components/media_player/mpd.py @@ -11,10 +11,23 @@ config/configuration.yaml media_player: platform: mpd - location: bedroom server: 127.0.0.1 port: 6600 + location: bedroom +Variables: + +server +*Required +IP address of the Music Player Daemon. Example: 192.168.1.32 + +port +*Optional +Port of the Music Player Daemon, defaults to 6600. Example: 6600 + +location +*Optional +Location of your Music Player Daemon. """ import logging import socket From fb63198688ee68dfe2855ce7a53a8fe4ba83c058 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Mon, 1 Jun 2015 13:25:55 +0200 Subject: [PATCH 9/9] fix return values, disconnect from mpd, and fix pylint issue --- homeassistant/components/media_player/mpd.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/media_player/mpd.py b/homeassistant/components/media_player/mpd.py index 0905588cfef..53faa37a605 100644 --- a/homeassistant/components/media_player/mpd.py +++ b/homeassistant/components/media_player/mpd.py @@ -50,7 +50,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None): port = config.get('port', 6600) location = config.get('location', 'MPD') - # pylint: disable=unused-argument try: from mpd import MPDClient @@ -59,23 +58,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "Unable to import mpd2. " "Did you maybe not install the 'python-mpd2' package?") - return None + return False - # pylint: disable=unused-argument + # pylint: disable=no-member try: mpd_client = MPDClient() mpd_client.connect(daemon, port) + mpd_client.close() + mpd_client.disconnect() except socket.error: _LOGGER.error( "Unable to connect to MPD. " "Please check your settings") - return None + return False mpd = [] - if daemon is not None and port is not None: - mpd.append(MpdDevice(daemon, port, location)) - + mpd.append(MpdDevice(daemon, port, location)) add_devices(mpd)