Let the on/off switch on a wide tile switch, instead of sending the opposite of what it shows (0.2.86)
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>
This commit is contained in:
@@ -2872,8 +2872,7 @@ inline void control_event(lv_event_t *e) {
|
||||
return;
|
||||
}
|
||||
if(t.waiting(now))return;
|
||||
if(command==tile_controls::TOGGLE)t.optimistic(t.state!="on");
|
||||
auto a=tile_controls::key_action(t,command,w.key_args[n]);
|
||||
auto a=tile_controls::press_key(t,command,w.key_args[n]);
|
||||
if(a.valid())action(a.service,t.entity,a.key,a.value);
|
||||
}
|
||||
// A busy card is covered by a translucent white sheet with a small spinner until
|
||||
|
||||
@@ -368,6 +368,14 @@ inline Action key_action(const Tile &t, int command, const std::string &arg = ""
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
// A tap on a key: the action follows the stand the tile had, and a toggle then shows its new stand at once. The order
|
||||
// matters, because Tile::optimistic writes the new stand into `state`: asked afterwards, key_action sent an off light
|
||||
// light.turn_off, so the knob flipped on and back while the light stayed off (firmware 0.2.59 to 0.2.71).
|
||||
inline Action press_key(Tile &t, int command, const std::string &arg = "") {
|
||||
Action a = key_action(t, command, arg);
|
||||
if (command == TOGGLE && a.valid()) t.optimistic(t.state != "on");
|
||||
return a;
|
||||
}
|
||||
// ---- Vacuum card rows (firmware 0.2.39+) ----
|
||||
// The value a row shows as chosen: the one just tapped while Home Assistant has not answered yet.
|
||||
inline const std::string &shown_value(const Tile &t, const runtime_tiles::Choice &c, uint32_t now) {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# 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):
|
||||
|
||||
1. `Tile::optimistic(t.state != "on")` draws the new position at once. Since 0.2.59 it writes that position into
|
||||
`Tile::state` (before, a separate `optimistic_on` field held it and `state` stayed as it was).
|
||||
2. `tile_controls::key_action(t, TOGGLE)` then chose `turn_off` when `state` was "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/main 311fa2a), print
|
||||
`light off -> knob shows on, sends light.turn_off` and `light on -> knob shows off, sends light.turn_on`.
|
||||
- Home Assistant's logbook for `light.badkamer_3` only had `light.toggle` state changes from the screen (taps on
|
||||
the tile). A `turn_off` sent 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 with `key_action` first, then shows a toggle's
|
||||
new position with `optimistic()`, and only when there is an action to send.
|
||||
- `runtime_tiles::control_event` calls `press_key` instead of the two lines.
|
||||
- `tests/test_tile_controls.cpp`: an off light sends `light.turn_on` and shows on, and undoing that brings back off;
|
||||
an on fan sends `fan.turn_off` and 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_key` line
|
||||
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**.
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
# fonts/materialdesignicons-webfont.ttf (Apache 2.0, github.com/Templarian/MaterialDesign-Webfont).
|
||||
############################################################
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.71"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.72"
|
||||
DASHBOARD_INBOX_INTERNAL: "false"
|
||||
CALIBRATION_ON_BOOT: "false" # a bench build: start on the touch test page
|
||||
ROOM_NAME: "Choose tiles in HA" # shown top left until ESP Screens sends the screen name
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
## 0.2.86 (firmware 0.2.72)
|
||||
|
||||
The on/off switch on a wide tile switches again.
|
||||
|
||||
- **The switch on a wide or full-page tile does what it shows.** Since firmware 0.2.59 (app 0.2.70), a tap on the *On/off switch* of a wide card sent the opposite of what it showed. The knob moved to on, but the screen asked Home Assistant to turn the light *off*. The light already was off, so nothing happened, and a moment later the knob slid back. On a light that was on, the same happened the other way round. The switch drew its new position before it chose the action, and then chose from that new position; it now chooses first. A tap on the tile itself was never affected. This fixes lights, switches, input booleans and fans. The switch is what a wide tile of these shows unless you choose another control, so most wide tiles of them had it.
|
||||
- Needs firmware 0.2.72: press **Update** on the screen. Includes everything from 0.2.85. CYD firmware: 1,677,504 bytes, 91.4 % of the update slot (16 bytes more than 0.2.85).
|
||||
|
||||
## 0.2.85 (firmware 0.2.71)
|
||||
|
||||
Off looks off on every tile, in Home Assistant's colours.
|
||||
|
||||
@@ -37,7 +37,7 @@ WEEKDAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']
|
||||
REPO = 'https://github.com/MaxGramser/homeassistant_espscreen'
|
||||
REFS = {'cyd': 'main', 'guition': 'main'}
|
||||
# Firmware shipped with this app release; screens below it get an update offer.
|
||||
FIRMWARE_VERSION = '0.2.71'
|
||||
FIRMWARE_VERSION = '0.2.72'
|
||||
# The Auto standby switch a screen offers Home Assistant automations.
|
||||
AUTO_STANDBY_MIN_FIRMWARE = '0.2.41'
|
||||
# The settings page the screen opens itself, and the screen.settings tile that opens it.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: ESP Screen Manager
|
||||
version: "0.2.85"
|
||||
version: "0.2.86"
|
||||
slug: esp_screen_manager
|
||||
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
|
||||
url: https://github.com/MaxGramser/homeassistant_espscreen
|
||||
|
||||
@@ -128,6 +128,17 @@ int main() {
|
||||
assert(key_action(make("light.lamp", "off"), TOGGLE).service == "light.turn_on");
|
||||
assert(!key_action(make("sensor.x", "1"), TOGGLE).valid());
|
||||
assert(!key_action(make("sensor.x", "1"), RUN).valid());
|
||||
// A toggle key sends what its tap asks for and then shows the new stand; firmware 0.2.59 to 0.2.71 showed it first
|
||||
// and so sent an off light light.turn_off.
|
||||
Tile off_lamp = make("light.lamp", "off");
|
||||
assert(press_key(off_lamp, TOGGLE).service == "light.turn_on" && off_lamp.state == "on" && off_lamp.optimistic_tap);
|
||||
off_lamp.undo_optimistic(); assert(off_lamp.state == "off");
|
||||
Tile on_fan = make("fan.ceiling", "on");
|
||||
assert(press_key(on_fan, TOGGLE).service == "fan.turn_off" && on_fan.state == "off");
|
||||
Tile odd = make("sensor.x", "1");
|
||||
assert(!press_key(odd, TOGGLE).valid() && odd.state == "1" && !odd.optimistic_tap);
|
||||
Tile shut = make("cover.shutter", "closed", 3);
|
||||
assert(press_key(shut, COVER_OPEN).service == "cover.open_cover" && shut.state == "closed" && !shut.optimistic_tap);
|
||||
|
||||
// Panel kinds.
|
||||
Tile lamp = make("light.lamp", "on"); lamp.controls = "brightness";
|
||||
|
||||
Reference in New Issue
Block a user