From bed30a5307f3da8b5c13455df4623f7d53cf51ff Mon Sep 17 00:00:00 2001 From: Per Sandstrom Date: Tue, 4 Aug 2015 17:22:56 +0200 Subject: [PATCH 01/10] added support for logitech squeezebox --- .../components/media_player/squeezebox.py | 273 ++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 homeassistant/components/media_player/squeezebox.py diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py new file mode 100644 index 00000000000..4d88d226f9d --- /dev/null +++ b/homeassistant/components/media_player/squeezebox.py @@ -0,0 +1,273 @@ +""" +homeassistant.components.media_player.squeezebox +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Provides an interface to the Logitech SqueezeBox API + +Configuration: + +To use SqueezeBox add something like this to your configuration: + +media_player: + platform: squeezebox + name: SqueezeBox + server: 192.168.1.21 + player: Player1 + port: 9090 + user: user + password: password + +Variables: + +name +*Optional +The name of the device + +server +*Required +The address of the Logitech Media Server + +player +*Required +The unique name of the player + +port +*Optional +Telnet port to Logitech Media Server, default 9090 + +user +*Optional +User, if password protection is enabled + +password +*Optional +Password, if password protection is enabled +""" + +import logging +import telnetlib +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) +from homeassistant.const import ( + STATE_IDLE, STATE_PLAYING, STATE_PAUSED, STATE_OFF, STATE_UNKNOWN) + +_LOGGER = logging.getLogger(__name__) + +SUPPORT_SQUEEZEBOX = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE |\ + SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK + + +# 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) + )]) + + +# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-public-methods +class SqueezeBoxDevice(MediaPlayerDevice): + """ Represents a SqueezeBox device. """ + + def __init__(self, name, server, player, port, user, password): + 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', '?') + + @property + def name(self): + """ Returns the name of the device. """ + return self._name + + @property + def state(self): + """ Returns the state of the device. """ + if ('power' in self._status and self._status['power'] == '0'): + return STATE_OFF + if('mode' in self._status): + if self._status['mode'] == 'pause': + return STATE_PAUSED + if self._status['mode'] == 'play': + return STATE_PLAYING + if self._status['mode'] == 'stop': + return STATE_IDLE + return STATE_UNKNOWN + + def update(self): + if(self._user and self._password): + self._query('login', self._user, self._password) + self._get_status() + + def _query(self, *parameters): + """ Send request and await response from server """ + telnet = telnetlib.Telnet(self._server, self._port) + 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 + + @property + def volume_level(self): + """ Volume level of the media player (0..1). """ + if('mixer volume' in self._status): + return int(self._status['mixer volume']) / 100.0 + + @property + def is_volume_muted(self): + if('mixer volume' in self._status): + return int(self._status['mixer volume']) < 0 + + @property + def media_content_id(self): + """ Content ID of current playing media. """ + if('current_title' in self._status): + return self._status['current_title'] + + @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. """ + if('duration' in self._status): + return int(float(self._status['duration'])) + + @property + def media_image_url(self): + """ Image url of current playing media. """ + if('artwork_url' in self._status): + 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) + + @property + def media_title(self): + """ Title of current playing media. """ + if('artist' in self._status and 'title' in self._status): + return '{artist} - {title}'.format( + artist=self._status['artist'], + title=self._status['title'] + ) + if('current_title' in self._status): + return self._status['current_title'] + + @property + def supported_media_commands(self): + """ Flags of media commands that are supported. """ + return SUPPORT_SQUEEZEBOX + + def turn_off(self): + """ turn_off media player. """ + self._query(self._player, 'power', '0') + self.update_ha_state() + + def volume_up(self): + """ volume_up media player. """ + self._query(self._player, 'mixer', 'volume', '+5') + self.update_ha_state() + + def volume_down(self): + """ volume_down media player. """ + self._query(self._player, '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.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.update_ha_state() + + def media_play_pause(self): + """ media_play_pause media player. """ + self._query(self._player, 'pause') + self.update_ha_state() + + def media_play(self): + """ media_play media player. """ + self._query(self._player, 'play') + self.update_ha_state() + + def media_pause(self): + """ media_pause media player. """ + self._query(self._player, 'pause', '0') + self.update_ha_state() + + def media_next_track(self): + """ Send next track command. """ + self._query(self._player, 'playlist', 'index', '+1') + self.update_ha_state() + + def media_previous_track(self): + """ Send next track command. """ + self._query(self._player, 'playlist', 'index', '-1') + self.update_ha_state() + + def media_seek(self, position): + """ Send seek command. """ + self._query(self._player, 'time', position) + self.update_ha_state() + + def turn_on(self): + """ turn the media player on. """ + self._query(self._player, 'power', '1') + self.update_ha_state() + + def play_youtube(self, media_id): + """ Plays a YouTube media. """ + raise NotImplementedError() From 4284a3f5dc3e0e8ff5907eb1655e82549a74719e Mon Sep 17 00:00:00 2001 From: Per Sandstrom Date: Tue, 4 Aug 2015 19:35:53 +0200 Subject: [PATCH 02/10] Fixed pylint conventions --- .../components/media_player/squeezebox.py | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index 4d88d226f9d..70e74f3e895 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -80,6 +80,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class SqueezeBoxDevice(MediaPlayerDevice): """ Represents a SqueezeBox device. """ + # pylint: disable=too-many-arguments def __init__(self, name, server, player, port, user, password): super(SqueezeBoxDevice, self).__init__() self._name = name @@ -100,9 +101,9 @@ class SqueezeBoxDevice(MediaPlayerDevice): @property def state(self): """ Returns the state of the device. """ - if ('power' in self._status and self._status['power'] == '0'): + if 'power' in self._status and self._status['power'] == '0': return STATE_OFF - if('mode' in self._status): + if 'mode' in self._status: if self._status['mode'] == 'pause': return STATE_PAUSED if self._status['mode'] == 'play': @@ -112,7 +113,7 @@ class SqueezeBoxDevice(MediaPlayerDevice): return STATE_UNKNOWN def update(self): - if(self._user and self._password): + if self._user and self._password: self._query('login', self._user, self._password) self._get_status() @@ -154,18 +155,18 @@ class SqueezeBoxDevice(MediaPlayerDevice): @property def volume_level(self): """ Volume level of the media player (0..1). """ - if('mixer volume' in self._status): + if 'mixer volume' in self._status: return int(self._status['mixer volume']) / 100.0 @property def is_volume_muted(self): - if('mixer volume' in self._status): + if 'mixer volume' in self._status: return int(self._status['mixer volume']) < 0 @property def media_content_id(self): """ Content ID of current playing media. """ - if('current_title' in self._status): + if 'current_title' in self._status: return self._status['current_title'] @property @@ -176,13 +177,13 @@ class SqueezeBoxDevice(MediaPlayerDevice): @property def media_duration(self): """ Duration of current playing media in seconds. """ - if('duration' in self._status): + if 'duration' in self._status: return int(float(self._status['duration'])) @property def media_image_url(self): """ Image url of current playing media. """ - if('artwork_url' in self._status): + if 'artwork_url' in self._status: return self._status['artwork_url'] return 'http://{server}:{port}/music/current/cover.jpg?player={player}'\ .format( @@ -193,12 +194,12 @@ class SqueezeBoxDevice(MediaPlayerDevice): @property def media_title(self): """ Title of current playing media. """ - if('artist' in self._status and 'title' in self._status): + if 'artist' in self._status and 'title' in self._status: return '{artist} - {title}'.format( artist=self._status['artist'], title=self._status['title'] ) - if('current_title' in self._status): + if 'current_title' in self._status: return self._status['current_title'] @property From e6c09f7413b76137361d178c97f2408b9649c0a6 Mon Sep 17 00:00:00 2001 From: Per Sandstrom Date: Tue, 4 Aug 2015 20:08:48 +0200 Subject: [PATCH 03/10] Fixed bug with password protected LMS --- homeassistant/components/media_player/squeezebox.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index 70e74f3e895..496e0170dc9 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -113,13 +113,16 @@ class SqueezeBoxDevice(MediaPlayerDevice): return STATE_UNKNOWN def update(self): - if self._user and self._password: - self._query('login', self._user, self._password) 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)\ From 30e24296c44aa68ccc1b934240f176a929d7a29a Mon Sep 17 00:00:00 2001 From: Per Sandstrom Date: Tue, 4 Aug 2015 20:30:01 +0200 Subject: [PATCH 04/10] Fixed flake8 --- homeassistant/components/media_player/squeezebox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index 496e0170dc9..70eab958000 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -120,7 +120,7 @@ class SqueezeBoxDevice(MediaPlayerDevice): telnet = telnetlib.Telnet(self._server, self._port) if self._user and self._password: telnet.write('login {user} {password}\n'.format( - user=self._user, + user=self._user, password=self._password).encode('UTF-8')) telnet.read_until(b'\n', timeout=3) message = '{}\n'.format(' '.join(parameters)) From db2cbf33c3172c889fcc70e95efb39bdd8d7e0f1 Mon Sep 17 00:00:00 2001 From: Per Sandstrom Date: Wed, 5 Aug 2015 13:49:45 +0200 Subject: [PATCH 05/10] Added support for multiple players --- .../components/media_player/squeezebox.py | 221 ++++++++++-------- 1 file changed, 129 insertions(+), 92 deletions(-) diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index 70eab958000..fa05e304c29 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -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): From eb83621fce2cb88e6773a26a928a536f5490a237 Mon Sep 17 00:00:00 2001 From: Per Sandstrom Date: Wed, 5 Aug 2015 20:02:39 +0200 Subject: [PATCH 06/10] fixing pylint issues --- .../components/media_player/squeezebox.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index fa05e304c29..16f1b3ef523 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -58,8 +58,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the squeezebox platform. """ if not config.get(CONF_HOST): _LOGGER.error( - "Missing required configuration items in {}: {}".format( - DOMAIN, CONF_HOST)) + "Missing required configuration items in %s: %s", + DOMAIN, CONF_HOST) return False lms = LogitechMediaServer( @@ -77,6 +77,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class LogitechMediaServer(object): + """ Represents a Logitech media server. """ + def __init__(self, host, port, username, password): self.host = host self.port = port @@ -92,17 +94,17 @@ class LogitechMediaServer(object): http_port = self.query('pref', 'httpport', '?') if not http_port: _LOGGER.error( - "Unable to read data from server {}:{}".format( + "Unable to read data from server %s:%s", self.host, - self.port)) + self.port) return return http_port except ConnectionError as ex: _LOGGER.error( - "Failed to connect to server {}:{} - {}".format( + "Failed to connect to server %s:%s - %s", self.host, self.port, - ex)) + ex) return def create_players(self): From 03f93063f84d4086d4cb2e503088082032ee7345 Mon Sep 17 00:00:00 2001 From: Per Sandstrom Date: Wed, 5 Aug 2015 20:09:20 +0200 Subject: [PATCH 07/10] fixed flake8 issues --- .../components/media_player/squeezebox.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index 16f1b3ef523..95715b1e555 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -59,7 +59,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if not config.get(CONF_HOST): _LOGGER.error( "Missing required configuration items in %s: %s", - DOMAIN, CONF_HOST) + DOMAIN, + CONF_HOST) return False lms = LogitechMediaServer( @@ -78,7 +79,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class LogitechMediaServer(object): """ Represents a Logitech media server. """ - + def __init__(self, host, port, username, password): self.host = host self.port = port @@ -95,16 +96,16 @@ class LogitechMediaServer(object): if not http_port: _LOGGER.error( "Unable to read data from server %s:%s", - self.host, - self.port) + self.host, + self.port) return return http_port except ConnectionError as ex: _LOGGER.error( "Failed to connect to server %s:%s - %s", - self.host, - self.port, - ex) + self.host, + self.port, + ex) return def create_players(self): From 393e88e7324428f4c1b8b5f29a4a6081e2d636f2 Mon Sep 17 00:00:00 2001 From: Per Sandstrom Date: Wed, 5 Aug 2015 22:25:03 +0200 Subject: [PATCH 08/10] add to .coveragerc --- .coveragerc | 1 + homeassistant/components/media_player/squeezebox.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index 39a3dee22bf..386480fa36b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -35,6 +35,7 @@ omit = homeassistant/components/light/hue.py homeassistant/components/media_player/cast.py homeassistant/components/media_player/mpd.py + homeassistant/components/media_player/squeezebox.py homeassistant/components/notify/file.py homeassistant/components/notify/instapush.py homeassistant/components/notify/nma.py diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index 95715b1e555..911f8b0faab 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -204,7 +204,8 @@ class SqueezeBoxDevice(MediaPlayerDevice): @property def is_volume_muted(self): if 'mixer volume' in self._status: - return int(self._status['mixer volume']) < 0 + _LOGGER.info(self._status['mixer volume']) + return self._status['mixer volume'].startswith('-') @property def media_content_id(self): From 9ead39e7036f8e6b30458b1b45976091cf71b069 Mon Sep 17 00:00:00 2001 From: Per Sandstrom Date: Thu, 6 Aug 2015 09:05:27 +0200 Subject: [PATCH 09/10] added turn on/off support --- homeassistant/components/media_player/squeezebox.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index 911f8b0faab..e9fa6bea759 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -41,6 +41,7 @@ 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, + SUPPORT_TURN_ON, SUPPORT_TURN_OFF, MEDIA_TYPE_MUSIC, DOMAIN) from homeassistant.const import ( @@ -50,7 +51,8 @@ from homeassistant.const import ( _LOGGER = logging.getLogger(__name__) SUPPORT_SQUEEZEBOX = SUPPORT_PAUSE | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE |\ - SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK + SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | SUPPORT_SEEK |\ + SUPPORT_TURN_ON | SUPPORT_TURN_OFF # pylint: disable=unused-argument From b681cf2eaad51dec15de072d4b94505a2ce93b1c Mon Sep 17 00:00:00 2001 From: Per Sandstrom Date: Thu, 6 Aug 2015 09:27:23 +0200 Subject: [PATCH 10/10] removed unnecessary log row --- homeassistant/components/media_player/squeezebox.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/media_player/squeezebox.py b/homeassistant/components/media_player/squeezebox.py index e9fa6bea759..8cbc087c50c 100644 --- a/homeassistant/components/media_player/squeezebox.py +++ b/homeassistant/components/media_player/squeezebox.py @@ -206,7 +206,6 @@ class SqueezeBoxDevice(MediaPlayerDevice): @property def is_volume_muted(self): if 'mixer volume' in self._status: - _LOGGER.info(self._status['mixer volume']) return self._status['mixer volume'].startswith('-') @property