python-bsblan 0.6.4__tar.gz → 0.6.6__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.
@@ -1,23 +1,24 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-bsblan
3
- Version: 0.6.4
4
- Summary: Asynchronous Python client for BSBLAN
3
+ Version: 0.6.6
4
+ Summary: Asynchronous Python client for BSBLAN API
5
5
  Home-page: https://github.com/liudger/python-bsblan
6
6
  License: MIT
7
- Keywords: bsblan,thermostat,client,api
7
+ Keywords: bsblan,thermostat,client,api,async
8
8
  Author: Willem-Jan van Rootselaar
9
9
  Author-email: liudgervr@gmail.com
10
10
  Maintainer: Willem-Jan van Rootselaar
11
11
  Maintainer-email: liudgervr@gmail.com
12
- Requires-Python: >=3.12,<4.0
12
+ Requires-Python: >=3.11,<4.0
13
13
  Classifier: Development Status :: 3 - Alpha
14
14
  Classifier: Framework :: AsyncIO
15
15
  Classifier: Intended Audience :: Developers
16
16
  Classifier: License :: OSI Approved :: MIT License
17
17
  Classifier: Natural Language :: English
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.12
20
19
  Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
21
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
23
  Requires-Dist: aiohttp (>=3.8.1)
23
24
  Requires-Dist: async-timeout (>=4.0.3,<5.0.0)
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "python-bsblan"
3
- version = "0.6.4"
4
- description = "Asynchronous Python client for BSBLAN"
3
+ version = "0.6.6"
4
+ description = "Asynchronous Python client for BSBLAN API"
5
5
  authors = ["Willem-Jan van Rootselaar <liudgervr@gmail.com>"]
6
6
  maintainers = ["Willem-Jan van Rootselaar <liudgervr@gmail.com>"]
7
7
  license = "MIT"
@@ -9,7 +9,7 @@ readme = "README.md"
9
9
  homepage = "https://github.com/liudger/python-bsblan"
10
10
  repository = "https://github.com/liudger/python-bsblan"
11
11
  documentation = "https://github.com/liudger/python-bsblan"
