PyPlumIO 0.5.37__py3-none-any.whl → 0.5.39__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/_version.py CHANGED
@@ -1,8 +1,13 @@
1
- # file generated by setuptools_scm
1
+ # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
3
6
  TYPE_CHECKING = False
4
7
  if TYPE_CHECKING:
5
- from typing import Tuple, Union
8
+ from typing import Tuple
9
+ from typing import Union
10
+
6
11
  VERSION_TUPLE = Tuple[Union[int, str], ...]
7
12
  else:
8
13
  VERSION_TUPLE = object
@@ -12,5 +17,5 @@ __version__: str
12
17
  __version_tuple__: VERSION_TUPLE
13
18
  version_tuple: VERSION_TUPLE
14
19
 
15
- __version__ = version = '0.5.37'
16
- __version_tuple__ = version_tuple = (0, 5, 37)
20
+ __version__ = version = '0.5.39'
21
+ __version_tuple__ = version_tuple = (0, 5, 39)
pyplumio/const.py CHANGED
@@ -3,11 +3,9 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from enum import Enum, IntEnum, unique
6
- from typing import Any, Final
6
+ from typing import Any, Final, Literal
7
7
 
8
- # Binary states.
9
- STATE_ON: Final = "on"
10
- STATE_OFF: Final = "off"
8
+ from typing_extensions import TypeAlias
11
9
 
12
10
  # General attributes.
13
11
  ATTR_CONNECTED: Final = "connected"
@@ -221,3 +219,9 @@ class UnitOfMeasurement(Enum):
221
219
 
222
220
 
223
221
  PERCENTAGE: Final = "%"
222
+
223
+ STATE_ON: Final = "on"
224
+ STATE_OFF: Final = "off"
225
+
226
+
227
+ State: TypeAlias = Literal["on", "off"]
@@ -21,7 +21,7 @@ from pyplumio.devices.mixer import Mixer
21
21
  from pyplumio.devices.thermostat import Thermostat
22
22
  from pyplumio.filters import on_change
23
23
  from pyplumio.frames import DataFrameDescription, Frame, Request
24
- from pyplumio.helpers.parameter import ParameterValues
24
+ from pyplumio.helpers.parameter import STATE_OFF, STATE_ON, ParameterValues, State
25
25
  from pyplumio.helpers.schedule import Schedule, ScheduleDay
26
26
  from pyplumio.structures.alerts import ATTR_TOTAL_ALERTS
