sonos 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/Readme.markdown +2 -1
- data/lib/sonos/cli.rb +18 -0
- data/lib/sonos/device/base.rb +1 -1
- data/lib/sonos/device/bridge.rb +1 -1
- data/lib/sonos/discovery.rb +6 -2
- data/lib/sonos/endpoint/a_v_transport.rb +28 -2
- data/lib/sonos/endpoint/content_directory.rb +3 -2
- data/lib/sonos/system.rb +17 -2
- data/lib/sonos/version.rb +1 -1
- metadata +12 -22
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ded5809db9b6874541a783058f58e2675f042092
|
4
|
+
data.tar.gz: 16b0167e929a9d328f1420ef41bd38576a34bf85
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7ccf9e8e83d533d33205b57620a0ec7d1de4d81f69faeff100469fe75a9cfed7d56f3ea6093a6f50bfb6c2c803ccf86f88b5e99684fe2ad2c35a00605056f32f
|
7
|
+
data.tar.gz: cdeaed6716f84e1a399c9d8e9049d8f482d717b5e801d14df9feeb71f85b04960116c4aa5dadc8b1d76265c821caa39a7bff607723a0e9da6d74e7dbbfa7ea24
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p195
|
data/Readme.markdown
CHANGED
@@ -53,6 +53,8 @@ speaker.volume
|
|
53
53
|
speaker.volume = 70
|
54
54
|
speaker.volume -= 10
|
55
55
|
speaker.queue
|
56
|
+
speaker.add_to_queue 'http://assets.samsoff.es/music/Airports.mp3'
|
57
|
+
speaker.remove_from_queue(speaker.queue[:items].last[:queue_id])
|
56
58
|
speaker.save_queue 'Jams'
|
57
59
|
speaker.clear_queue
|
58
60
|
```
|
@@ -91,7 +93,6 @@ You can also run `sonos pause_all` to pause all your Sonos groups.
|
|
91
93
|
* Set repeat mode
|
92
94
|
* Search music library
|
93
95
|
* Browse music library
|
94
|
-
* Add songs to queue
|
95
96
|
* Skip to song in queue
|
96
97
|
* Alarm clock
|
97
98
|
* Sleep timer
|
data/lib/sonos/cli.rb
CHANGED
@@ -22,6 +22,24 @@ module Sonos
|
|
22
22
|
system.pause_all
|
23
23
|
end
|
24
24
|
|
25
|
+
desc 'play_all', 'Resumes playing all Sonos speaker groups'
|
26
|
+
def play_all
|
27
|
+
system.play_all
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'groups', 'List all Sonos groups'
|
31
|
+
def groups
|
32
|
+
system.groups.each do |group|
|
33
|
+
puts group.master_speaker.name.ljust(20) + group.master_speaker.ip
|
34
|
+
|
35
|
+
group.slave_speakers.each do |speaker|
|
36
|
+
puts speaker.name.rjust(10).ljust(20) + speaker.ip
|
37
|
+
end
|
38
|
+
|
39
|
+
puts "\n"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
25
43
|
private
|
26
44
|
|
27
45
|
def system
|
data/lib/sonos/device/base.rb
CHANGED
@@ -14,7 +14,7 @@ module Sonos::Device
|
|
14
14
|
elsif Speaker.model_numbers.include?(model_number)
|
15
15
|
Speaker.new(ip, data)
|
16
16
|
else
|
17
|
-
raise ArgumentError.new("#{
|
17
|
+
raise ArgumentError.new("#{data[:model_number]} not supported")
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
data/lib/sonos/device/bridge.rb
CHANGED
data/lib/sonos/discovery.rb
CHANGED
@@ -17,12 +17,15 @@ module Sonos
|
|
17
17
|
MULTICAST_ADDR = '239.255.255.250'
|
18
18
|
MULTICAST_PORT = 1900
|
19
19
|
DEFAULT_TIMEOUT = 1
|
20
|
+
DEFAULT_IP = nil
|
20
21
|
|
21
22
|
attr_reader :timeout
|
22
23
|
attr_reader :first_device_ip
|
24
|
+
attr_reader :default_ip
|
23
25
|
|
24
|
-
def initialize(timeout = DEFAULT_TIMEOUT)
|
26
|
+
def initialize(timeout = DEFAULT_TIMEOUT,default_ip = DEFAULT_IP)
|
25
27
|
@timeout = timeout
|
28
|
+
@default_ip = default_ip
|
26
29
|
initialize_socket
|
27
30
|
end
|
28
31
|
|
@@ -61,7 +64,8 @@ module Sonos
|
|
61
64
|
end
|
62
65
|
end
|
63
66
|
rescue Timeout::Error => ex
|
64
|
-
|
67
|
+
puts "Timeout error; switching to the default IP"
|
68
|
+
return @default_ip
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
@@ -11,6 +11,9 @@ module Sonos::Endpoint::AVTransport
|
|
11
11
|
body = response.body[:get_position_info_response]
|
12
12
|
doc = Nokogiri::XML(body[:track_meta_data])
|
13
13
|
|
14
|
+
# No music
|
15
|
+
return nil if doc.children.length == 0
|
16
|
+
|
14
17
|
art_path = doc.xpath('//upnp:albumArtURI').inner_text
|
15
18
|
|
16
19
|
# TODO: No idea why this is necessary. Maybe its a Nokogiri thing
|
@@ -20,6 +23,7 @@ module Sonos::Endpoint::AVTransport
|
|
20
23
|
title: doc.xpath('//dc:title').inner_text,
|
21
24
|
artist: doc.xpath('//dc:creator').inner_text,
|
22
25
|
album: doc.xpath('//upnp:album').inner_text,
|
26
|
+
info: doc.xpath('//r:streamContent').inner_text,
|
23
27
|
queue_position: body[:track],
|
24
28
|
track_duration: body[:track_duration],
|
25
29
|
current_position: body[:rel_time],
|
@@ -28,13 +32,17 @@ module Sonos::Endpoint::AVTransport
|
|
28
32
|
}
|
29
33
|
end
|
30
34
|
|
35
|
+
def has_music?
|
36
|
+
!now_playing.nil?
|
37
|
+
end
|
38
|
+
|
31
39
|
# Pause the currently playing track.
|
32
40
|
def pause
|
33
41
|
send_transport_message('Pause')
|
34
42
|
end
|
35
43
|
|
36
44
|
# Play the currently selected track or play a stream.
|
37
|
-
# @param [String]
|
45
|
+
# @param [String] uri Optional uri of the track to play. Leaving this blank, plays the current track.
|
38
46
|
def play(uri = nil)
|
39
47
|
# Play a song from the uri
|
40
48
|
set_av_transport_uri(uri) and return if uri
|
@@ -78,6 +86,24 @@ module Sonos::Endpoint::AVTransport
|
|
78
86
|
send_transport_message('SaveQueue', "<Title>#{title}</Title><ObjectID></ObjectID>")
|
79
87
|
end
|
80
88
|
|
89
|
+
# Adds a track to the queue
|
90
|
+
# @param[String] uri Uri of track
|
91
|
+
# @return[Integer] Queue position of the added track
|
92
|
+
def add_to_queue(uri)
|
93
|
+
response = send_transport_message('AddURIToQueue', "<EnqueuedURI>#{uri}</EnqueuedURI><EnqueuedURIMetaData></EnqueuedURIMetaData><DesiredFirstTrackNumberEnqueued>0</DesiredFirstTrackNumberEnqueued><EnqueueAsNext>1</EnqueueAsNext>")
|
94
|
+
# TODO yeah, this error handling is a bit soft. For consistency's sake :)
|
95
|
+
pos = response.xpath('.//FirstTrackNumberEnqueued').text
|
96
|
+
if pos.length != 0
|
97
|
+
pos.to_i
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Removes a track from the queue
|
102
|
+
# @param[String] object_id Track's queue ID
|
103
|
+
def remove_from_queue(object_id)
|
104
|
+
send_transport_message('RemoveTrackFromQueue', "<ObjectID>#{object_id}</ObjectID><UpdateID>0</UpdateID></u:RemoveTrackFromQueue>")
|
105
|
+
end
|
106
|
+
|
81
107
|
# Join another speaker's group.
|
82
108
|
# Trying to call this on a stereo pair slave will fail.
|
83
109
|
def join(master)
|
@@ -96,7 +122,7 @@ module Sonos::Endpoint::AVTransport
|
|
96
122
|
send_transport_message('BecomeCoordinatorOfStandaloneGroup')
|
97
123
|
end
|
98
124
|
|
99
|
-
private
|
125
|
+
private
|
100
126
|
|
101
127
|
# Play a stream.
|
102
128
|
def set_av_transport_uri(uri)
|
@@ -28,7 +28,7 @@ module Sonos::Endpoint::ContentDirectory
|
|
28
28
|
hash
|
29
29
|
end
|
30
30
|
|
31
|
-
private
|
31
|
+
private
|
32
32
|
|
33
33
|
def content_directory_client
|
34
34
|
@content_directory_client ||= Savon.client endpoint: "http://#{self.ip}:#{Sonos::PORT}#{CONTENT_DIRECTORY_ENDPOINT}", namespace: Sonos::NAMESPACE
|
@@ -40,10 +40,11 @@ private
|
|
40
40
|
doc.css('item').each do |item|
|
41
41
|
res = item.css('res').first
|
42
42
|
result << {
|
43
|
+
queue_id: item['id'],
|
43
44
|
title: item.xpath('dc:title').inner_text,
|
44
45
|
artist: item.xpath('dc:creator').inner_text,
|
45
46
|
album: item.xpath('upnp:album').inner_text,
|
46
|
-
album_art: "http://#{self.ip}:#{PORT}#{item.xpath('upnp:albumArtURI').inner_text}",
|
47
|
+
album_art: "http://#{self.ip}:#{Sonos::PORT}#{item.xpath('upnp:albumArtURI').inner_text}",
|
47
48
|
duration: res['duration'],
|
48
49
|
id: res.inner_text
|
49
50
|
}
|
data/lib/sonos/system.rb
CHANGED
@@ -23,13 +23,28 @@ module Sonos
|
|
23
23
|
|
24
24
|
# Pause all speakers
|
25
25
|
def pause_all
|
26
|
-
|
27
|
-
|
26
|
+
speakers.each do |speaker|
|
27
|
+
speaker.pause if speaker.has_music?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Play all speakers
|
32
|
+
def play_all
|
33
|
+
speakers.each do |speaker|
|
34
|
+
speaker.play if speaker.has_music?
|
28
35
|
end
|
29
36
|
end
|
30
37
|
|
31
38
|
private
|
32
39
|
|
40
|
+
def send_to_all_speakers(action)
|
41
|
+
if self.groups.length > 0
|
42
|
+
self.groups.each { |group| group.send action }
|
43
|
+
else
|
44
|
+
self.speakers.each { |speaker| speaker.send action }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
33
48
|
def construct_groups
|
34
49
|
# Loop through all of the unique groups
|
35
50
|
@topology.collect(&:group).uniq.each do |group_uid|
|
data/lib/sonos/version.rb
CHANGED
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sonos
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
5
|
-
prerelease:
|
4
|
+
version: 0.3.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Sam Soffes
|
@@ -10,12 +9,11 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date: 2013-
|
12
|
+
date: 2013-06-28 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: savon
|
17
16
|
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
17
|
requirements:
|
20
18
|
- - ~>
|
21
19
|
- !ruby/object:Gem::Version
|
@@ -23,7 +21,6 @@ dependencies:
|
|
23
21
|
type: :runtime
|
24
22
|
prerelease: false
|
25
23
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
24
|
requirements:
|
28
25
|
- - ~>
|
29
26
|
- !ruby/object:Gem::Version
|
@@ -31,33 +28,29 @@ dependencies:
|
|
31
28
|
- !ruby/object:Gem::Dependency
|
32
29
|
name: nokogiri
|
33
30
|
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
31
|
requirements:
|
36
|
-
- -
|
32
|
+
- - '>='
|
37
33
|
- !ruby/object:Gem::Version
|
38
34
|
version: '0'
|
39
35
|
type: :runtime
|
40
36
|
prerelease: false
|
41
37
|
version_requirements: !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
38
|
requirements:
|
44
|
-
- -
|
39
|
+
- - '>='
|
45
40
|
- !ruby/object:Gem::Version
|
46
41
|
version: '0'
|
47
42
|
- !ruby/object:Gem::Dependency
|
48
43
|
name: thor
|
49
44
|
requirement: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
45
|
requirements:
|
52
|
-
- -
|
46
|
+
- - '>='
|
53
47
|
- !ruby/object:Gem::Version
|
54
48
|
version: '0'
|
55
49
|
type: :runtime
|
56
50
|
prerelease: false
|
57
51
|
version_requirements: !ruby/object:Gem::Requirement
|
58
|
-
none: false
|
59
52
|
requirements:
|
60
|
-
- -
|
53
|
+
- - '>='
|
61
54
|
- !ruby/object:Gem::Version
|
62
55
|
version: '0'
|
63
56
|
description: Control Sonos speakers with Ruby
|
@@ -70,6 +63,7 @@ extensions: []
|
|
70
63
|
extra_rdoc_files: []
|
71
64
|
files:
|
72
65
|
- .gitignore
|
66
|
+
- .ruby-version
|
73
67
|
- .travis.yml
|
74
68
|
- Changelog.markdown
|
75
69
|
- Gemfile
|
@@ -102,30 +96,26 @@ files:
|
|
102
96
|
homepage: https://github.com/soffes/sonos
|
103
97
|
licenses:
|
104
98
|
- MIT
|
99
|
+
metadata: {}
|
105
100
|
post_install_message:
|
106
101
|
rdoc_options: []
|
107
102
|
require_paths:
|
108
103
|
- lib
|
109
104
|
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
-
none: false
|
111
105
|
requirements:
|
112
|
-
- -
|
106
|
+
- - '>='
|
113
107
|
- !ruby/object:Gem::Version
|
114
108
|
version: 1.9.2
|
115
109
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
-
none: false
|
117
110
|
requirements:
|
118
|
-
- -
|
111
|
+
- - '>='
|
119
112
|
- !ruby/object:Gem::Version
|
120
113
|
version: '0'
|
121
|
-
segments:
|
122
|
-
- 0
|
123
|
-
hash: -1378351241916050478
|
124
114
|
requirements: []
|
125
115
|
rubyforge_project:
|
126
|
-
rubygems_version:
|
116
|
+
rubygems_version: 2.0.2
|
127
117
|
signing_key:
|
128
|
-
specification_version:
|
118
|
+
specification_version: 4
|
129
119
|
summary: Control Sonos speakers with Ruby
|
130
120
|
test_files:
|
131
121
|
- test/cassettes/topology.yml
|