PyPlumIO 0.5.25__tar.gz → 0.5.26__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 (144) hide show
  1. {pyplumio-0.5.25 → pyplumio-0.5.26}/PKG-INFO +7 -7
  2. {pyplumio-0.5.25 → pyplumio-0.5.26}/PyPlumIO.egg-info/PKG-INFO +7 -7
  3. {pyplumio-0.5.25 → pyplumio-0.5.26}/PyPlumIO.egg-info/requires.txt +6 -6
  4. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/_version.py +2 -2
  5. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/devices/__init__.py +27 -28
  6. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/devices/ecomax.py +51 -57
  7. pyplumio-0.5.26/pyplumio/devices/ecoster.py +12 -0
  8. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/devices/mixer.py +5 -8
  9. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/devices/thermostat.py +3 -3
  10. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/filters.py +6 -6
  11. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/frames/__init__.py +6 -6
  12. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/frames/messages.py +3 -3
  13. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/frames/requests.py +18 -18
  14. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/frames/responses.py +15 -15
  15. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/helpers/data_types.py +104 -68
  16. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/helpers/event_manager.py +7 -7
  17. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/helpers/factory.py +5 -6
  18. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/helpers/parameter.py +17 -15
  19. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/helpers/schedule.py +50 -46
  20. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/helpers/timeout.py +1 -1
  21. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/protocol.py +6 -7
  22. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/alerts.py +8 -6
  23. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/ecomax_parameters.py +30 -26
  24. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/frame_versions.py +2 -3
  25. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/mixer_parameters.py +9 -6
  26. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/mixer_sensors.py +10 -8
  27. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/modules.py +9 -7
  28. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/network_info.py +16 -16
  29. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/program_version.py +3 -0
  30. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/regulator_data.py +2 -4
  31. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/regulator_data_schema.py +2 -3
  32. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/schedules.py +33 -35
  33. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/thermostat_parameters.py +6 -4
  34. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/thermostat_sensors.py +13 -10
  35. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyproject.toml +6 -6
  36. pyplumio-0.5.26/requirements_test.txt +11 -0
  37. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/helpers/test_data_types.py +37 -1
  38. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/helpers/test_parameter.py +2 -3
  39. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/helpers/test_schedule.py +1 -1
  40. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/test_devices.py +2 -8
  41. pyplumio-0.5.25/pyplumio/devices/ecoster.py +0 -14
  42. pyplumio-0.5.25/requirements_test.txt +0 -11
  43. {pyplumio-0.5.25 → pyplumio-0.5.26}/.gitattributes +0 -0
  44. {pyplumio-0.5.25 → pyplumio-0.5.26}/.github/CODE_OF_CONDUCT.md +0 -0
  45. {pyplumio-0.5.25 → pyplumio-0.5.26}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  46. {pyplumio-0.5.25 → pyplumio-0.5.26}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  47. {pyplumio-0.5.25 → pyplumio-0.5.26}/.github/dependabot.yml +0 -0
  48. {pyplumio-0.5.25 → pyplumio-0.5.26}/.github/workflows/ci.yml +0 -0
  49. {pyplumio-0.5.25 → pyplumio-0.5.26}/.github/workflows/codeql-analysis.yml +0 -0
  50. {pyplumio-0.5.25 → pyplumio-0.5.26}/.github/workflows/deploy.yml +0 -0
  51. {pyplumio-0.5.25 → pyplumio-0.5.26}/.github/workflows/documentation.yml +0 -0
  52. {pyplumio-0.5.25 → pyplumio-0.5.26}/.gitignore +0 -0
  53. {pyplumio-0.5.25 → pyplumio-0.5.26}/.pre-commit-config.yaml +0 -0
  54. {pyplumio-0.5.25 → pyplumio-0.5.26}/.vscode/settings.json +0 -0
  55. {pyplumio-0.5.25 → pyplumio-0.5.26}/LICENSE +0 -0
  56. {pyplumio-0.5.25 → pyplumio-0.5.26}/MANIFEST.in +0 -0
  57. {pyplumio-0.5.25 → pyplumio-0.5.26}/PyPlumIO.egg-info/SOURCES.txt +0 -0
  58. {pyplumio-0.5.25 → pyplumio-0.5.26}/PyPlumIO.egg-info/dependency_links.txt +0 -0
  59. {pyplumio-0.5.25 → pyplumio-0.5.26}/PyPlumIO.egg-info/top_level.txt +0 -0
  60. {pyplumio-0.5.25 → pyplumio-0.5.26}/README.md +0 -0
  61. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/Makefile +0 -0
  62. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/make.bat +0 -0
  63. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/source/callbacks.rst +0 -0
  64. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/source/conf.py +0 -0
  65. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/source/connecting.rst +0 -0
  66. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/source/frames.rst +0 -0
  67. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/source/index.rst +0 -0
  68. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/source/mixers_thermostats.rst +0 -0
  69. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/source/protocol.rst +0 -0
  70. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/source/reading.rst +0 -0
  71. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/source/schedules.rst +0 -0
  72. {pyplumio-0.5.25 → pyplumio-0.5.26}/docs/source/writing.rst +0 -0
  73. {pyplumio-0.5.25 → pyplumio-0.5.26}/images/ecomax.png +0 -0
  74. {pyplumio-0.5.25 → pyplumio-0.5.26}/images/rs485.png +0 -0
  75. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/__init__.py +0 -0
  76. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/__main__.py +0 -0
  77. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/connection.py +0 -0
  78. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/const.py +0 -0
  79. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/exceptions.py +0 -0
  80. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/helpers/__init__.py +0 -0
  81. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/helpers/task_manager.py +0 -0
  82. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/helpers/uid.py +0 -0
  83. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/py.typed +0 -0
  84. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/stream.py +0 -0
  85. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/__init__.py +0 -0
  86. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/boiler_load.py +0 -0
  87. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/boiler_power.py +0 -0
  88. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/fan_power.py +0 -0
  89. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/fuel_consumption.py +0 -0
  90. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/fuel_level.py +0 -0
  91. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/lambda_sensor.py +0 -0
  92. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/output_flags.py +0 -0
  93. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/outputs.py +0 -0
  94. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/pending_alerts.py +0 -0
  95. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/product_info.py +0 -0
  96. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/statuses.py +0 -0
  97. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/structures/temperatures.py +0 -0
  98. {pyplumio-0.5.25 → pyplumio-0.5.26}/pyplumio/utils.py +0 -0
  99. {pyplumio-0.5.25 → pyplumio-0.5.26}/requirements.txt +0 -0
  100. {pyplumio-0.5.25 → pyplumio-0.5.26}/requirements_docs.txt +0 -0
  101. {pyplumio-0.5.25 → pyplumio-0.5.26}/setup.cfg +0 -0
  102. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/__init__.py +0 -0
  103. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/conftest.py +0 -0
  104. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/frames/test_init.py +0 -0
  105. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/frames/test_messages.py +0 -0
  106. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/frames/test_requests.py +0 -0
  107. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/frames/test_responses.py +0 -0
  108. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/helpers/__init__.py +0 -0
  109. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/helpers/test_event_manager.py +0 -0
  110. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/helpers/test_factory.py +0 -0
  111. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/helpers/test_task_manager.py +0 -0
  112. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/helpers/test_timeout.py +0 -0
  113. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/helpers/test_uid.py +0 -0
  114. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/ruff.toml +0 -0
  115. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/test_connection.py +0 -0
  116. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/test_filters.py +0 -0
  117. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/test_init.py +0 -0
  118. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/test_main.py +0 -0
  119. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/test_protocol.py +0 -0
  120. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/test_stream.py +0 -0
  121. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/test_utils.py +0 -0
  122. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/messages/regulator_data.json +0 -0
  123. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/messages/sensor_data.json +0 -0
  124. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/requests/alerts.json +0 -0
  125. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/requests/ecomax_control.json +0 -0
  126. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/requests/ecomax_parameters.json +0 -0
  127. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/requests/mixer_parameters.json +0 -0
  128. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/requests/set_ecomax_parameter.json +0 -0
  129. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/requests/set_mixer_parameter.json +0 -0
  130. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/requests/set_schedule.json +0 -0
  131. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/requests/set_thermostat_parameter.json +0 -0
  132. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/requests/thermostat_parameters.json +0 -0
  133. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/responses/alerts.json +0 -0
  134. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/responses/device_available.json +0 -0
  135. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/responses/ecomax_parameters.json +0 -0
  136. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/responses/mixer_parameters.json +0 -0
  137. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/responses/password.json +0 -0
  138. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/responses/program_version.json +0 -0
  139. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/responses/regulator_data_schema.json +0 -0
  140. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/responses/schedules.json +0 -0
  141. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/responses/thermostat_parameters.json +0 -0
  142. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/responses/uid.json +0 -0
  143. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/unknown/unknown_ecomax_parameter.json +0 -0
  144. {pyplumio-0.5.25 → pyplumio-0.5.26}/tests/testdata/unknown/unknown_mixer_parameter.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyPlumIO
