PyPlumIO 0.5.6__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.
@@ -58,7 +58,7 @@ def _normalize_parameter_value(value: ParameterValueType) -> int:
58
58
  class ParameterValues:
59
59
  """Represents a parameter values."""
60
60
 
61
- __slots__ = "value", "min_value", "max_value"
61
+ __slots__ = ("value", "min_value", "max_value")
62
62
 
63
63
  value: int
64
64
  min_value: int
@@ -81,13 +81,13 @@ class BinaryParameterDescription(ParameterDescription):
81
81
  class Parameter(ABC):
82
82
  """Represents a parameter."""
83
83
 
84
- __slots__ = ("device", "values", "description", "_index", "_pending_update")
84
+ __slots__ = ("device", "values", "description", "_pending_update", "_index")
85
85
 
86
86
  device: Device
87
87
  values: ParameterValues
88
88
  description: ParameterDescription
89
- _index: int
90
89
  _pending_update: bool
90
+ _index: int
91
91
 
92
92
  def __init__(
93
93
  self,
@@ -98,8 +98,8 @@ class Parameter(ABC):
98
98
  ):
99
99
  """Initialize a new parameter."""
100
100
  self.device = device
101
- self.description = description
102
101
  self.values = values
102
+ self.description = description
103
103
  self._pending_update = False
104
104
  self._index = index
105
105
 
@@ -235,19 +235,29 @@ class BinaryParameter(Parameter):
235
235
  """Represents binary device parameter."""
236
236
 
237
237
  async def turn_on(self) -> bool:
238
- """Set a parameter value to 'on'."""
238
+ """Set a parameter value to 'on'.
239
+
240
+ :return: `True` if parameter was successfully turned on, `False`
241
+ otherwise.
242
+ :rtype: bool
243
+ """
239
244
  return await self.set(STATE_ON)
240
245
 
241
246
  async def turn_off(self) -> bool:
242
- """Set a parameter value to 'off'."""
247
+ """Set a parameter value to 'off'.
248
+
249
+ :return: `True` if parameter was successfully turned off, `False`
250
+ otherwise.
251
+ :rtype: bool
252
+ """
243
253
  return await self.set(STATE_OFF)
244
254
 
245
255
  def turn_on_nowait(self) -> None:
246
- """Set a parameter state to 'on' without waiting."""
256
+ """Set a parameter value to 'on' without waiting."""
247
257
  self.set_nowait(STATE_ON)
248
258
 
249
259
  def turn_off_nowait(self) -> None:
250
- """Set a parameter state to 'off' without waiting."""
260
+ """Set a parameter value to 'off' without waiting."""
251
261
  self.set_nowait(STATE_OFF)
252
262
 
253
263
  @property
@@ -8,7 +8,7 @@ import math
8
8
  from typing import Final, Literal
9
9
 
10
10
  from pyplumio.const import STATE_OFF, STATE_ON
11
- from pyplumio.devices import Addressable
11
+ from pyplumio.devices import AddressableDevice
12
12
  from pyplumio.helpers.factory import factory
13
13
  from pyplumio.structures.schedules import collect_schedule_data
14
14
 
@@ -136,7 +136,7 @@ class Schedule(Iterable):
136
136
  )
137
137
 
138
138
  name: str
139
- device: Addressable
139
+ device: AddressableDevice
140
140
  monday: ScheduleDay
141
141
  tuesday: ScheduleDay
142
142
  wednesday: ScheduleDay
@@ -7,7 +7,6 @@ from typing import Any, Literal, Protocol, Union
7
7
  ParameterValueType = Union[int, float, bool, Literal["off"], Literal["on"]]
8
8
  EventDataType = dict[Union[str, int], Any]
9
9
  EventCallbackType = Callable[[Any], Awaitable[Any]]
10
- UndefinedType = Literal["undefined"]
11
10
 
12
11
 
13
12
  class SupportsSubtraction(Protocol):
pyplumio/protocol.py CHANGED
@@ -1,13 +1,14 @@
1
1
  """Contains protocol representation."""
2
2
  from __future__ import annotations
3
3
 
4
+ from abc import ABC, abstractmethod
4
5
  import asyncio
5
6
  from collections.abc import Awaitable, Callable
