ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
tcl112.cpp
Go to the documentation of this file.
1#include "tcl112.h"
2#include "esphome/core/log.h"
3
4namespace esphome::tcl112 {
5
6ESPHOME_LOG_TAG(TAG, "tcl112.climate");
7
8const uint16_t TCL112_STATE_LENGTH = 14;
9const uint16_t TCL112_BITS = TCL112_STATE_LENGTH * 8;
10
11const uint8_t TCL112_HEAT = 1;
12const uint8_t TCL112_DRY = 2;
13const uint8_t TCL112_COOL = 3;
14const uint8_t TCL112_FAN = 7;
15const uint8_t TCL112_AUTO = 8;
16
17const uint8_t TCL112_FAN_AUTO = 0;
18const uint8_t TCL112_FAN_LOW = 2;
19const uint8_t TCL112_FAN_MED = 3;
20const uint8_t TCL112_FAN_HIGH = 5;
21
22const uint8_t TCL112_VSWING_MASK = 0x38;
23const uint8_t TCL112_POWER_MASK = 0x04;
24
25const uint8_t TCL112_HALF_DEGREE = 0b00100000;
26
27const uint16_t TCL112_HEADER_MARK = 3100;
28const uint16_t TCL112_HEADER_SPACE = 1650;
29const uint16_t TCL112_BIT_MARK = 500;
30static constexpr remote_base::BitTimings TCL112_BIT_TIMINGS{
31 .bit_mark = TCL112_BIT_MARK, .one_space = 1100, .zero_space = 350};
33
35 uint8_t remote_state[TCL112_STATE_LENGTH] = {0};
36
37 // A known good state. (On, Cool, 24C)
38 remote_state[0] = 0x23;
39 remote_state[1] = 0xCB;
40 remote_state[2] = 0x26;
41 remote_state[3] = 0x01;
42 remote_state[5] = 0x24;
43 remote_state[6] = 0x03;
44 remote_state[7] = 0x07;
45 remote_state[8] = 0x40;
46
47 // Set mode
48 switch (this->mode) {
50 remote_state[6] &= 0xF0;
51 remote_state[6] |= TCL112_AUTO;
52 break;
54 remote_state[6] &= 0xF0;
55 remote_state[6] |= TCL112_COOL;
56 break;
58 remote_state[6] &= 0xF0;
59 remote_state[6] |= TCL112_HEAT;
60 break;
62 remote_state[6] &= 0xF0;
63 remote_state[6] |= TCL112_DRY;
64 break;
66 remote_state[6] &= 0xF0;
67 remote_state[6] |= TCL112_FAN;
68 break;
70 default:
71 remote_state[5] &= ~TCL112_POWER_MASK;
72 break;
73 }
74
75 // Set temperature
76 // Make sure we have desired temp in the correct range.
77 float safecelsius = std::max(this->target_temperature, TCL112_TEMP_MIN);
78 safecelsius = std::min(safecelsius, TCL112_TEMP_MAX);
79 // Convert to integer nr. of half degrees.
80 auto half_degrees = static_cast<uint8_t>(safecelsius * 2);
81 if (half_degrees & 1) { // Do we have a half degree celsius?
82 remote_state[12] |= TCL112_HALF_DEGREE; // Add 0.5 degrees
83 } else {
84 remote_state[12] &= ~TCL112_HALF_DEGREE; // Clear the half degree.
85 }
86 remote_state[7] &= 0xF0; // Clear temp bits.
87 remote_state[7] |= ((uint8_t) TCL112_TEMP_MAX - half_degrees / 2);
88
89 // Set fan
90 uint8_t selected_fan;
91 switch (this->fan_mode.value_or(climate::CLIMATE_FAN_ON)) {
93 selected_fan = TCL112_FAN_HIGH;
94 break;
96 selected_fan = TCL112_FAN_MED;
97 break;
99 selected_fan = TCL112_FAN_LOW;
100 break;
102 default:
103 selected_fan = TCL112_FAN_AUTO;
104 }
105 remote_state[8] |= selected_fan;
106
107 // Set swing
109 remote_state[8] |= TCL112_VSWING_MASK;
110 }
111
112 // Calculate & set the checksum for the current internal state of the remote.
113 // Stored the checksum value in the last byte.
114 for (uint8_t checksum_byte = 0; checksum_byte < TCL112_STATE_LENGTH - 1; checksum_byte++)
115 remote_state[TCL112_STATE_LENGTH - 1] += remote_state[checksum_byte];
116
117 ESP_LOGV(TAG, "Sending: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", remote_state[0],
118 remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], remote_state[6],
119 remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], remote_state[12],
120 remote_state[13]);
121
122 auto transmit = this->transmitter_->transmit();
123 auto *data = transmit.get_data();
124
125 data->set_carrier_frequency(38000);
126
127 // Header
128 data->mark(TCL112_HEADER_MARK);
129 data->space(TCL112_HEADER_SPACE);
130 // Data
131 data->item_bytes<TCL112_BIT_TIMINGS>(remote_state, TCL112_STATE_LENGTH);
132 // Footer
133 data->mark(TCL112_BIT_MARK);
134 data->space(TCL112_GAP);
135
136 transmit.perform();
137}
138
140 // Validate header
141 if (!data.expect_item(TCL112_HEADER_MARK, TCL112_HEADER_SPACE)) {
142 ESP_LOGVV(TAG, "Header fail");
143 return false;
144 }
145
146 uint8_t remote_state[TCL112_STATE_LENGTH];
147 if (!data.expect_bytes<TCL112_BIT_TIMINGS>(remote_state, TCL112_STATE_LENGTH)) {
148 ESP_LOGVV(TAG, "Data fail");
149 return false;
150 }
151 // Validate footer
152 if (!data.expect_mark(TCL112_BIT_MARK)) {
153 ESP_LOGVV(TAG, "Footer fail");
154 return false;
155 }
156
157 // Calculate & set the checksum for the current internal state of the remote.
158 // Stored the checksum value in the last byte.
159 const uint8_t checksum = sum_bytes(remote_state, TCL112_STATE_LENGTH - 1);
160 if (checksum != remote_state[TCL112_STATE_LENGTH - 1]) {
161 ESP_LOGVV(TAG, "Checksum fail");
162 return false;
163 }
164
165 ESP_LOGV(TAG, "Received: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X",
166 remote_state[0], remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5],
167 remote_state[6], remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11],
168 remote_state[12], remote_state[13]);
169
170 // two first bytes are constant
171 if (remote_state[0] != 0x23 || remote_state[1] != 0xCB)
172 return false;
173
174 if ((remote_state[5] & TCL112_POWER_MASK) == 0) {
176 } else {
177 auto mode = remote_state[6] & 0x0F;
178 switch (mode) {
179 case TCL112_HEAT:
181 break;
182 case TCL112_COOL:
184 break;
185 case TCL112_DRY:
187 break;
188 case TCL112_FAN:
190 break;
191 case TCL112_AUTO:
193 break;
194 default:
195 break;
196 }
197 }
198 auto temp = TCL112_TEMP_MAX - remote_state[7];
199 if (remote_state[12] & TCL112_HALF_DEGREE)
200 temp += .5f;
201 this->target_temperature = temp;
202 auto fan = remote_state[8] & 0x7;
203 switch (fan) {
204 case TCL112_FAN_HIGH:
206 break;
207 case TCL112_FAN_MED:
209 break;
210 case TCL112_FAN_LOW:
212 break;
213 case TCL112_FAN_AUTO:
214 default:
216 break;
217 }
218 if ((remote_state[8] & TCL112_VSWING_MASK) == TCL112_VSWING_MASK) {
220 } else {
222 }
223
224 this->publish_state();
225 return true;
226}
227
228} // namespace esphome::tcl112
uint8_t checksum
Definition bl0906.h:3
ClimateMode mode
The active mode of the climate device.
Definition climate.h:317
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition climate.h:311
float target_temperature
The target temperature of the climate device.
Definition climate.h:298
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition climate.h:323
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition climate.cpp:432
void transmit_state() override
Transmit via IR the state of this climate controller.
Definition tcl112.cpp:34
bool on_receive(remote_base::RemoteReceiveData data) override
Handle received IR Buffer.
Definition tcl112.cpp:139
@ CLIMATE_SWING_OFF
The swing mode is set to Off.
@ CLIMATE_SWING_VERTICAL
The fan mode is set to Vertical.
@ CLIMATE_MODE_DRY
The climate device is set to dry/humidity mode.
@ CLIMATE_MODE_FAN_ONLY
The climate device only has the fan enabled, no heating or cooling is taking place.
@ CLIMATE_MODE_HEAT
The climate device is set to heat to reach the target temperature.
@ CLIMATE_MODE_COOL
The climate device is set to cool to reach the target temperature.
@ CLIMATE_MODE_HEAT_COOL
The climate device is set to heat/cool to reach the target temperature.
@ CLIMATE_MODE_OFF
The climate device is off.
@ CLIMATE_FAN_MEDIUM
The fan mode is set to Medium.
@ CLIMATE_FAN_ON
The fan mode is set to On.
@ CLIMATE_FAN_AUTO
The fan mode is set to Auto.
@ CLIMATE_FAN_LOW
The fan mode is set to Low.
@ CLIMATE_FAN_HIGH
The fan mode is set to High.
const uint8_t TCL112_DRY
Definition tcl112.cpp:12
const uint8_t TCL112_FAN
Definition tcl112.cpp:14
const uint8_t TCL112_AUTO
Definition tcl112.cpp:15
const float TCL112_TEMP_MAX
Definition tcl112.h:8
const uint16_t TCL112_BIT_MARK
Definition tcl112.cpp:29
const uint8_t TCL112_HALF_DEGREE
Definition tcl112.cpp:25
const uint8_t TCL112_VSWING_MASK
Definition tcl112.cpp:22
const uint16_t TCL112_HEADER_MARK
Definition tcl112.cpp:27
ESPHOME_LOG_TAG(TAG, "tcl112.climate")
const uint16_t TCL112_HEADER_SPACE
Definition tcl112.cpp:28
const uint32_t TCL112_GAP
Definition tcl112.cpp:32
const uint8_t TCL112_FAN_MED
Definition tcl112.cpp:19
const uint8_t TCL112_FAN_HIGH
Definition tcl112.cpp:20
const float TCL112_TEMP_MIN
Definition tcl112.h:9
const uint8_t TCL112_FAN_LOW
Definition tcl112.cpp:18
const uint8_t TCL112_FAN_AUTO
Definition tcl112.cpp:17
const uint8_t TCL112_COOL
Definition tcl112.cpp:13
const uint16_t TCL112_BITS
Definition tcl112.cpp:9
const uint8_t TCL112_POWER_MASK
Definition tcl112.cpp:23
const uint16_t TCL112_STATE_LENGTH
Definition tcl112.cpp:8
const uint8_t TCL112_HEAT
Definition tcl112.cpp:11
constexpr uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t sum=0)
Add up the bytes of data with size len, truncated to 8 bits: the checksum many serial and IR protocol...
Definition helpers.h:894
static void uint32_t
A bit is a mark (one_mark or zero_mark, both bit_mark unless set) and a space (one_space or zero_spac...
Definition remote_base.h:29