PyPlumIO 0.5.21__py3-none-any.whl → 0.5.23__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.21.dist-info → PyPlumIO-0.5.23.dist-info}/METADATA +12 -10
- PyPlumIO-0.5.23.dist-info/RECORD +60 -0
- {PyPlumIO-0.5.21.dist-info → PyPlumIO-0.5.23.dist-info}/WHEEL +1 -1
- pyplumio/__init__.py +2 -2
- pyplumio/_version.py +2 -2
- pyplumio/connection.py +3 -12
- pyplumio/devices/__init__.py +16 -16
- pyplumio/devices/ecomax.py +126 -126
- pyplumio/devices/mixer.py +50 -44
- pyplumio/devices/thermostat.py +36 -35
- pyplumio/exceptions.py +9 -9
- pyplumio/filters.py +56 -37
- pyplumio/frames/__init__.py +6 -6
- pyplumio/frames/messages.py +4 -6
- pyplumio/helpers/data_types.py +8 -7
- pyplumio/helpers/event_manager.py +53 -33
- pyplumio/helpers/parameter.py +138 -52
- pyplumio/helpers/task_manager.py +7 -2
- pyplumio/helpers/timeout.py +0 -3
- pyplumio/helpers/uid.py +2 -2
- pyplumio/protocol.py +35 -28
- pyplumio/stream.py +2 -2
- pyplumio/structures/alerts.py +40 -31
- pyplumio/structures/ecomax_parameters.py +493 -282
- pyplumio/structures/frame_versions.py +5 -6
- pyplumio/structures/lambda_sensor.py +6 -6
- pyplumio/structures/mixer_parameters.py +136 -71
- pyplumio/structures/network_info.py +2 -3
- pyplumio/structures/product_info.py +0 -4
- pyplumio/structures/program_version.py +24 -17
- pyplumio/structures/schedules.py +35 -15
- pyplumio/structures/thermostat_parameters.py +82 -50
- pyplumio/utils.py +12 -7
- PyPlumIO-0.5.21.dist-info/RECORD +0 -61
- pyplumio/helpers/typing.py +0 -29
- {PyPlumIO-0.5.21.dist-info → PyPlumIO-0.5.23.dist-info}/LICENSE +0 -0
- {PyPlumIO-0.5.21.dist-info → PyPlumIO-0.5.23.dist-info}/top_level.txt +0 -0
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
from contextlib import suppress
|
5
6
|
from typing import Any, Final
|
6
7
|
|
7
8
|
from pyplumio.const import FrameType
|
@@ -21,15 +22,13 @@ class FrameVersionsStructure(StructureDecoder):
|
|
21
22
|
|
22
23
|
def _unpack_frame_versions(self, message: bytearray) -> tuple[FrameType | int, int]:
|
23
24
|
"""Unpack frame versions."""
|
24
|
-
|
25
|
-
frame_type = message[self._offset]
|
26
|
-
frame_type = FrameType(frame_type)
|
27
|
-
except ValueError:
|
28
|
-
pass
|
29
|
-
|
25
|
+
frame_type = message[self._offset]
|
30
26
|
self._offset += 1
|
31
27
|
version = UnsignedShort.from_bytes(message, self._offset)
|
32
28
|
self._offset += version.size
|
29
|
+
with suppress(ValueError):
|
30
|
+
frame_type = FrameType(frame_type)
|
31
|
+
|
33
32
|
return frame_type, version.value
|
34
33
|
|
35
34
|
def decode(
|
@@ -30,22 +30,22 @@ class LambdaSensorStructure(StructureDecoder):
|
|
30
30
|
if lambda_state == BYTE_UNDEFINED:
|
31
31
|
return ensure_dict(data), offset
|
32
32
|
|
33
|
-
with suppress(ValueError):
|
34
|
-
lambda_state = LambdaState(lambda_state)
|
35
|
-
|
36
33
|
lambda_target = message[offset]
|
37
34
|
offset += 1
|
38
35
|
level = UnsignedShort.from_bytes(message, offset)
|
39
36
|
offset += level.size
|
37
|
+
with suppress(ValueError):
|
38
|
+
lambda_state = LambdaState(lambda_state)
|
39
|
+
|
40
40
|
return (
|
41
41
|
ensure_dict(
|
42
42
|
data,
|
43
43
|
{
|
44
44
|
ATTR_LAMBDA_STATE: lambda_state,
|
45
45
|
ATTR_LAMBDA_TARGET: lambda_target,
|
46
|
-
ATTR_LAMBDA_LEVEL:
|
47
|
-
|
48
|
-
|
46
|
+
ATTR_LAMBDA_LEVEL: (
|
47
|
+
None if math.isnan(level.value) else (level.value / 10)
|
48
|
+
),
|
49
49
|
},
|
50
50
|
),
|
51
51
|
offset,
|
@@ -6,6 +6,8 @@ from collections.abc import Generator
|
|
6
6
|
from dataclasses import dataclass
|
7
7
|
from typing import TYPE_CHECKING, Any, Final
|
8
8
|
|
9
|
+
from dataslots import dataslots
|
10
|
+
|
9
11
|
from pyplumio.const import (
|
10
12
|
ATTR_DEVICE_INDEX,
|
11
13
|
ATTR_INDEX,
|
@@ -16,14 +18,16 @@ from pyplumio.const import (
|
|
16
18
|
)
|
17
19
|
from pyplumio.frames import Request
|
18
20
|
from pyplumio.helpers.parameter import (
|
19
|
-
|
20
|
-
|
21
|
+
SET_RETRIES,
|
22
|
+
Number,
|
23
|
+
NumberDescription,
|
21
24
|
Parameter,
|
22
25
|
ParameterDescription,
|
23
26
|
ParameterValues,
|
27
|
+
Switch,
|
28
|
+
SwitchDescription,
|
24
29
|
unpack_parameter,
|
25
30
|
)
|
26
|
-
from pyplumio.helpers.typing import ParameterValueType
|
27
31
|
from pyplumio.structures import StructureDecoder
|
28
32
|
from pyplumio.utils import ensure_dict
|
29
33
|
|
@@ -35,8 +39,18 @@ ATTR_MIXER_PARAMETERS: Final = "mixer_parameters"
|
|
35
39
|
MIXER_PARAMETER_SIZE: Final = 3
|
36
40
|
|
37
41
|
|
42
|
+
@dataclass
|
43
|
+
class MixerParameterDescription(ParameterDescription):
|
44
|
+
"""Represents a mixer parameter description."""
|
45
|
+
|
46
|
+
__slots__ = ()
|
47
|
+
|
48
|
+
multiplier: float = 1.0
|
49
|
+
offset: int = 0
|
50
|
+
|
51
|
+
|
38
52
|
class MixerParameter(Parameter):
|
39
|
-
"""
|
53
|
+
"""Represent a mixer parameter."""
|
40
54
|
|
41
55
|
__slots__ = ()
|
42
56
|
|
@@ -55,140 +69,191 @@ class MixerParameter(Parameter):
|
|
55
69
|
},
|
56
70
|
)
|
57
71
|
|
58
|
-
async def set(self, value: ParameterValueType, retries: int = 5) -> bool:
|
59
|
-
"""Set a parameter value."""
|
60
|
-
if isinstance(value, (int, float)):
|
61
|
-
value = int((value + self.description.offset) / self.description.multiplier)
|
62
72
|
|
73
|
+
@dataslots
|
74
|
+
@dataclass
|
75
|
+
class MixerNumberDescription(MixerParameterDescription, NumberDescription):
|
76
|
+
"""Represent a mixer number description."""
|
77
|
+
|
78
|
+
|
79
|
+
class MixerNumber(MixerParameter, Number):
|
80
|
+
"""Represents a mixer number."""
|
81
|
+
|
82
|
+
__slots__ = ()
|
83
|
+
|
84
|
+
description: MixerNumberDescription
|
85
|
+
|
86
|
+
async def set(self, value: int | float, retries: int = SET_RETRIES) -> bool:
|
87
|
+
"""Set a parameter value."""
|
88
|
+
value = (value + self.description.offset) / self.description.multiplier
|
63
89
|
return await super().set(value, retries)
|
64
90
|
|
65
91
|
@property
|
66
|
-
def value(self) ->
|
92
|
+
def value(self) -> float:
|
67
93
|
"""Return the parameter value."""
|
68
94
|
return (
|
69
95
|
self.values.value - self.description.offset
|
70
96
|
) * self.description.multiplier
|
71
97
|
|
72
98
|
@property
|
73
|
-
def min_value(self) ->
|
99
|
+
def min_value(self) -> float:
|
74
100
|
"""Return the minimum allowed value."""
|
75
101
|
return (
|
76
102
|
self.values.min_value - self.description.offset
|
77
103
|
) * self.description.multiplier
|
78
104
|
|
79
105
|
@property
|
80
|
-
def max_value(self) ->
|
106
|
+
def max_value(self) -> float:
|
81
107
|
"""Return the maximum allowed value."""
|
82
108
|
return (
|
83
109
|
self.values.max_value - self.description.offset
|
84
110
|
) * self.description.multiplier
|
85
111
|
|
86
112
|
|
87
|
-
|
88
|
-
"""Represents a mixer binary parameter."""
|
89
|
-
|
90
|
-
__slots__ = ()
|
91
|
-
|
92
|
-
|
113
|
+
@dataslots
|
93
114
|
@dataclass
|
94
|
-
class MixerParameterDescription
|
95
|
-
"""Represents a mixer
|
115
|
+
class MixerSwitchDescription(MixerParameterDescription, SwitchDescription):
|
116
|
+
"""Represents a mixer switch description."""
|
96
117
|
|
97
|
-
multiplier: float = 1
|
98
|
-
offset: int = 0
|
99
118
|
|
119
|
+
class MixerSwitch(MixerParameter, Switch):
|
120
|
+
"""Represents a mixer switch."""
|
100
121
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
):
|
105
|
-
"""Represents a mixer binary parameter description."""
|
122
|
+
__slots__ = ()
|
123
|
+
|
124
|
+
description: MixerSwitchDescription
|
106
125
|
|
107
126
|
|
108
127
|
MIXER_PARAMETERS: dict[ProductType, tuple[MixerParameterDescription, ...]] = {
|
109
128
|
ProductType.ECOMAX_P: (
|
110
|
-
|
111
|
-
name="mixer_target_temp",
|
129
|
+
MixerNumberDescription(
|
130
|
+
name="mixer_target_temp",
|
131
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
112
132
|
),
|
113
|
-
|
114
|
-
name="min_target_temp",
|
133
|
+
MixerNumberDescription(
|
134
|
+
name="min_target_temp",
|
135
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
115
136
|
),
|
116
|
-
|
117
|
-
name="max_target_temp",
|
137
|
+
MixerNumberDescription(
|
138
|
+
name="max_target_temp",
|
139
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
118
140
|
),
|
119
|
-
|
141
|
+
MixerNumberDescription(
|
120
142
|
name="thermostat_decrease_target_temp",
|
121
143
|
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
122
144
|
),
|
123
|
-
|
124
|
-
|
125
|
-
|
145
|
+
MixerSwitchDescription(
|
146
|
+
name="weather_control",
|
147
|
+
),
|
148
|
+
MixerNumberDescription(
|
149
|
+
name="heating_curve",
|
150
|
+
multiplier=0.1,
|
151
|
+
),
|
152
|
+
MixerNumberDescription(
|
126
153
|
name="heating_curve_shift",
|
127
154
|
offset=20,
|
128
155
|
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
129
156
|
),
|
130
|
-
|
131
|
-
|
132
|
-
|
157
|
+
MixerNumberDescription(
|
158
|
+
name="weather_factor",
|
159
|
+
),
|
160
|
+
MixerNumberDescription(
|
161
|
+
name="work_mode",
|
162
|
+
),
|
163
|
+
MixerNumberDescription(
|
133
164
|
name="mixer_input_dead_zone",
|
134
165
|
multiplier=0.1,
|
135
166
|
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
136
167
|
),
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
168
|
+
MixerSwitchDescription(
|
169
|
+
name="thermostat_operation",
|
170
|
+
),
|
171
|
+
MixerNumberDescription(
|
172
|
+
name="thermostat_mode",
|
173
|
+
),
|
174
|
+
MixerSwitchDescription(
|
175
|
+
name="disable_pump_on_thermostat",
|
176
|
+
),
|
177
|
+
MixerSwitchDescription(
|
178
|
+
name="summer_work",
|
179
|
+
),
|
141
180
|
),
|
142
181
|
ProductType.ECOMAX_I: (
|
143
|
-
|
144
|
-
|
145
|
-
|
182
|
+
MixerNumberDescription(
|
183
|
+
name="work_mode",
|
184
|
+
),
|
185
|
+
MixerNumberDescription(
|
186
|
+
name="circuit_target_temp",
|
187
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
146
188
|
),
|
147
|
-
|
148
|
-
name="day_target_temp",
|
189
|
+
MixerNumberDescription(
|
190
|
+
name="day_target_temp",
|
191
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
192
|
+
),
|
193
|
+
MixerNumberDescription(
|
194
|
+
name="night_target_temp",
|
195
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
196
|
+
),
|
197
|
+
MixerNumberDescription(
|
198
|
+
name="min_target_temp",
|
199
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
200
|
+
),
|
201
|
+
MixerNumberDescription(
|
202
|
+
name="max_target_temp",
|
203
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
149
204
|
),
|
150
|
-
|
151
|
-
name="
|
205
|
+
MixerSwitchDescription(
|
206
|
+
name="summer_work",
|
152
207
|
),
|
153
|
-
|
154
|
-
name="
|
208
|
+
MixerSwitchDescription(
|
209
|
+
name="weather_control",
|
155
210
|
),
|
156
|
-
|
157
|
-
name="
|
211
|
+
MixerNumberDescription(
|
212
|
+
name="enable_circuit",
|
158
213
|
),
|
159
|
-
|
160
|
-
MixerBinaryParameterDescription(name="weather_control"),
|
161
|
-
MixerParameterDescription(name="enable_circuit"),
|
162
|
-
MixerParameterDescription(
|
214
|
+
MixerNumberDescription(
|
163
215
|
name="constant_water_preset_temp",
|
164
216
|
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
165
217
|
),
|
166
|
-
|
218
|
+
MixerNumberDescription(
|
167
219
|
name="thermostat_decrease_temp",
|
168
220
|
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
169
221
|
),
|
170
|
-
|
171
|
-
name="thermostat_correction",
|
222
|
+
MixerNumberDescription(
|
223
|
+
name="thermostat_correction",
|
224
|
+
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
225
|
+
),
|
226
|
+
MixerSwitchDescription(
|
227
|
+
name="thermostat_pump_lock",
|
172
228
|
),
|
173
|
-
|
174
|
-
|
175
|
-
|
229
|
+
MixerNumberDescription(
|
230
|
+
name="valve_opening_time",
|
231
|
+
unit_of_measurement=UnitOfMeasurement.SECONDS,
|
176
232
|
),
|
177
|
-
|
233
|
+
MixerNumberDescription(
|
178
234
|
name="mixer_input_dead_zone",
|
179
235
|
multiplier=0.1,
|
180
236
|
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
181
237
|
),
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
238
|
+
MixerNumberDescription(
|
239
|
+
name="proportional_range",
|
240
|
+
),
|
241
|
+
MixerNumberDescription(
|
242
|
+
name="integration_time_constant",
|
243
|
+
),
|
244
|
+
MixerNumberDescription(
|
245
|
+
name="heating_curve",
|
246
|
+
multiplier=0.1,
|
247
|
+
),
|
248
|
+
MixerNumberDescription(
|
186
249
|
name="heating_curve_shift",
|
187
250
|
offset=20,
|
188
251
|
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
189
252
|
),
|
190
|
-
|
191
|
-
|
253
|
+
MixerNumberDescription(
|
254
|
+
name="thermostat_mode",
|
255
|
+
),
|
256
|
+
MixerNumberDescription(
|
192
257
|
name="decreasing_constant_water_temp",
|
193
258
|
unit_of_measurement=UnitOfMeasurement.CELSIUS,
|
194
259
|
),
|
@@ -66,9 +66,8 @@ class NetworkInfoStructure(Structure):
|
|
66
66
|
|
67
67
|
def encode(self, data: dict[str, Any]) -> bytearray:
|
68
68
|
"""Encode data to the bytearray message."""
|
69
|
-
message = bytearray()
|
70
|
-
|
71
|
-
network_info = data[ATTR_NETWORK] if ATTR_NETWORK in data else NetworkInfo()
|
69
|
+
message = bytearray(b"\x01")
|
70
|
+
network_info: NetworkInfo = data.get(ATTR_NETWORK, NetworkInfo())
|
72
71
|
message += IPv4(network_info.eth.ip).to_bytes()
|
73
72
|
message += IPv4(network_info.eth.netmask).to_bytes()
|
74
73
|
message += IPv4(network_info.eth.gateway).to_bytes()
|
@@ -53,16 +53,12 @@ class ProductInfoStructure(StructureDecoder):
|
|
53
53
|
"""Decode bytes and return message data and offset."""
|
54
54
|
product_type, product_id = struct.unpack_from("<BH", message)
|
55
55
|
offset += 3
|
56
|
-
|
57
56
|
uid = VarBytes.from_bytes(message, offset)
|
58
57
|
offset += uid.size
|
59
|
-
|
60
58
|
logo = UnsignedShort.from_bytes(message, offset)
|
61
59
|
offset += logo.size
|
62
|
-
|
63
60
|
image = UnsignedShort.from_bytes(message, offset)
|
64
61
|
offset += image.size
|
65
|
-
|
66
62
|
model_name = VarString.from_bytes(message, offset)
|
67
63
|
offset += model_name.size
|
68
64
|
|
@@ -14,7 +14,7 @@ ATTR_VERSION: Final = "version"
|
|
14
14
|
|
15
15
|
VERSION_INFO_SIZE: Final = 15
|
16
16
|
|
17
|
-
SOFTWARE_VERSION:
|
17
|
+
SOFTWARE_VERSION: Final = ".".join(str(x) for x in __version_tuple__[0:3])
|
18
18
|
|
19
19
|
struct_program_version = struct.Struct("<2sB2s3s3HB")
|
20
20
|
|
@@ -38,7 +38,7 @@ class ProgramVersionStructure(Structure):
|
|
38
38
|
def encode(self, data: dict[str, Any]) -> bytearray:
|
39
39
|
"""Encode data to the bytearray message."""
|
40
40
|
message = bytearray(struct_program_version.size)
|
41
|
-
version_info = data
|
41
|
+
version_info: VersionInfo = data.get(ATTR_VERSION, VersionInfo())
|
42
42
|
struct_program_version.pack_into(
|
43
43
|
message,
|
44
44
|
0,
|
@@ -55,22 +55,29 @@ class ProgramVersionStructure(Structure):
|
|
55
55
|
self, message: bytearray, offset: int = 0, data: dict[str, Any] | None = None
|
56
56
|
) -> tuple[dict[str, Any], int]:
|
57
57
|
"""Decode bytes and return message data and offset."""
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
] = struct_program_version.unpack_from(message)
|
69
|
-
version_info.software = ".".join(
|
70
|
-
map(str, [software_version1, software_version2, software_version3])
|
71
|
-
)
|
58
|
+
(
|
59
|
+
struct_tag,
|
60
|
+
struct_version,
|
61
|
+
device_id,
|
62
|
+
processor_signature,
|
63
|
+
software1,
|
64
|
+
software2,
|
65
|
+
software3,
|
66
|
+
_, # recipient
|
67
|
+
) = struct_program_version.unpack_from(message)
|
72
68
|
|
73
69
|
return (
|
74
|
-
ensure_dict(
|
70
|
+
ensure_dict(
|
71
|
+
data,
|
72
|
+
{
|
73
|
+
ATTR_VERSION: VersionInfo(
|
74
|
+
software=".".join(map(str, [software1, software2, software3])),
|
75
|
+
struct_tag=struct_tag,
|
76
|
+
struct_version=struct_version,
|
77
|
+
device_id=device_id,
|
78
|
+
processor_signature=processor_signature,
|
79
|
+
)
|
80
|
+
},
|
81
|
+
),
|
75
82
|
offset + VERSION_INFO_SIZE,
|
76
83
|
)
|
pyplumio/structures/schedules.py
CHANGED
@@ -8,6 +8,8 @@ from functools import reduce
|
|
8
8
|
from itertools import chain
|
9
9
|
from typing import Any, Final
|
10
10
|
|
11
|
+
from dataslots import dataslots
|
12
|
+
|
11
13
|
from pyplumio.const import (
|
12
14
|
ATTR_PARAMETER,
|
13
15
|
ATTR_SCHEDULE,
|
@@ -19,11 +21,13 @@ from pyplumio.devices import AddressableDevice, Device
|
|
19
21
|
from pyplumio.exceptions import FrameDataError
|
20
22
|
from pyplumio.frames import Request
|
21
23
|
from pyplumio.helpers.parameter import (
|
22
|
-
|
23
|
-
|
24
|
+
Number,
|
25
|
+
NumberDescription,
|
24
26
|
Parameter,
|
25
27
|
ParameterDescription,
|
26
28
|
ParameterValues,
|
29
|
+
Switch,
|
30
|
+
SwitchDescription,
|
27
31
|
unpack_parameter,
|
28
32
|
)
|
29
33
|
from pyplumio.structures import Structure
|
@@ -80,12 +84,20 @@ SCHEDULES: tuple[str, ...] = (
|
|
80
84
|
)
|
81
85
|
|
82
86
|
|
87
|
+
@dataclass
|
88
|
+
class ScheduleParameterDescription(ParameterDescription):
|
89
|
+
"""Represent a schedule parameter description."""
|
90
|
+
|
91
|
+
__slots__ = ()
|
92
|
+
|
93
|
+
|
83
94
|
class ScheduleParameter(Parameter):
|
84
95
|
"""Represents a schedule parameter."""
|
85
96
|
|
86
97
|
__slots__ = ()
|
87
98
|
|
88
99
|
device: AddressableDevice
|
100
|
+
description: ScheduleParameterDescription
|
89
101
|
|
90
102
|
async def create_request(self) -> Request:
|
91
103
|
"""Create a request to change the parameter."""
|
@@ -97,32 +109,40 @@ class ScheduleParameter(Parameter):
|
|
97
109
|
)
|
98
110
|
|
99
111
|
|
100
|
-
|
101
|
-
|
112
|
+
@dataslots
|
113
|
+
@dataclass
|
114
|
+
class ScheduleNumberDescription(ScheduleParameterDescription, NumberDescription):
|
115
|
+
"""Represents a schedule number description."""
|
116
|
+
|
117
|
+
|
118
|
+
class ScheduleNumber(ScheduleParameter, Number):
|
119
|
+
"""Represents a schedule number."""
|
102
120
|
|
103
121
|
__slots__ = ()
|
104
122
|
|
123
|
+
description: ScheduleNumberDescription
|
105
124
|
|
125
|
+
|
126
|
+
@dataslots
|
106
127
|
@dataclass
|
107
|
-
class ScheduleParameterDescription
|
108
|
-
"""Represents a schedule
|
128
|
+
class ScheduleSwitchDescription(ScheduleParameterDescription, SwitchDescription):
|
129
|
+
"""Represents a schedule switch description."""
|
109
130
|
|
110
131
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
)
|
115
|
-
|
132
|
+
class ScheduleSwitch(ScheduleParameter, Switch):
|
133
|
+
"""Represents a schedule switch."""
|
134
|
+
|
135
|
+
__slots__ = ()
|
136
|
+
|
137
|
+
description: ScheduleSwitchDescription
|
116
138
|
|
117
139
|
|
118
140
|
SCHEDULE_PARAMETERS: list[ScheduleParameterDescription] = list(
|
119
141
|
chain.from_iterable(
|
120
142
|
[
|
121
143
|
[
|
122
|
-
|
123
|
-
|
124
|
-
),
|
125
|
-
ScheduleParameterDescription(name=f"{name}_{ATTR_SCHEDULE_PARAMETER}"),
|
144
|
+
ScheduleSwitchDescription(name=f"{name}_{ATTR_SCHEDULE_SWITCH}"),
|
145
|
+
ScheduleNumberDescription(name=f"{name}_{ATTR_SCHEDULE_PARAMETER}"),
|
126
146
|
]
|
127
147
|
for name in SCHEDULES
|
128
148
|
]
|