3
- Version: 0.5.25
3
+ Version: 0.5.26
4
4
  Summary: PyPlumIO is a native ecoNET library for Plum ecoMAX controllers.
5
5
  Author-email: Denis Paavilainen <denpa@denpa.pro>
6
6
  License: MIT License
@@ -27,14 +27,14 @@ Requires-Dist: pyserial-asyncio==0.6
27
27
  Requires-Dist: typing-extensions==4.12.2
28
28
  Provides-Extra: test
29
29
  Requires-Dist: codespell==2.3.0; extra == "test"
30
- Requires-Dist: coverage==7.6.0; extra == "test"
31
- Requires-Dist: mypy==1.11.1; extra == "test"
30
+ Requires-Dist: coverage==7.6.1; extra == "test"
31
+ Requires-Dist: mypy==1.11.2; extra == "test"
32
32
  Requires-Dist: pyserial-asyncio-fast==0.14; extra == "test"
33
33
  Requires-Dist: pytest==8.3.2; extra == "test"
34
- Requires-Dist: pytest-asyncio==0.23.8; extra == "test"
35
- Requires-Dist: ruff==0.5.5; extra == "test"
36
- Requires-Dist: tox==4.16.0; extra == "test"
37
- Requires-Dist: types-pyserial==3.5.0.20240527; extra == "test"
34
+ Requires-Dist: pytest-asyncio==0.24.0; extra == "test"
35
+ Requires-Dist: ruff==0.5.6; extra == "test"
36
+ Requires-Dist: tox==4.18.0; extra == "test"
37
+ Requires-Dist: types-pyserial==3.5.0.20240826; extra == "test"
38
38
  Provides-Extra: docs
