PyPlumIO 0.5.42__py3-none-any.whl → 0.5.43__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.
Files changed (60) hide show
  1. pyplumio/__init__.py +3 -2
  2. pyplumio/_version.py +2 -2
  3. pyplumio/connection.py +14 -14
  4. pyplumio/const.py +7 -0
  5. pyplumio/devices/__init__.py +32 -19
  6. pyplumio/devices/ecomax.py +112 -128
  7. pyplumio/devices/ecoster.py +5 -0
  8. pyplumio/devices/mixer.py +21 -31
  9. pyplumio/devices/thermostat.py +19 -29
  10. pyplumio/filters.py +166 -147
  11. pyplumio/frames/__init__.py +20 -8
  12. pyplumio/frames/messages.py +3 -0
  13. pyplumio/frames/requests.py +21 -0
  14. pyplumio/frames/responses.py +18 -0
  15. pyplumio/helpers/data_types.py +23 -21
  16. pyplumio/helpers/event_manager.py +40 -3
  17. pyplumio/helpers/factory.py +5 -2
  18. pyplumio/helpers/schedule.py +8 -5
  19. pyplumio/helpers/task_manager.py +3 -0
  20. pyplumio/helpers/timeout.py +8 -8
  21. pyplumio/helpers/uid.py +8 -5
  22. pyplumio/{helpers/parameter.py → parameters/__init__.py} +98 -4
  23. pyplumio/parameters/ecomax.py +868 -0
  24. pyplumio/parameters/mixer.py +245 -0
  25. pyplumio/parameters/thermostat.py +197 -0
  26. pyplumio/protocol.py +6 -3
  27. pyplumio/stream.py +3 -0
  28. pyplumio/structures/__init__.py +3 -0
  29. pyplumio/structures/alerts.py +8 -5
  30. pyplumio/structures/boiler_load.py +3 -0
  31. pyplumio/structures/boiler_power.py +3 -0
  32. pyplumio/structures/ecomax_parameters.py +6 -800
  33. pyplumio/structures/fan_power.py +3 -0
  34. pyplumio/structures/frame_versions.py +3 -0
  35. pyplumio/structures/fuel_consumption.py +3 -0
  36. pyplumio/structures/fuel_level.py +3 -0
  37. pyplumio/structures/lambda_sensor.py +8 -0
  38. pyplumio/structures/mixer_parameters.py +8 -230
  39. pyplumio/structures/mixer_sensors.py +9 -0
  40. pyplumio/structures/modules.py +14 -0
  41. pyplumio/structures/network_info.py +11 -0
  42. pyplumio/structures/output_flags.py +9 -0
  43. pyplumio/structures/outputs.py +21 -0
  44. pyplumio/structures/pending_alerts.py +3 -0
  45. pyplumio/structures/product_info.py +5 -2
  46. pyplumio/structures/program_version.py +3 -0
  47. pyplumio/structures/regulator_data.py +4 -1
  48. pyplumio/structures/regulator_data_schema.py +3 -0
  49. pyplumio/structures/schedules.py +18 -1
  50. pyplumio/structures/statuses.py +9 -0
  51. pyplumio/structures/temperatures.py +22 -0
  52. pyplumio/structures/thermostat_parameters.py +13 -177
  53. pyplumio/structures/thermostat_sensors.py +9 -0
  54. pyplumio/utils.py +14 -12
  55. {pyplumio-0.5.42.dist-info → pyplumio-0.5.43.dist-info}/METADATA +30 -17
  56. pyplumio-0.5.43.dist-info/RECORD +63 -0
  57. {pyplumio-0.5.42.dist-info → pyplumio-0.5.43.dist-info}/WHEEL +1 -1
  58. pyplumio-0.5.42.dist-info/RECORD +0 -60
  59. {pyplumio-0.5.42.dist-info → pyplumio-0.5.43.dist-info}/licenses/LICENSE +0 -0
  60. {pyplumio-0.5.42.dist-info → pyplumio-0.5.43.dist-info}/top_level.txt +0 -0
@@ -4,58 +4,45 @@ from __future__ import annotations
4
4
 
5
5
  import asyncio
6
6
  from collections.abc import Coroutine, Generator, Sequence
