atrea_control 2.3.0 → 3.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd2ed6c46c2b1c7caef1d39ee4ed6e67971265273b1f985ba522876c6aaaa991
4
- data.tar.gz: d076c8e9079314311694dc98429bad429a4b49e55456bf44699fb2ec33d7101e
3
+ metadata.gz: 20263e53229b3ddf7cda715887e96db0e41859f76283ea39be936026350a9856
4
+ data.tar.gz: 1ad093d3927b23422394f4a0ee1df6cbd0613d7ef059a766ecd4e07c18f68710
5
5
  SHA512:
6
- metadata.gz: b7fc76c48e32e116d94135d23073df680f33ad30bc6637ee596a4762b61ed57f00a8af418a8e7e6a29ffb92d2daa11aecc67e4de2353b31681670de802e06bd2
7
- data.tar.gz: e2ff402d8e723d43c5baec15a694ee941ba3f8d5f96c31c3e30b080c03d0090209456b7e55b71085d89dd9b355840e4bf1413921e502ddef984edc4d6ad99000
6
+ metadata.gz: 8c97451f757393eaeb5cb3f98d7bd3b70bd1a962f0dd892528d5109604113b1fa43e138883dedfe598b8b08a388be8804a8b0b08c6a5059070d6b1c7d91fc0c5
7
+ data.tar.gz: ce1f31d0f7da01b6c407fc8c310717d456bc8654917835d8e809dd3d21f114b89455212c2d84cde458bd14ec55ef0ce9f46b1fde5f8984ea9554a59ae590e27f
@@ -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
@@ -1,11 +1,11 @@
1
1
  inherit_from: .rubocop_todo.yml
2
2
 
3
- require:
3
+ plugins:
4
4
  - rubocop-rspec
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.7
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.1)
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,60 +12,60 @@ 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
- diff-lcs (1.5.1)
21
+ diff-lcs (1.6.0)
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)
33
- language_server-protocol (3.17.0.3)
34
- logger (1.6.1)
30
+ json (2.10.2)
31
+ language_server-protocol (3.17.0.4)
32
+ lint_roller (1.1.0)
33
+ logger (1.6.6)
35
34
  method_source (1.1.0)
36
35
  mime-types (3.6.0)
37
36
  logger
38
37
  mime-types-data (~> 3.2015)
39
- mime-types-data (3.2024.1001)
38
+ mime-types-data (3.2025.0304)
40
39
  netrc (0.11.0)
41
- nokogiri (1.16.7-arm64-darwin)
40
+ nokogiri (1.18.4-arm64-darwin)
42
41
  racc (~> 1.4)
43
- nokogiri (1.16.7-x86_64-darwin)
42
+ nokogiri (1.18.4-x86_64-darwin)
44
43
  racc (~> 1.4)
45
- nokogiri (1.16.7-x86_64-linux)
44
+ nokogiri (1.18.4-x86_64-linux-gnu)
46
45
  racc (~> 1.4)
47
46
  parallel (1.26.3)
48
- parser (3.3.5.0)
47
+ parser (3.3.7.1)
49
48
  ast (~> 2.4.1)
50
49
  racc
51
- pry (0.14.2)
50
+ pry (0.15.2)
52
51
  coderay (~> 1.1)
53
52
  method_source (~> 1.0)
54
53
  public_suffix (6.0.1)
55
54
  racc (1.8.1)
56
55
  rainbow (3.1.1)
57
56
  rake (13.2.1)
58
- regexp_parser (2.9.2)
57
+ regexp_parser (2.10.0)
59
58
  rest-client (2.1.0)
60
59
  http-accept (>= 1.7.0, < 2.0)
61
60
  http-cookie (>= 1.0.2, < 2.0)
62
61
  mime-types (>= 1.16, < 4.0)
63
62
  netrc (~> 0.8)
64
- rexml (3.3.8)
63
+ rexml (3.4.1)
65
64
  rspec (3.13.0)
66
65
  rspec-core (~> 3.13.0)
67
66
  rspec-expectations (~> 3.13.0)
68
67
  rspec-mocks (~> 3.13.0)
69
- rspec-core (3.13.2)
68
+ rspec-core (3.13.3)
70
69
  rspec-support (~> 3.13.0)
71
70
  rspec-expectations (3.13.3)
72
71
  diff-lcs (>= 1.2.0, < 2.0)
@@ -74,45 +73,42 @@ GEM
74
73
  rspec-mocks (3.13.2)
75
74
  diff-lcs (>= 1.2.0, < 2.0)
76
75
  rspec-support (~> 3.13.0)
77
- rspec-support (3.13.1)
78
- rubocop (1.67.0)
76
+ rspec-support (3.13.2)
77
+ rubocop (1.74.0)
79
78
  json (~> 2.3)
80
- language_server-protocol (>= 3.17.0)
79
+ language_server-protocol (~> 3.17.0.2)
80
+ lint_roller (~> 1.1.0)
81
81
  parallel (~> 1.10)
82
82
  parser (>= 3.3.0.2)
83
83
  rainbow (>= 2.2.2, < 4.0)
84
- regexp_parser (>= 2.4, < 3.0)
85
- rubocop-ast (>= 1.32.2, < 2.0)
84
+ regexp_parser (>= 2.9.3, < 3.0)
85
+ rubocop-ast (>= 1.38.0, < 2.0)
86
86
  ruby-progressbar (~> 1.7)
87
- unicode-display_width (>= 2.4.0, < 3.0)
88
- rubocop-ast (1.32.3)
87
+ unicode-display_width (>= 2.4.0, < 4.0)
88
+ rubocop-ast (1.39.0)
89
89
  parser (>= 3.3.1.0)
90
- rubocop-rspec (3.1.0)
91
- rubocop (~> 1.61)
90
+ rubocop-rspec (3.5.0)
91
+ lint_roller (~> 1.1)
92
+ rubocop (~> 1.72, >= 1.72.1)
92
93
  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
94
  simplecov (0.22.0)
101
95
  docile (~> 1.1)
102
96
  simplecov-html (~> 0.11)
103
97
  simplecov_json_formatter (~> 0.1)
104
98
  simplecov-html (0.13.1)
105
99
  simplecov_json_formatter (0.1.4)
106
- unicode-display_width (2.6.0)
107
- webmock (3.24.0)
100
+ unicode-display_width (3.1.4)
101
+ unicode-emoji (~> 4.0, >= 4.0.4)
102
+ unicode-emoji (4.0.4)
103
+ webmock (3.25.1)
108
104
  addressable (>= 2.8.0)
109
105
  crack (>= 0.3.2)
110
106
  hashdiff (>= 0.4.0, < 2.0.0)
111
- websocket (1.2.11)
112
107
 
113
108
  PLATFORMS
114
109
  arm64-darwin-22
115
110
  arm64-darwin-23
111
+ arm64-darwin-24
116
112
  x86_64-darwin-20
117
113
  x86_64-linux
118
114
 
@@ -127,4 +123,4 @@ DEPENDENCIES
127
123
  webmock (~> 3.14)
128
124
 
129
125
  BUNDLED WITH
130
- 2.5.17
126
+ 2.6.6
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,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :nocov:
4
+
3
5
  module AtreaControl
4
- VERSION = "2.3.0"
6
+ VERSION = "3.0.1"
5
7
  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.1
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-03-17 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