A tap on the On/off switch of a wide or full-page tile moved the knob to on and back, and the light stayed off. Since firmware 0.2.59 control_event drew the new position first (Tile::optimistic writes it into state) and then asked key_action for the action, which read that new position: an off light got light.turn_off, an on light light.turn_on. A tap on the tile itself was never affected. tile_controls::press_key chooses the action first and then shows the new position; control_event uses it. Unit tests cover a light, a fan, a sensor and a cover key. Checked with tools/check.sh (471 Python tests, 19/19 C++) and compiled for both boards: CYD 1,677,504 B (91.4 %, +16 B), Guition 2,054,640 B (25.3 %). On Max's Guition Wallbox (OTA from this branch) every tap on the switch sent the right action and Home Assistant's logbook followed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4.1 KiB
Test results app 0.2.86 / firmware 0.2.72 (2026-09-19)
Max reported that a tap on the on/off switch at the right of a wide tile went "from off to on to off in one click",
as if it registered a double click. A tap on the tile itself worked. The tile was light.badkamer_3 on his Guition
Wallbox, a wide tile with the control On/off switch.
Cause
No double tap: the switch sent the opposite of what it showed. runtime_tiles::control_event did two things in the
wrong order since firmware 0.2.59 (app 0.2.70, the "tap looks instant" round):
Tile::optimistic(t.state != "on")draws the new position at once. Since 0.2.59 it writes that position intoTile::state(before, a separateoptimistic_onfield held it andstatestayed as it was).tile_controls::key_action(t, TOGGLE)then choseturn_offwhenstatewas "on", which it now always was for a light that had been off.
So an off light got light.turn_off. Home Assistant answered "it worked" without a new state, the wait ended 800 ms
after that answer, end_wait undid the optimistic position, and the knob slid back to off. An on light got
light.turn_on the same way. The other three callers of optimistic() (the tile's own tap, the light card's switch,
a slider on an off light) choose their action before they call it, or don't read state afterwards.
Evidence before the fix:
- The two lines of
control_event, run against the released headers (origin/main311fa2a), printlight off -> knob shows on, sends light.turn_offandlight on -> knob shows off, sends light.turn_on. - Home Assistant's logbook for
light.badkamer_3only hadlight.togglestate changes from the screen (taps on the tile). Aturn_offsent to a light that is off changes nothing, so it leaves no line in the logbook.
What changed
tile_controls::press_key(Tile &, command, arg): chooses the action withkey_actionfirst, then shows a toggle's new position withoptimistic(), and only when there is an action to send.runtime_tiles::control_eventcallspress_keyinstead of the two lines.tests/test_tile_controls.cpp: an off light sendslight.turn_onand shows on, and undoing that brings back off; an on fan sendsfan.turn_offand shows off; a toggle on a sensor sends nothing and leaves its state alone; a cover's open key leaves the state alone.
Automated
tools/check.sh: Python 471 tests, C++ 19/19, packages, icons, Vitest, vue-tsc, the editor build and the bundle in Git: all pass.tools/check.sh --firmware --baseline 1677488(ESPHome 2026.6.2): CYD 1,677,504 B = 91.4 % of the 1,835,008 B slot, +16 B against 0.2.85 (tight band, well under the 8 KB rule); Guition 2,054,640 B = 25.3 %, the same as 0.2.85. Built before the version bump, which only changes the version text (same length).
On the screen
With Max's OK the fix went over the air to his Guition Wallbox (192.168.146.136). The YAML was the one his ESPHome
Device Builder holds for it, with the packages and components of this branch instead of GitHub. That build still says
0.2.71, so ESP Screens offers the released 0.2.72 afterwards. device_info read back the build's compile time
(2026-09-19 08:10:24).
Max then tapped the switch and the tile, and the screen's log and Home Assistant's logbook agree on every tap:
| Time | Where | Sent | Light after |
|---|---|---|---|
| 08:13:12.9 | switch (light on) | light.turn_off |
off |
| 08:13:14.5 | switch | light.turn_on |
on |
| 08:13:15.8 | switch | light.turn_off |
off |
| 08:13:16.5 | tile | light.toggle |
on |
| 08:13:17.1 | tile | light.toggle |
off |
| 08:13:17.6 | switch | light.turn_on |
on |
| 08:13:19.2 | switch | light.turn_off |
off |
Home Assistant reported each new state 220-430 ms after the send. One tap at 08:13:18.1, 526 ms after the one
before, was dropped by the 600 ms same-control debounce (TouchGuard::accept), as before. Max: "werkt super!".
Not tested
- The switch of a switch, an input boolean or a fan on real hardware: they go through the same
press_keyline and the unit tests. - The CYD: the fix is the same shared code. Its screen has no tile with a switch now, and it gets 0.2.72 through Update.