python-bsblan 4.2.0__tar.gz → 5.0.0__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 (104) hide show
  1. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/copilot-instructions.md +17 -21
  2. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/prompts/add-parameter.prompt.md +1 -1
  3. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/prompts/code-review.prompt.md +3 -3
  4. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/skills/bsblan-parameters/SKILL.md +4 -5
  5. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/skills/bsblan-testing/SKILL.md +2 -3
  6. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/workflows/codeql.yaml +2 -2
  7. python_bsblan-5.0.0/.github/workflows/linting.yaml +66 -0
  8. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/workflows/stale.yaml +1 -1
  9. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.pre-commit-config.yaml +0 -6
  10. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/PKG-INFO +7 -8
  11. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/README.md +5 -5
  12. python_bsblan-5.0.0/examples/fetch_param.py +67 -0
  13. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/pyproject.toml +8 -9
  14. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/src/bsblan/__init__.py +2 -0
  15. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/src/bsblan/bsblan.py +9 -9
  16. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/src/bsblan/constants.py +1 -0
  17. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/src/bsblan/models.py +180 -148
  18. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/sensor.json +11 -0
  19. python_bsblan-5.0.0/tests/test_entity_info.py +224 -0
  20. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_entity_info_ha.py +2 -2
  21. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_sensor.py +8 -0
  22. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_state.py +1 -1
  23. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_temperature_unit.py +6 -6
  24. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/uv.lock +209 -286
  25. python_bsblan-4.2.0/.github/workflows/linting.yaml +0 -170
  26. python_bsblan-4.2.0/.github/workflows/typing.yaml +0 -36
  27. python_bsblan-4.2.0/tests/test_entity_info.py +0 -99
  28. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.editorconfig +0 -0
  29. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.gitattributes +0 -0
  30. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/CODE_OF_CONDUCT.md +0 -0
  31. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/CONTRIBUTING.md +0 -0
  32. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md +0 -0
  33. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  34. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  35. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/labels.yml +0 -0
  36. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/release-drafter.yml +0 -0
  37. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/renovate.json +0 -0
  38. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/workflows/auto-approve-renovate.yml +0 -0
  39. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/workflows/labels.yaml +0 -0
  40. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/workflows/lock.yaml +0 -0
  41. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/workflows/pr-labels.yaml +0 -0
  42. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/workflows/release-drafter.yaml +0 -0
  43. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/workflows/release.yaml +0 -0
  44. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.github/workflows/tests.yaml +0 -0
  45. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.gitignore +0 -0
  46. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.nvmrc +0 -0
  47. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.prettierignore +0 -0
  48. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/.yamllint +0 -0
  49. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/AGENTS.md +0 -0
  50. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/CLAUDE.md +0 -0
  51. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/LICENSE.md +0 -0
  52. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/examples/control.py +0 -0
  53. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/examples/discovery.py +0 -0
  54. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/examples/profile_init.py +0 -0
  55. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/examples/ruff.toml +0 -0
  56. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/examples/speed_test.py +0 -0
  57. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/package-lock.json +0 -0
  58. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/package.json +0 -0
  59. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/sonar-project.properties +0 -0
  60. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/src/bsblan/exceptions.py +0 -0
  61. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/src/bsblan/py.typed +0 -0
  62. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/src/bsblan/utility.py +0 -0
  63. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/__init__.py +0 -0
  64. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/conftest.py +0 -0
  65. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/device.json +0 -0
  66. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/dict_version.json +0 -0
  67. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/hot_water_state.json +0 -0
  68. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/info.json +0 -0
  69. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/password.txt +0 -0
  70. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/state.json +0 -0
  71. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/static_state.json +0 -0
  72. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/thermostat_hvac.json +0 -0
  73. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/thermostat_temp.json +0 -0
  74. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/fixtures/time.json +0 -0
  75. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/ruff.toml +0 -0
  76. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_api_initialization.py +0 -0
  77. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_api_validation.py +0 -0
  78. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_auth.py +0 -0
  79. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_backoff_retry.py +0 -0
  80. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_bsblan.py +0 -0
  81. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_bsblan_edge_cases.py +0 -0
  82. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_configuration.py +0 -0
  83. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_constants.py +0 -0
  84. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_context_manager.py +0 -0
  85. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_device.py +0 -0
  86. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_dhw_time_switch.py +0 -0
  87. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_hot_water_additional.py +0 -0
  88. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_hotwater_state.py +0 -0
  89. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_include_parameter.py +0 -0
  90. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_info.py +0 -0
  91. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_initialization.py +0 -0
  92. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_read_parameters.py +0 -0
  93. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_reset_validation.py +0 -0
  94. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_schedule_models.py +0 -0
  95. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_set_hot_water_schedule.py +0 -0
  96. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_set_hotwater.py +0 -0
  97. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_static_state.py +0 -0
  98. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_temperature_validation.py +0 -0
  99. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_thermostat.py +0 -0
  100. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_time.py +0 -0
  101. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_utility.py +0 -0
  102. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_utility_additional.py +0 -0
  103. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_utility_edge_cases.py +0 -0
  104. {python_bsblan-4.2.0 → python_bsblan-5.0.0}/tests/test_version_errors.py +0 -0
