@@ -1,12 +1,14 @@
/**
* Create by Miguel Ángel López on 20/07/19
* and modify by xaxexa
* and modify by xaxexa, edit for newer models by Kannix2005
* Refactoring & component making:
* Соловей с паяльником 15.03.2024
**/
# include "esphome.h"
# include "esphome/core/defines.h"
# include "tclac.h"
# include <sstream>
# include <iomanip>
namespace esphome {
namespace tclac {
@@ -24,11 +26,15 @@ ClimateTraits tclacClimate::traits() {
traits . set_supported_fan_modes ( this - > supported_fan_modes_ ) ;
traits . set_supported_swing_modes ( this - > supported_swing_modes_ ) ;
traits . add_supported_mode ( climate : : CLIMATE_MODE_OFF ) ; // Выключенный режим кондиционера доступен всегда
traits . add_supported_mode ( climate : : CLIMATE_MODE_AUTO ) ; // Автоматический режим кондиционера тоже
traits . add_supported_fan_ mode ( climate : : CLIMATE_FAN_AUTO ) ; // Автоматический режим вентилятора доступен всегда
traits . add_supported_swing_ mode ( climate : : CLIMATE_SWING_OFF ) ; // Выключенный режим качания заслонок доступен всегда
traits . add_supported_preset ( C limatePreset : : CLIMATE_PRESET_NONE ) ; // Н а всякий случай без предустановок
traits . add_supported_mode ( climate : : CLIMATE_MODE_OFF ) ; // Off mode is always available
traits . add_supported_mode ( climate : : CLIMATE_MODE_AUTO ) ; // Auto mode is always available
traits . add_supported_mode ( climate : : CLIMATE_MODE_COOL ) ; // Cool mode is always available
traits . add_supported_mode ( climate : : CLIMATE_MODE_HEAT ) ; // Heat mode is always available
traits . add_supported_mode ( c limate: : CLIMATE_MODE_DRY ) ; // Dry mode is always available
traits . add_supported_mode ( climate : : CLIMATE_MODE_FAN_ONLY ) ; // Fan only mode is also available
traits . add_supported_fan_mode ( climate : : CLIMATE_FAN_AUTO ) ; // Auto fan mode is always available
traits . add_supported_swing_mode ( climate : : CLIMATE_SWING_OFF ) ; // Swing off mode is always available
traits . add_supported_preset ( ClimatePreset : : CLIMATE_PRESET_NONE ) ; // No preset just in case
return traits ;
}
@@ -36,6 +42,16 @@ ClimateTraits tclacClimate::traits() {
void tclacClimate : : setup ( ) {
//this->esphome::uart::UARTDevice::write_array(setup1, sizeof(setup1));
//this->esphome::uart::UARTDevice::flush();
//this->esphome::uart::UARTDevice::write_array(setup2, sizeof(setup2));
//this->esphome::uart::UARTDevice::flush();
//this->esphome::uart::UARTDevice::write_array(setup3, sizeof(setup3));
//this->esphome::uart::UARTDevice::flush();
target_temperature_set = 20 ;
target_temperature = 20 ;
restore_state_ ( ) ;
# ifdef CONF_RX_LED
this - > rx_led_pin_ - > setup ( ) ;
this - > rx_led_pin_ - > digital_write ( false ) ;
@@ -47,17 +63,17 @@ void tclacClimate::setup() {
}
void tclacClimate : : loop ( ) {
// Если в буфере UART что-то есть, то читаем это что-то
if ( esphome : : uart : : UARTDevice : : available ( ) > 0 ) {
dataShow ( 0 , true ) ;
memset ( dataRX , 0 , sizeof ( dataRX ) ) ;
dataRX [ 0 ] = esphome : : uart : : UARTDevice : : read ( ) ;
// Если принятый байт- не заголовок (0xBB), то просто покидаем цикл
// If the received byte is not the header (0xBB), just exit the loop
if ( dataRX [ 0 ] ! = 0xBB ) {
ESP_LOGD ( " TCL " , " Wrong byte " ) ;
dataShow ( 0 , 0 ) ;
return ;
}
// А вот если совпал заголовок (0xBB), то начинаем чтение по цепочке еще 4 байт
// If the header (0xBB) matches, start reading 4 more bytes in sequence
delay ( 5 ) ;
dataRX [ 1 ] = esphome : : uart : : UARTDevice : : read ( ) ;
delay ( 5 ) ;
@@ -67,20 +83,25 @@ void tclacClimate::loop() {
delay ( 5 ) ;
dataRX [ 4 ] = esphome : : uart : : UARTDevice : : read ( ) ;
auto raw = getHex( dataRX, 5 ) ;
// auto raw = getHex( dataRX, 5);
ESP_LOGD( " TCL " , " first 5 byte : %s " , raw . c_str ( ) ) ;
// ESP_LOGD("TCL", "first 5 byte : %s ", raw.c_str());
// Из первых 5 байт нам нужен пятый- он содержит длину сообщения
// From the first 5 bytes we need the fifth - it contains the message length
esphome : : uart : : UARTDevice : : read_array ( dataRX + 5 , dataRX [ 4 ] + 1 ) ;
//int c = 0;
//while(esphome::uart::UARTDevice::available() != 0){
// esphome::uart::UARTDevice::read_byte(&dataRX[5+c]);
// c++;
//}
byte check = getChecksum ( dataRX , sizeof ( dataRX ) ) ;
uint8_t check = getChecksum ( dataRX , sizeof ( dataRX ) ) ;
raw = getHex ( dataRX , sizeof ( dataRX ) ) ;
auto raw = getHex ( dataRX , sizeof ( dataRX ) ) ;
ESP_LOGD( " TCL " , " RX full : %s " , raw . c_str ( ) ) ;
// ESP_LOGD("TCL", "RX full : %s ", raw.c_str());
// Проверяем контрольную сумму
// Check the checksum
if ( check ! = dataRX [ 60 ] ) {
ESP_LOGD ( " TCL " , " Invalid checksum %x " , check ) ;
tclacClimate : : dataShow ( 0 , 0 ) ;
@@ -89,7 +110,7 @@ void tclacClimate::loop() {
ESP_LOGD ( " TCL " , " checksum OK %x " , check ) ;
}
tclacClimate : : dataShow ( 0 , 0 ) ;
// Прочитав все из буфера приступаем к разбору данных
// After reading everything from the buffer, we proceed to parse the data
tclacClimate : : readData ( ) ;
}
}
@@ -97,153 +118,273 @@ void tclacClimate::loop() {
void tclacClimate : : update ( ) {
tclacClimate : : dataShow ( 1 , 1 ) ;
this - > esphome : : uart : : UARTDevice : : write_array ( poll , sizeof ( poll ) ) ;
auto raw = tclacClimate : : getHex ( poll , sizeof ( poll ) ) ;
ESP_LOGD ( " TCL " , " chek status sended " ) ;
//const char* raw = tclacClimate::getHex(poll, sizeof(poll)).c_str();
this - > esphome : : uart : : UARTDevice : : flush ( ) ;
//delay(100);
//this->esphome::uart::UARTDevice::write_array(poll2, sizeof(poll2));
//this->esphome::uart::UARTDevice::flush();
//this->esphome::uart::UARTDevice::write_array(poll3, sizeof(poll3));
//this->esphome::uart::UARTDevice::flush();
tclacClimate : : dataShow ( 1 , 0 ) ;
}
void tclacClimate : : readData ( ) {
current_temperature = float ( ( ( ( dataRX [ 17 ] < < 8 ) | dataRX [ 18 ] ) / 374 - 32 ) / 1.8 ) ;
target_temperature = ( dataRX[ FAN_SPEED_POS] & SET_TEMP_MASK) + 16 ;
// BBx01x00x03x37x04x00x00x00x00x00x00x00x00x00x80x36x00x23x08x00x00x00x00x00x00x00x00x00x20x66xFFx42x00x50x21x21x21x00x00x80x00x00x00x00xE9x00x00x00x54x40x03x23x00x00x7Ax00x00x00x00xBA
// current_temperature = float((( (dataRX[17] << 8) | dataRX[18] ) / 374 - 32)/1.8);
// target_temperature = ( dataRX[ FAN_SPEED_POS] & SET_TEMP_MASK) + 16;
//current_temperature = float((dataRX[29] | (dataRX[30] << 8))*0.001);
//auto current_temperature2 = float((( (dataRX[45] << 8) | dataRX[46] ) / 374 - 32)/1.8);
current_temperature = 256 - dataRX [ 45 ] ; // 256-233 = 23°C (plausible!)
//this->current_temperature = current_temperature;
//target_temperature = 20;
//this->target_temperature = target_temperature_set;
//current_temperature = float((( (dataRX[17] << 8) | dataRX[18] ) / 374 - 32)/1.8);
//target_temperature = (dataRX[FAN_SPEED_POS] & SET_TEMP_MASK) + 16;
ESP_LOGD ( " TCL " , " TEMP: %f " , current_temperature ) ;
//ESP_LOGD("TCL", "TEMP: %f ", current_temperature2);
if ( dataRX [ MODE_POS ] & ( 1 < < 4 ) ) {
// Если кондиционер включен, то разбираем данные для отображения
// If the air conditioner is on, parse the data for display
// ESP_LOGD("TCL", "AC is on");
uint8_t modeswitch = MODE_MASK & dataRX [ MODE_POS ] ;
uint8_t fanspeedswitch = FAN_SPEED_MASK & dataRX [ FAN_SPEED_POS ] ;
uint8_t swingmodeswitch = SWING_MODE_MASK & dataRX [ SWING_POS ] ;
switch ( modeswitch) {
case MODE_AUTO :
mode = climate : : CLIMATE_MODE_AUTO;
break;
case MODE_COOL :
mode = climate : : CLIMATE_MODE_COOL;
break;
case MODE_DRY :
mode = climate : : CLIMATE_MODE_DRY;
break;
case MODE_FAN_ONLY :
mode = climate : : CLIMATE_MODE_FAN_ONLY;
break;
case MODE_HEAT :
mode = climate : : CLIMATE_MODE_HEAT;
break;
default :
mode = climate : : CLIMATE_MODE_AUTO ;
}
// switch ( modeswitch) {
// case MODE_AUTO:
// mode = climate:: CLIMATE_MODE_AUTO;
// break;
// case MODE_COOL:
// mode = climate:: CLIMATE_MODE_COOL;
// break;
// case MODE_DRY:
// mode = climate:: CLIMATE_MODE_DRY;
// break;
// case MODE_FAN_ONLY:
// mode = climate:: CLIMATE_MODE_FAN_ONLY;
// break;
// case MODE_HEAT:
// mode = climate:: CLIMATE_MODE_HEAT;
// break;
// default:
// mode = climate:: CLIMATE_MODE_OFF;
// }
if ( dataRX [ FAN_QUIET_POS ] & FAN_QUIET ) {
fan_mode = climate : : CLIMATE_FAN_QUIET ;
} else if ( dataRX [ MODE_POS ] & FAN_DIFFUSE ) {
fan_mode = climate : : CLIMATE_FAN_DIFFUSE ;
} else {
switch ( fanspeedswitch ) {
case FAN_AUTO :
fan_mode = climate : : CLIMATE_FAN_AUTO ;
break ;
case FAN_LOW :
fan_mode = climate : : CLIMATE_FAN_LOW ;
break ;
case FAN_MIDDLE :
fan_mode = climate : : CLIMATE_FAN_MIDDLE ;
break ;
case FAN_MEDIUM :
fan_mode = climate : : CLIMATE_FAN_MEDIUM ;
break ;
case FAN_HIGH :
fan_mode = climate : : CLIMATE_FAN_HIGH ;
break ;
case FAN_FOCUS :
fan_mode = climate : : CLIMATE_FAN_FOCUS ;
break ;
default :
fan_mode = climate : : CLIMATE_FAN_AUTO ;
}
}
switch ( swingmodeswitch ) {
case SWING_OFF :
swing_mode = climate : : CLIMATE_SWING_OFF ;
break ;
case SWING_HORIZONTAL :
swing_mode = climate : : CLIMATE_SWING_HORIZONTAL ;
break ;
case SWING_VERTICAL :
swing_mode = climate : : CLIMATE_SWING_VERTICAL ;
break ;
case SWING_BOTH :
swing_mode = climate : : CLIMATE_SWING_BOTH ;
break ;
}
// //OVERRIDE
// switch (dataRX[MODE_POS])
// {
// case 0x08:
// //auto
// mode = climate::CLIMATE_MODE_AUTO;
// break;
// Обработка данных о пресете
preset = ClimatePreset : : CLIMATE_PRESET_NONE ;
if ( dataRX [ 7 ] & ( 1 < < 6 ) ) {
preset = ClimatePreset : : CLIMATE_PRESET_ECO ;
} else if ( dataRX [ 9 ] & ( 1 < < 2 ) ) {
preset = ClimatePreset : : CLIMATE_PRESET_COMFORT ;
} else if ( dataRX [ 19 ] & ( 1 < < 0 ) ) {
preset = ClimatePreset : : CLIMATE_PRESET_SLEEP ;
}
// case 0x07:
// //Lüfter
// mode = climate::CLIMATE_MODE_FAN_ONLY;
// break;
} else {
// Если кондиционер выключен, то все режимы показываются, как выключенные
mode = climate : : CLIMATE_MODE_OFF ;
// case 0x02:
// //Trocknen
// mode = climate:: CLIMATE_MODE_DRY;
// break;
// case 0x01:
// //Heizen
// mode = climate::CLIMATE_MODE_HEAT;
// break;
// case 0x03:
// //Kühlen
// mode = climate::CLIMATE_MODE_COOL;
// break;
// }
// if ( dataRX[FAN_QUIET_POS] & FAN_QUIET) {
// fan_mode = climate::CLIMATE_FAN_QUIET;
// } else if (dataRX[MODE_POS] & FAN_DIFFUSE){
// fan_mode = climate::CLIMATE_FAN_DIFFUSE;
// } else {
// switch (fanspeedswitch) {
// case FAN_AUTO:
// fan_mode = climate::CLIMATE_FAN_AUTO;
// break;
// case FAN_LOW:
// fan_mode = climate::CLIMATE_FAN_LOW;
// break;
// case FAN_MIDDLE:
// fan_mode = climate::CLIMATE_FAN_MIDDLE;
// break;
// case FAN_MEDIUM:
// fan_mode = climate::CLIMATE_FAN_MEDIUM;
// break;
// case FAN_HIGH:
// fan_mode = climate::CLIMATE_FAN_HIGH;
// break;
// case FAN_FOCUS:
// fan_mode = climate::CLIMATE_FAN_FOCUS;
// break;
// default:
// fan_mode = climate::CLIMATE_FAN_AUTO;
// }
// }
// switch (swingmodeswitch) {
// case SWING_OFF:
// swing_mode = climate::CLIMATE_SWING_OFF;
// break;
// case SWING_HORIZONTAL:
// swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
// break;
// case SWING_VERTICAL:
// swing_mode = climate::CLIMATE_SWING_VERTICAL;
// break;
// case SWING_BOTH:
// swing_mode = climate::CLIMATE_SWING_BOTH;
// break;
// }
// // Обработка данных о пресете
// preset = ClimatePreset::CLIMATE_PRESET_NONE;
// if (dataRX[7] & (1 << 6)){
// preset = ClimatePreset::CLIMATE_PRESET_ECO;
// } else if (dataRX[9] & (1 << 2)){
// preset = ClimatePreset::CLIMATE_PRESET_COMFORT;
// } else if (dataRX[19] & (1 << 0)){
// preset = ClimatePreset::CLIMATE_PRESET_SLEEP;
// }
} //else {
// If the air conditioner is off, all modes are shown as off
//mode = climate::CLIMATE_MODE_OFF;
//fan_mode = climate::CLIMATE_FAN_OFF;
swing_mode = climate : : CLIMATE_SWING_OFF;
preset = ClimatePreset: : CLIMATE_PRESET_NONE ;
}
// Публикуем данные
// swing_mode = climate:: CLIMATE_SWING_OFF;
//preset = ClimatePreset:: CLIMATE_PRESET_NONE;
//}
// Publish the data
this - > publish_state ( ) ;
allow_take_control = true ;
}
// Climate control
void tclacClimate : : control ( const ClimateCall & call ) {
// Запрашиваем данные из переключателя режимов работы кондиционера
// Request data from the AC mode switch
if ( call . get_mode ( ) . has_value ( ) ) {
switch_climate_mode = call . get_mode ( ) . value ( ) ;
ESP_LOGD ( " TCL " , " Get MODE from call " ) ;
} else {
switch_climate_mode = mode ;
ESP_LOGD ( " TCL " , " Get MODE from AC " ) ;
}
// Запрашиваем данные из переключателя предустановок кондиционера
switch ( switch_climate_mode ) {
case 6 :
mode = climate : : CLIMATE_MODE_AUTO ;
break ;
case 2 :
mode = climate : : CLIMATE_MODE_COOL ;
break ;
case 5 :
mode = climate : : CLIMATE_MODE_DRY ;
break ;
case 4 :
mode = climate : : CLIMATE_MODE_FAN_ONLY ;
break ;
case 3 :
mode = climate : : CLIMATE_MODE_HEAT ;
break ;
default :
mode = climate : : CLIMATE_MODE_OFF ;
}
ESP_LOGD ( " TCL " , " Get MODE %i from call " , ( int ) mode ) ;
} //else {
//switch_climate_mode = mode;
//ESP_LOGD("TCL", "Get MODE from AC");
//}
// Request data from the AC preset switch
if ( call . get_preset ( ) . has_value ( ) ) {
switch_preset = call . get_preset ( ) . value ( ) ;
} else {
switch_preset = preset . value ( ) ;
}
switch ( switch_preset ) {
case 0 :
preset = climate : : CLIMATE_PRESET_NONE ;
case 5 :
preset = climate : : CLIMATE_PRESET_ECO ;
case 6 :
preset = climate : : CLIMATE_PRESET_SLEEP ;
case 4 :
preset = climate : : CLIMATE_PRESET_COMFORT ;
}
} //else {
//switch_preset = preset.value();
//}
// Запрашиваем данные из переключателя режимов вентилятора
// Request data from the fan mode switch
if ( call . get_fan_mode ( ) . has_value ( ) ) {
switch_fan_mode = call . get_fan_mode ( ) . value ( ) ;
} else {
switch_fan_ mode = fan_mode . value ( ) ;
}
if ( mode ! = climate : : CLIMATE_MODE_OFF ) {
switch ( switch_fan_mode ) {
case 2 :
fan_mode = climate : : CLIMATE_FAN_AUTO ;
case 3 :
fan_mode = climate : : CLIMATE_FAN_LOW ;
case 6 :
fan_mode = climate : : CLIMATE_FAN_MIDDLE ;
case 4 :
fan_mode = climate : : CLIMATE_FAN_MEDIUM ;
case 5 :
fan_mode = climate : : CLIMATE_FAN_HIGH ;
case 7 :
fan_mode = climate : : CLIMATE_FAN_FOCUS ;
default :
fan_mode = climate : : CLIMATE_FAN_AUTO ;
}
} else {
fan_mode = climate : : CLIMATE_FAN_OFF ;
}
} //else {
// switch_fan_mode = fan_mode.value();
//}
// Запрашиваем данные из переключателя режимов качания заслонок
// Request data from the swing mode switch
if ( call . get_swing_mode ( ) . has_value ( ) ) {
switch_swing_mode = call . get_swing_mode ( ) . value ( ) ;
} else {
// А если в переключателе пусто- заполняем значением из последнего опроса состояния. Типа, ничего не поменялось.
switch_swing_mode = swing_mode ;
}
switch ( switch_swing_mode ) {
case 0 :
swing_mode = climate : : CLIMATE_SWING_OFF ;
case 3 :
swing_mode = climate : : CLIMATE_SWING_HORIZONTAL ;
case 2 :
swing_mode = climate : : CLIMATE_SWING_VERTICAL ;
case 1 :
swing_mode = climate : : CLIMATE_SWING_BOTH ;
default :
swing_mode = climate : : CLIMATE_SWING_OFF ;
}
} //else {
// If the switch is empty, fill with the value from the last state poll. Like, nothing changed.
//switch_swing_mode = swing_mode;
//}
// Расчет температуры
// Calculate temperature
if ( call . get_target_temperature ( ) . has_value ( ) ) {
target_temperature_set = 31 - ( int ) call . get_target_temperature ( ) . value ( ) ;
} else {
target_temperature_set = 31 - ( int ) target_temperature ;
}
target_temperature_set = ( int ) call . get_target_temperature ( ) . value ( ) ;
target_temperature = target_temperature_set ;
} //else {
//target_temperature_set = 111 - (int)target_temperature;
//}
is_call_control = true ;
takeControl ( ) ;
this - > publish_state ( ) ;
allow_take_control = true ;
}
@@ -265,10 +406,10 @@ void tclacClimate::takeControl() {
switch_preset = preset . value ( ) ;
switch_fan_mode = fan_mode . value ( ) ;
switch_swing_mode = swing_mode ;
target_temperature_set = 31 - ( int ) target_temperature ;
target_temperature_set = ( int ) target_temperature ;
}
// Включаем или отключаем пищалку в зависимости от переключателя в настройках
// Enable or disable the beeper depending on the setting switch
if ( beeper_status_ ) {
ESP_LOGD ( " TCL " , " Beep mode ON " ) ;
dataTX [ 7 ] + = 0b00100000 ;
@@ -277,20 +418,20 @@ void tclacClimate::takeControl() {
dataTX [ 7 ] + = 0b00000000 ;
}
// Включаем или отключаем дисплей на кондиционере в зависимости от переключателя в настройках
// Включаем дисплей только если кондиционер в одном из рабочих режимов
// Enable or disable the AC display depending on the setting switch
// Only enable the display if the AC is in one of the operating modes
// ВНИМАНИЕ! При выключении дисплея кондиционер сам принудительно переходит в автоматический режим !
// WARNING! When the display is turned off, the AC automatically switches to automatic mode !
if ( ( display_status_ ) & & ( switch_climate_mode ! = climate : : CLIMATE_MODE_OFF ) ) {
ESP_LOGD ( " TCL " , " Dispa ly turn ON " ) ;
ESP_LOGD ( " TCL " , " Displa y turn ON " ) ;
dataTX [ 7 ] + = 0b01000000 ;
} else {
ESP_LOGD ( " TCL " , " Dispa ly turn OFF " ) ;
ESP_LOGD ( " TCL " , " Displa y turn OFF " ) ;
dataTX [ 7 ] + = 0b00000000 ;
}
// Настраиваем режим работы кондиционера
// Configure the AC operating mode
switch ( switch_climate_mode ) {
case climate : : CLIMATE_MODE_OFF :
dataTX [ 7 ] + = 0b00000000 ;
@@ -318,7 +459,8 @@ void tclacClimate::takeControl() {
break ;
}
// Настраиваем режим вентилятора
// Configure the fan mode
switch ( switch_fan_mode ) {
case climate : : CLIMATE_FAN_AUTO :
dataTX [ 8 ] + = 0b00000000 ;
@@ -354,7 +496,7 @@ void tclacClimate::takeControl() {
break ;
}
// Устанавливаем режим качания заслонок
// Set the damper swing mode
switch ( switch_swing_mode ) {
case climate : : CLIMATE_SWING_OFF :
dataTX [ 10 ] + = 0b00000000 ;
@@ -374,7 +516,7 @@ void tclacClimate::takeControl() {
break ;
}
// Устанавливаем предустановки кондиционера
// Set the AC presets
switch ( switch_preset ) {
case ClimatePreset : : CLIMATE_PRESET_NONE :
break ;
@@ -389,42 +531,46 @@ void tclacClimate::takeControl() {
break ;
}
//Режим заслонок
// Вертикальная заслонка
// Качание вертикальной заслонки [10 байт, маска 00111000]:
// 000 - Качание отключено, заслонка в последней позиции или в фиксации
// 111 - Качание включено в выбранном режиме
// Режим качания вертикальной заслонки (режим фиксации заслонки роли не играет, если качание включено) [32 байт, маска 00011000]:
// 01 - качание сверху вниз, ПО УМОЛЧАНИЮ
// 10 - качание в верхней половине
// 11 - качание в нижней половине
// Режим фиксации заслонки (режим качания заслонки роли не играет, если качание выключено) [32 байт, маска 00000111]:
// 000 - нет фиксации, ПО УМОЛЧАНИЮ
// 001 - фиксация вверху
// 010 - фиксация между верхом и серединой
// 011 - фиксация в середине
// 100 - фиксация между серединой и низом
// 101 - фиксация внизу
// Горизонтальные заслонки
// Качание горизонтальных заслонок [11 байт, маска 00001000]:
// 0 - Качание отключено, заслонки в последней позиции или в фиксации
// 1 - Качание включено в выбранном режиме
// Режим качания горизонтальных заслонок (режим фиксации заслонок роли не играет, если качание включено) [33 байт, маска 00111000]:
// 001 - качание слева направо, ПО УМОЛЧАНИЮ
// 010 - качание слева
// 011 - качание по середине
// 100 - качание справа
// Режим фиксации горизонтальных заслонок (режим качания заслонок роли не играет, если качание выключено) [33 байт, маска 00000111]:
// 000 - нет фиксации, ПО УМОЛЧАНИЮ
// 001 - фиксация слева
// 010 - фиксация между левой стороной и серединой
// 011 - фиксация в середине
// 100 - фиксация между серединой и правой стороной
// 101 - фиксация справа
//Damper mode
// Vertical damper
// Vertical damper swing [byte 10, mask 00111000]:
// 000 - Swing off, damper in last position or fixed
// 111 - Swing on in selected mode
// Vertical damper swing mode (fixed mode doesn't matter if swing is on) [byte 32, mask 00011000]:
// 01 - swing up-down, DEFAULT
// 10 - swing in upper half
// 11 - swing in lower half
// Damper fixed mode (swing mode doesn't matter if swing is off) [byte 32, mask 00000111]:
// 000 - no fixation, DEFAULT
// 001 - fixed at top
// 010 - fixed between top and center
// 011 - fixed in center
// 100 - fixed between center and bottom
// 101 - fixed at bottom
// Horizontal dampers
// Horizontal damper swing [byte 11, mask 00001000]:
// 0 - Swing off, dampers in last position or fixed
// 1 - Swing on in selected mode
// Horizontal damper swing mode (fixed mode doesn't matter if swing is on) [byte 33, mask 00111000]:
// 001 - swing left-right, DEFAULT
// 010 - swing left
// 011 - swing center
// 100 - swing right
// Horizontal damper fixed mode (swing mode doesn't matter if swing is off) [byte 33, mask 00000111]:
// 000 - no fixation, DEFAULT
// 001 - fixed left
// 010 - fixed between left and center
// 011 - fixed in center
// 100 - fixed between center and right
// 101 - fixed right
// Устанавливаем режим для качания вертикальной заслонки
// Set the vertical damper swing mode
switch ( vertical_swing_direction_ ) {
case VerticalSwingDirection : : OFF :
dataTX [ 32 ] + = 0b00000000 ;
ESP_LOGD ( " TCL " , " Vertical swing: off " ) ;
break ;
case VerticalSwingDirection : : UP_DOWN :
dataTX [ 32 ] + = 0b00001000 ;
ESP_LOGD ( " TCL " , " Vertical swing: up-down " ) ;
@@ -438,8 +584,12 @@ void tclacClimate::takeControl() {
ESP_LOGD ( " TCL " , " Vertical swing: downer " ) ;
break ;
}
// Устанавливаем режим для качания горизонтальных заслонок
// Set the horizontal damper swing mode
switch ( horizontal_swing_direction_ ) {
case HorizontalSwingDirection : : OFF :
dataTX [ 33 ] + = 0b00000000 ;
ESP_LOGD ( " TCL " , " Horizontal swing: left-right " ) ;
break ;
case HorizontalSwingDirection : : LEFT_RIGHT :
dataTX [ 33 ] + = 0b00001000 ;
ESP_LOGD ( " TCL " , " Horizontal swing: left-right " ) ;
@@ -457,7 +607,7 @@ void tclacClimate::takeControl() {
ESP_LOGD ( " TCL " , " Horizontal swing: righter " ) ;
break ;
}
// Устанавливаем положение фиксации вертикальной заслонки
// Set the vertical damper fixed position
switch ( vertical_direction_ ) {
case AirflowVerticalDirection : : LAST :
dataTX [ 32 ] + = 0b00000000 ;
@@ -484,7 +634,7 @@ void tclacClimate::takeControl() {
ESP_LOGD ( " TCL " , " Vertical fix: down " ) ;
break ;
}
// Устанавливаем положение фиксации горизонтальных заслонок
// Set the horizontal damper fixed position
switch ( horizontal_direction_ ) {
case AirflowHorizontalDirection : : LAST :
dataTX [ 33 ] + = 0b00000000 ;
@@ -512,15 +662,15 @@ void tclacClimate::takeControl() {
break ;
}
// Установка температуры
dataTX [ 9 ] = target_temperature_set ;
// Set temperature
dataTX [ 9 ] = 111 - target_temperature_set ;
// Собираем массив байт для отправки в кондиционер
dataTX [ 0 ] = 0xBB ; //стартовый байт заголовка
dataTX [ 1 ] = 0x00 ; //стартовый байт заголовка
dataTX [ 2 ] = 0x01 ; //стартовый байт заголовка
dataTX [ 3 ] = 0x03 ; //0x03 - управление , 0x04 - опрос
dataTX [ 4 ] = 0x20 ; //0x20 - управление , 0x19 - опрос
// Assemble the byte array to send to the AC
dataTX [ 0 ] = 0xBB ; // Header start byte
dataTX [ 1 ] = 0x00 ; // Header start byte
dataTX [ 2 ] = 0x01 ; // Header start byte
dataTX [ 3 ] = 0x03 ; // 0x03 - control , 0x04 - poll
dataTX [ 4 ] = 0x20 ; // 0x20 - control , 0x19 - poll
dataTX [ 5 ] = 0x03 ; //??
dataTX [ 6 ] = 0x01 ; //??
//dataTX[7] = 0x64; //eco,display,beep,ontimerenable, offtimerenable,power,0,0
@@ -535,7 +685,7 @@ void tclacClimate::takeControl() {
dataTX [ 16 ] = 0x00 ; //??
dataTX [ 17 ] = 0x00 ; //??
dataTX [ 18 ] = 0x00 ; //??
//dataTX[19] = 0x00; //sleep on = 1 off=0
//dataTX[19] = 0x00; //sleep on = 1 off=0 --- 0,0,0,0,0,0,0,1 = sleep on; 0,0,0,1,0,0,0,0 = self clean on
dataTX [ 20 ] = 0x00 ; //??
dataTX [ 21 ] = 0x00 ; //??
dataTX [ 22 ] = 0x00 ; //??
@@ -545,14 +695,15 @@ void tclacClimate::takeControl() {
dataTX [ 26 ] = 0x00 ; //??
dataTX [ 27 ] = 0x00 ; //??
dataTX [ 28 ] = 0x00 ; //??
dataTX [ 29 ] = 0x20 ; //??
dataTX [ 30 ] = 0x00 ; //??
dataTX [ 31 ] = 0x00 ; //??
//dataTX[32] = 0x00; //0,0,0,режим вертикального качания(2),режим вертикальной фиксации (3)
//dataTX[33] = 0x00; //0,0,режим горизонтального качания(3),режим горизонтальной фиксации (3)
//dataTX[32] = 0x00; //0,0,0,vertical swing mode(2), vertical fixed position (3)
//dataTX[33] = 0x00; //0,0,vertical swing mode(2), vertical fixed position (3)
dataTX [ 34 ] = 0x00 ; //??
dataTX [ 35 ] = 0x00 ; //??
dataTX [ 36 ] = 0x00 ; //??
dataTX [ 37 ] = 0xFF ; //Контрольная сумма
dataTX [ 37 ] = 0xFF ; //Checksum
dataTX [ 37 ] = tclacClimate : : getChecksum ( dataTX , sizeof ( dataTX ) ) ;
tclacClimate : : sendData ( dataTX , sizeof ( dataTX ) ) ;
@@ -560,38 +711,48 @@ void tclacClimate::takeControl() {
is_call_control = false ;
}
// Отправка данных в кондиционер
void tclacClimate : : sendData ( byte * message , byte size ) {
// Send data to the AC
void tclacClimate : : sendData ( uint8_t * message , uint8_t size ) {
tclacClimate : : dataShow ( 1 , 1 ) ;
//Serial.write(message, size);
this - > esphome : : uart : : UARTDevice : : write_array ( message , size ) ;
auto raw = getHex( message, size ) ;
ESP_LOGD ( " TCL " , " TX full : %s " , raw . c_str ( ) ) ;
ESP_LOGD ( " TCL " , " Message to TCL sended... " ) ;
// auto raw = getHex( message, size);
ESP_LOGD ( " TCL " , " Message sent to TCL... " ) ;
tclacClimate : : dataShow ( 1 , 0 ) ;
}
// Преобразование байта в читабельный формат
String tclacClimate : : getHex ( byte * message , byte size ) {
String raw ;
for ( int i = 0 ; i < size ; i + + ) {
raw + = " \n " + String ( message [ i ] ) ;
// Convert byte to readable format
String tclacClimate : : getHex ( uint8_t * message , uint8_t size ) {
std : : ostringstream oss ;
// print each byte as two uppercase hex digits
for ( uint8_t i = 0 ; i < size ; + + i ) {
oss < < std : : hex
< < std : : uppercase
< < std : : setw ( 2 )
< < std : : setfill ( ' 0 ' )
< < static_cast < int > ( message [ i ] ) ;
if ( i + 1 < size )
oss < < ' ' ; // optional separator
}
raw . toU pperC ase( ) ;
return raw ;
// grab the std::string, ensure u pperc ase
std : : string s = oss . str ( ) ;
// (oss.str() is already uppercase thanks to std::uppercase, so
// this transform is optional)
std : : transform ( s . begin ( ) , s . end ( ) , s . begin ( ) , : : toupper ) ;
// convert to esphome::String and return
return String ( s . c_str ( ) ) ;
}
// Вычисление контрольной суммы
byte tclacClimate : : getChecksum ( const byte * message , size_t size ) {
byte position = size - 1 ;
byte crc = 0 ;
// Calculate checksum
uint8_t tclacClimate : : getChecksum ( const uint8_t * message , size_t size ) {
uint8_t position = size - 1 ;
uint8_t crc = 0 ;
for ( int i = 0 ; i < position ; i + + )
crc ^ = message [ i ] ;
return crc ;
}
// Мигаем светодиодами
// Blink LEDs
void tclacClimate : : dataShow ( bool flow , bool shine ) {
if ( module_display_status_ ) {
if ( flow = = 0 ) {
@@ -619,9 +780,9 @@ void tclacClimate::dataShow(bool flow, bool shine) {
}
}
// Действия с данными из конфига
// Actions with data from config
// Получение состояния пищалки
// Get beeper state
void tclacClimate : : set_beeper_state ( bool state ) {
this - > beeper_status_ = state ;
if ( force_mode_status_ ) {
@@ -630,7 +791,7 @@ void tclacClimate::set_beeper_state(bool state) {
}
}
}
// Получение состояния дисплея кондиционера
// Get AC display state
void tclacClimate : : set_display_state ( bool state ) {
this - > display_status_ = state ;
if ( force_mode_status_ ) {
@@ -639,27 +800,27 @@ void tclacClimate::set_display_state(bool state) {
}
}
}
// Получение состояния режима принудительного применения настроек
// Get forced settings application mode state
void tclacClimate : : set_force_mode_state ( bool state ) {
this - > force_mode_status_ = state ;
}
// Получение пина светодиода приема данных
// Get receive data LED pin
# ifdef CONF_RX_LED
void tclacClimate : : set_rx_led_pin ( GPIOPin * rx_led_pin ) {
this - > rx_led_pin_ = rx_led_pin ;
}
# endif
// Получение пина светодиода передачи данных
// Get transmit data LED pin
# ifdef CONF_TX_LED
void tclacClimate : : set_tx_led_pin ( GPIOPin * tx_led_pin ) {
this - > tx_led_pin_ = tx_led_pin ;
}
# endif
// Получение состояния светодиодов связи модуля
// Get module communication LED state
void tclacClimate : : set_module_display_state ( bool state ) {
this - > module_display_status_ = state ;
}
// Получение режима фиксации вертикальной заслонки
// Get vertical damper fixed mode
void tclacClimate : : set_vertical_airflow ( AirflowVerticalDirection direction ) {
this - > vertical_direction_ = direction ;
if ( force_mode_status_ ) {
@@ -668,7 +829,7 @@ void tclacClimate::set_vertical_airflow(AirflowVerticalDirection direction) {
}
}
}
// Получение режима фиксации горизонтальных заслонок
// Get horizontal damper fixed mode
void tclacClimate : : set_horizontal_airflow ( AirflowHorizontalDirection direction ) {
this - > horizontal_direction_ = direction ;
if ( force_mode_status_ ) {
@@ -677,7 +838,7 @@ void tclacClimate::set_horizontal_airflow(AirflowHorizontalDirection direction)
}
}
}
// Получение режима качания вертикальной заслонки
// Get vertical damper swing mode
void tclacClimate : : set_vertical_swing_direction ( VerticalSwingDirection direction ) {
this - > vertical_swing_direction_ = direction ;
if ( force_mode_status_ ) {
@@ -686,11 +847,11 @@ void tclacClimate::set_vertical_swing_direction(VerticalSwingDirection direction
}
}
}
// Получение доступных режимов работы кондиционера
// Get supported AC operating modes
void tclacClimate : : set_supported_modes ( const std : : set < climate : : ClimateMode > & modes ) {
this - > supported_modes_ = modes ;
}
// Получение режима качания горизонтальных заслонок
// Get horizontal damper swing mode
void tclacClimate : : set_horizontal_swing_direction ( HorizontalSwingDirection direction ) {
horizontal_swing_direction_ = direction ;
if ( force_mode_status_ ) {
@@ -699,18 +860,18 @@ void tclacClimate::set_horizontal_swing_direction(HorizontalSwingDirection direc
}
}
}
// Получение доступных скоростей вентилятора
// Get supported fan speeds
void tclacClimate : : set_supported_fan_modes ( const std : : set < climate : : ClimateFanMode > & modes ) {
this - > supported_fan_modes_ = modes ;
}
// Получение доступных режимов качания заслонок
// Get supported swing modes
void tclacClimate : : set_supported_swing_modes ( const std : : set < climate : : ClimateSwingMode > & modes ) {
this - > supported_swing_modes_ = modes ;
}
// Получение доступных предустановок
// Get supported presets
void tclacClimate : : set_supported_presets ( const std : : set < climate : : ClimatePreset > & presets ) {
this - > supported_presets_ = presets ;
}
}
}
}