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;
|
return;
|
||||||
}
|
}
|
||||||
if(t.waiting(now))return;
|
if(t.waiting(now))return;
|
||||||
if(command==tile_controls::TOGGLE)t.optimistic(t.state!="on");
|
auto a=tile_controls::press_key(t,command,w.key_args[n]);
|
||||||
auto a=tile_controls::key_action(t,command,w.key_args[n]);
|
|
||||||
if(a.valid())action(a.service,t.entity,a.key,a.value);
|
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
|
// 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 {};
|
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+) ----
|
// ---- 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.
|
// 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) {
|
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).
|
# fonts/materialdesignicons-webfont.ttf (Apache 2.0, github.com/Templarian/MaterialDesign-Webfont).
|
||||||
############################################################
|
############################################################
|
||||||
substitutions:
|
substitutions:
|
||||||
SCREEN_FIRMWARE_VERSION: "0.2.71"
|
SCREEN_FIRMWARE_VERSION: "0.2.72"
|
||||||
DASHBOARD_INBOX_INTERNAL: "false"
|
DASHBOARD_INBOX_INTERNAL: "false"
|
||||||
CALIBRATION_ON_BOOT: "false" # a bench build: start on the touch test page
|
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
|
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)
|
## 0.2.85 (firmware 0.2.71)
|
||||||
|
|
||||||
Off looks off on every tile, in Home Assistant's colours.
|
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'
|
REPO = 'https://github.com/MaxGramser/homeassistant_espscreen'
|
||||||
REFS = {'cyd': 'main', 'guition': 'main'}
|
REFS = {'cyd': 'main', 'guition': 'main'}
|
||||||
# Firmware shipped with this app release; screens below it get an update offer.
|
# 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.
|
# The Auto standby switch a screen offers Home Assistant automations.
|
||||||
AUTO_STANDBY_MIN_FIRMWARE = '0.2.41'
|
AUTO_STANDBY_MIN_FIRMWARE = '0.2.41'
|
||||||
# The settings page the screen opens itself, and the screen.settings tile that opens it.
|
# The settings page the screen opens itself, and the screen.settings tile that opens it.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: ESP Screen Manager
|
name: ESP Screen Manager
|
||||||
version: "0.2.85"
|
version: "0.2.86"
|
||||||
slug: esp_screen_manager
|
slug: esp_screen_manager
|
||||||
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
|
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
|
||||||
url: https://github.com/MaxGramser/homeassistant_espscreen
|
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("light.lamp", "off"), TOGGLE).service == "light.turn_on");
|
||||||
assert(!key_action(make("sensor.x", "1"), TOGGLE).valid());
|
assert(!key_action(make("sensor.x", "1"), TOGGLE).valid());
|
||||||
assert(!key_action(make("sensor.x", "1"), RUN).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.
|
// Panel kinds.
|
||||||
Tile lamp = make("light.lamp", "on"); lamp.controls = "brightness";
|
Tile lamp = make("light.lamp", "on"); lamp.controls = "brightness";
|
||||||
|
|||||||
Reference in New Issue
Block a user