From c72d379f211d31425ce5e4823755d17ab8cf420f Mon Sep 17 00:00:00 2001 From: denpamusic Date: Fri, 3 May 2024 23:41:46 +0300 Subject: [PATCH] Avoid blocking import_module call. --- pyplumio/devices/__init__.py | 4 +- pyplumio/devices/ecomax.py | 4 +- pyplumio/helpers/factory.py | 17 +++-- pyplumio/helpers/parameter.py | 11 ++-- pyplumio/helpers/schedule.py | 5 +- pyplumio/protocol.py | 18 ++--- pyplumio/stream.py | 4 +- pyplumio/structures/ecomax_parameters.py | 69 ++++++++++---------- pyplumio/structures/mixer_parameters.py | 33 +++++----- pyplumio/structures/schedules.py | 21 +++--- pyplumio/structures/thermostat_parameters.py | 39 +++++------ tests/helpers/test_factory.py | 14 ++-- tests/helpers/test_parameter.py | 9 ++- tests/helpers/test_schedule.py | 7 +- tests/test_devices.py | 39 +++++++---- tests/test_protocol.py | 2 +- 16 files changed, 156 insertions(+), 140 deletions(-) diff --git a/pyplumio/devices/__init__.py b/pyplumio/devices/__init__.py index 6c0a191b..8f916c2b 100644 --- a/pyplumio/devices/__init__.py +++ b/pyplumio/devices/__init__.py @@ -11,7 +11,7 @@ from pyplumio.exceptions import UnknownDeviceError from pyplumio.frames import DataFrameDescription, Frame, Request, get_frame_handler from pyplumio.helpers.event_manager import EventManager -from pyplumio.helpers.factory import factory +from pyplumio.helpers.factory import create_instance from pyplumio.helpers.parameter import SET_RETRIES, Parameter from pyplumio.helpers.typing import ParameterValueType from pyplumio.structures.network_info import NetworkInfo @@ -160,7 +160,7 @@ async def request( If value is not available before timeout, retry request. """ - request: Request = factory( + request: Request = await create_instance( get_frame_handler(frame_type), recipient=self.address ) diff --git a/pyplumio/devices/ecomax.py b/pyplumio/devices/ecomax.py index e4e3d8a8..11f8f4f4 100644 --- a/pyplumio/devices/ecomax.py +++ b/pyplumio/devices/ecomax.py @@ -28,7 +28,7 @@ get_frame_handler, is_known_frame_type, ) -from pyplumio.helpers.factory import factory +from pyplumio.helpers.factory import create_instance from pyplumio.helpers.parameter import ParameterValues from pyplumio.helpers.schedule import Schedule, ScheduleDay from pyplumio.structures.alerts import ATTR_TOTAL_ALERTS @@ -235,7 +235,7 @@ async def _update_frame_versions(self, versions: dict[int, int]) -> None: and not self._has_frame_version(frame_type, version) ): # We don't have this frame or it's version has changed. - request: Request = factory( + request: Request = await create_instance( get_frame_handler(frame_type), recipient=self.address ) self.queue.put_nowait(request) diff --git a/pyplumio/helpers/factory.py b/pyplumio/helpers/factory.py index 7a1dcae1..63ffcb21 100644 --- a/pyplumio/helpers/factory.py +++ b/pyplumio/helpers/factory.py @@ -1,20 +1,23 @@ """Contains a factory helper.""" from __future__ import annotations -import importlib +import asyncio +from importlib import import_module import logging from typing import Any _LOGGER = logging.getLogger(__name__) -def factory(class_path: str, **kwargs: Any) -> Any: - """Return class instance from the class path.""" +async def create_instance(class_path: str, **kwargs: Any) -> Any: + """Return a class instance from the class path.""" + loop = asyncio.get_running_loop() + module_name, class_name = class_path.rsplit(".", 1) try: - module_name, class_name = class_path.rsplit(".", 1) - return getattr( - importlib.import_module("." + module_name, "pyplumio"), class_name - )(**kwargs) + module = await loop.run_in_executor( + None, import_module, "." + module_name, "pyplumio" + ) + return getattr(module, class_name)(**kwargs) except Exception: _LOGGER.error("Failed to load module (%s)", class_path) raise diff --git a/pyplumio/helpers/parameter.py b/pyplumio/helpers/parameter.py index b9b282a8..9e3408d5 100644 --- a/pyplumio/helpers/parameter.py +++ b/pyplumio/helpers/parameter.py @@ -166,6 +166,10 @@ async def _confirm_update(self, parameter: Parameter) -> None: """Set parameter as no longer pending update.""" self._pending_update = False + async def create_request(self) -> Request: + """Create a request to change the parameter.""" + raise NotImplementedError + async def set(self, value: ParameterValueType, retries: int = SET_RETRIES) -> bool: """Set a parameter value.""" if (value := _normalize_parameter_value(value)) == self.values.value: @@ -188,7 +192,7 @@ async def set(self, value: ParameterValueType, retries: int = SET_RETRIES) -> bo self.device.unsubscribe(self.description.name, self._confirm_update) return False - await self.device.queue.put(self.request) + await self.device.queue.put(await self.create_request()) await asyncio.sleep(SET_TIMEOUT) retries -= 1 @@ -223,11 +227,6 @@ def unit_of_measurement(self) -> UnitOfMeasurement | Literal["%"] | None: """Return the unit of measurement.""" return self.description.unit_of_measurement - @property - def request(self) -> Request: - """Return request to change the parameter.""" - raise NotImplementedError - class BinaryParameter(Parameter): """Represents binary device parameter.""" diff --git a/pyplumio/helpers/schedule.py b/pyplumio/helpers/schedule.py index 564f9668..1ca8f678 100644 --- a/pyplumio/helpers/schedule.py +++ b/pyplumio/helpers/schedule.py @@ -9,7 +9,7 @@ from pyplumio.const import STATE_OFF, STATE_ON from pyplumio.devices import AddressableDevice -from pyplumio.helpers.factory import factory +from pyplumio.frames.requests import SetScheduleRequest from pyplumio.structures.schedules import collect_schedule_data TIME_FORMAT: Final = "%H:%M" @@ -161,8 +161,7 @@ def __iter__(self) -> Iterator[ScheduleDay]: def commit(self) -> None: """Commit a weekly schedule to the device.""" self.device.queue.put_nowait( - factory( - "frames.requests.SetScheduleRequest", + SetScheduleRequest( recipient=self.device.address, data=collect_schedule_data(self.name, self.device), ) diff --git a/pyplumio/protocol.py b/pyplumio/protocol.py index f40738f2..2d844713 100644 --- a/pyplumio/protocol.py +++ b/pyplumio/protocol.py @@ -4,7 +4,6 @@ from abc import ABC, abstractmethod import asyncio from collections.abc import Awaitable, Callable -from functools import cache import logging from typing import cast @@ -19,7 +18,7 @@ from pyplumio.frames import Frame from pyplumio.frames.requests import StartMasterRequest from pyplumio.helpers.event_manager import EventManager -from pyplumio.helpers.factory import factory +from pyplumio.helpers.factory import create_instance from pyplumio.stream import FrameReader, FrameWriter from pyplumio.structures.network_info import ( EthernetParameters, @@ -202,7 +201,9 @@ async def frame_producer( write_queue.task_done() if (response := await reader.read()) is not None: - device = self.get_device_entry(response.sender) + device = await self.get_device_entry( + cast(DeviceType, response.sender) + ) read_queue.put_nowait((device, response)) except FrameDataError as e: @@ -229,16 +230,17 @@ async def frame_consumer(self, read_queue: asyncio.Queue) -> None: device.handle_frame(frame) read_queue.task_done() - @cache - def get_device_entry(self, device_type: DeviceType) -> AddressableDevice: + async def get_device_entry(self, device_type: DeviceType) -> AddressableDevice: """Set up device entry.""" handler, name = get_device_handler_and_name(device_type) - return self.data.setdefault(name, self._create_device_entry(name, handler)) + return self.data.setdefault( + name, await self._create_device_entry(name, handler) + ) - def _create_device_entry(self, name: str, handler: str) -> AddressableDevice: + async def _create_device_entry(self, name: str, handler: str) -> AddressableDevice: """Create device entry.""" write_queue = self.queues[1] - device: AddressableDevice = factory( + device: AddressableDevice = await create_instance( handler, queue=write_queue, network=self._network ) device.dispatch_nowait(ATTR_CONNECTED, True) diff --git a/pyplumio/stream.py b/pyplumio/stream.py index 9b8930c1..e1ba8c44 100644 --- a/pyplumio/stream.py +++ b/pyplumio/stream.py @@ -9,7 +9,7 @@ from pyplumio.const import DeviceType from pyplumio.exceptions import ChecksumError, ReadError from pyplumio.frames import FRAME_START, Frame, bcc, get_frame_handler, struct_header -from pyplumio.helpers.factory import factory +from pyplumio.helpers.factory import create_instance from pyplumio.helpers.timeout import timeout READER_TIMEOUT: Final = 10 @@ -132,7 +132,7 @@ async def read(self) -> Frame | None: if payload[-2] != bcc(header + payload[:-2]): raise ChecksumError(f"Incorrect frame checksum ({payload[-2]})") - frame: Frame = factory( + frame: Frame = await create_instance( get_frame_handler(frame_type=payload[0]), recipient=recipient, message=payload[1:-2], diff --git a/pyplumio/structures/ecomax_parameters.py b/pyplumio/structures/ecomax_parameters.py index 19edc39b..709e7211 100644 --- a/pyplumio/structures/ecomax_parameters.py +++ b/pyplumio/structures/ecomax_parameters.py @@ -16,7 +16,7 @@ ) from pyplumio.devices import AddressableDevice from pyplumio.frames import Request -from pyplumio.helpers.factory import factory +from pyplumio.helpers.factory import create_instance from pyplumio.helpers.parameter import ( BinaryParameter, BinaryParameterDescription, @@ -44,39 +44,10 @@ class EcomaxParameter(Parameter): device: AddressableDevice description: EcomaxParameterDescription - async def set(self, value: ParameterValueType, retries: int = 5) -> bool: - """Set a parameter value.""" - if isinstance(value, (int, float)): - value = int((value + self.description.offset) / self.description.multiplier) - - return await super().set(value, retries) - - @property - def value(self) -> ParameterValueType: - """Return the parameter value.""" - return ( - self.values.value - self.description.offset - ) * self.description.multiplier - - @property - def min_value(self) -> ParameterValueType: - """Return the minimum allowed value.""" - return ( - self.values.min_value - self.description.offset - ) * self.description.multiplier - - @property - def max_value(self) -> ParameterValueType: - """Return the maximum allowed value.""" - return ( - self.values.max_value - self.description.offset - ) * self.description.multiplier - - @property - def request(self) -> Request: - """Return request to change the parameter.""" + async def create_request(self) -> Request: + """Create a request to change the parameter.""" if self.description.name == ATTR_ECOMAX_CONTROL: - request: Request = factory( + request: Request = await create_instance( "frames.requests.EcomaxControlRequest", recipient=self.device.address, data={ @@ -85,7 +56,7 @@ def request(self) -> Request: ) elif self.description.name == ATTR_THERMOSTAT_PROFILE: - request = factory( + request = await create_instance( "frames.requests.SetThermostatParameterRequest", recipient=self.device.address, data={ @@ -97,7 +68,7 @@ def request(self) -> Request: ) else: - request = factory( + request = await create_instance( "frames.requests.SetEcomaxParameterRequest", recipient=self.device.address, data={ @@ -108,6 +79,34 @@ def request(self) -> Request: return request + async def set(self, value: ParameterValueType, retries: int = 5) -> bool: + """Set a parameter value.""" + if isinstance(value, (int, float)): + value = int((value + self.description.offset) / self.description.multiplier) + + return await super().set(value, retries) + + @property + def value(self) -> ParameterValueType: + """Return the parameter value.""" + return ( + self.values.value - self.description.offset + ) * self.description.multiplier + + @property + def min_value(self) -> ParameterValueType: + """Return the minimum allowed value.""" + return ( + self.values.min_value - self.description.offset + ) * self.description.multiplier + + @property + def max_value(self) -> ParameterValueType: + """Return the maximum allowed value.""" + return ( + self.values.max_value - self.description.offset + ) * self.description.multiplier + class EcomaxBinaryParameter(BinaryParameter, EcomaxParameter): """Represents an ecoMAX binary parameter.""" diff --git a/pyplumio/structures/mixer_parameters.py b/pyplumio/structures/mixer_parameters.py index c4b135fc..22838fa2 100644 --- a/pyplumio/structures/mixer_parameters.py +++ b/pyplumio/structures/mixer_parameters.py @@ -3,7 +3,7 @@ from collections.abc import Generator from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Final +from typing import TYPE_CHECKING, Any, Final, cast from pyplumio.const import ( ATTR_DEVICE_INDEX, @@ -13,7 +13,7 @@ UnitOfMeasurement, ) from pyplumio.frames import Request -from pyplumio.helpers.factory import factory +from pyplumio.helpers.factory import create_instance from pyplumio.helpers.parameter import ( BinaryParameter, BinaryParameterDescription, @@ -42,6 +42,21 @@ class MixerParameter(Parameter): device: Mixer description: MixerParameterDescription + async def create_request(self) -> Request: + """Create a request to change the parameter.""" + return cast( + Request, + await create_instance( + "frames.requests.SetMixerParameterRequest", + recipient=self.device.parent.address, + data={ + ATTR_INDEX: self._index, + ATTR_VALUE: self.values.value, + ATTR_DEVICE_INDEX: self.device.index, + }, + ), + ) + async def set(self, value: ParameterValueType, retries: int = 5) -> bool: """Set a parameter value.""" if isinstance(value, (int, float)): @@ -70,20 +85,6 @@ def max_value(self) -> ParameterValueType: self.values.max_value - self.description.offset ) * self.description.multiplier - @property - def request(self) -> Request: - """Return request to change the parameter.""" - request: Request = factory( - "frames.requests.SetMixerParameterRequest", - recipient=self.device.parent.address, - data={ - ATTR_INDEX: self._index, - ATTR_VALUE: self.values.value, - ATTR_DEVICE_INDEX: self.device.index, - }, - ) - return request - class MixerBinaryParameter(BinaryParameter, MixerParameter): """Represents a mixer binary parameter.""" diff --git a/pyplumio/structures/schedules.py b/pyplumio/structures/schedules.py index 8f2f4610..37288570 100644 --- a/pyplumio/structures/schedules.py +++ b/pyplumio/structures/schedules.py @@ -5,13 +5,13 @@ from dataclasses import dataclass from functools import reduce from itertools import chain -from typing import Any, Final +from typing import Any, Final, cast from pyplumio.const import ATTR_PARAMETER, ATTR_SCHEDULE, ATTR_SWITCH, ATTR_TYPE from pyplumio.devices import AddressableDevice, Device from pyplumio.exceptions import FrameDataError from pyplumio.frames import Request -from pyplumio.helpers.factory import factory +from pyplumio.helpers.factory import create_instance from pyplumio.helpers.parameter import ( BinaryParameter, BinaryParameterDescription, @@ -81,16 +81,17 @@ class ScheduleParameter(Parameter): device: AddressableDevice - @property - def request(self) -> Request: - """Return request to change the parameter.""" + async def create_request(self) -> Request: + """Create a request to change the parameter.""" schedule_name, _ = self.description.name.split("_schedule_", 1) - request: Request = factory( - "frames.requests.SetScheduleRequest", - recipient=self.device.address, - data=collect_schedule_data(schedule_name, self.device), + return cast( + Request, + await create_instance( + "frames.requests.SetScheduleRequest", + recipient=self.device.address, + data=collect_schedule_data(schedule_name, self.device), + ), ) - return request class ScheduleBinaryParameter(ScheduleParameter, BinaryParameter): diff --git a/pyplumio/structures/thermostat_parameters.py b/pyplumio/structures/thermostat_parameters.py index 6200bbbb..8250981c 100644 --- a/pyplumio/structures/thermostat_parameters.py +++ b/pyplumio/structures/thermostat_parameters.py @@ -3,7 +3,7 @@ from collections.abc import Generator from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Final +from typing import TYPE_CHECKING, Any, Final, cast from pyplumio.const import ( ATTR_INDEX, @@ -14,7 +14,7 @@ ) from pyplumio.devices import AddressableDevice from pyplumio.frames import Request -from pyplumio.helpers.factory import factory +from pyplumio.helpers.factory import create_instance from pyplumio.helpers.parameter import ( BinaryParameter, BinaryParameterDescription, @@ -59,6 +59,24 @@ def __init__( self.offset = offset super().__init__(device, values, description, index) + async def create_request(self) -> Request: + """Create a request to change the parameter.""" + return cast( + Request, + await create_instance( + "frames.requests.SetThermostatParameterRequest", + recipient=self.device.parent.address, + data={ + # Increase the index by one to account for thermostat + # profile, which is being set at ecoMAX device level. + ATTR_INDEX: self._index + 1, + ATTR_VALUE: self.values.value, + ATTR_OFFSET: self.offset, + ATTR_SIZE: self.description.size, + }, + ), + ) + async def set(self, value: ParameterValueType, retries: int = 5) -> bool: """Set a parameter value.""" if isinstance(value, (int, float)): @@ -81,23 +99,6 @@ def max_value(self) -> ParameterValueType: """Return the maximum allowed value.""" return self.values.max_value * self.description.multiplier - @property - def request(self) -> Request: - """Return request to change the parameter.""" - request: Request = factory( - "frames.requests.SetThermostatParameterRequest", - recipient=self.device.parent.address, - data={ - # Increase the index by one to account for thermostat - # profile, which is being set at ecoMAX device level. - ATTR_INDEX: self._index + 1, - ATTR_VALUE: self.values.value, - ATTR_OFFSET: self.offset, - ATTR_SIZE: self.description.size, - }, - ) - return request - class ThermostatBinaryParameter(BinaryParameter, ThermostatParameter): """Represents a thermostat binary parameter.""" diff --git a/tests/helpers/test_factory.py b/tests/helpers/test_factory.py index 583ff5c7..c5add41e 100644 --- a/tests/helpers/test_factory.py +++ b/tests/helpers/test_factory.py @@ -3,22 +3,22 @@ import pytest from pyplumio.frames.requests import StopMasterRequest -from pyplumio.helpers.factory import factory +from pyplumio.helpers.factory import create_instance -def test_get_object() -> None: +async def test_get_object() -> None: """Test getting an object via class path.""" - cls = factory("frames.requests.StopMasterRequest") + cls = await create_instance("frames.requests.StopMasterRequest") assert isinstance(cls, StopMasterRequest) -def test_get_object_with_nonexistent_class() -> None: +async def test_get_object_with_nonexistent_class() -> None: """Test getting an object via class path for nonexistent class.""" with pytest.raises(AttributeError): - factory("frames.requests.NonExistent") + await create_instance("frames.requests.NonExistent") -def test_get_object_with_nonexistent_module() -> None: +async def test_get_object_with_nonexistent_module() -> None: """Test getting an object via class path for a class within nonexistent module.""" with pytest.raises(ModuleNotFoundError): - factory("frames.request.StopMasterRequest") + await create_instance("frames.request.StopMasterRequest") diff --git a/tests/helpers/test_parameter.py b/tests/helpers/test_parameter.py index f2ab8bf0..9d4cf3d3 100644 --- a/tests/helpers/test_parameter.py +++ b/tests/helpers/test_parameter.py @@ -20,9 +20,8 @@ class TestParameter(Parameter): __test__: bool = False - @property - def request(self) -> Request: - """A request to change the parameter.""" + async def create_request(self) -> Request: + """Create a request to change the parameter.""" return Request() @@ -73,7 +72,7 @@ async def test_parameter_values(parameter: Parameter) -> None: assert parameter.max_value == 5 -def test_base_parameter_request(ecomax: EcoMAX) -> None: +async def test_base_parameter_request(ecomax: EcoMAX) -> None: """Test that a base class request throws not implemented error.""" parameter = Parameter( device=ecomax, @@ -82,7 +81,7 @@ def test_base_parameter_request(ecomax: EcoMAX) -> None: ) with pytest.raises(NotImplementedError): - assert not parameter.request + assert not await parameter.create_request() @patch("pyplumio.devices.ecomax.EcoMAX.subscribe_once") diff --git a/tests/helpers/test_schedule.py b/tests/helpers/test_schedule.py index 383d57cd..f20deb0b 100644 --- a/tests/helpers/test_schedule.py +++ b/tests/helpers/test_schedule.py @@ -100,8 +100,8 @@ def test_schedule(schedule: Schedule) -> None: assert next(schedule_iter)[0] -@patch("pyplumio.helpers.schedule.factory") -def test_schedule_commit(mock_factory, schedule: Schedule) -> None: +@patch("pyplumio.helpers.schedule.SetScheduleRequest") +def test_schedule_commit(mock_set_schedule_request, schedule: Schedule) -> None: """Test committing a schedule.""" schedule.device = Mock(spec=Device) schedule.device.address = DeviceType.ECOMAX @@ -113,8 +113,7 @@ def test_schedule_commit(mock_factory, schedule: Schedule) -> None: schedule.device.queue = Mock(spec=asyncio.Queue) schedule.commit() - mock_factory.assert_called_once_with( - "frames.requests.SetScheduleRequest", + mock_set_schedule_request.assert_called_once_with( recipient=DeviceType.ECOMAX, data={ ATTR_TYPE: "test", diff --git a/tests/test_devices.py b/tests/test_devices.py index 0be970e2..9be80fbe 100644 --- a/tests/test_devices.py +++ b/tests/test_devices.py @@ -189,9 +189,11 @@ async def test_ecomax_data_callbacks(ecomax: EcoMAX) -> None: ecomax_control = await ecomax.get(ATTR_ECOMAX_CONTROL) assert isinstance(ecomax_control, EcomaxBinaryParameter) - assert isinstance(ecomax_control.request, EcomaxControlRequest) assert ecomax_control.value == STATE_OFF - assert ecomax_control.request.data == {ATTR_VALUE: 0} + + ecomax_control_request = await ecomax_control.create_request() + assert isinstance(ecomax_control_request, EcomaxControlRequest) + assert ecomax_control_request.data == {ATTR_VALUE: 0} # Check ecomax_control reference equility. ecomax.handle_frame(SensorDataMessage(data={ATTR_STATE: DeviceState.WORKING})) @@ -199,7 +201,8 @@ async def test_ecomax_data_callbacks(ecomax: EcoMAX) -> None: ecomax_control2 = await ecomax.get(ATTR_ECOMAX_CONTROL) assert ecomax_control2 is ecomax_control assert ecomax_control2.value == STATE_ON - assert ecomax_control2.request.data == {ATTR_VALUE: 1} + ecomax_control2_request = await ecomax_control.create_request() + assert ecomax_control2_request.data == {ATTR_VALUE: 1} async def test_ecomax_naming_collisions(ecomax: EcoMAX) -> None: @@ -223,10 +226,11 @@ async def test_ecomax_parameters_callbacks(ecomax: EcoMAX) -> None: await ecomax.wait_until_done() fuzzy_logic = await ecomax.get("fuzzy_logic") assert isinstance(fuzzy_logic, EcomaxBinaryParameter) - assert isinstance(fuzzy_logic.request, SetEcomaxParameterRequest) assert fuzzy_logic == STATE_ON assert fuzzy_logic.value == STATE_ON - assert fuzzy_logic.request.data == { + fuzzy_logic_request = await fuzzy_logic.create_request() + assert isinstance(fuzzy_logic_request, SetEcomaxParameterRequest) + assert fuzzy_logic_request.data == { ATTR_INDEX: 18, ATTR_VALUE: 1, } @@ -397,13 +401,14 @@ async def test_thermostat_parameters_callbacks(ecomax: EcoMAX) -> None: assert len(thermostat.data) == 13 party_target_temp = await thermostat.get("party_target_temp") assert isinstance(party_target_temp, ThermostatParameter) - assert isinstance(party_target_temp.request, SetThermostatParameterRequest) assert isinstance(party_target_temp.device, Thermostat) assert party_target_temp.value == 22.0 assert party_target_temp.min_value == 10.0 assert party_target_temp.max_value == 35.0 assert party_target_temp.offset == 0 - assert party_target_temp.request.data == { + party_target_temp_request = await party_target_temp.create_request() + assert isinstance(party_target_temp_request, SetThermostatParameterRequest) + assert party_target_temp_request.data == { ATTR_INDEX: 2, ATTR_VALUE: 220, ATTR_OFFSET: 0, @@ -442,11 +447,13 @@ async def test_thermostat_profile_callbacks(ecomax: EcoMAX) -> None: ) await ecomax.wait_until_done() thermostat_profile = await ecomax.get(ATTR_THERMOSTAT_PROFILE) + assert isinstance(thermostat_profile, EcomaxParameter) assert thermostat_profile.value == 0.0 assert thermostat_profile.min_value == 0.0 assert thermostat_profile.max_value == 5.0 - assert isinstance(thermostat_profile.request, SetThermostatParameterRequest) - assert thermostat_profile.request.data == { + thermostat_profile_request = await thermostat_profile.create_request() + assert isinstance(thermostat_profile_request, SetThermostatParameterRequest) + assert thermostat_profile_request.data == { ATTR_INDEX: 0, ATTR_VALUE: 0, ATTR_OFFSET: 0, @@ -470,12 +477,15 @@ async def test_mixer_parameters_callbacks(ecomax: EcoMAX) -> None: assert len(mixer.data) == 7 mixer_target_temp = await mixer.get("mixer_target_temp") assert isinstance(mixer_target_temp, MixerParameter) - assert isinstance(mixer_target_temp.request, SetMixerParameterRequest) assert isinstance(mixer_target_temp.device, Mixer) assert mixer_target_temp.value == 40.0 assert mixer_target_temp.min_value == 30.0 assert mixer_target_temp.max_value == 60.0 - assert mixer_target_temp.request.data == { + + # Test creating a request. + mixer_target_temp_request = await mixer_target_temp.create_request() + assert isinstance(mixer_target_temp_request, SetMixerParameterRequest) + assert mixer_target_temp_request.data == { ATTR_INDEX: 0, ATTR_VALUE: 40, ATTR_DEVICE_INDEX: 0, @@ -534,11 +544,14 @@ async def test_schedule_callback(ecomax: EcoMAX) -> None: "water_heater_schedule_parameter" ) assert isinstance(water_heater_schedule_parameter, ScheduleParameter) - assert isinstance(water_heater_schedule_parameter.request, SetScheduleRequest) assert water_heater_schedule_parameter.value == 5 assert water_heater_schedule_parameter.min_value == 0 assert water_heater_schedule_parameter.max_value == 30 - assert water_heater_schedule_parameter.request.data == { + water_heater_schedule_parameter_request = ( + await water_heater_schedule_parameter.create_request() + ) + assert isinstance(water_heater_schedule_parameter_request, SetScheduleRequest) + assert water_heater_schedule_parameter_request.data == { ATTR_TYPE: "water_heater", ATTR_SWITCH: ecomax.data[f"water_heater_{ATTR_SCHEDULE_SWITCH}"], ATTR_PARAMETER: ecomax.data[f"water_heater_{ATTR_SCHEDULE_PARAMETER}"], diff --git a/tests/test_protocol.py b/tests/test_protocol.py index 6cf9de7d..4057a003 100644 --- a/tests/test_protocol.py +++ b/tests/test_protocol.py @@ -335,7 +335,7 @@ async def test_async_protocol_frame_consumer( "pyplumio.protocol.AsyncProtocol.queues", return_value=(mock_read_queue, mock_write_queue), ) as mock_queues: - ecomax = async_protocol.get_device_entry(DeviceType.ECOMAX) + ecomax = await async_protocol.get_device_entry(DeviceType.ECOMAX) mock_read_queue.get.side_effect = ( (ecomax, CheckDeviceRequest()),