ewelink 3.0.0 → 3.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.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/ewelink.gemspec +1 -0
- data/lib/ewelink.rb +4 -0
- data/lib/ewelink/api.rb +299 -41
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45f904d5e72d0038360340adc26dd7eb963f664804b43eee8235ee02d48f60ff
|
4
|
+
data.tar.gz: 98ee94c2d9187ea17250c3f32c1932f8c0df8a2123c341a47159c32e2aec66e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42977ef17b87c9c6710973968ca739d642108d6595861a870090ef13fbe5332b7ff99fba0dc8444e8022dfba065f4334426daf2dc423ed08314b3939cc41e69a
|
7
|
+
data.tar.gz: 4fa2068348c252aecfa1f0d55f91013a17658a3403fa7813208f34f027757b2459181323aae9a9d8c71c915ac8c83e881150c4eacee77ed85832b1adaeadb451
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.1.0
|
data/ewelink.gemspec
CHANGED
@@ -16,6 +16,7 @@ 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
21
|
|
21
22
|
s.add_development_dependency 'byebug', '>= 11.0.0', '< 12.0.0'
|
data/lib/ewelink.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
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'
|
7
9
|
require 'logger'
|
8
10
|
require 'openssl'
|
9
11
|
require 'optparse'
|
12
|
+
require 'set'
|
13
|
+
require 'timeout'
|
10
14
|
|
11
15
|
module Ewelink
|
12
16
|
|
data/lib/ewelink/api.rb
CHANGED
@@ -5,12 +5,16 @@ module Ewelink
|
|
5
5
|
APP_ID = 'oeVkj2lYFGnJu5XUtWisfW4utiN4u9Mq'
|
6
6
|
APP_SECRET = '6Nz4n0xA8s8qdxQf2GqurZj2Fs55FUvM'
|
7
7
|
DEFAULT_REGION = 'us'
|
8
|
+
REQUEST_TIMEOUT = 10.seconds
|
8
9
|
RF_BRIDGE_DEVICE_UIID = 28
|
9
10
|
SWITCH_DEVICES_UIIDS = [1, 5, 6, 24]
|
10
|
-
TIMEOUT = 10
|
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_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
|
14
18
|
|
15
19
|
attr_reader :email, :password, :phone_number
|
16
20
|
|
@@ -19,31 +23,73 @@ module Ewelink
|
|
19
23
|
@mutexs = {}
|
20
24
|
@password = password.presence || raise(Error.new(":password must be specified"))
|
21
25
|
@phone_number = phone_number.presence.try(:strip)
|
22
|
-
|
26
|
+
@web_socket_authenticated = false
|
27
|
+
@web_socket_switches_statuses = {}
|
28
|
+
|
29
|
+
raise(Error.new(':email or :phone_number must be specified')) if email.blank? && phone_number.blank?
|
30
|
+
|
31
|
+
start_web_socket_authentication_check_thread
|
23
32
|
end
|
24
33
|
|
25
34
|
def press_rf_bridge_button!(uuid)
|
26
35
|
synchronize(:press_rf_bridge_button) do
|
27
36
|
button = find_rf_bridge_button!(uuid)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
'
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
|
38
|
+
params = {
|
39
|
+
'action' => 'update',
|
40
|
+
'apikey' => button[:api_key],
|
41
|
+
'deviceid' => button[:device_id],
|
42
|
+
'params' => {
|
43
|
+
'cmd' => 'transmit',
|
44
|
+
'rfChl' => button[:channel],
|
45
|
+
},
|
46
|
+
'sequence' => web_socket_sequence,
|
47
|
+
'ts' => 0,
|
48
|
+
'userAgent' => 'app',
|
49
|
+
}
|
50
|
+
Ewelink.logger.debug(self.class.name) { "Pressing RF bridge button #{button[:uuid].inspect}" }
|
51
|
+
send_to_web_socket(JSON.generate(params))
|
52
|
+
true
|
53
|
+
end
|
41
54
|
end
|
42
55
|
end
|
43
56
|
|
44
57
|
def reload
|
45
|
-
Ewelink.logger.debug(self.class.name) { 'Reloading API (authentication token, devices
|
46
|
-
|
58
|
+
Ewelink.logger.debug(self.class.name) { 'Reloading API (authentication token, api key, devices, region, connections,...)' }
|
59
|
+
|
60
|
+
@web_socket_authenticated = false
|
61
|
+
@web_socket_switches_statuses.clear
|
62
|
+
|
63
|
+
[@web_socket_ping_thread, @web_socket_thread].each do |thread|
|
64
|
+
next unless thread
|
65
|
+
if Thread.current == thread
|
66
|
+
thread[:stop] = true
|
67
|
+
else
|
68
|
+
thread.kill
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if @web_socket.present?
|
73
|
+
begin
|
74
|
+
@web_socket.close if @web_socket.open?
|
75
|
+
rescue
|
76
|
+
# Ignoring close errors
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
[
|
81
|
+
:@authentication_infos,
|
82
|
+
:@devices,
|
83
|
+
:@last_web_socket_pong_at,
|
84
|
+
:@region,
|
85
|
+
:@rf_bridge_buttons,
|
86
|
+
:@switches,
|
87
|
+
:@web_socket_ping_interval,
|
88
|
+
:@web_socket_ping_thread,
|
89
|
+
:@web_socket_thread,
|
90
|
+
:@web_socket_url,
|
91
|
+
:@web_socket,
|
92
|
+
].each do |variable|
|
47
93
|
remove_instance_variable(variable) if instance_variable_defined?(variable)
|
48
94
|
end
|
49
95
|
self
|
@@ -56,10 +102,12 @@ module Ewelink
|
|
56
102
|
Ewelink.logger.debug(self.class.name) { "Found #{devices.size} RF 433MHz bridge device(s)" }
|
57
103
|
end
|
58
104
|
rf_bridge_devices.each do |device|
|
105
|
+
api_key = device['apikey'].presence || next
|
59
106
|
device_id = device['deviceid'].presence || next
|
60
107
|
device_name = device['name'].presence || next
|
61
108
|
buttons = device['params']['rfList'].each do |rf|
|
62
109
|
button = {
|
110
|
+
api_key: api_key,
|
63
111
|
channel: rf['rfChl'],
|
64
112
|
device_id: device_id,
|
65
113
|
device_name: device_name,
|
@@ -83,15 +131,23 @@ module Ewelink
|
|
83
131
|
|
84
132
|
def switch_on?(uuid)
|
85
133
|
switch = find_switch!(uuid)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
134
|
+
if @web_socket_switches_statuses[switch[:uuid]].nil?
|
135
|
+
web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
|
136
|
+
Ewelink.logger.debug(self.class.name) { "Checking switch #{switch[:uuid].inspect} status" }
|
137
|
+
params = {
|
138
|
+
'action' => 'query',
|
139
|
+
'apikey' => switch[:api_key],
|
140
|
+
'deviceid' => switch[:device_id],
|
141
|
+
'sequence' => web_socket_sequence,
|
142
|
+
'ts' => 0,
|
143
|
+
'userAgent' => 'app',
|
144
|
+
}
|
145
|
+
send_to_web_socket(JSON.generate(params))
|
146
|
+
end
|
147
|
+
end
|
148
|
+
web_socket_wait_for(-> { !@web_socket_switches_statuses[switch[:uuid]].nil? }, initialize_web_socket: true) do
|
149
|
+
@web_socket_switches_statuses[switch[:uuid]] == 'on'
|
150
|
+
end
|
95
151
|
end
|
96
152
|
|
97
153
|
def switches
|
@@ -99,9 +155,11 @@ module Ewelink
|
|
99
155
|
@switches ||= [].tap do |switches|
|
100
156
|
switch_devices = devices.select { |device| SWITCH_DEVICES_UIIDS.include?(device['uiid']) }
|
101
157
|
switch_devices.each do |device|
|
158
|
+
api_key = device['apikey'].presence || next
|
102
159
|
device_id = device['deviceid'].presence || next
|
103
160
|
name = device['name'].presence || next
|
104
161
|
switch = {
|
162
|
+
api_key: api_key,
|
105
163
|
device_id: device_id,
|
106
164
|
name: name,
|
107
165
|
}
|
@@ -119,29 +177,56 @@ module Ewelink
|
|
119
177
|
on = false
|
120
178
|
end
|
121
179
|
switch = find_switch!(uuid)
|
180
|
+
@web_socket_switches_statuses[switch[:uuid]] = nil
|
181
|
+
web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
|
182
|
+
params = {
|
183
|
+
'action' => 'update',
|
184
|
+
'apikey' => switch[:api_key],
|
185
|
+
'deviceid' => switch[:device_id],
|
186
|
+
'params' => {
|
187
|
+
'switch' => on ? 'on' : 'off',
|
188
|
+
},
|
189
|
+
'sequence' => web_socket_sequence,
|
190
|
+
'ts' => 0,
|
191
|
+
'userAgent' => 'app',
|
192
|
+
}
|
193
|
+
Ewelink.logger.debug(self.class.name) { "Turning switch #{switch[:uuid].inspect} #{on ? 'on' : 'off'}" }
|
194
|
+
send_to_web_socket(JSON.generate(params))
|
195
|
+
end
|
196
|
+
sleep(SWITCH_STATUS_CHANGE_CHECK_TIMEOUT)
|
197
|
+
switch_on?(switch[:uuid]) # Waiting for switch status update
|
198
|
+
true
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def api_key
|
204
|
+
authentication_infos[:api_key]
|
205
|
+
end
|
206
|
+
|
207
|
+
def authenticate_web_socket_api_key
|
122
208
|
params = {
|
209
|
+
'action' => 'userOnline',
|
210
|
+
'apikey' => api_key,
|
123
211
|
'appid' => APP_ID,
|
124
|
-
'
|
212
|
+
'at' => authentication_token,
|
125
213
|
'nonce' => nonce,
|
126
|
-
'
|
127
|
-
'switch' => on ? 'on' : 'off',
|
128
|
-
},
|
214
|
+
'sequence' => web_socket_sequence,
|
129
215
|
'ts' => Time.now.to_i,
|
216
|
+
'userAgent' => 'app',
|
130
217
|
'version' => VERSION,
|
131
218
|
}
|
132
|
-
|
133
|
-
|
219
|
+
Ewelink.logger.debug(self.class.name) { "Authenticating WebSocket API key: #{api_key.truncate(16).inspect}" }
|
220
|
+
send_to_web_socket(JSON.generate(params))
|
134
221
|
end
|
135
222
|
|
136
|
-
private
|
137
|
-
|
138
223
|
def authentication_headers
|
139
224
|
{ 'Authorization' => "Bearer #{authentication_token}" }
|
140
225
|
end
|
141
226
|
|
142
|
-
def
|
143
|
-
synchronize(:
|
144
|
-
@
|
227
|
+
def authentication_infos
|
228
|
+
synchronize(:authentication_infos) do
|
229
|
+
@authentication_infos ||= begin
|
145
230
|
params = {
|
146
231
|
'appid' => APP_ID,
|
147
232
|
'imei' => SecureRandom.uuid.upcase,
|
@@ -158,11 +243,19 @@ module Ewelink
|
|
158
243
|
body = JSON.generate(params)
|
159
244
|
response = rest_request(:post, '/api/user/login', { body: body, headers: { 'Authorization' => "Sign #{Base64.encode64(OpenSSL::HMAC.digest('SHA256', APP_SECRET, body))}" } })
|
160
245
|
raise(Error.new('Authentication token not found')) if response['at'].blank?
|
161
|
-
|
246
|
+
raise(Error.new('API key not found')) if response['user'].blank? || response['user']['apikey'].blank?
|
247
|
+
{
|
248
|
+
authentication_token: response['at'].tap { Ewelink.logger.debug(self.class.name) { 'Authentication token found' } },
|
249
|
+
api_key: response['user']['apikey'].tap { Ewelink.logger.debug(self.class.name) { 'API key found' } },
|
250
|
+
}
|
162
251
|
end
|
163
252
|
end
|
164
253
|
end
|
165
254
|
|
255
|
+
def authentication_token
|
256
|
+
authentication_infos[:authentication_token]
|
257
|
+
end
|
258
|
+
|
166
259
|
def devices
|
167
260
|
synchronize(:devices) do
|
168
261
|
@devices ||= begin
|
@@ -200,24 +293,189 @@ module Ewelink
|
|
200
293
|
method = method.to_s.upcase
|
201
294
|
headers = (options[:headers] || {}).reverse_merge('Content-Type' => 'application/json')
|
202
295
|
Ewelink.logger.debug(self.class.name) { "#{method} #{url}" }
|
203
|
-
response = HTTParty.send(method.downcase, url, options.merge(headers: headers).reverse_merge(timeout:
|
296
|
+
response = HTTParty.send(method.downcase, url, options.merge(headers: headers).reverse_merge(timeout: REQUEST_TIMEOUT))
|
204
297
|
raise(Error.new("#{method} #{url}: #{response.code}")) unless response.success?
|
205
298
|
if response['error'] == 301 && response['region'].present?
|
206
299
|
@region = response['region']
|
207
300
|
Ewelink.logger.debug(self.class.name) { "Switched to region #{region.inspect}" }
|
208
301
|
return rest_request(method, path, options)
|
209
302
|
end
|
210
|
-
remove_instance_variable(:@
|
303
|
+
remove_instance_variable(:@authentication_infos) if instance_variable_defined?(:@authentication_infos) && [401, 403].include?(response['error'])
|
211
304
|
raise(Error.new("#{method} #{url}: #{response['error']} #{response['msg']}".strip)) if response['error'].present? && response['error'] != 0
|
212
|
-
response
|
305
|
+
response.to_h
|
213
306
|
rescue Errno::ECONNREFUSED, OpenSSL::OpenSSLError, SocketError, Timeout::Error => e
|
214
307
|
raise Error.new(e)
|
215
308
|
end
|
216
309
|
|
310
|
+
def send_to_web_socket(message)
|
311
|
+
web_socket.send(message)
|
312
|
+
rescue => e
|
313
|
+
reload
|
314
|
+
raise Error.new(e)
|
315
|
+
end
|
316
|
+
|
317
|
+
def start_web_socket_authentication_check_thread
|
318
|
+
raise Error.new('WebSocket authentication check must only be started once') if @web_socket_authentication_check_thread.present?
|
319
|
+
|
320
|
+
@web_socket_authentication_check_thread = Thread.new do
|
321
|
+
loop do
|
322
|
+
Ewelink.logger.debug(self.class.name) { 'Checking if WebSocket is authenticated' }
|
323
|
+
begin
|
324
|
+
web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
|
325
|
+
Ewelink.logger.debug(self.class.name) { 'WebSocket is authenticated' }
|
326
|
+
end
|
327
|
+
rescue => e
|
328
|
+
Ewelink.logger.error(self.class.name) { e }
|
329
|
+
end
|
330
|
+
sleep(WEB_SOCKET_CHECK_AUTHENTICATION_TIMEOUT)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def start_web_socket_ping_thread(interval)
|
336
|
+
@last_web_socket_pong_at = Time.now
|
337
|
+
@web_socket_ping_interval = interval
|
338
|
+
Ewelink.logger.debug(self.class.name) { "Creating thread for WebSocket ping every #{@web_socket_ping_interval} seconds" }
|
339
|
+
@web_socket_ping_thread = Thread.new do
|
340
|
+
loop do
|
341
|
+
break if Thread.current[:stop]
|
342
|
+
sleep(@web_socket_ping_interval)
|
343
|
+
Ewelink.logger.debug(self.class.name) { 'Sending WebSocket ping' }
|
344
|
+
send_to_web_socket('ping')
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
217
349
|
def synchronize(name, &block)
|
218
350
|
(@mutexs[name] ||= Mutex.new).synchronize(&block)
|
219
351
|
end
|
220
352
|
|
353
|
+
def web_socket
|
354
|
+
if web_socket_outdated_ping?
|
355
|
+
Ewelink.logger.warn(self.class.name) { 'WebSocket ping is outdated' }
|
356
|
+
reload
|
357
|
+
end
|
358
|
+
|
359
|
+
synchronize(:web_socket) do
|
360
|
+
next @web_socket if @web_socket
|
361
|
+
|
362
|
+
# Initializes caches before opening WebSocket: important in order to
|
363
|
+
# NOT cumulate requests Timeouts from #web_socket_wait_for.
|
364
|
+
api_key
|
365
|
+
web_socket_url
|
366
|
+
|
367
|
+
Ewelink.logger.debug(self.class.name) { "Opening WebSocket to #{web_socket_url.inspect}" }
|
368
|
+
|
369
|
+
@web_socket_thread = Thread.new do
|
370
|
+
EventMachine.run do
|
371
|
+
@web_socket = Faye::WebSocket::Client.new(web_socket_url)
|
372
|
+
|
373
|
+
@web_socket.on(:close) do |event|
|
374
|
+
Ewelink.logger.debug(self.class.name) { 'WebSocket closed' }
|
375
|
+
reload
|
376
|
+
end
|
377
|
+
|
378
|
+
@web_socket.on(:open) do |event|
|
379
|
+
Ewelink.logger.debug(self.class.name) { 'WebSocket opened' }
|
380
|
+
@last_web_socket_pong_at = Time.now
|
381
|
+
authenticate_web_socket_api_key
|
382
|
+
end
|
383
|
+
|
384
|
+
@web_socket.on(:message) do |event|
|
385
|
+
message = event.data
|
386
|
+
|
387
|
+
if message == 'pong'
|
388
|
+
Ewelink.logger.debug(self.class.name) { "Received WebSocket #{message.inspect} message" }
|
389
|
+
@last_web_socket_pong_at = Time.now
|
390
|
+
next
|
391
|
+
end
|
392
|
+
|
393
|
+
begin
|
394
|
+
json = JSON.parse(message)
|
395
|
+
rescue => e
|
396
|
+
Ewelink.logger.error(self.class.name) { 'WebSocket JSON parse error' }
|
397
|
+
reload
|
398
|
+
next
|
399
|
+
end
|
400
|
+
|
401
|
+
if json.key?('error') && json['error'] != 0
|
402
|
+
Ewelink.logger.error(self.class.name) { "WebSocket message error: #{message.inspect}" }
|
403
|
+
reload
|
404
|
+
next
|
405
|
+
end
|
406
|
+
|
407
|
+
if !@web_socket_ping_thread && json.key?('config') && json['config']['hb'] == 1 && json['config']['hbInterval'].present?
|
408
|
+
start_web_socket_ping_thread(json['config']['hbInterval'] + 7)
|
409
|
+
end
|
410
|
+
|
411
|
+
if json['apikey'].present? && !@web_socket_authenticated && json['apikey'] == api_key
|
412
|
+
@web_socket_authenticated = true
|
413
|
+
Ewelink.logger.debug(self.class.name) { "WebSocket successfully authenticated API key: #{json['apikey'].truncate(16).inspect}" }
|
414
|
+
end
|
415
|
+
|
416
|
+
if json['deviceid'].present? && json['params'].is_a?(Hash) && json['params']['switch'].present?
|
417
|
+
switch = switches.find { |switch| switch[:device_id] == json['deviceid'] }
|
418
|
+
if switch.present?
|
419
|
+
@web_socket_switches_statuses[switch[:uuid]] = json['params']['switch']
|
420
|
+
Ewelink.logger.debug(self.class.name) { "Switch #{switch[:uuid].inspect} is #{@web_socket_switches_statuses[switch[:uuid]]}" }
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
web_socket_wait_for(-> { @web_socket.present? }) do
|
428
|
+
@web_socket
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def web_socket_authenticated?
|
434
|
+
@web_socket_authenticated.present?
|
435
|
+
end
|
436
|
+
|
437
|
+
def web_socket_outdated_ping?
|
438
|
+
@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
|
439
|
+
end
|
440
|
+
|
441
|
+
def web_socket_sequence
|
442
|
+
(Time.now.to_f * 1000).round.to_s
|
443
|
+
end
|
444
|
+
|
445
|
+
def web_socket_url
|
446
|
+
synchronize(:web_socket_url) do
|
447
|
+
@web_socket_url ||= begin
|
448
|
+
params = {
|
449
|
+
'accept' => 'ws',
|
450
|
+
'appid' => APP_ID,
|
451
|
+
'nonce' => nonce,
|
452
|
+
'ts' => Time.now.to_i,
|
453
|
+
'version' => VERSION,
|
454
|
+
}
|
455
|
+
response = rest_request(:post, '/dispatch/app', body: JSON.generate(params), headers: authentication_headers)
|
456
|
+
raise('Error while getting WebSocket URL') unless response['error'] == 0
|
457
|
+
domain = response['domain'].presence || raise("Can't get WebSocket server domain")
|
458
|
+
port = response['port'].presence || raise("Can't get WebSocket server port")
|
459
|
+
"wss://#{domain}:#{port}/api/ws".tap { |url| Ewelink.logger.debug(self.class.name) { "WebSocket URL is: #{url.inspect}" } }
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def web_socket_wait_for(condition, initialize_web_socket: false, &block)
|
465
|
+
web_socket if initialize_web_socket
|
466
|
+
begin
|
467
|
+
Timeout.timeout(REQUEST_TIMEOUT) do
|
468
|
+
while !condition.call
|
469
|
+
sleep(WEB_SOCKET_WAIT_INTERVAL)
|
470
|
+
end
|
471
|
+
block_given? ? yield : true
|
472
|
+
end
|
473
|
+
rescue => e
|
474
|
+
reload
|
475
|
+
raise Error.new(e)
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
221
479
|
end
|
222
480
|
|
223
481
|
end
|
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: 3.
|
4
|
+
version: 3.1.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-
|
11
|
+
date: 2020-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -30,6 +30,26 @@ dependencies:
|
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 7.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
|
33
53
|
- !ruby/object:Gem::Dependency
|
34
54
|
name: httparty
|
35
55
|
requirement: !ruby/object:Gem::Requirement
|