atrea_control 1.2.1 → 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/.github/workflows/main.yml +1 -1
- data/.gitignore +1 -0
- data/CHANGELOG.md +24 -0
- data/Gemfile.lock +22 -15
- data/README.md +50 -16
- data/atrea_control.gemspec +16 -12
- data/config/locales/cs.yml +269 -0
- data/config/locales/de.yml +269 -0
- data/config/locales/en.yml +269 -0
- data/lib/atrea_control/duplex/login.rb +107 -0
- data/lib/atrea_control/duplex/request.rb +29 -0
- data/lib/atrea_control/duplex/unit.rb +93 -0
- data/lib/atrea_control/duplex/user_ctrl.rb +138 -0
- data/lib/atrea_control/duplex.rb +6 -197
- data/lib/atrea_control/logger.rb +11 -0
- data/lib/atrea_control/sensor_parser.rb +63 -0
- data/lib/atrea_control/version.rb +3 -1
- data/lib/atrea_control.rb +6 -11
- metadata +28 -8
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AtreaControl
|
4
|
+
module Duplex
|
5
|
+
# Communication with RD5 unit (read/write)
|
6
|
+
class Unit
|
7
|
+
include AtreaControl::Logger
|
8
|
+
|
9
|
+
attr_reader :current_mode, :current_power, :outdoor_temperature
|
10
|
+
# @return [DateTime] store time of last update
|
11
|
+
attr_reader :valid_for
|
12
|
+
# @return [UserCtrl]
|
13
|
+
attr_reader :user_ctrl
|
14
|
+
|
15
|
+
# @param [String, Integer] user_id
|
16
|
+
# @param [String, Integer] unit_id
|
17
|
+
# @param [String, Integer] sid
|
18
|
+
# @param [AtreaControl::Duplex::UserCtrl] user_ctrl
|
19
|
+
def initialize(user_id:, unit_id:, sid:, user_ctrl: nil)
|
20
|
+
@user_id = user_id
|
21
|
+
@unit_id = unit_id
|
22
|
+
@sid = sid
|
23
|
+
@user_ctrl = user_ctrl || UserCtrl.new(user_id: user_id, unit_id: unit_id, sid: sid)
|
24
|
+
end
|
25
|
+
|
26
|
+
def name
|
27
|
+
@user_ctrl.name
|
28
|
+
end
|
29
|
+
|
30
|
+
def mode
|
31
|
+
current_mode || values[:current_mode]
|
32
|
+
end
|
33
|
+
|
34
|
+
def power
|
35
|
+
current_power || values[:current_power]
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param [String] value 0 - power-off; 1 - automat
|
39
|
+
def mode=(value)
|
40
|
+
v = parser.input(@user_ctrl.sensors["mode_input"], value.to_s)
|
41
|
+
write(v)
|
42
|
+
end
|
43
|
+
|
44
|
+
def power=(value)
|
45
|
+
v = [parser.input(@user_ctrl.sensors["power_input"], value.to_s)]
|
46
|
+
v << parser.input(@user_ctrl.sensors["mode_switch"], "2")
|
47
|
+
write(v)
|
48
|
+
end
|
49
|
+
|
50
|
+
def values
|
51
|
+
parser.values(read.body).each do |name, value|
|
52
|
+
instance_variable_set :"@#{name}", value
|
53
|
+
end
|
54
|
+
as_json
|
55
|
+
end
|
56
|
+
|
57
|
+
def as_json(_options = nil)
|
58
|
+
{
|
59
|
+
current_mode: current_mode,
|
60
|
+
current_power: current_power,
|
61
|
+
outdoor_temperature: outdoor_temperature,
|
62
|
+
valid_for: valid_for,
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_json(*args)
|
67
|
+
values.to_json(*args)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def parser
|
73
|
+
@parser ||= ::AtreaControl::SensorParser.new(@user_ctrl)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Request to RD5
|
77
|
+
def request
|
78
|
+
@request ||= Request.new(user_id: @user_id, unit_id: @unit_id, sid: @sid)
|
79
|
+
end
|
80
|
+
|
81
|
+
def read
|
82
|
+
request.call(_t: "config/xml.xml")
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param [Array<String>] values in format SENSOR0000VALUE
|
86
|
+
def write(*values)
|
87
|
+
inputs = values.to_h { |i| [i, nil] }
|
88
|
+
logger.debug("set RD5 #{inputs.keys}")
|
89
|
+
request.call({ _t: "config/xml.cgi", _w: 1 }.merge(inputs))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
|
3
|
+
module AtreaControl
|
4
|
+
module Duplex
|
5
|
+
# Parse `userCtrl.xml`
|
6
|
+
class UserCtrl
|
7
|
+
# @param (see #initialize)
|
8
|
+
def self.data(user_id:, unit_id:, sid:)
|
9
|
+
user_ctrl = new(user_id: user_id, unit_id: unit_id, sid: sid)
|
10
|
+
{
|
11
|
+
name: user_ctrl.name,
|
12
|
+
sensors: user_ctrl.sensors,
|
13
|
+
modes: user_ctrl.modes,
|
14
|
+
user_modes: user_ctrl.user_modes,
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [String, Integer] user_id
|
19
|
+
# @param [String, Integer] unit_id
|
20
|
+
# @param [String, Integer] sid
|
21
|
+
def initialize(user_id:, unit_id:, sid:)
|
22
|
+
@user_id = user_id
|
23
|
+
@unit_id = unit_id
|
24
|
+
@sid = sid
|
25
|
+
end
|
26
|
+
|
27
|
+
# Request to RD5
|
28
|
+
def request
|
29
|
+
@request ||= Request.new(user_id: @user_id, unit_id: @unit_id, sid: @sid)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get and parse XML with user/unit configuration source
|
33
|
+
def user_ctrl
|
34
|
+
response = request.call(_t: "lang/userCtrl.xml")
|
35
|
+
Nokogiri::XML response.body
|
36
|
+
end
|
37
|
+
|
38
|
+
# User-defined name of home (RD5 unit)
|
39
|
+
# @return [String]
|
40
|
+
def name
|
41
|
+
@name ||= user_texts["UnitName"]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Gather ID of sensors based on userCtrl config file
|
45
|
+
def sensors
|
46
|
+
return @sensors if @sensors
|
47
|
+
|
48
|
+
power = user_ctrl.xpath("//body/content/i[@title='$currentPower']")
|
49
|
+
mode = user_ctrl.xpath("//body/content/i[@title='$currentMode']")
|
50
|
+
outdoor_temp = user_ctrl.xpath("//body/content/i[@title='$outdoorTemp']")
|
51
|
+
|
52
|
+
switch_mode = mode.xpath("displayval").text[/values\.(\w+)/, 1]
|
53
|
+
|
54
|
+
power_input = power.xpath("onchange").text[/getUrlPar\('(\w+)',val\)/, 1]
|
55
|
+
m = mode.xpath("onchange").text.match(/getUrlPar\('(\w+)',val\).*getUrlPar\(.(\w+).,2\)/)
|
56
|
+
|
57
|
+
@sensors = {
|
58
|
+
"outdoor_temperature" => outdoor_temp.attribute("id").value, # "I10208"
|
59
|
+
"current_power" => power.attribute("id").value, # "H10704"
|
60
|
+
"current_mode" => mode.attribute("id").value, # "H10705"
|
61
|
+
"current_mode_switch" => switch_mode, # "H10712"
|
62
|
+
"power_input" => power_input, # "H10708"
|
63
|
+
"mode_input" => m[1], # "H10709"
|
64
|
+
"mode_switch" => m[2], # "H10701"
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
# Generic modes
|
69
|
+
# @return [Hash]
|
70
|
+
def modes
|
71
|
+
return @modes if @modes
|
72
|
+
|
73
|
+
@modes = {}
|
74
|
+
user_ctrl.xpath("//op[@id='Mode']/i").each do |mode|
|
75
|
+
m = translate_mode(mode)
|
76
|
+
@modes[m[:id]] = m[:value]
|
77
|
+
end
|
78
|
+
@modes
|
79
|
+
end
|
80
|
+
|
81
|
+
# Modes by inputs - switches - named by user custom texts
|
82
|
+
# @return [Hash]
|
83
|
+
def user_modes
|
84
|
+
return @user_modes if @user_modes
|
85
|
+
|
86
|
+
@user_modes = {}
|
87
|
+
user_ctrl.xpath("//op[@id='ModeText']/i").each do |mode|
|
88
|
+
m = translate_mode(mode)
|
89
|
+
@user_modes[m[:id]] = m[:value]
|
90
|
+
end
|
91
|
+
@user_modes
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# User defined texts in RD5 unit
|
97
|
+
# @return [Hash]
|
98
|
+
def user_texts
|
99
|
+
return @user_texts if @user_texts
|
100
|
+
|
101
|
+
response = request.call(_t: "config/texts.xml")
|
102
|
+
xml = Nokogiri::XML response.body
|
103
|
+
@user_texts = xml.xpath("//i").to_h do |node|
|
104
|
+
value = node.attributes["value"].value
|
105
|
+
id = node.attributes["id"].value
|
106
|
+
[id, value.gsub(/%u([\dA-Z]{4})/) { |i| +"" << i[Regexp.last_match(1)].to_i(16) }]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param [Nokogiri::XML::Element] mode
|
111
|
+
# @return [Hash{Symbol->String}]
|
112
|
+
def translate_mode(mode)
|
113
|
+
id = mode.attributes["id"].value
|
114
|
+
title = mode.attributes["title"].value
|
115
|
+
title = if title.start_with?("$")
|
116
|
+
I18n.t(title[/\w+/])
|
117
|
+
else
|
118
|
+
user_texts[title]
|
119
|
+
end
|
120
|
+
{ id: id, value: title }
|
121
|
+
end
|
122
|
+
|
123
|
+
# Re-generate copy of locale files
|
124
|
+
# @note internal use only
|
125
|
+
def update_locales_files!
|
126
|
+
{ cs: "0", de: "1", en: "2" }.each do |name, atrea_id|
|
127
|
+
response = request.call(_t: "lang/texts_#{atrea_id}.xml")
|
128
|
+
|
129
|
+
xml = Nokogiri::XML response.body
|
130
|
+
# `eval` JS object (= hash)
|
131
|
+
locale = eval xml.xpath("//words/text()")[0].text
|
132
|
+
yaml = { name.to_s => JSON.parse(locale.to_json) }.to_yaml
|
133
|
+
File.write(File.expand_path("../../../config/locales/#{name}.yml", __dir__), yaml)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/lib/atrea_control/duplex.rb
CHANGED
@@ -1,206 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "logger"
|
4
|
-
require "nokogiri"
|
5
|
-
require "rest-client"
|
6
|
-
require "selenium-webdriver"
|
7
|
-
|
8
3
|
module AtreaControl
|
9
4
|
# Controller for +control.atrea.eu+
|
10
|
-
|
5
|
+
module Duplex
|
11
6
|
CONTROL_URI = "https://control.atrea.eu/"
|
7
|
+
CONTROL_VERSION = "003001009"
|
12
8
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# @param [String] login
|
18
|
-
# @param [String] password
|
19
|
-
# @param [Hash] sensors_map which box is related to sensor ID
|
20
|
-
# @option sensors_map [String] :outdoor_temperature
|
21
|
-
# @option sensors_map [String] :current_power
|
22
|
-
# @option sensors_map [String] :current_mode
|
23
|
-
def initialize(login:, password:, sensors_map: Config.default_sensors_map)
|
24
|
-
@login = login
|
25
|
-
@password = password
|
26
|
-
@sensors = sensors_map
|
27
|
-
end
|
28
|
-
|
29
|
-
# @return [Selenium::WebDriver::Firefox::Driver]
|
30
|
-
def driver
|
31
|
-
return @driver if defined?(@driver)
|
32
|
-
|
33
|
-
options = Selenium::WebDriver::Firefox::Options.new
|
34
|
-
options.headless! unless ENV["NO_HEADLESS"]
|
35
|
-
@driver ||= Selenium::WebDriver.for :firefox, options: options
|
36
|
-
end
|
37
|
-
|
38
|
-
def logged?
|
39
|
-
user&.[] "loged"
|
40
|
-
end
|
41
|
-
|
42
|
-
def login_in_progress?
|
43
|
-
@login_in_progress
|
44
|
-
end
|
45
|
-
|
46
|
-
# Login into control
|
47
|
-
def login
|
48
|
-
@login_in_progress = true
|
49
|
-
logger.debug "start new login"
|
50
|
-
driver.get CONTROL_URI
|
51
|
-
submit_login_form if user.nil? || !logged?
|
52
|
-
finish_login
|
53
|
-
@login_in_progress = false
|
54
|
-
inspect
|
55
|
-
end
|
56
|
-
|
57
|
-
# Submit given credentials and proceed login
|
58
|
-
def submit_login_form
|
59
|
-
form = driver.find_element(id: "loginFrm")
|
60
|
-
username = form.find_element(name: "username")
|
61
|
-
username.send_keys @login
|
62
|
-
password = form.find_element(name: "password")
|
63
|
-
password.send_keys @password
|
64
|
-
|
65
|
-
submit = form.find_element(css: "input[type=submit]")
|
66
|
-
submit.click
|
67
|
-
end
|
68
|
-
|
69
|
-
# Retrieve dashboard URI from object tag and open it again
|
70
|
-
def open_dashboard
|
71
|
-
uri = driver.find_element(tag_name: "object").attribute "data"
|
72
|
-
driver.get uri
|
73
|
-
logger.debug "#{name} login success"
|
74
|
-
end
|
75
|
-
|
76
|
-
# @return [String]
|
77
|
-
def name
|
78
|
-
return unless logged?
|
79
|
-
|
80
|
-
container = driver.find_element css: "div#pageTitle > h2"
|
81
|
-
container.text
|
82
|
-
end
|
83
|
-
|
84
|
-
# @return [String] ID of logged user
|
85
|
-
def user_id
|
86
|
-
@user_id ||= driver.execute_script("return window._user")
|
87
|
-
end
|
88
|
-
|
89
|
-
# @return [String] ID of recuperation unit
|
90
|
-
def unit_id
|
91
|
-
@unit_id ||= driver.execute_script("return window._unit")
|
92
|
-
end
|
93
|
-
|
94
|
-
# Window.user object from atrea
|
95
|
-
# @return [Hash, nil]
|
96
|
-
def user
|
97
|
-
driver.execute_script("return window.user")
|
98
|
-
end
|
99
|
-
|
100
|
-
# @return [String]
|
101
|
-
def current_mode_name
|
102
|
-
return current_mode unless logged?
|
103
|
-
|
104
|
-
element = sensor_element(@sensors[:current_mode])
|
105
|
-
element.find_element(css: "div:first-child").text
|
106
|
-
end
|
107
|
-
|
108
|
-
# quit selenium browser
|
109
|
-
def close
|
110
|
-
@user_auth = nil
|
111
|
-
driver.quit
|
112
|
-
remove_instance_variable :@driver
|
113
|
-
end
|
114
|
-
|
115
|
-
alias logout! close
|
116
|
-
|
117
|
-
def as_json(_options = nil)
|
118
|
-
{
|
119
|
-
logged: logged?,
|
120
|
-
current_mode: current_mode_name,
|
121
|
-
current_power: current_power,
|
122
|
-
outdoor_temperature: outdoor_temperature,
|
123
|
-
valid_for: valid_for,
|
124
|
-
}
|
125
|
-
end
|
126
|
-
|
127
|
-
alias values as_json
|
128
|
-
|
129
|
-
def to_json(*args)
|
130
|
-
as_json.to_json(*args)
|
131
|
-
end
|
132
|
-
|
133
|
-
def inspect
|
134
|
-
"<AtreaControl name: '#{name}' outdoor_temperature: '#{outdoor_temperature}°C' current_power: '#{current_power}%' current_mode: '#{current_mode_name}' valid_for: '#{valid_for}'>"
|
135
|
-
end
|
136
|
-
|
137
|
-
def call_unit!
|
138
|
-
return false if @login_in_progress
|
139
|
-
|
140
|
-
logger.debug "call_unit!"
|
141
|
-
parse_response(response_comm_unit)
|
142
|
-
@valid_for = Time.now
|
143
|
-
as_json
|
144
|
-
rescue RestClient::Forbidden
|
145
|
-
logger.debug "session expired..."
|
146
|
-
close if @logged
|
147
|
-
login && call_unit!
|
148
|
-
end
|
149
|
-
|
150
|
-
private
|
151
|
-
|
152
|
-
def parse_response(response)
|
153
|
-
xml = Nokogiri::XML response.body
|
154
|
-
sensors_values = @sensors.transform_values do |id|
|
155
|
-
xml.xpath("//O[@I=\"#{id}\"]/@V").last&.value
|
156
|
-
end
|
157
|
-
refresh_data(sensors_values)
|
158
|
-
end
|
159
|
-
|
160
|
-
# @param [Hash] values
|
161
|
-
# @return [Hash]
|
162
|
-
def refresh_data(values)
|
163
|
-
@outdoor_temperature = values[:outdoor_temperature].to_f / 10
|
164
|
-
@current_power = values[:current_power].to_f
|
165
|
-
@current_mode = mode_map[values[:current_mode]]
|
166
|
-
|
167
|
-
as_json
|
168
|
-
end
|
169
|
-
|
170
|
-
# ? I10204 ?
|
171
|
-
def mode_map
|
172
|
-
{ "0" => "Vypnuto", "1" => "Automat", "2" => "Větrání", "6" => "Rozvážení" }
|
173
|
-
end
|
174
|
-
|
175
|
-
def logger
|
176
|
-
@logger ||= ::Logger.new($stdout)
|
177
|
-
end
|
178
|
-
|
179
|
-
def finish_login
|
180
|
-
13.times do |i|
|
181
|
-
return true if open_dashboard
|
182
|
-
rescue Selenium::WebDriver::Error::NoSuchElementError => _e
|
183
|
-
t = [3 * (1 + i), 25].min
|
184
|
-
logger.debug "waiting #{t}s for login..."
|
185
|
-
sleep t
|
186
|
-
end
|
187
|
-
raise Error, "unable to login"
|
188
|
-
end
|
189
|
-
|
190
|
-
def sensor_element(sensor_id)
|
191
|
-
driver.find_element id: "contentBox#{sensor_id}"
|
192
|
-
end
|
9
|
+
autoload :Login, "atrea_control/duplex/login"
|
10
|
+
autoload :Request, "atrea_control/duplex/request"
|
11
|
+
autoload :Unit, "atrea_control/duplex/unit"
|
12
|
+
autoload :UserCtrl, "atrea_control/duplex/user_ctrl"
|
193
13
|
|
194
|
-
# @return [RestClient::Response]
|
195
|
-
def response_comm_unit
|
196
|
-
params = {
|
197
|
-
_user: user_id,
|
198
|
-
_unit: unit_id,
|
199
|
-
auth: user&.[]("auth"),
|
200
|
-
_t: "config/xml.xml",
|
201
|
-
}
|
202
|
-
autologin_token = CGI.escape([@login, @password].join("\b"))
|
203
|
-
RestClient.get "https://control.atrea.eu/comm/sw/unit.php", { Cookie: "autoLogin=#{autologin_token}", params: params }
|
204
|
-
end
|
205
14
|
end
|
206
15
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "nokogiri"
|
4
|
+
|
5
|
+
module AtreaControl
|
6
|
+
# Call RD5 unit ang get current sensors values
|
7
|
+
# parse it and return Hash
|
8
|
+
class SensorParser
|
9
|
+
# @param [AtreaControl::Duplex::UserCtrl] user_ctrl
|
10
|
+
def initialize(user_ctrl)
|
11
|
+
@user_ctrl = user_ctrl
|
12
|
+
end
|
13
|
+
|
14
|
+
def values(xml)
|
15
|
+
format_data(parse(xml))
|
16
|
+
end
|
17
|
+
|
18
|
+
def input(sensor, value)
|
19
|
+
sensor + value.to_s.rjust(5, "0")
|
20
|
+
end
|
21
|
+
|
22
|
+
# @see scripts.php -> loadRD5Values(node, init)
|
23
|
+
# @note
|
24
|
+
# if(values[key]>32767) values[key]-=65536;
|
25
|
+
# if(params[key] && params[key].offset)
|
26
|
+
# values[key]=values[key]-params[key].offset;
|
27
|
+
# if(params[key] && params[key].coef)
|
28
|
+
# values[key]=values[key]/params[key].coef;
|
29
|
+
def parse(xml)
|
30
|
+
xml = Nokogiri::XML xml
|
31
|
+
@user_ctrl.sensors.transform_values do |id|
|
32
|
+
value = xml.xpath("//O[@I=\"#{id}\"]/@V").last&.value.to_i
|
33
|
+
value -= 65_536 if value > 32_767
|
34
|
+
# value -= 0 if "offset"
|
35
|
+
# value -= 0 if "coef"
|
36
|
+
value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param [Hash] values
|
41
|
+
# @return [Hash]
|
42
|
+
def format_data(values)
|
43
|
+
{
|
44
|
+
"current_mode" => parse_current_mode(values),
|
45
|
+
"current_power" => values["current_power"].to_f,
|
46
|
+
"outdoor_temperature" => values["outdoor_temperature"].to_f / 10.0,
|
47
|
+
"valid_for" => Time.now,
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# `current_mode_switch` = mode trigger by wall switch or something similar
|
54
|
+
# `current_mode` = is common "builtin" mode
|
55
|
+
def parse_current_mode(values)
|
56
|
+
if values['current_mode_switch'].positive?
|
57
|
+
@user_ctrl.user_modes[values['current_mode_switch'].to_s]
|
58
|
+
else
|
59
|
+
@user_ctrl.modes[values['current_mode'].to_s]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/atrea_control.rb
CHANGED
@@ -1,22 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "atrea_control/version"
|
4
|
+
require "i18n"
|
5
|
+
require "yaml"
|
4
6
|
|
5
7
|
module AtreaControl
|
6
8
|
class Error < StandardError; end
|
7
9
|
|
8
10
|
autoload :Duplex, "atrea_control/duplex"
|
11
|
+
autoload :Logger, "atrea_control/logger"
|
12
|
+
autoload :SensorParser, "atrea_control/sensor_parser"
|
9
13
|
|
10
|
-
#
|
11
|
-
|
12
|
-
module_function
|
14
|
+
I18n.load_path.concat Dir["#{File.expand_path("../config/locales", __dir__)}/*.yml"]
|
15
|
+
I18n.default_locale = :cs
|
13
16
|
|
14
|
-
def default_sensors_map
|
15
|
-
{
|
16
|
-
outdoor_temperature: "I10208",
|
17
|
-
current_power: "H10704",
|
18
|
-
current_mode: "H10705",
|
19
|
-
}
|
20
|
-
end
|
21
|
-
end
|
22
17
|
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: atrea_control
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lukáš Pokorný
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: i18n
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.8'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: nokogiri
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +58,14 @@ dependencies:
|
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '4.1'
|
48
62
|
type: :runtime
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '4.1'
|
55
69
|
description: Read data from web controller of RD5 duplex by Atrea.
|
56
70
|
email:
|
57
71
|
- pokorny@luk4s.cz
|
@@ -73,16 +87,22 @@ files:
|
|
73
87
|
- atrea_control.gemspec
|
74
88
|
- bin/console
|
75
89
|
- bin/setup
|
90
|
+
- config/locales/cs.yml
|
91
|
+
- config/locales/de.yml
|
92
|
+
- config/locales/en.yml
|
76
93
|
- lib/atrea_control.rb
|
77
94
|
- lib/atrea_control/duplex.rb
|
95
|
+
- lib/atrea_control/duplex/login.rb
|
96
|
+
- lib/atrea_control/duplex/request.rb
|
97
|
+
- lib/atrea_control/duplex/unit.rb
|
98
|
+
- lib/atrea_control/duplex/user_ctrl.rb
|
99
|
+
- lib/atrea_control/logger.rb
|
100
|
+
- lib/atrea_control/sensor_parser.rb
|
78
101
|
- lib/atrea_control/version.rb
|
79
102
|
homepage: https://github.com/luk4s/atrea_control
|
80
103
|
licenses: []
|
81
104
|
metadata:
|
82
|
-
|
83
|
-
homepage_uri: https://github.com/luk4s/atrea_control
|
84
|
-
source_code_uri: https://github.com/luk4s/atrea_control
|
85
|
-
changelog_uri: https://github.com/luk4s/atrea_control/CHANGELOG.md
|
105
|
+
rubygems_mfa_required: 'true'
|
86
106
|
post_install_message:
|
87
107
|
rdoc_options: []
|
88
108
|
require_paths:
|