6
7
  import logging
7
8
  from typing import Final, cast
8
9
 
9
10
  from pyplumio.const import ATTR_CONNECTED, DeviceType
10
- from pyplumio.devices import Addressable, get_device_handler_and_name
11
+ from pyplumio.devices import AddressableDevice, get_device_handler_and_name
11
12
  from pyplumio.exceptions import (
12
13
  FrameDataError,
13
14
  FrameError,
@@ -30,24 +31,14 @@ _LOGGER = logging.getLogger(__name__)
30
31
  CONSUMERS_NUMBER: Final = 2
31
32
 
32
33
 
33
- class Protocol(EventManager):
34
- """Represents protocol."""
35
-
36
- __slots__ = (
37
- "writer",
38
- "reader",
39
- "connected",
40
- "_network",
41
- "_queues",
42
- "_connection_lost_callback",
43
- )
34
+ class Protocol(ABC):
35
+ """Represents a protocol."""
44
36
 
45
37
  writer: FrameWriter | None
46
38
  reader: FrameReader | None
47
39
  connected: asyncio.Event
48
- _network: NetworkInfo
49
- _queues: tuple[asyncio.Queue, asyncio.Queue]
50
40
  _connection_lost_callback: Callable[[], Awaitable[None]] | None
41
+ _network: NetworkInfo
51
42
 
52
43
  def __init__(
53
44
  self,
@@ -60,60 +51,107 @@ class Protocol(EventManager):
60
51
  self.writer = None
61
52
  self.reader = None
62
53
  self.connected = asyncio.Event()
63
- read_queue: asyncio.Queue = asyncio.Queue()
64
- write_queue: asyncio.Queue = asyncio.Queue()
65
- self._queues = (read_queue, write_queue)
66
54
  self._connection_lost_callback = connection_lost_callback
67
55
  if ethernet_parameters is None:
68
- ethernet_parameters = EthernetParameters()
56
+ ethernet_parameters = EthernetParameters(status=False)
69
57
 
70
58
  if wireless_parameters is None:
71
- wireless_parameters = WirelessParameters()
59
+ wireless_parameters = WirelessParameters(status=False)
72
60
 
73
61
  self._network = NetworkInfo(eth=ethernet_parameters, wlan=wireless_parameters)
74
62
 
75
- async def frame_producer(
76
- self, read_queue: asyncio.Queue, write_queue: asyncio.Queue
77
- ) -> None:
78
- """Handle frame reads and writes."""
79
- await self.connected.wait()
80
- while self.connected.is_set():
81
- try:
82
- if write_queue.qsize() > 0:
83
- await self.writer.write(await write_queue.get())
84
- write_queue.task_done()
63
+ async def close_writer(self) -> None:
64
+ """Ensure that writer is closed."""
65
+ if not self.writer:
66
+ return
85
67
 
86
- if (response := await self.reader.read()) is not None:
87
- read_queue.put_nowait(
88
- (self.setup_device_entry(response.sender), response)
89
- )
68
+ try:
69
+ await self.writer.close()
70
+ except (OSError, asyncio.TimeoutError):
71
+ # Ignore any connection errors when closing the writer.
72
+ pass
73
+ finally:
74
+ self.writer = None
90
75
 
91
- except FrameDataError as e:
92
- _LOGGER.warning("Incorrect payload: %s", e)
93
- except ReadError as e:
94
- _LOGGER.debug("Read error: %s", e)
95
- except UnknownDeviceError as e:
96
- _LOGGER.debug("Unknown device: %s", e)
97
- except FrameError as e:
98
- _LOGGER.debug("Can't process received frame: %s", e)
99
- except (OSError, asyncio.TimeoutError):
100
- self.create_task(self.connection_lost())
101
- break
102
- except Exception as e: # pylint: disable=broad-except
103
- _LOGGER.exception(e)
76
+ @abstractmethod
77
+ def connection_established(
78
+ self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
79
+ ):
80
+ """Called when connection is established."""
104
81
 
105
- async def frame_consumer(self, read_queue: asyncio.Queue) -> None:
106
- """Handle frame processing."""
107
- await self.connected.wait()
108
- while self.connected.is_set():
109
- device, frame = cast(tuple[Addressable, Frame], await read_queue.get())
110
- device.handle_frame(frame)
111
- read_queue.task_done()
82
+ @abstractmethod
83
+ async def connection_lost(self):
84
+ """Called when connection is lost."""
85
+
86
+ @abstractmethod
87
+ async def shutdown(self):
88
+ """Shutdown the protocol."""
89
+
90
+
91
+ class DummyProtocol(Protocol):
92
+ """Represents a dummy protocol.
93
+
94
+ This protocol sets frame reader and writer as attributes, then
95
+ sets connected event and does nothing.
96
+ """
97
+
98
+ def connection_established(
99
+ self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
100
+ ):
101
+ """Called when connection is established."""
102
+ self.reader = FrameReader(reader)
103
+ self.writer = FrameWriter(writer)
104
+ self.connected.set()
105
+
106
+ async def connection_lost(self):
107
+ """Called when connection is lost."""
108
+ if self.connected.is_set():
109
+ self.connected.clear()
110
+ await self.close_writer()
111
+ if self._connection_lost_callback is not None:
112
+ await self._connection_lost_callback()
113
+
114
+ async def shutdown(self):
115
+ """Shutdown the protocol."""
116
+ if self.connected.is_set():
117
+ self.connected.clear()
118
+ await self.close_writer()
119
+
120
+
121
+ class AsyncProtocol(Protocol, EventManager):
122
+ """Represents an async protocol.
123
+
124
+ This protocol implements producer-consumers pattern using
125
+ asyncio queues.
126
+
127
+ The frame producer tries to read frames from write queue, if any
128
+ available, and sends them to the device via frame writer.
129
+
130
+ It reads stream via frame reader, creates device handler entry
131
+ and puts received frame and handler into the read queue.
132
+
133
+ Frame consumers reads handler-frame tuples from the read queue and
134
+ sends frame to respective handler for further processing.
135
+ """
136
+
137
+ _queues: tuple[asyncio.Queue, asyncio.Queue]
138
+
139
+ def __init__(
140
+ self,
141
+ connection_lost_callback: Callable[[], Awaitable[None]] | None = None,
142
+ ethernet_parameters: EthernetParameters | None = None,
143
+ wireless_parameters: WirelessParameters | None = None,
144
+ ):
145
+ """Initialize a new default protocol."""
146
+ super().__init__(
147
+ connection_lost_callback, ethernet_parameters, wireless_parameters
148
+ )
149
+ self._queues = (asyncio.Queue(), asyncio.Queue())
112
150
 
113
151
  def connection_established(
114
152
  self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter
115
153
  ):
116
- """Start consumers and producer tasks."""
154
+ """Called when connection is established."""
117
155
  self.reader = FrameReader(reader)
118
156
  self.writer = FrameWriter(writer)
119
157
  read_queue, write_queue = self.queues
@@ -128,27 +166,69 @@ class Protocol(EventManager):
128
166
  self.connected.set()
129
167
 
130
168
  async def connection_lost(self):
131
- """Shutdown consumers and call connection lost callback."""
169
+ """Called when connection is lost."""
132
170
  if self.connected.is_set():
133
171
  self.connected.clear()
134
172
  for device in self.data.values():
135
173
  # Notify devices about connection loss.
136
174
  await device.dispatch(ATTR_CONNECTED, False)
137
175
 
138
- await self._remove_writer()
176
+ await self.close_writer()
139
177
  if self._connection_lost_callback is not None:
140
178
  await self._connection_lost_callback()
141
179
 
142
180
  async def shutdown(self):
143
181
  """Shutdown protocol tasks."""
144
182
  await asyncio.gather(*[queue.join() for queue in self.queues])
145
- await super().shutdown()
183
+ await super(Protocol, self).shutdown()
146
184
  for device in self.data.values():
147
185
  await device.shutdown()
148
186
 
149
- await self._remove_writer()
187
+ if self.connected.is_set():
188
+ self.connected.clear()
189
+ await self.close_writer()
190
+
191
+ async def frame_producer(
192
+ self, read_queue: asyncio.Queue, write_queue: asyncio.Queue
193
+ ) -> None:
194
+ """Handle frame reads and writes."""
195
+ await self.connected.wait()
196
+ while self.connected.is_set():
197
+ try:
198
+ if write_queue.qsize() > 0:
199
+ await self.writer.write(await write_queue.get())
200
+ write_queue.task_done()
201
+
202
+ if (response := await self.reader.read()) is not None:
203
+ read_queue.put_nowait(
204
+ (self.setup_device_entry(response.sender), response)
205
+ )
206
+
207
+ except FrameDataError as e:
208
+ _LOGGER.warning("Incorrect payload: %s", e)
209
+ except ReadError as e:
210
+ _LOGGER.debug("Read error: %s", e)
211
+ except UnknownDeviceError as e:
212
+ _LOGGER.debug("Unknown device: %s", e)
213
+ except FrameError as e:
214
+ _LOGGER.debug("Can't process received frame: %s", e)
215
+ except (OSError, asyncio.TimeoutError):
216
+ self.create_task(self.connection_lost())
217
+ break
218
+ except Exception as e: # pylint: disable=broad-except
219
+ _LOGGER.exception(e)
220
+
221
+ async def frame_consumer(self, read_queue: asyncio.Queue) -> None:
222
+ """Handle frame processing."""
223
+ await self.connected.wait()
224
+ while self.connected.is_set():
225
+ device, frame = cast(
226
+ tuple[AddressableDevice, Frame], await read_queue.get()
227
+ )
228
+ device.handle_frame(frame)
229
+ read_queue.task_done()
150
230
 
151
- def setup_device_entry(self, device_type: DeviceType) -> Addressable:
231
+ def setup_device_entry(self, device_type: DeviceType) -> AddressableDevice:
152
232
  """Setup the device entry."""
153
233
  handler, name = get_device_handler_and_name(device_type)
154
234
  if name not in self.data:
@@ -159,23 +239,14 @@ class Protocol(EventManager):
159
239
  def _create_device_entry(self, name: str, handler: str) -> None:
160
240
  """Create the device entry."""
161
241
  write_queue = self.queues[1]
162
- device: Addressable = factory(handler, queue=write_queue, network=self._network)
242
+ device: AddressableDevice = factory(
243
+ handler, queue=write_queue, network=self._network
244
+ )
163
245
  device.dispatch_nowait(ATTR_CONNECTED, True)
164
246
  self.create_task(device.async_setup())
165
247
  self.data[name] = device
166
248
  self.set_event(name)
167
249
 
168
- async def _remove_writer(self):
169
- """Attempt to gracefully remove the frame writer."""
170
- if self.writer:
171
- try:
172
- await self.writer.close()
173
- except (OSError, asyncio.TimeoutError):
174
- # Ignore any connection errors when closing the writer.
175
- pass
176
- finally:
177
- self.writer = None
178
-
179
250
  @property
180
251
  def queues(self) -> tuple[asyncio.Queue, asyncio.Queue]:
181
252
  """Protocol queues."""
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
 
4
4
  from dataclasses import dataclass
5
5
  from datetime import datetime
6
+ from functools import lru_cache
6
7
  from typing import Any, Final, Generator
7
8
 
8
9
  from pyplumio.const import AlertType
@@ -12,28 +13,21 @@ from pyplumio.structures import StructureDecoder
12
13
  from pyplumio.utils import ensure_dict
13
14
 
14
15
  ATTR_ALERTS: Final = "alerts"
15
- ATTR_YEAR: Final = "year"
16
- ATTR_MONTH: Final = "month"
17
- ATTR_DAY: Final = "day"
18
- ATTR_HOUR: Final = "hour"
19
- ATTR_MINUTE: Final = "minute"
20
- ATTR_SECOND: Final = "second"
21
-
22
- ALERT_SIZE: Final = 9
23
16
 
24
17
 
18
+ @lru_cache(maxsize=10)
25
19
  def _convert_to_datetime(seconds: int) -> datetime:
26
20
  """Convert timestamp to a datetime object."""
27
21
 
28
22
  def _seconds_to_datetime_args(seconds: int) -> Generator[Any, None, None]:
29
- """Convert seconds to a kwarg for a datetime class."""
23
+ """Convert seconds to a kwargs for a datetime class."""
30
24
  intervals: tuple[tuple[str, int, int], ...] = (
31
- (ATTR_YEAR, 32140800, 2000), # 60sec * 60min * 24h * 31d * 12m
32
- (ATTR_MONTH, 2678400, 1), # 60sec * 60min * 24h * 31d
33
- (ATTR_DAY, 86400, 1), # 60sec * 60min * 24h
34
- (ATTR_HOUR, 3600, 0), # 60sec * 60min
35
- (ATTR_MINUTE, 60, 0),
36
- (ATTR_SECOND, 1, 0),
25
+ ("year", 32140800, 2000), # 60sec * 60min * 24h * 31d * 12m
26
+ ("month", 2678400, 1), # 60sec * 60min * 24h * 31d
27
+ ("day", 86400, 1), # 60sec * 60min * 24h
28
+ ("hour", 3600, 0), # 60sec * 60min
29
+ ("minute", 60, 0),
30
+ ("second", 1, 0),
37
31
  )
38
32
 
39
33
  for name, count, offset in intervals:
@@ -13,7 +13,7 @@ from pyplumio.const import (
13
13
  ProductType,
14
14
  UnitOfMeasurement,
15
15
  )
16
- from pyplumio.devices import Addressable
16
+ from pyplumio.devices import AddressableDevice
17
17
  from pyplumio.frames import Request
18
18
  from pyplumio.helpers.factory import factory
19
19
  from pyplumio.helpers.parameter import (
@@ -38,7 +38,9 @@ ECOMAX_PARAMETER_SIZE: Final = 3
38
38
  class EcomaxParameter(Parameter):
39
39
  """Represents an ecoMAX parameter."""
40
40
 
41
- device: Addressable
41
+ __slots__ = ()
42
+
43
+ device: AddressableDevice
42
44
  description: EcomaxParameterDescription
43
45
 
44
46
  async def set(self, value: ParameterValueType, retries: int = 5) -> bool:
@@ -22,18 +22,30 @@ NETWORK_INFO_SIZE: Final = 25
22
22
  class EthernetParameters:
23
23
  """Represents an ethernet parameters."""
24
24
 
25
+ #: IP address.
25
26
  ip: str = DEFAULT_IP
27
+
28
+ #: IP subnet mask.
26
29
  netmask: str = DEFAULT_NETMASK
30
+
31
+ #: Gateway IP address.
27
32
  gateway: str = DEFAULT_IP
28
- status: bool = False
33
+
34
+ #: Connection status. Parameters will be ignored if set to False.
35
+ status: bool = True
29
36
 
30
37
 
31
38
  @dataclass
32
39
  class WirelessParameters(EthernetParameters):
33
40
  """Represents a wireless network parameters."""
34
41
 
42
+ #: Wireless Service Set IDentifier.
35
43
  ssid: str = ""
44
+
45
+ #: Wireless encryption standard.
36
46
  encryption: EncryptionType = EncryptionType.NONE
47
+
48
+ #: Wireless signal strength in percentage.
37
49
  signal_quality: int = 100
38
50
 
39
51
 
@@ -3,13 +3,13 @@ from __future__ import annotations
3
3
 
4
4
  from typing import Final
5
5
 
6
- from pyplumio.devices import Addressable
6
+ from pyplumio.devices import AddressableDevice
7
7
  from pyplumio.helpers.data_types import BitArray, DataType
8
8
  from pyplumio.helpers.event_manager import EventManager
9
9
  from pyplumio.helpers.typing import EventDataType
10
10
  from pyplumio.structures import StructureDecoder
11
- from pyplumio.structures.data_schema import ATTR_SCHEMA
12
11
  from pyplumio.structures.frame_versions import FrameVersionsStructure
12
+ from pyplumio.structures.regulator_data_schema import ATTR_REGDATA_SCHEMA
13
13
  from pyplumio.utils import ensure_dict
14
14
 
15
15
  ATTR_REGDATA: Final = "regdata"
@@ -60,8 +60,8 @@ class RegulatorDataStructure(StructureDecoder):
60
60
  )
61
61
 
62
62
  sender = self.frame.sender
63
- if isinstance(sender, Addressable) and (
64
- schema := sender.get_nowait(ATTR_SCHEMA, [])
63
+ if isinstance(sender, AddressableDevice) and (
64
+ schema := sender.get_nowait(ATTR_REGDATA_SCHEMA, [])
65
65
  ):
66
66
  data.setdefault(ATTR_REGDATA, RegulatorData()).load(
67
67
  {
@@ -8,13 +8,11 @@ from pyplumio.helpers.typing import EventDataType
8
8
  from pyplumio.structures import StructureDecoder
9
9
  from pyplumio.utils import ensure_dict
10
10
 
11
- ATTR_SCHEMA: Final = "schema"
11
+ ATTR_REGDATA_SCHEMA: Final = "regdata_schema"
12
12
 
13
- BLOCK_SIZE: Final = 3
14
13
 
15
-
16
- class DataSchemaStructure(StructureDecoder):
17
- """Represents a data schema structure."""
14
+ class RegulatorDataSchemaStructure(StructureDecoder):
15
+ """Represents a regulator data schema structure."""
18
16
 
19
17
  _offset: int
20
18
 
@@ -39,7 +37,7 @@ class DataSchemaStructure(StructureDecoder):
39
37
  ensure_dict(
40
38
  data,
41
39
  {
42
- ATTR_SCHEMA: [
40
+ ATTR_REGDATA_SCHEMA: [
43
41
  self._unpack_block(message) for _ in range(blocks.value)
44
42
  ]
45
43
  },
@@ -8,7 +8,7 @@ from itertools import chain
8
8
  from typing import Final
9
9
 
10
10
  from pyplumio.const import ATTR_PARAMETER, ATTR_SCHEDULE, ATTR_SWITCH, ATTR_TYPE
11
- from pyplumio.devices import Addressable, Device
11
+ from pyplumio.devices import AddressableDevice, Device
12
12
  from pyplumio.exceptions import FrameDataError
13
13
  from pyplumio.frames import Request
14
14
  from pyplumio.helpers.factory import factory
@@ -80,7 +80,7 @@ class ScheduleParameter(Parameter):
80
80
 
81
81
  __slots__ = ()
82
82
 
83
- device: Addressable
83
+ device: AddressableDevice
84
84
 
85
85
  @property
86
86
  def request(self) -> Request:
@@ -47,8 +47,6 @@ TEMPERATURES: tuple[str, ...] = (
47
47
  ATTR_AIR_OUT_TEMP,
48
48
  )
49
49
 
50
- TEMPERATURE_SIZE: Final = 5
51
-
52
50
 
53
51
  class TemperaturesStructure(StructureDecoder):
54
52
  """Represents a temperatures data structure."""
@@ -11,7 +11,7 @@ from pyplumio.const import (
11
11
  ATTR_VALUE,
12
12
  UnitOfMeasurement,
13
13
  )
14
- from pyplumio.devices import Addressable
14
+ from pyplumio.devices import AddressableDevice
15
15
  from pyplumio.frames import Request
16
16
  from pyplumio.helpers.factory import factory
17
17
  from pyplumio.helpers.parameter import (
@@ -211,7 +211,7 @@ class ThermostatParametersStructure(StructureDecoder):
211
211
  """Decode bytes and return message data and offset."""
212
212
  sender = self.frame.sender
213
213
  if (
214
- isinstance(sender, Addressable)
214
+ isinstance(sender, AddressableDevice)
215
215
  and (thermostats := sender.get_nowait(ATTR_THERMOSTATS_CONNECTED, 0)) == 0
216
216
  ):
217
217
  return (
@@ -1,60 +0,0 @@
1
- pyplumio/__init__.py,sha256=91S0kRCFRiKnYUFtsX6csms5lpYmo-vBFzY79Z0JosA,1257
2
- pyplumio/__main__.py,sha256=oop76iR-XDHhMFhW4LO8-xTnBHsqzUQ5VVh__94OaqY,499
3
- pyplumio/_version.py,sha256=4DWmCh7zMgrkQTSjlCuIoYpBzO1CjYxQmpAxbcgg7vE,411
4
- pyplumio/connection.py,sha256=02aYxStTqsXwFP3oRE2cNYTBf7VfwDsHKLt6Wt8xrKc,5566
5
- pyplumio/const.py,sha256=p7UGyDwWxA6MCzZE6wt-Fz9IDI62joehMB7WH6jzGAI,3812
6
- pyplumio/exceptions.py,sha256=cpLGRl9db6cU1Z6WXhQ7yl1V7Te193QymWiOoRF8KAI,798
7
- pyplumio/filters.py,sha256=V2Xn-XGv85zPAKZnTMbWLwIeoc8siyQeonV8ZkEvnjM,8319
8
- pyplumio/protocol.py,sha256=n-haCxPCl80xvyGGyWWePeKIYgksgnIH5mvcBxpLUls,6464
9
- pyplumio/stream.py,sha256=g_lZkMWuYmFQ4mZrkP6kLxrmWpnQrLmiBIv0f-to38U,4024
10
- pyplumio/utils.py,sha256=5QF-oJar4DfaaUHqrMIfKPFJFWGBLQfgo4nYo4doJWw,651
11
- pyplumio/devices/__init__.py,sha256=kuE2Qas6ZtlKNuFbcLoN6wvsmmGDJG36Tl8qkGzjPwM,4987
12
- pyplumio/devices/ecomax.py,sha256=_XrF_kLWvnkJq4Ze0rpu-lF4Ip6AsKGW1HzoKsnxS84,16421
13
- pyplumio/devices/ecoster.py,sha256=ds1qwCqdNDa4s-GixDic2LVWyQmrkzAYpbKOnN0zjfE,300
14
- pyplumio/devices/mixer.py,sha256=e53Re55K0pftpx2UIjgRvE6hbQpZyHND28AnfkjR6M0,2950
15
- pyplumio/devices/thermostat.py,sha256=i9fctBVzpOFQIwMsNCteGrhQrdIwintqrqbwX0gwuDQ,2406
16
- pyplumio/frames/__init__.py,sha256=tMaYn0ObMHOts7zBW-cCny2Q_5AjGKgqBs5wLYSft4U,7330
17
- pyplumio/frames/messages.py,sha256=uSN2kKzYRvO6GjqwH6UJej2f2jouuOdw1bdmPCkTTH0,3608
18
- pyplumio/frames/requests.py,sha256=bV1mlcNURzdNe4ihIaArBwUUKPCO6yfa4Se-D1uy83s,6760
19
- pyplumio/frames/responses.py,sha256=o0bMPr2Rkf5S0AXfvT9qwLmQ5apLmzu9Aa3oI2MIS54,6142
20
- pyplumio/helpers/__init__.py,sha256=H2xxdkF-9uADLwEbfBUoxNTdwru3L5Z2cfJjgsuRsn0,31
21
- pyplumio/helpers/data_types.py,sha256=EOVaC39gGo4sKJGKtQxemQyQpxuiHjzlzenV3JwRSH0,7594
22
- pyplumio/helpers/event_manager.py,sha256=AguUPtH-pirAg7D7TI-SVRyNpui_z1GQsfM9LUEGsMM,4117
23
- pyplumio/helpers/factory.py,sha256=403RYqlKbWRmL-RngzL1e2p5iI5_JTzwf65HeNxHfwM,527
24
- pyplumio/helpers/parameter.py,sha256=YJwAh3qg9JU78vkk2EOoi5liwqk6ebOL48XdsRDpyU8,8292
25
- pyplumio/helpers/schedule.py,sha256=fHL0uR8X0EHHrVYug0Nqt3R4Q0OK50_EwLvSBiYhT8c,4847
26
- pyplumio/helpers/task_manager.py,sha256=fZyBmHik7rU9Kb4EECQWqVrJPUlEtEyxrEUJcf8RVYc,1072
27
- pyplumio/helpers/timeout.py,sha256=UstSwBfrLO_YhUHHvhIgunAw89s6PocR80nmU5LN4oI,915
28
- pyplumio/helpers/typing.py,sha256=EW6jKweM6R6fvXWdGjxAKp_9y9pN_R49YDtWVAFisqE,515
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=sVYJT-JqD4XPjNK-P52LFMcq6bC4gXUHodawtyuwo1w,3135
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/data_schema.py,sha256=uloA46Vip1KzBK710g7j_rrNQvfwX8qUgO7rWwSmHn0,1497
35
- pyplumio/structures/ecomax_parameters.py,sha256=f0Tpj-h9aM1wNtfK_ESAZ4Z7lyTrOa1v2X5uA92sCiY,25907
36
- pyplumio/structures/fan_power.py,sha256=cBLmns_nh8I5eDNehFM1QMe5eqlUC-QBBm1i0kTbj38,893
37
- pyplumio/structures/frame_versions.py,sha256=SkHrgehJbf14Qf_uKY8zlZp8d8WjgAq3Swwjf1Y1trw,1572
38
- pyplumio/structures/fuel_consumption.py,sha256=qxT8vtOX6VIv6MgvTalYlsN7FIBzXL3ba8VHO2b6mbs,998
39
- pyplumio/structures/fuel_level.py,sha256=XqqciKIBM8-zmG_JNc9ILff3Rgd4aCgvht7STrw9Dz0,851
40
- pyplumio/structures/lambda_sensor.py,sha256=2aEZjY8y7qV76Ky4gE4Ui7zWyXxCQRdtMingtMBKhF0,1475
41
- pyplumio/structures/mixer_parameters.py,sha256=ssYHpReiPC7kpNiCwgqMkbR8Wwkqh4wCQqhKUbLWPQg,8194
42
- pyplumio/structures/mixer_sensors.py,sha256=sKm1gIoRcCLVad-9MqOkEwu_bXIZy-3Kbi2kCd5wOkM,2243
43
- pyplumio/structures/modules.py,sha256=y1WRxKk3vW3erquzsVd4G7mDLlna8DttueeUu0jCgYw,2532
44
- pyplumio/structures/network_info.py,sha256=SHKqMHI1Gx-wNwq8UExwH29Z2e9JGZgG9hBRmkOTq-s,3746
45
- pyplumio/structures/output_flags.py,sha256=JY_IVncL_ZnIV2xXfEXLDK8x2tPulIW1PZpWVHgC-C4,1367
46
- pyplumio/structures/outputs.py,sha256=dS0dYsl9bWLuZmj5rt0Dcg1chyQhjFOIwA6KU4Yw76g,1938
47
- pyplumio/structures/pending_alerts.py,sha256=L_uaizbL_H3gYkpPBN0T0H8Q4jNo17-5-QamiF5tQO0,761
48
- pyplumio/structures/product_info.py,sha256=hkUGlbEr-OtpCHozGI_8R_uLB5cyYwe9dJ1Xz-Sqdoc,2396
49
- pyplumio/structures/program_version.py,sha256=5gOmKpbr2ftsXtJ5Hqkg__30DRYtB-CAx5sXIvCLQeM,2477
50
- pyplumio/structures/regulator_data.py,sha256=dleGP3ODIc8vJupXdF-PRiU8MYlzJOFINWoqdtrZCcI,2506
51
- pyplumio/structures/schedules.py,sha256=rq58Du45O8PaevwpjHjB_e9pz8UL_MatQwKCFP-HmHs,6113
52
- pyplumio/structures/statuses.py,sha256=IWcQ1TNfvhVitF_vxTeF3c4txpU54NOyi4l445SJVk8,1188
53
- pyplumio/structures/temperatures.py,sha256=4Q3tfYNCFi4VhTlkCO9eF3GLgsfJD21cdefRri3PkIk,2388
54
- pyplumio/structures/thermostat_parameters.py,sha256=UjywzP8PeaRSk7XK608_dotDTrqOn5l4mvnUDkP9Kd8,7679
55
- pyplumio/structures/thermostat_sensors.py,sha256=sEH42iq05Wijfhfrv9o4iJcxX8EOcPoWcLpzUpq3qQ8,3071
56
- PyPlumIO-0.5.6.dist-info/LICENSE,sha256=g56wJgobsehVtkJD8MhM6isAnsdOUmRPv7fIaXI4Joo,1067
57
- PyPlumIO-0.5.6.dist-info/METADATA,sha256=pP9_WJyvn2Nmp_eNY3oX4USs_nMZqnkuNu1MFx5t9uQ,4848
58
- PyPlumIO-0.5.6.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
59
- PyPlumIO-0.5.6.dist-info/top_level.txt,sha256=kNBz9UPPkPD9teDn3U_sEy5LjzwLm9KfADCXtBlbw8A,9
60
- PyPlumIO-0.5.6.dist-info/RECORD,,