Work better with password-protected Squeezebox / LMS servers (#3953)
* Work better with password-protected Squeezebox / LMS servers, including getting file art. Refactored to only have a single method calling the telent lib. (Should make it easier to convert to the more appropriate JSON interface) * Update squeezebox.py * Update squeezebox.py * Update squeezebox.py * Update squeezebox.py * Update squeezebox.pypull/3987/head
parent
4f6ed09a99
commit
754d536974
|
@ -40,6 +40,8 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
"""Setup the squeezebox platform."""
|
"""Setup the squeezebox platform."""
|
||||||
|
import socket
|
||||||
|
|
||||||
username = config.get(CONF_USERNAME)
|
username = config.get(CONF_USERNAME)
|
||||||
password = config.get(CONF_PASSWORD)
|
password = config.get(CONF_PASSWORD)
|
||||||
|
|
||||||
|
@ -50,11 +52,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
host = config.get(CONF_HOST)
|
host = config.get(CONF_HOST)
|
||||||
port = config.get(CONF_PORT)
|
port = config.get(CONF_PORT)
|
||||||
|
|
||||||
# Only add a media server once
|
# Get IP of host, to prevent duplication of same host (different DNS names)
|
||||||
if host in KNOWN_DEVICES:
|
try:
|
||||||
|
ipaddr = socket.gethostbyname(host)
|
||||||
|
except (OSError) as error:
|
||||||
|
_LOGGER.error("Could not communicate with %s:%d: %s",
|
||||||
|
host, port, error)
|
||||||
return False
|
return False
|
||||||
KNOWN_DEVICES.append(host)
|
|
||||||
|
|
||||||
|
# Combine it with port to allow multiple servers at the same host
|
||||||
|
key = "{}:{}".format(ipaddr, port)
|
||||||
|
|
||||||
|
# Only add a media server once
|
||||||
|
if key in KNOWN_DEVICES:
|
||||||
|
return False
|
||||||
|
KNOWN_DEVICES.append(key)
|
||||||
|
|
||||||
|
_LOGGER.debug("Creating LMS object for %s", key)
|
||||||
lms = LogitechMediaServer(host, port, username, password)
|
lms = LogitechMediaServer(host, port, username, password)
|
||||||
|
|
||||||
if not lms.init_success:
|
if not lms.init_success:
|
||||||
|
@ -97,30 +111,12 @@ class LogitechMediaServer(object):
|
||||||
|
|
||||||
def query(self, *parameters):
|
def query(self, *parameters):
|
||||||
"""Send request and await response from server."""
|
"""Send request and await response from server."""
|
||||||
try:
|
response = urllib.parse.unquote(self.get(' '.join(parameters)))
|
||||||
telnet = telnetlib.Telnet(self.host, self.port)
|
|
||||||
if self._username and self._password:
|
return response.split(' ')[-1].strip()
|
||||||
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)
|
|
||||||
except (OSError, ConnectionError) as error:
|
|
||||||
_LOGGER.error("Could not communicate with %s:%d: %s",
|
|
||||||
self.host,
|
|
||||||
self.port,
|
|
||||||
error)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_player_status(self, player):
|
def get_player_status(self, player):
|
||||||
"""Get ithe status of a player."""
|
"""Get the status of a player."""
|
||||||
# (title) : Song title
|
# (title) : Song title
|
||||||
# Requested Information
|
# Requested Information
|
||||||
# a (artist): Artist name 'artist'
|
# a (artist): Artist name 'artist'
|
||||||
|
@ -129,25 +125,50 @@ class LogitechMediaServer(object):
|
||||||
# l (album): Album, including the server's "(N of M)"
|
# l (album): Album, including the server's "(N of M)"
|
||||||
tags = 'adKl'
|
tags = 'adKl'
|
||||||
new_status = {}
|
new_status = {}
|
||||||
try:
|
response = self.get('{player} status - 1 tags:{tags}\n'
|
||||||
telnet = telnetlib.Telnet(self.host, self.port)
|
.format(player=player, tags=tags))
|
||||||
telnet.write('{player} status - 1 tags:{tags}\n'.format(
|
|
||||||
player=player,
|
if not response:
|
||||||
tags=tags
|
return {}
|
||||||
).encode('UTF-8'))
|
|
||||||
response = telnet.read_until(b'\n', timeout=3)\
|
response = response.split(' ')
|
||||||
.decode('UTF-8')\
|
|
||||||
.split(' ')
|
|
||||||
telnet.write(b'exit\n')
|
|
||||||
for item in response:
|
for item in response:
|
||||||
parts = urllib.parse.unquote(item).partition(':')
|
parts = urllib.parse.unquote(item).partition(':')
|
||||||
new_status[parts[0]] = parts[2]
|
new_status[parts[0]] = parts[2]
|
||||||
except (OSError, ConnectionError) as error:
|
return new_status
|
||||||
|
|
||||||
|
def get(self, command):
|
||||||
|
"""Abstract out the telnet connection."""
|
||||||
|
try:
|
||||||
|
telnet = telnetlib.Telnet(self.host, self.port)
|
||||||
|
|
||||||
|
if self._username and self._password:
|
||||||
|
_LOGGER.debug("Logging in")
|
||||||
|
|
||||||
|
telnet.write('login {username} {password}\n'.format(
|
||||||
|
username=self._username,
|
||||||
|
password=self._password).encode('UTF-8'))
|
||||||
|
telnet.read_until(b'\n', timeout=3)
|
||||||
|
|
||||||
|
_LOGGER.debug("About to send message: %s", command)
|
||||||
|
message = '{}\n'.format(command)
|
||||||
|
telnet.write(message.encode('UTF-8'))
|
||||||
|
|
||||||
|
response = telnet.read_until(b'\n', timeout=3)\
|
||||||
|
.decode('UTF-8')\
|
||||||
|
|
||||||
|
telnet.write(b'exit\n')
|
||||||
|
_LOGGER.debug("Response: %s", response)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
except (OSError, ConnectionError, EOFError) as error:
|
||||||
_LOGGER.error("Could not communicate with %s:%d: %s",
|
_LOGGER.error("Could not communicate with %s:%d: %s",
|
||||||
self.host,
|
self.host,
|
||||||
self.port,
|
self.port,
|
||||||
error)
|
error)
|
||||||
return new_status
|
return None
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
@ -228,11 +249,22 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
||||||
media_url = ('/music/current/cover.jpg?player={player}').format(
|
media_url = ('/music/current/cover.jpg?player={player}').format(
|
||||||
player=self._id)
|
player=self._id)
|
||||||
|
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
if self._lms._username:
|
||||||
|
base_url = 'http://{username}:{password}@{server}:{port}/'.format(
|
||||||
|
username=self._lms._username,
|
||||||
|
password=self._lms._password,
|
||||||
|
server=self._lms.host,
|
||||||
|
port=self._lms.http_port)
|
||||||
|
else:
|
||||||
base_url = 'http://{server}:{port}/'.format(
|
base_url = 'http://{server}:{port}/'.format(
|
||||||
server=self._lms.host,
|
server=self._lms.host,
|
||||||
port=self._lms.http_port)
|
port=self._lms.http_port)
|
||||||
|
|
||||||
return urllib.parse.urljoin(base_url, media_url)
|
url = urllib.parse.urljoin(base_url, media_url)
|
||||||
|
|
||||||
|
_LOGGER.debug("Media image url: %s", url)
|
||||||
|
return url
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_title(self):
|
def media_title(self):
|
||||||
|
@ -337,7 +369,7 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
||||||
"""
|
"""
|
||||||
Replace the current play list with the uri.
|
Replace the current play list with the uri.
|
||||||
|
|
||||||
Telnet Command Strucutre:
|
Telnet Command Structure:
|
||||||
<playerid> playlist play <item> <title> <fadeInSecs>
|
<playerid> playlist play <item> <title> <fadeInSecs>
|
||||||
|
|
||||||
The "playlist play" command puts the specified song URL,
|
The "playlist play" command puts the specified song URL,
|
||||||
|
@ -361,7 +393,7 @@ class SqueezeBoxDevice(MediaPlayerDevice):
|
||||||
"""
|
"""
|
||||||
Add a items to the existing playlist.
|
Add a items to the existing playlist.
|
||||||
|
|
||||||
Telnet Command Strucutre:
|
Telnet Command Structure:
|
||||||
<playerid> playlist add <item>
|
<playerid> playlist add <item>
|
||||||
|
|
||||||
The "playlist add" command adds the specified song URL, playlist or
|
The "playlist add" command adds the specified song URL, playlist or
|
||||||
|
|
Loading…
Reference in New Issue