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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfe02b279946e6fa20e4615f417bbfa52e8128987b630338ae08d86d3e82374e
4
- data.tar.gz: f8be3e2e607a59f7592a36810455ae49383750f1e8755cb7fab28edbf83ba219
3
+ metadata.gz: 9b1f56f3518e257c26d3d00ac9e63dde021474827abb5461fdb11114f95196a2
4
+ data.tar.gz: 3dbe1a051f41b9b11f77e0f82c392f90c72d23ee4ba0314cc6ebc3c0deb31adc
5
5
  SHA512:
6
- metadata.gz: add9df3f63dc5f001ce55703de6e7b579ce646bf713a40a541843eca6ca6fc752a03209209f15c3cdc714bc5a066477381f2f746e6d0d1608c096e9cfcf35973
7
- data.tar.gz: 00a8dc58950293d74b42b924c83e4fadfea64f4a70eff6a42489c0d2b8048deac4e76ede59f4c6da594355a44a9c70c3dfe1a5d0f2ea07ebe8a08582713bd04f
6
+ metadata.gz: ff774816c05f4c379b0563f83ff5b9036161c58975438898723d2460aadbb29fe554fa6b84f6c5c12208fb1e375ccdaccac5d006944b7262c321c9a5b4abe724
7
+ data.tar.gz: 236d52c95e7421ee51af2d425523ddb85d193290398b4886bfae7c718a8e16f6b1450a316301dc8a077ba0436d59ecb2b7faa301041865ff179505a3001c3594
checksums.yaml.gz.sig CHANGED
@@ -1 +1,2 @@
1
- j^�II6��QrI㎞�_T ���� �<���c���8$�j���p&KFFD<'247��_��i��di���[S�F�}��k�:a'0E�ݹ�o��di�_ ��<Y',���O�E��Z�bw�ݺ �2�م�2`b��9XXw��f��^`4E���^!P���`�0"�.U��|��x6�,��J5���Ⲍ'N|Q
1
+ t$45Y!%=�zo����V�[�X&�F ǡ�wh)���aU%��Sdd(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����lJ4~jVO7YxSFH`�����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
@@ -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
- device_cmd('Pause')
89
+ pp_cmd('Pause')
68
90
  end
69
91
 
70
92
  def play
71
- device_cmd('Play')
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
- def device_cmd(cmd)
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?deviceSerialNumber=#{serialno}&deviceType=#{type}"
82
- uri = URI.parse(url)
83
- request = Net::HTTP::Post.new(uri)
84
- request.body = '{"type":"' + cmd + 'Command","contentFocusClientId":null}'
85
- request.content_type = "application/x-www-form-urlencoded; charset=UTF-8"
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
- request["Origin"] = "https://" + @domain
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) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36"
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.2.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-18 00:00:00.000000000 Z
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