sonos 0.3.4 → 0.3.5
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 +4 -4
- data/Gemfile +0 -9
- data/LICENSE +1 -1
- data/Rakefile +0 -8
- data/Readme.markdown +2 -0
- data/lib/sonos.rb +12 -0
- data/lib/sonos/device/base.rb +11 -3
- data/lib/sonos/device/speaker.rb +1 -1
- data/lib/sonos/discovery.rb +6 -6
- data/lib/sonos/endpoint/a_v_transport.rb +74 -14
- data/lib/sonos/endpoint/alarm.rb +9 -40
- data/lib/sonos/endpoint/content_directory.rb +1 -1
- data/lib/sonos/endpoint/device.rb +10 -2
- data/lib/sonos/endpoint/rendering.rb +7 -7
- data/lib/sonos/group.rb +1 -1
- data/lib/sonos/system.rb +7 -4
- data/lib/sonos/topology_node.rb +1 -1
- data/lib/sonos/version.rb +1 -1
- metadata +4 -14
- data/test/cassettes/topology.yml +0 -840
- data/test/support/discovery_macros.rb +0 -7
- data/test/support/vcr.rb +0 -4
- data/test/test_helper.rb +0 -15
- data/test/units/system_test.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2500064f1c4f07a8064c691be658195d7245c15f
|
4
|
+
data.tar.gz: 30d941094b116b5c4998c8f6f2cbdf8862de11b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae738d359b88707fbb0913d26f8760690ba497d3c7d1d9bc8cc4675e637e4e6c9f9617e2906c1aef23b407f710c15761746372d299445c8209f34116cbcb1df7
|
7
|
+
data.tar.gz: 438b11f8ffa8033572c44e99bfe91540a3e634558030a248540ec130f1e73b606dc53025fd641371de1e4a8aad151b943b071f09cdf2a4d07c4b1e167e862693
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/Rakefile
CHANGED
@@ -1,10 +1,4 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
|
-
require 'rake/testtask'
|
3
|
-
|
4
|
-
Rake::TestTask.new(:test) do |t|
|
5
|
-
t.libs << 'test'
|
6
|
-
t.pattern = 'test/**/*_test.rb'
|
7
|
-
end
|
8
2
|
|
9
3
|
desc "Open an irb session preloaded with this API"
|
10
4
|
task :console do
|
@@ -14,5 +8,3 @@ task :console do
|
|
14
8
|
ARGV.clear
|
15
9
|
IRB.start
|
16
10
|
end
|
17
|
-
|
18
|
-
task default: :test
|
data/Readme.markdown
CHANGED
@@ -6,6 +6,8 @@ Huge thanks to [Rahim Sonawalla](https://github.com/rahims) for making [SoCo](ht
|
|
6
6
|
|
7
7
|
[](https://codeclimate.com/github/soffes/sonos)
|
8
8
|
|
9
|
+
**Note: Currently discovery is broken in Ruby 2.1**
|
10
|
+
|
9
11
|
## Installation
|
10
12
|
|
11
13
|
Add this line to your application's Gemfile:
|
data/lib/sonos.rb
CHANGED
@@ -18,4 +18,16 @@ module Sonos
|
|
18
18
|
# def self.system
|
19
19
|
# @system ||= Sonos::System.new
|
20
20
|
# end
|
21
|
+
|
22
|
+
unless defined? @@logging_enabled
|
23
|
+
@@logging_enabled = false
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.logging_enabled
|
27
|
+
@@logging_enabled
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.logging_enabled=(logging)
|
31
|
+
@@logging_enabled = logging
|
32
|
+
end
|
21
33
|
end
|
data/lib/sonos/device/base.rb
CHANGED
@@ -4,7 +4,7 @@ require 'nokogiri'
|
|
4
4
|
module Sonos::Device
|
5
5
|
class Base
|
6
6
|
attr_reader :ip, :name, :uid, :serial_number, :software_version, :hardware_version,
|
7
|
-
:zone_type, :model_number, :mac_address, :group, :icon
|
7
|
+
:zone_type, :model_number, :mac_address, :group, :icon, :services
|
8
8
|
|
9
9
|
attr_accessor :group_master
|
10
10
|
|
@@ -40,6 +40,7 @@ module Sonos::Device
|
|
40
40
|
@hardware_version = data[:hardware_version]
|
41
41
|
@zone_type = data[:zone_type]
|
42
42
|
@model_number = data[:model_number]
|
43
|
+
@services = data[:services]
|
43
44
|
end
|
44
45
|
|
45
46
|
def data
|
@@ -50,7 +51,8 @@ module Sonos::Device
|
|
50
51
|
software_version: @software_version,
|
51
52
|
hardware_version: @hardware_version,
|
52
53
|
zone_type: @zone_type,
|
53
|
-
model_number: @model_number
|
54
|
+
model_number: @model_number,
|
55
|
+
services: @services
|
54
56
|
}
|
55
57
|
end
|
56
58
|
|
@@ -68,6 +70,10 @@ module Sonos::Device
|
|
68
70
|
|
69
71
|
protected
|
70
72
|
|
73
|
+
def parse_response(response)
|
74
|
+
response.success? ? :success : :failed
|
75
|
+
end
|
76
|
+
|
71
77
|
def self.retrieve_information(ip)
|
72
78
|
url = "http://#{ip}:#{Sonos::PORT}/xml/device_description.xml"
|
73
79
|
parse_description(Nokogiri::XML(open(url)))
|
@@ -82,7 +88,9 @@ module Sonos::Device
|
|
82
88
|
software_version: doc.xpath('/xmlns:root/xmlns:device/xmlns:hardwareVersion').inner_text,
|
83
89
|
hardware_version: doc.xpath('/xmlns:root/xmlns:device/xmlns:softwareVersion').inner_text,
|
84
90
|
zone_type: doc.xpath('/xmlns:root/xmlns:device/xmlns:zoneType').inner_text,
|
85
|
-
model_number: doc.xpath('/xmlns:root/xmlns:device/xmlns:modelNumber').inner_text
|
91
|
+
model_number: doc.xpath('/xmlns:root/xmlns:device/xmlns:modelNumber').inner_text,
|
92
|
+
services: doc.xpath('/xmlns:root/xmlns:device/xmlns:serviceList/xmlns:service/xmlns:serviceId').
|
93
|
+
collect(&:inner_text)
|
86
94
|
}
|
87
95
|
end
|
88
96
|
end
|
data/lib/sonos/device/speaker.rb
CHANGED
data/lib/sonos/discovery.rb
CHANGED
@@ -16,14 +16,13 @@ module Sonos
|
|
16
16
|
class Discovery
|
17
17
|
MULTICAST_ADDR = '239.255.255.250'
|
18
18
|
MULTICAST_PORT = 1900
|
19
|
-
DEFAULT_TIMEOUT =
|
20
|
-
DEFAULT_IP = nil
|
19
|
+
DEFAULT_TIMEOUT = 2
|
21
20
|
|
22
21
|
attr_reader :timeout
|
23
22
|
attr_reader :first_device_ip
|
24
23
|
attr_reader :default_ip
|
25
24
|
|
26
|
-
def initialize(timeout = DEFAULT_TIMEOUT,default_ip =
|
25
|
+
def initialize(timeout = DEFAULT_TIMEOUT, default_ip = nil)
|
27
26
|
@timeout = timeout
|
28
27
|
@default_ip = default_ip
|
29
28
|
initialize_socket
|
@@ -65,7 +64,8 @@ module Sonos
|
|
65
64
|
end
|
66
65
|
end
|
67
66
|
rescue Timeout::Error => ex
|
68
|
-
puts
|
67
|
+
puts 'Timed out...'
|
68
|
+
puts 'Switching to the default IP' if @default_ip
|
69
69
|
return @default_ip
|
70
70
|
end
|
71
71
|
end
|
@@ -74,8 +74,8 @@ module Sonos
|
|
74
74
|
# Create a socket
|
75
75
|
@socket = UDPSocket.open
|
76
76
|
|
77
|
-
# We're going to use IP with the multicast TTL
|
78
|
-
@socket.setsockopt(Socket::IPPROTO_IP,
|
77
|
+
# We're going to use IP with the multicast TTL
|
78
|
+
@socket.setsockopt(Socket::Option.new(:INET, :IPPROTO_IP, :IP_MULTICAST_TTL, 2.chr))
|
79
79
|
end
|
80
80
|
|
81
81
|
def search_message
|
@@ -36,9 +36,21 @@ module Sonos::Endpoint::AVTransport
|
|
36
36
|
!now_playing.nil?
|
37
37
|
end
|
38
38
|
|
39
|
+
# Get information about the state the player is in.
|
40
|
+
def get_player_state
|
41
|
+
response = send_transport_message('GetTransportInfo')
|
42
|
+
body = response.body[:get_transport_info_response]
|
43
|
+
|
44
|
+
{
|
45
|
+
status: body[:current_transport_status],
|
46
|
+
state: body[:current_transport_state],
|
47
|
+
speed: body[:current_speed],
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
39
51
|
# Pause the currently playing track.
|
40
52
|
def pause
|
41
|
-
send_transport_message('Pause')
|
53
|
+
parse_response send_transport_message('Pause')
|
42
54
|
end
|
43
55
|
|
44
56
|
# Play the currently selected track or play a stream.
|
@@ -48,22 +60,26 @@ module Sonos::Endpoint::AVTransport
|
|
48
60
|
set_av_transport_uri(uri) and return if uri
|
49
61
|
|
50
62
|
# Play the currently selected track
|
51
|
-
send_transport_message('Play')
|
63
|
+
parse_response send_transport_message('Play')
|
52
64
|
end
|
53
65
|
|
54
66
|
# Stop playing.
|
55
67
|
def stop
|
56
|
-
send_transport_message('Stop')
|
68
|
+
parse_response send_transport_message('Stop')
|
57
69
|
end
|
58
70
|
|
59
71
|
# Play the next track.
|
60
72
|
def next
|
61
|
-
send_transport_message('Next')
|
73
|
+
parse_response send_transport_message('Next')
|
62
74
|
end
|
63
75
|
|
64
76
|
# Play the previous track.
|
65
77
|
def previous
|
66
|
-
send_transport_message('Previous')
|
78
|
+
parse_response send_transport_message('Previous')
|
79
|
+
end
|
80
|
+
|
81
|
+
def line_in(speaker)
|
82
|
+
set_av_transport_uri('x-rincon-stream:' + speaker.uid.sub('uuid:', ''))
|
67
83
|
end
|
68
84
|
|
69
85
|
# Seeks to a given timestamp in the current track
|
@@ -71,24 +87,25 @@ module Sonos::Endpoint::AVTransport
|
|
71
87
|
def seek(seconds = 0)
|
72
88
|
# Must be sent in the format of HH:MM:SS
|
73
89
|
timestamp = Time.at(seconds).utc.strftime('%H:%M:%S')
|
74
|
-
send_transport_message('Seek', "<Unit>REL_TIME</Unit><Target>#{timestamp}</Target>")
|
90
|
+
parse_response send_transport_message('Seek', "<Unit>REL_TIME</Unit><Target>#{timestamp}</Target>")
|
75
91
|
end
|
76
92
|
|
77
93
|
# Clear the queue
|
78
94
|
def clear_queue
|
79
|
-
send_transport_message('RemoveAllTracksFromQueue')
|
95
|
+
parse_response parse_response send_transport_message('RemoveAllTracksFromQueue')
|
80
96
|
end
|
81
97
|
|
82
98
|
# Save queue
|
83
99
|
def save_queue(title)
|
84
|
-
send_transport_message('SaveQueue', "<Title>#{title}</Title><ObjectID></ObjectID>")
|
100
|
+
parse_response send_transport_message('SaveQueue', "<Title>#{title}</Title><ObjectID></ObjectID>")
|
85
101
|
end
|
86
102
|
|
87
103
|
# Adds a track to the queue
|
88
104
|
# @param[String] uri Uri of track
|
105
|
+
# @param[String] didl Stanza of DIDL-Lite metadata (generally created by #add_spotify_to_queue)
|
89
106
|
# @return[Integer] Queue position of the added track
|
90
|
-
def add_to_queue(uri)
|
91
|
-
response = send_transport_message('AddURIToQueue', "<EnqueuedURI>#{uri}</EnqueuedURI><EnqueuedURIMetaData
|
107
|
+
def add_to_queue(uri, didl = '')
|
108
|
+
response = send_transport_message('AddURIToQueue', "<EnqueuedURI>#{uri}</EnqueuedURI><EnqueuedURIMetaData>#{didl}</EnqueuedURIMetaData><DesiredFirstTrackNumberEnqueued>0</DesiredFirstTrackNumberEnqueued><EnqueueAsNext>1</EnqueueAsNext>")
|
92
109
|
# TODO yeah, this error handling is a bit soft. For consistency's sake :)
|
93
110
|
pos = response.xpath('.//FirstTrackNumberEnqueued').text
|
94
111
|
if pos.length != 0
|
@@ -96,16 +113,59 @@ module Sonos::Endpoint::AVTransport
|
|
96
113
|
end
|
97
114
|
end
|
98
115
|
|
116
|
+
# Adds a Spotify track to the queue along with extra data for better metadata retrieval
|
117
|
+
# @param[Hash] opts Various options (id, user, region and type)
|
118
|
+
# @return[Integer] Queue position of the added track(s)
|
119
|
+
def add_spotify_to_queue(opts = {})
|
120
|
+
opts = {
|
121
|
+
:id => '',
|
122
|
+
:user => nil,
|
123
|
+
:region => nil,
|
124
|
+
:type => 'track'
|
125
|
+
}.merge(opts)
|
126
|
+
|
127
|
+
# Basic validation of the accepted types; playlists need an associated user
|
128
|
+
# and the toplist (for tracks and albums) need to specify a region.
|
129
|
+
return nil if opts[:type] == 'playlist' and opts[:user].nil?
|
130
|
+
return nil if opts[:type] =~ /toplist_tracks/ and opts[:region].nil?
|
131
|
+
|
132
|
+
# In order for the player to retrieve track duration, artist, album etc
|
133
|
+
# we need to pass it some metadata ourselves.
|
134
|
+
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>"
|
135
|
+
|
136
|
+
r_id = rand(10000000..99999999)
|
137
|
+
|
138
|
+
case opts[:type]
|
139
|
+
when /playlist/
|
140
|
+
uri = "x-rincon-cpcontainer:#{r_id}spotify%3auser%3a#{opts[:user]}%3aplaylist%3a#{opts[:id]}"
|
141
|
+
when /toplist_(tracks)/
|
142
|
+
subtype = opts[:type].sub('toplist_', '') # only 'tracks' are supported right now by Sonos.
|
143
|
+
uri = "x-rincon-cpcontainer:#{r_id}toplist%2f#{subtype}%2fregion%2f#{opts[:region]}"
|
144
|
+
when /album/
|
145
|
+
uri = "x-rincon-cpcontainer:#{r_id}spotify%3aalbum%3a#{opts[:id]}"
|
146
|
+
when /artist/
|
147
|
+
uri = "x-rincon-cpcontainer:#{r_id}tophits%3aspotify%3aartist%3a#{opts[:id]}"
|
148
|
+
when /starred/
|
149
|
+
uri = "x-rincon-cpcontainer:#{r_id}starred"
|
150
|
+
when /track/
|
151
|
+
uri = "x-sonos-spotify:spotify%3a#{opts[:type]}%3a#{opts[:id]}"
|
152
|
+
else
|
153
|
+
return nil
|
154
|
+
end
|
155
|
+
|
156
|
+
add_to_queue(uri, didl_metadata)
|
157
|
+
end
|
158
|
+
|
99
159
|
# Removes a track from the queue
|
100
160
|
# @param[String] object_id Track's queue ID
|
101
161
|
def remove_from_queue(object_id)
|
102
|
-
send_transport_message('RemoveTrackFromQueue', "<ObjectID>#{object_id}</ObjectID><UpdateID>0</UpdateID></u:RemoveTrackFromQueue>")
|
162
|
+
parse_response send_transport_message('RemoveTrackFromQueue', "<ObjectID>#{object_id}</ObjectID><UpdateID>0</UpdateID></u:RemoveTrackFromQueue>")
|
103
163
|
end
|
104
164
|
|
105
165
|
# Join another speaker's group.
|
106
166
|
# Trying to call this on a stereo pair slave will fail.
|
107
167
|
def join(master)
|
108
|
-
set_av_transport_uri('x-rincon:' + master.uid.sub('uuid:', ''))
|
168
|
+
parse_response set_av_transport_uri('x-rincon:' + master.uid.sub('uuid:', ''))
|
109
169
|
end
|
110
170
|
|
111
171
|
# Add another speaker to this group.
|
@@ -117,7 +177,7 @@ module Sonos::Endpoint::AVTransport
|
|
117
177
|
# Ungroup from its current group.
|
118
178
|
# Trying to call this on a stereo pair slave will fail.
|
119
179
|
def ungroup
|
120
|
-
send_transport_message('BecomeCoordinatorOfStandaloneGroup')
|
180
|
+
parse_response send_transport_message('BecomeCoordinatorOfStandaloneGroup')
|
121
181
|
end
|
122
182
|
|
123
183
|
private
|
@@ -128,7 +188,7 @@ module Sonos::Endpoint::AVTransport
|
|
128
188
|
end
|
129
189
|
|
130
190
|
def transport_client
|
131
|
-
@transport_client ||= Savon.client endpoint: "http://#{self.group_master.ip}:#{Sonos::PORT}#{TRANSPORT_ENDPOINT}", namespace: Sonos::NAMESPACE,
|
191
|
+
@transport_client ||= Savon.client endpoint: "http://#{self.group_master.ip}:#{Sonos::PORT}#{TRANSPORT_ENDPOINT}", namespace: Sonos::NAMESPACE, log: Sonos.logging_enabled
|
132
192
|
end
|
133
193
|
|
134
194
|
def send_transport_message(name, part = '<Speed>1</Speed>')
|
data/lib/sonos/endpoint/alarm.rb
CHANGED
@@ -1,25 +1,3 @@
|
|
1
|
-
# POST /AlarmClock/Control HTTP/1.1
|
2
|
-
# SOAPACTION: "urn:schemas-upnp-org:service:AlarmClock:1#ListAlarms"
|
3
|
-
|
4
|
-
# Request
|
5
|
-
#<?xml version="1.0" encoding="UTF-8"?>
|
6
|
-
#<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
7
|
-
# <s:Body>
|
8
|
-
# <u:ListAlarms xmlns:u="urn:schemas-upnp-org:service:AlarmClock:1" />
|
9
|
-
# </s:Body>
|
10
|
-
#</s:Envelope>
|
11
|
-
|
12
|
-
# Response
|
13
|
-
#<?xml version="1.0" encoding="UTF-8"?>
|
14
|
-
#<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
15
|
-
# <s:Body>
|
16
|
-
# <u:ListAlarmsResponse xmlns:u="urn:schemas-upnp-org:service:AlarmClock:1">
|
17
|
-
# <CurrentAlarmList><Alarms><Alarm ID="8" StartTime="19:21:00" Duration="02:00:00" Recurrence="ONCE" Enabled="0" RoomUUID="RINCON_000E583564A601400" ProgramURI="x-rincon-buzzer:0" ProgramMetaData="" PlayMode="SHUFFLE_NOREPEAT" Volume="25" IncludeLinkedZones="0"/></Alarms></CurrentAlarmList>
|
18
|
-
# <CurrentAlarmListVersion>RINCON_000E583564A601400:26</CurrentAlarmListVersion>
|
19
|
-
# </u:ListAlarmsResponse>
|
20
|
-
# </s:Body>
|
21
|
-
#</s:Envelope>
|
22
|
-
|
23
1
|
module Sonos::Endpoint::Alarm
|
24
2
|
ALARM_CLOCK_ENDPOINT = '/AlarmClock/Control'
|
25
3
|
ALARM_CLOCK_XMLNS = 'urn:schemas-upnp-org:service:AlarmClock:1'
|
@@ -49,7 +27,7 @@ module Sonos::Endpoint::Alarm
|
|
49
27
|
hash_of_alarm_hashes[id] = alarm_hash
|
50
28
|
end
|
51
29
|
end
|
52
|
-
|
30
|
+
hash_of_alarm_hashes
|
53
31
|
end
|
54
32
|
|
55
33
|
def create_alarm(startLocalTime, duration, recurrence, enabled, roomUuid, playMode, volume, includeLinkedZones, programUri, programMetaData)
|
@@ -57,15 +35,11 @@ module Sonos::Endpoint::Alarm
|
|
57
35
|
:Recurrence => recurrence, :Enabled => enabled, :RoomUUID => roomUuid,
|
58
36
|
:PlayMode => playMode, :Volume => volume, :IncludeLinkedZones => includeLinkedZones,
|
59
37
|
:ProgramURI => programUri, :ProgramMetaData => programMetaData}
|
60
|
-
|
61
|
-
# :Recurrence => 'ONCE', :Enabled => 1, :RoomUUID => 'RINCON_000E583564A601400',
|
62
|
-
# :PlayMode => 'SHUFFLE_NOREPEAT', :Volume => 25, :IncludeLinkedZones => 0,
|
63
|
-
# :ProgramURI => 'x-rincon-buzzer:0', :ProgramMetaData => ""}
|
64
|
-
send_alarm_message('CreateAlarm', convert_hash_to_xml(options))
|
38
|
+
parse_response send_alarm_message('CreateAlarm', convert_hash_to_xml(options))
|
65
39
|
end
|
66
40
|
|
67
41
|
def destroy_alarm(id)
|
68
|
-
send_alarm_message('DestroyAlarm', "<ID>#{id}</ID>")
|
42
|
+
parse_response send_alarm_message('DestroyAlarm', "<ID>#{id}</ID>")
|
69
43
|
end
|
70
44
|
|
71
45
|
def update_alarm(id, startLocalTime, duration, recurrence, enabled, roomUuid, playMode, volume, includeLinkedZones, programUri, programMetaData)
|
@@ -73,11 +47,7 @@ module Sonos::Endpoint::Alarm
|
|
73
47
|
:Recurrence => recurrence, :Enabled => enabled, :RoomUUID => roomUuid,
|
74
48
|
:PlayMode => playMode, :Volume => volume, :IncludeLinkedZones => includeLinkedZones,
|
75
49
|
:ProgramURI => programUri, :ProgramMetaData => programMetaData}
|
76
|
-
|
77
|
-
# :Recurrence => 'ONCE', :Enabled => 1, :RoomUUID => 'RINCON_000E583564A601400',
|
78
|
-
# :PlayMode => 'SHUFFLE_NOREPEAT', :Volume => 25, :IncludeLinkedZones => 0,
|
79
|
-
# :ProgramURI => 'x-rincon-buzzer:0', :ProgramMetaData => ""}
|
80
|
-
send_alarm_message('UpdateAlarm', convert_hash_to_xml(alarm_hash))
|
50
|
+
parse_response send_alarm_message('UpdateAlarm', convert_hash_to_xml(alarm_hash))
|
81
51
|
end
|
82
52
|
|
83
53
|
def is_alarm_enabled?(id)
|
@@ -87,25 +57,25 @@ module Sonos::Endpoint::Alarm
|
|
87
57
|
def enable_alarm(id)
|
88
58
|
alarm_hash = list_alarms[id]
|
89
59
|
alarm_hash[:Enabled] = '1'
|
90
|
-
send_alarm_message('UpdateAlarm', convert_hash_to_xml(alarm_hash))
|
60
|
+
parse_response send_alarm_message('UpdateAlarm', convert_hash_to_xml(alarm_hash))
|
91
61
|
end
|
92
62
|
|
93
63
|
def disable_alarm(id)
|
94
64
|
alarm_hash = list_alarms[id]
|
95
65
|
alarm_hash[:Enabled] = '0'
|
96
|
-
send_alarm_message('UpdateAlarm', convert_hash_to_xml(alarm_hash))
|
66
|
+
parse_response send_alarm_message('UpdateAlarm', convert_hash_to_xml(alarm_hash))
|
97
67
|
end
|
98
68
|
|
99
69
|
def set_alarm_volume(id, volume)
|
100
70
|
alarm_hash = list_alarms[id]
|
101
71
|
alarm_hash[:Volume] = volume
|
102
|
-
send_alarm_message('UpdateAlarm', convert_hash_to_xml(alarm_hash))
|
72
|
+
parse_response send_alarm_message('UpdateAlarm', convert_hash_to_xml(alarm_hash))
|
103
73
|
end
|
104
74
|
|
105
75
|
private
|
106
76
|
|
107
77
|
def alarm_client
|
108
|
-
@alarm_client ||= Savon.client endpoint: "http://#{self.ip}:#{Sonos::PORT}#{ALARM_CLOCK_ENDPOINT}", namespace: Sonos::NAMESPACE
|
78
|
+
@alarm_client ||= Savon.client endpoint: "http://#{self.ip}:#{Sonos::PORT}#{ALARM_CLOCK_ENDPOINT}", namespace: Sonos::NAMESPACE, log: Sonos.logging_enabled
|
109
79
|
end
|
110
80
|
|
111
81
|
def send_alarm_message(name, part = '')
|
@@ -121,5 +91,4 @@ module Sonos::Endpoint::Alarm
|
|
121
91
|
end
|
122
92
|
updatePart
|
123
93
|
end
|
124
|
-
|
125
|
-
end
|
94
|
+
end
|
@@ -31,7 +31,7 @@ module Sonos::Endpoint::ContentDirectory
|
|
31
31
|
private
|
32
32
|
|
33
33
|
def content_directory_client
|
34
|
-
@content_directory_client ||= Savon.client endpoint: "http://#{self.ip}:#{Sonos::PORT}#{CONTENT_DIRECTORY_ENDPOINT}", namespace: Sonos::NAMESPACE,
|
34
|
+
@content_directory_client ||= Savon.client endpoint: "http://#{self.ip}:#{Sonos::PORT}#{CONTENT_DIRECTORY_ENDPOINT}", namespace: Sonos::NAMESPACE, log: Sonos.logging_enabled
|
35
35
|
end
|
36
36
|
|
37
37
|
def parse_items(string)
|
@@ -2,16 +2,24 @@ module Sonos::Endpoint::Device
|
|
2
2
|
DEVICE_ENDPOINT = '/DeviceProperties/Control'
|
3
3
|
DEVICE_XMLNS = 'urn:schemas-upnp-org:service:DeviceProperties:1'
|
4
4
|
|
5
|
+
# Retrieve the status light state; true if on, false otherwise.
|
6
|
+
def status_light_enabled?
|
7
|
+
response = send_device_message('GetLEDState', '')
|
8
|
+
body = response.body[:get_led_state_response]
|
9
|
+
|
10
|
+
body[:current_led_state] == 'On' ? true : false
|
11
|
+
end
|
12
|
+
|
5
13
|
# Turn the white status light on or off
|
6
14
|
# @param [Boolean] True to turn on the light. False to turn off the light.
|
7
15
|
def status_light_enabled=(enabled)
|
8
|
-
send_device_message('SetLEDState', enabled ? 'On' : 'Off')
|
16
|
+
parse_response send_device_message('SetLEDState', enabled ? 'On' : 'Off')
|
9
17
|
end
|
10
18
|
|
11
19
|
private
|
12
20
|
|
13
21
|
def device_client
|
14
|
-
@device_client ||= Savon.client endpoint: "http://#{self.ip}:#{Sonos::PORT}#{DEVICE_ENDPOINT}", namespace: Sonos::NAMESPACE,
|
22
|
+
@device_client ||= Savon.client endpoint: "http://#{self.ip}:#{Sonos::PORT}#{DEVICE_ENDPOINT}", namespace: Sonos::NAMESPACE, log: Sonos.logging_enabled
|
15
23
|
end
|
16
24
|
|
17
25
|
def send_device_message(name, value)
|