ESPHome  2024.12.2
whirlpool.cpp
Go to the documentation of this file.
1 #include "whirlpool.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace whirlpool {
6 
7 static const char *const TAG = "whirlpool.climate";
8 
9 const uint16_t WHIRLPOOL_HEADER_MARK = 9000;
10 const uint16_t WHIRLPOOL_HEADER_SPACE = 4494;
11 const uint16_t WHIRLPOOL_BIT_MARK = 572;
12 const uint16_t WHIRLPOOL_ONE_SPACE = 1659;
13 const uint16_t WHIRLPOOL_ZERO_SPACE = 553;
14 const uint32_t WHIRLPOOL_GAP = 7960;
15 
16 const uint32_t WHIRLPOOL_CARRIER_FREQUENCY = 38000;
17 
18 const uint8_t WHIRLPOOL_STATE_LENGTH = 21;
19 
20 const uint8_t WHIRLPOOL_HEAT = 0;
21 const uint8_t WHIRLPOOL_DRY = 3;
22 const uint8_t WHIRLPOOL_COOL = 2;
23 const uint8_t WHIRLPOOL_FAN = 4;
24 const uint8_t WHIRLPOOL_AUTO = 1;
25 
26 const uint8_t WHIRLPOOL_FAN_AUTO = 0;
27 const uint8_t WHIRLPOOL_FAN_HIGH = 1;
28 const uint8_t WHIRLPOOL_FAN_MED = 2;
29 const uint8_t WHIRLPOOL_FAN_LOW = 3;
30 
31 const uint8_t WHIRLPOOL_SWING_MASK = 128;
32 
33 const uint8_t WHIRLPOOL_POWER = 0x04;
34 
36  this->last_transmit_time_ = millis(); // setting the time of the last transmission.
37  uint8_t remote_state[WHIRLPOOL_STATE_LENGTH] = {0};
38  remote_state[0] = 0x83;
39  remote_state[1] = 0x06;
40  remote_state[6] = 0x80;
41  // MODEL DG11J191
42  remote_state[18] = 0x08;
43 
44  auto powered_on = this->mode != climate::CLIMATE_MODE_OFF;
45  if (powered_on != this->powered_on_assumed) {
46  // Set power toggle command
47  remote_state[2] = 4;
48  remote_state[15] = 1;
49  this->powered_on_assumed = powered_on;
50  }
51  switch (this->mode) {
53  // set fan auto
54  // set temp auto temp
55  // set sleep false
56  remote_state[3] = WHIRLPOOL_AUTO;
57  remote_state[15] = 0x17;
58  break;
60  remote_state[3] = WHIRLPOOL_HEAT;
61  remote_state[15] = 6;
62  break;
64  remote_state[3] = WHIRLPOOL_COOL;
65  remote_state[15] = 6;
66  break;
68  remote_state[3] = WHIRLPOOL_DRY;
69  remote_state[15] = 6;
70  break;
72  remote_state[3] = WHIRLPOOL_FAN;
73  remote_state[15] = 6;
74  break;
76  default:
77  break;
78  }
79 
80  // Temperature
81  auto temp = (uint8_t) roundf(clamp(this->target_temperature, this->temperature_min_(), this->temperature_max_()));
82  remote_state[3] |= (uint8_t) (temp - this->temperature_min_()) << 4;
83 
84  // Fan speed
85  switch (this->fan_mode.value()) {
87  remote_state[2] |= WHIRLPOOL_FAN_HIGH;
88  break;
90  remote_state[2] |= WHIRLPOOL_FAN_MED;
91  break;
93  remote_state[2] |= WHIRLPOOL_FAN_LOW;
94  break;
95  default:
96  break;
97  }
98 
99  // Swing
100  ESP_LOGV(TAG, "send swing %s", this->send_swing_cmd_ ? "true" : "false");
101  if (this->send_swing_cmd_) {
103  remote_state[2] |= 128;
104  remote_state[8] |= 64;
105  }
106  }
107 
108  // Checksum
109  for (uint8_t i = 2; i < 13; i++)
110  remote_state[13] ^= remote_state[i];
111  for (uint8_t i = 14; i < 20; i++)
112  remote_state[20] ^= remote_state[i];
113 
114  ESP_LOGV(TAG,
115  "Sending: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X "
116  "%02X %02X %02X",
117  remote_state[0], remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5],
118  remote_state[6], remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11],
119  remote_state[12], remote_state[13], remote_state[14], remote_state[15], remote_state[16], remote_state[17],
120  remote_state[18], remote_state[19], remote_state[20]);
121 
122  // Send code
123  auto transmit = this->transmitter_->transmit();
124  auto *data = transmit.get_data();
125 
126  data->set_carrier_frequency(38000);
127 
128  // Header
129  data->mark(WHIRLPOOL_HEADER_MARK);
130  data->space(WHIRLPOOL_HEADER_SPACE);
131  // Data
132  auto bytes_sent = 0;
133  for (uint8_t i : remote_state) {
134  for (uint8_t j = 0; j < 8; j++) {
135  data->mark(WHIRLPOOL_BIT_MARK);
136  bool bit = i & (1 << j);
137  data->space(bit ? WHIRLPOOL_ONE_SPACE : WHIRLPOOL_ZERO_SPACE);
138  }
139  bytes_sent++;
140  if (bytes_sent == 6 || bytes_sent == 14) {
141  // Divider
142  data->mark(WHIRLPOOL_BIT_MARK);
143  data->space(WHIRLPOOL_GAP);
144  }
145  }
146  // Footer
147  data->mark(WHIRLPOOL_BIT_MARK);
148 
149  transmit.perform();
150 }
151 
153  // Check if the esp isn't currently transmitting.
154  if (millis() - this->last_transmit_time_ < 500) {
155  ESP_LOGV(TAG, "Blocked receive because of current trasmittion");
156  return false;
157  }
158 
159  // Validate header
160  if (!data.expect_item(WHIRLPOOL_HEADER_MARK, WHIRLPOOL_HEADER_SPACE)) {
161  ESP_LOGV(TAG, "Header fail");
162  return false;
163  }
164 
165  uint8_t remote_state[WHIRLPOOL_STATE_LENGTH] = {0};
166  // Read all bytes.
167  for (int i = 0; i < WHIRLPOOL_STATE_LENGTH; i++) {
168  // Read bit
169  if (i == 6 || i == 14) {
170  if (!data.expect_item(WHIRLPOOL_BIT_MARK, WHIRLPOOL_GAP))
171  return false;
172  }
173  for (int j = 0; j < 8; j++) {
174  if (data.expect_item(WHIRLPOOL_BIT_MARK, WHIRLPOOL_ONE_SPACE)) {
175  remote_state[i] |= 1 << j;
176 
177  } else if (!data.expect_item(WHIRLPOOL_BIT_MARK, WHIRLPOOL_ZERO_SPACE)) {
178  ESP_LOGV(TAG, "Byte %d bit %d fail", i, j);
179  return false;
180  }
181  }
182 
183  ESP_LOGVV(TAG, "Byte %d %02X", i, remote_state[i]);
184  }
185  // Validate footer
186  if (!data.expect_mark(WHIRLPOOL_BIT_MARK)) {
187  ESP_LOGV(TAG, "Footer fail");
188  return false;
189  }
190 
191  uint8_t checksum13 = 0;
192  uint8_t checksum20 = 0;
193  // Calculate checksum and compare with signal value.
194  for (uint8_t i = 2; i < 13; i++)
195  checksum13 ^= remote_state[i];
196  for (uint8_t i = 14; i < 20; i++)
197  checksum20 ^= remote_state[i];
198 
199  if (checksum13 != remote_state[13] || checksum20 != remote_state[20]) {
200  ESP_LOGVV(TAG, "Checksum fail");
201  return false;
202  }
203 
204  ESP_LOGV(
205  TAG,
206  "Received: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X "
207  "%02X %02X %02X",
208  remote_state[0], remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5],
209  remote_state[6], remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11],
210  remote_state[12], remote_state[13], remote_state[14], remote_state[15], remote_state[16], remote_state[17],
211  remote_state[18], remote_state[19], remote_state[20]);
212 
213  // verify header remote code
214  if (remote_state[0] != 0x83 || remote_state[1] != 0x06)
215  return false;
216 
217  // powr on/off button
218  ESP_LOGV(TAG, "Power: %02X", (remote_state[2] & WHIRLPOOL_POWER));
219 
220  if ((remote_state[2] & WHIRLPOOL_POWER) == WHIRLPOOL_POWER) {
221  auto powered_on = this->mode != climate::CLIMATE_MODE_OFF;
222 
223  if (powered_on) {
225  this->powered_on_assumed = false;
226  } else {
227  this->powered_on_assumed = true;
228  }
229  }
230 
231  // Set received mode
232  if (powered_on_assumed) {
233  auto mode = remote_state[3] & 0x7;
234  ESP_LOGV(TAG, "Mode: %02X", mode);
235  switch (mode) {
236  case WHIRLPOOL_HEAT:
238  break;
239  case WHIRLPOOL_COOL:
241  break;
242  case WHIRLPOOL_DRY:
244  break;
245  case WHIRLPOOL_FAN:
247  break;
248  case WHIRLPOOL_AUTO:
250  break;
251  }
252  }
253 
254  // Set received temp
255  int temp = remote_state[3] & 0xF0;
256  ESP_LOGVV(TAG, "Temperature Raw: %02X", temp);
257  temp = (uint8_t) temp >> 4;
258  temp += static_cast<int>(this->temperature_min_());
259  ESP_LOGVV(TAG, "Temperature Climate: %u", temp);
260  this->target_temperature = temp;
261 
262  // Set received fan speed
263  auto fan = remote_state[2] & 0x03;
264  ESP_LOGVV(TAG, "Fan: %02X", fan);
265  switch (fan) {
266  case WHIRLPOOL_FAN_HIGH:
268  break;
269  case WHIRLPOOL_FAN_MED:
271  break;
272  case WHIRLPOOL_FAN_LOW:
274  break;
275  case WHIRLPOOL_FAN_AUTO:
276  default:
278  break;
279  }
280 
281  // Set received swing status
282  if ((remote_state[2] & WHIRLPOOL_SWING_MASK) == WHIRLPOOL_SWING_MASK && remote_state[8] == 0x40) {
283  ESP_LOGVV(TAG, "Swing toggle pressed ");
284  if (this->swing_mode == climate::CLIMATE_SWING_OFF) {
286  } else {
288  }
289  }
290 
291  this->publish_state();
292  return true;
293 }
294 
295 } // namespace whirlpool
296 } // namespace esphome
The fan mode is set to Low.
Definition: climate_mode.h:54
value_type const & value() const
Definition: optional.h:89
ClimateSwingMode swing_mode
The active swing mode of the climate device.
Definition: climate.h:202
const uint16_t WHIRLPOOL_BIT_MARK
Definition: whirlpool.cpp:11
const uint32_t WHIRLPOOL_GAP
Definition: whirlpool.cpp:14
const uint8_t WHIRLPOOL_AUTO
Definition: whirlpool.cpp:24
const uint16_t WHIRLPOOL_HEADER_MARK
Definition: whirlpool.cpp:9
const uint8_t WHIRLPOOL_FAN
Definition: whirlpool.cpp:23
float target_temperature
The target temperature of the climate device.
Definition: climate.h:186
void set_carrier_frequency(uint32_t carrier_frequency)
Definition: remote_base.h:34
const uint16_t WHIRLPOOL_HEADER_SPACE
Definition: whirlpool.cpp:10
const uint16_t WHIRLPOOL_ZERO_SPACE
Definition: whirlpool.cpp:13
The climate device is set to heat to reach the target temperature.
Definition: climate_mode.h:18
const uint8_t WHIRLPOOL_DRY
Definition: whirlpool.cpp:21
ClimateMode mode
The active mode of the climate device.
Definition: climate.h:173
void transmit_state() override
Transmit via IR the state of this climate controller.
Definition: whirlpool.cpp:35
The climate device is set to dry/humidity mode.
Definition: climate_mode.h:22
const uint16_t WHIRLPOOL_ONE_SPACE
Definition: whirlpool.cpp:12
constexpr const T & clamp(const T &v, const T &lo, const T &hi, Compare comp)
Definition: helpers.h:93
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
const uint8_t WHIRLPOOL_FAN_LOW
Definition: whirlpool.cpp:29
const uint8_t WHIRLPOOL_FAN_AUTO
Definition: whirlpool.cpp:26
const uint8_t WHIRLPOOL_SWING_MASK
Definition: whirlpool.cpp:31
int32_t last_transmit_time_
Set the time of the last transmission.
Definition: whirlpool.h:51
The climate device is set to cool to reach the target temperature.
Definition: climate_mode.h:16
The fan mode is set to Auto.
Definition: climate_mode.h:52
RemoteTransmitterBase * transmitter_
Definition: remote_base.h:276
const uint8_t WHIRLPOOL_POWER
Definition: whirlpool.cpp:33
The climate device is set to heat/cool to reach the target temperature.
Definition: climate_mode.h:14
The fan mode is set to Vertical.
Definition: climate_mode.h:76
void publish_state()
Publish the state of the climate device, to be called from integrations.
Definition: climate.cpp:395
The fan mode is set to High.
Definition: climate_mode.h:58
The swing mode is set to Off.
Definition: climate_mode.h:72
The climate device is off.
Definition: climate_mode.h:12
const uint32_t WHIRLPOOL_CARRIER_FREQUENCY
Definition: whirlpool.cpp:16
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
Definition: climate.h:199
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
const uint8_t WHIRLPOOL_STATE_LENGTH
Definition: whirlpool.cpp:18
const uint8_t WHIRLPOOL_COOL
Definition: whirlpool.cpp:22
The fan mode is set to Medium.
Definition: climate_mode.h:56
const uint8_t WHIRLPOOL_FAN_HIGH
Definition: whirlpool.cpp:27
bool on_receive(remote_base::RemoteReceiveData data) override
Handle received IR Buffer.
Definition: whirlpool.cpp:152
bool expect_item(uint32_t mark, uint32_t space)
Definition: remote_base.cpp:74
The climate device only has the fan enabled, no heating or cooling is taking place.
Definition: climate_mode.h:20
const uint8_t WHIRLPOOL_FAN_MED
Definition: whirlpool.cpp:28
const uint8_t WHIRLPOOL_HEAT
Definition: whirlpool.cpp:20