fix(gateway): fix BLE filtering and MQTT connection validation

BLE filtering:
- Replace UUID service filter with company_id 0xffff filter in
  manufacturer_data — Zephyr firmware does not announce a service UUID
- Remove preamble skip in decode_payload (i=0 instead of i=2) since
  company_id is handled by the BLE stack and not in raw bytes
- Fix endianness: switch temperature and CO2 decoding from little-endian
  to big-endian to match Zephyr firmware implementation

MQTT validation:
- Add on_connect callback to confirm successful connection to broker
- Log error with reason code if connection fails
- Add on_publish callback to confirm message delivery to broker
- Replace synchronous connected log with async connecting log

Validated end-to-end: Thingy:52 data received and confirmed by broker
This commit is contained in:
DjeAvd
2026-05-07 14:44:19 +01:00
committed by Klagarge
parent 2f43085843
commit 3aac195626

View File

@@ -54,22 +54,36 @@ class Gateway:
self.mqttc.tls_set()
log.info("TLS enabled")
# Callback to confirm connection to broker
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code == 0:
log.info("Successfully connected to MQTT broker")
else:
log.error(f"Failed to connect to MQTT broker — reason code: {reason_code}")
# Callback to confirm message delivery to broker
def on_publish(client, userdata, mid, reason_code, properties):
log.info(f"Message confirmed by broker — mid: {mid}")
self.mqttc.on_connect = on_connect
self.mqttc.on_publish = on_publish
self.mqttc.connect(self.mqtt_broker, self.mqtt_port)
self.mqttc.loop_start()
log.info("MQTT client connected")
log.info("MQTT client connecting...")
def decode_payload(self, data: bytes) -> dict:
"""Decode key/value pairs from BLE advertising payload.
Format per firmware spec:
2 bytes preamble (company id = 0xffff) — skipped
Format per firmware spec (no preamble in raw bytes — company_id
is handled by the BLE stack and not included in manufacturer data):
0x01 : window open (1 byte, 0 or 1)
0x02 : humidity (1 byte, integer %)
0x03 : temperature (2 bytes big-endian, integer / 10 = degrees C)
0x04 : CO2 ppm (4 bytes big-endian, integer)
"""
result = {}
i = 2 # skip 2-byte preamble (company id 0xffff)
i = 0 # no preamble to skip — company_id is not in raw bytes
while i < len(data):
key = data[i]
i += 1
@@ -114,15 +128,13 @@ class Gateway:
log.info(f"Published to {topic} : {payload}")
def on_device_found(self, device, adv_data):
"""BLE scan callback — filters on Adrien's preamble (0xffff) in manufacturer data."""
if not adv_data.manufacturer_data:
"""BLE scan callback — filters on company_id 0xffff in manufacturer data."""
if 0xffff not in adv_data.manufacturer_data:
return
raw = list(adv_data.manufacturer_data.values())[0]
# Filter on preamble 0xffff (company id defined in firmware spec)
if len(raw) < 2 or raw[0] != 0xff or raw[1] != 0xff:
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]
log.debug(f"{device.address} | Thingy detected, raw: {list(raw)}")