Skip to content
Merged
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
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ omit =
homeassistant/components/media_player/aquostv.py
homeassistant/components/media_player/braviatv.py
homeassistant/components/media_player/cast.py
homeassistant/components/media_player/clementine.py
homeassistant/components/media_player/cmus.py
homeassistant/components/media_player/denon.py
homeassistant/components/media_player/denonavr.py
Expand Down
220 changes: 220 additions & 0 deletions homeassistant/components/media_player/clementine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
"""
Support for Clementine Music Player as media player.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/media_player.clementine/
"""

import asyncio
from datetime import timedelta
import logging
import time

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.components.media_player import (
SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PREVIOUS_TRACK, PLATFORM_SCHEMA,
SUPPORT_VOLUME_STEP, SUPPORT_SELECT_SOURCE, SUPPORT_PLAY, MEDIA_TYPE_MUSIC,
SUPPORT_VOLUME_SET, MediaPlayerDevice)
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_PORT, CONF_ACCESS_TOKEN,
STATE_OFF, STATE_PLAYING, STATE_PAUSED, STATE_UNKNOWN)

REQUIREMENTS = ['python-clementine-remote==1.0.1']

SCAN_INTERVAL = timedelta(seconds=5)

_LOGGER = logging.getLogger(__name__)

DEFAULT_NAME = 'Clementine Remote'

SUPPORT_CLEMENTINE = SUPPORT_PAUSE | SUPPORT_VOLUME_STEP | \
SUPPORT_PREVIOUS_TRACK | SUPPORT_VOLUME_SET | \
SUPPORT_NEXT_TRACK | \
SUPPORT_SELECT_SOURCE | SUPPORT_PLAY

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=5500): cv.positive_int,
vol.Optional(CONF_ACCESS_TOKEN, default=None): cv.positive_int,
})


# pylint: disable=unused-argument
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup the Clementine platform."""
from clementineremote import ClementineRemote
client = ClementineRemote(config.get(CONF_HOST), config.get(CONF_PORT),
config.get(CONF_ACCESS_TOKEN), reconnect=True)

add_devices([ClementineDevice(client, config[CONF_NAME])])


class ClementineDevice(MediaPlayerDevice):
"""Representation of Clementine Player."""

def __init__(self, client, name):
"""Initialize the Clementine device."""
self._client = client
self._name = name
self._muted = False
self._volume = 0.0
self._track_id = 0
self._last_track_id = 0
self._track_name = ''
self._track_artist = ''
self._track_album_name = ''
self._state = STATE_UNKNOWN

def update(self):
"""Retrieve the latest data from the Clementine Player."""
try:
client = self._client

if client.state == 'Playing':
self._state = STATE_PLAYING
elif client.state == 'Paused':
self._state = STATE_PAUSED
elif client.state == 'Disconnected':
self._state = STATE_OFF
else:
self._state = STATE_PAUSED

if client.last_update and (time.time() - client.last_update > 40):
self._state = STATE_OFF

self._volume = float(client.volume) if client.volume else 0.0

if client.current_track:
self._track_id = client.current_track['track_id']
self._track_name = client.current_track['title']
self._track_artist = client.current_track['track_artist']
self._track_album_name = client.current_track['track_album']

except:
self._state = STATE_OFF
raise

@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 volume_level(self):
"""Volume level of the media player (0..1)."""
return self._volume / 100.0

@property
def source(self):
"""Return current source name."""
source_name = "Unknown"
client = self._client
if client.active_playlist_id in client.playlists:
source_name = client.playlists[client.active_playlist_id]['name']
return source_name

@property
def source_list(self):
"""List of available input sources."""
source_names = [s["name"] for s in self._client.playlists.values()]
return source_names

def select_source(self, source):
"""Select input source."""
client = self._client
sources = [s for s in client.playlists.values() if s['name'] == source]
if len(sources) == 1:
client.change_song(sources[0]['id'], 0)

@property
def media_content_type(self):
"""Content type of current playing media."""
return MEDIA_TYPE_MUSIC

@property
def media_title(self):
"""Title of current playing media."""
return self._track_name

@property
def media_artist(self):
"""Artist of current playing media, music track only."""
return self._track_artist

@property
def media_album_name(self):
"""Album name of current playing media, music track only."""
return self._track_album_name

@property
def supported_features(self):
"""Flag media player features that are supported."""
return SUPPORT_CLEMENTINE

@property
def media_image_hash(self):
"""Hash value for media image."""
if self._client.current_track:
return self._client.current_track['track_id']

return None

@asyncio.coroutine
def async_get_media_image(self):
"""Fetch media image of current playing image."""
if self._client.current_track:
image = bytes(self._client.current_track['art'])
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Your stay inside Event Loop. Your Access only of cached value? You can not access to a file or network resource on this Point.

return (image, 'image/png')

return None, None

def volume_up(self):
"""Volume up the media player."""
newvolume = min(self._client.volume + 4, 100)
self._client.set_volume(newvolume)

def volume_down(self):
"""Volume down media player."""
newvolume = max(self._client.volume - 4, 0)
self._client.set_volume(newvolume)

def mute_volume(self, mute):
"""Send mute command."""
self._client.set_volume(0)

def set_volume_level(self, volume):
"""Set volume level."""
self._client.set_volume(int(100 * volume))

def media_play_pause(self):
"""Simulate play pause media player."""
if self._state == STATE_PLAYING:
self.media_pause()
else:
self.media_play()

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.next()

def media_previous_track(self):
"""Send the previous track command."""
self._client.previous()
3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,9 @@ pysma==0.1.3
# homeassistant.components.sensor.snmp
pysnmp==4.3.3

# homeassistant.components.media_player.clementine
python-clementine-remote==1.0.1

# homeassistant.components.digital_ocean
python-digitalocean==1.10.1

Expand Down