ewelink 1.0.0 → 2.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: e6cbd50164b2f25247423b0cece7baee8ec1e6bbc49faa2545ad4c7450feaf3d
4
- data.tar.gz: cf1e6148a4cebdb5efd52acfb66feacaa096d51ca2f1cd0b06e7c45b002c6fa1
3
+ metadata.gz: 97046b44715a988f6ae92d986c0fad17ab4d5cae6420a125c10f23854cfaadc9
4
+ data.tar.gz: b95e52d88f8d57d5bf9f54b67d5a8c0779d26b66faadbc252e13eadb80854292
5
5
  SHA512:
6
- metadata.gz: e42fdd3244a0a9bb3a176c4ab760ba6100b8a8e9146c68b135044610075a4963d7e2d03df4cec6ae97febdcff21966d638ba9f4b6dd8e774c436acdf75174e0b
7
- data.tar.gz: 204e83b06e6b788095352b97f5553291ec20bca0e4191695abc30f72cf898be9194bc04b3518fd61220f45250a90dac6054cfc184bdc10400639c8dca70efbaa
6
+ metadata.gz: f153a280ff50b245434b2039cd3bb88341da6bd7688556a8a7c3fe4558a2b13a6be5c1497f5b06dc381495cb63d8c26d4d3f2625ea5bd4148e4c55a2f28b2faf
7
+ data.tar.gz: e3b69fa1607599325ea0f2867fbd0d32fd3abd56a4e7bd8d07107e083d4ebbfaf919bc4af34b28948163d1fb5340a53489aec1839cc59ba238f2917a8db03cc5
@@ -40,24 +40,30 @@ api.rf_bridge_buttons.each do |button|
40
40
  end
41
41
  ```
42
42
 
43
- ### Set switch on or off
43
+ ### Turn switch on or off
44
44
 
45
45
  ```ruby
46
46
  require 'ewelink'
47
47
 
48
48
  api = Ewelink::Api.new(phone_number: '+687 414243', password: 'secr$t')
49
- api.switch_on!(switch[:uuid])
50
- api.switch_off!(switch[:uuid])
49
+ api.turn_switch!(switch[:uuid], :on)
50
+ api.turn_switch!(switch[:uuid], :off)
51
51
  ```
52
52
 
53
- ### Check if switch is on or off
53
+ Or :
54
+
55
+ ```ruby
56
+ api.turn_switch!(switch[:uuid], true)
57
+ api.turn_switch!(switch[:uuid], false)
58
+ ```
59
+
60
+ ### Check if switch is on
54
61
 
55
62
  ```ruby
56
63
  require 'ewelink'
57
64
 
58
65
  api = Ewelink::Api.new(phone_number: '+687 414243', password: 'secr$t')
59
66
  puts api.switch_on?(switch[:uuid])
