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:
@@ -128,46 +128,41 @@ class TouchGuard {
|
||||
};
|
||||
inline TouchGuard touch_guard;
|
||||
|
||||
// Page navigation by swiping in from a side edge (Guition, firmware 0.2.24+). A touch that
|
||||
// starts within `band` pixels of the left or right edge of the screen as the user sees it
|
||||
// and travels `travel` pixels inward, clearly more sideways than up or down, flips one
|
||||
// Page navigation by swiping in from a side edge (Guition, firmware 0.2.24+). Fed from
|
||||
// LVGL's own pointer events (PRESSED/PRESSING), so the coordinates are already rotated the
|
||||
// way the user sees the screen. A touch that starts within `band` pixels of the left or
|
||||
// right edge and travels `travel` pixels inward, more sideways than up or down, flips one
|
||||
// page: from the right edge leftwards is "next", from the left edge rightwards "previous".
|
||||
// No speed requirement; a touch that starts in the middle never counts, so tapping and
|
||||
// dragging on tiles cannot change the page by accident.
|
||||
class EdgeSwipe {
|
||||
public:
|
||||
void configure(int width, int height, int band, int travel) {
|
||||
width_ = width; height_ = height; band_ = band; travel_ = travel;
|
||||
}
|
||||
// Native touch coordinates plus the LVGL rotation in degrees (0, 90, 180, 270), mapped
|
||||
// the way ESPHome's LVGL component rotates its pointer input.
|
||||
void begin(int x, int y, int rotation = 0) {
|
||||
rotation_ = rotation;
|
||||
rotate(x, y);
|
||||
void configure(int band, int travel) { band_ = band; travel_ = travel; }
|
||||
// `width` is the screen width as the user sees it (LVGL's horizontal resolution).
|
||||
void begin(int x, int y, int width) {
|
||||
start_x_ = x; start_y_ = y;
|
||||
const int width = (rotation == 90 || rotation == 270) ? height_ : width_;
|
||||
from_ = x < band_ ? 1 : x >= width - band_ ? -1 : 0; // 1: left edge, -1: right edge
|
||||
done_ = false;
|
||||
inward_ = sideways_ = 0;
|
||||
}
|
||||
bool armed() const { return from_ != 0 && !done_; }
|
||||
// +1 next page, -1 previous page, 0 nothing; fires at most once per touch.
|
||||
int update(int x, int y) {
|
||||
if (!armed()) return 0;
|
||||
rotate(x, y);
|
||||
const int dx = x - start_x_, dy = y - start_y_;
|
||||
const int inward = from_ == 1 ? dx : -dx;
|
||||
if (inward < travel_ || std::abs(dx) < 2 * std::abs(dy)) return 0;
|
||||
inward_ = std::max(inward_, inward);
|
||||
sideways_ = std::max(sideways_, std::abs(dy));
|
||||
if (inward < travel_ || std::abs(dx) < std::abs(dy)) return 0;
|
||||
done_ = true;
|
||||
return from_ == 1 ? -1 : 1;
|
||||
}
|
||||
// How far the finger got, for the log when an edge touch ends without a page flip.
|
||||
int inward() const { return inward_; }
|
||||
int sideways() const { return sideways_; }
|
||||
private:
|
||||
void rotate(int &x, int &y) const {
|
||||
if (rotation_ == 90) { const int tmp = y; y = width_ - x - 1; x = tmp; }
|
||||
else if (rotation_ == 180) { x = width_ - x - 1; y = height_ - y - 1; }
|
||||
else if (rotation_ == 270) { const int tmp = x; x = height_ - y - 1; y = tmp; }
|
||||
}
|
||||
int width_{480}, height_{480}, band_{32}, travel_{40}, rotation_{0};
|
||||
int start_x_{0}, start_y_{0}, from_{0};
|
||||
int band_{32}, travel_{40};
|
||||
int start_x_{0}, start_y_{0}, from_{0}, inward_{0}, sideways_{0};
|
||||
bool done_{true};
|
||||
};
|
||||
inline EdgeSwipe edge_swipe;
|
||||
|
||||
@@ -177,6 +177,17 @@ staan iconen in de browser uit het midden); draai daarna `generate_packages.py`.
|
||||
De substituties `MDI_GLYPH_*` zijn vervallen: `TILEn_ICON` in handmatige
|
||||
profielen moet uit de set komen. Geen gewijzigde preferences of sleutels.
|
||||
|
||||
### Compatibiliteit 0.2.32 / firmware 0.2.27
|
||||
|
||||
Alleen firmware. `cyd::EdgeSwipe` werkt op LVGL-pointercoördinaten: `configure(band, travel)`,
|
||||
`begin(x, y, width)` met `lv_display_get_horizontal_resolution()`, `update()` met de 45°-regel,
|
||||
plus `inward()`/`sideways()` voor het log. De Guition registreert één `lv_indev_add_event_cb`
|
||||
(PRESSED/PRESSING/RELEASED) in `on_boot`; de touchscreen-triggers voeden alleen nog de
|
||||
`TouchGuard`. Beide bordprofielen krijgen het script `close_cards` (roept
|
||||
`runtime_tiles::dismiss()` aan, anders overlays verbergen en `active_entity` wissen); de
|
||||
kruisjes en achtergrondtikken van de kaarten en `wake_display` gebruiken het. Protocol, opslag,
|
||||
preferences en sleutels ongewijzigd.
|
||||
|
||||
### Compatibiliteit 0.2.31 / firmware 0.2.26
|
||||
|
||||
Opslagversie en tegelprotocol blijven 1. `tiles[].slot` is additief: de absolute plek
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# Testresultaten app 0.2.32 / firmware 0.2.27 (2026-09-14)
|
||||
|
||||
Alleen firmware: de randveeg die op een echte Guition (Studio 1, firmware 0.2.24) nooit
|
||||
pakte, plus de veeg op LVGL's eigen pointer-events. De app verandert alleen de doelversie.
|
||||
|
||||
## Live diagnose op Studio 1 (ESPHome-log via de native API)
|
||||
|
||||
- Elke veeg begon netjes in de band (`GT911 press x=0…13` links, `x=470…478` rechts), maar er
|
||||
volgde nooit `randveeg`. Instelling stond aan (add-on: `swipe_pages: true`, 10 tegels,
|
||||
rotatie 0), `ui_state` meldde niet gedimd, geen kalibratie, geen runtime-kaart.
|
||||
- Na `preview_runtime_card -1` (roept `runtime_tiles::dismiss()` aan) werkte elke veeg
|
||||
meteen: `randveeg: pagina 0 -> 1`. Oorzaak: de kruisjes en achtergrondtikken van de
|
||||
licht-, klimaat- en stofzuigerkaart en het wakker worden uit standby verbergen de kaart
|
||||
zonder `active_entity` te wissen; de veeg-guard leest die en meldde stil "kaart open".
|
||||
Dezelfde gate zat in de oude LVGL-gesture en in het CYD-profiel.
|
||||
- Daarna: ongeveer één op de twee eerste vegen na een paginawissel vuurde niet, de
|
||||
tweede wel (bijvoorbeeld 12:57:28 en :29 vanaf rechts zonder wissel, :29 wel). De
|
||||
0.2.24-firmware logde daarvoor niets; 0.2.27 meldt nu `randveeg niet gevuurd: N px naar
|
||||
binnen, M px verticaal` en de richtingseis is 45° in plaats van "twee keer zo horizontaal".
|
||||
- Eén tik werd terecht geweigerd: `tik op script.studio_1_startup_sequence genegeerd:
|
||||
verplaatst 73 px (grens 67)`; de grens op de Guition is een open vraag aan Max.
|
||||
|
||||
## Wat veranderde
|
||||
|
||||
- `cyd::EdgeSwipe` op LVGL-pointercoördinaten (`begin(x, y, width)` met de horizontale
|
||||
resolutie van LVGL, dus rotatie is al verwerkt), 45°-regel, `inward()`/`sideways()`.
|
||||
- Guition: één `lv_indev_add_event_cb` in `on_boot` (PRESSED → begin, PRESSING → update en
|
||||
paginawissel met `lv_indev_wait_release`, RELEASED/PRESS_LOST → logregel als de veeg
|
||||
niet vuurde); geblokkeerde vegen loggen de reden. De touchscreen-triggers voeden alleen
|
||||
nog de tap-guard.
|
||||
- Beide borden: script `close_cards` (via `runtime_tiles::dismiss()`, anders overlays
|
||||
verbergen en `active_entity` wissen) voor de zes sluitknoppen en `wake_display`.
|
||||
|
||||
## Geautomatiseerd
|
||||
|
||||
- C++ (clang++ -std=c++17 -Wall -Wextra -Werror -I.): 11/11 PASS; `test_cyd_ui` dekt de
|
||||
nieuwe `EdgeSwipe`-API, de 45°-regel, de logwaarden en een smallere (gedraaide) breedte.
|
||||
- Python: 91 tests OK (`FIRMWARE_VERSION` 0.2.27 in app, profielen en pakketten).
|
||||
`generate_packages.py --check` groen.
|
||||
- ESPHome 2026.6.2 compile (verse worktree, geen cache): `easy-guition-device.yaml` SUCCESS in
|
||||
112 s (RAM 32,0%, flash 18,3%), `easy-cyd-device.yaml` SUCCESS in 86 s (RAM 31,0%, flash
|
||||
76,1%); geen nieuwe waarschuwingen.
|
||||
|
||||
## Nog te controleren op Studio 1
|
||||
|
||||
- Werkt de randveeg direct na een update zonder eerst een kaart te openen en te sluiten, en
|
||||
pakt de eerste veeg na een paginawissel nu ook; het log (`touch`) laat het zien.
|
||||
- Na het openen en sluiten van een lichtkaart (kruisje, tik ernaast, standby) blijft vegen werken.
|
||||
+53
-24
@@ -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
|
||||
|
||||
+21
-11
@@ -58,7 +58,7 @@
|
||||
############################################################
|
||||
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.26"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.27"
|
||||
# A tap survives a finger that drifts up to this far (320 px over 58 mm: 1 cm); further is a swipe.
|
||||
TOUCH_MOVE_LIMIT_PX: "56"
|
||||
# Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
|
||||
@@ -3608,7 +3608,7 @@ lvgl:
|
||||
scrollable: false
|
||||
clickable: true
|
||||
on_click:
|
||||
- lvgl.widget.hide: brightness_overlay
|
||||
- script.execute: close_cards
|
||||
|
||||
- obj:
|
||||
id: brightness_overlay_dim
|
||||
@@ -3949,7 +3949,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -3982,7 +3982,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -4048,7 +4048,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: climate_detail_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -4905,7 +4905,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: vacuum_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -4928,7 +4928,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: vacuum_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -5156,6 +5156,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:
|
||||
@@ -5169,10 +5182,7 @@ script:
|
||||
transition_length: 80ms
|
||||
brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
|
||||
- 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
|
||||
|
||||
- id: dim_display
|
||||
mode: restart
|
||||
|
||||
+21
-11
@@ -59,7 +59,7 @@
|
||||
############################################################
|
||||
|
||||
substitutions:
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.26"
|
||||
SCREEN_FIRMWARE_VERSION: "0.2.27"
|
||||
# A tap survives a finger that drifts up to this far (320 px over 58 mm: 1 cm); further is a swipe.
|
||||
TOUCH_MOVE_LIMIT_PX: "56"
|
||||
# Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
|
||||
@@ -3539,7 +3539,7 @@ lvgl:
|
||||
scrollable: false
|
||||
clickable: true
|
||||
on_click:
|
||||
- lvgl.widget.hide: brightness_overlay
|
||||
- script.execute: close_cards
|
||||
|
||||
- obj:
|
||||
id: brightness_overlay_dim
|
||||
@@ -3880,7 +3880,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -3913,7 +3913,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -3979,7 +3979,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: climate_detail_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -4836,7 +4836,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: vacuum_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -4859,7 +4859,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: vacuum_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -5087,6 +5087,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:
|
||||
@@ -5100,10 +5113,7 @@ script:
|
||||
transition_length: 80ms
|
||||
brightness: !lambda "return screen_settings::current.brightness / 100.0f;"
|
||||
- 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
|
||||
|
||||
- id: dim_display
|
||||
mode: restart
|
||||
|
||||
+53
-24
@@ -2,7 +2,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.
|
||||
@@ -600,7 +600,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);
|
||||
@@ -1801,7 +1832,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: |-
|
||||
@@ -1809,17 +1839,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: |-
|
||||
@@ -3403,7 +3422,7 @@ lvgl:
|
||||
scrollable: false
|
||||
clickable: true
|
||||
on_click:
|
||||
- lvgl.widget.hide: brightness_overlay
|
||||
- script.execute: close_cards
|
||||
|
||||
- obj:
|
||||
id: brightness_overlay_dim
|
||||
@@ -3744,7 +3763,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -3777,7 +3796,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: color_detail_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -3843,7 +3862,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: climate_detail_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -4700,7 +4719,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: vacuum_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -4723,7 +4742,7 @@ lvgl:
|
||||
clickable: true
|
||||
scrollable: false
|
||||
on_click:
|
||||
- lvgl.widget.hide: vacuum_overlay
|
||||
- script.execute: close_cards
|
||||
widgets:
|
||||
- label:
|
||||
align: CENTER
|
||||
@@ -4971,6 +4990,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:
|
||||
@@ -4981,10 +5013,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
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
## 0.2.32 (firmware 0.2.27)
|
||||
|
||||
- **Randveeg die nooit pakte, opgelost.** Live meegelezen op een Guition: de veeg werd herkend maar geblokkeerd door de voorwaarde "kaart open". De lichtkaart, klimaatkaart en stofzuigerkaart wisten bij sluiten (kruisje, tik naast de kaart) en bij wakker worden uit standby de interne "actieve kaart" niet, dus na één keer een kaart openen bleef vegen stil geblokkeerd tot de indeling veranderde. Dat gold ook voor de oude veeg over het hele scherm en voor de CYD. Beide borden sluiten kaarten nu via één script (`close_cards`) dat alles verbergt én die toestand wist; wakker worden gebruikt hetzelfde pad.
|
||||
- **Randveeg op LVGL's eigen manier.** De Guition volgt de veeg nu via de pointer-events van LVGL 9 (`lv_indev_add_event_cb`, PRESSED/PRESSING) in plaats van ESPHome's touchscreen-`on_update`: de coördinaten komen al gedraaid van LVGL (geen eigen rotatiemapping meer), en `lv_indev_wait_release` wordt vanuit LVGL zelf aangeroepen. De richtingseis is versoepeld van "twee keer zo horizontaal" naar "meer zijwaarts dan verticaal" (45°), zodat een schuine duimveeg vanaf rechts ook in één keer pakt.
|
||||
- **Log zegt waarom.** Een herkende maar geblokkeerde randveeg meldt de reden (`randveeg genegeerd: kaart open`), en een randveeg die zonder paginawissel eindigt meldt hoe ver de vinger kwam (`randveeg niet gevuurd: 28 px naar binnen, 35 px verticaal`), naast het bestaande `randveeg: pagina 0 -> 1`.
|
||||
- Firmware 0.2.27 voor beide borden; de app biedt de update aan. Alleen de doelversie verandert in de app.
|
||||
|
||||
## 0.2.31 (firmware 0.2.26)
|
||||
|
||||
- **Vrij slepen, met lege plekken**: de editor werkt met vaste plekken in plaats van een volgorde die steeds wordt aangeschoven. Elke tegel heeft zijn eigen plek (kolom, rij, pagina) en die verandert alleen als jij hem versleept. Lege plekken zijn gewoon lege plekken: naast een enkele tegel, midden op een pagina, waar je wilt; ze blijven staan zodat je kunt ordenen. Sleep een tegel op een lege plek en hij staat daar. Sleep hem op een andere tegel en die twee wisselen: de ander neemt de plek die vrijkwam, of anders de dichtstbijzijnde vrije plek. Alle overige tegels blijven waar ze staan. Tijdens het slepen laat het voorbeeld al zien waar alles terechtkomt; loslaten bevestigt precies dat, loslaten buiten het voorbeeld verandert niets.
|
||||
|
||||
@@ -16,7 +16,7 @@ WEEKDAYS = ['ma', 'di', 'wo', 'do', 'vr', 'za', 'zo']
|
||||
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.26'
|
||||
FIRMWARE_VERSION = '0.2.27'
|
||||
ATTRS = frozenset('brightness percentage current_position current_temperature temperature current_humidity min_temp max_temp target_temp_step supported_color_modes hvac_modes hvac_action hs_color color_temp_kelvin min_color_temp_kelvin max_color_temp_kelvin fan_speed_list unit_of_measurement battery_level fan_speed volume_level is_volume_muted media_title options min max step temperature_unit supported_features device_class next_rising next_setting finishes_at duration remaining humidity wind_speed wind_speed_unit apparent_temperature fan_modes swing_modes fan_mode swing_mode'.split())
|
||||
# Attributes whose boolean value the screen needs; every other bool stays behind.
|
||||
BOOL_ATTRS = frozenset(['is_volume_muted'])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: ESP Screen Manager
|
||||
version: "0.2.31"
|
||||
version: "0.2.32"
|
||||
slug: esp_screen_manager
|
||||
description: Kies tegels voor je CYD en Guition touchscreen, zonder opnieuw flashen.
|
||||
url: https://github.com/MaxGramser/homeassistant_espscreen
|
||||
|
||||
+16
-16
@@ -86,32 +86,32 @@ int main() {
|
||||
resistive.update(50, 180); // 70 px past the settled point: a swipe
|
||||
assert(!resistive.accept(1100, 1));
|
||||
assert(!resistive.accept_repeat(1100, 1));
|
||||
// Edge swipe (0.2.24): only a touch that starts in the side band flips a page, from the
|
||||
// right edge leftwards "next", from the left edge rightwards "previous", once per touch,
|
||||
// slow or fast, and never diagonally or from the middle. Rotation follows ESPHome's mapping.
|
||||
// Edge swipe (0.2.24, LVGL pointer coordinates since 0.2.27): only a touch that starts in
|
||||
// the side band flips a page, from the right edge leftwards "next", from the left edge
|
||||
// rightwards "previous", once per touch, slow or fast, more sideways than vertical, never
|
||||
// from the middle. The width follows the rotated screen.
|
||||
cyd::EdgeSwipe edge;
|
||||
edge.configure(480, 480, 32, 40);
|
||||
edge.begin(6, 240);
|
||||
edge.configure(32, 40);
|
||||
edge.begin(6, 240, 480);
|
||||
assert(edge.armed());
|
||||
assert(edge.update(30, 242) == 0); // not far enough yet
|
||||
assert(edge.update(48, 245) == -1); // 42 px inward from the left edge: previous page
|
||||
assert(edge.update(90, 245) == 0); // once per touch
|
||||
assert(!edge.armed());
|
||||
edge.begin(474, 100);
|
||||
edge.begin(474, 100, 480);
|
||||
assert(edge.update(430, 104) == 1); // from the right edge: next page
|
||||
edge.begin(240, 240);
|
||||
edge.begin(240, 240, 480);
|
||||
assert(!edge.armed());
|
||||
assert(edge.update(300, 240) == 0); // started in the middle: never a page swipe
|
||||
edge.begin(6, 240);
|
||||
assert(edge.update(50, 300) == 0); // diagonal: 44 px sideways against 60 down
|
||||
edge.begin(6, 240, 480);
|
||||
assert(edge.update(50, 300) == 0); // steeper than 45 degrees: 44 px sideways against 60 down
|
||||
assert(edge.update(-10, 240) == 0); // moving outward: nothing
|
||||
assert(edge.update(60, 244) == -1); // straightened out later: still counts
|
||||
edge.begin(240, 6, 90);
|
||||
assert(edge.update(240, 60) == -1); // rotated 90: the user's left edge is the panel's top
|
||||
edge.begin(240, 6, 270);
|
||||
assert(edge.update(240, 60) == 1); // rotated 270: that same edge is the user's right
|
||||
edge.begin(6, 240, 180);
|
||||
assert(edge.update(60, 240) == 1); // upside down: the panel's left is the user's right
|
||||
assert(edge.update(60, 270) == -1); // 54 sideways against 30 down: a slanted thumb swipe counts
|
||||
edge.begin(6, 240, 480);
|
||||
edge.update(20, 250);
|
||||
assert(edge.inward() == 14 && edge.sideways() == 10); // what the log reports for a swipe that ended early
|
||||
edge.begin(300, 240, 320);
|
||||
assert(edge.update(250, 240) == 1); // a 320 px wide (rotated or smaller) screen: right band from 288
|
||||
cyd::TouchGuard rollover;
|
||||
rollover.begin(std::numeric_limits<uint32_t>::max() - 30);
|
||||
assert(rollover.accept(50, 1)); // millis wraps after 49 days
|
||||
|
||||
Reference in New Issue
Block a user