PyPlumIO 0.5.43__tar.gz → 0.5.44__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.44}/PKG-INFO +3 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/PyPlumIO.egg-info/PKG-INFO +3 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/PyPlumIO.egg-info/SOURCES.txt +4 -2
- {pyplumio-0.5.43 → pyplumio-0.5.44}/PyPlumIO.egg-info/requires.txt +2 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/source/callbacks.rst +6 -3
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/source/frames.rst +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/_version.py +2 -2
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/const.py +1 -3
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/devices/__init__.py +16 -28
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/devices/ecomax.py +103 -59
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/devices/mixer.py +6 -6
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/devices/thermostat.py +9 -6
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/filters.py +44 -22
- pyplumio-0.5.44/pyplumio/helpers/async_cache.py +48 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/helpers/event_manager.py +20 -2
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/helpers/timeout.py +6 -5
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/parameters/__init__.py +9 -3
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/protocol.py +15 -7
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/alerts.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/boiler_power.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/fan_power.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/frame_versions.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/fuel_consumption.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/lambda_sensor.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/mixer_sensors.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/network_info.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/output_flags.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/outputs.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/product_info.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/regulator_data.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/regulator_data_schema.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/temperatures.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/thermostat_parameters.py +5 -7
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/thermostat_sensors.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyproject.toml +2 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/requirements_test.txt +2 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/conftest.py +8 -0
- pyplumio-0.5.44/tests/helpers/test_async_cache.py +49 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/helpers/test_event_manager.py +17 -2
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/parameters/test_init.py +9 -2
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/test_connection.py +5 -1
- {pyplumio-0.5.43/tests/helpers → pyplumio-0.5.44/tests}/test_data_types.py +1 -1
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/test_devices.py +23 -17
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/test_filters.py +63 -11
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/test_protocol.py +9 -2
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/responses/regulator_data_schema.json +44 -44
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.gitattributes +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.github/CODE_OF_CONDUCT.md +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.github/dependabot.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.github/workflows/ci.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.github/workflows/codeql.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.github/workflows/deploy.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.github/workflows/documentation.yml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.gitignore +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.pre-commit-config.yaml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.qlty/qlty.toml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/.vscode/settings.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/LICENSE +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/MANIFEST.in +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/PyPlumIO.egg-info/dependency_links.txt +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/PyPlumIO.egg-info/top_level.txt +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/README.md +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/Makefile +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/make.bat +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/source/conf.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/source/connecting.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/source/index.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/source/mixers_thermostats.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/source/protocol.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/source/reading.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/source/schedules.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/docs/source/writing.rst +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/images/ecomax.png +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/images/rs485.png +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/__main__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/connection.py +0 -0
- {pyplumio-0.5.43/pyplumio/helpers → pyplumio-0.5.44/pyplumio}/data_types.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/devices/ecoster.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/exceptions.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/frames/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/frames/messages.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/frames/requests.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/frames/responses.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/helpers/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/helpers/factory.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/helpers/schedule.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/helpers/task_manager.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/helpers/uid.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/parameters/ecomax.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/parameters/mixer.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/parameters/thermostat.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/py.typed +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/stream.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/boiler_load.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/ecomax_parameters.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/fuel_level.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/mixer_parameters.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/modules.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/pending_alerts.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/program_version.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/schedules.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/structures/statuses.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/pyplumio/utils.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/requirements.txt +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/requirements_docs.txt +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/setup.cfg +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/frames/test_init.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/frames/test_messages.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/frames/test_requests.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/frames/test_responses.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/helpers/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/helpers/test_factory.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/helpers/test_schedule.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/helpers/test_task_manager.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/helpers/test_timeout.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/helpers/test_uid.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/parameters/__init__.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/parameters/test_ecomax.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/parameters/test_mixers.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/parameters/test_thermostats.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/ruff.toml +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/test_init.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/test_main.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/test_stream.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/test_utils.py +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/messages/regulator_data.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/messages/sensor_data.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/requests/alerts.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/requests/ecomax_control.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/requests/ecomax_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/requests/mixer_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/requests/set_ecomax_parameter.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/requests/set_mixer_parameter.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/requests/set_schedule.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/requests/set_thermostat_parameter.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/requests/thermostat_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/responses/alerts.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/responses/device_available.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/responses/ecomax_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/responses/mixer_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/responses/password.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/responses/program_version.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/responses/schedules.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/responses/thermostat_parameters.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/responses/uid.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/unknown/unknown_ecomax_parameter.json +0 -0
- {pyplumio-0.5.43 → pyplumio-0.5.44}/tests/testdata/unknown/unknown_mixer_parameter.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.44
|
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,7 +29,9 @@ 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"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: PyPlumIO
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.44
|
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,7 +29,9 @@ 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"
|
@@ -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
|
@@ -99,6 +100,7 @@ tests/__init__.py
|
|
99
100
|
tests/conftest.py
|
100
101
|
tests/ruff.toml
|
101
102
|
tests/test_connection.py
|
103
|
+
tests/test_data_types.py
|
102
104
|
tests/test_devices.py
|
103
105
|
tests/test_filters.py
|
104
106
|
tests/test_init.py
|
@@ -111,7 +113,7 @@ tests/frames/test_messages.py
|
|
111
113
|
tests/frames/test_requests.py
|
112
114
|
tests/frames/test_responses.py
|
113
115
|
tests/helpers/__init__.py
|
114
|
-
tests/helpers/
|
116
|
+
tests/helpers/test_async_cache.py
|
115
117
|
tests/helpers/test_event_manager.py
|
116
118
|
tests/helpers/test_factory.py
|
117
119
|
tests/helpers/test_schedule.py
|
@@ -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
|
|
@@ -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
|
|
@@ -126,11 +125,11 @@ class PhysicalDevice(Device, ABC):
|
|
126
125
|
virtual devices associated with them via parent property.
|
127
126
|
"""
|
128
127
|
|
129
|
-
__slots__ = ("address", "_network", "
|
128
|
+
__slots__ = ("address", "_network", "_frame_versions")
|
130
129
|
|
131
130
|
address: ClassVar[int]
|
131
|
+
|
132
132
|
_network: NetworkInfo
|
133
|
-
_setup_frames: tuple[DataFrameDescription, ...]
|
134
133
|
_frame_versions: dict[int, int]
|
135
134
|
|
136
135
|
def __init__(self, queue: asyncio.Queue[Frame], network: NetworkInfo) -> None:
|
@@ -139,20 +138,27 @@ class PhysicalDevice(Device, ABC):
|
|
139
138
|
self._network = network
|
140
139
|
self._frame_versions = {}
|
141
140
|
|
142
|
-
@event_listener(
|
141
|
+
@event_listener(filter=on_change)
|
143
142
|
async def on_event_frame_versions(self, versions: dict[int, int]) -> None:
|
144
143
|
"""Check frame versions and update outdated frames."""
|
144
|
+
_LOGGER.info("Received version table")
|
145
145
|
for frame_type, version in versions.items():
|
146
146
|
if (
|
147
147
|
is_known_frame_type(frame_type)
|
148
148
|
and self.supports_frame_type(frame_type)
|
149
149
|
and not self.has_frame_version(frame_type, version)
|
150
150
|
):
|
151
|
-
|
152
|
-
request = await Request.create(frame_type, recipient=self.address)
|
153
|
-
self.queue.put_nowait(request)
|
151
|
+
await self._request_frame_version(frame_type, version)
|
154
152
|
self._frame_versions[frame_type] = version
|
155
153
|
|
154
|
+
async def _request_frame_version(
|
155
|
+
self, frame_type: FrameType | int, version: int
|
156
|
+
) -> None:
|
157
|
+
"""Request frame version from the device."""
|
158
|
+
_LOGGER.info("Updating frame %s to version %i", repr(frame_type), version)
|
159
|
+
request = await Request.create(frame_type, recipient=self.address)
|
160
|
+
self.queue.put_nowait(request)
|
161
|
+
|
156
162
|
def has_frame_version(self, frame_type: FrameType | int, version: int) -> bool:
|
157
163
|
"""Return True if frame data is up to date, False otherwise."""
|
158
164
|
return (
|
@@ -171,25 +177,6 @@ class PhysicalDevice(Device, ABC):
|
|
171
177
|
for name, value in frame.data.items():
|
172
178
|
self.dispatch_nowait(name, value)
|
173
179
|
|
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
180
|
async def request(
|
194
181
|
self, name: str, frame_type: FrameType, retries: int = 3, timeout: float = 3.0
|
195
182
|
) -> Any:
|
@@ -197,6 +184,7 @@ class PhysicalDevice(Device, ABC):
|
|
197
184
|
|
198
185
|
If value is not available before timeout, retry request.
|
199
186
|
"""
|
187
|
+
_LOGGER.info("Requesting '%s' with %s", name, repr(frame_type))
|
200
188
|
request = await Request.create(frame_type, recipient=self.address)
|
201
189
|
while retries > 0:
|
202
190
|
try:
|
@@ -3,15 +3,15 @@
|
|
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
|
-
ATTR_STATE,
|
15
15
|
STATE_OFF,
|
16
16
|
STATE_ON,
|
17
17
|
DeviceState,
|
@@ -22,6 +22,7 @@ from pyplumio.const import (
|
|
22
22
|
from pyplumio.devices import PhysicalDevice
|
23
23
|
from pyplumio.devices.mixer import Mixer
|
24
24
|
from pyplumio.devices.thermostat import Thermostat
|
25
|
+
from pyplumio.exceptions import RequestError
|
25
26
|
from pyplumio.filters import on_change
|
26
27
|
from pyplumio.frames import DataFrameDescription, Frame, Request
|
27
28
|
from pyplumio.helpers.event_manager import event_listener
|
@@ -40,14 +41,12 @@ from pyplumio.structures.ecomax_parameters import (
|
|
40
41
|
ATTR_ECOMAX_CONTROL,
|
41
42
|
ATTR_ECOMAX_PARAMETERS,
|
42
43
|
)
|
43
|
-
from pyplumio.structures.fuel_consumption import ATTR_FUEL_CONSUMPTION
|
44
44
|
from pyplumio.structures.mixer_parameters import ATTR_MIXER_PARAMETERS
|
45
45
|
from pyplumio.structures.mixer_sensors import ATTR_MIXER_SENSORS
|
46
46
|
from pyplumio.structures.network_info import ATTR_NETWORK, NetworkInfo
|
47
47
|
from pyplumio.structures.product_info import ATTR_PRODUCT, ProductInfo
|
48
48
|
from pyplumio.structures.regulator_data_schema import ATTR_REGDATA_SCHEMA
|
49
49
|
from pyplumio.structures.schedules import (
|
50
|
-
ATTR_SCHEDULE_PARAMETERS,
|
51
50
|
ATTR_SCHEDULES,
|
52
51
|
SCHEDULE_PARAMETERS,
|
53
52
|
SCHEDULES,
|
@@ -55,20 +54,53 @@ from pyplumio.structures.schedules import (
|
|
55
54
|
ScheduleSwitch,
|
56
55
|
ScheduleSwitchDescription,
|
57
56
|
)
|
58
|
-
from pyplumio.structures.thermostat_parameters import
|
59
|
-
ATTR_THERMOSTAT_PARAMETERS,
|
60
|
-
ATTR_THERMOSTAT_PROFILE,
|
61
|
-
)
|
57
|
+
from pyplumio.structures.thermostat_parameters import ATTR_THERMOSTAT_PARAMETERS
|
62
58
|
from pyplumio.structures.thermostat_sensors import ATTR_THERMOSTAT_SENSORS
|
63
59
|
|
60
|
+
_LOGGER = logging.getLogger(__name__)
|
61
|
+
|
62
|
+
|
64
63
|
ATTR_MIXERS: Final = "mixers"
|
65
64
|
ATTR_THERMOSTATS: Final = "thermostats"
|
66
65
|
ATTR_FUEL_BURNED: Final = "fuel_burned"
|
67
66
|
|
68
|
-
|
69
|
-
|
67
|
+
MAX_TIME_SINCE_LAST_FUEL_UPDATE: Final = 5 * 60
|
68
|
+
|
69
|
+
|
70
|
+
class FuelMeter:
|
71
|
+
"""Represents a fuel meter.
|
72
|
+
|
73
|
+
Calculates the fuel burned based on the time
|
74
|
+
elapsed since the last sensor message, which contains fuel
|
75
|
+
consumption data. If the elapsed time is within the acceptable
|
76
|
+
range, it returns the fuel burned data. Otherwise, it logs a
|
77
|
+
warning and returns None.
|
78
|
+
"""
|
79
|
+
|
80
|
+
__slots__ = ("_last_update_time",)
|
81
|
+
|
82
|
+
_last_update_time: float
|
70
83
|
|
71
|
-
|
84
|
+
def __init__(self) -> None:
|
85
|
+
"""Initialize a new fuel meter."""
|
86
|
+
self._last_update_time = time.monotonic()
|
87
|
+
|
88
|
+
def calculate(self, fuel_consumption: float) -> float | None:
|
89
|
+
"""Calculate the amount of burned fuel since last update."""
|
90
|
+
current_time = time.monotonic()
|
91
|
+
time_since_update = current_time - self._last_update_time
|
92
|
+
self._last_update_time = current_time
|
93
|
+
if time_since_update < MAX_TIME_SINCE_LAST_FUEL_UPDATE:
|
94
|
+
return fuel_consumption * (time_since_update / 3600)
|
95
|
+
|
96
|
+
_LOGGER.warning(
|
97
|
+
"Skipping outdated fuel consumption data (was %f seconds old)",
|
98
|
+
time_since_update,
|
99
|
+
)
|
100
|
+
return None
|
101
|
+
|
102
|
+
|
103
|
+
REQUIRED: tuple[DataFrameDescription, ...] = (
|
72
104
|
DataFrameDescription(
|
73
105
|
frame_type=FrameType.REQUEST_UID,
|
74
106
|
provides=ATTR_PRODUCT,
|
@@ -103,28 +135,22 @@ SETUP_FRAME_TYPES: tuple[DataFrameDescription, ...] = (
|
|
103
135
|
),
|
104
136
|
)
|
105
137
|
|
106
|
-
|
138
|
+
REQUIRED_TYPES = [description.frame_type for description in REQUIRED]
|
107
139
|
|
108
140
|
|
109
141
|
class EcoMAX(PhysicalDevice):
|
110
142
|
"""Represents an ecoMAX controller."""
|
111
143
|
|
112
|
-
__slots__ = ("
|
144
|
+
__slots__ = ("_fuel_meter",)
|
113
145
|
|
114
|
-
|
115
|
-
_setup_frames = SETUP_FRAME_TYPES
|
146
|
+
_fuel_meter: FuelMeter
|
116
147
|
|
117
|
-
|
148
|
+
address = DeviceType.ECOMAX
|
118
149
|
|
119
150
|
def __init__(self, queue: asyncio.Queue[Frame], network: NetworkInfo) -> None:
|
120
151
|
"""Initialize a new ecoMAX controller."""
|
121
152
|
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()
|
153
|
+
self._fuel_meter = FuelMeter()
|
128
154
|
|
129
155
|
def handle_frame(self, frame: Frame) -> None:
|
130
156
|
"""Handle frame received from the ecoMAX device."""
|
@@ -162,6 +188,13 @@ class EcoMAX(PhysicalDevice):
|
|
162
188
|
|
163
189
|
return self.dispatch_nowait(ATTR_THERMOSTATS, thermostats)
|
164
190
|
|
191
|
+
async def _request_frame_version(
|
192
|
+
self, frame_type: FrameType | int, version: int
|
193
|
+
) -> None:
|
194
|
+
"""Request frame version from the device."""
|
195
|
+
if frame_type not in REQUIRED_TYPES:
|
196
|
+
await super()._request_frame_version(frame_type, version)
|
197
|
+
|
165
198
|
async def _set_ecomax_state(self, state: State) -> bool:
|
166
199
|
"""Try to set the ecoMAX control state."""
|
167
200
|
try:
|
@@ -196,11 +229,34 @@ class EcoMAX(PhysicalDevice):
|
|
196
229
|
await asyncio.gather(*(device.shutdown() for device in devices))
|
197
230
|
await super().shutdown()
|
198
231
|
|
199
|
-
@event_listener
|
232
|
+
@event_listener
|
233
|
+
async def on_event_setup(self, setup: bool) -> None:
|
234
|
+
"""Request frames required to set up an ecoMAX entry."""
|
235
|
+
_LOGGER.info("Setting up device entry")
|
236
|
+
await self.wait_for(ATTR_SENSORS)
|
237
|
+
results = await asyncio.gather(
|
238
|
+
*(
|
239
|
+
self.request(description.provides, description.frame_type)
|
240
|
+
for description in REQUIRED
|
241
|
+
),
|
242
|
+
return_exceptions=True,
|
243
|
+
)
|
244
|
+
|
245
|
+
errors = [
|
246
|
+
result.frame_type for result in results if isinstance(result, RequestError)
|
247
|
+
]
|
248
|
+
|
249
|
+
if errors:
|
250
|
+
self.dispatch_nowait(ATTR_FRAME_ERRORS, errors)
|
251
|
+
|
252
|
+
_LOGGER.info("Device entry setup done")
|
253
|
+
|
254
|
+
@event_listener
|
200
255
|
async def on_event_ecomax_parameters(
|
201
|
-
self, parameters:
|
256
|
+
self, parameters: list[tuple[int, ParameterValues]]
|
202
257
|
) -> bool:
|
203
258
|
"""Update ecoMAX parameters and dispatch the events."""
|
259
|
+
_LOGGER.info("Received device parameters")
|
204
260
|
product_info: ProductInfo = await self.get(ATTR_PRODUCT)
|
205
261
|
|
206
262
|
def _ecomax_parameter_events() -> Generator[Coroutine, Any, None]:
|
@@ -236,37 +292,20 @@ class EcoMAX(PhysicalDevice):
|
|
236
292
|
await asyncio.gather(*_ecomax_parameter_events())
|
237
293
|
return True
|
238
294
|
|
239
|
-
@event_listener
|
295
|
+
@event_listener
|
240
296
|
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
|
-
)
|
297
|
+
"""Update the amount of burned fuel."""
|
298
|
+
fuel_burned = self._fuel_meter.calculate(fuel_consumption)
|
299
|
+
if fuel_burned is not None:
|
300
|
+
self.dispatch_nowait(ATTR_FUEL_BURNED, fuel_burned)
|
263
301
|
|
264
|
-
@event_listener
|
302
|
+
@event_listener
|
265
303
|
async def on_event_mixer_parameters(
|
266
304
|
self,
|
267
|
-
parameters: dict[int,
|
305
|
+
parameters: dict[int, list[tuple[int, ParameterValues]]] | None,
|
268
306
|
) -> bool:
|
269
307
|
"""Handle mixer parameters and dispatch the events."""
|
308
|
+
_LOGGER.info("Received mixer parameters")
|
270
309
|
if parameters:
|
271
310
|
await asyncio.gather(
|
272
311
|
*(
|
@@ -278,11 +317,12 @@ class EcoMAX(PhysicalDevice):
|
|
278
317
|
|
279
318
|
return False
|
280
319
|
|
281
|
-
@event_listener
|
320
|
+
@event_listener
|
282
321
|
async def on_event_mixer_sensors(
|
283
322
|
self, sensors: dict[int, dict[str, Any]] | None
|
284
323
|
) -> bool:
|
285
324
|
"""Update mixer sensors and dispatch the events."""
|
325
|
+
_LOGGER.info("Received mixer sensors")
|
286
326
|
if sensors:
|
287
327
|
await asyncio.gather(
|
288
328
|
*(
|
@@ -294,9 +334,9 @@ class EcoMAX(PhysicalDevice):
|
|
294
334
|
|
295
335
|
return False
|
296
336
|
|
297
|
-
@event_listener
|
337
|
+
@event_listener
|
298
338
|
async def on_event_schedule_parameters(
|
299
|
-
self, parameters:
|
339
|
+
self, parameters: list[tuple[int, ParameterValues]]
|
300
340
|
) -> bool:
|
301
341
|
"""Update schedule parameters and dispatch the events."""
|
302
342
|
|
@@ -319,20 +359,22 @@ class EcoMAX(PhysicalDevice):
|
|
319
359
|
await asyncio.gather(*_schedule_parameter_events())
|
320
360
|
return True
|
321
361
|
|
322
|
-
@event_listener
|
362
|
+
@event_listener
|
323
363
|
async def on_event_sensors(self, sensors: dict[str, Any]) -> bool:
|
324
364
|
"""Update ecoMAX sensors and dispatch the events."""
|
365
|
+
_LOGGER.info("Received device sensors")
|
325
366
|
await asyncio.gather(
|
326
367
|
*(self.dispatch(name, value) for name, value in sensors.items())
|
327
368
|
)
|
328
369
|
return True
|
329
370
|
|
330
|
-
@event_listener
|
371
|
+
@event_listener
|
331
372
|
async def on_event_thermostat_parameters(
|
332
373
|
self,
|
333
|
-
parameters: dict[int,
|
374
|
+
parameters: dict[int, list[tuple[int, ParameterValues]]] | None,
|
334
375
|
) -> bool:
|
335
376
|
"""Handle thermostat parameters and dispatch the events."""
|
377
|
+
_LOGGER.info("Received thermostat parameters")
|
336
378
|
if parameters:
|
337
379
|
await asyncio.gather(
|
338
380
|
*(
|
@@ -346,7 +388,7 @@ class EcoMAX(PhysicalDevice):
|
|
346
388
|
|
347
389
|
return False
|
348
390
|
|
349
|
-
@event_listener
|
391
|
+
@event_listener
|
350
392
|
async def on_event_thermostat_profile(
|
351
393
|
self, values: ParameterValues | None
|
352
394
|
) -> EcomaxNumber | None:
|
@@ -358,11 +400,12 @@ class EcoMAX(PhysicalDevice):
|
|
358
400
|
|
359
401
|
return None
|
360
402
|
|
361
|
-
@event_listener
|
403
|
+
@event_listener
|
362
404
|
async def on_event_thermostat_sensors(
|
363
405
|
self, sensors: dict[int, dict[str, Any]] | None
|
364
406
|
) -> bool:
|
365
407
|
"""Update thermostat sensors and dispatch the events."""
|
408
|
+
_LOGGER.info("Received thermostat sensors")
|
366
409
|
if sensors:
|
367
410
|
await asyncio.gather(
|
368
411
|
*(
|
@@ -377,11 +420,12 @@ class EcoMAX(PhysicalDevice):
|
|
377
420
|
|
378
421
|
return False
|
379
422
|
|
380
|
-
@event_listener
|
423
|
+
@event_listener
|
381
424
|
async def on_event_schedules(
|
382
425
|
self, schedules: list[tuple[int, list[list[bool]]]]
|
383
426
|
) -> dict[str, Schedule]:
|
384
427
|
"""Update schedules."""
|
428
|
+
_LOGGER.info("Received device schedules")
|
385
429
|
return {
|
386
430
|
SCHEDULES[index]: Schedule(
|
387
431
|
name=SCHEDULES[index],
|
@@ -397,7 +441,7 @@ class EcoMAX(PhysicalDevice):
|
|
397
441
|
for index, schedule in schedules
|
398
442
|
}
|
399
443
|
|
400
|
-
@event_listener(
|
444
|
+
@event_listener(filter=on_change)
|
401
445
|
async def on_event_state(self, state: DeviceState) -> None:
|
402
446
|
"""Update the ecoMAX control parameter."""
|
403
447
|
await self.dispatch(
|
@@ -3,7 +3,7 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import asyncio
|
6
|
-
from collections.abc import Coroutine, Generator
|
6
|
+
from collections.abc import Coroutine, Generator
|
7
7
|
import logging
|
8
8
|
from typing import Any
|
9
9
|
|
@@ -16,8 +16,6 @@ from pyplumio.parameters.mixer import (
|
|
16
16
|
MixerSwitchDescription,
|
17
17
|
get_mixer_parameter_types,
|
18
18
|
)
|
19
|
-
from pyplumio.structures.mixer_parameters import ATTR_MIXER_PARAMETERS
|
20
|
-
from pyplumio.structures.mixer_sensors import ATTR_MIXER_SENSORS
|
21
19
|
from pyplumio.structures.product_info import ATTR_PRODUCT, ProductInfo
|
22
20
|
|
23
21
|
_LOGGER = logging.getLogger(__name__)
|
@@ -28,19 +26,21 @@ class Mixer(VirtualDevice):
|
|
28
26
|
|
29
27
|
__slots__ = ()
|
30
28
|
|
31
|
-
@event_listener
|
29
|
+
@event_listener
|
32
30
|
async def on_event_mixer_sensors(self, sensors: dict[str, Any]) -> bool:
|
33
31
|
"""Update mixer sensors and dispatch the events."""
|
32
|
+
_LOGGER.info("Received mixer %i sensors", self.index)
|
34
33
|
await asyncio.gather(
|
35
34
|
*(self.dispatch(name, value) for name, value in sensors.items())
|
36
35
|
)
|
37
36
|
return True
|
38
37
|
|
39
|
-
@event_listener
|
38
|
+
@event_listener
|
40
39
|
async def on_event_mixer_parameters(
|
41
|
-
self, parameters:
|
40
|
+
self, parameters: list[tuple[int, ParameterValues]]
|
42
41
|
) -> bool:
|
43
42
|
"""Update mixer parameters and dispatch the events."""
|
43
|
+
_LOGGER.info("Received mixer %i parameters", self.index)
|
44
44
|
product_info: ProductInfo = await self.parent.get(ATTR_PRODUCT)
|
45
45
|
|
46
46
|
def _mixer_parameter_events() -> Generator[Coroutine, Any, None]:
|
@@ -3,7 +3,8 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import asyncio
|
6
|
-
from collections.abc import Coroutine, Generator
|
6
|
+
from collections.abc import Coroutine, Generator
|
7
|
+
import logging
|
7
8
|
from typing import Any
|
8
9
|
|
9
10
|
from pyplumio.devices import VirtualDevice
|
@@ -15,8 +16,8 @@ from pyplumio.parameters.thermostat import (
|
|
15
16
|
ThermostatSwitchDescription,
|
16
17
|
get_thermostat_parameter_types,
|
17
18
|
)
|
18
|
-
|
19
|
-
|
19
|
+
|
20
|
+
_LOGGER = logging.getLogger()
|
20
21
|
|
21
22
|
|
22
23
|
class Thermostat(VirtualDevice):
|
@@ -24,19 +25,21 @@ class Thermostat(VirtualDevice):
|
|
24
25
|
|
25
26
|
__slots__ = ()
|
26
27
|
|
27
|
-
@event_listener
|
28
|
+
@event_listener
|
28
29
|
async def on_event_thermostat_sensors(self, sensors: dict[str, Any]) -> bool:
|
29
30
|
"""Update thermostat sensors and dispatch the events."""
|
31
|
+
_LOGGER.info("Received thermostat %i sensors", self.index)
|
30
32
|
await asyncio.gather(
|
31
33
|
*(self.dispatch(name, value) for name, value in sensors.items())
|
32
34
|
)
|
33
35
|
return True
|
34
36
|
|
35
|
-
@event_listener
|
37
|
+
@event_listener
|
36
38
|
async def on_event_thermostat_parameters(
|
37
|
-
self, parameters:
|
39
|
+
self, parameters: list[tuple[int, ParameterValues]]
|
38
40
|
) -> bool:
|
39
41
|
"""Update thermostat parameters and dispatch the events."""
|
42
|
+
_LOGGER.info("Received thermostat %i parameters", self.index)
|
40
43
|
|
41
44
|
def _thermostat_parameter_events() -> Generator[Coroutine, Any, None]:
|
42
45
|
"""Get dispatch calls for thermostat parameter events."""
|