sonos 0.3.4 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Code Climate](https://codeclimate.com/github/soffes/sonos.png)](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)
|