PyPlumIO 0.5.43__tar.gz → 0.5.49__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.43 → pyplumio-0.5.49}/PKG-INFO +4 -2
- {pyplumio-0.5.43 → pyplumio-0.5.49}/PyPlumIO.egg-info/PKG-INFO +4 -2
- {pyplumio-0.5.43 → pyplumio-0.5.49}/PyPlumIO.egg-info/SOURCES.txt +16 -3
- {pyplumio-0.5.43 → pyplumio-0.5.49}/PyPlumIO.egg-info/requires.txt +3 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/source/callbacks.rst +20 -3
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/source/frames.rst +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/_version.py +2 -2
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/const.py +1 -3
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/devices/__init__.py +23 -31
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/devices/ecomax.py +107 -61
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/devices/mixer.py +8 -8
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/devices/thermostat.py +11 -8
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/filters.py +106 -29
- pyplumio-0.5.49/pyplumio/helpers/async_cache.py +49 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/helpers/event_manager.py +20 -2
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/helpers/factory.py +12 -13
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/helpers/schedule.py +4 -3
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/helpers/timeout.py +7 -5
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/parameters/__init__.py +4 -55
- pyplumio-0.5.49/pyplumio/parameters/custom/__init__.py +111 -0
- pyplumio-0.5.49/pyplumio/parameters/custom/ecomax_860d3_hb.py +80 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/parameters/ecomax.py +7 -54
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/protocol.py +15 -7
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/alerts.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/boiler_power.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/fan_power.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/frame_versions.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/fuel_consumption.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/lambda_sensor.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/mixer_sensors.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/network_info.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/output_flags.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/outputs.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/product_info.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/regulator_data.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/regulator_data_schema.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/temperatures.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/thermostat_parameters.py +5 -7
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/thermostat_sensors.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyproject.toml +3 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/requirements_test.txt +3 -1
- pyplumio-0.5.49/tests/__init__.py +1 -0
- pyplumio-0.5.49/tests/conftest.py +188 -0
- pyplumio-0.5.49/tests/devices/__init__.py +1 -0
- pyplumio-0.5.49/tests/devices/test_ecomax.py +554 -0
- pyplumio-0.5.49/tests/devices/test_ecoster.py +12 -0
- pyplumio-0.5.49/tests/devices/test_init.py +188 -0
- pyplumio-0.5.49/tests/devices/test_mixer.py +118 -0
- pyplumio-0.5.49/tests/devices/test_thermostat.py +134 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/frames/test_init.py +18 -8
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/frames/test_messages.py +10 -16
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/frames/test_requests.py +1 -25
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/frames/test_responses.py +1 -19
- pyplumio-0.5.49/tests/helpers/test_async_cache.py +49 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/helpers/test_event_manager.py +19 -3
- pyplumio-0.5.49/tests/helpers/test_factory.py +41 -0
- pyplumio-0.5.49/tests/helpers/test_schedule.py +190 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/helpers/test_task_manager.py +7 -7
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/helpers/test_timeout.py +8 -10
- pyplumio-0.5.49/tests/helpers/test_uid.py +17 -0
- pyplumio-0.5.49/tests/parameters/custom/__init__.py +1 -0
- pyplumio-0.5.49/tests/parameters/custom/test_ecomax_860d3_hb.py +87 -0
- pyplumio-0.5.49/tests/parameters/custom/test_init.py +89 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/parameters/test_ecomax.py +3 -3
- pyplumio-0.5.49/tests/parameters/test_init.py +550 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/parameters/test_thermostats.py +1 -1
- pyplumio-0.5.49/tests/test_connection.py +223 -0
- pyplumio-0.5.49/tests/test_data_types.py +87 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/test_filters.py +103 -34
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/test_main.py +3 -1
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/test_protocol.py +21 -16
- pyplumio-0.5.49/tests/test_stream.py +267 -0
- pyplumio-0.5.49/tests/test_utils.py +58 -0
- pyplumio-0.5.49/tests/testdata/parameters/ecomax_860d3_hb.json +20 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/responses/regulator_data_schema.json +44 -44
- pyplumio-0.5.49/tests/testdata/unknown/unknown_ecomax_parameter.json +20 -0
- pyplumio-0.5.49/tests/testdata/unknown/unknown_mixer_parameter.json +11 -0
- pyplumio-0.5.43/tests/__init__.py +0 -56
- pyplumio-0.5.43/tests/conftest.py +0 -33
- pyplumio-0.5.43/tests/helpers/test_data_types.py +0 -299
- pyplumio-0.5.43/tests/helpers/test_factory.py +0 -35
- pyplumio-0.5.43/tests/helpers/test_schedule.py +0 -150
- pyplumio-0.5.43/tests/helpers/test_uid.py +0 -17
- pyplumio-0.5.43/tests/parameters/test_init.py +0 -398
- pyplumio-0.5.43/tests/test_connection.py +0 -246
- pyplumio-0.5.43/tests/test_devices.py +0 -736
- pyplumio-0.5.43/tests/test_stream.py +0 -186
- pyplumio-0.5.43/tests/test_utils.py +0 -34
- pyplumio-0.5.43/tests/testdata/unknown/unknown_ecomax_parameter.json +0 -18
- pyplumio-0.5.43/tests/testdata/unknown/unknown_mixer_parameter.json +0 -9
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.gitattributes +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.github/CODE_OF_CONDUCT.md +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.github/dependabot.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.github/workflows/ci.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.github/workflows/codeql.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.github/workflows/deploy.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.github/workflows/documentation.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.gitignore +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.pre-commit-config.yaml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.qlty/qlty.toml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/.vscode/settings.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/LICENSE +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/MANIFEST.in +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/PyPlumIO.egg-info/dependency_links.txt +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/PyPlumIO.egg-info/top_level.txt +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/README.md +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/Makefile +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/make.bat +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/source/conf.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/source/connecting.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/source/index.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/source/mixers_thermostats.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/source/protocol.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/source/reading.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/source/schedules.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/docs/source/writing.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/images/ecomax.png +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/images/rs485.png +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/__main__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/connection.py +0 -0
- {pyplumio-0.5.43/pyplumio/helpers → pyplumio-0.5.49/pyplumio}/data_types.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/devices/ecoster.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/exceptions.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/frames/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/frames/messages.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/frames/requests.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/frames/responses.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/helpers/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/helpers/task_manager.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/helpers/uid.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/parameters/mixer.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/parameters/thermostat.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/py.typed +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/stream.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/boiler_load.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/ecomax_parameters.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/fuel_level.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/mixer_parameters.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/modules.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/pending_alerts.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/program_version.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/schedules.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/structures/statuses.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/pyplumio/utils.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/requirements.txt +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/requirements_docs.txt +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/setup.cfg +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/helpers/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/parameters/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/parameters/test_mixers.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/ruff.toml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/test_init.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/messages/regulator_data.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/messages/sensor_data.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/requests/alerts.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/requests/ecomax_control.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/requests/ecomax_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/requests/mixer_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/requests/set_ecomax_parameter.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/requests/set_mixer_parameter.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/requests/set_schedule.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/requests/set_thermostat_parameter.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/requests/thermostat_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/responses/alerts.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/responses/device_available.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/responses/ecomax_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/responses/mixer_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/responses/password.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/responses/program_version.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/responses/schedules.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/responses/thermostat_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.49}/tests/testdata/responses/uid.json +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: PyPlumIO
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.49
|
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
|
@@ -29,11 +29,13 @@ Requires-Dist: typing-extensions==4.13.2
|
|
29
29
|
Provides-Extra: test
|
30
30
|
Requires-Dist: codespell==2.4.1; extra == "test"
|
31
31
|
Requires-Dist: coverage==7.8.0; extra == "test"
|
32
|
+
Requires-Dist: freezegun==1.5.1; extra == "test"
|
32
33
|
Requires-Dist: mypy==1.15.0; extra == "test"
|
34
|
+
Requires-Dist: numpy<3.0.0,>=2.0.0; extra == "test"
|
33
35
|
Requires-Dist: pyserial-asyncio-fast==0.16; extra == "test"
|
34
36
|
Requires-Dist: pytest==8.3.5; extra == "test"
|
35
37
|
Requires-Dist: pytest-asyncio==0.26.0; extra == "test"
|
36
|
-
Requires-Dist: ruff==0.11.
|
38
|
+
Requires-Dist: ruff==0.11.9; extra == "test"
|
37
39
|
Requires-Dist: tox==4.25.0; extra == "test"
|
38
40
|
Requires-Dist: types-pyserial==3.5.0.20250326; extra == "test"
|
39
41
|
Provides-Extra: docs
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: PyPlumIO
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.49
|
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
|
@@ -29,11 +29,13 @@ Requires-Dist: typing-extensions==4.13.2
|
|
29
29
|
Provides-Extra: test
|
30
30
|
Requires-Dist: codespell==2.4.1; extra == "test"
|
31
31
|
Requires-Dist: coverage==7.8.0; extra == "test"
|
32
|
+
Requires-Dist: freezegun==1.5.1; extra == "test"
|
32
33
|
Requires-Dist: mypy==1.15.0; extra == "test"
|
34
|
+
Requires-Dist: numpy<3.0.0,>=2.0.0; extra == "test"
|
33
35
|
Requires-Dist: pyserial-asyncio-fast==0.16; extra == "test"
|
34
36
|
Requires-Dist: pytest==8.3.5; extra == "test"
|
35
37
|
Requires-Dist: pytest-asyncio==0.26.0; extra == "test"
|
36
|
-
Requires-Dist: ruff==0.11.
|
38
|
+
Requires-Dist: ruff==0.11.9; extra == "test"
|
37
39
|
Requires-Dist: tox==4.25.0; extra == "test"
|
38
40
|
Requires-Dist: types-pyserial==3.5.0.20250326; extra == "test"
|
39
41
|
Provides-Extra: docs
|
@@ -42,6 +42,7 @@ pyplumio/__main__.py
|
|
42
42
|
pyplumio/_version.py
|
43
43
|
pyplumio/connection.py
|
44
44
|
pyplumio/const.py
|
45
|
+
pyplumio/data_types.py
|
45
46
|
pyplumio/exceptions.py
|
46
47
|
pyplumio/filters.py
|
47
48
|
pyplumio/protocol.py
|
@@ -58,7 +59,7 @@ pyplumio/frames/messages.py
|
|
58
59
|
pyplumio/frames/requests.py
|
59
60
|
pyplumio/frames/responses.py
|
60
61
|
pyplumio/helpers/__init__.py
|
61
|
-
pyplumio/helpers/
|
62
|
+
pyplumio/helpers/async_cache.py
|
62
63
|
pyplumio/helpers/event_manager.py
|
63
64
|
pyplumio/helpers/factory.py
|
64
65
|
pyplumio/helpers/schedule.py
|
@@ -69,6 +70,8 @@ pyplumio/parameters/__init__.py
|
|
69
70
|
pyplumio/parameters/ecomax.py
|
70
71
|
pyplumio/parameters/mixer.py
|
71
72
|
pyplumio/parameters/thermostat.py
|
73
|
+
pyplumio/parameters/custom/__init__.py
|
74
|
+
pyplumio/parameters/custom/ecomax_860d3_hb.py
|
72
75
|
pyplumio/structures/__init__.py
|
73
76
|
pyplumio/structures/alerts.py
|
74
77
|
pyplumio/structures/boiler_load.py
|
@@ -99,19 +102,25 @@ tests/__init__.py
|
|
99
102
|
tests/conftest.py
|
100
103
|
tests/ruff.toml
|
101
104
|
tests/test_connection.py
|
102
|
-
tests/
|
105
|
+
tests/test_data_types.py
|
103
106
|
tests/test_filters.py
|
104
107
|
tests/test_init.py
|
105
108
|
tests/test_main.py
|
106
109
|
tests/test_protocol.py
|
107
110
|
tests/test_stream.py
|
108
111
|
tests/test_utils.py
|
112
|
+
tests/devices/__init__.py
|
113
|
+
tests/devices/test_ecomax.py
|
114
|
+
tests/devices/test_ecoster.py
|
115
|
+
tests/devices/test_init.py
|
116
|
+
tests/devices/test_mixer.py
|
117
|
+
tests/devices/test_thermostat.py
|
109
118
|
tests/frames/test_init.py
|
110
119
|
tests/frames/test_messages.py
|
111
120
|
tests/frames/test_requests.py
|
112
121
|
tests/frames/test_responses.py
|
113
122
|
tests/helpers/__init__.py
|
114
|
-
tests/helpers/
|
123
|
+
tests/helpers/test_async_cache.py
|
115
124
|
tests/helpers/test_event_manager.py
|
116
125
|
tests/helpers/test_factory.py
|
117
126
|
tests/helpers/test_schedule.py
|
@@ -123,8 +132,12 @@ tests/parameters/test_ecomax.py
|
|
123
132
|
tests/parameters/test_init.py
|
124
133
|
tests/parameters/test_mixers.py
|
125
134
|
tests/parameters/test_thermostats.py
|
135
|
+
tests/parameters/custom/__init__.py
|
136
|
+
tests/parameters/custom/test_ecomax_860d3_hb.py
|
137
|
+
tests/parameters/custom/test_init.py
|
126
138
|
tests/testdata/messages/regulator_data.json
|
127
139
|
tests/testdata/messages/sensor_data.json
|
140
|
+
tests/testdata/parameters/ecomax_860d3_hb.json
|
128
141
|
tests/testdata/requests/alerts.json
|
129
142
|
tests/testdata/requests/ecomax_control.json
|
130
143
|
tests/testdata/requests/ecomax_parameters.json
|
@@ -15,10 +15,12 @@ readthedocs-sphinx-search==0.3.2
|
|
15
15
|
[test]
|
16
16
|
codespell==2.4.1
|
17
17
|
coverage==7.8.0
|
18
|
+
freezegun==1.5.1
|
18
19
|
mypy==1.15.0
|
20
|
+
numpy<3.0.0,>=2.0.0
|
19
21
|
pyserial-asyncio-fast==0.16
|
20
22
|
pytest==8.3.5
|
21
23
|
pytest-asyncio==0.26.0
|
22
|
-
ruff==0.11.
|
24
|
+
ruff==0.11.9
|
23
25
|
tox==4.25.0
|
24
26
|
types-pyserial==3.5.0.20250326
|
@@ -38,15 +38,18 @@ All built-in filters are described below.
|
|
38
38
|
|
39
39
|
.. autofunction:: pyplumio.filters.aggregate
|
40
40
|
|
41
|
-
This filter aggregates
|
42
|
-
then calls the callback with the
|
41
|
+
This filter aggregates values for specified amount of seconds or until
|
42
|
+
certain sample size is reached and then calls the callback with the
|
43
|
+
sum of values collected.
|
43
44
|
|
44
45
|
.. code-block:: python
|
45
46
|
|
46
47
|
from pyplumio import filters
|
47
48
|
|
48
49
|
# Await the callback with the fuel burned during 30 seconds.
|
49
|
-
ecomax.subscribe(
|
50
|
+
ecomax.subscribe(
|
51
|
+
"fuel_burned", filters.aggregate(my_callback, seconds=30, sample_size=100)
|
52
|
+
)
|
50
53
|
|
51
54
|
.. autofunction:: pyplumio.filters.clamp
|
52
55
|
|
@@ -76,6 +79,20 @@ value is changed.
|
|
76
79
|
# last call.
|
77
80
|
ecomax.subscribe("heating_temp", filters.on_change(my_callback))
|
78
81
|
|
82
|
+
.. autofunction:: pyplumio.filters.deadband
|
83
|
+
|
84
|
+
This filter await the callback on signifacant changes only.
|
85
|
+
Significance is defined by ``tolerance`` argument (i. e. tolerance=0.1
|
86
|
+
will only await callback when value is changed by more that 0.1).
|
87
|
+
|
88
|
+
.. code-block:: python
|
89
|
+
|
90
|
+
from pyplumio import filters
|
91
|
+
|
92
|
+
# Await the callback once heating_temp value is changed by more
|
93
|
+
# than 0.1 since last call.
|
94
|
+
ecomax.subscribe("heating_temp", filters.deadband(my_callback, tolerance=0.1))
|
95
|
+
|
79
96
|
.. autofunction:: pyplumio.filters.debounce
|
80
97
|
|
81
98
|
This filter will only await the callback once value is settled across
|
@@ -436,7 +436,7 @@ Get the regulator data schema, that describes the data type of :ref:`regulatorda
|
|
436
436
|
It's represented by dictionary, that's indexed by regulator data field id and
|
437
437
|
a member of the following DataType class, that defines the regulator data field type.
|
438
438
|
|
439
|
-
.. autoclass:: pyplumio.
|
439
|
+
.. autoclass:: pyplumio.data_types.DataType
|
440
440
|
|
441
441
|
Request
|
442
442
|
^^^^^^^
|
@@ -14,11 +14,11 @@ ATTR_CURRENT_TEMP: Final = "current_temp"
|
|
14
14
|
ATTR_DEVICE_INDEX: Final = "device_index"
|
15
15
|
ATTR_FRAME_ERRORS: Final = "frame_errors"
|
16
16
|
ATTR_INDEX: Final = "index"
|
17
|
-
ATTR_LOADED: Final = "loaded"
|
18
17
|
ATTR_OFFSET: Final = "offset"
|
19
18
|
ATTR_PARAMETER: Final = "parameter"
|
20
19
|
ATTR_PASSWORD: Final = "password"
|
21
20
|
ATTR_SCHEDULE: Final = "schedule"
|
21
|
+
ATTR_SETUP: Final = "setup"
|
22
22
|
ATTR_SENSORS: Final = "sensors"
|
23
23
|
ATTR_START: Final = "start"
|
24
24
|
ATTR_STATE: Final = "state"
|
@@ -229,6 +229,4 @@ PERCENTAGE: Final = "%"
|
|
229
229
|
|
230
230
|
STATE_ON: Final = "on"
|
231
231
|
STATE_OFF: Final = "off"
|
232
|
-
|
233
|
-
|
234
232
|
State: TypeAlias = Literal["on", "off"]
|
@@ -8,14 +8,13 @@ from functools import cache
|
|
8
8
|
import logging
|
9
9
|
from typing import Any, ClassVar
|
10
10
|
|
11
|
-
from pyplumio.const import ATTR_FRAME_ERRORS,
|
11
|
+
from pyplumio.const import ATTR_FRAME_ERRORS, DeviceType, FrameType, State
|
12
12
|
from pyplumio.exceptions import RequestError, UnknownDeviceError
|
13
13
|
from pyplumio.filters import on_change
|
14
|
-
from pyplumio.frames import
|
14
|
+
from pyplumio.frames import Frame, Request, is_known_frame_type
|
15
15
|
from pyplumio.helpers.event_manager import EventManager, event_listener
|
16
16
|
from pyplumio.helpers.factory import create_instance
|
17
17
|
from pyplumio.parameters import NumericType, Parameter
|
18
|
-
from pyplumio.structures.frame_versions import ATTR_FRAME_VERSIONS
|
19
18
|
from pyplumio.structures.network_info import NetworkInfo
|
20
19
|
from pyplumio.utils import to_camelcase
|
21
20
|
|
@@ -40,7 +39,11 @@ def get_device_handler(device_type: int) -> str:
|
|
40
39
|
|
41
40
|
type_name = to_camelcase(
|
42
41
|
DeviceType(device_type).name,
|
43
|
-
overrides={
|
42
|
+
overrides={
|
43
|
+
"ecomax": "EcoMAX",
|
44
|
+
"ecoster": "EcoSTER",
|
45
|
+
"econet": "EcoNET",
|
46
|
+
},
|
44
47
|
)
|
45
48
|
return f"devices.{type_name.lower()}.{type_name}"
|
46
49
|
|
@@ -69,7 +72,7 @@ class Device(ABC, EventManager):
|
|
69
72
|
:param name: Name of the parameter
|
70
73
|
:type name: str
|
71
74
|
:param value: New value for the parameter
|
72
|
-
:type value: int | float | bool | Literal["
|
75
|
+
:type value: int | float | bool | Literal["on", "off"]
|
73
76
|
:param retries: Try setting parameter for this amount of
|
74
77
|
times, defaults to 5
|
75
78
|
:type retries: int, optional
|
@@ -101,7 +104,7 @@ class Device(ABC, EventManager):
|
|
101
104
|
:param name: Name of the parameter
|
102
105
|
:type name: str
|
103
106
|
:param value: New value for the parameter
|
104
|
-
:type value: int | float | bool | Literal["
|
107
|
+
:type value: int | float | bool | Literal["on", "off"]
|
105
108
|
:param retries: Try setting parameter for this amount of
|
106
109
|
times, defaults to 5
|
107
110
|
:type retries: int, optional
|
@@ -126,11 +129,11 @@ class PhysicalDevice(Device, ABC):
|
|
126
129
|
virtual devices associated with them via parent property.
|
127
130
|
"""
|
128
131
|
|
129
|
-
__slots__ = ("address", "_network", "
|
132
|
+
__slots__ = ("address", "_network", "_frame_versions")
|
130
133
|
|
131
134
|
address: ClassVar[int]
|
135
|
+
|
132
136
|
_network: NetworkInfo
|
133
|
-
_setup_frames: tuple[DataFrameDescription, ...]
|
134
137
|
_frame_versions: dict[int, int]
|
135
138
|
|
136
139
|
def __init__(self, queue: asyncio.Queue[Frame], network: NetworkInfo) -> None:
|
@@ -139,20 +142,27 @@ class PhysicalDevice(Device, ABC):
|
|
139
142
|
self._network = network
|
140
143
|
self._frame_versions = {}
|
141
144
|
|
142
|
-
@event_listener(
|
145
|
+
@event_listener(filter=on_change)
|
143
146
|
async def on_event_frame_versions(self, versions: dict[int, int]) -> None:
|
144
147
|
"""Check frame versions and update outdated frames."""
|
148
|
+
_LOGGER.info("Received frame version table")
|
145
149
|
for frame_type, version in versions.items():
|
146
150
|
if (
|
147
151
|
is_known_frame_type(frame_type)
|
148
152
|
and self.supports_frame_type(frame_type)
|
149
153
|
and not self.has_frame_version(frame_type, version)
|
150
154
|
):
|
151
|
-
|
152
|
-
request = await Request.create(frame_type, recipient=self.address)
|
153
|
-
self.queue.put_nowait(request)
|
155
|
+
await self._request_frame_version(frame_type, version)
|
154
156
|
self._frame_versions[frame_type] = version
|
155
157
|
|
158
|
+
async def _request_frame_version(
|
159
|
+
self, frame_type: FrameType | int, version: int
|
160
|
+
) -> None:
|
161
|
+
"""Request frame version from the device."""
|
162
|
+
_LOGGER.info("Updating frame %s to version %i", repr(frame_type), version)
|
163
|
+
request = await Request.create(frame_type, recipient=self.address)
|
164
|
+
self.queue.put_nowait(request)
|
165
|
+
|
156
166
|
def has_frame_version(self, frame_type: FrameType | int, version: int) -> bool:
|
157
167
|
"""Return True if frame data is up to date, False otherwise."""
|
158
168
|
return (
|
@@ -171,25 +181,6 @@ class PhysicalDevice(Device, ABC):
|
|
171
181
|
for name, value in frame.data.items():
|
172
182
|
self.dispatch_nowait(name, value)
|
173
183
|
|
174
|
-
async def async_setup(self) -> bool:
|
175
|
-
"""Set up addressable device."""
|
176
|
-
results = await asyncio.gather(
|
177
|
-
*(
|
178
|
-
self.request(description.provides, description.frame_type)
|
179
|
-
for description in self._setup_frames
|
180
|
-
),
|
181
|
-
return_exceptions=True,
|
182
|
-
)
|
183
|
-
|
184
|
-
errors = [
|
185
|
-
result.frame_type for result in results if isinstance(result, RequestError)
|
186
|
-
]
|
187
|
-
|
188
|
-
await asyncio.gather(
|
189
|
-
self.dispatch(ATTR_FRAME_ERRORS, errors), self.dispatch(ATTR_LOADED, True)
|
190
|
-
)
|
191
|
-
return True
|
192
|
-
|
193
184
|
async def request(
|
194
185
|
self, name: str, frame_type: FrameType, retries: int = 3, timeout: float = 3.0
|
195
186
|
) -> Any:
|
@@ -197,6 +188,7 @@ class PhysicalDevice(Device, ABC):
|
|
197
188
|
|
198
189
|
If value is not available before timeout, retry request.
|
199
190
|
"""
|
191
|
+
_LOGGER.info("Requesting '%s' with %s", name, repr(frame_type))
|
200
192
|
request = await Request.create(frame_type, recipient=self.address)
|
201
193
|
while retries > 0:
|
202
194
|
try:
|
@@ -3,15 +3,16 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import asyncio
|
6
|
-
from collections.abc import Coroutine, Generator, Iterable
|
6
|
+
from collections.abc import Coroutine, Generator, Iterable
|
7
7
|
import logging
|
8
8
|
import time
|
9
9
|
from typing import Any, Final
|
10
10
|
|
11
11
|
from pyplumio.const import (
|
12
|
+
ATTR_FRAME_ERRORS,
|
12
13
|
ATTR_PASSWORD,
|
13
14
|
ATTR_SENSORS,
|
14
|
-
|
15
|
+
ATTR_SETUP,
|
15
16
|
STATE_OFF,
|
16
17
|
STATE_ON,
|
17
18
|
DeviceState,
|
@@ -22,6 +23,7 @@ from pyplumio.const import (
|
|
22
23
|
from pyplumio.devices import PhysicalDevice
|
23
24
|
from pyplumio.devices.mixer import Mixer
|
24
25
|
from pyplumio.devices.thermostat import Thermostat
|
26
|
+
from pyplumio.exceptions import RequestError
|
25
27
|
from pyplumio.filters import on_change
|
26
28
|
from pyplumio.frames import DataFrameDescription, Frame, Request
|
27
29
|
from pyplumio.helpers.event_manager import event_listener
|
@@ -40,14 +42,12 @@ from pyplumio.structures.ecomax_parameters import (
|
|
40
42
|
ATTR_ECOMAX_CONTROL,
|
41
43
|
ATTR_ECOMAX_PARAMETERS,
|
42
44
|
)
|
43
|
-
from pyplumio.structures.fuel_consumption import ATTR_FUEL_CONSUMPTION
|
44
45
|
from pyplumio.structures.mixer_parameters import ATTR_MIXER_PARAMETERS
|
45
46
|
from pyplumio.structures.mixer_sensors import ATTR_MIXER_SENSORS
|
46
47
|
from pyplumio.structures.network_info import ATTR_NETWORK, NetworkInfo
|
47
48
|
from pyplumio.structures.product_info import ATTR_PRODUCT, ProductInfo
|
48
49
|
from pyplumio.structures.regulator_data_schema import ATTR_REGDATA_SCHEMA
|
49
50
|
from pyplumio.structures.schedules import (
|
50
|
-
ATTR_SCHEDULE_PARAMETERS,
|
51
51
|
ATTR_SCHEDULES,
|
52
52
|
SCHEDULE_PARAMETERS,
|
53
53
|
SCHEDULES,
|
@@ -55,20 +55,53 @@ from pyplumio.structures.schedules import (
|
|
55
55
|
ScheduleSwitch,
|
56
56
|
ScheduleSwitchDescription,
|
57
57
|
)
|
58
|
-
from pyplumio.structures.thermostat_parameters import
|
59
|
-
ATTR_THERMOSTAT_PARAMETERS,
|
60
|
-
ATTR_THERMOSTAT_PROFILE,
|
61
|
-
)
|
58
|
+
from pyplumio.structures.thermostat_parameters import ATTR_THERMOSTAT_PARAMETERS
|
62
59
|
from pyplumio.structures.thermostat_sensors import ATTR_THERMOSTAT_SENSORS
|
63
60
|
|
61
|
+
_LOGGER = logging.getLogger(__name__)
|
62
|
+
|
63
|
+
|
64
64
|
ATTR_MIXERS: Final = "mixers"
|
65
65
|
ATTR_THERMOSTATS: Final = "thermostats"
|
66
66
|
ATTR_FUEL_BURNED: Final = "fuel_burned"
|
67
67
|
|
68
|
-
|
69
|
-
|
68
|
+
MAX_TIME_SINCE_LAST_FUEL_UPDATE: Final = 5 * 60
|
69
|
+
|
70
|
+
|
71
|
+
class FuelMeter:
|
72
|
+
"""Represents a fuel meter.
|
73
|
+
|
74
|
+
Calculates the fuel burned based on the time
|
75
|
+
elapsed since the last sensor message, which contains fuel
|
76
|
+
consumption data. If the elapsed time is within the acceptable
|
77
|
+
range, it returns the fuel burned data. Otherwise, it logs a
|
78
|
+
warning and returns None.
|
79
|
+
"""
|
80
|
+
|
81
|
+
__slots__ = ("_last_update_time",)
|
82
|
+
|
83
|
+
_last_update_time: float
|
70
84
|
|
71
|
-
|
85
|
+
def __init__(self) -> None:
|
86
|
+
"""Initialize a new fuel meter."""
|
87
|
+
self._last_update_time = time.monotonic()
|
88
|
+
|
89
|
+
def calculate(self, fuel_consumption: float) -> float | None:
|
90
|
+
"""Calculate the amount of burned fuel since last update."""
|
91
|
+
current_time = time.monotonic()
|
92
|
+
time_since_update = current_time - self._last_update_time
|
93
|
+
self._last_update_time = current_time
|
94
|
+
if time_since_update < MAX_TIME_SINCE_LAST_FUEL_UPDATE:
|
95
|
+
return fuel_consumption * (time_since_update / 3600)
|
96
|
+
|
97
|
+
_LOGGER.warning(
|
98
|
+
"Skipping outdated fuel consumption data (was %f seconds old)",
|
99
|
+
time_since_update,
|
100
|
+
)
|
101
|
+
return None
|
102
|
+
|
103
|
+
|
104
|
+
REQUIRED: tuple[DataFrameDescription, ...] = (
|
72
105
|
DataFrameDescription(
|
73
106
|
frame_type=FrameType.REQUEST_UID,
|
74
107
|
provides=ATTR_PRODUCT,
|
@@ -103,28 +136,22 @@ SETUP_FRAME_TYPES: tuple[DataFrameDescription, ...] = (
|
|
103
136
|
),
|
104
137
|
)
|
105
138
|
|
106
|
-
|
139
|
+
REQUIRED_TYPES = [description.frame_type for description in REQUIRED]
|
107
140
|
|
108
141
|
|
109
142
|
class EcoMAX(PhysicalDevice):
|
110
143
|
"""Represents an ecoMAX controller."""
|
111
144
|
|
112
|
-
__slots__ = ("
|
145
|
+
__slots__ = ("_fuel_meter",)
|
113
146
|
|
114
|
-
|
115
|
-
_setup_frames = SETUP_FRAME_TYPES
|
147
|
+
_fuel_meter: FuelMeter
|
116
148
|
|
117
|
-
|
149
|
+
address = DeviceType.ECOMAX
|
118
150
|
|
119
151
|
def __init__(self, queue: asyncio.Queue[Frame], network: NetworkInfo) -> None:
|
120
152
|
"""Initialize a new ecoMAX controller."""
|
121
153
|
super().__init__(queue, network)
|
122
|
-
self.
|
123
|
-
|
124
|
-
async def async_setup(self) -> bool:
|
125
|
-
"""Set up an ecoMAX controller."""
|
126
|
-
await self.wait_for(ATTR_SENSORS)
|
127
|
-
return await super().async_setup()
|
154
|
+
self._fuel_meter = FuelMeter()
|
128
155
|
|
129
156
|
def handle_frame(self, frame: Frame) -> None:
|
130
157
|
"""Handle frame received from the ecoMAX device."""
|
@@ -162,6 +189,14 @@ class EcoMAX(PhysicalDevice):
|
|
162
189
|
|
163
190
|
return self.dispatch_nowait(ATTR_THERMOSTATS, thermostats)
|
164
191
|
|
192
|
+
async def _request_frame_version(
|
193
|
+
self, frame_type: FrameType | int, version: int
|
194
|
+
) -> None:
|
195
|
+
"""Request frame version from the device."""
|
196
|
+
setup_done = self.get_nowait(ATTR_SETUP, False)
|
197
|
+
if setup_done or frame_type not in REQUIRED_TYPES:
|
198
|
+
await super()._request_frame_version(frame_type, version)
|
199
|
+
|
165
200
|
async def _set_ecomax_state(self, state: State) -> bool:
|
166
201
|
"""Try to set the ecoMAX control state."""
|
167
202
|
try:
|
@@ -196,16 +231,39 @@ class EcoMAX(PhysicalDevice):
|
|
196
231
|
await asyncio.gather(*(device.shutdown() for device in devices))
|
197
232
|
await super().shutdown()
|
198
233
|
|
199
|
-
@event_listener
|
234
|
+
@event_listener
|
235
|
+
async def on_event_setup(self, setup: bool) -> None:
|
236
|
+
"""Request frames required to set up an ecoMAX entry."""
|
237
|
+
_LOGGER.info("Setting up device entry")
|
238
|
+
await self.wait_for(ATTR_SENSORS)
|
239
|
+
results = await asyncio.gather(
|
240
|
+
*(
|
241
|
+
self.request(description.provides, description.frame_type)
|
242
|
+
for description in REQUIRED
|
243
|
+
),
|
244
|
+
return_exceptions=True,
|
245
|
+
)
|
246
|
+
|
247
|
+
errors = [
|
248
|
+
result.frame_type for result in results if isinstance(result, RequestError)
|
249
|
+
]
|
250
|
+
|
251
|
+
if errors:
|
252
|
+
self.dispatch_nowait(ATTR_FRAME_ERRORS, errors)
|
253
|
+
|
254
|
+
_LOGGER.info("Device entry setup done")
|
255
|
+
|
256
|
+
@event_listener
|
200
257
|
async def on_event_ecomax_parameters(
|
201
|
-
self, parameters:
|
258
|
+
self, parameters: list[tuple[int, ParameterValues]]
|
202
259
|
) -> bool:
|
203
260
|
"""Update ecoMAX parameters and dispatch the events."""
|
261
|
+
_LOGGER.info("Received device parameters")
|
204
262
|
product_info: ProductInfo = await self.get(ATTR_PRODUCT)
|
263
|
+
parameter_types = await get_ecomax_parameter_types(product_info)
|
205
264
|
|
206
|
-
def _ecomax_parameter_events() -> Generator[Coroutine
|
265
|
+
def _ecomax_parameter_events() -> Generator[Coroutine]:
|
207
266
|
"""Get dispatch calls for ecoMAX parameter events."""
|
208
|
-
parameter_types = get_ecomax_parameter_types(product_info)
|
209
267
|
for index, values in parameters:
|
210
268
|
try:
|
211
269
|
description = parameter_types[index]
|
@@ -236,37 +294,20 @@ class EcoMAX(PhysicalDevice):
|
|
236
294
|
await asyncio.gather(*_ecomax_parameter_events())
|
237
295
|
return True
|
238
296
|
|
239
|
-
@event_listener
|
297
|
+
@event_listener
|
240
298
|
async def on_event_fuel_consumption(self, fuel_consumption: float) -> None:
|
241
|
-
"""Update the amount of burned fuel.
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
consumption data. If the elapsed time is within the acceptable
|
246
|
-
range, it dispatches the fuel burned data. Otherwise, it logs a
|
247
|
-
warning and skips the outdated data.
|
248
|
-
"""
|
249
|
-
time_ns = time.perf_counter_ns()
|
250
|
-
nanoseconds_passed = time_ns - self._fuel_burned_time_ns
|
251
|
-
self._fuel_burned_time_ns = time_ns
|
252
|
-
if nanoseconds_passed < MAX_TIME_SINCE_LAST_FUEL_UPDATE_NS:
|
253
|
-
return self.dispatch_nowait(
|
254
|
-
ATTR_FUEL_BURNED,
|
255
|
-
fuel_consumption * nanoseconds_passed / (3600 * NANOSECONDS_IN_SECOND),
|
256
|
-
)
|
257
|
-
|
258
|
-
_LOGGER.warning(
|
259
|
-
"Skipping outdated fuel consumption data: %f (was %i seconds old)",
|
260
|
-
fuel_consumption,
|
261
|
-
nanoseconds_passed / NANOSECONDS_IN_SECOND,
|
262
|
-
)
|
299
|
+
"""Update the amount of burned fuel."""
|
300
|
+
fuel_burned = self._fuel_meter.calculate(fuel_consumption)
|
301
|
+
if fuel_burned is not None:
|
302
|
+
self.dispatch_nowait(ATTR_FUEL_BURNED, fuel_burned)
|
263
303
|
|
264
|
-
@event_listener
|
304
|
+
@event_listener
|
265
305
|
async def on_event_mixer_parameters(
|
266
306
|
self,
|
267
|
-
parameters: dict[int,
|
307
|
+
parameters: dict[int, list[tuple[int, ParameterValues]]] | None,
|
268
308
|
) -> bool:
|
269
309
|
"""Handle mixer parameters and dispatch the events."""
|
310
|
+
_LOGGER.info("Received mixer parameters")
|
270
311
|
if parameters:
|
271
312
|
await asyncio.gather(
|
272
313
|
*(
|
@@ -278,11 +319,12 @@ class EcoMAX(PhysicalDevice):
|
|
278
319
|
|
279
320
|
return False
|
280
321
|
|
281
|
-
@event_listener
|
322
|
+
@event_listener
|
282
323
|
async def on_event_mixer_sensors(
|
283
324
|
self, sensors: dict[int, dict[str, Any]] | None
|
284
325
|
) -> bool:
|
285
326
|
"""Update mixer sensors and dispatch the events."""
|
327
|
+
_LOGGER.info("Received mixer sensors")
|
286
328
|
if sensors:
|
287
329
|
await asyncio.gather(
|
288
330
|
*(
|
@@ -294,9 +336,9 @@ class EcoMAX(PhysicalDevice):
|
|
294
336
|
|
295
337
|
return False
|
296
338
|
|
297
|
-
@event_listener
|
339
|
+
@event_listener
|
298
340
|
async def on_event_schedule_parameters(
|
299
|
-
self, parameters:
|
341
|
+
self, parameters: list[tuple[int, ParameterValues]]
|
300
342
|
) -> bool:
|
301
343
|
"""Update schedule parameters and dispatch the events."""
|
302
344
|
|
@@ -319,20 +361,22 @@ class EcoMAX(PhysicalDevice):
|
|
319
361
|
await asyncio.gather(*_schedule_parameter_events())
|
320
362
|
return True
|
321
363
|
|
322
|
-
@event_listener
|
364
|
+
@event_listener
|
323
365
|
async def on_event_sensors(self, sensors: dict[str, Any]) -> bool:
|
324
366
|
"""Update ecoMAX sensors and dispatch the events."""
|
367
|
+
_LOGGER.info("Received device sensors")
|
325
368
|
await asyncio.gather(
|
326
369
|
*(self.dispatch(name, value) for name, value in sensors.items())
|
327
370
|
)
|
328
371
|
return True
|
329
372
|
|
330
|
-
@event_listener
|
373
|
+
@event_listener
|
331
374
|
async def on_event_thermostat_parameters(
|
332
375
|
self,
|
333
|
-
parameters: dict[int,
|
376
|
+
parameters: dict[int, list[tuple[int, ParameterValues]]] | None,
|
334
377
|
) -> bool:
|
335
378
|
"""Handle thermostat parameters and dispatch the events."""
|
379
|
+
_LOGGER.info("Received thermostat parameters")
|
336
380
|
if parameters:
|
337
381
|
await asyncio.gather(
|
338
382
|
*(
|
@@ -346,7 +390,7 @@ class EcoMAX(PhysicalDevice):
|
|
346
390
|
|
347
391
|
return False
|
348
392
|
|
349
|
-
@event_listener
|
393
|
+
@event_listener
|
350
394
|
async def on_event_thermostat_profile(
|
351
395
|
self, values: ParameterValues | None
|
352
396
|
) -> EcomaxNumber | None:
|
@@ -358,11 +402,12 @@ class EcoMAX(PhysicalDevice):
|
|
358
402
|
|
359
403
|
return None
|
360
404
|
|
361
|
-
@event_listener
|
405
|
+
@event_listener
|
362
406
|
async def on_event_thermostat_sensors(
|
363
407
|
self, sensors: dict[int, dict[str, Any]] | None
|
364
408
|
) -> bool:
|
365
409
|
"""Update thermostat sensors and dispatch the events."""
|
410
|
+
_LOGGER.info("Received thermostat sensors")
|
366
411
|
if sensors:
|
367
412
|
await asyncio.gather(
|
368
413
|
*(
|
@@ -377,11 +422,12 @@ class EcoMAX(PhysicalDevice):
|
|
377
422
|
|
378
423
|
return False
|
379
424
|
|
380
|
-
@event_listener
|
425
|
+
@event_listener
|
381
426
|
async def on_event_schedules(
|
382
427
|
self, schedules: list[tuple[int, list[list[bool]]]]
|
383
428
|
) -> dict[str, Schedule]:
|
384
429
|
"""Update schedules."""
|
430
|
+
_LOGGER.info("Received device schedules")
|
385
431
|
return {
|
386
432
|
SCHEDULES[index]: Schedule(
|
387
433
|
name=SCHEDULES[index],
|
@@ -397,7 +443,7 @@ class EcoMAX(PhysicalDevice):
|
|
397
443
|
for index, schedule in schedules
|
398
444
|
}
|
399
445
|
|
400
|
-
@event_listener(
|
446
|
+
@event_listener(filter=on_change)
|
401
447
|
async def on_event_state(self, state: DeviceState) -> None:
|
402
448
|
"""Update the ecoMAX control parameter."""
|
403
449
|
await self.dispatch(
|