335 lines
12 KiB
YAML
335 lines
12 KiB
YAML
####################################################################################################
|
|
##### TX Ultimate Easy for ESPHome #####
|
|
##### Repository: https://github.com/edwardtfn/TX-Ultimate-Easy #####
|
|
####################################################################################################
|
|
##### Purpose: ESPHome Core - Hardware - Buttons #####
|
|
####################################################################################################
|
|
##### Author: edwardtfn - https://github.com/edwardtfn - https://buymeacoffee.com/edwardfirmo #####
|
|
####################################################################################################
|
|
##### NOTE: #####
|
|
##### - Make changes ONLY if absolutely necessary and you have the required knowledge. #####
|
|
##### - For normal system use, modifications to this file are NOT required. #####
|
|
####################################################################################################
|
|
---
|
|
substitutions:
|
|
invalid_cooldown: 100ms
|
|
|
|
BUTTON_ACTION_NONE_TEXT: "None"
|
|
BUTTON_1_ACTION_TEXT: "Relay 1 (toggle)"
|
|
BUTTON_2_ACTION_TEXT: "Relay 2 (toggle)"
|
|
BUTTON_3_ACTION_TEXT: "Relay 3 (toggle)"
|
|
BUTTON_4_ACTION_TEXT: "Relay 4 (toggle)"
|
|
BUTTON_1_ACTION_FAILSAFE_TEXT: "Relay 1 (API failsafe only)"
|
|
BUTTON_2_ACTION_FAILSAFE_TEXT: "Relay 2 (API failsafe only)"
|
|
BUTTON_3_ACTION_FAILSAFE_TEXT: "Relay 3 (API failsafe only)"
|
|
BUTTON_4_ACTION_FAILSAFE_TEXT: "Relay 4 (API failsafe only)"
|
|
|
|
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: '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
|
|
|
|
# Button numbers constants
|
|
BUTTON_1_ID: '1'
|
|
BUTTON_2_ID: '2'
|
|
BUTTON_3_ID: '3'
|
|
BUTTON_4_ID: '4'
|
|
|
|
binary_sensor:
|
|
- &binary_sensor_button_base
|
|
id: bs_button_2
|
|
name: Button 2
|
|
icon: mdi:gesture-tap-box
|
|
platform: template
|
|
internal: true
|
|
|
|
- id: bs_button_3
|
|
name: Button 3
|
|
<<: *binary_sensor_button_base
|
|
|
|
- id: bs_button_4
|
|
name: Button 4
|
|
<<: *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
|
|
then:
|
|
- script.execute: boot_initialize_buttons
|
|
|
|
- id: boot_initialize_buttons
|
|
mode: restart
|
|
then:
|
|
- wait_until:
|
|
condition:
|
|
- lambda: return sl_tx_model_gang->active_index().has_value();
|
|
- lambda: |-
|
|
const uint8_t num_gangs = (sl_tx_model_gang->active_index().has_value()) ?
|
|
(sl_tx_model_gang->active_index().value() + 1) : 0;
|
|
if (num_gangs < 1 || num_gangs > 4) {
|
|
ESP_LOGE("core_hw_buttons", "Invalid number of gangs: %" PRIu8, num_gangs);
|
|
return;
|
|
}
|
|
bs_button_1->publish_state(false);
|
|
bs_button_1->set_internal(num_gangs < 1);
|
|
sl_button_1_action->set_internal(num_gangs < 1);
|
|
bs_button_2->publish_state(false);
|
|
bs_button_2->set_internal(num_gangs < 2);
|
|
sl_button_2_action->set_internal(num_gangs < 2);
|
|
bs_button_3->publish_state(false);
|
|
bs_button_3->set_internal(num_gangs < 3);
|
|
sl_button_3_action->set_internal(num_gangs < 3);
|
|
bs_button_4->publish_state(false);
|
|
bs_button_4->set_internal(num_gangs < 4);
|
|
sl_button_4_action->set_internal(num_gangs < 4);
|
|
|
|
- id: button_action
|
|
mode: parallel
|
|
parameters:
|
|
button: uint8_t
|
|
action: string
|
|
count: uint8_t
|
|
then:
|
|
- homeassistant.event:
|
|
event: esphome.tx_ultimate_easy
|
|
data:
|
|
device_name: !lambda return tx_device_name->state.c_str();
|
|
firmware: ${version}
|
|
domain: touch
|
|
type: button
|
|
action: !lambda return action.c_str();
|
|
button_id: !lambda return button;
|
|
count: !lambda return count;
|
|
position: !lambda return id(button_press_position);
|
|
- lambda: |-
|
|
ESP_LOGI("core_hw_buttons", "Button %" PRIu8 " action: '%s'", button, action.c_str());
|
|
if (action == "click") {
|
|
switch (button) {
|
|
case ${BUTTON_1_ID}:
|
|
bs_button_1_click_event->publish_state(true);
|
|
break;
|
|
case ${BUTTON_2_ID}:
|
|
bs_button_2_click_event->publish_state(true);
|
|
break;
|
|
case ${BUTTON_3_ID}:
|
|
bs_button_3_click_event->publish_state(true);
|
|
break;
|
|
case ${BUTTON_4_ID}:
|
|
bs_button_4_click_event->publish_state(true);
|
|
break;
|
|
}
|
|
} else if (action == "double_click") {
|
|
switch (button) {
|
|
case ${BUTTON_1_ID}:
|
|
bs_button_1_double_click_event->publish_state(true);
|
|
break;
|
|
case ${BUTTON_2_ID}:
|
|
bs_button_2_double_click_event->publish_state(true);
|
|
break;
|
|
case ${BUTTON_3_ID}:
|
|
bs_button_3_double_click_event->publish_state(true);
|
|
break;
|
|
case ${BUTTON_4_ID}:
|
|
bs_button_4_double_click_event->publish_state(true);
|
|
break;
|
|
}
|
|
} else if (action == "long_press") {
|
|
switch (button) {
|
|
case ${BUTTON_1_ID}:
|
|
bs_button_1_long_press_event->publish_state(true);
|
|
break;
|
|
case ${BUTTON_2_ID}:
|
|
bs_button_2_long_press_event->publish_state(true);
|
|
break;
|
|
case ${BUTTON_3_ID}:
|
|
bs_button_3_long_press_event->publish_state(true);
|
|
break;
|
|
case ${BUTTON_4_ID}:
|
|
bs_button_4_long_press_event->publish_state(true);
|
|
break;
|
|
}
|
|
}
|
|
id(button_press_button) = 0;
|
|
id(button_press_position) = 0;
|
|
id(button_press_start_time) = 0;
|
|
id(click_counter) = 0;
|
|
buttons_release->execute();
|
|
|
|
- id: button_click_event
|
|
mode: restart
|
|
parameters:
|
|
button: uint8_t
|
|
click_count: uint8_t
|
|
then:
|
|
- delay:
|
|
milliseconds: ${BUTTON_MULTI_CLICK_DELAY}
|
|
- lambda: |-
|
|
std::string event_name;
|
|
if (click_count == 1) event_name = "click";
|
|
else if (click_count == 2) event_name = "double_click";
|
|
else event_name = "multiple_click";
|
|
button_action->execute(button, event_name.c_str(), click_count);
|
|
|
|
- id: buttons_release
|
|
mode: restart
|
|
then:
|
|
- lambda: |-
|
|
std::vector<binary_sensor::BinarySensor*> buttons = {
|
|
bs_button_1, bs_button_2, bs_button_3, bs_button_4
|
|
};
|
|
for (auto* button : buttons) {
|
|
if (button->state) {
|
|
button->publish_state(false);
|
|
}
|
|
}
|
|
|
|
- id: !extend touch_on_multi_touch_release
|
|
then:
|
|
- script.execute: buttons_release
|
|
|
|
- id: !extend touch_on_press
|
|
then:
|
|
- script.execute:
|
|
id: touch_on_press_buttons
|
|
button: !lambda return button;
|
|
position: !lambda return position;
|
|
|
|
- id: touch_on_press_buttons
|
|
mode: restart
|
|
parameters:
|
|
button: uint8_t
|
|
position: uint8_t
|
|
then:
|
|
- lambda: |-
|
|
id(button_press_start_time) = millis();
|
|
id(button_press_button) = button;
|
|
// Update counters
|
|
if (id(button_press_position) == position) {
|
|
id(click_counter)++;
|
|
} else {
|
|
id(click_counter) = 1;
|
|
id(button_press_position) = position;
|
|
}
|
|
// 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;
|
|
}
|
|
|
|
- id: !extend touch_on_release
|
|
then:
|
|
- 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(id(button_press_button), id(click_counter));
|
|
} else if (press_duration >= ${BUTTON_LONG_PRESS_DELAY} and press_duration <= ${BUTTON_PRESS_TIMEOUT}) {
|
|
button_action->execute(id(button_press_button), "long_press", 1);
|
|
} 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: !extend touch_swipe_left
|
|
then:
|
|
- script.execute: buttons_release
|
|
|
|
- id: !extend touch_swipe_right
|
|
then:
|
|
- script.execute: buttons_release
|
|
|
|
select:
|
|
- &button_select_action_base
|
|
id: sl_button_1_action
|
|
name: Button 1 action
|
|
platform: template
|
|
options:
|
|
- "${BUTTON_ACTION_NONE_TEXT}"
|
|
- "${BUTTON_1_ACTION_TEXT}"
|
|
- "${BUTTON_1_ACTION_FAILSAFE_TEXT}"
|
|
initial_option: "${BUTTON_1_ACTION_TEXT}"
|
|
optimistic: true
|
|
restore_value: true
|
|
internal: true
|
|
entity_category: config
|
|
disabled_by_default: false
|
|
icon: mdi:dip-switch
|
|
|
|
- id: sl_button_2_action
|
|
name: Button 2 action
|
|
platform: template
|
|
options:
|
|
- "${BUTTON_ACTION_NONE_TEXT}"
|
|
- "${BUTTON_2_ACTION_TEXT}"
|
|
- "${BUTTON_2_ACTION_FAILSAFE_TEXT}"
|
|
initial_option: "${BUTTON_2_ACTION_TEXT}"
|
|
<<: *button_select_action_base
|
|
|
|
- id: sl_button_3_action
|
|
name: Button 3 action
|
|
platform: template
|
|
options:
|
|
- "${BUTTON_ACTION_NONE_TEXT}"
|
|
- "${BUTTON_3_ACTION_TEXT}"
|
|
- "${BUTTON_3_ACTION_FAILSAFE_TEXT}"
|
|
initial_option: "${BUTTON_3_ACTION_TEXT}"
|
|
<<: *button_select_action_base
|
|
|
|
- id: sl_button_4_action
|
|
name: Button 4 action
|
|
platform: template
|
|
options:
|
|
- "${BUTTON_ACTION_NONE_TEXT}"
|
|
- "${BUTTON_4_ACTION_TEXT}"
|
|
- "${BUTTON_4_ACTION_FAILSAFE_TEXT}"
|
|
initial_option: "${BUTTON_4_ACTION_TEXT}"
|
|
<<: *button_select_action_base
|
|
...
|