Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ homeassistant/components/solax/* @squishykid
homeassistant/components/soma/* @ratsept
homeassistant/components/somfy/* @tetienne
homeassistant/components/songpal/* @rytilahti
homeassistant/components/sonos/* @amelchio
homeassistant/components/spaceapi/* @fabaff
homeassistant/components/speedtestdotnet/* @rohankapoorcom
homeassistant/components/spider/* @peternijssen
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/sonos/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
"name": "Sonos",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/sonos",
"requirements": ["pysonos==0.0.24"],
"requirements": ["pysonos==0.0.25"],
"dependencies": [],
"ssdp": [
{
"st": "urn:schemas-upnp-org:device:ZonePlayer:1"
}
],
"codeowners": []
"codeowners": ["@amelchio"]
}
122 changes: 29 additions & 93 deletions homeassistant/components/sonos/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
import functools as ft
import logging
import socket
import urllib

import async_timeout
import pysonos
from pysonos import alarms
from pysonos.exceptions import SoCoException, SoCoUPnPException
import pysonos.music_library
import pysonos.snapshot
import voluptuous as vol

Expand Down Expand Up @@ -338,19 +338,6 @@ def _timespan_secs(timespan):
return sum(60 ** x[0] * int(x[1]) for x in enumerate(reversed(timespan.split(":"))))


def _is_radio_uri(uri):
"""Return whether the URI is a stream (not a playlist)."""
radio_schemes = (
"x-rincon-mp3radio:",
"x-sonosapi-stream:",
"x-sonosapi-radio:",
"x-sonosapi-hls:",
"hls-radio:",
"x-rincon-stream:",
)
return uri.startswith(radio_schemes)


class SonosEntity(MediaPlayerDevice):
"""Representation of a Sonos entity."""

Expand Down Expand Up @@ -515,17 +502,6 @@ def _set_favorites(self):
# Skip unknown types
_LOGGER.error("Unhandled favorite '%s': %s", fav.title, ex)

def _radio_artwork(self, url):
"""Return the private URL with artwork for a radio stream."""
if url in UNAVAILABLE_VALUES:
return None

if url.find("tts_proxy") > 0:
# If the content is a tts don't try to fetch an image from it.
return None

return f"http://{self.soco.ip_address}:1400/getaa?s=1&u={urllib.parse.quote(url, safe='')}"

def _attach_player(self):
"""Get basic information and add event subscriptions."""
try:
Expand Down Expand Up @@ -576,6 +552,14 @@ def update_media(self, event=None):

self._shuffle = self.soco.shuffle
self._uri = None
self._media_duration = None
self._media_position = None
self._media_position_updated_at = None
self._media_image_url = None
self._media_artist = None
self._media_album_name = None
self._media_title = None
self._source_name = None

update_position = new_status != self._status
self._status = new_status
Expand All @@ -587,8 +571,11 @@ def update_media(self, event=None):
else:
track_info = self.soco.get_current_track_info()
self._uri = track_info["uri"]
self._media_artist = track_info.get("artist")
self._media_album_name = track_info.get("album")
self._media_title = track_info.get("title")

if _is_radio_uri(track_info["uri"]):
if self.soco.is_radio_uri(track_info["uri"]):
variables = event and event.variables
self.update_media_radio(variables, track_info)
else:
Expand All @@ -604,74 +591,29 @@ def update_media(self, event=None):

def update_media_linein(self, source):
"""Update state when playing from line-in/tv."""
self._media_duration = None
self._media_position = None
self._media_position_updated_at = None

self._media_image_url = None

self._media_artist = None
self._media_album_name = None
self._media_title = source

self._source_name = source

def update_media_radio(self, variables, track_info):
"""Update state when streaming radio."""
self._media_duration = None
self._media_position = None
self._media_position_updated_at = None

media_info = self.soco.avTransport.GetMediaInfo([("InstanceID", 0)])
self._media_image_url = self._radio_artwork(media_info["CurrentURI"])

self._media_artist = track_info.get("artist")
self._media_album_name = None
self._media_title = track_info.get("title")

if self._media_artist and self._media_title:
# artist and album name are in the data, concatenate
# that do display as artist.
# "Information" field in the sonos pc app
self._media_artist = "{artist} - {title}".format(
artist=self._media_artist, title=self._media_title
)
elif variables:
# "On Now" field in the sonos pc app
current_track_metadata = variables.get("current_track_meta_data")
if current_track_metadata:
self._media_artist = current_track_metadata.radio_show.split(",")[0]

# For radio streams we set the radio station name as the title.
current_uri_metadata = media_info["CurrentURIMetaData"]
if current_uri_metadata not in UNAVAILABLE_VALUES:
# currently soco does not have an API for this
current_uri_metadata = pysonos.xml.XML.fromstring(
pysonos.utils.really_utf8(current_uri_metadata)
)

md_title = current_uri_metadata.findtext(
".//{http://purl.org/dc/elements/1.1/}title"
)

if md_title not in UNAVAILABLE_VALUES:
self._media_title = md_title

if self._media_artist and self._media_title:
# some radio stations put their name into the artist
# name, e.g.:
# media_title = "Station"
# media_artist = "Station - Artist - Title"
# detect this case and trim from the front of
# media_artist for cosmetics
trim = f"{self._media_title} - "
chars = min(len(self._media_artist), len(trim))
try:
library = pysonos.music_library.MusicLibrary(self.soco)
album_art_uri = variables["current_track_meta_data"].album_art_uri
self._media_image_url = library.build_album_art_full_uri(album_art_uri)
except (TypeError, KeyError, AttributeError):
pass

if self._media_artist[:chars].upper() == trim[:chars].upper():
self._media_artist = self._media_artist[chars:]
# Radios without tagging can have the radio URI as title. Non-playing
# radios will not have a current title. In these cases we try to use
# the radio name instead.
try:
if self.soco.is_radio_uri(self._media_title) or self.state != STATE_PLAYING:
self._media_title = variables["enqueued_transport_uri_meta_data"].title
except (TypeError, KeyError, AttributeError):
pass

# Check if currently playing radio station is in favorites
self._source_name = None
media_info = self.soco.avTransport.GetMediaInfo([("InstanceID", 0)])
for fav in self._favorites:
if fav.reference.get_uri() == media_info["CurrentURI"]:
self._source_name = fav.title
Expand Down Expand Up @@ -710,12 +652,6 @@ def update_media_music(self, update_media_position, track_info):

self._media_image_url = track_info.get("album_art")

self._media_artist = track_info.get("artist")
self._media_album_name = track_info.get("album")
self._media_title = track_info.get("title")

self._source_name = None

def update_volume(self, event=None):
"""Update information about currently volume settings."""
if event:
Expand Down Expand Up @@ -936,7 +872,7 @@ def select_source(self, source):
if len(fav) == 1:
src = fav.pop()
uri = src.reference.get_uri()
if _is_radio_uri(uri):
if self.soco.is_radio_uri(uri):
self.soco.play_uri(uri, title=source)
else:
self.soco.clear_queue()
Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1545,7 +1545,7 @@ pysnmp==4.4.12
pysoma==0.0.10

# homeassistant.components.sonos
pysonos==0.0.24
pysonos==0.0.25

# homeassistant.components.spc
pyspcwebgw==0.4.0
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ pysmartthings==0.7.0
pysoma==0.0.10

# homeassistant.components.sonos
pysonos==0.0.24
pysonos==0.0.25

# homeassistant.components.spc
pyspcwebgw==0.4.0
Expand Down