39
39
  Requires-Dist: sphinx==7.4.7; extra == "docs"
40
40
  Requires-Dist: sphinx_rtd_theme==2.0.0; extra == "docs"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyPlumIO
3
- Version: 0.5.25
3
+ Version: 0.5.26
4
4
  Summary: PyPlumIO is a native ecoNET library for Plum ecoMAX controllers.
5
5
  Author-email: Denis Paavilainen <denpa@denpa.pro>
6
6
  License: MIT License
@@ -27,14 +27,14 @@ Requires-Dist: pyserial-asyncio==0.6
27
27
  Requires-Dist: typing-extensions==4.12.2
28
28
  Provides-Extra: test
29
29
  Requires-Dist: codespell==2.3.0; extra == "test"
30
- Requires-Dist: coverage==7.6.0; extra == "test"
31
- Requires-Dist: mypy==1.11.1; extra == "test"
30
+ Requires-Dist: coverage==7.6.1; extra == "test"
31
+ Requires-Dist: mypy==1.11.2; extra == "test"
32
32
  Requires-Dist: pyserial-asyncio-fast==0.14; extra == "test"
33
33
  Requires-Dist: pytest==8.3.2; extra == "test"
34
- Requires-Dist: pytest-asyncio==0.23.8; extra == "test"
35
- Requires-Dist: ruff==0.5.5; extra == "test"
36
- Requires-Dist: tox==4.16.0; extra == "test"
37
- Requires-Dist: types-pyserial==3.5.0.20240527; extra == "test"
34
+ Requires-Dist: pytest-asyncio==0.24.0; extra == "test"
35
+ Requires-Dist: ruff==0.5.6; extra == "test"
36
+ Requires-Dist: tox==4.18.0; extra == "test"
37
+ Requires-Dist: types-pyserial==3.5.0.20240826; extra == "test"
38
38
  Provides-Extra: docs
39
39
  Requires-Dist: sphinx==7.4.7; extra == "docs"
40
40
  Requires-Dist: sphinx_rtd_theme==2.0.0; extra == "docs"
@@ -14,11 +14,11 @@ readthedocs-sphinx-search==0.3.2
14
14
 
15
15
  [test]
16
16
  codespell==2.3.0
17
- coverage==7.6.0
18
- mypy==1.11.1
17
+ coverage==7.6.1
18
+ mypy==1.11.2
19
19
  pyserial-asyncio-fast==0.14
20
20
  pytest==8.3.2