@@ -16,15 +16,14 @@ This repository contains the `python-bsblan` library, an asynchronous Python cli
16
16
  Always run these commands after making changes:
17
17
 
18
18
  ```bash
19
- # Run all pre-commit hooks (ruff, mypy, pylint, pytest)
20
- uv run pre-commit run --all-files
19
+ # Run all prek hooks (ruff, mypy, pylint)
20
+ uv run prek run --all-files
21
21
  ```
22
22
 
23
- ### Pre-commit Includes
23
+ ### Prek Includes
24
24
  - **Ruff**: Linting and formatting (88 char line limit)
25
25
  - **MyPy**: Static type checking
26
26
  - **Pylint**: Code analysis
27
- - **Pytest**: Test execution with coverage
28
27
 
29
28
  ### Coverage Requirements
30
29
  - Maintain **95%+ total test coverage**
@@ -77,9 +76,8 @@ Parameters are identified by numeric IDs and mapped to readable names in `consta
77
76
 
78
77
  2. **Add to model in `models.py`**:
79
78
  ```python
80
- @dataclass
81
- class HotWaterConfig(DataClassORJSONMixin):
82
- legionella_function_setpoint: ParameterValue | None = None
79
+ class HotWaterConfig(BaseModel):
80
+ legionella_function_setpoint: EntityInfo[float] | None = None
83
81
  ```
84
82
 
85
83
  3. **Update method in `bsblan.py`** if the parameter is settable:
@@ -129,25 +127,23 @@ Defined in `constants.py`:
129
127
  ## Data Models
130
128
 
131
129
  ### Model Pattern
132
- All models use `mashumaro` for JSON serialization:
130
+ All models use `pydantic` `BaseModel` for validation and serialization:
133
131
 
134
132
  ```python
135
- from dataclasses import dataclass
136
- from mashumaro.mixins.orjson import DataClassORJSONMixin
133
+ from pydantic import BaseModel
137
134
 
138
- @dataclass
139
- class HotWaterConfig(DataClassORJSONMixin):
135
+ class HotWaterConfig(BaseModel):
140
136
  """Hot water configuration parameters."""
