alexa_remotectl 0.2.0 → 0.4.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 +4 -4
- checksums.yaml.gz.sig +2 -1
- data/lib/alexa_remotectl.rb +253 -13
- data.tar.gz.sig +0 -0
- metadata +2 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b1f56f3518e257c26d3d00ac9e63dde021474827abb5461fdb11114f95196a2
|
4
|
+
data.tar.gz: 3dbe1a051f41b9b11f77e0f82c392f90c72d23ee4ba0314cc6ebc3c0deb31adc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff774816c05f4c379b0563f83ff5b9036161c58975438898723d2460aadbb29fe554fa6b84f6c5c12208fb1e375ccdaccac5d006944b7262c321c9a5b4abe724
|
7
|
+
data.tar.gz: 236d52c95e7421ee51af2d425523ddb85d193290398b4886bfae7c718a8e16f6b1450a316301dc8a077ba0436d59ecb2b7faa301041865ff179505a3001c3594
|
checksums.yaml.gz.sig
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
|
1
|
+
�t$45Y!%=�z�o����V�[�X&�Fǡ�wh)���aU%��S�dd(C�߅�/�k�b�r�x�&�J]r��c9k��h)/E�Jz�?4���w_Kc��h�.+�S�W��/O�8��G���q��XR����%�#yz=2�wg����lJ�4~j�VO7Y�xS�FH`�����v�Z��X�&r��%�~�J��t/�
|
2
|
+
�@\x=M�� �/���,�۶�L�^�� yz*�p���q��{����9S�����ժ{��*A���V�q�K��?��)�*�*�oR+Pm[{!.Aⓡ��7�S��|�%�F ~�q�8p}������+�E�}�Zn������_/�a�}5���?��y�Kt����-���(Z7;>�O
|
data/lib/alexa_remotectl.rb
CHANGED
@@ -8,6 +8,7 @@
|
|
8
8
|
require 'net/http'
|
9
9
|
require 'uri'
|
10
10
|
require 'clipboard'
|
11
|
+
require 'json'
|
11
12
|
|
12
13
|
# Use the CodeWizard with the cURL command you've copied using Developer
|
13
14
|
# tools on Alexa's SPA page (within the web browser).
|
@@ -57,44 +58,173 @@ end
|
|
57
58
|
|
58
59
|
class AlexaRemoteCtl
|
59
60
|
|
60
|
-
def initialize(domain: 'alexa.amazon.co.uk', device: {}, cookie: ''
|
61
|
+
def initialize(domain: 'alexa.amazon.co.uk', device: {}, cookie: '',
|
62
|
+
customerid: '')
|
61
63
|
|
62
|
-
@domain, @device, @cookie = domain, device, cookie
|
64
|
+
@domain, @device, @cookie, @customerid = domain, device, cookie, customerid
|
63
65
|
|
64
66
|
end
|
65
67
|
|
68
|
+
def info()
|
69
|
+
device_player()[:playerInfo]
|
70
|
+
end
|
71
|
+
|
72
|
+
def list_ebooks()
|
73
|
+
get_json '/api/ebooks/library', "mediaOwnerCustomerId=#{@customerid}" +
|
74
|
+
"&nextToken=&size=50"
|
75
|
+
end
|
76
|
+
|
77
|
+
def mute?()
|
78
|
+
info()[:volume][:muted]
|
79
|
+
end
|
80
|
+
|
81
|
+
alias muted? mute?
|
82
|
+
|
83
|
+
# skip to the next music track
|
84
|
+
def next
|
85
|
+
pp_cmd('Next')
|
86
|
+
end
|
87
|
+
|
66
88
|
def pause
|
67
|
-
|
89
|
+
pp_cmd('Pause')
|
68
90
|
end
|
69
91
|
|
70
92
|
def play
|
71
|
-
|
93
|
+
pp_cmd('Play')
|
94
|
+
end
|
95
|
+
|
96
|
+
def play_ebook(id)
|
97
|
+
|
98
|
+
serialno = @device[:serialno]
|
99
|
+
type = @device[:type]
|
100
|
+
|
101
|
+
url = "https://#{@domain}/api/ebooks/queue-and-play?"
|
102
|
+
|
103
|
+
body = {
|
104
|
+
'asin' => id,
|
105
|
+
'deviceType' => type,
|
106
|
+
'deviceSerialNumber' => serialno,
|
107
|
+
'mediaOwnerCustomerId' => @customerid
|
108
|
+
}.to_json
|
109
|
+
|
110
|
+
post_request url, body
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
def play_hq(id)
|
115
|
+
device_phq(id)
|
116
|
+
end
|
117
|
+
|
118
|
+
def playing?()
|
119
|
+
info()[:state] == 'PLAYING'
|
120
|
+
end
|
121
|
+
|
122
|
+
# Artist name
|
123
|
+
#
|
124
|
+
def text1()
|
125
|
+
info()[:infoText][:subText1]
|
126
|
+
end
|
127
|
+
|
128
|
+
# music station
|
129
|
+
#
|
130
|
+
def text2()
|
131
|
+
info()[:infoText][:subText2]
|
132
|
+
end
|
133
|
+
|
134
|
+
# music track title
|
135
|
+
#
|
136
|
+
def title()
|
137
|
+
info()[:infoText][:title]
|
138
|
+
end
|
139
|
+
|
140
|
+
def vol()
|
141
|
+
info()[:volume][:volume]
|
142
|
+
end
|
143
|
+
|
144
|
+
def vol=(n)
|
145
|
+
|
146
|
+
return unless n.between? 0, 40
|
147
|
+
|
148
|
+
body = '{"type":"VolumeLevelCommand","volumeLevel":' + n.to_s \
|
149
|
+
+ ',"contentFocusClientId":null}'
|
150
|
+
device_cmd(body)
|
72
151
|
end
|
73
152
|
|
74
153
|
private
|
75
154
|
|
76
|
-
|
155
|
+
# play, pause, or next
|
156
|
+
#
|
157
|
+
def pp_cmd(s)
|
158
|
+
body = '{"type":"' + s + 'Command","contentFocusClientId":null}'
|
159
|
+
device_cmd body
|
160
|
+
end
|
161
|
+
|
162
|
+
def device_cmd(body)
|
77
163
|
|
78
164
|
serialno = @device[:serialno]
|
79
165
|
type = @device[:type]
|
80
166
|
|
81
|
-
url = "https://#{@domain}/api/np/command?
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
167
|
+
url = "https://#{@domain}/api/np/command?deviceSerial" +
|
168
|
+
"Number=#{serialno}&deviceType=#{type}"
|
169
|
+
post_command url, body
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
# play historical queue (PHQ)
|
174
|
+
#
|
175
|
+
def device_phq(queueid)
|
176
|
+
|
177
|
+
serialno = @device[:serialno]
|
178
|
+
type = @device[:type]
|
179
|
+
|
180
|
+
url = "https://#{@domain}/api/media/play-historical-queue"
|
181
|
+
|
182
|
+
body = {
|
183
|
+
'deviceType' => type,
|
184
|
+
'deviceSerialNumber' => serialno,
|
185
|
+
'queueId' => queueid,
|
186
|
+
'startTime' => nil,
|
187
|
+
'service' => nil,
|
188
|
+
'trackSource' => nil,
|
189
|
+
'mediaOwnerCustomerId' => @customerid
|
190
|
+
}.to_json
|
191
|
+
|
192
|
+
post_request url, body
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
def device_player()
|
197
|
+
get_json '/api/np/player'
|
198
|
+
end
|
199
|
+
|
200
|
+
def get_json(uri, params='')
|
201
|
+
|
202
|
+
serialno = @device[:serialno]
|
203
|
+
type = @device[:type]
|
204
|
+
|
205
|
+
url = "https://#{@domain}#{uri}?deviceSerial" +
|
206
|
+
"Number=#{serialno}&deviceType=#{type}"
|
207
|
+
url += '&' + params if params.length > 1
|
208
|
+
|
209
|
+
r = get_request url
|
210
|
+
JSON.parse(r.body, symbolize_names: true)
|
211
|
+
|
212
|
+
end
|
213
|
+
|
214
|
+
def make_response(uri, request)
|
86
215
|
|
87
216
|
request["Accept"] = "application/json, text/javascript, */*; q=0.01"
|
88
217
|
request["Accept-Language"] = "en-GB,en-US;q=0.9,en;q=0.8"
|
89
218
|
request["Connection"] = "keep-alive"
|
90
|
-
request["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8"
|
91
219
|
request["Cookie"] = @cookie
|
92
|
-
|
220
|
+
|
93
221
|
request["Referer"] = "https://#{@domain}/spa/index.html"
|
94
222
|
request["Sec-Fetch-Dest"] = "empty"
|
95
223
|
request["Sec-Fetch-Mode"] = "cors"
|
96
224
|
request["Sec-Fetch-Site"] = "same-origin"
|
97
|
-
request["User-Agent"] = "Mozilla/5.0 (X11; Linux x86_64)
|
225
|
+
request["User-Agent"] = "Mozilla/5.0 (X11; Linux x86_64) " +
|
226
|
+
"AppleWebKit/537.36 (KHTML, like Gecko) " +
|
227
|
+
"Chrome/101.0.4951.64 Safari/537.36"
|
98
228
|
request["X-Requested-With"] = "XMLHttpRequest"
|
99
229
|
request["csrf"] = "-990908140"
|
100
230
|
request["dnt"] = "1"
|
@@ -110,8 +240,118 @@ class AlexaRemoteCtl
|
|
110
240
|
http.request(request)
|
111
241
|
end
|
112
242
|
|
243
|
+
end
|
244
|
+
|
245
|
+
def post_command(url, body='')
|
246
|
+
|
247
|
+
uri = URI.parse(url)
|
248
|
+
request = Net::HTTP::Post.new(uri)
|
249
|
+
request.body = body
|
250
|
+
request.content_type = "application/x-www-form-urlencoded; charset=UTF-8"
|
251
|
+
request["Origin"] = "https://" + @domain
|
252
|
+
|
253
|
+
response = make_response(uri, request)
|
254
|
+
|
255
|
+
# response.code
|
256
|
+
# response.body
|
257
|
+
end
|
258
|
+
|
259
|
+
def get_request(url)
|
260
|
+
|
261
|
+
uri = URI.parse(url)
|
262
|
+
request = Net::HTTP::Get.new(uri)
|
263
|
+
response = make_response(uri, request)
|
264
|
+
|
113
265
|
# response.code
|
114
266
|
# response.body
|
115
267
|
|
116
268
|
end
|
269
|
+
|
270
|
+
|
271
|
+
def post_request(url, body='')
|
272
|
+
|
273
|
+
uri = URI.parse(url)
|
274
|
+
request = Net::HTTP::Post.new(uri)
|
275
|
+
request.body = body
|
276
|
+
response = make_response(uri, request)
|
277
|
+
|
278
|
+
# response.code
|
279
|
+
# response.body
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
class AlexaDevices
|
286
|
+
|
287
|
+
# devices = [[{*serialno*, *type*}, label], ...]
|
288
|
+
# note: label can be any identifier you choose e.g. kitchen
|
289
|
+
#
|
290
|
+
def initialize(devicesx=[], devices: devicesx,
|
291
|
+
domain: 'alexa.amazon.co.uk', cookie: '', customerid: '')
|
292
|
+
|
293
|
+
@devices, @domain, @cookie = devices, domain, cookie
|
294
|
+
@customerid = customerid
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
def playing()
|
299
|
+
|
300
|
+
@devices.map do |device, label|
|
301
|
+
alexa = get_alexa device
|
302
|
+
alexa.playing? ? [alexa, label] : nil
|
303
|
+
end.compact
|
304
|
+
|
305
|
+
end
|
306
|
+
|
307
|
+
def pause(id=nil)
|
308
|
+
|
309
|
+
a = playing()
|
310
|
+
|
311
|
+
if id then
|
312
|
+
|
313
|
+
alexa, _ = a.find {|_, label| label.to_sym == id.to_sym}
|
314
|
+
alexa.pause
|
315
|
+
|
316
|
+
else
|
317
|
+
|
318
|
+
a.each do |alexa, label|
|
319
|
+
puts 'Pausing @' + label.inspect
|
320
|
+
alexa.pause
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def play(id=nil)
|
327
|
+
|
328
|
+
a = @devices
|
329
|
+
|
330
|
+
if id then
|
331
|
+
|
332
|
+
device, _ = a.find {|_, label| label.to_sym == id.to_sym}
|
333
|
+
alexa = get_alexa device
|
334
|
+
alexa.play
|
335
|
+
|
336
|
+
else
|
337
|
+
|
338
|
+
a.each do |device, label|
|
339
|
+
|
340
|
+
puts 'Pausing @' + label.inspect
|
341
|
+
alexa = get_alexa device
|
342
|
+
alexa.play
|
343
|
+
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
private
|
350
|
+
|
351
|
+
def get_alexa(device)
|
352
|
+
|
353
|
+
AlexaRemoteCtl.new(cookie: @cookie, device: device,
|
354
|
+
customerid: @customerid)
|
355
|
+
end
|
356
|
+
|
117
357
|
end
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alexa_remotectl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Robertson
|
@@ -35,7 +35,7 @@ cert_chain:
|
|
35
35
|
d/gEwBtUmduwWZNX2NOJehf8s1cnH9OBP7s1ziHjdkZCgkQm3QewRRcdfZeYwFi6
|
36
36
|
4kVEcApgAgd+04ctE6uiRn5o
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date: 2022-05-
|
38
|
+
date: 2022-05-22 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: clipboard
|
metadata.gz.sig
CHANGED
Binary file
|