diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml index 085f35b..1bbee37 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core_hw_leds.yaml @@ -30,6 +30,7 @@ substitutions: LIGHT_INDIVIDUAL_RESTORE_MODE: RESTORE_DEFAULT_OFF LIGHT_ATTRS_DEFAULT: '0x64FFFFFF' # brightness=100, RGB=255,255,255 + LIGHT_DUAL_ATTRS_DEFAULT: '0x64FFFFFF64FFFFFF' # Both lights: brightness=100, RGB=255,255,255 TAG_CORE_HW_LEDS: core.hw.leds @@ -559,6 +560,22 @@ script: ESP_LOGI("${TAG_CORE_HW_LEDS}", "Restored light attributes: brightness=%d%%, RGB=(%d,%d,%d)", attrs.brightness, attrs.red, attrs.green, attrs.blue); + - id: light_attributes_restore_dual + mode: parallel + parameters: + light_ptr: light::LightState* + attr_global_ptr: uint64_t* + group_index: uint8_t # 1 or 2 + then: + - lambda: |- + auto attrs_pair = tx_ultimate_easy::unpack_dual_light_attributes(*attr_global_ptr); + tx_ultimate_easy::LightAttributes attrs = (group_index == 1) ? attrs_pair.first : attrs_pair.second; + + auto call = light_ptr->make_call(); + call.set_brightness(attrs.brightness / 100.0f); + call.set_rgb(attrs.red / 255.0f, attrs.green / 255.0f, attrs.blue / 255.0f); + call.perform(); + - id: light_attributes_save mode: parallel parameters: @@ -578,6 +595,32 @@ script: ESP_LOGD("${TAG_CORE_HW_LEDS}", "Saved light attributes: brightness=%d%%, RGB=(%d,%d,%d)", attrs.brightness, attrs.red, attrs.green, attrs.blue); + - id: light_attributes_save_dual + mode: parallel + parameters: + light_ptr: light::LightState* + attr_global_ptr: uint64_t* + group_index: uint8_t # 1 or 2 (which group within the dual pack) + then: + - lambda: |- + auto current = light_ptr->current_values; + tx_ultimate_easy::LightAttributes new_attrs = { + .brightness = uint8_t(current.get_brightness() * 100.0f), + .red = uint8_t(current.get_red() * 255.0f), + .green = uint8_t(current.get_green() * 255.0f), + .blue = uint8_t(current.get_blue() * 255.0f) + }; + + // Unpack existing dual attributes + auto existing = tx_ultimate_easy::unpack_dual_light_attributes(*attr_global_ptr); + + // Update the appropriate group + if (group_index == 1) { + *attr_global_ptr = tx_ultimate_easy::pack_dual_light_attributes(new_attrs, existing.second); + } else { + *attr_global_ptr = tx_ultimate_easy::pack_dual_light_attributes(existing.first, new_attrs); + } + select: # EU relay light mode template - &relay_light_mode_eu diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml index 7772766..e67cd67 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_standard_hw_relays.yaml @@ -69,89 +69,29 @@ globals: initial_value: '0' # All switches default to OFF # Bit layout: [unused(3)][sw_relay4][sw_relay3][sw_relay2][sw_relay1][sw_expose_leds] - - &global_attr_light - id: attr_light_eu_rl_1_1_bottom - type: uint32_t + - &global_attr_dual_light + id: attr_light_rl_1_1_both # 1-gang: relay 1, both groups + type: uint64_t restore_value: true - initial_value: '${LIGHT_ATTRS_DEFAULT}' - - id: attr_light_eu_rl_1_1_top - <<: *global_attr_light - - id: attr_light_eu_rl_1_2_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_1_2_top - <<: *global_attr_light - - id: attr_light_eu_rl_2_2_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_2_2_top - <<: *global_attr_light - - id: attr_light_eu_rl_1_3_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_1_3_top - <<: *global_attr_light - - id: attr_light_eu_rl_2_3_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_2_3_top - <<: *global_attr_light - - id: attr_light_eu_rl_3_3_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_3_3_top - <<: *global_attr_light - - id: attr_light_eu_rl_1_4_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_1_4_top - <<: *global_attr_light - - id: attr_light_eu_rl_2_4_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_2_4_top - <<: *global_attr_light - - id: attr_light_eu_rl_3_4_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_3_4_top - <<: *global_attr_light - - id: attr_light_eu_rl_4_4_bottom - <<: *global_attr_light - - id: attr_light_eu_rl_4_4_top - <<: *global_attr_light - - id: attr_light_us_rl_1_1_left - <<: *global_attr_light - - id: attr_light_us_rl_1_1_right - <<: *global_attr_light - - id: attr_light_us_rl_1_2_left - <<: *global_attr_light - - id: attr_light_us_rl_1_2_right - <<: *global_attr_light - - id: attr_light_us_rl_2_2_left - <<: *global_attr_light - - id: attr_light_us_rl_2_2_right - <<: *global_attr_light - - id: attr_light_us_rl_1_3_left - <<: *global_attr_light - - id: attr_light_us_rl_1_3_right - <<: *global_attr_light - - id: attr_light_us_rl_2_3_left - <<: *global_attr_light - - id: attr_light_us_rl_2_3_right - <<: *global_attr_light - - id: attr_light_us_rl_3_3_left - <<: *global_attr_light - - id: attr_light_us_rl_3_3_right - <<: *global_attr_light - - id: attr_light_us_rl_1_4_left - <<: *global_attr_light - - id: attr_light_us_rl_1_4_right - <<: *global_attr_light - - id: attr_light_us_rl_2_4_left - <<: *global_attr_light - - id: attr_light_us_rl_2_4_right - <<: *global_attr_light - - id: attr_light_us_rl_3_4_left - <<: *global_attr_light - - id: attr_light_us_rl_3_4_right - <<: *global_attr_light - - id: attr_light_us_rl_4_4_left - <<: *global_attr_light - - id: attr_light_us_rl_4_4_right - <<: *global_attr_light + initial_value: '${LIGHT_DUAL_ATTRS_DEFAULT}' + - id: attr_light_rl_1_2_both # 2-gang: relay 1, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_2_2_both # 2-gang: relay 2, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_1_3_both # 3-gang: relay 1, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_2_3_both # 3-gang: relay 2, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_3_3_both # 3-gang: relay 3, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_1_4_both # 4-gang: relay 1, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_2_4_both # 4-gang: relay 2, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_3_4_both # 4-gang: relay 3, both groups + <<: *global_attr_dual_light + - id: attr_light_rl_4_4_both # 4-gang: relay 4, both groups + <<: *global_attr_dual_light light: # These lights are used for the physical relays to be shown as lights @@ -248,9 +188,10 @@ light: to: 9 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_1_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_1_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_1_1_both); + group_index: 1 - id: light_eu_rl_1_1_top name: Light - Relay 1 of 1 (top) <<: *light_partition_relays @@ -260,9 +201,10 @@ light: to: 23 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_1_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_1_top); + attr_global_ptr: !lambda return &id(attr_light_rl_1_1_both); + group_index: 2 - id: light_eu_rl_1_2_bottom name: Light - Relay 1 of 2 (bottom) <<: *light_partition_relays @@ -272,9 +214,10 @@ light: to: 11 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_2_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_2_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_1_2_both); + group_index: 1 - id: light_eu_rl_1_2_top name: Light - Relay 1 of 2 (top) <<: *light_partition_relays @@ -284,9 +227,10 @@ light: to: 21 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_2_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_2_top); + attr_global_ptr: !lambda return &id(attr_light_rl_1_2_both); + group_index: 2 - id: light_eu_rl_2_2_bottom name: Light - Relay 2 of 2 (bottom) <<: *light_partition_relays @@ -296,9 +240,10 @@ light: to: 7 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_2_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_2_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_2_2_both); + group_index: 1 - id: light_eu_rl_2_2_top name: Light - Relay 2 of 2 (top) <<: *light_partition_relays @@ -308,9 +253,10 @@ light: to: 25 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_2_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_2_top); + attr_global_ptr: !lambda return &id(attr_light_rl_2_2_both); + group_index: 2 - id: light_eu_rl_1_3_bottom name: Light - Relay 1 of 3 (bottom) <<: *light_partition_relays @@ -320,9 +266,10 @@ light: to: 12 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_3_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_3_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_1_3_both); + group_index: 1 - id: light_eu_rl_1_3_top name: Light - Relay 1 of 3 (top) <<: *light_partition_relays @@ -332,9 +279,10 @@ light: to: 20 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_3_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_3_top); + attr_global_ptr: !lambda return &id(attr_light_rl_1_3_both); + group_index: 2 - id: light_eu_rl_2_3_bottom name: Light - Relay 2 of 3 (bottom) <<: *light_partition_relays @@ -344,9 +292,10 @@ light: to: 9 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_3_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_3_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_2_3_both); + group_index: 1 - id: light_eu_rl_2_3_top name: Light - Relay 2 of 3 (top) <<: *light_partition_relays @@ -356,9 +305,10 @@ light: to: 23 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_3_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_3_top); + attr_global_ptr: !lambda return &id(attr_light_rl_2_3_both); + group_index: 2 - id: light_eu_rl_3_3_bottom name: Light - Relay 3 of 3 (bottom) <<: *light_partition_relays @@ -368,9 +318,10 @@ light: to: 6 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_3_3_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_3_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_3_3_both); + group_index: 1 - id: light_eu_rl_3_3_top name: Light - Relay 3 of 3 (top) <<: *light_partition_relays @@ -380,9 +331,10 @@ light: to: 26 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_3_3_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_3_top); + attr_global_ptr: !lambda return &id(attr_light_rl_3_3_both); + group_index: 2 - id: light_eu_rl_1_4_bottom name: Light - Relay 1 of 4 (bottom) <<: *light_partition_relays @@ -392,9 +344,10 @@ light: to: 12 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_4_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_1_4_both); + group_index: 1 - id: light_eu_rl_1_4_top name: Light - Relay 1 of 4 (top) <<: *light_partition_relays @@ -404,9 +357,10 @@ light: to: 20 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_1_4_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_1_4_top); + attr_global_ptr: !lambda return &id(attr_light_rl_1_4_both); + group_index: 2 - id: light_eu_rl_2_4_bottom name: Light - Relay 2 of 4 (bottom) <<: *light_partition_relays @@ -416,9 +370,10 @@ light: to: 10 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_4_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_2_4_both); + group_index: 1 - id: light_eu_rl_2_4_top name: Light - Relay 2 of 4 (top) <<: *light_partition_relays @@ -428,9 +383,10 @@ light: to: 22 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_2_4_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_2_4_top); + attr_global_ptr: !lambda return &id(attr_light_rl_2_4_both); + group_index: 2 - id: light_eu_rl_3_4_bottom name: Light - Relay 3 of 4 (bottom) <<: *light_partition_relays @@ -440,9 +396,10 @@ light: to: 8 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_3_4_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_3_4_both); + group_index: 1 - id: light_eu_rl_3_4_top name: Light - Relay 3 of 4 (top) <<: *light_partition_relays @@ -452,9 +409,10 @@ light: to: 24 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_3_4_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_3_4_top); + attr_global_ptr: !lambda return &id(attr_light_rl_3_4_both); + group_index: 2 - id: light_eu_rl_4_4_bottom name: Light - Relay 4 of 4 (bottom) <<: *light_partition_relays @@ -464,9 +422,10 @@ light: to: 6 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_4_4_bottom); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_4_4_bottom); + attr_global_ptr: !lambda return &id(attr_light_rl_4_4_both); + group_index: 1 - id: light_eu_rl_4_4_top name: Light - Relay 4 of 4 (top) <<: *light_partition_relays @@ -476,9 +435,10 @@ light: to: 26 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_eu_rl_4_4_top); - attr_global_ptr: !lambda return &id(attr_light_eu_rl_4_4_top); + attr_global_ptr: !lambda return &id(attr_light_rl_4_4_both); + group_index: 2 - id: light_us_rl_1_1_left name: Light - Relay 1 of 1 (left) <<: *light_partition_relays @@ -488,9 +448,10 @@ light: to: 24 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_1_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_1_left); + attr_global_ptr: !lambda return &id(attr_light_rl_1_1_both); + group_index: 1 - id: light_us_rl_1_1_right name: Light - Relay 1 of 1 (right) <<: *light_partition_relays @@ -503,9 +464,10 @@ light: to: 8 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_1_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_1_right); + attr_global_ptr: !lambda return &id(attr_light_rl_1_1_both); + group_index: 2 - id: light_us_rl_1_2_left name: Light - Relay 1 of 2 (left) <<: *light_partition_relays @@ -515,9 +477,10 @@ light: to: 24 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_2_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_2_left); + attr_global_ptr: !lambda return &id(attr_light_rl_1_2_both); + group_index: 1 - id: light_us_rl_1_2_right name: Light - Relay 1 of 2 (right) <<: *light_partition_relays @@ -530,9 +493,10 @@ light: to: 3 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_2_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_2_right); + attr_global_ptr: !lambda return &id(attr_light_rl_1_2_both); + group_index: 2 - id: light_us_rl_2_2_left name: Light - Relay 2 of 2 (left) <<: *light_partition_relays @@ -542,9 +506,10 @@ light: to: 19 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_2_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_2_left); + attr_global_ptr: !lambda return &id(attr_light_rl_2_2_both); + group_index: 1 - id: light_us_rl_2_2_right name: Light - Relay 2 of 2 (right) <<: *light_partition_relays @@ -554,9 +519,10 @@ light: to: 8 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_2_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_2_right); + attr_global_ptr: !lambda return &id(attr_light_rl_2_2_both); + group_index: 2 - id: light_us_rl_1_3_left name: Light - Relay 1 of 3 (left) <<: *light_partition_relays @@ -566,9 +532,10 @@ light: to: 23 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_3_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_3_left); + attr_global_ptr: !lambda return &id(attr_light_rl_1_3_both); + group_index: 1 - id: light_us_rl_1_3_right name: Light - Relay 1 of 3 (right) <<: *light_partition_relays @@ -578,9 +545,10 @@ light: to: 1 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_3_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_3_right); + attr_global_ptr: !lambda return &id(attr_light_rl_1_3_both); + group_index: 2 - id: light_us_rl_2_3_left name: Light - Relay 2 of 3 (left) <<: *light_partition_relays @@ -590,9 +558,10 @@ light: to: 20 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_3_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_3_left); + attr_global_ptr: !lambda return &id(attr_light_rl_2_3_both); + group_index: 1 - id: light_us_rl_2_3_right name: Light - Relay 2 of 3 (right) <<: *light_partition_relays @@ -602,9 +571,10 @@ light: to: 4 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_3_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_3_right); + attr_global_ptr: !lambda return &id(attr_light_rl_2_3_both); + group_index: 2 - id: light_us_rl_3_3_left name: Light - Relay 3 of 3 (left) <<: *light_partition_relays @@ -614,9 +584,10 @@ light: to: 17 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_3_3_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_3_3_left); + attr_global_ptr: !lambda return &id(attr_light_rl_3_3_both); + group_index: 1 - id: light_us_rl_3_3_right name: Light - Relay 3 of 3 (right) <<: *light_partition_relays @@ -626,9 +597,10 @@ light: to: 7 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_3_3_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_3_3_right); + attr_global_ptr: !lambda return &id(attr_light_rl_3_3_both); + group_index: 2 - id: light_us_rl_1_4_left name: Light - Relay 1 of 4 (left) <<: *light_partition_relays @@ -638,9 +610,10 @@ light: to: 24 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_4_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_4_left); + attr_global_ptr: !lambda return &id(attr_light_rl_1_4_both); + group_index: 1 - id: light_us_rl_1_4_right name: Light - Relay 1 of 4 (right) <<: *light_partition_relays @@ -653,9 +626,10 @@ light: to: 0 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_1_4_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_1_4_right); + attr_global_ptr: !lambda return &id(attr_light_rl_1_4_both); + group_index: 2 - id: light_us_rl_2_4_left name: Light - Relay 2 of 4 (left) <<: *light_partition_relays @@ -665,9 +639,10 @@ light: to: 22 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_4_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_4_left); + attr_global_ptr: !lambda return &id(attr_light_rl_2_4_both); + group_index: 1 - id: light_us_rl_2_4_right name: Light - Relay 2 of 4 (right) <<: *light_partition_relays @@ -677,9 +652,10 @@ light: to: 2 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_2_4_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_2_4_right); + attr_global_ptr: !lambda return &id(attr_light_rl_2_4_both); + group_index: 2 - id: light_us_rl_3_4_left name: Light - Relay 3 of 4 (left) <<: *light_partition_relays @@ -689,9 +665,10 @@ light: to: 18 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_3_4_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_3_4_left); + attr_global_ptr: !lambda return &id(attr_light_rl_3_4_both); + group_index: 1 - id: light_us_rl_3_4_right name: Light - Relay 3 of 4 (right) <<: *light_partition_relays @@ -701,9 +678,10 @@ light: to: 6 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_3_4_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_3_4_right); + attr_global_ptr: !lambda return &id(attr_light_rl_3_4_both); + group_index: 2 - id: light_us_rl_4_4_left name: Light - Relay 4 of 4 (left) <<: *light_partition_relays @@ -713,9 +691,10 @@ light: to: 16 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_4_4_left); - attr_global_ptr: !lambda return &id(attr_light_us_rl_4_4_left); + attr_global_ptr: !lambda return &id(attr_light_rl_4_4_both); + group_index: 1 - id: light_us_rl_4_4_right name: Light - Relay 4 of 4 (right) <<: *light_partition_relays @@ -725,9 +704,10 @@ light: to: 8 on_state: - script.execute: - id: light_attributes_save + id: light_attributes_save_dual light_ptr: !lambda return id(light_us_rl_4_4_right); - attr_global_ptr: !lambda return &id(attr_light_us_rl_4_4_right); + attr_global_ptr: !lambda return &id(attr_light_rl_4_4_both); + group_index: 2 output: - id: output_relay_1 @@ -1144,7 +1124,7 @@ script: id: sl_relay_4_mode index: !lambda return std::min((uint8_t)((id(relay_modes_packed) >> 12) & 0x0F), (uint8_t)2); - # Switch states (new - your improved version) + # Switch states - if: condition: lambda: return (id(relay_switches_packed) & 0x01) == 0; @@ -1184,6 +1164,72 @@ script: # update lights based on new relay statuses (catch up, as this should be done when each relay is updated) - script.execute: show_relay_status + # Light attributes restoration + - lambda: |- + // Restore all light attributes based on current model and gang count + switch (id(gang_count)) { + case 1: + if (id(is_us_model)) { + light_attributes_restore_dual->execute(id(light_us_rl_1_1_left), &id(attr_light_rl_1_1_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_1_1_right), &id(attr_light_rl_1_1_both), 2); + } else { + light_attributes_restore_dual->execute(id(light_eu_rl_1_1_bottom), &id(attr_light_rl_1_1_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_1_1_top), &id(attr_light_rl_1_1_both), 2); + } + break; + case 2: + if (id(is_us_model)) { + light_attributes_restore_dual->execute(id(light_us_rl_1_2_left), &id(attr_light_rl_1_2_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_1_2_right), &id(attr_light_rl_1_2_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_2_2_left), &id(attr_light_rl_2_2_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_2_2_right), &id(attr_light_rl_2_2_both), 2); + } else { + light_attributes_restore_dual->execute(id(light_eu_rl_1_2_bottom), &id(attr_light_rl_1_2_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_1_2_top), &id(attr_light_rl_1_2_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_2_2_bottom), &id(attr_light_rl_2_2_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_2_2_top), &id(attr_light_rl_2_2_both), 2); + } + break; + case 3: + if (id(is_us_model)) { + light_attributes_restore_dual->execute(id(light_us_rl_1_3_left), &id(attr_light_rl_1_3_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_1_3_right), &id(attr_light_rl_1_3_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_2_3_left), &id(attr_light_rl_2_3_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_2_3_right), &id(attr_light_rl_2_3_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_3_3_left), &id(attr_light_rl_3_3_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_3_3_right), &id(attr_light_rl_3_3_both), 2); + } else { + light_attributes_restore_dual->execute(id(light_eu_rl_1_3_bottom), &id(attr_light_rl_1_3_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_1_3_top), &id(attr_light_rl_1_3_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_2_3_bottom), &id(attr_light_rl_2_3_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_2_3_top), &id(attr_light_rl_2_3_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_3_3_bottom), &id(attr_light_rl_3_3_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_3_3_top), &id(attr_light_rl_3_3_both), 2); + } + break; + case 3: + if (id(is_us_model)) { + light_attributes_restore_dual->execute(id(light_us_rl_1_4_left), &id(attr_light_rl_1_4_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_1_4_right), &id(attr_light_rl_1_4_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_2_4_left), &id(attr_light_rl_2_4_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_2_4_right), &id(attr_light_rl_2_4_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_3_4_left), &id(attr_light_rl_3_4_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_3_4_right), &id(attr_light_rl_3_4_both), 2); + light_attributes_restore_dual->execute(id(light_us_rl_4_4_left), &id(attr_light_rl_4_4_both), 1); + light_attributes_restore_dual->execute(id(light_us_rl_4_4_right), &id(attr_light_rl_4_4_both), 2); + } else { + light_attributes_restore_dual->execute(id(light_eu_rl_1_4_bottom), &id(attr_light_rl_1_4_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_1_4_top), &id(attr_light_rl_1_4_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_2_4_bottom), &id(attr_light_rl_2_4_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_2_4_top), &id(attr_light_rl_2_4_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_3_4_bottom), &id(attr_light_rl_3_4_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_3_4_top), &id(attr_light_rl_3_4_both), 2); + light_attributes_restore_dual->execute(id(light_eu_rl_4_4_bottom), &id(attr_light_rl_4_4_both), 1); + light_attributes_restore_dual->execute(id(light_eu_rl_4_4_top), &id(attr_light_rl_4_4_both), 2); + } + break; + } + - id: show_relay_status mode: restart then: diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp b/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp new file mode 100644 index 0000000..cd7a290 --- /dev/null +++ b/components/tx_ultimate_easy/tx_ultimate_easy_light.cpp @@ -0,0 +1,51 @@ +// tx_ultimate_easy_light.cpp + +#include "esphome/core/log.h" +#include "tx_ultimate_easy_light.h" +#include +#include + +namespace esphome { + namespace tx_ultimate_easy { + + // Constant definitions + const LightAttributes LIGHT_ATTRS_DEFAULT = {100, 255, 255, 255}; + const uint32_t LIGHT_ATTRS_DEFAULT_PACKED = 0x64FFFFFF; + const uint64_t DEFAULT_DUAL_LIGHT_ATTRS_PACKED = 0x64FFFFFF64FFFFFF; + + // Function implementations + uint32_t pack_light_attributes(const LightAttributes& attr) { + return (uint32_t(attr.brightness) << 24) | + (uint32_t(attr.red) << 16) | + (uint32_t(attr.green) << 8) | + (uint32_t(attr.blue)); + } + + LightAttributes unpack_light_attributes(uint32_t packed) { + return { + .brightness = uint8_t((packed >> 24) & 0xFF), + .red = uint8_t((packed >> 16) & 0xFF), + .green = uint8_t((packed >> 8) & 0xFF), + .blue = uint8_t(packed & 0xFF) + }; + } + + uint64_t pack_dual_light_attributes(const LightAttributes& group1_attrs, const LightAttributes& group2_attrs) { + uint32_t group1_packed = pack_light_attributes(group1_attrs); + uint32_t group2_packed = pack_light_attributes(group2_attrs); + + return (uint64_t(group2_packed) << 32) | uint64_t(group1_packed); + } + + std::pair unpack_dual_light_attributes(uint64_t packed) { + uint32_t group1_packed = uint32_t(packed & 0xFFFFFFFF); // Lower 32 bits + uint32_t group2_packed = uint32_t((packed >> 32) & 0xFFFFFFFF); // Upper 32 bits + + return std::make_pair( + unpack_light_attributes(group1_packed), + unpack_light_attributes(group2_packed) + ); + } + + } // namespace tx_ultimate_easy +} // namespace esphome diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_light.h b/components/tx_ultimate_easy/tx_ultimate_easy_light.h new file mode 100644 index 0000000..3a892c3 --- /dev/null +++ b/components/tx_ultimate_easy/tx_ultimate_easy_light.h @@ -0,0 +1,113 @@ +// tx_ultimate_easy_light.h + +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/components/uart/uart.h" +#include "esphome/components/uart/uart_component.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/switch/switch.h" +#include "esphome/components/light/addressable_light.h" +#include + +namespace esphome { + namespace tx_ultimate_easy { + + /** + * @brief Light attributes structure for storing color and brightness data. + * + * This structure represents the visual attributes of a light without the state (on/off). + * It's designed to be packed efficiently into a 32-bit integer for NVS storage. + * RGB components use the full 8-bit range (0-255) while brightness uses percentage (0-100). + */ + struct LightAttributes { + uint8_t brightness; ///< Light brightness percentage (0-100, where 100 = full brightness) + uint8_t red; ///< Red color component (0-255) + uint8_t green; ///< Green color component (0-255) + uint8_t blue; ///< Blue color component (0-255) + }; + + /** + * @brief Packs LightAttributes structure into a 32-bit integer for storage. + * + * Bit layout: [brightness(8)][red(8)][green(8)][blue(8)] + * This allows efficient storage in ESPHome globals with restore_value support. + * Note: Brightness values > 100 will be clamped to 100 during packing. + * + * @param attr The LightAttributes structure to pack + * @return uint32_t Packed representation suitable for NVS storage + * + * @example + * LightAttributes attrs = {75, 255, 128, 64}; + * uint32_t packed = pack_light_attributes(attrs); // Returns 0x4BFF8040 + */ + uint32_t pack_light_attributes(const LightAttributes& attr); + + /** + * @brief Unpacks a 32-bit integer back into LightAttributes structure. + * + * Reverses the packing operation to extract individual color and brightness values. + * Brightness values > 100 in the packed data will be clamped to 100. + * + * @param packed The packed 32-bit representation + * @return LightAttributes The unpacked structure with individual components + * + * @example + * uint32_t packed = 0x4BFF8040; + * LightAttributes attrs = unpack_light_attributes(packed); + * // attrs.brightness = 75, attrs.red = 255, attrs.green = 128, attrs.blue = 64 + */ + LightAttributes unpack_light_attributes(uint32_t packed); + + /** + * @brief Default light attributes for initialization. + * + * Provides sensible defaults for new lights: + * - Brightness: 100 (full brightness) + * - Red: 255 (full intensity) + * - Green: 255 (full intensity) + * - Blue: 255 (full intensity) + * Result: Pure white color at full brightness + */ + extern const LightAttributes LIGHT_ATTRS_DEFAULT; + + /** + * @brief Pre-packed version of DEFAULT_LIGHT_ATTRS for convenience. + * + * This constant contains LIGHT_ATTRS_DEFAULT already packed into uint32_t format, + * suitable for direct use in ESPHome YAML initial_value fields. + * + * Value: 0x64FFFFFF (brightness=100, RGB=255,255,255) + */ + extern const uint32_t LIGHT_ATTRS_DEFAULT_PACKED; + + /** + * @brief Packs two LightAttributes into a single uint64_t for dual light storage. + * + * Bit layout: [group2_light(32)][group1_light(32)] + * Each light uses 32 bits: [brightness(8)][red(8)][green(8)][blue(8)] + * + * @param group1_attrs First light attributes (stored in lower 32 bits) + * @param group2_attrs Second light attributes (stored in upper 32 bits) + * @return uint64_t Packed representation suitable for NVS storage + */ + uint64_t pack_dual_light_attributes(const LightAttributes& group1_attrs, const LightAttributes& group2_attrs); + + /** + * @brief Unpacks uint64_t back into two LightAttributes structures. + * + * @param packed The packed 64-bit representation + * @return std::pair Pair of (group1, group2) attributes + */ + std::pair unpack_dual_light_attributes(uint64_t packed); + + /** + * @brief Pre-packed version of dual default attributes for convenience. + * + * Value: 0x64FFFFFF64FFFFFF (both lights: brightness=100, RGB=255,255,255) + */ + extern const uint64_t DEFAULT_DUAL_LIGHT_ATTRS_PACKED; + + } // namespace tx_ultimate_easy +} // namespace esphome diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp b/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp new file mode 100644 index 0000000..92ad4cd --- /dev/null +++ b/components/tx_ultimate_easy/tx_ultimate_easy_touch.cpp @@ -0,0 +1,227 @@ +// tx_ultimate_easy_touch.cpp + +#include "esphome/core/log.h" +#include "tx_ultimate_easy_touch.h" +#include +#include + +namespace esphome { + namespace tx_ultimate_easy { + + void TxUltimateEasy::setup() { + ESP_LOGI(TAG, "TX Ultimate Easy is initialized"); + } + + void TxUltimateEasy::loop() { + bool found = false; + std::array uart_received_bytes{}; + int byte = -1; + int i = 0; + + while (this->available()) { + byte = this->read(); + if (byte == HEADER_BYTE_1) { + this->handle_touch(uart_received_bytes); + i = 0; + } + if (i < UART_RECEIVED_BYTES_SIZE) { + uart_received_bytes[i] = byte; + i++; + } + if (byte != 0x00) { + found = true; + } + } + if (found) this->handle_touch(uart_received_bytes); + } + + void TxUltimateEasy::handle_touch(const std::array &uart_received_bytes) { + ESP_LOGV(TAG, "------------"); + ESP_LOGV(TAG, "- UART-Log -"); + ESP_LOGV(TAG, "------------"); + for (int i = 0; i < UART_RECEIVED_BYTES_SIZE; i++) { + ESP_LOGV(TAG, "UART - Log - Byte[%i]: %i", i, uart_received_bytes[i]); + } + if (this->is_valid_data(uart_received_bytes)) { + this->send_touch_(this->get_touch_point(uart_received_bytes)); + } + } + + 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) { + // Hardware supports maximum of 4 touch-sensitive buttons + 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) { + // Validate position bounds + if (position > TOUCH_MAX_POSITION) + return 0; + + // Special case for single gang (only one button exists) + if (this->gang_count_ == 1) + return 1; + + // Calculate button number Change to round up instead of truncate (integer division) + const uint8_t width = (TOUCH_MAX_POSITION + gang_count_) / this->gang_count_; // Width of each button region + if (width < 1) // Invalid width - and prevents division by zero + return 0; + 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; + } + + void TxUltimateEasy::send_touch_(TouchPoint tp) { + this->trigger_touch_event_.trigger(tp); + switch (tp.state) { + case TOUCH_STATE_RELEASE: + if (tp.x >= 17) { + tp.x -= 16; + ESP_LOGV(TAG, "Long touch - Released (x=%d)", tp.x); + this->trigger_long_touch_release_.trigger(tp); + } else { + ESP_LOGV(TAG, "Touch - Released (x=%d)", tp.x); + this->trigger_release_.trigger(tp); + } + break; + + case TOUCH_STATE_PRESS: + ESP_LOGV(TAG, "Touch - Pressed (x=%d)", tp.x); + this->trigger_touch_.trigger(tp); + break; + + case TOUCH_STATE_SWIPE_LEFT: + ESP_LOGV(TAG, "Swipe - Left (x=%d)", tp.x); + this->trigger_swipe_left_.trigger(tp); + break; + + case TOUCH_STATE_SWIPE_RIGHT: + ESP_LOGV(TAG, "Swipe - Right (x=%d)", tp.x); + this->trigger_swipe_right_.trigger(tp); + break; + + case TOUCH_STATE_MULTI_TOUCH: + ESP_LOGV(TAG, "Multi touch - Released"); + this->trigger_multi_touch_release_.trigger(tp); + break; + + default: + break; + } + } + + bool TxUltimateEasy::is_valid_data(const std::array &uart_received_bytes) { + if (uart_received_bytes[0] != HEADER_BYTE_1 || + uart_received_bytes[1] != HEADER_BYTE_2 || + uart_received_bytes[2] != VALID_DATA_BYTE_2 || + uart_received_bytes[3] != VALID_DATA_BYTE_3) { + return false; + } + + int state = this->get_touch_state(uart_received_bytes); + return (state == TOUCH_STATE_PRESS || + state == TOUCH_STATE_RELEASE || + 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); + } + + int TxUltimateEasy::get_touch_position_x(const std::array &uart_received_bytes) { + switch (uart_received_bytes[4]) { + case TOUCH_STATE_RELEASE: + case TOUCH_STATE_MULTI_TOUCH: + return uart_received_bytes[5]; + + case TOUCH_STATE_SWIPE_LEFT: + case TOUCH_STATE_SWIPE_RIGHT: + // uart_received_bytes[5] indicates swipe direction: + // 12 = find last touch position (scan right to left) + // 13 = find first touch position (scan left to right) + // Bytes 6-7 contain a 10-bit bitmap of touch positions: + // Byte 7: bits 0-7 represent positions 1-8 + // Byte 6: bits 0-1 represent positions 9-10 + switch (uart_received_bytes[5]) { + case 12: + for (uint8_t i = 10; i > 0; i--) { + if (i > 8 + ? uart_received_bytes[6] & (1 << (i - 9)) + : uart_received_bytes[7] & (1 << (i - 1))) { + return i; + } + } + break; + case 13: + for (uint8_t i = 1; i <= 10; i++) { + if (i > 8 + ? uart_received_bytes[6] & (1 << (i - 9)) + : uart_received_bytes[7] & (1 << (i - 1))) { + return i; + } + } + break; + } + return uart_received_bytes[5]; + + default: + return uart_received_bytes[6]; + } + } + + int TxUltimateEasy::get_touch_state(const std::array &uart_received_bytes) { + int state = uart_received_bytes[4]; + if (state == TOUCH_STATE_PRESS && uart_received_bytes[5] != 0) + state = TOUCH_STATE_RELEASE; + if (state == TOUCH_STATE_RELEASE && uart_received_bytes[5] == TOUCH_STATE_MULTI_TOUCH) + state = TOUCH_STATE_MULTI_TOUCH; + if (state == TOUCH_STATE_SWIPE) { + state = (uart_received_bytes[5] == TOUCH_STATE_SWIPE_RIGHT) ? TOUCH_STATE_SWIPE_RIGHT : + (uart_received_bytes[5] == TOUCH_STATE_SWIPE_LEFT) ? TOUCH_STATE_SWIPE_LEFT : state; + } + return state; + } + + 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: + tp.state_str = "RELEASE"; + break; + case TOUCH_STATE_PRESS: + tp.state_str = "PRESS"; + break; + case TOUCH_STATE_SWIPE: + tp.state_str = "SWIPE"; + break; + case TOUCH_STATE_MULTI_TOUCH: + tp.state_str = "MULTI_TOUCH"; + break; + case TOUCH_STATE_SWIPE_RIGHT: + tp.state_str = "SWIPE_RIGHT"; + break; + case TOUCH_STATE_SWIPE_LEFT: + tp.state_str = "SWIPE_LEFT"; + break; + default: + tp.state_str = "Unknown"; + break; + } + return tp; + } + + } // namespace tx_ultimate_easy +} // namespace esphome diff --git a/components/tx_ultimate_easy/tx_ultimate_easy_touch.h b/components/tx_ultimate_easy/tx_ultimate_easy_touch.h new file mode 100644 index 0000000..08086a3 --- /dev/null +++ b/components/tx_ultimate_easy/tx_ultimate_easy_touch.h @@ -0,0 +1,86 @@ +// tx_ultimate_easy_touch.h + +#pragma once + +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/components/uart/uart.h" +#include "esphome/components/uart/uart_component.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "esphome/components/switch/switch.h" +#include "esphome/components/light/addressable_light.h" +#include + +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; + constexpr uint8_t TOUCH_STATE_SWIPE = 0x03; + constexpr uint8_t TOUCH_STATE_MULTI_TOUCH = 0x0B; + constexpr uint8_t TOUCH_STATE_SWIPE_RIGHT = 0x0C; + constexpr uint8_t TOUCH_STATE_SWIPE_LEFT = 0x0D; + + // UART Constants + constexpr int UART_RECEIVED_BYTES_SIZE = 15; + constexpr int HEADER_BYTE_1 = 0xAA; + constexpr int HEADER_BYTE_2 = 0x55; + constexpr int VALID_DATA_BYTE_2 = 0x01; + constexpr int VALID_DATA_BYTE_3 = 0x02; + + // Log tag + 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"; + }; + + class TxUltimateEasy : public uart::UARTDevice, public Component { + public: + Trigger *get_trigger_touch_event() { return &this->trigger_touch_event_; } + Trigger *get_trigger_touch() { return &this->trigger_touch_; } + Trigger *get_trigger_release() { return &this->trigger_release_; } + Trigger *get_trigger_swipe_left() { return &this->trigger_swipe_left_; } + Trigger *get_trigger_swipe_right() { return &this->trigger_swipe_right_; } + Trigger *get_trigger_multi_touch_release() { return &this->trigger_multi_touch_release_; } + Trigger *get_trigger_long_touch_release() { return &this->trigger_long_touch_release_; } + + void set_uart_component(esphome::uart::UARTComponent *uart_component) { this->set_uart_parent(uart_component); } + + void setup() override; + 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); + + TouchPoint get_touch_point(const std::array &bytes); + bool is_valid_data(const std::array &bytes); + int get_touch_position_x(const std::array &bytes); + int get_touch_state(const std::array &bytes); + + Trigger trigger_touch_event_; + Trigger trigger_touch_; + Trigger trigger_release_; + Trigger trigger_swipe_left_; + Trigger trigger_swipe_right_; + Trigger trigger_multi_touch_release_; + Trigger trigger_long_touch_release_; + + uint8_t gang_count_ = 1; + + }; // class TxUltimateEasy + + } // namespace tx_ultimate_easy +} // namespace esphome