PyPlumIO 0.6.6__tar.gz → 0.6.8__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.6.6 → pyplumio-0.6.8}/.pre-commit-config.yaml +2 -2
- {pyplumio-0.6.6 → pyplumio-0.6.8}/PKG-INFO +8 -8
- {pyplumio-0.6.6 → pyplumio-0.6.8}/PyPlumIO.egg-info/PKG-INFO +8 -8
- {pyplumio-0.6.6 → pyplumio-0.6.8}/PyPlumIO.egg-info/SOURCES.txt +0 -2
- {pyplumio-0.6.6 → pyplumio-0.6.8}/PyPlumIO.egg-info/requires.txt +7 -7
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/_version.py +3 -3
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/connection.py +22 -15
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/devices/__init__.py +1 -2
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/devices/ecomax.py +7 -5
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/devices/mixer.py +3 -3
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/devices/thermostat.py +1 -1
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/filters.py +14 -9
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/frames/__init__.py +53 -39
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/frames/messages.py +3 -3
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/frames/requests.py +15 -10
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/frames/responses.py +10 -10
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/helpers/async_cache.py +2 -2
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/helpers/event_manager.py +61 -31
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/parameters/custom/__init__.py +1 -2
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/protocol.py +1 -1
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/__init__.py +7 -9
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/regulator_data.py +3 -3
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/utils.py +28 -1
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyproject.toml +7 -7
- pyplumio-0.6.8/requirements.txt +1 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/requirements_test.txt +6 -6
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/devices/test_ecomax.py +1 -1
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/devices/test_init.py +1 -1
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/frames/test_init.py +2 -13
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/frames/test_requests.py +64 -36
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/helpers/test_event_manager.py +25 -4
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/test_connection.py +29 -36
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/test_filters.py +15 -1
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/test_protocol.py +3 -3
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/test_utils.py +31 -0
- pyplumio-0.6.6/pyplumio/helpers/factory.py +0 -36
- pyplumio-0.6.6/requirements.txt +0 -1
- pyplumio-0.6.6/tests/helpers/test_factory.py +0 -41
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.gitattributes +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.github/CODE_OF_CONDUCT.md +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.github/dependabot.yml +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.github/workflows/ci.yml +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.github/workflows/codeql.yml +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.github/workflows/deploy.yml +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.github/workflows/documentation.yml +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.gitignore +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.qlty/qlty.toml +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/.vscode/settings.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/LICENSE +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/MANIFEST.in +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/PyPlumIO.egg-info/dependency_links.txt +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/PyPlumIO.egg-info/top_level.txt +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/README.md +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/Makefile +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/make.bat +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/source/callbacks.rst +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/source/conf.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/source/connecting.rst +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/source/frames.rst +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/source/index.rst +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/source/mixers_thermostats.rst +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/source/protocol.rst +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/source/reading.rst +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/source/schedules.rst +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/docs/source/writing.rst +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/images/ecomax.png +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/images/rs485.png +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/__init__.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/__main__.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/const.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/data_types.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/devices/ecoster.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/exceptions.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/helpers/__init__.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/helpers/task_manager.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/parameters/__init__.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/parameters/custom/ecomax_860d3_hb.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/parameters/ecomax.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/parameters/mixer.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/parameters/thermostat.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/py.typed +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/stream.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/alerts.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/ecomax_parameters.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/frame_versions.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/mixer_parameters.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/network_info.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/product_info.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/program_version.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/regulator_data_schema.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/schedules.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/sensor_data.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/pyplumio/structures/thermostat_parameters.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/requirements_docs.txt +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/setup.cfg +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/__init__.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/conftest.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/devices/__init__.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/devices/test_ecoster.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/devices/test_mixer.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/devices/test_thermostat.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/frames/test_messages.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/frames/test_responses.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/helpers/__init__.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/helpers/test_async_cache.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/helpers/test_task_manager.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/helpers/test_uid.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/parameters/__init__.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/parameters/custom/__init__.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/parameters/custom/test_ecomax_860d3_hb.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/parameters/custom/test_init.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/parameters/test_ecomax.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/parameters/test_init.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/parameters/test_mixers.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/parameters/test_thermostats.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/ruff.toml +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/structures/__init__.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/structures/test_alerts.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/structures/test_ecomax_parameters.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/structures/test_frame_versions.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/structures/test_mixer_parameters.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/structures/test_product_info.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/structures/test_schedules.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/test_data_types.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/test_init.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/test_main.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/test_stream.py +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/messages/regulator_data.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/messages/sensor_data.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/parameters/ecomax_860d3_hb.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/requests/alerts.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/requests/ecomax_control.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/requests/ecomax_parameters.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/requests/mixer_parameters.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/requests/set_ecomax_parameter.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/requests/set_mixer_parameter.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/requests/set_schedule.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/requests/set_thermostat_parameter.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/requests/thermostat_parameters.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/responses/alerts.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/responses/device_available.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/responses/ecomax_parameters.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/responses/mixer_parameters.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/responses/password.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/responses/program_version.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/responses/regulator_data_schema.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/responses/schedules.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/responses/thermostat_parameters.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/responses/uid.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/tests/testdata/unknown/unknown_ecomax_parameter.json +0 -0
- {pyplumio-0.6.6 → pyplumio-0.6.8}/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.14.
|
|
6
|
+
rev: v0.14.10
|
|
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.19.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.6.
|
|
3
|
+
Version: 0.6.8
|
|
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
|
|
@@ -23,17 +23,17 @@ Classifier: Topic :: Home Automation
|
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
|
-
Requires-Dist: pyserial-asyncio==0.
|
|
26
|
+
Requires-Dist: pyserial-asyncio-fast==0.16
|
|
27
27
|
Provides-Extra: test
|
|
28
28
|
Requires-Dist: codespell==2.4.1; extra == "test"
|
|
29
|
-
Requires-Dist: coverage==7.
|
|
29
|
+
Requires-Dist: coverage==7.13.0; extra == "test"
|
|
30
30
|
Requires-Dist: freezegun==1.5.5; extra == "test"
|
|
31
|
-
Requires-Dist: mypy==1.
|
|
31
|
+
Requires-Dist: mypy==1.19.1; extra == "test"
|
|
32
32
|
Requires-Dist: numpy<3.0.0,>=2.0.0; extra == "test"
|
|
33
33
|
Requires-Dist: pyserial-asyncio-fast==0.16; extra == "test"
|
|
34
|
-
Requires-Dist: pytest==
|
|
35
|
-
Requires-Dist: pytest-asyncio==1.
|
|
36
|
-
Requires-Dist: ruff==0.14.
|
|
34
|
+
Requires-Dist: pytest==9.0.2; extra == "test"
|
|
35
|
+
Requires-Dist: pytest-asyncio==1.3.0; extra == "test"
|
|
36
|
+
Requires-Dist: ruff==0.14.10; extra == "test"
|
|
37
37
|
Requires-Dist: tox==4.32.0; extra == "test"
|
|
38
38
|
Requires-Dist: types-pyserial==3.5.0.20251001; extra == "test"
|
|
39
39
|
Provides-Extra: docs
|
|
@@ -42,7 +42,7 @@ Requires-Dist: sphinx_rtd_theme==3.0.2; extra == "docs"
|
|
|
42
42
|
Requires-Dist: readthedocs-sphinx-search==0.3.2; extra == "docs"
|
|
43
43
|
Provides-Extra: dev
|
|
44
44
|
Requires-Dist: pyplumio[docs,test]; extra == "dev"
|
|
45
|
-
Requires-Dist: pre-commit==4.
|
|
45
|
+
Requires-Dist: pre-commit==4.5.1; extra == "dev"
|
|
46
46
|
Requires-Dist: tomli==2.3.0; extra == "dev"
|
|
47
47
|
Dynamic: license-file
|
|
48
48
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyPlumIO
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.8
|
|
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
|
|
@@ -23,17 +23,17 @@ Classifier: Topic :: Home Automation
|
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
License-File: LICENSE
|
|
26
|
-
Requires-Dist: pyserial-asyncio==0.
|
|
26
|
+
Requires-Dist: pyserial-asyncio-fast==0.16
|
|
27
27
|
Provides-Extra: test
|
|
28
28
|
Requires-Dist: codespell==2.4.1; extra == "test"
|
|
29
|
-
Requires-Dist: coverage==7.
|
|
29
|
+
Requires-Dist: coverage==7.13.0; extra == "test"
|
|
30
30
|
Requires-Dist: freezegun==1.5.5; extra == "test"
|
|
31
|
-
Requires-Dist: mypy==1.
|
|
31
|
+
Requires-Dist: mypy==1.19.1; extra == "test"
|
|
32
32
|
Requires-Dist: numpy<3.0.0,>=2.0.0; extra == "test"
|
|
33
33
|
Requires-Dist: pyserial-asyncio-fast==0.16; extra == "test"
|
|
34
|
-
Requires-Dist: pytest==
|
|
35
|
-
Requires-Dist: pytest-asyncio==1.
|
|
36
|
-
Requires-Dist: ruff==0.14.
|
|
34
|
+
Requires-Dist: pytest==9.0.2; extra == "test"
|
|
35
|
+
Requires-Dist: pytest-asyncio==1.3.0; extra == "test"
|
|
36
|
+
Requires-Dist: ruff==0.14.10; extra == "test"
|
|
37
37
|
Requires-Dist: tox==4.32.0; extra == "test"
|
|
38
38
|
Requires-Dist: types-pyserial==3.5.0.20251001; extra == "test"
|
|
39
39
|
Provides-Extra: docs
|
|
@@ -42,7 +42,7 @@ Requires-Dist: sphinx_rtd_theme==3.0.2; extra == "docs"
|
|
|
42
42
|
Requires-Dist: readthedocs-sphinx-search==0.3.2; extra == "docs"
|
|
43
43
|
Provides-Extra: dev
|
|
44
44
|
Requires-Dist: pyplumio[docs,test]; extra == "dev"
|
|
45
|
-
Requires-Dist: pre-commit==4.
|
|
45
|
+
Requires-Dist: pre-commit==4.5.1; extra == "dev"
|
|
46
46
|
Requires-Dist: tomli==2.3.0; extra == "dev"
|
|
47
47
|
Dynamic: license-file
|
|
48
48
|
|
|
@@ -61,7 +61,6 @@ pyplumio/frames/responses.py
|
|
|
61
61
|
pyplumio/helpers/__init__.py
|
|
62
62
|
pyplumio/helpers/async_cache.py
|
|
63
63
|
pyplumio/helpers/event_manager.py
|
|
64
|
-
pyplumio/helpers/factory.py
|
|
65
64
|
pyplumio/helpers/task_manager.py
|
|
66
65
|
pyplumio/parameters/__init__.py
|
|
67
66
|
pyplumio/parameters/ecomax.py
|
|
@@ -106,7 +105,6 @@ tests/frames/test_responses.py
|
|
|
106
105
|
tests/helpers/__init__.py
|
|
107
106
|
tests/helpers/test_async_cache.py
|
|
108
107
|
tests/helpers/test_event_manager.py
|
|
109
|
-
tests/helpers/test_factory.py
|
|
110
108
|
tests/helpers/test_task_manager.py
|
|
111
109
|
tests/helpers/test_uid.py
|
|
112
110
|
tests/parameters/__init__.py
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
pyserial-asyncio==0.
|
|
1
|
+
pyserial-asyncio-fast==0.16
|
|
2
2
|
|
|
3
3
|
[dev]
|
|
4
4
|
pyplumio[docs,test]
|
|
5
|
-
pre-commit==4.
|
|
5
|
+
pre-commit==4.5.1
|
|
6
6
|
tomli==2.3.0
|
|
7
7
|
|
|
8
8
|
[docs]
|
|
@@ -12,13 +12,13 @@ readthedocs-sphinx-search==0.3.2
|
|
|
12
12
|
|
|
13
13
|
[test]
|
|
14
14
|
codespell==2.4.1
|
|
15
|
-
coverage==7.
|
|
15
|
+
coverage==7.13.0
|
|
16
16
|
freezegun==1.5.5
|
|
17
|
-
mypy==1.
|
|
17
|
+
mypy==1.19.1
|
|
18
18
|
numpy<3.0.0,>=2.0.0
|
|
19
19
|
pyserial-asyncio-fast==0.16
|
|
20
|
-
pytest==
|
|
21
|
-
pytest-asyncio==1.
|
|
22
|
-
ruff==0.14.
|
|
20
|
+
pytest==9.0.2
|
|
21
|
+
pytest-asyncio==1.3.0
|
|
22
|
+
ruff==0.14.10
|
|
23
23
|
tox==4.32.0
|
|
24
24
|
types-pyserial==3.5.0.20251001
|
|
@@ -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.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 6,
|
|
31
|
+
__version__ = version = '0.6.8'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 6, 8)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gbde57b4a1'
|
|
@@ -7,9 +7,11 @@ import asyncio
|
|
|
7
7
|
from collections.abc import AsyncGenerator
|
|
8
8
|
from contextlib import asynccontextmanager
|
|
9
9
|
import logging
|
|
10
|
+
from types import MappingProxyType
|
|
10
11
|
from typing import Any, Final
|
|
11
12
|
|
|
12
13
|
from serial import EIGHTBITS, PARITY_NONE, STOPBITS_ONE
|
|
14
|
+
import serial_asyncio_fast
|
|
13
15
|
|
|
14
16
|
from pyplumio.devices import PhysicalDevice
|
|
15
17
|
from pyplumio.exceptions import ConnectionFailedError
|
|
@@ -22,13 +24,6 @@ _LOGGER = logging.getLogger(__name__)
|
|
|
22
24
|
TRY_CONNECT_FOR_SECONDS: Final = 5
|
|
23
25
|
RECONNECT_AFTER_SECONDS: Final = 20
|
|
24
26
|
|
|
25
|
-
try:
|
|
26
|
-
import serial_asyncio_fast as pyserial_asyncio
|
|
27
|
-
|
|
28
|
-
_LOGGER.info("Using pyserial-asyncio-fast in place of pyserial-asyncio")
|
|
29
|
-
except ImportError:
|
|
30
|
-
import serial_asyncio as pyserial_asyncio # type: ignore[no-redef]
|
|
31
|
-
|
|
32
27
|
|
|
33
28
|
class Connection(ABC, TaskManager):
|
|
34
29
|
"""Represents a connection.
|
|
@@ -36,11 +31,12 @@ class Connection(ABC, TaskManager):
|
|
|
36
31
|
All specific connection classes MUST be inherited from this class.
|
|
37
32
|
"""
|
|
38
33
|
|
|
39
|
-
__slots__ = ("_protocol", "_reconnect_on_failure", "_options")
|
|
34
|
+
__slots__ = ("_protocol", "_reconnect_on_failure", "_options", "_retries")
|
|
40
35
|
|
|
41
36
|
_protocol: Protocol
|
|
42
37
|
_reconnect_on_failure: bool
|
|
43
38
|
_options: dict[str, Any]
|
|
39
|
+
_retries: int
|
|
44
40
|
|
|
45
41
|
def __init__(
|
|
46
42
|
self,
|
|
@@ -59,6 +55,7 @@ class Connection(ABC, TaskManager):
|
|
|
59
55
|
self._reconnect_on_failure = reconnect_on_failure
|
|
60
56
|
self._protocol = protocol
|
|
61
57
|
self._options = options
|
|
58
|
+
self._retries = 0
|
|
62
59
|
|
|
63
60
|
async def __aenter__(self) -> Connection:
|
|
64
61
|
"""Provide an entry point for the context manager."""
|
|
@@ -85,11 +82,21 @@ class Connection(ABC, TaskManager):
|
|
|
85
82
|
"""Try to connect and reconnect on failure."""
|
|
86
83
|
try:
|
|
87
84
|
await self._connect()
|
|
85
|
+
if self._retries > 0:
|
|
86
|
+
_LOGGER.info(
|
|
87
|
+
"Connection to device restored after %i retries", self._retries
|
|
88
|
+
)
|
|
89
|
+
self._retries = 0
|
|
88
90
|
except ConnectionFailedError:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
if self._retries == 0:
|
|
92
|
+
_LOGGER.error(
|
|
93
|
+
(
|
|
94
|
+
"Unable to connect to the device. Connection we be retried "
|
|
95
|
+
"automatically every %i seconds in background."
|
|
96
|
+
),
|
|
97
|
+
RECONNECT_AFTER_SECONDS,
|
|
98
|
+
)
|
|
99
|
+
self._retries += 1
|
|
93
100
|
await asyncio.sleep(RECONNECT_AFTER_SECONDS)
|
|
94
101
|
self.create_task(self._reconnect(), name="reconnect_task")
|
|
95
102
|
|
|
@@ -122,9 +129,9 @@ class Connection(ABC, TaskManager):
|
|
|
122
129
|
return self._protocol
|
|
123
130
|
|
|
124
131
|
@property
|
|
125
|
-
def options(self) ->
|
|
132
|
+
def options(self) -> MappingProxyType[str, Any]:
|
|
126
133
|
"""Return connection options."""
|
|
127
|
-
return self._options
|
|
134
|
+
return MappingProxyType(self._options)
|
|
128
135
|
|
|
129
136
|
@timeout(TRY_CONNECT_FOR_SECONDS)
|
|
130
137
|
@abstractmethod
|
|
@@ -208,7 +215,7 @@ class SerialConnection(Connection):
|
|
|
208
215
|
self,
|
|
209
216
|
) -> tuple[asyncio.StreamReader, asyncio.StreamWriter]:
|
|
210
217
|
"""Open the connection and return reader and writer objects."""
|
|
211
|
-
return await
|
|
218
|
+
return await serial_asyncio_fast.open_serial_connection(
|
|
212
219
|
url=self.url,
|
|
213
220
|
baudrate=self.baudrate,
|
|
214
221
|
bytesize=EIGHTBITS,
|
|
@@ -14,10 +14,9 @@ from pyplumio.exceptions import RequestError, UnknownDeviceError
|
|
|
14
14
|
from pyplumio.filters import on_change
|
|
15
15
|
from pyplumio.frames import Frame, Request, is_known_frame_type
|
|
16
16
|
from pyplumio.helpers.event_manager import EventManager, event_listener
|
|
17
|
-
from pyplumio.helpers.factory import create_instance
|
|
18
17
|
from pyplumio.parameters import Numeric, Parameter
|
|
19
18
|
from pyplumio.structures.network_info import NetworkInfo
|
|
20
|
-
from pyplumio.utils import to_camelcase
|
|
19
|
+
from pyplumio.utils import create_instance, to_camelcase
|
|
21
20
|
|
|
22
21
|
_LOGGER = logging.getLogger(__name__)
|
|
23
22
|
|
|
@@ -43,7 +43,7 @@ from pyplumio.structures.ecomax_parameters import (
|
|
|
43
43
|
)
|
|
44
44
|
from pyplumio.structures.mixer_parameters import ATTR_MIXER_PARAMETERS
|
|
45
45
|
from pyplumio.structures.network_info import ATTR_NETWORK_INFO, NetworkInfo
|
|
46
|
-
from pyplumio.structures.product_info import ATTR_PRODUCT
|
|
46
|
+
from pyplumio.structures.product_info import ATTR_PRODUCT
|
|
47
47
|
from pyplumio.structures.regulator_data_schema import ATTR_REGDATA_SCHEMA
|
|
48
48
|
from pyplumio.structures.schedules import (
|
|
49
49
|
ATTR_SCHEDULES,
|
|
@@ -142,7 +142,9 @@ class EcoMAX(PhysicalDevice):
|
|
|
142
142
|
def handle_frame(self, frame: Frame) -> None:
|
|
143
143
|
"""Handle frame received from the ecoMAX device."""
|
|
144
144
|
if isinstance(frame, Request) and (
|
|
145
|
-
response := frame.
|
|
145
|
+
response := frame.create_response(
|
|
146
|
+
data={ATTR_NETWORK_INFO: self._network_info}
|
|
147
|
+
)
|
|
146
148
|
):
|
|
147
149
|
self.queue_send(response)
|
|
148
150
|
|
|
@@ -253,10 +255,10 @@ class EcoMAX(PhysicalDevice):
|
|
|
253
255
|
) -> bool:
|
|
254
256
|
"""Update ecoMAX parameters and dispatch the events."""
|
|
255
257
|
_LOGGER.debug("Received device parameters")
|
|
256
|
-
product_info
|
|
258
|
+
product_info = await self.get(ATTR_PRODUCT)
|
|
257
259
|
parameter_types = await get_ecomax_parameter_types(product_info)
|
|
258
260
|
|
|
259
|
-
def _ecomax_parameter_events() -> Generator[Coroutine]:
|
|
261
|
+
def _ecomax_parameter_events() -> Generator[Coroutine[Any, Any, None]]:
|
|
260
262
|
"""Get dispatch calls for ecoMAX parameter events."""
|
|
261
263
|
for index, values in parameters:
|
|
262
264
|
try:
|
|
@@ -333,7 +335,7 @@ class EcoMAX(PhysicalDevice):
|
|
|
333
335
|
) -> bool:
|
|
334
336
|
"""Update schedule parameters and dispatch the events."""
|
|
335
337
|
|
|
336
|
-
def _schedule_parameter_events() -> Generator[Coroutine]:
|
|
338
|
+
def _schedule_parameter_events() -> Generator[Coroutine[Any, Any, None]]:
|
|
337
339
|
"""Get dispatch calls for schedule parameter events."""
|
|
338
340
|
for index, values in parameters:
|
|
339
341
|
description = SCHEDULE_PARAMETERS[index]
|
|
@@ -16,7 +16,7 @@ from pyplumio.parameters.mixer import (
|
|
|
16
16
|
MixerSwitchDescription,
|
|
17
17
|
get_mixer_parameter_types,
|
|
18
18
|
)
|
|
19
|
-
from pyplumio.structures.product_info import ATTR_PRODUCT
|
|
19
|
+
from pyplumio.structures.product_info import ATTR_PRODUCT
|
|
20
20
|
|
|
21
21
|
_LOGGER = logging.getLogger(__name__)
|
|
22
22
|
|
|
@@ -41,10 +41,10 @@ class Mixer(LogicalDevice):
|
|
|
41
41
|
) -> bool:
|
|
42
42
|
"""Update mixer parameters and dispatch the events."""
|
|
43
43
|
_LOGGER.debug("Received mixer %i parameters", self.index)
|
|
44
|
-
product_info
|
|
44
|
+
product_info = await self.parent.get(ATTR_PRODUCT)
|
|
45
45
|
parameter_types = get_mixer_parameter_types(product_info)
|
|
46
46
|
|
|
47
|
-
def _mixer_parameter_events() -> Generator[Coroutine]:
|
|
47
|
+
def _mixer_parameter_events() -> Generator[Coroutine[Any, Any, None]]:
|
|
48
48
|
"""Get dispatch calls for mixer parameter events."""
|
|
49
49
|
for index, values in parameters:
|
|
50
50
|
try:
|
|
@@ -42,7 +42,7 @@ class Thermostat(LogicalDevice):
|
|
|
42
42
|
_LOGGER.debug("Received thermostat %i parameters", self.index)
|
|
43
43
|
parameter_types = get_thermostat_parameter_types()
|
|
44
44
|
|
|
45
|
-
def _thermostat_parameter_events() -> Generator[Coroutine]:
|
|
45
|
+
def _thermostat_parameter_events() -> Generator[Coroutine[Any, Any, None]]:
|
|
46
46
|
"""Get dispatch calls for thermostat parameter events."""
|
|
47
47
|
for index, values in parameters:
|
|
48
48
|
description = parameter_types[index]
|
|
@@ -69,7 +69,7 @@ DEFAULT_TOLERANCE: Final = 1e-6
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
@overload
|
|
72
|
-
def is_close(old: Parameter, new: Parameter
|
|
72
|
+
def is_close(old: Parameter, new: Parameter) -> bool: ...
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
@overload
|
|
@@ -79,9 +79,7 @@ def is_close(
|
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
@overload
|
|
82
|
-
def is_close(
|
|
83
|
-
old: SupportsComparison, new: SupportsComparison, *, tolerance: None = None
|
|
84
|
-
) -> bool: ...
|
|
82
|
+
def is_close(old: SupportsComparison, new: SupportsComparison) -> bool: ...
|
|
85
83
|
|
|
86
84
|
|
|
87
85
|
def is_close(
|
|
@@ -100,6 +98,9 @@ def is_close(
|
|
|
100
98
|
return old.__eq__(new)
|
|
101
99
|
|
|
102
100
|
|
|
101
|
+
_SubtractableT = TypeVar("_SubtractableT", list, SupportsSubtraction)
|
|
102
|
+
|
|
103
|
+
|
|
103
104
|
@overload
|
|
104
105
|
def diffence_between(old: list, new: list) -> list: ...
|
|
105
106
|
|
|
@@ -110,9 +111,7 @@ def diffence_between(
|
|
|
110
111
|
) -> SupportsSubtraction: ...
|
|
111
112
|
|
|
112
113
|
|
|
113
|
-
def diffence_between(
|
|
114
|
-
old: SupportsSubtraction | list, new: SupportsSubtraction | list
|
|
115
|
-
) -> SupportsSubtraction | list | None:
|
|
114
|
+
def diffence_between(old: _SubtractableT, new: _SubtractableT) -> Any:
|
|
116
115
|
"""Return a difference between values."""
|
|
117
116
|
if isinstance(old, list) and isinstance(new, list):
|
|
118
117
|
return [x for x in new if x not in old]
|
|
@@ -120,7 +119,7 @@ def diffence_between(
|
|
|
120
119
|
if isinstance(old, SupportsSubtraction) and isinstance(new, SupportsSubtraction):
|
|
121
120
|
return new.__sub__(old)
|
|
122
121
|
|
|
123
|
-
return
|
|
122
|
+
return NotImplemented
|
|
124
123
|
|
|
125
124
|
|
|
126
125
|
_Numeric: TypeAlias = float | int | Decimal | Number
|
|
@@ -172,6 +171,11 @@ class Filter(ABC):
|
|
|
172
171
|
|
|
173
172
|
return NotImplemented
|
|
174
173
|
|
|
174
|
+
@property
|
|
175
|
+
def __name__(self) -> str:
|
|
176
|
+
"""Return a callback name."""
|
|
177
|
+
return self._callback.__name__
|
|
178
|
+
|
|
175
179
|
@abstractmethod
|
|
176
180
|
async def __call__(self, new_value: Any) -> Any:
|
|
177
181
|
"""Set a new value for the callback."""
|
|
@@ -536,7 +540,8 @@ class _Delta(ComparisonFilter):
|
|
|
536
540
|
if self.is_undefined() or not is_close(self.value, new_value):
|
|
537
541
|
old_value = self.value
|
|
538
542
|
self.value = new_value
|
|
539
|
-
|
|
543
|
+
difference = diffence_between(old_value, new_value)
|
|
544
|
+
if difference is not NotImplemented:
|
|
540
545
|
return await self._callback(difference)
|
|
541
546
|
|
|
542
547
|
|
|
@@ -10,8 +10,7 @@ from typing import TYPE_CHECKING, Any, ClassVar, Final, TypeVar
|
|
|
10
10
|
|
|
11
11
|
from pyplumio.const import DeviceType, FrameType
|
|
12
12
|
from pyplumio.exceptions import UnknownFrameError
|
|
13
|
-
from pyplumio.
|
|
14
|
-
from pyplumio.utils import ensure_dict, to_camelcase
|
|
13
|
+
from pyplumio.utils import create_instance, ensure_dict, to_camelcase
|
|
15
14
|
|
|
16
15
|
if TYPE_CHECKING:
|
|
17
16
|
from pyplumio.structures import Structure
|
|
@@ -243,11 +242,29 @@ class Frame(ABC):
|
|
|
243
242
|
"""Decode a frame message."""
|
|
244
243
|
|
|
245
244
|
|
|
245
|
+
def frame_handler(
|
|
246
|
+
frame_type: FrameType, structure: type[Structure] | None = None
|
|
247
|
+
) -> Callable[[type[_FrameT]], type[_FrameT]]:
|
|
248
|
+
"""Specify frame type for the frame class."""
|
|
249
|
+
|
|
250
|
+
def wrapper(cls: type[_FrameT]) -> type[_FrameT]:
|
|
251
|
+
"""Wrap the frame class."""
|
|
252
|
+
setattr(cls, "frame_type", frame_type)
|
|
253
|
+
if structure and issubclass(cls, Response):
|
|
254
|
+
setattr(cls, "structures", (structure,))
|
|
255
|
+
|
|
256
|
+
return cls
|
|
257
|
+
|
|
258
|
+
return wrapper
|
|
259
|
+
|
|
260
|
+
|
|
246
261
|
class Request(Frame):
|
|
247
262
|
"""Represents a request."""
|
|
248
263
|
|
|
249
264
|
__slots__ = ()
|
|
250
265
|
|
|
266
|
+
response: ClassVar[type[Response]]
|
|
267
|
+
|
|
251
268
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
|
252
269
|
"""Create a frame message."""
|
|
253
270
|
return bytearray()
|
|
@@ -256,38 +273,47 @@ class Request(Frame):
|
|
|
256
273
|
"""Decode a frame message."""
|
|
257
274
|
return {}
|
|
258
275
|
|
|
259
|
-
def
|
|
260
|
-
"""
|
|
276
|
+
def create_response(self, **kwargs: Any) -> Response | None:
|
|
277
|
+
"""Create response frame."""
|
|
278
|
+
if hasattr(self, "response"):
|
|
279
|
+
return self.response(recipient=self.sender, **kwargs)
|
|
280
|
+
|
|
261
281
|
return None
|
|
262
282
|
|
|
283
|
+
def validate_response(self, response: Response) -> bool:
|
|
284
|
+
"""Validate if response is valid for the request."""
|
|
285
|
+
if not hasattr(self, "response"):
|
|
286
|
+
raise NotImplementedError
|
|
263
287
|
|
|
264
|
-
|
|
265
|
-
|
|
288
|
+
if isinstance(response, self.response):
|
|
289
|
+
return True
|
|
266
290
|
|
|
267
|
-
|
|
291
|
+
return False
|
|
268
292
|
|
|
269
|
-
def create_message(self, data: dict[str, Any]) -> bytearray:
|
|
270
|
-
"""Create a frame message."""
|
|
271
|
-
return bytearray()
|
|
272
293
|
|
|
273
|
-
|
|
274
|
-
"""Decode a frame message."""
|
|
275
|
-
return {}
|
|
294
|
+
_RequestT = TypeVar("_RequestT", bound=Request)
|
|
276
295
|
|
|
277
296
|
|
|
278
|
-
|
|
279
|
-
|
|
297
|
+
def expect_response(
|
|
298
|
+
response: type[Response],
|
|
299
|
+
) -> Callable[[type[_RequestT]], type[_RequestT]]:
|
|
300
|
+
"""Assign response to the request class."""
|
|
280
301
|
|
|
281
|
-
|
|
302
|
+
def wrapper(cls: type[_RequestT]) -> type[_RequestT]:
|
|
303
|
+
"""Wrap the request class."""
|
|
304
|
+
setattr(cls, "response", response)
|
|
305
|
+
return cls
|
|
282
306
|
|
|
307
|
+
return wrapper
|
|
283
308
|
|
|
284
|
-
|
|
285
|
-
|
|
309
|
+
|
|
310
|
+
class Response(Frame):
|
|
311
|
+
"""Represents a response."""
|
|
286
312
|
|
|
287
313
|
__slots__ = ("_structures",)
|
|
288
314
|
|
|
289
315
|
container: ClassVar[str]
|
|
290
|
-
structures: ClassVar[list[type[Structure]]]
|
|
316
|
+
structures: ClassVar[list[type[Structure]]] = []
|
|
291
317
|
|
|
292
318
|
_structures: list[Structure]
|
|
293
319
|
|
|
@@ -302,9 +328,7 @@ class Structured(Frame):
|
|
|
302
328
|
**kwargs: Any,
|
|
303
329
|
) -> None:
|
|
304
330
|
"""Initialize a new structured frame."""
|
|
305
|
-
|
|
306
|
-
self._structures = [structure(self) for structure in self.structures]
|
|
307
|
-
|
|
331
|
+
self._structures = [structure(self) for structure in self.structures]
|
|
308
332
|
super().__init__(
|
|
309
333
|
recipient, sender, econet_type, econet_version, message, data, **kwargs
|
|
310
334
|
)
|
|
@@ -330,28 +354,18 @@ class Structured(Frame):
|
|
|
330
354
|
return data
|
|
331
355
|
|
|
332
356
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
) -> Callable[[type[_FrameT]], type[_FrameT]]:
|
|
336
|
-
"""Specify frame type for the frame class."""
|
|
337
|
-
|
|
338
|
-
def wrapper(cls: type[_FrameT]) -> type[_FrameT]:
|
|
339
|
-
"""Wrap the frame class."""
|
|
340
|
-
setattr(cls, "frame_type", frame_type)
|
|
341
|
-
if structure and issubclass(cls, Structured):
|
|
342
|
-
setattr(cls, "structures", (structure,))
|
|
343
|
-
|
|
344
|
-
return cls
|
|
357
|
+
class Message(Response):
|
|
358
|
+
"""Represents a message."""
|
|
345
359
|
|
|
346
|
-
|
|
360
|
+
__slots__ = ()
|
|
347
361
|
|
|
348
362
|
|
|
349
|
-
|
|
363
|
+
_ResponseT = TypeVar("_ResponseT", bound=Response)
|
|
350
364
|
|
|
351
365
|
|
|
352
366
|
def contains(
|
|
353
367
|
*structures: type[Structure], container: str | None = None
|
|
354
|
-
) -> Callable[[type[
|
|
368
|
+
) -> Callable[[type[_ResponseT]], type[_ResponseT]]:
|
|
355
369
|
"""Decorate frame class with structure.
|
|
356
370
|
|
|
357
371
|
Indicate which structures need to be used for encoding/decoding
|
|
@@ -359,7 +373,7 @@ def contains(
|
|
|
359
373
|
a single key.
|
|
360
374
|
"""
|
|
361
375
|
|
|
362
|
-
def wrapper(cls: type[
|
|
376
|
+
def wrapper(cls: type[_ResponseT]) -> type[_ResponseT]:
|
|
363
377
|
"""Wrap the frame class."""
|
|
364
378
|
setattr(cls, "structures", structures)
|
|
365
379
|
if container:
|
|
@@ -375,9 +389,9 @@ __all__ = [
|
|
|
375
389
|
"Request",
|
|
376
390
|
"Response",
|
|
377
391
|
"Message",
|
|
378
|
-
"Structured",
|
|
379
392
|
"bcc",
|
|
380
393
|
"contains",
|
|
394
|
+
"expect_response",
|
|
381
395
|
"frame_handler",
|
|
382
396
|
"get_frame_handler",
|
|
383
397
|
"is_known_frame_type",
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from pyplumio.const import ATTR_SENSORS, FrameType
|
|
6
|
-
from pyplumio.frames import Message,
|
|
6
|
+
from pyplumio.frames import Message, contains, frame_handler
|
|
7
7
|
from pyplumio.structures.frame_versions import FrameVersionsStructure
|
|
8
8
|
from pyplumio.structures.regulator_data import RegulatorDataStructure
|
|
9
9
|
from pyplumio.structures.sensor_data import SensorDataStructure
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@frame_handler(FrameType.MESSAGE_REGULATOR_DATA, structure=RegulatorDataStructure)
|
|
13
|
-
class RegulatorDataMessage(
|
|
13
|
+
class RegulatorDataMessage(Message):
|
|
14
14
|
"""Represents a regulator data message."""
|
|
15
15
|
|
|
16
16
|
__slots__ = ()
|
|
@@ -18,7 +18,7 @@ class RegulatorDataMessage(Structured, Message):
|
|
|
18
18
|
|
|
19
19
|
@frame_handler(FrameType.MESSAGE_SENSOR_DATA)
|
|
20
20
|
@contains(FrameVersionsStructure, SensorDataStructure, container=ATTR_SENSORS)
|
|
21
|
-
class SensorDataMessage(
|
|
21
|
+
class SensorDataMessage(Message):
|
|
22
22
|
"""Represents a sensor data message."""
|
|
23
23
|
|
|
24
24
|
__slots__ = ()
|