ewelink 3.2.0 → 4.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: c9b83156bba48622088845d22bd8fc3b559d6e8732acf4664ba35886c6c19d35
4
- data.tar.gz: d69303e93b04b56fd713fc27220d15c4ab66ca767190a289d6440932dc468532
3
+ metadata.gz: b8e686b44abcc1c59621d612a64b930f89a0041ac4607070bd47c801f04148ac
4
+ data.tar.gz: 3990a53b5d57e0c0523ba83050e7af904dd54a96337692a5fc800e6e3d0edaa9
5
5
  SHA512:
6
- metadata.gz: fa3fd8b8a36ad7e4570db2941ebdffc1d134106ea40ce93866eaebe7b81a4cc0a35920f5366a48c0a9141476101cc03be42adf244b15bcf98eeec64b0e77a312
7
- data.tar.gz: d98f59eaec9c582dd0627a52a392c45e98e351676b69d36f081df4ec819153bd32e340ced77440152c674d58013f413d8e42a5e86a50f65d57efce5782f26943
6
+ metadata.gz: 41aaa80ba63866bf04728c60489ed91b3b8294a61dc7e7d78779628b7530006bd41d306adb4e6a72ccdb1e72ed8322dd94efe897053ed02ce1da977e7560319a
7
+ data.tar.gz: c6af766c67c28efffd27a4186c3b603e9c4d3ab48e5fb8bc8ca1b52b5ce7aec8969297b932134c3c640ca5ece581ab70c6932ffb8a5597ebb54cc831b9bbfb98
data/README.mdown CHANGED
@@ -77,6 +77,9 @@ api.press_rf_bridge_button!(button[:uuid])
77
77
 
78
78
  ### Additional options
79
79
 
80
+ - `async_actions` (`true` | `false`): To perform actions (pressing an RF
81
+ bridge button or turning a switch on/off) in asynchronous mode. (default:
82
+ `false`).
80
83
  - `update_devices_status_on_connect` (`true` | `false`): To update devices
81
84
  status (on, off) when connecting to Ewelink API (default: `false`).
82
85
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0
1
+ 4.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,16 +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
+ s.add_dependency 'thread', '>= 0.2.0', '< 0.3.0'
21
22
 
22
23
  s.add_development_dependency 'byebug', '>= 11.0.0', '< 12.0.0'
23
- s.add_development_dependency 'rake', '>= 12.0.0', '< 13.0.0'
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'
24
27
  end
data/lib/ewelink/api.rb CHANGED
@@ -2,14 +2,13 @@ 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 = 'oeVkj2lYFGnJu5XUtWisfW4utiN4u9Mq'.freeze
6
+ APP_SECRET = '6Nz4n0xA8s8qdxQf2GqurZj2Fs55FUvM'.freeze
7
+ DEFAULT_REGION = 'us'.freeze
8
8
  REQUEST_TIMEOUT = 10.seconds
9
9
  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';
10
+ SWITCH_DEVICES_UIIDS = [1, 5, 6, 24].freeze
11
+ URL = 'https://#{region}-api.coolkit.cc:8080'.freeze
13
12
  VERSION = 8
14
13
  WEB_SOCKET_CHECK_AUTHENTICATION_TIMEOUT = 30.seconds
15
14
  WEB_SOCKET_PING_TOLERANCE_FACTOR = 1.5
@@ -18,10 +17,11 @@ module Ewelink
18
17
 
19
18
  attr_reader :email, :password, :phone_number
20
19
 
21
- def initialize(email: nil, password:, phone_number: nil, update_devices_status_on_connect: false)
20
+ def initialize(password:, async_actions: false, email: nil, phone_number: nil, update_devices_status_on_connect: false)
21
+ @async_actions = async_actions.present?
22
22
  @email = email.presence.try(:strip)
23
23
  @mutexs = {}
24
- @password = password.presence || raise(Error.new(":password must be specified"))
24
+ @password = password.presence || raise(Error.new(':password must be specified'))
25
25
  @phone_number = phone_number.presence.try(:strip)
26
26
  @update_devices_status_on_connect = update_devices_status_on_connect.present?
27
27
  @web_socket_authenticated = false
@@ -32,25 +32,31 @@ module Ewelink
32
32
  start_web_socket_authentication_check_thread
33
33
  end
34
34
 
35
+ def async_actions?
36
+ @async_actions
37
+ end
38
+
35
39
  def press_rf_bridge_button!(uuid)
