PyPlumIO 0.5.5__py3-none-any.whl → 0.5.7__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.5.dist-info → PyPlumIO-0.5.7.dist-info}/METADATA +7 -12
- PyPlumIO-0.5.7.dist-info/RECORD +60 -0
- pyplumio/__init__.py +83 -16
- pyplumio/_version.py +2 -2
- pyplumio/connection.py +46 -14
- pyplumio/const.py +2 -2
- pyplumio/devices/__init__.py +38 -12
- pyplumio/devices/ecomax.py +4 -6
- pyplumio/devices/ecoster.py +2 -2
- pyplumio/devices/mixer.py +2 -2
- pyplumio/devices/thermostat.py +2 -2
- pyplumio/filters.py +56 -2
- pyplumio/frames/__init__.py +12 -7
- pyplumio/frames/messages.py +6 -2
- pyplumio/frames/requests.py +53 -19
- pyplumio/frames/responses.py +46 -18
- pyplumio/helpers/data_types.py +32 -1
- pyplumio/helpers/event_manager.py +57 -8
- pyplumio/helpers/parameter.py +18 -8
- pyplumio/helpers/schedule.py +2 -2
- pyplumio/helpers/task_manager.py +0 -2
- pyplumio/helpers/typing.py +0 -1
- pyplumio/protocol.py +142 -71
- pyplumio/stream.py +0 -4
- pyplumio/structures/alerts.py +9 -15
- pyplumio/structures/ecomax_parameters.py +4 -2
- pyplumio/structures/network_info.py +13 -1
- pyplumio/structures/regulator_data.py +4 -4
- pyplumio/structures/{data_schema.py → regulator_data_schema.py} +4 -6
- pyplumio/structures/schedules.py +2 -2
- pyplumio/structures/temperatures.py +0 -2
- pyplumio/structures/thermostat_parameters.py +2 -2
- PyPlumIO-0.5.5.dist-info/RECORD +0 -60
- {PyPlumIO-0.5.5.dist-info → PyPlumIO-0.5.7.dist-info}/LICENSE +0 -0
- {PyPlumIO-0.5.5.dist-info → PyPlumIO-0.5.7.dist-info}/WHEEL +0 -0
- {PyPlumIO-0.5.5.dist-info → PyPlumIO-0.5.7.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.7
|
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
|
@@ -44,17 +44,12 @@ Devices can be connected directly via RS-485 to USB adapter or through network b
|
|
44
44
|

|
45
45
|
|
46
46
|
## Table of contents
|
47
|
-
- [
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
- [Filters](https://pyplumio.denpa.pro/usage.html#filters)
|
54
|
-
- [Regulator Data](https://pyplumio.denpa.pro/usage.html#regulator-data)
|
55
|
-
- [Mixers/Thermostats](https://pyplumio.denpa.pro/usage.html#mixers-thermostats)
|
56
|
-
- [Schedules](https://pyplumio.denpa.pro/usage.html#schedules)
|
57
|
-
- [Network Information](https://pyplumio.denpa.pro/usage.html#network-information)
|
47
|
+
- [Connecting](https://pyplumio.denpa.pro/connecting.html)
|
48
|
+
- [Reading](https://pyplumio.denpa.pro/reading.html)
|
49
|
+
- [Writing](https://pyplumio.denpa.pro/writing.html)
|
50
|
+
- [Callbacks](https://pyplumio.denpa.pro/callbacks.html)
|
51
|
+
- [Mixers/Thermostats](https://pyplumio.denpa.pro/mixers_thermostats.html)
|
52
|
+
- [Schedules](https://pyplumio.denpa.pro/schedules.html)
|
58
53
|
- [Protocol](https://pyplumio.denpa.pro/protocol.html)
|
59
54
|
- [Frame Structure](https://pyplumio.denpa.pro/protocol.html#frame-structure)
|
60
55
|
- [Requests and Responses](https://pyplumio.denpa.pro/protocol.html#requests-and-responses)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
pyplumio/__init__.py,sha256=JpT80zjHEk7N9e5sY4N53sOyUr8D7K3wU6cShkySeqI,3846
|
2
|
+
pyplumio/__main__.py,sha256=oop76iR-XDHhMFhW4LO8-xTnBHsqzUQ5VVh__94OaqY,499
|
3
|
+
pyplumio/_version.py,sha256=WG2gUkVRG4TIqG0-naezn012emNIbgERkkZYUI1CHww,411
|
4
|
+
pyplumio/connection.py,sha256=gxf4qoVRDz_ZYOEnGGnrs0QCGHxpclNWXE7_yzWDhN0,6522
|
5
|
+
pyplumio/const.py,sha256=lDHu7QkP8zKWDc5anOgyQGiXcF4Unv7hyibXfmoPDQk,3832
|
6
|
+
pyplumio/exceptions.py,sha256=cpLGRl9db6cU1Z6WXhQ7yl1V7Te193QymWiOoRF8KAI,798
|
7
|
+
pyplumio/filters.py,sha256=Q8ffCv7vH0wgTwrPKEfp861E44ev5yi41W9x19VKckc,10284
|
8
|
+
pyplumio/protocol.py,sha256=MuhnZ7fFT318X916-kH3TlMmJtVFE20PaHsbmkzKT84,8699
|
9
|
+
pyplumio/stream.py,sha256=g_lZkMWuYmFQ4mZrkP6kLxrmWpnQrLmiBIv0f-to38U,4024
|
10
|
+
pyplumio/utils.py,sha256=5QF-oJar4DfaaUHqrMIfKPFJFWGBLQfgo4nYo4doJWw,651
|
11
|
+
pyplumio/devices/__init__.py,sha256=DsPes_1xsfqW3RCfP2lnQcrdoA8ej16UNaNW89UFMWo,6488
|
12
|
+
pyplumio/devices/ecomax.py,sha256=wjm66TL9X_QU9KbmTD7fwsoACKNjM_K9nghzOlE1TEw,16469
|
13
|
+
pyplumio/devices/ecoster.py,sha256=QH68bzs199x9Id-wOY6J5WddyR69STNQi8SxaWDwKG4,312
|
14
|
+
pyplumio/devices/mixer.py,sha256=7PWwaMQ-cezvIRNot3OkemipT65lLFv4kTXPXAaH4oY,2962
|
15
|
+
pyplumio/devices/thermostat.py,sha256=BIYuSuv-1bUjGbMmWkWR0ej-57chYO9YPM5W-vAy55M,2418
|
16
|
+
pyplumio/frames/__init__.py,sha256=M0kTQii4Ver3WRCOC8eeXOb910mCDOLsUbJarksT72g,7410
|
17
|
+
pyplumio/frames/messages.py,sha256=BS5d4M1pz99pNQhu8OgE2Q6ejNO8qT2R621lUPvD9fs,3672
|
18
|
+
pyplumio/frames/requests.py,sha256=A7ixLYAzY-nebJ9DcLH55RkhNNum8JnBGr5Dv2SP4SU,7333
|
19
|
+
pyplumio/frames/responses.py,sha256=z7m0MRUVckadoghzm9por0Wn879z1C9zGk5lPZfgFqE,6647
|
20
|
+
pyplumio/helpers/__init__.py,sha256=H2xxdkF-9uADLwEbfBUoxNTdwru3L5Z2cfJjgsuRsn0,31
|
21
|
+
pyplumio/helpers/data_types.py,sha256=AGMDOlz7B_dxf2qsyfUrpb2aN9I-H9tIj40twt4ankg,7903
|
22
|
+
pyplumio/helpers/event_manager.py,sha256=uxeFFsyIiZ33f22Q_Wx0PI1aoOI2lkZa2fPZ0QGCCZw,5980
|
23
|
+
pyplumio/helpers/factory.py,sha256=403RYqlKbWRmL-RngzL1e2p5iI5_JTzwf65HeNxHfwM,527
|
24
|
+
pyplumio/helpers/parameter.py,sha256=5FXrftafV31YaFx-UamhBncrZjWeMOaHsmsoYIcd3VY,8549
|
25
|
+
pyplumio/helpers/schedule.py,sha256=gNZcgLfa-D3Y6Vx77U61cm6bMlD5iIxl0f1m5FZIkBM,4859
|
26
|
+
pyplumio/helpers/task_manager.py,sha256=fZyBmHik7rU9Kb4EECQWqVrJPUlEtEyxrEUJcf8RVYc,1072
|
27
|
+
pyplumio/helpers/timeout.py,sha256=UstSwBfrLO_YhUHHvhIgunAw89s6PocR80nmU5LN4oI,915
|
28
|
+
pyplumio/helpers/typing.py,sha256=gn7TWudOAqViQYMQk1vCk9_JnsFs8Q1KK3FrvkyI8AI,478
|
29
|
+
pyplumio/helpers/uid.py,sha256=FTwuET3WLNyEkAHpxD4ZA3hu-l_JCS6pq12xibAQs6s,974
|
30
|
+
pyplumio/structures/__init__.py,sha256=L976WdfoNb_Mmv-pN86XKBLEZydsVlD_Xlw85cnCS3E,1247
|
31
|
+
pyplumio/structures/alerts.py,sha256=gkUOYCFt5PvLusrrI9jw3qjvvbVYaX2BgUztW9CkYC0,2986
|
32
|
+
pyplumio/structures/boiler_load.py,sha256=tRlKr5gZQ-UZh042f98wH3ctOuXgCAb16ne_zFP5I28,860
|
33
|
+
pyplumio/structures/boiler_power.py,sha256=BRl3QkPtoFoEVHUeWN5KKkxATIO4-EZB-0fEsvRKECI,923
|
34
|
+
pyplumio/structures/ecomax_parameters.py,sha256=71c-dMSx3QebyAF1okO5hc6DQcJ9V-c3NRkTN8thSYY,25939
|
35
|
+
pyplumio/structures/fan_power.py,sha256=cBLmns_nh8I5eDNehFM1QMe5eqlUC-QBBm1i0kTbj38,893
|
36
|
+
pyplumio/structures/frame_versions.py,sha256=SkHrgehJbf14Qf_uKY8zlZp8d8WjgAq3Swwjf1Y1trw,1572
|
37
|
+
pyplumio/structures/fuel_consumption.py,sha256=qxT8vtOX6VIv6MgvTalYlsN7FIBzXL3ba8VHO2b6mbs,998
|
38
|
+
pyplumio/structures/fuel_level.py,sha256=XqqciKIBM8-zmG_JNc9ILff3Rgd4aCgvht7STrw9Dz0,851
|
39
|
+
pyplumio/structures/lambda_sensor.py,sha256=2aEZjY8y7qV76Ky4gE4Ui7zWyXxCQRdtMingtMBKhF0,1475
|
40
|
+
pyplumio/structures/mixer_parameters.py,sha256=ssYHpReiPC7kpNiCwgqMkbR8Wwkqh4wCQqhKUbLWPQg,8194
|
41
|
+
pyplumio/structures/mixer_sensors.py,sha256=sKm1gIoRcCLVad-9MqOkEwu_bXIZy-3Kbi2kCd5wOkM,2243
|
42
|
+
pyplumio/structures/modules.py,sha256=y1WRxKk3vW3erquzsVd4G7mDLlna8DttueeUu0jCgYw,2532
|
43
|
+
pyplumio/structures/network_info.py,sha256=O09zE4AzU-yEi_yMB4VPN-2_BYNasCAbOBANLJbh4P0,4013
|
44
|
+
pyplumio/structures/output_flags.py,sha256=JY_IVncL_ZnIV2xXfEXLDK8x2tPulIW1PZpWVHgC-C4,1367
|
45
|
+
pyplumio/structures/outputs.py,sha256=dS0dYsl9bWLuZmj5rt0Dcg1chyQhjFOIwA6KU4Yw76g,1938
|
46
|
+
pyplumio/structures/pending_alerts.py,sha256=L_uaizbL_H3gYkpPBN0T0H8Q4jNo17-5-QamiF5tQO0,761
|
47
|
+
pyplumio/structures/product_info.py,sha256=hkUGlbEr-OtpCHozGI_8R_uLB5cyYwe9dJ1Xz-Sqdoc,2396
|
48
|
+
pyplumio/structures/program_version.py,sha256=5gOmKpbr2ftsXtJ5Hqkg__30DRYtB-CAx5sXIvCLQeM,2477
|
49
|
+
pyplumio/structures/regulator_data.py,sha256=9rEy_JLij9XE2Kyc5jgWAfJKJG3OmL2W2Bix6t2zKnU,2544
|
50
|
+
pyplumio/structures/regulator_data_schema.py,sha256=W2b3-n3PZoo4pU2esqeCWP-_zz1AxymJ9Y6fcS_y6bg,1517
|
51
|
+
pyplumio/structures/schedules.py,sha256=4I74MHutWcb_i-z2ZvDxdavSnOUivWRBhvjRnK5aKK0,6125
|
52
|
+
pyplumio/structures/statuses.py,sha256=IWcQ1TNfvhVitF_vxTeF3c4txpU54NOyi4l445SJVk8,1188
|
53
|
+
pyplumio/structures/temperatures.py,sha256=znL_gSfVeO8hAvl2GYRyRiYC3E3PSS6JmJXAJoTYea8,2359
|
54
|
+
pyplumio/structures/thermostat_parameters.py,sha256=mqoqLSAPW3TfuFImEHGWmvvd-LCPR6pFcfH0Y8kd0Xg,7691
|
55
|
+
pyplumio/structures/thermostat_sensors.py,sha256=sEH42iq05Wijfhfrv9o4iJcxX8EOcPoWcLpzUpq3qQ8,3071
|
56
|
+
PyPlumIO-0.5.7.dist-info/LICENSE,sha256=g56wJgobsehVtkJD8MhM6isAnsdOUmRPv7fIaXI4Joo,1067
|
57
|
+
PyPlumIO-0.5.7.dist-info/METADATA,sha256=YVfg3gm8Tfq0l-953p69oGyJld0ihjaaj9R1PPW8HVY,4463
|
58
|
+
PyPlumIO-0.5.7.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
|
59
|
+
PyPlumIO-0.5.7.dist-info/top_level.txt,sha256=kNBz9UPPkPD9teDn3U_sEy5LjzwLm9KfADCXtBlbw8A,9
|
60
|
+
PyPlumIO-0.5.7.dist-info/RECORD,,
|
pyplumio/__init__.py
CHANGED
@@ -3,39 +3,106 @@ from __future__ import annotations
|
|
3
3
|
|
4
4
|
from pyplumio._version import __version__, __version_tuple__
|
5
5
|
from pyplumio.connection import Connection, SerialConnection, TcpConnection
|
6
|
+
from pyplumio.protocol import AsyncProtocol, Protocol
|
6
7
|
from pyplumio.structures.network_info import EthernetParameters, WirelessParameters
|
7
8
|
|
8
9
|
|
9
10
|
def open_serial_connection(
|
10
|
-
device: str,
|
11
|
+
device: str,
|
12
|
+
baudrate: int = 115200,
|
13
|
+
*,
|
14
|
+
ethernet_parameters: EthernetParameters | None = None,
|
15
|
+
wireless_parameters: WirelessParameters | None = None,
|
16
|
+
reconnect_on_failure: bool = True,
|
17
|
+
protocol: type[Protocol] = AsyncProtocol,
|
18
|
+
**kwargs,
|
11
19
|
) -> SerialConnection:
|
12
|
-
"""Create a serial connection.
|
13
|
-
return SerialConnection(device, baudrate, **kwargs)
|
20
|
+
r"""Create a serial connection.
|
14
21
|
|
22
|
+
:param device: Serial port device name. e. g. /dev/ttyUSB0
|
23
|
+
:type device: str
|
24
|
+
:param baudrate: Serial port baud rate, defaults to 115200
|
25
|
+
:type baudrate: int, optional
|
26
|
+
:param ethernet_parameters: Ethernet parameters to send to an
|
27
|
+
ecoMAX controller
|
28
|
+
:type ethernet_parameters: EthernetParameters, optional
|
29
|
+
:param wireless_parameters: Wireless parameters to send to an
|
30
|
+
ecoMAX controller
|
31
|
+
:type wireless_parameters: WirelessParameters, optional
|
32
|
+
:param reconnect_on_failure: `True` if PyPlumIO should try
|
33
|
+
reconnecting on failure, otherwise `False`, default to `True`
|
34
|
+
:type reconnect_on_failure: bool, optional
|
35
|
+
:param protocol: Protocol that will be used for communication with
|
36
|
+
the ecoMAX controller, default to AsyncProtocol
|
37
|
+
:type protocol: Protocol, optional
|
38
|
+
:param \**kwargs: Additional keyword arguments to be passed to
|
39
|
+
serial_asyncio.open_serial_connection()
|
40
|
+
:return: An instance of serial connection
|
41
|
+
:rtype: SerialConnection
|
42
|
+
"""
|
43
|
+
return SerialConnection(
|
44
|
+
device,
|
45
|
+
baudrate,
|
46
|
+
ethernet_parameters=ethernet_parameters,
|
47
|
+
wireless_parameters=wireless_parameters,
|
48
|
+
reconnect_on_failure=reconnect_on_failure,
|
49
|
+
protocol=protocol,
|
50
|
+
**kwargs,
|
51
|
+
)
|
15
52
|
|
16
|
-
def open_tcp_connection(host: str, port: int, **kwargs) -> TcpConnection:
|
17
|
-
"""Create a TCP connection."""
|
18
|
-
return TcpConnection(host, port, **kwargs)
|
19
53
|
|
54
|
+
def open_tcp_connection(
|
55
|
+
host: str,
|
56
|
+
port: int,
|
57
|
+
*,
|
58
|
+
ethernet_parameters: EthernetParameters | None = None,
|
59
|
+
wireless_parameters: WirelessParameters | None = None,
|
60
|
+
reconnect_on_failure: bool = True,
|
61
|
+
protocol: type[Protocol] = AsyncProtocol,
|
62
|
+
**kwargs,
|
63
|
+
) -> TcpConnection:
|
64
|
+
r"""Create a TCP connection.
|
20
65
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
66
|
+
:param host: IP address or host name of the remote RS-485 server
|
67
|
+
:type host: str
|
68
|
+
:param port: Port that remote RS-485 server is listening to
|
69
|
+
:type port: int
|
70
|
+
:param ethernet_parameters: Ethernet parameters to send to an
|
71
|
+
ecoMAX controller
|
72
|
+
:type ethernet_parameters: EthernetParameters, optional
|
73
|
+
:param wireless_parameters: Wireless parameters to send to an
|
74
|
+
ecoMAX controller
|
75
|
+
:type wireless_parameters: WirelessParameters, optional
|
76
|
+
:param reconnect_on_failure: `True` if PyPlumIO should try
|
77
|
+
reconnecting on failure, otherwise `False`, default to `True`
|
78
|
+
:type reconnect_on_failure: bool, optional
|
79
|
+
:param protocol: Protocol that will be used for communication with
|
80
|
+
the ecoMAX controller, default to AsyncProtocol
|
81
|
+
:type protocol: Protocol, optional
|
82
|
+
:param \**kwargs: Additional keyword arguments to be passed to
|
83
|
+
asyncio.open_connection()
|
84
|
+
:return: An instance of TCP connection
|
85
|
+
:rtype: TcpConnection
|
86
|
+
"""
|
87
|
+
return TcpConnection(
|
88
|
+
host,
|
89
|
+
port,
|
90
|
+
ethernet_parameters=ethernet_parameters,
|
91
|
+
wireless_parameters=wireless_parameters,
|
92
|
+
reconnect_on_failure=reconnect_on_failure,
|
93
|
+
protocol=protocol,
|
94
|
+
**kwargs,
|
95
|
+
)
|
29
96
|
|
30
97
|
|
31
98
|
__all__ = [
|
32
99
|
"Connection",
|
33
100
|
"SerialConnection",
|
34
101
|
"TcpConnection",
|
102
|
+
"EthernetParameters",
|
103
|
+
"WirelessParameters",
|
35
104
|
"open_serial_connection",
|
36
105
|
"open_tcp_connection",
|
37
|
-
"ethernet_parameters",
|
38
|
-
"wireless_parameters",
|
39
106
|
"__version__",
|
40
107
|
"__version_tuple__",
|
41
108
|
]
|
pyplumio/_version.py
CHANGED
pyplumio/connection.py
CHANGED
@@ -12,7 +12,8 @@ import serial_asyncio
|
|
12
12
|
|
13
13
|
from pyplumio.exceptions import ConnectionFailedError
|
14
14
|
from pyplumio.helpers.timeout import timeout
|
15
|
-
from pyplumio.protocol import Protocol
|
15
|
+
from pyplumio.protocol import AsyncProtocol, Protocol
|
16
|
+
from pyplumio.structures.network_info import EthernetParameters, WirelessParameters
|
16
17
|
|
17
18
|
_LOGGER = logging.getLogger(__name__)
|
18
19
|
|
@@ -26,29 +27,28 @@ class Connection(ABC):
|
|
26
27
|
All specific connection classes MUST be inherited from this class.
|
27
28
|
"""
|
28
29
|
|
29
|
-
__slots__ = ("_kwargs", "_protocol", "_closing", "_reconnect_on_failure")
|
30
|
-
|
31
|
-
_kwargs: MutableMapping[str, Any]
|
32
|
-
_protocol: Protocol
|
33
30
|
_closing: bool
|
31
|
+
_protocol: Protocol
|
34
32
|
_reconnect_on_failure: bool
|
33
|
+
_kwargs: MutableMapping[str, Any]
|
35
34
|
|
36
35
|
def __init__(
|
37
36
|
self,
|
37
|
+
ethernet_parameters: EthernetParameters | None = None,
|
38
|
+
wireless_parameters: WirelessParameters | None = None,
|
38
39
|
reconnect_on_failure: bool = True,
|
39
|
-
|
40
|
-
wireless_parameters=None,
|
40
|
+
protocol: type[Protocol] = AsyncProtocol,
|
41
41
|
**kwargs,
|
42
42
|
):
|
43
43
|
"""Initialize a new connection."""
|
44
|
-
self._kwargs = kwargs
|
45
44
|
self._closing = False
|
46
|
-
self.
|
45
|
+
self._reconnect_on_failure = reconnect_on_failure
|
46
|
+
self._protocol = protocol(
|
47
47
|
self._connection_lost,
|
48
48
|
ethernet_parameters,
|
49
49
|
wireless_parameters,
|
50
50
|
)
|
51
|
-
self.
|
51
|
+
self._kwargs = kwargs
|
52
52
|
|
53
53
|
async def __aenter__(self):
|
54
54
|
"""Provide an entry point for the context manager."""
|
@@ -125,9 +125,25 @@ class TcpConnection(Connection):
|
|
125
125
|
host: str
|
126
126
|
port: int
|
127
127
|
|
128
|
-
def __init__(
|
128
|
+
def __init__(
|
129
|
+
self,
|
130
|
+
host: str,
|
131
|
+
port: int,
|
132
|
+
*,
|
133
|
+
ethernet_parameters: EthernetParameters | None = None,
|
134
|
+
wireless_parameters: WirelessParameters | None = None,
|
135
|
+
reconnect_on_failure: bool = True,
|
136
|
+
protocol: type[Protocol] = AsyncProtocol,
|
137
|
+
**kwargs,
|
138
|
+
):
|
129
139
|
"""Initialize a new TCP connection."""
|
130
|
-
super().__init__(
|
140
|
+
super().__init__(
|
141
|
+
ethernet_parameters,
|
142
|
+
wireless_parameters,
|
143
|
+
reconnect_on_failure,
|
144
|
+
protocol,
|
145
|
+
**kwargs,
|
146
|
+
)
|
131
147
|
self.host = host
|
132
148
|
self.port = port
|
133
149
|
|
@@ -153,9 +169,25 @@ class SerialConnection(Connection):
|
|
153
169
|
device: str
|
154
170
|
baudrate: int
|
155
171
|
|
156
|
-
def __init__(
|
172
|
+
def __init__(
|
173
|
+
self,
|
174
|
+
device: str,
|
175
|
+
baudrate: int = 115200,
|
176
|
+
*,
|
177
|
+
ethernet_parameters: EthernetParameters | None = None,
|
178
|
+
wireless_parameters: WirelessParameters | None = None,
|
179
|
+
reconnect_on_failure: bool = True,
|
180
|
+
protocol: type[Protocol] = AsyncProtocol,
|
181
|
+
**kwargs,
|
182
|
+
):
|
157
183
|
"""Initialize a new serial connection."""
|
158
|
-
super().__init__(
|
184
|
+
super().__init__(
|
185
|
+
ethernet_parameters,
|
186
|
+
wireless_parameters,
|
187
|
+
reconnect_on_failure,
|
188
|
+
protocol,
|
189
|
+
**kwargs,
|
190
|
+
)
|
159
191
|
self.device = device
|
160
192
|
self.baudrate = baudrate
|
161
193
|
|
pyplumio/const.py
CHANGED
@@ -133,7 +133,7 @@ class FrameType(IntEnum):
|
|
133
133
|
REQUEST_ECOMAX_CONTROL = 59
|
134
134
|
REQUEST_ALERTS = 61
|
135
135
|
REQUEST_PROGRAM_VERSION = 64
|
136
|
-
|
136
|
+
REQUEST_REGULATOR_DATA_SCHEMA = 85
|
137
137
|
REQUEST_THERMOSTAT_PARAMETERS = 92
|
138
138
|
REQUEST_SET_THERMOSTAT_PARAMETER = 93
|
139
139
|
RESPONSE_DEVICE_AVAILABLE = 176
|
@@ -147,7 +147,7 @@ class FrameType(IntEnum):
|
|
147
147
|
RESPONSE_ECOMAX_CONTROL = 187
|
148
148
|
RESPONSE_ALERTS = 189
|
149
149
|
RESPONSE_PROGRAM_VERSION = 192
|
150
|
-
|
150
|
+
RESPONSE_REGULATOR_DATA_SCHEMA = 213
|
151
151
|
RESPONSE_THERMOSTAT_PARAMETERS = 220
|
152
152
|
RESPONSE_SET_THERMOSTAT_PARAMETER = 221
|
153
153
|
MESSAGE_REGULATOR_DATA = 8
|
pyplumio/devices/__init__.py
CHANGED
@@ -42,8 +42,6 @@ def get_device_handler_and_name(device_type: int) -> tuple[str, str]:
|
|
42
42
|
class Device(ABC, EventManager):
|
43
43
|
"""Represents a device."""
|
44
44
|
|
45
|
-
__slots__ = ("queue",)
|
46
|
-
|
47
45
|
queue: asyncio.Queue
|
48
46
|
|
49
47
|
def __init__(self, queue: asyncio.Queue):
|
@@ -60,8 +58,23 @@ class Device(ABC, EventManager):
|
|
60
58
|
) -> bool:
|
61
59
|
"""Set a parameter value.
|
62
60
|
|
63
|
-
|
64
|
-
|
61
|
+
:param name: Name of the parameter
|
62
|
+
:type name: str
|
63
|
+
:param value: New value for the parameter
|
64
|
+
:type value: int | float | bool | Literal["on"] | Literal["off"]
|
65
|
+
:param timeout: Wait this amount of seconds for confirmation,
|
66
|
+
defaults to `None`
|
67
|
+
:type timeout: float, optional
|
68
|
+
:param retries: Try setting parameter for this amount of
|
69
|
+
times, defaults to 5
|
70
|
+
:type retries: int, optional
|
71
|
+
:return: `True` if parameter was successfully set, `False`
|
72
|
+
otherwise.
|
73
|
+
:rtype: bool
|
74
|
+
:raise pyplumio.exceptions.ParameterNotFoundError: when
|
75
|
+
parameter with the specified name is not found
|
76
|
+
:raise asyncio.TimeoutError: when waiting past specified timeout
|
77
|
+
:raise ValueError: when a new value is outside of allowed range
|
65
78
|
"""
|
66
79
|
parameter = await self.get(name, timeout=timeout)
|
67
80
|
if not isinstance(parameter, Parameter):
|
@@ -76,15 +89,30 @@ class Device(ABC, EventManager):
|
|
76
89
|
timeout: float | None = None,
|
77
90
|
retries: int = SET_RETRIES,
|
78
91
|
) -> None:
|
79
|
-
"""Set a parameter value without waiting for the result.
|
92
|
+
"""Set a parameter value without waiting for the result.
|
93
|
+
|
94
|
+
:param name: Name of the parameter
|
95
|
+
:type name: str
|
96
|
+
:param value: New value for the parameter
|
97
|
+
:type value: int | float | bool | Literal["on"] | Literal["off"]
|
98
|
+
:param timeout: Wait this amount of seconds for confirmation.
|
99
|
+
As this method operates in the background without waiting,
|
100
|
+
this value is used to determine failure when
|
101
|
+
retrying and doesn't block, defaults to `None`
|
102
|
+
:type timeout: float, optional
|
103
|
+
:param retries: Try setting parameter for this amount of
|
104
|
+
times, defaults to 5
|
105
|
+
:type retries: int, optional
|
106
|
+
:return: `True` if parameter was successfully set, `False`
|
107
|
+
otherwise.
|
108
|
+
:rtype: bool
|
109
|
+
"""
|
80
110
|
self.create_task(self.set(name, value, timeout, retries))
|
81
111
|
|
82
112
|
|
83
|
-
class
|
113
|
+
class AddressableDevice(Device, ABC):
|
84
114
|
"""Represents an addressable device."""
|
85
115
|
|
86
|
-
__slots__ = ("address", "_network", "_setup_frames")
|
87
|
-
|
88
116
|
address: ClassVar[int]
|
89
117
|
_network: NetworkInfo
|
90
118
|
_setup_frames: Iterable[DataFrameDescription]
|
@@ -153,12 +181,10 @@ class Addressable(Device, ABC):
|
|
153
181
|
class SubDevice(Device, ABC):
|
154
182
|
"""Represents the sub-device."""
|
155
183
|
|
156
|
-
|
157
|
-
|
158
|
-
parent: Addressable
|
184
|
+
parent: AddressableDevice
|
159
185
|
index: int
|
160
186
|
|
161
|
-
def __init__(self, queue: asyncio.Queue, parent:
|
187
|
+
def __init__(self, queue: asyncio.Queue, parent: AddressableDevice, index: int = 0):
|
162
188
|
"""Initialize a new sub-device object."""
|
163
189
|
super().__init__(queue)
|
164
190
|
self.parent = parent
|
pyplumio/devices/ecomax.py
CHANGED
@@ -16,7 +16,7 @@ from pyplumio.const import (
|
|
16
16
|
DeviceType,
|
17
17
|
FrameType,
|
18
18
|
)
|
19
|
-
from pyplumio.devices import
|
19
|
+
from pyplumio.devices import AddressableDevice
|
20
20
|
from pyplumio.devices.mixer import Mixer
|
21
21
|
from pyplumio.devices.thermostat import Thermostat
|
22
22
|
from pyplumio.filters import on_change
|
@@ -32,7 +32,6 @@ from pyplumio.helpers.parameter import ParameterValues
|
|
32
32
|
from pyplumio.helpers.schedule import Schedule, ScheduleDay
|
33
33
|
from pyplumio.helpers.typing import EventDataType
|
34
34
|
from pyplumio.structures.alerts import ATTR_ALERTS
|
35
|
-
from pyplumio.structures.data_schema import ATTR_SCHEMA
|
36
35
|
from pyplumio.structures.ecomax_parameters import (
|
37
36
|
ATTR_ECOMAX_CONTROL,
|
38
37
|
ATTR_ECOMAX_PARAMETERS,
|
@@ -49,6 +48,7 @@ from pyplumio.structures.mixer_parameters import ATTR_MIXER_PARAMETERS
|
|
49
48
|
from pyplumio.structures.mixer_sensors import ATTR_MIXER_SENSORS
|
50
49
|
from pyplumio.structures.network_info import ATTR_NETWORK, NetworkInfo
|
51
50
|
from pyplumio.structures.product_info import ATTR_PRODUCT, ProductInfo
|
51
|
+
from pyplumio.structures.regulator_data_schema import ATTR_REGDATA_SCHEMA
|
52
52
|
from pyplumio.structures.schedules import (
|
53
53
|
ATTR_SCHEDULE_PARAMETERS,
|
54
54
|
ATTR_SCHEDULES,
|
@@ -73,7 +73,7 @@ MAX_TIME_SINCE_LAST_FUEL_UPDATE_NS: Final = 300 * 1000000000
|
|
73
73
|
SETUP_FRAME_TYPES: tuple[DataFrameDescription, ...] = (
|
74
74
|
DataFrameDescription(frame_type=FrameType.REQUEST_UID, provides=ATTR_PRODUCT),
|
75
75
|
DataFrameDescription(
|
76
|
-
frame_type=FrameType.
|
76
|
+
frame_type=FrameType.REQUEST_REGULATOR_DATA_SCHEMA, provides=ATTR_REGDATA_SCHEMA
|
77
77
|
),
|
78
78
|
DataFrameDescription(
|
79
79
|
frame_type=FrameType.REQUEST_ECOMAX_PARAMETERS, provides=ATTR_ECOMAX_PARAMETERS
|
@@ -95,11 +95,9 @@ SETUP_FRAME_TYPES: tuple[DataFrameDescription, ...] = (
|
|
95
95
|
_LOGGER = logging.getLogger(__name__)
|
96
96
|
|
97
97
|
|
98
|
-
class EcoMAX(
|
98
|
+
class EcoMAX(AddressableDevice):
|
99
99
|
"""Represents an ecoMAX controller."""
|
100
100
|
|
101
|
-
__slots__ = ("_frame_versions", "_fuel_burned_timestamp_ns")
|
102
|
-
|
103
101
|
address: ClassVar[int] = DeviceType.ECOMAX
|
104
102
|
_setup_frames: Iterable[DataFrameDescription] = SETUP_FRAME_TYPES
|
105
103
|
_frame_versions: dict[int, int]
|
pyplumio/devices/ecoster.py
CHANGED
@@ -4,10 +4,10 @@ from __future__ import annotations
|
|
4
4
|
from typing import ClassVar
|
5
5
|
|
6
6
|
from pyplumio.const import DeviceType
|
7
|
-
from pyplumio.devices import
|
7
|
+
from pyplumio.devices import AddressableDevice
|
8
8
|
|
9
9
|
|
10
|
-
class EcoSTER(
|
10
|
+
class EcoSTER(AddressableDevice):
|
11
11
|
"""Represents an ecoSTER thermostat."""
|
12
12
|
|
13
13
|
address: ClassVar[int] = DeviceType.ECOSTER
|
pyplumio/devices/mixer.py
CHANGED
@@ -5,7 +5,7 @@ import asyncio
|
|
5
5
|
import logging
|
6
6
|
from typing import Sequence
|
7
7
|
|
8
|
-
from pyplumio.devices import
|
8
|
+
from pyplumio.devices import AddressableDevice, SubDevice
|
9
9
|
from pyplumio.helpers.parameter import ParameterValues
|
10
10
|
from pyplumio.helpers.typing import EventDataType
|
11
11
|
from pyplumio.structures.mixer_parameters import (
|
@@ -24,7 +24,7 @@ _LOGGER = logging.getLogger(__name__)
|
|
24
24
|
class Mixer(SubDevice):
|
25
25
|
"""Represents an mixer."""
|
26
26
|
|
27
|
-
def __init__(self, queue: asyncio.Queue, parent:
|
27
|
+
def __init__(self, queue: asyncio.Queue, parent: AddressableDevice, index: int = 0):
|
28
28
|
"""Initialize a new mixer."""
|
29
29
|
super().__init__(queue, parent, index)
|
30
30
|
self.subscribe(ATTR_MIXER_SENSORS, self._handle_sensors)
|
pyplumio/devices/thermostat.py
CHANGED
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
4
4
|
import asyncio
|
5
5
|
from typing import Sequence
|
6
6
|
|
7
|
-
from pyplumio.devices import
|
7
|
+
from pyplumio.devices import AddressableDevice, SubDevice
|
8
8
|
from pyplumio.helpers.parameter import ParameterValues
|
9
9
|
from pyplumio.helpers.typing import EventDataType
|
10
10
|
from pyplumio.structures.thermostat_parameters import (
|
@@ -20,7 +20,7 @@ from pyplumio.structures.thermostat_sensors import ATTR_THERMOSTAT_SENSORS
|
|
20
20
|
class Thermostat(SubDevice):
|
21
21
|
"""Represents a thermostat."""
|
22
22
|
|
23
|
-
def __init__(self, queue: asyncio.Queue, parent:
|
23
|
+
def __init__(self, queue: asyncio.Queue, parent: AddressableDevice, index: int = 0):
|
24
24
|
"""Initialize a new thermostat."""
|
25
25
|
super().__init__(queue, parent, index)
|
26
26
|
self.subscribe(ATTR_THERMOSTAT_SENSORS, self._handle_sensors)
|
pyplumio/filters.py
CHANGED
@@ -96,6 +96,8 @@ class _OnChange(Filter):
|
|
96
96
|
previous callback call.
|
97
97
|
"""
|
98
98
|
|
99
|
+
__slots__ = ()
|
100
|
+
|
99
101
|
async def __call__(self, new_value):
|
100
102
|
"""Set a new value for the callback."""
|
101
103
|
if self._value == UNDEFINED or _significantly_changed(self._value, new_value):
|
@@ -109,6 +111,11 @@ def on_change(callback: EventCallbackType) -> _OnChange:
|
|
109
111
|
|
110
112
|
A callback function will only be called if value is changed from the
|
111
113
|
previous call.
|
114
|
+
|
115
|
+
:param callback: A callback function to be awaited on value change
|
116
|
+
:type callback: Callable[[Any], Awaitable[Any]]
|
117
|
+
:return: A instance of callable filter
|
118
|
+
:rtype: _OnChange
|
112
119
|
"""
|
113
120
|
return _OnChange(callback)
|
114
121
|
|
@@ -125,7 +132,7 @@ class _Debounce(Filter):
|
|
125
132
|
_calls: int
|
126
133
|
_min_calls: int
|
127
134
|
|
128
|
-
def __init__(self, callback: EventCallbackType, min_calls: int
|
135
|
+
def __init__(self, callback: EventCallbackType, min_calls: int):
|
129
136
|
"""Initialize a new debounce filter."""
|
130
137
|
super().__init__(callback)
|
131
138
|
self._calls = 0
|
@@ -149,6 +156,14 @@ def debounce(callback: EventCallbackType, min_calls) -> _Debounce:
|
|
149
156
|
|
150
157
|
A callback function will only called once value is stabilized
|
151
158
|
across multiple filter calls.
|
159
|
+
|
160
|
+
:param callback: A callback function to be awaited on value change
|
161
|
+
:type callback: Callable[[Any], Awaitable[Any]]
|
162
|
+
:param min_calls: Value shouldn't change for this amount of
|
163
|
+
filter calls
|
164
|
+
:type min_calls: int
|
165
|
+
:return: A instance of callable filter
|
166
|
+
:rtype: _Debounce
|
152
167
|
"""
|
153
168
|
return _Debounce(callback, min_calls)
|
154
169
|
|
@@ -187,6 +202,15 @@ def throttle(callback: EventCallbackType, seconds: float) -> _Throttle:
|
|
187
202
|
|
188
203
|
A callback function will only be called once a certain amount of
|
189
204
|
seconds passed since the last call.
|
205
|
+
|
206
|
+
:param callback: A callback function that will be awaited once
|
207
|
+
filter conditions are fulfilled
|
208
|
+
:type callback: Callable[[Any], Awaitable[Any]]
|
209
|
+
:param seconds: A callback will be awaited at most once per
|
210
|
+
this amount of seconds
|
211
|
+
:type seconds: float
|
212
|
+
:return: A instance of callable filter
|
213
|
+
:rtype: _Throttle
|
190
214
|
"""
|
191
215
|
return _Throttle(callback, seconds)
|
192
216
|
|
@@ -197,6 +221,8 @@ class _Delta(Filter):
|
|
197
221
|
Calls a callback with a difference between two subsequent values.
|
198
222
|
"""
|
199
223
|
|
224
|
+
__slots__ = ()
|
225
|
+
|
200
226
|
async def __call__(self, new_value):
|
201
227
|
"""Set a new value for the callback."""
|
202
228
|
if self._value == UNDEFINED or _significantly_changed(self._value, new_value):
|
@@ -214,6 +240,12 @@ def delta(callback: EventCallbackType) -> _Delta:
|
|
214
240
|
|
215
241
|
A callback function will be called with a difference between two
|
216
242
|
subsequent value.
|
243
|
+
|
244
|
+
:param callback: A callback function that will be awaited with
|
245
|
+
difference between values in two subsequent calls
|
246
|
+
:type callback: Callable[[Any], Awaitable[Any]]
|
247
|
+
:return: A instance of callable filter
|
248
|
+
:rtype: _Delta
|
217
249
|
"""
|
218
250
|
return _Delta(callback)
|
219
251
|
|
@@ -259,7 +291,16 @@ def aggregate(callback: EventCallbackType, seconds: float) -> _Aggregate:
|
|
259
291
|
"""An aggregate filter.
|
260
292
|
|
261
293
|
A callback function will be called with a sum of values collected
|
262
|
-
over a specified time period.
|
294
|
+
over a specified time period. Can only be used with numeric values.
|
295
|
+
|
296
|
+
:param callback: A callback function to be awaited once filter
|
297
|
+
conditions are fulfilled
|
298
|
+
:type callback: Callable[[Any], Awaitable[Any]]
|
299
|
+
:param seconds: A callback will be awaited with a sum of values
|
300
|
+
aggregated over this amount of seconds.
|
301
|
+
:type seconds: float
|
302
|
+
:return: A instance of callable filter
|
303
|
+
:rtype: _Aggregate
|
263
304
|
"""
|
264
305
|
return _Aggregate(callback, seconds)
|
265
306
|
|
@@ -272,6 +313,10 @@ class _Custom(Filter):
|
|
272
313
|
returns true.
|
273
314
|
"""
|
274
315
|
|
316
|
+
__slots__ = ("_filter_fn",)
|
317
|
+
|
318
|
+
filter_fn: Callable[[Any], bool]
|
319
|
+
|
275
320
|
def __init__(self, callback: EventCallbackType, filter_fn: Callable[[Any], bool]):
|
276
321
|
"""Initialize a new custom filter."""
|
277
322
|
super().__init__(callback)
|
@@ -289,5 +334,14 @@ def custom(callback: EventCallbackType, filter_fn: Callable[[Any], bool]) -> _Cu
|
|
289
334
|
A callback function will be called when user-defined filter
|
290
335
|
function, that's being called with the value as an argument,
|
291
336
|
returns true.
|
337
|
+
|
338
|
+
:param callback: A callback function to be awaited when
|
339
|
+
filter function return true
|
340
|
+
:type callback: Callable[[Any], Awaitable[Any]]
|
341
|
+
:param filter_fn: Filter function, that will be called with a
|
342
|
+
value and should return `True` to await filter's callback
|
343
|
+
:type filter_fn: Callable[[Any], bool]
|
344
|
+
:return: A instance of callable filter
|
345
|
+
:rtype: _Custom
|
292
346
|
"""
|
293
347
|
return _Custom(callback, filter_fn)
|