ewelink 5.0.0 → 6.0.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: f2f26d6875823958f38a894377c7dfbce3531d7f98b1c1c69b18bd88caf44c1d
4
- data.tar.gz: d25335eb91529c57d2780c42e8cef5856cb7bfa7f222e5282816bff122854b08
3
+ metadata.gz: '03889642d2851312650d6419544fd7423b2b8ca0a64a272386de46646640b191'
4
+ data.tar.gz: b36f496794dce02953b423d601f4e71905afca7c0aebe4fe5bfc65d08f65daff
5
5
  SHA512:
6
- metadata.gz: 42786625ddc6d8482a5d85922e25b4b650e080866148b378aeb579ad25e0dcee00d4f12b8ebf74492e207d80e6ed0d65b6f0c71ccdaf5c3f81280b1efbd4bc7b
7
- data.tar.gz: 8eb9cc344312a30a7b0924ff02b052516bf5123a7d706acb15e000b91db2c91ecf56a2173d22543403f7fbbc31773e30d97d3c5379ad37ab42e34b55f8f67366
6
+ metadata.gz: c3a3349b5c5f5384eb4f02abcf3749b0674ef839c2354f38d319db4ff15e272f7ec551058b52601a1971307f225eac8d0c5c7cfee00d6537a527bb09f044aceb
7
+ data.tar.gz: 63035965d8732f4fda004b16331ee06a4ad6f55da7a4338b041585823397b14bb6dfecacf83123354b024f2a9fb8e1b724d32011862d9343babec527fff12d2e
data/README.mdown CHANGED
@@ -80,8 +80,6 @@ api.press_rf_bridge_button!(button[:uuid])
80
80
  - `async_actions` (`true` | `false`): To perform actions (pressing an RF
81
81
  bridge button or turning a switch on/off) in asynchronous mode. (default:
82
82
  `false`).
83
- - `update_devices_status_on_connect` (`true` | `false`): To update devices
84
- status (on, off) when connecting to Ewelink API (default: `false`).
85
83
 
86
84
  ### Configuring logger
87
85
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 5.0.0
1
+ 6.0.0
data/ewelink.gemspec CHANGED
@@ -16,7 +16,6 @@ Gem::Specification.new do |s|
16
16
  s.required_ruby_version = '>= 3.1.0'
17
17
 
18
18
  s.add_dependency 'activesupport', '>= 7.0.0', '< 8.0.0'
19
- s.add_dependency 'faye-websocket', '>= 0.11.0', '< 0.12.0'
20
19
  s.add_dependency 'httparty', '>= 0.20.0', '< 0.21.0'
21
20
  s.add_dependency 'thread', '>= 0.2.0', '< 0.3.0'
22
21
 
data/lib/ewelink/api.rb CHANGED
@@ -11,26 +11,17 @@ module Ewelink
11
11
  SWITCH_DEVICES_UIIDS = [1, 5, 6, 24].freeze
12
12
  URL = 'https://#{region}-apia.coolkit.cc'.freeze
13
13
  VERSION = 8
14
- WEB_SOCKET_CHECK_AUTHENTICATION_TIMEOUT = 30.seconds
15
- WEB_SOCKET_PING_TOLERANCE_FACTOR = 1.5
16
- SWITCH_STATUS_CHANGE_CHECK_TIMEOUT = 2.seconds
17
- WEB_SOCKET_WAIT_INTERVAL = 0.2.seconds
18
14
 
19
15
  attr_reader :email, :password, :phone_number
20
16
 
21
- def initialize(password:, async_actions: false, email: nil, phone_number: nil, update_devices_status_on_connect: false)
17
+ def initialize(password:, async_actions: false, email: nil, phone_number: nil)
22
18
  @async_actions = async_actions.present?
23
19
  @email = email.presence.try(:strip)
24
20
  @mutexs = {}
25
21
  @password = password.presence || raise(Error.new(':password must be specified'))
26
22
  @phone_number = phone_number.presence.try(:strip)
27
- @update_devices_status_on_connect = update_devices_status_on_connect.present?
28
- @web_socket_authenticated = false
29
- @web_socket_switches_statuses = {}
30
23
 
31
24
  raise(Error.new(':email or :phone_number must be specified')) if email.blank? && phone_number.blank?
32
-
33
- start_web_socket_authentication_check_thread
34
25
  end
35
26
 
36
27
  def async_actions?
@@ -63,39 +54,13 @@ module Ewelink
63
54
  def reload
64
55
  Ewelink.logger.debug(self.class.name) { 'Reloading API (authentication token, api key, devices, region, connections,...)' }
