atrea_control 2.3.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd2ed6c46c2b1c7caef1d39ee4ed6e67971265273b1f985ba522876c6aaaa991
4
- data.tar.gz: d076c8e9079314311694dc98429bad429a4b49e55456bf44699fb2ec33d7101e
3
+ metadata.gz: 919ab4f4ed1dde112816e58cecfb4ffc393cb539cecf81796a2490219683c75d
4
+ data.tar.gz: 7b450714f08130ad30ad565c5ce04b22b29435c43cd8230dbf10103a04c9e624
5
5
  SHA512:
6
- metadata.gz: b7fc76c48e32e116d94135d23073df680f33ad30bc6637ee596a4762b61ed57f00a8af418a8e7e6a29ffb92d2daa11aecc67e4de2353b31681670de802e06bd2
7
- data.tar.gz: e2ff402d8e723d43c5baec15a694ee941ba3f8d5f96c31c3e30b080c03d0090209456b7e55b71085d89dd9b355840e4bf1413921e502ddef984edc4d6ad99000
6
+ metadata.gz: 360fc2c895cc21dabffd4450a5013899263077b7a9f5c968c7046584728e457f795e8a159dc714021ac2dfaed2b5fe3792debd67c88c52810144d83bea792cff
7
+ data.tar.gz: bfdd992e729adccf960aee9d26e9088ef9fe7beb20f1067396099248659c187e079f303504e04fefb7bdac9dee0dd0249f01c053b39966cef67f55c2da2ada69
@@ -1,17 +1,19 @@
1
1
  name: Ruby
2
2
 
3
- on: [push,pull_request]
3
+ on: [push, pull_request]
4
4
 
5
5
  jobs:
6
6
  spec:
7
7
  runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby-version: [3.2, 3.3, 3]
8
11
  steps:
9
12
  - uses: actions/checkout@v4
10
13
  - name: Set up Ruby
11
14
  uses: ruby/setup-ruby@v1
12
15
  with:
13
- ruby-version: 3.2.2
16
+ ruby-version: ${{ matrix.ruby-version }}
14
17
  bundler-cache: true
15
- - uses: browser-actions/setup-firefox@latest
16
18
  - name: Run tests
17
- run: bundle exec rspec
19
+ run: bundle exec rspec
data/.gitignore CHANGED
@@ -11,3 +11,4 @@
11
11
 
12
12
  # rspec failure tracking
13
13
  .rspec_status
14
+ .env
data/.rubocop.yml CHANGED
@@ -5,7 +5,7 @@ require:
5
5
  AllCops:
6
6
  NewCops: enable
7
7
  SuggestExtensions: false
8
- TargetRubyVersion: 3.2
8
+ TargetRubyVersion: 3.3
9
9
 
10
10
  Style/StringLiterals:
11
11
  Enabled: true
data/.rubocop_todo.yml CHANGED
@@ -1,17 +1,24 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2023-03-01 20:32:36 UTC using RuboCop version 1.47.0.
3
+ # on 2025-01-24 16:50:34 UTC using RuboCop version 1.70.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 3
9
+ # Offense count: 1
10
+ # Configuration parameters: Severity, Include.
11
+ # Include: **/*.gemspec
12
+ Gemspec/RequiredRubyVersion:
13
+ Exclude:
14
+ - 'atrea_control.gemspec'
15
+
16
+ # Offense count: 5
10
17
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
11
18
  Metrics/AbcSize:
12
19
  Max: 34
13
20
 
14
- # Offense count: 2
21
+ # Offense count: 3
15
22
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
16
23
  Metrics/MethodLength:
17
24
  Max: 21
@@ -28,28 +35,30 @@ RSpec/ExpectInHook:
28
35
  Exclude:
29
36
  - 'spec/atrea_control/duplex/unit_spec.rb'
30
37
 
31
- # Offense count: 7
32
- # Configuration parameters: EnforcedStyle.
38
+ # Offense count: 4
39
+ # Configuration parameters: .
33
40
  # SupportedStyles: have_received, receive
34
41
  RSpec/MessageSpies:
35
- Exclude:
36
- - 'spec/atrea_control/duplex/login_spec.rb'
37
- - 'spec/atrea_control/duplex/unit_spec.rb'
42
+ EnforcedStyle: receive
38
43
 
