PyPlumIO 0.5.22__py3-none-any.whl → 0.5.24__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.22.dist-info → PyPlumIO-0.5.24.dist-info}/METADATA +10 -8
- {PyPlumIO-0.5.22.dist-info → PyPlumIO-0.5.24.dist-info}/RECORD +18 -18
- {PyPlumIO-0.5.22.dist-info → PyPlumIO-0.5.24.dist-info}/WHEEL +1 -1
- pyplumio/_version.py +2 -2
- pyplumio/connection.py +1 -2
- pyplumio/devices/__init__.py +2 -2
- pyplumio/devices/ecomax.py +17 -17
- pyplumio/devices/mixer.py +6 -6
- pyplumio/devices/thermostat.py +6 -6
- pyplumio/helpers/event_manager.py +19 -10
- pyplumio/helpers/parameter.py +125 -57
- pyplumio/protocol.py +4 -3
- pyplumio/structures/ecomax_parameters.py +229 -208
- pyplumio/structures/mixer_parameters.py +80 -61
- pyplumio/structures/schedules.py +35 -15
- pyplumio/structures/thermostat_parameters.py +61 -42
- {PyPlumIO-0.5.22.dist-info → PyPlumIO-0.5.24.dist-info}/LICENSE +0 -0
- {PyPlumIO-0.5.22.dist-info → PyPlumIO-0.5.24.dist-info}/top_level.txt +0 -0
pyplumio/helpers/parameter.py
CHANGED
@@ -2,12 +2,15 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
-
from abc import ABC
|
5
|
+
from abc import ABC, abstractmethod
|
6
6
|
import asyncio
|
7
7
|
from dataclasses import dataclass
|
8
8
|
import logging
|
9
9
|
from typing import TYPE_CHECKING, Any, Final, Literal, TypeVar, Union
|
10
10
|
|
11
|
+
from dataslots import dataslots
|
12
|
+
from typing_extensions import TypeAlias
|
13
|
+
|
11
14
|
from pyplumio.const import BYTE_UNDEFINED, STATE_OFF, STATE_ON, UnitOfMeasurement
|
12
15
|
from pyplumio.frames import Request
|
13
16
|
|
@@ -19,7 +22,8 @@ _LOGGER = logging.getLogger(__name__)
|
|
19
22
|
SET_TIMEOUT: Final = 5
|
20
23
|
SET_RETRIES: Final = 5
|
21
24
|
|
22
|
-
ParameterValueType = Union[int, float, bool, Literal["off"
|
25
|
+
ParameterValueType: TypeAlias = Union[int, float, bool, Literal["off", "on"]]
|
26
|
+
ParameterT = TypeVar("ParameterT", bound="Parameter")
|
23
27
|
|
24
28
|
|
25
29
|
def unpack_parameter(
|
@@ -46,13 +50,10 @@ def check_parameter(data: bytearray) -> bool:
|
|
46
50
|
|
47
51
|
|
48
52
|
def _normalize_parameter_value(value: ParameterValueType) -> int:
|
49
|
-
"""Normalize a parameter value
|
50
|
-
if
|
53
|
+
"""Normalize a parameter value."""
|
54
|
+
if value in (STATE_OFF, STATE_ON):
|
51
55
|
return 1 if value == STATE_ON else 0
|
52
56
|
|
53
|
-
if isinstance(value, ParameterValues):
|
54
|
-
value = value.value
|
55
|
-
|
56
57
|
return int(value)
|
57
58
|
|
58
59
|
|
@@ -67,21 +68,16 @@ class ParameterValues:
|
|
67
68
|
max_value: int
|
68
69
|
|
69
70
|
|
71
|
+
@dataslots
|
70
72
|
@dataclass
|
71
|
-
class ParameterDescription
|
73
|
+
class ParameterDescription:
|
72
74
|
"""Represents a parameter description."""
|
73
75
|
|
74
76
|
name: str
|
75
|
-
unit_of_measurement: UnitOfMeasurement | Literal["%"] | None = None
|
76
|
-
|
77
|
-
|
78
|
-
@dataclass
|
79
|
-
class BinaryParameterDescription(ParameterDescription, ABC):
|
80
|
-
"""Represent a binary parameter description."""
|
81
77
|
|
82
78
|
|
83
79
|
class Parameter(ABC):
|
84
|
-
"""Represents a parameter."""
|
80
|
+
"""Represents a base parameter."""
|
85
81
|
|
86
82
|
__slots__ = ("device", "description", "_pending_update", "_index", "_values")
|
87
83
|
|
@@ -117,8 +113,18 @@ class Parameter(ABC):
|
|
117
113
|
|
118
114
|
def _call_relational_method(self, method_to_call: str, other: Any) -> Any:
|
119
115
|
"""Call a specified relational method."""
|
120
|
-
|
121
|
-
|
116
|
+
if isinstance(other, Parameter):
|
117
|
+
other = other.values
|
118
|
+
|
119
|
+
if isinstance(other, ParameterValues):
|
120
|
+
handler = getattr(self.values, method_to_call)
|
121
|
+
return handler(other)
|
122
|
+
|
123
|
+
if isinstance(other, (int, float, bool)) or other in (STATE_OFF, STATE_ON):
|
124
|
+
handler = getattr(self.values.value, method_to_call)
|
125
|
+
return handler(_normalize_parameter_value(other))
|
126
|
+
else:
|
127
|
+
return NotImplemented
|
122
128
|
|
123
129
|
def __int__(self) -> int:
|
124
130
|
"""Return an integer representation of parameter's value."""
|
@@ -164,11 +170,7 @@ class Parameter(ABC):
|
|
164
170
|
"""Compare if parameter value is less that other."""
|
165
171
|
return self._call_relational_method("__lt__", other)
|
166
172
|
|
167
|
-
async def
|
168
|
-
"""Create a request to change the parameter."""
|
169
|
-
raise NotImplementedError
|
170
|
-
|
171
|
-
async def set(self, value: ParameterValueType, retries: int = SET_RETRIES) -> bool:
|
173
|
+
async def set(self, value: Any, retries: int = SET_RETRIES) -> bool:
|
172
174
|
"""Set a parameter value."""
|
173
175
|
if (value := _normalize_parameter_value(value)) == self.values.value:
|
174
176
|
return True
|
@@ -194,10 +196,6 @@ class Parameter(ABC):
|
|
194
196
|
|
195
197
|
return True
|
196
198
|
|
197
|
-
def set_nowait(self, value: ParameterValueType, retries: int = SET_RETRIES) -> None:
|
198
|
-
"""Set a parameter value without waiting."""
|
199
|
-
self.device.create_task(self.set(value, retries))
|
200
|
-
|
201
199
|
def update(self, values: ParameterValues) -> None:
|
202
200
|
"""Update the parameter values."""
|
203
201
|
self._values = values
|
@@ -213,26 +211,6 @@ class Parameter(ABC):
|
|
213
211
|
"""Return the parameter values."""
|
214
212
|
return self._values
|
215
213
|
|
216
|
-
@property
|
217
|
-
def value(self) -> ParameterValueType:
|
218
|
-
"""Return the parameter value."""
|
219
|
-
return self.values.value
|
220
|
-
|
221
|
-
@property
|
222
|
-
def min_value(self) -> ParameterValueType:
|
223
|
-
"""Return the minimum allowed value."""
|
224
|
-
return self.values.min_value
|
225
|
-
|
226
|
-
@property
|
227
|
-
def max_value(self) -> ParameterValueType:
|
228
|
-
"""Return the maximum allowed value."""
|
229
|
-
return self.values.max_value
|
230
|
-
|
231
|
-
@property
|
232
|
-
def unit_of_measurement(self) -> UnitOfMeasurement | Literal["%"] | None:
|
233
|
-
"""Return the unit of measurement."""
|
234
|
-
return self.description.unit_of_measurement
|
235
|
-
|
236
214
|
@classmethod
|
237
215
|
def create_or_update(
|
238
216
|
cls: type[ParameterT],
|
@@ -252,15 +230,101 @@ class Parameter(ABC):
|
|
252
230
|
|
253
231
|
return parameter
|
254
232
|
|
233
|
+
@property
|
234
|
+
@abstractmethod
|
235
|
+
def value(self) -> Any:
|
236
|
+
"""Return the value."""
|
237
|
+
|
238
|
+
@property
|
239
|
+
@abstractmethod
|
240
|
+
def min_value(self) -> Any:
|
241
|
+
"""Return the minimum allowed value."""
|
242
|
+
|
243
|
+
@property
|
244
|
+
@abstractmethod
|
245
|
+
def max_value(self) -> Any:
|
246
|
+
"""Return the maximum allowed value."""
|
247
|
+
|
248
|
+
@abstractmethod
|
249
|
+
async def create_request(self) -> Request:
|
250
|
+
"""Create a request to change the parameter."""
|
251
|
+
|
255
252
|
|
256
|
-
|
253
|
+
@dataslots
|
254
|
+
@dataclass
|
255
|
+
class NumberDescription(ParameterDescription):
|
256
|
+
"""Represents a parameter description."""
|
257
|
+
|
258
|
+
unit_of_measurement: UnitOfMeasurement | Literal["%"] | None = None
|
259
|
+
|
260
|
+
|
261
|
+
class Number(Parameter):
|
262
|
+
"""Represents a number."""
|
263
|
+
|
264
|
+
__slots__ = ()
|
265
|
+
|
266
|
+
description: NumberDescription
|
267
|
+
|
268
|
+
async def set(self, value: int | float, retries: int = SET_RETRIES) -> bool:
|
269
|
+
"""Set a parameter value."""
|
270
|
+
return await super().set(value, retries)
|
271
|
+
|
272
|
+
def set_nowait(self, value: int | float, retries: int = SET_RETRIES) -> None:
|
273
|
+
"""Set a parameter value without waiting."""
|
274
|
+
self.device.create_task(self.set(value, retries))
|
275
|
+
|
276
|
+
async def create_request(self) -> Request:
|
277
|
+
"""Create a request to change the number."""
|
278
|
+
return Request()
|
279
|
+
|
280
|
+
@property
|
281
|
+
def value(self) -> int | float:
|
282
|
+
"""Return the value."""
|
283
|
+
return self.values.value
|
284
|
+
|
285
|
+
@property
|
286
|
+
def min_value(self) -> int | float:
|
287
|
+
"""Return the minimum allowed value."""
|
288
|
+
return self.values.min_value
|
257
289
|
|
290
|
+
@property
|
291
|
+
def max_value(self) -> int | float:
|
292
|
+
"""Return the maximum allowed value."""
|
293
|
+
return self.values.max_value
|
258
294
|
|
259
|
-
|
260
|
-
|
295
|
+
@property
|
296
|
+
def unit_of_measurement(self) -> UnitOfMeasurement | Literal["%"] | None:
|
297
|
+
"""Return the unit of measurement."""
|
298
|
+
return self.description.unit_of_measurement
|
299
|
+
|
300
|
+
|
301
|
+
@dataslots
|
302
|
+
@dataclass
|
303
|
+
class SwitchDescription(ParameterDescription):
|
304
|
+
"""Represents a switch description."""
|
305
|
+
|
306
|
+
|
307
|
+
class Switch(Parameter):
|
308
|
+
"""Represents a switch."""
|
309
|
+
|
310
|
+
__slots__ = ()
|
311
|
+
|
312
|
+
description: SwitchDescription
|
313
|
+
|
314
|
+
async def set(
|
315
|
+
self, value: bool | Literal["off", "on"], retries: int = SET_RETRIES
|
316
|
+
) -> bool:
|
317
|
+
"""Set a parameter value."""
|
318
|
+
return await super().set(value, retries)
|
319
|
+
|
320
|
+
def set_nowait(
|
321
|
+
self, value: bool | Literal["off", "on"], retries: int = SET_RETRIES
|
322
|
+
) -> None:
|
323
|
+
"""Set a switch value without waiting."""
|
324
|
+
self.device.create_task(self.set(value, retries))
|
261
325
|
|
262
326
|
async def turn_on(self) -> bool:
|
263
|
-
"""Set a
|
327
|
+
"""Set a switch value to 'on'.
|
264
328
|
|
265
329
|
:return: `True` if parameter was successfully turned on, `False`
|
266
330
|
otherwise.
|
@@ -269,7 +333,7 @@ class BinaryParameter(Parameter):
|
|
269
333
|
return await self.set(STATE_ON)
|
270
334
|
|
271
335
|
async def turn_off(self) -> bool:
|
272
|
-
"""Set a
|
336
|
+
"""Set a switch value to 'off'.
|
273
337
|
|
274
338
|
:return: `True` if parameter was successfully turned off, `False`
|
275
339
|
otherwise.
|
@@ -278,24 +342,28 @@ class BinaryParameter(Parameter):
|
|
278
342
|
return await self.set(STATE_OFF)
|
279
343
|
|
280
344
|
def turn_on_nowait(self) -> None:
|
281
|
-
"""Set a
|
345
|
+
"""Set a switch value to 'on' without waiting."""
|
282
346
|
self.set_nowait(STATE_ON)
|
283
347
|
|
284
348
|
def turn_off_nowait(self) -> None:
|
285
|
-
"""Set a
|
349
|
+
"""Set a switch value to 'off' without waiting."""
|
286
350
|
self.set_nowait(STATE_OFF)
|
287
351
|
|
352
|
+
async def create_request(self) -> Request:
|
353
|
+
"""Create a request to change the switch."""
|
354
|
+
return Request()
|
355
|
+
|
288
356
|
@property
|
289
|
-
def value(self) ->
|
290
|
-
"""Return the
|
357
|
+
def value(self) -> Literal["off", "on"]:
|
358
|
+
"""Return the value."""
|
291
359
|
return STATE_ON if self.values.value == 1 else STATE_OFF
|
292
360
|
|
293
361
|
@property
|
294
|
-
def min_value(self) ->
|
362
|
+
def min_value(self) -> Literal["off"]:
|
295
363
|
"""Return the minimum allowed value."""
|
296
364
|
return STATE_OFF
|
297
365
|
|
298
366
|
@property
|
299
|
-
def max_value(self) ->
|
367
|
+
def max_value(self) -> Literal["on"]:
|
300
368
|
"""Return the maximum allowed value."""
|
301
369
|
return STATE_ON
|
pyplumio/protocol.py
CHANGED
@@ -8,6 +8,8 @@ from collections.abc import Awaitable, Callable
|
|
8
8
|
from dataclasses import dataclass
|
9
9
|
import logging
|
10
10
|
|
11
|
+
from typing_extensions import TypeAlias
|
12
|
+
|
11
13
|
from pyplumio.const import ATTR_CONNECTED, DeviceType
|
12
14
|
from pyplumio.devices import AddressableDevice
|
13
15
|
from pyplumio.exceptions import ProtocolError
|
@@ -23,7 +25,7 @@ from pyplumio.structures.network_info import (
|
|
23
25
|
|
24
26
|
_LOGGER = logging.getLogger(__name__)
|
25
27
|
|
26
|
-
_Callback = Callable[[], Awaitable[None]]
|
28
|
+
_Callback: TypeAlias = Callable[[], Awaitable[None]]
|
27
29
|
|
28
30
|
|
29
31
|
class Protocol(ABC):
|
@@ -111,7 +113,7 @@ class Queues:
|
|
111
113
|
await asyncio.gather(self.read.join(), self.write.join())
|
112
114
|
|
113
115
|
|
114
|
-
class AsyncProtocol(Protocol, EventManager):
|
116
|
+
class AsyncProtocol(Protocol, EventManager[AddressableDevice]):
|
115
117
|
"""Represents an async protocol.
|
116
118
|
|
117
119
|
This protocol implements producer-consumers pattern using
|
@@ -128,7 +130,6 @@ class AsyncProtocol(Protocol, EventManager):
|
|
128
130
|
"""
|
129
131
|
|
130
132
|
consumers_count: int
|
131
|
-
data: dict[str, AddressableDevice]
|
132
133
|
_network: NetworkInfo
|
133
134
|
_queues: Queues
|
134
135
|
|