Files
weatherstation-datacollector/test_pool_decode.py
olaf 5b4340fab2 changed: data-logging configuration to rotate more frequently
fixed: database connection retry interval and storing in local SQLite when DB is down
2025-12-28 07:35:50 +00:00

155 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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.0100.0%
continue
if not (8000 <= pres1 <= 11000): # 800.01100.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!")