PyPlumIO 0.5.48__tar.gz → 0.5.50__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.48 → pyplumio-0.5.50}/PKG-INFO +1 -1
- {pyplumio-0.5.48 → pyplumio-0.5.50}/PyPlumIO.egg-info/PKG-INFO +1 -1
- {pyplumio-0.5.48 → pyplumio-0.5.50}/PyPlumIO.egg-info/SOURCES.txt +2 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/_version.py +2 -2
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/const.py +2 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/devices/__init__.py +10 -5
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/devices/ecomax.py +9 -9
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/devices/mixer.py +2 -2
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/devices/thermostat.py +2 -2
- pyplumio-0.5.50/pyplumio/parameters/custom/ecomax_860d3_hb.py +80 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/protocol.py +10 -13
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/conftest.py +42 -7
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/devices/test_ecomax.py +42 -26
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/devices/test_init.py +20 -5
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/helpers/test_schedule.py +2 -3
- pyplumio-0.5.50/tests/parameters/custom/test_ecomax_860d3_hb.py +87 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/parameters/test_init.py +5 -7
- pyplumio-0.5.50/tests/testdata/parameters/ecomax_860d3_hb.json +20 -0
- pyplumio-0.5.48/pyplumio/parameters/custom/ecomax_860d3_hb.py +0 -38
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.gitattributes +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.github/CODE_OF_CONDUCT.md +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.github/dependabot.yml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.github/workflows/ci.yml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.github/workflows/codeql.yml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.github/workflows/deploy.yml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.github/workflows/documentation.yml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.gitignore +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.pre-commit-config.yaml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.qlty/qlty.toml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/.vscode/settings.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/LICENSE +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/MANIFEST.in +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/PyPlumIO.egg-info/dependency_links.txt +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/PyPlumIO.egg-info/requires.txt +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/PyPlumIO.egg-info/top_level.txt +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/README.md +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/Makefile +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/make.bat +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/source/callbacks.rst +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/source/conf.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/source/connecting.rst +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/source/frames.rst +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/source/index.rst +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/source/mixers_thermostats.rst +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/source/protocol.rst +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/source/reading.rst +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/source/schedules.rst +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/docs/source/writing.rst +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/images/ecomax.png +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/images/rs485.png +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/__main__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/connection.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/data_types.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/devices/ecoster.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/exceptions.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/filters.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/frames/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/frames/messages.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/frames/requests.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/frames/responses.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/helpers/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/helpers/async_cache.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/helpers/event_manager.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/helpers/factory.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/helpers/schedule.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/helpers/task_manager.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/helpers/timeout.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/helpers/uid.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/parameters/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/parameters/custom/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/parameters/ecomax.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/parameters/mixer.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/parameters/thermostat.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/py.typed +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/stream.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/alerts.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/boiler_load.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/boiler_power.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/ecomax_parameters.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/fan_power.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/frame_versions.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/fuel_consumption.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/fuel_level.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/lambda_sensor.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/mixer_parameters.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/mixer_sensors.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/modules.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/network_info.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/output_flags.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/outputs.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/pending_alerts.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/product_info.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/program_version.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/regulator_data.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/regulator_data_schema.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/schedules.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/statuses.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/temperatures.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/thermostat_parameters.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/structures/thermostat_sensors.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyplumio/utils.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/pyproject.toml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/requirements.txt +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/requirements_docs.txt +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/requirements_test.txt +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/setup.cfg +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/devices/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/devices/test_ecoster.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/devices/test_mixer.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/devices/test_thermostat.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/frames/test_init.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/frames/test_messages.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/frames/test_requests.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/frames/test_responses.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/helpers/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/helpers/test_async_cache.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/helpers/test_event_manager.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/helpers/test_factory.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/helpers/test_task_manager.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/helpers/test_timeout.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/helpers/test_uid.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/parameters/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/parameters/custom/__init__.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/parameters/custom/test_init.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/parameters/test_ecomax.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/parameters/test_mixers.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/parameters/test_thermostats.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/ruff.toml +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/test_connection.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/test_data_types.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/test_filters.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/test_init.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/test_main.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/test_protocol.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/test_stream.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/test_utils.py +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/messages/regulator_data.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/messages/sensor_data.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/requests/alerts.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/requests/ecomax_control.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/requests/ecomax_parameters.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/requests/mixer_parameters.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/requests/set_ecomax_parameter.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/requests/set_mixer_parameter.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/requests/set_schedule.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/requests/set_thermostat_parameter.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/requests/thermostat_parameters.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/responses/alerts.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/responses/device_available.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/responses/ecomax_parameters.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/responses/mixer_parameters.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/responses/password.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/responses/program_version.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/responses/regulator_data_schema.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/responses/schedules.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/responses/thermostat_parameters.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/responses/uid.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/unknown/unknown_ecomax_parameter.json +0 -0
- {pyplumio-0.5.48 → pyplumio-0.5.50}/tests/testdata/unknown/unknown_mixer_parameter.json +0 -0
@@ -133,9 +133,11 @@ tests/parameters/test_init.py
|
|
133
133
|
tests/parameters/test_mixers.py
|
134
134
|
tests/parameters/test_thermostats.py
|
135
135
|
tests/parameters/custom/__init__.py
|
136
|
+
tests/parameters/custom/test_ecomax_860d3_hb.py
|
136
137
|
tests/parameters/custom/test_init.py
|
137
138
|
tests/testdata/messages/regulator_data.json
|
138
139
|
tests/testdata/messages/sensor_data.json
|
140
|
+
tests/testdata/parameters/ecomax_860d3_hb.json
|
139
141
|
tests/testdata/requests/alerts.json
|
140
142
|
tests/testdata/requests/ecomax_control.json
|
141
143
|
tests/testdata/requests/ecomax_parameters.json
|
@@ -184,6 +184,7 @@ class FrameType(IntEnum):
|
|
184
184
|
REQUEST_SET_MIXER_PARAMETER = 52
|
185
185
|
REQUEST_SCHEDULES = 54
|
186
186
|
REQUEST_SET_SCHEDULE = 55
|
187
|
+
REQUEST_ECOMAX_PARAMETER_CHANGES = 56
|
187
188
|
REQUEST_UID = 57
|
188
189
|
REQUEST_PASSWORD = 58
|
189
190
|
REQUEST_ECOMAX_CONTROL = 59
|
@@ -203,6 +204,7 @@ class FrameType(IntEnum):
|
|
203
204
|
RESPONSE_ECOMAX_CONTROL = 187
|
204
205
|
RESPONSE_ALERTS = 189
|
205
206
|
RESPONSE_PROGRAM_VERSION = 192
|
207
|
+
RESPONSE_ECOMAX_PARAMETER_CHANGES = 212
|
206
208
|
RESPONSE_REGULATOR_DATA_SCHEMA = 213
|
207
209
|
RESPONSE_THERMOSTAT_PARAMETERS = 220
|
208
210
|
RESPONSE_SET_THERMOSTAT_PARAMETER = 221
|
@@ -72,7 +72,7 @@ class Device(ABC, EventManager):
|
|
72
72
|
:param name: Name of the parameter
|
73
73
|
:type name: str
|
74
74
|
:param value: New value for the parameter
|
75
|
-
:type value: int | float | bool | Literal["
|
75
|
+
:type value: int | float | bool | Literal["on", "off"]
|
76
76
|
:param retries: Try setting parameter for this amount of
|
77
77
|
times, defaults to 5
|
78
78
|
:type retries: int, optional
|
@@ -104,7 +104,7 @@ class Device(ABC, EventManager):
|
|
104
104
|
:param name: Name of the parameter
|
105
105
|
:type name: str
|
106
106
|
:param value: New value for the parameter
|
107
|
-
:type value: int | float | bool | Literal["
|
107
|
+
:type value: int | float | bool | Literal["on", "off"]
|
108
108
|
:param retries: Try setting parameter for this amount of
|
109
109
|
times, defaults to 5
|
110
110
|
:type retries: int, optional
|
@@ -145,21 +145,26 @@ class PhysicalDevice(Device, ABC):
|
|
145
145
|
@event_listener(filter=on_change)
|
146
146
|
async def on_event_frame_versions(self, versions: dict[int, int]) -> None:
|
147
147
|
"""Check frame versions and update outdated frames."""
|
148
|
-
_LOGGER.
|
148
|
+
_LOGGER.debug("Received frame version table")
|
149
149
|
for frame_type, version in versions.items():
|
150
150
|
if (
|
151
151
|
is_known_frame_type(frame_type)
|
152
152
|
and self.supports_frame_type(frame_type)
|
153
153
|
and not self.has_frame_version(frame_type, version)
|
154
154
|
):
|
155
|
-
|
155
|
+
request_frame_type = (
|
156
|
+
FrameType.REQUEST_ECOMAX_PARAMETERS
|
157
|
+
if frame_type == FrameType.REQUEST_ECOMAX_PARAMETER_CHANGES
|
158
|
+
else frame_type
|
159
|
+
)
|
160
|
+
await self._request_frame_version(request_frame_type, version)
|
156
161
|
self._frame_versions[frame_type] = version
|
157
162
|
|
158
163
|
async def _request_frame_version(
|
159
164
|
self, frame_type: FrameType | int, version: int
|
160
165
|
) -> None:
|
161
166
|
"""Request frame version from the device."""
|
162
|
-
_LOGGER.
|
167
|
+
_LOGGER.debug("Updating frame %s to version %i", repr(frame_type), version)
|
163
168
|
request = await Request.create(frame_type, recipient=self.address)
|
164
169
|
self.queue.put_nowait(request)
|
165
170
|
|
@@ -234,7 +234,7 @@ class EcoMAX(PhysicalDevice):
|
|
234
234
|
@event_listener
|
235
235
|
async def on_event_setup(self, setup: bool) -> None:
|
236
236
|
"""Request frames required to set up an ecoMAX entry."""
|
237
|
-
_LOGGER.
|
237
|
+
_LOGGER.debug("Setting up device entry")
|
238
238
|
await self.wait_for(ATTR_SENSORS)
|
239
239
|
results = await asyncio.gather(
|
240
240
|
*(
|
@@ -251,14 +251,14 @@ class EcoMAX(PhysicalDevice):
|
|
251
251
|
if errors:
|
252
252
|
self.dispatch_nowait(ATTR_FRAME_ERRORS, errors)
|
253
253
|
|
254
|
-
_LOGGER.
|
254
|
+
_LOGGER.debug("Device entry setup done")
|
255
255
|
|
256
256
|
@event_listener
|
257
257
|
async def on_event_ecomax_parameters(
|
258
258
|
self, parameters: list[tuple[int, ParameterValues]]
|
259
259
|
) -> bool:
|
260
260
|
"""Update ecoMAX parameters and dispatch the events."""
|
261
|
-
_LOGGER.
|
261
|
+
_LOGGER.debug("Received device parameters")
|
262
262
|
product_info: ProductInfo = await self.get(ATTR_PRODUCT)
|
263
263
|
parameter_types = await get_ecomax_parameter_types(product_info)
|
264
264
|
|
@@ -307,7 +307,7 @@ class EcoMAX(PhysicalDevice):
|
|
307
307
|
parameters: dict[int, list[tuple[int, ParameterValues]]] | None,
|
308
308
|
) -> bool:
|
309
309
|
"""Handle mixer parameters and dispatch the events."""
|
310
|
-
_LOGGER.
|
310
|
+
_LOGGER.debug("Received mixer parameters")
|
311
311
|
if parameters:
|
312
312
|
await asyncio.gather(
|
313
313
|
*(
|
@@ -324,7 +324,7 @@ class EcoMAX(PhysicalDevice):
|
|
324
324
|
self, sensors: dict[int, dict[str, Any]] | None
|
325
325
|
) -> bool:
|
326
326
|
"""Update mixer sensors and dispatch the events."""
|
327
|
-
_LOGGER.
|
327
|
+
_LOGGER.debug("Received mixer sensors")
|
328
328
|
if sensors:
|
329
329
|
await asyncio.gather(
|
330
330
|
*(
|
@@ -364,7 +364,7 @@ class EcoMAX(PhysicalDevice):
|
|
364
364
|
@event_listener
|
365
365
|
async def on_event_sensors(self, sensors: dict[str, Any]) -> bool:
|
366
366
|
"""Update ecoMAX sensors and dispatch the events."""
|
367
|
-
_LOGGER.
|
367
|
+
_LOGGER.debug("Received device sensors")
|
368
368
|
await asyncio.gather(
|
369
369
|
*(self.dispatch(name, value) for name, value in sensors.items())
|
370
370
|
)
|
@@ -376,7 +376,7 @@ class EcoMAX(PhysicalDevice):
|
|
376
376
|
parameters: dict[int, list[tuple[int, ParameterValues]]] | None,
|
377
377
|
) -> bool:
|
378
378
|
"""Handle thermostat parameters and dispatch the events."""
|
379
|
-
_LOGGER.
|
379
|
+
_LOGGER.debug("Received thermostat parameters")
|
380
380
|
if parameters:
|
381
381
|
await asyncio.gather(
|
382
382
|
*(
|
@@ -407,7 +407,7 @@ class EcoMAX(PhysicalDevice):
|
|
407
407
|
self, sensors: dict[int, dict[str, Any]] | None
|
408
408
|
) -> bool:
|
409
409
|
"""Update thermostat sensors and dispatch the events."""
|
410
|
-
_LOGGER.
|
410
|
+
_LOGGER.debug("Received thermostat sensors")
|
411
411
|
if sensors:
|
412
412
|
await asyncio.gather(
|
413
413
|
*(
|
@@ -427,7 +427,7 @@ class EcoMAX(PhysicalDevice):
|
|
427
427
|
self, schedules: list[tuple[int, list[list[bool]]]]
|
428
428
|
) -> dict[str, Schedule]:
|
429
429
|
"""Update schedules."""
|
430
|
-
_LOGGER.
|
430
|
+
_LOGGER.debug("Received device schedules")
|
431
431
|
return {
|
432
432
|
SCHEDULES[index]: Schedule(
|
433
433
|
name=SCHEDULES[index],
|
@@ -29,7 +29,7 @@ class Mixer(VirtualDevice):
|
|
29
29
|
@event_listener
|
30
30
|
async def on_event_mixer_sensors(self, sensors: dict[str, Any]) -> bool:
|
31
31
|
"""Update mixer sensors and dispatch the events."""
|
32
|
-
_LOGGER.
|
32
|
+
_LOGGER.debug("Received mixer %i sensors", self.index)
|
33
33
|
await asyncio.gather(
|
34
34
|
*(self.dispatch(name, value) for name, value in sensors.items())
|
35
35
|
)
|
@@ -40,7 +40,7 @@ class Mixer(VirtualDevice):
|
|
40
40
|
self, parameters: list[tuple[int, ParameterValues]]
|
41
41
|
) -> bool:
|
42
42
|
"""Update mixer parameters and dispatch the events."""
|
43
|
-
_LOGGER.
|
43
|
+
_LOGGER.debug("Received mixer %i parameters", self.index)
|
44
44
|
product_info: ProductInfo = await self.parent.get(ATTR_PRODUCT)
|
45
45
|
parameter_types = get_mixer_parameter_types(product_info)
|
46
46
|
|
@@ -28,7 +28,7 @@ class Thermostat(VirtualDevice):
|
|
28
28
|
@event_listener
|
29
29
|
async def on_event_thermostat_sensors(self, sensors: dict[str, Any]) -> bool:
|
30
30
|
"""Update thermostat sensors and dispatch the events."""
|
31
|
-
_LOGGER.
|
31
|
+
_LOGGER.debug("Received thermostat %i sensors", self.index)
|
32
32
|
await asyncio.gather(
|
33
33
|
*(self.dispatch(name, value) for name, value in sensors.items())
|
34
34
|
)
|
@@ -39,7 +39,7 @@ class Thermostat(VirtualDevice):
|
|
39
39
|
self, parameters: list[tuple[int, ParameterValues]]
|
40
40
|
) -> bool:
|
41
41
|
"""Update thermostat parameters and dispatch the events."""
|
42
|
-
_LOGGER.
|
42
|
+
_LOGGER.debug("Received thermostat %i parameters", self.index)
|
43
43
|
parameter_types = get_thermostat_parameter_types()
|
44
44
|
|
45
45
|
def _thermostat_parameter_events() -> Generator[Coroutine]:
|
@@ -0,0 +1,80 @@
|
|
1
|
+
"""Contains patch for ecoMAX 860D3-HB."""
|
2
|
+
|
3
|
+
from pyplumio.const import UnitOfMeasurement
|
4
|
+
from pyplumio.parameters.custom import CustomParameter, CustomParameters, Signature
|
5
|
+
from pyplumio.parameters.ecomax import EcomaxNumberDescription, EcomaxSwitchDescription
|
6
|
+
|
7
|
+
|
8
|
+
class EcoMAX860D3HB(CustomParameters):
|
9
|
+
"""Replacements for ecoMAX 860D3-HB."""
|
10
|
+
|
11
|
+
__slots__ = ()
|
12
|
+
|
13
|
+
signature = Signature(model="ecoMAX 860D3-HB", id=48)
|
14
|
+
|
15
|
+
replacements = (
|
16
|
+
# Summer mode
|
17
|
+
CustomParameter(
|
18
|
+
original="summer_mode_disable_temp",
|
19
|
+
replacement=EcomaxNumberDescription(name="__unknown_parameter_1"),
|
20
|
+
),
|
21
|
+
CustomParameter(
|
22
|
+
original="water_heater_target_temp",
|
23
|
+
replacement=EcomaxNumberDescription(name="summer_mode"),
|
24
|
+
),
|
25
|
+
CustomParameter(
|
26
|
+
original="min_water_heater_target_temp",
|
27
|
+
replacement=EcomaxNumberDescription(
|
28
|
+
name="summer_mode_enable_temp",
|
29
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
30
|
+
),
|
31
|
+
),
|
32
|
+
CustomParameter(
|
33
|
+
original="max_water_heater_target_temp",
|
34
|
+
replacement=EcomaxNumberDescription(
|
35
|
+
name="summer_mode_disable_temp",
|
36
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
37
|
+
),
|
38
|
+
),
|
39
|
+
# Water heater
|
40
|
+
CustomParameter(
|
41
|
+
original="disable_pump_on_thermostat",
|
42
|
+
replacement=EcomaxNumberDescription(
|
43
|
+
name="water_heater_target_temp",
|
44
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
45
|
+
),
|
46
|
+
),
|
47
|
+
CustomParameter(
|
48
|
+
original="boiler_alert_temp",
|
49
|
+
replacement=EcomaxNumberDescription(
|
50
|
+
name="min_water_heater_target_temp",
|
51
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
52
|
+
),
|
53
|
+
),
|
54
|
+
CustomParameter(
|
55
|
+
original="max_feeder_temp",
|
56
|
+
replacement=EcomaxNumberDescription(
|
57
|
+
name="max_water_heater_target_temp",
|
58
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
59
|
+
),
|
60
|
+
),
|
61
|
+
CustomParameter(
|
62
|
+
original="water_heater_work_mode",
|
63
|
+
replacement=EcomaxNumberDescription(name="water_heater_feeding_extension"),
|
64
|
+
),
|
65
|
+
CustomParameter(
|
66
|
+
original="external_boiler_temp",
|
67
|
+
replacement=EcomaxNumberDescription(name="water_heater_work_mode"),
|
68
|
+
),
|
69
|
+
CustomParameter(
|
70
|
+
original="alert_notify",
|
71
|
+
replacement=EcomaxNumberDescription(
|
72
|
+
name="water_heater_hysteresis",
|
73
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
74
|
+
),
|
75
|
+
),
|
76
|
+
CustomParameter(
|
77
|
+
original="pump_hysteresis",
|
78
|
+
replacement=EcomaxSwitchDescription(name="water_heater_disinfection"),
|
79
|
+
),
|
80
|
+
)
|
@@ -230,20 +230,17 @@ class AsyncProtocol(Protocol, EventManager[PhysicalDevice]):
|
|
230
230
|
@acache
|
231
231
|
async def get_device_entry(self, device_type: DeviceType) -> PhysicalDevice:
|
232
232
|
"""Return the device entry."""
|
233
|
-
|
234
|
-
@acache
|
235
|
-
async def _setup_device_entry(device_type: DeviceType) -> PhysicalDevice:
|
236
|
-
"""Set up the device entry."""
|
237
|
-
device = await PhysicalDevice.create(
|
238
|
-
device_type, queue=self._queues.write, network=self._network
|
239
|
-
)
|
240
|
-
device.dispatch_nowait(ATTR_CONNECTED, True)
|
241
|
-
device.dispatch_nowait(ATTR_SETUP, True)
|
242
|
-
self.dispatch_nowait(device_type.name.lower(), device)
|
243
|
-
return device
|
244
|
-
|
245
233
|
async with self._entry_lock:
|
246
|
-
|
234
|
+
name = device_type.name.lower()
|
235
|
+
if name not in self.data:
|
236
|
+
device = await PhysicalDevice.create(
|
237
|
+
device_type, queue=self._queues.write, network=self._network
|
238
|
+
)
|
239
|
+
device.dispatch_nowait(ATTR_CONNECTED, True)
|
240
|
+
device.dispatch_nowait(ATTR_SETUP, True)
|
241
|
+
await self.dispatch(device_type.name.lower(), device)
|
242
|
+
|
243
|
+
return self.data[name]
|
247
244
|
|
248
245
|
|
249
246
|
__all__ = ["Protocol", "DummyProtocol", "AsyncProtocol"]
|
@@ -8,6 +8,7 @@ import functools
|
|
8
8
|
import importlib
|
9
9
|
import inspect
|
10
10
|
import json
|
11
|
+
from math import isclose
|
11
12
|
import os
|
12
13
|
import pathlib
|
13
14
|
from typing import Any, Final, TypeVar
|
@@ -16,8 +17,9 @@ from unittest.mock import patch
|
|
16
17
|
from freezegun import freeze_time
|
17
18
|
import pytest
|
18
19
|
|
19
|
-
from pyplumio.const import ProductType
|
20
|
+
from pyplumio.const import ProductType, State
|
20
21
|
from pyplumio.devices.ecomax import EcoMAX
|
22
|
+
from pyplumio.parameters import NumericType
|
21
23
|
from pyplumio.structures.network_info import NetworkInfo
|
22
24
|
from pyplumio.structures.product_info import ATTR_PRODUCT, ProductInfo
|
23
25
|
|
@@ -34,7 +36,7 @@ def _create_class_instance(module_name: str, class_name: str, **kwargs):
|
|
34
36
|
return getattr(importlib.import_module(module_name), class_name)(**kwargs)
|
35
37
|
|
36
38
|
|
37
|
-
def
|
39
|
+
def _try_int(key: Any) -> Any:
|
38
40
|
"""Try to convert key to integer or return key unchanged on error."""
|
39
41
|
try:
|
40
42
|
return int(key)
|
@@ -55,7 +57,7 @@ def _decode_hinted_objects(d: Any) -> Any:
|
|
55
57
|
if "__tuple__" in d:
|
56
58
|
return tuple(d["items"])
|
57
59
|
|
58
|
-
return {
|
60
|
+
return {_try_int(k): v for k, v in d.items()}
|
59
61
|
|
60
62
|
|
61
63
|
def load_json_test_data(path: str) -> Any:
|
@@ -83,9 +85,16 @@ def _bypass_pytest_argument_inspection(
|
|
83
85
|
return wrapper
|
84
86
|
|
85
87
|
|
86
|
-
def json_test_data(
|
88
|
+
def json_test_data(
|
89
|
+
json_path: str,
|
90
|
+
selector: str | None = None,
|
91
|
+
dataset: int = 0,
|
92
|
+
name: str | None = None,
|
93
|
+
):
|
87
94
|
"""Pytest decorator to inject JSON test data as a test argument."""
|
88
|
-
|
95
|
+
if not name:
|
96
|
+
name = json_path.split("/")[-1].split("\\")[-1].rsplit(".", 1)[0]
|
97
|
+
|
89
98
|
if selector:
|
90
99
|
name += f"_{selector}"
|
91
100
|
|
@@ -104,10 +113,17 @@ def json_test_data(json_path: str, selector: str | None = None, dataset: int = 0
|
|
104
113
|
|
105
114
|
|
106
115
|
def class_from_json(
|
107
|
-
cls: type,
|
116
|
+
cls: type,
|
117
|
+
json_path: str,
|
118
|
+
/,
|
119
|
+
dataset: int = 0,
|
120
|
+
arguments: Sequence | None = None,
|
121
|
+
name: str | None = None,
|
108
122
|
):
|
109
123
|
"""Pytest decorator to inject JSON test data as a test argument."""
|
110
|
-
|
124
|
+
if not name:
|
125
|
+
name = json_path.split("/")[-1].split("\\")[-1].rsplit(".", 1)[0]
|
126
|
+
|
111
127
|
init_args = arguments if arguments else ()
|
112
128
|
|
113
129
|
def decorator(test_func):
|
@@ -124,6 +140,16 @@ def class_from_json(
|
|
124
140
|
return decorator
|
125
141
|
|
126
142
|
|
143
|
+
def equal_parameter_value(
|
144
|
+
a: NumericType | State | None, b: NumericType | State | None
|
145
|
+
) -> bool:
|
146
|
+
"""Compare the parameter values."""
|
147
|
+
if isinstance(a, float) and isinstance(b, float):
|
148
|
+
return isclose(a, b, rel_tol=DEFAULT_TOLERANCE)
|
149
|
+
else:
|
150
|
+
return True if a == b else False
|
151
|
+
|
152
|
+
|
127
153
|
@pytest.fixture(autouse=True)
|
128
154
|
def skip_asyncio_sleep():
|
129
155
|
"""Skip an asyncio sleep calls."""
|
@@ -151,3 +177,12 @@ def fixture_frozen_time():
|
|
151
177
|
"""Get frozen time."""
|
152
178
|
with freeze_time("2012-12-12 12:00:00") as frozen_time:
|
153
179
|
yield frozen_time
|
180
|
+
|
181
|
+
|
182
|
+
__all__ = [
|
183
|
+
"class_from_json",
|
184
|
+
"equal_parameter_value",
|
185
|
+
"json_test_data",
|
186
|
+
"load_json_parameters",
|
187
|
+
"load_json_test_data",
|
188
|
+
]
|
@@ -2,8 +2,7 @@
|
|
2
2
|
|
3
3
|
from datetime import timedelta
|
4
4
|
import logging
|
5
|
-
from
|
6
|
-
from typing import Any, Literal, cast
|
5
|
+
from typing import Any, cast
|
7
6
|
from unittest.mock import AsyncMock, Mock, patch
|
8
7
|
|
9
8
|
import pytest
|
@@ -26,6 +25,7 @@ from pyplumio.const import (
|
|
26
25
|
DeviceState,
|
27
26
|
DeviceType,
|
28
27
|
FrameType,
|
28
|
+
State,
|
29
29
|
UnitOfMeasurement,
|
30
30
|
)
|
31
31
|
from pyplumio.devices.ecomax import (
|
@@ -43,6 +43,7 @@ from pyplumio.frames.messages import SensorDataMessage
|
|
43
43
|
from pyplumio.frames.requests import (
|
44
44
|
AlertsRequest,
|
45
45
|
EcomaxControlRequest,
|
46
|
+
EcomaxParametersRequest,
|
46
47
|
SetEcomaxParameterRequest,
|
47
48
|
SetScheduleRequest,
|
48
49
|
SetThermostatParameterRequest,
|
@@ -81,7 +82,12 @@ from pyplumio.structures.thermostat_sensors import (
|
|
81
82
|
ATTR_THERMOSTATS_AVAILABLE,
|
82
83
|
ATTR_THERMOSTATS_CONNECTED,
|
83
84
|
)
|
84
|
-
from tests.conftest import
|
85
|
+
from tests.conftest import (
|
86
|
+
UNDEFINED,
|
87
|
+
class_from_json,
|
88
|
+
equal_parameter_value,
|
89
|
+
json_test_data,
|
90
|
+
)
|
85
91
|
|
86
92
|
|
87
93
|
@patch("asyncio.Queue.put_nowait")
|
@@ -99,10 +105,24 @@ async def test_ecomax_handle_frame(
|
|
99
105
|
mock_handle_frame.assert_called_once_with(request)
|
100
106
|
|
101
107
|
|
108
|
+
@pytest.mark.parametrize(
|
109
|
+
("frame_type", "frame_request"),
|
110
|
+
[
|
111
|
+
(FrameType.REQUEST_ALERTS, AlertsRequest(recipient=DeviceType.ECOMAX)),
|
112
|
+
(
|
113
|
+
FrameType.REQUEST_ECOMAX_PARAMETER_CHANGES,
|
114
|
+
EcomaxParametersRequest(recipient=DeviceType.ECOMAX),
|
115
|
+
),
|
116
|
+
],
|
117
|
+
)
|
102
118
|
@patch("asyncio.Queue.put_nowait")
|
103
119
|
@class_from_json(SensorDataMessage, "messages/sensor_data.json", arguments=("message",))
|
104
120
|
async def test_frame_versions_tracker(
|
105
|
-
mock_put_nowait,
|
121
|
+
mock_put_nowait,
|
122
|
+
ecomax: EcoMAX,
|
123
|
+
sensor_data: SensorDataMessage,
|
124
|
+
frame_type: FrameType,
|
125
|
+
frame_request: Request,
|
106
126
|
) -> None:
|
107
127
|
"""Test frame version tracker."""
|
108
128
|
|
@@ -111,7 +131,7 @@ async def test_frame_versions_tracker(
|
|
111
131
|
return {ATTR_SENSORS: {ATTR_FRAME_VERSIONS: {frame: version}}}
|
112
132
|
|
113
133
|
# Test with frame type that are handled during setup.
|
114
|
-
sensor_data.data = _frame_version_data(
|
134
|
+
sensor_data.data = _frame_version_data(frame_type, 1)
|
115
135
|
ecomax.data.clear()
|
116
136
|
ecomax.handle_frame(sensor_data)
|
117
137
|
await ecomax.wait_until_done()
|
@@ -119,18 +139,16 @@ async def test_frame_versions_tracker(
|
|
119
139
|
|
120
140
|
# Test with same frame type after setup done.
|
121
141
|
mock_put_nowait.reset_mock()
|
122
|
-
sensor_data.data = _frame_version_data(
|
142
|
+
sensor_data.data = _frame_version_data(frame_type, 2)
|
123
143
|
ecomax.data.clear()
|
124
144
|
ecomax.data[ATTR_SETUP] = True
|
125
145
|
ecomax.handle_frame(sensor_data)
|
126
146
|
await ecomax.wait_until_done()
|
127
|
-
mock_put_nowait.assert_called_once_with(
|
147
|
+
mock_put_nowait.assert_called_once_with(frame_request)
|
128
148
|
|
129
149
|
|
130
150
|
@pytest.mark.parametrize("state", [STATE_ON, STATE_OFF])
|
131
|
-
async def test_ecomax_control(
|
132
|
-
state: Literal["on", "off"], ecomax: EcoMAX, caplog
|
133
|
-
) -> None:
|
151
|
+
async def test_ecomax_control(state: State, ecomax: EcoMAX, caplog) -> None:
|
134
152
|
"""Test ecoMAX control."""
|
135
153
|
coro = getattr(ecomax, f"turn_{state}")
|
136
154
|
await coro()
|
@@ -143,9 +161,7 @@ async def test_ecomax_control(
|
|
143
161
|
|
144
162
|
@pytest.mark.parametrize("state", [STATE_ON, STATE_OFF])
|
145
163
|
@patch("pyplumio.devices.ecomax.EcoMAX.create_task")
|
146
|
-
def test_ecomax_control_nowait(
|
147
|
-
mock_create_task, ecomax: EcoMAX, state: Literal["on", "off"]
|
148
|
-
) -> None:
|
164
|
+
def test_ecomax_control_nowait(mock_create_task, ecomax: EcoMAX, state: State) -> None:
|
149
165
|
"""Test ecoMAX control without waiting for result."""
|
150
166
|
func = getattr(ecomax, f"turn_{state}_nowait")
|
151
167
|
with patch(
|
@@ -237,9 +253,9 @@ async def test_ecomax_parameters_event_listener(
|
|
237
253
|
# Test parameter with the step (heating_heat_curve)
|
238
254
|
fuel_calorific_value = ecomax.get_nowait("fuel_calorific_value")
|
239
255
|
assert isinstance(fuel_calorific_value, EcomaxNumber)
|
240
|
-
assert
|
241
|
-
assert
|
242
|
-
assert
|
256
|
+
assert equal_parameter_value(fuel_calorific_value.value, 4.6)
|
257
|
+
assert equal_parameter_value(fuel_calorific_value.min_value, 0.1)
|
258
|
+
assert equal_parameter_value(fuel_calorific_value.max_value, 25.0)
|
243
259
|
|
244
260
|
# Test setting parameter with the step.
|
245
261
|
await fuel_calorific_value.set(2.5)
|
@@ -250,9 +266,9 @@ async def test_ecomax_parameters_event_listener(
|
|
250
266
|
# Test parameter with the offset (heating_heat_curve_shift)
|
251
267
|
heating_heat_curve_shift = ecomax.get_nowait("heating_curve_shift")
|
252
268
|
assert isinstance(heating_heat_curve_shift, EcomaxNumber)
|
253
|
-
assert
|
254
|
-
assert
|
255
|
-
assert
|
269
|
+
assert equal_parameter_value(heating_heat_curve_shift.value, 0.0)
|
270
|
+
assert equal_parameter_value(heating_heat_curve_shift.min_value, -20.0)
|
271
|
+
assert equal_parameter_value(heating_heat_curve_shift.max_value, 20.0)
|
256
272
|
assert heating_heat_curve_shift.unit_of_measurement == UnitOfMeasurement.CELSIUS
|
257
273
|
|
258
274
|
# Test setting the parameter with the offset.
|
@@ -302,7 +318,7 @@ async def test_fuel_consumption_event_listener(
|
|
302
318
|
ecomax.handle_frame(Response(data={ATTR_FUEL_CONSUMPTION: 3.6}))
|
303
319
|
await ecomax.wait_until_done()
|
304
320
|
fuel_burned = cast(float, ecomax.get_nowait(ATTR_FUEL_BURNED))
|
305
|
-
assert
|
321
|
+
assert equal_parameter_value(fuel_burned, 0.01)
|
306
322
|
|
307
323
|
# Test with outdated data.
|
308
324
|
message = "Skipping outdated fuel consumption"
|
@@ -310,7 +326,7 @@ async def test_fuel_consumption_event_listener(
|
|
310
326
|
ecomax.handle_frame(Response(data={ATTR_FUEL_CONSUMPTION: 1}))
|
311
327
|
await ecomax.wait_until_done()
|
312
328
|
fuel_burned = cast(float, ecomax.get_nowait(ATTR_FUEL_BURNED))
|
313
|
-
assert
|
329
|
+
assert equal_parameter_value(fuel_burned, 0.01)
|
314
330
|
assert message in caplog.text
|
315
331
|
caplog.clear()
|
316
332
|
|
@@ -319,7 +335,7 @@ async def test_fuel_consumption_event_listener(
|
|
319
335
|
await ecomax.wait_until_done()
|
320
336
|
assert message not in caplog.text
|
321
337
|
fuel_burned = cast(float, ecomax.get_nowait(ATTR_FUEL_BURNED))
|
322
|
-
assert
|
338
|
+
assert equal_parameter_value(fuel_burned, 0.02)
|
323
339
|
|
324
340
|
|
325
341
|
@class_from_json(
|
@@ -376,7 +392,7 @@ async def test_ecomax_sensors_event_listener(
|
|
376
392
|
ecomax.handle_frame(sensor_data)
|
377
393
|
await ecomax.wait_until_done()
|
378
394
|
heating_target = cast(float, ecomax.get_nowait("heating_target"))
|
379
|
-
assert
|
395
|
+
assert equal_parameter_value(heating_target, 41.0)
|
380
396
|
|
381
397
|
ecomax_control = ecomax.get_nowait(ATTR_ECOMAX_CONTROL)
|
382
398
|
assert isinstance(ecomax_control, EcomaxSwitch)
|
@@ -447,9 +463,9 @@ async def test_thermostat_profile_event_listener(
|
|
447
463
|
await ecomax.wait_until_done()
|
448
464
|
thermostat_profile = ecomax.get_nowait(ATTR_THERMOSTAT_PROFILE)
|
449
465
|
assert isinstance(thermostat_profile, EcomaxNumber)
|
450
|
-
assert
|
451
|
-
assert
|
452
|
-
assert
|
466
|
+
assert equal_parameter_value(thermostat_profile.value, 0.0)
|
467
|
+
assert equal_parameter_value(thermostat_profile.min_value, 0.0)
|
468
|
+
assert equal_parameter_value(thermostat_profile.max_value, 5.0)
|
453
469
|
thermostat_profile_request = await thermostat_profile.create_request()
|
454
470
|
assert isinstance(thermostat_profile_request, SetThermostatParameterRequest)
|
455
471
|
assert thermostat_profile_request.data == {
|
@@ -102,19 +102,34 @@ def fixture_physical_device() -> PhysicalDevice:
|
|
102
102
|
class TestPhysicalDevice:
|
103
103
|
"""Contains tests for PhysicalDevice class."""
|
104
104
|
|
105
|
+
@pytest.mark.parametrize(
|
106
|
+
("frame_type", "requested_frame_type"),
|
107
|
+
[
|
108
|
+
(FrameType.REQUEST_ALERTS, FrameType.REQUEST_ALERTS),
|
109
|
+
(
|
110
|
+
FrameType.REQUEST_ECOMAX_PARAMETER_CHANGES,
|
111
|
+
FrameType.REQUEST_ECOMAX_PARAMETERS,
|
112
|
+
),
|
113
|
+
],
|
114
|
+
)
|
105
115
|
@patch("pyplumio.frames.Request.create", autospec=True)
|
106
116
|
@patch("asyncio.Queue.put_nowait")
|
107
117
|
async def test_frame_versions_event_listener(
|
108
|
-
self,
|
118
|
+
self,
|
119
|
+
mock_put_nowait,
|
120
|
+
mock_request_create,
|
121
|
+
physical_device: PhysicalDevice,
|
122
|
+
frame_type: FrameType,
|
123
|
+
requested_frame_type: FrameType,
|
109
124
|
) -> None:
|
110
125
|
"""Test event listener for frame versions."""
|
111
|
-
assert physical_device.has_frame_version(
|
112
|
-
await physical_device.on_event_frame_versions({
|
126
|
+
assert physical_device.has_frame_version(frame_type, 1) is False
|
127
|
+
await physical_device.on_event_frame_versions({frame_type: 1})
|
113
128
|
mock_request_create.assert_awaited_once_with(
|
114
|
-
|
129
|
+
requested_frame_type, recipient=DummyPhysicalDevice.address
|
115
130
|
)
|
116
131
|
mock_put_nowait.assert_called_once_with(mock_request_create.return_value)
|
117
|
-
assert physical_device.has_frame_version(
|
132
|
+
assert physical_device.has_frame_version(frame_type, 1) is True
|
118
133
|
|
119
134
|
def test_frame_versions_event_listener_decorator(self) -> None:
|
120
135
|
"""Test decorator for the frame version event listener."""
|