Zigbee Smart Heating: Telegraf Crashes and TRVs Lie

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.

[[img:1]]