36
- synchronize(:press_rf_bridge_button) do
37
- button = find_rf_bridge_button!(uuid)
38
- web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
39
- params = {
40
- 'action' => 'update',
41
- 'apikey' => button[:api_key],
42
- 'deviceid' => button[:device_id],
43
- 'params' => {
44
- 'cmd' => 'transmit',
45
- 'rfChl' => button[:channel],
46
- },
47
- 'sequence' => web_socket_sequence,
48
- 'ts' => 0,
49
- 'userAgent' => 'app',
50
- }
51
- Ewelink.logger.debug(self.class.name) { "Pressing RF bridge button #{button[:uuid].inspect}" }
52
- send_to_web_socket(JSON.generate(params))
53
- true
40
+ process_action do
41
+ synchronize(:press_rf_bridge_button) do
42
+ button = find_rf_bridge_button!(uuid)
43
+ web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
44
+ params = {
45
+ 'action' => 'update',
46
+ 'apikey' => button[:api_key],
47
+ 'deviceid' => button[:device_id],
48
+ 'params' => {
49
+ 'cmd' => 'transmit',
50
+ 'rfChl' => button[:channel],
51
+ },
52
+ 'sequence' => web_socket_sequence,
53
+ 'ts' => 0,
54
+ 'userAgent' => 'app',
55
+ }
56
+ Ewelink.logger.debug(self.class.name) { "Pressing RF bridge button #{button[:uuid].inspect}" }
57
+ send_to_web_socket(JSON.generate(params))
58
+ true
59
+ end
54
60
  end
55
61
  end
56
62
  end
@@ -78,19 +84,19 @@ module Ewelink
78
84
  end
79
85
  end
80
86
 
81
- [
82
- :@authentication_infos,
83
- :@devices,
84
- :@last_web_socket_pong_at,
85
- :@region,
86
- :@rf_bridge_buttons,
87
- :@switches,
88
- :@web_socket_ping_interval,
89
- :@web_socket_ping_thread,
90
- :@web_socket_thread,
91
- :@web_socket_url,
92
- :@web_socket,
93
- ].each do |variable|
87
+ %i(
88
+ @authentication_infos
89
+ @devices
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|
94
100
  remove_instance_variable(variable) if instance_variable_defined?(variable)
95
101
  end
96
102
  self
@@ -108,10 +114,10 @@ module Ewelink
108
114
  device_name = device['name'].presence || next
109
115
  buttons = device['params']['rfList'].each do |rf|
110
116
  button = {
111
- api_key: api_key,
117
+ api_key:,
112
118
  channel: rf['rfChl'],
113
- device_id: device_id,
114
- device_name: device_name,
119
+ device_id:,
120
+ device_name:,
115
121
  }
116
122
  remote_info = device['tags']['zyx_info'].find { |info| info['buttonName'].find { |data| data.key?(button[:channel].to_s) } }.presence || next
117
123
  remote_name = remote_info['name'].try(:squish).presence || next
@@ -119,10 +125,10 @@ module Ewelink
119
125
  button_name = button_info.values.first.try(:squish).presence || next
120
126
  button.merge!({
121
127
  name: button_name,
122
- remote_name: remote_name,
128
+ remote_name:,
123
129
  remote_type: remote_info['remote_type'],
124
130
  })
125
- 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]}")
126
132
  buttons << button
127
133
  end
128
134
  end
@@ -160,11 +166,11 @@ module Ewelink
160
166
  device_id = device['deviceid'].presence || next
161
167
  name = device['name'].presence || next
162
168
  switch = {
163
- api_key: api_key,
164
- device_id: device_id,
165
- name: name,
169
+ api_key:,
170
+ device_id:,
171
+ name:,
166
172
  }
167
- switch[:uuid] = Digest::UUID.uuid_v5(UUID_NAMESPACE, switch[:device_id])
173
+ switch[:uuid] = Digest::UUID.uuid_v5(Digest::UUID::DNS_NAMESPACE, switch[:device_id])
168
174
  switches << switch
169
175
  end
170
176
  end.tap { |switches| Ewelink.logger.debug(self.class.name) { "Found #{switches.size} switch(es)" } }
@@ -172,31 +178,33 @@ module Ewelink
172
178
  end
173
179
 
174
180
  def turn_switch!(uuid, on)
