lib/sonos/endpoint/a_v_transport.rb in sonos-0.3.4 vs lib/sonos/endpoint/a_v_transport.rb in sonos-0.3.5
- old
+ new
@@ -34,80 +34,140 @@
def has_music?
!now_playing.nil?
end
+ # Get information about the state the player is in.
+ def get_player_state
+ response = send_transport_message('GetTransportInfo')
+ body = response.body[:get_transport_info_response]
+
+ {
+ status: body[:current_transport_status],
+ state: body[:current_transport_state],
+ speed: body[:current_speed],
+ }
+ end
+
# Pause the currently playing track.
def pause
- send_transport_message('Pause')
+ parse_response send_transport_message('Pause')
end
# Play the currently selected track or play a stream.
# @param [String] uri Optional uri of the track to play. Leaving this blank, plays the current track.
def play(uri = nil)
# Play a song from the uri
set_av_transport_uri(uri) and return if uri
# Play the currently selected track
- send_transport_message('Play')
+ parse_response send_transport_message('Play')
end
# Stop playing.
def stop
- send_transport_message('Stop')
+ parse_response send_transport_message('Stop')
end
# Play the next track.
def next
- send_transport_message('Next')
+ parse_response send_transport_message('Next')
end
# Play the previous track.
def previous
- send_transport_message('Previous')
+ parse_response send_transport_message('Previous')
end
+
+ def line_in(speaker)
+ set_av_transport_uri('x-rincon-stream:' + speaker.uid.sub('uuid:', ''))
+ end
# Seeks to a given timestamp in the current track
# @param [Fixnum] seconds
def seek(seconds = 0)
# Must be sent in the format of HH:MM:SS
timestamp = Time.at(seconds).utc.strftime('%H:%M:%S')
- send_transport_message('Seek', "<Unit>REL_TIME</Unit><Target>#{timestamp}</Target>")
+ parse_response send_transport_message('Seek', "<Unit>REL_TIME</Unit><Target>#{timestamp}</Target>")
end
# Clear the queue
def clear_queue
- send_transport_message('RemoveAllTracksFromQueue')
+ parse_response parse_response send_transport_message('RemoveAllTracksFromQueue')
end
# Save queue
def save_queue(title)
- send_transport_message('SaveQueue', "<Title>#{title}</Title><ObjectID></ObjectID>")
+ parse_response send_transport_message('SaveQueue', "<Title>#{title}</Title><ObjectID></ObjectID>")
end
# Adds a track to the queue
# @param[String] uri Uri of track
+ # @param[String] didl Stanza of DIDL-Lite metadata (generally created by #add_spotify_to_queue)
# @return[Integer] Queue position of the added track
- def add_to_queue(uri)
- response = send_transport_message('AddURIToQueue', "<EnqueuedURI>#{uri}</EnqueuedURI><EnqueuedURIMetaData></EnqueuedURIMetaData><DesiredFirstTrackNumberEnqueued>0</DesiredFirstTrackNumberEnqueued><EnqueueAsNext>1</EnqueueAsNext>")
+ def add_to_queue(uri, didl = '')
+ response = send_transport_message('AddURIToQueue', "<EnqueuedURI>#{uri}</EnqueuedURI><EnqueuedURIMetaData>#{didl}</EnqueuedURIMetaData><DesiredFirstTrackNumberEnqueued>0</DesiredFirstTrackNumberEnqueued><EnqueueAsNext>1</EnqueueAsNext>")
# TODO yeah, this error handling is a bit soft. For consistency's sake :)
pos = response.xpath('.//FirstTrackNumberEnqueued').text
if pos.length != 0
pos.to_i
end
end
+ # Adds a Spotify track to the queue along with extra data for better metadata retrieval
+ # @param[Hash] opts Various options (id, user, region and type)
+ # @return[Integer] Queue position of the added track(s)
+ def add_spotify_to_queue(opts = {})
+ opts = {
+ :id => '',
+ :user => nil,
+ :region => nil,
+ :type => 'track'
+ }.merge(opts)
+
+ # Basic validation of the accepted types; playlists need an associated user
+ # and the toplist (for tracks and albums) need to specify a region.
+ return nil if opts[:type] == 'playlist' and opts[:user].nil?
+ return nil if opts[:type] =~ /toplist_tracks/ and opts[:region].nil?
+
+ # In order for the player to retrieve track duration, artist, album etc
+ # we need to pass it some metadata ourselves.
+ didl_metadata = "<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="#{rand(10000000..99999999)}spotify%3a#{opts[:type]}%3a#{opts[:id]}" restricted="true"><dc:title></dc:title><upnp:class>object.item.audioItem.musicTrack</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON2311_X_#Svc2311-0-Token</desc></item></DIDL-Lite>"
+
+ r_id = rand(10000000..99999999)
+
+ case opts[:type]
+ when /playlist/
+ uri = "x-rincon-cpcontainer:#{r_id}spotify%3auser%3a#{opts[:user]}%3aplaylist%3a#{opts[:id]}"
+ when /toplist_(tracks)/
+ subtype = opts[:type].sub('toplist_', '') # only 'tracks' are supported right now by Sonos.
+ uri = "x-rincon-cpcontainer:#{r_id}toplist%2f#{subtype}%2fregion%2f#{opts[:region]}"
+ when /album/
+ uri = "x-rincon-cpcontainer:#{r_id}spotify%3aalbum%3a#{opts[:id]}"
+ when /artist/
+ uri = "x-rincon-cpcontainer:#{r_id}tophits%3aspotify%3aartist%3a#{opts[:id]}"
+ when /starred/
+ uri = "x-rincon-cpcontainer:#{r_id}starred"
+ when /track/
+ uri = "x-sonos-spotify:spotify%3a#{opts[:type]}%3a#{opts[:id]}"
+ else
+ return nil
+ end
+
+ add_to_queue(uri, didl_metadata)
+ end
+
# Removes a track from the queue
# @param[String] object_id Track's queue ID
def remove_from_queue(object_id)
- send_transport_message('RemoveTrackFromQueue', "<ObjectID>#{object_id}</ObjectID><UpdateID>0</UpdateID></u:RemoveTrackFromQueue>")
+ parse_response send_transport_message('RemoveTrackFromQueue', "<ObjectID>#{object_id}</ObjectID><UpdateID>0</UpdateID></u:RemoveTrackFromQueue>")
end
# Join another speaker's group.
# Trying to call this on a stereo pair slave will fail.
def join(master)
- set_av_transport_uri('x-rincon:' + master.uid.sub('uuid:', ''))
+ parse_response set_av_transport_uri('x-rincon:' + master.uid.sub('uuid:', ''))
end
# Add another speaker to this group.
# Trying to call this on a stereo pair slave will fail.
def group(slave)
@@ -115,21 +175,21 @@
end
# Ungroup from its current group.
# Trying to call this on a stereo pair slave will fail.
def ungroup
- send_transport_message('BecomeCoordinatorOfStandaloneGroup')
+ parse_response send_transport_message('BecomeCoordinatorOfStandaloneGroup')
end
private
# Play a stream.
def set_av_transport_uri(uri)
send_transport_message('SetAVTransportURI', "<CurrentURI>#{uri}</CurrentURI><CurrentURIMetaData></CurrentURIMetaData>")
end
def transport_client
- @transport_client ||= Savon.client endpoint: "http://#{self.group_master.ip}:#{Sonos::PORT}#{TRANSPORT_ENDPOINT}", namespace: Sonos::NAMESPACE, log_level: :error
+ @transport_client ||= Savon.client endpoint: "http://#{self.group_master.ip}:#{Sonos::PORT}#{TRANSPORT_ENDPOINT}", namespace: Sonos::NAMESPACE, log: Sonos.logging_enabled
end
def send_transport_message(name, part = '<Speed>1</Speed>')
action = "#{TRANSPORT_XMLNS}##{name}"
message = %Q{<u:#{name} xmlns:u="#{TRANSPORT_XMLNS}"><InstanceID>0</InstanceID>#{part}</u:#{name}>}