private@homelab: ~/latest
local-first guides · privacy-aware · no noisy tracking
private@homelab:~$ cat guides/article.md
·
· ·
8–13 minutes
read

Sonoff TRVZB Radiator Valve: Zigbee2MQTT + Home Assistant

Pair the Sonoff TRVZB into Zigbee2MQTT with no Sonoff bridge or eWeLink account, set valve opening degree, and drive it off an external room sensor.

The Sonoff TRVZB is a battery-powered Zigbee thermostatic radiator valve built for the M30 x 1.5mm valve thread common on European-style radiators. It pairs directly into Zigbee2MQTT — no Sonoff hub, no eWeLink account, no cloud round-trip for anything it does. That makes it a straightforward fit for a local-only heating setup, but the device exposes more configuration than a typical TRV, and the one workflow people consistently get stuck on is making it heat off a room sensor instead of the temperature reading built into the valve body.

This is a research-synthesis guide, not a bench report. Device behaviour below is sourced from the official Zigbee2MQTT device page for the TRVZB and Sonoff’s own product documentation; the verification section at the end says exactly what that covers.

What the TRVZB is and who it fits

The TRVZB screws onto M30 x 1.5mm valve threads, the standard fitting on most European radiators. Sonoff states that adapters for other common heating systems ship in the box, so a radiator with a different valve body is not automatically disqualifying — but check what thread your existing head unscrews from before buying, because that is the one detail no amount of Zigbee2MQTT configuration can fix afterwards.

It runs on 3x AA batteries, with a vendor-stated typical life of 6+ months. That figure comes from Sonoff’s own marketing material, not independent measurement, and real life will vary with how often the valve motor cycles.

Note · compatibility

This guide covers the TRVZB paired through Zigbee2MQTT. Sonoff also sells it for pairing through the eWeLink app and, separately, it works with ZHA. If you want the eWeLink/cloud route instead of local-only, the entity names and workflow below won’t match what you see in that app.

Pairing with Zigbee2MQTT — no Sonoff bridge required

The TRVZB is a standard Zigbee device and joins any Zigbee2MQTT network the same way any other Zigbee end device does: put Zigbee2MQTT into pairing mode, then trigger pairing on the valve itself (check the unit’s manual for the exact button sequence). No Sonoff-branded bridge or gateway is in the path — the valve talks Zigbee directly to whatever coordinator your Zigbee2MQTT instance is running on. If you haven’t settled on coordinator hardware yet, our ZBDongle-E vs ZBDongle-P comparison covers that decision separately.

After pairing, the valve runs a self-calibration pass automatically, measuring the travel of the valve pin. This is also re-triggered by a factory reset. Once it completes, you can confirm the device and its exposes are visible from the CLI:

zigbee2mqtt confirm pairing (CLI)
mosquitto_sub -h 192.168.10.20 -t 'zigbee2mqtt/bridge/devices' -C 1 | grep -A2 TRVZB

The full exposed-entity list, explained

Once paired, Zigbee2MQTT publishes state under whatever friendly name you assign — the examples below use radiator_livingroom. The core climate entities are what you’ll actually interact with day to day:

Entity Range / values What it does
occupied_heating_setpoint 4–35°C Target temperature you’re asking the valve to heat toward
local_temperature read-only Current measured temperature — from the internal sensor, or from an external sensor if selected
system_mode off / auto / heat Overall operating mode
running_state idle / heat Whether the valve is currently calling for heat
local_temperature_calibration -12.7°C to +12.7°C, 0.2° steps Manual offset applied to the temperature reading
temperature_sensor_select internal / external / external_2 / external_3 Which sensor the valve controls against
valve_opening_degree 0–100%, writable How far the valve opens when it calls for heat
valve_closing_degree 0–100%, writable, default 100 How far the valve closes when it stops calling for heat
frost_protection_temperature 4–35°C Minimum temperature the valve will open for regardless of setpoint
child_lock on / off Disables the physical buttons on the unit
open_window on / off Built-in detection that halts heating on a rapid temperature drop

