diff --git a/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml b/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml index 9363012..d7385da 100644 --- a/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml +++ b/ESPHome/TX-Ultimate-Easy-ESPHome_core.yaml @@ -2,7 +2,11 @@ substitutions: name: tx-ultimate-easy friendly_name: TX Ultimate Easy - version: 0.0.1 + latitude: '0' + longitude: '0' + zone_home: 'zone.home' + + version: '0.0.1' api: id: api_server @@ -93,9 +97,20 @@ esphome: sw_relay_3->set_internal(index.value() < 2); sw_relay_4->set_internal(index.value() < 3); } - - priority: -100 + - priority: 600 # This is where most sensors are set up. then: - - lambda: tx_fw_version->publish_state("${version}"); + - lambda: |- + tx_fw_version->publish_state("${version}"); + tx_device_name->publish_state("${name}"); + +external_components: + - source: + type: git + url: https://github.com/edwardtfn/TX-Ultimate-Easy/ + ref: dev + path: ESPHome/components + components: + - tx_ultimate_easy i2s_audio: id: if_i2s_audio @@ -226,7 +241,7 @@ sensor: - id: zone_home_latitude platform: homeassistant - entity_id: zone.home + entity_id: ${zone_home} attribute: latitude internal: false on_value: @@ -234,7 +249,7 @@ sensor: - id: zone_home_longitude platform: homeassistant - entity_id: zone.home + entity_id: ${zone_home} attribute: longitude internal: false on_value: @@ -242,8 +257,8 @@ sensor: sun: id: sun_entity - latitude: 90 - longitude: 180 + latitude: ${latitude} + longitude: ${longitude} switch: - id: sw_speaker_aplifier @@ -317,10 +332,37 @@ text_sensor: lambda: |- return {"${version}"}; + - id: tx_device_name + name: Device Name + platform: template + icon: mdi:identifier + entity_category: diagnostic + internal: false + disabled_by_default: false + lambda: |- + return {"${name}"}; + filters: + - lambda: |- + const std::string raw_name = x; + std::string result; + bool last_was_underscore = false; + for (const char& c : raw_name) { + if (isalnum(c)) { + result += tolower(c); // Add alphanumeric characters as lowercase + last_was_underscore = false; + } else if (!last_was_underscore) { // Replace non-alphanumeric with '_' but avoid consecutive '_' + result += '_'; + last_was_underscore = true; + } + } + return result; + time: - id: time_source platform: homeassistant +tx_ultimate_easy: + uart: id: if_uart_touch tx_pin: GPIO19 diff --git a/ESPHome/components/tx_ultimate_easy/__init__.py b/ESPHome/components/tx_ultimate_easy/__init__.py new file mode 100644 index 0000000..edac612 --- /dev/null +++ b/ESPHome/components/tx_ultimate_easy/__init__.py @@ -0,0 +1,99 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import uart +from esphome.const import ( + CONF_ID, +) + +from esphome import automation + +CODEOWNERS = ["@edwardtfn"] +DEPENDENCIES = ['uart'] + +CONF_TX_ULTIMATE_EASY = "tx_ultimate_easy" + +CONF_UART = "uart" + +CONF_ON_PRESS = "on_press" +CONF_ON_RELEASE = "on_release" + +CONF_ON_SWIPE_LEFT = "on_swipe_left" +CONF_ON_SWIPE_RIGHT = "on_swipe_right" + +CONF_ON_FULL_TOUCH_RELEASE = "on_full_touch_release" +CONF_ON_LONG_TOUCH_RELEASE = "on_long_touch_release" + +tx_ultimate_easy_ns = cg.esphome_ns.namespace('tx_ultimate_easy') +TouchPoint = tx_ultimate_easy_ns.struct("TouchPoint") + +TxUltimateTouch = tx_ultimate_easy_ns.class_( + 'TxUltimateEasy', cg.Component, uart.UARTDevice) + + +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(TxUltimateTouch), + + cv.Required(CONF_UART): cv.use_id(uart), + + cv.Optional(CONF_ON_PRESS): automation.validate_automation(single=True), + cv.Optional(CONF_ON_RELEASE): automation.validate_automation(single=True), + cv.Optional(CONF_ON_SWIPE_LEFT): automation.validate_automation(single=True), + cv.Optional(CONF_ON_SWIPE_RIGHT): automation.validate_automation(single=True), + cv.Optional(CONF_ON_FULL_TOUCH_RELEASE): automation.validate_automation(single=True), + cv.Optional(CONF_ON_LONG_TOUCH_RELEASE): automation.validate_automation(single=True), + +}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA) + + +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_ON_PRESS in config: + await automation.build_automation( + var.get_touch_trigger(), + [(TouchPoint, "touch")], + config[CONF_ON_PRESS], + ) + + if CONF_ON_RELEASE in config: + await automation.build_automation( + var.get_release_trigger(), + [(TouchPoint, "touch")], + config[CONF_ON_RELEASE], + ) + + if CONF_ON_SWIPE_LEFT in config: + await automation.build_automation( + var.get_swipe_left_trigger(), + [(TouchPoint, "touch")], + config[CONF_ON_SWIPE_LEFT], + ) + + if CONF_ON_SWIPE_RIGHT in config: + await automation.build_automation( + var.get_swipe_right_trigger(), + [(TouchPoint, "touch")], + config[CONF_ON_SWIPE_RIGHT], + ) + + if CONF_ON_FULL_TOUCH_RELEASE in config: + await automation.build_automation( + var.get_full_touch_release_trigger(), + [(TouchPoint, "touch")], + config[CONF_ON_FULL_TOUCH_RELEASE], + ) + + if CONF_ON_LONG_TOUCH_RELEASE in config: + await automation.build_automation( + var.get_long_touch_release_trigger(), + [(TouchPoint, "touch")], + config[CONF_ON_LONG_TOUCH_RELEASE], + ) + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) + + await register_tx_ultimate_easy(var, config) diff --git a/ESPHome/components/tx_ultimate_easy/tx_ultimate_easy.cpp b/ESPHome/components/tx_ultimate_easy/tx_ultimate_easy.cpp new file mode 100644 index 0000000..5bd5af9 --- /dev/null +++ b/ESPHome/components/tx_ultimate_easy/tx_ultimate_easy.cpp @@ -0,0 +1,204 @@ +#include "esphome/core/log.h" +#include "tx_ultimate_easy.h" + +namespace esphome +{ + namespace tx_ultimate_easy + { + static const char *TAG = "tx_ultimate_easy"; + + void TxUltimateEasy::setup() + { + ESP_LOGI("log", "%s", "Tx Ultimate Touch is initialized"); + } + + void TxUltimateEasy::loop() + { + bool found = false; + + int bytes[15] = {}; + int byte = -1; + int i = 0; + + while (this->available()) + { + byte = this->read(); + if (byte == 170) + { + handle_touch(bytes); + i = 0; + } + + bytes[i] = byte; + + i++; + + if (byte != 0) + { + found = true; + } + }; + + if (found) + { + handle_touch(bytes); + } + } + + void TxUltimateEasy::handle_touch(int bytes[]) + { + ESP_LOGV("UART-Log", "------------"); + for (int i = 0; i < 15; i++) + { + ESP_LOGV("UART-Log", "%i", bytes[i]); + } + + if (is_valid_data(bytes)) + { + send_touch_(get_touch_point(bytes)); + } + } + + void TxUltimateEasy::dump_config() + { + ESP_LOGCONFIG(TAG, "Tx Ultimate Touch"); + } + + void TxUltimateEasy::send_touch_(TouchPoint tp) + { + switch (tp.state) + { + case TOUCH_STATE_RELEASE: + if (tp.x >= 17) + { + tp.x = tp.x - 16; + ESP_LOGD(TAG, "Long Press Release (x=%d)", tp.x); + this->long_touch_release_trigger_.trigger(tp); + } + else + { + ESP_LOGD(TAG, "Release (x=%d)", tp.x); + this->release_trigger_.trigger(tp); + } + break; + + case TOUCH_STATE_PRESS: + ESP_LOGD(TAG, "Press (x=%d)", tp.x); + this->touch_trigger_.trigger(tp); + break; + + case TOUCH_STATE_SWIPE_LEFT: + ESP_LOGD(TAG, "Swipe Left (x=%d)", tp.x); + this->swipe_trigger_left_.trigger(tp); + break; + + case TOUCH_STATE_SWIPE_RIGHT: + ESP_LOGD(TAG, "Swipe Right (x=%d)", tp.x); + this->swipe_trigger_right_.trigger(tp); + break; + + case TOUCH_STATE_ALL_FIELDS: + ESP_LOGD(TAG, "Full Touch Release"); + this->full_touch_release_trigger_.trigger(tp); + break; + + default: + break; + } + } + + bool TxUltimateEasy::is_valid_data(int bytes[]) + { + bool valid = true; + + if (!(bytes[0] == 170 && bytes[1] == 85 && bytes[2] == 1 && bytes[3] == 2)) + { + return false; + } + + int state = get_touch_state(bytes); + if (state != TOUCH_STATE_PRESS && + state != TOUCH_STATE_RELEASE && + state != TOUCH_STATE_SWIPE_LEFT && + state != TOUCH_STATE_SWIPE_RIGHT && + state != TOUCH_STATE_ALL_FIELDS) + { + return false; + } + + if (bytes[6] < 0 && state != TOUCH_STATE_ALL_FIELDS) + { + return false; + } + + return true; + } + + int TxUltimateEasy::get_x_touch_position(int bytes[]) + { + int state = bytes[4]; + switch (state) + { + case TOUCH_STATE_RELEASE: + return bytes[5]; + break; + + case TOUCH_STATE_ALL_FIELDS: + return bytes[5]; + break; + + case TOUCH_STATE_SWIPE_LEFT: + return bytes[5]; + break; + + case TOUCH_STATE_SWIPE_RIGHT: + return bytes[5]; + break; + + default: + return bytes[6]; + break; + } + } + + int TxUltimateEasy::get_touch_state(int bytes[]) + { + int state = bytes[4]; + + if (state == TOUCH_STATE_PRESS && bytes[5] != 0) + { + state = TOUCH_STATE_RELEASE; + } + + if (state == TOUCH_STATE_RELEASE && bytes[5] == TOUCH_STATE_ALL_FIELDS) + { + state = TOUCH_STATE_ALL_FIELDS; + } + + if (state == TOUCH_STATE_SWIPE) + { + if (bytes[5] == TOUCH_STATE_SWIPE_RIGHT) + { + state = TOUCH_STATE_SWIPE_RIGHT; + } + else if (bytes[5] == TOUCH_STATE_SWIPE_LEFT) + { + state = TOUCH_STATE_SWIPE_LEFT; + } + } + + return state; + } + + TouchPoint TxUltimateEasy::get_touch_point(int bytes[]) + { + TouchPoint tp; + + tp.x = get_x_touch_position(bytes); + tp.state = get_touch_state(bytes); + + return tp; + } + + } // namespace tx_ultimate_easy +} // namespace esphome \ No newline at end of file diff --git a/ESPHome/components/tx_ultimate_easy/tx_ultimate_easy.h b/ESPHome/components/tx_ultimate_easy/tx_ultimate_easy.h new file mode 100644 index 0000000..54f33a2 --- /dev/null +++ b/ESPHome/components/tx_ultimate_easy/tx_ultimate_easy.h @@ -0,0 +1,68 @@ +#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" + +namespace esphome +{ + namespace tx_ultimate_easy + { + const static uint8_t TOUCH_STATE_RELEASE = 1; + const static uint8_t TOUCH_STATE_PRESS = 2; + const static uint8_t TOUCH_STATE_SWIPE = 3; + + const static uint8_t TOUCH_STATE_ALL_FIELDS = 11; + + const static uint8_t TOUCH_STATE_SWIPE_RIGHT = 12; + const static uint8_t TOUCH_STATE_SWIPE_LEFT = 13; + + struct TouchPoint + { + int8_t x = -1; + int8_t state = -1; + }; + + class TxUltimateEasy : public uart::UARTDevice, public Component + { + public: + Trigger *get_touch_trigger() { return &this->touch_trigger_; } + Trigger *get_release_trigger() { return &this->release_trigger_; } + Trigger *get_swipe_left_trigger() { return &this->swipe_trigger_left_; } + Trigger *get_swipe_right_trigger() { return &this->swipe_trigger_right_; } + Trigger *get_full_touch_release_trigger() { return &this->full_touch_release_trigger_; } + Trigger *get_long_touch_release_trigger() { return &this->long_touch_release_trigger_; } + + 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; + + protected: + void send_touch_(TouchPoint tp); + void handle_touch(int bytes[]); + + TouchPoint get_touch_point(int bytes[]); + bool is_valid_data(int bytes[]); + int get_x_touch_position(int bytes[]); + int get_touch_state(int bytes[]); + + Trigger touch_trigger_; + Trigger release_trigger_; + Trigger swipe_trigger_left_; + Trigger swipe_trigger_right_; + Trigger full_touch_release_trigger_; + Trigger long_touch_release_trigger_; + + }; // class TxUltimateEasy + + } // namespace tx_ultimate_easy +} // namespace esphome \ No newline at end of file