From 5c4581563e29fba521afa1dd4db81f8f09c5709f Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 21 Dec 2024 21:14:41 +0100 Subject: [PATCH 01/13] Rebuild Button click engine The native engine from ESPHome cannot be used here as a button press event is triggered on touch, which would trigger the click even on swipe. Other projects solved this by tracking only single click and only at release, so we decided to implement this in a different way, so we can handle multiple or long-clicks also. This should finally solve #14, but some test is required to ensure nothing else was broken. --- ...Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 235 +++++++++--------- ...X-Ultimate-Easy-ESPHome_core_hw_touch.yaml | 8 +- 2 files changed, 119 insertions(+), 124 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index 2e8a8a0..f65f53d 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -20,111 +20,53 @@ substitutions: BUTTON_3_ACTION_TEXT: "Relay 3 (toggle)" BUTTON_4_ACTION_TEXT: "Relay 4 (toggle)" -binary_sensor: - - id: bs_button_1 - name: Button 1 - icon: mdi:gesture-tap-box - internal: false - platform: template - on_click: - then: - - script.execute: - id: button_action - component: bs_button_1 - event: click - on_double_click: - then: - - script.execute: - id: button_action - component: bs_button_1 - event: double_click - on_multi_click: - - timing: &long_click-timing - - ON for at least 0.8s - invalid_cooldown: ${invalid_cooldown} - then: - - script.execute: - id: button_action - component: bs_button_1 - event: long_click + BUTTON_CLICK_MIN_LENGTH: '50' # The minimum duration the click should last, in msec + BUTTON_CLICK_MAX_LENGTH: '350' # The maximum duration the click should last, in msec + BUTTON_MULTI_CLICK_DELAY: '500' # The time to wait for another click, in msec + BUTTON_PRESS_TIMEOUT: '10000' # Ignore if buttor is pressed for longer than this time, in msec + BUTTON_LONG_PRESS_DELAY: '800' # The time to wait to consider a long press, in msec - - id: bs_button_2 +binary_sensor: + - &binary_sensor_button_base + id: bs_button_2 name: Button 2 icon: mdi:gesture-tap-box - internal: true platform: template - on_click: - then: - - script.execute: - id: button_action - component: bs_button_2 - event: click - on_double_click: - then: - - script.execute: - id: button_action - component: bs_button_2 - event: double_click - on_multi_click: - - timing: *long_click-timing - invalid_cooldown: ${invalid_cooldown} - then: - - script.execute: - id: button_action - component: bs_button_2 - event: long_click + internal: true - id: bs_button_3 name: Button 3 - icon: mdi:gesture-tap-box - internal: true - platform: template - on_click: - then: - - script.execute: - id: button_action - component: bs_button_3 - event: click - on_double_click: - then: - - script.execute: - id: button_action - component: bs_button_3 - event: double_click - on_multi_click: - - timing: *long_click-timing - invalid_cooldown: ${invalid_cooldown} - then: - - script.execute: - id: button_action - component: bs_button_3 - event: long_click + <<: *binary_sensor_button_base - id: bs_button_4 name: Button 4 - icon: mdi:gesture-tap-box - internal: true - platform: template - on_click: - then: - - script.execute: - id: button_action - component: bs_button_4 - event: click - on_double_click: - then: - - script.execute: - id: button_action - component: bs_button_4 - event: double_click - on_multi_click: - - timing: *long_click-timing - invalid_cooldown: ${invalid_cooldown} - then: - - script.execute: - id: button_action - component: bs_button_4 - event: long_click + <<: *binary_sensor_button_base + + - id: bs_button_1 + name: Button 1 + internal: false + <<: *binary_sensor_button_base + +globals: + - id: button_press_button + type: uint8_t + restore_value: false + initial_value: '0' + + - id: button_press_position + type: uint8_t + restore_value: false + initial_value: '0' + + - id: button_press_start_time + type: uint32_t + restore_value: false + initial_value: '0' + + - id: click_counter + type: uint8_t + restore_value: false + initial_value: '0' script: - id: !extend boot_initialize @@ -162,9 +104,15 @@ script: parameters: component: string event: string - then: # There's nothing here so far - # Extended by: - # - core_api + then: + # Extended by: + # - core_api + - lambda: |- + ESP_LOGI("core_hw_buttons", "Button '%s' action: '%s'", component.c_str(), event.c_str()); + id(button_press_button) = 0; + id(click_counter) = 0; + id(button_press_start_time) = 0; + buttons_release->execute(); - id: buttons_release mode: restart @@ -195,34 +143,81 @@ script: touch_x: uint8_t then: - lambda: |- + id(button_press_start_time) = millis(); + id(button_press_position) = touch_x; + uint8_t button = 0; auto model_index = sl_tx_model_gang->active_index(); if (model_index.has_value()) { - uint8_t model_idx = model_index.value() + 1; - switch (model_idx) { - case 1: // 1 Gang - bs_button_1->publish_state(true); - break; - case 2: // 2 Gang - if (touch_x <= 5) bs_button_1->publish_state(true); - else bs_button_2->publish_state(true); - break; - case 3: // 3 Gang - if (touch_x <= 3) bs_button_1->publish_state(true); - else if (touch_x <= 7) bs_button_2->publish_state(true); - else bs_button_3->publish_state(true); - break; - case 4: // 4 Gang - if (touch_x <= 2) bs_button_1->publish_state(true); - else if (touch_x <= 5) bs_button_2->publish_state(true); - else if (touch_x <= 8) bs_button_3->publish_state(true); - else bs_button_4->publish_state(true); - break; - } + const uint8_t model_idx = model_index.value() + 1; // Increment for 1-based indexing + if (model_idx == 1) { + button = 1; // Single button, always 1 + } else { + const uint8_t step = 10 / model_idx; // Calculate step size for regions + button = (touch_x / step) + 1; // Determine button region + if (button > model_idx) + button = model_idx; // Clamp to max button count + } + } + // Update binary sensor + switch (button) { + case 1: + bs_button_1->publish_state(true); + break; + case 2: + bs_button_2->publish_state(true); + break; + case 3: + bs_button_3->publish_state(true); + break; + case 4: + bs_button_4->publish_state(true); + break; + } + // Update counters + if (id(button_press_button) == button) { + id(click_counter)++; + } else { + id(click_counter) = 1; + id(button_press_button) = button; } - id: !extend touch_on_release then: - - script.execute: buttons_release + - lambda: |- + uint32_t current_time = millis(); + buttons_release->execute(); + if (id(button_press_start_time) > 0 and + id(button_press_start_time) < current_time) { + uint32_t press_duration = current_time - id(button_press_start_time); + // Handle overflow (optional, since it's unlikely to happen here) + ESP_LOGI("core_hw_buttons", "Button press duration: %" PRIu32 " ms", press_duration); + if (press_duration < ${BUTTON_CLICK_MIN_LENGTH}) { + ESP_LOGW("core_hw_buttons", "Ignoring button press (too short)"); + } else if (press_duration >= ${BUTTON_CLICK_MIN_LENGTH} and + press_duration <= ${BUTTON_CLICK_MAX_LENGTH}) { // Short/normal click + button_click_event->execute(); + } else if (press_duration >= ${BUTTON_LONG_PRESS_DELAY} and press_duration <= ${BUTTON_PRESS_TIMEOUT}) { + button_action->execute(("bs_button_" + std::to_string(id(button_press_button))).c_str(), "long_click"); + } else if (press_duration > ${BUTTON_PRESS_TIMEOUT}) { // Timeout or invalid + ESP_LOGW("core_hw_buttons", "Button press cancelled or timed out after ${BUTTON_PRESS_TIMEOUT} ms"); + } + } else { + ESP_LOGW("core_hw_buttons", "Press event timestamp not recorded yet"); + } + id(button_press_start_time) = 0; + + - id: button_click_event + mode: restart + then: + - delay: + milliseconds: ${BUTTON_MULTI_CLICK_DELAY} + - lambda: |- + button_action->execute( + ("bs_button_" + std::to_string(id(button_press_button))).c_str(), + (id(click_counter) == 1 ? "click" : + (id(click_counter) == 2 ? "double_click" : + (std::to_string(id(click_counter)) + "_click").c_str())) + ); - id: !extend touch_swipe_left then: diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml index 08c6490..ed5cf00 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml @@ -233,8 +233,8 @@ tx_ultimate_easy: ESP_LOGD("tx_ultimate_easy", " Position: %i", touch.x); uart: - id: uart_touch - tx_pin: GPIO19 - rx_pin: GPIO22 - baud_rate: 115200 + - id: uart_touch + tx_pin: GPIO19 + rx_pin: GPIO22 + baud_rate: 115200 ... From 35cc0c4a8e849240c906d7fe791368573250c390 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 21 Dec 2024 21:18:54 +0100 Subject: [PATCH 02/13] Reorganize scripts for better clarity --- ...Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index f65f53d..a7bfec1 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -114,6 +114,19 @@ script: id(button_press_start_time) = 0; buttons_release->execute(); + - id: button_click_event + mode: restart + then: + - delay: + milliseconds: ${BUTTON_MULTI_CLICK_DELAY} + - lambda: |- + button_action->execute( + ("bs_button_" + std::to_string(id(button_press_button))).c_str(), + (id(click_counter) == 1 ? "click" : + (id(click_counter) == 2 ? "double_click" : + (std::to_string(id(click_counter)) + "_click").c_str())) + ); + - id: buttons_release mode: restart then: @@ -206,19 +219,6 @@ script: } id(button_press_start_time) = 0; - - id: button_click_event - mode: restart - then: - - delay: - milliseconds: ${BUTTON_MULTI_CLICK_DELAY} - - lambda: |- - button_action->execute( - ("bs_button_" + std::to_string(id(button_press_button))).c_str(), - (id(click_counter) == 1 ? "click" : - (id(click_counter) == 2 ? "double_click" : - (std::to_string(id(click_counter)) + "_click").c_str())) - ); - - id: !extend touch_swipe_left then: - script.execute: buttons_release From 976a90311850c69a1aeb59df27dcf35d919b594a Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 21 Dec 2024 21:25:29 +0100 Subject: [PATCH 03/13] Fix references --- .test/esphome_ard_basic.yaml | 2 +- .test/esphome_idf53_bluetooth_proxy.yaml | 2 +- .test/esphome_idf_bluetooth_proxy.yaml | 2 +- TX-Ultimate-Easy-ESPHome.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.test/esphome_ard_basic.yaml b/.test/esphome_ard_basic.yaml index dbda8d8..ed32a8f 100644 --- a/.test/esphome_ard_basic.yaml +++ b/.test/esphome_ard_basic.yaml @@ -1,6 +1,6 @@ --- packages: - basic_package: !include ../TX-Ultimate-Easy-ESPHome.yaml # Basic package + basic_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml # Basic package esp32: framework: diff --git a/.test/esphome_idf53_bluetooth_proxy.yaml b/.test/esphome_idf53_bluetooth_proxy.yaml index 1e8643f..a8d3868 100644 --- a/.test/esphome_idf53_bluetooth_proxy.yaml +++ b/.test/esphome_idf53_bluetooth_proxy.yaml @@ -1,6 +1,6 @@ --- packages: - basic_package: !include ../TX-Ultimate-Easy-ESPHome.yaml # Core package + core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml esp32: diff --git a/.test/esphome_idf_bluetooth_proxy.yaml b/.test/esphome_idf_bluetooth_proxy.yaml index a2f9a0b..3e434a5 100644 --- a/.test/esphome_idf_bluetooth_proxy.yaml +++ b/.test/esphome_idf_bluetooth_proxy.yaml @@ -1,5 +1,5 @@ --- packages: - basic_package: !include ../TX-Ultimate-Easy-ESPHome.yaml # Core package + core_package: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml addon_bluetooth_proxy: !include ../ESPHome/TX-Ultimate-Easy-ESPHome_addon_ble_proxy.yaml ... diff --git a/TX-Ultimate-Easy-ESPHome.yaml b/TX-Ultimate-Easy-ESPHome.yaml index 3b95ea8..0befff0 100644 --- a/TX-Ultimate-Easy-ESPHome.yaml +++ b/TX-Ultimate-Easy-ESPHome.yaml @@ -16,7 +16,7 @@ wifi: packages: remote_package: url: https://github.com/edwardtfn/TX-Ultimate-Easy - ref: dev + ref: latest refresh: 30s files: - ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml From 8668fe8dc92967902a9b7b9899c0552e8c031a4d Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 21 Dec 2024 21:29:05 +0100 Subject: [PATCH 04/13] Fix another bad reference --- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml index ed5cf00..86bf53c 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml @@ -61,7 +61,7 @@ external_components: - source: type: git url: https://github.com/edwardtfn/TX-Ultimate-Easy - ref: ${version} + ref: v${version} refresh: 1h components: - tx_ultimate_easy From 857c43940dc82621a71c4c1aaa280e4ae7698f13 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 21 Dec 2024 21:54:54 +0100 Subject: [PATCH 05/13] CodeRabbitAI Nitpick --- ...TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 16 ++++++++-------- .../TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml | 3 +++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index a7bfec1..072479e 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -23,7 +23,7 @@ substitutions: BUTTON_CLICK_MIN_LENGTH: '50' # The minimum duration the click should last, in msec BUTTON_CLICK_MAX_LENGTH: '350' # The maximum duration the click should last, in msec BUTTON_MULTI_CLICK_DELAY: '500' # The time to wait for another click, in msec - BUTTON_PRESS_TIMEOUT: '10000' # Ignore if buttor is pressed for longer than this time, in msec + BUTTON_PRESS_TIMEOUT: '10000' # Ignore if button is pressed for longer than this time, in msec BUTTON_LONG_PRESS_DELAY: '800' # The time to wait to consider a long press, in msec binary_sensor: @@ -120,12 +120,12 @@ script: - delay: milliseconds: ${BUTTON_MULTI_CLICK_DELAY} - lambda: |- - button_action->execute( - ("bs_button_" + std::to_string(id(button_press_button))).c_str(), - (id(click_counter) == 1 ? "click" : - (id(click_counter) == 2 ? "double_click" : - (std::to_string(id(click_counter)) + "_click").c_str())) - ); + const std::string button_name = "bs_button_" + std::to_string(id(button_press_button)); + std::string event_name; + if (id(click_counter) == 1) event_name = "click"; + else if (id(click_counter) == 2) event_name = "double_click"; + else event_name = std::to_string(id(click_counter)) + "_click"; + button_action->execute(button_name.c_str(), event_name.c_str()); - id: buttons_release mode: restart @@ -165,7 +165,7 @@ script: if (model_idx == 1) { button = 1; // Single button, always 1 } else { - const uint8_t step = 10 / model_idx; // Calculate step size for regions + const uint8_t step = ${TOUCH_POSITION_MAX_VALUE} / model_idx; // Width of each button region button = (touch_x / step) + 1; // Determine button region if (button > model_idx) button = model_idx; // Clamp to max button count diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml index 86bf53c..b8c8b90 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml @@ -11,6 +11,9 @@ ##### - For normal system use, modifications to this file are NOT required. ##### #################################################################################################### --- +substitutions: + TOUCH_POSITION_MAX_VALUE: '10' # Maximum touch position value + binary_sensor: - id: bs_multi_touch name: Multi-touch From 5ae4564e6a7804e7d2e5c471a55fcf4613ed9006 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 21 Dec 2024 22:08:19 +0100 Subject: [PATCH 06/13] CodeRabbitAI Nitpick 2 --- .../TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 8 ++++---- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml | 13 ++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index 072479e..667b380 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -148,16 +148,16 @@ script: then: - script.execute: id: touch_on_press_buttons - touch_x: !lambda return touch_x; + touch_position: !lambda return touch_position; - id: touch_on_press_buttons mode: restart parameters: - touch_x: uint8_t + touch_position: uint8_t then: - lambda: |- id(button_press_start_time) = millis(); - id(button_press_position) = touch_x; + id(button_press_position) = touch_position; uint8_t button = 0; auto model_index = sl_tx_model_gang->active_index(); if (model_index.has_value()) { @@ -166,7 +166,7 @@ script: button = 1; // Single button, always 1 } else { const uint8_t step = ${TOUCH_POSITION_MAX_VALUE} / model_idx; // Width of each button region - button = (touch_x / step) + 1; // Determine button region + button = (touch_position / step) + 1; // Determine button region if (button > model_idx) button = model_idx; // Clamp to max button count } diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml index b8c8b90..81636b3 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml @@ -127,7 +127,7 @@ script: - id: touch_on_press mode: restart parameters: - touch_x: uint8_t + touch_position: uint8_t then: # Extended by: # - HW Buttons @@ -212,10 +212,13 @@ tx_ultimate_easy: on_press: - lambda: |- - ESP_LOGI("tx_ultimate_easy", "Pressed at position %" PRIu8, static_cast(touch.x)); - - script.execute: - id: touch_on_press - touch_x: !lambda return static_cast(touch.x); + const uint8_t touch_position = static_cast(touch.x); + if (touch_position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range + ESP_LOGE("core_hw_buttons", "Invalid touch position: %" PRIu8, touch_position); + } else { + ESP_LOGI("tx_ultimate_easy", "Pressed at position %" PRIu8, touch_position); + touch_on_press->execute(touch_position); + } on_release: - lambda: ESP_LOGI("tx_ultimate_easy", "Released"); From 696e3bf28d65f27117efe8c6b2e5d9228ce83172 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 21 Dec 2024 23:10:16 +0100 Subject: [PATCH 07/13] Component to provide button number --- components/tx_ultimate_easy/__init__.py | 5 ++++ .../tx_ultimate_easy/tx_ultimate_easy.cpp | 28 ++++++++++++++++++- .../tx_ultimate_easy/tx_ultimate_easy.h | 10 +++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/components/tx_ultimate_easy/__init__.py b/components/tx_ultimate_easy/__init__.py index 3a598ad..7120ead 100644 --- a/components/tx_ultimate_easy/__init__.py +++ b/components/tx_ultimate_easy/__init__.py @@ -13,6 +13,7 @@ CONF_TX_ULTIMATE_EASY = "tx_ultimate_easy" CONF_UART = "uart" +CONF_GANG_COUNT = "gang_count" CONF_ON_TOUCH_EVENT = "on_touch_event" CONF_ON_PRESS = "on_press" @@ -33,6 +34,7 @@ cv.GenerateID(): cv.declare_id(TxUltimateTouch), cv.Required(CONF_UART): cv.use_id(uart), + cv.Optional(CONF_GANG_COUNT, default=1): cv.int_range(min=1, max=4), cv.Optional(CONF_ON_TOUCH_EVENT): automation.validate_automation(single=True), cv.Optional(CONF_ON_PRESS): automation.validate_automation(single=True), @@ -49,6 +51,9 @@ async def register_tx_ultimate_easy(var, config): uart_component = await cg.get_variable(config[CONF_UART]) cg.add(var.set_uart_component(uart_component)) + if CONF_GANG_COUNT in config: + cg.add(var.set_gang_count(config[CONF_GANG_COUNT])) + if CONF_ON_TOUCH_EVENT in config: await automation.build_automation( var.get_trigger_touch_event(), diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.cpp b/components/tx_ultimate_easy/tx_ultimate_easy.cpp index 84150db..61be4f6 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy.cpp +++ b/components/tx_ultimate_easy/tx_ultimate_easy.cpp @@ -48,6 +48,30 @@ namespace esphome { void TxUltimateEasy::dump_config() { ESP_LOGCONFIG(TAG, "TX Ultimate Easy"); + ESP_LOGCONFIG(TAG, " Gang count: %" PRIu8, this->gang_count_); + } + + bool TxUltimateEasy::set_gang_count(const uint8_t gang_count) { + if (gang_count < 1 or gang_count > 4) + return false; + this->gang_count_ = gang_count; + return true; + } + + uint8_t TxUltimateEasy::get_button_from_position(const uint8_t position) { + if (position > TOUCH_MAX_POSITION) // Invalid position + return 0; + if (this->gang_count_ == 1) // For 1 Gang model, any position is button 1 + return 1; + if (this->gang_count_ < 1 or this->gang_count_ > 4) // Invalid gang count + return 0; + const uint8_t width = TOUCH_MAX_POSITION / this->gang_count_; // Width of each button region + if (width < 1 or width > this->gang_count_) // Invalid width - and prevents division by zero + return 0; + uint8_t button = (position / width) + 1; // Determine button region + if (button > this->gang_count_) + button = this->gang_count_; // Clamp to max button count + return button; } void TxUltimateEasy::send_touch_(TouchPoint tp) { @@ -103,7 +127,7 @@ namespace esphome { state == TOUCH_STATE_SWIPE_LEFT || state == TOUCH_STATE_SWIPE_RIGHT || state == TOUCH_STATE_MULTI_TOUCH) && - (uart_received_bytes[6] >= 0 || state == TOUCH_STATE_MULTI_TOUCH); + (uart_received_bytes[6] >= 0 || state == TOUCH_STATE_MULTI_TOUCH); } int TxUltimateEasy::get_touch_position_x(const std::array &uart_received_bytes) { @@ -133,6 +157,8 @@ namespace esphome { TouchPoint TxUltimateEasy::get_touch_point(const std::array &uart_received_bytes) { TouchPoint tp; tp.x = this->get_touch_position_x(uart_received_bytes); + if (tp.x >= 0) + tp.button = this->get_button_from_position(static_cast(tp.x)); tp.state = this->get_touch_state(uart_received_bytes); switch (tp.state) { case TOUCH_STATE_RELEASE: diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.h b/components/tx_ultimate_easy/tx_ultimate_easy.h index 5d7b3a0..cfb9e8b 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy.h +++ b/components/tx_ultimate_easy/tx_ultimate_easy.h @@ -13,6 +13,9 @@ namespace esphome { namespace tx_ultimate_easy { + // Touch Max Position + constexpr uint8_t TOUCH_MAX_POSITION = 10; + // Touch State Constants constexpr uint8_t TOUCH_STATE_RELEASE = 0x01; constexpr uint8_t TOUCH_STATE_PRESS = 0x02; @@ -32,6 +35,7 @@ namespace esphome { static const char *TAG = "tx_ultimate_easy"; struct TouchPoint { + uint8_t button = 0; int8_t x = -1; int8_t state = -1; std::string state_str = "Unknown"; @@ -53,6 +57,10 @@ namespace esphome { void loop() override; void dump_config() override; + uint8_t get_gang_count() { return this->gang_count_; } + bool set_gang_count(const uint8_t gang_count); + uint8_t get_button_from_position(const uint8_t position); + protected: void send_touch_(TouchPoint tp); void handle_touch(const std::array &bytes); @@ -70,6 +78,8 @@ namespace esphome { Trigger trigger_multi_touch_release_; Trigger trigger_long_touch_release_; + uint8_t gang_count_ = 1; + }; // class TxUltimateEasy } // namespace tx_ultimate_easy From 8c610a406a63d86d55f8d9c8c4cd114923bd718e Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 21 Dec 2024 23:17:42 +0100 Subject: [PATCH 08/13] Log long-press from component If this works fine, it could replace this part on the new implementation. --- .../TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 6 ++++-- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml | 13 +++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index 667b380..50b7170 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -165,8 +165,10 @@ script: if (model_idx == 1) { button = 1; // Single button, always 1 } else { - const uint8_t step = ${TOUCH_POSITION_MAX_VALUE} / model_idx; // Width of each button region - button = (touch_position / step) + 1; // Determine button region + const uint8_t width = ${TOUCH_POSITION_MAX_VALUE} / model_idx; // Width of each button region + ESP_LOGV("core_hw_buttons", "Button regions: width=%" PRIu8 ", touch_position=%" PRIu8, + width, touch_position); + button = (touch_position / width) + 1; // Determine button region if (button > model_idx) button = model_idx; // Clamp to max button count } diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml index 81636b3..0a47ab3 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_touch.yaml @@ -12,7 +12,7 @@ #################################################################################################### --- substitutions: - TOUCH_POSITION_MAX_VALUE: '10' # Maximum touch position value + TOUCH_POSITION_MAX_VALUE: '10' # Maximum touch position value returned by the touch pad via uart binary_sensor: - id: bs_multi_touch @@ -206,6 +206,15 @@ tx_ultimate_easy: id: tx_ultimate uart: uart_touch + on_long_touch_release: + - lambda: |- + const uint8_t touch_position = static_cast(touch.x); + if (touch_position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range + ESP_LOGE("tx_ultimate_easy", "Invalid long-touch position: %" PRIu8, touch_position); + } else { + ESP_LOGI("tx_ultimate_easy", "Long-touch released at position %" PRIu8, touch_position); + } + on_multi_touch_release: - lambda: ESP_LOGI("tx_ultimate_easy", "Multi-touch released"); - script.execute: touch_on_multi_touch_release @@ -214,7 +223,7 @@ tx_ultimate_easy: - lambda: |- const uint8_t touch_position = static_cast(touch.x); if (touch_position > ${TOUCH_POSITION_MAX_VALUE}) { // Check for valid range - ESP_LOGE("core_hw_buttons", "Invalid touch position: %" PRIu8, touch_position); + ESP_LOGE("tx_ultimate_easy", "Invalid touch position: %" PRIu8, touch_position); } else { ESP_LOGI("tx_ultimate_easy", "Pressed at position %" PRIu8, touch_position); touch_on_press->execute(touch_position); From 65bbd901b14c1b921d8f12c0ed61df750db54db9 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sat, 21 Dec 2024 23:32:15 +0100 Subject: [PATCH 09/13] Clean-up `get_button_from_position` --- .../tx_ultimate_easy/tx_ultimate_easy.cpp | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.cpp b/components/tx_ultimate_easy/tx_ultimate_easy.cpp index 61be4f6..6d90d4c 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy.cpp +++ b/components/tx_ultimate_easy/tx_ultimate_easy.cpp @@ -59,18 +59,22 @@ namespace esphome { } uint8_t TxUltimateEasy::get_button_from_position(const uint8_t position) { - if (position > TOUCH_MAX_POSITION) // Invalid position + // Validate position bounds + if (position > TOUCH_MAX_POSITION) return 0; - if (this->gang_count_ == 1) // For 1 Gang model, any position is button 1 + + // Special case for single gang (only one button exists) + if (this->gang_count_ == 1) return 1; - if (this->gang_count_ < 1 or this->gang_count_ > 4) // Invalid gang count - return 0; - const uint8_t width = TOUCH_MAX_POSITION / this->gang_count_; // Width of each button region + + // Calculate button number + const uint8_t width = (TOUCH_MAX_POSITION + 1) / this->gang_count_; // Width of each button region if (width < 1 or width > this->gang_count_) // Invalid width - and prevents division by zero return 0; - uint8_t button = (position / width) + 1; // Determine button region - if (button > this->gang_count_) - button = this->gang_count_; // Clamp to max button count + const uint8_t button = std::min( + static_cast((position / width) + 1), // Convert position to button index + this->gang_count_ // Clamp to max gang count + ); return button; } From 4d8107faf200e99d4b3b5da73c04030e5ca3787c Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 22 Dec 2024 01:15:26 +0100 Subject: [PATCH 10/13] Remove relay from binary sensor --- ...-Ultimate-Easy-ESPHome_core_hw_relays.yaml | 82 ++++++++----------- 1 file changed, 33 insertions(+), 49 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml index 79a92ce..7bb4e55 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml @@ -11,55 +11,6 @@ ##### - For normal system use, modifications to this file are NOT required. ##### #################################################################################################### --- -binary_sensor: - - id: !extend bs_button_1 - on_click: - then: - - if: - condition: - - lambda: return sl_button_1_action->active_index().has_value(); - - lambda: return sl_button_1_action->active_index().value() == 1; - - and: &button_click_no_other_click - - lambda: return not bs_multi_touch->state; - - lambda: return not bs_swipe_left->state; - - lambda: return not bs_swipe_down->state; - - lambda: return not bs_swipe_right->state; - then: - - switch.toggle: sw_relay_1 - - - id: !extend bs_button_2 - on_click: - then: - - if: - condition: - - lambda: return sl_button_2_action->active_index().has_value(); - - lambda: return sl_button_2_action->active_index().value() == 1; - - and: *button_click_no_other_click - then: - - switch.toggle: sw_relay_2 - - - id: !extend bs_button_3 - on_click: - then: - - if: - condition: - - lambda: return sl_button_3_action->active_index().has_value(); - - lambda: return sl_button_3_action->active_index().value() == 1; - - and: *button_click_no_other_click - then: - - switch.toggle: sw_relay_3 - - - id: !extend bs_button_4 - on_click: - then: - - if: - condition: - - lambda: return sl_button_4_action->active_index().has_value(); - - lambda: return sl_button_4_action->active_index().value() == 1; - - and: *button_click_no_other_click - then: - - switch.toggle: sw_relay_4 - globals: - id: boot_initialization_relays type: bool @@ -159,6 +110,39 @@ script: } id(boot_initialization_relays) = true; + - id: !extend button_click_event + then: + - lambda: |- + if (id(click_counter) == 1) { // Single click + if (!bs_multi_touch->state and + !bs_swipe_left->state and + !bs_swipe_down->state and + !bs_swipe_right->state) { // Ignore if other events are active + switch (id(button_press_button)) { + case 1: + if (sl_button_1_action->active_index().has_value() and + sl_button_1_action->active_index().value() == 1) + sw_relay_1->toggle(); + break; + case 2: + if (sl_button_2_action->active_index().has_value() and + sl_button_2_action->active_index().value() == 1) + sw_relay_2->toggle(); + break; + case 3: + if (sl_button_3_action->active_index().has_value() and + sl_button_3_action->active_index().value() == 1) + sw_relay_3->toggle(); + break; + case 4: + if (sl_button_4_action->active_index().has_value() and + sl_button_4_action->active_index().value() == 1) + sw_relay_4->toggle(); + break; + } + } + } + - id: show_relay_status mode: restart then: From c65acc329876db6be7309aea53af27fe40993ce6 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 22 Dec 2024 01:37:07 +0100 Subject: [PATCH 11/13] Pass parameters to `button_click_event` --- .../TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 13 ++++++++----- .../TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml | 7 +++++-- components/tx_ultimate_easy/tx_ultimate_easy.cpp | 1 + 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index 50b7170..6de6fcf 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -116,15 +116,18 @@ script: - id: button_click_event mode: restart + parameters: + button_id: uint8_t + click_count: uint8_t then: - delay: milliseconds: ${BUTTON_MULTI_CLICK_DELAY} - lambda: |- - const std::string button_name = "bs_button_" + std::to_string(id(button_press_button)); + const std::string button_name = "bs_button_" + std::to_string(button_id); std::string event_name; - if (id(click_counter) == 1) event_name = "click"; - else if (id(click_counter) == 2) event_name = "double_click"; - else event_name = std::to_string(id(click_counter)) + "_click"; + if (click_count == 1) event_name = "click"; + else if (click_count == 2) event_name = "double_click"; + else event_name = std::to_string(click_count) + "_click"; button_action->execute(button_name.c_str(), event_name.c_str()); - id: buttons_release @@ -210,7 +213,7 @@ script: ESP_LOGW("core_hw_buttons", "Ignoring button press (too short)"); } else if (press_duration >= ${BUTTON_CLICK_MIN_LENGTH} and press_duration <= ${BUTTON_CLICK_MAX_LENGTH}) { // Short/normal click - button_click_event->execute(); + button_click_event->execute(id(button_press_button), id(click_counter)); } else if (press_duration >= ${BUTTON_LONG_PRESS_DELAY} and press_duration <= ${BUTTON_PRESS_TIMEOUT}) { button_action->execute(("bs_button_" + std::to_string(id(button_press_button))).c_str(), "long_click"); } else if (press_duration > ${BUTTON_PRESS_TIMEOUT}) { // Timeout or invalid diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml index 7bb4e55..9bbf7a0 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml @@ -113,12 +113,15 @@ script: - id: !extend button_click_event then: - lambda: |- - if (id(click_counter) == 1) { // Single click + ESP_LOGW("DEBUG", "button_click_event"); + ESP_LOGW("DEBUG", " button_id: %" PRIu8, button_id); + ESP_LOGW("DEBUG", " click_count: %" PRIu8, click_count); + if (click_count == 1) { // Single click if (!bs_multi_touch->state and !bs_swipe_left->state and !bs_swipe_down->state and !bs_swipe_right->state) { // Ignore if other events are active - switch (id(button_press_button)) { + switch (button_id) { case 1: if (sl_button_1_action->active_index().has_value() and sl_button_1_action->active_index().value() == 1) diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.cpp b/components/tx_ultimate_easy/tx_ultimate_easy.cpp index 6d90d4c..c6cce8d 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy.cpp +++ b/components/tx_ultimate_easy/tx_ultimate_easy.cpp @@ -131,6 +131,7 @@ namespace esphome { state == TOUCH_STATE_SWIPE_LEFT || state == TOUCH_STATE_SWIPE_RIGHT || state == TOUCH_STATE_MULTI_TOUCH) && + // Multi-touch events may have x < 0, all other events require valid x position (uart_received_bytes[6] >= 0 || state == TOUCH_STATE_MULTI_TOUCH); } From fbc5cb79ccd2c701348c4ac74b2338f2ddc4d2fc Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 22 Dec 2024 01:46:28 +0100 Subject: [PATCH 12/13] Reduce `BUTTON_MULTI_CLICK_DELAY` to 250ms --- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml | 2 +- ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml | 3 --- components/tx_ultimate_easy/tx_ultimate_easy.cpp | 1 + 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml index 6de6fcf..20eb5a7 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_buttons.yaml @@ -22,7 +22,7 @@ substitutions: BUTTON_CLICK_MIN_LENGTH: '50' # The minimum duration the click should last, in msec BUTTON_CLICK_MAX_LENGTH: '350' # The maximum duration the click should last, in msec - BUTTON_MULTI_CLICK_DELAY: '500' # The time to wait for another click, in msec + BUTTON_MULTI_CLICK_DELAY: '250' # The time to wait for another click, in msec BUTTON_PRESS_TIMEOUT: '10000' # Ignore if button is pressed for longer than this time, in msec BUTTON_LONG_PRESS_DELAY: '800' # The time to wait to consider a long press, in msec diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml index 9bbf7a0..e639876 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml @@ -113,9 +113,6 @@ script: - id: !extend button_click_event then: - lambda: |- - ESP_LOGW("DEBUG", "button_click_event"); - ESP_LOGW("DEBUG", " button_id: %" PRIu8, button_id); - ESP_LOGW("DEBUG", " click_count: %" PRIu8, click_count); if (click_count == 1) { // Single click if (!bs_multi_touch->state and !bs_swipe_left->state and diff --git a/components/tx_ultimate_easy/tx_ultimate_easy.cpp b/components/tx_ultimate_easy/tx_ultimate_easy.cpp index c6cce8d..640f3db 100644 --- a/components/tx_ultimate_easy/tx_ultimate_easy.cpp +++ b/components/tx_ultimate_easy/tx_ultimate_easy.cpp @@ -52,6 +52,7 @@ namespace esphome { } bool TxUltimateEasy::set_gang_count(const uint8_t gang_count) { + // Hardware supports maximum of 4 touch-sensitive buttons if (gang_count < 1 or gang_count > 4) return false; this->gang_count_ = gang_count; From be0fc353f6bdfa5e2372322c714d3fec57628902 Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Sun, 22 Dec 2024 02:03:35 +0100 Subject: [PATCH 13/13] Simplify relay handler --- ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml | 2 +- ...-Ultimate-Easy-ESPHome_core_hw_relays.yaml | 62 ++++++++++--------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml index 12ecd7d..5911a02 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml @@ -74,7 +74,7 @@ improv_serial: id: serial_improv logger: - level: INFO + level: DEBUG ota: platform: esphome diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml index e639876..dc59f0a 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_relays.yaml @@ -113,34 +113,40 @@ script: - id: !extend button_click_event then: - lambda: |- - if (click_count == 1) { // Single click - if (!bs_multi_touch->state and - !bs_swipe_left->state and - !bs_swipe_down->state and - !bs_swipe_right->state) { // Ignore if other events are active - switch (button_id) { - case 1: - if (sl_button_1_action->active_index().has_value() and - sl_button_1_action->active_index().value() == 1) - sw_relay_1->toggle(); - break; - case 2: - if (sl_button_2_action->active_index().has_value() and - sl_button_2_action->active_index().value() == 1) - sw_relay_2->toggle(); - break; - case 3: - if (sl_button_3_action->active_index().has_value() and - sl_button_3_action->active_index().value() == 1) - sw_relay_3->toggle(); - break; - case 4: - if (sl_button_4_action->active_index().has_value() and - sl_button_4_action->active_index().value() == 1) - sw_relay_4->toggle(); - break; - } - } + // Handle only single clicks + if (click_count != 1) + return; + + // Ignore if other touch events are active + if (bs_multi_touch->state || + bs_swipe_left->state || + bs_swipe_down->state || + bs_swipe_right->state) { + return; + } + + // Toggle relay if corresponding button action is enabled + switch (button_id) { + case 1: + if (sl_button_1_action->active_index().has_value() and + sl_button_1_action->active_index().value() == 1) + sw_relay_1->toggle(); + break; + case 2: + if (sl_button_2_action->active_index().has_value() and + sl_button_2_action->active_index().value() == 1) + sw_relay_2->toggle(); + break; + case 3: + if (sl_button_3_action->active_index().has_value() and + sl_button_3_action->active_index().value() == 1) + sw_relay_3->toggle(); + break; + case 4: + if (sl_button_4_action->active_index().has_value() and + sl_button_4_action->active_index().value() == 1) + sw_relay_4->toggle(); + break; } - id: show_relay_status