39
- # Offense count: 2
44
+ # Offense count: 1
40
45
  RSpec/MultipleExpectations:
41
46
  Max: 3
42
47
 
43
- # Offense count: 3
48
+ # Offense count: 4
49
+ # This cop supports unsafe autocorrection (--autocorrect-all).
50
+ RSpec/ReceiveMessages:
51
+ Exclude:
52
+ - 'spec/atrea_control/duplex/unit_spec.rb'
53
+
54
+ # Offense count: 1
44
55
  RSpec/StubbedMock:
45
56
  Exclude:
46
- - 'spec/atrea_control/duplex/login_spec.rb'
47
57
  - 'spec/atrea_control/duplex/unit_spec.rb'
48
58
 
49
- # Offense count: 6
59
+ # Offense count: 9
50
60
  RSpec/SubjectStub:
51
61
  Exclude:
52
- - 'spec/atrea_control/duplex/login_spec.rb'
53
62
  - 'spec/atrea_control/duplex/unit_spec.rb'
54
63
  - 'spec/atrea_control/duplex/user_ctrl_spec.rb'
55
64
 
@@ -57,7 +66,6 @@ RSpec/SubjectStub:
57
66
  # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
58
67
  RSpec/VerifiedDoubles:
59
68
  Exclude:
60
- - 'spec/atrea_control/duplex/login_spec.rb'
61
69
  - 'spec/atrea_control/duplex/unit_spec.rb'
62
70
  - 'spec/atrea_control/duplex/user_ctrl_spec.rb'
63
71
 
@@ -66,7 +74,7 @@ Security/Eval:
66
74
  Exclude:
67
75
  - 'lib/atrea_control/duplex/user_ctrl.rb'
68
76
 
69
- # Offense count: 2
77
+ # Offense count: 3
70
78
  # Configuration parameters: AllowedConstants.
71
79
  Style/Documentation:
72
80
  Exclude:
@@ -74,3 +82,10 @@ Style/Documentation:
74
82
  - 'test/**/*'
75
83
  - 'lib/atrea_control.rb'
76
84
  - 'lib/atrea_control/logger.rb'
85
+
86
+ # Offense count: 9
87
+ # This cop supports safe autocorrection (--autocorrect).
88
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
89
+ # URISchemes: http, https
90
+ Layout/LineLength:
91
+ Max: 238
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.4
1
+ 3.3.6
data/CHANGELOG.md CHANGED
@@ -1,4 +1,14 @@
1
1
  ## [Unreleased]
2
+ ## [3.0.0] - 2025-01-25
3
+ ### Removed
4
+ - selenium-based login procedure
5
+ ### Added
6
+ - token / phpsessionid-based login in background
7
+ - "session" validity check & expiration (exception)
8
+ ### Changed
9
+ - ruby version 3.3+
10
+ -
11
+ ## [2.2.0] - 2024-10-20
2
12
  ### Added
3
13
  - timestamp from atrea server
4
14
  ### Changed
data/Gemfile.lock CHANGED
@@ -1,11 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- atrea_control (2.3.0)
4
+ atrea_control (3.0.0)
5
5
  i18n (~> 1.14)
6
6
  nokogiri (~> 1.15)
7
7
  rest-client (~> 2.1)
8
- selenium-webdriver (~> 4.25.0)
9
8
 
10
9
  GEM
11
10
  remote: https://rubygems.org/
@@ -13,55 +12,54 @@ GEM
13
12
  addressable (2.8.7)
14
13
  public_suffix (>= 2.0.2, < 7.0)
15
14
  ast (2.4.2)
16
- base64 (0.2.0)
17
- bigdecimal (3.1.8)
15
+ bigdecimal (3.1.9)
18
16
  coderay (1.1.3)
19
- concurrent-ruby (1.3.4)
17
+ concurrent-ruby (1.3.5)
20
18
  crack (1.0.0)
21
19
  bigdecimal
22
20
  rexml
23
21
  diff-lcs (1.5.1)
24
22
  docile (1.4.1)
25
23
  domain_name (0.6.20240107)
26
- hashdiff (1.1.1)
24
+ hashdiff (1.1.2)
27
25
  http-accept (1.7.0)
28
- http-cookie (1.0.7)
26
+ http-cookie (1.0.8)
29
27
  domain_name (~> 0.5)