65
56
 
66
- @web_socket_authenticated = false
67
- @web_socket_switches_statuses.clear
68
-
69
- [@web_socket_ping_thread, @web_socket_thread].each do |thread|
70
- next unless thread
71
- if Thread.current == thread
72
- thread[:stop] = true
73
- else
74
- thread.kill
75
- end
76
- end
77
-
78
- if @web_socket.present?
79
- begin
80
- @web_socket.close if @web_socket.open?
81
- rescue
82
- # Ignoring close errors
83
- end
84
- end
85
-
86
57
  %i(
87
58
  @authentication_infos
88
59
  @devices
89
60
  @homes_ids
90
- @last_web_socket_pong_at
91
61
  @region
92
62
  @rf_bridge_buttons
93
63
  @switches
94
- @web_socket_ping_interval
95
- @web_socket_ping_thread
96
- @web_socket_thread
97
- @web_socket_url
98
- @web_socket
99
64
  ).each do |variable|
100
65
  remove_instance_variable(variable) if instance_variable_defined?(variable)
101
66
  end
@@ -138,23 +103,18 @@ module Ewelink
138
103
 
139
104
  def switch_on?(uuid)
140
105
  switch = find_switch!(uuid)
141
- if @web_socket_switches_statuses[switch[:uuid]].nil?
142
- web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
143
- Ewelink.logger.debug(self.class.name) { "Checking switch #{switch[:uuid].inspect} status" }
144
- params = {
145
- 'action' => 'query',
146
- 'apikey' => switch[:api_key],
147
- 'deviceid' => switch[:device_id],
148
- 'sequence' => web_socket_sequence,
149
- 'ts' => 0,
150
- 'userAgent' => 'app',
151
- }
152
- send_to_web_socket(JSON.generate(params))
153
- end
154
- end
155
- web_socket_wait_for(-> { !@web_socket_switches_statuses[switch[:uuid]].nil? }, initialize_web_socket: true) do
156
- @web_socket_switches_statuses[switch[:uuid]] == 'on'
157
- end
106
+ headers = authentication_headers.merge(
107
+ 'X-CK-Appid' => APP_ID,
108
+ 'X-CK-Nonce' => nonce,
109
+ )
110
+ params = {
111
+ 'type' => 1,
112
+ 'id' => switch[:device_id],
113
+ 'params' => 'switch',
114
+ }
115
+ Ewelink.logger.debug(self.class.name) { "Checking switch #{switch[:uuid].inspect} status" }
116
+ response = rest_request(:get, '/v2/device/thing/status', headers:, query: params)
117
+ response['data']['params']['switch'] == 'on'
158
118
  end
159
119
 
160
120
  def switches
@@ -186,54 +146,30 @@ module Ewelink
186
146
  on = false
187
147
  end
188
148
  switch = find_switch!(uuid)
189
- @web_socket_switches_statuses[switch[:uuid]] = nil
190
- web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
191
- params = {
192
- 'action' => 'update',
193
- 'apikey' => switch[:api_key],
194
- 'deviceid' => switch[:device_id],
195
- 'params' => {
196
- 'switch' => on ? 'on' : 'off',
197
- },
198
- 'sequence' => web_socket_sequence,
199
- 'ts' => 0,
200
- 'userAgent' => 'app',
201
- }
202
- Ewelink.logger.debug(self.class.name) { "Turning switch #{switch[:uuid].inspect} #{on ? 'on' : 'off'}" }
203
- send_to_web_socket(JSON.generate(params))
204
- end
205
- sleep(SWITCH_STATUS_CHANGE_CHECK_TIMEOUT)
206
- switch_on?(switch[:uuid]) # Waiting for switch status update
207
- true
149
+ headers = authentication_headers.merge(
150
+ 'X-CK-Appid' => APP_ID,
151
+ 'X-CK-Nonce' => nonce,
152
+ )
153
+ params = {
154
+ 'type' => 1,
155
+ 'id' => switch[:device_id],
156
+ 'params' => {
157
+ 'switch' => on ? 'on' : 'off',
158
+ },
159
+ }
160
+ body = JSON.generate(params)
161
+ Ewelink.logger.debug(self.class.name) { "Turning switch #{switch[:uuid].inspect} #{on ? 'on' : 'off'}" }
162
+ response = rest_request(:post, '/v2/device/thing/status', headers:, body:)
163
+ response['error'] == 0
208
164
  end
209
165
  end
210
166
 
