ewelink 5.0.0 → 6.0.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 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