ramses-rf 0.52.4__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_tx/helpers.py CHANGED
@@ -131,28 +131,36 @@ file_time = _FILE_TIME()
131
131
  def timestamp() -> float:
132
132
  """Return the number of seconds since the Unix epoch.
133
133
 
134
- Return an accurate value, even for Windows-based systems.
134
+ This function attempts to return a high-precision value, using specific
135
+ system calls on Windows if available.
136
+ :return: The current timestamp in seconds.
137
+ :rtype: float
135
138
  """
136
139
 
137
140
  # see: https://www.python.org/dev/peps/pep-0564/
138
- if sys.platform != "win32": # since 1970-01-01T00:00:00Z, time.gmtime(0)
141
+ if sys.platform == "win32":
142
+ # Windows uses a different epoch (1601-01-01)
143
+ ctypes.windll.kernel32.GetSystemTimePreciseAsFileTime(ctypes.byref(file_time))
144
+ _time = (file_time.dwLowDateTime + (file_time.dwHighDateTime << 32)) / 1e7
145
+ return float(_time - 134774 * 24 * 60 * 60)
146
+ else:
147
+ # Linux/macOS uses the Unix epoch (1970-01-01)
139
148
  return time.time_ns() / 1e9
140
149
 
141
- # otherwise, is since 1601-01-01T00:00:00Z
142
- ctypes.windll.kernel32.GetSystemTimePreciseAsFileTime(ctypes.byref(file_time)) # type: ignore[unreachable]
143
- _time = (file_time.dwLowDateTime + (file_time.dwHighDateTime << 32)) / 1e7
144
- return _time - 134774 * 24 * 60 * 60
145
-
146
150
 
147
151
  def dt_now() -> dt:
148
152
  """Return the current datetime as a local/naive datetime object.
149
153
 
150
154
  This is slower, but potentially more accurate, than dt.now(), and is used mainly for
151
155
  packet timestamps.
156
+
157
+ :return: The current local datetime.
158
+ :rtype: dt
152
159
  """
153
160
  if sys.platform == "win32":
154
161
  return dt.fromtimestamp(timestamp())
155
- return dt.now()
162
+ else:
163
+ return dt.now()
156
164
 
157
165
 
158
166
  def dt_str() -> str:
@@ -369,7 +377,14 @@ def hex_from_str(value: str) -> str:
369
377
 
370
378
 
371
379
  def hex_to_temp(value: HexStr4) -> bool | float | None: # TODO: remove bool
372
- """Convert a 2's complement 4-byte hex string to a float."""
380
+ """Convert a 4-byte 2's complement hex string to a float temperature ('C).
381
+
382
+ :param value: The 4-character hex string (e.g., '07D0')
383
+ :type value: HexStr4
384
+ :return: The temperature in Celsius, or None if N/A
385
+ :rtype: float | None
386
+ :raises ValueError: If input is not a 4-char hex string or temperature is invalid.
387
+ """
373
388
  if not isinstance(value, str) or len(value) != 4:
374
389
  raise ValueError(f"Invalid value: {value}, is not a 4-char hex string")
375
390
  if value == "31FF": # means: N/A (== 127.99, 2s complement), signed?
@@ -488,13 +503,18 @@ AIR_QUALITY_BASIS: dict[str, str] = {
488
503
 
489
504
  # 31DA[2:6] and 12C8[2:6]
490
505
  def parse_air_quality(value: HexStr4) -> PayDictT.AIR_QUALITY:
491
- """Return the air quality (%): poor (0.0) to excellent (1.0).
506
+ """Return the air quality percentage (0.0 to 1.0) and its basis.
492
507
 
493
508
  The basis of the air quality level should be one of: VOC, CO2 or relative humidity.
494
509
  If air_quality is EF, air_quality_basis should be 00.
495
510
 
496
511
  The sensor value is None if there is no sensor present (is not an error).
497
512
  The dict does not include the key if there is a sensor fault.
513
+
514
+ :param value: The 4-character hex string encoding quality and basis
515
+ :type value: HexStr4
516
+ :return: A dictionary containing the air quality and its basis (e.g., CO2, VOC)
517
+ :rtype: PayDictT.AIR_QUALITY
498
518
  """ # VOC: Volatile organic compounds
499
519
 
500
520
  # TODO: remove this as API used only internally...
ramses_tx/message.py CHANGED
@@ -54,7 +54,9 @@ class MessageBase:
54
54
  def __init__(self, pkt: Packet) -> None:
55
55
  """Create a message from a valid packet.
56
56
 
57
- :raises InvalidPacketError if message payload is invalid.
57
+ :param pkt: The packet to process into a message
58
+ :type pkt: Packet
59
+ :raises PacketInvalid: If the packet payload cannot be parsed.
58
60
  """
59
61
 
60
62
  self._pkt = pkt
@@ -359,10 +361,14 @@ def re_compile_re_match(regex: str, string: str) -> bool: # Optional[Match[Any]
359
361
 
360
362
 
361
363
  def _check_msg_payload(msg: MessageBase, payload: str) -> None:
362
- """Validate the packet's payload against its verb/code pair.
363
-
364
- :raises InvalidPayloadError if the payload is seen as invalid. Such payloads may
365
- actually be valid, in which case the rules (likely the regex) will need updating.
364
+ """Validate a packet's payload against its verb/code pair.
365
+
366
+ :param msg: The message object being validated
367
+ :type msg: MessageBase
368
+ :param payload: The raw hex payload string
369
+ :type payload: str
370
+ :raises PacketInvalid: If the code is unknown or verb/code pair is invalid.
371
+ :raises PacketPayloadInvalid: If the payload does not match the expected regex.
366
372
  """
367
373
 
368
374
  _ = repr(msg._pkt) # HACK: ? raise InvalidPayloadError
ramses_tx/packet.py CHANGED
@@ -48,9 +48,14 @@ class Packet(Frame):
48
48
  _rssi: str
49
49
 
50
50
  def __init__(self, dtm: dt, frame: str, **kwargs: Any) -> None:
51
- """Create a packet from a string (actually from f"{RSSI} {frame}").
52
-
53
- Will raise InvalidPacketError if it is invalid.
51
+ """Create a packet from a raw frame string.
52
+
53
+ :param dtm: The timestamp when the packet was received
54
+ :type dtm: dt
55
+ :param frame: The raw frame string, typically including RSSI
56
+ :type frame: str
57
+ :param kwargs: Metadata including 'comment', 'err_msg', or 'raw_frame'
58
+ :raises PacketInvalid: If the frame content is malformed.
54
59
  """
55
60
 
56
61
  super().__init__(frame[4:]) # remove RSSI
@@ -156,9 +161,12 @@ class Packet(Frame):
156
161
 
157
162
  # TODO: remove None as a possible return value
158
163
  def pkt_lifespan(pkt: Packet) -> td: # import OtbGateway??
159
- """Return the pkt lifespan, or dt.max() if the packet does not expire.
164
+ """Return the lifespan of a packet before it expires.
160
165
 
161
- Some codes require a valid payload to best determine lifespan (e.g. 1F09).
166
+ :param pkt: The packet instance to evaluate
167
+ :type pkt: Packet
168
+ :return: The duration the packet's data remains valid
169
+ :rtype: td
162
170
  """
163
171
 
164
172
  if pkt.verb in (RQ, W_):