12
- keywords = ["bsblan", "thermostat", "client" , "api"]
12
+ keywords = ["bsblan", "thermostat", "client" , "api", "async"]
13
13
  classifiers = [
14
14
  "Development Status :: 3 - Alpha",
15
15
  "Framework :: AsyncIO",
@@ -25,7 +25,7 @@ packages = [
25
25
  ]
26
26
 
27
27
  [tool.poetry.dependencies]
28
- python = "^3.12"
28
+ python = "^3.11"
29
29
  aiohttp = ">=3.8.1"
30
30
  yarl = ">=1.7.2"
31
31
  packaging = ">=21.3"
@@ -34,9 +34,13 @@ async-timeout = "^4.0.3"
34
34
  mashumaro = "^3.13.1"
35
35
  orjson = "^3.9.10"
36
36
 
37
+ [tool.poetry.urls]
38
+ "Bug Tracker" = "https://github.com/liudger/python-bsblan/issues"
39
+ Changelog = "https://github.com/liudger/python-bsblan/releases"
40
+
37
41
  [tool.poetry.dev-dependencies]
38
42
  covdefaults = "^2.3.0"
39
- ruff = "^0.6.0"
43
+ ruff = "^0.7.0"
40
44
  aresponses = "^3.0.0"
41
45
  black = "^24.0.0"
42
46
  blacken-docs = "^1.13.0"
@@ -44,8 +48,8 @@ coverage = "^7.0.5"
44
48
  flake8 = "^7.0.0"
45
49
  isort = "^5.11.4"
46
50
  mypy = "^1.0.0"
47
- pre-commit = "^3.0.0"
48
- pre-commit-hooks = "^4.3.0"
51
+ pre-commit = "^4.0.0"
52
+ pre-commit-hooks = "^5.0.0"
49
53
  pylint = "^3.0.0"
50
54
  pytest = "^8.0.0"
51
55
  pytest-asyncio = "^0.24.0"
@@ -59,27 +63,21 @@ safety = "^3.0.0"
59
63
  codespell = "^2.2.2"
60
64
  bandit = "^1.7.4"
61
65
 
62
- [tool.poetry.urls]
63
- "Bug Tracker" = "https://github.com/liudger/python-bsblan/issues"
64
- Changelog = "https://github.com/liudger/python-bsblan/releases"
65
-
66
- [tool.coverage.report]
67
- show_missing = true
68
- fail_under = 53
69
66
 
70
67
  [tool.coverage.run]
71
68
  plugins = ["covdefaults"]
72
69
  source = ["bsblan"]
73
70
 
74
- [tool.isort]
75
- profile = "black"
76
- multi_line_output = 3
71
+ [tool.coverage.report]
72
+ show_missing = true
73
+ fail_under = 53
77
74
 
78
75
  [tool.mypy]
79
76
  # Specify the target platform details in config, so your developers are
80
77
  # free to run mypy on Windows, Linux, or macOS and get consistent
81
78
  # results.
82
79
  platform = "linux"
80
+ python_version = "3.11"
83
81
 
84
82
  # show error messages from unrelated files
85
83
  follow_imports = "normal"
@@ -106,9 +104,6 @@ warn_unused_configs = true
106
104
  warn_unused_ignores = true
107
105
 
108
106
  [tool.pylint.MASTER]
109
- extension-pkg-whitelist = [
110
- "pydantic"
111
- ]
112
107
  ignore= [
113
108
  "tests"
114
109
  ]
@@ -128,11 +123,11 @@ good-names = [
128
123
  ]
129
124
 
130
125
  [tool.pylint.DESIGN]
131
- max-attributes = 20
126
+ max-attributes = 10
132
127
 
133
128
  [tool.pylint."MESSAGES CONTROL"]
134
129
  disable= [
135
- "too-few-public-methods",
130
+ # "too-few-public-methods",
136
131
  "duplicate-code",
137
132
  "format",
138
133
  "unsubscriptable-object",
@@ -153,11 +148,16 @@ asyncio_mode = "auto"
153
148
  select = ["ALL"]
154
149
  ignore = [
155
150
  "ANN101", # Self... explanatory
151
+ "ANN102", # cls... Not classy enough
156
152
  "ANN401", # Opinioated warning on disallowing dynamically typed expressions
157
153
  "D203", # Conflicts with other rules
158
154
  "D213", # Conflicts with other rules
159
155
  "D417", # False positives in some occasions
160
156
  "PLR2004", # Just annoying, not really useful
157
+
158
+ # Conflicts with the Ruff formatter
159
+ "COM812",
160
+ "ISC001",
161
161
  ]
162
162
 
163
163
  [tool.ruff.lint.flake8-pytest-style]
@@ -167,9 +167,12 @@ fixture-parentheses = false
167
167
  [tool.ruff.lint.isort]
168
168
  known-first-party = ["bsblan"]
169
169
 
170
+ [tool.ruff.lint.flake8-type-checking]
171
+ runtime-evaluated-base-classes = ["mashumaro.mixins.orjson.DataClassORJSONMixin"]
172
+
170
173
  [tool.ruff.lint.mccabe]
171
174
  max-complexity = 25
172
175
 
173
176
  [build-system]
174
- build-backend = "poetry.core.masonry.api"
175
177
  requires = ["poetry-core>=1.0.0"]
178
+ build-backend = "poetry.core.masonry.api"
@@ -11,7 +11,6 @@ import aiohttp
11
11
  from aiohttp.hdrs import METH_POST
12
12
  from aiohttp.helpers import BasicAuth
13
13
  from packaging import version as pkg_version
14
- from typing_extensions import Self
15
14
  from yarl import URL
16
15
 
17
16
  from .constants import (
@@ -38,6 +37,7 @@ from .models import Device, HotWaterState, Info, Sensor, State, StaticState
38
37
 
39
38
  if TYPE_CHECKING:
40
39
  from aiohttp.client import ClientSession
40
+ from typing_extensions import Self
41
41
 
42
42
  logging.basicConfig(level=logging.DEBUG)
43
43
  logger = logging.getLogger(__name__)
@@ -71,7 +71,12 @@ class BSBLAN:
71
71
  _initialized: bool = False
72
72
 
73
73
  async def __aenter__(self) -> Self:
74
- """Enter the context manager."""
74
+ """Enter the context manager.
75
+
76
+ Returns:
77
+ Self: The initialized BSBLAN instance.
78
+
79
+ """
75
80
  if self.session is None:
76
81
  self.session = aiohttp.ClientSession()
77
82
  self._close_session = True
@@ -79,7 +84,12 @@ class BSBLAN:
79
84
  return self
80
85
 
81
86
  async def __aexit__(self, *args: object) -> None:
82
- """Exit the context manager."""
87
+ """Exit the context manager.
88
+
89
+ Args:
90
+ *args: Variable length argument list.
91
+
92
+ """
83
93
  if self._close_session and self.session:
84
94
  await self.session.close()
85
95
 
@@ -100,7 +110,13 @@ class BSBLAN:
100
110
  self._set_api_version()
101
111
 
102
112
  def _set_api_version(self) -> None:
103
- """Set the API version based on the firmware version."""
113
+ """Set the API version based on the firmware version.
114
+
115
+ Raises:
116
+ BSBLANError: If the firmware version is not set.
117
+ BSBLANVersionError: If the firmware version is not supported.
118
+
119
+ """
104
120
  if not self._firmware_version:
105
121
  raise BSBLANError(FIRMWARE_VERSION_ERROR_MSG)
106
122
 
@@ -126,7 +142,15 @@ class BSBLAN:
126
142
  )
127
143
 
128
144
  async def _initialize_api_data(self) -> APIConfig:
129
- """Initialize and cache the API data."""
145
+ """Initialize and cache the API data.
146
+
147
+ Returns:
148
+ APIConfig: The API configuration data.
149
+
150
+ Raises:
151
+ BSBLANError: If the API version or data is not initialized.
152
+
153
+ """
130
154
  if self._api_data is None:
131
155
  if self._api_version is None:
132
156
  raise BSBLANError(API_VERSION_ERROR_MSG)
@@ -143,7 +167,23 @@ class BSBLAN:
143
167
  data: dict[str, object] | None = None,
144
168
  params: Mapping[str, str | int] | str | None = None,
145
169
  ) -> dict[str, Any]:
146
- """Handle a request to a BSBLAN device."""
170
+ """Handle a request to a BSBLAN device.
171
+
172
+ Args:
173
+ method (str): The HTTP method to use for the request.
174
+ base_path (str): The base path for the URL.
175
+ data (dict[str, object] | None): The data to send in the request body.
176
+ params (Mapping[str, str | int] | str | None): The query parameters
177
+ to include in the request.
178
+
179
+ Returns:
180
+ dict[str, Any]: The JSON response from the BSBLAN device.
181
+
182
+ Raises:
183
+ BSBLANConnectionError: If there is a connection error.
184
+ BSBLANError: If there is an error with the request.
185
+
186
+ """
147
187
  if self.session is None:
148
188
  raise BSBLANError(SESSION_NOT_INITIALIZED_ERROR_MSG)
149
189
  url = self._build_url(base_path)
@@ -170,7 +210,15 @@ class BSBLAN:
170
210
  raise BSBLANError(str(e)) from e
171
211
 
172
212
  def _build_url(self, base_path: str) -> URL:
173
- """Build the URL for the request."""
213
+ """Build the URL for the request.
214
+
215
+ Args:
216
+ base_path (str): The base path for the URL.
217
+
218
+ Returns:
219
+ URL: The constructed URL.
220
+
221
+ """
174
222
  if self.config.passkey:
175
223
  base_path = f"/{self.config.passkey}{base_path}"
176
224
  return URL.build(
@@ -181,30 +229,63 @@ class BSBLAN:
181
229
  )
182
230
 
183
231
  def _get_auth(self) -> BasicAuth | None:
184
- """Get the authentication for the request."""
232
+ """Get the authentication for the request.
233
+
234
+ Returns:
235
+ BasicAuth | None: The authentication object or None if no authentication
236
+ is required.
237
+
238
+ """
185
239
  if self.config.username and self.config.password:
186
240
  return BasicAuth(self.config.username, self.config.password)
187
241
  return None
188
242
 
189
243
  def _get_headers(self) -> dict[str, str]:
190
- """Get the headers for the request."""
244
+ """Get the headers for the request.
245
+
246
+ Returns:
247
+ dict[str, str]: The headers for the request.
248
+
249
+ """
191
250
  return {
192
251
  "User-Agent": f"PythonBSBLAN/{self._firmware_version}",
193
252
  "Accept": "application/json, */*",
194
253
  }
195
254
 
196
255
  def _validate_single_parameter(self, *params: Any, error_msg: str) -> None:
197
- """Validate that exactly one parameter is provided."""
256
+ """Validate that exactly one parameter is provided.
257
+
258
+ Args:
259
+ *params: Variable length argument list of parameters to validate.
260
+ error_msg (str): The error message to raise if validation fails.
261
+
262
+ Raises:
263
+ BSBLANError: If the validation fails.
264
+
265
+ """
198
266
  if sum(param is not None for param in params) != 1:
199
267
  raise BSBLANError(error_msg)
200
268
 
201
269
  async def _get_parameters(self, params: dict[Any, Any]) -> dict[Any, Any]:
202
- """Get the parameters info from BSBLAN device."""
270
+ """Get the parameters info from BSBLAN device.
271
+
272
+ Args:
273
+ params (dict[Any, Any]): The parameters to get info for.
274
+
275
+ Returns:
276
+ dict[Any, Any]: The parameters info from the BSBLAN device.
277
+
278
+ """
203
279
  string_params = ",".join(map(str, params))
204
280
  return {"string_par": string_params, "list": list(params.values())}
205
281
 
206
282
  async def state(self) -> State:
207
- """Get the current state from BSBLAN device."""
283
+ """Get the current state from BSBLAN device.
284
+
285
+ Returns:
286
+ State: The current state of the BSBLAN device.
287
+
288
+ """
208
289
  api_data = await self._initialize_api_data()
209
290
  params = await self._get_parameters(api_data["heating"])
210
291
  data = await self._request(params={"Parameter": params["string_par"]})
@@ -213,7 +294,12 @@ class BSBLAN:
213
294
  return State.from_dict(data)
214
295
 
215
296
  async def sensor(self) -> Sensor:
216
- """Get the sensor information from BSBLAN device."""
297
+ """Get the sensor information from BSBLAN device.
298
+
299
+ Returns:
300
+ Sensor: The sensor information from the BSBLAN device.
301
+
302
+ """
217
303
  api_data = await self._initialize_api_data()
218
304
  params = await self._get_parameters(api_data["sensor"])
219
305
  data = await self._request(params={"Parameter": params["string_par"]})
@@ -221,7 +307,12 @@ class BSBLAN:
221
307
  return Sensor.from_dict(data)
222
308
 
223
309
  async def static_values(self) -> StaticState:
224
- """Get the static information from BSBLAN device."""
310
+ """Get the static information from BSBLAN device.
311
+
312
+ Returns:
313
+ StaticState: The static information from the BSBLAN device.
314
+
315
+ """
225
316
  api_data = await self._initialize_api_data()
226
317
  params = await self._get_parameters(api_data["staticValues"])
227
318
  data = await self._request(params={"Parameter": params["string_par"]})
@@ -229,12 +320,22 @@ class BSBLAN:
229
320
  return StaticState.from_dict(data)
230
321
 
231
322
  async def device(self) -> Device:
232
- """Get BSBLAN device info."""
323
+ """Get BSBLAN device info.
324
+
325
+ Returns:
326
+ Device: The BSBLAN device information.
327
+
328
+ """
233
329
  device_info = await self._request(base_path="/JI")
234
330
  return Device.from_dict(device_info)
235
331
 
236
332
  async def info(self) -> Info:
237
- """Get information about the current heating system config."""
333
+ """Get information about the current heating system config.
334
+
335
+ Returns:
336
+ Info: The information about the current heating system config.
337
+
338
+ """
238
339
  api_data = await self._initialize_api_data()
239
340
  params = await self._get_parameters(api_data["device"])
240
341
  data = await self._request(params={"Parameter": params["string_par"]})
@@ -246,7 +347,13 @@ class BSBLAN:
246
347
  target_temperature: str | None = None,
247
348
  hvac_mode: str | None = None,
248
349
  ) -> None:
