PyPlumIO 0.5.19__py3-none-any.whl → 0.5.20__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.
- {PyPlumIO-0.5.19.dist-info → PyPlumIO-0.5.20.dist-info}/METADATA +2 -2
- {PyPlumIO-0.5.19.dist-info → PyPlumIO-0.5.20.dist-info}/RECORD +12 -12
- pyplumio/_version.py +2 -2
- pyplumio/connection.py +1 -4
- pyplumio/devices/__init__.py +6 -3
- pyplumio/helpers/event_manager.py +0 -5
- pyplumio/helpers/timeout.py +6 -20
- pyplumio/protocol.py +31 -39
- pyplumio/stream.py +27 -28
- {PyPlumIO-0.5.19.dist-info → PyPlumIO-0.5.20.dist-info}/LICENSE +0 -0
- {PyPlumIO-0.5.19.dist-info → PyPlumIO-0.5.20.dist-info}/WHEEL +0 -0
- {PyPlumIO-0.5.19.dist-info → PyPlumIO-0.5.20.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: PyPlumIO
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.20
|
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
|
@@ -38,7 +38,7 @@ Requires-Dist: mypy ==1.10.0 ; extra == 'test'
|
|
38
38
|
Requires-Dist: pyserial-asyncio-fast ==0.11 ; extra == 'test'
|
39
39
|
Requires-Dist: pytest ==8.2.1 ; extra == 'test'
|
40
40
|
Requires-Dist: pytest-asyncio ==0.23.7 ; extra == 'test'
|
41
|
-
Requires-Dist: ruff ==0.4.
|
41
|
+
Requires-Dist: ruff ==0.4.7 ; extra == 'test'
|
42
42
|
Requires-Dist: tox ==4.15.0 ; extra == 'test'
|
43
43
|
Requires-Dist: types-pyserial ==3.5.0.20240527 ; extra == 'test'
|
44
44
|
|
@@ -1,15 +1,15 @@
|
|
1
1
|
pyplumio/__init__.py,sha256=cclyAwy7OsW673iHcwkVrJSNnf32oF51Y_0uEEF5cdI,3293
|
2
2
|
pyplumio/__main__.py,sha256=3IwHHSq-iay5FaeMc95klobe-xv82yydSKcBE7BFZ6M,500
|
3
|
-
pyplumio/_version.py,sha256=
|
4
|
-
pyplumio/connection.py,sha256=
|
3
|
+
pyplumio/_version.py,sha256=eONbmd59kUJvI6efSg_Cy7fvmhvKc1Uota8ebahOmE0,413
|
4
|
+
pyplumio/connection.py,sha256=ZZHXHFpbOBVd9DGZV_H8lpdYtYoc3nP9fRolKATKDnQ,6096
|
5
5
|
pyplumio/const.py,sha256=8rpiVbVb5R_6Rm6J2sgCnaVrkD-2Fzhd1RYMz0MBgwo,3915
|
6
6
|
pyplumio/exceptions.py,sha256=193z3zfnswYhIYPzCIpxCiWat4qI3cV85sqT4YOSo-4,699
|
7
7
|
pyplumio/filters.py,sha256=bIonYc_QbGMsL8aWweSLUmP7gKqDD646zELf_PqqQBg,11161
|
8
|
-
pyplumio/protocol.py,sha256=
|
8
|
+
pyplumio/protocol.py,sha256=i4C7WYALp6BEHzeMjiebH8GWI2qGIEPk6OlUIx_2UP4,7870
|
9
9
|
pyplumio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
pyplumio/stream.py,sha256=
|
10
|
+
pyplumio/stream.py,sha256=DqMqdi3HG9hODgfGo4eTKLkfoaSh5RS4kBHNn3ODvVg,4472
|
11
11
|
pyplumio/utils.py,sha256=GV7P1hPLoQsx3uqYviQ15FXJmkmTxwtDibAc-yRarvo,688
|
12
|
-
pyplumio/devices/__init__.py,sha256=
|
12
|
+
pyplumio/devices/__init__.py,sha256=O5SyEt_x1nJ1JYkG6v3dTZ54tu9sKIdj4l256JhvLHg,6585
|
13
13
|
pyplumio/devices/ecomax.py,sha256=W9YW4nw6v2wdKKqFblUx03_hFJw0dVjnVkDHNL8P2dg,16632
|
14
14
|
pyplumio/devices/ecoster.py,sha256=J4YtPmFmFwaq4LzYf28aMmB97cRAbMsVyUdBLGki42g,313
|
15
15
|
pyplumio/devices/mixer.py,sha256=PGk0lXveN6q5sm0B50YG0yCVYoKi1a9TA3v44bGgi3A,2947
|
@@ -20,12 +20,12 @@ pyplumio/frames/requests.py,sha256=Ra8xH5oKYhkEUtadN-9ZsJKkt5xZkz5O7edQVsDhNsM,7
|
|
20
20
|
pyplumio/frames/responses.py,sha256=j4awA2-MfsoPdENC4Fvae4_Oa70rDhH19ebmEoAqhh8,6532
|
21
21
|
pyplumio/helpers/__init__.py,sha256=H2xxdkF-9uADLwEbfBUoxNTdwru3L5Z2cfJjgsuRsn0,31
|
22
22
|
pyplumio/helpers/data_types.py,sha256=H_pYkLgIu30lDFU0UUZ1V3vYxa9A_-1nhiJu-HCLuoc,8212
|
23
|
-
pyplumio/helpers/event_manager.py,sha256=
|
23
|
+
pyplumio/helpers/event_manager.py,sha256=eSaqFGwED_UlugDwGRusfpivolqV6TISs1XnzBH6e60,5863
|
24
24
|
pyplumio/helpers/factory.py,sha256=eiTkYUCernUn0VNDDdEN4IyjNPrXK8vnJESXyLaqFzE,1017
|
25
25
|
pyplumio/helpers/parameter.py,sha256=gYCA2SLU_lbdtQZq5U64yzpyLoEIa0R1wyJJGmgL63I,8699
|
26
26
|
pyplumio/helpers/schedule.py,sha256=-IZJ-CU4PhFlsE586wTw--ovDrTo2Hs4JneCHhc0e-Y,5013
|
27
27
|
pyplumio/helpers/task_manager.py,sha256=RpdYSguc0cap_Onf9VnL-yCd_KwR2JPD49trZCRKPpI,1090
|
28
|
-
pyplumio/helpers/timeout.py,sha256=
|
28
|
+
pyplumio/helpers/timeout.py,sha256=k-829fBcHT5IR3isrMSgNbPYK-ubeY1BAwndCDIiX9E,824
|
29
29
|
pyplumio/helpers/typing.py,sha256=y55UdpIpPIRuUBPgfPmZHAwPdIUjQO924-kO7AVXhes,685
|
30
30
|
pyplumio/helpers/uid.py,sha256=yaBjcsFKuhOaznftk33kdIepQHpK-labEQr59QNKhPM,975
|
31
31
|
pyplumio/structures/__init__.py,sha256=EjK-5qJZ0F7lpP2b6epvTMg9cIBl4Kn91nqNkEcLwTc,1299
|
@@ -54,8 +54,8 @@ pyplumio/structures/statuses.py,sha256=wkoynyMRr1VREwfBC6vU48kPA8ZQ83pcXuciy2xHJ
|
|
54
54
|
pyplumio/structures/temperatures.py,sha256=1CDzehNmbALz1Jyt_9gZNIk52q6Wv-xQXjijVDCVYec,2337
|
55
55
|
pyplumio/structures/thermostat_parameters.py,sha256=pjbWsT6z7mlDiUrC5MWGqMtGP0deeVMYeeTa7yGEwJ8,7706
|
56
56
|
pyplumio/structures/thermostat_sensors.py,sha256=ZmjWgYtTZ5M8Lnz_Q5N4JD8G3MvEmByPFjYsy6XZOmo,3177
|
57
|
-
PyPlumIO-0.5.
|
58
|
-
PyPlumIO-0.5.
|
59
|
-
PyPlumIO-0.5.
|
60
|
-
PyPlumIO-0.5.
|
61
|
-
PyPlumIO-0.5.
|
57
|
+
PyPlumIO-0.5.20.dist-info/LICENSE,sha256=m-UuZFjXJ22uPTGm9kSHS8bqjsf5T8k2wL9bJn1Y04o,1088
|
58
|
+
PyPlumIO-0.5.20.dist-info/METADATA,sha256=nTyxUOv5kPz-K5xNnm389H-eFlLN48mXYMmc3E4zHQ0,5415
|
59
|
+
PyPlumIO-0.5.20.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
60
|
+
PyPlumIO-0.5.20.dist-info/top_level.txt,sha256=kNBz9UPPkPD9teDn3U_sEy5LjzwLm9KfADCXtBlbw8A,9
|
61
|
+
PyPlumIO-0.5.20.dist-info/RECORD,,
|
pyplumio/_version.py
CHANGED
pyplumio/connection.py
CHANGED
@@ -72,10 +72,7 @@ class Connection(ABC, TaskManager):
|
|
72
72
|
async def _connect(self) -> None:
|
73
73
|
"""Establish connection and initialize the protocol object."""
|
74
74
|
try:
|
75
|
-
reader, writer =
|
76
|
-
tuple[asyncio.StreamReader, asyncio.StreamWriter],
|
77
|
-
await self._open_connection(),
|
78
|
-
)
|
75
|
+
reader, writer = await self._open_connection()
|
79
76
|
self.protocol.connection_established(reader, writer)
|
80
77
|
except (OSError, SerialException, asyncio.TimeoutError) as err:
|
81
78
|
raise ConnectionFailedError from err
|
pyplumio/devices/__init__.py
CHANGED
@@ -111,6 +111,11 @@ class Device(ABC, EventManager):
|
|
111
111
|
"""
|
112
112
|
self.create_task(self.set(name, value, timeout, retries))
|
113
113
|
|
114
|
+
async def shutdown(self) -> None:
|
115
|
+
"""Cancel device tasks."""
|
116
|
+
self.cancel_tasks()
|
117
|
+
await self.wait_until_done()
|
118
|
+
|
114
119
|
|
115
120
|
class AddressableDevice(Device, ABC):
|
116
121
|
"""Represents an addressable device."""
|
@@ -139,9 +144,7 @@ class AddressableDevice(Device, ABC):
|
|
139
144
|
"""Set up addressable device."""
|
140
145
|
results = await asyncio.gather(
|
141
146
|
*{
|
142
|
-
self.
|
143
|
-
self.request(description.provides, description.frame_type)
|
144
|
-
)
|
147
|
+
self.request(description.provides, description.frame_type)
|
145
148
|
for description in self._setup_frames
|
146
149
|
},
|
147
150
|
return_exceptions=True,
|
@@ -165,11 +165,6 @@ class EventManager(TaskManager):
|
|
165
165
|
if not event.is_set():
|
166
166
|
event.set()
|
167
167
|
|
168
|
-
async def shutdown(self) -> None:
|
169
|
-
"""Cancel scheduled tasks."""
|
170
|
-
self.cancel_tasks()
|
171
|
-
await self.wait_until_done()
|
172
|
-
|
173
168
|
@property
|
174
169
|
def events(self) -> dict[str, asyncio.Event]:
|
175
170
|
"""Return the events."""
|
pyplumio/helpers/timeout.py
CHANGED
@@ -17,30 +17,16 @@ _LOGGER = logging.getLogger(__name__)
|
|
17
17
|
|
18
18
|
|
19
19
|
def timeout(
|
20
|
-
seconds: int,
|
21
|
-
) -> Callable[[Callable[P, Awaitable[T]]], Callable[P, Coroutine[Any, Any, T
|
22
|
-
"""Decorate a timeout for the awaitable.
|
23
|
-
|
24
|
-
Return None on exception if raise_exception parameter is set to false.
|
25
|
-
"""
|
20
|
+
seconds: int,
|
21
|
+
) -> Callable[[Callable[P, Awaitable[T]]], Callable[P, Coroutine[Any, Any, T]]]:
|
22
|
+
"""Decorate a timeout for the awaitable."""
|
26
23
|
|
27
24
|
def decorator(
|
28
25
|
func: Callable[P, Awaitable[T]],
|
29
|
-
) -> Callable[P, Coroutine[Any, Any, T
|
26
|
+
) -> Callable[P, Coroutine[Any, Any, T]]:
|
30
27
|
@wraps(func)
|
31
|
-
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T
|
32
|
-
|
33
|
-
return await asyncio.wait_for(func(*args, **kwargs), timeout=seconds)
|
34
|
-
except asyncio.TimeoutError:
|
35
|
-
if raise_exception:
|
36
|
-
raise
|
37
|
-
|
38
|
-
_LOGGER.warning(
|
39
|
-
"Function '%s' timed out after %i seconds",
|
40
|
-
func.__name__,
|
41
|
-
seconds,
|
42
|
-
)
|
43
|
-
return None
|
28
|
+
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
29
|
+
return await asyncio.wait_for(func(*args, **kwargs), timeout=seconds)
|
44
30
|
|
45
31
|
return wrapper
|
46
32
|
|
pyplumio/protocol.py
CHANGED
@@ -5,17 +5,12 @@ from __future__ import annotations
|
|
5
5
|
from abc import ABC, abstractmethod
|
6
6
|
import asyncio
|
7
7
|
from collections.abc import Awaitable, Callable
|
8
|
+
from dataclasses import dataclass
|
8
9
|
import logging
|
9
|
-
from typing import NamedTuple, cast
|
10
10
|
|
11
11
|
from pyplumio.const import ATTR_CONNECTED, DeviceType
|
12
12
|
from pyplumio.devices import AddressableDevice
|
13
|
-
from pyplumio.exceptions import
|
14
|
-
FrameDataError,
|
15
|
-
FrameError,
|
16
|
-
ReadError,
|
17
|
-
UnknownDeviceError,
|
18
|
-
)
|
13
|
+
from pyplumio.exceptions import FrameError, ReadError, UnknownDeviceError
|
19
14
|
from pyplumio.frames import Frame
|
20
15
|
from pyplumio.frames.requests import StartMasterRequest
|
21
16
|
from pyplumio.helpers.event_manager import EventManager
|
@@ -101,12 +96,20 @@ class DummyProtocol(Protocol):
|
|
101
96
|
await self.close_writer()
|
102
97
|
|
103
98
|
|
104
|
-
|
99
|
+
@dataclass
|
100
|
+
class Queues:
|
105
101
|
"""Represents asyncio queues."""
|
106
102
|
|
103
|
+
__slots__ = ("read", "write")
|
104
|
+
|
107
105
|
read: asyncio.Queue
|
108
106
|
write: asyncio.Queue
|
109
107
|
|
108
|
+
async def join(self) -> None:
|
109
|
+
"""Wait for queues to finish."""
|
110
|
+
for queue in (self.read, self.write):
|
111
|
+
await queue.join()
|
112
|
+
|
110
113
|
|
111
114
|
class AsyncProtocol(Protocol, EventManager):
|
112
115
|
"""Represents an async protocol.
|
@@ -117,11 +120,11 @@ class AsyncProtocol(Protocol, EventManager):
|
|
117
120
|
The frame producer tries to read frames from the write queue.
|
118
121
|
If any is available, it sends them to the device via frame writer.
|
119
122
|
|
120
|
-
It then reads stream via frame reader
|
121
|
-
|
123
|
+
It then reads stream via frame reader and puts received frame
|
124
|
+
into the read queue.
|
122
125
|
|
123
|
-
Frame consumers read frames from the read queue
|
124
|
-
|
126
|
+
Frame consumers read frames from the read queue, create device
|
127
|
+
entry, if needed, and send frame to the entry for the processing.
|
125
128
|
"""
|
126
129
|
|
127
130
|
consumers_count: int
|
@@ -139,18 +142,10 @@ class AsyncProtocol(Protocol, EventManager):
|
|
139
142
|
super().__init__()
|
140
143
|
self.consumers_count = consumers_count
|
141
144
|
self._network = NetworkInfo(
|
142
|
-
eth=(
|
143
|
-
|
144
|
-
if ethernet_parameters is None
|
145
|
-
else ethernet_parameters
|
146
|
-
),
|
147
|
-
wlan=(
|
148
|
-
WirelessParameters(status=False)
|
149
|
-
if wireless_parameters is None
|
150
|
-
else wireless_parameters
|
151
|
-
),
|
145
|
+
eth=ethernet_parameters or EthernetParameters(status=False),
|
146
|
+
wlan=wireless_parameters or WirelessParameters(status=False),
|
152
147
|
)
|
153
|
-
self._queues = Queues(asyncio.Queue(), asyncio.Queue())
|
148
|
+
self._queues = Queues(read=asyncio.Queue(), write=asyncio.Queue())
|
154
149
|
|
155
150
|
def connection_established(
|
156
151
|
self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
|
@@ -159,7 +154,9 @@ class AsyncProtocol(Protocol, EventManager):
|
|
159
154
|
self.reader = FrameReader(reader)
|
160
155
|
self.writer = FrameWriter(writer)
|
161
156
|
self._queues.write.put_nowait(StartMasterRequest(recipient=DeviceType.ECOMAX))
|
162
|
-
self.create_task(
|
157
|
+
self.create_task(
|
158
|
+
self.frame_producer(self._queues, reader=self.reader, writer=self.writer)
|
159
|
+
)
|
163
160
|
for _ in range(self.consumers_count):
|
164
161
|
self.create_task(self.frame_consumer(self._queues.read))
|
165
162
|
|
@@ -184,8 +181,9 @@ class AsyncProtocol(Protocol, EventManager):
|
|
184
181
|
|
185
182
|
async def shutdown(self) -> None:
|
186
183
|
"""Shutdown protocol tasks."""
|
187
|
-
await
|
188
|
-
|
184
|
+
await self._queues.join()
|
185
|
+
self.cancel_tasks()
|
186
|
+
await self.wait_until_done()
|
189
187
|
for device in self.data.values():
|
190
188
|
await device.shutdown()
|
191
189
|
|
@@ -193,33 +191,27 @@ class AsyncProtocol(Protocol, EventManager):
|
|
193
191
|
self.connected.clear()
|
194
192
|
await self.close_writer()
|
195
193
|
|
196
|
-
async def frame_producer(
|
194
|
+
async def frame_producer(
|
195
|
+
self, queues: Queues, reader: FrameReader, writer: FrameWriter
|
196
|
+
) -> None:
|
197
197
|
"""Handle frame reads and writes."""
|
198
198
|
await self.connected.wait()
|
199
|
-
reader = cast(FrameReader, self.reader)
|
200
|
-
writer = cast(FrameWriter, self.writer)
|
201
199
|
while self.connected.is_set():
|
202
200
|
try:
|
203
|
-
if queues.write.
|
201
|
+
if not queues.write.empty():
|
204
202
|
await writer.write(await queues.write.get())
|
205
203
|
queues.write.task_done()
|
206
204
|
|
207
205
|
if (response := await reader.read()) is not None:
|
208
206
|
queues.read.put_nowait(response)
|
209
207
|
|
210
|
-
except
|
211
|
-
_LOGGER.warning("Incorrect payload: %s", e)
|
212
|
-
except ReadError as e:
|
213
|
-
_LOGGER.debug("Read error: %s", e)
|
214
|
-
except UnknownDeviceError as e:
|
215
|
-
_LOGGER.debug("Unknown device: %s", e)
|
216
|
-
except FrameError as e:
|
208
|
+
except (ReadError, UnknownDeviceError, FrameError) as e:
|
217
209
|
_LOGGER.debug("Can't process received frame: %s", e)
|
218
210
|
except (OSError, asyncio.TimeoutError):
|
219
211
|
self.create_task(self.connection_lost())
|
220
212
|
break
|
221
|
-
except Exception
|
222
|
-
_LOGGER.exception(
|
213
|
+
except Exception:
|
214
|
+
_LOGGER.exception("Unexpected exception")
|
223
215
|
|
224
216
|
async def frame_consumer(self, queue: asyncio.Queue) -> None:
|
225
217
|
"""Handle frame processing."""
|
pyplumio/stream.py
CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
5
5
|
import asyncio
|
6
6
|
from asyncio import IncompleteReadError, StreamReader, StreamWriter
|
7
7
|
import logging
|
8
|
-
from typing import Final
|
8
|
+
from typing import Final, NamedTuple
|
9
9
|
|
10
10
|
from pyplumio.const import DeviceType
|
11
11
|
from pyplumio.devices import is_known_device_type
|
@@ -54,6 +54,18 @@ class FrameWriter:
|
|
54
54
|
await self._writer.wait_closed()
|
55
55
|
|
56
56
|
|
57
|
+
class Header(NamedTuple):
|
58
|
+
"""Represents a frame header."""
|
59
|
+
|
60
|
+
bytes: bytes
|
61
|
+
frame_start: int
|
62
|
+
frame_length: int
|
63
|
+
recipient: int
|
64
|
+
sender: int
|
65
|
+
econet_type: int
|
66
|
+
econet_version: int
|
67
|
+
|
68
|
+
|
57
69
|
class FrameReader:
|
58
70
|
"""Represents a frame reader."""
|
59
71
|
|
@@ -65,11 +77,11 @@ class FrameReader:
|
|
65
77
|
"""Initialize a new frame reader."""
|
66
78
|
self._reader = reader
|
67
79
|
|
68
|
-
async def _read_header(self) ->
|
80
|
+
async def _read_header(self) -> Header:
|
69
81
|
"""Locate and read a frame header.
|
70
82
|
|
71
83
|
Raise pyplumio.ReadError if header size is too small and
|
72
|
-
OSError
|
84
|
+
OSError if serial connection is broken.
|
73
85
|
"""
|
74
86
|
while buffer := await self._reader.read(1):
|
75
87
|
if FRAME_START not in buffer:
|
@@ -79,23 +91,7 @@ class FrameReader:
|
|
79
91
|
if len(buffer) < struct_header.size:
|
80
92
|
raise ReadError(f"Header can't be less than {struct_header.size} bytes")
|
81
93
|
|
82
|
-
|
83
|
-
_,
|
84
|
-
length,
|
85
|
-
recipient,
|
86
|
-
sender,
|
87
|
-
econet_type,
|
88
|
-
econet_version,
|
89
|
-
] = struct_header.unpack_from(buffer)
|
90
|
-
|
91
|
-
return (
|
92
|
-
buffer,
|
93
|
-
length,
|
94
|
-
recipient,
|
95
|
-
sender,
|
96
|
-
econet_type,
|
97
|
-
econet_version,
|
98
|
-
)
|
94
|
+
return Header(buffer, *struct_header.unpack_from(buffer))
|
99
95
|
|
100
96
|
raise OSError("Serial connection broken")
|
101
97
|
|
@@ -108,8 +104,9 @@ class FrameReader:
|
|
108
104
|
checksum.
|
109
105
|
"""
|
110
106
|
(
|
111
|
-
|
112
|
-
|
107
|
+
header_bytes,
|
108
|
+
_,
|
109
|
+
frame_length,
|
113
110
|
recipient,
|
114
111
|
sender,
|
115
112
|
econet_type,
|
@@ -122,19 +119,21 @@ class FrameReader:
|
|
122
119
|
if not is_known_device_type(sender):
|
123
120
|
raise UnknownDeviceError(f"Unknown sender type ({sender})")
|
124
121
|
|
125
|
-
if
|
126
|
-
raise ReadError(f"Unexpected frame length ({
|
122
|
+
if frame_length > MAX_FRAME_LENGTH or frame_length < MIN_FRAME_LENGTH:
|
123
|
+
raise ReadError(f"Unexpected frame length ({frame_length})")
|
127
124
|
|
128
125
|
try:
|
129
|
-
payload = await self._reader.readexactly(
|
126
|
+
payload = await self._reader.readexactly(frame_length - struct_header.size)
|
130
127
|
except IncompleteReadError as e:
|
131
128
|
raise ReadError(
|
132
129
|
"Got an incomplete frame while trying to read "
|
133
|
-
+ f"'{
|
130
|
+
+ f"'{frame_length - struct_header.size}' bytes"
|
134
131
|
) from e
|
135
132
|
|
136
|
-
if
|
137
|
-
raise ChecksumError(
|
133
|
+
if (checksum := bcc(header_bytes + payload[:-2])) and checksum != payload[-2]:
|
134
|
+
raise ChecksumError(
|
135
|
+
f"Incorrect frame checksum ({checksum} != {payload[-2]})"
|
136
|
+
)
|
138
137
|
|
139
138
|
frame = await Frame.create(
|
140
139
|
frame_type=payload[0],
|
File without changes
|
File without changes
|
File without changes
|