ramses-rf 0.22.40__py3-none-any.whl → 0.51.2__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/__init__.py +18 -0
- ramses_cli/client.py +597 -0
- ramses_cli/debug.py +20 -0
- ramses_cli/discovery.py +405 -0
- ramses_cli/utils/cat_slow.py +17 -0
- ramses_cli/utils/convert.py +60 -0
- ramses_rf/__init__.py +31 -10
- ramses_rf/binding_fsm.py +787 -0
- ramses_rf/const.py +124 -105
- ramses_rf/database.py +297 -0
- ramses_rf/device/__init__.py +69 -39
- ramses_rf/device/base.py +187 -376
- ramses_rf/device/heat.py +540 -552
- ramses_rf/device/hvac.py +279 -171
- ramses_rf/dispatcher.py +153 -177
- ramses_rf/entity_base.py +478 -361
- ramses_rf/exceptions.py +82 -0
- ramses_rf/gateway.py +377 -513
- ramses_rf/helpers.py +57 -19
- ramses_rf/py.typed +0 -0
- ramses_rf/schemas.py +148 -194
- ramses_rf/system/__init__.py +16 -23
- ramses_rf/system/faultlog.py +363 -0
- ramses_rf/system/heat.py +295 -302
- ramses_rf/system/schedule.py +312 -198
- ramses_rf/system/zones.py +318 -238
- ramses_rf/version.py +2 -8
- ramses_rf-0.51.2.dist-info/METADATA +72 -0
- ramses_rf-0.51.2.dist-info/RECORD +55 -0
- {ramses_rf-0.22.40.dist-info → ramses_rf-0.51.2.dist-info}/WHEEL +1 -2
- ramses_rf-0.51.2.dist-info/entry_points.txt +2 -0
- {ramses_rf-0.22.40.dist-info → ramses_rf-0.51.2.dist-info/licenses}/LICENSE +1 -1
- ramses_tx/__init__.py +160 -0
- {ramses_rf/protocol → ramses_tx}/address.py +65 -59
- ramses_tx/command.py +1454 -0
- ramses_tx/const.py +903 -0
- ramses_tx/exceptions.py +92 -0
- {ramses_rf/protocol → ramses_tx}/fingerprints.py +56 -15
- {ramses_rf/protocol → ramses_tx}/frame.py +132 -131
- ramses_tx/gateway.py +338 -0
- ramses_tx/helpers.py +883 -0
- {ramses_rf/protocol → ramses_tx}/logger.py +67 -53
- {ramses_rf/protocol → ramses_tx}/message.py +155 -191
- ramses_tx/opentherm.py +1260 -0
- ramses_tx/packet.py +210 -0
- {ramses_rf/protocol → ramses_tx}/parsers.py +1266 -1003
- ramses_tx/protocol.py +801 -0
- ramses_tx/protocol_fsm.py +672 -0
- ramses_tx/py.typed +0 -0
- {ramses_rf/protocol → ramses_tx}/ramses.py +262 -185
- {ramses_rf/protocol → ramses_tx}/schemas.py +150 -133
- ramses_tx/transport.py +1471 -0
- ramses_tx/typed_dicts.py +492 -0
- ramses_tx/typing.py +181 -0
- ramses_tx/version.py +4 -0
- ramses_rf/discovery.py +0 -398
- ramses_rf/protocol/__init__.py +0 -59
- ramses_rf/protocol/backports.py +0 -42
- ramses_rf/protocol/command.py +0 -1576
- ramses_rf/protocol/const.py +0 -697
- ramses_rf/protocol/exceptions.py +0 -111
- ramses_rf/protocol/helpers.py +0 -390
- ramses_rf/protocol/opentherm.py +0 -1170
- ramses_rf/protocol/packet.py +0 -235
- ramses_rf/protocol/protocol.py +0 -613
- ramses_rf/protocol/transport.py +0 -1011
- ramses_rf/protocol/version.py +0 -10
- ramses_rf/system/hvac.py +0 -82
- ramses_rf-0.22.40.dist-info/METADATA +0 -64
- ramses_rf-0.22.40.dist-info/RECORD +0 -42
- ramses_rf-0.22.40.dist-info/top_level.txt +0 -1
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
#
|
|
4
2
|
"""RAMSES RF - a RAMSES-II protocol decoder & analyser.
|
|
5
3
|
|
|
6
4
|
This module wraps logger to provide bespoke functionality, especially for timestamps.
|
|
7
5
|
"""
|
|
6
|
+
|
|
8
7
|
from __future__ import annotations
|
|
9
8
|
|
|
10
9
|
import logging
|
|
@@ -12,15 +11,14 @@ import os
|
|
|
12
11
|
import re
|
|
13
12
|
import shutil
|
|
14
13
|
import sys
|
|
14
|
+
from collections.abc import Callable, Mapping
|
|
15
15
|
from datetime import datetime as dt
|
|
16
16
|
from logging.handlers import TimedRotatingFileHandler as _TimedRotatingFileHandler
|
|
17
|
-
from typing import
|
|
17
|
+
from typing import Any
|
|
18
18
|
|
|
19
|
-
from .const import __dev_mode__
|
|
20
|
-
from .schemas import SZ_FILE_NAME, SZ_ROTATE_BACKUPS, SZ_ROTATE_BYTES
|
|
21
19
|
from .version import VERSION
|
|
22
20
|
|
|
23
|
-
DEV_MODE =
|
|
21
|
+
DEV_MODE = False
|
|
24
22
|
|
|
25
23
|
_LOGGER = logging.getLogger(__name__)
|
|
26
24
|
if DEV_MODE:
|
|
@@ -69,23 +67,23 @@ class _Logger(logging.Logger): # use pkt.dtm for the log record timestamp
|
|
|
69
67
|
|
|
70
68
|
def makeRecord(
|
|
71
69
|
self,
|
|
72
|
-
name,
|
|
73
|
-
level,
|
|
74
|
-
fn,
|
|
75
|
-
lno,
|
|
76
|
-
msg,
|
|
77
|
-
args,
|
|
78
|
-
exc_info,
|
|
79
|
-
func=None,
|
|
80
|
-
extra=None,
|
|
81
|
-
sinfo=None,
|
|
82
|
-
):
|
|
70
|
+
name: str,
|
|
71
|
+
level: int,
|
|
72
|
+
fn: str,
|
|
73
|
+
lno: int,
|
|
74
|
+
msg: object,
|
|
75
|
+
args: Any,
|
|
76
|
+
exc_info: Any,
|
|
77
|
+
func: str | None = None,
|
|
78
|
+
extra: Mapping[str, object] | None = None,
|
|
79
|
+
sinfo: str | None = None,
|
|
80
|
+
) -> logging.LogRecord:
|
|
83
81
|
"""Create a specialized LogRecord with a bespoke timestamp.
|
|
84
82
|
|
|
85
83
|
Will overwrite created and msecs (and thus asctime), but not relativeCreated.
|
|
86
84
|
"""
|
|
87
85
|
|
|
88
|
-
extra = dict(extra) # work with a copy
|
|
86
|
+
extra = dict(extra or {}) # work with a copy
|
|
89
87
|
extra["frame"] = extra.pop("_frame", "")
|
|
90
88
|
if extra["frame"]:
|
|
91
89
|
extra["frame"] = f" {extra['_rssi']} {extra['frame']}"
|
|
@@ -102,11 +100,11 @@ class _Logger(logging.Logger): # use pkt.dtm for the log record timestamp
|
|
|
102
100
|
if rv.msg:
|
|
103
101
|
rv.msg = f" < {rv.msg}"
|
|
104
102
|
|
|
105
|
-
if getattr(rv, "error_text", None):
|
|
106
|
-
rv.error_text = f" * {
|
|
103
|
+
if value := getattr(rv, "error_text", None): # HACK
|
|
104
|
+
rv.error_text = f" * {value}"
|
|
107
105
|
|
|
108
|
-
if getattr(rv, "comment", None):
|
|
109
|
-
rv.comment = f" # {
|
|
106
|
+
if value := getattr(rv, "comment", None): # HACK
|
|
107
|
+
rv.comment = f" # {value}"
|
|
110
108
|
|
|
111
109
|
return rv
|
|
112
110
|
|
|
@@ -118,7 +116,7 @@ class _Formatter: # format asctime with configurable precision
|
|
|
118
116
|
default_time_format = "%Y-%m-%dT%H:%M:%S.%f"
|
|
119
117
|
precision = 6
|
|
120
118
|
|
|
121
|
-
def formatTime(self, record, datefmt=None) -> str:
|
|
119
|
+
def formatTime(self, record: logging.LogRecord, datefmt: str | None = None) -> str:
|
|
122
120
|
"""Return the creation time (asctime) of the LogRecord as formatted text.
|
|
123
121
|
|
|
124
122
|
Allows for sub-millisecond precision, using datetime instead of time objects.
|
|
@@ -143,7 +141,7 @@ class Formatter(_Formatter, logging.Formatter): # type: ignore[misc]
|
|
|
143
141
|
class PktLogFilter(logging.Filter): # record.levelno in (logging.INFO, logging.WARNING)
|
|
144
142
|
"""For packet log files, process only wanted packets."""
|
|
145
143
|
|
|
146
|
-
def filter(self, record) -> bool:
|
|
144
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
147
145
|
"""Return True if the record is to be processed."""
|
|
148
146
|
# if record._frame[4:] or record.comment or record.error_text:
|
|
149
147
|
return record.levelno in (logging.INFO, logging.WARNING)
|
|
@@ -152,21 +150,21 @@ class PktLogFilter(logging.Filter): # record.levelno in (logging.INFO, logging.
|
|
|
152
150
|
class StdErrFilter(logging.Filter): # record.levelno >= logging.WARNING
|
|
153
151
|
"""For sys.stderr, process only wanted packets."""
|
|
154
152
|
|
|
155
|
-
def filter(self, record) -> bool:
|
|
156
|
-
"""Return True if the record is to be processed."""
|
|
157
|
-
return record.levelno >= logging.WARNING
|
|
153
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
154
|
+
"""Return True if the record is to be processed.""" # WARNING-30, ERROR-40
|
|
155
|
+
return record.levelno >= logging.WARNING
|
|
158
156
|
|
|
159
157
|
|
|
160
158
|
class StdOutFilter(logging.Filter): # record.levelno < logging.WARNING
|
|
161
159
|
"""For sys.stdout, process only wanted packets."""
|
|
162
160
|
|
|
163
|
-
def filter(self, record) -> bool:
|
|
164
|
-
"""Return True if the record is to be processed."""
|
|
165
|
-
return record.levelno < logging.WARNING
|
|
161
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
162
|
+
"""Return True if the record is to be processed.""" # INFO-20, DEBUG-10
|
|
163
|
+
return record.levelno < logging.WARNING
|
|
166
164
|
|
|
167
165
|
|
|
168
166
|
class TimedRotatingFileHandler(_TimedRotatingFileHandler):
|
|
169
|
-
def __init__(self, *args, **kwargs) -> None:
|
|
167
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
170
168
|
super().__init__(*args, **kwargs)
|
|
171
169
|
assert self.when == "MIDNIGHT"
|
|
172
170
|
self.extMatch = re.compile(r"^\d{4}-\d{2}-\d{2}$", re.ASCII)
|
|
@@ -176,10 +174,10 @@ class TimedRotatingFileHandler(_TimedRotatingFileHandler):
|
|
|
176
174
|
# self.doRollover()
|
|
177
175
|
# return super().emit(record)
|
|
178
176
|
|
|
179
|
-
def getFilesToDelete(self): # zxdavb: my version
|
|
177
|
+
def getFilesToDelete(self) -> list[str]: # zxdavb: my version
|
|
180
178
|
"""Determine the files to delete when rolling over.
|
|
181
179
|
|
|
182
|
-
|
|
180
|
+
Overridden as old log files not being deleted.
|
|
183
181
|
"""
|
|
184
182
|
# See bpo-44753 (this code is as was before that commit), bpo45628, bpo-46063
|
|
185
183
|
dirName, baseName = os.path.split(self.baseFilename)
|
|
@@ -200,7 +198,9 @@ class TimedRotatingFileHandler(_TimedRotatingFileHandler):
|
|
|
200
198
|
return result
|
|
201
199
|
|
|
202
200
|
|
|
203
|
-
def getLogger(
|
|
201
|
+
def getLogger( # permits a bespoke Logger class
|
|
202
|
+
name: str | None = None, pkt_log: bool = False
|
|
203
|
+
) -> logging.Logger:
|
|
204
204
|
"""Return a logger with the specified name, creating it if necessary.
|
|
205
205
|
|
|
206
206
|
Used to set record timestamps to its packet timestamp instead of the current time.
|
|
@@ -208,25 +208,34 @@ def getLogger(name=None, pkt_log=None): # permits a bespoke Logger class
|
|
|
208
208
|
if name is None or not pkt_log:
|
|
209
209
|
return logging.getLogger(name)
|
|
210
210
|
|
|
211
|
-
|
|
211
|
+
# Acquire lock, so no-one else uses our Logger class
|
|
212
|
+
try: # TODO: remove this ASAP
|
|
213
|
+
logging._acquireLock() # type: ignore[attr-defined]
|
|
214
|
+
except AttributeError: # Python 3.13+
|
|
215
|
+
logging._lock.acquire() # type: ignore[attr-defined]
|
|
216
|
+
|
|
212
217
|
klass = logging.getLoggerClass()
|
|
213
218
|
logging.setLoggerClass(_Logger)
|
|
214
219
|
|
|
215
220
|
logger = logging.getLogger(name)
|
|
216
221
|
|
|
217
222
|
logging.setLoggerClass(klass)
|
|
218
|
-
|
|
223
|
+
|
|
224
|
+
try: # TODO: remove this ASAP
|
|
225
|
+
logging._releaseLock() # type: ignore[attr-defined]
|
|
226
|
+
except AttributeError: # Python 3.13+
|
|
227
|
+
logging._lock.release() # type: ignore[attr-defined]
|
|
219
228
|
|
|
220
229
|
return logger
|
|
221
230
|
|
|
222
231
|
|
|
223
|
-
def set_logger_timesource(dtm_now: Callable):
|
|
232
|
+
def set_logger_timesource(dtm_now: Callable[..., dt]) -> None:
|
|
224
233
|
"""Set a custom record factory, with a bespoke source of timestamps.
|
|
225
234
|
|
|
226
235
|
Used to have records with the same datetime as the most recent packet log record.
|
|
227
236
|
"""
|
|
228
237
|
|
|
229
|
-
def record_factory(*args, **kwargs):
|
|
238
|
+
def record_factory(*args: Any, **kwargs: Any) -> logging.LogRecord:
|
|
230
239
|
record = old_factory(*args, **kwargs)
|
|
231
240
|
|
|
232
241
|
ct = dtm_now().timestamp()
|
|
@@ -240,12 +249,19 @@ def set_logger_timesource(dtm_now: Callable):
|
|
|
240
249
|
logging.setLogRecordFactory(record_factory)
|
|
241
250
|
|
|
242
251
|
|
|
243
|
-
def set_pkt_logging(
|
|
252
|
+
def set_pkt_logging(
|
|
253
|
+
logger: logging.Logger,
|
|
254
|
+
cc_console: bool = False,
|
|
255
|
+
file_name: str | None = None,
|
|
256
|
+
rotate_backups: int = 0,
|
|
257
|
+
rotate_bytes: int | None = None,
|
|
258
|
+
) -> None:
|
|
244
259
|
"""Create/configure handlers, formatters, etc.
|
|
245
260
|
|
|
246
261
|
Parameters:
|
|
247
|
-
-
|
|
248
|
-
-
|
|
262
|
+
- file_name: base of file to store packet logs in, from root
|
|
263
|
+
- rotate_backups: keep this many copies, and rotate at midnight unless:
|
|
264
|
+
- rotate_bytes: rotate log files when log > rotate_size
|
|
249
265
|
"""
|
|
250
266
|
|
|
251
267
|
logger.propagate = False # log file is distinct from any app/debug logging
|
|
@@ -255,18 +271,15 @@ def set_pkt_logging(logger, dt_now=None, cc_console: bool = False, **kwargs) ->
|
|
|
255
271
|
for handler in logger.handlers: # dont use logger.hasHandlers() as not propagating
|
|
256
272
|
logger.removeHandler(handler)
|
|
257
273
|
|
|
258
|
-
if file_name
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if max_bytes:
|
|
263
|
-
bkp_count = bkp_count or 2
|
|
274
|
+
if file_name: # note: this opens the packet_log file IO and may block
|
|
275
|
+
if rotate_bytes:
|
|
276
|
+
rotate_backups = rotate_backups or 2
|
|
264
277
|
handler = logging.handlers.RotatingFileHandler(
|
|
265
|
-
file_name, maxBytes=
|
|
278
|
+
file_name, maxBytes=rotate_bytes, backupCount=rotate_backups
|
|
266
279
|
)
|
|
267
|
-
elif
|
|
280
|
+
elif rotate_backups:
|
|
268
281
|
handler = TimedRotatingFileHandler(
|
|
269
|
-
file_name, when="MIDNIGHT", backupCount=
|
|
282
|
+
file_name, when="MIDNIGHT", backupCount=rotate_backups
|
|
270
283
|
)
|
|
271
284
|
else:
|
|
272
285
|
handler = logging.FileHandler(file_name)
|
|
@@ -285,7 +298,8 @@ def set_pkt_logging(logger, dt_now=None, cc_console: bool = False, **kwargs) ->
|
|
|
285
298
|
logger.setLevel(logging.CRITICAL)
|
|
286
299
|
return
|
|
287
300
|
|
|
288
|
-
if cc_console:
|
|
301
|
+
if cc_console: # CC: output to stdout/stderr
|
|
302
|
+
console_fmt: ColoredFormatter | Formatter
|
|
289
303
|
if _use_color_:
|
|
290
304
|
console_fmt = ColoredFormatter(
|
|
291
305
|
fmt=f"%(log_color)s{CONSOLE_FMT + COLOR_SUFFIX}",
|
|
@@ -310,6 +324,6 @@ def set_pkt_logging(logger, dt_now=None, cc_console: bool = False, **kwargs) ->
|
|
|
310
324
|
extras = {
|
|
311
325
|
"_frame": "",
|
|
312
326
|
"error_text": "",
|
|
313
|
-
"comment": f"
|
|
327
|
+
"comment": f"ramses_tx {VERSION}",
|
|
314
328
|
}
|
|
315
|
-
logger.warning("", extra=extras)
|
|
329
|
+
logger.warning("", extra=extras) # initial log line
|