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
pyplumio/structures/alerts.py
CHANGED
@@ -3,10 +3,11 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
from collections.abc import Generator
|
6
|
+
from contextlib import suppress
|
6
7
|
from dataclasses import dataclass
|
7
8
|
from datetime import datetime
|
8
9
|
from functools import lru_cache
|
9
|
-
from typing import Any, Final
|
10
|
+
from typing import Any, Final, Literal, NamedTuple
|
10
11
|
|
11
12
|
from pyplumio.const import AlertType
|
12
13
|
from pyplumio.helpers.data_types import UnsignedInt
|
@@ -19,27 +20,40 @@ ATTR_TOTAL_ALERTS: Final = "total_alerts"
|
|
19
20
|
MAX_UINT32: Final = 4294967295
|
20
21
|
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
("year", 32140800, 2000), # 60sec * 60min * 24h * 31d * 12m
|
30
|
-
("month", 2678400, 1), # 60sec * 60min * 24h * 31d
|
31
|
-
("day", 86400, 1), # 60sec * 60min * 24h
|
32
|
-
("hour", 3600, 0), # 60sec * 60min
|
33
|
-
("minute", 60, 0),
|
34
|
-
("second", 1, 0),
|
35
|
-
)
|
23
|
+
class DateTimeInterval(NamedTuple):
|
24
|
+
"""Represents an alert time interval."""
|
25
|
+
|
26
|
+
name: Literal["year", "month", "day", "hour", "minute", "second"]
|
27
|
+
seconds: int
|
28
|
+
offset: int = 0
|
29
|
+
|
36
30
|
|
37
|
-
|
38
|
-
|
39
|
-
|
31
|
+
DATETIME_INTERVALS: tuple[DateTimeInterval, ...] = (
|
32
|
+
DateTimeInterval("year", seconds=60 * 60 * 24 * 31 * 12, offset=2000),
|
33
|
+
DateTimeInterval("month", seconds=60 * 60 * 24 * 31, offset=1),
|
34
|
+
DateTimeInterval("day", seconds=60 * 60 * 24, offset=1),
|
35
|
+
DateTimeInterval("hour", seconds=60 * 60),
|
36
|
+
DateTimeInterval("minute", seconds=60),
|
37
|
+
DateTimeInterval("second", seconds=1),
|
38
|
+
)
|
39
|
+
|
40
|
+
|
41
|
+
@lru_cache(maxsize=10)
|
42
|
+
def _seconds_to_datetime(timestamp: int) -> datetime:
|
43
|
+
"""Convert timestamp to a datetime object.
|
44
|
+
|
45
|
+
The ecoMAX controller stores alert time as a special timestamp value
|
46
|
+
in seconds counted from Jan 1st, 2000.
|
47
|
+
"""
|
48
|
+
|
49
|
+
def _datetime_kwargs(timestamp: int) -> Generator[Any, None, None]:
|
50
|
+
"""Yield a tuple, that represents a single datetime kwarg."""
|
51
|
+
for name, seconds, offset in DATETIME_INTERVALS:
|
52
|
+
value = timestamp // seconds
|
53
|
+
timestamp -= value * seconds
|
40
54
|
yield name, (value + offset)
|
41
55
|
|
42
|
-
return datetime(**dict(
|
56
|
+
return datetime(**dict(_datetime_kwargs(timestamp)))
|
43
57
|
|
44
58
|
|
45
59
|
@dataclass
|
@@ -62,24 +76,20 @@ class AlertsStructure(StructureDecoder):
|
|
62
76
|
|
63
77
|
def _unpack_alert(self, message: bytearray) -> Alert:
|
64
78
|
"""Unpack an alert."""
|
65
|
-
|
66
|
-
code = message[self._offset]
|
67
|
-
code = AlertType(code)
|
68
|
-
except ValueError:
|
69
|
-
pass
|
70
|
-
|
79
|
+
code = message[self._offset]
|
71
80
|
self._offset += 1
|
72
81
|
from_seconds = UnsignedInt.from_bytes(message, self._offset)
|
73
82
|
self._offset += from_seconds.size
|
74
83
|
to_seconds = UnsignedInt.from_bytes(message, self._offset)
|
75
84
|
self._offset += to_seconds.size
|
76
|
-
|
77
|
-
from_dt = _convert_to_datetime(from_seconds.value)
|
85
|
+
from_dt = _seconds_to_datetime(from_seconds.value)
|
78
86
|
to_dt = (
|
79
87
|
None
|
80
88
|
if to_seconds.value == MAX_UINT32
|
81
|
-
else
|
89
|
+
else _seconds_to_datetime(to_seconds.value)
|
82
90
|
)
|
91
|
+
with suppress(ValueError):
|
92
|
+
code = AlertType(code)
|
83
93
|
|
84
94
|
return Alert(code, from_dt, to_dt)
|
85
95
|
|
@@ -90,12 +100,11 @@ class AlertsStructure(StructureDecoder):
|
|
90
100
|
total_alerts = message[offset + 0]
|
91
101
|
start = message[offset + 1]
|
92
102
|
end = message[offset + 2]
|
93
|
-
|
103
|
+
self._offset = offset + 3
|
94
104
|
if end == 0:
|
95
105
|
# No alerts found.
|
96
|
-
return ensure_dict(data, {ATTR_TOTAL_ALERTS: total_alerts}),
|
106
|
+
return ensure_dict(data, {ATTR_TOTAL_ALERTS: total_alerts}), self._offset
|
97
107
|
|
98
|
-
self._offset = offset + 3
|
99
108
|
return (
|
100
109
|
ensure_dict(
|
101
110
|
data,
|