PyPlumIO 0.5.15__py3-none-any.whl → 0.5.17__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.15.dist-info → PyPlumIO-0.5.17.dist-info}/METADATA +10 -10
- {PyPlumIO-0.5.15.dist-info → PyPlumIO-0.5.17.dist-info}/RECORD +23 -23
- {PyPlumIO-0.5.15.dist-info → PyPlumIO-0.5.17.dist-info}/WHEEL +1 -1
- pyplumio/_version.py +2 -2
- pyplumio/connection.py +10 -22
- pyplumio/devices/__init__.py +2 -2
- pyplumio/devices/ecomax.py +16 -9
- pyplumio/frames/__init__.py +1 -1
- pyplumio/frames/messages.py +2 -2
- pyplumio/frames/requests.py +17 -17
- pyplumio/frames/responses.py +14 -14
- pyplumio/helpers/factory.py +10 -7
- pyplumio/helpers/parameter.py +5 -6
- pyplumio/helpers/schedule.py +2 -3
- pyplumio/protocol.py +10 -8
- pyplumio/stream.py +2 -2
- pyplumio/structures/alerts.py +7 -1
- pyplumio/structures/ecomax_parameters.py +34 -35
- pyplumio/structures/mixer_parameters.py +17 -16
- pyplumio/structures/schedules.py +11 -10
- pyplumio/structures/thermostat_parameters.py +20 -19
- {PyPlumIO-0.5.15.dist-info → PyPlumIO-0.5.17.dist-info}/LICENSE +0 -0
- {PyPlumIO-0.5.15.dist-info → PyPlumIO-0.5.17.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: PyPlumIO
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.17
|
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
|
@@ -25,22 +25,22 @@ License-File: LICENSE
|
|
25
25
|
Requires-Dist: pyserial-asyncio ==0.6
|
26
26
|
Provides-Extra: dev
|
27
27
|
Requires-Dist: pyplumio[docs,test] ; extra == 'dev'
|
28
|
-
Requires-Dist: pre-commit ==3.
|
28
|
+
Requires-Dist: pre-commit ==3.7.0 ; extra == 'dev'
|
29
29
|
Requires-Dist: tomli ==2.0.1 ; extra == 'dev'
|
30
30
|
Provides-Extra: docs
|
31
|
-
Requires-Dist: sphinx ==7.
|
31
|
+
Requires-Dist: sphinx ==7.3.7 ; extra == 'docs'
|
32
32
|
Requires-Dist: sphinx-rtd-theme ==2.0.0 ; extra == 'docs'
|
33
33
|
Requires-Dist: readthedocs-sphinx-search ==0.3.2 ; extra == 'docs'
|
34
34
|
Provides-Extra: test
|
35
35
|
Requires-Dist: codespell ==2.2.6 ; extra == 'test'
|
36
|
-
Requires-Dist: coverage ==7.
|
37
|
-
Requires-Dist: mypy ==1.
|
36
|
+
Requires-Dist: coverage ==7.5.0 ; extra == 'test'
|
37
|
+
Requires-Dist: mypy ==1.9.0 ; extra == 'test'
|
38
38
|
Requires-Dist: pyserial-asyncio-fast ==0.11 ; extra == 'test'
|
39
|
-
Requires-Dist: pytest ==8.
|
40
|
-
Requires-Dist: pytest-asyncio ==0.23.
|
41
|
-
Requires-Dist: ruff ==0.2
|
42
|
-
Requires-Dist: tox ==4.
|
43
|
-
Requires-Dist: types-pyserial ==3.5.0.
|
39
|
+
Requires-Dist: pytest ==8.2.0 ; extra == 'test'
|
40
|
+
Requires-Dist: pytest-asyncio ==0.23.6 ; extra == 'test'
|
41
|
+
Requires-Dist: ruff ==0.4.2 ; extra == 'test'
|
42
|
+
Requires-Dist: tox ==4.15.0 ; extra == 'test'
|
43
|
+
Requires-Dist: types-pyserial ==3.5.0.20240311 ; extra == 'test'
|
44
44
|
|
45
45
|
# PyPlumIO is a native ecoNET library for Plum ecoMAX controllers.
|
46
46
|
[](https://badge.fury.io/py/PyPlumIO)
|
@@ -1,44 +1,44 @@
|
|
1
1
|
pyplumio/__init__.py,sha256=4v9BaIkJ440qu2ITrcwVLOg9KO8k2W1gnGHe86sDLM8,3292
|
2
2
|
pyplumio/__main__.py,sha256=oop76iR-XDHhMFhW4LO8-xTnBHsqzUQ5VVh__94OaqY,499
|
3
|
-
pyplumio/_version.py,sha256=
|
4
|
-
pyplumio/connection.py,sha256=
|
3
|
+
pyplumio/_version.py,sha256=dQYxLlSh6o3oTwPaW5cCsGzUzw8dHq1HmpZtnNncm_0,413
|
4
|
+
pyplumio/connection.py,sha256=0PmpNOcV7RgSfEeM7Wnhtbw0TpdtMTyqpJPQ2h7IX9M,6199
|
5
5
|
pyplumio/const.py,sha256=4_VRx5mY7qf5fwaUluVlhuK_NIb7bClzhnhRLA-3GkY,3914
|
6
6
|
pyplumio/exceptions.py,sha256=3tgfe0GD-T-DV2TUybsv7dwsk9P9f2g-4Gd8jnXw6DI,698
|
7
7
|
pyplumio/filters.py,sha256=kXR4SUS7YXGaljW35NpEB9prHgxDKpsf_U1ACMTnh5I,11174
|
8
|
-
pyplumio/protocol.py,sha256=
|
8
|
+
pyplumio/protocol.py,sha256=Gl3aUxdcK5Z_ZkO88bPwiv9dBR_JO8VQR_H7aKKbHiM,8756
|
9
9
|
pyplumio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
pyplumio/stream.py,sha256=
|
10
|
+
pyplumio/stream.py,sha256=UPqlUmNBobSS4SvS5BI19g_FeSTLpLj1k3l7OeE32bI,4374
|
11
11
|
pyplumio/utils.py,sha256=h46IS7hOHFbAFGGmnyAUVwlfk0m_eSwa3U8oZq6-bJY,687
|
12
|
-
pyplumio/devices/__init__.py,sha256=
|
13
|
-
pyplumio/devices/ecomax.py,sha256=
|
12
|
+
pyplumio/devices/__init__.py,sha256=zw0DDvXLdASaMeTUMZog-osKhsugNpPMYU4dV1Jr4iU,6379
|
13
|
+
pyplumio/devices/ecomax.py,sha256=cdPZ2LbUvH0l8hpwWxZiB570OloIpeilXWdHEDlixW4,16795
|
14
14
|
pyplumio/devices/ecoster.py,sha256=QH68bzs199x9Id-wOY6J5WddyR69STNQi8SxaWDwKG4,312
|
15
15
|
pyplumio/devices/mixer.py,sha256=a-1klZuEO7dLQfpdOor0vUZ9ksllf8D1w0AbMeGmE_g,2946
|
16
16
|
pyplumio/devices/thermostat.py,sha256=CXHAWwqyDATcI8UYNcTv6TK3YHYDSUyHE3Txyy9fVAI,2401
|
17
|
-
pyplumio/frames/__init__.py,sha256=
|
18
|
-
pyplumio/frames/messages.py,sha256=
|
19
|
-
pyplumio/frames/requests.py,sha256=
|
20
|
-
pyplumio/frames/responses.py,sha256=
|
17
|
+
pyplumio/frames/__init__.py,sha256=p3YFapqx7q9KJt3KhmG4qKFmQqNu05Y7hK1k9B9tENY,7264
|
18
|
+
pyplumio/frames/messages.py,sha256=hZn8mvPuSw4kD7EL8Xtroh1sDZe6CqLc5HyvctMzGMc,3616
|
19
|
+
pyplumio/frames/requests.py,sha256=F64kJ29jLfyy1YRqWLVTqib5MALGeB72NY8-kAx42IY,7220
|
20
|
+
pyplumio/frames/responses.py,sha256=7MHwXkNVTcwYpWRAXBMiPxmaJw3vgX9kgoaBTXeRy60,6531
|
21
21
|
pyplumio/helpers/__init__.py,sha256=H2xxdkF-9uADLwEbfBUoxNTdwru3L5Z2cfJjgsuRsn0,31
|
22
22
|
pyplumio/helpers/data_types.py,sha256=UIbJu0JfXaNCiHlg8EnYOfeyKq9Mxiu6j_D8GyRudZk,8211
|
23
23
|
pyplumio/helpers/event_manager.py,sha256=E9gMGCxOOBNZgnHCwpb8rtFgKsNIkjHvIN73XBgwQDk,6004
|
24
|
-
pyplumio/helpers/factory.py,sha256=
|
25
|
-
pyplumio/helpers/parameter.py,sha256=
|
26
|
-
pyplumio/helpers/schedule.py,sha256=
|
24
|
+
pyplumio/helpers/factory.py,sha256=9U0vYy137nDI5Zl0CjlWhm6p0ztGeafOtkydZU0kA6I,694
|
25
|
+
pyplumio/helpers/parameter.py,sha256=ihdom_tON2urYXmS7x_2-zk2I-PcogdjILE7tOiGR8M,8698
|
26
|
+
pyplumio/helpers/schedule.py,sha256=zfVmawRjpKa5JpDGg2n9E985UtW-hEWEyn6cKSm6U6c,4911
|
27
27
|
pyplumio/helpers/task_manager.py,sha256=P17Nw9HDbA9NMSdkJo2WQRbEsykzzFSwQyyRzI3uFPk,1089
|
28
28
|
pyplumio/helpers/timeout.py,sha256=bWBWvLPpgjCvdG5hlrSTXok_CsLme-jGnY9rHwupRc0,1286
|
29
29
|
pyplumio/helpers/typing.py,sha256=V3uYCMyC4oePM7YzL0S-xEsyTgjgDbkOM0VNe-1LBPo,684
|
30
30
|
pyplumio/helpers/uid.py,sha256=FTwuET3WLNyEkAHpxD4ZA3hu-l_JCS6pq12xibAQs6s,974
|
31
31
|
pyplumio/structures/__init__.py,sha256=-nbLcQvbWcs2EmnChqJmMVo1CUfj8lqMHb8yK1mV4j8,1298
|
32
|
-
pyplumio/structures/alerts.py,sha256=
|
32
|
+
pyplumio/structures/alerts.py,sha256=v--wbEKoB07r9KHMuZgMRT1dk4r8dPwxxB5cPv4lzZY,3259
|
33
33
|
pyplumio/structures/boiler_load.py,sha256=HVPKt53VWvp2KDuSK1B9kcpX1h3Bz3GPBqBI4Oscm2Q,837
|
34
34
|
pyplumio/structures/boiler_power.py,sha256=tP00IMz5qVQaePr70uTz_jCLoXHnmj_g3pvKxdNvenc,900
|
35
|
-
pyplumio/structures/ecomax_parameters.py,sha256=
|
35
|
+
pyplumio/structures/ecomax_parameters.py,sha256=D4IFbRzMtGK91XUCAzGp2VJ9LouYnRcPa-QP2mH2lnA,26184
|
36
36
|
pyplumio/structures/fan_power.py,sha256=fGU_BTPTtAplnjmTQQHhMXdoiHVYOdLlZ_PDA9F5u9c,870
|
37
37
|
pyplumio/structures/frame_versions.py,sha256=9lFnrlxkok_3CTbXz38cntacnubWYGcaubDnYq9NOAM,1559
|
38
38
|
pyplumio/structures/fuel_consumption.py,sha256=H23GVw9s0fbURvJHAfReTEvGp5q-oPOidh1mToOO3ec,975
|
39
39
|
pyplumio/structures/fuel_level.py,sha256=27QfxB_LXAREmHs7q8KyohpLbagLUZolCQoZJUDTPz0,1068
|
40
40
|
pyplumio/structures/lambda_sensor.py,sha256=JdKHWN2RcMgCXhMZ0IhYCYrHWnw2TgsR-iitLn7QLe0,1586
|
41
|
-
pyplumio/structures/mixer_parameters.py,sha256=
|
41
|
+
pyplumio/structures/mixer_parameters.py,sha256=UsOey8D5d7rrUACwOcoBB_x2lHQYKC2Xsoxqcpf6DG4,8380
|
42
42
|
pyplumio/structures/mixer_sensors.py,sha256=f6BjpX-ENGlKIDOOWUHMlb0TZ5HMYD9dIkRoSvgRXHg,2259
|
43
43
|
pyplumio/structures/modules.py,sha256=t332XxB-6dTtDbY9mNuqTqb21zDy2XG_17pmWgJfGvw,2519
|
44
44
|
pyplumio/structures/network_info.py,sha256=pLbCB5Z8nCB6efb6ZZImw3TaVAOB3Af5K1z9vVbuGgo,4054
|
@@ -49,13 +49,13 @@ pyplumio/structures/product_info.py,sha256=Os0AS88geH2JqvVRC2zQxEucmGJoDzR3ed3Pl
|
|
49
49
|
pyplumio/structures/program_version.py,sha256=WX5tM3X3aaJCjNd5rsAN6EXivBXM-kLXQvhfQR8wKIM,2358
|
50
50
|
pyplumio/structures/regulator_data.py,sha256=9d7fGcZ1SI2QosUQSd0CpPqaHpgXihqV5-QgHWGzkNU,2379
|
51
51
|
pyplumio/structures/regulator_data_schema.py,sha256=5uwdw8nCqSX9Ex5jH9t4pghj6fLRRsCnGQx69LVGAy0,1504
|
52
|
-
pyplumio/structures/schedules.py,sha256=
|
52
|
+
pyplumio/structures/schedules.py,sha256=j7kuT7gYesFKJJ4yMrW6JVO0aJX9O6PT2D_PTCQre04,6441
|
53
53
|
pyplumio/structures/statuses.py,sha256=zjDQTU5fdORRzRkvlkzplgVS8A3AdyHG2qihfkdOHIM,1165
|
54
54
|
pyplumio/structures/temperatures.py,sha256=O8rANFN-wz5NvTWqh_25oUfFajjVyI9hoOMmz8vkKHg,2336
|
55
|
-
pyplumio/structures/thermostat_parameters.py,sha256=
|
55
|
+
pyplumio/structures/thermostat_parameters.py,sha256=1SJzHguN12JE-e2utwA448VgUkFddQxBQy3m66qCobk,7934
|
56
56
|
pyplumio/structures/thermostat_sensors.py,sha256=N9nm3Rp1Rhb8wUPUkJIX56olZztixoxyFxIxP4R5P2g,3176
|
57
|
-
PyPlumIO-0.5.
|
58
|
-
PyPlumIO-0.5.
|
59
|
-
PyPlumIO-0.5.
|
60
|
-
PyPlumIO-0.5.
|
61
|
-
PyPlumIO-0.5.
|
57
|
+
PyPlumIO-0.5.17.dist-info/LICENSE,sha256=m-UuZFjXJ22uPTGm9kSHS8bqjsf5T8k2wL9bJn1Y04o,1088
|
58
|
+
PyPlumIO-0.5.17.dist-info/METADATA,sha256=hYBntfwRNAwg3Zdb_S0HailPnsFm9ewT99rRNys494U,5414
|
59
|
+
PyPlumIO-0.5.17.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
60
|
+
PyPlumIO-0.5.17.dist-info/top_level.txt,sha256=kNBz9UPPkPD9teDn3U_sEy5LjzwLm9KfADCXtBlbw8A,9
|
61
|
+
PyPlumIO-0.5.17.dist-info/RECORD,,
|
pyplumio/_version.py
CHANGED
pyplumio/connection.py
CHANGED
@@ -10,6 +10,7 @@ from typing import Any, Final, cast
|
|
10
10
|
from serial import EIGHTBITS, PARITY_NONE, STOPBITS_ONE, SerialException
|
11
11
|
|
12
12
|
from pyplumio.exceptions import ConnectionFailedError
|
13
|
+
from pyplumio.helpers.task_manager import TaskManager
|
13
14
|
from pyplumio.helpers.timeout import timeout
|
14
15
|
from pyplumio.protocol import AsyncProtocol, Protocol
|
15
16
|
|
@@ -26,13 +27,12 @@ except ImportError:
|
|
26
27
|
import serial_asyncio as pyserial_asyncio
|
27
28
|
|
28
29
|
|
29
|
-
class Connection(ABC):
|
30
|
+
class Connection(ABC, TaskManager):
|
30
31
|
"""Represents a connection.
|
31
32
|
|
32
33
|
All specific connection classes MUST be inherited from this class.
|
33
34
|
"""
|
34
35
|
|
35
|
-
_closing: bool
|
36
36
|
_protocol: Protocol
|
37
37
|
_reconnect_on_failure: bool
|
38
38
|
_kwargs: MutableMapping[str, Any]
|
@@ -44,13 +44,13 @@ class Connection(ABC):
|
|
44
44
|
**kwargs: Any,
|
45
45
|
) -> None:
|
46
46
|
"""Initialize a new connection."""
|
47
|
+
super().__init__()
|
47
48
|
if protocol is None:
|
48
49
|
protocol = AsyncProtocol()
|
49
50
|
|
50
51
|
if reconnect_on_failure:
|
51
|
-
protocol.on_connection_lost.add(self.
|
52
|
+
protocol.on_connection_lost.add(self._reconnect)
|
52
53
|
|
53
|
-
self._closing = False
|
54
54
|
self._reconnect_on_failure = reconnect_on_failure
|
55
55
|
self._protocol = protocol
|
56
56
|
self._kwargs = kwargs
|
@@ -76,29 +76,20 @@ class Connection(ABC):
|
|
76
76
|
await self._open_connection(),
|
77
77
|
)
|
78
78
|
self.protocol.connection_established(reader, writer)
|
79
|
-
except (
|
80
|
-
|
81
|
-
SerialException,
|
82
|
-
asyncio.TimeoutError,
|
83
|
-
) as connection_error:
|
84
|
-
raise ConnectionFailedError from connection_error
|
79
|
+
except (OSError, SerialException, asyncio.TimeoutError) as err:
|
80
|
+
raise ConnectionFailedError from err
|
85
81
|
|
86
82
|
async def _reconnect(self) -> None:
|
87
83
|
"""Try to connect and reconnect on failure."""
|
88
84
|
try:
|
89
|
-
|
85
|
+
await self._connect()
|
90
86
|
except ConnectionFailedError:
|
91
|
-
await self._connection_lost()
|
92
|
-
|
93
|
-
async def _connection_lost(self) -> None:
|
94
|
-
"""Resume connection on the connection loss."""
|
95
|
-
if not self._closing:
|
96
87
|
_LOGGER.error(
|
97
88
|
"Can't connect to the device, retrying in %.1f seconds",
|
98
89
|
RECONNECT_TIMEOUT,
|
99
90
|
)
|
100
91
|
await asyncio.sleep(RECONNECT_TIMEOUT)
|
101
|
-
|
92
|
+
self.create_task(self._reconnect())
|
102
93
|
|
103
94
|
async def connect(self) -> None:
|
104
95
|
"""Open the connection.
|
@@ -106,14 +97,11 @@ class Connection(ABC):
|
|
106
97
|
Initialize a connection via connect or reconnect
|
107
98
|
routines, depending on '_reconnect_on_failure' property.
|
108
99
|
"""
|
109
|
-
if self._reconnect_on_failure
|
110
|
-
await self._reconnect()
|
111
|
-
else:
|
112
|
-
await self._connect()
|
100
|
+
await (self._reconnect if self._reconnect_on_failure else self._connect)()
|
113
101
|
|
114
102
|
async def close(self) -> None:
|
115
103
|
"""Close the connection."""
|
116
|
-
self.
|
104
|
+
self.cancel_tasks()
|
117
105
|
await self.protocol.shutdown()
|
118
106
|
|
119
107
|
@property
|
pyplumio/devices/__init__.py
CHANGED
@@ -11,7 +11,7 @@ from pyplumio.const import ATTR_FRAME_ERRORS, ATTR_LOADED, DeviceType, FrameType
|
|
11
11
|
from pyplumio.exceptions import UnknownDeviceError
|
12
12
|
from pyplumio.frames import DataFrameDescription, Frame, Request, get_frame_handler
|
13
13
|
from pyplumio.helpers.event_manager import EventManager
|
14
|
-
from pyplumio.helpers.factory import
|
14
|
+
from pyplumio.helpers.factory import create_instance
|
15
15
|
from pyplumio.helpers.parameter import SET_RETRIES, Parameter
|
16
16
|
from pyplumio.helpers.typing import ParameterValueType
|
17
17
|
from pyplumio.structures.network_info import NetworkInfo
|
@@ -160,7 +160,7 @@ class AddressableDevice(Device, ABC):
|
|
160
160
|
|
161
161
|
If value is not available before timeout, retry request.
|
162
162
|
"""
|
163
|
-
request: Request =
|
163
|
+
request: Request = await create_instance(
|
164
164
|
get_frame_handler(frame_type), recipient=self.address
|
165
165
|
)
|
166
166
|
|
pyplumio/devices/ecomax.py
CHANGED
@@ -28,7 +28,7 @@ from pyplumio.frames import (
|
|
28
28
|
get_frame_handler,
|
29
29
|
is_known_frame_type,
|
30
30
|
)
|
31
|
-
from pyplumio.helpers.factory import
|
31
|
+
from pyplumio.helpers.factory import create_instance
|
32
32
|
from pyplumio.helpers.parameter import ParameterValues
|
33
33
|
from pyplumio.helpers.schedule import Schedule, ScheduleDay
|
34
34
|
from pyplumio.structures.alerts import ATTR_TOTAL_ALERTS
|
@@ -235,7 +235,7 @@ class EcoMAX(AddressableDevice):
|
|
235
235
|
and not self._has_frame_version(frame_type, version)
|
236
236
|
):
|
237
237
|
# We don't have this frame or it's version has changed.
|
238
|
-
request: Request =
|
238
|
+
request: Request = await create_instance(
|
239
239
|
get_frame_handler(frame_type), recipient=self.address
|
240
240
|
)
|
241
241
|
self.queue.put_nowait(request)
|
@@ -346,16 +346,23 @@ class EcoMAX(AddressableDevice):
|
|
346
346
|
|
347
347
|
return True
|
348
348
|
|
349
|
-
async def _add_ecomax_control_parameter(self, mode:
|
349
|
+
async def _add_ecomax_control_parameter(self, mode: DeviceState) -> None:
|
350
350
|
"""Create ecoMAX control parameter instance and dispatch an event."""
|
351
|
+
description = ECOMAX_CONTROL_PARAMETER
|
352
|
+
name = description.name
|
353
|
+
values = ParameterValues(
|
354
|
+
value=int(mode != DeviceState.OFF), min_value=0, max_value=1
|
355
|
+
)
|
356
|
+
|
357
|
+
if name in self.data:
|
358
|
+
parameter: EcomaxBinaryParameter = self.data[name]
|
359
|
+
parameter.values = values
|
360
|
+
return await self.dispatch(name, parameter)
|
361
|
+
|
351
362
|
await self.dispatch(
|
352
|
-
|
363
|
+
name,
|
353
364
|
EcomaxBinaryParameter(
|
354
|
-
device=self,
|
355
|
-
description=ECOMAX_CONTROL_PARAMETER,
|
356
|
-
values=ParameterValues(
|
357
|
-
value=int(mode != DeviceState.OFF), min_value=0, max_value=1
|
358
|
-
),
|
365
|
+
device=self, description=ECOMAX_CONTROL_PARAMETER, values=values
|
359
366
|
),
|
360
367
|
)
|
361
368
|
|
pyplumio/frames/__init__.py
CHANGED
pyplumio/frames/messages.py
CHANGED
@@ -35,7 +35,7 @@ class RegulatorDataMessage(Message):
|
|
35
35
|
|
36
36
|
__slots__ = ()
|
37
37
|
|
38
|
-
frame_type: ClassVar[FrameType
|
38
|
+
frame_type: ClassVar[FrameType] = FrameType.MESSAGE_REGULATOR_DATA
|
39
39
|
|
40
40
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
41
41
|
"""Decode a frame message."""
|
@@ -47,7 +47,7 @@ class SensorDataMessage(Message):
|
|
47
47
|
|
48
48
|
__slots__ = ()
|
49
49
|
|
50
|
-
frame_type: ClassVar[FrameType
|
50
|
+
frame_type: ClassVar[FrameType] = FrameType.MESSAGE_SENSOR_DATA
|
51
51
|
|
52
52
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
53
53
|
"""Decode a frame message."""
|
pyplumio/frames/requests.py
CHANGED
@@ -24,7 +24,7 @@ class ProgramVersionRequest(Request):
|
|
24
24
|
|
25
25
|
__slots__ = ()
|
26
26
|
|
27
|
-
frame_type: ClassVar[FrameType
|
27
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_PROGRAM_VERSION
|
28
28
|
|
29
29
|
def response(self, **kwargs: Any) -> Response | None:
|
30
30
|
"""Return a response frame."""
|
@@ -36,7 +36,7 @@ class CheckDeviceRequest(Request):
|
|
36
36
|
|
37
37
|
__slots__ = ()
|
38
38
|
|
39
|
-
frame_type: ClassVar[FrameType
|
39
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_CHECK_DEVICE
|
40
40
|
|
41
41
|
def response(self, **kwargs: Any) -> Response | None:
|
42
42
|
"""Return a response frame."""
|
@@ -48,7 +48,7 @@ class UIDRequest(Request):
|
|
48
48
|
|
49
49
|
__slots__ = ()
|
50
50
|
|
51
|
-
frame_type: ClassVar[FrameType
|
51
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_UID
|
52
52
|
|
53
53
|
|
54
54
|
class PasswordRequest(Request):
|
@@ -56,7 +56,7 @@ class PasswordRequest(Request):
|
|
56
56
|
|
57
57
|
__slots__ = ()
|
58
58
|
|
59
|
-
frame_type: ClassVar[FrameType
|
59
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_PASSWORD
|
60
60
|
|
61
61
|
|
62
62
|
class EcomaxParametersRequest(Request):
|
@@ -67,7 +67,7 @@ class EcomaxParametersRequest(Request):
|
|
67
67
|
|
68
68
|
__slots__ = ()
|
69
69
|
|
70
|
-
frame_type: ClassVar[FrameType
|
70
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_ECOMAX_PARAMETERS
|
71
71
|
|
72
72
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
73
73
|
"""Create a frame message."""
|
@@ -83,7 +83,7 @@ class MixerParametersRequest(Request):
|
|
83
83
|
|
84
84
|
__slots__ = ()
|
85
85
|
|
86
|
-
frame_type: ClassVar[FrameType
|
86
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_MIXER_PARAMETERS
|
87
87
|
|
88
88
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
89
89
|
"""Create a frame message."""
|
@@ -99,7 +99,7 @@ class ThermostatParametersRequest(Request):
|
|
99
99
|
|
100
100
|
__slots__ = ()
|
101
101
|
|
102
|
-
frame_type: ClassVar[FrameType
|
102
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_THERMOSTAT_PARAMETERS
|
103
103
|
|
104
104
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
105
105
|
"""Create a frame message."""
|
@@ -111,7 +111,7 @@ class RegulatorDataSchemaRequest(Request):
|
|
111
111
|
|
112
112
|
__slots__ = ()
|
113
113
|
|
114
|
-
frame_type: ClassVar[FrameType
|
114
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_REGULATOR_DATA_SCHEMA
|
115
115
|
|
116
116
|
|
117
117
|
class SetEcomaxParameterRequest(Request):
|
@@ -122,7 +122,7 @@ class SetEcomaxParameterRequest(Request):
|
|
122
122
|
|
123
123
|
__slots__ = ()
|
124
124
|
|
125
|
-
frame_type: ClassVar[FrameType
|
125
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_SET_ECOMAX_PARAMETER
|
126
126
|
|
127
127
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
128
128
|
"""Create a frame message."""
|
@@ -140,7 +140,7 @@ class SetMixerParameterRequest(Request):
|
|
140
140
|
|
141
141
|
__slots__ = ()
|
142
142
|
|
143
|
-
frame_type: ClassVar[FrameType
|
143
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_SET_MIXER_PARAMETER
|
144
144
|
|
145
145
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
146
146
|
"""Create a frame message."""
|
@@ -168,7 +168,7 @@ class SetThermostatParameterRequest(Request):
|
|
168
168
|
|
169
169
|
__slots__ = ()
|
170
170
|
|
171
|
-
frame_type: ClassVar[FrameType
|
171
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_SET_THERMOSTAT_PARAMETER
|
172
172
|
|
173
173
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
174
174
|
"""Create a frame message."""
|
@@ -191,7 +191,7 @@ class EcomaxControlRequest(Request):
|
|
191
191
|
|
192
192
|
__slots__ = ()
|
193
193
|
|
194
|
-
frame_type: ClassVar[FrameType
|
194
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_ECOMAX_CONTROL
|
195
195
|
|
196
196
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
197
197
|
"""Create a frame message."""
|
@@ -210,7 +210,7 @@ class StartMasterRequest(Request):
|
|
210
210
|
|
211
211
|
__slots__ = ()
|
212
212
|
|
213
|
-
frame_type: ClassVar[FrameType
|
213
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_START_MASTER
|
214
214
|
|
215
215
|
|
216
216
|
class StopMasterRequest(Request):
|
@@ -222,7 +222,7 @@ class StopMasterRequest(Request):
|
|
222
222
|
|
223
223
|
__slots__ = ()
|
224
224
|
|
225
|
-
frame_type: ClassVar[FrameType
|
225
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_STOP_MASTER
|
226
226
|
|
227
227
|
|
228
228
|
class AlertsRequest(Request):
|
@@ -234,7 +234,7 @@ class AlertsRequest(Request):
|
|
234
234
|
|
235
235
|
__slots__ = ()
|
236
236
|
|
237
|
-
frame_type: ClassVar[FrameType
|
237
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_ALERTS
|
238
238
|
|
239
239
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
240
240
|
"""Create a frame message."""
|
@@ -246,7 +246,7 @@ class SchedulesRequest(Request):
|
|
246
246
|
|
247
247
|
__slots__ = ()
|
248
248
|
|
249
|
-
frame_type: ClassVar[FrameType
|
249
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_SCHEDULES
|
250
250
|
|
251
251
|
|
252
252
|
class SetScheduleRequest(Request):
|
@@ -254,7 +254,7 @@ class SetScheduleRequest(Request):
|
|
254
254
|
|
255
255
|
__slots__ = ()
|
256
256
|
|
257
|
-
frame_type: ClassVar[FrameType
|
257
|
+
frame_type: ClassVar[FrameType] = FrameType.REQUEST_SET_SCHEDULE
|
258
258
|
|
259
259
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
260
260
|
"""Create a frame message."""
|
pyplumio/frames/responses.py
CHANGED
@@ -24,7 +24,7 @@ class ProgramVersionResponse(Response):
|
|
24
24
|
|
25
25
|
__slots__ = ()
|
26
26
|
|
27
|
-
frame_type: ClassVar[FrameType
|
27
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_PROGRAM_VERSION
|
28
28
|
|
29
29
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
30
30
|
"""Create a frame message."""
|
@@ -43,7 +43,7 @@ class DeviceAvailableResponse(Response):
|
|
43
43
|
|
44
44
|
__slots__ = ()
|
45
45
|
|
46
|
-
frame_type: ClassVar[FrameType
|
46
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_DEVICE_AVAILABLE
|
47
47
|
|
48
48
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
49
49
|
"""Create a frame message."""
|
@@ -62,7 +62,7 @@ class UIDResponse(Response):
|
|
62
62
|
|
63
63
|
__slots__ = ()
|
64
64
|
|
65
|
-
frame_type: ClassVar[FrameType
|
65
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_UID
|
66
66
|
|
67
67
|
def create_message(self, data: dict[str, Any]) -> bytearray:
|
68
68
|
"""Create a frame message."""
|
@@ -81,7 +81,7 @@ class PasswordResponse(Response):
|
|
81
81
|
|
82
82
|
__slots__ = ()
|
83
83
|
|
84
|
-
frame_type: ClassVar[FrameType
|
84
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_PASSWORD
|
85
85
|
|
86
86
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
87
87
|
"""Decode a frame message."""
|
@@ -97,7 +97,7 @@ class EcomaxParametersResponse(Response):
|
|
97
97
|
|
98
98
|
__slots__ = ()
|
99
99
|
|
100
|
-
frame_type: ClassVar[FrameType
|
100
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_ECOMAX_PARAMETERS
|
101
101
|
|
102
102
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
103
103
|
"""Decode a frame message."""
|
@@ -112,7 +112,7 @@ class MixerParametersResponse(Response):
|
|
112
112
|
|
113
113
|
__slots__ = ()
|
114
114
|
|
115
|
-
frame_type: ClassVar[FrameType
|
115
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_MIXER_PARAMETERS
|
116
116
|
|
117
117
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
118
118
|
"""Decode a frame message."""
|
@@ -127,7 +127,7 @@ class ThermostatParametersResponse(Response):
|
|
127
127
|
|
128
128
|
__slots__ = ()
|
129
129
|
|
130
|
-
frame_type: ClassVar[FrameType
|
130
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_THERMOSTAT_PARAMETERS
|
131
131
|
|
132
132
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
133
133
|
"""Decode a frame message."""
|
@@ -143,7 +143,7 @@ class RegulatorDataSchemaResponse(Response):
|
|
143
143
|
|
144
144
|
__slots__ = ()
|
145
145
|
|
146
|
-
frame_type: ClassVar[FrameType
|
146
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_REGULATOR_DATA_SCHEMA
|
147
147
|
|
148
148
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
149
149
|
"""Decode a frame message."""
|
@@ -159,7 +159,7 @@ class SetEcomaxParameterResponse(Response):
|
|
159
159
|
|
160
160
|
__slots__ = ()
|
161
161
|
|
162
|
-
frame_type: ClassVar[FrameType
|
162
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_SET_ECOMAX_PARAMETER
|
163
163
|
|
164
164
|
|
165
165
|
class SetMixerParameterResponse(Response):
|
@@ -171,7 +171,7 @@ class SetMixerParameterResponse(Response):
|
|
171
171
|
|
172
172
|
__slots__ = ()
|
173
173
|
|
174
|
-
frame_type: ClassVar[FrameType
|
174
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_SET_MIXER_PARAMETER
|
175
175
|
|
176
176
|
|
177
177
|
class SetThermostatParameterResponse(Response):
|
@@ -183,7 +183,7 @@ class SetThermostatParameterResponse(Response):
|
|
183
183
|
|
184
184
|
__slots__ = ()
|
185
185
|
|
186
|
-
frame_type: ClassVar[FrameType
|
186
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_SET_THERMOSTAT_PARAMETER
|
187
187
|
|
188
188
|
|
189
189
|
class EcomaxControlResponse(Response):
|
@@ -195,7 +195,7 @@ class EcomaxControlResponse(Response):
|
|
195
195
|
|
196
196
|
__slots__ = ()
|
197
197
|
|
198
|
-
frame_type: ClassVar[FrameType
|
198
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_ECOMAX_CONTROL
|
199
199
|
|
200
200
|
|
201
201
|
class AlertsResponse(Response):
|
@@ -203,7 +203,7 @@ class AlertsResponse(Response):
|
|
203
203
|
|
204
204
|
__slots__ = ()
|
205
205
|
|
206
|
-
frame_type: ClassVar[FrameType
|
206
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_ALERTS
|
207
207
|
|
208
208
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
209
209
|
"""Decode a frame message."""
|
@@ -215,7 +215,7 @@ class SchedulesResponse(Response):
|
|
215
215
|
|
216
216
|
__slots__ = ()
|
217
217
|
|
218
|
-
frame_type: ClassVar[FrameType
|
218
|
+
frame_type: ClassVar[FrameType] = FrameType.RESPONSE_SCHEDULES
|
219
219
|
|
220
220
|
def decode_message(self, message: bytearray) -> dict[str, Any]:
|
221
221
|
"""Decode a frame message."""
|
pyplumio/helpers/factory.py
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
"""Contains a factory helper."""
|
2
2
|
from __future__ import annotations
|
3
3
|
|
4
|
-
import
|
4
|
+
import asyncio
|
5
|
+
from importlib import import_module
|
5
6
|
import logging
|
6
7
|
from typing import Any
|
7
8
|
|
8
9
|
_LOGGER = logging.getLogger(__name__)
|
9
10
|
|
10
11
|
|
11
|
-
def
|
12
|
-
"""Return class instance from the class path."""
|
12
|
+
async def create_instance(class_path: str, **kwargs: Any) -> Any:
|
13
|
+
"""Return a class instance from the class path."""
|
14
|
+
loop = asyncio.get_running_loop()
|
15
|
+
module_name, class_name = class_path.rsplit(".", 1)
|
13
16
|
try:
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
)(**kwargs)
|
17
|
+
module = await loop.run_in_executor(
|
18
|
+
None, import_module, "." + module_name, "pyplumio"
|
19
|
+
)
|
20
|
+
return getattr(module, class_name)(**kwargs)
|
18
21
|
except Exception:
|
19
22
|
_LOGGER.error("Failed to load module (%s)", class_path)
|
20
23
|
raise
|
pyplumio/helpers/parameter.py
CHANGED
@@ -166,6 +166,10 @@ class Parameter(ABC):
|
|
166
166
|
"""Set parameter as no longer pending update."""
|
167
167
|
self._pending_update = False
|
168
168
|
|
169
|
+
async def create_request(self) -> Request:
|
170
|
+
"""Create a request to change the parameter."""
|
171
|
+
raise NotImplementedError
|
172
|
+
|
169
173
|
async def set(self, value: ParameterValueType, retries: int = SET_RETRIES) -> bool:
|
170
174
|
"""Set a parameter value."""
|
171
175
|
if (value := _normalize_parameter_value(value)) == self.values.value:
|
@@ -188,7 +192,7 @@ class Parameter(ABC):
|
|
188
192
|
self.device.unsubscribe(self.description.name, self._confirm_update)
|
189
193
|
return False
|
190
194
|
|
191
|
-
await self.device.queue.put(self.
|
195
|
+
await self.device.queue.put(await self.create_request())
|
192
196
|
await asyncio.sleep(SET_TIMEOUT)
|
193
197
|
retries -= 1
|
194
198
|
|
@@ -223,11 +227,6 @@ class Parameter(ABC):
|
|
223
227
|
"""Return the unit of measurement."""
|
224
228
|
return self.description.unit_of_measurement
|
225
229
|
|
226
|
-
@property
|
227
|
-
def request(self) -> Request:
|
228
|
-
"""Return request to change the parameter."""
|
229
|
-
raise NotImplementedError
|
230
|
-
|
231
230
|
|
232
231
|
class BinaryParameter(Parameter):
|
233
232
|
"""Represents binary device parameter."""
|
pyplumio/helpers/schedule.py
CHANGED
@@ -9,7 +9,7 @@ from typing import Final, Literal
|
|
9
9
|
|
10
10
|
from pyplumio.const import STATE_OFF, STATE_ON
|
11
11
|
from pyplumio.devices import AddressableDevice
|
12
|
-
from pyplumio.
|
12
|
+
from pyplumio.frames.requests import SetScheduleRequest
|
13
13
|
from pyplumio.structures.schedules import collect_schedule_data
|
14
14
|
|
15
15
|
TIME_FORMAT: Final = "%H:%M"
|
@@ -161,8 +161,7 @@ class Schedule(Iterable):
|
|
161
161
|
def commit(self) -> None:
|
162
162
|
"""Commit a weekly schedule to the device."""
|
163
163
|
self.device.queue.put_nowait(
|
164
|
-
|
165
|
-
"frames.requests.SetScheduleRequest",
|
164
|
+
SetScheduleRequest(
|
166
165
|
recipient=self.device.address,
|
167
166
|
data=collect_schedule_data(self.name, self.device),
|
168
167
|
)
|
pyplumio/protocol.py
CHANGED
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
4
4
|
from abc import ABC, abstractmethod
|
5
5
|
import asyncio
|
6
6
|
from collections.abc import Awaitable, Callable
|
7
|
-
from functools import cache
|
8
7
|
import logging
|
9
8
|
from typing import cast
|
10
9
|
|
@@ -19,7 +18,7 @@ from pyplumio.exceptions import (
|
|
19
18
|
from pyplumio.frames import Frame
|
20
19
|
from pyplumio.frames.requests import StartMasterRequest
|
21
20
|
from pyplumio.helpers.event_manager import EventManager
|
22
|
-
from pyplumio.helpers.factory import
|
21
|
+
from pyplumio.helpers.factory import create_instance
|
23
22
|
from pyplumio.stream import FrameReader, FrameWriter
|
24
23
|
from pyplumio.structures.network_info import (
|
25
24
|
EthernetParameters,
|
@@ -202,7 +201,9 @@ class AsyncProtocol(Protocol, EventManager):
|
|
202
201
|
write_queue.task_done()
|
203
202
|
|
204
203
|
if (response := await reader.read()) is not None:
|
205
|
-
device = self.get_device_entry(
|
204
|
+
device = await self.get_device_entry(
|
205
|
+
cast(DeviceType, response.sender)
|
206
|
+
)
|
206
207
|
read_queue.put_nowait((device, response))
|
207
208
|
|
208
209
|
except FrameDataError as e:
|
@@ -229,16 +230,17 @@ class AsyncProtocol(Protocol, EventManager):
|
|
229
230
|
device.handle_frame(frame)
|
230
231
|
read_queue.task_done()
|
231
232
|
|
232
|
-
|
233
|
-
def get_device_entry(self, device_type: DeviceType) -> AddressableDevice:
|
233
|
+
async def get_device_entry(self, device_type: DeviceType) -> AddressableDevice:
|
234
234
|
"""Set up device entry."""
|
235
235
|
handler, name = get_device_handler_and_name(device_type)
|
236
|
-
return self.data.setdefault(
|
236
|
+
return self.data.setdefault(
|
237
|
+
name, await self._create_device_entry(name, handler)
|
238
|
+
)
|
237
239
|
|
238
|
-
def _create_device_entry(self, name: str, handler: str) -> AddressableDevice:
|
240
|
+
async def _create_device_entry(self, name: str, handler: str) -> AddressableDevice:
|
239
241
|
"""Create device entry."""
|
240
242
|
write_queue = self.queues[1]
|
241
|
-
device: AddressableDevice =
|
243
|
+
device: AddressableDevice = await create_instance(
|
242
244
|
handler, queue=write_queue, network=self._network
|
243
245
|
)
|
244
246
|
device.dispatch_nowait(ATTR_CONNECTED, True)
|
pyplumio/stream.py
CHANGED
@@ -9,7 +9,7 @@ from typing import Final
|
|
9
9
|
from pyplumio.const import DeviceType
|
10
10
|
from pyplumio.exceptions import ChecksumError, ReadError
|
11
11
|
from pyplumio.frames import FRAME_START, Frame, bcc, get_frame_handler, struct_header
|
12
|
-
from pyplumio.helpers.factory import
|
12
|
+
from pyplumio.helpers.factory import create_instance
|
13
13
|
from pyplumio.helpers.timeout import timeout
|
14
14
|
|
15
15
|
READER_TIMEOUT: Final = 10
|
@@ -132,7 +132,7 @@ class FrameReader:
|
|
132
132
|
if payload[-2] != bcc(header + payload[:-2]):
|
133
133
|
raise ChecksumError(f"Incorrect frame checksum ({payload[-2]})")
|
134
134
|
|
135
|
-
frame: Frame =
|
135
|
+
frame: Frame = await create_instance(
|
136
136
|
get_frame_handler(frame_type=payload[0]),
|
137
137
|
recipient=recipient,
|
138
138
|
message=payload[1:-2],
|
pyplumio/structures/alerts.py
CHANGED
@@ -15,6 +15,8 @@ from pyplumio.utils import ensure_dict
|
|
15
15
|
ATTR_ALERTS: Final = "alerts"
|
16
16
|
ATTR_TOTAL_ALERTS: Final = "total_alerts"
|
17
17
|
|
18
|
+
MAX_UINT32: Final = 4294967295
|
19
|
+
|
18
20
|
|
19
21
|
@lru_cache(maxsize=10)
|
20
22
|
def _convert_to_datetime(seconds: int) -> datetime:
|
@@ -72,7 +74,11 @@ class AlertsStructure(StructureDecoder):
|
|
72
74
|
self._offset += to_seconds.size
|
73
75
|
|
74
76
|
from_dt = _convert_to_datetime(from_seconds.value)
|
75
|
-
to_dt =
|
77
|
+
to_dt = (
|
78
|
+
None
|
79
|
+
if to_seconds.value == MAX_UINT32
|
80
|
+
else _convert_to_datetime(to_seconds.value)
|
81
|
+
)
|
76
82
|
|
77
83
|
return Alert(code, from_dt, to_dt)
|
78
84
|
|
@@ -16,7 +16,7 @@ from pyplumio.const import (
|
|
16
16
|
)
|
17
17
|
from pyplumio.devices import AddressableDevice
|
18
18
|
from pyplumio.frames import Request
|
19
|
-
from pyplumio.helpers.factory import
|
19
|
+
from pyplumio.helpers.factory import create_instance
|
20
20
|
from pyplumio.helpers.parameter import (
|
21
21
|
BinaryParameter,
|
22
22
|
BinaryParameterDescription,
|
@@ -44,39 +44,10 @@ class EcomaxParameter(Parameter):
|
|
44
44
|
device: AddressableDevice
|
45
45
|
description: EcomaxParameterDescription
|
46
46
|
|
47
|
-
async def
|
48
|
-
"""
|
49
|
-
if isinstance(value, (int, float)):
|
50
|
-
value = int((value + self.description.offset) / self.description.multiplier)
|
51
|
-
|
52
|
-
return await super().set(value, retries)
|
53
|
-
|
54
|
-
@property
|
55
|
-
def value(self) -> ParameterValueType:
|
56
|
-
"""Return the parameter value."""
|
57
|
-
return (
|
58
|
-
self.values.value - self.description.offset
|
59
|
-
) * self.description.multiplier
|
60
|
-
|
61
|
-
@property
|
62
|
-
def min_value(self) -> ParameterValueType:
|
63
|
-
"""Return the minimum allowed value."""
|
64
|
-
return (
|
65
|
-
self.values.min_value - self.description.offset
|
66
|
-
) * self.description.multiplier
|
67
|
-
|
68
|
-
@property
|
69
|
-
def max_value(self) -> ParameterValueType:
|
70
|
-
"""Return the maximum allowed value."""
|
71
|
-
return (
|
72
|
-
self.values.max_value - self.description.offset
|
73
|
-
) * self.description.multiplier
|
74
|
-
|
75
|
-
@property
|
76
|
-
def request(self) -> Request:
|
77
|
-
"""Return request to change the parameter."""
|
47
|
+
async def create_request(self) -> Request:
|
48
|
+
"""Create a request to change the parameter."""
|
78
49
|
if self.description.name == ATTR_ECOMAX_CONTROL:
|
79
|
-
request: Request =
|
50
|
+
request: Request = await create_instance(
|
80
51
|
"frames.requests.EcomaxControlRequest",
|
81
52
|
recipient=self.device.address,
|
82
53
|
data={
|
@@ -85,7 +56,7 @@ class EcomaxParameter(Parameter):
|
|
85
56
|
)
|
86
57
|
|
87
58
|
elif self.description.name == ATTR_THERMOSTAT_PROFILE:
|
88
|
-
request =
|
59
|
+
request = await create_instance(
|
89
60
|
"frames.requests.SetThermostatParameterRequest",
|
90
61
|
recipient=self.device.address,
|
91
62
|
data={
|
@@ -97,7 +68,7 @@ class EcomaxParameter(Parameter):
|
|
97
68
|
)
|
98
69
|
|
99
70
|
else:
|
100
|
-
request =
|
71
|
+
request = await create_instance(
|
101
72
|
"frames.requests.SetEcomaxParameterRequest",
|
102
73
|
recipient=self.device.address,
|
103
74
|
data={
|
@@ -108,6 +79,34 @@ class EcomaxParameter(Parameter):
|
|
108
79
|
|
109
80
|
return request
|
110
81
|
|
82
|
+
async def set(self, value: ParameterValueType, retries: int = 5) -> bool:
|
83
|
+
"""Set a parameter value."""
|
84
|
+
if isinstance(value, (int, float)):
|
85
|
+
value = int((value + self.description.offset) / self.description.multiplier)
|
86
|
+
|
87
|
+
return await super().set(value, retries)
|
88
|
+
|
89
|
+
@property
|
90
|
+
def value(self) -> ParameterValueType:
|
91
|
+
"""Return the parameter value."""
|
92
|
+
return (
|
93
|
+
self.values.value - self.description.offset
|
94
|
+
) * self.description.multiplier
|
95
|
+
|
96
|
+
@property
|
97
|
+
def min_value(self) -> ParameterValueType:
|
98
|
+
"""Return the minimum allowed value."""
|
99
|
+
return (
|
100
|
+
self.values.min_value - self.description.offset
|
101
|
+
) * self.description.multiplier
|
102
|
+
|
103
|
+
@property
|
104
|
+
def max_value(self) -> ParameterValueType:
|
105
|
+
"""Return the maximum allowed value."""
|
106
|
+
return (
|
107
|
+
self.values.max_value - self.description.offset
|
108
|
+
) * self.description.multiplier
|
109
|
+
|
111
110
|
|
112
111
|
class EcomaxBinaryParameter(BinaryParameter, EcomaxParameter):
|
113
112
|
"""Represents an ecoMAX binary parameter."""
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
3
3
|
|
4
4
|
from collections.abc import Generator
|
5
5
|
from dataclasses import dataclass
|
6
|
-
from typing import TYPE_CHECKING, Any, Final
|
6
|
+
from typing import TYPE_CHECKING, Any, Final, cast
|
7
7
|
|
8
8
|
from pyplumio.const import (
|
9
9
|
ATTR_DEVICE_INDEX,
|
@@ -13,7 +13,7 @@ from pyplumio.const import (
|
|
13
13
|
UnitOfMeasurement,
|
14
14
|
)
|
15
15
|
from pyplumio.frames import Request
|
16
|
-
from pyplumio.helpers.factory import
|
16
|
+
from pyplumio.helpers.factory import create_instance
|
17
17
|
from pyplumio.helpers.parameter import (
|
18
18
|
BinaryParameter,
|
19
19
|
BinaryParameterDescription,
|
@@ -42,6 +42,21 @@ class MixerParameter(Parameter):
|
|
42
42
|
device: Mixer
|
43
43
|
description: MixerParameterDescription
|
44
44
|
|
45
|
+
async def create_request(self) -> Request:
|
46
|
+
"""Create a request to change the parameter."""
|
47
|
+
return cast(
|
48
|
+
Request,
|
49
|
+
await create_instance(
|
50
|
+
"frames.requests.SetMixerParameterRequest",
|
51
|
+
recipient=self.device.parent.address,
|
52
|
+
data={
|
53
|
+
ATTR_INDEX: self._index,
|
54
|
+
ATTR_VALUE: self.values.value,
|
55
|
+
ATTR_DEVICE_INDEX: self.device.index,
|
56
|
+
},
|
57
|
+
),
|
58
|
+
)
|
59
|
+
|
45
60
|
async def set(self, value: ParameterValueType, retries: int = 5) -> bool:
|
46
61
|
"""Set a parameter value."""
|
47
62
|
if isinstance(value, (int, float)):
|
@@ -70,20 +85,6 @@ class MixerParameter(Parameter):
|
|
70
85
|
self.values.max_value - self.description.offset
|
71
86
|
) * self.description.multiplier
|
72
87
|
|
73
|
-
@property
|
74
|
-
def request(self) -> Request:
|
75
|
-
"""Return request to change the parameter."""
|
76
|
-
request: Request = factory(
|
77
|
-
"frames.requests.SetMixerParameterRequest",
|
78
|
-
recipient=self.device.parent.address,
|
79
|
-
data={
|
80
|
-
ATTR_INDEX: self._index,
|
81
|
-
ATTR_VALUE: self.values.value,
|
82
|
-
ATTR_DEVICE_INDEX: self.device.index,
|
83
|
-
},
|
84
|
-
)
|
85
|
-
return request
|
86
|
-
|
87
88
|
|
88
89
|
class MixerBinaryParameter(BinaryParameter, MixerParameter):
|
89
90
|
"""Represents a mixer binary parameter."""
|
pyplumio/structures/schedules.py
CHANGED
@@ -5,13 +5,13 @@ from collections.abc import Sequence
|
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from functools import reduce
|
7
7
|
from itertools import chain
|
8
|
-
from typing import Any, Final
|
8
|
+
from typing import Any, Final, cast
|
9
9
|
|
10
10
|
from pyplumio.const import ATTR_PARAMETER, ATTR_SCHEDULE, ATTR_SWITCH, ATTR_TYPE
|
11
11
|
from pyplumio.devices import AddressableDevice, Device
|
12
12
|
from pyplumio.exceptions import FrameDataError
|
13
13
|
from pyplumio.frames import Request
|
14
|
-
from pyplumio.helpers.factory import
|
14
|
+
from pyplumio.helpers.factory import create_instance
|
15
15
|
from pyplumio.helpers.parameter import (
|
16
16
|
BinaryParameter,
|
17
17
|
BinaryParameterDescription,
|
@@ -81,16 +81,17 @@ class ScheduleParameter(Parameter):
|
|
81
81
|
|
82
82
|
device: AddressableDevice
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
"""Return request to change the parameter."""
|
84
|
+
async def create_request(self) -> Request:
|
85
|
+
"""Create a request to change the parameter."""
|
87
86
|
schedule_name, _ = self.description.name.split("_schedule_", 1)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
87
|
+
return cast(
|
88
|
+
Request,
|
89
|
+
await create_instance(
|
90
|
+
"frames.requests.SetScheduleRequest",
|
91
|
+
recipient=self.device.address,
|
92
|
+
data=collect_schedule_data(schedule_name, self.device),
|
93
|
+
),
|
92
94
|
)
|
93
|
-
return request
|
94
95
|
|
95
96
|
|
96
97
|
class ScheduleBinaryParameter(ScheduleParameter, BinaryParameter):
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
3
3
|
|
4
4
|
from collections.abc import Generator
|
5
5
|
from dataclasses import dataclass
|
6
|
-
from typing import TYPE_CHECKING, Any, Final
|
6
|
+
from typing import TYPE_CHECKING, Any, Final, cast
|
7
7
|
|
8
8
|
from pyplumio.const import (
|
9
9
|
ATTR_INDEX,
|
@@ -14,7 +14,7 @@ from pyplumio.const import (
|
|
14
14
|
)
|
15
15
|
from pyplumio.devices import AddressableDevice
|
16
16
|
from pyplumio.frames import Request
|
17
|
-
from pyplumio.helpers.factory import
|
17
|
+
from pyplumio.helpers.factory import create_instance
|
18
18
|
from pyplumio.helpers.parameter import (
|
19
19
|
BinaryParameter,
|
20
20
|
BinaryParameterDescription,
|
@@ -59,6 +59,24 @@ class ThermostatParameter(Parameter):
|
|
59
59
|
self.offset = offset
|
60
60
|
super().__init__(device, values, description, index)
|
61
61
|
|
62
|
+
async def create_request(self) -> Request:
|
63
|
+
"""Create a request to change the parameter."""
|
64
|
+
return cast(
|
65
|
+
Request,
|
66
|
+
await create_instance(
|
67
|
+
"frames.requests.SetThermostatParameterRequest",
|
68
|
+
recipient=self.device.parent.address,
|
69
|
+
data={
|
70
|
+
# Increase the index by one to account for thermostat
|
71
|
+
# profile, which is being set at ecoMAX device level.
|
72
|
+
ATTR_INDEX: self._index + 1,
|
73
|
+
ATTR_VALUE: self.values.value,
|
74
|
+
ATTR_OFFSET: self.offset,
|
75
|
+
ATTR_SIZE: self.description.size,
|
76
|
+
},
|
77
|
+
),
|
78
|
+
)
|
79
|
+
|
62
80
|
async def set(self, value: ParameterValueType, retries: int = 5) -> bool:
|
63
81
|
"""Set a parameter value."""
|
64
82
|
if isinstance(value, (int, float)):
|
@@ -81,23 +99,6 @@ class ThermostatParameter(Parameter):
|
|
81
99
|
"""Return the maximum allowed value."""
|
82
100
|
return self.values.max_value * self.description.multiplier
|
83
101
|
|
84
|
-
@property
|
85
|
-
def request(self) -> Request:
|
86
|
-
"""Return request to change the parameter."""
|
87
|
-
request: Request = factory(
|
88
|
-
"frames.requests.SetThermostatParameterRequest",
|
89
|
-
recipient=self.device.parent.address,
|
90
|
-
data={
|
91
|
-
# Increase the index by one to account for thermostat
|
92
|
-
# profile, which is being set at ecoMAX device level.
|
93
|
-
ATTR_INDEX: self._index + 1,
|
94
|
-
ATTR_VALUE: self.values.value,
|
95
|
-
ATTR_OFFSET: self.offset,
|
96
|
-
ATTR_SIZE: self.description.size,
|
97
|
-
},
|
98
|
-
)
|
99
|
-
return request
|
100
|
-
|
101
102
|
|
102
103
|
class ThermostatBinaryParameter(BinaryParameter, ThermostatParameter):
|
103
104
|
"""Represents a thermostat binary parameter."""
|
File without changes
|
File without changes
|