ramses-rf 0.52.3__py3-none-any.whl → 0.52.5__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.
- ramses_cli/client.py +1 -0
- ramses_cli/debug.py +1 -1
- ramses_cli/utils/convert.py +2 -2
- ramses_rf/database.py +47 -20
- ramses_rf/device/base.py +14 -3
- ramses_rf/device/heat.py +2 -2
- ramses_rf/device/hvac.py +24 -21
- ramses_rf/entity_base.py +70 -29
- ramses_rf/gateway.py +30 -19
- ramses_rf/schemas.py +1 -1
- ramses_rf/system/heat.py +1 -1
- ramses_rf/system/zones.py +22 -2
- ramses_rf/version.py +1 -1
- {ramses_rf-0.52.3.dist-info → ramses_rf-0.52.5.dist-info}/METADATA +2 -1
- ramses_rf-0.52.5.dist-info/RECORD +55 -0
- {ramses_rf-0.52.3.dist-info → ramses_rf-0.52.5.dist-info}/WHEEL +1 -1
- ramses_tx/address.py +21 -6
- ramses_tx/command.py +18 -2
- ramses_tx/const.py +1 -1
- ramses_tx/helpers.py +30 -10
- ramses_tx/message.py +11 -5
- ramses_tx/packet.py +13 -5
- ramses_tx/parsers.py +1096 -28
- ramses_tx/protocol.py +95 -20
- ramses_tx/ramses.py +9 -5
- ramses_tx/transport.py +31 -6
- ramses_tx/version.py +1 -1
- ramses_rf-0.52.3.dist-info/RECORD +0 -55
- {ramses_rf-0.52.3.dist-info → ramses_rf-0.52.5.dist-info}/entry_points.txt +0 -0
- {ramses_rf-0.52.3.dist-info → ramses_rf-0.52.5.dist-info}/licenses/LICENSE +0 -0
ramses_tx/protocol.py
CHANGED
|
@@ -66,7 +66,7 @@ class _BaseProtocol(asyncio.Protocol):
|
|
|
66
66
|
self._msg_handler = msg_handler
|
|
67
67
|
self._msg_handlers: list[MsgHandlerT] = []
|
|
68
68
|
|
|
69
|
-
self._transport: RamsesTransportT
|
|
69
|
+
self._transport: RamsesTransportT | None = None
|
|
70
70
|
self._loop = asyncio.get_running_loop()
|
|
71
71
|
|
|
72
72
|
self._pause_writing = False # FIXME: Start in R/O mode as no connection yet?
|
|
@@ -80,6 +80,9 @@ class _BaseProtocol(asyncio.Protocol):
|
|
|
80
80
|
|
|
81
81
|
self._is_evofw3: bool | None = None
|
|
82
82
|
|
|
83
|
+
self._active_hgi: DeviceIdT | None = None
|
|
84
|
+
self._context: ProtocolContext | None = None
|
|
85
|
+
|
|
83
86
|
@property
|
|
84
87
|
def hgi_id(self) -> DeviceIdT:
|
|
85
88
|
return HGI_DEV_ADDR.id
|
|
@@ -140,7 +143,16 @@ class _BaseProtocol(asyncio.Protocol):
|
|
|
140
143
|
received or the connection was aborted or closed).
|
|
141
144
|
"""
|
|
142
145
|
|
|
143
|
-
|
|
146
|
+
# FIX: Check if _wait_connection_lost exists before asserting
|
|
147
|
+
# This handles cases where connection was never fully established (e.g. timeout)
|
|
148
|
+
if not self._wait_connection_lost:
|
|
149
|
+
_LOGGER.debug(
|
|
150
|
+
"connection_lost called but no connection was established (ignoring)"
|
|
151
|
+
)
|
|
152
|
+
# Reset the connection made future for next attempt
|
|
153
|
+
if self._wait_connection_made.done():
|
|
154
|
+
self._wait_connection_made = self._loop.create_future()
|
|
155
|
+
return
|
|
144
156
|
|
|
145
157
|
if self._wait_connection_lost.done(): # BUG: why is callback invoked twice?
|
|
146
158
|
return
|
|
@@ -199,6 +211,10 @@ class _BaseProtocol(asyncio.Protocol):
|
|
|
199
211
|
|
|
200
212
|
self._pause_writing = False
|
|
201
213
|
|
|
214
|
+
async def _send_impersonation_alert(self, cmd: Command) -> None:
|
|
215
|
+
"""Allow the Protocol to send an impersonation alert (stub)."""
|
|
216
|
+
return
|
|
217
|
+
|
|
202
218
|
async def send_cmd(
|
|
203
219
|
self,
|
|
204
220
|
cmd: Command,
|
|
@@ -207,22 +223,64 @@ class _BaseProtocol(asyncio.Protocol):
|
|
|
207
223
|
gap_duration: float = DEFAULT_GAP_DURATION,
|
|
208
224
|
num_repeats: int = DEFAULT_NUM_REPEATS,
|
|
209
225
|
priority: Priority = Priority.DEFAULT,
|
|
210
|
-
qos: QosParams = DEFAULT_QOS,
|
|
226
|
+
qos: QosParams = DEFAULT_QOS, # max_retries, timeout, wait_for_reply
|
|
211
227
|
) -> Packet:
|
|
212
|
-
"""
|
|
228
|
+
"""Send a Command with Qos (with retries, until success or ProtocolError).
|
|
213
229
|
|
|
214
|
-
|
|
215
|
-
|
|
230
|
+
Returns the Command's response Packet or the Command echo if a response is not
|
|
231
|
+
expected (e.g. sending an RP).
|
|
216
232
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
_LOGGER.debug(f"QUEUED: {cmd}")
|
|
233
|
+
If wait_for_reply is True, return the RQ's RP (or W's I), or raise an exception
|
|
234
|
+
if one doesn't arrive. If it is False, return the echo of the Command only. If
|
|
235
|
+
it is None (the default), act as True for RQs, and False for all other Commands.
|
|
221
236
|
|
|
222
|
-
|
|
223
|
-
|
|
237
|
+
num_repeats is # of times to send the Command, in addition to the fist transmit,
|
|
238
|
+
with gap_duration seconds between each transmission. If wait_for_reply is True,
|
|
239
|
+
then num_repeats is ignored.
|
|
240
|
+
|
|
241
|
+
Commands are queued and sent FIFO, except higher-priority Commands are always
|
|
242
|
+
sent first.
|
|
243
|
+
|
|
244
|
+
Will raise:
|
|
245
|
+
ProtocolSendFailed: tried to Tx Command, but didn't get echo/reply
|
|
246
|
+
ProtocolError: didn't attempt to Tx Command for some reason
|
|
247
|
+
"""
|
|
224
248
|
|
|
225
|
-
|
|
249
|
+
assert gap_duration == DEFAULT_GAP_DURATION
|
|
250
|
+
assert 0 <= num_repeats <= 3 # if QoS, only Tx x1, with no repeats
|
|
251
|
+
|
|
252
|
+
# FIX: Patch command with actual HGI ID if it uses the default placeholder
|
|
253
|
+
# NOTE: HGI80s (TI 3410) require the default ID (18:000730), or they will silent-fail
|
|
254
|
+
if (
|
|
255
|
+
self._active_hgi
|
|
256
|
+
and self._is_evofw3 # Only patch if using evofw3 (not HGI80)
|
|
257
|
+
and cmd._addrs[0].id == HGI_DEV_ADDR.id
|
|
258
|
+
and self._active_hgi != HGI_DEV_ADDR.id
|
|
259
|
+
):
|
|
260
|
+
# The command uses the default 18:000730, but we know the real ID.
|
|
261
|
+
# Reconstruct the command string with the correct address.
|
|
262
|
+
|
|
263
|
+
# Get current addresses as strings
|
|
264
|
+
new_addrs = [a.id for a in cmd._addrs]
|
|
265
|
+
|
|
266
|
+
# ONLY patch the Source Address (Index 0).
|
|
267
|
+
# Leave Dest (Index 1/2) alone to avoid breaking tests that expect 18:000730 there.
|
|
268
|
+
new_addrs[0] = self._active_hgi
|
|
269
|
+
|
|
270
|
+
new_frame = f"{cmd.verb} {cmd.seqn} {new_addrs[0]} {new_addrs[1]} {new_addrs[2]} {cmd.code} {int(cmd.len_):03d} {cmd.payload}"
|
|
271
|
+
cmd = Command(new_frame)
|
|
272
|
+
|
|
273
|
+
if qos and not self._context:
|
|
274
|
+
_LOGGER.warning(f"{cmd} < QoS is currently disabled by this Protocol")
|
|
275
|
+
|
|
276
|
+
if cmd.src.id != self.hgi_id: # Was HGI_DEV_ADDR.id
|
|
277
|
+
await self._send_impersonation_alert(cmd)
|
|
278
|
+
|
|
279
|
+
if qos.wait_for_reply and num_repeats:
|
|
280
|
+
_LOGGER.warning(f"{cmd} < num_repeats set to 0, as wait_for_reply is True")
|
|
281
|
+
num_repeats = 0 # the lesser crime over wait_for_reply=False
|
|
282
|
+
|
|
283
|
+
pkt = await self._send_cmd( # may: raise ProtocolError/ProtocolSendFailed
|
|
226
284
|
cmd,
|
|
227
285
|
gap_duration=gap_duration,
|
|
228
286
|
num_repeats=num_repeats,
|
|
@@ -230,6 +288,11 @@ class _BaseProtocol(asyncio.Protocol):
|
|
|
230
288
|
qos=qos,
|
|
231
289
|
)
|
|
232
290
|
|
|
291
|
+
if not pkt: # HACK: temporary workaround for returning None
|
|
292
|
+
raise exc.ProtocolSendFailed(f"Failed to send command: {cmd} (REPORT THIS)")
|
|
293
|
+
|
|
294
|
+
return pkt
|
|
295
|
+
|
|
233
296
|
async def _send_cmd(
|
|
234
297
|
self,
|
|
235
298
|
cmd: Command,
|
|
@@ -249,6 +312,10 @@ class _BaseProtocol(asyncio.Protocol):
|
|
|
249
312
|
self, frame: str, num_repeats: int = 0, gap_duration: float = 0.0
|
|
250
313
|
) -> None: # _send_frame() -> transport
|
|
251
314
|
"""Write to the transport."""
|
|
315
|
+
|
|
316
|
+
if self._transport is None:
|
|
317
|
+
raise exc.ProtocolSendFailed("Transport is not connected")
|
|
318
|
+
|
|
252
319
|
await self._transport.write_frame(frame)
|
|
253
320
|
for _ in range(num_repeats - 1):
|
|
254
321
|
await asyncio.sleep(gap_duration)
|
|
@@ -555,8 +622,17 @@ class PortProtocol(_DeviceIdFilterMixin, _BaseProtocol):
|
|
|
555
622
|
super().connection_made(transport)
|
|
556
623
|
# TODO: needed? self.resume_writing()
|
|
557
624
|
|
|
558
|
-
self.
|
|
559
|
-
self.
|
|
625
|
+
# ROBUSTNESS FIX: Ensure self._transport is set even if the wait future was cancelled
|
|
626
|
+
if self._transport is None:
|
|
627
|
+
_LOGGER.warning(
|
|
628
|
+
f"{self}: Transport bound after wait cancelled (late connection)"
|
|
629
|
+
)
|
|
630
|
+
self._transport = transport
|
|
631
|
+
|
|
632
|
+
# Safe access with check (optional but recommended)
|
|
633
|
+
if self._transport:
|
|
634
|
+
self._set_active_hgi(self._transport.get_extra_info(SZ_ACTIVE_HGI))
|
|
635
|
+
self._is_evofw3 = self._transport.get_extra_info(SZ_IS_EVOFW3)
|
|
560
636
|
|
|
561
637
|
if not self._context:
|
|
562
638
|
return
|
|
@@ -583,7 +659,7 @@ class PortProtocol(_DeviceIdFilterMixin, _BaseProtocol):
|
|
|
583
659
|
self._context.pause_writing()
|
|
584
660
|
|
|
585
661
|
def resume_writing(self) -> None:
|
|
586
|
-
"""Inform the FSM that the Protocol has been
|
|
662
|
+
"""Inform the FSM that the Protocol has been resumed."""
|
|
587
663
|
|
|
588
664
|
super().resume_writing()
|
|
589
665
|
if self._context:
|
|
@@ -597,7 +673,7 @@ class PortProtocol(_DeviceIdFilterMixin, _BaseProtocol):
|
|
|
597
673
|
self._context.pkt_received(pkt)
|
|
598
674
|
|
|
599
675
|
async def _send_impersonation_alert(self, cmd: Command) -> None:
|
|
600
|
-
"""Send
|
|
676
|
+
"""Send a puzzle packet warning that impersonation is occurring."""
|
|
601
677
|
|
|
602
678
|
if _DBG_DISABLE_IMPERSONATION_ALERTS:
|
|
603
679
|
return
|
|
@@ -656,6 +732,8 @@ class PortProtocol(_DeviceIdFilterMixin, _BaseProtocol):
|
|
|
656
732
|
# f"{self}: Failed to send {cmd._hdr}: excluded by list"
|
|
657
733
|
# )
|
|
658
734
|
|
|
735
|
+
assert self._context
|
|
736
|
+
|
|
659
737
|
try:
|
|
660
738
|
return await self._context.send_cmd(send_cmd, cmd, priority, qos)
|
|
661
739
|
# except InvalidStateError as err: # TODO: handle InvalidStateError separately
|
|
@@ -701,9 +779,6 @@ class PortProtocol(_DeviceIdFilterMixin, _BaseProtocol):
|
|
|
701
779
|
if qos and not self._context:
|
|
702
780
|
_LOGGER.warning(f"{cmd} < QoS is currently disabled by this Protocol")
|
|
703
781
|
|
|
704
|
-
if cmd.src.id != HGI_DEV_ADDR.id: # or actual HGI addr
|
|
705
|
-
await self._send_impersonation_alert(cmd)
|
|
706
|
-
|
|
707
782
|
if qos.wait_for_reply and num_repeats:
|
|
708
783
|
_LOGGER.warning(f"{cmd} < num_repeats set to 0, as wait_for_reply is True")
|
|
709
784
|
num_repeats = 0 # the lesser crime over wait_for_reply=False
|
ramses_tx/ramses.py
CHANGED
|
@@ -377,10 +377,10 @@ CODES_SCHEMA: dict[Code, dict[str, Any]] = { # rf_unknown
|
|
|
377
377
|
I_: r"^(0[0-9A-F][0-9A-F]{8}0[12]){1,4}(0[12]03)?$", # (0[12]03)? only if len(array) == 1
|
|
378
378
|
W_: r"^(0[0-9A-F][0-9A-F]{8}0[12])$", # never an array
|
|
379
379
|
},
|
|
380
|
-
Code._22D0: { # unknown_22d0, HVAC system switch?
|
|
380
|
+
Code._22D0: { # unknown_22d0, Spider thermostat, HVAC system switch?
|
|
381
381
|
SZ_NAME: "message_22d0",
|
|
382
|
-
I_: r"^(00|03)",
|
|
383
|
-
W_: r"^03",
|
|
382
|
+
I_: r"^(00|03)[0-9]{6}$",
|
|
383
|
+
W_: r"^03[0-9]{4}1E14030020$",
|
|
384
384
|
},
|
|
385
385
|
Code._22D9: { # boiler_setpoint
|
|
386
386
|
SZ_NAME: "boiler_setpoint",
|
|
@@ -843,6 +843,7 @@ _DEV_KLASSES_HEAT: dict[str, dict[Code, dict[VerbT, Any]]] = {
|
|
|
843
843
|
Code._000C: {I_: {}},
|
|
844
844
|
Code._000E: {I_: {}},
|
|
845
845
|
Code._0016: {RQ: {}},
|
|
846
|
+
Code._01FF: {I_: {}, RQ: {}},
|
|
846
847
|
Code._042F: {I_: {}},
|
|
847
848
|
Code._1030: {I_: {}},
|
|
848
849
|
Code._1060: {I_: {}},
|
|
@@ -853,9 +854,11 @@ _DEV_KLASSES_HEAT: dict[str, dict[Code, dict[VerbT, Any]]] = {
|
|
|
853
854
|
Code._1F09: {I_: {}},
|
|
854
855
|
Code._1FC9: {I_: {}},
|
|
855
856
|
Code._22C9: {W_: {}}, # DT4R
|
|
857
|
+
Code._22D0: {W_: {}}, # Spider master THM
|
|
856
858
|
Code._2309: {I_: {}, RQ: {}, W_: {}},
|
|
857
859
|
Code._2349: {RQ: {}, W_: {}},
|
|
858
860
|
Code._30C9: {I_: {}},
|
|
861
|
+
Code._3110: {I_: {}}, # Spider THM
|
|
859
862
|
Code._3120: {I_: {}},
|
|
860
863
|
Code._313F: {
|
|
861
864
|
I_: {}
|
|
@@ -880,6 +883,7 @@ _DEV_KLASSES_HEAT: dict[str, dict[Code, dict[VerbT, Any]]] = {
|
|
|
880
883
|
Code._3110: {I_: {}}, # Spider Autotemp
|
|
881
884
|
Code._3150: {I_: {}},
|
|
882
885
|
Code._4E01: {I_: {}}, # Spider Autotemp Zone controller
|
|
886
|
+
Code._4E04: {I_: {}}, # idem
|
|
883
887
|
},
|
|
884
888
|
DevType.TRV: { # e.g. HR92/HR91: Radiator Controller
|
|
885
889
|
Code._0001: {W_: {r"^0[0-9A-F]"}},
|
|
@@ -1178,9 +1182,9 @@ _CODE_ONLY_FROM_CTL: tuple[Code, ...] = tuple(
|
|
|
1178
1182
|
CODES_ONLY_FROM_CTL: tuple[Code, ...] = (
|
|
1179
1183
|
Code._1030,
|
|
1180
1184
|
Code._1F09,
|
|
1181
|
-
Code._22D0,
|
|
1185
|
+
# Code._22D0, # also _W from the Spider master THM! issue #340
|
|
1182
1186
|
Code._313F,
|
|
1183
|
-
) # I packets, TODO: 31Dx too?
|
|
1187
|
+
) # I packets, TODO: 31Dx too? not 31D9/31DA!
|
|
1184
1188
|
|
|
1185
1189
|
#
|
|
1186
1190
|
########################################################################################
|
ramses_tx/transport.py
CHANGED
|
@@ -103,7 +103,7 @@ if TYPE_CHECKING:
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
_DEFAULT_TIMEOUT_PORT: Final[float] = 3
|
|
106
|
-
_DEFAULT_TIMEOUT_MQTT: Final[float] =
|
|
106
|
+
_DEFAULT_TIMEOUT_MQTT: Final[float] = 60 # Updated from 9s to 60s for robustness
|
|
107
107
|
|
|
108
108
|
_SIGNATURE_GAP_SECS = 0.05
|
|
109
109
|
_SIGNATURE_MAX_TRYS = 40 # was: 24
|
|
@@ -1368,7 +1368,13 @@ class MqttTransport(_FullTransport, _MqttTransportAbstractor):
|
|
|
1368
1368
|
)
|
|
1369
1369
|
# FIXME: convert all dt early, and convert to aware, i.e. dt.now().astimezone()
|
|
1370
1370
|
|
|
1371
|
-
|
|
1371
|
+
try:
|
|
1372
|
+
self._frame_read(dtm.isoformat(), _normalise(payload["msg"]))
|
|
1373
|
+
except exc.TransportError:
|
|
1374
|
+
# If the transport is closing, we expect this error and can safely ignore it
|
|
1375
|
+
# prevents "Uncaught thread exception" in paho.mqtt client
|
|
1376
|
+
if not self._closing:
|
|
1377
|
+
raise
|
|
1372
1378
|
|
|
1373
1379
|
async def write_frame(self, frame: str, disable_tx_limits: bool = False) -> None:
|
|
1374
1380
|
"""Transmit a frame via the underlying handler (e.g. serial port, MQTT).
|
|
@@ -1572,13 +1578,31 @@ async def transport_factory(
|
|
|
1572
1578
|
assert port_config is not None # mypy check
|
|
1573
1579
|
|
|
1574
1580
|
# MQTT
|
|
1575
|
-
if port_name[:4] == "mqtt":
|
|
1581
|
+
if port_name[:4] == "mqtt":
|
|
1582
|
+
# Check for custom timeout in kwargs, fallback to constant
|
|
1583
|
+
mqtt_timeout = kwargs.get("timeout", _DEFAULT_TIMEOUT_MQTT)
|
|
1584
|
+
|
|
1576
1585
|
transport = MqttTransport(
|
|
1577
|
-
port_name,
|
|
1586
|
+
port_name,
|
|
1587
|
+
protocol,
|
|
1588
|
+
disable_sending=bool(
|
|
1589
|
+
disable_sending
|
|
1590
|
+
), # Feature Added: handled disable_sending
|
|
1591
|
+
extra=extra,
|
|
1592
|
+
loop=loop,
|
|
1593
|
+
log_all=log_all,
|
|
1594
|
+
**kwargs,
|
|
1578
1595
|
)
|
|
1579
1596
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1597
|
+
try:
|
|
1598
|
+
# Robustness Fix: Wait with timeout, handle failure gracefully
|
|
1599
|
+
await protocol.wait_for_connection_made(timeout=mqtt_timeout)
|
|
1600
|
+
except Exception:
|
|
1601
|
+
# CRITICAL FIX: Close the transport if setup fails to prevent "Zombie" callbacks
|
|
1602
|
+
# This prevents the "AttributeError: 'NoneType'..." crash later on
|
|
1603
|
+
transport.close()
|
|
1604
|
+
raise
|
|
1605
|
+
|
|
1582
1606
|
return transport
|
|
1583
1607
|
|
|
1584
1608
|
# Serial
|
|
@@ -1598,4 +1622,5 @@ async def transport_factory(
|
|
|
1598
1622
|
|
|
1599
1623
|
# TODO: remove this? better to invoke timeout after factory returns?
|
|
1600
1624
|
await protocol.wait_for_connection_made(timeout=_DEFAULT_TIMEOUT_PORT)
|
|
1625
|
+
# pytest-cov times out in virtual_rf.py when set below 30.0 on GitHub Actions
|
|
1601
1626
|
return transport
|
ramses_tx/version.py
CHANGED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
ramses_cli/__init__.py,sha256=uvGzWqOf4avvgzxJNSLFWEelIWqSZ-AeLAZzg5x58bc,397
|
|
2
|
-
ramses_cli/client.py,sha256=NTLhHhTiYmp7nyE3vqIFR9zjwfot1wdIT7QuMUgubD0,20350
|
|
3
|
-
ramses_cli/debug.py,sha256=vgR0lOHoYjWarN948dI617WZZGNuqHbeq6Tc16Da7b4,608
|
|
4
|
-
ramses_cli/discovery.py,sha256=MWoahBnAAVzfK2S7EDLsY2WYqN_ZK9L-lktrj8_4cb0,12978
|
|
5
|
-
ramses_cli/utils/cat_slow.py,sha256=AhUpM5gnegCitNKU-JGHn-DrRzSi-49ZR1Qw6lxe_t8,607
|
|
6
|
-
ramses_cli/utils/convert.py,sha256=D_YiCyX5na9pgC-_NhBlW9N1dgRKUK-uLtLBfofjzZM,1804
|
|
7
|
-
ramses_rf/__init__.py,sha256=vp2TyFGqc1fGQHsevhmaw0QEmSSCnZx7fqizKiEwHtw,1245
|
|
8
|
-
ramses_rf/binding_fsm.py,sha256=fuqvcc9YW-wr8SPH8zadpPqrHAvzl_eeWF-IBtlLppY,26632
|
|
9
|
-
ramses_rf/const.py,sha256=L3z31CZ-xqno6oZp_h-67CB_5tDDqTwSWXsqRtsjMcs,5460
|
|
10
|
-
ramses_rf/database.py,sha256=rMfEJvapc3Zh6SFqjPjntuuaYynuZiAdYTw1pcXsJPE,20344
|
|
11
|
-
ramses_rf/dispatcher.py,sha256=YjEU-QrBLo9IfoEhJo2ikg_FxOaMYoWvzelr9Vi-JZ8,11398
|
|
12
|
-
ramses_rf/entity_base.py,sha256=uN8QGGv0cutCYTT-oyDkvzv4xpBEObWF4jo9ArtvbLE,57640
|
|
13
|
-
ramses_rf/exceptions.py,sha256=mt_T7irqHSDKir6KLaf6oDglUIdrw0S40JbOrWJk5jc,3657
|
|
14
|
-
ramses_rf/gateway.py,sha256=VYZqMppU_kDYhT3EUmqpHf0LuLXPMH7ASx9jylAopWE,21218
|
|
15
|
-
ramses_rf/helpers.py,sha256=TNk_QkpIOB3alOp1sqnA9LOzi4fuDCeapNlW3zTzNas,4250
|
|
16
|
-
ramses_rf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
ramses_rf/schemas.py,sha256=jvpx0hZiMhaogyxbxnrbxS7m2GekKK4DFfVTNs7h-WQ,13476
|
|
18
|
-
ramses_rf/version.py,sha256=E3rkZTOmbKgrnpOmzEdweYaquvnmc18LcdiXXdsnakM,125
|
|
19
|
-
ramses_rf/device/__init__.py,sha256=sUbH5dhbYFXSoM_TPFRutpRutBRpup7_cQ9smPtDTy8,4858
|
|
20
|
-
ramses_rf/device/base.py,sha256=Yx0LZwMEb49naY8FolZ8HEBFb6XCPQBTVN_TWyO2nKg,17777
|
|
21
|
-
ramses_rf/device/heat.py,sha256=tjORTbjxBKFzgPQbfpLuj74IlrVtOmj9GpzslK9j0A0,54569
|
|
22
|
-
ramses_rf/device/hvac.py,sha256=gdpVACUvtq6ERMI0mwRhqIJtKYEmybzJA2NeyN1ELrs,48431
|
|
23
|
-
ramses_rf/system/__init__.py,sha256=uZLKio3gLlBzePa2aDQ1nxkcp1YXOGrn6iHTG8LiNIw,711
|
|
24
|
-
ramses_rf/system/faultlog.py,sha256=GdGmVGT3137KsTlV_nhccgIFEmYu6DFsLTn4S-8JSok,12799
|
|
25
|
-
ramses_rf/system/heat.py,sha256=3jaFEChU-HlWCRMY1y7u09s7AH4hT0pC63hnqwdmZOc,39223
|
|
26
|
-
ramses_rf/system/schedule.py,sha256=Ts6tdZPTQLV5NkgwA73tPa5QUsnZNIIuYoKC-8VsXDk,18808
|
|
27
|
-
ramses_rf/system/zones.py,sha256=YN_HAbeaa2YioUOjafEpp-0IHmIwmKNSK_77pPcjtns,36072
|
|
28
|
-
ramses_tx/__init__.py,sha256=sqnjM7pUGJDmec6igTtKViSB8FLX49B5gwhAmcY9ERY,3596
|
|
29
|
-
ramses_tx/address.py,sha256=F5ZE-EbPNNom1fW9XXUILvD7DYSMBxNJvsHVliT5gjw,8452
|
|
30
|
-
ramses_tx/command.py,sha256=tqVECwd_QokEcRv2MSk7TUU4JSBzCZcJh1eQ0jIGgoY,125122
|
|
31
|
-
ramses_tx/const.py,sha256=pnxq5upXvLUizv9Ye_I1llD9rAa3wddHgsSkc91AIUc,30300
|
|
32
|
-
ramses_tx/exceptions.py,sha256=FJSU9YkvpKjs3yeTqUJX1o3TPFSe_B01gRGIh9b3PNc,2632
|
|
33
|
-
ramses_tx/fingerprints.py,sha256=nfftA1E62HQnb-eLt2EqjEi_la0DAoT0wt-PtTMie0s,11974
|
|
34
|
-
ramses_tx/frame.py,sha256=GzNsXr15YLeidJYGtk_xPqsZQh4ehDDlUCtT6rTDhT8,22046
|
|
35
|
-
ramses_tx/gateway.py,sha256=Fl6EqAUU2DnLOiA2_87sS7VPBEyxA1a_ICCmg55WMMA,11494
|
|
36
|
-
ramses_tx/helpers.py,sha256=J4OCRckp3JshGQTvvqEskFjB1hPS7uA_opVsuIqmZds,32915
|
|
37
|
-
ramses_tx/logger.py,sha256=1iKRHKUaqHqGd76CkE_6mCVR0sYODtxshRRwfY61fTk,10426
|
|
38
|
-
ramses_tx/message.py,sha256=-moQ8v3HVlNSl-x3U0DDfDcj8WQ7vLqclMNxsohbmnw,13449
|
|
39
|
-
ramses_tx/opentherm.py,sha256=58PXz9l5x8Ou6Fm3y-R_UnGHCYahoi2RKIDdYStUMzk,42378
|
|
40
|
-
ramses_tx/packet.py,sha256=_qHiPFWpQpKueZOgf1jJ93Y09iZjo3LZWStLglVkXg4,7370
|
|
41
|
-
ramses_tx/parsers.py,sha256=Z0ochrNyO2l8SAP0oJxN-tx2nJluEaNP_uJCIm_lsA8,111279
|
|
42
|
-
ramses_tx/protocol.py,sha256=9R3aCzuiWEyXmugmB5tyR_RhBlgfnpUXj7AsMP9BzzU,28867
|
|
43
|
-
ramses_tx/protocol_fsm.py,sha256=ZKtehCr_4TaDdfdlfidFLJaOVTYtaEq5h4tLqNIhb9s,26827
|
|
44
|
-
ramses_tx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
-
ramses_tx/ramses.py,sha256=NG81GBNZlap-Gi9ac-r6OFE-KaHvXsgPDWy-I2Irr-4,53698
|
|
46
|
-
ramses_tx/schemas.py,sha256=bqKW_V0bR6VbBD8ZQiBExNtVdXs0fryVKe3GEhupgIo,13424
|
|
47
|
-
ramses_tx/transport.py,sha256=oVClKNnCfHioIk5tF0TGmYspJhpggQ6EspOYeyBBR4g,58841
|
|
48
|
-
ramses_tx/typed_dicts.py,sha256=w-0V5t2Q3GiNUOrRAWiW9GtSwbta_7luME6GfIb1zhI,10869
|
|
49
|
-
ramses_tx/typing.py,sha256=eF2SlPWhNhEFQj6WX2AhTXiyRQVXYnFutiepllYl2rI,5042
|
|
50
|
-
ramses_tx/version.py,sha256=OmZAmWYXnodOMX2FqbBZjLsKSaBVpA4Y37368OIox1o,123
|
|
51
|
-
ramses_rf-0.52.3.dist-info/METADATA,sha256=u8gl35WcjLN5rO1P_BTfchK23MayeJLDQxFEhHOM-v4,4000
|
|
52
|
-
ramses_rf-0.52.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
53
|
-
ramses_rf-0.52.3.dist-info/entry_points.txt,sha256=NnyK29baOCNg8DinPYiZ368h7MTH7bgTW26z2A1NeIE,50
|
|
54
|
-
ramses_rf-0.52.3.dist-info/licenses/LICENSE,sha256=-Kc35W7l1UkdiQ4314_yVWv7vDDrg7IrJfMLUiq6Nfs,1074
|
|
55
|
-
ramses_rf-0.52.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|