21
- pytest-asyncio==0.23.8
22
- ruff==0.5.5
23
- tox==4.16.0
24
- types-pyserial==3.5.0.20240527
21
+ pytest-asyncio==0.24.0
22
+ ruff==0.5.6
23
+ tox==4.18.0
24
+ types-pyserial==3.5.0.20240826
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.5.25'
16
- __version_tuple__ = version_tuple = (0, 5, 25)
15
+ __version__ = version = '0.5.26'
16
+ __version_tuple__ = version_tuple = (0, 5, 26)
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
 
5
5
  from abc import ABC
6
6
  import asyncio
7
- from collections.abc import Iterable
8
7
  from functools import cache
9
8
  from typing import Any, ClassVar
10
9
 
@@ -13,7 +12,7 @@ from pyplumio.exceptions import UnknownDeviceError
13
12
  from pyplumio.frames import DataFrameDescription, Frame, Request
14
13
  from pyplumio.helpers.event_manager import EventManager
15
14
  from pyplumio.helpers.factory import create_instance
16
- from pyplumio.helpers.parameter import SET_RETRIES, Parameter, ParameterValueType
15
+ from pyplumio.helpers.parameter import Parameter, ParameterValue
17
16
  from pyplumio.structures.network_info import NetworkInfo
18
17
  from pyplumio.utils import to_camelcase
19
18
 
@@ -54,9 +53,9 @@ class Device(ABC, EventManager):
54
53
  async def set(
55
54
  self,
56
55
  name: str,
57
- value: ParameterValueType,
56
+ value: ParameterValue,
57
+ retries: int = 5,
58
58
  timeout: float | None = None,
59
- retries: int = SET_RETRIES,
60
59
  ) -> bool:
61
60
  """Set a parameter value.
62
61
 
@@ -64,12 +63,12 @@ class Device(ABC, EventManager):
64
63
  :type name: str
65
64
  :param value: New value for the parameter
66
65
  :type value: int | float | bool | Literal["off", "on"]
67
- :param timeout: Wait this amount of seconds for confirmation,
68
- defaults to `None`
69
- :type timeout: float, optional
70
66
  :param retries: Try setting parameter for this amount of
71
67
  times, defaults to 5
72
68
  :type retries: int, optional
69
+ :param timeout: Wait this amount of seconds for confirmation,
70
+ defaults to `None`
71
+ :type timeout: float, optional
73
72
  :return: `True` if parameter was successfully set, `False`
74
73
  otherwise.
75
74
  :rtype: bool
@@ -77,7 +76,7 @@ class Device(ABC, EventManager):
77
76
  :raise ValueError: when a new value is outside of allowed range
78
77
  :raise TypeError: when found data is not valid parameter
79
78
  """
80
- parameter = await self.get(name, timeout=timeout)
79
+ parameter = await self.get(name, timeout)
81
80
  if not isinstance(parameter, Parameter):
82
81
  raise TypeError(f"{name} is not valid parameter")
83
82
 
@@ -86,9 +85,9 @@ class Device(ABC, EventManager):
86
85
  def set_nowait(
87
86
  self,
88
87
  name: str,
89
- value: ParameterValueType,
88
+ value: ParameterValue,
89
+ retries: int = 5,
90
90
  timeout: float | None = None,
91
- retries: int = SET_RETRIES,
92
91
  ) -> None:
93
92
  """Set a parameter value without waiting for the result.
94
93
 
@@ -96,19 +95,19 @@ class Device(ABC, EventManager):
96
95
  :type name: str
97
96
  :param value: New value for the parameter
98
97
  :type value: int | float | bool | Literal["off", "on"]
98
+ :param retries: Try setting parameter for this amount of
99
+ times, defaults to 5
100
+ :type retries: int, optional
99
101
  :param timeout: Wait this amount of seconds for confirmation.
100
102
  As this method operates in the background without waiting,
101
103
  this value is used to determine failure when
102
104
  retrying and doesn't block, defaults to `None`
103
105
  :type timeout: float, optional
104
- :param retries: Try setting parameter for this amount of
105
- times, defaults to 5
106
- :type retries: int, optional
107
106
  :return: `True` if parameter was successfully set, `False`
108
107
  otherwise.
109
108
  :rtype: bool
110
109
  """
111
- self.create_task(self.set(name, value, timeout, retries))
110
+ self.create_task(self.set(name, value, retries, timeout))
112
111
 
113
112
  async def shutdown(self) -> None:
114
113
  """Cancel device tasks."""
@@ -116,22 +115,22 @@ class Device(ABC, EventManager):
116
115
  await self.wait_until_done()
117
116
 
118
117
 
