python-bsblan 4.1.0b1__tar.gz → 4.1.0b2__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 (100) hide show
  1. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/PKG-INFO +1 -1
  2. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/pyproject.toml +1 -1
  3. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/src/bsblan/bsblan.py +16 -2
  4. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_hot_water_additional.py +135 -0
  5. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.editorconfig +0 -0
  6. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.gitattributes +0 -0
  7. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/CODE_OF_CONDUCT.md +0 -0
  8. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/CONTRIBUTING.md +0 -0
  9. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/ISSUE_TEMPLATE/PULL_REQUEST_TEMPLATE.md +0 -0
  10. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  11. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  12. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/copilot-instructions.md +0 -0
  13. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/labels.yml +0 -0
  14. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/prompts/add-parameter.prompt.md +0 -0
  15. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/prompts/code-review.prompt.md +0 -0
  16. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/release-drafter.yml +0 -0
  17. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/renovate.json +0 -0
  18. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/skills/bsblan-parameters/SKILL.md +0 -0
  19. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/skills/bsblan-testing/SKILL.md +0 -0
  20. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/auto-approve-renovate.yml +0 -0
  21. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/codeql.yaml +0 -0
  22. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/labels.yaml +0 -0
  23. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/linting.yaml +0 -0
  24. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/lock.yaml +0 -0
  25. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/pr-labels.yaml +0 -0
  26. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/release-drafter.yaml +0 -0
  27. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/release.yaml +0 -0
  28. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/stale.yaml +0 -0
  29. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/tests.yaml +0 -0
  30. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.github/workflows/typing.yaml +0 -0
  31. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.gitignore +0 -0
  32. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.nvmrc +0 -0
  33. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.pre-commit-config.yaml +0 -0
  34. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.prettierignore +0 -0
  35. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/.yamllint +0 -0
  36. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/AGENTS.md +0 -0
  37. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/CLAUDE.md +0 -0
  38. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/LICENSE.md +0 -0
  39. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/README.md +0 -0
  40. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/examples/control.py +0 -0
  41. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/examples/discovery.py +0 -0
  42. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/examples/profile_init.py +0 -0
  43. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/examples/ruff.toml +0 -0
  44. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/examples/speed_test.py +0 -0
  45. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/package-lock.json +0 -0
  46. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/package.json +0 -0
  47. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/sonar-project.properties +0 -0
  48. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/src/bsblan/__init__.py +0 -0
  49. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/src/bsblan/constants.py +0 -0
  50. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/src/bsblan/exceptions.py +0 -0
  51. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/src/bsblan/models.py +0 -0
  52. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/src/bsblan/py.typed +0 -0
  53. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/src/bsblan/utility.py +0 -0
  54. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/__init__.py +0 -0
  55. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/conftest.py +0 -0
  56. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/device.json +0 -0
  57. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/dict_version.json +0 -0
  58. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/hot_water_state.json +0 -0
  59. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/info.json +0 -0
  60. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/password.txt +0 -0
  61. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/sensor.json +0 -0
  62. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/state.json +0 -0
  63. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/static_state.json +0 -0
  64. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/thermostat_hvac.json +0 -0
  65. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/thermostat_temp.json +0 -0
  66. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/fixtures/time.json +0 -0
  67. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/ruff.toml +0 -0
  68. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_api_initialization.py +0 -0
  69. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_api_validation.py +0 -0
  70. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_auth.py +0 -0
  71. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_backoff_retry.py +0 -0
  72. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_bsblan.py +0 -0
  73. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_bsblan_edge_cases.py +0 -0
  74. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_configuration.py +0 -0
  75. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_constants.py +0 -0
  76. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_context_manager.py +0 -0
  77. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_device.py +0 -0
  78. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_dhw_time_switch.py +0 -0
  79. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_entity_info.py +0 -0
  80. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_hotwater_state.py +0 -0
  81. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_include_parameter.py +0 -0
  82. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_info.py +0 -0
  83. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_initialization.py +0 -0
  84. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_read_parameters.py +0 -0
  85. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_reset_validation.py +0 -0
  86. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_schedule_models.py +0 -0
  87. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_sensor.py +0 -0
  88. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_set_hot_water_schedule.py +0 -0
  89. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_set_hotwater.py +0 -0
  90. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_state.py +0 -0
  91. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_static_state.py +0 -0
  92. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_temperature_unit.py +0 -0
  93. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_temperature_validation.py +0 -0
  94. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_thermostat.py +0 -0
  95. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_time.py +0 -0
  96. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_utility.py +0 -0
  97. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_utility_additional.py +0 -0
  98. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_utility_edge_cases.py +0 -0
  99. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/tests/test_version_errors.py +0 -0
  100. {python_bsblan-4.1.0b1 → python_bsblan-4.1.0b2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-bsblan
3
- Version: 4.1.0b1
3
+ Version: 4.1.0b2
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-bsblan"
3
- version = "4.1.0-beta.1"
3
+ version = "4.1.0-beta.2"
4
4
  description = "Asynchronous Python client for BSBLAN API"
5
5
  authors = [
6
6
  {name = "Willem-Jan van Rootselaar", email = "liudgervr@gmail.com"}
@@ -213,7 +213,10 @@ class BSBLAN:
213
213
  self._extract_temperature_unit_from_response(response_data)
214
214
 
215
215
  async def _ensure_hot_water_group_validated(
216
- self, group_name: str, param_filter: set[str]
216
+ self,
217
+ group_name: str,
218
+ param_filter: set[str],
219
+ include: list[str] | None = None,
217
220
  ) -> None:
218
221
  """Validate only a specific hot water parameter group (lazy loading).
219
222
 
@@ -227,6 +230,8 @@ class BSBLAN:
227
230
  Args:
228
231
  group_name: Name of the group (essential, config, schedule)
229
232
  param_filter: Set of parameter IDs for this group
233
+ include: Optional list of parameter names to include in validation.
234
+ If provided, only these parameters will be validated.
230
235
 
231
236
  """
232
237
  # Fast path: skip if already validated (no lock needed)
@@ -260,6 +265,14 @@ class BSBLAN:
260
265
  if param_id in param_filter
261
266
  }
262
267
 
268
+ # Apply include filter if specified - only validate requested params
269
+ if include is not None:
270
+ group_params = {
271
+ param_id: name
272
+ for param_id, name in group_params.items()
273
+ if name in include
274
+ }
275
+
263
276
  if not group_params:
264
277
  logger.debug("No parameters to validate for group %s", group_name)
265
278
  self._validated_hot_water_groups.add(group_name)
@@ -1105,7 +1118,8 @@ class BSBLAN:
1105
1118
 
1106
1119
  """
1107
1120
  # Granular lazy load: validate only this param group on first access
1108
- await self._ensure_hot_water_group_validated(group_name, param_filter)
1121
+ # Pass include filter so we only validate requested params
1122
+ await self._ensure_hot_water_group_validated(group_name, param_filter, include)
1109
1123
 
1110
1124
  # Use cached validated params
1111
1125
  filtered_params = {
@@ -571,3 +571,138 @@ async def test_ensure_hot_water_group_concurrent_double_check() -> None:
571
571
 
572
572
  # Only one request should have been made
573
573
  assert request_count == 1
574
+
575
+
576
+ @pytest.mark.asyncio
577
+ async def test_ensure_hot_water_group_validated_with_include_filter() -> None:
578
+ """Test that include filter limits which params are validated."""
579
+ async with aiohttp.ClientSession() as session:
580
+ bsblan = BSBLAN(BSBLANConfig(host="example.com"), session=session)
581
+ bsblan._api_version = "v3"
582
+ # Set up api_data with multiple params in the config group
583
+ bsblan._api_data = { # type: ignore[assignment]
584
+ "hot_water": {
585
+ "1640": "legionella_function",
586
+ "1645": "legionella_function_setpoint",
587
+ "1648": "legionella_circulation_temp_diff",
588
+ }
589
+ }
590
+ bsblan._api_validator = APIValidator(bsblan._api_data)
591
+
592
+ requested_params: list[str] = []
593
+
594
+ async def mock_request(
595
+ params: dict[str, str] | None = None,
596
+ **_kwargs: Any,
597
+ ) -> dict[str, Any]:
598
+ if params:
599
+ requested_params.append(params.get("Parameter", ""))
600
+ # Return valid data for all requested params
601
+ return {
602
+ "1640": {"value": "1", "unit": ""},
603
+ "1645": {"value": "60", "unit": "°C"},
604
+ }
605
+
606
+ bsblan._request = mock_request # type: ignore[method-assign]
607
+
608
+ # Validate with include filter - only request 2 of 3 params
609
+ await bsblan._ensure_hot_water_group_validated(
610
+ "config",
611
+ {"1640", "1645", "1648"},
612
+ include=["legionella_function", "legionella_function_setpoint"],
613
+ )
614
+
615
+ # Verify only filtered params were requested (not 1648)
616
+ assert len(requested_params) == 1
617
+ assert "1648" not in requested_params[0]
618
+ assert "1640" in requested_params[0]
619
+ assert "1645" in requested_params[0]
620
+
621
+ # Cache should only contain the validated params
622
+ assert "1640" in bsblan._hot_water_param_cache
623
+ assert "1645" in bsblan._hot_water_param_cache
624
+ assert "1648" not in bsblan._hot_water_param_cache
625
+
626
+
627
+ @pytest.mark.asyncio
628
+ async def test_ensure_hot_water_group_validated_include_empty_result() -> None:
629
+ """Test that include filter with no matching params marks group validated."""
630
+ async with aiohttp.ClientSession() as session:
631
+ bsblan = BSBLAN(BSBLANConfig(host="example.com"), session=session)
632
+ bsblan._api_version = "v3"
633
+ bsblan._api_data = { # type: ignore[assignment]
634
+ "hot_water": {
635
+ "1640": "legionella_function",
636
+ }
637
+ }
638
+ bsblan._api_validator = APIValidator(bsblan._api_data)
639
+
640
+ request_count = 0
641
+
642
+ async def mock_request(**_kwargs: Any) -> dict[str, Any]:
643
+ nonlocal request_count
644
+ request_count += 1
645
+ return {}
646
+
647
+ bsblan._request = mock_request # type: ignore[method-assign]
648
+
649
+ # Include filter with a param name that doesn't exist in the group
650
+ await bsblan._ensure_hot_water_group_validated(
651
+ "config",
652
+ {"1640"},
653
+ include=["nonexistent_param"],
654
+ )
655
+
656
+ # No request should be made since no params match
657
+ assert request_count == 0
658
+ # Group should still be marked as validated
659
+ assert "config" in bsblan._validated_hot_water_groups
660
+
661
+
662
+ @pytest.mark.asyncio
663
+ async def test_ensure_hot_water_group_validated_without_include() -> None:
664
+ """Test that without include filter all group params are validated."""
665
+ async with aiohttp.ClientSession() as session:
666
+ bsblan = BSBLAN(BSBLANConfig(host="example.com"), session=session)
667
+ bsblan._api_version = "v3"
668
+ bsblan._api_data = { # type: ignore[assignment]
669
+ "hot_water": {
670
+ "1640": "legionella_function",
671
+ "1645": "legionella_function_setpoint",
672
+ "1648": "legionella_circulation_temp_diff",
673
+ }
674
+ }
675
+ bsblan._api_validator = APIValidator(bsblan._api_data)
676
+
677
+ requested_params: list[str] = []
678
+
679
+ async def mock_request(
680
+ params: dict[str, str] | None = None,
681
+ **_kwargs: Any,
682
+ ) -> dict[str, Any]:
683
+ if params:
684
+ requested_params.append(params.get("Parameter", ""))
685
+ return {
686
+ "1640": {"value": "1", "unit": ""},
687
+ "1645": {"value": "60", "unit": "°C"},
688
+ "1648": {"value": "5", "unit": "K"},
689
+ }
690
+
691
+ bsblan._request = mock_request # type: ignore[method-assign]
692
+
693
+ # Validate without include filter - all params should be requested
694
+ await bsblan._ensure_hot_water_group_validated(
695
+ "config",
696
+ {"1640", "1645", "1648"},
697
+ )
698
+
699
+ # All 3 params should be in the request
700
+ assert len(requested_params) == 1
701
+ assert "1640" in requested_params[0]
702
+ assert "1645" in requested_params[0]
703
+ assert "1648" in requested_params[0]
704
+
705
+ # All params should be cached
706
+ assert "1640" in bsblan._hot_water_param_cache
707
+ assert "1645" in bsblan._hot_water_param_cache
708
+ assert "1648" in bsblan._hot_water_param_cache
File without changes
File without changes