PyPlumIO 0.5.16__py3-none-any.whl → 0.5.18__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyPlumIO
3
- Version: 0.5.16
3
+ Version: 0.5.18
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
@@ -25,22 +25,22 @@ License-File: LICENSE
25
25
  Requires-Dist: pyserial-asyncio ==0.6
26
26
  Provides-Extra: dev
27
27
  Requires-Dist: pyplumio[docs,test] ; extra == 'dev'
28
- Requires-Dist: pre-commit ==3.6.2 ; extra == 'dev'
28
+ Requires-Dist: pre-commit ==3.7.0 ; extra == 'dev'
29
29
  Requires-Dist: tomli ==2.0.1 ; extra == 'dev'
30
30
  Provides-Extra: docs
31
- Requires-Dist: sphinx ==7.2.6 ; extra == 'docs'
31
+ Requires-Dist: sphinx ==7.3.7 ; extra == 'docs'
32
32
  Requires-Dist: sphinx-rtd-theme ==2.0.0 ; extra == 'docs'
33
33
  Requires-Dist: readthedocs-sphinx-search ==0.3.2 ; extra == 'docs'
34
34
  Provides-Extra: test
35
35
  Requires-Dist: codespell ==2.2.6 ; extra == 'test'
36
- Requires-Dist: coverage ==7.4.3 ; extra == 'test'
37
- Requires-Dist: mypy ==1.8.0 ; extra == 'test'
36
+ Requires-Dist: coverage ==7.5.1 ; extra == 'test'
37
+ Requires-Dist: mypy ==1.10.0 ; extra == 'test'
38
38
  Requires-Dist: pyserial-asyncio-fast ==0.11 ; extra == 'test'
39
- Requires-Dist: pytest ==8.1.0 ; extra == 'test'
40
- Requires-Dist: pytest-asyncio ==0.23.5 ; extra == 'test'
41
- Requires-Dist: ruff ==0.3.0 ; extra == 'test'
42
- Requires-Dist: tox ==4.13.0 ; extra == 'test'
43
- Requires-Dist: types-pyserial ==3.5.0.20240205 ; extra == 'test'
39
+ Requires-Dist: pytest ==8.2.0 ; extra == 'test'
40
+ Requires-Dist: pytest-asyncio ==0.23.6 ; extra == 'test'
41
+ Requires-Dist: ruff ==0.4.3 ; extra == 'test'
42
+ Requires-Dist: tox ==4.15.0 ; extra == 'test'
43
+ Requires-Dist: types-pyserial ==3.5.0.20240311 ; extra == 'test'
44
44
 
45
45
  # PyPlumIO is a native ecoNET library for Plum ecoMAX controllers.
