Added support for multiple players
parent
30e24296c4
commit
db2cbf33c3
|
@ -10,34 +10,24 @@ To use SqueezeBox add something like this to your configuration:
|
|||
|
||||
media_player:
|
||||
platform: squeezebox
|
||||
name: SqueezeBox
|
||||
server: 192.168.1.21
|
||||
player: Player1
|
||||
host: 192.168.1.21
|
||||
port: 9090
|
||||
user: user
|
||||
username: user
|
||||
password: password
|
||||
|
||||
Variables:
|
||||
|
||||
name
|
||||
*Optional
|
||||
The name of the device
|
||||
|
||||
server
|
||||
host
|
||||
*Required
|
||||
The address of the Logitech Media Server
|
||||
|
||||
player
|
||||
*Required
|
||||
The unique name of the player
|
||||
The host name or address of the Logitech Media Server
|
||||
|
||||
port
|
||||
*Optional
|
||||
Telnet port to Logitech Media Server, default 9090
|
||||
|
||||
user
|
||||
usermame
|
||||
*Optional
|
||||
User, if password protection is enabled
|
||||
Username, if password protection is enabled
|
||||
|
||||
password
|
||||
*Optional
|
||||
|
@ -51,8 +41,10 @@ import urllib.parse
|
|||
from homeassistant.components.media_player import (
|
||||
MediaPlayerDevice, SUPPORT_PAUSE, SUPPORT_SEEK, SUPPORT_VOLUME_SET,
|
||||
SUPPORT_VOLUME_MUTE, SUPPORT_PREVIOUS_TRACK, SUPPORT_NEXT_TRACK,
|
||||
MEDIA_TYPE_MUSIC)
|
||||
MEDIA_TYPE_MUSIC, DOMAIN)
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_HOST, CONF_USERNAME, CONF_PASSWORD,
|
||||
STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_UNKNOWN)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -64,15 +56,104 @@ SUPPORT_SQUEEZEBOX = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE |\
|
|||
# pylint: disable=unused-argument
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
""" Sets up the squeezebox platform. """
|
||||
add_devices([
|
||||
SqueezeBoxDevice(
|
||||
config.get('name', 'SqueezeBox'),
|
||||
config.get('server'),
|
||||
config.get('player'),
|
||||
config.get('port', '9090'),
|
||||
config.get('user', None),
|
||||
config.get('password', None)
|
||||
)])
|
||||
if not config.get(CONF_HOST):
|
||||
_LOGGER.error(
|
||||
"Missing required configuration items in {}: {}".format(
|
||||
DOMAIN, CONF_HOST))
|
||||
return False
|
||||
|
||||
lms = LogitechMediaServer(
|
||||
config.get(CONF_HOST),
|
||||
config.get('port', '9090'),
|
||||
config.get(CONF_USERNAME),
|
||||
config.get(CONF_PASSWORD))
|
||||
|
||||
if not lms.init_success:
|
||||
return False
|
||||
|
||||
add_devices(lms.create_players())
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class LogitechMediaServer(object):
|
||||
def __init__(self, host, port, username, password):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self._username = username
|
||||
self._password = password
|
||||
self.http_port = self._get_http_port()
|
||||
self.init_success = True if self.http_port else False
|
||||
|
||||
def _get_http_port(self):
|
||||
""" Get http port from media server, it is used to get cover art """
|
||||
http_port = None
|
||||
try:
|
||||
http_port = self.query('pref', 'httpport', '?')
|
||||
if not http_port:
|
||||
_LOGGER.error(
|
||||
"Unable to read data from server {}:{}".format(
|
||||
self.host,
|
||||
self.port))
|
||||
return
|
||||
return http_port
|
||||
except ConnectionError as ex:
|
||||
_LOGGER.error(
|
||||
"Failed to connect to server {}:{} - {}".format(
|
||||
self.host,
|
||||
self.port,
|
||||
ex))
|
||||
return
|
||||
|
||||
def create_players(self):
|
||||
""" Create a list of SqueezeBoxDevices connected to the LMS """
|
||||
players = []
|
||||
count = self.query('player', 'count', '?')
|
||||
for index in range(0, int(count)):
|
||||
player_id = self.query('player', 'id', str(index), '?')
|
||||
player = SqueezeBoxDevice(self, player_id)
|
||||
players.append(player)
|
||||
return players
|
||||
|
||||
def query(self, *parameters):
|
||||
""" Send request and await response from server """
|
||||
telnet = telnetlib.Telnet(self.host, self.port)
|
||||
if self._username and self._password:
|
||||
telnet.write('login {username} {password}\n'.format(
|
||||
username=self._username,
|
||||
password=self._password).encode('UTF-8'))
|
||||
telnet.read_until(b'\n', timeout=3)
|
||||
message = '{}\n'.format(' '.join(parameters))
|
||||
telnet.write(message.encode('UTF-8'))
|
||||
response = telnet.read_until(b'\n', timeout=3)\
|
||||
.decode('UTF-8')\
|
||||
.split(' ')[-1]\
|
||||
.strip()
|
||||
telnet.write(b'exit\n')
|
||||
return urllib.parse.unquote(response)
|
||||
|
||||
def get_player_status(self, player):
|
||||
""" Get ithe status of a player """
|
||||
# (title) : Song title
|
||||
# Requested Information
|
||||
# a (artist): Artist name 'artist'
|
||||
# d (duration): Song duration in seconds 'duration'
|
||||
# K (artwork_url): URL to remote artwork
|
||||
tags = 'adK'
|
||||
new_status = {}
|
||||
telnet = telnetlib.Telnet(self.host, self.port)
|
||||
telnet.write('{player} status - 1 tags:{tags}\n'.format(
|
||||
player=player,
|
||||
tags=tags
|
||||
).encode('UTF-8'))
|
||||
response = telnet.read_until(b'\n', timeout=3)\
|
||||
.decode('UTF-8')\
|
||||
.split(' ')
|
||||
telnet.write(b'exit\n')
|
||||
for item in response:
|
||||
parts = urllib.parse.unquote(item).partition(':')
|
||||
new_status[parts[0]] = parts[2]
|
||||
return new_status
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
@ -81,17 +162,12 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
|||
""" Represents a SqueezeBox device. """
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def __init__(self, name, server, player, port, user, password):
|
||||
def __init__(self, lms, player_id):
|
||||
super(SqueezeBoxDevice, self).__init__()
|
||||
self._name = name
|
||||
self._server = server
|
||||
self._player = player
|
||||
self._port = port
|
||||
self._user = user
|
||||
self._password = password
|
||||
self._status = {}
|
||||
self.update()
|
||||
self._http_port = self._query('pref', 'httpport', '?')
|
||||
self._lms = lms
|
||||
self._id = player_id
|
||||
self._name = self._lms.query(self._id, 'name', '?')
|
||||
self._status = self._lms.get_player_status(self._id)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -113,47 +189,8 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
|||
return STATE_UNKNOWN
|
||||
|
||||
def update(self):
|
||||
self._get_status()
|
||||
|
||||
def _query(self, *parameters):
|
||||
""" Send request and await response from server """
|
||||
telnet = telnetlib.Telnet(self._server, self._port)
|
||||
if self._user and self._password:
|
||||
telnet.write('login {user} {password}\n'.format(
|
||||
user=self._user,
|
||||
password=self._password).encode('UTF-8'))
|
||||
telnet.read_until(b'\n', timeout=3)
|
||||
message = '{}\n'.format(' '.join(parameters))
|
||||
telnet.write(message.encode('UTF-8'))
|
||||
response = telnet.read_until(b'\n', timeout=3)\
|
||||
.decode('UTF-8')\
|
||||
.split(' ')[-1]\
|
||||
.strip()
|
||||
telnet.write(b'exit\n')
|
||||
return urllib.parse.unquote(response)
|
||||
|
||||
def _get_status(self):
|
||||
""" request status and parse result """
|
||||
# (title) : Song title
|
||||
# Requested Information
|
||||
# a (artist): Artist name 'artist'
|
||||
# d (duration): Song duration in seconds 'duration'
|
||||
# K (artwork_url): URL to remote artwork
|
||||
tags = 'adK'
|
||||
new_status = {}
|
||||
telnet = telnetlib.Telnet(self._server, self._port)
|
||||
telnet.write('{player} status - 1 tags:{tags}\n'.format(
|
||||
player=self._player,
|
||||
tags=tags
|
||||
).encode('UTF-8'))
|
||||
response = telnet.read_until(b'\n', timeout=3)\
|
||||
.decode('UTF-8')\
|
||||
.split(' ')
|
||||
telnet.write(b'exit\n')
|
||||
for item in response:
|
||||
parts = urllib.parse.unquote(item).partition(':')
|
||||
new_status[parts[0]] = parts[2]
|
||||
self._status = new_status
|
||||
""" Retrieve latest state. """
|
||||
self._status = self._lms.get_player_status(self._name)
|
||||
|
||||
@property
|
||||
def volume_level(self):
|
||||
|
@ -190,9 +227,9 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
|||
return self._status['artwork_url']
|
||||
return 'http://{server}:{port}/music/current/cover.jpg?player={player}'\
|
||||
.format(
|
||||
server=self._server,
|
||||
port=self._http_port,
|
||||
player=self._player)
|
||||
server=self._lms.host,
|
||||
port=self._lms.http_port,
|
||||
player=self._id)
|
||||
|
||||
@property
|
||||
def media_title(self):
|
||||
|
@ -212,64 +249,64 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
|||
|
||||
def turn_off(self):
|
||||
""" turn_off media player. """
|
||||
self._query(self._player, 'power', '0')
|
||||
self._lms.query(self._id, 'power', '0')
|
||||
self.update_ha_state()
|
||||
|
||||
def volume_up(self):
|
||||
""" volume_up media player. """
|
||||
self._query(self._player, 'mixer', 'volume', '+5')
|
||||
self._lms.query(self._id, 'mixer', 'volume', '+5')
|
||||
self.update_ha_state()
|
||||
|
||||
def volume_down(self):
|
||||
""" volume_down media player. """
|
||||
self._query(self._player, 'mixer', 'volume', '-5')
|
||||
self._lms.query(self._id, 'mixer', 'volume', '-5')
|
||||
self.update_ha_state()
|
||||
|
||||
def set_volume_level(self, volume):
|
||||
""" set volume level, range 0..1. """
|
||||
volume_percent = str(int(volume*100))
|
||||
self._query(self._player, 'mixer', 'volume', volume_percent)
|
||||
self._lms.query(self._id, 'mixer', 'volume', volume_percent)
|
||||
self.update_ha_state()
|
||||
|
||||
def mute_volume(self, mute):
|
||||
""" mute (true) or unmute (false) media player. """
|
||||
mute_numeric = '1' if mute else '0'
|
||||
self._query(self._player, 'mixer', 'muting', mute_numeric)
|
||||
self._lms.query(self._id, 'mixer', 'muting', mute_numeric)
|
||||
self.update_ha_state()
|
||||
|
||||
def media_play_pause(self):
|
||||
""" media_play_pause media player. """
|
||||
self._query(self._player, 'pause')
|
||||
self._lms.query(self._id, 'pause')
|
||||
self.update_ha_state()
|
||||
|
||||
def media_play(self):
|
||||
""" media_play media player. """
|
||||
self._query(self._player, 'play')
|
||||
self._lms.query(self._id, 'play')
|
||||
self.update_ha_state()
|
||||
|
||||
def media_pause(self):
|
||||
""" media_pause media player. """
|
||||
self._query(self._player, 'pause', '0')
|
||||
self._lms.query(self._id, 'pause', '0')
|
||||
self.update_ha_state()
|
||||
|
||||
def media_next_track(self):
|
||||
""" Send next track command. """
|
||||
self._query(self._player, 'playlist', 'index', '+1')
|
||||
self._lms.query(self._id, 'playlist', 'index', '+1')
|
||||
self.update_ha_state()
|
||||
|
||||
def media_previous_track(self):
|
||||
""" Send next track command. """
|
||||
self._query(self._player, 'playlist', 'index', '-1')
|
||||
self._lms.query(self._id, 'playlist', 'index', '-1')
|
||||
self.update_ha_state()
|
||||
|
||||
def media_seek(self, position):
|
||||
""" Send seek command. """
|
||||
self._query(self._player, 'time', position)
|
||||
self._lms.query(self._id, 'time', position)
|
||||
self.update_ha_state()
|
||||
|
||||
def turn_on(self):
|
||||
""" turn the media player on. """
|
||||
self._query(self._player, 'power', '1')
|
||||
self._lms.query(self._id, 'power', '1')
|
||||
self.update_ha_state()
|
||||
|
||||
def play_youtube(self, media_id):
|
||||
|
|
Loading…
Reference in New Issue