PyPlumIO 0.6.1__tar.gz → 0.6.2__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 (173) hide show
  1. {pyplumio-0.6.1 → pyplumio-0.6.2}/.pre-commit-config.yaml +2 -2
  2. {pyplumio-0.6.1 → pyplumio-0.6.2}/PKG-INFO +4 -4
  3. {pyplumio-0.6.1 → pyplumio-0.6.2}/PyPlumIO.egg-info/PKG-INFO +4 -4
  4. {pyplumio-0.6.1 → pyplumio-0.6.2}/PyPlumIO.egg-info/SOURCES.txt +1 -20
  5. {pyplumio-0.6.1 → pyplumio-0.6.2}/PyPlumIO.egg-info/requires.txt +3 -3
  6. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/source/callbacks.rst +12 -0
  7. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/__init__.py +3 -1
  8. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/_version.py +3 -3
  9. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/const.py +0 -5
  10. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/data_types.py +2 -2
  11. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/devices/__init__.py +23 -5
  12. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/devices/ecomax.py +30 -53
  13. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/devices/ecoster.py +2 -3
  14. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/filters.py +199 -136
  15. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/frames/__init__.py +101 -15
  16. pyplumio-0.6.2/pyplumio/frames/messages.py +27 -0
  17. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/frames/requests.py +38 -38
  18. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/frames/responses.py +30 -86
  19. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/helpers/async_cache.py +13 -8
  20. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/helpers/event_manager.py +24 -18
  21. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/helpers/factory.py +0 -3
  22. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/parameters/__init__.py +38 -35
  23. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/protocol.py +14 -8
  24. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/alerts.py +2 -2
  25. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/ecomax_parameters.py +1 -1
  26. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/frame_versions.py +3 -2
  27. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/mixer_parameters.py +5 -3
  28. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/network_info.py +1 -0
  29. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/product_info.py +1 -1
  30. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/program_version.py +2 -2
  31. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/schedules.py +8 -40
  32. pyplumio-0.6.2/pyplumio/structures/sensor_data.py +498 -0
  33. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/thermostat_parameters.py +7 -4
  34. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/utils.py +41 -4
  35. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyproject.toml +3 -3
  36. {pyplumio-0.6.1 → pyplumio-0.6.2}/requirements_test.txt +3 -3
  37. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/conftest.py +5 -6
  38. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/devices/test_ecomax.py +22 -25
  39. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/devices/test_init.py +9 -3
  40. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/devices/test_mixer.py +1 -1
  41. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/devices/test_thermostat.py +1 -1
  42. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/frames/test_init.py +38 -4
  43. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/frames/test_messages.py +2 -1
  44. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/frames/test_responses.py +1 -1
  45. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/helpers/test_event_manager.py +2 -2
  46. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/parameters/custom/test_ecomax_860d3_hb.py +14 -11
  47. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/parameters/test_init.py +15 -5
  48. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/structures/test_schedules.py +33 -35
  49. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/test_filters.py +70 -20
  50. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/test_protocol.py +13 -7
  51. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/test_stream.py +1 -1
  52. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/test_utils.py +48 -0
  53. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/messages/sensor_data.json +3 -3
  54. pyplumio-0.6.1/pyplumio/frames/messages.py +0 -84
  55. pyplumio-0.6.1/pyplumio/structures/boiler_load.py +0 -32
  56. pyplumio-0.6.1/pyplumio/structures/boiler_power.py +0 -33
  57. pyplumio-0.6.1/pyplumio/structures/fan_power.py +0 -33
  58. pyplumio-0.6.1/pyplumio/structures/fuel_consumption.py +0 -36
  59. pyplumio-0.6.1/pyplumio/structures/fuel_level.py +0 -39
  60. pyplumio-0.6.1/pyplumio/structures/lambda_sensor.py +0 -57
  61. pyplumio-0.6.1/pyplumio/structures/mixer_sensors.py +0 -80
  62. pyplumio-0.6.1/pyplumio/structures/modules.py +0 -102
  63. pyplumio-0.6.1/pyplumio/structures/output_flags.py +0 -47
  64. pyplumio-0.6.1/pyplumio/structures/outputs.py +0 -88
  65. pyplumio-0.6.1/pyplumio/structures/pending_alerts.py +0 -28
  66. pyplumio-0.6.1/pyplumio/structures/statuses.py +0 -52
  67. pyplumio-0.6.1/pyplumio/structures/temperatures.py +0 -94
  68. pyplumio-0.6.1/pyplumio/structures/thermostat_sensors.py +0 -106
  69. pyplumio-0.6.1/tests/structures/test_boiler_load.py +0 -33
  70. pyplumio-0.6.1/tests/structures/test_boiler_power.py +0 -32
  71. pyplumio-0.6.1/tests/structures/test_fan_power.py +0 -30
  72. pyplumio-0.6.1/tests/structures/test_fuel_consumption.py +0 -35
  73. pyplumio-0.6.1/tests/structures/test_fuel_level.py +0 -33
  74. pyplumio-0.6.1/tests/structures/test_lambda_sensor.py +0 -42
  75. {pyplumio-0.6.1 → pyplumio-0.6.2}/.gitattributes +0 -0
  76. {pyplumio-0.6.1 → pyplumio-0.6.2}/.github/CODE_OF_CONDUCT.md +0 -0
  77. {pyplumio-0.6.1 → pyplumio-0.6.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  78. {pyplumio-0.6.1 → pyplumio-0.6.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  79. {pyplumio-0.6.1 → pyplumio-0.6.2}/.github/dependabot.yml +0 -0
  80. {pyplumio-0.6.1 → pyplumio-0.6.2}/.github/workflows/ci.yml +0 -0
  81. {pyplumio-0.6.1 → pyplumio-0.6.2}/.github/workflows/codeql.yml +0 -0
  82. {pyplumio-0.6.1 → pyplumio-0.6.2}/.github/workflows/deploy.yml +0 -0
  83. {pyplumio-0.6.1 → pyplumio-0.6.2}/.github/workflows/documentation.yml +0 -0
  84. {pyplumio-0.6.1 → pyplumio-0.6.2}/.gitignore +0 -0
  85. {pyplumio-0.6.1 → pyplumio-0.6.2}/.qlty/qlty.toml +0 -0
  86. {pyplumio-0.6.1 → pyplumio-0.6.2}/.vscode/settings.json +0 -0
  87. {pyplumio-0.6.1 → pyplumio-0.6.2}/LICENSE +0 -0
  88. {pyplumio-0.6.1 → pyplumio-0.6.2}/MANIFEST.in +0 -0
  89. {pyplumio-0.6.1 → pyplumio-0.6.2}/PyPlumIO.egg-info/dependency_links.txt +0 -0
  90. {pyplumio-0.6.1 → pyplumio-0.6.2}/PyPlumIO.egg-info/top_level.txt +0 -0
  91. {pyplumio-0.6.1 → pyplumio-0.6.2}/README.md +0 -0
  92. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/Makefile +0 -0
  93. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/make.bat +0 -0
  94. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/source/conf.py +0 -0
  95. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/source/connecting.rst +0 -0
  96. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/source/frames.rst +0 -0
  97. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/source/index.rst +0 -0
  98. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/source/mixers_thermostats.rst +0 -0
  99. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/source/protocol.rst +0 -0
  100. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/source/reading.rst +0 -0
  101. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/source/schedules.rst +0 -0
  102. {pyplumio-0.6.1 → pyplumio-0.6.2}/docs/source/writing.rst +0 -0
  103. {pyplumio-0.6.1 → pyplumio-0.6.2}/images/ecomax.png +0 -0
  104. {pyplumio-0.6.1 → pyplumio-0.6.2}/images/rs485.png +0 -0
  105. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/__main__.py +0 -0
  106. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/connection.py +0 -0
  107. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/devices/mixer.py +0 -0
  108. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/devices/thermostat.py +0 -0
  109. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/exceptions.py +0 -0
  110. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/helpers/__init__.py +0 -0
  111. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/helpers/task_manager.py +0 -0
  112. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/parameters/custom/__init__.py +0 -0
  113. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/parameters/custom/ecomax_860d3_hb.py +0 -0
  114. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/parameters/ecomax.py +0 -0
  115. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/parameters/mixer.py +0 -0
  116. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/parameters/thermostat.py +0 -0
  117. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/py.typed +0 -0
  118. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/stream.py +0 -0
  119. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/__init__.py +0 -0
  120. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/regulator_data.py +0 -0
  121. {pyplumio-0.6.1 → pyplumio-0.6.2}/pyplumio/structures/regulator_data_schema.py +0 -0
  122. {pyplumio-0.6.1 → pyplumio-0.6.2}/requirements.txt +0 -0
  123. {pyplumio-0.6.1 → pyplumio-0.6.2}/requirements_docs.txt +0 -0
  124. {pyplumio-0.6.1 → pyplumio-0.6.2}/setup.cfg +0 -0
  125. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/__init__.py +0 -0
  126. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/devices/__init__.py +0 -0
  127. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/devices/test_ecoster.py +0 -0
  128. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/frames/test_requests.py +0 -0
  129. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/helpers/__init__.py +0 -0
  130. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/helpers/test_async_cache.py +0 -0
  131. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/helpers/test_factory.py +0 -0
  132. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/helpers/test_task_manager.py +0 -0
  133. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/helpers/test_uid.py +0 -0
  134. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/parameters/__init__.py +0 -0
  135. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/parameters/custom/__init__.py +0 -0
  136. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/parameters/custom/test_init.py +0 -0
  137. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/parameters/test_ecomax.py +0 -0
  138. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/parameters/test_mixers.py +0 -0
  139. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/parameters/test_thermostats.py +0 -0
  140. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/ruff.toml +0 -0
  141. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/structures/__init__.py +0 -0
  142. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/structures/test_alerts.py +0 -0
  143. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/structures/test_ecomax_parameters.py +0 -0
  144. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/structures/test_frame_versions.py +0 -0
  145. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/structures/test_mixer_parameters.py +0 -0
  146. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/structures/test_product_info.py +0 -0
  147. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/test_connection.py +0 -0
  148. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/test_data_types.py +0 -0
  149. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/test_init.py +0 -0
  150. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/test_main.py +0 -0
  151. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/messages/regulator_data.json +0 -0
  152. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/parameters/ecomax_860d3_hb.json +0 -0
  153. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/requests/alerts.json +0 -0
  154. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/requests/ecomax_control.json +0 -0
  155. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/requests/ecomax_parameters.json +0 -0
  156. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/requests/mixer_parameters.json +0 -0
  157. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/requests/set_ecomax_parameter.json +0 -0
  158. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/requests/set_mixer_parameter.json +0 -0
  159. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/requests/set_schedule.json +0 -0
  160. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/requests/set_thermostat_parameter.json +0 -0
  161. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/requests/thermostat_parameters.json +0 -0
  162. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/responses/alerts.json +0 -0
  163. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/responses/device_available.json +0 -0
  164. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/responses/ecomax_parameters.json +0 -0
  165. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/responses/mixer_parameters.json +0 -0
  166. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/responses/password.json +0 -0
  167. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/responses/program_version.json +0 -0
  168. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/responses/regulator_data_schema.json +0 -0
  169. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/responses/schedules.json +0 -0
  170. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/responses/thermostat_parameters.json +0 -0
  171. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/responses/uid.json +0 -0
  172. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/unknown/unknown_ecomax_parameter.json +0 -0
  173. {pyplumio-0.6.1 → pyplumio-0.6.2}/tests/testdata/unknown/unknown_mixer_parameter.json +0 -0
@@ -3,7 +3,7 @@
3
3
  # See https://pre-commit.com/hooks.html for more hooks
4
4
  repos:
5
5
  - repo: https://github.com/astral-sh/ruff-pre-commit
6
- rev: v0.13.0
6
+ rev: v0.14.0
7
7
  hooks:
8
8
  - id: ruff
9
9
  args:
@@ -13,6 +13,6 @@ repos:
13
13
  hooks:
14
14
  - id: codespell
15
15
  - repo: https://github.com/pre-commit/mirrors-mypy
16
- rev: v1.18.1
16
+ rev: v1.18.2
17
17
  hooks:
18
18
  - id: mypy
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPlumIO
3
- Version: 0.6.1
3
+ Version: 0.6.2
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
@@ -32,9 +32,9 @@ Requires-Dist: numpy<3.0.0,>=2.0.0; extra == "test"
32
32
  Requires-Dist: pyserial-asyncio-fast==0.16; extra == "test"
33
33
  Requires-Dist: pytest==8.4.2; extra == "test"
34
34
  Requires-Dist: pytest-asyncio==1.2.0; extra == "test"
35
- Requires-Dist: ruff==0.13.2; extra == "test"
36
- Requires-Dist: tox==4.30.2; extra == "test"
37
- Requires-Dist: types-pyserial==3.5.0.20250919; extra == "test"
35
+ Requires-Dist: ruff==0.13.3; extra == "test"
36
+ Requires-Dist: tox==4.30.3; extra == "test"
37
+ Requires-Dist: types-pyserial==3.5.0.20251001; extra == "test"
38
38
  Provides-Extra: docs
39
39
  Requires-Dist: sphinx==8.1.3; extra == "docs"
40
40
  Requires-Dist: sphinx_rtd_theme==3.0.2; extra == "docs"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPlumIO
3
- Version: 0.6.1
3
+ Version: 0.6.2
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
@@ -32,9 +32,9 @@ Requires-Dist: numpy<3.0.0,>=2.0.0; extra == "test"
32
32
  Requires-Dist: pyserial-asyncio-fast==0.16; extra == "test"
33
33
  Requires-Dist: pytest==8.4.2; extra == "test"
34
34
  Requires-Dist: pytest-asyncio==1.2.0; extra == "test"
35
- Requires-Dist: ruff==0.13.2; extra == "test"
36
- Requires-Dist: tox==4.30.2; extra == "test"
37
- Requires-Dist: types-pyserial==3.5.0.20250919; extra == "test"
35
+ Requires-Dist: ruff==0.13.3; extra == "test"
36
+ Requires-Dist: tox==4.30.3; extra == "test"
37
+ Requires-Dist: types-pyserial==3.5.0.20251001; extra == "test"
38
38
  Provides-Extra: docs
39
39
  Requires-Dist: sphinx==8.1.3; extra == "docs"
40
40
  Requires-Dist: sphinx_rtd_theme==3.0.2; extra == "docs"
@@ -71,30 +71,17 @@ pyplumio/parameters/custom/__init__.py
71
71
  pyplumio/parameters/custom/ecomax_860d3_hb.py
72
72
  pyplumio/structures/__init__.py
73
73
  pyplumio/structures/alerts.py
74
- pyplumio/structures/boiler_load.py
75
- pyplumio/structures/boiler_power.py
76
74
  pyplumio/structures/ecomax_parameters.py
77
- pyplumio/structures/fan_power.py
78
75
  pyplumio/structures/frame_versions.py
79
- pyplumio/structures/fuel_consumption.py
80
- pyplumio/structures/fuel_level.py
81
- pyplumio/structures/lambda_sensor.py
82
76
  pyplumio/structures/mixer_parameters.py
83
- pyplumio/structures/mixer_sensors.py
84
- pyplumio/structures/modules.py
85
77
  pyplumio/structures/network_info.py
86
- pyplumio/structures/output_flags.py
87
- pyplumio/structures/outputs.py
88
- pyplumio/structures/pending_alerts.py
89
78
  pyplumio/structures/product_info.py
90
79
  pyplumio/structures/program_version.py
91
80
  pyplumio/structures/regulator_data.py
92
81
  pyplumio/structures/regulator_data_schema.py
93
82
  pyplumio/structures/schedules.py
94
- pyplumio/structures/statuses.py
95
- pyplumio/structures/temperatures.py
83
+ pyplumio/structures/sensor_data.py
96
84
  pyplumio/structures/thermostat_parameters.py
97
- pyplumio/structures/thermostat_sensors.py
98
85
  tests/__init__.py
99
86
  tests/conftest.py
100
87
  tests/ruff.toml
@@ -132,14 +119,8 @@ tests/parameters/custom/test_ecomax_860d3_hb.py
132
119
  tests/parameters/custom/test_init.py
133
120
  tests/structures/__init__.py
134
121
  tests/structures/test_alerts.py
135
- tests/structures/test_boiler_load.py
136
- tests/structures/test_boiler_power.py
137
122
  tests/structures/test_ecomax_parameters.py
138
- tests/structures/test_fan_power.py
139
123
  tests/structures/test_frame_versions.py
140
- tests/structures/test_fuel_consumption.py
141
- tests/structures/test_fuel_level.py
142
- tests/structures/test_lambda_sensor.py
143
124
  tests/structures/test_mixer_parameters.py
144
125
  tests/structures/test_product_info.py
145
126
  tests/structures/test_schedules.py
@@ -19,6 +19,6 @@ numpy<3.0.0,>=2.0.0
19
19
  pyserial-asyncio-fast==0.16
20
20
  pytest==8.4.2
21
21
  pytest-asyncio==1.2.0
22
- ruff==0.13.2
23
- tox==4.30.2
24
- types-pyserial==3.5.0.20250919
22
+ ruff==0.13.3
23
+ tox==4.30.3
24
+ types-pyserial==3.5.0.20251001
@@ -63,6 +63,18 @@ It can be used to filter out outliers and corrupted data.
63
63
  # Await the callback with value clamped between 0 and 100.
64
64
  ecomax.subscribe("load", filters.clamp(my_callback, min_value=0, max_value=100))
65
65
 
66
+ # Await the callback with value clamped between 0 and 100 and
67
+ # do not call the callback if value is out of range.
68
+ ecomax.subscribe(
69
+ "load",
70
+ filters.clamp(
71
+ my_callback2,
72
+ min_value=0,
73
+ max_value=100,
74
+ ignore_out_of_range=False
75
+ )
76
+ )
77
+
66
78
  .. autofunction:: pyplumio.filters.on_change
67
79
 
68
80
  Normally callbacks are awaited each time the PyPlumIO receives data
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any
6
6
 
7
- from pyplumio._version import __version__, __version_tuple__
7
+ from pyplumio._version import __version__, __version_tuple__, version, version_tuple
8
8
  from pyplumio.connection import SerialConnection, TcpConnection
9
9
  from pyplumio.exceptions import (
10
10
  ChecksumError,
@@ -93,6 +93,8 @@ def open_tcp_connection(
93
93
  __all__ = [
94
94
  "__version__",
95
95
  "__version_tuple__",
96
+ "version",
97
+ "version_tuple",
96
98
  "AsyncProtocol",
97
99
  "ChecksumError",
98
100
  "ConnectionFailedError",
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.6.1'
32
- __version_tuple__ = version_tuple = (0, 6, 1)
31
+ __version__ = version = '0.6.2'
32
+ __version_tuple__ = version_tuple = (0, 6, 2)
33
33
 
34
- __commit_id__ = commit_id = 'g7cf7d589c'
34
+ __commit_id__ = commit_id = 'g4f707ffa0'
@@ -8,7 +8,6 @@ from typing import Any, Final, Literal, TypeAlias
8
8
  # General attributes.
9
9
  ATTR_CONNECTED: Final = "connected"
10
10
  ATTR_COUNT: Final = "count"
11
- ATTR_CURRENT_TEMP: Final = "current_temp"
12
11
  ATTR_DEVICE_INDEX: Final = "device_index"
13
12
  ATTR_FRAME_ERRORS: Final = "frame_errors"
14
13
  ATTR_INDEX: Final = "index"
@@ -19,11 +18,7 @@ ATTR_SCHEDULE: Final = "schedule"
19
18
  ATTR_SETUP: Final = "setup"
20
19
  ATTR_SENSORS: Final = "sensors"
21
20
  ATTR_START: Final = "start"
22
- ATTR_STATE: Final = "state"
23
21
  ATTR_SWITCH: Final = "switch"
24
- ATTR_TARGET_TEMP: Final = "target_temp"
25
- ATTR_THERMOSTAT: Final = "thermostat"
26
- ATTR_TRANSMISSION: Final = "transmission"
27
22
  ATTR_TYPE: Final = "type"
28
23
  ATTR_VALUE: Final = "value"
29
24
  ATTR_SIZE: Final = "size"
@@ -8,7 +8,7 @@ import struct
8
8
  from typing import ClassVar, Final, Generic, TypeVar
9
9
 
10
10
  T = TypeVar("T")
11
- DataTypeT = TypeVar("DataTypeT", bound="DataType")
11
+ _DataTypeT = TypeVar("_DataTypeT", bound="DataType")
12
12
 
13
13
 
14
14
  class DataType(ABC, Generic[T]):
@@ -66,7 +66,7 @@ class DataType(ABC, Generic[T]):
66
66
  return buffer if self.size == 0 else buffer[: self.size]
67
67
 
68
68
  @classmethod
69
- def from_bytes(cls: type[DataTypeT], buffer: bytes, offset: int = 0) -> DataTypeT:
69
+ def from_bytes(cls: type[_DataTypeT], buffer: bytes, offset: int = 0) -> _DataTypeT:
70
70
  """Initialize a new data type from bytes."""
71
71
  data_type = cls()
72
72
  data_type.unpack(buffer[offset:])
@@ -4,9 +4,10 @@ from __future__ import annotations
4
4
 
5
5
  from abc import ABC
6
6
  import asyncio
7
+ from collections.abc import Callable
7
8
  from functools import cache
8
9
  import logging
9
- from typing import Any, ClassVar
10
+ from typing import Any, ClassVar, TypeVar
10
11
 
11
12
  from pyplumio.const import ATTR_FRAME_ERRORS, DeviceType, FrameType, State
12
13
  from pyplumio.exceptions import RequestError, UnknownDeviceError
@@ -14,7 +15,7 @@ from pyplumio.filters import on_change
14
15
  from pyplumio.frames import Frame, Request, is_known_frame_type
15
16
  from pyplumio.helpers.event_manager import EventManager, event_listener
16
17
  from pyplumio.helpers.factory import create_instance
17
- from pyplumio.parameters import NumericType, Parameter
18
+ from pyplumio.parameters import Numeric, Parameter
18
19
  from pyplumio.structures.network_info import NetworkInfo
19
20
  from pyplumio.utils import to_camelcase
20
21
 
@@ -63,7 +64,7 @@ class Device(ABC, EventManager):
63
64
  async def set(
64
65
  self,
65
66
  name: str,
66
- value: NumericType | State | bool,
67
+ value: Numeric | State | bool,
67
68
  retries: int = 0,
68
69
  timeout: float | None = None,
69
70
  ) -> bool:
@@ -95,7 +96,7 @@ class Device(ABC, EventManager):
95
96
  def set_nowait(
96
97
  self,
97
98
  name: str,
98
- value: NumericType | State | bool,
99
+ value: Numeric | State | bool,
99
100
  retries: int = 0,
100
101
  timeout: float | None = None,
101
102
  ) -> None:
@@ -232,10 +233,27 @@ class VirtualDevice(Device, ABC):
232
233
  self.index = index
233
234
 
234
235
 
236
+ _PhysicalDeviceT = TypeVar("_PhysicalDeviceT", bound=PhysicalDevice)
237
+
238
+
239
+ def device_handler(
240
+ device_type: DeviceType,
241
+ ) -> Callable[[type[_PhysicalDeviceT]], type[_PhysicalDeviceT]]:
242
+ """Specify device type (address) for the physical device class."""
243
+
244
+ def wrapper(cls: type[_PhysicalDeviceT]) -> type[_PhysicalDeviceT]:
245
+ """Wrap the physical device class."""
246
+ setattr(cls, "address", device_type)
247
+ return cls
248
+
249
+ return wrapper
250
+
251
+
235
252
  __all__ = [
236
253
  "Device",
237
254
  "PhysicalDevice",
238
255
  "VirtualDevice",
239
- "is_known_device_type",
256
+ "device_hander",
240
257
  "get_device_handler",
258
+ "is_known_device_type",
241
259
  ]
@@ -6,7 +6,7 @@ import asyncio
6
6
  from collections.abc import Coroutine, Generator, Iterable
7
7
  import logging
8
8
  import time
9
- from typing import Any, Final
9
+ from typing import Any, Final, NamedTuple
10
10
 
11
11
  from pyplumio.const import (
12
12
  ATTR_FRAME_ERRORS,
@@ -20,12 +20,12 @@ from pyplumio.const import (
20
20
  FrameType,
21
21
  State,
22
22
  )
23
- from pyplumio.devices import PhysicalDevice
23
+ from pyplumio.devices import PhysicalDevice, device_handler
24
24
  from pyplumio.devices.mixer import Mixer
25
25
  from pyplumio.devices.thermostat import Thermostat
26
26
  from pyplumio.exceptions import RequestError
27
27
  from pyplumio.filters import on_change
28
- from pyplumio.frames import DataFrameDescription, Frame, Request
28
+ from pyplumio.frames import Frame, Request
29
29
  from pyplumio.helpers.event_manager import event_listener
30
30
  from pyplumio.parameters import ParameterValues
31
31
  from pyplumio.parameters.ecomax import (
@@ -42,7 +42,6 @@ from pyplumio.structures.ecomax_parameters import (
42
42
  ATTR_ECOMAX_PARAMETERS,
43
43
  )
44
44
  from pyplumio.structures.mixer_parameters import ATTR_MIXER_PARAMETERS
45
- from pyplumio.structures.mixer_sensors import ATTR_MIXER_SENSORS
46
45
  from pyplumio.structures.network_info import ATTR_NETWORK, NetworkInfo
47
46
  from pyplumio.structures.product_info import ATTR_PRODUCT, ProductInfo
48
47
  from pyplumio.structures.regulator_data_schema import ATTR_REGDATA_SCHEMA
@@ -56,8 +55,8 @@ from pyplumio.structures.schedules import (
56
55
  ScheduleSwitch,
57
56
  ScheduleSwitchDescription,
58
57
  )
58
+ from pyplumio.structures.sensor_data import ATTR_MIXER_SENSORS, ATTR_THERMOSTAT_SENSORS
59
59
  from pyplumio.structures.thermostat_parameters import ATTR_THERMOSTAT_PARAMETERS
60
- from pyplumio.structures.thermostat_sensors import ATTR_THERMOSTAT_SENSORS
61
60
 
62
61
  _LOGGER = logging.getLogger(__name__)
63
62
 
@@ -102,46 +101,30 @@ class FuelMeter:
102
101
  return None
103
102
 
104
103
 
105
- REQUIRED: tuple[DataFrameDescription, ...] = (
106
- DataFrameDescription(
107
- frame_type=FrameType.REQUEST_UID,
108
- provides=ATTR_PRODUCT,
109
- ),
110
- DataFrameDescription(
111
- frame_type=FrameType.REQUEST_REGULATOR_DATA_SCHEMA,
112
- provides=ATTR_REGDATA_SCHEMA,
113
- ),
114
- DataFrameDescription(
115
- frame_type=FrameType.REQUEST_ECOMAX_PARAMETERS,
116
- provides=ATTR_ECOMAX_PARAMETERS,
117
- ),
118
- DataFrameDescription(
119
- frame_type=FrameType.REQUEST_ALERTS,
120
- provides=ATTR_TOTAL_ALERTS,
121
- ),
122
- DataFrameDescription(
123
- frame_type=FrameType.REQUEST_SCHEDULES,
124
- provides=ATTR_SCHEDULES,
125
- ),
126
- DataFrameDescription(
127
- frame_type=FrameType.REQUEST_MIXER_PARAMETERS,
128
- provides=ATTR_MIXER_PARAMETERS,
129
- ),
130
- DataFrameDescription(
131
- frame_type=FrameType.REQUEST_THERMOSTAT_PARAMETERS,
132
- provides=ATTR_THERMOSTAT_PARAMETERS,
133
- ),
134
- DataFrameDescription(
135
- frame_type=FrameType.REQUEST_PASSWORD,
136
- provides=ATTR_PASSWORD,
137
- ),
104
+ class DataKey(NamedTuple):
105
+ """Map a data key to frame type."""
106
+
107
+ key: str
108
+ provided_by: FrameType
109
+
110
+
111
+ REQUIRED_KEYS: tuple[DataKey, ...] = (
112
+ DataKey(ATTR_PRODUCT, FrameType.REQUEST_UID),
113
+ DataKey(ATTR_REGDATA_SCHEMA, FrameType.REQUEST_REGULATOR_DATA_SCHEMA),
114
+ DataKey(ATTR_ECOMAX_PARAMETERS, FrameType.REQUEST_ECOMAX_PARAMETERS),
115
+ DataKey(ATTR_TOTAL_ALERTS, FrameType.REQUEST_ALERTS),
116
+ DataKey(ATTR_SCHEDULES, FrameType.REQUEST_SCHEDULES),
117
+ DataKey(ATTR_MIXER_PARAMETERS, FrameType.REQUEST_MIXER_PARAMETERS),
118
+ DataKey(ATTR_THERMOSTAT_PARAMETERS, FrameType.REQUEST_THERMOSTAT_PARAMETERS),
119
+ DataKey(ATTR_PASSWORD, FrameType.REQUEST_PASSWORD),
138
120
  )
139
121
 
140
- REQUIRED_TYPES = [description.frame_type for description in REQUIRED]
122
+ REQUIRED_TYPES = [frame_type for _, frame_type in REQUIRED_KEYS]
141
123
 
142
124
  SETUP_TIMEOUT: Final = 60.0
143
125
 
144
126
 
127
+ @device_handler(DeviceType.ECOMAX)
145
128
  class EcoMAX(PhysicalDevice):
146
129
  """Represents an ecoMAX controller."""
147
130
 
@@ -149,8 +132,6 @@ class EcoMAX(PhysicalDevice):
149
132
 
150
133
  _fuel_meter: FuelMeter
151
134
 
152
- address = DeviceType.ECOMAX
153
-
154
135
  def __init__(self, queue: asyncio.Queue[Frame], network: NetworkInfo) -> None:
155
136
  """Initialize a new ecoMAX controller."""
156
137
  super().__init__(queue, network)
@@ -165,26 +146,26 @@ class EcoMAX(PhysicalDevice):
165
146
 
166
147
  super().handle_frame(frame)
167
148
 
168
- def _mixers(self, indexes: Iterable[int]) -> Generator[Mixer, None, None]:
149
+ def _mixers(self, indexes: Iterable[int]) -> Generator[Mixer]:
169
150
  """Iterate through the mixer indexes.
170
151
 
171
152
  For each index, return or create an instance of the mixer class.
172
153
  Once done, dispatch the 'mixers' event without waiting.
173
154
  """
174
- mixers: dict[int, Mixer] = self.data.setdefault(ATTR_MIXERS, {})
155
+ mixers: dict[int, Mixer] = self._data.setdefault(ATTR_MIXERS, {})
175
156
  for index in indexes:
176
157
  yield mixers.setdefault(index, Mixer(self.queue, parent=self, index=index))
177
158
 
178
159
  return self.dispatch_nowait(ATTR_MIXERS, mixers)
179
160
 
180
- def _thermostats(self, indexes: Iterable[int]) -> Generator[Thermostat, None, None]:
161
+ def _thermostats(self, indexes: Iterable[int]) -> Generator[Thermostat]:
181
162
  """Iterate through the thermostat indexes.
182
163
 
183
164
  For each index, return or create an instance of the thermostat
184
165
  class. Once done, dispatch the 'thermostats' event without
185
166
  waiting.
186
167
  """
187
- thermostats: dict[int, Thermostat] = self.data.setdefault(ATTR_THERMOSTATS, {})
168
+ thermostats: dict[int, Thermostat] = self._data.setdefault(ATTR_THERMOSTATS, {})
188
169
  for index in indexes:
189
170
  yield thermostats.setdefault(
190
171
  index, Thermostat(self.queue, parent=self, index=index)
@@ -203,9 +184,8 @@ class EcoMAX(PhysicalDevice):
203
184
  async def _set_ecomax_state(self, state: State) -> bool:
204
185
  """Try to set the ecoMAX control state."""
205
186
  try:
206
- switch: EcomaxSwitch = self.data[ATTR_ECOMAX_CONTROL]
207
- return await switch.set(state)
208
- except KeyError:
187
+ return await self.set(ATTR_ECOMAX_CONTROL, state, timeout=0.0)
188
+ except asyncio.TimeoutError:
209
189
  _LOGGER.error("ecoMAX control is not available. Please try again later.")
210
190
 
211
191
  return False
@@ -249,10 +229,7 @@ class EcoMAX(PhysicalDevice):
249
229
  return False
250
230
 
251
231
  results = await asyncio.gather(
252
- *(
253
- self.request(description.provides, description.frame_type)
254
- for description in REQUIRED
255
- ),
232
+ *(self.request(key, frame_type) for key, frame_type in REQUIRED_KEYS),
256
233
  return_exceptions=True,
257
234
  )
258
235
 
@@ -352,7 +329,7 @@ class EcoMAX(PhysicalDevice):
352
329
  ) -> bool:
353
330
  """Update schedule parameters and dispatch the events."""
354
331
 
355
- def _schedule_parameter_events() -> Generator[Coroutine, Any, None]:
332
+ def _schedule_parameter_events() -> Generator[Coroutine]:
356
333
  """Get dispatch calls for schedule parameter events."""
357
334
  for index, values in parameters:
358
335
  description = SCHEDULE_PARAMETERS[index]
@@ -3,15 +3,14 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from pyplumio.const import DeviceType
6
- from pyplumio.devices import PhysicalDevice
6
+ from pyplumio.devices import PhysicalDevice, device_handler
7
7
 
8
8
 
9
+ @device_handler(DeviceType.ECOSTER)
9
10
  class EcoSTER(PhysicalDevice):
10
11
  """Represents an ecoSTER thermostat."""
11
12
 
12
13
  __slots__ = ()
13
14
 
14
- address = DeviceType.ECOSTER
15
-
16
15
 
17
16
  __all__ = ["EcoSTER"]