Close cards through one path so the page swipe is never left blocked (0.2.32)

Read live on a Guition: every edge swipe was recognised but refused by the
"card open" check, because the light, climate and vacuum cards cleared
their overlay on close (X, tap beside the card, wake from standby) without
forgetting active_entity. One opened card blocked swiping until the next
layout change; the old whole-screen gesture and the CYD had the same gate.
Both boards now close cards through one script, close_cards, which hides
every overlay and clears that state; wake_display uses it too.

The Guition edge swipe now follows LVGL's own pointer events
(lv_indev_add_event_cb: PRESSED begins, PRESSING flips the page and calls
lv_indev_wait_release from inside LVGL) instead of ESPHome's touchscreen
on_update, so the point arrives already rotated. The direction rule is 45
degrees instead of twice as horizontal, and the log now says why a swipe
was refused or how far an unfinished one got. Firmware 0.2.27 for both
boards; the app only moves its target version.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-14 13:09:53 +02:00
parent 95cdfa30cf
commit b9e1dfbd95
11 changed files with 248 additions and 109 deletions
+53 -24
View File
@@ -1,7 +1,7 @@
# Guition ESP32-S3-4848S040 / 4-inch EU wallbox, 480x480 IPS + GT911.
# Use guition-device.yaml; see docs/GUITION.md. CYD hardware is a separate profile.
substitutions:
SCREEN_FIRMWARE_VERSION: "0.2.26"
SCREEN_FIRMWARE_VERSION: "0.2.27"
# A tap survives a finger that drifts up to this far (480 px over 72 mm: 1 cm); further is a swipe.
TOUCH_MOVE_LIMIT_PX: "67"
# Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
@@ -596,7 +596,38 @@ esphome:
then:
- lambda: |-
cyd::touch_guard.configure(${TOUCH_MOVE_LIMIT_PX}, ${TOUCH_MIN_PRESS_MS});
cyd::edge_swipe.configure(${DISPLAY_W}, ${DISPLAY_H}, ${EDGE_SWIPE_BAND_PX}, ${EDGE_SWIPE_TRAVEL_PX});
cyd::edge_swipe.configure(${EDGE_SWIPE_BAND_PX}, ${EDGE_SWIPE_TRAVEL_PX});
// Edge swipe on LVGL's own pointer events: the point is already rotated, the callback
// runs inside LVGL, and lv_indev_wait_release keeps the tile under the finger quiet.
if (auto *pointer = lv_indev_get_next(nullptr)) {
lv_indev_add_event_cb(pointer, [](lv_event_t *e) {
auto *indev = static_cast<lv_indev_t *>(lv_event_get_current_target(e));
const auto code = lv_event_get_code(e);
lv_point_t p;
lv_indev_get_point(indev, &p);
if (code == LV_EVENT_PRESSED) {
cyd::edge_swipe.begin(p.x, p.y, lv_display_get_horizontal_resolution(lv_display_get_default()));
return;
}
if (code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
if (cyd::edge_swipe.armed() && cyd::edge_swipe.inward() > 0)
ESP_LOGI("touch", "randveeg niet gevuurd: %d px naar binnen, %d px verticaal", cyd::edge_swipe.inward(), cyd::edge_swipe.sideways());
return;
}
if (code != LV_EVENT_PRESSING) return;
const int step = cyd::edge_swipe.update(p.x, p.y);
if (!step) return;
const char *blocked = !runtime_tiles::enabled ? "geen runtime-tegels" : !runtime_tiles::swipe_pages ? "instelling uit"
: id(display_dimmed) ? "scherm gedimd" : id(calibration_active) ? "kalibratie" : !id(active_entity).empty() ? "kaart open"
: (runtime_tiles::detail_root && !lv_obj_has_flag(runtime_tiles::detail_root, LV_OBJ_FLAG_HIDDEN)) ? "detailkaart open" : nullptr;
if (blocked) { ESP_LOGI("touch", "randveeg genegeerd: %s", blocked); return; }
cyd::touch_guard.consume();
lv_indev_wait_release(indev);
ESP_LOGI("touch", "randveeg: pagina %d -> %d", id(tile_page), id(tile_page) + step);
id(tile_page) += step;
id(show_tile_page).execute();
}, LV_EVENT_ALL, nullptr);
}
id(last_touch_ms) = millis();
id(display_dimmed) = false;
lv_obj_set_style_pad_row(id(vacuum_actions), 4, 0);
@@ -1870,7 +1901,6 @@ touchscreen:
id(last_touch_ms) = millis();
id(touch_down) = true;
cyd::touch_guard.begin(millis(), touch.x, touch.y, touch.id);
cyd::edge_swipe.begin(touch.x, touch.y, static_cast<int>(id(screen_lvgl).get_rotation()));
ESP_LOGI("touch", "GT911 press x=%u y=%u test=%d", touch.x, touch.y, id(calibration_active));
on_update:
- lambda: |-
@@ -1878,17 +1908,6 @@ touchscreen:
// Only the contact that started the touch counts; the guard ignores other ids.
for (const auto &point : touches) {
cyd::touch_guard.update(point.x, point.y, point.id);
if (point.id != cyd::touch_guard.contact()) continue;
// Swiping in from a side edge flips the page ("Vegen tussen pagina's"); the tap
// under the finger is consumed and LVGL waits for the release.
const int step = cyd::edge_swipe.update(point.x, point.y);
if (!step || !runtime_tiles::enabled || !runtime_tiles::swipe_pages || id(display_dimmed) || id(calibration_active) ||
!id(active_entity).empty() || (runtime_tiles::detail_root && !lv_obj_has_flag(runtime_tiles::detail_root, LV_OBJ_FLAG_HIDDEN))) continue;
cyd::touch_guard.consume();
for (auto *indev = lv_indev_get_next(nullptr); indev; indev = lv_indev_get_next(indev)) lv_indev_wait_release(indev);
ESP_LOGI("touch", "randveeg: pagina %d -> %d", id(tile_page), id(tile_page) + step);
id(tile_page) += step;
id(show_tile_page).execute();
}
on_release:
- lambda: |-
@@ -3472,7 +3491,7 @@ lvgl:
scrollable: false
clickable: true
on_click:
- lvgl.widget.hide: brightness_overlay
- script.execute: close_cards
- obj:
id: brightness_overlay_dim
@@ -3813,7 +3832,7 @@ lvgl:
clickable: true
scrollable: false
on_click:
- lvgl.widget.hide: color_detail_overlay
- script.execute: close_cards
widgets:
- label:
align: CENTER
@@ -3846,7 +3865,7 @@ lvgl:
clickable: true
scrollable: false
on_click:
- lvgl.widget.hide: color_detail_overlay
- script.execute: close_cards
widgets:
- label:
align: CENTER
@@ -3912,7 +3931,7 @@ lvgl:
clickable: true
scrollable: false
on_click:
- lvgl.widget.hide: climate_detail_overlay
- script.execute: close_cards
widgets:
- label:
align: CENTER
@@ -4769,7 +4788,7 @@ lvgl:
clickable: true
scrollable: false
on_click:
- lvgl.widget.hide: vacuum_overlay
- script.execute: close_cards
widgets:
- label:
align: CENTER
@@ -4792,7 +4811,7 @@ lvgl:
clickable: true
scrollable: false
on_click:
- lvgl.widget.hide: vacuum_overlay
- script.execute: close_cards
widgets:
- label:
align: CENTER
@@ -5040,6 +5059,19 @@ script:
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
if (!id(ui_refresh).is_running()) id(ui_refresh).execute();
- id: close_cards
then:
- lambda: |-
// Runtime cards and the legacy overlays close through one path; the swipe guard
// reads active_entity, so a hidden card must never leave it behind.
if (runtime_tiles::dismiss) { runtime_tiles::dismiss(); return; }
id(active_entity).clear();
- lvgl.widget.hide: brightness_overlay
- lvgl.widget.hide: color_detail_overlay
- lvgl.widget.hide: climate_detail_overlay
- lvgl.widget.hide: climate_mode_overlay
- lvgl.widget.hide: vacuum_overlay
- id: wake_display
mode: restart
then:
@@ -5050,10 +5082,7 @@ script:
// als je het scherm aanraakt en op licht staat te wachten.
id(set_backlight).execute(screen_settings::current.brightness, 80);
- lvgl.widget.hide: dim_wake_overlay
- lvgl.widget.hide: color_detail_overlay
- lvgl.widget.hide: climate_detail_overlay
- lvgl.widget.hide: climate_mode_overlay
- lvgl.widget.hide: vacuum_overlay
- script.execute: close_cards
# One owner for the backlight level: the LEDC fader steps the duty itself, so
# LVGL redraws cannot make the transition stutter. A new call interrupts the