Two of those deserve more than a table row. valve_opening_degree and valve_closing_degree are not diagnostics — they are writable percentages, and together they let you pin the valve to a fixed position rather than letting it swing fully open and fully shut. The Zigbee2MQTT device page’s own worked example: set the opening degree to 30% and the closing degree to 70% and the valve effectively opens to 30%. Both have to be set together for that to hold; setting one alone will not give you a stable position. This pair is the practical answer to an over-powered radiator that cooks a small room, and it needs device firmware v1.1.4 or newer.

Warning

An issue on the zigbee-OTA tracker (#1062) reports that valve calibration is lost when the opening and closing degree settings are used. That report is filed by title here, not independently reproduced — but if you set these and the valve subsequently behaves as though it has forgotten its travel range, re-running calibration is the first thing to try rather than assuming the device has failed.

The genuinely read-only diagnostics are idle_steps and closing_steps, which report motor step counts from the self-calibration run. They’re mainly useful for working out whether the valve mechanism calibrated against the radiator correctly.

Separately, weekly_schedule_[day] entities (one per day) let you define a full seven-day heating schedule stored on the device itself, independent of any Home Assistant automation.

Note · context

Noisy or frequent local_temperature updates can make dashboards and history graphs busier than they need to be. If that becomes a problem, our piece on Zigbee2MQTT debounce vs throttle covers how to tame update frequency without losing data you actually need.

Calibrating against the valve’s sensor vs. using an external temperature sensor

By default the TRVZB heats off its own body-mounted temperature sensor. Because the valve sits directly on the radiator, that reading can run warmer than the actual room temperature a few feet away — the classic TRV complaint. Zigbee2MQTT gives you two ways to correct for this, and they are alternatives rather than a sequence.

Manual offset. local_temperature_calibration applies a fixed correction, from -12.7°C to +12.7°C in 0.2° steps, to whatever the internal sensor reads. This is the simplest fix if the valve is consistently off by a roughly constant amount — measure the real room temperature a few times, compare against local_temperature, and set an offset that closes the gap.

External sensor input. For a more accurate ongoing reading, temperature_sensor_select switches the valve’s control input away from its internal sensor to one of three external slots (external, external_2, external_3). The corresponding external_temperature_input property (0–99.9°C) then becomes what the valve heats against. Zigbee2MQTT’s documentation notes that when you do this, local temperature calibration is no longer needed — you are feeding the valve a reading you already trust, so there is nothing to offset.

manual test publish
mosquitto_pub -h 192.168.10.20 \
  -t zigbee2mqtt/radiator_livingroom/set \
  -m '{"temperature_sensor_select": "external", "external_temperature_input": 21.4}'
Warning

Nothing populates external_temperature_input for you. The Zigbee2MQTT device page states that synchronisation of this value with your actual external sensor has to happen outside Zigbee2MQTT — this is documented design, not a bug or a pending feature. You need an automation that republishes your chosen room sensor’s reading to the device’s /set topic every time it changes, and if that automation stops running the valve keeps heating against whatever value it last received.

The Home Assistant side is a standard automation: trigger on your chosen room-temperature sensor’s state change, then publish to the same /set topic and payload shape shown above.

automations.yaml
- alias: "Sync living room TRV external temperature"
  trigger:
    - platform: state
      entity_id: sensor.living_room_temperature
  action:
    - service: mqtt.publish
      data:
        topic: zigbee2mqtt/radiator_livingroom/set
        payload: >
          {"external_temperature_input": {{ trigger.to_state.state }}}

Community blueprints exist that package this pattern, including re-asserting the sensor selection. They are community contributions rather than a vendor-endorsed integration path, so read one before you import it. Worth knowing before you build on this: an open Zigbee2MQTT issue (#29650) is titled around temperature_sensor_select changing on its own — filed by title only here, not verified in depth, but if your valve silently reverts to its internal sensor you are not the first. Our guide to the Aqara SRTS-A01 radiator thermostat documents the same external-sensor-over-MQTT pattern on a different vendor’s TRV, including the reversion behaviour on that device, if you want a second implementation to compare against.

If you’d rather not maintain an automation, the manual offset is the lower-maintenance option — less accurate, but nothing to keep running.

Frost protection, open-window detection, and on-device schedules

frost_protection_temperature is a floor: regardless of what setpoint or schedule is active, the valve opens if the room drops to this temperature, which matters for radiators in rooms that go unheated for stretches. Read it for what it is, though — it opens this valve. If the boiler isn’t running or the heating system is off for the season, an open valve delivers nothing, so don’t treat it as whole-house freeze protection.

open_window is the device’s own rapid-temperature-drop detection, which pauses heating if it looks like a window just got opened. Like the on-device weekly schedule, it runs on the valve itself rather than as a Home Assistant automation, so both keep working if Home Assistant restarts or your Zigbee network hiccups. Most readers already running Home Assistant will prefer driving the valve through HA’s own climate automations and keeping the on-device schedule as a fallback.

Child lock

child_lock disables the physical buttons on the unit itself without affecting anything you control through Zigbee2MQTT or Home Assistant — useful if the valve is somewhere small hands or curious pets can reach it.

FAQ

Does the Sonoff TRVZB work without the eWeLink app or cloud? Yes. Paired through Zigbee2MQTT, it operates entirely over your local Zigbee network — no eWeLink account or cloud connection is involved in its normal operation. (If you’re weighing Sonoff’s app-based ecosystem generally, our piece on what eWeLink actually collects covers that separately, and our guide to Sonoff DIY mode covers the Wi-Fi side of Sonoff’s lineup, which works differently from this Zigbee device.)

How do I use an external temperature sensor with the Sonoff TRVZB? Set temperature_sensor_select to external, then keep external_temperature_input updated by publishing to the device’s Zigbee2MQTT /set topic — either manually, or automatically via a Home Assistant automation triggered on your room sensor’s state changes. Zigbee2MQTT does not sync this value for you by design.

How do I set the valve opening degree on the TRVZB? Publish valve_opening_degree and valve_closing_degree to the device’s /set topic as percentages from 0 to 100. Set both together — opening 30% with closing 70% holds the valve at roughly 30% open. Device firmware v1.1.4 or newer is required.

What valve thread size does the Sonoff TRVZB fit? M30 x 1.5mm natively, the common European radiator valve standard, with adapters in the box that Sonoff says cover most other common heating systems.

Does the TRVZB need a Sonoff Zigbee bridge, or does it pair directly with Zigbee2MQTT? It pairs directly with any Zigbee2MQTT-compatible coordinator. No Sonoff bridge or gateway is required for local operation.

Can I set a weekly heating schedule directly on the TRVZB without Home Assistant? Yes — the weekly_schedule_[day] entities store a full seven-day schedule on the device itself, independent of any Home Assistant automation.

What’s verified, what stays local, what to check yourself

The entity list, ranges, the opening/closing degree mechanics, and the external-sensor behaviour above come from the official Zigbee2MQTT device reference page for the TRVZB, retrieved for this article in August 2026. The M30 x 1.5mm thread spec, the included adapters, and the 6+ month battery figure come from Sonoff’s own product pages — the battery figure is a vendor claim, not a measured one. No hands-on testing of this device was performed for this piece.

Two items are weaker than the rest and flagged as such in the text: the calibration-loss report and the sensor-selection-drift report are cited by issue title, not reproduced or read in full. If either matches what you’re seeing, read the tracker rather than this article. Entity names and available settings also move with Zigbee2MQTT releases and device firmware, so your own device page is the authority for your install.

Everything described — pairing, the exposed entities, calibration, valve positioning, scheduling, and the external-sensor workflow — operates entirely over your local Zigbee network through Zigbee2MQTT. Nothing depends on the eWeLink app or a Sonoff cloud account. The one moving part outside the device is the external-sensor sync, which depends on an automation you maintain; check after setup that it is actually firing rather than assuming it is.

local-firstHome Assistantno-cloud