211
- def update_devices_status_on_connect?
212
- @update_devices_status_on_connect
213
- end
214
-
215
167
  private
216
168
 
217
169
  def api_key
218
170
  authentication_infos[:api_key]
219
171
  end
220
172
 
221
- def authenticate_web_socket_api_key
222
- params = {
223
- 'action' => 'userOnline',
224
- 'apikey' => api_key,
225
- 'appid' => APP_ID,
226
- 'at' => authentication_token,
227
- 'nonce' => nonce,
228
- 'sequence' => web_socket_sequence,
229
- 'ts' => Time.now.to_i,
230
- 'userAgent' => 'app',
231
- 'version' => VERSION,
232
- }
233
- Ewelink.logger.debug(self.class.name) { "Authenticating WebSocket API key: #{api_key.truncate(16).inspect}" }
234
- send_to_web_socket(JSON.generate(params))
235
- end
236
-
237
173
  def authentication_headers
238
174
  { 'Authorization' => "Bearer #{authentication_token}" }
239
175
  end
@@ -353,173 +289,10 @@ module Ewelink
353
289
  raise Error.new(e)
354
290
  end
355
291
 
356
- def send_to_web_socket(message)
357
- web_socket.send(message)
358
- rescue => e
359
- reload
360
- raise Error.new(e)
361
- end
362
-
363
- def start_web_socket_authentication_check_thread
364
- raise Error.new('WebSocket authentication check must only be started once') if @web_socket_authentication_check_thread.present?
365
-
366
- @web_socket_authentication_check_thread = Thread.new do
367
- loop do
368
- Ewelink.logger.debug(self.class.name) { 'Checking if WebSocket is authenticated' }
369
- begin
370
- web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
371
- Ewelink.logger.debug(self.class.name) { 'WebSocket is authenticated' }
372
- end
373
- rescue => e
374
- Ewelink.logger.error(self.class.name) { e }
375
- end
376
- sleep(WEB_SOCKET_CHECK_AUTHENTICATION_TIMEOUT)
377
- end
378
- end
379
- end
380
-
381
- def start_web_socket_ping_thread(interval)
382
- @last_web_socket_pong_at = Time.now
383
- @web_socket_ping_interval = interval
384
- Ewelink.logger.debug(self.class.name) { "Creating thread for WebSocket ping every #{@web_socket_ping_interval} seconds" }
385
- @web_socket_ping_thread = Thread.new do
386
- loop do
387
- break if Thread.current[:stop]
388
- sleep(@web_socket_ping_interval)
389
- Ewelink.logger.debug(self.class.name) { 'Sending WebSocket ping' }
390
- send_to_web_socket('ping')
391
- end
392
- end
393
- end
394
-
395
292
  def synchronize(name, &block)
396
293
  (@mutexs[name] ||= Mutex.new).synchronize(&block)
397
294
  end
398
295
 