119
- class AddressableDevice(Device, ABC):
120
- """Represents an addressable device."""
118
+ class PhysicalDevice(Device, ABC):
119
+ """Represents a physical device.
120
+
121
+ Physical device have network address and can have multiple
122
+ virtual devices associated with them via parent property.
123
+ """
121
124
 
122
125
  address: ClassVar[int]
123
126
  _network: NetworkInfo
124
- _setup_frames: Iterable[DataFrameDescription]
127
+ _setup_frames: tuple[DataFrameDescription, ...]
125
128
 
126
129
  def __init__(self, queue: asyncio.Queue[Frame], network: NetworkInfo):
127
- """Initialize a new addressable device."""
130
+ """Initialize a new physical device."""
128
131
  super().__init__(queue)
129
132
  self._network = network
130
133
 
131
- def __int__(self) -> int:
132
- """Return the device address."""
133
- return int(self.address)
134
-
135
134
  def handle_frame(self, frame: Frame) -> None:
136
135
  """Handle frame received from the device."""
137
136
  frame.sender_device = self
@@ -176,19 +175,19 @@ class AddressableDevice(Device, ABC):
176
175
  raise ValueError(f'could not request "{name}"', frame_type)
177
176
 
178
177
  @classmethod
179
- async def create(cls, device_type: int, **kwargs: Any) -> AddressableDevice:
180
- """Create a device handler object."""
178
+ async def create(cls, device_type: int, **kwargs: Any) -> PhysicalDevice:
179
+ """Create a physical device handler object."""
181
180
  return await create_instance(get_device_handler(device_type), cls=cls, **kwargs)
182
181
 
183
182
 
184
- class SubDevice(Device, ABC):
185
- """Represents a sub-device."""
183
+ class VirtualDevice(Device, ABC):
184
+ """Represents a virtual device associated with physical device."""
186
185
 
187
- parent: AddressableDevice
186
+ parent: PhysicalDevice
188
187
  index: int
189
188
 
190
189
  def __init__(
191
- self, queue: asyncio.Queue[Frame], parent: AddressableDevice, index: int = 0
190
+ self, queue: asyncio.Queue[Frame], parent: PhysicalDevice, index: int = 0
192
191
  ):
193
192
  """Initialize a new sub-device."""
194
193
  super().__init__(queue)
@@ -6,7 +6,7 @@ import asyncio
6
6
  from collections.abc import Coroutine, Generator, Iterable, Sequence
7
7
  import logging
8
8
  import time
9
- from typing import Any, ClassVar, Final
9
+ from typing import Any, Final
10
10
 
