atrea_control 3.2.1 → 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/.github/workflows/main.yml +11 -0
- data/.rubocop_todo.yml +7 -1
- data/.ruby-version +1 -1
- data/AGENTS.md +51 -0
- data/CHANGELOG.md +63 -1
- data/Gemfile +4 -4
- data/Gemfile.lock +49 -47
- data/README.md +215 -62
- data/atrea_control.gemspec +4 -3
- data/docs/rd5-cloud-protocol.md +253 -0
- data/docs/superpowers/plans/2026-10-24-rd5-reliability.md +238 -0
- data/lib/atrea_control/duplex/login.rb +94 -109
- data/lib/atrea_control/duplex/login_discovery.rb +82 -0
- data/lib/atrea_control/duplex/login_session.rb +53 -0
- data/lib/atrea_control/duplex/rd5_command.rb +72 -0
- data/lib/atrea_control/duplex/rd5_configuration.rb +104 -0
- data/lib/atrea_control/duplex/request.rb +34 -6
- data/lib/atrea_control/duplex/unit.rb +58 -52
- data/lib/atrea_control/duplex/unit_commands.rb +59 -0
- data/lib/atrea_control/duplex/user_ctrl.rb +75 -64
- data/lib/atrea_control/duplex/write_verification.rb +75 -0
- data/lib/atrea_control/duplex.rb +2 -0
- data/lib/atrea_control/logger.rb +3 -1
- data/lib/atrea_control/sensor_parser.rb +94 -30
- data/lib/atrea_control/version.rb +2 -1
- data/lib/atrea_control.rb +7 -0
- metadata +31 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: da49b797a6e3e359751ab8ef6b64768d53ddc81b06f91c78dbe868f510540926
|
|
4
|
+
data.tar.gz: 869cddecea901cc565ff961818a0788e45243a9ec337978ebcb2e3e157de6f53
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ceba4c6afb798d0f72fb58b087bf65594881894b5d6284b1623122774c14df01ac3904f824c96fc00ff09b57698aa1d8578b33223e8874696c7ea76155955457
|
|
7
|
+
data.tar.gz: 3035973ce25e37db961c3a8aac07c124e75284afce1f8caa7d90b43b60771e359196e65f6496f71a6e8f5888f3419852f8627a7425bc8b665ea2dd26c87ea9f9
|
data/.github/workflows/main.yml
CHANGED
|
@@ -17,3 +17,14 @@ jobs:
|
|
|
17
17
|
bundler-cache: true
|
|
18
18
|
- name: Run tests
|
|
19
19
|
run: bundle exec rspec
|
|
20
|
+
lint:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- name: Set up Ruby
|
|
25
|
+
uses: ruby/setup-ruby@v1
|
|
26
|
+
with:
|
|
27
|
+
ruby-version: '4'
|
|
28
|
+
bundler-cache: true
|
|
29
|
+
- name: Run RuboCop
|
|
30
|
+
run: bundle exec rubocop
|
data/.rubocop_todo.yml
CHANGED
|
@@ -18,10 +18,16 @@ Gemspec/RequiredRubyVersion:
|
|
|
18
18
|
Metrics/AbcSize:
|
|
19
19
|
Max: 34
|
|
20
20
|
|
|
21
|
+
# Offense count: 1
|
|
22
|
+
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
23
|
+
Metrics/ClassLength:
|
|
24
|
+
Exclude:
|
|
25
|
+
- 'lib/atrea_control/duplex/login.rb'
|
|
26
|
+
|
|
21
27
|
# Offense count: 3
|
|
22
28
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
|
23
29
|
Metrics/MethodLength:
|
|
24
|
-
Max:
|
|
30
|
+
Max: 23
|
|
25
31
|
|
|
26
32
|
# Offense count: 1
|
|
27
33
|
# Configuration parameters: Prefixes, AllowedPatterns.
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.0.
|
|
1
|
+
4.0.7
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Repository guidance
|
|
2
|
+
|
|
3
|
+
## Commands and toolchain
|
|
4
|
+
- Install dependencies: `bin/setup` (`bundle install`).
|
|
5
|
+
- Full local checks: `bundle exec rake` runs specs, then RuboCop.
|
|
6
|
+
- CI runs the RSpec Ruby matrix and a separate `bundle exec rubocop` job.
|
|
7
|
+
- Focused test: `bundle exec rspec spec/atrea_control/duplex/unit_spec.rb`;
|
|
8
|
+
append `:LINE` to select an example.
|
|
9
|
+
- Ruby versions differ across config: `.ruby-version` pins 4.0.7, gemspec
|
|
10
|
+
allows >= 3.2, RuboCop targets 3.3. Do not infer minimum Ruby from local version.
|
|
11
|
+
- RuboCop inherits `.rubocop_todo.yml`; RSpec message expectations use
|
|
12
|
+
`receive`, not `have_received`.
|
|
13
|
+
|
|
14
|
+
## Protocol and state
|
|
15
|
+
- This gem wraps Atrea RD5's web portal. `Duplex::Login` obtains tokens;
|
|
16
|
+
`Duplex::Request` performs unit requests; `UserCtrl` discovers sensor/mode
|
|
17
|
+
mappings; `SensorParser` decodes readings and encodes writes.
|
|
18
|
+
- Sensor and mode maps use string keys. Derive sensor IDs from `UserCtrl`;
|
|
19
|
+
fixture IDs are not universal device configuration.
|
|
20
|
+
- `UserCtrl.data` is persisted by `home_control`. Preserve JSON-safe
|
|
21
|
+
`input_temperature_source` metadata when exporting/reconstructing `user_ctrl:`;
|
|
22
|
+
a sensor ID alone cannot represent conditional inlet-temperature selection.
|
|
23
|
+
- RD5 Modbus register semantics inform command policy, not cloud HTTP framing.
|
|
24
|
+
See `docs/rd5-cloud-protocol.md` for supported profiles and evidence limits.
|
|
25
|
+
- `Unit` caches parsed readings; `refresh!` fetches again. `values` raises
|
|
26
|
+
`AtreaControl::SessionExpired` when device timestamp is missing or older
|
|
27
|
+
than 15 minutes, rather than relying on HTTP authentication errors.
|
|
28
|
+
- All current-value readers validate snapshots. Failed refresh stays failed until
|
|
29
|
+
explicit `refresh!`; write attempts discard prewrite data even on failure.
|
|
30
|
+
- `set_power`/`set_mode` verify by default and require caller-proven
|
|
31
|
+
`verification_registers:`; legacy setters are transport-only. Never infer a
|
|
32
|
+
read-back register from PDF addresses or automatically replay timed-out writes.
|
|
33
|
+
- Use Faraday/Net::HTTP: login has a 300-second total limit, HTTP requests 60.
|
|
34
|
+
Standard `Timeout` wrappers use an explicit exception class so Net::HTTP closes
|
|
35
|
+
sockets during TLS setup; do not reintroduce a custom transport or write retries.
|
|
36
|
+
- Loading `atrea_control` registers `config/locales/*.yml` and sets global
|
|
37
|
+
`I18n.default_locale` to `:cs`.
|
|
38
|
+
|
|
39
|
+
## Tests
|
|
40
|
+
- Specs use WebMock and XML/HTML fixtures in `spec/fixtures/files`;
|
|
41
|
+
normal test runs need no portal credentials or physical unit.
|
|
42
|
+
- Support files are not auto-loaded. Specs using shared `"userCtrl"` context
|
|
43
|
+
explicitly require `spec/support/user_ctrl.rb`.
|
|
44
|
+
- `unit.xml` contains an old timestamp; most Unit specs stub `valid?`.
|
|
45
|
+
Exercise real validity behavior with controlled timestamps.
|
|
46
|
+
|
|
47
|
+
## Packaging
|
|
48
|
+
- Gem contents come from `git ls-files`, excluding test/spec/features.
|
|
49
|
+
New runtime files must be tracked to enter built gem.
|
|
50
|
+
- `bundle exec rake release` pushes commits/tags and publishes to RubyGems;
|
|
51
|
+
it is not a local verification command.
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,66 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## [4.0.0] - 2026-10-26
|
|
4
|
+
### Breaking changes
|
|
5
|
+
- Reject `nil` (formerly liable to become zero), booleans, negative/fractional
|
|
6
|
+
values and malformed setter strings with `ArgumentError`; never truncate or clamp.
|
|
7
|
+
- Validate power domains, discovered modes, equipment prerequisites and schedule
|
|
8
|
+
state. Unsupported or incomplete configuration now fails explicitly.
|
|
9
|
+
- Recheck freshness on all current-value getters and serializers. Failed refresh
|
|
10
|
+
invalidates accessible data until `refresh!`; stale cached values no longer leak.
|
|
11
|
+
- Preserve manual schedule control; scheduled/temporary writes request temporary
|
|
12
|
+
control even for zero power, rather than unconditionally switching policy.
|
|
13
|
+
- Return `nil` for unavailable/unsupported optional inlet temperature, not a
|
|
14
|
+
fabricated zero. Add `required_temperature` reader/hash key while retaining
|
|
15
|
+
historical `preheat_temperature`; both mean requested temperature.
|
|
16
|
+
|
|
17
|
+
### Change
|
|
18
|
+
- Export JSON-safe `input_temperature_source` in `UserCtrl.data` so persisted
|
|
19
|
+
configuration retains conditional inlet sensor selection. Custom `user_ctrl:`
|
|
20
|
+
objects should expose that metadata; the unreleased `input_temperature_profile`
|
|
21
|
+
accessor is replaced. Sensor-selection rules stay in the gem, not consumer UIs.
|
|
22
|
+
- Include `Sync=1` on the unit-login POST, matching the current portal's
|
|
23
|
+
synchronous handshake. Without it, the session query could stay at SID zero
|
|
24
|
+
despite recent unit communication. Add a regression that requires this flag
|
|
25
|
+
before establishing a session.
|
|
26
|
+
- Parse strict unit/configuration XML and unsigned raw registers; decode signed
|
|
27
|
+
temperatures only at projection. Reject malformed data through library errors.
|
|
28
|
+
- Accept valid device timestamps without leading zeroes, as observed live
|
|
29
|
+
(`17:7:32`), while retaining strict date/time and timezone validation.
|
|
30
|
+
- Publish atomic snapshots, enforce the strict 15-minute age boundary and allow
|
|
31
|
+
at most 60 seconds of future clock skew. Discard snapshots on every write attempt.
|
|
32
|
+
- Add `RD5Configuration`, configuration-derived command/control mappings and exact
|
|
33
|
+
unsigned 16-bit command encoding. Never execute downloaded configuration code.
|
|
34
|
+
- Add optional IANA `time_zone:`; `nil` retains process-local interpretation.
|
|
35
|
+
Invalid zones and ambiguous/nonexistent DST timestamps raise `ProtocolError`.
|
|
36
|
+
- Add `set_power`/`set_mode` with verification enabled by default, a 30-second
|
|
37
|
+
absolute budget and five-second polling. Verification requires caller-supplied,
|
|
38
|
+
independently proven `verification_registers:`; no current cloud read-back
|
|
39
|
+
profile is automatically trusted. Missing mappings fail before writing.
|
|
40
|
+
- Keep assignment setters unconfirmed (`verify: false`); HTTP success alone is
|
|
41
|
+
not application acknowledgement. Verification timeout after dispatch reports
|
|
42
|
+
unknown outcome through `WriteNotConfirmed`, without write retries.
|
|
43
|
+
- Add explicit login unit selection by `ident` (default: first listed unit), a
|
|
44
|
+
five-minute total limit, one-second polling and bounded HTTP requests.
|
|
45
|
+
Distinguish authentication, protocol, transport, timeout and configuration errors.
|
|
46
|
+
- Add `tzinfo ~> 2.0` for explicit IANA/DST handling. Keep Faraday/Net::HTTP as
|
|
47
|
+
the sole HTTP transport, with standard-library total timeouts: login at most
|
|
48
|
+
300 seconds, each HTTP operation at most 60 seconds. Remove the HTTPX dependency,
|
|
49
|
+
custom transport plugin and socket/resolver machinery. Explicit timeout exception
|
|
50
|
+
classes preserve Net::HTTP TLS cleanup. Automatic retries remain disabled.
|
|
51
|
+
- Verify live RD5 login, configuration, values, getters/JSON, three refreshes and
|
|
52
|
+
expiry/recovery on 2026-10-25 after correcting the handshake. Validate command
|
|
53
|
+
construction against the live configuration without dispatching writes.
|
|
54
|
+
Acknowledgements, live writes and requested-value read-back remain unverified.
|
|
55
|
+
Add an independent RuboCop CI job alongside the Ruby spec matrix.
|
|
56
|
+
|
|
57
|
+
### Security
|
|
58
|
+
- Remove credential-bearing response/URL/SID diagnostics and executable locale
|
|
59
|
+
regeneration; validate XML without DTD/network resolution. Login diagnostics
|
|
60
|
+
retain stage, status, elapsed time and poll counts without secrets.
|
|
61
|
+
- upgrade ruby 4.0.7
|
|
62
|
+
- upgrade gems
|
|
63
|
+
|
|
2
64
|
## [3.2.0] - 2026-04-16
|
|
3
65
|
### Security
|
|
4
66
|
- upgrade gems
|
|
@@ -23,7 +85,7 @@
|
|
|
23
85
|
- "session" validity check & expiration (exception)
|
|
24
86
|
### Changed
|
|
25
87
|
- ruby version 3.3+
|
|
26
|
-
|
|
88
|
+
|
|
27
89
|
## [2.2.0] - 2024-10-20
|
|
28
90
|
### Added
|
|
29
91
|
- timestamp from atrea server
|
data/Gemfile
CHANGED
|
@@ -7,10 +7,10 @@ gemspec
|
|
|
7
7
|
|
|
8
8
|
gem "pry"
|
|
9
9
|
gem "rake", "~> 13.0"
|
|
10
|
-
gem "rspec", "~> 3.
|
|
11
|
-
gem "rubocop", "~> 1.
|
|
12
|
-
gem "rubocop-rspec", "~> 3.
|
|
10
|
+
gem "rspec", "~> 3.13"
|
|
11
|
+
gem "rubocop", "~> 1.88", require: false
|
|
12
|
+
gem "rubocop-rspec", "~> 3.10", require: false
|
|
13
13
|
group :test do
|
|
14
14
|
gem "simplecov", require: false
|
|
15
|
-
gem "webmock", "~> 3.
|
|
15
|
+
gem "webmock", "~> 3.26"
|
|
16
16
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
atrea_control (
|
|
5
|
-
faraday (~> 2.
|
|
4
|
+
atrea_control (4.0.0)
|
|
5
|
+
faraday (~> 2.14)
|
|
6
6
|
faraday-cookie_jar (~> 0.0)
|
|
7
|
-
i18n (~> 1.
|
|
8
|
-
nokogiri (~> 1.
|
|
7
|
+
i18n (~> 1.15)
|
|
8
|
+
nokogiri (~> 1.16)
|
|
9
|
+
tzinfo (~> 2.0)
|
|
9
10
|
|
|
10
11
|
GEM
|
|
11
12
|
remote: https://rubygems.org/
|
|
@@ -13,49 +14,51 @@ GEM
|
|
|
13
14
|
addressable (2.9.0)
|
|
14
15
|
public_suffix (>= 2.0.2, < 8.0)
|
|
15
16
|
ast (2.4.3)
|
|
16
|
-
bigdecimal (4.1.
|
|
17
|
+
bigdecimal (4.1.3)
|
|
17
18
|
coderay (1.1.3)
|
|
18
|
-
concurrent-ruby (1.3.
|
|
19
|
+
concurrent-ruby (1.3.8)
|
|
19
20
|
crack (1.0.1)
|
|
20
21
|
bigdecimal
|
|
21
22
|
rexml
|
|
22
23
|
diff-lcs (1.6.2)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
faraday-net_http (>= 2.0, < 3.5)
|
|
24
|
+
domain_name (0.7.20261007)
|
|
25
|
+
faraday (2.15.0)
|
|
26
|
+
faraday-net_http (>= 2.0, < 3.6)
|
|
27
27
|
json
|
|
28
28
|
logger
|
|
29
29
|
faraday-cookie_jar (0.0.8)
|
|
30
30
|
faraday (>= 0.8.0)
|
|
31
31
|
http-cookie (>= 1.0.0)
|
|
32
|
-
faraday-net_http (3.
|
|
32
|
+
faraday-net_http (3.5.0)
|
|
33
33
|
net-http (~> 0.5)
|
|
34
34
|
hashdiff (1.2.1)
|
|
35
|
-
http-cookie (1.1.
|
|
35
|
+
http-cookie (1.1.6)
|
|
36
36
|
domain_name (~> 0.5)
|
|
37
|
-
i18n (1.
|
|
37
|
+
i18n (1.15.2)
|
|
38
38
|
concurrent-ruby (~> 1.0)
|
|
39
|
-
io-console (0.
|
|
40
|
-
json (
|
|
41
|
-
language_server-protocol (3.17.0.
|
|
39
|
+
io-console (0.9.4)
|
|
40
|
+
json (3.1.0)
|
|
41
|
+
language_server-protocol (3.17.0.6)
|
|
42
42
|
lint_roller (1.1.0)
|
|
43
43
|
logger (1.7.0)
|
|
44
44
|
method_source (1.1.0)
|
|
45
|
-
net-http (0.
|
|
45
|
+
net-http (0.10.0)
|
|
46
|
+
net-protocol (>= 0.3.0)
|
|
46
47
|
uri (>= 0.11.1)
|
|
47
|
-
|
|
48
|
+
net-protocol (0.4.0)
|
|
49
|
+
timeout
|
|
50
|
+
nokogiri (1.19.4-arm64-darwin)
|
|
48
51
|
racc (~> 1.4)
|
|
49
|
-
nokogiri (1.19.
|
|
52
|
+
nokogiri (1.19.4-x86_64-darwin)
|
|
50
53
|
racc (~> 1.4)
|
|
51
|
-
nokogiri (1.19.
|
|
54
|
+
nokogiri (1.19.4-x86_64-linux-gnu)
|
|
52
55
|
racc (~> 1.4)
|
|
53
|
-
parallel (2.0
|
|
54
|
-
parser (3.3.
|
|
56
|
+
parallel (2.3.0)
|
|
57
|
+
parser (3.3.13.0)
|
|
55
58
|
ast (~> 2.4.1)
|
|
56
59
|
racc
|
|
57
60
|
prism (1.9.0)
|
|
58
|
-
pry (0.16.
|
|
61
|
+
pry (0.16.1)
|
|
59
62
|
coderay (~> 1.1)
|
|
60
63
|
method_source (~> 1.0)
|
|
61
64
|
reline (>= 0.6.0)
|
|
@@ -63,8 +66,8 @@ GEM
|
|
|
63
66
|
racc (1.8.1)
|
|
64
67
|
rainbow (3.1.1)
|
|
65
68
|
rake (13.4.2)
|
|
66
|
-
regexp_parser (2.
|
|
67
|
-
reline (0.
|
|
69
|
+
regexp_parser (2.13.1)
|
|
70
|
+
reline (0.7.0)
|
|
68
71
|
io-console (~> 0.5)
|
|
69
72
|
rexml (3.4.4)
|
|
70
73
|
rspec (3.13.2)
|
|
@@ -80,35 +83,34 @@ GEM
|
|
|
80
83
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
81
84
|
rspec-support (~> 3.13.0)
|
|
82
85
|
rspec-support (3.13.7)
|
|
83
|
-
rubocop (1.
|
|
84
|
-
json (
|
|
86
|
+
rubocop (1.92.1)
|
|
87
|
+
json (>= 2.3)
|
|
85
88
|
language_server-protocol (~> 3.17.0.2)
|
|
86
89
|
lint_roller (~> 1.1.0)
|
|
87
90
|
parallel (>= 1.10)
|
|
88
91
|
parser (>= 3.3.0.2)
|
|
89
92
|
rainbow (>= 2.2.2, < 4.0)
|
|
90
|
-
regexp_parser (>= 2.
|
|
93
|
+
regexp_parser (>= 2.11.3, < 3.0)
|
|
91
94
|
rubocop-ast (>= 1.49.0, < 2.0)
|
|
92
95
|
ruby-progressbar (~> 1.7)
|
|
93
96
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
94
|
-
rubocop-ast (1.
|
|
97
|
+
rubocop-ast (1.50.0)
|
|
95
98
|
parser (>= 3.3.7.2)
|
|
96
99
|
prism (~> 1.7)
|
|
97
|
-
rubocop-rspec (3.
|
|
100
|
+
rubocop-rspec (3.10.2)
|
|
98
101
|
lint_roller (~> 1.1)
|
|
99
|
-
|
|
102
|
+
regexp_parser (>= 2.0)
|
|
103
|
+
rubocop (~> 1.86, >= 1.86.2)
|
|
100
104
|
ruby-progressbar (1.13.0)
|
|
101
|
-
simplecov (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
unicode-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
uri (1.1.1)
|
|
111
|
-
webmock (3.26.2)
|
|
105
|
+
simplecov (1.3.2)
|
|
106
|
+
timeout (0.6.1)
|
|
107
|
+
tzinfo (2.0.6)
|
|
108
|
+
concurrent-ruby (~> 1.0)
|
|
109
|
+
unicode-display_width (3.3.0)
|
|
110
|
+
unicode-emoji (~> 4.3)
|
|
111
|
+
unicode-emoji (4.3.0)
|
|
112
|
+
uri (1.1.2)
|
|
113
|
+
webmock (3.34.0)
|
|
112
114
|
addressable (>= 2.8.0)
|
|
113
115
|
crack (>= 0.3.2)
|
|
114
116
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
@@ -125,11 +127,11 @@ DEPENDENCIES
|
|
|
125
127
|
atrea_control!
|
|
126
128
|
pry
|
|
127
129
|
rake (~> 13.0)
|
|
128
|
-
rspec (~> 3.
|
|
129
|
-
rubocop (~> 1.
|
|
130
|
-
rubocop-rspec (~> 3.
|
|
130
|
+
rspec (~> 3.13)
|
|
131
|
+
rubocop (~> 1.88)
|
|
132
|
+
rubocop-rspec (~> 3.10)
|
|
131
133
|
simplecov
|
|
132
|
-
webmock (~> 3.
|
|
134
|
+
webmock (~> 3.26)
|
|
133
135
|
|
|
134
136
|
BUNDLED WITH
|
|
135
|
-
4.0.
|
|
137
|
+
4.0.20
|