ESPHomeESP32Home Assistant

CYD touch thermostat

A single-purpose room thermostat on the ESP32-2432S028R Cheap Yellow Display. Touch steps the target temperature, and current and target readings sync from Home Assistant.

Difficulty · IntermediateSetup · 45–90 minutesHA · current stable (native API)ESPHome · current stable
Get the pack · from $9Have 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.

The 12-button CYD touch panel is a grid of scenes on the same Cheap Yellow Display. This pack is one job: a wall thermostat. Plus and minus change the target temperature, heat and off change the mode, and the screen shows the room’s current temperature next to the target Home Assistant is actually holding.

Entity ids stay in substitutions (climate.room, sensor.room_temperature). Do not paste a live thermostat name into a published YAML. Wi-Fi credentials stay in secrets.yaml.

If the number on the climate entity should follow the weather, pair this display with adaptive comfort setpoint. If an open sash should shut the equipment off, pair it with window-open climate pause. A TRV that ignores its own sensor can take the same room reading through Sonoff TRVZB external temperature.

What problem it solves

You want to bump the heat from the hallway without unlocking a phone, and you want the number on the wall to be a room sensor, not the probe buried in a radiator valve.

How it works

  1. One screen, four touches — stock CYD pinout (ILI9341 SPI, XPT2046 touch, backlight on GPIO). The face shows current temperature, target temperature, and four zones: minus, plus, heat, off. It is not a button grid and it does not toggle lights.
  2. Current from the room — a homeassistant sensor subscribes to sensor.room_temperature. A valve’s built-in probe sits on the radiator and in the draft from the window, so the pack does not use it for the big number. A commented alternate reads current_temperature from the climate entity if that really is the room.
  3. Target from the climate entity — a second sensor reads the temperature attribute. The screen follows whatever last wrote the setpoint, including a dashboard, a schedule, or another automation.
  4. Plus and minus — a touch calls climate.set_temperature with the current target plus or minus a step (pack default 0.5). The buttons only run while the entity state is heat, so a tap in off does not invent a setpoint.
  5. Heat and off — those zones call climate.turn_on and climate.turn_off. ESPHome is not allowed to call Home Assistant actions until you enable that on the device.

An optional one-hour graph plots the room sensor as a solid line and the target as a dashed line. The graph is a picture of history. It does not control anything.

Hardware notes

  • Board. ESP32-2432S028R, the common 2.8″ resistive “CYD”. A capacitive rev needs a different touch component. The stock pinout is the same one the 12-button panel comments. This YAML does not reuse that grid.
  • Power. 5 V USB. Flash over USB the first time. Later updates can go over the air. A flickering backlight is usually a weak supply, not the display driver.
  • Sensors. Any temperature sensor already in Home Assistant. The display does not measure the room. Put the sensor in the occupied air.
  • Climate. Whatever entity already heats. heat uses the temperature field. heat_cool often wants target_temp_low instead. The pack has a substitution for which field to write. Confirm it in Developer Tools before you trust the buttons.

Snippet

Substitutions at the top. One minus zone, trimmed. Plus is the same lambda with the step added.

substitutions:
  thermostat_entity: "climate.room"
  temperature_entity: "sensor.room_temperature"
  step: "0.5"

sensor:
  - platform: homeassistant
    id: current_temperature
    entity_id: ${temperature_entity}
    internal: true
  - platform: homeassistant
    id: target_temperature
    entity_id: ${thermostat_entity}
    attribute: temperature
    internal: true

text_sensor:
  - platform: homeassistant
    id: thermostat_mode
    entity_id: ${thermostat_entity}
    internal: true

binary_sensor:
  - platform: touchscreen
    id: minus_zone
    # pixel box for the minus control; the other three zones live in the pack
    on_press:
      - if:
          condition:
            text_sensor.state:
              id: thermostat_mode
              state: "heat"
          then:
            - homeassistant.action:
                action: climate.set_temperature
                data:
                  entity_id: ${thermostat_entity}
                  temperature: !lambda |-
                    return id(target_temperature).state - ${step};
# display, touch calibration, heat/off zones: full YAML in the pack

The display prints id(current_temperature).state as the room and id(target_temperature).state as the target, and it prints Off when thermostat_mode is off.

Setup outline

  1. Add the CYD in ESPHome over USB. Confirm the backlight and that a tap shows up in the log.
  2. Paste the pack YAML. Set thermostat_entity and temperature_entity. Leave the step at 0.5 unless the climate entity only accepts whole degrees.
  3. Adopt the node. In Settings → Devices → ESPHome, allow the device to perform Home Assistant actions.
  4. With the climate entity in heat, tap plus and minus. The target on the screen and the attribute in Developer Tools should move by one step. Tap off, then heat, and confirm the mode follows.
  5. Change the setpoint from a dashboard. The target on the CYD should catch up without a tap.

Troubleshooting

  • White screen. Backlight GPIO or dc_pin does not match this CYD rev (one USB port versus two). Use the stock pin comments in the pack, not a pinout from a different display.
  • Taps hit the wrong zone. Resistive calibration or swap_xy is off. Recalibrate. Do not rotate the display a second time if the driver already rotated it.
  • Tap does nothing. Action permission is off, the entity id is wrong, or the mode is not heat (plus and minus are gated). heat_cool entities that ignore temperature need the low or high field instead.
  • Target on screen is stuck, room number moves. The attribute sensor is pointed at the wrong field. Read the climate entity in Developer Tools and match temperature, target_temp_low, or target_temp_high.
  • Room number is nonsense. The sensor is in °F and the face format assumes °C, or the entity is unavailable. The pack does not convert units. Fix the sensor, or change the format string to the unit you actually store.
  • Setpoint runs away. Something else is also writing climate.set_temperature (a schedule, adaptive comfort, a TRV calibration). This display only sends a step when a zone is pressed.

Credits

Based on cyd-thermostat (ESPHome on an ESP32-2432S028R, touch zones calling the climate entity, room temperature from a separate sensor). This write-up is an original single-purpose thermostat face. It is not the 12-button scene panel, and it is not a paste of that repository.

Changelog

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