python-bsblan 5.1.2__tar.gz → 5.1.3__tar.gz

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.
Files changed (106) hide show
  1. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/renovate.json +1 -2
  2. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/workflows/codeql.yaml +2 -2
  3. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/workflows/release-drafter.yaml +1 -1
  4. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/workflows/tests.yaml +2 -2
  5. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.gitignore +3 -0
  6. python_bsblan-5.1.3/.nvmrc +1 -0
  7. python_bsblan-5.1.3/Makefile +20 -0
  8. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/PKG-INFO +14 -2
  9. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/README.md +13 -1
  10. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/examples/speed_test.py +2 -85
  11. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/pyproject.toml +16 -8
  12. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/src/bsblan/bsblan.py +13 -15
  13. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/src/bsblan/constants.py +5 -53
  14. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/src/bsblan/models.py +0 -1
  15. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/dict_version.json +1 -2
  16. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/state.json +0 -12
  17. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/state_circuit2.json +2 -14
  18. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_api_validation.py +0 -2
  19. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_circuit.py +12 -110
  20. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_constants.py +0 -2
  21. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_hotwater_state.py +0 -2
  22. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_initialization.py +0 -2
  23. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_state.py +1 -6
  24. python_bsblan-5.1.2/.nvmrc +0 -1
  25. python_bsblan-5.1.2/uv.lock +0 -2381
  26. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.editorconfig +0 -0
  27. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.gitattributes +0 -0
  28. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/CODE_OF_CONDUCT.md +0 -0
  29. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/CONTRIBUTING.md +0 -0
  30. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md +0 -0
  31. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  32. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  33. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/copilot-instructions.md +0 -0
  34. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/labels.yml +0 -0
  35. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/prompts/add-parameter.prompt.md +0 -0
  36. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/prompts/code-review.prompt.md +0 -0
  37. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/release-drafter.yml +0 -0
  38. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/skills/bsblan-parameters/SKILL.md +0 -0
  39. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/skills/bsblan-testing/SKILL.md +0 -0
  40. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/workflows/auto-approve-renovate.yml +0 -0
  41. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/workflows/labels.yaml +0 -0
  42. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/workflows/linting.yaml +0 -0
  43. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/workflows/lock.yaml +0 -0
  44. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/workflows/pr-labels.yaml +0 -0
  45. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/workflows/release.yaml +0 -0
  46. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.github/workflows/stale.yaml +0 -0
  47. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.pre-commit-config.yaml +0 -0
  48. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.prettierignore +0 -0
  49. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/.yamllint +0 -0
  50. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/AGENTS.md +0 -0
  51. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/CLAUDE.md +0 -0
  52. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/LICENSE.md +0 -0
  53. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/examples/control.py +0 -0
  54. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/examples/discovery.py +0 -0
  55. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/examples/fetch_param.py +0 -0
  56. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/examples/profile_init.py +0 -0
  57. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/examples/ruff.toml +0 -0
  58. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/package-lock.json +0 -0
  59. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/package.json +0 -0
  60. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/sonar-project.properties +0 -0
  61. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/src/bsblan/__init__.py +0 -0
  62. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/src/bsblan/exceptions.py +0 -0
  63. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/src/bsblan/py.typed +0 -0
  64. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/src/bsblan/utility.py +0 -0
  65. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/__init__.py +0 -0
  66. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/conftest.py +0 -0
  67. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/device.json +0 -0
  68. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/hot_water_state.json +0 -0
  69. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/info.json +0 -0
  70. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/password.txt +0 -0
  71. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/sensor.json +0 -0
  72. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/static_state.json +0 -0
  73. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/static_state_circuit2.json +0 -0
  74. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/thermostat_hvac.json +0 -0
  75. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/thermostat_temp.json +0 -0
  76. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/fixtures/time.json +0 -0
  77. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/ruff.toml +0 -0
  78. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_api_initialization.py +0 -0
  79. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_auth.py +0 -0
  80. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_backoff_retry.py +0 -0
  81. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_bsblan.py +0 -0
  82. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_bsblan_edge_cases.py +0 -0
  83. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_configuration.py +0 -0
  84. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_context_manager.py +0 -0
  85. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_device.py +0 -0
  86. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_dhw_time_switch.py +0 -0
  87. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_entity_info.py +0 -0
  88. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_entity_info_ha.py +0 -0
  89. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_hot_water_additional.py +0 -0
  90. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_include_parameter.py +0 -0
  91. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_info.py +0 -0
  92. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_read_parameters.py +0 -0
  93. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_reset_validation.py +0 -0
  94. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_schedule_models.py +0 -0
  95. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_sensor.py +0 -0
  96. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_set_hot_water_schedule.py +0 -0
  97. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_set_hotwater.py +0 -0
  98. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_static_state.py +0 -0
  99. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_temperature_unit.py +0 -0
  100. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_temperature_validation.py +0 -0
  101. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_thermostat.py +0 -0
  102. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_time.py +0 -0
  103. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_utility.py +0 -0
  104. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_utility_additional.py +0 -0
  105. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_utility_edge_cases.py +0 -0
  106. {python_bsblan-5.1.2 → python_bsblan-5.1.3}/tests/test_version_errors.py +0 -0
