ewelink 2.1.1 → 2.2.0
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/VERSION +1 -1
- data/ewelink.gemspec +1 -1
- data/lib/ewelink.rb +2 -1
- data/lib/ewelink/api.rb +102 -92
- metadata +12 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2392627c54c851fb5fd14194b484b56b1013b7a26e8d5bafe2a79e37dfa80464
|
4
|
+
data.tar.gz: 2a5362bafa04f9e3cf212a5899b4e6183514fdff68b3146a4fa245c52e8fcadd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2d3ecbf884461e11a51cf7e4efe7438f1c066cc7c076629371c96b3255b50f2a3f4e0314f6a40f9b7247adef06d69eb0b1f66b31ebcabff1c46b7def21d8291
|
7
|
+
data.tar.gz: a2d064d2af9e800ef0165bf5d614e4857abb0503b1faad7e280f3f2df422f08c5c49e57e10b3202bfcac700dc667861690c0a29a44faaf13a5ae00307201fe87
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.0
|
data/ewelink.gemspec
CHANGED
@@ -16,8 +16,8 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.required_ruby_version = '>= 2.0.0'
|
17
17
|
|
18
18
|
s.add_dependency 'activesupport', '>= 6.0.0', '< 7.0.0'
|
19
|
+
s.add_dependency 'faye-websocket', '>= 0.11.0', '< 0.12.0'
|
19
20
|
s.add_dependency 'httparty', '>= 0.18.0', '< 0.19.0'
|
20
|
-
s.add_dependency 'websocket-client-simple', '>= 0.3.0', '< 0.4.0'
|
21
21
|
|
22
22
|
s.add_development_dependency 'byebug', '>= 11.0.0', '< 12.0.0'
|
23
23
|
s.add_development_dependency 'rake', '>= 12.0.0', '< 13.0.0'
|
data/lib/ewelink.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'active_support'
|
2
2
|
require 'active_support/core_ext'
|
3
3
|
require 'byebug' if ENV['DEBUGGER']
|
4
|
+
require 'eventmachine'
|
5
|
+
require 'faye/websocket'
|
4
6
|
require 'httparty'
|
5
7
|
require 'io/console'
|
6
8
|
require 'json'
|
@@ -9,7 +11,6 @@ require 'openssl'
|
|
9
11
|
require 'optparse'
|
10
12
|
require 'set'
|
11
13
|
require 'timeout'
|
12
|
-
require 'websocket-client-simple'
|
13
14
|
|
14
15
|
module Ewelink
|
15
16
|
|
data/lib/ewelink/api.rb
CHANGED
@@ -50,7 +50,7 @@ module Ewelink
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def reload
|
53
|
-
Ewelink.logger.debug(self.class.name) { 'Reloading API (authentication token, devices, region
|
53
|
+
Ewelink.logger.debug(self.class.name) { 'Reloading API (authentication token, devices, region,...)' }
|
54
54
|
dispose_web_socket
|
55
55
|
[
|
56
56
|
:@api_keys,
|
@@ -102,21 +102,20 @@ module Ewelink
|
|
102
102
|
def switch_on?(uuid)
|
103
103
|
switch = find_switch!(uuid)
|
104
104
|
if @web_socket_switches_statuses[switch[:uuid]].nil?
|
105
|
-
params = {
|
106
|
-
'action' => 'query',
|
107
|
-
'apikey' => switch[:api_key],
|
108
|
-
'deviceid' => switch[:device_id],
|
109
|
-
'sequence' => web_socket_sequence,
|
110
|
-
'ts' => 0,
|
111
|
-
'userAgent' => 'app',
|
112
|
-
}
|
113
105
|
web_socket_wait_for(-> { web_socket_authenticated? }) do
|
114
106
|
Ewelink.logger.debug(self.class.name) { "Checking switch #{switch[:uuid].inspect} status" }
|
107
|
+
params = {
|
108
|
+
'action' => 'query',
|
109
|
+
'apikey' => switch[:api_key],
|
110
|
+
'deviceid' => switch[:device_id],
|
111
|
+
'sequence' => web_socket_sequence,
|
112
|
+
'ts' => 0,
|
113
|
+
'userAgent' => 'app',
|
114
|
+
}
|
115
115
|
send_to_web_socket(JSON.generate(params))
|
116
116
|
end
|
117
117
|
end
|
118
118
|
web_socket_wait_for(-> { !@web_socket_switches_statuses[switch[:uuid]].nil? }) do
|
119
|
-
Ewelink.logger.debug(self.class.name) { "Switch #{switch[:uuid].inspect} is #{@web_socket_switches_statuses[switch[:uuid]]}" }
|
120
119
|
@web_socket_switches_statuses[switch[:uuid]] == 'on'
|
121
120
|
end
|
122
121
|
end
|
@@ -176,6 +175,24 @@ module Ewelink
|
|
176
175
|
end
|
177
176
|
end
|
178
177
|
|
178
|
+
def authenticate_web_socket_api_keys
|
179
|
+
api_keys.each do |api_key|
|
180
|
+
params = {
|
181
|
+
'action' => 'userOnline',
|
182
|
+
'apikey' => api_key,
|
183
|
+
'appid' => APP_ID,
|
184
|
+
'at' => authentication_token,
|
185
|
+
'nonce' => nonce,
|
186
|
+
'sequence' => web_socket_sequence,
|
187
|
+
'ts' => Time.now.to_i,
|
188
|
+
'userAgent' => 'app',
|
189
|
+
'version' => VERSION,
|
190
|
+
}
|
191
|
+
Ewelink.logger.debug(self.class.name) { "Authenticating WebSocket API key: #{api_key.truncate(16).inspect}" }
|
192
|
+
send_to_web_socket(JSON.generate(params))
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
179
196
|
def authentication_headers
|
180
197
|
{ 'Authorization' => "Bearer #{authentication_token}" }
|
181
198
|
end
|
@@ -221,14 +238,16 @@ module Ewelink
|
|
221
238
|
end
|
222
239
|
|
223
240
|
def dispose_web_socket
|
241
|
+
Ewelink.logger.debug(self.class.name) { 'Dispose WebSocket' }
|
224
242
|
@web_socket_authenticated_api_keys.clear
|
225
243
|
@web_socket_switches_statuses.clear
|
226
244
|
|
227
|
-
|
228
|
-
|
229
|
-
|
245
|
+
[@web_socket_ping_thread, @web_socket_thread].each do |thread|
|
246
|
+
next unless thread
|
247
|
+
if Thread.current == thread
|
248
|
+
thread[:stop] = true
|
230
249
|
else
|
231
|
-
|
250
|
+
thread.kill
|
232
251
|
end
|
233
252
|
end
|
234
253
|
|
@@ -244,6 +263,7 @@ module Ewelink
|
|
244
263
|
:@last_web_socket_pong_at,
|
245
264
|
:@web_socket_ping_interval,
|
246
265
|
:@web_socket_ping_thread,
|
266
|
+
:@web_socket_thread,
|
247
267
|
:@web_socket_url,
|
248
268
|
:@web_socket,
|
249
269
|
].each do |variable|
|
@@ -286,15 +306,29 @@ module Ewelink
|
|
286
306
|
raise Error.new(e)
|
287
307
|
end
|
288
308
|
|
289
|
-
def send_to_web_socket(
|
309
|
+
def send_to_web_socket(message)
|
290
310
|
if web_socket_outdated_ping?
|
291
311
|
Ewelink.logger.warn(self.class.name) { 'WebSocket ping is outdated' }
|
292
312
|
dispose_web_socket
|
293
313
|
end
|
294
|
-
web_socket.send(
|
295
|
-
rescue
|
314
|
+
web_socket.send(message)
|
315
|
+
rescue => e
|
296
316
|
dispose_web_socket
|
297
|
-
raise
|
317
|
+
raise Error.new(e)
|
318
|
+
end
|
319
|
+
|
320
|
+
def start_web_socket_ping_thread(interval)
|
321
|
+
@last_web_socket_pong_at = Time.now
|
322
|
+
@web_socket_ping_interval = interval
|
323
|
+
Ewelink.logger.debug(self.class.name) { "Creating thread for WebSocket ping every #{@web_socket_ping_interval} seconds" }
|
324
|
+
@web_socket_ping_thread = Thread.new do
|
325
|
+
loop do
|
326
|
+
break if Thread.current[:stop]
|
327
|
+
sleep(@web_socket_ping_interval)
|
328
|
+
Ewelink.logger.debug(self.class.name) { 'Sending WebSocket ping' }
|
329
|
+
send_to_web_socket('ping')
|
330
|
+
end
|
331
|
+
end
|
298
332
|
end
|
299
333
|
|
300
334
|
def synchronize(name, &block)
|
@@ -303,94 +337,73 @@ module Ewelink
|
|
303
337
|
|
304
338
|
def web_socket
|
305
339
|
synchronize(:web_socket) do
|
306
|
-
@web_socket
|
307
|
-
api = self
|
340
|
+
next @web_socket if @web_socket
|
308
341
|
|
309
|
-
|
342
|
+
@web_socket_thread = Thread.new do
|
343
|
+
EventMachine.run do
|
310
344
|
Ewelink.logger.debug(self.class.name) { "Opening WebSocket to #{web_socket_url.inspect}" }
|
311
345
|
|
312
|
-
web_socket.
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
346
|
+
@web_socket = Faye::WebSocket::Client.new('wss://as-pconnect3.coolkit.cc:8080/api/ws')
|
347
|
+
|
348
|
+
@web_socket.on(:close) do |event|
|
349
|
+
Ewelink.logger.debug(self.class.name) { 'WebSocket closed' }
|
350
|
+
dispose_web_socket
|
317
351
|
end
|
318
352
|
|
319
|
-
web_socket.on(:
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
end
|
353
|
+
@web_socket.on(:open) do |event|
|
354
|
+
Ewelink.logger.debug(self.class.name) { 'WebSocket opened' }
|
355
|
+
@last_web_socket_pong_at = Time.now
|
356
|
+
authenticate_web_socket_api_keys
|
324
357
|
end
|
325
358
|
|
326
|
-
web_socket.on(:message) do |
|
327
|
-
|
328
|
-
if message.data == 'pong'
|
329
|
-
Ewelink.logger.debug(self.class.name) { "Received WebSocket #{message.data.inspect} message" }
|
330
|
-
@last_web_socket_pong_at = Time.now
|
331
|
-
next
|
332
|
-
end
|
359
|
+
@web_socket.on(:message) do |event|
|
360
|
+
message = event.data
|
333
361
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
end
|
362
|
+
if message == 'pong'
|
363
|
+
Ewelink.logger.debug(self.class.name) { "Received WebSocket #{message.inspect} message" }
|
364
|
+
@last_web_socket_pong_at = Time.now
|
365
|
+
next
|
366
|
+
end
|
340
367
|
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
368
|
+
begin
|
369
|
+
json = JSON.parse(message)
|
370
|
+
rescue => e
|
371
|
+
Ewelink.logger.error(self.class.name) { 'WebSocket JSON parse error' }
|
372
|
+
next
|
373
|
+
end
|
345
374
|
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
@web_socket_ping_thread = Thread.new do
|
351
|
-
loop do
|
352
|
-
break if Thread.current[:stop]
|
353
|
-
sleep(@web_socket_ping_interval)
|
354
|
-
Ewelink.logger.debug(self.class.name) { 'Sending WebSocket ping' }
|
355
|
-
send_to_web_socket('ping')
|
356
|
-
end
|
357
|
-
end
|
358
|
-
end
|
375
|
+
if json.key?('error') && json['error'] != 0
|
376
|
+
Ewelink.logger.error(self.class.name) { "WebSocket message error: #{message.inspect}" }
|
377
|
+
next
|
378
|
+
end
|
359
379
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
end
|
380
|
+
if !@web_socket_ping_thread && json.key?('config') && json['config']['hb'] == 1 && json['config']['hbInterval'].present?
|
381
|
+
start_web_socket_ping_thread(json['config']['hbInterval'] + 7)
|
382
|
+
end
|
364
383
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
end
|
384
|
+
if json['apikey'].present? && !@web_socket_authenticated_api_keys.include?(json['apikey'])
|
385
|
+
@web_socket_authenticated_api_keys << json['apikey']
|
386
|
+
Ewelink.logger.debug(self.class.name) { "WebSocket successfully authenticated API key: #{json['apikey'].truncate(16).inspect}" }
|
369
387
|
end
|
370
|
-
end
|
371
388
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
'action' => 'userOnline',
|
378
|
-
'apikey' => api_key,
|
379
|
-
'appid' => APP_ID,
|
380
|
-
'at' => authentication_token,
|
381
|
-
'nonce' => nonce,
|
382
|
-
'sequence' => web_socket_sequence,
|
383
|
-
'ts' => Time.now.to_i,
|
384
|
-
'userAgent' => 'app',
|
385
|
-
'version' => VERSION,
|
386
|
-
}
|
387
|
-
Ewelink.logger.debug(self.class.name) { "Authenticating WebSocket API key: #{api_key.truncate(16).inspect}" }
|
388
|
-
send_to_web_socket(JSON.generate(params))
|
389
|
+
if json['deviceid'].present? && json['params'].is_a?(Hash) && json['params']['switch'].present?
|
390
|
+
switch = switches.find { |switch| switch[:device_id] == json['deviceid'] }
|
391
|
+
if switch.present?
|
392
|
+
@web_socket_switches_statuses[switch[:uuid]] = json['params']['switch']
|
393
|
+
Ewelink.logger.debug(self.class.name) { "Switch #{switch[:uuid].inspect} is #{@web_socket_switches_statuses[switch[:uuid]]}" }
|
389
394
|
end
|
390
395
|
end
|
391
396
|
end
|
392
397
|
end
|
393
398
|
end
|
399
|
+
|
400
|
+
Timeout.timeout(REQUEST_TIMEOUT) do
|
401
|
+
while @web_socket.blank?
|
402
|
+
sleep(WEB_SOCKET_WAIT_INTERVAL)
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
@web_socket
|
394
407
|
end
|
395
408
|
end
|
396
409
|
|
@@ -428,13 +441,10 @@ module Ewelink
|
|
428
441
|
def web_socket_wait_for(condition, &block)
|
429
442
|
web_socket # Initializes WebSocket
|
430
443
|
Timeout.timeout(REQUEST_TIMEOUT) do
|
431
|
-
|
432
|
-
if condition.call
|
433
|
-
return yield if block_given?
|
434
|
-
return true
|
435
|
-
end
|
444
|
+
while !condition.call
|
436
445
|
sleep(WEB_SOCKET_WAIT_INTERVAL)
|
437
446
|
end
|
447
|
+
block_given? ? yield : true
|
438
448
|
end
|
439
449
|
end
|
440
450
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ewelink
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexis Toulotte
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
11
|
+
date: 2020-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -31,45 +31,45 @@ dependencies:
|
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 7.0.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: faye-websocket
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: 0.
|
39
|
+
version: 0.11.0
|
40
40
|
- - "<"
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: 0.
|
42
|
+
version: 0.12.0
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
47
|
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: 0.
|
49
|
+
version: 0.11.0
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version: 0.
|
52
|
+
version: 0.12.0
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
|
-
name:
|
54
|
+
name: httparty
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
56
56
|
requirements:
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: 0.
|
59
|
+
version: 0.18.0
|
60
60
|
- - "<"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: 0.
|
62
|
+
version: 0.19.0
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
67
|
- - ">="
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version: 0.
|
69
|
+
version: 0.18.0
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version: 0.
|
72
|
+
version: 0.19.0
|
73
73
|
- !ruby/object:Gem::Dependency
|
74
74
|
name: byebug
|
75
75
|
requirement: !ruby/object:Gem::Requirement
|