249
- """Change the state of the thermostat through BSB-Lan."""
350
+ """Change the state of the thermostat through BSB-Lan.
351
+
352
+ Args:
353
+ target_temperature (str | None): The target temperature to set.
354
+ hvac_mode (str | None): The HVAC mode to set.
355
+
356
+ """
250
357
  await self._initialize_temperature_range()
251
358
 
252
359
  self._validate_single_parameter(
@@ -263,11 +370,22 @@ class BSBLAN:
263
370
  target_temperature: str | None,
264
371
  hvac_mode: str | None,
265
372
  ) -> dict[str, Any]:
266
- """Prepare the thermostat state for setting."""
373
+ """Prepare the thermostat state for setting.
374
+
375
+ Args:
376
+ target_temperature (str | None): The target temperature to set.
377
+ hvac_mode (str | None): The HVAC mode to set.
378
+
379
+ Returns:
380
+ dict[str, Any]: The prepared state for the thermostat.
381
+
382
+ """
267
383
  state: dict[str, Any] = {}
268
384
  if target_temperature is not None:
269
385
  self._validate_target_temperature(target_temperature)
270
- state.update({"Parameter": "710", "Value": target_temperature, "Type": "1"})
386
+ state.update(
387
+ {"Parameter": "710", "Value": target_temperature, "Type": "1"},
388
+ )
271
389
  if hvac_mode is not None:
