python-duco-connectivity 0.11.0__tar.gz → 0.12.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 (26) hide show
  1. {python_duco_connectivity-0.11.0/src/python_duco_connectivity.egg-info → python_duco_connectivity-0.12.0}/PKG-INFO +2 -1
  2. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/README.md +1 -0
  3. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/pyproject.toml +1 -1
  4. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/duco_connectivity/client.py +32 -0
  5. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0/src/python_duco_connectivity.egg-info}/PKG-INFO +2 -1
  6. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/tests/test_client.py +75 -0
  7. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/LICENSE +0 -0
  8. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/setup.cfg +0 -0
  9. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/duco_connectivity/__init__.py +0 -0
  10. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/duco_connectivity/__main__.py +0 -0
  11. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/duco_connectivity/cli.py +0 -0
  12. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/duco_connectivity/exceptions.py +0 -0
  13. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/duco_connectivity/models.py +0 -0
  14. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/duco_connectivity/py.typed +0 -0
  15. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/python_duco_connectivity.egg-info/SOURCES.txt +0 -0
  16. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/python_duco_connectivity.egg-info/dependency_links.txt +0 -0
  17. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/python_duco_connectivity.egg-info/entry_points.txt +0 -0
  18. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/python_duco_connectivity.egg-info/requires.txt +0 -0
  19. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/src/python_duco_connectivity.egg-info/top_level.txt +0 -0
  20. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/tests/test_api_reference.py +0 -0
  21. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/tests/test_cli.py +0 -0
  22. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/tests/test_exceptions.py +0 -0
  23. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/tests/test_local_sample_validation.py +0 -0
  24. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/tests/test_models.py +0 -0
  25. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/tests/test_pytest_live_support.py +0 -0
  26. {python_duco_connectivity-0.11.0 → python_duco_connectivity-0.12.0}/tests/test_replay_helpers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-duco-connectivity
3
- Version: 0.11.0
3
+ Version: 0.12.0
4
4
  Summary: Async HTTP client for the local Duco Connectivity API
5
5
  Author: Ronald van der Meer
6
6
  License-Expression: MIT
@@ -184,6 +184,7 @@ For local function probes without activating the environment first:
184
184
  .venv/bin/python -m duco_connectivity --host 192.168.1.10 call async_get_board_info
185
185
  .venv/bin/duco-probe --host 192.168.1.10 call async_get_board_info
186
186
  .venv/bin/duco-probe --host 192.168.1.10 call async_get_ventilation_temperature_info
187
+ .venv/bin/duco-probe --host 192.168.1.10 call async_get_bypass_supply_temperature_targets
187
188
  .venv/bin/duco-probe --host 192.168.1.10 call async_get_bypass_supply_temperature_target --kwargs '{"zone_id": 1}'
188
189
  ```
189
190
 
@@ -152,6 +152,7 @@ For local function probes without activating the environment first:
152
152
  .venv/bin/python -m duco_connectivity --host 192.168.1.10 call async_get_board_info
153
153
  .venv/bin/duco-probe --host 192.168.1.10 call async_get_board_info
154
154
  .venv/bin/duco-probe --host 192.168.1.10 call async_get_ventilation_temperature_info
155
+ .venv/bin/duco-probe --host 192.168.1.10 call async_get_bypass_supply_temperature_targets
155
156
  .venv/bin/duco-probe --host 192.168.1.10 call async_get_bypass_supply_temperature_target --kwargs '{"zone_id": 1}'
156
157
  ```
157
158
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-duco-connectivity"
7
- version = "0.11.0"
7
+ version = "0.12.0"
8
8
  description = "Async HTTP client for the local Duco Connectivity API"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -400,6 +400,19 @@ class DucoClient:
400
400
 
401
401
  return cls._build_bypass_supply_temperature_target(zone_id, raw_value)
402
402
 
403
+ @classmethod
404
+ def _extract_bypass_supply_temperature_targets(
405
+ cls,
406
+ config: Config,
407
+ ) -> dict[int, BypassSupplyTemperatureTarget]:
408
+ """Read all bypass supply targets from a typed config response."""
409
+ return {
410
+ zone_id: target
411
+ for zone_id in range(1, 9)
412
+ if (target := cls._extract_bypass_supply_temperature_target(config, zone_id))
413
+ is not None
414
+ }
415
+
403
416
  @staticmethod
404
417
  def _read_scalar_value(payload: dict[str, Any], key: str) -> Any:
405
418
  raw_value = payload[key]
@@ -2318,6 +2331,25 @@ class DucoClient:
2318
2331
  raise DucoError(msg)
2319
2332
  return target
2320
2333
 
