PyPlumIO 0.5.25__py3-none-any.whl → 0.5.27__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.25.dist-info → PyPlumIO-0.5.27.dist-info}/METADATA +7 -7
- PyPlumIO-0.5.27.dist-info/RECORD +60 -0
- {PyPlumIO-0.5.25.dist-info → PyPlumIO-0.5.27.dist-info}/WHEEL +1 -1
- pyplumio/_version.py +2 -2
- pyplumio/devices/__init__.py +27 -28
- pyplumio/devices/ecomax.py +51 -57
- pyplumio/devices/ecoster.py +3 -5
- pyplumio/devices/mixer.py +5 -8
- pyplumio/devices/thermostat.py +3 -3
- pyplumio/filters.py +6 -6
- pyplumio/frames/__init__.py +6 -6
- pyplumio/frames/messages.py +3 -3
- pyplumio/frames/requests.py +18 -18
- pyplumio/frames/responses.py +15 -15
- pyplumio/helpers/data_types.py +104 -68
- pyplumio/helpers/event_manager.py +6 -6
- pyplumio/helpers/factory.py +5 -6
- pyplumio/helpers/parameter.py +17 -15
- pyplumio/helpers/schedule.py +50 -46
- pyplumio/helpers/timeout.py +1 -1
- pyplumio/protocol.py +6 -7
- pyplumio/structures/alerts.py +8 -6
- pyplumio/structures/ecomax_parameters.py +30 -26
- pyplumio/structures/frame_versions.py +2 -3
- pyplumio/structures/mixer_parameters.py +9 -6
- pyplumio/structures/mixer_sensors.py +10 -8
- pyplumio/structures/modules.py +9 -7
- pyplumio/structures/network_info.py +16 -16
- pyplumio/structures/program_version.py +3 -0
- pyplumio/structures/regulator_data.py +2 -4
- pyplumio/structures/regulator_data_schema.py +2 -3
- pyplumio/structures/schedules.py +33 -35
- pyplumio/structures/thermostat_parameters.py +6 -4
- pyplumio/structures/thermostat_sensors.py +13 -10
- PyPlumIO-0.5.25.dist-info/RECORD +0 -60
- {PyPlumIO-0.5.25.dist-info → PyPlumIO-0.5.27.dist-info}/LICENSE +0 -0
- {PyPlumIO-0.5.25.dist-info → PyPlumIO-0.5.27.dist-info}/top_level.txt +0 -0
pyplumio/frames/requests.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
-
from typing import Any
|
5
|
+
from typing import Any
|
6
6
|
|
7
7
|
from pyplumio.const import (
|
8
8
|
ATTR_COUNT,
|
@@ -25,7 +25,7 @@ class ProgramVersionRequest(Request):
|
|
25
25
|
|
26
26
|
__slots__ = ()
|
27
27
|
|
28
|
-
frame_type
|
28
|
+
frame_type = FrameType.REQUEST_PROGRAM_VERSION
|
29
29
|
|
30
30
|
def response(self, **kwargs: Any) -> Response | None:
|
31
31
|
"""Return a response frame."""
|
@@ -37,7 +37,7 @@ class CheckDeviceRequest(Request):
|
|
37
37
|
|
38
38
|
__slots__ = ()
|
39
39
|
|
40
|
-
frame_type
|
40
|
+
frame_type = FrameType.REQUEST_CHECK_DEVICE
|
41
41
|
|
42
42
|
def response(self, **kwargs: Any) -> Response | None:
|
43
43
|
"""Return a response frame."""
|
@@ -49,7 +49,7 @@ class UIDRequest(Request):
|
|
49
49
|
|
50
50
|
__slots__ = ()
|
51
51
|
|
52
|
-
frame_type
|
52
|
+
frame_type = FrameType.REQUEST_UID
|
53
53
|
|
54
54
|
|
55
55
|
class PasswordRequest(Request):
|
@@ -57,7 +57,7 @@ class PasswordRequest(Request):
|
|
57
57
|
|
58
58
|
__slots__ = ()
|
59
59
|
|
60
|
-
frame_type
|
60
|
+
frame_type = FrameType.REQUEST_PASSWORD
|
61
61
|
|
62
62
|
|
63
63
|
class EcomaxParametersRequest(Request):
|
@@ -68,7 +68,7 @@ class EcomaxParametersRequest(Request):
|
|
68
68
|
|
69
69
|
__slots__ = ()
|
70
70
|
|
71
|
-
frame_type
|
71
|
+
frame_type = FrameType.REQUEST_ECOMAX_PARAMETERS
|
72
72
|
|
73
73
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
74
74
|
"""Create a frame message."""
|
@@ -84,7 +84,7 @@ class MixerParametersRequest(Request):
|
|
84
84
|
|
85
85
|
__slots__ = ()
|
86
86
|
|
87
|
-
frame_type
|
87
|
+
frame_type = FrameType.REQUEST_MIXER_PARAMETERS
|
88
88
|
|
89
89
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
90
90
|
"""Create a frame message."""
|
@@ -100,7 +100,7 @@ class ThermostatParametersRequest(Request):
|
|
100
100
|
|
101
101
|
__slots__ = ()
|
102
102
|
|
103
|
-
frame_type
|
103
|
+
frame_type = FrameType.REQUEST_THERMOSTAT_PARAMETERS
|
104
104
|
|
105
105
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
106
106
|
"""Create a frame message."""
|
@@ -112,7 +112,7 @@ class RegulatorDataSchemaRequest(Request):
|
|
112
112
|
|
113
113
|
__slots__ = ()
|
114
114
|
|
115
|
-
frame_type
|
115
|
+
frame_type = FrameType.REQUEST_REGULATOR_DATA_SCHEMA
|
116
116
|
|
117
117
|
|
118
118
|
class SetEcomaxParameterRequest(Request):
|
@@ -123,7 +123,7 @@ class SetEcomaxParameterRequest(Request):
|
|
123
123
|
|
124
124
|
__slots__ = ()
|
125
125
|
|
126
|
-
frame_type
|
126
|
+
frame_type = FrameType.REQUEST_SET_ECOMAX_PARAMETER
|
127
127
|
|
128
128
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
129
129
|
"""Create a frame message."""
|
@@ -141,7 +141,7 @@ class SetMixerParameterRequest(Request):
|
|
141
141
|
|
142
142
|
__slots__ = ()
|
143
143
|
|
144
|
-
frame_type
|
144
|
+
frame_type = FrameType.REQUEST_SET_MIXER_PARAMETER
|
145
145
|
|
146
146
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
147
147
|
"""Create a frame message."""
|
@@ -169,7 +169,7 @@ class SetThermostatParameterRequest(Request):
|
|
169
169
|
|
170
170
|
__slots__ = ()
|
171
171
|
|
172
|
-
frame_type
|
172
|
+
frame_type = FrameType.REQUEST_SET_THERMOSTAT_PARAMETER
|
173
173
|
|
174
174
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
175
175
|
"""Create a frame message."""
|
@@ -192,7 +192,7 @@ class EcomaxControlRequest(Request):
|
|
192
192
|
|
193
193
|
__slots__ = ()
|
194
194
|
|
195
|
-
frame_type
|
195
|
+
frame_type = FrameType.REQUEST_ECOMAX_CONTROL
|
196
196
|
|
197
197
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
198
198
|
"""Create a frame message."""
|
@@ -211,7 +211,7 @@ class StartMasterRequest(Request):
|
|
211
211
|
|
212
212
|
__slots__ = ()
|
213
213
|
|
214
|
-
frame_type
|
214
|
+
frame_type = FrameType.REQUEST_START_MASTER
|
215
215
|
|
216
216
|
|
217
217
|
class StopMasterRequest(Request):
|
@@ -223,7 +223,7 @@ class StopMasterRequest(Request):
|
|
223
223
|
|
224
224
|
__slots__ = ()
|
225
225
|
|
226
|
-
frame_type
|
226
|
+
frame_type = FrameType.REQUEST_STOP_MASTER
|
227
227
|
|
228
228
|
|
229
229
|
class AlertsRequest(Request):
|
@@ -235,7 +235,7 @@ class AlertsRequest(Request):
|
|
235
235
|
|
236
236
|
__slots__ = ()
|
237
237
|
|
238
|
-
frame_type
|
238
|
+
frame_type = FrameType.REQUEST_ALERTS
|
239
239
|
|
240
240
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
241
241
|
"""Create a frame message."""
|
@@ -247,7 +247,7 @@ class SchedulesRequest(Request):
|
|
247
247
|
|
248
248
|
__slots__ = ()
|
249
249
|
|
250
|
-
frame_type
|
250
|
+
frame_type = FrameType.REQUEST_SCHEDULES
|
251
251
|
|
252
252
|
|
253
253
|
class SetScheduleRequest(Request):
|
@@ -255,7 +255,7 @@ class SetScheduleRequest(Request):
|
|
255
255
|
|
256
256
|
__slots__ = ()
|
257
257
|
|
258
|
-
frame_type
|
258
|
+
frame_type = FrameType.REQUEST_SET_SCHEDULE
|
259
259
|
|
260
260
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
261
261
|
"""Create a frame message."""
|
pyplumio/frames/responses.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
-
from typing import Any
|
5
|
+
from typing import Any
|
6
6
|
|
7
7
|
from pyplumio.const import ATTR_PASSWORD, FrameType
|
8
8
|
from pyplumio.frames import Response
|
@@ -25,7 +25,7 @@ class ProgramVersionResponse(Response):
|
|
25
25
|
|
26
26
|
__slots__ = ()
|
27
27
|
|
28
|
-
frame_type
|
28
|
+
frame_type = FrameType.RESPONSE_PROGRAM_VERSION
|
29
29
|
|
30
30
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
31
31
|
"""Create a frame message."""
|
@@ -44,7 +44,7 @@ class DeviceAvailableResponse(Response):
|
|
44
44
|
|
45
45
|
__slots__ = ()
|
46
46
|
|
47
|
-
frame_type
|
47
|
+
frame_type = FrameType.RESPONSE_DEVICE_AVAILABLE
|
48
48
|
|
49
49
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
50
50
|
"""Create a frame message."""
|
@@ -63,7 +63,7 @@ class UIDResponse(Response):
|
|
63
63
|
|
64
64
|
__slots__ = ()
|
65
65
|
|
66
|
-
frame_type
|
66
|
+
frame_type = FrameType.RESPONSE_UID
|
67
67
|
|
68
68
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
69
69
|
"""Create a frame message."""
|
@@ -82,7 +82,7 @@ class PasswordResponse(Response):
|
|
82
82
|
|
83
83
|
__slots__ = ()
|
84
84
|
|
85
|
-
frame_type
|
85
|
+
frame_type = FrameType.RESPONSE_PASSWORD
|
86
86
|
|
87
87
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
88
88
|
"""Decode a frame message."""
|
@@ -98,7 +98,7 @@ class EcomaxParametersResponse(Response):
|
|
98
98
|
|
99
99
|
__slots__ = ()
|
100
100
|
|
101
|
-
frame_type
|
101
|
+
frame_type = FrameType.RESPONSE_ECOMAX_PARAMETERS
|
102
102
|
|
103
103
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
104
104
|
"""Decode a frame message."""
|
@@ -113,7 +113,7 @@ class MixerParametersResponse(Response):
|
|
113
113
|
|
114
114
|
__slots__ = ()
|
115
115
|
|
116
|
-
frame_type
|
116
|
+
frame_type = FrameType.RESPONSE_MIXER_PARAMETERS
|
117
117
|
|
118
118
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
119
119
|
"""Decode a frame message."""
|
@@ -128,7 +128,7 @@ class ThermostatParametersResponse(Response):
|
|
128
128
|
|
129
129
|
__slots__ = ()
|
130
130
|
|
131
|
-
frame_type
|
131
|
+
frame_type = FrameType.RESPONSE_THERMOSTAT_PARAMETERS
|
132
132
|
|
133
133
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
134
134
|
"""Decode a frame message."""
|
@@ -144,7 +144,7 @@ class RegulatorDataSchemaResponse(Response):
|
|
144
144
|
|
145
145
|
__slots__ = ()
|
146
146
|
|
147
|
-
frame_type
|
147
|
+
frame_type = FrameType.RESPONSE_REGULATOR_DATA_SCHEMA
|
148
148
|
|
149
149
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
150
150
|
"""Decode a frame message."""
|
@@ -160,7 +160,7 @@ class SetEcomaxParameterResponse(Response):
|
|
160
160
|
|
161
161
|
__slots__ = ()
|
162
162
|
|
163
|
-
frame_type
|
163
|
+
frame_type = FrameType.RESPONSE_SET_ECOMAX_PARAMETER
|
164
164
|
|
165
165
|
|
166
166
|
class SetMixerParameterResponse(Response):
|
@@ -172,7 +172,7 @@ class SetMixerParameterResponse(Response):
|
|
172
172
|
|
173
173
|
__slots__ = ()
|
174
174
|
|
175
|
-
frame_type
|
175
|
+
frame_type = FrameType.RESPONSE_SET_MIXER_PARAMETER
|
176
176
|
|
177
177
|
|
178
178
|
class SetThermostatParameterResponse(Response):
|
@@ -184,7 +184,7 @@ class SetThermostatParameterResponse(Response):
|
|
184
184
|
|
185
185
|
__slots__ = ()
|
186
186
|
|
187
|
-
frame_type
|
187
|
+
frame_type = FrameType.RESPONSE_SET_THERMOSTAT_PARAMETER
|
188
188
|
|
189
189
|
|
190
190
|
class EcomaxControlResponse(Response):
|
@@ -196,7 +196,7 @@ class EcomaxControlResponse(Response):
|
|
196
196
|
|
197
197
|
__slots__ = ()
|
198
198
|
|
199
|
-
frame_type
|
199
|
+
frame_type = FrameType.RESPONSE_ECOMAX_CONTROL
|
200
200
|
|
201
201
|
|
202
202
|
class AlertsResponse(Response):
|
@@ -204,7 +204,7 @@ class AlertsResponse(Response):
|
|
204
204
|
|
205
205
|
__slots__ = ()
|
206
206
|
|
207
|
-
frame_type
|
207
|
+
frame_type = FrameType.RESPONSE_ALERTS
|
208
208
|
|
209
209
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
210
210
|
"""Decode a frame message."""
|
@@ -216,7 +216,7 @@ class SchedulesResponse(Response):
|
|
216
216
|
|
217
217
|
__slots__ = ()
|
218
218
|
|
219
|
-
frame_type
|
219
|
+
frame_type = FrameType.RESPONSE_SCHEDULES
|
220
220
|
|
221
221
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
222
222
|
"""Decode a frame message."""
|
pyplumio/helpers/data_types.py
CHANGED
@@ -5,36 +5,58 @@ from __future__ import annotations
|
|
5
5
|
from abc import ABC, abstractmethod
|
6
6
|
import socket
|
7
7
|
import struct
|
8
|
-
from typing import
|
8
|
+
from typing import ClassVar, Final, Generic, TypeVar
|
9
9
|
|
10
|
+
T = TypeVar("T")
|
11
|
+
DataTypeT = TypeVar("DataTypeT", bound="DataType")
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
|
14
|
+
class DataType(ABC, Generic[T]):
|
15
|
+
"""Represents a data type."""
|
13
16
|
|
14
17
|
__slots__ = ("_value", "_size")
|
15
18
|
|
16
|
-
_value:
|
19
|
+
_value: T
|
17
20
|
_size: int
|
18
21
|
|
19
|
-
def __init__(self, value:
|
22
|
+
def __init__(self, value: T | None = None):
|
20
23
|
"""Initialize a new data type."""
|
21
|
-
|
24
|
+
if value is not None:
|
25
|
+
self._value = value
|
26
|
+
|
22
27
|
self._size = 0
|
23
28
|
|
24
29
|
def __repr__(self) -> str:
|
25
30
|
"""Return serializable string representation of the class."""
|
26
|
-
|
31
|
+
if hasattr(self, "_value"):
|
32
|
+
return f"{self.__class__.__name__}(value={self._value})"
|
33
|
+
|
34
|
+
return f"{self.__class__.__name__}()"
|
27
35
|
|
28
36
|
def __eq__(self, other: object) -> bool:
|
29
37
|
"""Compare if this data type is equal to other."""
|
30
|
-
if
|
38
|
+
if (
|
39
|
+
isinstance(other, DataType)
|
40
|
+
and hasattr(other, "_value")
|
41
|
+
and hasattr(self, "_value")
|
42
|
+
):
|
31
43
|
return bool(self._value == other._value)
|
32
44
|
|
33
|
-
|
45
|
+
if (
|
46
|
+
isinstance(other, DataType)
|
47
|
+
and not hasattr(other, "_value")
|
48
|
+
and not hasattr(self, "_value")
|
49
|
+
):
|
50
|
+
return type(self) is type(other)
|
51
|
+
|
52
|
+
if hasattr(self, "_value"):
|
53
|
+
return bool(self._value == other)
|
54
|
+
|
55
|
+
return NotImplemented
|
34
56
|
|
35
57
|
def _slice_data(self, data: bytes) -> bytes:
|
36
58
|
"""Slice the data to data type size."""
|
37
|
-
return data
|
59
|
+
return data if self.size == 0 else data[: self.size]
|
38
60
|
|
39
61
|
@classmethod
|
40
62
|
def from_bytes(cls: type[DataTypeT], data: bytes, offset: int = 0) -> DataTypeT:
|
@@ -48,7 +70,7 @@ class DataType(ABC):
|
|
48
70
|
return self.pack()
|
49
71
|
|
50
72
|
@property
|
51
|
-
def value(self) ->
|
73
|
+
def value(self) -> T:
|
52
74
|
"""Return the data type value."""
|
53
75
|
return self._value
|
54
76
|
|
@@ -66,9 +88,6 @@ class DataType(ABC):
|
|
66
88
|
"""Unpack the data."""
|
67
89
|
|
68
90
|
|
69
|
-
DataTypeT = TypeVar("DataTypeT", bound=DataType)
|
70
|
-
|
71
|
-
|
72
91
|
class Undefined(DataType):
|
73
92
|
"""Represents an undefined."""
|
74
93
|
|
@@ -86,21 +105,26 @@ class Undefined(DataType):
|
|
86
105
|
BITARRAY_LAST_INDEX: Final = 7
|
87
106
|
|
88
107
|
|
89
|
-
class BitArray(DataType):
|
108
|
+
class BitArray(DataType[int]):
|
90
109
|
"""Represents a bit array."""
|
91
110
|
|
92
111
|
__slots__ = ("_index",)
|
93
112
|
|
94
113
|
_index: int
|
95
114
|
|
96
|
-
def __init__(self, value:
|
115
|
+
def __init__(self, value: bool | None = None, index: int = 0):
|
97
116
|
"""Initialize a new bit array."""
|
98
117
|
super().__init__(value)
|
99
118
|
self._index = index
|
100
119
|
|
101
120
|
def __repr__(self) -> str:
|
102
121
|
"""Return serializable string representation of the class."""
|
103
|
-
|
122
|
+
if hasattr(self, "_value"):
|
123
|
+
return (
|
124
|
+
f"{self.__class__.__name__}(value={self._value}, index={self._index})"
|
125
|
+
)
|
126
|
+
|
127
|
+
return f"{self.__class__.__name__}(index={self._index})"
|
104
128
|
|
105
129
|
def next(self, index: int = 0) -> int:
|
106
130
|
"""Set current bit and return the next index in the bit array."""
|
@@ -109,16 +133,22 @@ class BitArray(DataType):
|
|
109
133
|
|
110
134
|
def pack(self) -> bytes:
|
111
135
|
"""Pack the data."""
|
112
|
-
|
136
|
+
if hasattr(self, "_value"):
|
137
|
+
return UnsignedChar(self._value).to_bytes()
|
138
|
+
|
139
|
+
return b""
|
113
140
|
|
114
141
|
def unpack(self, data: bytes) -> None:
|
115
142
|
"""Unpack the data."""
|
116
143
|
self._value = UnsignedChar.from_bytes(data[:1]).value
|
117
144
|
|
118
145
|
@property
|
119
|
-
def value(self) -> bool
|
146
|
+
def value(self) -> bool:
|
120
147
|
"""Return the data type value."""
|
121
|
-
|
148
|
+
if hasattr(self, "_value"):
|
149
|
+
return bool(self._value & (1 << self._index))
|
150
|
+
|
151
|
+
raise ValueError
|
122
152
|
|
123
153
|
@property
|
124
154
|
def size(self) -> int:
|
@@ -126,7 +156,7 @@ class BitArray(DataType):
|
|
126
156
|
return 1 if self._index == BITARRAY_LAST_INDEX else 0
|
127
157
|
|
128
158
|
|
129
|
-
class IPv4(DataType):
|
159
|
+
class IPv4(DataType[str]):
|
130
160
|
"""Represents an IPv4 address."""
|
131
161
|
|
132
162
|
__slots__ = ()
|
@@ -145,7 +175,7 @@ class IPv4(DataType):
|
|
145
175
|
self._value = socket.inet_ntoa(self._slice_data(data))
|
146
176
|
|
147
177
|
|
148
|
-
class IPv6(DataType):
|
178
|
+
class IPv6(DataType[str]):
|
149
179
|
"""Represents an IPv6 address."""
|
150
180
|
|
151
181
|
__slots__ = ()
|
@@ -164,20 +194,19 @@ class IPv6(DataType):
|
|
164
194
|
self._value = socket.inet_ntop(socket.AF_INET6, self._slice_data(data))
|
165
195
|
|
166
196
|
|
167
|
-
class String(DataType):
|
168
|
-
"""Represents a null
|
197
|
+
class String(DataType[str]):
|
198
|
+
"""Represents a null-terminated string."""
|
169
199
|
|
170
200
|
__slots__ = ()
|
171
201
|
|
172
|
-
def __init__(self, value:
|
173
|
-
"""Initialize a new null
|
202
|
+
def __init__(self, value: str = ""):
|
203
|
+
"""Initialize a new null-terminated string data type."""
|
174
204
|
super().__init__(value)
|
175
205
|
self._size = len(self.value) + 1
|
176
206
|
|
177
207
|
def pack(self) -> bytes:
|
178
208
|
"""Pack the data."""
|
179
|
-
|
180
|
-
return value.encode() + b"\0"
|
209
|
+
return self.value.encode() + b"\0"
|
181
210
|
|
182
211
|
def unpack(self, data: bytes) -> None:
|
183
212
|
"""Unpack the data."""
|
@@ -185,20 +214,19 @@ class String(DataType):
|
|
185
214
|
self._size = len(self.value) + 1
|
186
215
|
|
187
216
|
|
188
|
-
class VarBytes(DataType):
|
189
|
-
"""Represents a variable
|
217
|
+
class VarBytes(DataType[bytes]):
|
218
|
+
"""Represents a variable-length bytes."""
|
190
219
|
|
191
220
|
__slots__ = ()
|
192
221
|
|
193
|
-
def __init__(self, value:
|
194
|
-
"""Initialize a new variable
|
222
|
+
def __init__(self, value: bytes = b""):
|
223
|
+
"""Initialize a new variable-length bytes data type."""
|
195
224
|
super().__init__(value)
|
196
225
|
self._size = len(value) + 1
|
197
226
|
|
198
227
|
def pack(self) -> bytes:
|
199
228
|
"""Pack the data."""
|
200
|
-
|
201
|
-
return UnsignedChar(self.size - 1).to_bytes() + value
|
229
|
+
return UnsignedChar(self.size - 1).to_bytes() + self.value
|
202
230
|
|
203
231
|
def unpack(self, data: bytes) -> None:
|
204
232
|
"""Unpack the data."""
|
@@ -206,26 +234,30 @@ class VarBytes(DataType):
|
|
206
234
|
self._value = data[1 : self.size]
|
207
235
|
|
208
236
|
|
209
|
-
class VarString(
|
237
|
+
class VarString(DataType[str]):
|
210
238
|
"""Represents a variable length string."""
|
211
239
|
|
212
240
|
__slots__ = ()
|
213
241
|
|
242
|
+
def __init__(self, value: str = ""):
|
243
|
+
"""Initialize a new variable length bytes data type."""
|
244
|
+
super().__init__(value)
|
245
|
+
self._size = len(value) + 1
|
246
|
+
|
214
247
|
def pack(self) -> bytes:
|
215
248
|
"""Pack the data."""
|
216
|
-
|
217
|
-
return UnsignedChar(self.size - 1).to_bytes() + value.encode()
|
249
|
+
return UnsignedChar(self.size - 1).to_bytes() + self.value.encode()
|
218
250
|
|
219
251
|
def unpack(self, data: bytes) -> None:
|
220
252
|
"""Unpack the data."""
|
221
|
-
|
222
|
-
self._value = self.
|
253
|
+
self._size = data[0] + 1
|
254
|
+
self._value = data[1 : self.size].decode("utf-8", "replace")
|
223
255
|
|
224
256
|
|
225
|
-
class BuiltInDataType(DataType, ABC):
|
257
|
+
class BuiltInDataType(DataType[T], ABC):
|
226
258
|
"""Represents a data type that is supported by the struct module."""
|
227
259
|
|
228
|
-
__slots__ = ()
|
260
|
+
__slots__ = ("_struct",)
|
229
261
|
|
230
262
|
_struct: ClassVar[struct.Struct]
|
231
263
|
|
@@ -240,86 +272,90 @@ class BuiltInDataType(DataType, ABC):
|
|
240
272
|
@property
|
241
273
|
def size(self) -> int:
|
242
274
|
"""Return a data type size."""
|
243
|
-
|
275
|
+
if not self._size:
|
276
|
+
self._size = self._struct.size
|
244
277
|
|
278
|
+
return self._size
|
245
279
|
|
246
|
-
|
280
|
+
|
281
|
+
class SignedChar(BuiltInDataType[int]):
|
247
282
|
"""Represents a signed char."""
|
248
283
|
|
249
284
|
__slots__ = ()
|
250
285
|
|
251
|
-
_struct
|
286
|
+
_struct = struct.Struct("<b")
|
252
287
|
|
253
288
|
|
254
|
-
class UnsignedChar(BuiltInDataType):
|
289
|
+
class UnsignedChar(BuiltInDataType[int]):
|
255
290
|
"""Represents an unsigned char."""
|
256
291
|
|
257
292
|
__slots__ = ()
|
258
293
|
|
259
|
-
_struct
|
294
|
+
_struct = struct.Struct("<B")
|
260
295
|
|
261
296
|
|
262
|
-
class Short(BuiltInDataType):
|
263
|
-
"""Represents a 16
|
297
|
+
class Short(BuiltInDataType[int]):
|
298
|
+
"""Represents a 16-bit integer."""
|
264
299
|
|
265
300
|
__slots__ = ()
|
266
301
|
|
267
|
-
_struct
|
302
|
+
_struct = struct.Struct("<h")
|
268
303
|
|
269
304
|
|
270
|
-
class UnsignedShort(BuiltInDataType):
|
271
|
-
"""Represents an unsigned 16
|
305
|
+
class UnsignedShort(BuiltInDataType[int]):
|
306
|
+
"""Represents an unsigned 16-bit integer."""
|
272
307
|
|
273
308
|
__slots__ = ()
|
274
309
|
|
275
|
-
_struct
|
310
|
+
_struct = struct.Struct("<H")
|
276
311
|
|
277
312
|
|
278
|
-
class Int(BuiltInDataType):
|
279
|
-
"""Represents a 32
|
313
|
+
class Int(BuiltInDataType[int]):
|
314
|
+
"""Represents a 32-bit integer."""
|
280
315
|
|
281
316
|
__slots__ = ()
|
282
317
|
|
283
|
-
_struct
|
318
|
+
_struct = struct.Struct("<i")
|
284
319
|
|
285
320
|
|
286
|
-
class UnsignedInt(BuiltInDataType):
|
287
|
-
"""Represents a unsigned 32
|
321
|
+
class UnsignedInt(BuiltInDataType[int]):
|
322
|
+
"""Represents a unsigned 32-bit integer."""
|
288
323
|
|
289
324
|
__slots__ = ()
|
290
325
|
|
291
|
-
_struct
|
326
|
+
_struct = struct.Struct("<I")
|
292
327
|
|
293
328
|
|
294
|
-
class Float(BuiltInDataType):
|
329
|
+
class Float(BuiltInDataType[int]):
|
295
330
|
"""Represents a float."""
|
296
331
|
|
297
332
|
__slots__ = ()
|
298
333
|
|
299
|
-
_struct
|
334
|
+
_struct = struct.Struct("<f")
|
300
335
|
|
301
336
|
|
302
|
-
class Double(BuiltInDataType):
|
337
|
+
class Double(BuiltInDataType[int]):
|
303
338
|
"""Represents a double."""
|
304
339
|
|
305
340
|
__slots__ = ()
|
306
341
|
|
307
|
-
_struct
|
342
|
+
_struct = struct.Struct("<d")
|
308
343
|
|
309
344
|
|
310
|
-
class Int64(BuiltInDataType):
|
311
|
-
"""Represents a 64
|
345
|
+
class Int64(BuiltInDataType[int]):
|
346
|
+
"""Represents a 64-bit signed integer."""
|
312
347
|
|
313
348
|
__slots__ = ()
|
314
349
|
|
315
|
-
_struct
|
350
|
+
_struct = struct.Struct("<q")
|
316
351
|
|
317
352
|
|
318
|
-
class UInt64(BuiltInDataType):
|
319
|
-
"""Represents a 64
|
353
|
+
class UInt64(BuiltInDataType[int]):
|
354
|
+
"""Represents a 64-bit unsigned integer."""
|
320
355
|
|
321
356
|
__slots__ = ()
|
322
|
-
|
357
|
+
|
358
|
+
_struct = struct.Struct("<Q")
|
323
359
|
|
324
360
|
|
325
361
|
# The regdata type map links data type classes to their
|