Update, October 2026. The first version of this post, from June, explained a symptom on my KNX room controllers with a cause I had never checked. It said that only the Auto preset lets you set a target temperature from Home Assistant, that the Comfort and Economy setpoints are fixed in the device, and that the controller overrules whatever you write. The tests on 23 October showed that writing the base changed the reported setpoint without any mode command before it. I could not read the controller's actual mode during the tests, and I never tested the advice to switch to Auto first.
On the standard Home Assistant thermostat card I set a temperature, and a moment later the card showed a different value, in my tests one or two degrees lower. My writes reached the controller while Home Assistant showed Comfort; the controller's actual mode I could not read from the device. The value the card shows afterwards is a different one, reported by the controller. This post covers the tests from 23 October and the script and dashboard card I built for the 11 room controllers in the house.
The five HVAC modes on a KNX climate entity
The controllers take the standard KNX HVAC mode (DPT 20.102) with five states: Auto, Comfort, Standby, Economy and Building Protection. Home Assistant shows them as preset modes. My automations use Comfort for the day, Economy for the night and Building Protection while a window is open. According to the controller documentation, Standby is a setback mode. The earlier version of this post treated it as heating off, which the documentation does not support.
The card cannot tell you which mode the controller is really in. In the ETS project my controllers have no readable status object for the operating mode. Home Assistant sends mode changes to the command object. After a restart, reading that object returns Standby even if Home Assistant had sent Comfort before, and the controller's actual mode cannot be checked through it. The preset shown is only reliable when Home Assistant wrote it itself, which is the background of the Standby-after-restart post.
Two values behind one Home Assistant thermostat card
According to the manufacturer documentation and the ETS export, each of the 11 JUNG controllers has a writable input for the comfort base temperature and a readable output for the current setpoint. Home Assistant uses the first as target temperature address and the second as target temperature state address. The controller adds the operating mode and any local shift at the device on top of the base. The climate entities live in knx.yaml, like the switches since I moved them out of the UI. The excerpt shows them under the knx: key, as in a package file; with knx: !include knx.yaml that outer key is left out. The new sensor is the second block:
# knx.yaml — one room controller (group addresses are placeholders)
knx:
climate:
- name: "Thermostat Room A"
temperature_address: "9/0/3" # room temperature (read)
target_temperature_address: "9/1/3" # comfort base temperature (write)
target_temperature_state_address: "9/2/3" # active setpoint the controller reports
operation_mode_address: "9/3/3" # HVAC mode command, no readable status
temperature_step: 0.5
min_temp: 18.0
max_temp: 25.0
sensor:
# separate read-only sensor on the same active-setpoint address
- name: "RC setpoint Room A"
state_address: "9/2/3"
type: temperature
sync_state: init
always_callback: true # every answer updates last_updated, even with an unchanged value
Reading the base back does not work here. The comfort input was not meant to be read, its read flag was added in the ETS later, and the answers decode to values like 0.21 °C, for reasons I have not found. The only reliable feedback is the active setpoint.
What the tests on 23 October showed
On the controller I tested I wrote a base of 23 °C, without any mode command. Home Assistant showed Comfort, which as described above is no confirmation from the device. The controller reported 21 °C, on its own and on a fresh read request. A base of 25 °C gave 23 °C, which looked like a fixed difference of −2 K.
Then I raised the setpoint by one degree at the controller itself, and it reported 24 °C. From the Home Assistant dialog I lowered it to 23 °C, Home Assistant wrote a base of 23 °C, and the controller reported 22 °C. The difference was now −1 K, so an automation that always adds 2 K would have been wrong after one button press on the wall. Whether the original −2 K came from the operating mode or a local shift I do not know.
Half degrees did not arrive exactly. Aiming for 23.5 °C, a base of 23.5 gave 23, a base of 24 gave 23, and a base of 24.5 gave 24. I tested this on one controller only and limited the new control to whole degrees for all of them.
I did not switch any controller to Auto during these tests and have not tested what Auto does to the reported setpoint. The documentation also describes writing the comfort base in Comfort mode; in my test, writing worked without first sending a mode command, though I could not verify the controller's mode.
The fix: a read-back sensor and a correcting script
Each controller now has its own read-only KNX sensor on the active-setpoint address. With always_callback set, every answer updates the sensor's last_updated timestamp, even when the value is unchanged, so the script can tell a fresh answer from a value Home Assistant already had.
The script takes a room and a wanted temperature in whole degrees from 18 to 25 °C. It requests a fresh value and waits up to 5 seconds. If the controller already reports the wanted value, it stops without writing. Otherwise it writes a base, waits one second and reads again. For the next attempt, the script adds the difference between the wanted and the reported value to the last base it wrote; it determines that difference again on every call. After three writes, or when the next base would leave 18–25 °C, it stops with an error and the last reported value. It reports success only after the controller has returned the wanted setpoint twice, one second apart. The excerpt below shows only the correction loop; the room mapping, the variable setup and the separate read script are left out.
# packages/raumcontroller.yaml — shortened
script:
rc_set_temperature:
mode: single # one job at a time, no competing correction loops
fields:
entity_id: {} # climate entity of the room
temperature: {} # wanted active setpoint, whole degrees 18–25
sequence:
# fresh read: knx.read, then wait up to 5 s for a newer last_updated
- &read_fresh
action: script.rc_read_setpoint
data: { address: "{{ room.address }}", sensor: "{{ room.sensor }}" }
response_variable: reading
# (already right -> report success without writing; omitted here)
- repeat:
count: 3
sequence:
- if: "{{ candidate < 18 or candidate > 25 }}"
then:
- stop: "Not reachable within the base limits 18–25 °C."
response_variable: result # ok: false plus the last reported setpoint
- action: climate.set_temperature # writes the comfort base
target: { entity_id: "{{ entity_id }}" }
data: { temperature: "{{ candidate }}" }
- delay: { seconds: 1 }
- *read_fresh
- variables:
actual: "{{ reading.actual }}" # use the new reading for the next step
# (no fresh answer -> error result; omitted)
# (match -> wait 1 s, read again, success only on a second match; omitted)
- variables:
candidate: "{{ (candidate + wanted - actual) | round(2) }}"
The script runs in single mode, and it does not hold the temperature afterwards: later changes at the controller or by the schedule apply as usual. With the measured difference of −1 K, an active setpoint of 25 °C would need a base of 26 °C, which Home Assistant is not allowed to write, so the card shows an error. Any other climate.set_temperature call, from another dashboard or a voice assistant, still writes the base directly and skips all of this, so automations should call the script.
The dashboard card and the tests
All 11 standard thermostat tiles on my main dashboard are replaced by a small custom card with the room temperature, the active setpoint from the new sensor, and a field with plus and minus buttons in whole degrees. An empty field starts from the last reported setpoint. Nothing is sent until Apply starts the script. During a job the controls are locked, at 18 and 25 °C the respective button is disabled, and the result stays on the card, errors included.
Eleven automated tests run the script's actual YAML sequences against a simulated bus: positive, negative and changing differences, a value that is already right, missing answers at each step, the base limits, too many attempts and invalid input. Browser tests cover the card. On the real controller I requested 24 °C and read back 24 °C after a base of 25 °C; the next request for 23 °C read back 23 °C at a base of 24 °C.
Automations that switch presets
The heating automations never write a temperature, only the preset, so the setpoint problem did not affect them. A helper called heating night mode goes on at 21:00 and off at 05:00, which sets all 11 controllers to Economy or Comfort. The earlier version said 06:00 because the old automation's entity id says so, though it has always run at 05:00. Night was Standby in one place and Economy in two. On the new instance I set all three to Economy and made the helper keep its state across a restart.
The window logic covers eight rooms. After a window has been open for 5 seconds, that room goes to Building Protection, and 5 seconds after closing it returns to the mode that currently applies. On the old instance this was 16 copied UI automations, and closing always set Comfort, also at night. On the new instance it is one automation with a table from window to controller, and the day and night automations skip rooms with an open window. More in the window post.
Without a readable mode status, sending the mode again after a start is the only way to a known state. The startup automation waits 30 seconds, then sets the night or day mode, again skipping open windows. On the old instance it has been disabled since January. The new heating automations described in this section are set up on the new instance but stay disabled until I move heating over; until then the old instance runs the schedule and the window logic. The setpoint sensors, the script and the card are already in use on the new instance. Whether I keep that day-or-night choice at startup when I move heating over is not decided yet.
Frequently asked questions
Why does my KNX thermostat show a different temperature than I set in Home Assistant?
On controllers like mine, Home Assistant sends the comfort base and then displays the setpoint the controller reports, which includes the operating mode and any local shift. The standard card has room for one target value only.
Do I have to switch to Auto before changing the temperature?
My writes reached the JUNG controller without switching modes first. I have not tested Auto itself or its effect on the reported setpoint.
Can I compensate the difference with a fixed offset?
Not reliably. The difference I measured went from −2 K to −1 K after one local adjustment, which is why the script recalculates it from fresh read-backs on every call and gives up after three attempts.
Top comments (0)