Home AssistantESPHome

Sonoff TRVZB external temperature

When a room sensor reports, write that temperature into one or more Sonoff TRVZB external-temperature inputs (Zigbee2MQTT 2.1.2 or newer) so the valves follow the room instead of the sensor inside the radiator. An ESPHome DHT22 or AHT20, or any temperature sensor, can be the source.

Difficulty · IntermediateSetup · 45–90 minutesHA · current stable (blueprint)ESPHome · current stable
Get the pack · from $19Have us set it up

Some hardware links on this page are affiliate links. As an Amazon Associate I earn from qualifying purchases. We may earn a commission if you buy through them, at no extra cost to you. See the affiliate disclosure.

A TRV bolted to a radiator measures the air next to the hot metal, then throttles the valve from that. The room can be degrees cooler. Zigbee2MQTT 2.1.2 or newer exposes an external-temperature input on the Sonoff TRVZB (firmware 1.2.1 or newer). This workflow copies a room sensor into that input whenever the sensor reports, for one valve or every valve in the room.

The valve then uses that reading as the temperature. It does not keep using its internal sensor plus a calibration offset. An offset still trusts the internal sensor and only shifts it. This pack replaces the source.

Entity ids are blueprint inputs. Do not publish a valve’s Zigbee id or a room-by-room radiator list.

The room node is the same build as the ESP32-C6 climate sensor. Pausing heat while a window is open is window-open climate pause.

What problem it solves

You want one trustworthy room temperature to drive one or more TRVZB valves, including when the valve’s own sensor sits in a warm corner, without a second thermostat integration rewriting setpoints.

How it works

  1. External mode first — in Zigbee2MQTT, set each valve to use an external temperature sensor. The converter then exposes a number entity (device class temperature) for the external reading. Writing that number while the valve is still on its internal sensor does not change what the valve controls from.
  2. Copy on report — the blueprint triggers on every state change of the room temperature sensor, including attribute-only updates, and calls number.set_value on every valve you picked. The value is the sensor state, not a delta.
  3. Fail-safe — if a valve configured for an external sensor receives nothing for more than two hours, it falls back to its internal sensor. That protects against a dead battery or a disabled automation. The next external write switches it back. The room sensor must report at least once inside that window even when the temperature is flat.
  4. ESPHome source — an AHT20 or DHT22 on an ESP32 publishes on a fixed interval (pack default 60 seconds). Flat temperature still produces writes, so the two-hour fallback stays asleep.
  5. Zigbee source — a battery sensor often reports only after a minimum change, and Zigbee2MQTT hides repeats when the value did not change. Set the sensor’s maximum reporting interval under two hours (an hour is a safe start) and enable force_update for that temperature in Zigbee2MQTT’s device config so an unchanged report still reaches Home Assistant. The key in that file is the id Zigbee2MQTT already assigned. Do not copy an id from someone else’s network.
  6. Several valves, one room — the number action accepts a list. One sensor updates all of them. A second room gets a second automation and a second sensor.

Reset any old local-calibration offset to zero before you switch. A leftover offset plus an external reading will not match the room.

Hardware notes

  • Valve. Sonoff TRVZB on Zigbee2MQTT 2.1.2 or newer. Other brands do not expose this input. The valve wants its own batteries (the pack does not include them) and a Zigbee coordinator you already run. Pair it and confirm the external-temperature number exists in Developer Tools before you import anything.
  • Room sensor. AHT20 on I²C is the steadier reading. DHT22 on one GPIO is fine if that is the module you have; it is noisier, so the valve will see more small steps. Mount the sensor in the occupied part of the room, not on the radiator and not in sun.
  • Placement. Keep the ESP32 and its USB supply away from the sensing element. The climate-sensor pack’s wiring applies: AHT20 on 3.3 V, SDA/SCL on the Super Mini’s GPIO0 and GPIO1. Leave GPIO8, GPIO9, GPIO12, GPIO13, and GPIO15 alone.
  • Wi-Fi. Credentials stay in secrets.yaml. Never paste an SSID, a PSK, or an API key into a published YAML.

Snippet

Room node (substitutions). AHT20 shown; the pack includes the DHT22 one-wire variant.

substitutions:
  friendly_name: Office climate
  sda_pin: GPIO0
  scl_pin: GPIO1
  update_seconds: "60"

sensor:
  - platform: aht10
    variant: AHT20
    temperature:
      name: Temperature
      id: room_t
    humidity:
      name: Humidity
    update_interval: ${update_seconds}s

DHT22 alternative, if that is the module on the bench:

substitutions:
  friendly_name: Office climate
  data_pin: GPIO4

sensor:
  - platform: dht
    pin: ${data_pin}
    model: DHT22
    temperature:
      name: Temperature
    humidity:
      name: Humidity
    update_interval: 60s

The automation (blueprint inputs replace entity ids):

trigger:
  - platform: state
    entity_id: !input room_temperature
action:
  - service: number.set_value
    target:
      entity_id: !input trv_external_temperature
    data:
      value: "{{ states(room_temperature) | float(none) }}"

Skip the write when the state is unknown or unavailable. The pack’s condition does that. A bare float without a skip will push a bad number.

Setup outline

  1. Pair each TRVZB. Update firmware if the external-temperature number is missing. In Zigbee2MQTT, select external temperature as the sensor source. Confirm a number entity per valve.
  2. Flash the room node, or pick a temperature sensor you already have. Watch it for a few minutes. An ESPHome node should update on the interval even when the value is unchanged.
  3. For a Zigbee sensor, set max reporting interval below two hours and force_update on temperature. Clear any old calibration offset on the valves.
  4. Import the blueprint. Select the room sensor and every valve number in that room.
  5. Test: warm the room sensor in your hand. Each valve’s external-temperature number should follow within one report. Pull the sensor’s battery or disable the automation for more than two hours and the valve should fall back; the next report should return it to the external reading.

Troubleshooting

  • The number never moves. External mode is off, the blueprint is pointed at the internal temperature sensor (a sensor, not the external number), or the trigger entity is not updating. Developer Tools → the number entity → set a value by hand. If that works, the automation is the gap.
  • It works, then the valve ignores the room. The two-hour fail-safe ran. The sensor stopped reporting, Zigbee2MQTT swallowed an unchanged value, or the automation was off. Check last_changed on the room sensor overnight.
  • The room and the valve disagree by a fixed amount. A local calibration offset is still applied. Set that offset back to zero. This pack does not use it.
  • DHT22 jumps. That module is noisy. Prefer the AHT20, or add a short throttle filter so the valve is not chasing half-degree noise.
  • One valve in a row stays on its internal sensor. It was not in the entity list, or that device’s firmware is older than 1.2.1. The action is per number entity; a group of climate entities will not do this.
  • Zigbee2MQTT has no external-temperature number. The add-on is older than 2.1.2, or the herd converter for the TRVZB was not updated. Upgrade Zigbee2MQTT, re-interview the device, and look again.

Credits

Based on the community blueprint Sonoff TRVZB External Temperature Sensor Calibration. This write-up is an original overview of pushing a room sensor (ESPHome AHT20 / DHT22 or Zigbee) into the valve’s external-temperature input, including the two-hour fail-safe.

Changelog

v1.0 · Oct 21, 2026: First publish.