fixed: database connection retry interval and storing in local SQLite when DB is down
155 lines
4.2 KiB
Python
155 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
||
"""Test pool payload decoding with the new MQTT message format"""
|
||
|
||
import json
|
||
import struct
|
||
from typing import Optional
|
||
|
||
PAYLOAD_SIZE = 15
|
||
MAGIC1 = 0x42
|
||
MAGIC2 = 0x99
|
||
|
||
|
||
def crc8_xor(data: bytes) -> int:
|
||
"""Simple XOR checksum used by the pool payload."""
|
||
c = 0
|
||
for b in data:
|
||
c ^= b
|
||
return c
|
||
|
||
|
||
def decode_pool_payload(candidate_bytes: bytes, expected_seq: Optional[int] = None):
|
||
"""Scan a byte stream for a plausible pool payload.
|
||
|
||
Slides a 15-byte window, validates with CRC, version/nodeId, and range checks,
|
||
and scores candidates. Returns the best decoded dict or None.
|
||
"""
|
||
# Drop leading preamble (0xAA) if present
|
||
while candidate_bytes.startswith(b"\xaa"):
|
||
candidate_bytes = candidate_bytes[1:]
|
||
|
||
best = None
|
||
best_score = -1
|
||
|
||
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(
|
||
'<BBBBHhhHHB', chunk
|
||
)
|
||
except struct.error:
|
||
continue
|
||
|
||
crc_calculated = crc8_xor(chunk[:-1])
|
||
if crc_calculated != crc_received:
|
||
continue
|
||
|
||
if version != 1 or nodeId != 1:
|
||
continue
|
||
|
||
# Plausibility checks (unit scaled)
|
||
if not (-300 <= t_ds10 <= 600): # -30.0 to 60.0°C
|
||
continue
|
||
if not (-300 <= t_bme10 <= 600):
|
||
continue
|
||
if not (0 <= hum10 <= 1000): # 0.0–100.0%
|
||
continue
|
||
if not (8000 <= pres1 <= 11000): # 800.0–1100.0 hPa
|
||
continue
|
||
|
||
score = 0
|
||
if magic1 == MAGIC1 and magic2 == MAGIC2:
|
||
score += 2
|
||
if expected_seq is not None and seq == expected_seq:
|
||
score += 1
|
||
# CRC already validated; reward shorter offset to prefer first valid
|
||
score -= offset * 0.001
|
||
|
||
if score > best_score:
|
||
best_score = score
|
||
best = {
|
||
"offset": offset,
|
||
"magic_ok": magic1 == MAGIC1 and magic2 == MAGIC2,
|
||
"version": version,
|
||
"nodeId": nodeId,
|
||
"sequence": seq,
|
||
"t_ds_c": t_ds10 / 10.0,
|
||
"t_bme_c": t_bme10 / 10.0,
|
||
"humidity": hum10 / 10.0,
|
||
"pressure_hpa": pres1 / 10.0,
|
||
"crc_valid": True,
|
||
}
|
||
|
||
return best
|
||
|
||
|
||
# Test with the actual MQTT message
|
||
mqtt_message = {
|
||
"time": "2025-12-27T13:26:47",
|
||
"model": "pool",
|
||
"count": 1,
|
||
"num_rows": 1,
|
||
"rows": [
|
||
{
|
||
"len": 143,
|
||
"data": "429901013400a801f6002b0294272a000000"
|
||
}
|
||
],
|
||
"codes": ["{143}429901013400a801f6002b0294272a000000"]
|
||
}
|
||
|
||
print("Testing pool payload decode with new MQTT format:")
|
||
print(f"MQTT message: {json.dumps(mqtt_message, indent=2)}")
|
||
print()
|
||
|
||
hex_data = mqtt_message['rows'][0]['data']
|
||
print(f"Hex data from rows[0]['data']: {hex_data}")
|
||
print(f"Hex data length: {len(hex_data)} chars ({len(hex_data)//2} bytes)")
|
||
|
||
# Strip 'aaaaaa' prefix if present
|
||
if hex_data.startswith('aaaaaa'):
|
||
hex_data = hex_data[6:]
|
||
print(f"Stripped 'aaaaaa' prefix, remaining: {hex_data}")
|
||
|
||
byte_data = bytes.fromhex(hex_data)
|
||
print(f"Byte data: {byte_data.hex()}")
|
||
print()
|
||
|
||
# Decode with sliding window
|
||
decoded = decode_pool_payload(byte_data)
|
||
|
||
if decoded:
|
||
print("✓ Payload decoded successfully!")
|
||
print(json.dumps(decoded, indent=2))
|
||
|
||
print()
|
||
print("Generated sensor messages:")
|
||
|
||
bme_msg = {
|
||
'time': mqtt_message['time'],
|
||
'model': 'pool',
|
||
'id': decoded['nodeId'] * 10 + 1,
|
||
'battery_ok': 1,
|
||
'temperature_C': decoded['t_bme_c'],
|
||
'humidity': decoded['humidity'],
|
||
'pressure_rel': decoded['pressure_hpa'],
|
||
'mic': 'CRC'
|
||
}
|
||
|
||
ds_msg = {
|
||
'time': mqtt_message['time'],
|
||
'model': 'pool',
|
||
'id': decoded['nodeId'] * 10 + 2,
|
||
'battery_ok': 1,
|
||
'temperature_C': decoded['t_ds_c'],
|
||
'mic': 'CRC'
|
||
}
|
||
|
||
print("BME280 message:")
|
||
print(json.dumps(bme_msg, indent=2))
|
||
print()
|
||
print("DS18B20 message:")
|
||
print(json.dumps(ds_msg, indent=2))
|
||
else:
|
||
print("✗ Failed to decode payload!")
|