60
- puts api.switch_off?(switch[:uuid])
61
67
  ```
62
68
 
63
69
  ### Press RF bridge button
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 2.0.0
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
17
17
 
18
18
  s.add_dependency 'activesupport', '>= 6.0.0', '< 7.0.0'
19
19
  s.add_dependency 'httparty', '>= 0.18.0', '< 0.19.0'
20
+ s.add_dependency 'websocket-client-simple', '>= 0.3.0', '< 0.4.0'
20
21
 
21
22
  s.add_development_dependency 'byebug', '>= 11.0.0', '< 12.0.0'
22
23
  s.add_development_dependency 'rake', '>= 12.0.0', '< 13.0.0'
@@ -7,6 +7,9 @@ require 'json'
7
7
  require 'logger'
8
8
  require 'openssl'
9
9
  require 'optparse'
10
+ require 'set'
11
+ require 'timeout'
12
+ require 'websocket-client-simple'
10
13
 
11
14
  module Ewelink
12
15
 
@@ -7,10 +7,11 @@ module Ewelink
7
7
  DEFAULT_REGION = 'us'
8
8
  RF_BRIDGE_DEVICE_UIID = 28
9
9
  SWITCH_DEVICES_UIIDS = [1, 5, 6, 24]
10
- TIMEOUT = 10
10
+ TIMEOUT = 10.seconds
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_WAIT_INTERVAL = 0.2.seconds
14
15
 
15
16
  attr_reader :email, :password, :phone_number
16
17
 
@@ -19,109 +20,150 @@ module Ewelink
19
20
  @mutexs = {}
20
21
  @password = password.presence || raise(Error.new(":password must be specified"))
21
22
  @phone_number = phone_number.presence.try(:strip)
23
+ @switches_statuses = {}
24
+ @web_socket_authenticated_api_keys = Set.new
22
25
  raise(Error.new(":email or :phone_number must be specified")) if email.blank? && phone_number.blank?
23
26
  end
24
27
 
25
28
  def press_rf_bridge_button!(uuid)
26
- button = find_rf_bridge_button!(uuid)
27
- params = {
28
- 'appid' => APP_ID,
29
- 'deviceid' => button[:device_id],
30
- 'nonce' => nonce,
31
- 'params' => {
32
- 'cmd' => 'transmit',
33
- 'rfChl' => button[:channel],
34
- },
35
- 'ts' => Time.now.to_i,
36
- 'version' => VERSION,
37
- }
38
- http_request(:post, '/api/user/device/status', body: JSON.generate(params), headers: authentication_headers)
39
- true
29
+ synchronize(:press_rf_bridge_button) do
30
+ button = find_rf_bridge_button!(uuid)
31
+ web_socket_wait_for(-> { web_socket_authenticated? }) do
32
+ params = {
33
+ 'action' => 'update',
34
+ 'apikey' => button[:api_key],
35
+ 'deviceid' => button[:device_id],
36
+ 'params' => {
37
+ 'cmd' => 'transmit',
38
+ 'rfChl' => button[:channel],
39
+ },
40
+ 'sequence' => web_socket_sequence,
41
+ 'ts' => 0,
42
+ 'userAgent' => 'app',
43
+ }
44
+ send_to_web_socket(JSON.generate(params))
45
+ true
46
+ end
47
+ end
40
48
  end
41
49
 
42
50
  def reload
43
- Ewelink.logger.debug(self.class.name) { 'Reloading API (authentication token, devices & region cache)' }
44
- [:@authentication_token, :@devices, :@rf_bridge_buttons, :@region, :@switches].each do |variable|
51
+ Ewelink.logger.debug(self.class.name) { 'Reloading API (authentication token, devices, region,...)' }
52
+ dispose_web_socket
53
+ @switches_statuses.clear
54
+ [:@api_keys, :@authentication_token, :@devices, :@rf_bridge_buttons, :@region, :@switches, :@web_socket, :@web_socket_url].each do |variable|
45
55
  remove_instance_variable(variable) if instance_variable_defined?(variable)
46
56
  end
47
57
  self
48
58
  end
49
59
 
50
60
  def rf_bridge_buttons
51
- @rf_bridge_buttons ||= [].tap do |buttons|
52
- rf_bridge_devices = devices.select { |device| device['uiid'] == RF_BRIDGE_DEVICE_UIID }.tap do |devices|
53
- Ewelink.logger.debug(self.class.name) { "Found #{devices.size} RF 433MHz Bridge device(s)" }
54
- end
55
- rf_bridge_devices.each do |device|
56
- device_id = device['deviceid'].presence || next
57
- device_name = device['name'].presence || next
58
- buttons = device['params']['rfList'].each do |rf|
59
- button = {
60
- channel: rf['rfChl'],
61
- device_id: device_id,
62
- device_name: device_name,
63
- }
64
- remote_info = device['tags']['zyx_info'].find { |info| info['buttonName'].find { |data| data.key?(button[:channel].to_s) } }.presence || next
65
- remote_name = remote_info['name'].try(:squish).presence || next
66
- button_info = remote_info['buttonName'].find { |info| info.key?(button[:channel].to_s) }.presence || next
67
- button_name = button_info.values.first.try(:squish).presence || next
68
- button.merge!({
69
- name: button_name,
70
- remote_name: remote_name,
71
- remote_type: remote_info['remote_type'],
72
- })
73
- button[:uuid] = Digest::UUID.uuid_v5(UUID_NAMESPACE, "#{button[:device_id]}/#{button[:channel]}")
74
- buttons << button
61
+ synchronize(:rf_bridge_buttons) do
62
+ @rf_bridge_buttons ||= [].tap do |buttons|
63
+ rf_bridge_devices = devices.select { |device| device['uiid'] == RF_BRIDGE_DEVICE_UIID }.tap do |devices|
64
+ Ewelink.logger.debug(self.class.name) { "Found #{devices.size} RF 433MHz bridge device(s)" }
75
65
  end
76
- end
77
- end.tap { |buttons| Ewelink.logger.debug(self.class.name) { "Found #{buttons.size} RF 433MHz bridge button(s)" } }
78
- end
79
-
80
- def switch_off!(uuid)
81
- update_switch_on!(uuid, false)
82
- end
83
-
84
- def switch_off?(uuid)
85
- !switch_on?(uuid)
86
- end
87
-
88
- def switch_on!(uuid)
89
- update_switch_on!(uuid, true)
66
+ rf_bridge_devices.each do |device|
67
+ api_key = device['apikey'].presence || next
68
+ device_id = device['deviceid'].presence || next
69
+ device_name = device['name'].presence || next
70
+ buttons = device['params']['rfList'].each do |rf|
71
+ button = {
72
+ api_key: api_key,
73
+ channel: rf['rfChl'],
74
+ device_id: device_id,
75
+ device_name: device_name,
76
+ }
77
+ remote_info = device['tags']['zyx_info'].find { |info| info['buttonName'].find { |data| data.key?(button[:channel].to_s) } }.presence || next
78
+ remote_name = remote_info['name'].try(:squish).presence || next
79
+ button_info = remote_info['buttonName'].find { |info| info.key?(button[:channel].to_s) }.presence || next
80
+ button_name = button_info.values.first.try(:squish).presence || next
81
+ button.merge!({
82
+ name: button_name,
83
+ remote_name: remote_name,
84
+ remote_type: remote_info['remote_type'],
85
+ })
86
+ button[:uuid] = Digest::UUID.uuid_v5(UUID_NAMESPACE, "#{button[:device_id]}/#{button[:channel]}")
87
+ buttons << button
88
+ end
89
+ end
90
+ end.tap { |buttons| Ewelink.logger.debug(self.class.name) { "Found #{buttons.size} RF 433MHz bridge button(s)" } }
91
+ end
90
92
  end
91
93
 
92
94
  def switch_on?(uuid)
93
95
  switch = find_switch!(uuid)
94
- params = {
95
- 'appid' => APP_ID,
96
- 'deviceid' => switch[:device_id],
97
- 'nonce' => nonce,
98
- 'ts' => Time.now.to_i,
99
- 'version' => VERSION,
100
- }
101
- response = http_request(:get, '/api/user/device/status', headers: authentication_headers, query: params)
102
- response['params']['switch'] == 'on'
96
+ if @switches_statuses[switch[:uuid]].nil?
97
+ params = {
98
+ 'action' => 'query',
99
+ 'apikey' => switch[:api_key],
100
+ 'deviceid' => switch[:device_id],
101
+ 'sequence' => web_socket_sequence,
102
+ 'ts' => 0,
103
+ 'userAgent' => 'app',
104
+ }
105
+ web_socket_wait_for(-> { web_socket_authenticated? }) do
106
+ send_to_web_socket(JSON.generate(params))
107
+ end
108
+ end
109
+ web_socket_wait_for(-> { !@switches_statuses[switch[:uuid]].nil? }) do
110
+ @switches_statuses[switch[:uuid]] == 'on'
111
+ end
103
112
  end
104
113
 
105
114
  def switches
106
- @switches ||= [].tap do |switches|
107
- switch_devices = devices.select { |device| SWITCH_DEVICES_UIIDS.include?(device['uiid']) }.tap do |devices|
108
- Ewelink.logger.debug(self.class.name) { "Found #{devices.size} switch device(s)" }
109
- end
110
- switch_devices.each do |device|
111
- device_id = device['deviceid'].presence || next
112
- name = device['name'].presence || next
113
- switch = {
114
- device_id: device_id,
115
- name: name,
116
- }
117
- switch[:uuid] = Digest::UUID.uuid_v5(UUID_NAMESPACE, switch[:device_id])
118
- switches << switch
119
- end
120
- end.tap { |switches| Ewelink.logger.debug(self.class.name) { "Found #{switches.size} switch(es)" } }
115
+ synchronize(:switches) do
116
+ @switches ||= [].tap do |switches|
117
+ switch_devices = devices.select { |device| SWITCH_DEVICES_UIIDS.include?(device['uiid']) }
118
+ switch_devices.each do |device|
119
+ api_key = device['apikey'].presence || next
120
+ device_id = device['deviceid'].presence || next
121
+ name = device['name'].presence || next
122
+ switch = {
123
+ api_key: api_key,
124
+ device_id: device_id,
125
+ name: name,
126
+ }
127
+ switch[:uuid] = Digest::UUID.uuid_v5(UUID_NAMESPACE, switch[:device_id])
128
+ switches << switch
129
+ end
130
+ end.tap { |switches| Ewelink.logger.debug(self.class.name) { "Found #{switches.size} switch(es)" } }
131
+ end
132
+ end
133
+
134
+ def turn_switch!(uuid, on)
135
+ if ['on', :on, 'true'].include?(on)
136
+ on = true
137
+ elsif ['off', :off, 'false'].include?(on)
138
+ on = false
139
+ end
140
+ switch = find_switch!(uuid)
141
+ @switches_statuses[switch[:uuid]] = nil
142
+ web_socket_wait_for(-> { web_socket_authenticated? }) do
143
+ params = {
144
+ 'action' => 'update',
145
+ 'apikey' => switch[:api_key],
146
+ 'deviceid' => switch[:device_id],
147
+ 'params' => {
148
+ 'switch' => on ? 'on' : 'off',
149
+ },
150
+ 'sequence' => web_socket_sequence,
151
+ 'ts' => 0,
152
+ 'userAgent' => 'app',
153
+ }
154
+ send_to_web_socket(JSON.generate(params))
155
+ true
156
+ end
121
157
  end
122
158
 
123
159
  private
124
160
 
161
+ def api_keys
162
+ synchronize(:api_keys) do
163
+ @api_keys ||= Set.new(devices.map { |device| device['apikey'] })
164
+ end
165
+ end
166
+
125
167
  def authentication_headers
126
168
  { 'Authorization' => "Bearer #{authentication_token}" }
127
169
  end
@@ -143,7 +185,7 @@ module Ewelink
143
185
  params['phoneNumber'] = phone_number
144
186
  end
145
187
  body = JSON.generate(params)
146
- response = http_request(:post, '/api/user/login', { body: body, headers: { 'Authorization' => "Sign #{Base64.encode64(OpenSSL::HMAC.digest('SHA256', APP_SECRET, body))}" } })
188
+ response = rest_request(:post, '/api/user/login', { body: body, headers: { 'Authorization' => "Sign #{Base64.encode64(OpenSSL::HMAC.digest('SHA256', APP_SECRET, body))}" } })
147
189
  raise(Error.new('Authentication token not found')) if response['at'].blank?
148
190
  response['at'].tap { Ewelink.logger.debug(self.class.name) { 'Authentication token found' } }
149
191
  end
@@ -160,12 +202,24 @@ module Ewelink
160
202
  'ts' => Time.now.to_i,
161
203
  'version' => VERSION,
162
204
  }
163
- response = http_request(:get, '/api/user/device', headers: authentication_headers, query: params)
205
+ response = rest_request(:get, '/api/user/device', headers: authentication_headers, query: params)
164
206
  response['devicelist'].tap { |devices| Ewelink.logger.debug(self.class.name) { "Found #{devices.size} device(s)" } }
165
207
  end
166
208
  end
167
209
  end
168
210
 
211
+ def dispose_web_socket
212
+ @web_socket_authenticated_api_keys = Set.new
213
+ if instance_variable_defined?(:@web_socket)
214
+ begin
215
+ @web_socket.close if @web_socket.open?
216
+ rescue
217
+ # Ignoring close errors
218
+ end
219
+ remove_instance_variable(:@web_socket) if instance_variable_defined?(:@web_socket)
220
+ end
221
+ end
222
+
169
223
  def find_rf_bridge_button!(uuid)
170
224
  rf_bridge_buttons.find { |button| button[:uuid] == uuid } || raise(Error.new("No such RF bridge button with UUID: #{uuid.inspect}"))
171
225
  end
@@ -182,43 +236,139 @@ module Ewelink
182
236
  @region ||= DEFAULT_REGION
183
237
  end
184
238
 
185
- def http_request(method, path, options = {})
239
+ def rest_request(method, path, options = {})
186
240
  url = "#{URL.gsub('#{region}', region)}#{path}"
187
241
  method = method.to_s.upcase
188
242
  headers = (options[:headers] || {}).reverse_merge('Content-Type' => 'application/json')
189
243
  Ewelink.logger.debug(self.class.name) { "#{method} #{url}" }
190
- response = synchronize(:http_request) { HTTParty.send(method.downcase, url, options.merge(headers: headers).reverse_merge(timeout: TIMEOUT)) }
244
+ response = HTTParty.send(method.downcase, url, options.merge(headers: headers).reverse_merge(timeout: TIMEOUT))
191
245
  raise(Error.new("#{method} #{url}: #{response.code}")) unless response.success?
192
246
  if response['error'] == 301 && response['region'].present?
193
247
  @region = response['region']
194
248
  Ewelink.logger.debug(self.class.name) { "Switched to region #{region.inspect}" }
195
- return http_request(method, path, options)
249
+ return rest_request(method, path, options)
196
250
  end
197
251
  remove_instance_variable(:@authentication_token) if instance_variable_defined?(:@authentication_token) && [401, 403].include?(response['error'])
198
252
  raise(Error.new("#{method} #{url}: #{response['error']} #{response['msg']}".strip)) if response['error'].present? && response['error'] != 0
199
- response
253
+ response.to_h
200
254
  rescue Errno::ECONNREFUSED, OpenSSL::OpenSSLError, SocketError, Timeout::Error => e
201
255
  raise Error.new(e)
202
256
  end
203
257
 
258
+ def send_to_web_socket(data)
259
+ web_socket.send(data)
260
+ rescue
261
+ dispose_web_socket
262
+ raise
263
+ end
264
+
204
265
  def synchronize(name, &block)
205
266
  (@mutexs[name] ||= Mutex.new).synchronize(&block)
206
267
  end
207
268
 
208
- def update_switch_on!(uuid, on)
209
- switch = find_switch!(uuid)
210
- params = {
211
- 'appid' => APP_ID,
212
- 'deviceid' => switch[:device_id],
213
- 'nonce' => nonce,
214
- 'params' => {
215
- 'switch' => on ? 'on' : 'off',
216
- },
217
- 'ts' => Time.now.to_i,
218
- 'version' => VERSION,
219
- }
220
- http_request(:post, '/api/user/device/status', body: JSON.generate(params), headers: authentication_headers)
221
- true
269
+ def web_socket
270
+ synchronize(:web_socket) do
271
+ @web_socket ||= begin
272
+ api = self
273
+
274
+ WebSocket::Client::Simple.connect(web_socket_url) do |web_socket|
275
+ Ewelink.logger.debug(self.class.name) { "Opening WebSocket to #{web_socket_url}" }
276
+
277
+ web_socket.on(:close) do
278
+ api.instance_eval do
279
+ Ewelink.logger.debug(self.class.name) { 'WebSocket closed' }
280
+ dispose_web_socket
281
+ end
282
+ end
283
+
284
+ web_socket.on(:error) do |e|
285
+ api.instance_eval do
286
+ Ewelink.logger.warn(self.class.name) { "WebSocket error: #{e}" }
287
+ dispose_web_socket
288
+ end
289
+ end
290
+
291
+ web_socket.on(:message) do |message|
292
+ api.instance_eval do
293
+ response = JSON.parse(message.data)
294
+
295
+ if response.key?('error') && response['error'] != 0
296
+ Ewelink.logger.error(self.class.name) { "WebSocket message error: #{message.data}" }
297
+ next
298
+ end
299
+
300
+ if response['apikey'].present? && !@web_socket_authenticated_api_keys.include?(response['apikey'])
301
+ @web_socket_authenticated_api_keys << response['apikey']
302
+ Ewelink.logger.debug(self.class.name) { "WebSocket successfully authenticated API key: #{response['apikey'].inspect}" }
303
+ end
304
+
305
+ if response['deviceid'].present? && response['params'].is_a?(Hash) && response['params']['switch'].present?
306
+ switch = switches.find { |switch| switch[:device_id] == response['deviceid'] }
307
+ @switches_statuses[switch[:uuid]] = response['params']['switch'] if switch.present?
308
+ end
309
+ end
310
+ end
311
+
312
+ web_socket.on(:open) do
313
+ api.instance_eval do
314
+ Ewelink.logger.debug(self.class.name) { 'WebSocket opened' }
315
+ api_keys.each do |api_key|
316
+ params = {
317
+ 'action' => 'userOnline',
318
+ 'apikey' => api_key,
319
+ 'appid' => APP_ID,
320
+ 'at' => authentication_token,
321
+ 'nonce' => nonce,
322
+ 'sequence' => web_socket_sequence,
323
+ 'ts' => Time.now.to_i,
324
+ 'userAgent' => 'app',
325
+ 'version' => VERSION,
326
+ }
327
+ Ewelink.logger.debug(self.class.name) { "Authenticating WebSocket API key: #{api_key.inspect}" }
328
+ send_to_web_socket(JSON.generate(params))
329
+ end
330
+ end
331
+ end
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+ def web_socket_authenticated?
338
+ api_keys == @web_socket_authenticated_api_keys
339
+ end
340
+
341
+ def web_socket_sequence
342
+ (Time.now.to_f * 1000).round.to_s
343
+ end
344
+
345
+ def web_socket_url
346
+ synchronize(:web_socket_url) do
347
+ @web_socket_url ||= begin
348
+ params = {
349
+ 'accept' => 'ws',
350
+ 'appid' => APP_ID,
351
+ 'nonce' => nonce,
352
+ 'ts' => Time.now.to_i,
353
+ 'version' => VERSION,
354
+ }
355
+ response = rest_request(:post, '/dispatch/app', body: JSON.generate(params), headers: authentication_headers)
356
+ raise('Error while getting WebSocket URL') unless response['error'] == 0
357
+ domain = response['domain'].presence || raise("Can't get WebSocket server domain")
358
+ port = response['port'].presence || raise("Can't get WebSocket server port")
359
+ "wss://#{domain}:#{port}/api/ws".tap { |url| Ewelink.logger.debug(self.class.name) { "WebSocket URL is: #{url.inspect}" } }
360
+ end
361
+ end
362
+ end
363
+
364
+ def web_socket_wait_for(condition, &block)
365
+ web_socket # Initializes WebSocket
366
+ Timeout.timeout(TIMEOUT) do
367
+ loop do
368
+ return yield if condition.call
369
+ sleep(WEB_SOCKET_WAIT_INTERVAL)
370
+ end
371
+ end
222
372
  end
223
373
 
224
374
  end
@@ -6,16 +6,17 @@ module Ewelink
6
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
- options[:switches_on_uuids].each { |uuid| api.switch_on!(uuid) }
10
- options[:switches_off_uuids].each { |uuid| api.switch_off!(uuid) }
9
+ options[:turn_switches_on_uuids].each { |uuid| api.turn_switch!(uuid, :on) }
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
13
  end
13
14
 
14
15
  private
15
16
 
16
17
  def options
17
18
  @options ||= begin
18
- options = { press_rf_bridge_buttons_uuids: [], switches_off_uuids: [], switches_on_uuids: [] }
19
+ options = { press_rf_bridge_buttons_uuids: [], turn_switches_off_uuids: [], turn_switches_on_uuids: [], switch_status_uuids: [] }
19
20
  parser = OptionParser.new do |opts|
20
21
  opts.banner = 'Manage eWeLink smart home devices'
21
22
  opts.version = File.read(File.expand_path('../../VERSION', __dir__)).strip
@@ -37,15 +38,18 @@ module Ewelink
37
38
  opts.on('--list-rf-bridge-buttons', 'List all RF 433MHz bridge buttons in JSON format') do
38
39
  options[:list_rf_bridge_buttons] = true
39
40
  end
40
- opts.on('--switch-on SWITCH_UUID', 'Set the switch with specified UUID on') do |uuid|
41
- options[:switches_on_uuids] << uuid
41
+ opts.on('--turn-switch-on SWITCH_UUID', 'Turn the switch with specified UUID on') do |uuid|
42
+ options[:turn_switches_on_uuids] << uuid
42
43
  end
43
- opts.on('--switch-off SWITCH_UUID', 'Set the switch with specified UUID off') do |uuid|
44
- options[:switches_off_uuids] << uuid
44
+ opts.on('--turn-switch-off SWITCH_UUID', 'Turn the switch with specified UUID off') do |uuid|
45
+ options[:turn_switches_off_uuids] << uuid
45
46
  end
46
47
  opts.on('--press-rf-bridge-button BUTTON_UUID', 'Press RF 433MHz bridge button with specified UUID') do |uuid|
47
48
  options[:press_rf_bridge_buttons_uuids] << uuid
48
49
  end
50
+ opts.on('--switch-status SWITCH_UUID', 'Displays switch status of specified UUID') do |uuid|
51
+ options[:switch_status_uuids] << uuid
52
+ end
49
53
  opts.on('-v', '--verbose', 'Verbose mode') do
50
54
  Ewelink.logger.level = :debug
51
55
  end
@@ -61,7 +65,7 @@ module Ewelink
61
65
  STDERR.puts(parser.summarize)
62
66
  exit(1)
63
67
  end
64
- if [:list_switches, :list_rf_bridge_buttons, :switches_on_uuids, :switches_off_uuids, :press_rf_bridge_buttons_uuids].map { |action| options[action] }.all?(&:blank?)
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?)
65
69
  STDERR.puts('An action must be specified (listing switches, press RF bridge button, etc.)')
66
70
  STDERR.puts(parser.summarize)
67
71
  exit(1)
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: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexis Toulotte
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-18 00:00:00.000000000 Z
11
+ date: 2020-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -50,6 +50,26 @@ dependencies:
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
52
  version: 0.19.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: websocket-client-simple
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 0.3.0
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: 0.4.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 0.3.0
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: 0.4.0
53
73
  - !ruby/object:Gem::Dependency
54
74
  name: byebug
55
75
  requirement: !ruby/object:Gem::Requirement
@@ -109,7 +129,7 @@ homepage: https://github.com/alexistoulotte/ewelink
109
129
  licenses:
110
130
  - MIT
111
131
  metadata: {}
112
- post_install_message:
132
+ post_install_message:
113
133
  rdoc_options: []
114
134
  require_paths:
115
135
  - lib
@@ -125,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
145
  version: '0'
126
146
  requirements: []
127
147
  rubygems_version: 3.0.3
128
- signing_key:
148
+ signing_key:
129
149
  specification_version: 4
130
150
  summary: Manage eWeLink devices
131
151
  test_files: []