175
- if ['on', :on, 'true'].include?(on)
176
- on = true
177
- elsif ['off', :off, 'false'].include?(on)
178
- on = false
179
- end
180
- switch = find_switch!(uuid)
181
- @web_socket_switches_statuses[switch[:uuid]] = nil
182
- web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
183
- params = {
184
- 'action' => 'update',
185
- 'apikey' => switch[:api_key],
186
- 'deviceid' => switch[:device_id],
187
- 'params' => {
188
- 'switch' => on ? 'on' : 'off',
189
- },
190
- 'sequence' => web_socket_sequence,
191
- 'ts' => 0,
192
- 'userAgent' => 'app',
193
- }
194
- Ewelink.logger.debug(self.class.name) { "Turning switch #{switch[:uuid].inspect} #{on ? 'on' : 'off'}" }
195
- send_to_web_socket(JSON.generate(params))
181
+ process_action do
182
+ if ['on', :on, 'true'].include?(on)
183
+ on = true
184
+ elsif ['off', :off, 'false'].include?(on)
185
+ on = false
186
+ end
187
+ switch = find_switch!(uuid)
188
+ @web_socket_switches_statuses[switch[:uuid]] = nil
189
+ web_socket_wait_for(-> { web_socket_authenticated? }, initialize_web_socket: true) do
190
+ params = {
191
+ 'action' => 'update',
192
+ 'apikey' => switch[:api_key],
193
+ 'deviceid' => switch[:device_id],
194
+ 'params' => {
195
+ 'switch' => on ? 'on' : 'off',
196
+ },
197
+ 'sequence' => web_socket_sequence,
198
+ 'ts' => 0,
199
+ 'userAgent' => 'app',
200
+ }
201
+ Ewelink.logger.debug(self.class.name) { "Turning switch #{switch[:uuid].inspect} #{on ? 'on' : 'off'}" }
202
+ send_to_web_socket(JSON.generate(params))
203
+ end
204
+ sleep(SWITCH_STATUS_CHANGE_CHECK_TIMEOUT)
205
+ switch_on?(switch[:uuid]) # Waiting for switch status update
206
+ true
196
207
  end
197
- sleep(SWITCH_STATUS_CHANGE_CHECK_TIMEOUT)
198
- switch_on?(switch[:uuid]) # Waiting for switch status update
199
- true
200
208
  end
201
209
 
202
210
  def update_devices_status_on_connect?
@@ -246,7 +254,7 @@ module Ewelink
246
254
  params['phoneNumber'] = phone_number
247
255
  end
248
256
  body = JSON.generate(params)
249
- response = rest_request(:post, '/api/user/login', { body: body, headers: { 'Authorization' => "Sign #{Base64.encode64(OpenSSL::HMAC.digest('SHA256', APP_SECRET, body))}" } })
257
+ response = rest_request(:post, '/api/user/login', { body:, headers: { 'Authorization' => "Sign #{Base64.encode64(OpenSSL::HMAC.digest('SHA256', APP_SECRET, body))}" } })
250
258
  raise(Error.new('Authentication token not found')) if response['at'].blank?
251
259
  raise(Error.new('API key not found')) if response['user'].blank? || response['user']['apikey'].blank?