11
11
  from pyplumio.const import (
12
12
  ATTR_FRAME_ERRORS,
@@ -17,7 +17,7 @@ from pyplumio.const import (
17
17
  DeviceType,
18
18
  FrameType,
19
19
  )
20
- from pyplumio.devices import AddressableDevice
20
+ from pyplumio.devices import PhysicalDevice
21
21
  from pyplumio.devices.mixer import Mixer
22
22
  from pyplumio.devices.thermostat import Thermostat
23
23
  from pyplumio.filters import on_change
@@ -101,13 +101,14 @@ SETUP_FRAME_TYPES: tuple[DataFrameDescription, ...] = (
101
101
  _LOGGER = logging.getLogger(__name__)
102
102
 
103
103
 
104
- class EcoMAX(AddressableDevice):
104
+ class EcoMAX(PhysicalDevice):
105
105
  """Represents an ecoMAX controller."""
106
106
 
107
- address: ClassVar[int] = DeviceType.ECOMAX
108
- _setup_frames: tuple[DataFrameDescription, ...] = SETUP_FRAME_TYPES
107
+ address = DeviceType.ECOMAX
108
+
109
109
  _frame_versions: dict[int, int]
110
110
  _fuel_burned_timestamp_ns: int
111
+ _setup_frames = SETUP_FRAME_TYPES
111
112
 
112
113
  def __init__(self, queue: asyncio.Queue[Frame], network: NetworkInfo):
113
114
  """Initialize a new ecoMAX controller."""
@@ -216,10 +217,7 @@ class EcoMAX(AddressableDevice):
216
217
  yield self.dispatch(
217
218
  description.name,
218
219
  handler.create_or_update(
219
- device=self,
220
- description=description,
221
- values=values,
222
- index=index,
220
+ device=self, description=description, values=values, index=index
223
221
  ),
224
222
  )
225
223
 
@@ -263,17 +261,16 @@ class EcoMAX(AddressableDevice):
263
261
  parameter's name and value. Events are dispatched for the
264
262
  respective mixer instance.
265
263
  """
266
- if not parameters:
267
- return False
268
-
269
- await asyncio.gather(
270
- *(
271
- mixer.dispatch(ATTR_MIXER_PARAMETERS, parameters[mixer.index])
272
- for mixer in self._mixers(indexes=parameters.keys())
264
+ if parameters:
265
+ await asyncio.gather(
266
+ *(
267
+ mixer.dispatch(ATTR_MIXER_PARAMETERS, parameters[mixer.index])
268
+ for mixer in self._mixers(indexes=parameters.keys())
269
+ )
273
270
  )
274
- )
271
+ return True
275
272
 
276
- return True
273
+ return False
277
274
 
278
275
  async def _handle_mixer_sensors(
279
276
  self, sensors: dict[int, dict[str, Any]] | None
@@ -284,17 +281,16 @@ class EcoMAX(AddressableDevice):
284
281
  sensor's name and value. Events are dispatched for the
285
282
  respective mixer instance.
286
283
  """
287
- if not sensors:
288
- return False
289
-
290
- await asyncio.gather(
291
- *(
292
- mixer.dispatch(ATTR_MIXER_SENSORS, sensors[mixer.index])
293
- for mixer in self._mixers(indexes=sensors.keys())
284
+ if sensors:
285
+ await asyncio.gather(
286
+ *(
287
+ mixer.dispatch(ATTR_MIXER_SENSORS, sensors[mixer.index])
288
+ for mixer in self._mixers(indexes=sensors.keys())
289
+ )
294
290
  )
295
- )
291
+ return True
296
292
 
297
- return True
293
+ return False
298
294
 
299
295
  async def _add_schedules(
300
296
  self, schedules: list[tuple[int, list[list[bool]]]]
@@ -332,10 +328,7 @@ class EcoMAX(AddressableDevice):
332
328
  yield self.dispatch(
333
329
  description.name,
334
330
  handler.create_or_update(
335
- device=self,
336
- description=description,
337
- values=values,
338
- index=index,
331
+ device=self, description=description, values=values, index=index
339
332
  ),
340
333
  )
341
334
 
@@ -376,29 +369,29 @@ class EcoMAX(AddressableDevice):
376
369
  parameter's name and value. Events are dispatched for the
377
370
  respective thermostat instance.
378
371
  """
379
- if not parameters:
380
- return False
381
-
382
- await asyncio.gather(
383
- *(
384
- thermostat.dispatch(
385
- ATTR_THERMOSTAT_PARAMETERS, parameters[thermostat.index]
372
+ if parameters:
373
+ await asyncio.gather(
374
+ *(
375
+ thermostat.dispatch(
376
+ ATTR_THERMOSTAT_PARAMETERS, parameters[thermostat.index]
377
+ )
378
+ for thermostat in self._thermostats(indexes=parameters.keys())
386
379
  )
387
- for thermostat in self._thermostats(indexes=parameters.keys())
388
380
  )
389
- )
390
- return True
381
+ return True
382
+
383
+ return False
391
384
 
392
385
  async def _add_thermostat_profile_parameter(
393
386
  self, values: ParameterValues | None
394
387
  ) -> EcomaxNumber | None:
395
388
  """Add thermostat profile parameter to the dataset."""
396
- if not values:
397
- return None
389
+ if values:
390
+ return EcomaxNumber(
391
+ device=self, description=THERMOSTAT_PROFILE_PARAMETER, values=values
392
+ )
398
393
 
399
- return EcomaxNumber(
400
- device=self, description=THERMOSTAT_PROFILE_PARAMETER, values=values
401
- )
394
+ return None
402
395
 
403
396
  async def _handle_thermostat_sensors(
404
397
  self, sensors: dict[int, dict[str, Any]] | None
@@ -409,18 +402,19 @@ class EcoMAX(AddressableDevice):
409
402
  sensor's name and value. Events are dispatched for the
410
403
  respective thermostat instance.
411
404
  """
412
- if not sensors:
413
- return False
414
-
415
- await asyncio.gather(
416
- *(
417
- thermostat.dispatch(ATTR_THERMOSTAT_SENSORS, sensors[thermostat.index])
418
- for thermostat in self._thermostats(indexes=sensors.keys())
419
- ),
420
- return_exceptions=True,
421
- )
405
+ if sensors:
406
+ await asyncio.gather(
407
+ *(
408
+ thermostat.dispatch(
409
+ ATTR_THERMOSTAT_SENSORS, sensors[thermostat.index]
410
+ )
411
+ for thermostat in self._thermostats(indexes=sensors.keys())
412
+ ),
413
+ return_exceptions=True,
414
+ )
415
+ return True
422
416
 
423
- return True
417
+ return False
424
418
 
425
419
  async def turn_on(self) -> bool:
426
420
  """Turn on the ecoMAX controller."""
@@ -0,0 +1,12 @@
1
+ """Contains an ecoSTER class."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pyplumio.const import DeviceType
6
+ from pyplumio.devices import PhysicalDevice
7
+
8
+
9
+ class EcoSTER(PhysicalDevice):
10
+ """Represents an ecoSTER thermostat."""
11
+
12
+ address = DeviceType.ECOSTER
@@ -7,7 +7,7 @@ from collections.abc import Coroutine, Generator, Sequence
7
7
  import logging
8
8
  from typing import TYPE_CHECKING, Any
9
9
 
10
- from pyplumio.devices import AddressableDevice, SubDevice
10
+ from pyplumio.devices import PhysicalDevice, VirtualDevice
11
11
  from pyplumio.helpers.parameter import ParameterValues
12
12
  from pyplumio.structures.mixer_parameters import (
13
13
  ATTR_MIXER_PARAMETERS,
@@ -25,11 +25,11 @@ if TYPE_CHECKING:
25
25
  _LOGGER = logging.getLogger(__name__)
26
26
 
27
27
 
28
- class Mixer(SubDevice):
29
- """Represents an mixer."""
28
+ class Mixer(VirtualDevice):
29
+ """Represents a mixer."""
30
30
 
31
31
  def __init__(
32
- self, queue: asyncio.Queue[Frame], parent: AddressableDevice, index: int = 0
32
+ self, queue: asyncio.Queue[Frame], parent: PhysicalDevice, index: int = 0
33
33
  ):
34
34
  """Initialize a new mixer."""
35
35
  super().__init__(queue, parent, index)
@@ -85,10 +85,7 @@ class Mixer(SubDevice):
85
85
  yield self.dispatch(
86
86
  description.name,
87
87
  handler.create_or_update(
88
- device=self,
89
- description=description,
90
- values=values,
91
- index=index,
88
+ device=self, description=description, values=values, index=index
92
89
  ),
93
90
  )
94
91
 
@@ -6,7 +6,7 @@ import asyncio
6
6
  from collections.abc import Coroutine, Generator, Sequence
7
7
  from typing import TYPE_CHECKING, Any
8
8
 
9
- from pyplumio.devices import AddressableDevice, SubDevice
9
+ from pyplumio.devices import PhysicalDevice, VirtualDevice
10
10
  from pyplumio.helpers.parameter import ParameterValues
11
11
  from pyplumio.structures.thermostat_parameters import (
12
12
  ATTR_THERMOSTAT_PARAMETERS,
@@ -21,11 +21,11 @@ if TYPE_CHECKING:
21
21
  from pyplumio.frames import Frame
22
22
 
23
23
 
24
- class Thermostat(SubDevice):
24
+ class Thermostat(VirtualDevice):
25
25
  """Represents a thermostat."""
26
26
 
27
27
  def __init__(
28
- self, queue: asyncio.Queue[Frame], parent: AddressableDevice, index: int = 0
28
+ self, queue: asyncio.Queue[Frame], parent: PhysicalDevice, index: int = 0
29
29
  ):
30
30
  """Initialize a new thermostat."""
31
31
  super().__init__(queue, parent, index)
@@ -150,7 +150,7 @@ def on_change(callback: Callback) -> _OnChange:
150
150
  previous call.
151
151
 
152
152
  :param callback: A callback function to be awaited on value change
153
- :type callback: Callable[[Any], Coroutine[Any, Any, Any]]
153
+ :type callback: Callback
154
154
  :return: A instance of callable filter
155
155
  :rtype: _OnChange
156
156
  """
@@ -197,7 +197,7 @@ def debounce(callback: Callback, min_calls: int) -> _Debounce:
197
197
  across multiple filter calls.
198
198
 
199
199
  :param callback: A callback function to be awaited on value change
200
- :type callback: Callable[[Any], Coroutine[Any, Any, Any]]
200
+ :type callback: Callback
201
201
  :param min_calls: Value shouldn't change for this amount of
202
202
  filter calls
203
203
  :type min_calls: int
@@ -244,7 +244,7 @@ def throttle(callback: Callback, seconds: float) -> _Throttle:
244
244
 
245
245
  :param callback: A callback function that will be awaited once
246
246
  filter conditions are fulfilled
247
- :type callback: Callable[[Any], Coroutine[Any, Any, Any]]
247
+ :type callback: Callback
248
248
  :param seconds: A callback will be awaited at most once per
249
249
  this amount of seconds
250
250
  :type seconds: float
@@ -284,7 +284,7 @@ def delta(callback: Callback) -> _Delta:
284
284
 
285
285
  :param callback: A callback function that will be awaited with
286
286
  difference between values in two subsequent calls
287
- :type callback: Callable[[Any], Coroutine[Any, Any, Any]]
287
+ :type callback: Callback
288
288
  :return: A instance of callable filter
289
289
  :rtype: _Delta
290
290
  """
@@ -336,7 +336,7 @@ def aggregate(callback: Callback, seconds: float) -> _Aggregate:
336
336
 
337
337
  :param callback: A callback function to be awaited once filter
338
338
  conditions are fulfilled
339
- :type callback: Callable[[Any], Coroutine[Any, Any, Any]]
339
+ :type callback: Callback
340
340
  :param seconds: A callback will be awaited with a sum of values
341
341
  aggregated over this amount of seconds.
342
342
  :type seconds: float
@@ -378,7 +378,7 @@ def custom(callback: Callback, filter_fn: Callable[[Any], bool]) -> _Custom:
378
378
 
379
379
  :param callback: A callback function to be awaited when
380
380
  filter function return true
381
- :type callback: Callable[[Any], Coroutine[Any, Any, Any]]
381
+ :type callback: Callback
382
382
  :param filter_fn: Filter function, that will be called with a
383
383
  value and should return `True` to await filter's callback
384
384
  :type filter_fn: Callable[[Any], bool]
@@ -26,7 +26,7 @@ ECONET_VERSION: Final = 5
26
26
  struct_header = struct.Struct("<BH4B")
27
27
 
28
28
  if TYPE_CHECKING:
29
- from pyplumio.devices import AddressableDevice
29
+ from pyplumio.devices import PhysicalDevice
30
30
 
31
31
 
32
32
  def bcc(data: bytes) -> int:
@@ -65,6 +65,9 @@ class DataFrameDescription:
65
65
  provides: str
66
66
 
67
67
 
68
+ FrameT = TypeVar("FrameT", bound="Frame")
69
+
70
+
68
71
  class Frame(ABC):
69
72
  """Represents a frame."""
70
73
 
@@ -80,9 +83,9 @@ class Frame(ABC):
80
83
  )
81
84
 
82
85
  recipient: DeviceType
83
- recipient_device: AddressableDevice | None
86
+ recipient_device: PhysicalDevice | None
84
87
  sender: DeviceType
85
- sender_device: AddressableDevice | None
88
+ sender_device: PhysicalDevice | None
86
89
  econet_type: int
87
90
  econet_version: int
88
91
  frame_type: ClassVar[FrameType]
@@ -234,9 +237,6 @@ class Frame(ABC):
234
237
  """Decode frame message."""
235
238
 
236
239
 
237
- FrameT = TypeVar("FrameT", bound=Frame)
238
-
239
-
240
240
  class Request(Frame):
241
241
  """Represents a request."""
242
242
 
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from contextlib import suppress
6
- from typing import Any, ClassVar
6
+ from typing import Any
7
7
 
8
8
  from pyplumio.const import (
9
9
  ATTR_SENSORS,
@@ -37,7 +37,7 @@ class RegulatorDataMessage(Message):
37
37
 
38
38
  __slots__ = ()
39
39
 
40
- frame_type: ClassVar[FrameType] = FrameType.MESSAGE_REGULATOR_DATA
40
+ frame_type = FrameType.MESSAGE_REGULATOR_DATA
41
41
 
42
42
  def decode_message(self, message: bytearray) -> dict[str, Any]:
43
43
  """Decode a frame message."""
@@ -49,7 +49,7 @@ class SensorDataMessage(Message):
49
49
 
50
50
  __slots__ = ()
51
51
 
52
- frame_type: ClassVar[FrameType] = FrameType.MESSAGE_SENSOR_DATA
52
+ frame_type = FrameType.MESSAGE_SENSOR_DATA
53
53
 
54
54
  def decode_message(self, message: bytearray) -> dict[str, Any]:
55
55
  """Decode a frame message."""