30
- i18n (1.14.6)
28
+ i18n (1.14.7)
31
29
  concurrent-ruby (~> 1.0)
32
- json (2.7.2)
30
+ json (2.9.1)
33
31
  language_server-protocol (3.17.0.3)
34
- logger (1.6.1)
32
+ logger (1.6.5)
35
33
  method_source (1.1.0)
36
34
  mime-types (3.6.0)
37
35
  logger
38
36
  mime-types-data (~> 3.2015)
39
- mime-types-data (3.2024.1001)
37
+ mime-types-data (3.2025.0107)
40
38
  netrc (0.11.0)
41
- nokogiri (1.16.7-arm64-darwin)
39
+ nokogiri (1.18.2-arm64-darwin)
42
40
  racc (~> 1.4)
43
- nokogiri (1.16.7-x86_64-darwin)
41
+ nokogiri (1.18.2-x86_64-darwin)
44
42
  racc (~> 1.4)
45
- nokogiri (1.16.7-x86_64-linux)
43
+ nokogiri (1.18.2-x86_64-linux-gnu)
46
44
  racc (~> 1.4)
47
45
  parallel (1.26.3)
48
- parser (3.3.5.0)
46
+ parser (3.3.7.0)
49
47
  ast (~> 2.4.1)
50
48
  racc
51
- pry (0.14.2)
49
+ pry (0.15.2)
52
50
  coderay (~> 1.1)
53
51
  method_source (~> 1.0)
54
52
  public_suffix (6.0.1)
55
53
  racc (1.8.1)
56
54
  rainbow (3.1.1)
57
55
  rake (13.2.1)
58
- regexp_parser (2.9.2)
56
+ regexp_parser (2.10.0)
59
57
  rest-client (2.1.0)
60
58
  http-accept (>= 1.7.0, < 2.0)
61
59
  http-cookie (>= 1.0.2, < 2.0)
62
60
  mime-types (>= 1.16, < 4.0)
63
61
  netrc (~> 0.8)
64
- rexml (3.3.8)
62
+ rexml (3.4.0)
65
63
  rspec (3.13.0)
66
64
  rspec-core (~> 3.13.0)
67
65
  rspec-expectations (~> 3.13.0)
@@ -74,45 +72,40 @@ GEM
74
72
  rspec-mocks (3.13.2)
75
73
  diff-lcs (>= 1.2.0, < 2.0)
76
74
  rspec-support (~> 3.13.0)
77
- rspec-support (3.13.1)
78
- rubocop (1.67.0)
75
+ rspec-support (3.13.2)
76
+ rubocop (1.70.0)
79
77
  json (~> 2.3)
80
78
  language_server-protocol (>= 3.17.0)
81
79
  parallel (~> 1.10)
82
80
  parser (>= 3.3.0.2)
83
81
  rainbow (>= 2.2.2, < 4.0)
84
- regexp_parser (>= 2.4, < 3.0)
85
- rubocop-ast (>= 1.32.2, < 2.0)
82
+ regexp_parser (>= 2.9.3, < 3.0)
83
+ rubocop-ast (>= 1.36.2, < 2.0)
86
84
  ruby-progressbar (~> 1.7)
87
- unicode-display_width (>= 2.4.0, < 3.0)
88
- rubocop-ast (1.32.3)
85
+ unicode-display_width (>= 2.4.0, < 4.0)
86
+ rubocop-ast (1.37.0)
89
87
  parser (>= 3.3.1.0)
90
- rubocop-rspec (3.1.0)
88
+ rubocop-rspec (3.4.0)
91
89
  rubocop (~> 1.61)
92
90
  ruby-progressbar (1.13.0)
93
- rubyzip (2.3.2)
94
- selenium-webdriver (4.25.0)
95
- base64 (~> 0.2)
96
- logger (~> 1.4)
97
- rexml (~> 3.2, >= 3.2.5)
98
- rubyzip (>= 1.2.2, < 3.0)
99
- websocket (~> 1.0)
100
91
  simplecov (0.22.0)
101
92
  docile (~> 1.1)
102
93
  simplecov-html (~> 0.11)
103
94
  simplecov_json_formatter (~> 0.1)
104
95
  simplecov-html (0.13.1)
105
96
  simplecov_json_formatter (0.1.4)
