ewelink 2.0.0 → 2.1.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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/ewelink/api.rb +77 -9
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97046b44715a988f6ae92d986c0fad17ab4d5cae6420a125c10f23854cfaadc9
4
- data.tar.gz: b95e52d88f8d57d5bf9f54b67d5a8c0779d26b66faadbc252e13eadb80854292
3
+ metadata.gz: 2c58323e838b9fbbdca7c7ee0fdf22d1e7950c627fb4fcf8851b3263e69ce883
4
+ data.tar.gz: 475d05f731b079f7734fde4dd93ec8f151077d6e5b716a86e611f11bdbb8d49b
5
5
  SHA512:
6
- metadata.gz: f153a280ff50b245434b2039cd3bb88341da6bd7688556a8a7c3fe4558a2b13a6be5c1497f5b06dc381495cb63d8c26d4d3f2625ea5bd4148e4c55a2f28b2faf
7
- data.tar.gz: e3b69fa1607599325ea0f2867fbd0d32fd3abd56a4e7bd8d07107e083d4ebbfaf919bc4af34b28948163d1fb5340a53489aec1839cc59ba238f2917a8db03cc5
6
+ metadata.gz: 50ba5474e9012f219e380c47515d599da29a72b50cc26c36a19e253623c997e429a32c1b279619eef0e2d8b7f454fccde519500f998575c38d028ac86e6c1ba4
7
+ data.tar.gz: 122f2997897893b395cd04204e3e169d90bac15fc1a9eaabbf31def8da1c7f754bd502341411727364a79011379791a89031e9d42090d22bf96be233a24a5958
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0
1
+ 2.1.0
@@ -11,6 +11,7 @@ module Ewelink
11
11
  URL = 'https://#{region}-api.coolkit.cc:8080'
12
12
  UUID_NAMESPACE = 'e25750fb-3710-41af-b831-23224f4dd609';
13
13
  VERSION = 8
14
+ WEB_SOCKET_PING_TOLERANCE_FACTOR = 1.5
14
15
  WEB_SOCKET_WAIT_INTERVAL = 0.2.seconds
15
16
 
16
17
  attr_reader :email, :password, :phone_number
@@ -41,6 +42,7 @@ module Ewelink
41
42
  'ts' => 0,
42
43
  'userAgent' => 'app',
43
44
  }
45
+ Ewelink.logger.debug(self.class.name) { "Pressing RF bridge button #{button[:uuid].inspect}" }
44
46
  send_to_web_socket(JSON.generate(params))
45
47
  true
46
48
  end
@@ -48,10 +50,17 @@ module Ewelink
48
50
  end
49
51
 
50
52
  def reload
51
- 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, connections,...)' }
52
54
  dispose_web_socket
53
55
  @switches_statuses.clear
54
- [:@api_keys, :@authentication_token, :@devices, :@rf_bridge_buttons, :@region, :@switches, :@web_socket, :@web_socket_url].each do |variable|
56
+ [
57
+ :@api_keys,
58
+ :@authentication_token,
59
+ :@devices,
60
+ :@region,
61
+ :@rf_bridge_buttons,
62
+ :@switches,
63
+ ].each do |variable|
55
64
  remove_instance_variable(variable) if instance_variable_defined?(variable)
56
65
  end
57
66
  self
@@ -103,10 +112,12 @@ module Ewelink
103
112
  'userAgent' => 'app',
104
113
  }
105
114
  web_socket_wait_for(-> { web_socket_authenticated? }) do
115
+ Ewelink.logger.debug(self.class.name) { "Checking switch #{switch[:uuid].inspect} status" }
106
116
  send_to_web_socket(JSON.generate(params))
107
117
  end
108
118
  end
109
119
  web_socket_wait_for(-> { !@switches_statuses[switch[:uuid]].nil? }) do
120
+ Ewelink.logger.debug(self.class.name) { "Switch #{switch[:uuid].inspect} is #{@switches_statuses[switch[:uuid]]}" }
110
121
  @switches_statuses[switch[:uuid]] == 'on'
111
122
  end
112
123
  end
@@ -151,9 +162,11 @@ module Ewelink
151
162
  'ts' => 0,
152
163
  'userAgent' => 'app',
153
164
  }
165
+ Ewelink.logger.debug(self.class.name) { "Turning switch #{switch[:uuid].inspect} #{on ? 'on' : 'off'}" }
154
166
  send_to_web_socket(JSON.generate(params))
155
- true
156
167
  end
168
+ switch_on?(switch[:uuid]) # Waiting for switch status update
169
+ true
157
170
  end
158
171
 
159
172
  private
@@ -210,13 +223,31 @@ module Ewelink
210
223
 
211
224
  def dispose_web_socket