46
46
  [![PyPI version](https://badge.fury.io/py/PyPlumIO.svg)](https://badge.fury.io/py/PyPlumIO)
@@ -1,16 +1,16 @@
1
1
  pyplumio/__init__.py,sha256=4v9BaIkJ440qu2ITrcwVLOg9KO8k2W1gnGHe86sDLM8,3292
2
2
  pyplumio/__main__.py,sha256=oop76iR-XDHhMFhW4LO8-xTnBHsqzUQ5VVh__94OaqY,499
3
- pyplumio/_version.py,sha256=2cL-_pzEwHOhqhZvMuQ5G5-PKmh58YpDDGsGLMEepvQ,413
3
+ pyplumio/_version.py,sha256=TJSWAybqN12Cw_kxmDDEn4jE4KWfkCe6C38YXdi-hN8,413
4
4
  pyplumio/connection.py,sha256=0PmpNOcV7RgSfEeM7Wnhtbw0TpdtMTyqpJPQ2h7IX9M,6199
5
5
  pyplumio/const.py,sha256=4_VRx5mY7qf5fwaUluVlhuK_NIb7bClzhnhRLA-3GkY,3914
6
6
  pyplumio/exceptions.py,sha256=3tgfe0GD-T-DV2TUybsv7dwsk9P9f2g-4Gd8jnXw6DI,698
7
7
  pyplumio/filters.py,sha256=kXR4SUS7YXGaljW35NpEB9prHgxDKpsf_U1ACMTnh5I,11174
8
- pyplumio/protocol.py,sha256=1TTSLn2UoTruRCiChKE_ZFw6a8pJSzqNKpPMKvO6fSY,8663
8
+ pyplumio/protocol.py,sha256=btBJiCyB2ys-tiQOm8kj97_Y7yesO4cIOKrEGbrCrsk,8787
9
9
  pyplumio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- pyplumio/stream.py,sha256=sOg4jTPRVqQFvp0UartRFZWTmzdp3DSns-5VKqh2HgU,4352
10
+ pyplumio/stream.py,sha256=UPqlUmNBobSS4SvS5BI19g_FeSTLpLj1k3l7OeE32bI,4374
11
11
  pyplumio/utils.py,sha256=h46IS7hOHFbAFGGmnyAUVwlfk0m_eSwa3U8oZq6-bJY,687
12
- pyplumio/devices/__init__.py,sha256=qvj8P4dcAxYUcIIphS2eaYAHmi6Y_T3uv462nynSyJ8,6357
13
- pyplumio/devices/ecomax.py,sha256=r4GCmETK8cMJwDI6zNp53QMTqmFiIyV9DM5ClG6_c-c,16773
12
+ pyplumio/devices/__init__.py,sha256=zw0DDvXLdASaMeTUMZog-osKhsugNpPMYU4dV1Jr4iU,6379
13
+ pyplumio/devices/ecomax.py,sha256=cdPZ2LbUvH0l8hpwWxZiB570OloIpeilXWdHEDlixW4,16795
14
14
  pyplumio/devices/ecoster.py,sha256=QH68bzs199x9Id-wOY6J5WddyR69STNQi8SxaWDwKG4,312
15
15
  pyplumio/devices/mixer.py,sha256=a-1klZuEO7dLQfpdOor0vUZ9ksllf8D1w0AbMeGmE_g,2946
16
16
  pyplumio/devices/thermostat.py,sha256=CXHAWwqyDATcI8UYNcTv6TK3YHYDSUyHE3Txyy9fVAI,2401
@@ -21,9 +21,9 @@ pyplumio/frames/responses.py,sha256=7MHwXkNVTcwYpWRAXBMiPxmaJw3vgX9kgoaBTXeRy60,
21
21
  pyplumio/helpers/__init__.py,sha256=H2xxdkF-9uADLwEbfBUoxNTdwru3L5Z2cfJjgsuRsn0,31
22
22
  pyplumio/helpers/data_types.py,sha256=UIbJu0JfXaNCiHlg8EnYOfeyKq9Mxiu6j_D8GyRudZk,8211
23
23
  pyplumio/helpers/event_manager.py,sha256=E9gMGCxOOBNZgnHCwpb8rtFgKsNIkjHvIN73XBgwQDk,6004
24
- pyplumio/helpers/factory.py,sha256=6GUgny_h6FZUabpL9p6Sg3voaR7U10utNgpv6OsM2j0,562
25
- pyplumio/helpers/parameter.py,sha256=6fqrPovJLlDoN5PydhcL0myJyEmRxxkMzJtYiPicpTE,8682
26
- pyplumio/helpers/schedule.py,sha256=UOo672ZJJbO4DjwWqU_6J30myiW5fqnHVgU-goqeuRI,4943
24
+ pyplumio/helpers/factory.py,sha256=Ld_PbKnkj0HFnuYcoDwcitiMRa-88UQKXcsE5vdgLqs,824
25
+ pyplumio/helpers/parameter.py,sha256=ihdom_tON2urYXmS7x_2-zk2I-PcogdjILE7tOiGR8M,8698
26
+ pyplumio/helpers/schedule.py,sha256=395yTDAKV43so5vEG-q0mgqXGoFVWKpIyhdmqzKT-X4,4958
27
27
  pyplumio/helpers/task_manager.py,sha256=P17Nw9HDbA9NMSdkJo2WQRbEsykzzFSwQyyRzI3uFPk,1089
28
28
  pyplumio/helpers/timeout.py,sha256=bWBWvLPpgjCvdG5hlrSTXok_CsLme-jGnY9rHwupRc0,1286
29
29
  pyplumio/helpers/typing.py,sha256=V3uYCMyC4oePM7YzL0S-xEsyTgjgDbkOM0VNe-1LBPo,684
@@ -32,13 +32,13 @@ pyplumio/structures/__init__.py,sha256=-nbLcQvbWcs2EmnChqJmMVo1CUfj8lqMHb8yK1mV4
32
32
  pyplumio/structures/alerts.py,sha256=v--wbEKoB07r9KHMuZgMRT1dk4r8dPwxxB5cPv4lzZY,3259
33
33
  pyplumio/structures/boiler_load.py,sha256=HVPKt53VWvp2KDuSK1B9kcpX1h3Bz3GPBqBI4Oscm2Q,837
34
34
  pyplumio/structures/boiler_power.py,sha256=tP00IMz5qVQaePr70uTz_jCLoXHnmj_g3pvKxdNvenc,900
35
- pyplumio/structures/ecomax_parameters.py,sha256=djm8BY9bxWUOcHUxn8NQ26nvdsAXiU_hrxh1ljIdDY8,26133
35
+ pyplumio/structures/ecomax_parameters.py,sha256=Ywiabk0panLHSScv8SZC_cBmkdSXuXPEfDFyEsjR0ts,25895
36
36
  pyplumio/structures/fan_power.py,sha256=fGU_BTPTtAplnjmTQQHhMXdoiHVYOdLlZ_PDA9F5u9c,870
37
37
  pyplumio/structures/frame_versions.py,sha256=9lFnrlxkok_3CTbXz38cntacnubWYGcaubDnYq9NOAM,1559
38
38
  pyplumio/structures/fuel_consumption.py,sha256=H23GVw9s0fbURvJHAfReTEvGp5q-oPOidh1mToOO3ec,975
39
39
  pyplumio/structures/fuel_level.py,sha256=27QfxB_LXAREmHs7q8KyohpLbagLUZolCQoZJUDTPz0,1068
40
40
  pyplumio/structures/lambda_sensor.py,sha256=JdKHWN2RcMgCXhMZ0IhYCYrHWnw2TgsR-iitLn7QLe0,1586
41
- pyplumio/structures/mixer_parameters.py,sha256=z29fhv8NQmdX_uPNSLCn505XhGs37qnzAkZ0P93GJH0,8304
41
+ pyplumio/structures/mixer_parameters.py,sha256=UsOey8D5d7rrUACwOcoBB_x2lHQYKC2Xsoxqcpf6DG4,8380
42
42
  pyplumio/structures/mixer_sensors.py,sha256=f6BjpX-ENGlKIDOOWUHMlb0TZ5HMYD9dIkRoSvgRXHg,2259
43
43
  pyplumio/structures/modules.py,sha256=t332XxB-6dTtDbY9mNuqTqb21zDy2XG_17pmWgJfGvw,2519
44
44
  pyplumio/structures/network_info.py,sha256=pLbCB5Z8nCB6efb6ZZImw3TaVAOB3Af5K1z9vVbuGgo,4054
@@ -49,13 +49,13 @@ pyplumio/structures/product_info.py,sha256=Os0AS88geH2JqvVRC2zQxEucmGJoDzR3ed3Pl
49
49
  pyplumio/structures/program_version.py,sha256=WX5tM3X3aaJCjNd5rsAN6EXivBXM-kLXQvhfQR8wKIM,2358
50
50
  pyplumio/structures/regulator_data.py,sha256=9d7fGcZ1SI2QosUQSd0CpPqaHpgXihqV5-QgHWGzkNU,2379
51
51
  pyplumio/structures/regulator_data_schema.py,sha256=5uwdw8nCqSX9Ex5jH9t4pghj6fLRRsCnGQx69LVGAy0,1504
52
- pyplumio/structures/schedules.py,sha256=9CaWAWJ1LAFUcIjqE05djc4AFJA7uaKex41D2dJQEdI,6381
52
+ pyplumio/structures/schedules.py,sha256=j7kuT7gYesFKJJ4yMrW6JVO0aJX9O6PT2D_PTCQre04,6441
53
53
  pyplumio/structures/statuses.py,sha256=zjDQTU5fdORRzRkvlkzplgVS8A3AdyHG2qihfkdOHIM,1165
54
54
  pyplumio/structures/temperatures.py,sha256=O8rANFN-wz5NvTWqh_25oUfFajjVyI9hoOMmz8vkKHg,2336
55
- pyplumio/structures/thermostat_parameters.py,sha256=k35TdGTqVYXr7Eug-kRENOc-2UMy3nAYjY9vgmqZGv0,7846
55
+ pyplumio/structures/thermostat_parameters.py,sha256=1SJzHguN12JE-e2utwA448VgUkFddQxBQy3m66qCobk,7934
56
56
  pyplumio/structures/thermostat_sensors.py,sha256=N9nm3Rp1Rhb8wUPUkJIX56olZztixoxyFxIxP4R5P2g,3176
57
- PyPlumIO-0.5.16.dist-info/LICENSE,sha256=m-UuZFjXJ22uPTGm9kSHS8bqjsf5T8k2wL9bJn1Y04o,1088
58
- PyPlumIO-0.5.16.dist-info/METADATA,sha256=-afOinjgRM3wwES8fLDLgSQjnkRUKab-toO0sHqHc8U,5414
59
- PyPlumIO-0.5.16.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
60
- PyPlumIO-0.5.16.dist-info/top_level.txt,sha256=kNBz9UPPkPD9teDn3U_sEy5LjzwLm9KfADCXtBlbw8A,9
61
- PyPlumIO-0.5.16.dist-info/RECORD,,
57
+ PyPlumIO-0.5.18.dist-info/LICENSE,sha256=m-UuZFjXJ22uPTGm9kSHS8bqjsf5T8k2wL9bJn1Y04o,1088
58
+ PyPlumIO-0.5.18.dist-info/METADATA,sha256=fHzLAiOHlj3nYHmvnm4OZsFBUMyJcSnSgmRg7zbbr0s,5415
59
+ PyPlumIO-0.5.18.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
60
+ PyPlumIO-0.5.18.dist-info/top_level.txt,sha256=kNBz9UPPkPD9teDn3U_sEy5LjzwLm9KfADCXtBlbw8A,9
61
+ PyPlumIO-0.5.18.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
pyplumio/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.5.16'
16
- __version_tuple__ = version_tuple = (0, 5, 16)
15
+ __version__ = version = '0.5.18'
16
+ __version_tuple__ = version_tuple = (0, 5, 18)
@@ -11,7 +11,7 @@ from pyplumio.const import ATTR_FRAME_ERRORS, ATTR_LOADED, DeviceType, FrameType
11
11
  from pyplumio.exceptions import UnknownDeviceError
12
12
  from pyplumio.frames import DataFrameDescription, Frame, Request, get_frame_handler
13
13
  from pyplumio.helpers.event_manager import EventManager
14
- from pyplumio.helpers.factory import factory
14
+ from pyplumio.helpers.factory import create_instance
15
15
  from pyplumio.helpers.parameter import SET_RETRIES, Parameter
16
16
  from pyplumio.helpers.typing import ParameterValueType
17
17
  from pyplumio.structures.network_info import NetworkInfo
@@ -160,7 +160,7 @@ class AddressableDevice(Device, ABC):
160
160
 
161
161
  If value is not available before timeout, retry request.
162
162
  """
163
- request: Request = factory(
163
+ request: Request = await create_instance(
164
164
  get_frame_handler(frame_type), recipient=self.address
165
165
  )
166
166
 
@@ -28,7 +28,7 @@ from pyplumio.frames import (
28
28
  get_frame_handler,
29
29
  is_known_frame_type,
30
30
  )
31
- from pyplumio.helpers.factory import factory
31
+ from pyplumio.helpers.factory import create_instance
32
32
  from pyplumio.helpers.parameter import ParameterValues
33
33
  from pyplumio.helpers.schedule import Schedule, ScheduleDay
34
34
  from pyplumio.structures.alerts import ATTR_TOTAL_ALERTS
@@ -235,7 +235,7 @@ class EcoMAX(AddressableDevice):
235
235
  and not self._has_frame_version(frame_type, version)
236
236
  ):
237
237
  # We don't have this frame or it's version has changed.
238
- request: Request = factory(
238
+ request: Request = await create_instance(
239
239
  get_frame_handler(frame_type), recipient=self.address
240
240
  )
241
241
  self.queue.put_nowait(request)
@@ -1,20 +1,28 @@
1
1
  """Contains a factory helper."""
2
2
  from __future__ import annotations
3
3
 
4
+ import asyncio
4
5
  import importlib
5
6
  import logging
7
+ from types import ModuleType
6
8
  from typing import Any
7
9
 
8
10
  _LOGGER = logging.getLogger(__name__)
9
11
 
10
12
 
11
- def factory(class_path: str, **kwargs: Any) -> Any:
12
- """Return class instance from the class path."""
13
+ async def _load_module(module_name: str) -> ModuleType:
14
+ """Load a module by name."""
15
+ return await asyncio.get_running_loop().run_in_executor(
16
+ None, importlib.import_module, f".{module_name}", "pyplumio"
17
+ )
18
+
19
+
20
+ async def create_instance(class_path: str, **kwargs: Any) -> Any:
21
+ """Return a class instance from the class path."""
22
+ module_name, class_name = class_path.rsplit(".", 1)
13
23
  try:
14
- module_name, class_name = class_path.rsplit(".", 1)
15
- return getattr(
16
- importlib.import_module("." + module_name, "pyplumio"), class_name
17
- )(**kwargs)
24
+ module = await _load_module(module_name)
25
+ return getattr(module, class_name)(**kwargs)
18
26
  except Exception:
19
27
  _LOGGER.error("Failed to load module (%s)", class_path)
20
28
  raise
@@ -166,6 +166,10 @@ class Parameter(ABC):
166
166
  """Set parameter as no longer pending update."""
167
167
  self._pending_update = False
168
168
 
169
+ async def create_request(self) -> Request:
170
+ """Create a request to change the parameter."""
171
+ raise NotImplementedError
172
+
169
173
  async def set(self, value: ParameterValueType, retries: int = SET_RETRIES) -> bool:
170
174
  """Set a parameter value."""
171
175
  if (value := _normalize_parameter_value(value)) == self.values.value:
@@ -188,7 +192,7 @@ class Parameter(ABC):
188
192
  self.device.unsubscribe(self.description.name, self._confirm_update)
189
193
  return False
190
194
 
191
- await self.device.queue.put(self.request)
195
+ await self.device.queue.put(await self.create_request())
192
196
  await asyncio.sleep(SET_TIMEOUT)
193
197
  retries -= 1
194
198
 
@@ -223,11 +227,6 @@ class Parameter(ABC):
223
227
  """Return the unit of measurement."""
224
228
  return self.description.unit_of_measurement
225
229
 
226
- @property
227
- def request(self) -> Request:
228
- """Return request to change the parameter."""
229
- raise NotImplementedError
230
-
231
230
 
232
231
  class BinaryParameter(Parameter):
233
232
  """Represents binary device parameter."""
@@ -9,7 +9,7 @@ from typing import Final, Literal
9
9
 
10
10
  from pyplumio.const import STATE_OFF, STATE_ON
11
11
  from pyplumio.devices import AddressableDevice
12
- from pyplumio.helpers.factory import factory
12
+ from pyplumio.helpers.factory import create_instance
13
13
  from pyplumio.structures.schedules import collect_schedule_data
14
14
 
15
15
  TIME_FORMAT: Final = "%H:%M"
@@ -158,12 +158,11 @@ class Schedule(Iterable):
158
158
  self.saturday,
159
159
  ).__iter__()
160
160
 
161
- def commit(self) -> None:
161
+ async def commit(self) -> None:
162
162
  """Commit a weekly schedule to the device."""
163
- self.device.queue.put_nowait(
164
- factory(
165
- "frames.requests.SetScheduleRequest",
166
- recipient=self.device.address,
167
- data=collect_schedule_data(self.name, self.device),
168
- )
163
+ request = await create_instance(
164
+ "frames.requests.SetScheduleRequest",
165
+ recipient=self.device.address,
166
+ data=collect_schedule_data(self.name, self.device),
169
167
  )
168
+ await self.device.queue.put(request)
pyplumio/protocol.py CHANGED
@@ -4,7 +4,6 @@ from __future__ import annotations
4
4
  from abc import ABC, abstractmethod
5
5
  import asyncio
6
6
  from collections.abc import Awaitable, Callable
7
- from functools import cache
8
7
  import logging
9
8
  from typing import cast
10
9
 
@@ -19,7 +18,7 @@ from pyplumio.exceptions import (
19
18
  from pyplumio.frames import Frame
20
19
  from pyplumio.frames.requests import StartMasterRequest
21
20
  from pyplumio.helpers.event_manager import EventManager
22
- from pyplumio.helpers.factory import factory
21
+ from pyplumio.helpers.factory import create_instance
23
22
  from pyplumio.stream import FrameReader, FrameWriter
24
23
  from pyplumio.structures.network_info import (
25
24
  EthernetParameters,
@@ -202,7 +201,9 @@ class AsyncProtocol(Protocol, EventManager):
202
201
  write_queue.task_done()
203
202
 
204
203
  if (response := await reader.read()) is not None:
205
- device = self.get_device_entry(response.sender)
204
+ device = await self.get_device_entry(
205
+ cast(DeviceType, response.sender)
206
+ )
206
207
  read_queue.put_nowait((device, response))
207
208
 
208
209
  except FrameDataError as e:
@@ -229,16 +230,18 @@ class AsyncProtocol(Protocol, EventManager):
229
230
  device.handle_frame(frame)
230
231
  read_queue.task_done()
231
232
 
232
- @cache
233
- def get_device_entry(self, device_type: DeviceType) -> AddressableDevice:
233
+ async def get_device_entry(self, device_type: DeviceType) -> AddressableDevice:
234
234
  """Set up device entry."""
235
235
  handler, name = get_device_handler_and_name(device_type)
236
- return self.data.setdefault(name, self._create_device_entry(name, handler))
236
+ if name not in self.data:
237
+ self.data[name] = await self._create_device_entry(name, handler)
237
238
 
238
- def _create_device_entry(self, name: str, handler: str) -> AddressableDevice:
239
+ return self.data[name]
240
+
241
+ async def _create_device_entry(self, name: str, handler: str) -> AddressableDevice:
239
242
  """Create device entry."""
240
243
  write_queue = self.queues[1]
241
- device: AddressableDevice = factory(
244
+ device: AddressableDevice = await create_instance(
242
245
  handler, queue=write_queue, network=self._network
243
246
  )
244
247
  device.dispatch_nowait(ATTR_CONNECTED, True)
pyplumio/stream.py CHANGED
@@ -9,7 +9,7 @@ from typing import Final
9
9
  from pyplumio.const import DeviceType
10
10
  from pyplumio.exceptions import ChecksumError, ReadError
11
11
  from pyplumio.frames import FRAME_START, Frame, bcc, get_frame_handler, struct_header
12
- from pyplumio.helpers.factory import factory
12
+ from pyplumio.helpers.factory import create_instance
13
13
  from pyplumio.helpers.timeout import timeout
14
14
 
15
15
  READER_TIMEOUT: Final = 10
@@ -132,7 +132,7 @@ class FrameReader:
132
132
  if payload[-2] != bcc(header + payload[:-2]):
133
133
  raise ChecksumError(f"Incorrect frame checksum ({payload[-2]})")
134
134
 
135
- frame: Frame = factory(
135
+ frame: Frame = await create_instance(
136
136
  get_frame_handler(frame_type=payload[0]),
137
137
  recipient=recipient,
138
138
  message=payload[1:-2],
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
 
4
4
  from collections.abc import Generator
5
5
  from dataclasses import dataclass
6
- from typing import Any, Final
6
+ from typing import Any, Final, cast
7
7
 
8
8
  from pyplumio.const import (
9
9
  ATTR_INDEX,
@@ -16,7 +16,7 @@ from pyplumio.const import (
16
16
  )
17
17
  from pyplumio.devices import AddressableDevice
18
18
  from pyplumio.frames import Request
19
- from pyplumio.helpers.factory import factory
19
+ from pyplumio.helpers.factory import create_instance
20
20
  from pyplumio.helpers.parameter import (
21
21
  BinaryParameter,
22
22
  BinaryParameterDescription,
@@ -44,6 +44,31 @@ class EcomaxParameter(Parameter):
44
44
  device: AddressableDevice
45
45
  description: EcomaxParameterDescription
46
46
 
47
+ async def create_request(self) -> Request:
48
+ """Create a request to change the parameter."""
49
+ if self.description.name == ATTR_ECOMAX_CONTROL:
50
+ cls = "frames.requests.EcomaxControlRequest"
51
+ data = {ATTR_VALUE: self.values.value}
52
+ elif self.description.name == ATTR_THERMOSTAT_PROFILE:
53
+ cls = "frames.requests.SetThermostatParameterRequest"
54
+ data = {
55
+ ATTR_INDEX: self._index,
56
+ ATTR_VALUE: self.values.value,
57
+ ATTR_OFFSET: 0,
58
+ ATTR_SIZE: 1,
59
+ }
60
+ else:
61
+ cls = "frames.requests.SetEcomaxParameterRequest"
62
+ data = {
63
+ ATTR_INDEX: self._index,
64
+ ATTR_VALUE: self.values.value,
65
+ }
66
+
67
+ return cast(
68
+ Request,
69
+ await create_instance(cls, recipient=self.device.address, data=data),
70
+ )
71
+
47
72
  async def set(self, value: ParameterValueType, retries: int = 5) -> bool:
48
73
  """Set a parameter value."""
49
74
  if isinstance(value, (int, float)):
@@ -72,42 +97,6 @@ class EcomaxParameter(Parameter):
72
97
  self.values.max_value - self.description.offset
73
98
  ) * self.description.multiplier
74
99
 
75
- @property
76
- def request(self) -> Request:
77
- """Return request to change the parameter."""
78
- if self.description.name == ATTR_ECOMAX_CONTROL:
79
- request: Request = factory(
80
- "frames.requests.EcomaxControlRequest",
81
- recipient=self.device.address,
82
- data={
83
- ATTR_VALUE: self.values.value,
84
- },
85
- )
86
-
87
- elif self.description.name == ATTR_THERMOSTAT_PROFILE:
88
- request = factory(
89
- "frames.requests.SetThermostatParameterRequest",
90
- recipient=self.device.address,
91
- data={
92
- ATTR_INDEX: self._index,
93
- ATTR_VALUE: self.values.value,
94
- ATTR_OFFSET: 0,
95
- ATTR_SIZE: 1,
96
- },
97
- )
98
-
99
- else:
100
- request = factory(
101
- "frames.requests.SetEcomaxParameterRequest",
102
- recipient=self.device.address,
103
- data={
104
- ATTR_INDEX: self._index,
105
- ATTR_VALUE: self.values.value,
106
- },
107
- )
108
-
109
- return request
110
-
111
100
 
112
101
  class EcomaxBinaryParameter(BinaryParameter, EcomaxParameter):
113
102
  """Represents an ecoMAX binary parameter."""
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
 
4
4
  from collections.abc import Generator
5
5
  from dataclasses import dataclass
6
- from typing import TYPE_CHECKING, Any, Final
6
+ from typing import TYPE_CHECKING, Any, Final, cast
7
7
 
8
8
  from pyplumio.const import (
9
9
  ATTR_DEVICE_INDEX,
@@ -13,7 +13,7 @@ from pyplumio.const import (
13
13
  UnitOfMeasurement,
14
14
  )
15
15
  from pyplumio.frames import Request
16
- from pyplumio.helpers.factory import factory
16
+ from pyplumio.helpers.factory import create_instance
17
17
  from pyplumio.helpers.parameter import (
18
18
  BinaryParameter,
19
19
  BinaryParameterDescription,
@@ -42,6 +42,21 @@ class MixerParameter(Parameter):
42
42
  device: Mixer
43
43
  description: MixerParameterDescription
44
44
 
45
+ async def create_request(self) -> Request:
46
+ """Create a request to change the parameter."""
47
+ return cast(
48
+ Request,
49
+ await create_instance(
50
+ "frames.requests.SetMixerParameterRequest",
51
+ recipient=self.device.parent.address,
52
+ data={
53
+ ATTR_INDEX: self._index,
54
+ ATTR_VALUE: self.values.value,
55
+ ATTR_DEVICE_INDEX: self.device.index,
56
+ },
57
+ ),
58
+ )
59
+
45
60
  async def set(self, value: ParameterValueType, retries: int = 5) -> bool:
46
61
  """Set a parameter value."""
47
62
  if isinstance(value, (int, float)):
@@ -70,20 +85,6 @@ class MixerParameter(Parameter):
70
85
  self.values.max_value - self.description.offset
71
86
  ) * self.description.multiplier
72
87
 
73
- @property
74
- def request(self) -> Request:
75
- """Return request to change the parameter."""
76
- request: Request = factory(
77
- "frames.requests.SetMixerParameterRequest",
78
- recipient=self.device.parent.address,
79
- data={
80
- ATTR_INDEX: self._index,
81
- ATTR_VALUE: self.values.value,
82
- ATTR_DEVICE_INDEX: self.device.index,
83
- },
84
- )
85
- return request
86
-
87
88
 
88
89
  class MixerBinaryParameter(BinaryParameter, MixerParameter):
89
90
  """Represents a mixer binary parameter."""
@@ -5,13 +5,13 @@ from collections.abc import Sequence
5
5
  from dataclasses import dataclass
6
6
  from functools import reduce
7
7
  from itertools import chain
8
- from typing import Any, Final
8
+ from typing import Any, Final, cast
9
9
 
10
10
  from pyplumio.const import ATTR_PARAMETER, ATTR_SCHEDULE, ATTR_SWITCH, ATTR_TYPE
11
11
  from pyplumio.devices import AddressableDevice, Device
12
12
  from pyplumio.exceptions import FrameDataError
13
13
  from pyplumio.frames import Request
14
- from pyplumio.helpers.factory import factory
14
+ from pyplumio.helpers.factory import create_instance
15
15
  from pyplumio.helpers.parameter import (
16
16
  BinaryParameter,
17
17
  BinaryParameterDescription,
@@ -81,16 +81,17 @@ class ScheduleParameter(Parameter):
81
81
 
82
82
  device: AddressableDevice
83
83
 
84
- @property
85
- def request(self) -> Request:
86
- """Return request to change the parameter."""
84
+ async def create_request(self) -> Request:
85
+ """Create a request to change the parameter."""
87
86
  schedule_name, _ = self.description.name.split("_schedule_", 1)
88
- request: Request = factory(
89
- "frames.requests.SetScheduleRequest",
90
- recipient=self.device.address,
91
- data=collect_schedule_data(schedule_name, self.device),
87
+ return cast(
88
+ Request,
89
+ await create_instance(
90
+ "frames.requests.SetScheduleRequest",
91
+ recipient=self.device.address,
92
+ data=collect_schedule_data(schedule_name, self.device),
93
+ ),
92
94
  )
93
- return request
94
95
 
95
96
 
96
97
  class ScheduleBinaryParameter(ScheduleParameter, BinaryParameter):
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
 
4
4
  from collections.abc import Generator
5
5
  from dataclasses import dataclass
6
- from typing import TYPE_CHECKING, Any, Final
6
+ from typing import TYPE_CHECKING, Any, Final, cast
7
7
 
8
8
  from pyplumio.const import (
9
9
  ATTR_INDEX,
@@ -14,7 +14,7 @@ from pyplumio.const import (
14
14
  )
15
15
  from pyplumio.devices import AddressableDevice
16
16
  from pyplumio.frames import Request
17
- from pyplumio.helpers.factory import factory
17
+ from pyplumio.helpers.factory import create_instance
18
18
  from pyplumio.helpers.parameter import (
19
19
  BinaryParameter,
20
20
  BinaryParameterDescription,
@@ -59,6 +59,24 @@ class ThermostatParameter(Parameter):
59
59
  self.offset = offset
60
60
  super().__init__(device, values, description, index)
61
61
 
62
+ async def create_request(self) -> Request:
63
+ """Create a request to change the parameter."""
64
+ return cast(
65
+ Request,
66
+ await create_instance(
67
+ "frames.requests.SetThermostatParameterRequest",
68
+ recipient=self.device.parent.address,
69
+ data={
70
+ # Increase the index by one to account for thermostat
71
+ # profile, which is being set at ecoMAX device level.
72
+ ATTR_INDEX: self._index + 1,
73
+ ATTR_VALUE: self.values.value,
74
+ ATTR_OFFSET: self.offset,
75
+ ATTR_SIZE: self.description.size,
76
+ },
77
+ ),
78
+ )
79
+
62
80
  async def set(self, value: ParameterValueType, retries: int = 5) -> bool:
63
81
  """Set a parameter value."""
64
82
  if isinstance(value, (int, float)):
@@ -81,23 +99,6 @@ class ThermostatParameter(Parameter):
81
99
  """Return the maximum allowed value."""
82
100
  return self.values.max_value * self.description.multiplier
83
101
 
84
- @property
85
- def request(self) -> Request:
86
- """Return request to change the parameter."""
87
- request: Request = factory(
88
- "frames.requests.SetThermostatParameterRequest",
89
- recipient=self.device.parent.address,
90
- data={
91
- # Increase the index by one to account for thermostat
92
- # profile, which is being set at ecoMAX device level.
93
- ATTR_INDEX: self._index + 1,
94
- ATTR_VALUE: self.values.value,
95
- ATTR_OFFSET: self.offset,
96
- ATTR_SIZE: self.description.size,
97
- },
98
- )
99
- return request
100
-
101
102
 
102
103
  class ThermostatBinaryParameter(BinaryParameter, ThermostatParameter):
103
104
  """Represents a thermostat binary parameter."""