ewelink 3.3.2 → 5.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: 0fba630fc8ea0be922637f79d4618baccf676775fc37e35ffbd9847e479930f4
4
- data.tar.gz: 8e03090c2c1f9e469b25a9b3ff4dc21da74fd150c6432196038c464cca1608bb
3
+ metadata.gz: f2f26d6875823958f38a894377c7dfbce3531d7f98b1c1c69b18bd88caf44c1d
4
+ data.tar.gz: d25335eb91529c57d2780c42e8cef5856cb7bfa7f222e5282816bff122854b08
5
5
  SHA512:
6
- metadata.gz: 854c5679733f93e5a54c3085858315874db86319b0d81898a64f807d6780f146cfe1f0767ce5b6e4c9ed576adcab59b3154a35399a2cae77a2d4bcac338e8d58
7
- data.tar.gz: 328c7a4e3c10ca31d1b4d636c97c12e6a8af6358401aac77fea2739a0300cac162fa85ee1028dcf5097e42989a5d3035d39f8abf832030b3eabf0037dd43f244
6
+ metadata.gz: 42786625ddc6d8482a5d85922e25b4b650e080866148b378aeb579ad25e0dcee00d4f12b8ebf74492e207d80e6ed0d65b6f0c71ccdaf5c3f81280b1efbd4bc7b
7
+ data.tar.gz: 8eb9cc344312a30a7b0924ff02b052516bf5123a7d706acb15e000b91db2c91ecf56a2173d22543403f7fbbc31773e30d97d3c5379ad37ab42e34b55f8f67366
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.3.2
1
+ 5.0.0
data/bin/ewelink CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative '../lib/ewelink'
4
4
 
