Module: Sonos::Endpoint::AVTransport
- Included in:
- Device::Speaker
- Defined in:
- lib/sonos/endpoint/a_v_transport.rb
Constant Summary collapse
- TRANSPORT_ENDPOINT =
'/MediaRenderer/AVTransport/Control'
- TRANSPORT_XMLNS =
'urn:schemas-upnp-org:service:AVTransport:1'
Instance Method Summary collapse
-
#add_rdio_to_queue(opts = {}, position = 0) ⇒ Object
Add an Rdio object to the queue (album or track), anything else can only be streamed (play now).
- #add_spotify_to_queue(opts = {}, position = 0) ⇒ Object
- #add_to_queue(uri, didl = '', position = 0) ⇒ Object
-
#clear_queue ⇒ Object
Clear the queue.
-
#get_player_state ⇒ Object
Get information about the state the player is in.
-
#group(slave) ⇒ Object
Add another speaker to this group.
- #has_music? ⇒ Boolean
-
#is_playing? ⇒ Boolean
Returns true if the player is not in a paused or stopped state.
-
#join(master) ⇒ Object
Join another speaker’s group.
- #line_in(speaker) ⇒ Object
-
#next ⇒ Object
Play the next track.
-
#now_playing ⇒ Hash
Get information about the currently playing track.
-
#pause ⇒ Object
Pause the currently playing track.
-
#play(uri = nil) ⇒ Object
Play the currently selected track or play a stream.
-
#previous ⇒ Object
Play the previous track.
-
#remove_from_queue(object_id) ⇒ Object
Removes a track from the queue @param object_id Track’s queue ID.
-
#save_queue(title) ⇒ Object
Save queue.
-
#seek(seconds = 0) ⇒ Object
Seeks to a given timestamp in the current track.
-
#select_track(index) ⇒ Object
Seeks the playlist selection to the provided index.
-
#set_sleep_timer(duration) ⇒ Object
Set a sleep timer up to 23:59:59 E.g.
-
#stop ⇒ Object
Stop playing.
-
#ungroup ⇒ Object
Ungroup from its current group.
Instance Method Details
#add_rdio_to_queue(opts = {}, position = 0) ⇒ Object
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 177 def add_rdio_to_queue(opts = {}, position = 0) opts = { :username => nil, :album => nil, :track => nil, :type => 'track', :format => 'mp3' # can be changed, but only 'mp3' is valid. }.merge(opts) return nil if opts[:username].nil? # Both tracks and albums require the album key. And tracks need a track # key of course. return nil if opts[:album].nil? return nil if opts[:type] == 'track' and opts[:track].nil? # In order for valid DIDL we'll pass an empty :track for albums. opts[:track] = '' if opts[:type] == 'album' = "<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="00030020_t%3a%3a#{opts[:track]}%3a%3aa%3a%3a#{opts[:album]}" parentID="0004006c_a%3a%3a#{opts[:album]}" 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_RINCON2823_#{opts[:username]}</desc></item></DIDL-Lite>" case opts[:type] when /track/ uri = "x-sonos-http:_t%3a%3a#{opts[:track]}%3a%3aa%3a%3a#{opts[:album]}.#{opts[:format]}?sid=11&flags=32" when /album/ type_id = '0004006c_a' uri = "x-rincon-cpcontainer:#{type_id}%3a%3a#{opts[:album]}" else return nil end add_to_queue(uri, , position) end |
#add_spotify_to_queue(opts = {}, position = 0) ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 132 def add_spotify_to_queue(opts = {}, position = 0) 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-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, , position) end |
#add_to_queue(uri, didl = '', position = 0) ⇒ Object
119 120 121 122 123 124 125 126 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 119 def add_to_queue(uri, didl = '', position = 0) response = ('AddURIToQueue', "<EnqueuedURI>#{uri}</EnqueuedURI><EnqueuedURIMetaData>#{didl}</EnqueuedURIMetaData><DesiredFirstTrackNumberEnqueued>#{position}</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 |
#clear_queue ⇒ Object
Clear the queue
105 106 107 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 105 def clear_queue parse_response ('RemoveAllTracksFromQueue') end |
#get_player_state ⇒ Object
Get information about the state the player is in.
40 41 42 43 44 45 46 47 48 49 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 40 def get_player_state response = ('GetTransportInfo') body = response.body[:get_transport_info_response] { status: body[:current_transport_status], state: body[:current_transport_state], speed: body[:current_speed], } end |
#group(slave) ⇒ Object
Add another speaker to this group. Trying to call this on a stereo pair slave will fail.
225 226 227 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 225 def group(slave) slave.join(self) end |
#has_music? ⇒ Boolean
35 36 37 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 35 def has_music? !.nil? end |
#is_playing? ⇒ Boolean
Returns true if the player is not in a paused or stopped state
52 53 54 55 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 52 def state = get_player_state[:state] !['PAUSED_PLAYBACK', 'STOPPED'].include?(state) end |
#join(master) ⇒ Object
Join another speaker’s group. Trying to call this on a stereo pair slave will fail.
219 220 221 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 219 def join(master) parse_response set_av_transport_uri('x-rincon:' + master.uid.sub('uuid:', '')) end |
#line_in(speaker) ⇒ Object
87 88 89 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 87 def line_in(speaker) set_av_transport_uri('x-rincon-stream:' + speaker.uid.sub('uuid:', '')) end |
#next ⇒ Object
Play the next track.
78 79 80 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 78 def next parse_response ('Next') end |
#now_playing ⇒ Hash
Get information about the currently playing track.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 9 def response = ('GetPositionInfo') body = response.body[:get_position_info_response] doc = Nokogiri::XML(body[:track_meta_data]) # No music return nil if doc.children.length == 0 art_path = doc.xpath('//upnp:albumArtURI').inner_text # TODO: No idea why this is necessary. Maybe its a Nokogiri thing art_path.sub!('/getaa?s=1=x-sonos-http', '/getaa?s=1&u=x-sonos-http') { title: doc.xpath('//dc:title').inner_text, artist: doc.xpath('//dc:creator').inner_text, album: doc.xpath('//upnp:album').inner_text, info: doc.xpath('//r:streamContent').inner_text, queue_position: body[:track], track_duration: body[:track_duration], current_position: body[:rel_time], uri: body[:track_uri], album_art: "http://#{self.ip}:#{Sonos::PORT}#{art_path}" } end |
#pause ⇒ Object
Pause the currently playing track.
58 59 60 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 58 def pause parse_response ('Pause') end |
#play(uri = nil) ⇒ Object
Play the currently selected track or play a stream.
64 65 66 67 68 69 70 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 64 def play(uri = nil) # Play a song from the uri set_av_transport_uri(uri) and return if uri # Play the currently selected track parse_response ('Play') end |
#previous ⇒ Object
Play the previous track.
83 84 85 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 83 def previous parse_response ('Previous') end |
#remove_from_queue(object_id) ⇒ Object
Removes a track from the queue @param object_id Track’s queue ID
213 214 215 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 213 def remove_from_queue(object_id) parse_response ('RemoveTrackFromQueue', "<ObjectID>#{object_id}</ObjectID><UpdateID>0</UpdateID></u:RemoveTrackFromQueue>") end |
#save_queue(title) ⇒ Object
Save queue
110 111 112 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 110 def save_queue(title) parse_response ('SaveQueue', "<Title>#{title}</Title><ObjectID></ObjectID>") end |
#seek(seconds = 0) ⇒ Object
Seeks to a given timestamp in the current track
93 94 95 96 97 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 93 def seek(seconds = 0) # Must be sent in the format of HH:MM:SS = Time.at(seconds).utc.strftime('%H:%M:%S') parse_response ('Seek', "<Unit>REL_TIME</Unit><Target>#{}</Target>") end |
#select_track(index) ⇒ Object
Seeks the playlist selection to the provided index
100 101 102 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 100 def select_track(index) parse_response ('Seek', "<Unit>TRACK_NR</Unit><Target>#{index}</Target>") end |
#set_sleep_timer(duration) ⇒ Object
Set a sleep timer up to 23:59:59 E.g. ‘00:11:00’ for 11 minutes.
238 239 240 241 242 243 244 245 246 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 238 def set_sleep_timer(duration) if duration.nil? duration = '' elsif duration.gsub(':', '').to_i > 235959 duration = '23:59:59' end parse_response ('ConfigureSleepTimer', "<NewSleepTimerDuration>#{duration}</NewSleepTimerDuration>") end |
#stop ⇒ Object
Stop playing.
73 74 75 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 73 def stop parse_response ('Stop') end |
#ungroup ⇒ Object
Ungroup from its current group. Trying to call this on a stereo pair slave will fail.
231 232 233 |
# File 'lib/sonos/endpoint/a_v_transport.rb', line 231 def ungroup parse_response ('BecomeCoordinatorOfStandaloneGroup') end |