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.
This commit is contained in:
@@ -20,111 +20,53 @@ substitutions:
|
|||||||
BUTTON_3_ACTION_TEXT: "Relay 3 (toggle)"
|
BUTTON_3_ACTION_TEXT: "Relay 3 (toggle)"
|
||||||
BUTTON_4_ACTION_TEXT: "Relay 4 (toggle)"
|
BUTTON_4_ACTION_TEXT: "Relay 4 (toggle)"
|
||||||
|
|
||||||
binary_sensor:
|
BUTTON_CLICK_MIN_LENGTH: '50' # The minimum duration the click should last, in msec
|
||||||
- id: bs_button_1
|
BUTTON_CLICK_MAX_LENGTH: '350' # The maximum duration the click should last, in msec
|
||||||
name: Button 1
|
BUTTON_MULTI_CLICK_DELAY: '500' # The time to wait for another click, in msec
|
||||||
icon: mdi:gesture-tap-box
|
BUTTON_PRESS_TIMEOUT: '10000' # Ignore if buttor is pressed for longer than this time, in msec
|
||||||
internal: false
|
BUTTON_LONG_PRESS_DELAY: '800' # The time to wait to consider a long press, in msec
|
||||||
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
|
|
||||||
|
|
||||||
- id: bs_button_2
|
binary_sensor:
|
||||||
|
- &binary_sensor_button_base
|
||||||
|
id: bs_button_2
|
||||||
name: Button 2
|
name: Button 2
|
||||||
icon: mdi:gesture-tap-box
|
icon: mdi:gesture-tap-box
|
||||||
internal: true
|
|
||||||
platform: template
|
platform: template
|
||||||
on_click:
|
internal: true
|
||||||
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
|
|
||||||
|
|
||||||
- id: bs_button_3
|
- id: bs_button_3
|
||||||
name: Button 3
|
name: Button 3
|
||||||
icon: mdi:gesture-tap-box
|
<<: *binary_sensor_button_base
|
||||||
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
|
|
||||||
|
|
||||||
- id: bs_button_4
|
- id: bs_button_4
|
||||||
name: Button 4
|
name: Button 4
|
||||||
icon: mdi:gesture-tap-box
|
<<: *binary_sensor_button_base
|
||||||
internal: true
|
|
||||||
platform: template
|
- id: bs_button_1
|
||||||
on_click:
|
name: Button 1
|
||||||
then:
|
internal: false
|
||||||
- script.execute:
|
<<: *binary_sensor_button_base
|
||||||
id: button_action
|
|
||||||
component: bs_button_4
|
globals:
|
||||||
event: click
|
- id: button_press_button
|
||||||
on_double_click:
|
type: uint8_t
|
||||||
then:
|
restore_value: false
|
||||||
- script.execute:
|
initial_value: '0'
|
||||||
id: button_action
|
|
||||||
component: bs_button_4
|
- id: button_press_position
|
||||||
event: double_click
|
type: uint8_t
|
||||||
on_multi_click:
|
restore_value: false
|
||||||
- timing: *long_click-timing
|
initial_value: '0'
|
||||||
invalid_cooldown: ${invalid_cooldown}
|
|
||||||
then:
|
- id: button_press_start_time
|
||||||
- script.execute:
|
type: uint32_t
|
||||||
id: button_action
|
restore_value: false
|
||||||
component: bs_button_4
|
initial_value: '0'
|
||||||
event: long_click
|
|
||||||
|
- id: click_counter
|
||||||
|
type: uint8_t
|
||||||
|
restore_value: false
|
||||||
|
initial_value: '0'
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- id: !extend boot_initialize
|
- id: !extend boot_initialize
|
||||||
@@ -162,9 +104,15 @@ script:
|
|||||||
parameters:
|
parameters:
|
||||||
component: string
|
component: string
|
||||||
event: string
|
event: string
|
||||||
then: # There's nothing here so far
|
then:
|
||||||
# Extended by:
|
# Extended by:
|
||||||
# - core_api
|
# - 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
|
- id: buttons_release
|
||||||
mode: restart
|
mode: restart
|
||||||
@@ -195,34 +143,81 @@ script:
|
|||||||
touch_x: uint8_t
|
touch_x: uint8_t
|
||||||
then:
|
then:
|
||||||
- lambda: |-
|
- 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();
|
auto model_index = sl_tx_model_gang->active_index();
|
||||||
if (model_index.has_value()) {
|
if (model_index.has_value()) {
|
||||||
uint8_t model_idx = model_index.value() + 1;
|
const uint8_t model_idx = model_index.value() + 1; // Increment for 1-based indexing
|
||||||
switch (model_idx) {
|
if (model_idx == 1) {
|
||||||
case 1: // 1 Gang
|
button = 1; // Single button, always 1
|
||||||
bs_button_1->publish_state(true);
|
} else {
|
||||||
break;
|
const uint8_t step = 10 / model_idx; // Calculate step size for regions
|
||||||
case 2: // 2 Gang
|
button = (touch_x / step) + 1; // Determine button region
|
||||||
if (touch_x <= 5) bs_button_1->publish_state(true);
|
if (button > model_idx)
|
||||||
else bs_button_2->publish_state(true);
|
button = model_idx; // Clamp to max button count
|
||||||
break;
|
}
|
||||||
case 3: // 3 Gang
|
}
|
||||||
if (touch_x <= 3) bs_button_1->publish_state(true);
|
// Update binary sensor
|
||||||
else if (touch_x <= 7) bs_button_2->publish_state(true);
|
switch (button) {
|
||||||
else bs_button_3->publish_state(true);
|
case 1:
|
||||||
break;
|
bs_button_1->publish_state(true);
|
||||||
case 4: // 4 Gang
|
break;
|
||||||
if (touch_x <= 2) bs_button_1->publish_state(true);
|
case 2:
|
||||||
else if (touch_x <= 5) bs_button_2->publish_state(true);
|
bs_button_2->publish_state(true);
|
||||||
else if (touch_x <= 8) bs_button_3->publish_state(true);
|
break;
|
||||||
else bs_button_4->publish_state(true);
|
case 3:
|
||||||
break;
|
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
|
- id: !extend touch_on_release
|
||||||
then:
|
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
|
- id: !extend touch_swipe_left
|
||||||
then:
|
then:
|
||||||
|
|||||||
@@ -233,8 +233,8 @@ tx_ultimate_easy:
|
|||||||
ESP_LOGD("tx_ultimate_easy", " Position: %i", touch.x);
|
ESP_LOGD("tx_ultimate_easy", " Position: %i", touch.x);
|
||||||
|
|
||||||
uart:
|
uart:
|
||||||
id: uart_touch
|
- id: uart_touch
|
||||||
tx_pin: GPIO19
|
tx_pin: GPIO19
|
||||||
rx_pin: GPIO22
|
rx_pin: GPIO22
|
||||||
baud_rate: 115200
|
baud_rate: 115200
|
||||||
...
|
...
|
||||||
|
|||||||
Reference in New Issue
Block a user