106
- unicode-display_width (2.6.0)
97
+ unicode-display_width (3.1.4)
98
+ unicode-emoji (~> 4.0, >= 4.0.4)
99
+ unicode-emoji (4.0.4)
107
100
  webmock (3.24.0)
108
101
  addressable (>= 2.8.0)
109
102
  crack (>= 0.3.2)
110
103
  hashdiff (>= 0.4.0, < 2.0.0)
111
- websocket (1.2.11)
112
104
 
113
105
  PLATFORMS
114
106
  arm64-darwin-22
115
107
  arm64-darwin-23
108
+ arm64-darwin-24
116
109
  x86_64-darwin-20
117
110
  x86_64-linux
118
111
 
@@ -127,4 +120,4 @@ DEPENDENCIES
127
120
  webmock (~> 3.14)
128
121
 
129
122
  BUNDLED WITH
130
- 2.5.17
123
+ 2.6.3
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # atrea_control
2
2
  Ventilation systems by https://www.atrea.eu are build with web UI portal - but this portal did not provide any API interface...
3
3
 
4
- This gem provide simple DSL by parsing content of https://control.atrea.eu with selenium webdriver.
4
+ This gem provide way how to connect to portal and obtain data from unit as API.
5
5
 
6
6
  ## Highlights
7
7
 
@@ -19,25 +19,38 @@ This gem provide simple DSL by parsing content of https://control.atrea.eu with
19
19
  Add this line to your application's Gemfile:
20
20
 
21
21
  ```ruby
22
- gem 'atrea_control'
22
+ gem "atrea_control"
23
23
  ```
24
24
 
25
25
  And then execute:
26
+ ```bash
27
+ bundle install
28
+ ```
26
29
 
27
- $ bundle install
30
+ Or
31
+ ```bash
32
+ bundle add atrea_control
33
+ ```
28
34
 
29
35
  Or install it yourself as:
30
36
 
31
- $ gem install atrea_control
37
+ ```bash
38
+ gem install atrea_control
39
+ ```
32
40
 
33
41
  ## Usage
34
42
 
35
43
  At the begin you need obtain `user_id`, `unit_id` and `sid` (auth token). For this use "Login"
44
+
45
+ * `user_id` is atrea internal ID of user account
46
+ * `unit_id` is atrea `ident` - identification number of airflow unit (ventilation system) - 10 digits
47
+ * `sid` is session ID - auth token, valid for logged session. Its validity is unknown
48
+
36
49
  ```ruby
37
50
  tokens = AtreaControl::Duplex::Login.user_tokens login: "myhome", password: "sup3r-S3CR3T-kocicka"
38
51
  tokens # => { user_id: "1234", unit_id: "85425324672", sid: 4012 }
39
52
  ```
40
- I recommend to store then somewhere...
53
+ I recommend to store then somewhere...
41
54
  Then you can call Unit for data...
42
55
 
43
56
  Example usage:
@@ -46,6 +59,7 @@ control = AtreaControl::Duplex::Unit.new user_id: "1234", unit_id: "85425324672"
46
59
  control.values # => { current_power: 88.0, current_mode: "CO2" }
47
60
  control.power # => 88.0