272
390
  self._validate_hvac_mode(hvac_mode)
273
391
  state.update(
@@ -280,7 +398,16 @@ class BSBLAN:
280
398
  return state
281
399
 
282
400
  def _validate_target_temperature(self, target_temperature: str) -> None:
283
- """Validate the target temperature."""
401
+ """Validate the target temperature.
402
+
403
+ Args:
404
+ target_temperature (str): The target temperature to validate.
405
+
406
+ Raises:
407
+ BSBLANError: If the temperature range is not initialized.
408
+ BSBLANInvalidParameterError: If the target temperature is invalid.
409
+
410
+ """
284
411
  if self._min_temp is None or self._max_temp is None:
285
412
  raise BSBLANError(TEMPERATURE_RANGE_ERROR_MSG)
286
413
 
@@ -292,17 +419,35 @@ class BSBLAN:
292
419
  raise BSBLANInvalidParameterError(target_temperature) from err
293
420
 
294
421
  def _validate_hvac_mode(self, hvac_mode: str) -> None:
295
- """Validate the HVAC mode."""
422
+ """Validate the HVAC mode.
423
+
424
+ Args:
425
+ hvac_mode (str): The HVAC mode to validate.
426
+
427
+ Raises:
428
+ BSBLANInvalidParameterError: If the HVAC mode is invalid.
429
+
430
+ """
296
431
  if hvac_mode not in HVAC_MODE_DICT_REVERSE:
297
432
  raise BSBLANInvalidParameterError(hvac_mode)
298
433
 
299
434
  async def _set_thermostat_state(self, state: dict[str, Any]) -> None:
300
- """Set the thermostat state."""
435
+ """Set the thermostat state.
436
+
437
+ Args:
438
+ state (dict[str, Any]): The state to set for the thermostat.
439
+
440
+ """
301
441
  response = await self._request(base_path="/JS", data=state)
302
442
  logger.debug("Response for setting: %s", response)
303
443
 
304
444
  async def hot_water_state(self) -> HotWaterState:
305
- """Get the current hot water state from BSBLAN device."""
445
+ """Get the current hot water state from BSBLAN device.
446
+
447
+ Returns:
448
+ HotWaterState: The current hot water state.
449
+
450
+ """
306
451
  api_data = await self._initialize_api_data()
307
452
  params = await self._get_parameters(api_data["hot_water"])
308
453
  data = await self._request(params={"Parameter": params["string_par"]})
@@ -311,20 +456,23 @@ class BSBLAN:
311
456
 
312
457
  async def set_hot_water(
313
458
  self,
314
- operating_mode: str | None = None,
315
459
  nominal_setpoint: float | None = None,
316
460
  reduced_setpoint: float | None = None,
317
461
  ) -> None:
318
- """Change the state of the hot water system through BSB-Lan."""
462
+ """Change the state of the hot water system through BSB-Lan.
463
+
464
+ Args:
465
+ nominal_setpoint (float | None): The nominal setpoint temperature to set.
466
+ reduced_setpoint (float | None): The reduced setpoint temperature to set.
467
+
468
+ """
319
469
  self._validate_single_parameter(
320
- operating_mode,
321
470
  nominal_setpoint,
322
471
  reduced_setpoint,
323
472
  error_msg=MULTI_PARAMETER_ERROR_MSG,
324
473
  )
325
474
 
326
475
  state = self._prepare_hot_water_state(
327
- operating_mode,
328
476
  nominal_setpoint,
329
477
  reduced_setpoint,
330
478
  )
@@ -332,16 +480,23 @@ class BSBLAN:
332
480
 
333
481
  def _prepare_hot_water_state(
334
482
  self,
335
- operating_mode: str | None,
336
483
  nominal_setpoint: float | None,
337
484
  reduced_setpoint: float | None,
338
485
  ) -> dict[str, Any]:
339
- """Prepare the hot water state for setting."""
486
+ """Prepare the hot water state for setting.
487
+
488
+ Args:
489
+ nominal_setpoint (float | None): The nominal setpoint temperature to set.
490
+ reduced_setpoint (float | None): The reduced setpoint temperature to set.
491
+
492
+ Returns:
493
+ dict[str, Any]: The prepared state for the hot water.
494
+
495
+ Raises:
496
+ BSBLANError: If no state is provided.
497
+
498
+ """
340
499
  state: dict[str, Any] = {}
341
- if operating_mode is not None:
342
- state.update(
343
- {"Parameter": "1600", "EnumValue": operating_mode, "Type": "1"},
344
- )
345
500
  if nominal_setpoint is not None:
346
501
  state.update(
347
502
  {"Parameter": "1610", "Value": str(nominal_setpoint), "Type": "1"},
@@ -355,6 +510,11 @@ class BSBLAN:
355
510
  return state
356
511
 
357
512
  async def _set_hot_water_state(self, state: dict[str, Any]) -> None:
358
- """Set the hot water state."""
513
+ """Set the hot water state.
514
+
515
+ Args:
516
+ state (dict[str, Any]): The state to set for the hot water.
517
+
518
+ """
359
519
  response = await self._request(base_path="/JS", data=state)
360
520
  logger.debug("Response for setting: %s", response)
@@ -75,11 +75,14 @@ API_V3: Final[APIConfig] = {
75
75
  "hot_water": {
76
76
  "1600": "operating_mode",
77
77
  "1610": "nominal_setpoint",
78
+ "1614": "nominal_setpoint_max",
78
79
  "1612": "reduced_setpoint",
79
80
  "1620": "release",
80
81
  "1640": "legionella_function",
81
82
  "1645": "legionella_setpoint",
82
83
  "1641": "legionella_periodically",
84
+ "8830": "dhw_actual_value_top_temperature",
85
+ "8820": "state_dhw_pump",
83
86
  },
84
87
  }
85
88
 
@@ -9,7 +9,12 @@ class BSBLANError(Exception):
9
9
  message: str = "Unexpected response from the BSBLAN device."
10
10
 
11
11
  def __init__(self, message: str | None = None) -> None:
12
- """Initialize a new instance of the BSBLANError class."""
12
+ """Initialize a new instance of the BSBLANError class.
13
+
14
+ Args:
15
+ message: Optional custom error message.
16
+
17
+ """
13
18
  if message is not None:
14
19
  self.message = message
15
20
  super().__init__(self.message)
@@ -22,7 +27,12 @@ class BSBLANConnectionError(BSBLANError):
22
27
  message_error: str = "Error occurred while connecting to BSBLAN device."
23
28
 
24
29
  def __init__(self, response: str | None = None) -> None:
25
- """Initialize a new instance of the BSBLANConnectionError class."""
30
+ """Initialize a new instance of the BSBLANConnectionError class.
31
+
32
+ Args:
33
+ response: Optional response message.
34
+
35
+ """
26
36
  self.response = response
27
37
  super().__init__(self.message)
28
38
 
@@ -37,6 +47,11 @@ class BSBLANInvalidParameterError(BSBLANError):
37
47
  """Raised when an invalid parameter is provided."""
38
48
 
39
49
  def __init__(self, parameter: str) -> None:
40
- """Initialize a new instance of the BSBLANInvalidParameterError class."""
50
+ """Initialize a new instance of the BSBLANInvalidParameterError class.
51
+
52
+ Args:
53
+ parameter: The invalid parameter that caused the error.
54
+
55
+ """
41
56
  self.message = f"Invalid values provided: {parameter}"
42
57
  super().__init__(self.message)
@@ -93,11 +93,14 @@ class HotWaterState(DataClassJSONMixin):
93
93
 
94
94
  operating_mode: EntityInfo
95
95
  nominal_setpoint: EntityInfo
96
+ nominal_setpoint_max: EntityInfo # 1614
96
97
  reduced_setpoint: EntityInfo
97
98
  release: EntityInfo
98
99
  legionella_function: EntityInfo
99
100
  legionella_setpoint: EntityInfo
100
101
  legionella_periodically: EntityInfo
102
+ dhw_actual_value_top_temperature: EntityInfo # 8830
103
+ state_dhw_pump: EntityInfo # 8820
101
104
 
102
105
 
103
106
  @dataclass
File without changes