PyPlumIO 0.5.18__py3-none-any.whl → 0.5.19__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.
Files changed (60) hide show
  1. {PyPlumIO-0.5.18.dist-info → PyPlumIO-0.5.19.dist-info}/METADATA +8 -8
  2. PyPlumIO-0.5.19.dist-info/RECORD +61 -0
  3. pyplumio/__init__.py +1 -0
  4. pyplumio/__main__.py +1 -0
  5. pyplumio/_version.py +2 -2
  6. pyplumio/connection.py +1 -0
  7. pyplumio/const.py +1 -0
  8. pyplumio/devices/__init__.py +25 -19
  9. pyplumio/devices/ecomax.py +3 -11
  10. pyplumio/devices/ecoster.py +1 -0
  11. pyplumio/devices/mixer.py +1 -0
  12. pyplumio/devices/thermostat.py +1 -0
  13. pyplumio/exceptions.py +1 -0
  14. pyplumio/filters.py +8 -10
  15. pyplumio/frames/__init__.py +35 -28
  16. pyplumio/frames/messages.py +1 -0
  17. pyplumio/frames/requests.py +1 -0
  18. pyplumio/frames/responses.py +1 -0
  19. pyplumio/helpers/data_types.py +1 -0
  20. pyplumio/helpers/event_manager.py +1 -0
  21. pyplumio/helpers/factory.py +10 -3
  22. pyplumio/helpers/parameter.py +1 -0
  23. pyplumio/helpers/schedule.py +11 -7
  24. pyplumio/helpers/task_manager.py +1 -0
  25. pyplumio/helpers/timeout.py +1 -0
  26. pyplumio/helpers/typing.py +1 -0
  27. pyplumio/helpers/uid.py +1 -0
  28. pyplumio/protocol.py +44 -54
  29. pyplumio/stream.py +16 -12
  30. pyplumio/structures/__init__.py +1 -0
  31. pyplumio/structures/alerts.py +1 -0
  32. pyplumio/structures/boiler_load.py +1 -0
  33. pyplumio/structures/boiler_power.py +1 -0
  34. pyplumio/structures/ecomax_parameters.py +8 -8
  35. pyplumio/structures/fan_power.py +1 -0
  36. pyplumio/structures/frame_versions.py +1 -0
  37. pyplumio/structures/fuel_consumption.py +1 -0
  38. pyplumio/structures/fuel_level.py +1 -0
  39. pyplumio/structures/lambda_sensor.py +1 -0
  40. pyplumio/structures/mixer_parameters.py +11 -13
  41. pyplumio/structures/mixer_sensors.py +1 -0
  42. pyplumio/structures/modules.py +1 -0
  43. pyplumio/structures/network_info.py +1 -0
  44. pyplumio/structures/output_flags.py +1 -0
  45. pyplumio/structures/outputs.py +1 -0
  46. pyplumio/structures/pending_alerts.py +1 -0
  47. pyplumio/structures/product_info.py +1 -0
  48. pyplumio/structures/program_version.py +3 -2
  49. pyplumio/structures/regulator_data.py +3 -4
  50. pyplumio/structures/regulator_data_schema.py +1 -0
  51. pyplumio/structures/schedules.py +14 -11
  52. pyplumio/structures/statuses.py +1 -0
  53. pyplumio/structures/temperatures.py +1 -0
  54. pyplumio/structures/thermostat_parameters.py +17 -22
  55. pyplumio/structures/thermostat_sensors.py +1 -0
  56. pyplumio/utils.py +1 -0
  57. PyPlumIO-0.5.18.dist-info/RECORD +0 -61
  58. {PyPlumIO-0.5.18.dist-info → PyPlumIO-0.5.19.dist-info}/LICENSE +0 -0
  59. {PyPlumIO-0.5.18.dist-info → PyPlumIO-0.5.19.dist-info}/WHEEL +0 -0
  60. {PyPlumIO-0.5.18.dist-info → PyPlumIO-0.5.19.dist-info}/top_level.txt +0 -0
