ewelink 3.2.0 → 4.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: 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