212
225
  @web_socket_authenticated_api_keys = Set.new
213
- if instance_variable_defined?(:@web_socket)
226
+
227
+ if @web_socket_ping_thread
228
+ if Thread.current == @web_socket_ping_thread
229
+ Thread.current[:stop] = true
230
+ else
231
+ @web_socket_ping_thread.kill
232
+ end
233
+ end
234
+
235
+ if @web_socket.present?
214
236
  begin
215
237
  @web_socket.close if @web_socket.open?
216
238
  rescue
217
239
  # Ignoring close errors
218
240
  end
219
- remove_instance_variable(:@web_socket) if instance_variable_defined?(:@web_socket)
241
+ end
242
+
243
+ [
244
+ :@last_web_socket_pong_at,
245
+ :@web_socket_ping_interval,
246
+ :@web_socket_ping_thread,
247
+ :@web_socket_url,
248
+ :@web_socket,
249
+ ].each do |variable|
250
+ remove_instance_variable(variable) if instance_variable_defined?(variable)
220
251
  end
221
252
  end
222
253
 
@@ -256,6 +287,10 @@ module Ewelink
256
287
  end
257
288
 
258
289
  def send_to_web_socket(data)
290
+ if web_socket_outdated_ping?
291
+ Ewelink.logger.warn(self.class.name) { 'WebSocket ping is outdated' }
292
+ dispose_web_socket
293
+ end
259
294
  web_socket.send(data)
260
295
  rescue
261
296
  dispose_web_socket
@@ -290,16 +325,42 @@ module Ewelink
290
325
 
291
326
  web_socket.on(:message) do |message|
292
327
  api.instance_eval do
293
- response = JSON.parse(message.data)
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
333
+
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
294
340
 
295
341
  if response.key?('error') && response['error'] != 0
296
342
  Ewelink.logger.error(self.class.name) { "WebSocket message error: #{message.data}" }
297
343
  next
298
344
  end
299
345
 
346
+ if !@web_socket_ping_thread && response.key?('config') && response['config'].key?('hbInterval')
347
+ @last_web_socket_pong_at = Time.now
348
+ # @web_socket_ping_interval = response['config']['hbInterval']
349
+ @web_socket_ping_interval = 30.seconds
350
+ Ewelink.logger.debug(self.class.name) { "Creating thread for WebSocket ping every #{@web_socket_ping_interval} seconds" }
351
+ @web_socket_ping_thread = Thread.new do
352
+ loop do
353
+ break if Thread.current[:stop]
354
+ sleep(@web_socket_ping_interval)
355
+ Ewelink.logger.debug(self.class.name) { 'Sending WebSocket ping' }
356
+ send_to_web_socket('ping')
357
+ end
358
+ end
359
+ end
360
+
300
361
  if response['apikey'].present? && !@web_socket_authenticated_api_keys.include?(response['apikey'])
301
362
  @web_socket_authenticated_api_keys << response['apikey']
302
- Ewelink.logger.debug(self.class.name) { "WebSocket successfully authenticated API key: #{response['apikey'].inspect}" }
363
+ Ewelink.logger.debug(self.class.name) { "WebSocket successfully authenticated API key: #{response['apikey'].truncate(16).inspect}" }
303
364
  end
304
365
 
305
366
  if response['deviceid'].present? && response['params'].is_a?(Hash) && response['params']['switch'].present?
@@ -324,7 +385,7 @@ module Ewelink
324
385
  'userAgent' => 'app',
325
386
  'version' => VERSION,
326
387
  }
327
- Ewelink.logger.debug(self.class.name) { "Authenticating WebSocket API key: #{api_key.inspect}" }
388
+ Ewelink.logger.debug(self.class.name) { "Authenticating WebSocket API key: #{api_key.truncate(16).inspect}" }
328
389
  send_to_web_socket(JSON.generate(params))
329
390
  end
330
391
  end
@@ -338,6 +399,10 @@ module Ewelink
338
399
  api_keys == @web_socket_authenticated_api_keys
339
400
  end
340
401
 
402
+ def web_socket_outdated_ping?
403
+ @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
404
+ end
405
+
341
406
  def web_socket_sequence
342
407
  (Time.now.to_f * 1000).round.to_s
343
408
  end
@@ -365,7 +430,10 @@ module Ewelink
365
430
  web_socket # Initializes WebSocket
366
431
  Timeout.timeout(TIMEOUT) do
367
432
  loop do
368
- return yield if condition.call
433
+ if condition.call
434
+ return yield if block_given?
435
+ return true
436
+ end
369
437
  sleep(WEB_SOCKET_WAIT_INTERVAL)
370
438
  end
371
439
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ewelink
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Toulotte