fix(gateway): use exact payload size filter and improve robustness
- Use exact 12-byte payload size filter instead of minimum to avoid false positives from non-Thingy devices with same company_id - Discard CO2 value 0xFFFFFFFF indicating sensor not ready or failed - Downgrade unknown key and empty payload logs to DEBUG level - Add on_connect callback to confirm successful broker connection - Add on_publish callback to confirm message delivery to broker - Rename MIN_PAYLOAD_SIZE to EXPECTED_PAYLOAD_SIZE for clarity Validated: data received and confirmed by broker after Thingy reboot
This commit is contained in:
@@ -24,6 +24,13 @@ class Gateway:
|
||||
KEY_TEMP = 0x03
|
||||
KEY_CO2 = 0x04
|
||||
|
||||
# Sentinel value indicating sensor failure or not ready
|
||||
INVALID_VALUE = 0xFFFFFFFF
|
||||
|
||||
# Expected payload size in bytes:
|
||||
# 4 keys (1B each) + window(1B) + humidity(1B) + temp(2B) + co2(4B) = 12 bytes
|
||||
EXPECTED_PAYLOAD_SIZE = 12
|
||||
|
||||
def __init__(self, config: dict):
|
||||
self.gateway_id = config["gateway_id"]
|
||||
self.mqtt_broker = config["mqtt"]["broker"]
|
||||
@@ -81,6 +88,9 @@ class Gateway:
|
||||
0x02 : humidity (1 byte, integer %)
|
||||
0x03 : temperature (2 bytes big-endian, integer / 10 = degrees C)
|
||||
0x04 : CO2 ppm (4 bytes big-endian, integer)
|
||||
|
||||
Values equal to 0xFFFFFFFF indicate sensor failure or not ready
|
||||
and are discarded.
|
||||
"""
|
||||
result = {}
|
||||
i = 0 # no preamble to skip — company_id is not in raw bytes
|
||||
@@ -100,10 +110,16 @@ class Gateway:
|
||||
result["temp"] = raw / 10
|
||||
i += 2
|
||||
elif key == self.KEY_CO2 and i + 3 < len(data):
|
||||
result["co2_ppm"] = int.from_bytes(data[i:i+4], byteorder='big')
|
||||
co2 = int.from_bytes(data[i:i+4], byteorder='big')
|
||||
# 0xFFFFFFFF indicates sensor not ready or failed
|
||||
if co2 != self.INVALID_VALUE:
|
||||
result["co2_ppm"] = co2
|
||||
else:
|
||||
log.debug(f"CO2 sensor not ready — discarding value 0xFFFFFFFF")
|
||||
i += 4
|
||||
else:
|
||||
log.warning(f"Unknown key 0x{key:02x} at offset {i-1}")
|
||||
# Unknown key — likely a non-Thingy device, ignore silently
|
||||
log.debug(f"Unknown key 0x{key:02x} at offset {i-1}")
|
||||
break
|
||||
return result
|
||||
|
||||
@@ -132,15 +148,19 @@ class Gateway:
|
||||
if 0xffff not in adv_data.manufacturer_data:
|
||||
return
|
||||
|
||||
# company_id 0xffff is defined in the firmware spec
|
||||
# the raw bytes do not include the company_id itself
|
||||
raw = adv_data.manufacturer_data[0xffff]
|
||||
|
||||
# Filter on exact payload size to avoid false positives from
|
||||
# other BLE devices using the same company_id
|
||||
if len(raw) != self.EXPECTED_PAYLOAD_SIZE:
|
||||
log.debug(f"{device.address} | ignored — unexpected payload size: {len(raw)}")
|
||||
return
|
||||
|
||||
log.debug(f"{device.address} | Thingy detected, raw: {list(raw)}")
|
||||
|
||||
data = self.decode_payload(raw)
|
||||
if not data:
|
||||
log.warning(f"{device.address} | empty decoded payload")
|
||||
log.debug(f"{device.address} | empty decoded payload — ignored")
|
||||
return
|
||||
|
||||
log.debug(f"{device.address} | decoded: {data}")
|
||||
|
||||
Reference in New Issue
Block a user