141
- operating_mode: ParameterValue | None = None
142
- nominal_setpoint: ParameterValue | None = None
137
+ operating_mode: EntityInfo[int] | None = None
138
+ nominal_setpoint: EntityInfo[float] | None = None
143
139
  ```
144
140
 
145
- ### ParameterValue Structure
146
- Each parameter returns a `ParameterValue` with:
147
- - `value`: The actual value
141
+ ### EntityInfo Structure
142
+ Each parameter returns an `EntityInfo[T]` (generic `BaseModel`) with:
143
+ - `value`: The actual value (typed via generic `T`)
148
144
  - `unit`: Unit of measurement
149
145
  - `desc`: Human-readable description
150
- - `dataType`: Data type information
146
+ - `data_type`: Data type information
151
147
 
152
148
  ## Async Patterns
153
149
 
@@ -223,7 +219,7 @@ Test fixtures (JSON responses) are in `tests/fixtures/`
223
219
  4. Update docstring with parameter description
224
220
  5. Add state preparation logic in `_prepare_*_state()` method
225
221
  6. Add tests for the new parameter
226
- 7. Run `uv run pre-commit run --all-files`
222
+ 7. Run `uv run prek run --all-files`
227
223
 
228
224
  ### Renaming a Parameter
229
225
 
@@ -233,7 +229,7 @@ When renaming parameters for consistency:
233
229
  3. Update `bsblan.py` - method parameters and state handling
234
230
  4. Update `tests/` - all test files using the parameter
235
231
  5. Update `examples/` - any example code
236
- 6. Run `uv run pre-commit run --all-files`
232
+ 6. Run `uv run prek run --all-files`
237
233
 
238
234
  ## API Versions
239
235
 
@@ -245,7 +241,7 @@ Version-specific parameters are handled in `constants.py` with extension diction
245
241
 
246
242
  ## Don't Forget
247
243
 
248
- - ✅ Run `uv run pre-commit run --all-files` after every change
244
+ - ✅ Run `uv run prek run --all-files` after every change
249
245
  - ✅ Maintain 95%+ test coverage
250
246
  - ✅ Use type hints on all functions
251
247
  - ✅ Add docstrings to public methods
@@ -26,7 +26,7 @@ Add a new parameter to the python-bsblan library following the established patte
26
26
 
27
27
  After changes, run:
28
28
  ```bash
29
- uv run pre-commit run --all-files
29
+ uv run prek run --all-files
30
30
  ```
31
31
 
32
32
  Coverage must be 95%+ total and 100% for new code.
@@ -21,12 +21,12 @@ Review the changes against the python-bsblan coding standards.
21
21
  - [ ] Patch coverage 100%
22
22
 
23
23
  ### Patterns
24
- - [ ] Uses `mashumaro` for JSON serialization
24
+ - [ ] Uses `pydantic` `BaseModel` for validation and serialization
25
25
  - [ ] Uses `aiohttp` for async HTTP
26
26
  - [ ] Follows existing parameter naming conventions
27
27
  - [ ] Error handling uses custom exceptions (`BSBLANError`, `BSBLANConnectionError`)
28
28
 
29
- ### Pre-commit
29
+ ### Prek
30
30
  - [ ] Ruff passes (linting + formatting)
31
31
  - [ ] MyPy passes (type checking)
32
32
  - [ ] Pylint passes (code analysis)
@@ -35,6 +35,6 @@ Review the changes against the python-bsblan coding standards.
35
35
  ## Run Validation
36
36
 
37
37
  ```bash
38
- uv run pre-commit run --all-files
38
+ uv run prek run --all-files
39
39
  uv run pytest --cov=src/bsblan --cov-report=term-missing
40
40
  ```
@@ -28,12 +28,11 @@ BASE_HOT_WATER_PARAMS: Final[dict[str, str]] = {
28
28
 
29
29
  ### 2. Add to Model in `models.py`
30
30
 
31
- Add the field to the appropriate dataclass:
31
+ Add the field to the appropriate model:
32
32
 
33
33
  ```python
34
- @dataclass
35
- class HotWaterConfig(DataClassORJSONMixin):
36
- legionella_function_setpoint: ParameterValue | None = None
34
+ class HotWaterConfig(BaseModel):
35
+ legionella_function_setpoint: EntityInfo[float] | None = None
37
36
  ```
38
37
 
39
38
  ### 3. Update Method in `bsblan.py` (if settable)
@@ -100,7 +99,7 @@ When adding new sections or groups, the lock is created automatically on first a
100
99
  Always run after changes:
101
100
 
102
101
  ```bash
103
- uv run pre-commit run --all-files
102
+ uv run prek run --all-files
104
103
  uv run pytest --cov=src/bsblan --cov-report=term-missing
105
104
  ```
106
105
 
@@ -101,12 +101,12 @@ uv run pytest -v
101
101
  uv run pytest tests/test_bsblan.py::test_function_name
102
102
  ```
103
103
 
104
- ## Pre-commit Hooks
104
+ ## Prek Hooks
105
105
 
