Pool sensor v2: VCC monitoring, database resilience, receiver improvements

- Added voltage monitoring table and storage pipeline
- Extended pool payload to 17 bytes with VCC field (protocol v2)
- Improved database connection pool resilience (reduced pool size, aggressive recycling, pool disposal on failure)
- Added environment variable support for database configuration
- Fixed receiver MQTT deprecation warning (CallbackAPIVersion.VERSION2)
- Silenced excessive RSSI status logging in receiver
- Added reset flag tracking and reporting
- Updated Docker compose with DB config and log rotation limits
This commit is contained in:
2026-01-25 11:25:15 +00:00
parent d1c1f63cb9
commit f55c1fe6f1
9 changed files with 1512 additions and 101 deletions

View File

@@ -8,6 +8,12 @@ from typing import Optional, Tuple, Dict
PAYLOAD_SIZE = 15
MAGIC1 = 0x42
MAGIC2 = 0x99
RESET_FLAG_MAP = [
(0x1, "PORF (power-on)"),
(0x2, "EXTRF (external reset)"),
(0x4, "BORF (brown-out)"),
(0x8, "WDRF (watchdog)"),
]
def crc8_xor(data: bytes) -> int:
@@ -18,6 +24,13 @@ def crc8_xor(data: bytes) -> int:
return c
def parse_version_and_reset_flags(version_byte: int):
protocol_version = version_byte & 0x0F
reset_flags = (version_byte >> 4) & 0x0F
reset_causes = [desc for bit, desc in RESET_FLAG_MAP if reset_flags & bit]
return protocol_version, reset_flags, reset_causes
def decode_pool_payload(candidate_bytes: bytes, expected_seq: Optional[int] = None):
"""Scan a byte stream for a plausible pool payload.
@@ -34,7 +47,7 @@ def decode_pool_payload(candidate_bytes: bytes, expected_seq: Optional[int] = No
for offset in range(0, len(candidate_bytes) - PAYLOAD_SIZE + 1):
chunk = candidate_bytes[offset:offset + PAYLOAD_SIZE]
try:
magic1, magic2, version, nodeId, seq, t_ds10, t_bme10, hum10, pres1, crc_received = struct.unpack(
magic1, magic2, version_byte, nodeId, seq, t_ds10, t_bme10, hum10, pres1, crc_received = struct.unpack(
'<BBBBHhhHHB', chunk
)
except struct.error:
@@ -44,7 +57,9 @@ def decode_pool_payload(candidate_bytes: bytes, expected_seq: Optional[int] = No
if crc_calculated != crc_received:
continue
if version != 1 or nodeId != 1:
protocol_version, reset_flags, reset_causes = parse_version_and_reset_flags(version_byte)
if protocol_version != 1 or nodeId != 1:
continue
# Plausibility checks (unit scaled)
@@ -70,7 +85,10 @@ def decode_pool_payload(candidate_bytes: bytes, expected_seq: Optional[int] = No
best = {
"offset": offset,
"magic_ok": magic1 == MAGIC1 and magic2 == MAGIC2,
"version": version,
"version": protocol_version,
"version_byte": version_byte,
"reset_flags": reset_flags,
"reset_causes": reset_causes,
"nodeId": nodeId,
"sequence": seq,
"t_ds_c": t_ds10 / 10.0,
@@ -83,9 +101,10 @@ def decode_pool_payload(candidate_bytes: bytes, expected_seq: Optional[int] = No
return best
def build_payload(seq: int, t_ds10: int, t_bme10: int, hum10: int, pres1: int) -> bytes:
def build_payload(seq: int, t_ds10: int, t_bme10: int, hum10: int, pres1: int, reset_flags: int = 0) -> bytes:
"""Build a valid payload with CRC appended."""
header = struct.pack('<BBBBHhhHH', MAGIC1, MAGIC2, 1, 1, seq, t_ds10, t_bme10, hum10, pres1)
version_byte = ((reset_flags & 0x0F) << 4) | 0x01
header = struct.pack('<BBBBHhhHH', MAGIC1, MAGIC2, version_byte, 1, seq, t_ds10, t_bme10, hum10, pres1)
crc = crc8_xor(header)
return header + bytes([crc])