399
- def web_socket
400
- if web_socket_outdated_ping?
401
- Ewelink.logger.warn(self.class.name) { 'WebSocket ping is outdated' }
402
- reload
403
- end
404
-
405
- synchronize(:web_socket) do
406
- next @web_socket if @web_socket
407
-
408
- # Initializes caches before opening WebSocket: important in order to
409
- # NOT cumulate requests Timeouts from #web_socket_wait_for.
410
- api_key
411
- web_socket_url
412
-
413
- Ewelink.logger.debug(self.class.name) { "Opening WebSocket to #{web_socket_url.inspect}" }
414
-
415
- @web_socket_thread = Thread.new do
416
- EventMachine.run do
417
- @web_socket = Faye::WebSocket::Client.new(web_socket_url)
418
-
419
- @web_socket.on(:close) do
420
- Ewelink.logger.debug(self.class.name) { 'WebSocket closed' }
421
- reload
422
- end
423
-
424
- @web_socket.on(:open) do
425
- Ewelink.logger.debug(self.class.name) { 'WebSocket opened' }
426
- @last_web_socket_pong_at = Time.now
427
- authenticate_web_socket_api_key
428
- end
429
-
430
- @web_socket.on(:message) do |event|
431
- message = event.data
432
-
433
- if message == 'pong'
434
- Ewelink.logger.debug(self.class.name) { "Received WebSocket #{message.inspect} message" }
435
- @last_web_socket_pong_at = Time.now
436
- next
437
- end
438
-
439
- begin
440
- json = JSON.parse(message)
441
- rescue
442
- Ewelink.logger.error(self.class.name) { 'WebSocket JSON parse error' }
443
- reload
444
- next
445
- end
446
-
447
- if json.key?('error') && json['error'] != 0
448
- Ewelink.logger.error(self.class.name) { "WebSocket message error: #{message.inspect}" }
449
- reload
450
- next
451
- end
452
-
453
- if !@web_socket_ping_thread && json.key?('config') && json['config']['hb'] == 1 && json['config']['hbInterval'].present?
454
- start_web_socket_ping_thread(json['config']['hbInterval'] + 7)
455
- end
456
-
457
- if json['apikey'].present? && !@web_socket_authenticated && json['apikey'] == api_key
458
- @web_socket_authenticated = true
459
- Ewelink.logger.debug(self.class.name) { "WebSocket successfully authenticated API key: #{json['apikey'].truncate(16).inspect}" }
460
- Thread.new { switches.each { |switch| switch_on?(switch[:uuid]) } } if update_devices_status_on_connect?
461
- end
462
-
463
- if json['deviceid'].present? && json['params'].is_a?(Hash) && json['params']['switch'].present?
464
- switch = switches.find { |item| item[:device_id] == json['deviceid'] }
465
- if switch.present?
466
- @web_socket_switches_statuses[switch[:uuid]] = json['params']['switch']
467
- Ewelink.logger.debug(self.class.name) { "Switch #{switch[:uuid].inspect} is #{@web_socket_switches_statuses[switch[:uuid]]}" }
468
- end
469
- end
470
- end
471
- end
472
- end
473
-
474
- web_socket_wait_for(-> { @web_socket.present? }) do
475
- @web_socket
476
- end
477
- end
478
- end
479
-
480
- def web_socket_authenticated?
481
- @web_socket_authenticated.present?
482
- end
483
-
484
- def web_socket_outdated_ping?
485
- @last_web_socket_pong_at.present? && @web_socket_ping_interval.present? && @last_web_socket_pong_at < (@web_socket_ping_interval * WEB_SOCKET_PING_TOLERANCE_FACTOR).seconds.ago
486
- end
487
-
488
- def web_socket_sequence
489
- (Time.now.to_f * 1000).round.to_s
490
- end
491
-
492
- def web_socket_url
493
- synchronize(:web_socket_url) do
494
- @web_socket_url ||= begin
495
- params = {
496
- 'appid' => APP_ID,
497
- 'nonce' => nonce,
498
- 'ts' => Time.now.to_i,
499
- 'version' => VERSION,
500
- }
501
- response = rest_request(:post, '/dispatch/app', body: JSON.generate(params), dispatch: true, headers: authentication_headers)
502
- raise('Error while getting WebSocket URL') unless response['error'] == 0
503
- domain = response['domain'].presence || raise("Can't get WebSocket server domain")
504
- port = response['port'].presence || raise("Can't get WebSocket server port")
505
- "wss://#{domain}:#{port}/api/ws".tap { |url| Ewelink.logger.debug(self.class.name) { "WebSocket URL is: #{url.inspect}" } }
506
- end
507
- end
508
- end
509
-
510
- def web_socket_wait_for(condition, initialize_web_socket: false)
511
- web_socket if initialize_web_socket
512
- begin
513
- Timeout.timeout(REQUEST_TIMEOUT) do
514
- sleep(WEB_SOCKET_WAIT_INTERVAL) until condition.call
515
- block_given? ? yield : true
516
- end
517
- rescue => e
518
- reload
519
- raise Error.new(e)
520
- end
521
- end
522
-
523
296
  end
524
297
 
525
298
  end
data/lib/ewelink.rb CHANGED
@@ -1,8 +1,6 @@
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'
6
4
  require 'httparty'
7
5
  require 'io/console'
8
6
  require 'json'
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: 5.0.0
4
+ version: 6.0.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: 2022-07-09 00:00:00.000000000 Z
11
+ date: 2023-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -30,26 +30,6 @@ dependencies:
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: 8.0.0
33
- - !ruby/object:Gem::Dependency
34
- name: faye-websocket
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: 0.11.0
40
- - - "<"
41
- - !ruby/object:Gem::Version
42
- version: 0.12.0
43
- type: :runtime
44
- prerelease: false
45
- version_requirements: !ruby/object:Gem::Requirement
46
- requirements:
47
- - - ">="
48
- - !ruby/object:Gem::Version
49
- version: 0.11.0
50
- - - "<"
51
- - !ruby/object:Gem::Version
52
- version: 0.12.0
53
33
  - !ruby/object:Gem::Dependency
54
34
  name: httparty
55
35
  requirement: !ruby/object:Gem::Requirement