106
106
  Always run before committing:
107
107
 
108
108
  ```bash
109
- uv run pre-commit run --all-files
109
+ uv run prek run --all-files
110
110
  ```
111
111
 
112
112
  This runs:
@@ -114,7 +114,6 @@ This runs:
114
114
  - **Ruff**: Linting and formatting (88 char line limit)
115
115
  - **MyPy**: Static type checking
116
116
  - **Pylint**: Code analysis
117
- - **Pytest**: Test execution with coverage
118
117
 
119
118
  ## Mock Patterns
120
119
 
@@ -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.2
27
+ uses: github/codeql-action/init@v4.32.4
28
28
  - name: 🚀 Perform CodeQL Analysis
29
- uses: github/codeql-action/analyze@v4.32.2
29
+ uses: github/codeql-action/analyze@v4.32.4
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: Linting
3
+
4
+ # yamllint disable-line rule:truthy
5
+ on:
6
+ push:
7
+ branches: [main]
8
+ pull_request:
9
+ branches: [main]
10
+ workflow_dispatch:
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ env:
16
+ DEFAULT_PYTHON: "3.13"
17
+
18
+ jobs:
19
+ lint:
20
+ name: Prek hooks
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - name: ⤵️ Check out code from GitHub
24
+ uses: actions/checkout@v6.0.2
25
+ - name: 🏗 Set up uv
26
+ uses: astral-sh/setup-uv@v7
27
+ with:
28
+ enable-cache: true
29
+ - name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
30
+ id: python
31
+ uses: actions/setup-python@v6.2.0
32
+ with:
33
+ python-version: ${{ env.DEFAULT_PYTHON }}
34
+ - name: 🏗 Install Python dependencies
35
+ run: uv sync --dev
36
+ - name: 🚀 Run all prek hooks
37
+ env:
38
+ SKIP: no-commit-to-branch
39
+ run: uv run prek run --all-files
40
+
41
+ prettier:
42
+ name: Prettier
43
+ runs-on: ubuntu-latest
44
+ steps:
45
+ - name: ⤵️ Check out code from GitHub
46
+ uses: actions/checkout@v6.0.2
47
+ - name: 🏗 Set up uv
48
+ uses: astral-sh/setup-uv@v7
49
+ with:
50
+ enable-cache: true
51
+ - name: 🏗 Set up Python ${{ env.DEFAULT_PYTHON }}
52
+ id: python
53
+ uses: actions/setup-python@v6.2.0
54
+ with:
55
+ python-version: ${{ env.DEFAULT_PYTHON }}
56
+ - name: 🏗 Install Python dependencies
57
+ run: uv sync --dev
58
+ - name: 🏗 Set up Node.js
59
+ uses: actions/setup-node@v6.2.0
60
+ with:
61
+ node-version-file: ".nvmrc"
62
+ cache: "npm"
63
+ - name: 🏗 Install NPM dependencies
64
+ run: npm install
65
+ - name: 🚀 Run prettier
66
+ run: uv run prek run prettier --all-files
@@ -17,7 +17,7 @@ jobs:
17
17
  runs-on: ubuntu-latest
18
18
  steps:
19
19
  - name: 🚀 Run stale
20
- uses: actions/stale@v10.1.1
20
+ uses: actions/stale@v10.2.0
21
21
  with:
22
22
  repo-token: ${{ secrets.GITHUB_TOKEN }}
23
23
  days-before-stale: 30
@@ -109,12 +109,6 @@ repos:
109
109
  language: system
110
110
  types: [python]
111
111
  entry: uv run pylint --exit-zero
112
- - id: pytest
113
- name: 🧪 Running tests and test coverage with pytest
114
- language: system
115
- types: [python]
116
- entry: uv run pytest
117
- pass_filenames: false
118
112
  - id: trailing-whitespace
119
113
  name: ✄ Trim Trailing Whitespace
120
114
  language: system
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-bsblan
3
- Version: 4.2.0
3
+ Version: 5.0.0
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
@@ -25,9 +25,8 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
25
  Requires-Python: >=3.12
26
26
  Requires-Dist: aiohttp>=3.8.1