5
- Ewelink.logger = Logger.new(STDOUT, formatter: -> (severity, time, progname, message) {
5
+ Ewelink.logger = Logger.new($stdout, formatter: lambda { |_severity, _time, progname, message|
6
6
  text = ''
7
7
  text << "[#{progname}] " if progname.present?
8
8
  text << message.to_s << "\n"
data/ewelink.gemspec CHANGED
@@ -9,17 +9,19 @@ Gem::Specification.new do |s|
9
9
  s.description = 'Manage eWeLink smart home devices'
10
10
  s.license = 'MIT'
11
11
 
12
- s.files = `git ls-files | grep -vE '^(spec/|test/|\\.|Gemfile|Rakefile)'`.split("\n")
13
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ s.files = %x(git ls-files | grep -vE '^(spec/|test/|\\.|Gemfile|Rakefile)').split("\n")
13
+ s.executables = %x(git ls-files -- bin/*).split("\n").map { |f| File.basename(f) }
14
14
  s.require_paths = ['lib']
15
15
 
16
- s.required_ruby_version = '>= 2.0.0'
16
+ s.required_ruby_version = '>= 3.1.0'
17
17
 
18
- s.add_dependency 'activesupport', '>= 6.0.0', '< 7.0.0'
18
+ s.add_dependency 'activesupport', '>= 7.0.0', '< 8.0.0'
19
19
  s.add_dependency 'faye-websocket', '>= 0.11.0', '< 0.12.0'
20
- s.add_dependency 'httparty', '>= 0.18.0', '< 0.19.0'
20
+ s.add_dependency 'httparty', '>= 0.20.0', '< 0.21.0'
21
21
  s.add_dependency 'thread', '>= 0.2.0', '< 0.3.0'
22
22
 
23
23
  s.add_development_dependency 'byebug', '>= 11.0.0', '< 12.0.0'
24
24
  s.add_development_dependency 'rake', '>= 13.0.0', '< 14.0.0'
25
+ s.add_development_dependency 'rubocop', '>= 1.25.0', '< 2.0.0'
26
+ s.add_development_dependency 'rubocop-rake', '>= 0.6.0', '< 1.0.0'
25
27
  end
data/lib/ewelink/api.rb CHANGED
@@ -2,14 +2,14 @@ module Ewelink
2
2
 
3
3
  class Api
4
4
 
5
- APP_ID = 'oeVkj2lYFGnJu5XUtWisfW4utiN4u9Mq'
6
- APP_SECRET = '6Nz4n0xA8s8qdxQf2GqurZj2Fs55FUvM'
7
- DEFAULT_REGION = 'us'
5
+ APP_ID = 'Uw83EKZFxdif7XFXEsrpduz5YyjP7nTl'.freeze
6
+ APP_SECRET = 'mXLOjea0woSMvK9gw7Fjsy7YlFO4iSu6'.freeze
7
+ DEFAULT_COUNTRY_CODE = '+44'.freeze
8
+ DEFAULT_REGION = 'us'.freeze
8
9
  REQUEST_TIMEOUT = 10.seconds
9
10
  RF_BRIDGE_DEVICE_UIID = 28
10
- SWITCH_DEVICES_UIIDS = [1, 5, 6, 24]
11
- URL = 'https://#{region}-api.coolkit.cc:8080'
12
- UUID_NAMESPACE = 'e25750fb-3710-41af-b831-23224f4dd609';
11
+ SWITCH_DEVICES_UIIDS = [1, 5, 6, 24].freeze
12
+ URL = 'https://#{region}-apia.coolkit.cc'.freeze
13
13
  VERSION = 8
14
14
  WEB_SOCKET_CHECK_AUTHENTICATION_TIMEOUT = 30.seconds
15
15
  WEB_SOCKET_PING_TOLERANCE_FACTOR = 1.5
@@ -18,11 +18,11 @@ module Ewelink
18
18
 
19
19
  attr_reader :email, :password, :phone_number
20
20
 
21
- def initialize(async_actions: false, email: nil, password:, phone_number: nil, update_devices_status_on_connect: false)
21
+ def initialize(password:, async_actions: false, email: nil, phone_number: nil, update_devices_status_on_connect: false)
22
22
  @async_actions = async_actions.present?
23
23
  @email = email.presence.try(:strip)
24
24
  @mutexs = {}
25
- @password = password.presence || raise(Error.new(":password must be specified"))
25
+ @password = password.presence || raise(Error.new(':password must be specified'))
26
26
  @phone_number = phone_number.presence.try(:strip)
27
27
  @update_devices_status_on_connect = update_devices_status_on_connect.present?
28
28
  @web_socket_authenticated = false
@@ -41,23 +41,21 @@ module Ewelink
41
41
  process_action do
42
42
  synchronize(:press_rf_bridge_button) do
43
43
  button = find_rf_bridge_button!(uuid)
44
- web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
45
- params = {
46
- 'action' => 'update',
47
- 'apikey' => button[:api_key],
48
- 'deviceid' => button[:device_id],
49
- 'params' => {
50
- 'cmd' => 'transmit',
51
- 'rfChl' => button[:channel],
52
- },
53
- 'sequence' => web_socket_sequence,
54
- 'ts' => 0,
55
- 'userAgent' => 'app',
56
- }
57
- Ewelink.logger.debug(self.class.name) { "Pressing RF bridge button #{button[:uuid].inspect}" }
58
- send_to_web_socket(JSON.generate(params))
59
- true
60
- end
44
+ headers = authentication_headers.merge(
45
+ 'X-CK-Appid' => APP_ID,
46
+ 'X-CK-Nonce' => nonce,
47
+ )
48
+ params = {
49
+ 'type' => 1,
50
+ 'id' => button[:device_id],
51
+ 'params' => {
52
+ 'cmd' => 'transmit',
53
+ 'rfChl' => button[:channel],
54
+ },
55
+ }
56
+ body = JSON.generate(params)
57
+ response = rest_request(:post, '/v2/device/thing/status', headers:, body:)
58
+ response['error'] == 0
61
59
  end
62
60
  end
63
61
  end
@@ -85,19 +83,20 @@ module Ewelink
85
83
  end
86
84
  end
87
85
 
88
- [
89
- :@authentication_infos,
90
- :@devices,
91
- :@last_web_socket_pong_at,
92
- :@region,
93
- :@rf_bridge_buttons,
94
- :@switches,
95
- :@web_socket_ping_interval,
96
- :@web_socket_ping_thread,
97
- :@web_socket_thread,
98
- :@web_socket_url,
99
- :@web_socket,
100
- ].each do |variable|
86
+ %i(
87
+ @authentication_infos
88
+ @devices
89
+ @homes_ids
90
+ @last_web_socket_pong_at
91
+ @region
92
+ @rf_bridge_buttons
93
+ @switches
94
+ @web_socket_ping_interval
95
+ @web_socket_ping_thread
96
+ @web_socket_thread
97
+ @web_socket_url
98
+ @web_socket
99
+ ).each do |variable|
101
100
  remove_instance_variable(variable) if instance_variable_defined?(variable)
102
101
  end
103
102
  self
@@ -106,30 +105,30 @@ module Ewelink
106
105
  def rf_bridge_buttons
107
106
  synchronize(:rf_bridge_buttons) do
108
107
  @rf_bridge_buttons ||= [].tap do |buttons|
109
- rf_bridge_devices = devices.select { |device| device['uiid'] == RF_BRIDGE_DEVICE_UIID }.tap do |devices|
108
+ rf_bridge_devices = devices.select { |device| device['itemData']['extra']['uiid'] == RF_BRIDGE_DEVICE_UIID }.tap do |devices|
110
109
  Ewelink.logger.debug(self.class.name) { "Found #{devices.size} RF 433MHz bridge device(s)" }
111
110
  end
112
111
  rf_bridge_devices.each do |device|
113
- api_key = device['apikey'].presence || next
114
- device_id = device['deviceid'].presence || next
115
- device_name = device['name'].presence || next
116
- buttons = device['params']['rfList'].each do |rf|
112
+ api_key = device['itemData']['apikey'].presence || next
113
+ device_id = device['itemData']['deviceid'].presence || next
114
+ device_name = device['itemData']['name'].presence || next
115
+ buttons = device['itemData']['params']['rfList'].each do |rf|
117
116
  button = {
118
- api_key: api_key,
117
+ api_key:,
119
118
  channel: rf['rfChl'],
120
- device_id: device_id,
121
- device_name: device_name,
119
+ device_id:,
120
+ device_name:,
122
121
  }
123
- remote_info = device['tags']['zyx_info'].find { |info| info['buttonName'].find { |data| data.key?(button[:channel].to_s) } }.presence || next
122
+ remote_info = device['itemData']['tags']['zyx_info'].find { |info| info['buttonName'].find { |data| data.key?(button[:channel].to_s) } }.presence || next
124
123
  remote_name = remote_info['name'].try(:squish).presence || next
125
124
  button_info = remote_info['buttonName'].find { |info| info.key?(button[:channel].to_s) }.presence || next
126
125
  button_name = button_info.values.first.try(:squish).presence || next
127
126
  button.merge!({
128
127
  name: button_name,
129
- remote_name: remote_name,
128
+ remote_name:,
130
129
  remote_type: remote_info['remote_type'],
131
130
  })
132
- button[:uuid] = Digest::UUID.uuid_v5(UUID_NAMESPACE, "#{button[:device_id]}/#{button[:channel]}")
131
+ button[:uuid] = Digest::UUID.uuid_v5(Digest::UUID::DNS_NAMESPACE, "#{button[:device_id]}/#{button[:channel]}")
133
132
  buttons << button
134
133
  end
135
134
  end
@@ -161,17 +160,18 @@ module Ewelink
161
160
  def switches
162
161
  synchronize(:switches) do
163
162
  @switches ||= [].tap do |switches|
164
- switch_devices = devices.select { |device| SWITCH_DEVICES_UIIDS.include?(device['uiid']) }
163
+ switch_devices = devices.select { |device| SWITCH_DEVICES_UIIDS.include?(device['itemData']['extra']['uiid']) }
165
164
  switch_devices.each do |device|
166
- api_key = device['apikey'].presence || next
167
- device_id = device['deviceid'].presence || next
168
- name = device['name'].presence || next
165
+ api_key = device['itemData']['apikey'].presence || next
166
+ device_id = device['itemData']['deviceid'].presence || next
167
+ name = device['itemData']['name'].presence || next
169
168
  switch = {
170
- api_key: api_key,
171
- device_id: device_id,
172
- name: name,
169
+ api_key:,
170
+ device_id:,
171
+ model: device['itemData']['productModel'],
172
+ name:,
173
173
  }
174
- switch[:uuid] = Digest::UUID.uuid_v5(UUID_NAMESPACE, switch[:device_id])
174
+ switch[:uuid] = Digest::UUID.uuid_v5(Digest::UUID::DNS_NAMESPACE, switch[:device_id])
175
175
  switches << switch
176
176
  end
177
177
  end.tap { |switches| Ewelink.logger.debug(self.class.name) { "Found #{switches.size} switch(es)" } }
@@ -242,12 +242,8 @@ module Ewelink
242
242
  synchronize(:authentication_infos) do
243
243
  @authentication_infos ||= begin
244
244
  params = {
245
- 'appid' => APP_ID,
246
- 'imei' => SecureRandom.uuid.upcase,
247
- 'nonce' => nonce,
245
+ 'countryCode' => DEFAULT_COUNTRY_CODE,
248
246
  'password' => password,
249
- 'ts' => Time.now.to_i,
250
- 'version' => VERSION,
251
247
  }
252
248
  if email.present?
253
249
  params['email'] = email
@@ -255,12 +251,20 @@ module Ewelink
255
251
  params['phoneNumber'] = phone_number
256
252
  end
257
253
  body = JSON.generate(params)
258
- response = rest_request(:post, '/api/user/login', { body: body, headers: { 'Authorization' => "Sign #{Base64.encode64(OpenSSL::HMAC.digest('SHA256', APP_SECRET, body))}" } })
259
- raise(Error.new('Authentication token not found')) if response['at'].blank?
260
- raise(Error.new('API key not found')) if response['user'].blank? || response['user']['apikey'].blank?
254
+ response = rest_request(:post, '/v2/user/login', {
255
+ body:,
256
+ headers: {
257
+ 'Authorization' => "Sign #{Base64.encode64(OpenSSL::HMAC.digest('SHA256', APP_SECRET, body))}",
258
+ 'X-CK-Appid': APP_ID,
259
+ 'X-CK-Nonce': nonce,
260
+ },
261
+ })
262
+ raise(Error.new('Invalid authentication response')) unless response['data'].is_a?(Hash) && response['data']['user'].is_a?(Hash)
263
+ raise(Error.new('Authentication token not found')) unless response['data']['at'].present?
264
+ raise(Error.new('API key not found')) unless response['data']['user']['apikey'].present?
261
265
  {
262
- authentication_token: response['at'].tap { Ewelink.logger.debug(self.class.name) { 'Authentication token found' } },
263
- api_key: response['user']['apikey'].tap { Ewelink.logger.debug(self.class.name) { 'API key found' } },
266
+ api_key: response['data']['user']['apikey'].tap { Ewelink.logger.debug(self.class.name) { 'API key found' } },
267
+ authentication_token: response['data']['at'].tap { Ewelink.logger.debug(self.class.name) { 'Authentication token found' } },
264
268
  }
265
269
  end
266
270
  end
@@ -273,15 +277,22 @@ module Ewelink
273
277
  def devices
274
278
  synchronize(:devices) do
275
279
  @devices ||= begin
276
- params = {
277
- 'appid' => APP_ID,
278
- 'getTags' => 1,
279
- 'nonce' => nonce,
280
- 'ts' => Time.now.to_i,
281
- 'version' => VERSION,
282
- }
283
- response = rest_request(:get, '/api/user/device', headers: authentication_headers, query: params)
284
- response['devicelist'].tap { |devices| Ewelink.logger.debug(self.class.name) { "Found #{devices.size} device(s)" } }
280
+ devices = []
281
+ homes_ids.each do |home_id|
282
+ params = {
283
+ 'familyid' => home_id,
284
+ 'num' => 0,
285
+ }
286
+ headers = authentication_headers.merge(
287
+ 'X-CK-Appid' => APP_ID,
288
+ 'X-CK-Nonce' => nonce,
289
+ )
290
+ response = rest_request(:get, '/v2/device/thing', headers:, query: params)
291
+ raise('Invalid devices response') unless response['data'].is_a?(Hash) && response['data']['thingList'].is_a?(Array)
292
+ devices += response['data']['thingList']
293
+ end
294
+ Ewelink.logger.debug(self.class.name) { "Found #{devices.size} device(s)" }
295
+ devices
285
296
  end
286
297
  end
287
298
  end
@@ -294,6 +305,20 @@ module Ewelink
294
305
  switches.find { |switch| switch[:uuid] == uuid } || raise(Error.new("No such switch with UUID: #{uuid.inspect}"))
295
306
  end
296
307
 
308
+ def homes_ids
309
+ synchronize(:homes_ids) do
310
+ @homes_ids ||= begin
311
+ headers = authentication_headers.merge(
312
+ 'X-CK-Appid' => APP_ID,
313
+ 'X-CK-Nonce' => nonce,
314
+ )
315
+ response = rest_request(:get, '/v2/family', headers:)
316
+ raise('Invalid homes response') unless response['data'].is_a?(Hash) && response['data']['familyList'].is_a?(Array)
317
+ response['data']['familyList'].map { |home| home['id'] }.compact.uniq
318
+ end
319
+ end
320
+ end
321
+
297
322
  def nonce
298
323
  SecureRandom.hex[0, 8]
299
324
  end
@@ -311,17 +336,17 @@ module Ewelink
311
336
 
312
337
  def rest_request(method, path, options = {})
313
338
  url = "#{URL.gsub('#{region}', region)}#{path}"
339
+ url.gsub!(/#{Regexp.escape("#{region}-api")}/, "#{region}-disp") if options[:dispatch].present?
314
340
  method = method.to_s.upcase
315
341
  headers = (options[:headers] || {}).reverse_merge('Content-Type' => 'application/json')
316
342
  Ewelink.logger.debug(self.class.name) { "#{method} #{url}" }
317
- response = HTTParty.send(method.downcase, url, options.merge(headers: headers).reverse_merge(timeout: REQUEST_TIMEOUT))
343
+ response = HTTParty.send(method.downcase, url, options.merge(headers:).reverse_merge(timeout: REQUEST_TIMEOUT))
318
344
  raise(Error.new("#{method} #{url}: #{response.code}")) unless response.success?
319
- if response['error'] == 301 && response['region'].present?
320
- @region = response['region']
345
+ if response['error'] == 10_004 && response['data'].present? && response['data']['region'].present?
346
+ @region = response['data']['region']
321
347
  Ewelink.logger.debug(self.class.name) { "Switched to region #{region.inspect}" }
322
348
  return rest_request(method, path, options)
323
349
  end
324
- remove_instance_variable(:@authentication_infos) if instance_variable_defined?(:@authentication_infos) && [401, 403].include?(response['error'])
325
350
  raise(Error.new("#{method} #{url}: #{response['error']} #{response['msg']}".strip)) if response['error'].present? && response['error'] != 0
326
351
  response.to_h
327
352
  rescue Errno::ECONNREFUSED, OpenSSL::OpenSSLError, SocketError, Timeout::Error => e
@@ -391,12 +416,12 @@ module Ewelink
391
416
  EventMachine.run do
392
417
  @web_socket = Faye::WebSocket::Client.new(web_socket_url)
393
418
 
394
- @web_socket.on(:close) do |event|
419
+ @web_socket.on(:close) do
395
420
  Ewelink.logger.debug(self.class.name) { 'WebSocket closed' }
396
421
  reload
397
422
  end
398
423
 
399
- @web_socket.on(:open) do |event|
424
+ @web_socket.on(:open) do
400
425
  Ewelink.logger.debug(self.class.name) { 'WebSocket opened' }
401
426
  @last_web_socket_pong_at = Time.now
402
427
  authenticate_web_socket_api_key
@@ -413,7 +438,7 @@ module Ewelink
413
438
 
414
439
  begin
415
440
  json = JSON.parse(message)
416
- rescue => e
441
+ rescue
417
442
  Ewelink.logger.error(self.class.name) { 'WebSocket JSON parse error' }
418
443
  reload
419
444
  next
@@ -436,7 +461,7 @@ module Ewelink
436
461
  end
437
462
 
438
463
  if json['deviceid'].present? && json['params'].is_a?(Hash) && json['params']['switch'].present?
439
- switch = switches.find { |switch| switch[:device_id] == json['deviceid'] }
464
+ switch = switches.find { |item| item[:device_id] == json['deviceid'] }
440
465
  if switch.present?
441
466
  @web_socket_switches_statuses[switch[:uuid]] = json['params']['switch']
442
467
  Ewelink.logger.debug(self.class.name) { "Switch #{switch[:uuid].inspect} is #{@web_socket_switches_statuses[switch[:uuid]]}" }
@@ -468,13 +493,12 @@ module Ewelink
468
493
  synchronize(:web_socket_url) do
469
494
  @web_socket_url ||= begin
470
495
  params = {
471
- 'accept' => 'ws',
472
496
  'appid' => APP_ID,
473
497
  'nonce' => nonce,
474
498
  'ts' => Time.now.to_i,
475
499
  'version' => VERSION,
476
500
  }
477
- response = rest_request(:post, '/dispatch/app', body: JSON.generate(params), headers: authentication_headers)
501
+ response = rest_request(:post, '/dispatch/app', body: JSON.generate(params), dispatch: true, headers: authentication_headers)
478
502
  raise('Error while getting WebSocket URL') unless response['error'] == 0
479
503
  domain = response['domain'].presence || raise("Can't get WebSocket server domain")
480
504
  port = response['port'].presence || raise("Can't get WebSocket server port")
@@ -483,13 +507,11 @@ module Ewelink
483
507
  end
484
508
  end
485
509
 
486
- def web_socket_wait_for(condition, initialize_web_socket: false, &block)
510
+ def web_socket_wait_for(condition, initialize_web_socket: false)
487
511
  web_socket if initialize_web_socket
488
512
  begin
489
513
  Timeout.timeout(REQUEST_TIMEOUT) do
490
- while !condition.call
491
- sleep(WEB_SOCKET_WAIT_INTERVAL)
492
- end
514
+ sleep(WEB_SOCKET_WAIT_INTERVAL) until condition.call
493
515
  block_given? ? yield : true
494
516
  end
495
517
  rescue => e
@@ -9,7 +9,7 @@ module Ewelink
9
9
  options[:turn_switches_on_uuids].each { |uuid| api.turn_switch!(uuid, :on) }
10
10
  options[:turn_switches_off_uuids].each { |uuid| api.turn_switch!(uuid, :off) }
11
11
  options[:press_rf_bridge_buttons_uuids].each { |uuid| api.press_rf_bridge_button!(uuid) }
12
- puts(JSON.pretty_generate(options[:switch_status_uuids].map { |uuid| [uuid, api.switch_on?(uuid) ? 'on' : 'off'] }.to_h)) if options[:switch_status_uuids].present?
12
+ puts(JSON.pretty_generate(options[:switch_status_uuids].to_h { |uuid| [uuid, api.switch_on?(uuid) ? 'on' : 'off'] })) if options[:switch_status_uuids].present?
13
13
  end
14
14
 
15
15
  private
@@ -56,23 +56,21 @@ module Ewelink
56
56
  end
57
57
  arguments = parser.parse!
58
58
  if arguments.any?
59
- STDERR.puts("Invalid option specified: #{arguments.first}")
60
- STDERR.puts(parser.summarize)
59
+ warn("Invalid option specified: #{arguments.first}")
60
+ warn(parser.summarize)
61
61
  exit(1)
62
62
  end
63
63
  if options[:email].blank? && options[:phone_number].blank?
64
- STDERR.puts('Email or phone number must be specified')
65
- STDERR.puts(parser.summarize)
64
+ warn('Email or phone number must be specified')
65
+ warn(parser.summarize)
66
66
  exit(1)
67
67
  end
68
- if [:list_switches, :list_rf_bridge_buttons, :turn_switches_on_uuids, :turn_switches_off_uuids, :press_rf_bridge_buttons_uuids, :switch_status_uuids].map { |action| options[action] }.all?(&:blank?)
69
- STDERR.puts('An action must be specified (listing switches, press RF bridge button, etc.)')
70
- STDERR.puts(parser.summarize)
68
+ if %i(list_switches list_rf_bridge_buttons turn_switches_on_uuids turn_switches_off_uuids press_rf_bridge_buttons_uuids switch_status_uuids).map { |action| options[action] }.all?(&:blank?)
69
+ warn('An action must be specified (listing switches, press RF bridge button, etc.)')
70
+ warn(parser.summarize)
71
71
  exit(1)
72
72
  end
73
- while options[:password].blank?
74
- options[:password] = IO::console.getpass("Enter eWeLink account's password: ")
75
- end
73
+ options[:password] = IO.console.getpass("Enter eWeLink account's password: ") while options[:password].blank?
76
74
  options
77
75
  end
78
76
  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.3.2
4
+ version: 5.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: 2021-09-26 00:00:00.000000000 Z
11
+ date: 2022-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 6.0.0
19
+ version: 7.0.0
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: 7.0.0
22
+ version: 8.0.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 6.0.0
29
+ version: 7.0.0
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: 7.0.0
32
+ version: 8.0.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: faye-websocket
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -56,20 +56,20 @@ dependencies:
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: 0.18.0
59
+ version: 0.20.0
60
60
  - - "<"
61
61
  - !ruby/object:Gem::Version
62
- version: 0.19.0
62
+ version: 0.21.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.18.0
69
+ version: 0.20.0
70
70
  - - "<"
71
71
  - !ruby/object:Gem::Version
72
- version: 0.19.0
72
+ version: 0.21.0
73
73
  - !ruby/object:Gem::Dependency
74
74
  name: thread
75
75
  requirement: !ruby/object:Gem::Requirement
@@ -130,6 +130,46 @@ dependencies:
130
130
  - - "<"
131
131
  - !ruby/object:Gem::Version
132
132
  version: 14.0.0
133
+ - !ruby/object:Gem::Dependency
134
+ name: rubocop
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 1.25.0
140
+ - - "<"
141
+ - !ruby/object:Gem::Version
142
+ version: 2.0.0
143
+ type: :development
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: 1.25.0
150
+ - - "<"
151
+ - !ruby/object:Gem::Version
152
+ version: 2.0.0
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop-rake
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: 0.6.0
160
+ - - "<"
161
+ - !ruby/object:Gem::Version
162
+ version: 1.0.0
163
+ type: :development
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: 0.6.0
170
+ - - "<"
171
+ - !ruby/object:Gem::Version
172
+ version: 1.0.0
133
173
  description: Manage eWeLink smart home devices
134
174
  email: al@alweb.org
135
175
  executables:
@@ -157,14 +197,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
157
197
  requirements:
158
198
  - - ">="
159
199
  - !ruby/object:Gem::Version
160
- version: 2.0.0
200
+ version: 3.1.0
161
201
  required_rubygems_version: !ruby/object:Gem::Requirement
162
202
  requirements:
163
203
  - - ">="
164
204
  - !ruby/object:Gem::Version
165
205
  version: '0'
166
206
  requirements: []
167
- rubygems_version: 3.2.15
207
+ rubygems_version: 3.3.3
168
208
  signing_key:
169
209
  specification_version: 4
170
210
  summary: Manage eWeLink devices