From 3f9c49d28fa0421b107715a650257c5f27c7e683 Mon Sep 17 00:00:00 2001 From: christoph Date: Wed, 17 Dec 2025 15:13:38 +0100 Subject: [PATCH] Fix status parsing and power flag handling --- esphome_components/components/tclac/tclac.cpp | 77 +++++++++++-------- esphome_components/components/tclac/tclac.h | 2 + 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/esphome_components/components/tclac/tclac.cpp b/esphome_components/components/tclac/tclac.cpp index 82a2b5d..8340419 100644 --- a/esphome_components/components/tclac/tclac.cpp +++ b/esphome_components/components/tclac/tclac.cpp @@ -14,6 +14,8 @@ namespace esphome{ namespace tclac{ +constexpr uint8_t TCL_FRAME_TYPE_STATUS = 0x04; +constexpr size_t TCL_STATUS_FRAME_MIN_SIZE = 40; ClimateTraits tclacClimate::traits() { auto traits = climate::ClimateTraits(); @@ -51,17 +53,27 @@ void tclacClimate::setup() { } void tclacClimate::loop() { - // Если в буфере UART что-то есть, то читаем это что-то if (esphome::uart::UARTDevice::available() > 0) { dataShow(0, true); - dataRX[0] = esphome::uart::UARTDevice::read(); - // Если принятый байт- не заголовок (0xBB), то просто покидаем цикл - if (dataRX[0] != 0xBB) { - ESP_LOGD("TCL", "Wrong byte"); + size_t skipped = 0; + bool header_found = false; + while (esphome::uart::UARTDevice::available() > 0) { + uint8_t byte = esphome::uart::UARTDevice::read(); + if (byte == 0xBB) { + dataRX[0] = byte; + header_found = true; + break; + } + skipped += 1; + } + if (!header_found) { + if (skipped > 0) + ESP_LOGV("TCL", "Skipped %u byte(s) waiting for header", (unsigned) skipped); dataShow(0,0); return; } - // А вот если совпал заголовок (0xBB), то начинаем чтение по цепочке еще 4 байт + if (skipped > 0) + ESP_LOGV("TCL", "Resynced after skipping %u byte(s)", (unsigned) skipped); delay(5); dataRX[1] = esphome::uart::UARTDevice::read(); delay(5); @@ -70,32 +82,30 @@ void tclacClimate::loop() { dataRX[3] = esphome::uart::UARTDevice::read(); delay(5); dataRX[4] = esphome::uart::UARTDevice::read(); - - //auto raw = getHex(dataRX, 5); - - //ESP_LOGD("TCL", "first 5 byte : %s ", raw.c_str()); - - // Из первы - 5 байт нам нужен пятый- он содержит длину сообщения - esphome::uart::UARTDevice::read_array(dataRX+5, dataRX[4]+1); - - uint8_t check = getChecksum(dataRX, sizeof(dataRX)); - - //raw = getHex(dataRX, sizeof(dataRX)); - - //ESP_LOGD("TCL", "RX full : %s ", raw.c_str()); - - // Проверяем контрольную сумму - if (check != dataRX[60]) { + size_t payload_with_checksum = static_cast(dataRX[4]) + 1; + size_t frame_size = payload_with_checksum + 5; + if (frame_size > sizeof(dataRX)) { + ESP_LOGW("TCL", "Frame size %u exceeds buffer", (unsigned) frame_size); + tclacClimate::dataShow(0,0); + return; + } + esphome::uart::UARTDevice::read_array(dataRX + 5, payload_with_checksum); + uint8_t check = getChecksum(dataRX, frame_size); + if (check != dataRX[frame_size - 1]) { ESP_LOGD("TCL", "Invalid checksum %x", check); tclacClimate::dataShow(0,0); return; - } else { - //ESP_LOGD("TCL", "checksum OK %x", check); } tclacClimate::dataShow(0,0); - // Прочитав все из буфера приступаем к разбору данны - + uint8_t frame_type = dataRX[3]; + if (frame_type != TCL_FRAME_TYPE_STATUS) { + ESP_LOGV("TCL", "Ignoring frame type 0x%02X", frame_type); + return; + } + if (frame_size < TCL_STATUS_FRAME_MIN_SIZE) { + ESP_LOGV("TCL", "Ignoring short status frame (%u bytes)", (unsigned) frame_size); + return; + } tclacClimate::readData(); } } @@ -115,7 +125,8 @@ void tclacClimate::readData() { //ESP_LOGD("TCL", "TEMP: %f ", current_temperature); - if (dataRX[MODE_POS] & ( 1 << 4)) { + bool device_is_on = (dataRX[MODE_POS] & MODE_STATUS_POWER_FLAG) != 0; + if (device_is_on) { // Если кондиционер включен, то разбираем данные для отображения // ESP_LOGD("TCL", "AC is on"); uint8_t modeswitch = MODE_MASK & dataRX[MODE_POS]; @@ -308,23 +319,23 @@ void tclacClimate::takeControl() { dataTX[8] += 0b00000000; break; case climate::CLIMATE_MODE_AUTO: - dataTX[7] += 0b00000100; + dataTX[7] += MODE_COMMAND_POWER_FLAG; dataTX[8] += 0b00001000; break; case climate::CLIMATE_MODE_COOL: - dataTX[7] += 0b00000100; + dataTX[7] += MODE_COMMAND_POWER_FLAG; dataTX[8] += 0b00000011; break; case climate::CLIMATE_MODE_DRY: - dataTX[7] += 0b00000100; + dataTX[7] += MODE_COMMAND_POWER_FLAG; dataTX[8] += 0b00000010; break; case climate::CLIMATE_MODE_FAN_ONLY: - dataTX[7] += 0b00000100; + dataTX[7] += MODE_COMMAND_POWER_FLAG; dataTX[8] += 0b00000111; break; case climate::CLIMATE_MODE_HEAT: - dataTX[7] += 0b00000100; + dataTX[7] += MODE_COMMAND_POWER_FLAG; dataTX[8] += 0b00000001; break; } diff --git a/esphome_components/components/tclac/tclac.h b/esphome_components/components/tclac/tclac.h index b703448..4a816a1 100644 --- a/esphome_components/components/tclac/tclac.h +++ b/esphome_components/components/tclac/tclac.h @@ -28,6 +28,8 @@ using byte = uint8_t; #define MODE_POS 7 #define MODE_MASK 0b00111111 +#define MODE_STATUS_POWER_FLAG 0b00010000 +#define MODE_COMMAND_POWER_FLAG 0b00000100 #define MODE_AUTO 0b00110101 #define MODE_COOL 0b00110001