27
27
  Requires-Dist: backoff>=2.2.1
28
- Requires-Dist: mashumaro>=3.13.1
29
- Requires-Dist: orjson>=3.9.10
30
28
  Requires-Dist: packaging>=21.3
29
+ Requires-Dist: pydantic>=2.0
31
30
  Requires-Dist: yarl>=1.7.2
32
31
  Description-Content-Type: text/markdown
33
32
 
@@ -186,12 +185,12 @@ npm install
186
185
  uv sync --dev
187
186
  ```
188
187
 
189
- As this repository uses the [pre-commit][pre-commit] framework, all changes
190
- are linted and tested with each commit. You can run all checks and tests
191
- manually, using the following command:
188
+ As this repository uses [prek][prek] (a faster, Rust-based drop-in replacement
189
+ for pre-commit), all changes are linted and tested with each commit. You can
190
+ run all checks and tests manually, using the following command:
192
191
 
193
192
  ```bash
194
- uv run pre-commit run --all-files
193
+ uv run prek run --all-files
195
194
  ```
196
195
 
197
196
  To run just the Python tests:
@@ -247,7 +246,7 @@ SOFTWARE.
247
246
  [maintenance-shield]: https://img.shields.io/maintenance/yes/2026.svg
248
247
  [uv]: https://docs.astral.sh/uv/
249
248
  [uv-install]: https://docs.astral.sh/uv/getting-started/installation/
250
- [pre-commit]: https://pre-commit.com/
249
+ [prek]: https://github.com/j178/prek
251
250
  [project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg
252
251
  [pypi]: https://pypi.org/project/python-bsblan/
253
252
  [python-versions-shield]: https://img.shields.io/pypi/pyversions/python-bsblan
@@ -153,12 +153,12 @@ npm install
153
153
  uv sync --dev
154
154
  ```
155
155
 
156
- As this repository uses the [pre-commit][pre-commit] framework, all changes
157
- are linted and tested with each commit. You can run all checks and tests
158
- manually, using the following command:
156
+ As this repository uses [prek][prek] (a faster, Rust-based drop-in replacement
157
+ for pre-commit), all changes are linted and tested with each commit. You can
158
+ run all checks and tests manually, using the following command:
159
159
 
160
160
  ```bash
161
- uv run pre-commit run --all-files
161
+ uv run prek run --all-files
162
162
  ```
163
163
 
164
164
  To run just the Python tests:
@@ -214,7 +214,7 @@ SOFTWARE.
214
214
  [maintenance-shield]: https://img.shields.io/maintenance/yes/2026.svg
215
215
  [uv]: https://docs.astral.sh/uv/
216
216
  [uv-install]: https://docs.astral.sh/uv/getting-started/installation/