2334
+ async def async_get_bypass_supply_temperature_targets(
2335
+ self,
2336
+ ) -> dict[int, BypassSupplyTemperatureTarget]:
2337
+ """Return all bypass supply targets exposed through `/config` in Celsius."""
2338
+ try:
2339
+ config = await self.async_get_config(
2340
+ module=ConfigModuleSelector.HEAT_RECOVERY,
2341
+ submodule=ConfigHeatRecoverySubmoduleSelector.BYPASS,
2342
+ )
2343
+ except DucoResponseError as err:
2344
+ if _is_unsupported_optional_endpoint_error(err, "/config"):
2345
+ raise DucoUnsupportedCapabilityError(
2346
+ err.status,
2347
+ err.path,
2348
+ err.body,
2349
+ ) from err
2350
+ raise
2351
+ return self._extract_bypass_supply_temperature_targets(config)
2352
+
2321
2353
  async def async_set_bypass_supply_temperature_target(
2322
2354
  self,
2323
2355
  zone_id: int,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-duco-connectivity
3
- Version: 0.11.0
3
+ Version: 0.12.0
4
4
  Summary: Async HTTP client for the local Duco Connectivity API
5
5
  Author: Ronald van der Meer
6
6
  License-Expression: MIT
@@ -184,6 +184,7 @@ For local function probes without activating the environment first:
184
184
  .venv/bin/python -m duco_connectivity --host 192.168.1.10 call async_get_board_info
185
185
  .venv/bin/duco-probe --host 192.168.1.10 call async_get_board_info
186
186
  .venv/bin/duco-probe --host 192.168.1.10 call async_get_ventilation_temperature_info
187
+ .venv/bin/duco-probe --host 192.168.1.10 call async_get_bypass_supply_temperature_targets
187
188
  .venv/bin/duco-probe --host 192.168.1.10 call async_get_bypass_supply_temperature_target --kwargs '{"zone_id": 1}'
188
189
  ```
189
190
 
@@ -3794,6 +3794,81 @@ async def test_get_bypass_supply_temperature_target_returns_converted_values(
3794
3794
  }
3795
3795
 
3796
3796
 
3797
+ async def test_get_bypass_supply_temperature_targets_returns_available_targets(
3798
+ config_data: dict[str, object],
3799
+ ) -> None:
3800
+ """Bulk bypass target reads should return all available targets in Celsius."""
3801
+ mock_response = _response(json_payload=config_data)
3802
+
3803
+ async with aiohttp.ClientSession() as session:
3804
+ client = DucoClient(session=session, host="192.0.2.94")
3805
+ request_mock = _request(mock_response)
3806
+ with patch.object(session, "request", request_mock):
3807
+ targets = await client.async_get_bypass_supply_temperature_targets()
3808
+
3809
+ assert targets == {
3810
+ 1: BypassSupplyTemperatureTarget(
3811
+ zone_id=1,
3812
+ value=18.0,
3813
+ minimum=12.0,
3814
+ increment=0.5,
3815
+ maximum=22.0,
3816
+ ),
3817
+ 2: BypassSupplyTemperatureTarget(
3818
+ zone_id=2,
3819
+ value=18.5,
3820
+ minimum=12.0,
3821
+ increment=0.5,
3822
+ maximum=22.0,
3823
+ ),
3824
+ }
3825
+ assert request_mock.call_args.kwargs["params"] == {
3826
+ "module": "HeatRecovery",
3827
+ "submodule": "Bypass",
3828
+ }
3829
+
3830
+
3831
+ async def test_get_bypass_supply_temperature_targets_returns_empty_when_absent() -> None:
3832
+ """Bulk bypass target reads should omit targets absent from the response."""
3833
+ mock_response = _response(json_payload={"HeatRecovery": {"Bypass": {}}})
3834
+
3835
+ async with aiohttp.ClientSession() as session:
3836
+ client = DucoClient(session=session, host="192.0.2.94")
3837
+ with patch.object(session, "request", _request(mock_response)):
3838
+ targets = await client.async_get_bypass_supply_temperature_targets()
3839
+
3840
+ assert targets == {}
3841
+
3842
+
3843
+ async def test_get_bypass_supply_temperature_targets_raises_when_unsupported() -> None:
3844
+ """Unsupported bulk bypass target reads should raise a typed capability error."""
3845
+ mock_response = _response(
3846
+ status=400,
3847
+ text_payload='{"Code":3,"Result":"FAILED"}',
3848
+ )
3849
+
3850
+ async with aiohttp.ClientSession() as session:
3851
+ client = DucoClient(session=session, host="192.0.2.94")
3852
+ with (
3853
+ patch.object(session, "request", _request(mock_response)),
3854
+ pytest.raises(DucoUnsupportedCapabilityError),
3855
+ ):
3856
+ await client.async_get_bypass_supply_temperature_targets()
3857
+
3858
+
3859
+ async def test_get_bypass_supply_temperature_targets_reraises_other_client_errors() -> None:
3860
+ """Unexpected bulk bypass target failures should remain visible to callers."""
3861
+ mock_response = _response(status=400, text_payload='{"Code":4,"Result":"FAILED"}')
3862
+
3863
+ async with aiohttp.ClientSession() as session:
3864
+ client = DucoClient(session=session, host="192.0.2.94")
3865
+ with (
3866
+ patch.object(session, "request", _request(mock_response)),
3867
+ pytest.raises(DucoResponseError),
3868
+ ):
3869
+ await client.async_get_bypass_supply_temperature_targets()
3870
+
3871
+
3797
3872
  async def test_get_bypass_supply_temperature_target_raises_when_not_reported() -> None:
3798
3873
  """Missing bypass targets should fail for parameter-specific helper reads."""
3799
3874
  mock_response = _response(json_payload={"HeatRecovery": {"Bypass": {}}})