#include "daikin.h" #include "esphome/components/remote_base/remote_base.h" namespace esphome { namespace daikin { static const char *const TAG = "daikin.climate"; void DaikinClimate::transmit_state() { uint8_t remote_state[35] = {0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00, 0x42, 0x49, 0x05, 0xA2, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00}; remote_state[21] = this->operation_mode_(); remote_state[22] = this->temperature_(); uint16_t fan_speed = this->fan_speed_(); remote_state[24] = fan_speed >> 8; remote_state[25] = fan_speed & 0xff; // Calculate checksum for (int i = 16; i < 34; i++) { remote_state[34] += remote_state[i]; } auto transmit = this->transmitter_->transmit(); auto *data = transmit.get_data(); data->set_carrier_frequency(DAIKIN_IR_FREQUENCY); data->mark(DAIKIN_HEADER_MARK); data->space(DAIKIN_HEADER_SPACE); for (int i = 0; i < 8; i++) { for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask data->mark(DAIKIN_BIT_MARK); bool bit = remote_state[i] & mask; data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE); } } data->mark(DAIKIN_BIT_MARK); data->space(DAIKIN_MESSAGE_SPACE); data->mark(DAIKIN_HEADER_MARK); data->space(DAIKIN_HEADER_SPACE); for (int i = 8; i < 16; i++) { for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask data->mark(DAIKIN_BIT_MARK); bool bit = remote_state[i] & mask; data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE); } } data->mark(DAIKIN_BIT_MARK); data->space(DAIKIN_MESSAGE_SPACE); data->mark(DAIKIN_HEADER_MARK); data->space(DAIKIN_HEADER_SPACE); for (int i = 16; i < 35; i++) { for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask data->mark(DAIKIN_BIT_MARK); bool bit = remote_state[i] & mask; data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE); } } data->mark(DAIKIN_BIT_MARK); data->space(0); transmit.perform(); } uint8_t DaikinClimate::operation_mode_() { uint8_t operating_mode = DAIKIN_MODE_ON; switch (this->mode) { case climate::CLIMATE_MODE_COOL: operating_mode |= DAIKIN_MODE_COOL; break; case climate::CLIMATE_MODE_DRY: operating_mode |= DAIKIN_MODE_DRY; break; case climate::CLIMATE_MODE_HEAT: operating_mode |= DAIKIN_MODE_HEAT; break; case climate::CLIMATE_MODE_HEAT_COOL: operating_mode |= DAIKIN_MODE_AUTO; break; case climate::CLIMATE_MODE_FAN_ONLY: operating_mode |= DAIKIN_MODE_FAN; break; case climate::CLIMATE_MODE_OFF: default: operating_mode = DAIKIN_MODE_OFF; break; } return operating_mode; } uint16_t DaikinClimate::fan_speed_() { uint16_t fan_speed; switch (this->fan_mode.value()) { case climate::CLIMATE_FAN_LOW: fan_speed = DAIKIN_FAN_1 << 8; break; case climate::CLIMATE_FAN_MEDIUM: fan_speed = DAIKIN_FAN_3 << 8; break; case climate::CLIMATE_FAN_HIGH: fan_speed = DAIKIN_FAN_5 << 8; break; case climate::CLIMATE_FAN_AUTO: default: fan_speed = DAIKIN_FAN_AUTO << 8; } // If swing is enabled switch first 4 bits to 1111 switch (this->swing_mode) { case climate::CLIMATE_SWING_VERTICAL: fan_speed |= 0x0F00; break; case climate::CLIMATE_SWING_HORIZONTAL: fan_speed |= 0x000F; break; case climate::CLIMATE_SWING_BOTH: fan_speed |= 0x0F0F; break; default: break; } return fan_speed; } uint8_t DaikinClimate::temperature_() { // Force special temperatures depending on the mode switch (this->mode) { case climate::CLIMATE_MODE_FAN_ONLY: return 0x32; case climate::CLIMATE_MODE_HEAT_COOL: case climate::CLIMATE_MODE_DRY: return 0xc0; default: uint8_t temperature = (uint8_t) roundf(clamp(this->target_temperature, DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX)); return temperature << 1; } } bool DaikinClimate::parse_state_frame_(const uint8_t frame[]) { uint8_t checksum = 0; for (int i = 0; i < (DAIKIN_STATE_FRAME_SIZE - 1); i++) { checksum += frame[i]; } if (frame[DAIKIN_STATE_FRAME_SIZE - 1] != checksum) return false; uint8_t mode = frame[5]; if (mode & DAIKIN_MODE_ON) { switch (mode & 0xF0) { case DAIKIN_MODE_COOL: this->mode = climate::CLIMATE_MODE_COOL; break; case DAIKIN_MODE_DRY: this->mode = climate::CLIMATE_MODE_DRY; break; case DAIKIN_MODE_HEAT: this->mode = climate::CLIMATE_MODE_HEAT; break; case DAIKIN_MODE_AUTO: this->mode = climate::CLIMATE_MODE_HEAT_COOL; break; case DAIKIN_MODE_FAN: this->mode = climate::CLIMATE_MODE_FAN_ONLY; break; } } else { this->mode = climate::CLIMATE_MODE_OFF; } uint8_t temperature = frame[6]; if (!(temperature & 0xC0)) { this->target_temperature = temperature >> 1; } uint8_t fan_mode = frame[8]; uint8_t swing_mode = frame[9]; if (fan_mode & 0xF && swing_mode & 0xF) { this->swing_mode = climate::CLIMATE_SWING_BOTH; } else if (fan_mode & 0xF) { this->swing_mode = climate::CLIMATE_SWING_VERTICAL; } else if (swing_mode & 0xF) { this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; } else { this->swing_mode = climate::CLIMATE_SWING_OFF; } switch (fan_mode & 0xF0) { case DAIKIN_FAN_1: case DAIKIN_FAN_2: case DAIKIN_FAN_SILENT: this->fan_mode = climate::CLIMATE_FAN_LOW; break; case DAIKIN_FAN_3: this->fan_mode = climate::CLIMATE_FAN_MEDIUM; break; case DAIKIN_FAN_4: case DAIKIN_FAN_5: this->fan_mode = climate::CLIMATE_FAN_HIGH; break; case DAIKIN_FAN_AUTO: this->fan_mode = climate::CLIMATE_FAN_AUTO; break; } this->publish_state(); return true; } bool DaikinClimate::on_receive(remote_base::RemoteReceiveData data) { uint8_t state_frame[DAIKIN_STATE_FRAME_SIZE] = {}; if (!data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) { return false; } for (uint8_t pos = 0; pos < DAIKIN_STATE_FRAME_SIZE; pos++) { uint8_t byte = 0; for (int8_t bit = 0; bit < 8; bit++) { if (data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE)) { byte |= 1 << bit; } else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) { return false; } } state_frame[pos] = byte; if (pos == 0) { // frame header if (byte != 0x11) return false; } else if (pos == 1) { // frame header if (byte != 0xDA) return false; } else if (pos == 2) { // frame header if (byte != 0x27) return false; } else if (pos == 3) { // NOLINT(bugprone-branch-clone) // frame header if (byte != 0x00) return false; } else if (pos == 4) { // frame type if (byte != 0x00) return false; } } return this->parse_state_frame_(state_frame); } } // namespace daikin } // namespace esphome