@@ -6,8 +6,7 @@
6
6
  "labels": ["dependencies", "no-stale"],
7
7
  "platformAutomerge": true,
8
8
  "lockFileMaintenance": {
9
- "enabled": true,
10
- "automerge": true
9
+ "enabled": false
11
10
  },
12
11
  "commitMessagePrefix": "⬆️",
13
12
  "packageRules": [
@@ -24,6 +24,6 @@ jobs:
24
24
  - name: ⤵️ Check out code from GitHub
25
25
  uses: actions/checkout@v6.0.2
26
26
  - name: 🏗 Initialize CodeQL
27
- uses: github/codeql-action/init@v4.32.5
27
+ uses: github/codeql-action/init@v4.34.1
28
28
  - name: 🚀 Perform CodeQL Analysis
29
- uses: github/codeql-action/analyze@v4.32.5
29
+ uses: github/codeql-action/analyze@v4.34.1
@@ -34,7 +34,7 @@ jobs:
34
34
  runs-on: ubuntu-latest
35
35
  steps:
36
36
  - name: 🚀 Run Release Drafter
37
- uses: release-drafter/release-drafter@v6.2.0
37
+ uses: release-drafter/release-drafter@v7.1.1
38
38
  with:
39
39
  prerelease: ${{ github.event.inputs.prerelease == 'true' }}
40
40
  prerelease-identifier: ${{ github.event.inputs.prerelease_identifier }}
@@ -58,7 +58,7 @@ jobs:
58
58
  with:
59
59
  fetch-depth: 0
60
60
  - name: ⬇️ Download coverage data
61
- uses: actions/download-artifact@v8.0.0
61
+ uses: actions/download-artifact@v8.0.1
62
62
  - name: 🏗 Set up uv
63
63
  uses: astral-sh/setup-uv@v7
64
64
  with:
@@ -76,7 +76,7 @@ jobs:
76
76
  uv run python -m coverage xml -i
77
77
  - name: 🚀 Upload coverage report
78
78
  if: env.HAS_CODECOV_TOKEN == 'true'
79
- uses: codecov/codecov-action@v5.5.2
79
+ uses: codecov/codecov-action@v5.5.3
80
80
  with:
81
81
  token: ${{ secrets.CODECOV_TOKEN }}
82
82
  - name: ℹ️ Skip Codecov upload (missing token)
@@ -94,6 +94,9 @@ ENV/
94
94
  # ruff
95
95
  .ruff_cache
96
96
 
97
+ # uv
98
+ uv.lock
99
+
97
100
  # Visual Studio Code
98
101
  .vscode
99
102
 
@@ -0,0 +1 @@
1
+ 24.14.1
@@ -0,0 +1,20 @@
1
+ .PHONY: setup lint test coverage help
2
+
3
+ .DEFAULT_GOAL := help
4
+
5
+ help: ## Show this help message
6
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
7
+ awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
8
+
9
+ setup: ## Install dev dependencies and git hooks
10
+ uv sync --dev
11
+ uv run prek install
12
+
13
+ lint: ## Run all pre-commit hooks
14
+ uv run prek run --all-files
15
+
16
+ test: ## Run tests
17
+ uv run pytest
18
+
19
+ coverage: ## Run tests with coverage report
20
+ uv run pytest --cov=src/bsblan --cov-report=term-missing
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-bsblan
3
- Version: 5.1.2
3
+ Version: 5.1.3
4
4
  Summary: Asynchronous Python client for BSBLAN API
5
5
  Project-URL: Homepage, https://github.com/liudger/python-bsblan
6
6
  Project-URL: Repository, https://github.com/liudger/python-bsblan
@@ -182,9 +182,21 @@ To install all packages, including all development requirements:
182
182
 
183
183
  ```bash
184
184
  npm install
185
- uv sync --dev
185
+ make setup
186
186
  ```
187
187
 
188
+ ### Makefile Targets
189
+
190
+ A `Makefile` is provided for common development tasks. Run `make help` to
191
+ see all available targets:
192
+
193
+ | Command | Description |
194
+ |-----------------|--------------------------------------|
195
+ | `make setup` | Install dev dependencies & git hooks |
196
+ | `make lint` | Run all pre-commit hooks |
197
+ | `make test` | Run tests |
198
+ | `make coverage` | Run tests with coverage report |
199
+
188
200
  As this repository uses [prek][prek] (a faster, Rust-based drop-in replacement
189
201
  for pre-commit), all changes are linted and tested with each commit. You can
190
202
  run all checks and tests manually, using the following command:
@@ -150,9 +150,21 @@ To install all packages, including all development requirements:
150
150
 
151
151
  ```bash
152
152
  npm install
153
- uv sync --dev
153
+ make setup
154
154
  ```
155
155
 
156
+ ### Makefile Targets
157
+
158
+ A `Makefile` is provided for common development tasks. Run `make help` to
159
+ see all available targets:
160
+
161
+ | Command | Description |
162
+ |-----------------|--------------------------------------|
163
+ | `make setup` | Install dev dependencies & git hooks |
164
+ | `make lint` | Run all pre-commit hooks |
165
+ | `make test` | Run tests |
166
+ | `make coverage` | Run tests with coverage report |
167
+
156
168
  As this repository uses [prek][prek] (a faster, Rust-based drop-in replacement
157
169
  for pre-commit), all changes are linted and tested with each commit. You can
158
170
  run all checks and tests manually, using the following command:
@@ -4,7 +4,6 @@ Compares different approaches using pluggable benchmark suites:
4
4
  - basic: Original tests (parallel calls, read_parameters, filtering)
5
5
  - scalability: Large parameter set tests
6
6
  - dual-circuit: Single vs parallel calls for dual heating circuit params
7
- - triple-circuit: Same idea extended to 3 heating circuits
8
7
  - hot-water: Hot water parameter group loading tests
9
8
 
10
9
  Usage:
@@ -56,29 +55,20 @@ STATIC_PARAMS = ["714", "716"] # Min/max temp setpoints
56
55
  ALL_PARAMS = INFO_PARAMS + STATIC_PARAMS
57
56
 
58
57
  # Heating circuit 1 (700-series)
59
- HC1_PARAMS = ["700", "710", "900", "8000", "8740", "8749"]
58
+ HC1_PARAMS = ["700", "710", "900", "8000", "8740"]
60
59
 
61
60
  # Heating circuit 2 (1000-series) — mirrors HC1 with offset
62
- HC2_PARAMS = ["1000", "1010", "1200", "8001", "8741", "8750"]
63
-
64
- # Heating circuit 3 (1300-series) — mirrors HC1 with offset
65
- HC3_PARAMS = ["1300", "1310", "1500", "8002", "8742", "8751"]
61
+ HC2_PARAMS = ["1000", "1010", "1200", "8001", "8770"]
66
62
 
67
63
  # Static values per circuit
68
64
  HC1_STATIC_PARAMS = ["714", "716"]
69
65
  HC2_STATIC_PARAMS = ["1014", "1016"]
70
- HC3_STATIC_PARAMS = ["1314", "1316"]
71
66
 
72
67
  # Combined dual circuit parameter sets
73
68
  DUAL_HEATING_PARAMS = HC1_PARAMS + HC2_PARAMS
74
69
  DUAL_STATIC_PARAMS = HC1_STATIC_PARAMS + HC2_STATIC_PARAMS
75
70
  DUAL_ALL_PARAMS = DUAL_HEATING_PARAMS + DUAL_STATIC_PARAMS
76
71
 
77
- # Triple circuit parameter sets
78
- TRIPLE_HEATING_PARAMS = HC1_PARAMS + HC2_PARAMS + HC3_PARAMS
79
- TRIPLE_STATIC_PARAMS = HC1_STATIC_PARAMS + HC2_STATIC_PARAMS + HC3_STATIC_PARAMS
80
- TRIPLE_ALL_PARAMS = TRIPLE_HEATING_PARAMS + TRIPLE_STATIC_PARAMS
81
-
82
72
  # Sensor parameters
83
73
  SENSOR_PARAMS = ["8700", "8740"]
84
74
 
@@ -515,78 +505,6 @@ def build_dual_circuit_suite(bsblan: BSBLAN) -> BenchmarkSuite:
515
505
  return suite
516
506
 
517
507
 
518
- def build_triple_circuit_suite(bsblan: BSBLAN) -> BenchmarkSuite:
519
- """Build the triple heating circuit benchmark suite.
520
-
521
- Same idea as dual-circuit but for 3 circuits. Most systems have
522
- at most 2 circuits; HC3 params will return '---' on those
523
- devices but this still measures the network call overhead.
524
- """
525
- suite = BenchmarkSuite(
526
- name="Triple Heating Circuit",
527
- description=(
528
- "Compare fetching strategies for 3 heating circuits.\n"
529
- " HC1: " + ", ".join(HC1_PARAMS) + "\n"
530
- " HC2: " + ", ".join(HC2_PARAMS) + "\n"
531
- " HC3: " + ", ".join(HC3_PARAMS)
532
- ),
533
- )
534
-
535
- suite.add(
536
- (f"HC1+HC2+HC3 combined — 1 call ({len(TRIPLE_HEATING_PARAMS)} params)"),
537
- f"1 call ({len(TRIPLE_HEATING_PARAMS)}p)",
538
- lambda: bsblan.read_parameters(TRIPLE_HEATING_PARAMS),
539
- param_count=len(TRIPLE_HEATING_PARAMS),
540
- )
541
-
542
- suite.add(
543
- "HC1+HC2+HC3 parallel — 3 calls",
544
- "3 parallel",
545
- lambda: asyncio.gather(
546
- bsblan.read_parameters(HC1_PARAMS),
547
- bsblan.read_parameters(HC2_PARAMS),
548
- bsblan.read_parameters(HC3_PARAMS),
549
- ),
550
- param_count=len(TRIPLE_HEATING_PARAMS),
551
- )
552
-
553
- async def _sequential_3() -> None:
554
- await bsblan.read_parameters(HC1_PARAMS)
555
- await bsblan.read_parameters(HC2_PARAMS)
556
- await bsblan.read_parameters(HC3_PARAMS)
557
-
558
- suite.add(
559
- "HC1+HC2+HC3 sequential — 3 calls",
560
- "3 sequential",
561
- _sequential_3,
562
- param_count=len(TRIPLE_HEATING_PARAMS),
563
- )
564
-
565
- # Full init with static values
566
- suite.add(
567
- (f"All circuits + static — 1 call ({len(TRIPLE_ALL_PARAMS)} params)"),
568
- f"1 call all ({len(TRIPLE_ALL_PARAMS)}p)",
569
- lambda: bsblan.read_parameters(TRIPLE_ALL_PARAMS),
570
- param_count=len(TRIPLE_ALL_PARAMS),
571
- )
572
-
573
- suite.add(
574
- "All circuits + static — 6 parallel (heat+static per circ)",
575
- "6 parallel per section",
576
- lambda: asyncio.gather(
577
- bsblan.read_parameters(HC1_PARAMS),
578
- bsblan.read_parameters(HC2_PARAMS),
579
- bsblan.read_parameters(HC3_PARAMS),
580
- bsblan.read_parameters(HC1_STATIC_PARAMS),
581
- bsblan.read_parameters(HC2_STATIC_PARAMS),
582
- bsblan.read_parameters(HC3_STATIC_PARAMS),
583
- ),
584
- param_count=len(TRIPLE_ALL_PARAMS),
585
- )
586
-
587
- return suite
588
-
589
-
590
508
  def build_hot_water_suite(bsblan: BSBLAN) -> BenchmarkSuite:
591
509
  """Build the hot water parameter benchmark suite."""
592
510
  suite = BenchmarkSuite(
@@ -645,7 +563,6 @@ SUITE_BUILDERS: dict[str, Callable[[BSBLAN], BenchmarkSuite]] = {
645
563
  "basic": build_basic_suite,
646
564
  "scalability": build_scalability_suite,
647
565
  "dual-circuit": build_dual_circuit_suite,
648
- "triple-circuit": build_triple_circuit_suite,
649
566
  "hot-water": build_hot_water_suite,
650
567
  }
651
568
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-bsblan"
3
- version = "5.1.2"
3
+ version = "5.1.3"
4
4
  description = "Asynchronous Python client for BSBLAN API"
5
5
  authors = [
6
6
  {name = "Willem-Jan van Rootselaar", email = "liudgervr@gmail.com"}
@@ -166,6 +166,14 @@ runtime-evaluated-base-classes = ["pydantic.BaseModel"]
166
166
  [tool.ruff.lint.mccabe]
167
167
  max-complexity = 25
168
168
 
169
+ [[tool.ty.overrides]]
170
+ include = ["tests/**", "**/test_*.py", "examples/**"]
171
+
172
+ [tool.ty.overrides.rules]
173
+ invalid-assignment = "ignore"
174
+ invalid-argument-type = "ignore"
175
+ unresolved-attribute = "ignore"
176
+
169
177
  [tool.codespell]
170
178
  ignore-words-list = "astroid"
171
179
 
@@ -177,29 +185,29 @@ build-backend = "hatchling.build"
177
185
  dev = [
178
186
  "aresponses==3.0.0",
179
187
  "bandit==1.9.4",
180
- "black==26.1.0",
188
+ "black==26.3.1",
181
189
  "blacken-docs==1.20.0",
182
- "codespell==2.4.1",
190
+ "codespell==2.4.2",
183
191
  "covdefaults==2.3.0",
184
- "coverage==7.13.4",
192
+ "coverage==7.13.5",
185
193
  "darglint==1.8.1",
186
194
  "flake8==7.3.0",
187
195
  "flake8-simplify==0.30.0",
188
196
  # hatch is required to support type hinting and proper packaging of the py.typed file.
189
197
  "hatch>=1.14.1",
190
198
  "isort==8.0.1",
191
- "ty==0.0.20",
199
+ "ty==0.0.25",
192
200
  "prek>=0.3.3",
193
201
  "pre-commit-hooks==6.0.0",
194
202
  "pylint==4.0.5",
195
203
  "pytest>=8.3.5",
196
204
  "pytest-asyncio==1.3.0",
197
- "pytest-cov==7.0.0",
205
+ "pytest-cov==7.1.0",
198
206
  "pytest-xdist>=3.8.0",
199
207
  "pyupgrade==3.21.2",
200
- "ruff==0.15.5",
208
+ "ruff==0.15.7",
201
209
  "safety==3.7.0",
202
- "vulture==2.14",
210
+ "vulture==2.15",
203
211
  "yamllint==1.38.0",
204
212
  "zeroconf>=0.148.0",
205
213
  ]
@@ -91,9 +91,7 @@ SectionLiteral = Literal[
91
91
  "sensor",
92
92
  "hot_water",
93
93
  "heating_circuit2",
94
- "heating_circuit3",
95
94
  "staticValues_circuit2",
96
- "staticValues_circuit3",
97
95
  ]
98
96
 
99
97
  # TypeVar for hot water data models
@@ -184,10 +182,10 @@ class BSBLAN:
184
182
  async def get_available_circuits(self) -> list[int]:
185
183
  """Detect which heating circuits are available on the device.
186
184
 
187
- Uses a two-step probe for each circuit (1, 2, 3):
185
+ Uses a two-step probe for each circuit (1, 2):
188
186
  1. Query the operating mode parameter — the response must be
189
187
  non-empty and contain actual data.
190
- 2. Query the status parameter (8000/8001/8002) — an inactive
188
+ 2. Query the status parameter (8000/8001) — an inactive
191
189
  circuit returns ``value="0"`` with ``desc="---"``.
192
190
 
193
191
  A circuit is only considered available when both checks pass.
@@ -614,7 +612,7 @@ class BSBLAN:
614
612
  """Fetch min/max temperature range for a circuit from the device.
615
613
 
616
614
  Args:
617
- circuit: The heating circuit number (1, 2, or 3).
615
+ circuit: The heating circuit number (1 or 2).
618
616
 
619
617
  Returns:
620
618
  dict with 'min' and 'max' keys (values may be None if unavailable).
@@ -661,7 +659,7 @@ class BSBLAN:
661
659
  the staticValues section if not already done.
662
660
 
663
661
  Args:
664
- circuit: The heating circuit number (1, 2, or 3).
662
+ circuit: The heating circuit number (1 or 2).
665
663
 
666
664
  Note: Temperature unit is extracted during heating section validation
667
665
  from the response (parameter 710), so no extra API call is needed here.
@@ -680,7 +678,7 @@ class BSBLAN:
680
678
  self._max_temp = temp_range["max"]
681
679
  self._temperature_range_initialized = True
682
680
  else:
683
- # HC2/HC3 use per-circuit storage
681
+ # HC2 uses per-circuit storage
684
682
  self._circuit_temp_ranges[circuit] = temp_range
685
683
  self._circuit_temp_initialized.add(circuit)
686
684
 
@@ -1010,9 +1008,9 @@ class BSBLAN:
1010
1008
  fetches all state parameters. Valid names include:
1011
1009
  hvac_mode, target_temperature, hvac_action,
1012
1010
  hvac_mode_changeover, current_temperature,
1013
- room1_thermostat_mode, room1_temp_setpoint_boost.
1014
- circuit: The heating circuit number (1, 2, or 3). Defaults to 1.
1015
- Circuit 2 and 3 use separate parameter IDs but return the
1011
+ room1_temp_setpoint_boost.
1012
+ circuit: The heating circuit number (1 or 2). Defaults to 1.
1013
+ Circuit 2 uses separate parameter IDs but returns the
1016
1014
  same State model with the same field names.
1017
1015
 
1018
1016
  Returns:
@@ -1065,7 +1063,7 @@ class BSBLAN:
1065
1063
  include: Optional list of parameter names to fetch. If None,
1066
1064
  fetches all static parameters. Valid names include:
1067
1065
  min_temp, max_temp.
1068
- circuit: The heating circuit number (1, 2, or 3). Defaults to 1.
1066
+ circuit: The heating circuit number (1 or 2). Defaults to 1.
1069
1067
 
1070
1068
  Returns:
1071
1069
  StaticState: The static information from the BSBLAN device.
@@ -1157,7 +1155,7 @@ class BSBLAN:
1157
1155
  target_temperature (str | None): The target temperature to set.
1158
1156
  hvac_mode (int | None): The HVAC mode to set as raw integer value.
1159
1157
  Valid values: 0=off, 1=auto, 2=eco, 3=heat.
1160
- circuit: The heating circuit number (1, 2, or 3). Defaults to 1.
1158
+ circuit: The heating circuit number (1 or 2). Defaults to 1.
1161
1159
 
1162
1160
  Example:
1163
1161
  # Set HC1 temperature
@@ -1194,7 +1192,7 @@ class BSBLAN:
1194
1192
  Args:
1195
1193
  target_temperature (str | None): The target temperature to set.
1196
1194
  hvac_mode (int | None): The HVAC mode to set as raw integer.
1197
- circuit: The heating circuit number (1, 2, or 3).
1195
+ circuit: The heating circuit number (1 or 2).
1198
1196
 
1199
1197
  Returns:
1200
1198
  dict[str, Any]: The prepared state for the thermostat.
@@ -1236,7 +1234,7 @@ class BSBLAN:
1236
1234
 
1237
1235
  Args:
1238
1236
  target_temperature (str): The target temperature to validate.
1239
- circuit: The heating circuit number (1, 2, or 3).
1237
+ circuit: The heating circuit number (1 or 2).
1240
1238
 
1241
1239
  Raises:
1242
1240
  BSBLANError: If the temperature range cannot be initialized.
@@ -1254,7 +1252,7 @@ class BSBLAN:
1254
1252
  min_temp = self._min_temp
1255
1253
  max_temp = self._max_temp
1256
1254
  else:
1257
- # HC2/HC3 use per-circuit storage
1255
+ # HC2 uses per-circuit storage
1258
1256
  if circuit not in self._circuit_temp_initialized:
1259
1257
  await self._initialize_temperature_range(circuit)
1260
1258
 
@@ -7,8 +7,8 @@ from typing import Final, TypedDict
7
7
 
8
8
  # Supported heating circuits (1-based)
9
9
  MIN_CIRCUIT: Final[int] = 1
10
- MAX_CIRCUIT: Final[int] = 3
11
- VALID_CIRCUITS: Final[set[int]] = {1, 2, 3}
10
+ MAX_CIRCUIT: Final[int] = 2
11
+ VALID_CIRCUITS: Final[set[int]] = {1, 2}
12
12
 
13
13
 
14
14
  # API Versions
@@ -20,11 +20,9 @@ class APIConfig(TypedDict):
20
20
  device: dict[str, str]
21
21
  sensor: dict[str, str]
22
22
  hot_water: dict[str, str]
23
- # Multi-circuit sections (heating circuit 2 and 3)
23
+ # Multi-circuit sections (heating circuit 2)
24
24
  heating_circuit2: dict[str, str]
25
- heating_circuit3: dict[str, str]
26
25
  staticValues_circuit2: dict[str, str]
27
- staticValues_circuit3: dict[str, str]
28
26
 
29
27
 
30
28
  # Base parameters that exist in all API versions
@@ -35,7 +33,6 @@ BASE_HEATING_PARAMS: Final[dict[str, str]] = {
35
33
  # -------
36
34
  "8000": "hvac_action",
37
35
  "8740": "current_temperature",
38
- "8749": "room1_thermostat_mode",
39
36
  }
40
37
 
41
38
  BASE_STATIC_VALUES_PARAMS: Final[dict[str, str]] = {
@@ -111,8 +108,7 @@ BASE_HEATING_CIRCUIT2_PARAMS: Final[dict[str, str]] = {
111
108
  "1200": "hvac_mode_changeover",
112
109
  # -------
113
110
  "8001": "hvac_action",
114
- "8741": "current_temperature",
115
- "8750": "room1_thermostat_mode",
111
+ "8770": "current_temperature",
116
112
  }
117
113
 
118
114
  BASE_STATIC_VALUES_CIRCUIT2_PARAMS: Final[dict[str, str]] = {
@@ -131,52 +127,21 @@ V3_STATIC_VALUES_CIRCUIT2_EXTENSIONS: Final[dict[str, str]] = {
131
127
  "1016": "max_temp",
132
128
  }
133
129
 
134
- # --- Heating Circuit 3 parameters (1300-series) ---
135
- # These mirror HC1 (700-series) with an offset of +600
136
- BASE_HEATING_CIRCUIT3_PARAMS: Final[dict[str, str]] = {
137
- "1300": "hvac_mode",
138
- "1310": "target_temperature",
139
- "1500": "hvac_mode_changeover",
140
- # -------
141
- "8002": "hvac_action",
142
- "8742": "current_temperature",
143
- "8751": "room1_thermostat_mode",
144
- }
145
-
146
- BASE_STATIC_VALUES_CIRCUIT3_PARAMS: Final[dict[str, str]] = {
147
- "1314": "min_temp",
148
- }
149
-
150
- V1_STATIC_VALUES_CIRCUIT3_EXTENSIONS: Final[dict[str, str]] = {
151
- "1330": "max_temp",
152
- }
153
-
154
- V3_HEATING_CIRCUIT3_EXTENSIONS: Final[dict[str, str]] = {
155
- "1370": "room1_temp_setpoint_boost",
156
- }
157
-
158
- V3_STATIC_VALUES_CIRCUIT3_EXTENSIONS: Final[dict[str, str]] = {
159
- "1316": "max_temp",
160
- }
161
-
162
130
  # Mapping from circuit number to section names
163
131
  CIRCUIT_HEATING_SECTIONS: Final[dict[int, str]] = {
164
132
  1: "heating",
165
133
  2: "heating_circuit2",
166
- 3: "heating_circuit3",
167
134
  }
168
135
 
169
136
  CIRCUIT_STATIC_SECTIONS: Final[dict[int, str]] = {
170
137
  1: "staticValues",
171
138
  2: "staticValues_circuit2",
172
- 3: "staticValues_circuit3",
173
139
  }
174
140
 
175
141
  # Mapping from circuit number to thermostat parameter IDs
176
142
  CIRCUIT_THERMOSTAT_PARAMS: Final[dict[int, dict[str, str]]] = {
177
143
  1: {"target_temperature": "710", "hvac_mode": "700"},
178
144
  2: {"target_temperature": "1010", "hvac_mode": "1000"},
179
- 3: {"target_temperature": "1310", "hvac_mode": "1300"},
180
145
  }
181
146
 
182
147
  # Parameter IDs used to probe whether a heating circuit exists on the device.
@@ -184,7 +149,6 @@ CIRCUIT_THERMOSTAT_PARAMS: Final[dict[int, dict[str, str]]] = {
184
149
  CIRCUIT_PROBE_PARAMS: Final[dict[int, str]] = {
185
150
  1: "700",
186
151
  2: "1000",
187
- 3: "1300",
188
152
  }
189
153
 
190
154
  # Status parameter IDs used as a secondary check for circuit availability.
@@ -192,7 +156,6 @@ CIRCUIT_PROBE_PARAMS: Final[dict[int, str]] = {
192
156
  CIRCUIT_STATUS_PARAMS: Final[dict[int, str]] = {
193
157
  1: "8000",
194
158
  2: "8001",
195
- 3: "8002",
196
159
  }
197
160
 
198
161
  # Marker value returned by BSB-LAN for parameters on inactive circuits
@@ -217,9 +180,7 @@ def build_api_config(version: str) -> APIConfig:
217
180
  "hot_water": BASE_HOT_WATER_PARAMS.copy(),
218
181
  # Multi-circuit sections
219
182
  "heating_circuit2": BASE_HEATING_CIRCUIT2_PARAMS.copy(),
220
- "heating_circuit3": BASE_HEATING_CIRCUIT3_PARAMS.copy(),
221
183
  "staticValues_circuit2": BASE_STATIC_VALUES_CIRCUIT2_PARAMS.copy(),
222
- "staticValues_circuit3": BASE_STATIC_VALUES_CIRCUIT3_PARAMS.copy(),
223
184
  }
224
185
 
225
186
  if version == "v1":
@@ -227,9 +188,6 @@ def build_api_config(version: str) -> APIConfig:
227
188
  config["staticValues_circuit2"].update(
228
189
  V1_STATIC_VALUES_CIRCUIT2_EXTENSIONS,
229
190
  )
230
- config["staticValues_circuit3"].update(
231
- V1_STATIC_VALUES_CIRCUIT3_EXTENSIONS,
232
- )
233
191
  elif version == "v3":
234
192
  config["heating"].update(V3_HEATING_EXTENSIONS)
235
193
  config["staticValues"].update(V3_STATIC_VALUES_EXTENSIONS)
@@ -237,10 +195,6 @@ def build_api_config(version: str) -> APIConfig:
237
195
  config["staticValues_circuit2"].update(
238
196
  V3_STATIC_VALUES_CIRCUIT2_EXTENSIONS,
239
197
  )
240
- config["heating_circuit3"].update(V3_HEATING_CIRCUIT3_EXTENSIONS)
241
- config["staticValues_circuit3"].update(
242
- V3_STATIC_VALUES_CIRCUIT3_EXTENSIONS,
243
- )
244
198
 
245
199
  return config
246
200
 
@@ -520,9 +474,7 @@ SESSION_NOT_INITIALIZED_ERROR_MSG: Final[str] = "Session not initialized"
520
474
  API_DATA_NOT_INITIALIZED_ERROR_MSG: Final[str] = "API data not initialized"
521
475
  API_VALIDATOR_NOT_INITIALIZED_ERROR_MSG: Final[str] = "API validator not initialized"
522
476
  SECTION_NOT_FOUND_ERROR_MSG: Final[str] = "Section '{}' not found in API data"
523
- INVALID_CIRCUIT_ERROR_MSG: Final[str] = (
524
- "Invalid circuit number: {}. Must be 1, 2, or 3."
525
- )
477
+ INVALID_CIRCUIT_ERROR_MSG: Final[str] = "Invalid circuit number: {}. Must be 1 or 2."
526
478
  INVALID_RESPONSE_ERROR_MSG: Final[str] = (
527
479
  "Invalid response format from BSB-LAN device: {}"
528
480
  )
@@ -453,7 +453,6 @@ class State(BaseModel):
453
453
  hvac_action: EntityInfo[int] | None = None
454
454
  hvac_mode_changeover: EntityInfo[int] | None = None
455
455
  current_temperature: EntityInfo[float] | None = None
456
- room1_thermostat_mode: EntityInfo[int] | None = None
457
456
  room1_temp_setpoint_boost: EntityInfo[float] | None = None
458
457
 
459
458
 
@@ -4,8 +4,7 @@
4
4
  "710": "target_temperature",
5
5
  "900": "hvac_mode_changeover",
6
6
  "8000": "hvac_action",
7
- "8740": "current_temperature",
8
- "8749": "room1_thermostat_mode"
7
+ "8740": "current_temperature"
9
8
  },
10
9
  "staticValues": {
11
10
  "714": "min_temp",
@@ -61,18 +61,6 @@
61
61
  "readwrite": 1,
62
62
  "unit": "°C"
63
63
  },
64
- "8749": {
65
- "name": "Room thermostat heating circuit 1",
66
- "dataType_name": "ENUM",
67
- "dataType_family": "ENUM",
68
- "error": 0,
69
- "value": "0",
70
- "desc": "No demand",
71
- "dataType": 1,
72
- "readonly": 1,
73
- "readwrite": 1,
74
- "unit": ""
75
- },
76
64
  "770": {
77
65
  "name": "Room temp setpoint boost (boost heating)",
78
66
  "dataType_name": "TEMP",
@@ -48,8 +48,8 @@
48
48
  "readwrite": 1,
49
49
  "unit": ""
50
50
  },
51
- "8741": {
52
- "name": "Room temperature setpoint 1",
51
+ "8770": {
52
+ "name": "Room temperature actual value 2",
53
53
  "dataType_name": "TEMP",
54
54
  "dataType_family": "VALS",
55
55
  "error": 0,
@@ -61,18 +61,6 @@
61
61
  "readwrite": 1,
62
62
  "unit": "°C"
63
63
  },
64
- "8750": {
65
- "name": "Room thermostat heating circuit 2",
66
- "dataType_name": "ENUM",
67
- "dataType_family": "ENUM",
68
- "error": 0,
69
- "value": "0",
70
- "desc": "No demand",
71
- "dataType": 1,
72
- "readonly": 1,
73
- "readwrite": 1,
74
- "unit": ""
75
- },
76
64
  "1070": {
77
65
  "name": "Room temp setpoint boost (boost heating)",
78
66
  "dataType_name": "TEMP",
@@ -372,9 +372,7 @@ async def test_validate_api_section_hot_water_cache() -> None:
372
372
  "device": {},
373
373
  "hot_water": {"1600": "operating_mode", "1610": "nominal_setpoint"},
374
374
  "heating_circuit2": {},
375
- "heating_circuit3": {},
376
375
  "staticValues_circuit2": {},
377
- "staticValues_circuit3": {},
378
376
  }
379
377
  bsblan._api_validator = APIValidator(bsblan._api_data)
380
378