pyplumio/protocol.py CHANGED
@@ -1,14 +1,15 @@
1
1
  """Contains protocol representation."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from abc import ABC, abstractmethod
5
6
  import asyncio
6
7
  from collections.abc import Awaitable, Callable
7
8
  import logging
8
- from typing import cast
9
+ from typing import NamedTuple, cast
9
10
 
10
11
  from pyplumio.const import ATTR_CONNECTED, DeviceType
11
- from pyplumio.devices import AddressableDevice, get_device_handler_and_name
12
+ from pyplumio.devices import AddressableDevice
12
13
  from pyplumio.exceptions import (
13
14
  FrameDataError,
14
15
  FrameError,
@@ -18,7 +19,6 @@ from pyplumio.exceptions import (
18
19
  from pyplumio.frames import Frame
19
20
  from pyplumio.frames.requests import StartMasterRequest
20
21
  from pyplumio.helpers.event_manager import EventManager
21
- from pyplumio.helpers.factory import create_instance
22
22
  from pyplumio.stream import FrameReader, FrameWriter
23
23
  from pyplumio.structures.network_info import (
24
24
  EthernetParameters,
@@ -101,36 +101,43 @@ class DummyProtocol(Protocol):
101
101
  await self.close_writer()
102
102
 
103
103
 
104
+ class Queues(NamedTuple):
105
+ """Represents asyncio queues."""
106
+
107
+ read: asyncio.Queue
108
+ write: asyncio.Queue
109
+
110
+
104
111
  class AsyncProtocol(Protocol, EventManager):
105
112
  """Represents an async protocol.
106
113
 
107
114
  This protocol implements producer-consumers pattern using
108
115
  asyncio queues.
109
116
 
110
- The frame producer tries to read frames from write queue, if any
111
- available, and sends them to the device via frame writer.
117
+ The frame producer tries to read frames from the write queue.
118
+ If any is available, it sends them to the device via frame writer.
112
119
 
113
- It reads stream via frame reader, creates device handler entry
114
- and puts received frame and handler into the read queue.
120
+ It then reads stream via frame reader, creates device entry and puts
121
+ received frame into the read queue.
115
122
 