252
260
  {
@@ -289,6 +297,13 @@ module Ewelink
289
297
  SecureRandom.hex[0, 8]
290
298
  end
291
299
 
300
+ def process_action(&block)
301
+ return yield unless async_actions?
302
+ @async_actions_thread_pool ||= Thread.pool(1)
303
+ @async_actions_thread_pool.process(&block)
304
+ true
305
+ end
306
+
292
307
  def region
293
308
  @region ||= DEFAULT_REGION
294
309
  end
@@ -298,7 +313,7 @@ module Ewelink
298
313
  method = method.to_s.upcase
299
314
  headers = (options[:headers] || {}).reverse_merge('Content-Type' => 'application/json')
300
315
  Ewelink.logger.debug(self.class.name) { "#{method} #{url}" }
301
- response = HTTParty.send(method.downcase, url, options.merge(headers: headers).reverse_merge(timeout: REQUEST_TIMEOUT))
316
+ response = HTTParty.send(method.downcase, url, options.merge(headers:).reverse_merge(timeout: REQUEST_TIMEOUT))
302
317
  raise(Error.new("#{method} #{url}: #{response.code}")) unless response.success?
303
318
  if response['error'] == 301 && response['region'].present?
304
319
  @region = response['region']
@@ -375,12 +390,12 @@ module Ewelink
375
390
  EventMachine.run do
376
391
  @web_socket = Faye::WebSocket::Client.new(web_socket_url)
377
392
 
378
- @web_socket.on(:close) do |event|
393
+ @web_socket.on(:close) do
379
394
  Ewelink.logger.debug(self.class.name) { 'WebSocket closed' }
380
395
  reload
381
396
  end
382
397
 
383
- @web_socket.on(:open) do |event|
398
+ @web_socket.on(:open) do
384
399
  Ewelink.logger.debug(self.class.name) { 'WebSocket opened' }
385
400
  @last_web_socket_pong_at = Time.now
386
401
  authenticate_web_socket_api_key
@@ -397,7 +412,7 @@ module Ewelink
397
412
 
398
413
  begin
399
414
  json = JSON.parse(message)
400
- rescue => e
415
+ rescue
401
416
  Ewelink.logger.error(self.class.name) { 'WebSocket JSON parse error' }
402
417
  reload
403
418
  next
@@ -420,7 +435,7 @@ module Ewelink
420
435
  end
421
436
 
422
437
  if json['deviceid'].present? && json['params'].is_a?(Hash) && json['params']['switch'].present?
423
- switch = switches.find { |switch| switch[:device_id] == json['deviceid'] }
438
+ switch = switches.find { |item| item[:device_id] == json['deviceid'] }
424
439
  if switch.present?
425
440
  @web_socket_switches_statuses[switch[:uuid]] = json['params']['switch']
426
441
  Ewelink.logger.debug(self.class.name) { "Switch #{switch[:uuid].inspect} is #{@web_socket_switches_statuses[switch[:uuid]]}" }
@@ -467,13 +482,11 @@ module Ewelink
467
482
  end
468
483
  end
469
484
 
470
- def web_socket_wait_for(condition, initialize_web_socket: false, &block)
485
+ def web_socket_wait_for(condition, initialize_web_socket: false)
471
486
  web_socket if initialize_web_socket
472
487
  begin
473
488
  Timeout.timeout(REQUEST_TIMEOUT) do
474
- while !condition.call
475
- sleep(WEB_SOCKET_WAIT_INTERVAL)
476
- end
489
+ sleep(WEB_SOCKET_WAIT_INTERVAL) until condition.call
477
490
  block_given? ? yield : true
478
491
  end
479
492
  rescue => e
@@ -3,13 +3,13 @@ module Ewelink
3
3
  class Runner
4
4
 
5
5
  def run
6
- api = Api.new(options.slice(:email, :password, :phone_number))
6
+ api = Api.new(**options.slice(:email, :password, :phone_number))
7
7
  puts(JSON.pretty_generate(api.switches)) if options[:list_switches]
8
8
  puts(JSON.pretty_generate(api.rf_bridge_buttons)) if options[:list_rf_bridge_buttons]
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
data/lib/ewelink.rb CHANGED
@@ -10,6 +10,7 @@ require 'logger'
10
10
  require 'openssl'
11
11
  require 'optparse'
12
12
  require 'set'
13
+ require 'thread/pool'
13
14
  require 'timeout'
14
15
 
15
16
  module Ewelink
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.2.0
4
+ version: 4.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: 2020-12-29 00:00:00.000000000 Z
11
+ date: 2022-02-05 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,40 @@ 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
+ - !ruby/object:Gem::Dependency
74
+ name: thread
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 0.2.0
80
+ - - "<"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.3.0
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 0.2.0
90
+ - - "<"
91
+ - !ruby/object:Gem::Version
92
+ version: 0.3.0
73
93
  - !ruby/object:Gem::Dependency
74
94
  name: byebug
75
95
  requirement: !ruby/object:Gem::Requirement
@@ -96,20 +116,60 @@ dependencies:
96
116
  requirements:
97
117
  - - ">="
98
118
  - !ruby/object:Gem::Version
99
- version: 12.0.0
119
+ version: 13.0.0
100
120
  - - "<"
121
+ - !ruby/object:Gem::Version
122
+ version: 14.0.0
123
+ type: :development
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
101
128
  - !ruby/object:Gem::Version
102
129
  version: 13.0.0
130
+ - - "<"
131
+ - !ruby/object:Gem::Version
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
103
143
  type: :development
104
144
  prerelease: false
105
145
  version_requirements: !ruby/object:Gem::Requirement
106
146
  requirements:
107
147
  - - ">="
108
148
  - !ruby/object:Gem::Version
109
- version: 12.0.0
149
+ version: 1.25.0
110
150
  - - "<"
111
151
  - !ruby/object:Gem::Version
112
- version: 13.0.0
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
113
173
  description: Manage eWeLink smart home devices
114
174
  email: al@alweb.org
115
175
  executables:
@@ -137,14 +197,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
137
197
  requirements:
138
198
  - - ">="
139
199
  - !ruby/object:Gem::Version
140
- version: 2.0.0
200
+ version: 3.1.0
141
201
  required_rubygems_version: !ruby/object:Gem::Requirement
142
202
  requirements:
143
203
  - - ">="
144
204
  - !ruby/object:Gem::Version
145
205
  version: '0'
146
206
  requirements: []
147
- rubygems_version: 3.0.3
207
+ rubygems_version: 3.3.3
148
208
  signing_key:
149
209
  specification_version: 4
150
210
  summary: Manage eWeLink devices