Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vlc telnet #24290

Merged
merged 20 commits into from Jun 21, 2019
Merged

Vlc telnet #24290

Changes from 11 commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -644,6 +644,7 @@ omit =
homeassistant/components/viaggiatreno/sensor.py
homeassistant/components/vizio/media_player.py
homeassistant/components/vlc/media_player.py
homeassistant/components/vlc_telnet/media_player.py
homeassistant/components/volkszaehler/sensor.py
homeassistant/components/volumio/media_player.py
homeassistant/components/volvooncall/*
@@ -253,6 +253,7 @@ homeassistant/components/utility_meter/* @dgomes
homeassistant/components/velux/* @Julius2342
homeassistant/components/version/* @fabaff
homeassistant/components/vizio/* @raman325
homeassistant/components/vlc_telnet/* @rodripf
homeassistant/components/waqi/* @andrey-git
homeassistant/components/watson_tts/* @rutkai
homeassistant/components/weather/* @fabaff
@@ -0,0 +1 @@
"""The vlc component."""
@@ -0,0 +1,10 @@
{
"domain": "vlc_telnet",
"name": "VLC telnet",
"documentation": "https://www.home-assistant.io/components/vlc-telnet",
"requirements": [
"python-telnet-vlc==1.0.4"
],
"dependencies": [],
"codeowners": ["@rodripf"]
}
@@ -0,0 +1,231 @@
"""Provide functionality to interact with the vlc telnet interface."""
import logging
import voluptuous as vol

from homeassistant.components.media_player import (
MediaPlayerDevice, PLATFORM_SCHEMA)
from homeassistant.components.media_player.const import (
MEDIA_TYPE_MUSIC, SUPPORT_PAUSE, SUPPORT_PLAY,
SUPPORT_PLAY_MEDIA, SUPPORT_STOP, SUPPORT_VOLUME_MUTE,
SUPPORT_VOLUME_SET, SUPPORT_PREVIOUS_TRACK, SUPPORT_SEEK,
SUPPORT_NEXT_TRACK, SUPPORT_CLEAR_PLAYLIST, SUPPORT_SHUFFLE_SET)
from homeassistant.const import (
CONF_NAME, STATE_IDLE, STATE_PAUSED, STATE_PLAYING, STATE_UNAVAILABLE,
CONF_HOST, CONF_PORT, CONF_PASSWORD)
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(__name__)

DOMAIN = 'vlc_telnet'

DEFAULT_NAME = 'Vlc Telnet'
This conversation was marked as resolved by rodripf

This comment has been minimized.

Copy link
@OttoWinter

OttoWinter Jun 4, 2019

Member
Suggested change
DEFAULT_NAME = 'Vlc Telnet'
DEFAULT_NAME = 'VLC Telnet'

?


SUPPORT_VLC = SUPPORT_PAUSE | SUPPORT_SEEK | SUPPORT_VOLUME_SET \
| SUPPORT_VOLUME_MUTE | SUPPORT_PREVIOUS_TRACK \
| SUPPORT_NEXT_TRACK | SUPPORT_PLAY_MEDIA | SUPPORT_STOP \
| SUPPORT_CLEAR_PLAYLIST | SUPPORT_PLAY \
| SUPPORT_SHUFFLE_SET
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOST, default='127.0.0.1'): cv.string,
vol.Optional(CONF_PORT, default='4212'): cv.positive_int,
vol.Required(CONF_PASSWORD, default='test'): cv.string,
This conversation was marked as resolved by rodripf

This comment has been minimized.

Copy link
@OttoWinter

OttoWinter Jun 4, 2019

Member

Should this have a default?

This comment has been minimized.

Copy link
@rodripf

rodripf Jun 4, 2019

Author Contributor

Hi! Thanks for the suggestions! About this, I put the default values thinking on the default values of the Local VLC add-on for hassio, having in mind an easy integration, with less configuration. But I'm not sure. Should I remove it?

This comment has been minimized.

Copy link
@pvizeli

pvizeli Jun 5, 2019

Member

Yes. Set no default for host and password. for other use:

DEFAULT_PORT = 4212
....
vol.Optional(CONF_PORT, default=DEFAULT_PORT)

This comment has been minimized.

Copy link
@rodripf

rodripf Jun 8, 2019

Author Contributor

Done. I'll update now the documentation.

vol.Optional(CONF_NAME, default='VLC-TELNET'): cv.string,
})


def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the vlc platform."""
add_entities([VlcDevice(config.get(CONF_NAME),
config.get(CONF_HOST),
config.get(CONF_PORT),
config.get(CONF_PASSWORD))])


class VlcDevice(MediaPlayerDevice):
"""Representation of a vlc player."""

def __init__(self, name, host, port, passwd):
"""Initialize the vlc device."""
self._instance = None
self._name = name
self._volume = None
self._muted = None
self._state = STATE_UNAVAILABLE
self._media_position_updated_at = None
self._media_position = None
self._media_duration = None
self._host = host
self._port = port
self._password = passwd
self._vlc = None

This comment has been minimized.

Copy link
@OttoWinter

OttoWinter Jun 4, 2019

Member

Though it's probably not too easy to achieve, this could have a timing issue: What if one of the functions like play_media is called before update? Please add , True to add_entities call to update on start.

This comment has been minimized.

Copy link
@rodripf

rodripf Jun 5, 2019

Author Contributor

Added the parameter! There is another case... what if the state is unavailable because the connection to VLC can't be made, but the user calls to play_media. The current behavior will be an exception thrown "None has no attribute...". Should I throw another exception or return False?

self._volume_bkp = 0
self._media_artist = ""
self._media_title = ""
print("loaded!")
This conversation was marked as resolved by rodripf

This comment has been minimized.

Copy link
@OttoWinter

OttoWinter Jun 4, 2019

Member

stale debug print

This comment has been minimized.

Copy link
@rodripf

rodripf Jun 5, 2019

Author Contributor

Removed!


def update(self):
"""Get the latest details from the device."""
from python_telnet_vlc import VLCTelnet, ConnectionError as ConnErr
This conversation was marked as resolved by rodripf

This comment has been minimized.

Copy link
@pvizeli

pvizeli Jun 5, 2019

Member

Import can move on top with new convention


if self._vlc is None:
try:
self._vlc = VLCTelnet(self._host, self._password, self._port)
self._state = STATE_IDLE
except ConnErr:
self._state = STATE_UNAVAILABLE
This conversation was marked as resolved by rodripf

This comment has been minimized.

Copy link
@OttoWinter

OttoWinter Jun 4, 2019

Member

Instead of overriding state, implement def available and return True or False.

This comment has been minimized.

Copy link
@rodripf

rodripf Jun 5, 2019

Author Contributor

Mmm I didn't understand this one. "Available" is not a method to override on the MediaPlayerDevice super class. Instead, "def state" is, which uses the value self._state to return the current state.

This comment has been minimized.

Copy link
@pvizeli

pvizeli Jun 5, 2019

Member
def ini...
   self._available = False # (or true what you like)

@property
def available(self):
   """..COPY ME FROM helpers/entity.py"""
  return self._available

else:
try:
status = self._vlc.status()
if status:
if 'volume' in status:
self._volume = int(status['volume']) / 500.0
else:
self._volume = None
if 'state' in status:
state = status["state"]
if state == "playing":
self._state = STATE_PLAYING
elif state == "paused":
self._state = STATE_PAUSED
else:
self._state = STATE_IDLE
else:
self._state = STATE_IDLE

self._media_duration = self._vlc.get_length()
self._media_position = self._vlc.get_time()

info = self._vlc.info()
if info is not None and info:
This conversation was marked as resolved by rodripf

This comment has been minimized.

Copy link
@OttoWinter

OttoWinter Jun 4, 2019

Member
Suggested change
if info is not None and info:
if info:
if 'artist' in info[0]:
self._media_artist = info[0]['artist']
else:
self._media_artist = None
This conversation was marked as resolved by rodripf

This comment has been minimized.

Copy link
@OttoWinter

OttoWinter Jun 4, 2019

Member

Can be simplified:

Suggested change
self._media_artist = None
self._media_artist = info[0].get('artist')

Same below

This comment has been minimized.

Copy link
@rodripf

rodripf Jun 5, 2019

Author Contributor

Simplified both similar calls!

if 'title' in info[0]:
self._media_title = info[0]['title']
else:
self._media_title = None

except ConnectionError as err:
_LOGGER.error(err)
pass
This conversation was marked as resolved by rodripf

This comment has been minimized.

Copy link
@OttoWinter

OttoWinter Jun 4, 2019

Member
Suggested change
pass

Pass doesn't do anything here.


return True

@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

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

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

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

@property
def media_duration(self):
"""Duration of current playing media in seconds."""
return self._media_duration

@property
def media_position(self):
"""Position of current playing media in seconds."""
return self._media_position

@property
def media_position_updated_at(self):
"""When was the position of the current playing media valid."""
return self._media_position_updated_at

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

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

def media_seek(self, position):
"""Seek the media to a specific location."""
track_length = self._vlc.get_length() / 1000
self._vlc.seek(position / track_length)

def mute_volume(self, mute):
"""Mute the volume."""
if mute:
self._volume_bkp = self._volume
self._volume = 0
self._vlc.set_volume("0")
else:
self._vlc.set_volume(str(self._volume_bkp))
self._volume = self._volume_bkp

self._muted = mute

def set_volume_level(self, volume):
"""Set volume level, range 0..1."""
self._vlc.set_volume(str(volume * 500))
self._volume = volume

def media_play(self):
"""Send play command."""
self._vlc.play()
self._state = STATE_PLAYING

def media_pause(self):
"""Send pause command."""
self._vlc.pause()
self._state = STATE_PAUSED

def media_stop(self):
"""Send stop command."""
self._vlc.stop()
self._state = STATE_IDLE

def play_media(self, media_type, media_id, **kwargs):
"""Play media from a URL or file."""
if not media_type == MEDIA_TYPE_MUSIC:
This conversation was marked as resolved by rodripf

This comment has been minimized.

Copy link
@OttoWinter

OttoWinter Jun 4, 2019

Member
Suggested change
if not media_type == MEDIA_TYPE_MUSIC:
if media_type != MEDIA_TYPE_MUSIC:
_LOGGER.error(
"Invalid media type %s. Only %s is supported",
media_type, MEDIA_TYPE_MUSIC)
return
self._vlc.add(media_id)
self._state = STATE_PLAYING

def media_previous_track(self):
"""Send previous track command."""
self._vlc.prev()

def media_next_track(self):
"""Send next track command."""
self._vlc.next()

def clear_playlist(self):
"""Clear players playlist."""
self._vlc.clear()

def set_shuffle(self, shuffle):
"""Enable/disable shuffle mode."""
self._vlc.random(shuffle)
@@ -1425,6 +1425,9 @@ python-tado==0.2.9
# homeassistant.components.telegram_bot
python-telegram-bot==11.1.0

# homeassistant.components.vlc_telnet
python-telnet-vlc==1.0.4

# homeassistant.components.twitch
python-twitch-client==0.6.0

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.