7
- from typing import TYPE_CHECKING, Any
7
+ from typing import Any
8
8
 
9
- from pyplumio.devices import PhysicalDevice, VirtualDevice
10
- from pyplumio.helpers.parameter import ParameterValues
11
- from pyplumio.structures.thermostat_parameters import (
12
- ATTR_THERMOSTAT_PARAMETERS,
13
- THERMOSTAT_PARAMETERS,
9
+ from pyplumio.devices import VirtualDevice
10
+ from pyplumio.helpers.event_manager import event_listener
11
+ from pyplumio.parameters import ParameterValues
12
+ from pyplumio.parameters.thermostat import (
14
13
  ThermostatNumber,
15
14
  ThermostatSwitch,
16
15
  ThermostatSwitchDescription,
16
+ get_thermostat_parameter_types,
17
17
  )
18
+ from pyplumio.structures.thermostat_parameters import ATTR_THERMOSTAT_PARAMETERS
18
19
  from pyplumio.structures.thermostat_sensors import ATTR_THERMOSTAT_SENSORS
19
20
 
20
- if TYPE_CHECKING:
21
- from pyplumio.frames import Frame
22
-
23
21
 
24
22
  class Thermostat(VirtualDevice):
25
23
  """Represents a thermostat."""
26
24
 
27
- def __init__(
28
- self, queue: asyncio.Queue[Frame], parent: PhysicalDevice, index: int = 0
29
- ) -> None:
30
- """Initialize a new thermostat."""
31
- super().__init__(queue, parent, index)
32
- self.subscribe(ATTR_THERMOSTAT_SENSORS, self._handle_thermostat_sensors)
33
- self.subscribe(ATTR_THERMOSTAT_PARAMETERS, self._handle_thermostat_parameters)
34
-
35
- async def _handle_thermostat_sensors(self, sensors: dict[str, Any]) -> bool:
36
- """Handle thermostat sensors.
25
+ __slots__ = ()
37
26
 
38
- For each sensor dispatch an event with the
39
- sensor's name and value.
40
- """
27
+ @event_listener(ATTR_THERMOSTAT_SENSORS)
28
+ async def on_event_thermostat_sensors(self, sensors: dict[str, Any]) -> bool:
29
+ """Update thermostat sensors and dispatch the events."""
41
30
  await asyncio.gather(
42
31
  *(self.dispatch(name, value) for name, value in sensors.items())
43
32
  )
44
33
  return True
45
34
 
46
- async def _handle_thermostat_parameters(
35
+ @event_listener(ATTR_THERMOSTAT_PARAMETERS)
36
+ async def on_event_thermostat_parameters(
47
37
  self, parameters: Sequence[tuple[int, ParameterValues]]
48
38
  ) -> bool:
49
- """Handle thermostat parameters.
50
-
51
- For each parameter dispatch an event with the
52
- parameter's name and value.
53
- """
39
+ """Update thermostat parameters and dispatch the events."""
54
40
 
55
41
  def _thermostat_parameter_events() -> Generator[Coroutine, Any, None]:
56
42
  """Get dispatch calls for thermostat parameter events."""
43
+ parameter_types = get_thermostat_parameter_types()
57
44
  for index, values in parameters:
58
- description = THERMOSTAT_PARAMETERS[index]
45
+ description = parameter_types[index]
59
46
  handler = (
60
47
  ThermostatSwitch
61
48
  if isinstance(description, ThermostatSwitchDescription)
@@ -74,3 +61,6 @@ class Thermostat(VirtualDevice):
74
61
 
75
62
  await asyncio.gather(*_thermostat_parameter_events())
76
63
  return True
64
+
65
+
66
+ __all__ = ["Thermostat"]
pyplumio/filters.py CHANGED
@@ -17,8 +17,10 @@ from typing import (
17
17
  runtime_checkable,
18
18
  )
19
19
 
20
+ from typing_extensions import TypeAlias
21
+
20
22
  from pyplumio.helpers.event_manager import Callback
21
- from pyplumio.helpers.parameter import Parameter
23
+ from pyplumio.parameters import Parameter
22
24
 
23
25
  UNDEFINED: Final = "undefined"
24
26
  TOLERANCE: Final = 0.1
@@ -50,20 +52,18 @@ Comparable = TypeVar("Comparable", Parameter, SupportsFloat, SupportsComparison)
50
52
 
51
53
 
52
54
  @overload
53
- def _significantly_changed(old: Parameter, new: Parameter) -> bool: ...
55
+ def is_close(old: Parameter, new: Parameter) -> bool: ...
54
56
 
55
57
 
56
58
  @overload
57
- def _significantly_changed(old: SupportsFloat, new: SupportsFloat) -> bool: ...
59
+ def is_close(old: SupportsFloat, new: SupportsFloat) -> bool: ...
58
60
 
59
61
 
60
62
  @overload
61
- def _significantly_changed(
62
- old: SupportsComparison, new: SupportsComparison
63
- ) -> bool: ...
63
+ def is_close(old: SupportsComparison, new: SupportsComparison) -> bool: ...
64
64
 
65
65
 
66
- def _significantly_changed(old: Comparable, new: Comparable) -> bool:
66
+ def is_close(old: Comparable, new: Comparable) -> bool:
67
67
  """Check if value is significantly changed."""
68
68
  if isinstance(old, Parameter) and isinstance(new, Parameter):
69
69
  return new.pending_update or old.values.__ne__(new.values)
@@ -75,16 +75,16 @@ def _significantly_changed(old: Comparable, new: Comparable) -> bool:
75
75
 
76
76
 
77
77
  @overload
78
- def _diffence_between(old: list, new: list) -> list: ...
78
+ def diffence_between(old: list, new: list) -> list: ...
79
79
 
80
80
 
81
81
  @overload
82
- def _diffence_between(
82
+ def diffence_between(
83
83
  old: SupportsSubtraction, new: SupportsSubtraction
84
84
  ) -> SupportsSubtraction: ...
85
85
 
86
86
 
87
- def _diffence_between(
87
+ def diffence_between(
88
88
  old: SupportsSubtraction | list, new: SupportsSubtraction | list
89
89
  ) -> SupportsSubtraction | list | None:
90
90
  """Return a difference between values."""
@@ -125,6 +125,61 @@ class Filter(ABC):
125
125
  """Set a new value for the callback."""
126
126
 
127
127
 
128
+ class _Aggregate(Filter):
129
+ """Represents an aggregate filter.
130
+
131
+ Calls a callback with a sum of values collected over a specified
132
+ time period.
133
+ """
134
+
135
+ __slots__ = ("_sum", "_last_update", "_timeout")
136
+
137
+ _sum: complex
138
+ _last_update: float
139
+ _timeout: float
140
+
141
+ def __init__(self, callback: Callback, seconds: float) -> None:
142
+ """Initialize a new aggregate filter."""
143
+ super().__init__(callback)
144
+ self._last_update = time.monotonic()
145
+ self._timeout = seconds
146
+ self._sum = 0.0
147
+
148
+ async def __call__(self, new_value: Any) -> Any:
149
+ """Set a new value for the callback."""
150
+ current_timestamp = time.monotonic()
151
+ try:
152
+ self._sum += new_value
153
+ except TypeError as e:
154
+ raise ValueError(
155
+ "Aggregate filter can only be used with numeric values"
156
+ ) from e
157
+
158
+ if current_timestamp - self._last_update >= self._timeout:
159
+ result = await self._callback(self._sum)
160
+ self._last_update = current_timestamp
161
+ self._sum = 0.0
162
+ return result
163
+
164
+
165
+ def aggregate(callback: Callback, seconds: float) -> _Aggregate:
166
+ """Create a new aggregate filter.
167
+
168
+ A callback function will be called with a sum of values collected
169
+ over a specified time period. Can only be used with numeric values.
170
+
171
+ :param callback: A callback function to be awaited once filter
172
+ conditions are fulfilled
173
+ :type callback: Callback
174
+ :param seconds: A callback will be awaited with a sum of values
175
+ aggregated over this amount of seconds.
176
+ :type seconds: float
177
+ :return: An instance of callable filter
178
+ :rtype: _Aggregate
179
+ """
180
+ return _Aggregate(callback, seconds)
181
+
182
+
128
183
  class _Clamp(Filter):
129
184
  """Represents a clamp filter.
130
185
 
@@ -137,7 +192,7 @@ class _Clamp(Filter):
137
192
  _max_value: float
138
193
 
139
194
  def __init__(self, callback: Callback, min_value: float, max_value: float) -> None:
140
- """Initialize a new clamp filter."""
195
+ """Initialize a new Clamp filter."""
141
196
  super().__init__(callback)
142
197
  self._min_value = min_value
143
198
  self._max_value = max_value
@@ -154,10 +209,10 @@ class _Clamp(Filter):
154
209
 
155
210
 
156
211
  def clamp(callback: Callback, min_value: float, max_value: float) -> _Clamp:
157
- """Return a clamp filter.
212
+ """Create a new clamp filter.
158
213
 
159
- A callback function will be called with value clamped between
160
- specified boundaries.
214
+ A callback function will be called and passed value clamped
215
+ between specified boundaries.
161
216
 
162
217
  :param callback: A callback function to be awaited on new value
163
218
  :type callback: Callback
@@ -171,36 +226,49 @@ def clamp(callback: Callback, min_value: float, max_value: float) -> _Clamp:
171
226
  return _Clamp(callback, min_value, max_value)
172
227
 
173
228
 
174
- class _OnChange(Filter):
175
- """Represents a value changed filter.
229
+ _FilterT: TypeAlias = Callable[[Any], bool]
176
230
 
177
- Calls a callback only when value is changed from the
178
- previous callback call.
231
+
232
+ class _Custom(Filter):
233
+ """Represents a custom filter.
234
+
235
+ Calls a callback with value, if user-defined filter function
236
+ that's called by this class with the value as an argument
237
+ returns true.
179
238
  """
180
239
 
181
- __slots__ = ()
240
+ __slots__ = ("_filter_fn",)
241
+
242
+ _filter_fn: _FilterT
243
+
244
+ def __init__(self, callback: Callback, filter_fn: _FilterT) -> None:
245
+ """Initialize a new custom filter."""
246
+ super().__init__(callback)
247
+ self._filter_fn = filter_fn
182
248
 
183
249
  async def __call__(self, new_value: Any) -> Any:
184
250
  """Set a new value for the callback."""
185
- if self._value == UNDEFINED or _significantly_changed(self._value, new_value):
186
- self._value = (
187
- copy(new_value) if isinstance(new_value, Parameter) else new_value
188
- )
189
- return await self._callback(new_value)
251
+ if self._filter_fn(new_value):
252
+ await self._callback(new_value)
190
253
 
191
254
 
192
- def on_change(callback: Callback) -> _OnChange:
193
- """Return a value changed filter.
255
+ def custom(callback: Callback, filter_fn: _FilterT) -> _Custom:
256
+ """Create a new custom filter.
194
257
 
195
- A callback function will only be called if value is changed from the
196
- previous call.
258
+ A callback function will be called when a user-defined filter
259
+ function, that's being called with the value as an argument,
260
+ returns true.
197
261
 
198
- :param callback: A callback function to be awaited on value change
262
+ :param callback: A callback function to be awaited when
263
+ filter function return true
199
264
  :type callback: Callback
265
+ :param filter_fn: Filter function, that will be called with a
266
+ value and should return `True` to await filter's callback
267
+ :type filter_fn: Callable[[Any], bool]
200
268
  :return: An instance of callable filter
201
- :rtype: _OnChange
269
+ :rtype: _Custom
202
270
  """
203
- return _OnChange(callback)
271
+ return _Custom(callback, filter_fn)
204
272
 
205
273
 
206
274
  class _Debounce(Filter):
@@ -223,7 +291,7 @@ class _Debounce(Filter):
223
291
 
224
292
  async def __call__(self, new_value: Any) -> Any:
225
293
  """Set a new value for the callback."""
226
- if self._value == UNDEFINED or _significantly_changed(self._value, new_value):
294
+ if self._value == UNDEFINED or is_close(self._value, new_value):
227
295
  self._calls += 1
228
296
  else:
229
297
  self._calls = 0
@@ -237,9 +305,9 @@ class _Debounce(Filter):
237
305
 
238
306
 
239
307
  def debounce(callback: Callback, min_calls: int) -> _Debounce:
240
- """Return a debounce filter.
308
+ """Create a new debounce filter.
241
309
 
242
- A callback function will only called once value is stabilized
310
+ A callback function will only be called once the value is stabilized
243
311
  across multiple filter calls.
244
312
 
245
313
  :param callback: A callback function to be awaited on value change
@@ -253,53 +321,6 @@ def debounce(callback: Callback, min_calls: int) -> _Debounce:
253
321
  return _Debounce(callback, min_calls)
254
322
 
255
323
 
256
- class _Throttle(Filter):
257
- """Represents a throttle filter.
258
-
259
- Calls a callback only when certain amount of seconds passed
260
- since the last call.
261
- """
262
-
263
- __slots__ = ("_last_called", "_timeout")
264
-
265
- _last_called: float | None
266
- _timeout: float
267
-
268
- def __init__(self, callback: Callback, seconds: float) -> None:
269
- """Initialize a new throttle filter."""
270
- super().__init__(callback)
271
- self._last_called = None
272
- self._timeout = seconds
273
-
274
- async def __call__(self, new_value: Any) -> Any:
275
- """Set a new value for the callback."""
276
- current_timestamp = time.monotonic()
277
- if (
278
- self._last_called is None
279
- or (current_timestamp - self._last_called) >= self._timeout
280
- ):
281
- self._last_called = current_timestamp
282
- return await self._callback(new_value)
283
-
284
-
285
- def throttle(callback: Callback, seconds: float) -> _Throttle:
286
- """Return a throttle filter.
287
-
288
- A callback function will only be called once a certain amount of
289
- seconds passed since the last call.
290
-
291
- :param callback: A callback function that will be awaited once
292
- filter conditions are fulfilled
293
- :type callback: Callback
294
- :param seconds: A callback will be awaited at most once per
295
- this amount of seconds
296
- :type seconds: float
297
- :return: An instance of callable filter
298
- :rtype: _Throttle
299
- """
300
- return _Throttle(callback, seconds)
301
-
302
-
303
324
  class _Delta(Filter):
304
325
  """Represents a difference filter.
305
326
 
@@ -310,23 +331,23 @@ class _Delta(Filter):
310
331
 
311
332
  async def __call__(self, new_value: Any) -> Any:
312
333
  """Set a new value for the callback."""
313
- if self._value == UNDEFINED or _significantly_changed(self._value, new_value):
334
+ if self._value == UNDEFINED or is_close(self._value, new_value):
314
335
  old_value = self._value
315
336
  self._value = (
316
337
  copy(new_value) if isinstance(new_value, Parameter) else new_value
317
338
  )
318
339
  if (
319
340
  self._value != UNDEFINED
320
- and (difference := _diffence_between(old_value, new_value)) is not None
341
+ and (difference := diffence_between(old_value, new_value)) is not None
321
342
  ):
322
343
  return await self._callback(difference)
323
344
 
324
345
 
325
346
  def delta(callback: Callback) -> _Delta:
326
- """Return a difference filter.
347
+ """Create a new difference filter.
327
348
 
328
349
  A callback function will be called with a difference between two
329
- subsequent value.
350
+ subsequent values.
330
351
 
331
352
  :param callback: A callback function that will be awaited with
332
353
  difference between values in two subsequent calls
@@ -337,98 +358,96 @@ def delta(callback: Callback) -> _Delta:
337
358
  return _Delta(callback)
338
359
 
339
360
 
340
- class _Aggregate(Filter):
341
- """Represents an aggregate filter.
361
+ class _OnChange(Filter):
362
+ """Represents a value changed filter.
342
363
 
343
- Calls a callback with a sum of values collected over a specified
344
- time period.
364
+ Calls a callback only when value is changed from the
365
+ previous callback call.
345
366
  """
346
367
 
347
- __slots__ = ("_sum", "_last_update", "_timeout")
348
-
349
- _sum: complex
350
- _last_update: float
351
- _timeout: float
368
+ __slots__ = ()
352
369
 
353
- def __init__(self, callback: Callback, seconds: float) -> None:
354
- """Initialize a new aggregate filter."""
370
+ def __init__(self, callback: Callback) -> None:
371
+ """Initialize a new value changed filter."""
355
372
  super().__init__(callback)
356
- self._last_update = time.monotonic()
357
- self._timeout = seconds
358
- self._sum = 0.0
359
373
 
360
374
  async def __call__(self, new_value: Any) -> Any:
361
375
  """Set a new value for the callback."""
362
- current_timestamp = time.monotonic()
363
- try:
364
- self._sum += new_value
365
- except TypeError as e:
366
- raise ValueError(
367
- "Aggregate filter can only be used with numeric values"
368
- ) from e
369
-
370
- if current_timestamp - self._last_update >= self._timeout:
371
- result = await self._callback(self._sum)
372
- self._last_update = current_timestamp
373
- self._sum = 0.0
374
- return result
376
+ if self._value == UNDEFINED or is_close(self._value, new_value):
377
+ self._value = (
378
+ copy(new_value) if isinstance(new_value, Parameter) else new_value
379
+ )
380
+ return await self._callback(new_value)
375
381
 
376
382
 
377
- def aggregate(callback: Callback, seconds: float) -> _Aggregate:
378
- """Return an aggregate filter.
383
+ def on_change(callback: Callback) -> _OnChange:
384
+ """Create a new value changed filter.
379
385
 
380
- A callback function will be called with a sum of values collected
381
- over a specified time period. Can only be used with numeric values.
386
+ A callback function will only be called if the value is changed from the
387
+ previous call.
382
388
 
383
- :param callback: A callback function to be awaited once filter
384
- conditions are fulfilled
389
+ :param callback: A callback function to be awaited on value change
385
390
  :type callback: Callback
386
- :param seconds: A callback will be awaited with a sum of values
387
- aggregated over this amount of seconds.
388
- :type seconds: float
389
391
  :return: An instance of callable filter
390
- :rtype: _Aggregate
392
+ :rtype: _OnChange
391
393
  """
392
- return _Aggregate(callback, seconds)
394
+ return _OnChange(callback)
393
395
 
394
396
 
395
- class _Custom(Filter):
396
- """Represents a custom filter.
397
+ class _Throttle(Filter):
398
+ """Represents a throttle filter.
397
399
 
398
- Calls a callback with value, if user-defined filter function
399
- that's called by this class with the value as an argument
400
- returns true.
400
+ Calls a callback only when certain amount of seconds passed
401
+ since the last call.
401
402
  """
402
403
 
403
- __slots__ = ("_filter_fn",)
404
+ __slots__ = ("_last_called", "_timeout")
404
405
 
405
- filter_fn: Callable[[Any], bool]
406
+ _last_called: float | None
407
+ _timeout: float
406
408
 
407
- def __init__(self, callback: Callback, filter_fn: Callable[[Any], bool]) -> None:
408
- """Initialize a new custom filter."""
409
+ def __init__(self, callback: Callback, seconds: float) -> None:
410
+ """Initialize a new throttle filter."""
409
411
  super().__init__(callback)
410
- self._filter_fn = filter_fn
412
+ self._last_called = None
413
+ self._timeout = seconds
411
414
 
412
415
  async def __call__(self, new_value: Any) -> Any:
413
416
  """Set a new value for the callback."""
414
- if self._filter_fn(new_value):
415
- await self._callback(new_value)
417
+ current_timestamp = time.monotonic()
418
+ if (
419
+ self._last_called is None
420
+ or (current_timestamp - self._last_called) >= self._timeout
421
+ ):
422
+ self._last_called = current_timestamp
423
+ return await self._callback(new_value)
416
424
 
417
425
 
418
- def custom(callback: Callback, filter_fn: Callable[[Any], bool]) -> _Custom:
419
- """Return a custom filter.
426
+ def throttle(callback: Callback, seconds: float) -> _Throttle:
427
+ """Create a new throttle filter.
420
428
 
421
- A callback function will be called when user-defined filter
422
- function, that's being called with the value as an argument,
423
- returns true.
429
+ A callback function will only be called once a certain amount of
430
+ seconds passed since the last call.
424
431
 
425
- :param callback: A callback function to be awaited when
426
- filter function return true
432
+ :param callback: A callback function that will be awaited once
433
+ filter conditions are fulfilled
427
434
  :type callback: Callback
428
- :param filter_fn: Filter function, that will be called with a
429
- value and should return `True` to await filter's callback
430
- :type filter_fn: Callable[[Any], bool]
435
+ :param seconds: A callback will be awaited at most once per
436
+ this amount of seconds
437
+ :type seconds: float
431
438
  :return: An instance of callable filter
432
- :rtype: _Custom
439
+ :rtype: _Throttle
433
440
  """
434
- return _Custom(callback, filter_fn)
441
+ return _Throttle(callback, seconds)
442
+
443
+
444
+ __all__ = [
445
+ "Filter",
446
+ "aggregate",
447
+ "clamp",
448
+ "custom",
449
+ "debounce",
450
+ "delta",
451
+ "on_change",
452
+ "throttle",
453
+ ]
@@ -30,9 +30,9 @@ if TYPE_CHECKING:
30
30
  from pyplumio.devices import PhysicalDevice
31
31
 
32
32
 
33
- def bcc(data: bytes) -> int:
33
+ def bcc(buffer: bytes) -> int:
34
34
  """Return a block check character."""
35
- return reduce(lambda x, y: x ^ y, data)
35
+ return reduce(lambda x, y: x ^ y, buffer)
36
36
 
37
37
 
38
38
  @cache
@@ -121,12 +121,12 @@ class Frame(ABC):
121
121
  self._message,
122
122
  self._data,
123
123
  ) == (
124
- self.recipient,
125
- self.sender,
126
- self.econet_type,
127
- self.econet_version,
128
- self._message,
129
- self._data,
124
+ other.recipient,
125
+ other.sender,
126
+ other.econet_type,
127
+ other.econet_version,
128
+ other._message,
129
+ other._data,
130
130
  )
131
131
 
132
132
  return NotImplemented
@@ -280,3 +280,15 @@ class Message(Response):
280
280
  """Represents a message."""
281
281
 
282
282
  __slots__ = ()
283
+
284
+
285
+ __all__ = [
286
+ "Frame",
287
+ "Request",
288
+ "Response",
289
+ "Message",
290
+ "DataFrameDescription",
291
+ "bcc",
292
+ "is_known_frame_type",
293
+ "get_frame_handler",
294
+ ]
@@ -79,3 +79,6 @@ class SensorDataMessage(Message):
79
79
  sensors[ATTR_STATE] = DeviceState(sensors[ATTR_STATE])
80
80
 
81
81
  return {ATTR_SENSORS: sensors}
82
+
83
+
84
+ __all__ = ["RegulatorDataMessage", "SensorDataMessage"]
@@ -260,3 +260,24 @@ class UIDRequest(Request):
260
260
  __slots__ = ()
261
261
 
262
262
  frame_type = FrameType.REQUEST_UID
263
+
264
+
265
+ __all__ = [
266
+ "AlertsRequest",
267
+ "CheckDeviceRequest",
268
+ "EcomaxControlRequest",
269
+ "EcomaxParametersRequest",
270
+ "MixerParametersRequest",
271
+ "PasswordRequest",
272
+ "ProgramVersionRequest",
273
+ "RegulatorDataSchemaRequest",
274
+ "SchedulesRequest",
275
+ "SetEcomaxParameterRequest",
276
+ "SetMixerParameterRequest",
277
+ "SetScheduleRequest",
278
+ "SetThermostatParameterRequest",
279
+ "StartMasterRequest",
280
+ "StopMasterRequest",
281
+ "ThermostatParametersRequest",
282
+ "UIDRequest",
283
+ ]
@@ -221,3 +221,21 @@ class UIDResponse(Response):
221
221
  def decode_message(self, message: bytearray) -> dict[str, Any]:
222
222
  """Decode a frame message."""
223
223
  return ProductInfoStructure(self).decode(message)[0]
224
+
225
+
226
+ __all__ = [
227
+ "AlertsResponse",
228
+ "DeviceAvailableResponse",
229
+ "EcomaxControlResponse",
230
+ "EcomaxParametersResponse",
231
+ "MixerParametersResponse",
232
+ "PasswordResponse",
233
+ "ProgramVersionResponse",
234
+ "RegulatorDataSchemaResponse",
235
+ "SchedulesResponse",
236
+ "SetEcomaxParameterResponse",
237
+ "SetMixerParameterResponse",
238
+ "SetThermostatParameterResponse",
239
+ "ThermostatParametersResponse",
240
+ "UIDResponse",
241
+ ]