ewelink 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb352abf05d6bb6ef2f3b277c6f551d436bed969086478d3e121fd2e178fe4f9
4
- data.tar.gz: b74266f2cbc92457a3bb0dc087752299a5a9f8348d3928a79d8c3c0719653e6a
3
+ metadata.gz: 2392627c54c851fb5fd14194b484b56b1013b7a26e8d5bafe2a79e37dfa80464
4
+ data.tar.gz: 2a5362bafa04f9e3cf212a5899b4e6183514fdff68b3146a4fa245c52e8fcadd
5
5
  SHA512:
6
- metadata.gz: 2c5bb695c7d0094436454694008dd3c05c660fb1c7dfe2436ec1fa2fae3341559f901f3b9ae4ebfa99fcc276dcd4ceee02205ed1608d03e515b8da64b52f8a25
7
- data.tar.gz: 4713a948c736a40496b3b33fff3b83253a4559693d692cfaa7251a4fc2d3f0e67699834f6513226edcbfe6cf7759ab54b1cb6c5a24cbe1559f54a75d6cbda6fd
6
+ metadata.gz: b2d3ecbf884461e11a51cf7e4efe7438f1c066cc7c076629371c96b3255b50f2a3f4e0314f6a40f9b7247adef06d69eb0b1f66b31ebcabff1c46b7def21d8291
7
+ data.tar.gz: a2d064d2af9e800ef0165bf5d614e4857abb0503b1faad7e280f3f2df422f08c5c49e57e10b3202bfcac700dc667861690c0a29a44faaf13a5ae00307201fe87
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.1.1
1
+ 2.2.0
@@ -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'
@@ -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
 
@@ -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, connections,...)' }
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
- if @web_socket_ping_thread
228
- if Thread.current == @web_socket_ping_thread
229
- Thread.current[:stop] = true
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
- @web_socket_ping_thread.kill
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(data)
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(data)
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 ||= begin
307
- api = self
340
+ next @web_socket if @web_socket
308
341
 
309
- WebSocket::Client::Simple.connect(web_socket_url) do |web_socket|
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.on(:close) do
313
- api.instance_eval do
314
- Ewelink.logger.debug(self.class.name) { 'WebSocket closed' }
315
- dispose_web_socket
316
- end
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(:error) do |e|
320
- api.instance_eval do
321
- Ewelink.logger.warn(self.class.name) { "WebSocket error: #{e}" }
322
- dispose_web_socket
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 |message|
327
- api.instance_eval do
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
- begin
335
- response = JSON.parse(message.data)
336
- rescue => e
337
- Ewelink.logger.error(self.class.name) { "WebSocket JSON parse error" }
338
- next
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
- if response.key?('error') && response['error'] != 0
342
- Ewelink.logger.error(self.class.name) { "WebSocket message error: #{message.data}" }
343
- next
344
- end
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
- if !@web_socket_ping_thread && response.key?('config') && response['config']['hb'] == 1 && response['config']['hbInterval'].present?
347
- @last_web_socket_pong_at = Time.now
348
- @web_socket_ping_interval = response['config']['hbInterval'] + 7
349
- Ewelink.logger.debug(self.class.name) { "Creating thread for WebSocket ping every #{@web_socket_ping_interval} seconds" }
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
- if response['apikey'].present? && !@web_socket_authenticated_api_keys.include?(response['apikey'])
361
- @web_socket_authenticated_api_keys << response['apikey']
362
- Ewelink.logger.debug(self.class.name) { "WebSocket successfully authenticated API key: #{response['apikey'].truncate(16).inspect}" }
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
- if response['deviceid'].present? && response['params'].is_a?(Hash) && response['params']['switch'].present?
366
- switch = switches.find { |switch| switch[:device_id] == response['deviceid'] }
367
- @web_socket_switches_statuses[switch[:uuid]] = response['params']['switch'] if switch.present?
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
- web_socket.on(:open) do
373
- api.instance_eval do
374
- Ewelink.logger.debug(self.class.name) { 'WebSocket opened' }
375
- api_keys.each do |api_key|
376
- params = {
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
- loop do
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.1.1
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-02 00:00:00.000000000 Z
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: httparty
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.18.0
39
+ version: 0.11.0
40
40
  - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: 0.19.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.18.0
49
+ version: 0.11.0
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: 0.19.0
52
+ version: 0.12.0
53
53
  - !ruby/object:Gem::Dependency
54
- name: websocket-client-simple
54
+ name: httparty
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: 0.3.0
59
+ version: 0.18.0
60
60
  - - "<"
61
61
  - !ruby/object:Gem::Version
62
- version: 0.4.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.3.0
69
+ version: 0.18.0
70
70
  - - "<"
71
71
  - !ruby/object:Gem::Version
72
- version: 0.4.0
72
+ version: 0.19.0
73
73
  - !ruby/object:Gem::Dependency
74
74
  name: byebug
75
75
  requirement: !ruby/object:Gem::Requirement