217
- [pre-commit]: https://pre-commit.com/
217
+ [prek]: https://github.com/j178/prek
218
218
  [project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg
219
219
  [pypi]: https://pypi.org/project/python-bsblan/
220
220
  [python-versions-shield]: https://img.shields.io/pypi/pyversions/python-bsblan
@@ -0,0 +1,67 @@
1
+ """Fetch one or more BSB-LAN parameters and print the raw API response.
2
+
3
+ Usage:
4
+ export BSBLAN_HOST=10.0.2.60
5
+ export BSBLAN_PASSKEY=your_passkey # if needed
6
+
7
+ # Single parameter
8
+ cd examples && python fetch_param.py 3113
9
+
10
+ # Multiple parameters
11
+ cd examples && python fetch_param.py 3113 8700 8740
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import argparse
17
+ import asyncio
18
+ import json
19
+
20
+ from bsblan import BSBLAN, BSBLANConfig
21
+ from discovery import get_bsblan_host, get_config_from_env
22
+
23
+
24
+ async def fetch_parameters(param_ids: list[str]) -> None:
25
+ """Fetch and print raw API response for the given parameter IDs.
26
+
27
+ Args:
28
+ param_ids: List of BSB-LAN parameter IDs to fetch.
29
+
30
+ """
31
+ host, port = await get_bsblan_host()
32
+ env = get_config_from_env()
33
+
34
+ config = BSBLANConfig(
35
+ host=host,
36
+ port=port,
37
+ passkey=str(env["passkey"]) if env.get("passkey") else None,
38
+ username=str(env["username"]) if env.get("username") else None,
39
+ password=str(env["password"]) if env.get("password") else None,
40
+ )
41
+
42
+ params_string = ",".join(param_ids)
43
+
44
+ async with BSBLAN(config) as client:
45
+ result = await client._request( # noqa: SLF001
46
+ params={"Parameter": params_string},
47
+ )
48
+ print(f"Raw API response for parameter(s) {params_string}:")
49
+ print(json.dumps(result, indent=2, ensure_ascii=False))
50
+
51
+
52
+ def main() -> None:
53
+ """Parse arguments and run the fetch."""
54
+ parser = argparse.ArgumentParser(
55
+ description="Fetch BSB-LAN parameters and print raw JSON response.",
56
+ )
57
+ parser.add_argument(
58
+ "params",
59
+ nargs="+",
60
+ help="One or more BSB-LAN parameter IDs (e.g. 3113 8700)",
61
+ )
62
+ args = parser.parse_args()
63
+ asyncio.run(fetch_parameters(args.params))
64
+
65
+
66
+ if __name__ == "__main__":
67
+ main()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-bsblan"
3
- version = "4.2.0"
3
+ version = "5.0.0"
4
4
  description = "Asynchronous Python client for BSBLAN API"
5
5
  authors = [
6
6
  {name = "Willem-Jan van Rootselaar", email = "liudgervr@gmail.com"}
@@ -29,8 +29,7 @@ dependencies = [
29
29
  "yarl>=1.7.2",
30
30
  "packaging>=21.3",
31
31
  "backoff>=2.2.1",
32
- "mashumaro>=3.13.1",
33
- "orjson>=3.9.10",
32
+ "pydantic>=2.0",
34
33
  ]
35
34
 
36
35
  [project.urls]
@@ -148,7 +147,7 @@ ignore = [
148
147
  "D213", # Conflicts with other rules
149
148
  "D417", # False positives in some occasions
150
149
  "PLR2004", # Just annoying, not really useful
151
-
150
+ "UP046", # Pydantic v2 does not support PEP 695 type parameter syntax
152
151
  # Conflicts with the Ruff formatter
153
152
  "COM812",
154
153
  "ISC001",
@@ -162,7 +161,7 @@ fixture-parentheses = false
162
161
  known-first-party = ["bsblan"]
163
162
 
164
163
  [tool.ruff.lint.flake8-type-checking]
165
- runtime-evaluated-base-classes = ["mashumaro.mixins.orjson.DataClassORJSONMixin"]
164
+ runtime-evaluated-base-classes = ["pydantic.BaseModel"]
166
165
 
167
166
  [tool.ruff.lint.mccabe]
168
167
  max-complexity = 25
@@ -189,16 +188,16 @@ dev = [
189
188
  # hatch is required to support type hinting and proper packaging of the py.typed file.
190
189
  "hatch>=1.14.1",
191
190
  "isort==7.0.0",
192
- "ty==0.0.16",
193
- "pre-commit==4.5.1",
191
+ "ty==0.0.18",
192
+ "prek>=0.3.3",
194
193
  "pre-commit-hooks==6.0.0",
195
- "pylint==4.0.4",
194
+ "pylint==4.0.5",
196
195
  "pytest>=8.3.5",
197
196
  "pytest-asyncio==1.3.0",
198
197
  "pytest-cov==7.0.0",
199
198
  "pytest-xdist>=3.8.0",
200
199
  "pyupgrade==3.21.2",
201
- "ruff==0.15.0",
200
+ "ruff==0.15.2",
202
201
  "safety==3.7.0",
203
202
  "vulture==2.14",
204
203
  "yamllint==1.38.0",
@@ -16,6 +16,7 @@ from .models import (
16
16
  DHWSchedule,
17
17
  DHWTimeSwitchPrograms,
18
18
  EntityInfo,
19
+ EntityValue,
19
20
  HotWaterConfig,
20
21
  HotWaterSchedule,
21
22
  HotWaterState,
@@ -41,6 +42,7 @@ __all__ = [
41
42
  "Device",
42
43
  "DeviceTime",
43
44
  "EntityInfo",
45
+ "EntityValue",
44
46
  "HVACActionCategory",
45
47
  "HeatingCircuitStatus",
46
48
  "HotWaterConfig",
@@ -524,8 +524,8 @@ class BSBLAN:
524
524
  try:
525
525
  static_values = await self.static_values()
526
526
  if static_values.min_temp is not None:
527
- self._min_temp = float(static_values.min_temp.value)
528
- logger.debug("Min temperature initialized: %f", self._min_temp)
527
+ self._min_temp = static_values.min_temp.value
528
+ logger.debug("Min temperature initialized: %s", self._min_temp)
529
529
  else:
530
530
  logger.warning(
531
531
  "min_temp not available from device, "
@@ -533,8 +533,8 @@ class BSBLAN:
533
533
  )
534
534
 
535
535
  if static_values.max_temp is not None:
536
- self._max_temp = float(static_values.max_temp.value)
537
- logger.debug("Max temperature initialized: %f", self._max_temp)
536
+ self._max_temp = static_values.max_temp.value
537
+ logger.debug("Max temperature initialized: %s", self._max_temp)
538
538
  else:
539
539
  logger.warning(
540
540
  "max_temp not available from device, "
@@ -841,7 +841,7 @@ class BSBLAN:
841
841
  params = await self._extract_params_summary(section_params)
842
842
  data = await self._request(params={"Parameter": params["string_par"]})
843
843
  data = dict(zip(params["list"], list(data.values()), strict=True))
844
- return model_class.from_dict(data)
844
+ return model_class.model_validate(data)
845
845
 
846
846
  async def state(self, include: list[str] | None = None) -> State:
847
847
  """Get the current state from BSBLAN device.
@@ -911,7 +911,7 @@ class BSBLAN:
911
911
 
912
912
  """
913
913
  device_info = await self._request(base_path="/JI")
914
- return Device.from_dict(device_info)
914
+ return Device.model_validate(device_info)
915
915
 
916
916
  async def info(self, include: list[str] | None = None) -> Info:
917
917
  """Get information about the current heating system config.
@@ -942,7 +942,7 @@ class BSBLAN:
942
942
  data = await self._request(params={"Parameter": "0"})
943
943
  # Create the data dictionary in the expected format
944
944
  time_data = {"time": data["0"]}
945
- return DeviceTime.from_dict(time_data)
945
+ return DeviceTime.model_validate(time_data)
946
946
 
947
947
  async def set_time(self, time_value: str) -> None:
948
948
  """Set the time on the BSB-LAN device.
@@ -1146,7 +1146,7 @@ class BSBLAN:
1146
1146
  params = await self._extract_params_summary(filtered_params)
1147
1147
  data = await self._request(params={"Parameter": params["string_par"]})
1148
1148
  data = dict(zip(params["list"], list(data.values()), strict=True))
1149
- return model_class.from_dict(data)
1149
+ return model_class.model_validate(data)
1150
1150
 
1151
1151
  async def hot_water_state(self, include: list[str] | None = None) -> HotWaterState:
1152
1152
  """Get essential hot water state for frequent polling.
@@ -1436,7 +1436,7 @@ class BSBLAN:
1436
1436
  if param_id in response_data:
1437
1437
  param_data = response_data[param_id]
1438
1438
  if param_data and isinstance(param_data, dict):
1439
- result[param_id] = EntityInfo.from_dict(param_data)
1439
+ result[param_id] = EntityInfo.model_validate(param_data)
1440
1440
 
1441
1441
  return result
1442
1442
 
@@ -41,6 +41,7 @@ BASE_DEVICE_PARAMS: Final[dict[str, str]] = {
41
41
  BASE_SENSOR_PARAMS: Final[dict[str, str]] = {
42
42
  "8700": "outside_temperature",
43
43
  "8740": "current_temperature",
44
+ "3113": "total_energy",
44
45
  }
45
46
 
46
47
  BASE_HOT_WATER_PARAMS: Final[dict[str, str]] = {