From b0fd2342dbdf812bb1a8ec0cab07ec0f0e990f03 Mon Sep 17 00:00:00 2001 From: thrawnarn Date: Sat, 7 Apr 2018 10:09:09 +0200 Subject: [PATCH] Bluesound bugfix status 595 and await (#13727) * 595 fix * Await fixes and last 595 fix * Lint * Made internal exception class * Fix lint issue --- .../components/media_player/bluesound.py | 127 +++++++++--------- 1 file changed, 67 insertions(+), 60 deletions(-) diff --git a/homeassistant/components/media_player/bluesound.py b/homeassistant/components/media_player/bluesound.py index 1b6310d4cab..283c4af032e 100644 --- a/homeassistant/components/media_player/bluesound.py +++ b/homeassistant/components/media_player/bluesound.py @@ -37,30 +37,30 @@ REQUIREMENTS = ['xmltodict==0.11.0'] _LOGGER = logging.getLogger(__name__) -STATE_GROUPED = 'grouped' - ATTR_MASTER = 'master' -SERVICE_JOIN = 'bluesound_join' -SERVICE_UNJOIN = 'bluesound_unjoin' -SERVICE_SET_TIMER = 'bluesound_set_sleep_timer' -SERVICE_CLEAR_TIMER = 'bluesound_clear_sleep_timer' - DATA_BLUESOUND = 'bluesound' DEFAULT_PORT = 11000 -SYNC_STATUS_INTERVAL = timedelta(minutes=5) -UPDATE_CAPTURE_INTERVAL = timedelta(minutes=30) -UPDATE_SERVICES_INTERVAL = timedelta(minutes=30) -UPDATE_PRESETS_INTERVAL = timedelta(minutes=30) NODE_OFFLINE_CHECK_TIMEOUT = 180 NODE_RETRY_INITIATION = timedelta(minutes=3) +SERVICE_CLEAR_TIMER = 'bluesound_clear_sleep_timer' +SERVICE_JOIN = 'bluesound_join' +SERVICE_SET_TIMER = 'bluesound_set_sleep_timer' +SERVICE_UNJOIN = 'bluesound_unjoin' +STATE_GROUPED = 'grouped' +SYNC_STATUS_INTERVAL = timedelta(minutes=5) + +UPDATE_CAPTURE_INTERVAL = timedelta(minutes=30) +UPDATE_PRESETS_INTERVAL = timedelta(minutes=30) +UPDATE_SERVICES_INTERVAL = timedelta(minutes=30) + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_HOSTS): vol.All(cv.ensure_list, [{ vol.Required(CONF_HOST): cv.string, - vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, vol.Optional(CONF_NAME): cv.string, + vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port, }]) }) @@ -131,8 +131,8 @@ def _add_player(hass, async_add_devices, host, port=None, name=None): hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, _init_player) -async def async_setup_platform(hass, config, async_add_devices, - discovery_info=None): +async def async_setup_platform( + hass, config, async_add_devices, discovery_info=None): """Set up the Bluesound platforms.""" if DATA_BLUESOUND not in hass.data: hass.data[DATA_BLUESOUND] = [] @@ -202,6 +202,9 @@ class BluesoundPlayer(MediaPlayerDevice): if self.port is None: self.port = DEFAULT_PORT + class _TimeoutException(Exception): + pass + @staticmethod def _try_get_index(string, search_string): """Get the index.""" @@ -258,7 +261,8 @@ class BluesoundPlayer(MediaPlayerDevice): while True: await self.async_update_status() - except (asyncio.TimeoutError, ClientError): + except (asyncio.TimeoutError, ClientError, + BluesoundPlayer._TimeoutException): _LOGGER.info("Node %s is offline, retrying later", self._name) await asyncio.sleep( NODE_OFFLINE_CHECK_TIMEOUT, loop=self._hass.loop) @@ -293,8 +297,8 @@ class BluesoundPlayer(MediaPlayerDevice): self._retry_remove = async_track_time_interval( self._hass, self.async_init, NODE_RETRY_INITIATION) except Exception: - _LOGGER.exception("Unexpected when initiating error in %s", - self.host) + _LOGGER.exception( + "Unexpected when initiating error in %s", self.host) raise async def async_update(self): @@ -307,8 +311,8 @@ class BluesoundPlayer(MediaPlayerDevice): await self.async_update_captures() await self.async_update_services() - async def send_bluesound_command(self, method, raise_timeout=False, - allow_offline=False): + async def send_bluesound_command( + self, method, raise_timeout=False, allow_offline=False): """Send command to the player.""" import xmltodict @@ -321,6 +325,7 @@ class BluesoundPlayer(MediaPlayerDevice): _LOGGER.debug("Calling URL: %s", url) response = None + try: websession = async_get_clientsession(self._hass) with async_timeout.timeout(10, loop=self._hass.loop): @@ -332,6 +337,9 @@ class BluesoundPlayer(MediaPlayerDevice): data = None else: data = xmltodict.parse(result) + elif response.status == 595: + _LOGGER.info("Status 595 returned, treating as timeout") + raise BluesoundPlayer._TimeoutException() else: _LOGGER.error("Error %s on %s", response.status, url) return None @@ -366,13 +374,9 @@ class BluesoundPlayer(MediaPlayerDevice): with async_timeout.timeout(125, loop=self._hass.loop): response = await self._polling_session.get( - url, - headers={CONNECTION: KEEP_ALIVE}) + url, headers={CONNECTION: KEEP_ALIVE}) - if response.status != 200: - _LOGGER.error("Error %s on %s. Trying one more time.", - response.status, url) - else: + if response.status == 200: result = await response.text() self._is_online = True self._last_status_update = dt_util.utcnow() @@ -380,8 +384,8 @@ class BluesoundPlayer(MediaPlayerDevice): group_name = self._status.get('groupName', None) if group_name != self._group_name: - _LOGGER.debug('Group name change detected on device: %s', - self.host) + _LOGGER.debug( + "Group name change detected on device: %s", self.host) self._group_name = group_name # the sleep is needed to make sure that the # devices is synced @@ -398,14 +402,20 @@ class BluesoundPlayer(MediaPlayerDevice): await self.force_update_sync_status() self.async_schedule_update_ha_state() + elif response.status == 595: + _LOGGER.info("Status 595 returned, treating as timeout") + raise BluesoundPlayer._TimeoutException() + else: + _LOGGER.error("Error %s on %s. Trying one more time", + response.status, url) except (asyncio.TimeoutError, ClientError): self._is_online = False self._last_status_update = None self._status = None self.async_schedule_update_ha_state() - _LOGGER.info("Client connection error, marking %s as offline", - self._name) + _LOGGER.info( + "Client connection error, marking %s as offline", self._name) raise async def async_trigger_sync_on_all(self): @@ -416,8 +426,8 @@ class BluesoundPlayer(MediaPlayerDevice): await player.force_update_sync_status() @Throttle(SYNC_STATUS_INTERVAL) - async def async_update_sync_status(self, on_updated_cb=None, - raise_timeout=False): + async def async_update_sync_status( + self, on_updated_cb=None, raise_timeout=False): """Update sync status.""" await self.force_update_sync_status( on_updated_cb, raise_timeout=False) @@ -465,7 +475,7 @@ class BluesoundPlayer(MediaPlayerDevice): 'image': item.get('@image', ''), 'is_raw_url': True, 'url2': item.get('@url', ''), - 'url': 'Preset?id=' + item.get('@id', '') + 'url': 'Preset?id={}'.format(item.get('@id', '')) }) if 'presets' in resp and 'preset' in resp['presets']: @@ -503,11 +513,6 @@ class BluesoundPlayer(MediaPlayerDevice): return self._services_items - @property - def should_poll(self): - """No need to poll information.""" - return True - @property def media_content_type(self): """Content type of current playing media.""" @@ -803,22 +808,22 @@ class BluesoundPlayer(MediaPlayerDevice): async def async_add_slave(self, slave_device): """Add slave to master.""" - return self.send_bluesound_command('/AddSlave?slave={}&port={}' - .format(slave_device.host, - slave_device.port)) + return await self.send_bluesound_command( + '/AddSlave?slave={}&port={}'.format( + slave_device.host, slave_device.port)) async def async_remove_slave(self, slave_device): """Remove slave to master.""" - return self.send_bluesound_command('/RemoveSlave?slave={}&port={}' - .format(slave_device.host, - slave_device.port)) + return await self.send_bluesound_command( + '/RemoveSlave?slave={}&port={}'.format( + slave_device.host, slave_device.port)) async def async_increase_timer(self): """Increase sleep time on player.""" sleep_time = await self.send_bluesound_command('/Sleep') if sleep_time is None: - _LOGGER.error('Error while increasing sleep time on player: %s', - self.host) + _LOGGER.error( + "Error while increasing sleep time on player: %s", self.host) return 0 return int(sleep_time.get('sleep', '0')) @@ -831,8 +836,9 @@ class BluesoundPlayer(MediaPlayerDevice): async def async_set_shuffle(self, shuffle): """Enable or disable shuffle mode.""" - return self.send_bluesound_command('/Shuffle?state={}' - .format('1' if shuffle else '0')) + value = '1' if shuffle else '0' + return await self.send_bluesound_command( + '/Shuffle?state={}'.format(value)) async def async_select_source(self, source): """Select input source.""" @@ -856,14 +862,14 @@ class BluesoundPlayer(MediaPlayerDevice): if 'is_raw_url' in selected_source and selected_source['is_raw_url']: url = selected_source['url'] - return self.send_bluesound_command(url) + return await self.send_bluesound_command(url) async def async_clear_playlist(self): """Clear players playlist.""" if self.is_grouped and not self.is_master: return - return self.send_bluesound_command('Clear') + return await self.send_bluesound_command('Clear') async def async_media_next_track(self): """Send media_next command to media player.""" @@ -877,7 +883,7 @@ class BluesoundPlayer(MediaPlayerDevice): action['@name'] == 'skip'): cmd = action['@url'] - return self.send_bluesound_command(cmd) + return await self.send_bluesound_command(cmd) async def async_media_previous_track(self): """Send media_previous command to media player.""" @@ -891,35 +897,36 @@ class BluesoundPlayer(MediaPlayerDevice): action['@name'] == 'back'): cmd = action['@url'] - return self.send_bluesound_command(cmd) + return await self.send_bluesound_command(cmd) async def async_media_play(self): """Send media_play command to media player.""" if self.is_grouped and not self.is_master: return - return self.send_bluesound_command('Play') + return await self.send_bluesound_command('Play') async def async_media_pause(self): """Send media_pause command to media player.""" if self.is_grouped and not self.is_master: return - return self.send_bluesound_command('Pause') + return await self.send_bluesound_command('Pause') async def async_media_stop(self): """Send stop command.""" if self.is_grouped and not self.is_master: return - return self.send_bluesound_command('Pause') + return await self.send_bluesound_command('Pause') async def async_media_seek(self, position): """Send media_seek command to media player.""" if self.is_grouped and not self.is_master: return - return self.send_bluesound_command('Play?seek=' + str(float(position))) + return await self.send_bluesound_command( + 'Play?seek={}'.format(float(position))) async def async_play_media(self, media_type, media_id, **kwargs): """ @@ -933,9 +940,9 @@ class BluesoundPlayer(MediaPlayerDevice): url = 'Play?url={}'.format(media_id) if kwargs.get(ATTR_MEDIA_ENQUEUE): - return self.send_bluesound_command(url) + return await self.send_bluesound_command(url) - return self.send_bluesound_command(url) + return await self.send_bluesound_command(url) async def async_volume_up(self): """Volume up the media player.""" @@ -957,7 +964,7 @@ class BluesoundPlayer(MediaPlayerDevice): volume = 0 elif volume > 1: volume = 1 - return self.send_bluesound_command( + return await self.send_bluesound_command( 'Volume?level=' + str(float(volume) * 100)) async def async_mute_volume(self, mute): @@ -966,7 +973,7 @@ class BluesoundPlayer(MediaPlayerDevice): volume = self.volume_level if volume > 0: self._lastvol = volume - return self.send_bluesound_command('Volume?level=0') + return await self.send_bluesound_command('Volume?level=0') else: - return self.send_bluesound_command( + return await self.send_bluesound_command( 'Volume?level=' + str(float(self._lastvol) * 100))