ewelink 1.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 +7 -0
- data/README.mdown +84 -0
- data/VERSION +1 -0
- data/bin/ewelink +17 -0
- data/ewelink.gemspec +23 -0
- data/lib/ewelink/api.rb +226 -0
- data/lib/ewelink/error.rb +6 -0
- data/lib/ewelink/runner.rb +78 -0
- data/lib/ewelink.rb +20 -0
- metadata +131 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e6cbd50164b2f25247423b0cece7baee8ec1e6bbc49faa2545ad4c7450feaf3d
|
4
|
+
data.tar.gz: cf1e6148a4cebdb5efd52acfb66feacaa096d51ca2f1cd0b06e7c45b002c6fa1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e42fdd3244a0a9bb3a176c4ab760ba6100b8a8e9146c68b135044610075a4963d7e2d03df4cec6ae97febdcff21966d638ba9f4b6dd8e774c436acdf75174e0b
|
7
|
+
data.tar.gz: 204e83b06e6b788095352b97f5553291ec20bca0e4191695abc30f72cf898be9194bc04b3518fd61220f45250a90dac6054cfc184bdc10400639c8dca70efbaa
|
data/README.mdown
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# Ewelink
|
2
|
+
|
3
|
+
Ruby API to manage eWeLink smart home devices.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Just add this into your `Gemfile`:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'ewelink'
|
11
|
+
```
|
12
|
+
|
13
|
+
Then, just run `bundle install`.
|
14
|
+
|
15
|
+
## Examples
|
16
|
+
|
17
|
+
### Displaying all switches
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
require 'ewelink'
|
21
|
+
|
22
|
+
api = Ewelink::Api.new(email: 'john@example.com', password: 'secr$t')
|
23
|
+
api.switches.each do |switch|
|
24
|
+
puts switch[:name]
|
25
|
+
puts switch[:uuid]
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
`email` or `phone_number` must be specified for authentication.
|
30
|
+
|
31
|
+
### Displaying all RF bridge buttons
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
require 'ewelink'
|
35
|
+
|
36
|
+
api = Ewelink::Api.new(email: 'john@example.com', password: 'secr$t')
|
37
|
+
api.rf_bridge_buttons.each do |button|
|
38
|
+
puts button[:name]
|
39
|
+
puts button[:uuid]
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
### Set switch on or off
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
require 'ewelink'
|
47
|
+
|
48
|
+
api = Ewelink::Api.new(phone_number: '+687 414243', password: 'secr$t')
|
49
|
+
api.switch_on!(switch[:uuid])
|
50
|
+
api.switch_off!(switch[:uuid])
|
51
|
+
```
|
52
|
+
|
53
|
+
### Check if switch is on or off
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
require 'ewelink'
|
57
|
+
|
58
|
+
api = Ewelink::Api.new(phone_number: '+687 414243', password: 'secr$t')
|
59
|
+
puts api.switch_on?(switch[:uuid])
|
60
|
+
puts api.switch_off?(switch[:uuid])
|
61
|
+
```
|
62
|
+
|
63
|
+
### Press RF bridge button
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
require 'ewelink'
|
67
|
+
|
68
|
+
api = Ewelink::Api.new(email: 'john@example.com', password: 'secr$t')
|
69
|
+
api.press_rf_bridge_button!(button[:uuid])
|
70
|
+
```
|
71
|
+
|
72
|
+
### Configuring logger
|
73
|
+
|
74
|
+
In order to have some debug informations about what kagu does, you could
|
75
|
+
configure its logger:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Ewelink.logger = Logger.new(STDERR)
|
79
|
+
```
|
80
|
+
|
81
|
+
### Executable
|
82
|
+
|
83
|
+
This gem also provides a `ewelink` executable, just run it with
|
84
|
+
`--help` option to get all available options.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/bin/ewelink
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/ewelink'
|
4
|
+
|
5
|
+
Ewelink.logger = Logger.new(STDOUT, formatter: -> (severity, time, progname, message) {
|
6
|
+
text = ''
|
7
|
+
text << "[#{progname}] " if progname.present?
|
8
|
+
text << message.to_s << "\n"
|
9
|
+
})
|
10
|
+
Ewelink.logger.level = :warn
|
11
|
+
|
12
|
+
begin
|
13
|
+
Ewelink::Runner.new.run
|
14
|
+
rescue => e
|
15
|
+
Ewelink.logger.fatal(Ewelink::Runner.name) { e }
|
16
|
+
exit(1)
|
17
|
+
end
|
data/ewelink.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'ewelink'
|
3
|
+
s.version = File.read("#{File.dirname(__FILE__)}/VERSION").strip
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.author = 'Alexis Toulotte'
|
6
|
+
s.email = 'al@alweb.org'
|
7
|
+
s.homepage = 'https://github.com/alexistoulotte/ewelink'
|
8
|
+
s.summary = 'Manage eWeLink devices'
|
9
|
+
s.description = 'Manage eWeLink smart home devices'
|
10
|
+
s.license = 'MIT'
|
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) }
|
14
|
+
s.require_paths = ['lib']
|
15
|
+
|
16
|
+
s.required_ruby_version = '>= 2.0.0'
|
17
|
+
|
18
|
+
s.add_dependency 'activesupport', '>= 6.0.0', '< 7.0.0'
|
19
|
+
s.add_dependency 'httparty', '>= 0.18.0', '< 0.19.0'
|
20
|
+
|
21
|
+
s.add_development_dependency 'byebug', '>= 11.0.0', '< 12.0.0'
|
22
|
+
s.add_development_dependency 'rake', '>= 12.0.0', '< 13.0.0'
|
23
|
+
end
|
data/lib/ewelink/api.rb
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
module Ewelink
|
2
|
+
|
3
|
+
class Api
|
4
|
+
|
5
|
+
APP_ID = 'oeVkj2lYFGnJu5XUtWisfW4utiN4u9Mq'
|
6
|
+
APP_SECRET = '6Nz4n0xA8s8qdxQf2GqurZj2Fs55FUvM'
|
7
|
+
DEFAULT_REGION = 'us'
|
8
|
+
RF_BRIDGE_DEVICE_UIID = 28
|
9
|
+
SWITCH_DEVICES_UIIDS = [1, 5, 6, 24]
|
10
|
+
TIMEOUT = 10
|
11
|
+
URL = 'https://#{region}-api.coolkit.cc:8080'
|
12
|
+
UUID_NAMESPACE = 'e25750fb-3710-41af-b831-23224f4dd609';
|
13
|
+
VERSION = 8
|
14
|
+
|
15
|
+
attr_reader :email, :password, :phone_number
|
16
|
+
|
17
|
+
def initialize(email: nil, password:, phone_number: nil)
|
18
|
+
@email = email.presence.try(:strip)
|
19
|
+
@mutexs = {}
|
20
|
+
@password = password.presence || raise(Error.new(":password must be specified"))
|
21
|
+
@phone_number = phone_number.presence.try(:strip)
|
22
|
+
raise(Error.new(":email or :phone_number must be specified")) if email.blank? && phone_number.blank?
|
23
|
+
end
|
24
|
+
|
25
|
+
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
|
40
|
+
end
|
41
|
+
|
42
|
+
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|
|
45
|
+
remove_instance_variable(variable) if instance_variable_defined?(variable)
|
46
|
+
end
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
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
|
75
|
+
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)
|
90
|
+
end
|
91
|
+
|
92
|
+
def switch_on?(uuid)
|
93
|
+
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'
|
103
|
+
end
|
104
|
+
|
105
|
+
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)" } }
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def authentication_headers
|
126
|
+
{ 'Authorization' => "Bearer #{authentication_token}" }
|
127
|
+
end
|
128
|
+
|
129
|
+
def authentication_token
|
130
|
+
synchronize(:authentication_token) do
|
131
|
+
@authentication_token ||= begin
|
132
|
+
params = {
|
133
|
+
'appid' => APP_ID,
|
134
|
+
'imei' => SecureRandom.uuid.upcase,
|
135
|
+
'nonce' => nonce,
|
136
|
+
'password' => password,
|
137
|
+
'ts' => Time.now.to_i,
|
138
|
+
'version' => VERSION,
|
139
|
+
}
|
140
|
+
if email.present?
|
141
|
+
params['email'] = email
|
142
|
+
else
|
143
|
+
params['phoneNumber'] = phone_number
|
144
|
+
end
|
145
|
+
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))}" } })
|
147
|
+
raise(Error.new('Authentication token not found')) if response['at'].blank?
|
148
|
+
response['at'].tap { Ewelink.logger.debug(self.class.name) { 'Authentication token found' } }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def devices
|
154
|
+
synchronize(:devices) do
|
155
|
+
@devices ||= begin
|
156
|
+
params = {
|
157
|
+
'appid' => APP_ID,
|
158
|
+
'getTags' => 1,
|
159
|
+
'nonce' => nonce,
|
160
|
+
'ts' => Time.now.to_i,
|
161
|
+
'version' => VERSION,
|
162
|
+
}
|
163
|
+
response = http_request(:get, '/api/user/device', headers: authentication_headers, query: params)
|
164
|
+
response['devicelist'].tap { |devices| Ewelink.logger.debug(self.class.name) { "Found #{devices.size} device(s)" } }
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def find_rf_bridge_button!(uuid)
|
170
|
+
rf_bridge_buttons.find { |button| button[:uuid] == uuid } || raise(Error.new("No such RF bridge button with UUID: #{uuid.inspect}"))
|
171
|
+
end
|
172
|
+
|
173
|
+
def find_switch!(uuid)
|
174
|
+
switches.find { |switch| switch[:uuid] == uuid } || raise(Error.new("No such switch with UUID: #{uuid.inspect}"))
|
175
|
+
end
|
176
|
+
|
177
|
+
def nonce
|
178
|
+
SecureRandom.hex[0, 8]
|
179
|
+
end
|
180
|
+
|
181
|
+
def region
|
182
|
+
@region ||= DEFAULT_REGION
|
183
|
+
end
|
184
|
+
|
185
|
+
def http_request(method, path, options = {})
|
186
|
+
url = "#{URL.gsub('#{region}', region)}#{path}"
|
187
|
+
method = method.to_s.upcase
|
188
|
+
headers = (options[:headers] || {}).reverse_merge('Content-Type' => 'application/json')
|
189
|
+
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)) }
|
191
|
+
raise(Error.new("#{method} #{url}: #{response.code}")) unless response.success?
|
192
|
+
if response['error'] == 301 && response['region'].present?
|
193
|
+
@region = response['region']
|
194
|
+
Ewelink.logger.debug(self.class.name) { "Switched to region #{region.inspect}" }
|
195
|
+
return http_request(method, path, options)
|
196
|
+
end
|
197
|
+
remove_instance_variable(:@authentication_token) if instance_variable_defined?(:@authentication_token) && [401, 403].include?(response['error'])
|
198
|
+
raise(Error.new("#{method} #{url}: #{response['error']} #{response['msg']}".strip)) if response['error'].present? && response['error'] != 0
|
199
|
+
response
|
200
|
+
rescue Errno::ECONNREFUSED, OpenSSL::OpenSSLError, SocketError, Timeout::Error => e
|
201
|
+
raise Error.new(e)
|
202
|
+
end
|
203
|
+
|
204
|
+
def synchronize(name, &block)
|
205
|
+
(@mutexs[name] ||= Mutex.new).synchronize(&block)
|
206
|
+
end
|
207
|
+
|
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
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Ewelink
|
2
|
+
|
3
|
+
class Runner
|
4
|
+
|
5
|
+
def run
|
6
|
+
api = Api.new(options.slice(:email, :password, :phone_number))
|
7
|
+
puts(JSON.pretty_generate(api.switches)) if options[:list_switches]
|
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) }
|
11
|
+
options[:press_rf_bridge_buttons_uuids].each { |uuid| api.press_rf_bridge_button!(uuid) }
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def options
|
17
|
+
@options ||= begin
|
18
|
+
options = { press_rf_bridge_buttons_uuids: [], switches_off_uuids: [], switches_on_uuids: [] }
|
19
|
+
parser = OptionParser.new do |opts|
|
20
|
+
opts.banner = 'Manage eWeLink smart home devices'
|
21
|
+
opts.version = File.read(File.expand_path('../../VERSION', __dir__)).strip
|
22
|
+
opts.separator('')
|
23
|
+
opts.separator('Usage: ewelink [options]')
|
24
|
+
opts.separator('')
|
25
|
+
opts.on('-e', '--email EMAIL', "eWeLink account's email (mandatory if phone number is not specified)") do |email|
|
26
|
+
options[:email] = email
|
27
|
+
end
|
28
|
+
opts.on('-p', '--password PASSWORD', "eWeLink account's password (mandatory, prompted if not specified on command line)") do |password|
|
29
|
+
options[:password] = password
|
30
|
+
end
|
31
|
+
opts.on('-n', '--phone-number PHONE_NUMBER', "eWeLink account's phone number (mandatory if email is not specified)") do |phone_number|
|
32
|
+
options[:phone_number] = phone_number
|
33
|
+
end
|
34
|
+
opts.on('--list-switches', 'List all switches in JSON format') do
|
35
|
+
options[:list_switches] = true
|
36
|
+
end
|
37
|
+
opts.on('--list-rf-bridge-buttons', 'List all RF 433MHz bridge buttons in JSON format') do
|
38
|
+
options[:list_rf_bridge_buttons] = true
|
39
|
+
end
|
40
|
+
opts.on('--switch-on SWITCH_UUID', 'Set the switch with specified UUID on') do |uuid|
|
41
|
+
options[:switches_on_uuids] << uuid
|
42
|
+
end
|
43
|
+
opts.on('--switch-off SWITCH_UUID', 'Set the switch with specified UUID off') do |uuid|
|
44
|
+
options[:switches_off_uuids] << uuid
|
45
|
+
end
|
46
|
+
opts.on('--press-rf-bridge-button BUTTON_UUID', 'Press RF 433MHz bridge button with specified UUID') do |uuid|
|
47
|
+
options[:press_rf_bridge_buttons_uuids] << uuid
|
48
|
+
end
|
49
|
+
opts.on('-v', '--verbose', 'Verbose mode') do
|
50
|
+
Ewelink.logger.level = :debug
|
51
|
+
end
|
52
|
+
end
|
53
|
+
arguments = parser.parse!
|
54
|
+
if arguments.any?
|
55
|
+
STDERR.puts("Invalid option specified: #{arguments.first}")
|
56
|
+
STDERR.puts(parser.summarize)
|
57
|
+
exit(1)
|
58
|
+
end
|
59
|
+
if options[:email].blank? && options[:phone_number].blank?
|
60
|
+
STDERR.puts('Email or phone number must be specified')
|
61
|
+
STDERR.puts(parser.summarize)
|
62
|
+
exit(1)
|
63
|
+
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?)
|
65
|
+
STDERR.puts('An action must be specified (listing switches, press RF bridge button, etc.)')
|
66
|
+
STDERR.puts(parser.summarize)
|
67
|
+
exit(1)
|
68
|
+
end
|
69
|
+
while options[:password].blank?
|
70
|
+
options[:password] = IO::console.getpass("Enter eWeLink account's password: ")
|
71
|
+
end
|
72
|
+
options
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
data/lib/ewelink.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
require 'byebug' if ENV['DEBUGGER']
|
4
|
+
require 'httparty'
|
5
|
+
require 'io/console'
|
6
|
+
require 'json'
|
7
|
+
require 'logger'
|
8
|
+
require 'openssl'
|
9
|
+
require 'optparse'
|
10
|
+
|
11
|
+
module Ewelink
|
12
|
+
|
13
|
+
mattr_accessor :logger
|
14
|
+
self.logger = Logger.new(nil)
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
require_relative 'ewelink/api'
|
19
|
+
require_relative 'ewelink/error'
|
20
|
+
require_relative 'ewelink/runner'
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ewelink
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexis Toulotte
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-04-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.0.0
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 7.0.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 6.0.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 7.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: httparty
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 0.18.0
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.19.0
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 0.18.0
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.19.0
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: byebug
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 11.0.0
|
60
|
+
- - "<"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 12.0.0
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 11.0.0
|
70
|
+
- - "<"
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 12.0.0
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: rake
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 12.0.0
|
80
|
+
- - "<"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 13.0.0
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 12.0.0
|
90
|
+
- - "<"
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 13.0.0
|
93
|
+
description: Manage eWeLink smart home devices
|
94
|
+
email: al@alweb.org
|
95
|
+
executables:
|
96
|
+
- ewelink
|
97
|
+
extensions: []
|
98
|
+
extra_rdoc_files: []
|
99
|
+
files:
|
100
|
+
- README.mdown
|
101
|
+
- VERSION
|
102
|
+
- bin/ewelink
|
103
|
+
- ewelink.gemspec
|
104
|
+
- lib/ewelink.rb
|
105
|
+
- lib/ewelink/api.rb
|
106
|
+
- lib/ewelink/error.rb
|
107
|
+
- lib/ewelink/runner.rb
|
108
|
+
homepage: https://github.com/alexistoulotte/ewelink
|
109
|
+
licenses:
|
110
|
+
- MIT
|
111
|
+
metadata: {}
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 2.0.0
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubygems_version: 3.0.3
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: Manage eWeLink devices
|
131
|
+
test_files: []
|