116
- Frame consumers reads handler-frame tuples from the read queue and
117
- sends frame to respective handler for further processing.
123
+ Frame consumers read frames from the read queue and send frame to
124
+ their respective device class for further processing.
118
125
  """
119
126
 
127
+ consumers_count: int
120
128
  data: dict[str, AddressableDevice]
121
- consumers_number: int
122
129
  _network: NetworkInfo
123
- _queues: tuple[asyncio.Queue, asyncio.Queue]
130
+ _queues: Queues
124
131
 
125
132
  def __init__(
126
133
  self,
127
134
  ethernet_parameters: EthernetParameters | None = None,
128
135
  wireless_parameters: WirelessParameters | None = None,
129
- consumers_number: int = 3,
136
+ consumers_count: int = 3,
130
137
  ) -> None:
131
138
  """Initialize a new async protocol."""
132
139
  super().__init__()
133
- self.consumers_number = consumers_number
140
+ self.consumers_count = consumers_count
134
141
  self._network = NetworkInfo(
135
142
  eth=(
136
143
  EthernetParameters(status=False)
@@ -143,7 +150,7 @@ class AsyncProtocol(Protocol, EventManager):
143
150
  else wireless_parameters
144
151
  ),
145
152
  )
146
- self._queues = (asyncio.Queue(), asyncio.Queue())
153
+ self._queues = Queues(asyncio.Queue(), asyncio.Queue())
147
154
 
148
155
  def connection_established(
149
156
  self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
@@ -151,11 +158,10 @@ class AsyncProtocol(Protocol, EventManager):
151
158
  """Start frame producer and consumers."""
152
159
  self.reader = FrameReader(reader)
153
160
  self.writer = FrameWriter(writer)
154
- read_queue, write_queue = self.queues
155
- write_queue.put_nowait(StartMasterRequest(recipient=DeviceType.ECOMAX))
156
- self.create_task(self.frame_producer(*self.queues))
157
- for _ in range(self.consumers_number):
158
- self.create_task(self.frame_consumer(read_queue))
161
+ self._queues.write.put_nowait(StartMasterRequest(recipient=DeviceType.ECOMAX))
162
+ self.create_task(self.frame_producer(self._queues))
163
+ for _ in range(self.consumers_count):
164
+ self.create_task(self.frame_consumer(self._queues.read))
159
165
 
160
166
  for device in self.data.values():
161
167
  device.dispatch_nowait(ATTR_CONNECTED, True)
@@ -178,7 +184,7 @@ class AsyncProtocol(Protocol, EventManager):
178
184
 
179
185
  async def shutdown(self) -> None:
180
186
  """Shutdown protocol tasks."""
181
- await asyncio.gather(*[queue.join() for queue in self.queues])
187
+ await asyncio.gather(*[queue.join() for queue in self._queues])
182
188
  await super(Protocol, self).shutdown()
183
189
  for device in self.data.values():
184
190
  await device.shutdown()
@@ -187,24 +193,19 @@ class AsyncProtocol(Protocol, EventManager):
187
193
  self.connected.clear()
188
194
  await self.close_writer()
189
195
 
190
- async def frame_producer(
191
- self, read_queue: asyncio.Queue, write_queue: asyncio.Queue
192
- ) -> None:
196
+ async def frame_producer(self, queues: Queues) -> None:
193
197
  """Handle frame reads and writes."""
194
198
  await self.connected.wait()
195
199
  reader = cast(FrameReader, self.reader)
196
200
  writer = cast(FrameWriter, self.writer)
197
201
  while self.connected.is_set():
198
202
  try:
199
- if write_queue.qsize() > 0:
200
- await writer.write(await write_queue.get())
201
- write_queue.task_done()
203
+ if queues.write.qsize() > 0:
204
+ await writer.write(await queues.write.get())
205
+ queues.write.task_done()
202
206
 
203
207
  if (response := await reader.read()) is not None:
204
- device = await self.get_device_entry(
205
- cast(DeviceType, response.sender)
206
- )
207
- read_queue.put_nowait((device, response))
208
+ queues.read.put_nowait(response)
208
209
 
209
210
  except FrameDataError as e:
210
211
  _LOGGER.warning("Incorrect payload: %s", e)
@@ -220,36 +221,25 @@ class AsyncProtocol(Protocol, EventManager):
220
221
  except Exception as e: # pylint: disable=broad-except
221
222
  _LOGGER.exception(e)
222
223
 
223
- async def frame_consumer(self, read_queue: asyncio.Queue) -> None:
224
+ async def frame_consumer(self, queue: asyncio.Queue) -> None:
224
225
  """Handle frame processing."""
225
226
  await self.connected.wait()
226
227
  while self.connected.is_set():
227
- device, frame = cast(
228
- tuple[AddressableDevice, Frame], await read_queue.get()
229
- )
228
+ frame: Frame = await queue.get()
229
+ device = await self.get_device_entry(frame.sender)
230
230
  device.handle_frame(frame)
231
- read_queue.task_done()
231
+ queue.task_done()
232
232
 
233
233
  async def get_device_entry(self, device_type: DeviceType) -> AddressableDevice:
234
- """Set up device entry."""
235
- handler, name = get_device_handler_and_name(device_type)
234
+ """Set up or return a device entry."""
235
+ name = device_type.name.lower()
236
236
  if name not in self.data:
237
- self.data[name] = await self._create_device_entry(name, handler)
237
+ device = await AddressableDevice.create(
238
+ device_type, queue=self._queues.write, network=self._network
239
+ )
240
+ device.dispatch_nowait(ATTR_CONNECTED, True)
241
+ self.create_task(device.async_setup())
242
+ self.set_event(name)
243
+ self.data[name] = device
238
244
 
239
245
  return self.data[name]
240
-
241
- async def _create_device_entry(self, name: str, handler: str) -> AddressableDevice:
242
- """Create device entry."""
243
- write_queue = self.queues[1]
244
- device: AddressableDevice = await create_instance(
245
- handler, queue=write_queue, network=self._network
246
- )
247
- device.dispatch_nowait(ATTR_CONNECTED, True)
248
- self.create_task(device.async_setup())
249
- self.set_event(name)
250
- return device
251
-
252
- @property
253
- def queues(self) -> tuple[asyncio.Queue, asyncio.Queue]:
254
- """Return the protocol queues."""
255
- return self._queues
pyplumio/stream.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Contains a frame reader and writer classes."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import asyncio
@@ -7,9 +8,9 @@ import logging
7
8
  from typing import Final
8
9
 
9
10
  from pyplumio.const import DeviceType
10
- from pyplumio.exceptions import ChecksumError, ReadError
11
- from pyplumio.frames import FRAME_START, Frame, bcc, get_frame_handler, struct_header
12
- from pyplumio.helpers.factory import create_instance
11
+ from pyplumio.devices import is_known_device_type
12
+ from pyplumio.exceptions import ChecksumError, ReadError, UnknownDeviceError
13
+ from pyplumio.frames import FRAME_START, Frame, bcc, struct_header
13
14
  from pyplumio.helpers.timeout import timeout
14
15
 
15
16
  READER_TIMEOUT: Final = 10
@@ -83,7 +84,7 @@ class FrameReader:
83
84
  length,
84
85
  recipient,
85
86
  sender,
86
- sender_type,
87
+ econet_type,
87
88
  econet_version,
88
89
  ] = struct_header.unpack_from(buffer)
89
90
 
@@ -92,7 +93,7 @@ class FrameReader:
92
93
  length,
93
94
  recipient,
94
95
  sender,
95
- sender_type,
96
+ econet_type,
96
97
  econet_version,
97
98
  )
98
99
 
@@ -111,13 +112,16 @@ class FrameReader:
111
112
  length,
112
113
  recipient,
113
114
  sender,
114
- sender_type,
115
+ econet_type,
115
116
  econet_version,
116
117
  ) = await self._read_header()
117
118
 
118
119
  if recipient not in (DeviceType.ECONET, DeviceType.ALL):
119
120
  return None
120
121
 
122
+ if not is_known_device_type(sender):
123
+ raise UnknownDeviceError(f"Unknown sender type ({sender})")
124
+
121
125
  if length > MAX_FRAME_LENGTH or length < MIN_FRAME_LENGTH:
122
126
  raise ReadError(f"Unexpected frame length ({length})")
123
127
 
@@ -132,13 +136,13 @@ class FrameReader:
132
136
  if payload[-2] != bcc(header + payload[:-2]):
133
137
  raise ChecksumError(f"Incorrect frame checksum ({payload[-2]})")
134
138
 
135
- frame: Frame = await create_instance(
136
- get_frame_handler(frame_type=payload[0]),
137
- recipient=recipient,
138
- message=payload[1:-2],
139
- sender=sender,
140
- sender_type=sender_type,
139
+ frame = await Frame.create(
140
+ frame_type=payload[0],
141
+ recipient=DeviceType(recipient),
142
+ sender=DeviceType(sender),
143
+ econet_type=econet_type,
141
144
  econet_version=econet_version,
145
+ message=payload[1:-2],
142
146
  )
143
147
  _LOGGER.debug("Received frame: %s", frame)
144
148
 
@@ -1,4 +1,5 @@
1
1
  """Contains structure decoders."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from abc import ABC, abstractmethod
