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 +4 -4
- data/README.mdown +11 -5
- data/VERSION +1 -1
- data/ewelink.gemspec +1 -0
- data/lib/ewelink.rb +3 -0
- data/lib/ewelink/api.rb +249 -99
- data/lib/ewelink/runner.rb +12 -8
- metadata +25 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97046b44715a988f6ae92d986c0fad17ab4d5cae6420a125c10f23854cfaadc9
|
4
|
+
data.tar.gz: b95e52d88f8d57d5bf9f54b67d5a8c0779d26b66faadbc252e13eadb80854292
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f153a280ff50b245434b2039cd3bb88341da6bd7688556a8a7c3fe4558a2b13a6be5c1497f5b06dc381495cb63d8c26d4d3f2625ea5bd4148e4c55a2f28b2faf
|
7
|
+
data.tar.gz: e3b69fa1607599325ea0f2867fbd0d32fd3abd56a4e7bd8d07107e083d4ebbfaf919bc4af34b28948163d1fb5340a53489aec1839cc59ba238f2917a8db03cc5
|
data/README.mdown
CHANGED
@@ -40,24 +40,30 @@ api.rf_bridge_buttons.each do |button|
|
|
40
40
|
end
|
41
41
|
```
|
42
42
|
|
43
|
-
###
|
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.
|
50
|
-
api.
|
49
|
+
api.turn_switch!(switch[:uuid], :on)
|
50
|
+
api.turn_switch!(switch[:uuid], :off)
|
51
51
|
```
|
52
52
|
|
53
|
-
|
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
|
+
2.0.0
|
data/ewelink.gemspec
CHANGED
@@ -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'
|
data/lib/ewelink.rb
CHANGED
data/lib/ewelink/api.rb
CHANGED
@@ -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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
44
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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 =
|
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 =
|
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
|
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 =
|
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
|
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
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
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
|
data/lib/ewelink/runner.rb
CHANGED
@@ -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[:
|
10
|
-
options[:
|
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: [],
|
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', '
|
41
|
-
options[:
|
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', '
|
44
|
-
options[:
|
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, :
|
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:
|
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-
|
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: []
|