27
27
  from pyplumio.structures.ecomax_parameters import (
@@ -99,8 +99,6 @@ SETUP_FRAME_TYPES: tuple[DataFrameDescription, ...] = (
99
99
 
100
100
  _LOGGER = logging.getLogger(__name__)
101
101
 
102
- ecomax_control_error = "ecoMAX control is not available. Please try again later."
103
-
104
102
 
105
103
  class EcoMAX(PhysicalDevice):
106
104
  """Represents an ecoMAX controller."""
@@ -282,13 +280,13 @@ class EcoMAX(PhysicalDevice):
282
280
  SCHEDULES[index]: Schedule(
283
281
  name=SCHEDULES[index],
284
282
  device=self,
285
- monday=ScheduleDay(schedule[1]),
286
- tuesday=ScheduleDay(schedule[2]),
287
- wednesday=ScheduleDay(schedule[3]),
288
- thursday=ScheduleDay(schedule[4]),
289
- friday=ScheduleDay(schedule[5]),
290
- saturday=ScheduleDay(schedule[6]),
291
- sunday=ScheduleDay(schedule[0]),
283
+ monday=ScheduleDay.from_iterable(schedule[1]),
284
+ tuesday=ScheduleDay.from_iterable(schedule[2]),
285
+ wednesday=ScheduleDay.from_iterable(schedule[3]),
286
+ thursday=ScheduleDay.from_iterable(schedule[4]),
287
+ friday=ScheduleDay.from_iterable(schedule[5]),
288
+ saturday=ScheduleDay.from_iterable(schedule[6]),
289
+ sunday=ScheduleDay.from_iterable(schedule[0]),
292
290
  )
293
291
  for index, schedule in schedules
294
292
  }
@@ -398,23 +396,23 @@ class EcoMAX(PhysicalDevice):
398
396
 
399
397
  return False
400
398
 
401
- async def turn_on(self) -> bool:
402
- """Turn on the ecoMAX controller."""
399
+ async def _set_ecomax_state(self, state: State) -> bool:
400
+ """Try to set the ecoMAX control state."""
403
401
  try:
404
- ecomax_control: EcomaxSwitch = self.data[ATTR_ECOMAX_CONTROL]
405
- return await ecomax_control.turn_on()
402
+ switch: EcomaxSwitch = self.data[ATTR_ECOMAX_CONTROL]
403
+ return await switch.set(state)
406
404
  except KeyError:
407
- _LOGGER.error(ecomax_control_error)
408
- return False
405
+ _LOGGER.error("ecoMAX control is not available. Please try again later.")
406
+
407
+ return False
408
+
409
+ async def turn_on(self) -> bool:
410
+ """Turn on the ecoMAX controller."""
411
+ return await self._set_ecomax_state(STATE_ON)
409
412
 
410
413
  async def turn_off(self) -> bool:
411
414
  """Turn off the ecoMAX controller."""
412
- try:
413
- ecomax_control: EcomaxSwitch = self.data[ATTR_ECOMAX_CONTROL]
414
- return await ecomax_control.turn_off()
415
- except KeyError:
416
- _LOGGER.error(ecomax_control_error)
417
- return False
415
+ return await self._set_ecomax_state(STATE_OFF)
418
416
 
419
417
  def turn_on_nowait(self) -> None:
420
418
  """Turn on the ecoMAX controller without waiting."""
@@ -19,7 +19,7 @@ async def _import_module(name: str) -> ModuleType:
19
19
  return await loop.run_in_executor(None, importlib.import_module, f"pyplumio.{name}")
20
20
 
21
21
 
22
- async def create_instance(class_path: str, cls: type[T], **kwargs: Any) -> T:
22
+ async def create_instance(class_path: str, /, cls: type[T], **kwargs: Any) -> T:
23
23
  """Return a class instance from the class path."""
24
24
  module_name, class_name = class_path.rsplit(".", 1)
25
25
  try:
@@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union, get_args
11
11
  from dataslots import dataslots
12
12
  from typing_extensions import TypeAlias
13
13
 
14
- from pyplumio.const import BYTE_UNDEFINED, STATE_OFF, STATE_ON, UnitOfMeasurement
14
+ from pyplumio.const import BYTE_UNDEFINED, STATE_OFF, STATE_ON, State, UnitOfMeasurement
15
15
  from pyplumio.frames import Request
16
16
 
17
17
  if TYPE_CHECKING:
@@ -19,8 +19,8 @@ if TYPE_CHECKING:
19
19
 
20
20
  _LOGGER = logging.getLogger(__name__)
21
21
 
22
+
22
23
  NumericType: TypeAlias = Union[int, float]
23
- State: TypeAlias = Literal["on", "off"]
24
24
  ParameterT = TypeVar("ParameterT", bound="Parameter")
25
25
 
26
26
 
@@ -108,7 +108,7 @@ class Parameter(ABC):
108
108
  """Return a serializable string representation."""
109
109
  return (
110
110
  f"{self.__class__.__name__}("
111
- f"device={self.device.__class__.__name__}, "
111
+ f"device={self.device}, "
112
112
  f"description={self.description}, "
113
113
  f"values={self.values}, "
114
114
  f"index={self._index})"
@@ -2,130 +2,128 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from collections.abc import Generator, Iterable, Iterator, MutableMapping
5
+ from collections.abc import Iterable, Iterator, MutableMapping
6
6
  from dataclasses import dataclass
7
7
  import datetime as dt
8
8
  from functools import lru_cache
9
- import math
10
- from typing import Annotated, Final, Literal, get_args
9
+ from typing import Annotated, Final, get_args
11
10
 
12
- from typing_extensions import TypeAlias
13
-
14
- from pyplumio.const import STATE_OFF, STATE_ON, FrameType
11
+ from pyplumio.const import STATE_OFF, STATE_ON, FrameType, State
15
12
  from pyplumio.devices import PhysicalDevice
16
13
  from pyplumio.frames import Request
17
14
  from pyplumio.structures.schedules import collect_schedule_data
18
15
 
19
16
  TIME_FORMAT: Final = "%H:%M"
20
17
 
21
- STATE_NIGHT: Final = "night"
22
- STATE_DAY: Final = "day"
18
+ MIDNIGHT: Final = "00:00"
19
+ MIDNIGHT_DT = dt.datetime.strptime(MIDNIGHT, TIME_FORMAT)
20
+
21
+ STEP = dt.timedelta(minutes=30)
23
22
 
24
- _ON_STATES: Final = {STATE_ON, STATE_DAY}
23
+ Time = Annotated[str, "Time string in %H:%M format"]
25
24
 
26
- ScheduleState: TypeAlias = Literal["on", "off", "day", "night"]
27
- Time = Annotated[str, "time in HH:MM format"]
28
25
 
29
- start_of_day_dt = dt.datetime.strptime("00:00", TIME_FORMAT)
26
+ def _get_time(
27
+ index: int, start: dt.datetime = MIDNIGHT_DT, step: dt.timedelta = STEP
28
+ ) -> Time:
29
+ """Return time for a specific index."""
30
+ time_dt = start + (step * index)
31
+ return time_dt.strftime(TIME_FORMAT)
30
32
 
31
33
 
32
- def _get_time_range(
33
- start: Time, end: Time, step: int = 30
34
- ) -> Generator[int, None, None]:
34
+ @lru_cache(maxsize=10)
35
+ def _get_time_range(start: Time, end: Time, step: dt.timedelta = STEP) -> list[Time]:
35
36
  """Get a time range.
36
37
 
37
- Start and end times should be specified in HH:MM format, step in
38
- minutes.
38
+ Start and end boundaries should be specified in %H:%M format.
39
+ Both are inclusive.
39
40
  """
41
+ start_dt = dt.datetime.strptime(start, TIME_FORMAT)
42
+ end_dt = dt.datetime.strptime(end, TIME_FORMAT)
40
43
 
41
- @lru_cache(maxsize=10)
42
- def _get_time_range_cached(start: Time, end: Time, step: int = 30) -> range:
43
- """Get a time range and cache it using LRU cache."""
44
- start_dt = dt.datetime.strptime(start, TIME_FORMAT)
45
- end_dt = dt.datetime.strptime(end, TIME_FORMAT)
46
- if end_dt == start_of_day_dt:
47
- # Upper boundary of the interval is midnight.
48
- end_dt += dt.timedelta(hours=24) - dt.timedelta(minutes=step)
49
-
50
- if end_dt <= start_dt:
51
- raise ValueError(
52
- f"Invalid time range: start time ({start}) must be earlier "
53
- f"than end time ({end})."
54
- )
44
+ if end_dt == MIDNIGHT_DT:
45
+ # Upper boundary of the interval is midnight.
46
+ end_dt += dt.timedelta(hours=24) - step
55
47
 
56
- def _dt_to_index(dt: dt.datetime) -> int:
57
- """Convert datetime to index in schedule list."""
58
- return math.floor((dt - start_of_day_dt).total_seconds() // (60 * step))
48
+ if end_dt <= start_dt:
49
+ raise ValueError(
50
+ f"Invalid time range: start time ({start}) must be earlier "
51
+ f"than end time ({end})."
52
+ )
59
53
 
60
- return range(_dt_to_index(start_dt), _dt_to_index(end_dt) + 1)
54
+ seconds = (end_dt - start_dt).total_seconds()
55
+ steps = seconds // step.total_seconds() + 1
61
56
 
62
- yield from _get_time_range_cached(start, end, step)
57
+ return [_get_time(index, start=start_dt, step=step) for index in range(int(steps))]
63
58
 
64
59
 
65
60
  class ScheduleDay(MutableMapping):
66
61
  """Represents a single day of schedule."""
67
62
 
68
- __slots__ = ("_intervals",)
63
+ __slots__ = ("_schedule",)
69
64
 
70
- _intervals: list[bool]
65
+ _schedule: dict[Time, bool]
71
66
 
72
- def __init__(self, intervals: list[bool]) -> None:
67
+ def __init__(self, schedule: dict[Time, bool]) -> None:
73
68
  """Initialize a new schedule day."""
74
- self._intervals = intervals
69
+ self._schedule = schedule
75
70
 
76
71
  def __repr__(self) -> str:
77
72
  """Return serializable representation of the class."""
78
- return f"ScheduleDay({self._intervals})"
73
+ return f"ScheduleDay({self._schedule})"
79
74
 
80
75
  def __len__(self) -> int:
81
76
  """Return a schedule length."""
82
- return len(self._intervals)
77
+ return self._schedule.__len__()
83
78
 
84
- def __iter__(self) -> Iterator[bool]:
79
+ def __iter__(self) -> Iterator[Time]:
85
80
  """Return an iterator."""
86
- return self._intervals.__iter__()
81
+ return self._schedule.__iter__()
87
82
 
88
- def __getitem__(self, index: int) -> bool:
83
+ def __getitem__(self, time: Time) -> State:
89
84
  """Return a schedule item."""
90
- return self._intervals.__getitem__(index)
85
+ state = self._schedule.__getitem__(time)
86
+ return STATE_ON if state else STATE_OFF
91
87
 
92
- def __delitem__(self, index: int) -> None:
88
+ def __delitem__(self, time: Time) -> None:
93
89
  """Delete a schedule item."""
94
- return self._intervals.__delitem__(index)
90
+ self._schedule.__delitem__(time)
95
91
 
96
- def __setitem__(self, index: int, value: bool) -> None:
92
+ def __setitem__(self, time: Time, state: State | bool) -> None:
97
93
  """Set a schedule item."""
98
- return self._intervals.__setitem__(index, value)
99
-
100
- def append(self, item: bool) -> None:
101
- """Append a value to the interval."""
102
- self._intervals.append(item)
94
+ if state in get_args(State):
95
+ state = True if state == STATE_ON else False
96
+ if isinstance(state, bool):
97
+ self._schedule.__setitem__(time, state)
98
+ else:
99
+ raise TypeError(
100
+ f"Expected boolean value or one of: {', '.join(get_args(State))}."
101
+ )
103
102
 
104
103
  def set_state(
105
- self, state: ScheduleState, start: Time = "00:00", end: Time = "00:00"
104
+ self, state: State | bool, start: Time = MIDNIGHT, end: Time = MIDNIGHT
106
105
  ) -> None:
107
106
  """Set a schedule interval state."""
108
- if state not in get_args(ScheduleState):
109
- raise ValueError(
110
- f"Invalid state '{state}'. Allowed states are: "
111
- f"{', '.join(get_args(ScheduleState))}"
112
- )
113
-
114
- for index in _get_time_range(start, end):
115
- self._intervals[index] = True if state in _ON_STATES else False
107
+ for time in _get_time_range(start, end):
108
+ self.__setitem__(time, state)
116
109
 
117
- def set_on(self, start: Time = "00:00", end: Time = "00:00") -> None:
110
+ def set_on(self, start: Time = MIDNIGHT, end: Time = MIDNIGHT) -> None:
118
111
  """Set a schedule interval state to 'on'."""
119
112
  self.set_state(STATE_ON, start, end)
120
113
 
121
- def set_off(self, start: Time = "00:00", end: Time = "00:00") -> None:
114
+ def set_off(self, start: Time = MIDNIGHT, end: Time = MIDNIGHT) -> None:
122
115
  """Set a schedule interval state to 'off'."""
123
116
  self.set_state(STATE_OFF, start, end)
124
117
 
125
118
  @property
126
- def intervals(self) -> list[bool]:
127
- """Return the schedule intervals."""
128
- return self._intervals
119
+ def schedule(self) -> dict[Time, bool]:
120
+ """Return the schedule."""
121
+ return self._schedule
122
+
123
+ @classmethod
124
+ def from_iterable(cls: type[ScheduleDay], intervals: Iterable[bool]) -> ScheduleDay:
125
+ """Make schedule day from iterable."""
126
+ return cls({_get_time(index): state for index, state in enumerate(intervals)})
129
127
 
130
128
 
131
129
  @dataclass
pyplumio/helpers/uid.py CHANGED
@@ -10,8 +10,8 @@ POLYNOMIAL: Final = 0xA001
10
10
  BASE5_KEY: Final = "0123456789ABCDEFGHIJKLMNZPQRSTUV"
11
11
 
12
12
 
13
- def decode_uid(buffer: bytes) -> str:
14
- """Decode an UID string."""
13
+ def unpack_uid(buffer: bytes) -> str:
14
+ """Unpack UID from bytes."""
15
15
  return _base5(buffer + _crc16(buffer))
16
16
 
17
17
 
@@ -17,7 +17,7 @@ from pyplumio.utils import ensure_dict
17
17
  ATTR_ALERTS: Final = "alerts"
18
18
  ATTR_TOTAL_ALERTS: Final = "total_alerts"
19
19
 
20
- MAX_UINT32: Final = 4294967295
20
+ MAX_UINT32: Final = 0xFFFFFFFF
21
21
 
22
22
 
23
23
  class DateTimeInterval(NamedTuple):
@@ -2,7 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import math
6
5
  from typing import Any, Final
7
6
 
8
7
  from pyplumio.helpers.data_types import UnsignedInt
@@ -60,7 +59,7 @@ class OutputsStructure(StructureDecoder):
60
59
  ensure_dict(
61
60
  data,
62
61
  {
63
- output: bool(outputs.value & int(math.pow(2, index)))
62
+ output: bool(outputs.value & 2**index)
64
63
  for index, output in enumerate(OUTPUTS)
65
64
  },
66
65
  ),
@@ -10,7 +10,7 @@ from typing import Any, Final
10
10
 
11
11
  from pyplumio.const import ProductType
12
12
  from pyplumio.helpers.data_types import UnsignedShort, VarBytes, VarString
13
- from pyplumio.helpers.uid import decode_uid
13
+ from pyplumio.helpers.uid import unpack_uid
14
14
  from pyplumio.structures import StructureDecoder
15
15
  from pyplumio.utils import ensure_dict
16
16
 
@@ -69,7 +69,7 @@ class ProductInfoStructure(StructureDecoder):
69
69
  ATTR_PRODUCT: ProductInfo(
70
70
  type=ProductType(product_type),
71
71
  id=product_id,
72
- uid=decode_uid(uid.value),
72
+ uid=unpack_uid(uid.value),
73
73
  logo=logo.value,
74
74
  image=image.value,
75
75
  model=format_model_name(model_name.value),
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: PyPlumIO
3
- Version: 0.5.37
3
+ Version: 0.5.39
4
4
  Summary: PyPlumIO is a native ecoNET library for Plum ecoMAX controllers.
5
5
  Author-email: Denis Paavilainen <denpa@denpa.pro>
6
6
  License: MIT License
@@ -28,22 +28,23 @@ Requires-Dist: pyserial-asyncio==0.6
28
28
  Requires-Dist: typing-extensions==4.12.2
29
29
  Provides-Extra: test
30
30
  Requires-Dist: codespell==2.4.1; extra == "test"
31
- Requires-Dist: coverage==7.6.10; extra == "test"
32
- Requires-Dist: mypy==1.14.1; extra == "test"
31
+ Requires-Dist: coverage==7.8.0; extra == "test"
32
+ Requires-Dist: mypy==1.15.0; extra == "test"
33
33
  Requires-Dist: pyserial-asyncio-fast==0.14; extra == "test"
34
- Requires-Dist: pytest==8.3.4; extra == "test"
34
+ Requires-Dist: pytest==8.3.5; extra == "test"
35
35
  Requires-Dist: pytest-asyncio==0.25.3; extra == "test"
36
- Requires-Dist: ruff==0.9.4; extra == "test"
37
- Requires-Dist: tox==4.24.1; extra == "test"
38
- Requires-Dist: types-pyserial==3.5.0.20250130; extra == "test"
36
+ Requires-Dist: ruff==0.11.2; extra == "test"
37
+ Requires-Dist: tox==4.24.2; extra == "test"
38
+ Requires-Dist: types-pyserial==3.5.0.20250304; extra == "test"
39
39
  Provides-Extra: docs
40
40
  Requires-Dist: sphinx==8.1.3; extra == "docs"
41
41
  Requires-Dist: sphinx_rtd_theme==3.0.2; extra == "docs"
42
42
  Requires-Dist: readthedocs-sphinx-search==0.3.2; extra == "docs"
43
43
  Provides-Extra: dev
44
44
  Requires-Dist: pyplumio[docs,test]; extra == "dev"
45
- Requires-Dist: pre-commit==4.1.0; extra == "dev"
45
+ Requires-Dist: pre-commit==4.2.0; extra == "dev"
46
46
  Requires-Dist: tomli==2.2.1; extra == "dev"
47
+ Dynamic: license-file
47
48
 
48
49
  # PyPlumIO is a native ecoNET library for Plum ecoMAX controllers.
49
50
  [![PyPI version](https://badge.fury.io/py/PyPlumIO.svg)](https://badge.fury.io/py/PyPlumIO)
@@ -1,8 +1,8 @@
1
1
  pyplumio/__init__.py,sha256=3ibJ43RIdfFrWp1PAsQixybAA--NPRw43B5OdLOwsU8,3319
2
2
  pyplumio/__main__.py,sha256=3IwHHSq-iay5FaeMc95klobe-xv82yydSKcBE7BFZ6M,500
3
- pyplumio/_version.py,sha256=orZLgglRSt7G3Yf4YysqzFGUuXQI2XkO9JT2vDRdeJc,413
3
+ pyplumio/_version.py,sha256=v7a2gQwjvA79-wWIFaeBNU5SpLePHsodAUXPcj2RZBc,513
4
4
  pyplumio/connection.py,sha256=-dbrIK6ewoYNeBQod9ZmXT8JkxMKbcS6nosINFsg9RI,5972
5
- pyplumio/const.py,sha256=LyXa5aVy2KxnZq7H7F8s5SYsAgEC2UzZYMMRauliB2E,5502
5
+ pyplumio/const.py,sha256=26s1TJF7IJa6o1pjDmHaAzPgMJ5c-fb0jeSkzDQ6Bic,5577
6
6
  pyplumio/exceptions.py,sha256=_B_0EgxDxd2XyYv3WpZM733q0cML5m6J-f55QOvYRpI,996
7
7
  pyplumio/filters.py,sha256=AMW1zHQ1YjJfHX7e87Dhv7AGixJ3y9Vn-_JAQn7vIsg,12526
8
8
  pyplumio/protocol.py,sha256=VRxrj8vZ1FMawqblKkyxg_V61TBSvVynd9u0JXYnMUU,8090
@@ -10,7 +10,7 @@ pyplumio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  pyplumio/stream.py,sha256=Ne-mWkO6FpILAjGdagbAh_VL3QEla-eDiT2N-kOc5o4,4883
11
11
  pyplumio/utils.py,sha256=TnBzRopinyp92wruguijxcIYmaeyNVTFX0dygI5FCMU,823
12
12
  pyplumio/devices/__init__.py,sha256=Erjd3DeEop_yelnLtRRaPbwMIuD1NwVh7dMM1_2KxtI,8155
13
- pyplumio/devices/ecomax.py,sha256=hb4QULpyg5KrCWeQDNnwy7IA9k5oRETK5AWllVgA0Kg,15806
13
+ pyplumio/devices/ecomax.py,sha256=0LCVeTMzC1isu0HE_MHp7bEXJXUCinXNWVVFTn4k92E,15855
14
14
  pyplumio/devices/ecoster.py,sha256=jNWli7ye9T6yfkcFJZhhUHH7KOv-L6AgYFp_dKyv3OM,263
15
15
  pyplumio/devices/mixer.py,sha256=HdJNsvX3obYyLsuDhERX4IkodX3hGv3veP9ymjQnoUk,3108
16
16
  pyplumio/devices/thermostat.py,sha256=-CZNRyywoDU6csFu85KSmQ5woVXY0x6peXkeOsi_fqg,2617
@@ -21,14 +21,14 @@ pyplumio/frames/responses.py,sha256=dzrL0Yx7SoJuJAQyjOE8_ARfy7yvOqk2uq4kdnH5t1U,
21
21
  pyplumio/helpers/__init__.py,sha256=H2xxdkF-9uADLwEbfBUoxNTdwru3L5Z2cfJjgsuRsn0,31
22
22
  pyplumio/helpers/data_types.py,sha256=nB3afOLmppgSCWkZoX1-1yWPNMMNSem77x7XQ1Mi8H8,9103
23
23
  pyplumio/helpers/event_manager.py,sha256=xQOfiP_nP1Pz5zhB6HU5gXyyJXjhisYshL8_HRxDgt8,6412
24
- pyplumio/helpers/factory.py,sha256=v07s9DyihfkNUzt7ndyJbNd_DLS8UpRkut_xkGrbi6c,1123
25
- pyplumio/helpers/parameter.py,sha256=LoTYAtSLv2bGjEMABn7S1Ycqd_DzcMt_6UPG8frFZ-8,12740
26
- pyplumio/helpers/schedule.py,sha256=0lkghnnpQRdRtgqoNv7PnHMYYJpJNMHl9PR4_SaHB8w,5374
24
+ pyplumio/helpers/factory.py,sha256=9uXUmVRvPkg9IyrfYYVbz9wsYAXltMTXkm1x82dhMyA,1126
25
+ pyplumio/helpers/parameter.py,sha256=zfcSJK-55uwJTcLYMY5e7Zwr4M4SQXq8bsM4pnHZ7aQ,12689
26
+ pyplumio/helpers/schedule.py,sha256=Dl28p3iz8okr5AT5v78WiJv6ggYlO-f2Jk6r5t1wY0A,5266
27
27
  pyplumio/helpers/task_manager.py,sha256=HAd69yGTRL0zQsu-ywnbLu1UXiJzgHWuhYWA--vs4lQ,1181
28
28
  pyplumio/helpers/timeout.py,sha256=JAhWNtIpcXyVILIwHWVy5mYofqbbRDGKLdTUKkQuajs,772
29
- pyplumio/helpers/uid.py,sha256=qcE8sx8YwrUX3xEfL0cgjNP65rOZmv-M3fDlgFezUwc,989
29
+ pyplumio/helpers/uid.py,sha256=-7OCw7fJjd6kGVZmYU8nHl2WQpNvSIj-m-VtlngDZz4,990
30
30
  pyplumio/structures/__init__.py,sha256=EjK-5qJZ0F7lpP2b6epvTMg9cIBl4Kn91nqNkEcLwTc,1299
31
- pyplumio/structures/alerts.py,sha256=8ievMl5_tUBlnTLCiZoIloucIngCcoAYy6uI9sSXrt0,3664
31
+ pyplumio/structures/alerts.py,sha256=O4P0sbBu1g7AN_AApcViy9CcrY5Vry_LZJgidNUF7Co,3664
32
32
  pyplumio/structures/boiler_load.py,sha256=p3mOzZUU-g7A2tG_yp8podEqpI81hlsOZmHELyPNRY8,838
33
33
  pyplumio/structures/boiler_power.py,sha256=72qsvccg49FdRdXv2f2K5sGpjT7wAOLFjlIGWpO-DVg,901
34
34
  pyplumio/structures/ecomax_parameters.py,sha256=4hsLM8pgcLrfYL0loLqTH4kMSdVzOThu5SL_QTodSYs,27997
@@ -42,9 +42,9 @@ pyplumio/structures/mixer_sensors.py,sha256=-cN7U-Fr2fmAQ5McQL7bZUC8CFlb1y8TN0f_
42
42
  pyplumio/structures/modules.py,sha256=oXUIqrOAV1dZzBV5zUH3HDUSFvNOjpUSx0TF9nZVnbs,2569
43
43
  pyplumio/structures/network_info.py,sha256=kPxmIaDGm5SyLRKVFzcrODlUtB0u5JjiZqekoKSyDpA,4159
44
44
  pyplumio/structures/output_flags.py,sha256=07N0kxlvR5WZAURuChk_BqSiXR8eaQrtI5qlkgCf4Yc,1345
45
- pyplumio/structures/outputs.py,sha256=1xsJPkjN643-aFawqVoupGatUIUJfQG_g252n051Qi0,1916
45
+ pyplumio/structures/outputs.py,sha256=pZ8AoTFLSs4aRh2-4CVI29GWNyLCGo4JviqQTmrPPak,1889
46
46
  pyplumio/structures/pending_alerts.py,sha256=Uq9WpB4MW9AhDkqmDhk-g0J0h4pVq0Q50z12dYEv6kY,739
47
- pyplumio/structures/product_info.py,sha256=uiEN6DFQlzmBvQByTirFzXQShoex0YGdFS9WI-MAxPc,2405
47
+ pyplumio/structures/product_info.py,sha256=ex_1DIiVzqHdBGaagV9Gy1ZN8JxxkzVxRTNYRddjdhw,2405
48
48
  pyplumio/structures/program_version.py,sha256=R-medELYHDlk_ALsw5HOVbZRb7JD3yBUsGwqwVCjrkU,2550
49
49
  pyplumio/structures/regulator_data.py,sha256=z2mSE-cxImn8YRr_yZCcDlIbXnKdETkN7GigV5vEJqA,2265
50
50
  pyplumio/structures/regulator_data_schema.py,sha256=XM6M9ep3NyogbLPqp88mMTg8Sa9e5SFzV5I5pSYw5GY,1487
@@ -53,8 +53,8 @@ pyplumio/structures/statuses.py,sha256=wkoynyMRr1VREwfBC6vU48kPA8ZQ83pcXuciy2xHJ
53
53
  pyplumio/structures/temperatures.py,sha256=1CDzehNmbALz1Jyt_9gZNIk52q6Wv-xQXjijVDCVYec,2337
54
54
  pyplumio/structures/thermostat_parameters.py,sha256=QA-ZyulBG3P10sqgdI7rmpQYlKm9SJIXxBxAXs8Bwow,8295
55
55
  pyplumio/structures/thermostat_sensors.py,sha256=8e1TxYIJTQKT0kIGO9gG4hGdLOBUpIhiPToQyOMyeNE,3237
56
- PyPlumIO-0.5.37.dist-info/LICENSE,sha256=m-UuZFjXJ22uPTGm9kSHS8bqjsf5T8k2wL9bJn1Y04o,1088
57
- PyPlumIO-0.5.37.dist-info/METADATA,sha256=CNXGeqKEbJVnM1LlpzV33S7TDJXCJYlrY8pU8n_4w1w,5510
58
- PyPlumIO-0.5.37.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
59
- PyPlumIO-0.5.37.dist-info/top_level.txt,sha256=kNBz9UPPkPD9teDn3U_sEy5LjzwLm9KfADCXtBlbw8A,9
60
- PyPlumIO-0.5.37.dist-info/RECORD,,
56
+ pyplumio-0.5.39.dist-info/licenses/LICENSE,sha256=m-UuZFjXJ22uPTGm9kSHS8bqjsf5T8k2wL9bJn1Y04o,1088
57
+ pyplumio-0.5.39.dist-info/METADATA,sha256=Qo29aT1mKgRimnxFp_ipIMLzdg_0lxSve54ILTxUKRA,5532
58
+ pyplumio-0.5.39.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
59
+ pyplumio-0.5.39.dist-info/top_level.txt,sha256=kNBz9UPPkPD9teDn3U_sEy5LjzwLm9KfADCXtBlbw8A,9
60
+ pyplumio-0.5.39.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5