PyPlumIO 0.5.52__tar.gz → 0.5.54__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.52 → pyplumio-0.5.54}/.pre-commit-config.yaml +2 -2
- {pyplumio-0.5.52 → pyplumio-0.5.54}/PKG-INFO +10 -10
- {pyplumio-0.5.52 → pyplumio-0.5.54}/PyPlumIO.egg-info/PKG-INFO +10 -10
- {pyplumio-0.5.52 → pyplumio-0.5.54}/PyPlumIO.egg-info/SOURCES.txt +0 -2
- {pyplumio-0.5.52 → pyplumio-0.5.54}/PyPlumIO.egg-info/requires.txt +9 -9
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/_version.py +16 -3
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/connection.py +1 -1
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/data_types.py +7 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/devices/__init__.py +2 -1
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/devices/ecomax.py +1 -1
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/filters.py +9 -1
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/frames/__init__.py +13 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/parameters/__init__.py +5 -1
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/stream.py +39 -45
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/utils.py +26 -2
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyproject.toml +9 -9
- {pyplumio-0.5.52 → pyplumio-0.5.54}/requirements.txt +1 -1
- pyplumio-0.5.54/requirements_test.txt +13 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/devices/test_ecomax.py +2 -2
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/parameters/test_init.py +9 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/test_connection.py +3 -4
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/test_data_types.py +7 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/test_filters.py +10 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/test_stream.py +106 -79
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/test_utils.py +27 -0
- pyplumio-0.5.52/pyplumio/helpers/timeout.py +0 -32
- pyplumio-0.5.52/requirements_test.txt +0 -13
- pyplumio-0.5.52/tests/helpers/test_timeout.py +0 -32
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.gitattributes +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.github/CODE_OF_CONDUCT.md +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.github/dependabot.yml +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.github/workflows/ci.yml +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.github/workflows/codeql.yml +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.github/workflows/deploy.yml +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.github/workflows/documentation.yml +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.gitignore +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.qlty/qlty.toml +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/.vscode/settings.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/LICENSE +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/MANIFEST.in +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/PyPlumIO.egg-info/dependency_links.txt +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/PyPlumIO.egg-info/top_level.txt +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/README.md +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/Makefile +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/make.bat +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/source/callbacks.rst +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/source/conf.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/source/connecting.rst +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/source/frames.rst +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/source/index.rst +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/source/mixers_thermostats.rst +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/source/protocol.rst +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/source/reading.rst +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/source/schedules.rst +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/docs/source/writing.rst +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/images/ecomax.png +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/images/rs485.png +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/__init__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/__main__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/const.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/devices/ecoster.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/devices/mixer.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/devices/thermostat.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/exceptions.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/frames/messages.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/frames/requests.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/frames/responses.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/helpers/__init__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/helpers/async_cache.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/helpers/event_manager.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/helpers/factory.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/helpers/task_manager.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/parameters/custom/__init__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/parameters/custom/ecomax_860d3_hb.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/parameters/ecomax.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/parameters/mixer.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/parameters/thermostat.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/protocol.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/py.typed +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/__init__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/alerts.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/boiler_load.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/boiler_power.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/ecomax_parameters.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/fan_power.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/frame_versions.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/fuel_consumption.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/fuel_level.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/lambda_sensor.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/mixer_parameters.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/mixer_sensors.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/modules.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/network_info.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/output_flags.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/outputs.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/pending_alerts.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/product_info.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/program_version.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/regulator_data.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/regulator_data_schema.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/schedules.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/statuses.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/temperatures.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/thermostat_parameters.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/pyplumio/structures/thermostat_sensors.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/requirements_docs.txt +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/setup.cfg +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/__init__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/conftest.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/devices/__init__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/devices/test_ecoster.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/devices/test_init.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/devices/test_mixer.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/devices/test_thermostat.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/frames/test_init.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/frames/test_messages.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/frames/test_requests.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/frames/test_responses.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/helpers/__init__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/helpers/test_async_cache.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/helpers/test_event_manager.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/helpers/test_factory.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/helpers/test_task_manager.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/helpers/test_uid.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/parameters/__init__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/parameters/custom/__init__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/parameters/custom/test_ecomax_860d3_hb.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/parameters/custom/test_init.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/parameters/test_ecomax.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/parameters/test_mixers.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/parameters/test_thermostats.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/ruff.toml +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/__init__.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_alerts.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_boiler_load.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_boiler_power.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_ecomax_parameters.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_fan_power.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_frame_versions.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_fuel_consumption.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_fuel_level.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_lambda_sensor.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_mixer_parameters.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_product_info.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/structures/test_schedules.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/test_init.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/test_main.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/test_protocol.py +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/messages/regulator_data.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/messages/sensor_data.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/parameters/ecomax_860d3_hb.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/requests/alerts.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/requests/ecomax_control.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/requests/ecomax_parameters.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/requests/mixer_parameters.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/requests/set_ecomax_parameter.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/requests/set_mixer_parameter.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/requests/set_schedule.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/requests/set_thermostat_parameter.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/requests/thermostat_parameters.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/responses/alerts.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/responses/device_available.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/responses/ecomax_parameters.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/responses/mixer_parameters.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/responses/password.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/responses/program_version.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/responses/regulator_data_schema.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/responses/schedules.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/responses/thermostat_parameters.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/responses/uid.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/tests/testdata/unknown/unknown_ecomax_parameter.json +0 -0
- {pyplumio-0.5.52 → pyplumio-0.5.54}/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.
|
6
|
+
rev: v0.12.11
|
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.
|
16
|
+
rev: v1.17.1
|
17
17
|
hooks:
|
18
18
|
- id: mypy
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: PyPlumIO
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.54
|
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
|
@@ -25,26 +25,26 @@ Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
26
26
|
Requires-Dist: dataslots==1.2.0
|
27
27
|
Requires-Dist: pyserial-asyncio==0.6
|
28
|
-
Requires-Dist: typing-extensions
|
28
|
+
Requires-Dist: typing-extensions<5.0,>=4.14.0
|
29
29
|
Provides-Extra: test
|
30
30
|
Requires-Dist: codespell==2.4.1; extra == "test"
|
31
|
-
Requires-Dist: coverage==7.
|
32
|
-
Requires-Dist: freezegun==1.5.
|
33
|
-
Requires-Dist: mypy==1.
|
31
|
+
Requires-Dist: coverage==7.10.5; extra == "test"
|
32
|
+
Requires-Dist: freezegun==1.5.5; extra == "test"
|
33
|
+
Requires-Dist: mypy==1.17.1; extra == "test"
|
34
34
|
Requires-Dist: numpy<3.0.0,>=2.0.0; extra == "test"
|
35
35
|
Requires-Dist: pyserial-asyncio-fast==0.16; extra == "test"
|
36
36
|
Requires-Dist: pytest==8.4.1; extra == "test"
|
37
|
-
Requires-Dist: pytest-asyncio==1.
|
38
|
-
Requires-Dist: ruff==0.
|
39
|
-
Requires-Dist: tox==4.
|
40
|
-
Requires-Dist: types-pyserial==3.5.0.
|
37
|
+
Requires-Dist: pytest-asyncio==1.1.0; extra == "test"
|
38
|
+
Requires-Dist: ruff==0.12.10; extra == "test"
|
39
|
+
Requires-Dist: tox==4.28.4; extra == "test"
|
40
|
+
Requires-Dist: types-pyserial==3.5.0.20250822; extra == "test"
|
41
41
|
Provides-Extra: docs
|
42
42
|
Requires-Dist: sphinx==8.1.3; extra == "docs"
|
43
43
|
Requires-Dist: sphinx_rtd_theme==3.0.2; extra == "docs"
|
44
44
|
Requires-Dist: readthedocs-sphinx-search==0.3.2; extra == "docs"
|
45
45
|
Provides-Extra: dev
|
46
46
|
Requires-Dist: pyplumio[docs,test]; extra == "dev"
|
47
|
-
Requires-Dist: pre-commit==4.
|
47
|
+
Requires-Dist: pre-commit==4.3.0; extra == "dev"
|
48
48
|
Requires-Dist: tomli==2.2.1; extra == "dev"
|
49
49
|
Dynamic: license-file
|
50
50
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: PyPlumIO
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.54
|
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
|
@@ -25,26 +25,26 @@ Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
26
26
|
Requires-Dist: dataslots==1.2.0
|
27
27
|
Requires-Dist: pyserial-asyncio==0.6
|
28
|
-
Requires-Dist: typing-extensions
|
28
|
+
Requires-Dist: typing-extensions<5.0,>=4.14.0
|
29
29
|
Provides-Extra: test
|
30
30
|
Requires-Dist: codespell==2.4.1; extra == "test"
|
31
|
-
Requires-Dist: coverage==7.
|
32
|
-
Requires-Dist: freezegun==1.5.
|
33
|
-
Requires-Dist: mypy==1.
|
31
|
+
Requires-Dist: coverage==7.10.5; extra == "test"
|
32
|
+
Requires-Dist: freezegun==1.5.5; extra == "test"
|
33
|
+
Requires-Dist: mypy==1.17.1; extra == "test"
|
34
34
|
Requires-Dist: numpy<3.0.0,>=2.0.0; extra == "test"
|
35
35
|
Requires-Dist: pyserial-asyncio-fast==0.16; extra == "test"
|
36
36
|
Requires-Dist: pytest==8.4.1; extra == "test"
|
37
|
-
Requires-Dist: pytest-asyncio==1.
|
38
|
-
Requires-Dist: ruff==0.
|
39
|
-
Requires-Dist: tox==4.
|
40
|
-
Requires-Dist: types-pyserial==3.5.0.
|
37
|
+
Requires-Dist: pytest-asyncio==1.1.0; extra == "test"
|
38
|
+
Requires-Dist: ruff==0.12.10; extra == "test"
|
39
|
+
Requires-Dist: tox==4.28.4; extra == "test"
|
40
|
+
Requires-Dist: types-pyserial==3.5.0.20250822; extra == "test"
|
41
41
|
Provides-Extra: docs
|
42
42
|
Requires-Dist: sphinx==8.1.3; extra == "docs"
|
43
43
|
Requires-Dist: sphinx_rtd_theme==3.0.2; extra == "docs"
|
44
44
|
Requires-Dist: readthedocs-sphinx-search==0.3.2; extra == "docs"
|
45
45
|
Provides-Extra: dev
|
46
46
|
Requires-Dist: pyplumio[docs,test]; extra == "dev"
|
47
|
-
Requires-Dist: pre-commit==4.
|
47
|
+
Requires-Dist: pre-commit==4.3.0; extra == "dev"
|
48
48
|
Requires-Dist: tomli==2.2.1; extra == "dev"
|
49
49
|
Dynamic: license-file
|
50
50
|
|
@@ -63,7 +63,6 @@ pyplumio/helpers/async_cache.py
|
|
63
63
|
pyplumio/helpers/event_manager.py
|
64
64
|
pyplumio/helpers/factory.py
|
65
65
|
pyplumio/helpers/task_manager.py
|
66
|
-
pyplumio/helpers/timeout.py
|
67
66
|
pyplumio/parameters/__init__.py
|
68
67
|
pyplumio/parameters/ecomax.py
|
69
68
|
pyplumio/parameters/mixer.py
|
@@ -122,7 +121,6 @@ tests/helpers/test_async_cache.py
|
|
122
121
|
tests/helpers/test_event_manager.py
|
123
122
|
tests/helpers/test_factory.py
|
124
123
|
tests/helpers/test_task_manager.py
|
125
|
-
tests/helpers/test_timeout.py
|
126
124
|
tests/helpers/test_uid.py
|
127
125
|
tests/parameters/__init__.py
|
128
126
|
tests/parameters/test_ecomax.py
|
@@ -1,10 +1,10 @@
|
|
1
1
|
dataslots==1.2.0
|
2
2
|
pyserial-asyncio==0.6
|
3
|
-
typing-extensions
|
3
|
+
typing-extensions<5.0,>=4.14.0
|
4
4
|
|
5
5
|
[dev]
|
6
6
|
pyplumio[docs,test]
|
7
|
-
pre-commit==4.
|
7
|
+
pre-commit==4.3.0
|
8
8
|
tomli==2.2.1
|
9
9
|
|
10
10
|
[docs]
|
@@ -14,13 +14,13 @@ readthedocs-sphinx-search==0.3.2
|
|
14
14
|
|
15
15
|
[test]
|
16
16
|
codespell==2.4.1
|
17
|
-
coverage==7.
|
18
|
-
freezegun==1.5.
|
19
|
-
mypy==1.
|
17
|
+
coverage==7.10.5
|
18
|
+
freezegun==1.5.5
|
19
|
+
mypy==1.17.1
|
20
20
|
numpy<3.0.0,>=2.0.0
|
21
21
|
pyserial-asyncio-fast==0.16
|
22
22
|
pytest==8.4.1
|
23
|
-
pytest-asyncio==1.
|
24
|
-
ruff==0.
|
25
|
-
tox==4.
|
26
|
-
types-pyserial==3.5.0.
|
23
|
+
pytest-asyncio==1.1.0
|
24
|
+
ruff==0.12.10
|
25
|
+
tox==4.28.4
|
26
|
+
types-pyserial==3.5.0.20250822
|
@@ -1,7 +1,14 @@
|
|
1
1
|
# file generated by setuptools-scm
|
2
2
|
# don't change, don't track in version control
|
3
3
|
|
4
|
-
__all__ = [
|
4
|
+
__all__ = [
|
5
|
+
"__version__",
|
6
|
+
"__version_tuple__",
|
7
|
+
"version",
|
8
|
+
"version_tuple",
|
9
|
+
"__commit_id__",
|
10
|
+
"commit_id",
|
11
|
+
]
|
5
12
|
|
6
13
|
TYPE_CHECKING = False
|
7
14
|
if TYPE_CHECKING:
|
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
|
|
9
16
|
from typing import Union
|
10
17
|
|
11
18
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
19
|
+
COMMIT_ID = Union[str, None]
|
12
20
|
else:
|
13
21
|
VERSION_TUPLE = object
|
22
|
+
COMMIT_ID = object
|
14
23
|
|
15
24
|
version: str
|
16
25
|
__version__: str
|
17
26
|
__version_tuple__: VERSION_TUPLE
|
18
27
|
version_tuple: VERSION_TUPLE
|
28
|
+
commit_id: COMMIT_ID
|
29
|
+
__commit_id__: COMMIT_ID
|
19
30
|
|
20
|
-
__version__ = version = '0.5.
|
21
|
-
__version_tuple__ = version_tuple = (0, 5,
|
31
|
+
__version__ = version = '0.5.54'
|
32
|
+
__version_tuple__ = version_tuple = (0, 5, 54)
|
33
|
+
|
34
|
+
__commit_id__ = commit_id = 'g7b7ef864f'
|
@@ -11,8 +11,8 @@ from serial import EIGHTBITS, PARITY_NONE, STOPBITS_ONE
|
|
11
11
|
|
12
12
|
from pyplumio.exceptions import ConnectionFailedError
|
13
13
|
from pyplumio.helpers.task_manager import TaskManager
|
14
|
-
from pyplumio.helpers.timeout import timeout
|
15
14
|
from pyplumio.protocol import AsyncProtocol, Protocol
|
15
|
+
from pyplumio.utils import timeout
|
16
16
|
|
17
17
|
_LOGGER = logging.getLogger(__name__)
|
18
18
|
|
@@ -33,6 +33,13 @@ class DataType(ABC, Generic[T]):
|
|
33
33
|
|
34
34
|
return f"{self.__class__.__name__}()"
|
35
35
|
|
36
|
+
def __hash__(self) -> int:
|
37
|
+
"""Return a hash of the data type based on its value."""
|
38
|
+
if hasattr(self, "_value"):
|
39
|
+
return hash(self._value)
|
40
|
+
|
41
|
+
return hash(type(self))
|
42
|
+
|
36
43
|
def __eq__(self, other: object) -> bool:
|
37
44
|
"""Compare if this data type is equal to other."""
|
38
45
|
if (
|
@@ -195,6 +195,7 @@ class PhysicalDevice(Device, ABC):
|
|
195
195
|
"""
|
196
196
|
_LOGGER.info("Requesting '%s' with %s", name, repr(frame_type))
|
197
197
|
request = await Request.create(frame_type, recipient=self.address)
|
198
|
+
initial_retries = retries
|
198
199
|
while retries > 0:
|
199
200
|
try:
|
200
201
|
self.queue.put_nowait(request)
|
@@ -204,7 +205,7 @@ class PhysicalDevice(Device, ABC):
|
|
204
205
|
|
205
206
|
raise RequestError(
|
206
207
|
f"Failed to request '{name}' with frame type '{frame_type}' after "
|
207
|
-
f"{
|
208
|
+
f"{initial_retries} retries.",
|
208
209
|
frame_type=frame_type,
|
209
210
|
)
|
210
211
|
|
@@ -236,7 +236,7 @@ class EcoMAX(PhysicalDevice):
|
|
236
236
|
async def on_event_setup(self, setup: bool) -> None:
|
237
237
|
"""Request frames required to set up an ecoMAX entry."""
|
238
238
|
_LOGGER.debug("Setting up device entry")
|
239
|
-
await self.wait_for(ATTR_SENSORS)
|
239
|
+
await self.wait_for(ATTR_SENSORS, timeout=30.0)
|
240
240
|
results = await asyncio.gather(
|
241
241
|
*(
|
242
242
|
self.request(description.provides, description.frame_type)
|
@@ -56,6 +56,9 @@ class SupportsComparison(Protocol):
|
|
56
56
|
|
57
57
|
__slots__ = ()
|
58
58
|
|
59
|
+
def __hash__(self) -> int:
|
60
|
+
"""Return a hash of the value."""
|
61
|
+
|
59
62
|
def __eq__(self: SupportsComparison, other: SupportsComparison) -> bool:
|
60
63
|
"""Compare a value."""
|
61
64
|
|
@@ -130,6 +133,10 @@ class Filter(ABC):
|
|
130
133
|
self._callback = callback
|
131
134
|
self._value = UNDEFINED
|
132
135
|
|
136
|
+
def __hash__(self) -> int:
|
137
|
+
"""Return a hash of the filter based on its callback."""
|
138
|
+
return hash(self._callback)
|
139
|
+
|
133
140
|
def __eq__(self, other: Any) -> bool:
|
134
141
|
"""Compare callbacks."""
|
135
142
|
if isinstance(other, Filter):
|
@@ -179,9 +186,10 @@ class _Aggregate(Filter):
|
|
179
186
|
self._values.append(new_value)
|
180
187
|
time_since_call = current_time - self._last_call_time
|
181
188
|
if time_since_call >= self._timeout or len(self._values) >= self._sample_size:
|
182
|
-
|
189
|
+
sum_of_values = (
|
183
190
|
np.sum(self._values) if numpy_installed else sum(self._values)
|
184
191
|
)
|
192
|
+
result = await self._callback(float(sum_of_values))
|
185
193
|
self._last_call_time = current_time
|
186
194
|
self._values = []
|
187
195
|
return result
|
@@ -114,6 +114,19 @@ class Frame(ABC):
|
|
114
114
|
self._data = data if not kwargs else ensure_dict(data, kwargs)
|
115
115
|
self._message = message
|
116
116
|
|
117
|
+
def __hash__(self) -> int:
|
118
|
+
"""Return a hash of the frame based on its values."""
|
119
|
+
return hash(
|
120
|
+
(
|
121
|
+
self.recipient,
|
122
|
+
self.sender,
|
123
|
+
self.econet_type,
|
124
|
+
self.econet_version,
|
125
|
+
self._message,
|
126
|
+
frozenset(self._data.items()) if self._data else None,
|
127
|
+
)
|
128
|
+
)
|
129
|
+
|
117
130
|
def __eq__(self, other: object) -> bool:
|
118
131
|
"""Compare if this frame is equal to other."""
|
119
132
|
if isinstance(other, Frame):
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
5
5
|
from abc import ABC, abstractmethod
|
6
6
|
import asyncio
|
7
7
|
from contextlib import suppress
|
8
|
-
from dataclasses import dataclass
|
8
|
+
from dataclasses import asdict, dataclass
|
9
9
|
import logging
|
10
10
|
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union, get_args
|
11
11
|
|
@@ -115,6 +115,10 @@ class Parameter(ABC):
|
|
115
115
|
f"index={self._index})"
|
116
116
|
)
|
117
117
|
|
118
|
+
def __hash__(self) -> int:
|
119
|
+
"""Return a hash of the parameter based on its values."""
|
120
|
+
return hash(frozenset(asdict(self.values).items()))
|
121
|
+
|
118
122
|
def _call_relational_method(self, method_to_call: str, other: Any) -> Any:
|
119
123
|
"""Call a specified relational method."""
|
120
124
|
if isinstance(other, Parameter):
|
@@ -20,7 +20,7 @@ from pyplumio.frames import (
|
|
20
20
|
bcc,
|
21
21
|
struct_header,
|
22
22
|
)
|
23
|
-
from pyplumio.
|
23
|
+
from pyplumio.utils import timeout
|
24
24
|
|
25
25
|
READER_TIMEOUT: Final = 10
|
26
26
|
WRITER_TIMEOUT: Final = 10
|
@@ -67,7 +67,7 @@ class FrameWriter:
|
|
67
67
|
await self._writer.wait_closed()
|
68
68
|
|
69
69
|
|
70
|
-
class
|
70
|
+
class BufferedReader:
|
71
71
|
"""Represents a buffered reader for reading frames."""
|
72
72
|
|
73
73
|
__slots__ = ("_buffer", "_reader")
|
@@ -89,7 +89,7 @@ class BufferManager:
|
|
89
89
|
try:
|
90
90
|
data = await self._reader.readexactly(bytes_to_read)
|
91
91
|
self._buffer.extend(data)
|
92
|
-
self.trim_to(
|
92
|
+
self.trim_to(DEFAULT_BUFFER_SIZE)
|
93
93
|
except IncompleteReadError as e:
|
94
94
|
raise ReadError(
|
95
95
|
f"Incomplete read. Tried to read {bytes_to_read} additional bytes "
|
@@ -104,22 +104,36 @@ class BufferManager:
|
|
104
104
|
f"Serial connection broken while trying to ensure {size} bytes: {e}"
|
105
105
|
) from e
|
106
106
|
|
107
|
+
async def peek(self, size: int) -> bytes:
|
108
|
+
"""Read the specified number of bytes without consuming them."""
|
109
|
+
await self.ensure_buffer(size)
|
110
|
+
return memoryview(self._buffer)[:size].tobytes()
|
111
|
+
|
107
112
|
async def consume(self, size: int) -> None:
|
108
113
|
"""Consume the specified number of bytes from the buffer."""
|
109
114
|
await self.ensure_buffer(size)
|
110
115
|
self._buffer = self._buffer[size:]
|
111
116
|
|
112
|
-
async def
|
113
|
-
"""Read the specified number of bytes
|
114
|
-
await self.ensure_buffer(size)
|
115
|
-
return self._buffer[:size]
|
116
|
-
|
117
|
-
async def read(self, size: int) -> bytearray:
|
118
|
-
"""Read the bytes from buffer or stream and consume them."""
|
117
|
+
async def read_into_buffer(self, size: int) -> None:
|
118
|
+
"""Read the specified number of bytes from the stream."""
|
119
119
|
try:
|
120
|
-
|
121
|
-
|
122
|
-
|
120
|
+
chunk = await self._reader.read(size)
|
121
|
+
except asyncio.CancelledError:
|
122
|
+
_LOGGER.debug("Read operation cancelled while filling internal buffer.")
|
123
|
+
raise
|
124
|
+
except Exception as e:
|
125
|
+
raise OSError(
|
126
|
+
f"Serial connection broken while filling internal buffer: {e}"
|
127
|
+
) from e
|
128
|
+
|
129
|
+
if not chunk:
|
130
|
+
_LOGGER.debug("Stream ended while filling internal buffer.")
|
131
|
+
raise OSError(
|
132
|
+
"Serial connection broken: stream ended while filling internal buffer"
|
133
|
+
)
|
134
|
+
|
135
|
+
self._buffer.extend(chunk)
|
136
|
+
self.trim_to(DEFAULT_BUFFER_SIZE)
|
123
137
|
|
124
138
|
def seek_to(self, delimiter: SupportsIndex) -> bool:
|
125
139
|
"""Trim the buffer to the first occurrence of the delimiter.
|
@@ -137,27 +151,6 @@ class BufferManager:
|
|
137
151
|
if len(self._buffer) > size:
|
138
152
|
self._buffer = self._buffer[-size:]
|
139
153
|
|
140
|
-
async def fill(self) -> None:
|
141
|
-
"""Fill the buffer with data from the stream."""
|
142
|
-
try:
|
143
|
-
chunk = await self._reader.read(MAX_FRAME_LENGTH)
|
144
|
-
except asyncio.CancelledError:
|
145
|
-
_LOGGER.debug("Read operation cancelled while filling read buffer.")
|
146
|
-
raise
|
147
|
-
except Exception as e:
|
148
|
-
raise OSError(
|
149
|
-
f"Serial connection broken while filling read buffer: {e}"
|
150
|
-
) from e
|
151
|
-
|
152
|
-
if not chunk:
|
153
|
-
_LOGGER.debug("Stream ended while filling read buffer.")
|
154
|
-
raise OSError(
|
155
|
-
"Serial connection broken: stream ended while filling read buffer"
|
156
|
-
)
|
157
|
-
|
158
|
-
self._buffer.extend(chunk)
|
159
|
-
self.trim_to(DEFAULT_BUFFER_SIZE)
|
160
|
-
|
161
154
|
@property
|
162
155
|
def buffer(self) -> bytearray:
|
163
156
|
"""Return the internal buffer."""
|
@@ -177,22 +170,23 @@ class Header(NamedTuple):
|
|
177
170
|
class FrameReader:
|
178
171
|
"""Represents a frame reader."""
|
179
172
|
|
180
|
-
__slots__ = ("
|
173
|
+
__slots__ = ("_reader",)
|
181
174
|
|
182
|
-
|
175
|
+
_reader: BufferedReader
|
183
176
|
|
184
177
|
def __init__(self, reader: StreamReader) -> None:
|
185
178
|
"""Initialize a new frame reader."""
|
186
|
-
self.
|
179
|
+
self._reader = BufferedReader(reader)
|
187
180
|
|
188
181
|
async def _read_header(self) -> Header:
|
189
182
|
"""Locate and read a frame header."""
|
190
183
|
while True:
|
191
|
-
if self.
|
192
|
-
header_bytes = await self.
|
193
|
-
|
184
|
+
if self._reader.seek_to(FRAME_START):
|
185
|
+
header_bytes = await self._reader.peek(HEADER_SIZE)
|
186
|
+
header_fields = struct_header.unpack_from(header_bytes)
|
187
|
+
return Header(*header_fields[DELIMITER_SIZE:])
|
194
188
|
|
195
|
-
await self.
|
189
|
+
await self._reader.read_into_buffer(MAX_FRAME_LENGTH)
|
196
190
|
|
197
191
|
@timeout(READER_TIMEOUT)
|
198
192
|
async def read(self) -> Frame | None:
|
@@ -201,22 +195,22 @@ class FrameReader:
|
|
201
195
|
frame_length, recipient, sender, econet_type, econet_version = header
|
202
196
|
|
203
197
|
if frame_length > MAX_FRAME_LENGTH or frame_length < MIN_FRAME_LENGTH:
|
204
|
-
await self.
|
198
|
+
await self._reader.consume(HEADER_SIZE)
|
205
199
|
raise ReadError(
|
206
200
|
f"Unexpected frame length ({frame_length}), expected between "
|
207
201
|
f"{MIN_FRAME_LENGTH} and {MAX_FRAME_LENGTH}"
|
208
202
|
)
|
209
203
|
|
210
|
-
frame_bytes = await self.
|
204
|
+
frame_bytes = await self._reader.peek(frame_length)
|
211
205
|
checksum = bcc(frame_bytes[:BCC_INDEX])
|
212
206
|
if checksum != frame_bytes[BCC_INDEX]:
|
213
|
-
await self.
|
207
|
+
await self._reader.consume(HEADER_SIZE)
|
214
208
|
raise ChecksumError(
|
215
209
|
f"Incorrect frame checksum: calculated {checksum}, "
|
216
210
|
f"expected {frame_bytes[BCC_INDEX]}. Frame data: {frame_bytes.hex()}"
|
217
211
|
)
|
218
212
|
|
219
|
-
await self.
|
213
|
+
await self._reader.consume(frame_length)
|
220
214
|
if recipient not in (DeviceType.ECONET, DeviceType.ALL):
|
221
215
|
_LOGGER.debug(
|
222
216
|
"Skipping frame intended for different recipient (%s)", recipient
|
@@ -2,9 +2,13 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
-
|
5
|
+
import asyncio
|
6
|
+
from collections.abc import Awaitable, Callable, Mapping
|
7
|
+
from functools import wraps
|
6
8
|
from typing import TypeVar
|
7
9
|
|
10
|
+
from typing_extensions import ParamSpec
|
11
|
+
|
8
12
|
KT = TypeVar("KT") # Key type.
|
9
13
|
VT = TypeVar("VT") # Value type.
|
10
14
|
|
@@ -40,4 +44,24 @@ def is_divisible(a: float, b: float, precision: int = 6) -> bool:
|
|
40
44
|
return a_scaled % b_scaled == 0
|
41
45
|
|
42
46
|
|
43
|
-
|
47
|
+
T = TypeVar("T")
|
48
|
+
P = ParamSpec("P")
|
49
|
+
|
50
|
+
|
51
|
+
def timeout(
|
52
|
+
seconds: float,
|
53
|
+
) -> Callable[[Callable[P, Awaitable[T]]], Callable[P, Awaitable[T]]]:
|
54
|
+
"""Decorate a timeout for the awaitable."""
|
55
|
+
|
56
|
+
def decorator(func: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
|
57
|
+
@wraps(func)
|
58
|
+
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
59
|
+
return await asyncio.wait_for(func(*args, **kwargs), timeout=seconds)
|
60
|
+
|
61
|
+
setattr(wrapper, "_has_timeout_seconds", seconds)
|
62
|
+
return wrapper
|
63
|
+
|
64
|
+
return decorator
|
65
|
+
|
66
|
+
|
67
|
+
__all__ = ["ensure_dict", "is_divisible", "to_camelcase", "timeout"]
|
@@ -26,7 +26,7 @@ classifiers = [
|
|
26
26
|
dependencies = [
|
27
27
|
"dataslots==1.2.0",
|
28
28
|
"pyserial-asyncio==0.6",
|
29
|
-
"typing-extensions
|
29
|
+
"typing-extensions>=4.14.0,<5.0"
|
30
30
|
]
|
31
31
|
dynamic = ["version"]
|
32
32
|
|
@@ -38,16 +38,16 @@ dynamic = ["version"]
|
|
38
38
|
[project.optional-dependencies]
|
39
39
|
test = [
|
40
40
|
"codespell==2.4.1",
|
41
|
-
"coverage==7.
|
42
|
-
"freezegun==1.5.
|
43
|
-
"mypy==1.
|
41
|
+
"coverage==7.10.5",
|
42
|
+
"freezegun==1.5.5",
|
43
|
+
"mypy==1.17.1",
|
44
44
|
"numpy<3.0.0,>=2.0.0",
|
45
45
|
"pyserial-asyncio-fast==0.16",
|
46
46
|
"pytest==8.4.1",
|
47
|
-
"pytest-asyncio==1.
|
48
|
-
"ruff==0.
|
49
|
-
"tox==4.
|
50
|
-
"types-pyserial==3.5.0.
|
47
|
+
"pytest-asyncio==1.1.0",
|
48
|
+
"ruff==0.12.10",
|
49
|
+
"tox==4.28.4",
|
50
|
+
"types-pyserial==3.5.0.20250822"
|
51
51
|
]
|
52
52
|
docs = [
|
53
53
|
"sphinx==8.1.3",
|
@@ -56,7 +56,7 @@ docs = [
|
|
56
56
|
]
|
57
57
|
dev = [
|
58
58
|
"pyplumio[test,docs]",
|
59
|
-
"pre-commit==4.
|
59
|
+
"pre-commit==4.3.0",
|
60
60
|
"tomli==2.2.1"
|
61
61
|
]
|
62
62
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
codespell==2.4.1
|
2
|
+
coverage==7.10.5
|
3
|
+
freezegun==1.5.5
|
4
|
+
mypy==1.17.1
|
5
|
+
numpy<3.0.0,>=2.0.0
|
6
|
+
pre-commit==4.3.0
|
7
|
+
pyserial-asyncio-fast==0.16
|
8
|
+
pytest-asyncio==1.1.0
|
9
|
+
pytest==8.4.1
|
10
|
+
ruff==0.12.10
|
11
|
+
tomli==2.2.1
|
12
|
+
tox==4.28.4
|
13
|
+
types-pyserial==3.5.0.20250822
|
@@ -206,7 +206,7 @@ async def test_ecomax_setup(mock_wait_for, mock_request, ecomax: EcoMAX) -> None
|
|
206
206
|
await ecomax.wait_until_done()
|
207
207
|
assert ATTR_FRAME_ERRORS not in ecomax.data
|
208
208
|
assert mock_request.await_count == len(REQUIRED)
|
209
|
-
mock_wait_for.assert_awaited_once_with(ATTR_SENSORS)
|
209
|
+
mock_wait_for.assert_awaited_once_with(ATTR_SENSORS, timeout=30.0)
|
210
210
|
|
211
211
|
|
212
212
|
@patch(
|
@@ -220,7 +220,7 @@ async def test_ecomax_setup_errors(mock_wait_for, mock_request, ecomax: EcoMAX)
|
|
220
220
|
await ecomax.wait_until_done()
|
221
221
|
assert FrameType.REQUEST_ALERTS in ecomax.get_nowait(ATTR_FRAME_ERRORS, [])
|
222
222
|
assert mock_request.await_count == len(REQUIRED)
|
223
|
-
mock_wait_for.assert_awaited_once_with(ATTR_SENSORS)
|
223
|
+
mock_wait_for.assert_awaited_once_with(ATTR_SENSORS, timeout=30.0)
|
224
224
|
|
225
225
|
|
226
226
|
@patch("asyncio.Queue.put")
|
@@ -139,6 +139,15 @@ class TestParameter:
|
|
139
139
|
assert result == 6
|
140
140
|
assert isinstance(result, int)
|
141
141
|
|
142
|
+
def test_hash(self, parameter: Parameter) -> None:
|
143
|
+
"""Test __hash__.
|
144
|
+
|
145
|
+
Checks parameter hashing.
|
146
|
+
"""
|
147
|
+
assert hash(parameter) == hash(
|
148
|
+
frozenset({("value", 6), ("min_value", 0), ("max_value", 10)})
|
149
|
+
)
|
150
|
+
|
142
151
|
@pytest.mark.parametrize(
|
143
152
|
("func_name", "other", "expected_result", "expected_type"),
|
144
153
|
[
|
@@ -6,22 +6,21 @@ from asyncio import StreamReader, StreamWriter
|
|
6
6
|
from importlib import reload
|
7
7
|
import logging
|
8
8
|
import sys
|
9
|
-
from typing import Final
|
9
|
+
from typing import Any, Final
|
10
10
|
from unittest.mock import AsyncMock, Mock, patch
|
11
11
|
|
12
12
|
import pytest
|
13
13
|
from serial import EIGHTBITS, PARITY_NONE, STOPBITS_ONE
|
14
14
|
|
15
|
+
import pyplumio.connection
|
15
16
|
from pyplumio.connection import Connection, SerialConnection, TcpConnection
|
16
17
|
from pyplumio.exceptions import ConnectionFailedError
|
17
18
|
from pyplumio.protocol import Protocol
|
18
19
|
|
19
20
|
|
20
21
|
@pytest.fixture(name="use_fast", params=(True, False))
|
21
|
-
def fixture_use_fast(request, monkeypatch, caplog):
|
22
|
+
def fixture_use_fast(request, monkeypatch, caplog) -> Any:
|
22
23
|
"""Try with and without serial-asyncio-fast package."""
|
23
|
-
import pyplumio.connection
|
24
|
-
|
25
24
|
if not request.param:
|
26
25
|
monkeypatch.setitem(sys.modules, "serial_asyncio_fast", None)
|
27
26
|
|