@@ -1,4 +1,5 @@
1
1
  """Contains an alerts structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from collections.abc import Generator
@@ -1,4 +1,5 @@
1
1
  """Contains a boiler load structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from typing import Any, Final
@@ -1,4 +1,5 @@
1
1
  """Contains a boiler power structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import math
@@ -1,9 +1,10 @@
1
1
  """Contains regulator parameter structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from collections.abc import Generator
5
6
  from dataclasses import dataclass
6
- from typing import Any, Final, cast
7
+ from typing import Any, Final
7
8
 
8
9
  from pyplumio.const import (
9
10
  ATTR_INDEX,
@@ -11,12 +12,12 @@ from pyplumio.const import (
11
12
  ATTR_SIZE,
12
13
  ATTR_VALUE,
13
14
  PERCENTAGE,
15
+ FrameType,
14
16
  ProductType,
15
17
  UnitOfMeasurement,
16
18
  )
17
19
  from pyplumio.devices import AddressableDevice
18
20
  from pyplumio.frames import Request
19
- from pyplumio.helpers.factory import create_instance
20
21
  from pyplumio.helpers.parameter import (
21
22
  BinaryParameter,
22
23
  BinaryParameterDescription,
@@ -47,10 +48,10 @@ class EcomaxParameter(Parameter):
47
48
  async def create_request(self) -> Request:
48
49
  """Create a request to change the parameter."""
49
50
  if self.description.name == ATTR_ECOMAX_CONTROL:
50
- cls = "frames.requests.EcomaxControlRequest"
51
+ frame_type = FrameType.REQUEST_ECOMAX_CONTROL
51
52
  data = {ATTR_VALUE: self.values.value}
52
53
  elif self.description.name == ATTR_THERMOSTAT_PROFILE:
53
- cls = "frames.requests.SetThermostatParameterRequest"
54
+ frame_type = FrameType.REQUEST_SET_THERMOSTAT_PARAMETER
54
55
  data = {
55
56
  ATTR_INDEX: self._index,
56
57
  ATTR_VALUE: self.values.value,
@@ -58,15 +59,14 @@ class EcomaxParameter(Parameter):
58
59
  ATTR_SIZE: 1,
59
60
  }
60
61
  else:
61
- cls = "frames.requests.SetEcomaxParameterRequest"
62
+ frame_type = FrameType.REQUEST_SET_ECOMAX_PARAMETER
62
63
  data = {
63
64
  ATTR_INDEX: self._index,
64
65
  ATTR_VALUE: self.values.value,
65
66
  }
66
67
 
67
- return cast(
68
- Request,
69
- await create_instance(cls, recipient=self.device.address, data=data),
68
+ return await Request.create(
69
+ frame_type, recipient=self.device.address, data=data
70
70
  )
71
71
 
72
72
  async def set(self, value: ParameterValueType, retries: int = 5) -> bool:
@@ -1,4 +1,5 @@
1
1
  """Contains a fan power structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import math
@@ -1,4 +1,5 @@
1
1
  """Contains a frame versions structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from typing import Any, Final
@@ -1,4 +1,5 @@
1
1
  """Contains fuel consumption structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import math
@@ -1,4 +1,5 @@
1
1
  """Contains a fuel level structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from typing import Any, Final
@@ -1,4 +1,5 @@
1
1
  """Contains a lambda sensor structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from contextlib import suppress
@@ -1,19 +1,20 @@
1
1
  """Contains a mixer parameter structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from collections.abc import Generator
5
6
  from dataclasses import dataclass
6
- from typing import TYPE_CHECKING, Any, Final, cast
7
+ from typing import TYPE_CHECKING, Any, Final
7
8
 
8
9
  from pyplumio.const import (
9
10
  ATTR_DEVICE_INDEX,
10
11
  ATTR_INDEX,
11
12
  ATTR_VALUE,
13
+ FrameType,
12
14
  ProductType,
13
15
  UnitOfMeasurement,
14
16
  )
15
17
  from pyplumio.frames import Request
16
- from pyplumio.helpers.factory import create_instance
17
18
  from pyplumio.helpers.parameter import (
18
19
  BinaryParameter,
19
20
  BinaryParameterDescription,
@@ -44,17 +45,14 @@ class MixerParameter(Parameter):
44
45
 
45
46
  async def create_request(self) -> Request:
46
47
  """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
- ),
48
+ return await Request.create(
49
+ FrameType.REQUEST_SET_MIXER_PARAMETER,
50
+ recipient=self.device.parent.address,
51
+ data={
52
+ ATTR_INDEX: self._index,
53
+ ATTR_VALUE: self.values.value,
54
+ ATTR_DEVICE_INDEX: self.device.index,
55
+ },
58
56
  )
59
57
 
60
58
  async def set(self, value: ParameterValueType, retries: int = 5) -> bool:
@@ -1,4 +1,5 @@
1
1
  """Contains a mixer sensors structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from collections.abc import Generator
@@ -1,4 +1,5 @@
1
1
  """Contains a modules structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from dataclasses import dataclass
@@ -1,4 +1,5 @@
1
1
  """Contains a network info structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from dataclasses import dataclass, field
@@ -1,4 +1,5 @@
1
1
  """Contains an output flags structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from typing import Any, Final
@@ -1,4 +1,5 @@
1
1
  """Contains an outputs structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import math
@@ -1,4 +1,5 @@
1
1
  """Contains a pending alerts structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from typing import Any, Final
@@ -1,4 +1,5 @@
1
1
  """Contains a product info structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from dataclasses import dataclass
@@ -1,4 +1,5 @@
1
1
  """Contains a program version decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from dataclasses import dataclass
@@ -23,9 +24,9 @@ class VersionInfo:
23
24
  """Represents a version info provided in program version response."""
24
25
 
25
26
  software: str = SOFTWARE_VERSION
26
- struct_tag: bytes = b"\xFF\xFF"
27
+ struct_tag: bytes = b"\xff\xff"
27
28
  struct_version: int = 5
28
- device_id: bytes = b"\x7A\x00"
29
+ device_id: bytes = b"\x7a\x00"
29
30
  processor_signature: bytes = b"\x00\x00\x00"
30
31
 
31
32
 
@@ -1,9 +1,9 @@
1
1
  """Contains a regulator data structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from typing import Any, Final
5
6
 
6
- from pyplumio.devices import AddressableDevice
7
7
  from pyplumio.helpers.data_types import BitArray, DataType
8
8
  from pyplumio.structures import StructureDecoder
9
9
  from pyplumio.structures.frame_versions import FrameVersionsStructure
@@ -55,9 +55,8 @@ class RegulatorDataStructure(StructureDecoder):
55
55
  message, offset + 2, data
56
56
  )
57
57
 
58
- sender = self.frame.sender
59
- if isinstance(sender, AddressableDevice) and (
60
- schema := sender.get_nowait(ATTR_REGDATA_SCHEMA, [])
58
+ if (device := self.frame.sender_device) is not None and (
59
+ schema := device.get_nowait(ATTR_REGDATA_SCHEMA, [])
61
60
  ):
62
61
  self._bitarray_index = 0
63
62
  data[ATTR_REGDATA] = {
@@ -1,4 +1,5 @@
1
1
  """Contains data schema structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from typing import Any, Final
@@ -1,17 +1,23 @@
1
1
  """Contains schedule decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from collections.abc import Sequence
5
6
  from dataclasses import dataclass
6
7
  from functools import reduce
7
8
  from itertools import chain
8
- from typing import Any, Final, cast
9
-
10
- from pyplumio.const import ATTR_PARAMETER, ATTR_SCHEDULE, ATTR_SWITCH, ATTR_TYPE
9
+ from typing import Any, Final
10
+
11
+ from pyplumio.const import (
12
+ ATTR_PARAMETER,
13
+ ATTR_SCHEDULE,
14
+ ATTR_SWITCH,
15
+ ATTR_TYPE,
16
+ FrameType,
17
+ )
11
18
  from pyplumio.devices import AddressableDevice, Device
12
19
  from pyplumio.exceptions import FrameDataError
13
20
  from pyplumio.frames import Request
14
- from pyplumio.helpers.factory import create_instance
15
21
  from pyplumio.helpers.parameter import (
16
22
  BinaryParameter,
17
23
  BinaryParameterDescription,
@@ -84,13 +90,10 @@ class ScheduleParameter(Parameter):
84
90
  async def create_request(self) -> Request:
85
91
  """Create a request to change the parameter."""
86
92
  schedule_name, _ = self.description.name.split("_schedule_", 1)
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
- ),
93
+ return await Request.create(
94
+ FrameType.REQUEST_SET_SCHEDULE,
95
+ recipient=self.device.address,
96
+ data=collect_schedule_data(schedule_name, self.device),
94
97
  )
95
98
 
96
99
 
@@ -1,4 +1,5 @@
1
1
  """Contains a statuses structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  from typing import Any, Final
@@ -1,4 +1,5 @@
1
1
  """Contains a temperatures structure decoder."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import math