PyPlumIO 0.5.36__tar.gz → 0.5.37__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.
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.pre-commit-config.yaml +2 -2
- {pyplumio-0.5.36 → pyplumio-0.5.37}/PKG-INFO +7 -7
- {pyplumio-0.5.36 → pyplumio-0.5.37}/PyPlumIO.egg-info/PKG-INFO +7 -7
- {pyplumio-0.5.36 → pyplumio-0.5.37}/PyPlumIO.egg-info/requires.txt +4 -4
- {pyplumio-0.5.36 → pyplumio-0.5.37}/README.md +2 -2
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/source/index.rst +3 -2
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/_version.py +2 -2
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/helpers/parameter.py +14 -5
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/ecomax_parameters.py +3 -1
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyproject.toml +7 -6
- {pyplumio-0.5.36 → pyplumio-0.5.37}/requirements_test.txt +4 -4
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/helpers/test_parameter.py +53 -9
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.gitattributes +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.github/CODE_OF_CONDUCT.md +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.github/dependabot.yml +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.github/workflows/ci.yml +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.github/workflows/codeql-analysis.yml +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.github/workflows/deploy.yml +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.github/workflows/documentation.yml +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.gitignore +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/.vscode/settings.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/LICENSE +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/MANIFEST.in +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/PyPlumIO.egg-info/SOURCES.txt +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/PyPlumIO.egg-info/dependency_links.txt +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/PyPlumIO.egg-info/top_level.txt +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/Makefile +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/make.bat +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/source/callbacks.rst +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/source/conf.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/source/connecting.rst +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/source/frames.rst +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/source/mixers_thermostats.rst +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/source/protocol.rst +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/source/reading.rst +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/source/schedules.rst +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/docs/source/writing.rst +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/images/ecomax.png +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/images/rs485.png +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/__init__.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/__main__.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/connection.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/const.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/devices/__init__.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/devices/ecomax.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/devices/ecoster.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/devices/mixer.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/devices/thermostat.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/exceptions.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/filters.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/frames/__init__.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/frames/messages.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/frames/requests.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/frames/responses.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/helpers/__init__.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/helpers/data_types.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/helpers/event_manager.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/helpers/factory.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/helpers/schedule.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/helpers/task_manager.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/helpers/timeout.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/helpers/uid.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/protocol.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/py.typed +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/stream.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/__init__.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/alerts.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/boiler_load.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/boiler_power.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/fan_power.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/frame_versions.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/fuel_consumption.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/fuel_level.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/lambda_sensor.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/mixer_parameters.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/mixer_sensors.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/modules.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/network_info.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/output_flags.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/outputs.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/pending_alerts.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/product_info.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/program_version.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/regulator_data.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/regulator_data_schema.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/schedules.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/statuses.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/temperatures.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/thermostat_parameters.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/structures/thermostat_sensors.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/pyplumio/utils.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/requirements.txt +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/requirements_docs.txt +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/setup.cfg +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/__init__.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/conftest.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/frames/test_init.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/frames/test_messages.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/frames/test_requests.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/frames/test_responses.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/helpers/__init__.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/helpers/test_data_types.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/helpers/test_event_manager.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/helpers/test_factory.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/helpers/test_schedule.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/helpers/test_task_manager.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/helpers/test_timeout.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/helpers/test_uid.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/ruff.toml +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/test_connection.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/test_devices.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/test_filters.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/test_init.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/test_main.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/test_protocol.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/test_stream.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/test_utils.py +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/messages/regulator_data.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/messages/sensor_data.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/requests/alerts.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/requests/ecomax_control.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/requests/ecomax_parameters.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/requests/mixer_parameters.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/requests/set_ecomax_parameter.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/requests/set_mixer_parameter.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/requests/set_schedule.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/requests/set_thermostat_parameter.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/requests/thermostat_parameters.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/responses/alerts.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/responses/device_available.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/responses/ecomax_parameters.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/responses/mixer_parameters.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/responses/password.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/responses/program_version.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/responses/regulator_data_schema.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/responses/schedules.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/responses/thermostat_parameters.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/responses/uid.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/unknown/unknown_ecomax_parameter.json +0 -0
- {pyplumio-0.5.36 → pyplumio-0.5.37}/tests/testdata/unknown/unknown_mixer_parameter.json +0 -0
@@ -2,13 +2,13 @@
|
|
2
2
|
# See https://pre-commit.com/hooks.html for more hooks
|
3
3
|
repos:
|
4
4
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
5
|
-
rev: v0.9.
|
5
|
+
rev: v0.9.4
|
6
6
|
hooks:
|
7
7
|
- id: ruff
|
8
8
|
args:
|
9
9
|
- --fix
|
10
10
|
- repo: https://github.com/codespell-project/codespell
|
11
|
-
rev: v2.4.
|
11
|
+
rev: v2.4.1
|
12
12
|
hooks:
|
13
13
|
- id: codespell
|
14
14
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: PyPlumIO
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.37
|
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,15 +27,15 @@ Requires-Dist: dataslots==1.2.0
|
|
27
27
|
Requires-Dist: pyserial-asyncio==0.6
|
28
28
|
Requires-Dist: typing-extensions==4.12.2
|
29
29
|
Provides-Extra: test
|
30
|
-
Requires-Dist: codespell==2.4.
|
30
|
+
Requires-Dist: codespell==2.4.1; extra == "test"
|
31
31
|
Requires-Dist: coverage==7.6.10; extra == "test"
|
32
32
|
Requires-Dist: mypy==1.14.1; extra == "test"
|
33
33
|
Requires-Dist: pyserial-asyncio-fast==0.14; extra == "test"
|
34
34
|
Requires-Dist: pytest==8.3.4; extra == "test"
|
35
|
-
Requires-Dist: pytest-asyncio==0.25.
|
36
|
-
Requires-Dist: ruff==0.9.
|
35
|
+
Requires-Dist: pytest-asyncio==0.25.3; extra == "test"
|
36
|
+
Requires-Dist: ruff==0.9.4; extra == "test"
|
37
37
|
Requires-Dist: tox==4.24.1; extra == "test"
|
38
|
-
Requires-Dist: types-pyserial==3.5.0.
|
38
|
+
Requires-Dist: types-pyserial==3.5.0.20250130; extra == "test"
|
39
39
|
Provides-Extra: docs
|
40
40
|
Requires-Dist: sphinx==8.1.3; extra == "docs"
|
41
41
|
Requires-Dist: sphinx_rtd_theme==3.0.2; extra == "docs"
|
@@ -49,8 +49,8 @@ Requires-Dist: tomli==2.2.1; extra == "dev"
|
|
49
49
|
[](https://badge.fury.io/py/PyPlumIO)
|
50
50
|
[](https://pypi.python.org/pypi/pyplumio/)
|
51
51
|
[](https://github.com/denpamusic/PyPlumIO/actions/workflows/ci.yml)
|
52
|
-
[](https://codeclimate.com/github/denpamusic/PyPlumIO/maintainability)
|
53
|
+
[](https://codeclimate.com/github/denpamusic/PyPlumIO/test_coverage)
|
54
54
|
[](https://guidelines.denpa.pro/stability#release-candidate)
|
55
55
|
[](https://github.com/astral-sh/ruff)
|
56
56
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: PyPlumIO
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.37
|
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,15 +27,15 @@ Requires-Dist: dataslots==1.2.0
|
|
27
27
|
Requires-Dist: pyserial-asyncio==0.6
|
28
28
|
Requires-Dist: typing-extensions==4.12.2
|
29
29
|
Provides-Extra: test
|
30
|
-
Requires-Dist: codespell==2.4.
|
30
|
+
Requires-Dist: codespell==2.4.1; extra == "test"
|
31
31
|
Requires-Dist: coverage==7.6.10; extra == "test"
|
32
32
|
Requires-Dist: mypy==1.14.1; extra == "test"
|
33
33
|
Requires-Dist: pyserial-asyncio-fast==0.14; extra == "test"
|
34
34
|
Requires-Dist: pytest==8.3.4; extra == "test"
|
35
|
-
Requires-Dist: pytest-asyncio==0.25.
|
36
|
-
Requires-Dist: ruff==0.9.
|
35
|
+
Requires-Dist: pytest-asyncio==0.25.3; extra == "test"
|
36
|
+
Requires-Dist: ruff==0.9.4; extra == "test"
|
37
37
|
Requires-Dist: tox==4.24.1; extra == "test"
|
38
|
-
Requires-Dist: types-pyserial==3.5.0.
|
38
|
+
Requires-Dist: types-pyserial==3.5.0.20250130; extra == "test"
|
39
39
|
Provides-Extra: docs
|
40
40
|
Requires-Dist: sphinx==8.1.3; extra == "docs"
|
41
41
|
Requires-Dist: sphinx_rtd_theme==3.0.2; extra == "docs"
|
@@ -49,8 +49,8 @@ Requires-Dist: tomli==2.2.1; extra == "dev"
|
|
49
49
|
[](https://badge.fury.io/py/PyPlumIO)
|
50
50
|
[](https://pypi.python.org/pypi/pyplumio/)
|
51
51
|
[](https://github.com/denpamusic/PyPlumIO/actions/workflows/ci.yml)
|
52
|
-
[](https://codeclimate.com/github/denpamusic/PyPlumIO/maintainability)
|
53
|
+
[](https://codeclimate.com/github/denpamusic/PyPlumIO/test_coverage)
|
54
54
|
[](https://guidelines.denpa.pro/stability#release-candidate)
|
55
55
|
[](https://github.com/astral-sh/ruff)
|
56
56
|
|
@@ -13,12 +13,12 @@ sphinx_rtd_theme==3.0.2
|
|
13
13
|
readthedocs-sphinx-search==0.3.2
|
14
14
|
|
15
15
|
[test]
|
16
|
-
codespell==2.4.
|
16
|
+
codespell==2.4.1
|
17
17
|
coverage==7.6.10
|
18
18
|
mypy==1.14.1
|
19
19
|
pyserial-asyncio-fast==0.14
|
20
20
|
pytest==8.3.4
|
21
|
-
pytest-asyncio==0.25.
|
22
|
-
ruff==0.9.
|
21
|
+
pytest-asyncio==0.25.3
|
22
|
+
ruff==0.9.4
|
23
23
|
tox==4.24.1
|
24
|
-
types-pyserial==3.5.0.
|
24
|
+
types-pyserial==3.5.0.20250130
|
@@ -2,8 +2,8 @@
|
|
2
2
|
[](https://badge.fury.io/py/PyPlumIO)
|
3
3
|
[](https://pypi.python.org/pypi/pyplumio/)
|
4
4
|
[](https://github.com/denpamusic/PyPlumIO/actions/workflows/ci.yml)
|
5
|
-
[](https://codeclimate.com/github/denpamusic/PyPlumIO/maintainability)
|
6
|
+
[](https://codeclimate.com/github/denpamusic/PyPlumIO/test_coverage)
|
7
7
|
[](https://guidelines.denpa.pro/stability#release-candidate)
|
8
8
|
[](https://github.com/astral-sh/ruff)
|
9
9
|
|
@@ -6,8 +6,9 @@
|
|
6
6
|
Welcome to PyPlumIO's documentation!
|
7
7
|
====================================
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
The `PyPlumIO <https://github.com/denpamusic/PyPlumIO/>`_ projects aims to
|
10
|
+
provide complete and easy to use solution for communicating with
|
11
|
+
climate devices manufactured by `Plum Sp. z o.o. <https://www.plum.pl/>`_
|
11
12
|
|
12
13
|
Currently it supports reading and writing parameters of ecoMAX controllers by
|
13
14
|
Plum Sp. z o.o., getting service password and sending network information to
|
@@ -28,7 +28,7 @@ def unpack_parameter(
|
|
28
28
|
data: bytearray, offset: int = 0, size: int = 1
|
29
29
|
) -> ParameterValues | None:
|
30
30
|
"""Unpack a device parameter."""
|
31
|
-
if not
|
31
|
+
if not is_valid_parameter(data[offset : offset + size * 3]):
|
32
32
|
return None
|
33
33
|
|
34
34
|
value = data[offset : offset + size]
|
@@ -42,7 +42,7 @@ def unpack_parameter(
|
|
42
42
|
)
|
43
43
|
|
44
44
|
|
45
|
-
def
|
45
|
+
def is_valid_parameter(data: bytearray) -> bool:
|
46
46
|
"""Check if parameter contains any bytes besides 0xFF."""
|
47
47
|
return any(x for x in data if x != BYTE_UNDEFINED)
|
48
48
|
|
@@ -75,6 +75,7 @@ class ParameterDescription:
|
|
75
75
|
"""Represents a parameter description."""
|
76
76
|
|
77
77
|
name: str
|
78
|
+
optimistic: bool = False
|
78
79
|
|
79
80
|
|
80
81
|
class Parameter(ABC):
|
@@ -204,13 +205,21 @@ class Parameter(ABC):
|
|
204
205
|
self, value: int, retries: int = 5, timeout: float = 5.0
|
205
206
|
) -> bool:
|
206
207
|
"""Attempt to update a parameter value on the remote device."""
|
208
|
+
_LOGGER.debug(
|
209
|
+
"Attempting to update '%s' parameter to %d", self.description.name, value
|
210
|
+
)
|
207
211
|
if value == self.values.value:
|
208
212
|
# Value is unchanged
|
209
213
|
return True
|
210
214
|
|
211
|
-
self._pending_update = True
|
212
215
|
self._values.value = value
|
213
|
-
|
216
|
+
request = await self.create_request()
|
217
|
+
if self.description.optimistic or not (initial_retries := retries):
|
218
|
+
# No retries
|
219
|
+
await self.device.queue.put(request)
|
220
|
+
return True
|
221
|
+
|
222
|
+
self._pending_update = True
|
214
223
|
while self.pending_update:
|
215
224
|
if retries <= 0:
|
216
225
|
_LOGGER.warning(
|
@@ -220,7 +229,7 @@ class Parameter(ABC):
|
|
220
229
|
)
|
221
230
|
return False
|
222
231
|
|
223
|
-
await self.device.queue.put(
|
232
|
+
await self.device.queue.put(request)
|
224
233
|
await asyncio.sleep(timeout)
|
225
234
|
retries -= 1
|
226
235
|
|
@@ -825,7 +825,9 @@ ECOMAX_PARAMETERS: dict[ProductType, tuple[EcomaxParameterDescription, ...]] = {
|
|
825
825
|
),
|
826
826
|
}
|
827
827
|
|
828
|
-
ECOMAX_CONTROL_PARAMETER = EcomaxSwitchDescription(
|
828
|
+
ECOMAX_CONTROL_PARAMETER = EcomaxSwitchDescription(
|
829
|
+
name=ATTR_ECOMAX_CONTROL, optimistic=True
|
830
|
+
)
|
829
831
|
THERMOSTAT_PROFILE_PARAMETER = EcomaxNumberDescription(name=ATTR_THERMOSTAT_PROFILE)
|
830
832
|
|
831
833
|
|
@@ -37,15 +37,15 @@ dynamic = ["version"]
|
|
37
37
|
|
38
38
|
[project.optional-dependencies]
|
39
39
|
test = [
|
40
|
-
"codespell==2.4.
|
40
|
+
"codespell==2.4.1",
|
41
41
|
"coverage==7.6.10",
|
42
42
|
"mypy==1.14.1",
|
43
43
|
"pyserial-asyncio-fast==0.14",
|
44
44
|
"pytest==8.3.4",
|
45
|
-
"pytest-asyncio==0.25.
|
46
|
-
"ruff==0.9.
|
45
|
+
"pytest-asyncio==0.25.3",
|
46
|
+
"ruff==0.9.4",
|
47
47
|
"tox==4.24.1",
|
48
|
-
"types-pyserial==3.5.0.
|
48
|
+
"types-pyserial==3.5.0.20250130"
|
49
49
|
]
|
50
50
|
docs = [
|
51
51
|
"sphinx==8.1.3",
|
@@ -243,7 +243,7 @@ asyncio_default_fixture_loop_scope = "function"
|
|
243
243
|
[tool.tox]
|
244
244
|
legacy_tox_ini = """
|
245
245
|
[tox]
|
246
|
-
envlist = lint, type, py{39,310,311,312}
|
246
|
+
envlist = lint, type, py{39,310,311,312,313}
|
247
247
|
isolated_build = True
|
248
248
|
skip_missing_interpreters = True
|
249
249
|
ignore_basepython_conflict = True
|
@@ -253,7 +253,8 @@ legacy_tox_ini = """
|
|
253
253
|
3.9: py39
|
254
254
|
3.10: py310
|
255
255
|
3.11: py311
|
256
|
-
3.12: py312
|
256
|
+
3.12: py312
|
257
|
+
3.13: py313, lint, type
|
257
258
|
|
258
259
|
[testenv]
|
259
260
|
deps =
|
@@ -1,11 +1,11 @@
|
|
1
|
-
codespell==2.4.
|
1
|
+
codespell==2.4.1
|
2
2
|
coverage==7.6.10
|
3
3
|
mypy==1.14.1
|
4
4
|
pre-commit==4.1.0
|
5
5
|
pyserial-asyncio-fast==0.14
|
6
|
-
pytest-asyncio==0.25.
|
6
|
+
pytest-asyncio==0.25.3
|
7
7
|
pytest==8.3.4
|
8
|
-
ruff==0.9.
|
8
|
+
ruff==0.9.4
|
9
9
|
tomli==2.2.1
|
10
10
|
tox==4.24.1
|
11
|
-
types-pyserial==3.5.0.
|
11
|
+
types-pyserial==3.5.0.20250130
|
@@ -15,7 +15,7 @@ from pyplumio.helpers.parameter import (
|
|
15
15
|
ParameterValues,
|
16
16
|
Switch,
|
17
17
|
SwitchDescription,
|
18
|
-
|
18
|
+
is_valid_parameter,
|
19
19
|
)
|
20
20
|
|
21
21
|
|
@@ -41,16 +41,16 @@ def fixture_switch(ecomax: EcoMAX) -> Switch:
|
|
41
41
|
)
|
42
42
|
|
43
43
|
|
44
|
-
def
|
44
|
+
def test_is_valid_parameter() -> None:
|
45
45
|
"""Test checking if parameter is valid."""
|
46
|
-
assert
|
46
|
+
assert is_valid_parameter(
|
47
47
|
bytearray([BYTE_UNDEFINED, 0xFE, BYTE_UNDEFINED, BYTE_UNDEFINED])
|
48
48
|
)
|
49
49
|
|
50
50
|
|
51
|
-
def
|
51
|
+
def test_is_valid_parameter_invalid() -> None:
|
52
52
|
"""Test checking if parameter is invalid."""
|
53
|
-
assert not
|
53
|
+
assert not is_valid_parameter(
|
54
54
|
bytearray([BYTE_UNDEFINED, BYTE_UNDEFINED, BYTE_UNDEFINED, BYTE_UNDEFINED])
|
55
55
|
)
|
56
56
|
|
@@ -120,14 +120,36 @@ async def test_number_validate(number: Number) -> None:
|
|
120
120
|
async def test_number_set(number: Number, bypass_asyncio_sleep) -> None:
|
121
121
|
"""Test setting a number."""
|
122
122
|
await number.set(5)
|
123
|
+
assert number.pending_update
|
123
124
|
number.update(ParameterValues(value=5, min_value=0, max_value=5))
|
124
125
|
assert number == 5
|
125
126
|
assert not number.pending_update
|
126
|
-
with patch("pyplumio.helpers.parameter.Parameter.pending_update", False):
|
127
|
+
with patch("pyplumio.helpers.parameter.Parameter.pending_update", False): # type: ignore [unreachable]
|
127
128
|
assert await number.set(3)
|
128
129
|
assert number == 3
|
129
130
|
|
130
131
|
|
132
|
+
async def test_number_set_with_no_retries(number: Number, bypass_asyncio_sleep) -> None:
|
133
|
+
"""Test setting a number with no retries."""
|
134
|
+
with patch("asyncio.Queue.put") as mock_put:
|
135
|
+
assert await number.set(5, retries=0)
|
136
|
+
|
137
|
+
mock_put.assert_awaited_once_with(await number.create_request())
|
138
|
+
assert number == 5
|
139
|
+
assert not number.pending_update
|
140
|
+
|
141
|
+
|
142
|
+
async def test_number_set_optimistic(number: Number, bypass_asyncio_sleep) -> None:
|
143
|
+
"""Test setting a number optimistically."""
|
144
|
+
number.description.optimistic = True
|
145
|
+
with patch("asyncio.Queue.put") as mock_put:
|
146
|
+
assert await number.set(5)
|
147
|
+
|
148
|
+
mock_put.assert_awaited_once_with(await number.create_request())
|
149
|
+
assert number == 5
|
150
|
+
assert not number.pending_update
|
151
|
+
|
152
|
+
|
131
153
|
async def test_switch_validate(switch: Switch) -> None:
|
132
154
|
"""Test the switch validation."""
|
133
155
|
assert switch.validate(STATE_ON)
|
@@ -139,15 +161,37 @@ async def test_switch_validate(switch: Switch) -> None:
|
|
139
161
|
async def test_switch_set(switch: Switch, bypass_asyncio_sleep) -> None:
|
140
162
|
"""Test setting a number."""
|
141
163
|
await switch.set(STATE_ON)
|
164
|
+
assert switch.pending_update
|
142
165
|
switch.update(ParameterValues(value=1, min_value=0, max_value=1))
|
143
166
|
assert switch == 1
|
144
167
|
assert not switch.pending_update
|
145
|
-
with patch("pyplumio.helpers.parameter.Parameter.pending_update", False):
|
168
|
+
with patch("pyplumio.helpers.parameter.Parameter.pending_update", False): # type: ignore [unreachable]
|
146
169
|
assert await switch.set(STATE_OFF)
|
147
170
|
|
148
171
|
assert switch == 0
|
149
172
|
|
150
173
|
|
174
|
+
async def test_switch_set_with_no_retries(switch: Switch, bypass_asyncio_sleep) -> None:
|
175
|
+
"""Test setting a switch with no retries."""
|
176
|
+
with patch("asyncio.Queue.put") as mock_put:
|
177
|
+
assert await switch.set(STATE_ON, retries=0)
|
178
|
+
|
179
|
+
mock_put.assert_awaited_once_with(await switch.create_request())
|
180
|
+
assert switch == 1
|
181
|
+
assert not switch.pending_update
|
182
|
+
|
183
|
+
|
184
|
+
async def test_switch_set_optimistic(switch: Switch, bypass_asyncio_sleep) -> None:
|
185
|
+
"""Test setting a switch optimistically."""
|
186
|
+
switch.description.optimistic = True
|
187
|
+
with patch("asyncio.Queue.put") as mock_put:
|
188
|
+
assert await switch.set(STATE_ON)
|
189
|
+
|
190
|
+
mock_put.assert_awaited_once_with(await switch.create_request())
|
191
|
+
assert switch == 1
|
192
|
+
assert not switch.pending_update
|
193
|
+
|
194
|
+
|
151
195
|
async def test_number_set_nowait(number: Number):
|
152
196
|
"""Test setting a number without waiting for result."""
|
153
197
|
number.set_nowait(5)
|
@@ -241,7 +285,7 @@ def test_number_repr(number: Number) -> None:
|
|
241
285
|
"""Test a number representation."""
|
242
286
|
assert repr(number) == (
|
243
287
|
"Number(device=EcoMAX, "
|
244
|
-
"description=NumberDescription(name='test_number', "
|
288
|
+
"description=NumberDescription(name='test_number', optimistic=False, "
|
245
289
|
"unit_of_measurement=<UnitOfMeasurement.CELSIUS: '°C'>), "
|
246
290
|
"values=ParameterValues(value=1, min_value=0, max_value=5), "
|
247
291
|
"index=0)"
|
@@ -252,7 +296,7 @@ def test_switch_repr(switch: Switch) -> None:
|
|
252
296
|
"""Test a number representation."""
|
253
297
|
assert repr(switch) == (
|
254
298
|
"Switch(device=EcoMAX, "
|
255
|
-
"description=SwitchDescription(name='test_switch'), "
|
299
|
+
"description=SwitchDescription(name='test_switch', optimistic=False), "
|
256
300
|
"values=ParameterValues(value=0, min_value=0, max_value=1), "
|
257
301
|
"index=0)"
|
258
302
|
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|