Revert "Use entity class attributes for Bluesound (#53033)" (#54365)

pull/54369/head
Paulus Schoutsen 2021-08-09 16:45:39 -07:00 committed by GitHub
parent d80da944a3
commit a40deac714
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 115 additions and 70 deletions

View File

@ -203,29 +203,33 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
class BluesoundPlayer(MediaPlayerEntity): class BluesoundPlayer(MediaPlayerEntity):
"""Representation of a Bluesound Player.""" """Representation of a Bluesound Player."""
_attr_media_content_type = MEDIA_TYPE_MUSIC def __init__(self, hass, host, port=None, name=None, init_callback=None):
def __init__(self, hass, host, port=DEFAULT_PORT, name=None, init_callback=None):
"""Initialize the media player.""" """Initialize the media player."""
self.host = host self.host = host
self._hass = hass self._hass = hass
self.port = port self.port = port
self._polling_session = async_get_clientsession(hass) self._polling_session = async_get_clientsession(hass)
self._polling_task = None # The actual polling task. self._polling_task = None # The actual polling task.
self._attr_name = name self._name = name
self._icon = None
self._capture_items = [] self._capture_items = []
self._services_items = [] self._services_items = []
self._preset_items = [] self._preset_items = []
self._sync_status = {} self._sync_status = {}
self._status = None self._status = None
self._is_online = None self._last_status_update = None
self._is_online = False
self._retry_remove = None self._retry_remove = None
self._muted = False
self._master = None self._master = None
self._group_name = None
self._bluesound_device_name = None
self._is_master = False self._is_master = False
self._group_name = None
self._group_list = [] self._group_list = []
self._bluesound_device_name = None
self._init_callback = init_callback self._init_callback = init_callback
if self.port is None:
self.port = DEFAULT_PORT
class _TimeoutException(Exception): class _TimeoutException(Exception):
pass pass
@ -248,12 +252,12 @@ class BluesoundPlayer(MediaPlayerEntity):
return None return None
self._sync_status = resp["SyncStatus"].copy() self._sync_status = resp["SyncStatus"].copy()
if not self.name: if not self._name:
self._attr_name = self._sync_status.get("@name", self.host) self._name = self._sync_status.get("@name", self.host)
if not self._bluesound_device_name: if not self._bluesound_device_name:
self._bluesound_device_name = self._sync_status.get("@name", self.host) self._bluesound_device_name = self._sync_status.get("@name", self.host)
if not self.icon: if not self._icon:
self._attr_icon = self._sync_status.get("@icon", self.host) self._icon = self._sync_status.get("@icon", self.host)
master = self._sync_status.get("master") master = self._sync_status.get("master")
if master is not None: if master is not None:
@ -287,14 +291,14 @@ class BluesoundPlayer(MediaPlayerEntity):
await self.async_update_status() await self.async_update_status()
except (asyncio.TimeoutError, ClientError, BluesoundPlayer._TimeoutException): except (asyncio.TimeoutError, ClientError, BluesoundPlayer._TimeoutException):
_LOGGER.info("Node %s is offline, retrying later", self.name) _LOGGER.info("Node %s is offline, retrying later", self._name)
await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT) await asyncio.sleep(NODE_OFFLINE_CHECK_TIMEOUT)
self.start_polling() self.start_polling()
except CancelledError: except CancelledError:
_LOGGER.debug("Stopping the polling of node %s", self.name) _LOGGER.debug("Stopping the polling of node %s", self._name)
except Exception: except Exception:
_LOGGER.exception("Unexpected error in %s", self.name) _LOGGER.exception("Unexpected error in %s", self._name)
raise raise
def start_polling(self): def start_polling(self):
@ -398,7 +402,7 @@ class BluesoundPlayer(MediaPlayerEntity):
if response.status == HTTP_OK: if response.status == HTTP_OK:
result = await response.text() result = await response.text()
self._is_online = True self._is_online = True
self._attr_media_position_updated_at = dt_util.utcnow() self._last_status_update = dt_util.utcnow()
self._status = xmltodict.parse(result)["status"].copy() self._status = xmltodict.parse(result)["status"].copy()
group_name = self._status.get("groupName") group_name = self._status.get("groupName")
@ -434,58 +438,11 @@ class BluesoundPlayer(MediaPlayerEntity):
except (asyncio.TimeoutError, ClientError): except (asyncio.TimeoutError, ClientError):
self._is_online = False self._is_online = False
self._attr_media_position_updated_at = None self._last_status_update = None
self._status = None self._status = None
self.async_write_ha_state() self.async_write_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 raise
self.update_state_attr()
def update_state_attr(self):
"""Update state attributes."""
if self._status is None:
self._attr_state = STATE_OFF
self._attr_supported_features = 0
elif self.is_grouped and not self.is_master:
self._attr_state = STATE_GROUPED
self._attr_supported_features = (
SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE
)
else:
status = self._status.get("state")
self._attr_state = STATE_IDLE
if status in ("pause", "stop"):
self._attr_state = STATE_PAUSED
elif status in ("stream", "play"):
self._attr_state = STATE_PLAYING
supported = SUPPORT_CLEAR_PLAYLIST
if self._status.get("indexing", "0") == "0":
supported = (
supported
| SUPPORT_PAUSE
| SUPPORT_PREVIOUS_TRACK
| SUPPORT_NEXT_TRACK
| SUPPORT_PLAY_MEDIA
| SUPPORT_STOP
| SUPPORT_PLAY
| SUPPORT_SELECT_SOURCE
| SUPPORT_SHUFFLE_SET
)
if self.volume_level is not None and self.volume_level >= 0:
supported = (
supported
| SUPPORT_VOLUME_STEP
| SUPPORT_VOLUME_SET
| SUPPORT_VOLUME_MUTE
)
if self._status.get("canSeek", "") == "1":
supported = supported | SUPPORT_SEEK
self._attr_supported_features = supported
self._attr_extra_state_attributes = {}
if self._group_list:
self._attr_extra_state_attributes = {ATTR_BLUESOUND_GROUP: self._group_list}
self._attr_extra_state_attributes[ATTR_MASTER] = self._is_master
self._attr_shuffle = self._status.get("shuffle", "0") == "1"
async def async_trigger_sync_on_all(self): async def async_trigger_sync_on_all(self):
"""Trigger sync status update on all devices.""" """Trigger sync status update on all devices."""
@ -585,6 +542,27 @@ class BluesoundPlayer(MediaPlayerEntity):
return self._services_items return self._services_items
@property
def media_content_type(self):
"""Content type of current playing media."""
return MEDIA_TYPE_MUSIC
@property
def state(self):
"""Return the state of the device."""
if self._status is None:
return STATE_OFF
if self.is_grouped and not self.is_master:
return STATE_GROUPED
status = self._status.get("state")
if status in ("pause", "stop"):
return STATE_PAUSED
if status in ("stream", "play"):
return STATE_PLAYING
return STATE_IDLE
@property @property
def media_title(self): def media_title(self):
"""Title of current playing media.""" """Title of current playing media."""
@ -639,7 +617,7 @@ class BluesoundPlayer(MediaPlayerEntity):
return None return None
mediastate = self.state mediastate = self.state
if self.media_position_updated_at is None or mediastate == STATE_IDLE: if self._last_status_update is None or mediastate == STATE_IDLE:
return None return None
position = self._status.get("secs") position = self._status.get("secs")
@ -648,9 +626,7 @@ class BluesoundPlayer(MediaPlayerEntity):
position = float(position) position = float(position)
if mediastate == STATE_PLAYING: if mediastate == STATE_PLAYING:
position += ( position += (dt_util.utcnow() - self._last_status_update).total_seconds()
dt_util.utcnow() - self.media_position_updated_at
).total_seconds()
return position return position
@ -665,6 +641,11 @@ class BluesoundPlayer(MediaPlayerEntity):
return None return None
return float(duration) return float(duration)
@property
def media_position_updated_at(self):
"""Last time status was updated."""
return self._last_status_update
@property @property
def volume_level(self): def volume_level(self):
"""Volume level of the media player (0..1).""" """Volume level of the media player (0..1)."""
@ -687,11 +668,21 @@ class BluesoundPlayer(MediaPlayerEntity):
mute = bool(int(mute)) mute = bool(int(mute))
return mute return mute
@property
def name(self):
"""Return the name of the device."""
return self._name
@property @property
def bluesound_device_name(self): def bluesound_device_name(self):
"""Return the device name as returned by the device.""" """Return the device name as returned by the device."""
return self._bluesound_device_name return self._bluesound_device_name
@property
def icon(self):
"""Return the icon of the device."""
return self._icon
@property @property
def source_list(self): def source_list(self):
"""List of available input sources.""" """List of available input sources."""
@ -787,15 +778,58 @@ class BluesoundPlayer(MediaPlayerEntity):
return None return None
@property @property
def is_master(self) -> bool: def supported_features(self):
"""Flag of media commands that are supported."""
if self._status is None:
return 0
if self.is_grouped and not self.is_master:
return SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE
supported = SUPPORT_CLEAR_PLAYLIST
if self._status.get("indexing", "0") == "0":
supported = (
supported
| SUPPORT_PAUSE
| SUPPORT_PREVIOUS_TRACK
| SUPPORT_NEXT_TRACK
| SUPPORT_PLAY_MEDIA
| SUPPORT_STOP
| SUPPORT_PLAY
| SUPPORT_SELECT_SOURCE
| SUPPORT_SHUFFLE_SET
)
current_vol = self.volume_level
if current_vol is not None and current_vol >= 0:
supported = (
supported
| SUPPORT_VOLUME_STEP
| SUPPORT_VOLUME_SET
| SUPPORT_VOLUME_MUTE
)
if self._status.get("canSeek", "") == "1":
supported = supported | SUPPORT_SEEK
return supported
@property
def is_master(self):
"""Return true if player is a coordinator.""" """Return true if player is a coordinator."""
return self._is_master return self._is_master
@property @property
def is_grouped(self) -> bool: def is_grouped(self):
"""Return true if player is a coordinator.""" """Return true if player is a coordinator."""
return self._master is not None or self._is_master return self._master is not None or self._is_master
@property
def shuffle(self):
"""Return true if shuffle is active."""
return self._status.get("shuffle", "0") == "1"
async def async_join(self, master): async def async_join(self, master):
"""Join the player to a group.""" """Join the player to a group."""
master_device = [ master_device = [
@ -815,6 +849,17 @@ class BluesoundPlayer(MediaPlayerEntity):
else: else:
_LOGGER.error("Master not found %s", master_device) _LOGGER.error("Master not found %s", master_device)
@property
def extra_state_attributes(self):
"""List members in group."""
attributes = {}
if self._group_list:
attributes = {ATTR_BLUESOUND_GROUP: self._group_list}
attributes[ATTR_MASTER] = self._is_master
return attributes
def rebuild_bluesound_group(self): def rebuild_bluesound_group(self):
"""Rebuild the list of entities in speaker group.""" """Rebuild the list of entities in speaker group."""
if self._group_name is None: if self._group_name is None: