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.
@@ -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"], Literal["on"]]
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 to an integer."""
50
- if isinstance(value, str):
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(ABC):
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
- handler = getattr(self.values.value, method_to_call)
121
- return handler(_normalize_parameter_value(other))
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 create_request(self) -> Request:
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
- ParameterT = TypeVar("ParameterT", bound=Parameter)
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
- class BinaryParameter(Parameter):
260
- """Represents binary device parameter."""
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 parameter value to 'on'.
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 parameter value to 'off'.
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 parameter value to 'on' without waiting."""
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 parameter value to 'off' without waiting."""
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) -> ParameterValueType:
290
- """Return the parameter value."""
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) -> ParameterValueType:
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) -> ParameterValueType:
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