ewelink 3.3.2 → 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 +4 -4
- data/VERSION +1 -1
- data/bin/ewelink +1 -1
- data/ewelink.gemspec +7 -5
- data/lib/ewelink/api.rb +37 -40
- data/lib/ewelink/runner.rb +9 -11
- metadata +52 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8e686b44abcc1c59621d612a64b930f89a0041ac4607070bd47c801f04148ac
|
4
|
+
data.tar.gz: 3990a53b5d57e0c0523ba83050e7af904dd54a96337692a5fc800e6e3d0edaa9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41aaa80ba63866bf04728c60489ed91b3b8294a61dc7e7d78779628b7530006bd41d306adb4e6a72ccdb1e72ed8322dd94efe897053ed02ce1da977e7560319a
|
7
|
+
data.tar.gz: c6af766c67c28efffd27a4186c3b603e9c4d3ab48e5fb8bc8ca1b52b5ce7aec8969297b932134c3c640ca5ece581ab70c6932ffb8a5597ebb54cc831b9bbfb98
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
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(
|
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,17 +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 =
|
13
|
-
s.executables =
|
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 = '>=
|
16
|
+
s.required_ruby_version = '>= 3.1.0'
|
17
17
|
|
18
|
-
s.add_dependency 'activesupport', '>=
|
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.
|
20
|
+
s.add_dependency 'httparty', '>= 0.20.0', '< 0.21.0'
|
21
21
|
s.add_dependency 'thread', '>= 0.2.0', '< 0.3.0'
|
22
22
|
|
23
23
|
s.add_development_dependency 'byebug', '>= 11.0.0', '< 12.0.0'
|
24
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'
|
25
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,11 +17,11 @@ module Ewelink
|
|
18
17
|
|
19
18
|
attr_reader :email, :password, :phone_number
|
20
19
|
|
21
|
-
def initialize(async_actions: false, email: nil,
|
20
|
+
def initialize(password:, async_actions: false, email: nil, phone_number: nil, update_devices_status_on_connect: false)
|
22
21
|
@async_actions = async_actions.present?
|
23
22
|
@email = email.presence.try(:strip)
|
24
23
|
@mutexs = {}
|
25
|
-
@password = password.presence || raise(Error.new(
|
24
|
+
@password = password.presence || raise(Error.new(':password must be specified'))
|
26
25
|
@phone_number = phone_number.presence.try(:strip)
|
27
26
|
@update_devices_status_on_connect = update_devices_status_on_connect.present?
|
28
27
|
@web_socket_authenticated = false
|
@@ -85,19 +84,19 @@ module Ewelink
|
|
85
84
|
end
|
86
85
|
end
|
87
86
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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|
|
101
100
|
remove_instance_variable(variable) if instance_variable_defined?(variable)
|
102
101
|
end
|
103
102
|
self
|
@@ -115,10 +114,10 @@ module Ewelink
|
|
115
114
|
device_name = device['name'].presence || next
|
116
115
|
buttons = device['params']['rfList'].each do |rf|
|
117
116
|
button = {
|
118
|
-
api_key
|
117
|
+
api_key:,
|
119
118
|
channel: rf['rfChl'],
|
120
|
-
device_id
|
121
|
-
device_name
|
119
|
+
device_id:,
|
120
|
+
device_name:,
|
122
121
|
}
|
123
122
|
remote_info = device['tags']['zyx_info'].find { |info| info['buttonName'].find { |data| data.key?(button[:channel].to_s) } }.presence || next
|
124
123
|
remote_name = remote_info['name'].try(:squish).presence || next
|
@@ -126,10 +125,10 @@ module Ewelink
|
|
126
125
|
button_name = button_info.values.first.try(:squish).presence || next
|
127
126
|
button.merge!({
|
128
127
|
name: button_name,
|
129
|
-
remote_name
|
128
|
+
remote_name:,
|
130
129
|
remote_type: remote_info['remote_type'],
|
131
130
|
})
|
132
|
-
button[:uuid] = Digest::UUID.uuid_v5(
|
131
|
+
button[:uuid] = Digest::UUID.uuid_v5(Digest::UUID::DNS_NAMESPACE, "#{button[:device_id]}/#{button[:channel]}")
|
133
132
|
buttons << button
|
134
133
|
end
|
135
134
|
end
|
@@ -167,11 +166,11 @@ module Ewelink
|
|
167
166
|
device_id = device['deviceid'].presence || next
|
168
167
|
name = device['name'].presence || next
|
169
168
|
switch = {
|
170
|
-
api_key
|
171
|
-
device_id
|
172
|
-
name
|
169
|
+
api_key:,
|
170
|
+
device_id:,
|
171
|
+
name:,
|
173
172
|
}
|
174
|
-
switch[:uuid] = Digest::UUID.uuid_v5(
|
173
|
+
switch[:uuid] = Digest::UUID.uuid_v5(Digest::UUID::DNS_NAMESPACE, switch[:device_id])
|
175
174
|
switches << switch
|
176
175
|
end
|
177
176
|
end.tap { |switches| Ewelink.logger.debug(self.class.name) { "Found #{switches.size} switch(es)" } }
|
@@ -255,7 +254,7 @@ module Ewelink
|
|
255
254
|
params['phoneNumber'] = phone_number
|
256
255
|
end
|
257
256
|
body = JSON.generate(params)
|
258
|
-
response = rest_request(:post, '/api/user/login', { body
|
257
|
+
response = rest_request(:post, '/api/user/login', { body:, headers: { 'Authorization' => "Sign #{Base64.encode64(OpenSSL::HMAC.digest('SHA256', APP_SECRET, body))}" } })
|
259
258
|
raise(Error.new('Authentication token not found')) if response['at'].blank?
|
260
259
|
raise(Error.new('API key not found')) if response['user'].blank? || response['user']['apikey'].blank?
|
261
260
|
{
|
@@ -314,7 +313,7 @@ module Ewelink
|
|
314
313
|
method = method.to_s.upcase
|
315
314
|
headers = (options[:headers] || {}).reverse_merge('Content-Type' => 'application/json')
|
316
315
|
Ewelink.logger.debug(self.class.name) { "#{method} #{url}" }
|
317
|
-
response = HTTParty.send(method.downcase, url, options.merge(headers:
|
316
|
+
response = HTTParty.send(method.downcase, url, options.merge(headers:).reverse_merge(timeout: REQUEST_TIMEOUT))
|
318
317
|
raise(Error.new("#{method} #{url}: #{response.code}")) unless response.success?
|
319
318
|
if response['error'] == 301 && response['region'].present?
|
320
319
|
@region = response['region']
|
@@ -391,12 +390,12 @@ module Ewelink
|
|
391
390
|
EventMachine.run do
|
392
391
|
@web_socket = Faye::WebSocket::Client.new(web_socket_url)
|
393
392
|
|
394
|
-
@web_socket.on(:close) do
|
393
|
+
@web_socket.on(:close) do
|
395
394
|
Ewelink.logger.debug(self.class.name) { 'WebSocket closed' }
|
396
395
|
reload
|
397
396
|
end
|
398
397
|
|
399
|
-
@web_socket.on(:open) do
|
398
|
+
@web_socket.on(:open) do
|
400
399
|
Ewelink.logger.debug(self.class.name) { 'WebSocket opened' }
|
401
400
|
@last_web_socket_pong_at = Time.now
|
402
401
|
authenticate_web_socket_api_key
|
@@ -413,7 +412,7 @@ module Ewelink
|
|
413
412
|
|
414
413
|
begin
|
415
414
|
json = JSON.parse(message)
|
416
|
-
rescue
|
415
|
+
rescue
|
417
416
|
Ewelink.logger.error(self.class.name) { 'WebSocket JSON parse error' }
|
418
417
|
reload
|
419
418
|
next
|
@@ -436,7 +435,7 @@ module Ewelink
|
|
436
435
|
end
|
437
436
|
|
438
437
|
if json['deviceid'].present? && json['params'].is_a?(Hash) && json['params']['switch'].present?
|
439
|
-
switch = switches.find { |
|
438
|
+
switch = switches.find { |item| item[:device_id] == json['deviceid'] }
|
440
439
|
if switch.present?
|
441
440
|
@web_socket_switches_statuses[switch[:uuid]] = json['params']['switch']
|
442
441
|
Ewelink.logger.debug(self.class.name) { "Switch #{switch[:uuid].inspect} is #{@web_socket_switches_statuses[switch[:uuid]]}" }
|
@@ -483,13 +482,11 @@ module Ewelink
|
|
483
482
|
end
|
484
483
|
end
|
485
484
|
|
486
|
-
def web_socket_wait_for(condition, initialize_web_socket: false
|
485
|
+
def web_socket_wait_for(condition, initialize_web_socket: false)
|
487
486
|
web_socket if initialize_web_socket
|
488
487
|
begin
|
489
488
|
Timeout.timeout(REQUEST_TIMEOUT) do
|
490
|
-
|
491
|
-
sleep(WEB_SOCKET_WAIT_INTERVAL)
|
492
|
-
end
|
489
|
+
sleep(WEB_SOCKET_WAIT_INTERVAL) until condition.call
|
493
490
|
block_given? ? yield : true
|
494
491
|
end
|
495
492
|
rescue => e
|
data/lib/ewelink/runner.rb
CHANGED
@@ -9,7 +9,7 @@ module Ewelink
|
|
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].
|
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
|
-
|
60
|
-
|
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
|
-
|
65
|
-
|
64
|
+
warn('Email or phone number must be specified')
|
65
|
+
warn(parser.summarize)
|
66
66
|
exit(1)
|
67
67
|
end
|
68
|
-
if
|
69
|
-
|
70
|
-
|
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
|
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: 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:
|
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:
|
19
|
+
version: 7.0.0
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
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:
|
29
|
+
version: 7.0.0
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
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,20 @@ dependencies:
|
|
56
56
|
requirements:
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: 0.
|
59
|
+
version: 0.20.0
|
60
60
|
- - "<"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: 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.
|
69
|
+
version: 0.20.0
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version: 0.
|
72
|
+
version: 0.21.0
|
73
73
|
- !ruby/object:Gem::Dependency
|
74
74
|
name: thread
|
75
75
|
requirement: !ruby/object:Gem::Requirement
|
@@ -130,6 +130,46 @@ dependencies:
|
|
130
130
|
- - "<"
|
131
131
|
- !ruby/object:Gem::Version
|
132
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
|
143
|
+
type: :development
|
144
|
+
prerelease: false
|
145
|
+
version_requirements: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 1.25.0
|
150
|
+
- - "<"
|
151
|
+
- !ruby/object:Gem::Version
|
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
|
133
173
|
description: Manage eWeLink smart home devices
|
134
174
|
email: al@alweb.org
|
135
175
|
executables:
|
@@ -157,14 +197,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
157
197
|
requirements:
|
158
198
|
- - ">="
|
159
199
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
200
|
+
version: 3.1.0
|
161
201
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
202
|
requirements:
|
163
203
|
- - ">="
|
164
204
|
- !ruby/object:Gem::Version
|
165
205
|
version: '0'
|
166
206
|
requirements: []
|
167
|
-
rubygems_version: 3.
|
207
|
+
rubygems_version: 3.3.3
|
168
208
|
signing_key:
|
169
209
|
specification_version: 4
|
170
210
|
summary: Manage eWeLink devices
|