48
61
  ```
62
+
49
63
  ### Dig deeper
50
64
  `AtreaControl::Duplex::Unit` expect optional argument `user_ctrl` which should be object respond to
51
65
 
@@ -56,36 +70,12 @@ control.power # => 88.0
56
70
 
57
71
  __Please check [lib/atrea_control/duplex/user_ctrl.rb](./lib/atrea_control/duplex/user_ctrl.rb) for more details !__
58
72
 
59
- ## Development / TODO
60
- Login is currently done by selenium - fill login form.
61
- I found that Atre submit form to BE, generate some "empty" HTML and JS which onLoad start doing request to queue for "login".
62
-
63
- Re-login user, add login procedure into queue:
64
- ```bash
65
- curl -X POST -d "comm=config%2Flogin.cgi" "https://control.atrea.eu/apps/rd5Control/handle.php?action=unitLogin&user=XXXX&unit=NNNNNNN&table=userUnits&idPwd=YYYYYYY&NFP"
66
- ```
67
- Response is time in seconds when login will ready:
68
- ```xml
69
- <root><sended time="264"/></root>
70
- ```
71
- Based it su shown countdown ...
72
-
73
-
74
- Request for current queue status
75
- ```bash
76
- curl 'https://control.atrea.eu/apps/rd5Control/handle.php?Sync=1&action=unitQuery&query=loged&user=XXXX&unit=NNNNNNN'
77
- ```
78
- if queue is processed:
79
- ```xml
80
- <root><login uconn="16395889" sid="010101" ver="3001009"/></root>
81
- ```
82
- else
83
- ```xml
84
- <root><login uconn="16390480" sid="0"/></root>
85
- ```
73
+ This object is used to translate internal unit modes to user-friendly texts & translations.
86
74
 
87
- Goal is to obtain "SID".
75
+ I strong recommend to use `AtreaControl::Duplex::UserCtrl` object from "cache",
76
+ for optimize network traffic - because they are static data.
88
77
 
78
+ ## Development
89
79
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
90
80
 
91
81
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
@@ -39,5 +39,4 @@ Gem::Specification.new do |spec|
39
39
  spec.add_dependency "i18n", "~> 1.14"
40
40
  spec.add_dependency "nokogiri", "~> 1.15"
41
41
  spec.add_dependency "rest-client", "~> 2.1"
42
- spec.add_dependency "selenium-webdriver", "~> 4.25.0"
43
42
  end
data/bin/console CHANGED
@@ -8,8 +8,8 @@ require "atrea_control"
8
8
  # with your gem easier. You can also use a different console, if you like.
9
9
 
10
10
  # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
11
+ require "pry"
12
+ Pry.start
13
13
 
14
- require "irb"
15
- IRB.start(__FILE__)
14
+ # require "irb"
15
+ # IRB.start(__FILE__)
@@ -1,23 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "digest"
3
+ require "nokogiri"
4
4
  require "rest-client"
5
- require "selenium-webdriver"
5
+ require "securerandom"
6
6
 
7
7
  module AtreaControl
8
8
  module Duplex
9
- # Process login into RD5 with selenium to get `sid` ( auth_token ) for direct API communication
9
+ # Process login into RD5 to get `sid` ( auth_token ) for direct API communication
10
10
  class Login
11
11
  include AtreaControl::Logger
12
12
 
13
13
  # @return [Hash] - user_id, unit_id, sid
14
14
  def self.user_tokens(login:, password:)
15
- i = new(login: login, password: password)
16
- tokens = i.user
17
-
18
- tokens
19
- ensure
20
- i.close
15
+ instance = new(login: login, password: password)
16
+ instance.call
21
17
  end
22
18
 
23
19
  # @param [String] login
@@ -26,101 +22,107 @@ module AtreaControl
26
22
  @login = login
27
23
  @password = password
28
24
  end
29
- #
30
- # def crypto_password
31
- # md5 = Digest::MD5.new
32
- # md5 << "\r\n"
33
- # md5 << @password
34
- # md5.hexdigest
35
- # end
36
- #
37
- # def token
38
- # RestClient.get "#{AtreaControl::Duplex::CONTROL_URI}/config/login.cgi", params: { magic: crypto_password }
39
- # end
40
25
 
41
- def user
42
- raise AtreaControl::Error, "Must be logged in" unless login
26
+ # Perform login procedure for retrieve `sid` (auth_token)
27
+ # @return [Hash] - user_id, unit_id, sid
28
+ # @raise [AtreaControl::Error] if login failed
29
+ def call
30
+ @sid = sid
31
+ if @sid == "0"
32
+ re_login = RestClient.post "#{AtreaControl::Duplex::CONTROL_URI}/apps/rd5Control/handle.php?action=unitLogin&user=#{user_id}&unit=#{unit_id}&table=userUnits&idPwd=#{unit[:iid]}&#{SecureRandom.hex(2)}&_ts=#{SecureRandom.hex(4)}",
33
+ { comm: "config/login.cgi?magic=" }, headers
34
+ time = Nokogiri::XML(re_login.body).at_xpath("//sended")["time"].to_i
35
+ logger.debug "Login in #{time} seconds..."
36
+ time.times do
37
+ @sid = sid
38
+ break if @sid != "0"
39
+
40
+ sleep 1
41
+ end
42
+ raise AtreaControl::Error, "Login failed" if @sid == "0"
43
+
44
+ logger.debug "Login complete !"
45
+ else
46
+ logger.debug "Login is not necessary ! SID: #{@sid}"
47
+ end
48
+ { user_id:, unit_id:, sid: @sid }
49
+ end
43
50
 
44
- logger.debug "refresh user data based on session"
45
- @user_id = driver.execute_script("return window._user")
46
- @unit_id = driver.execute_script("return window._unit")
47
- @auth_token = driver.execute_script("return window.user")&.[]("auth") # sid
51
+ private
48
52
 
49
- { user_id: @user_id, unit_id: @unit_id, sid: @auth_token }
50
- end
53
+ # @!group Login steps, order is important
51
54
 
52
- # @return [Selenium::WebDriver::Firefox::Driver]
53
- def driver
54
- return @driver if defined?(@driver)
55
+ # Retrieve user details from RD5 core.php?action=init
56
+ # @return [Hash] - user_id, name
57
+ def user
58
+ core_init = RestClient.get "#{AtreaControl::Duplex::CONTROL_URI}/core/core.php?action=init&_ts=#{SecureRandom.hex(4)}",
59
+ headers
60
+ client = Nokogiri::XML(core_init.body).at_xpath("//client")
61
+ user_id = client["id"]
62
+ name = client["name"]
63
+ logger.debug "User ID: #{user_id}, User Name: #{name}"
64
+
65
+ { user_id:, name: }
66
+ end
55
67
 
56
- # options = Selenium::WebDriver::Firefox::Options.new
57
- # options.headless! unless ENV["NO_HEADLESS"]
58
- # @driver ||= Selenium::WebDriver.for :firefox, capabilities: [options]
59
- options = Selenium::WebDriver::Firefox::Options.new
60
- options.add_argument "-headless" unless ENV["NO_HEADLESS"]
61
- @driver ||= Selenium::WebDriver::Firefox::Driver.new options: options
68
+ def user_id
69
+ @user_id ||= user[:user_id]
62
70
  end
63
71
 
64
- # Login into control
65
- def login
66
- return driver if @logged
67
-
68
- @login_in_progress = true
69
- logger.debug "start new login..."
70
- driver.get "#{AtreaControl::Duplex::CONTROL_URI}?action=logout"
71
- submit_login_form
72
- finish_login
73
- driver
74
- ensure
75
- @login_in_progress = false
72
+ # For some reason, this requests must be done before `unit_id` requested
73
+ def run_rd5_app
74
+ RestClient.post "#{AtreaControl::Duplex::CONTROL_URI}/core/core.php?Sync=1&action=run&object=app&lng=28&rVer=1&_ts=#{SecureRandom.hex(4)}",
75
+ { name: "rd5Control", path: "apps/rd5Control/" }, headers
76
+ RestClient.post "#{AtreaControl::Duplex::CONTROL_URI}/core/core.php?Sync=1&action=load&object=setting&_ts=#{SecureRandom.hex(4)}",
77
+ { path: "apps/rd5Control" }, headers
76
78
  end
77
79
 
78
- # Submit given credentials and proceed login
79
- def submit_login_form
80
- form = driver.find_element(id: "loginFrm")
81
- username = form.find_element(name: "username")
82
- username.send_keys @login
83
- password = form.find_element(name: "password")
84
- password.send_keys @password
85
- logger.debug "Submit login form..."
86
-
87
- submit = form.find_element(css: "input[type=submit]")
88
- submit.click
80
+ # Retrieve overview of RD5 unit
81
+ # @return [Hash] - unit_number (digit code/ID from list) and iid (unit salt?)
82
+ def unit
83
+ return @unit if @unit
84
+
85
+ # run_rd5_app
86
+ units_table = RestClient.get "#{AtreaControl::Duplex::CONTROL_URI}/_data/data.php?Sync=1&action=getdata&rH&rE&table=userUnits&ds=rd5&_ts=#{SecureRandom.hex(4)}",
87
+ headers
88
+ item = Nokogiri::XML(units_table.body).at_xpath("//i")
89
+ unit_number = item["unit"]
90
+ iid = item["id"]
91
+ @unit ||= { unit_number:, iid: }
89
92
  end
90
93
 
91
- # Retrieve dashboard URI from object tag and open it again
92
- def open_dashboard
93
- uri = driver.find_element(tag_name: "object").attribute "data"
94
- # Open "iframe" with atrea dashboard - it propagate window objects...
95
- driver.get uri
96
- logger.debug "login success"
97
- @logged = true
94
+ # With `unit_number` from `unit` method, get `unit_id` from RD5 unit records
95
+ # @return [String] - unit_id
96
+ def unit_id
97
+ return @unit_id if @unit_id
98
+
99
+ records = RestClient.get "#{AtreaControl::Duplex::CONTROL_URI}/_data/data.php?Sync=1&action=getrecord&id=#{unit[:unit_number]}&table=units&ds=rd5&_ts=#{SecureRandom.hex(4)}",
100
+ headers
101
+ @unit_id ||= Nokogiri::XML(records.body).at_xpath("//table/i")["ident"]
98
102
  end
99
103
 
100
- # quit selenium browser
101
- def close
102
- begin
103
- driver.quit
104
- rescue StandardError
105
- nil
106
- end
107
- logger.debug "driver closed & destroyed"
108
- ensure
109
- remove_instance_variable :@driver
104
+ def sid
105
+ data = RestClient.get "#{AtreaControl::Duplex::CONTROL_URI}/apps/rd5Control/handle.php?Sync=1&action=unitQuery&query=loged&user=#{user_id}&unit=#{unit_id}&#{SecureRandom.hex(2)}&_ts=#{SecureRandom.hex(4)}",
106
+ headers
107
+ logger.debug data.body
108
+ Nokogiri::XML(data.body).at_xpath("//login")["sid"]
110
109
  end
111
110
 
112
- private
111
+ # @!group Private methods
113
112
 
114
- def finish_login
115
- 30.times do |i|
116
- return true if open_dashboard
117
- rescue Selenium::WebDriver::Error::NoSuchElementError => e
118
- logger.debug e.message
119
- logger.debug "#{i + 1}/30 attempt for login..."
120
- sleep 10
113
+ # @return [String] session ID from PHP BE
114
+ def php_session_id
115
+ return @php_session_id if @php_session_id
116
+
117
+ payload = { username: @login, password: @password }
118
+ RestClient.post "#{AtreaControl::Duplex::CONTROL_URI}?action=login", payload do |response|
119
+ @php_session_id = response.cookies["PHPSESSID"]
121
120
  end
122
- File.write("/tmp/failed_login-#{@login}.html", driver.page_source)
123
- raise AtreaControl::Error, "unable to login"
121
+ @php_session_id
122
+ end
123
+
124
+ def headers
125
+ { cookies: { PHPSESSID: php_session_id }, "App-name": "rd5Control" }
124
126
  end
125
127
  end
126
128
  end
@@ -29,3 +29,4 @@ module AtreaControl
29
29
  end
30
30
  end
31
31
  end
32
+ # https://control.atrea.eu/comm/sw/unit.php?ver=003001022&_user=2113&_unit=126399332270040&auth=49852&_t=config/xml.xml&_X=Ti&_async=1
@@ -9,8 +9,6 @@ module AtreaControl
9
9
  attr_reader :current_mode, :current_power, :outdoor_temperature, :preheat_temperature, :input_temperature
10
10
  # @return [Boolean] preheating air is ON ?
11
11
  attr_reader :preheating
12
- # @return [DateTime] store time of last update
13
- attr_reader :valid_for
14
12
  # @return [UserCtrl]
15
13
  attr_reader :user_ctrl
16
14
  # @return [Time, DateTime]
@@ -69,6 +67,8 @@ module AtreaControl
69
67
  parsed.each do |name, value|
70
68
  instance_variable_set :"@#{name}", value
71
69
  end
70
+ raise AtreaControl::SessionExpired unless valid?
71
+
72
72
  as_json
73
73
  end
74
74
 
@@ -80,7 +80,6 @@ module AtreaControl
80
80
  preheat_temperature: preheat_temperature,
81
81
  input_temperature: input_temperature,
82
82
  preheating: preheating,
83
- valid_for: valid_for,
84
83
  timestamp: timestamp,
85
84
  }
86
85
  end
@@ -89,15 +88,29 @@ module AtreaControl
89
88
  values.to_json(*)
90
89
  end
91
90
 
92
- # Additional "parameters" for each sensors
93
- # @note its changed in time ?
91
+ # Expire cached data and fetch them again
92
+ # @return [Hash] new values
93
+ def refresh!
94
+ remove_instance_variable(:@parsed) if defined?(@parsed)
95
+ values
96
+ end
97
+
98
+ # Data are valid if timestamp within 15.minutes
99
+ def valid?
100
+ return false unless timestamp
101
+
102
+ timestamp > (Time.now - (15 * 60.0))
103
+ end
104
+
105
+ private
106
+
107
+ # Additional "parameters" for each sensor
108
+ # @note it's changed in time ?
94
109
  def params
95
110
  response = request.call(_t: "user/params.xml")
96
111
  Nokogiri::XML response.body
97
112
  end
98
113
 
99
- private
100
-
101
114
  def parser
102
115
  @parser ||= ::AtreaControl::SensorParser.new(@user_ctrl)
103
116
  end
@@ -33,7 +33,7 @@ module AtreaControl
33
33
 
34
34
  # Get and parse XML with user/unit configuration source
35
35
  def user_ctrl
36
- response = request.call(_t: "lang/userCtrl.xml")
36
+ response = request.call(_t: "lang/userCtrl.xml", _async: 1)
37
37
  Nokogiri::XML response.body
38
38
  end
39
39
 
@@ -4,7 +4,7 @@ module AtreaControl
4
4
  # Controller for +control.atrea.eu+
5
5
  module Duplex
6
6
  CONTROL_URI = "https://control.atrea.eu/"
7
- CONTROL_VERSION = "003001009"
7
+ CONTROL_VERSION = "003001022"
8
8
 
9
9
  autoload :Login, "atrea_control/duplex/login"
10
10
  autoload :Request, "atrea_control/duplex/request"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "nokogiri"
4
+ require "time"
4
5
 
5
6
  module AtreaControl
6
7
  # Call RD5 unit ang get current sensors values
@@ -43,6 +44,7 @@ module AtreaControl
43
44
  preheating = %w[C10200 C10202 C10215 C10217].any? do |i|
44
45
  xml.xpath("//O[@I=\"#{i}\"]/@V").last&.value == "1"
45
46
  end
47
+ # @note timestamp seems to be localtime of creation of xml
46
48
  parsed["timestamp"] = xml.xpath("//RD5WEB").attribute("t").to_s
47
49
  parsed["preheating"] = preheating
48
50
  parsed
@@ -58,8 +60,7 @@ module AtreaControl
58
60
  "preheat_temperature" => values["preheat_temperature"].to_f / 10.0,
59
61
  "input_temperature" => values["input_temperature"].to_f / 10.0,
60
62
  "preheating" => values["preheating"],
61
- "timestamp" => DateTime.parse(values["timestamp"]),
62
- "valid_for" => Time.now,
63
+ "timestamp" => Time.strptime(values["timestamp"], "%Y-%m-%d %H:%M:%S"),
63
64
  }
64
65
  end
65
66
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
+ # :nocov:
2
3
 
3
4
  module AtreaControl
4
- VERSION = "2.3.0"
5
+ VERSION = "3.0.0"
5
6
  end
data/lib/atrea_control.rb CHANGED
@@ -7,6 +7,12 @@ require "yaml"
7
7
  module AtreaControl
8
8
  class Error < StandardError; end
9
9
 
10
+ class SessionExpired < Error
11
+ def message
12
+ "Session expired. Please perform login again."
13
+ end
14
+ end
15
+
10
16
  autoload :Duplex, "atrea_control/duplex"
11
17
  autoload :Logger, "atrea_control/logger"
12
18
  autoload :SensorParser, "atrea_control/sensor_parser"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atrea_control
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 3.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: 2024-10-20 00:00:00.000000000 Z
11
+ date: 2025-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.1'
55
- - !ruby/object:Gem::Dependency
56
- name: selenium-webdriver
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: 4.25.0
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: 4.25.0
69
55
  description: Read data from web controller of RD5 duplex by Atrea.
70
56
  email:
71
57
  - pokorny@luk4s.cz
@@ -119,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
105
  - !ruby/object:Gem::Version
120
106
  version: '0'
121
107
  requirements: []
122
- rubygems_version: 3.5.11
108
+ rubygems_version: 3.5.22
123
109
  signing_key:
124
110
  specification_version: 4
125
111
  summary: Get data control.atrea.eu