I have 6 Zigbee devices for heating control: 4 Sonoff thermostats (TRVs) and 2 temperature sensors. But only 3 of them showed up in InfluxDB. Telegraf was running, zigbee2mqtt too, but something in between was broken. So I debugged it.
The Zigbee smart heating setup ran for a few days, then I noticed that not all devices were delivering data. Time to debug.
Telegraf crashes with bridge topics
Telegraf subscribed to zigbee2mqtt/+, a wildcard that matches ALL topics. That went wrong because zigbee2mqtt also sends zigbee2mqtt/bridge messages with arrays and nested objects. Telegraf's JSON parser couldn't handle them and crashed.
The fix was explicit device topics instead of a wildcard in the Telegraf config:
[[inputs.mqtt_consumer]]
topics = [
"zigbee2mqtt/Büro Temp",
"zigbee2mqtt/Büro groß",
"zigbee2mqtt/Büro klein",
"zigbee2mqtt/Wohnzimmer Temp",
"zigbee2mqtt/Wohnzimmer groß",
"zigbee2mqtt/Wohnzimmer klein",
]
After that all 6 devices arrived in InfluxDB.
TRV measures way too warm
The thermostat "Büro klein" showed 24°C even though the external sensor reported 21°C. The valve regulated accordingly wrong. Turned out that temperature_sensor_select was set to internal. The internal sensor sits right on the radiator and measures the radiator temperature, not the room air.
So I switched it to the external sensor via MQTT:
mosquitto_pub -t "zigbee2mqtt/Büro klein/set" \ -m '{"temperature_sensor_select": "external_2"}'
Now the TRV regulates based on the real room sensor.
valve_opening_degree misunderstanding
I thought valve_opening_degree showed the current valve position in percent. But it stayed constantly at 100%, regardless of whether the valve was open or closed. Research in the zigbee2mqtt documentation and GitHub issues led to the realization that the field is a calibration limit, not the position.
The Sonoff TRVZB doesn't output the actual valve position as a percentage. Only running_state (idle/heat) shows whether it's actively heating.
So I removed the panel and built a state timeline for running_state instead. For that I had to add running_state to the Telegraf config as json_string_fields:
json_string_fields = ["running_state", "battery", "linkquality"]
Now I can see for each TRV whether it's currently heating or idle. That's enough.
Lessons Learned
- Wildcards in MQTT subscriptions are dangerous when the topics have different structures
- TRVs with an internal sensor are unreliable → always use external sensors
- Read the documentation before getting annoyed about "broken" values, sometimes fields don't mean what you think
The heating control now runs cleanly. All 6 devices log correctly, the TRVs regulate based on real room temperatures.
