Files
esphome-dev/esphome/components/fujitsu_general/fujitsu_general.cpp
T
Kevin Pelzel 01bbd04a5a Add Fan and Swing Support to fujitsu-general Component (#1287)
* Added fan and swing support to fujitsu-general

* fixed formatting
2020-09-24 00:07:51 -03:00

255 lines
8.8 KiB
C++

#include "fujitsu_general.h"
namespace esphome {
namespace fujitsu_general {
static const char *TAG = "fujitsu_general.climate";
// Control packet
const uint16_t FUJITSU_GENERAL_STATE_LENGTH = 16;
const uint8_t FUJITSU_GENERAL_BASE_BYTE0 = 0x14;
const uint8_t FUJITSU_GENERAL_BASE_BYTE1 = 0x63;
const uint8_t FUJITSU_GENERAL_BASE_BYTE2 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE3 = 0x10;
const uint8_t FUJITSU_GENERAL_BASE_BYTE4 = 0x10;
const uint8_t FUJITSU_GENERAL_BASE_BYTE5 = 0xFE;
const uint8_t FUJITSU_GENERAL_BASE_BYTE6 = 0x09;
const uint8_t FUJITSU_GENERAL_BASE_BYTE7 = 0x30;
// Temperature and POWER ON
const uint8_t FUJITSU_GENERAL_POWER_ON_MASK_BYTE8 = 0b00000001;
const uint8_t FUJITSU_GENERAL_BASE_BYTE8 = 0x40;
// Mode
const uint8_t FUJITSU_GENERAL_MODE_AUTO_BYTE9 = 0x00;
const uint8_t FUJITSU_GENERAL_MODE_HEAT_BYTE9 = 0x04;
const uint8_t FUJITSU_GENERAL_MODE_COOL_BYTE9 = 0x01;
const uint8_t FUJITSU_GENERAL_MODE_DRY_BYTE9 = 0x02;
const uint8_t FUJITSU_GENERAL_MODE_FAN_BYTE9 = 0x03;
const uint8_t FUJITSU_GENERAL_MODE_10C_BYTE9 = 0x0B;
const uint8_t FUJITSU_GENERAL_BASE_BYTE9 = 0x01;
// Fan speed and swing
const uint8_t FUJITSU_GENERAL_FAN_AUTO_BYTE10 = 0x00;
const uint8_t FUJITSU_GENERAL_FAN_HIGH_BYTE10 = 0x01;
const uint8_t FUJITSU_GENERAL_FAN_MEDIUM_BYTE10 = 0x02;
const uint8_t FUJITSU_GENERAL_FAN_LOW_BYTE10 = 0x03;
const uint8_t FUJITSU_GENERAL_FAN_SILENT_BYTE10 = 0x04;
const uint8_t FUJITSU_GENERAL_SWING_NONE_BYTE10 = 0x00;
const uint8_t FUJITSU_GENERAL_SWING_VERTICAL_BYTE10 = 0x01;
const uint8_t FUJITSU_GENERAL_SWING_HORIZONTAL_BYTE10 = 0x02;
const uint8_t FUJITSU_GENERAL_SWING_BOTH_BYTE10 = 0x03;
const uint8_t FUJITSU_GENERAL_BASE_BYTE10 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE11 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE12 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE13 = 0x00;
// Outdoor Unit Low Noise
const uint8_t FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14 = 0xA0;
const uint8_t FUJITSU_GENERAL_BASE_BYTE14 = 0x20;
// CRC
const uint8_t FUJITSU_GENERAL_BASE_BYTE15 = 0x6F;
// Power off packet is specific
const uint16_t FUJITSU_GENERAL_OFF_LENGTH = 7;
const uint8_t FUJITSU_GENERAL_OFF_BYTE0 = FUJITSU_GENERAL_BASE_BYTE0;
const uint8_t FUJITSU_GENERAL_OFF_BYTE1 = FUJITSU_GENERAL_BASE_BYTE1;
const uint8_t FUJITSU_GENERAL_OFF_BYTE2 = FUJITSU_GENERAL_BASE_BYTE2;
const uint8_t FUJITSU_GENERAL_OFF_BYTE3 = FUJITSU_GENERAL_BASE_BYTE3;
const uint8_t FUJITSU_GENERAL_OFF_BYTE4 = FUJITSU_GENERAL_BASE_BYTE4;
const uint8_t FUJITSU_GENERAL_OFF_BYTE5 = 0x02;
const uint8_t FUJITSU_GENERAL_OFF_BYTE6 = 0xFD;
const uint8_t FUJITSU_GENERAL_TEMP_MAX = 30; // Celsius
const uint8_t FUJITSU_GENERAL_TEMP_MIN = 16; // Celsius
const uint16_t FUJITSU_GENERAL_HEADER_MARK = 3300;
const uint16_t FUJITSU_GENERAL_HEADER_SPACE = 1600;
const uint16_t FUJITSU_GENERAL_BIT_MARK = 420;
const uint16_t FUJITSU_GENERAL_ONE_SPACE = 1200;
const uint16_t FUJITSU_GENERAL_ZERO_SPACE = 420;
const uint16_t FUJITSU_GENERAL_TRL_MARK = 420;
const uint16_t FUJITSU_GENERAL_TRL_SPACE = 8000;
const uint32_t FUJITSU_GENERAL_CARRIER_FREQUENCY = 38000;
FujitsuGeneralClimate::FujitsuGeneralClimate()
: ClimateIR(
FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1.0f, true, true,
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH},
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL,
climate::CLIMATE_SWING_BOTH}) {}
void FujitsuGeneralClimate::transmit_state() {
if (this->mode == climate::CLIMATE_MODE_OFF) {
this->transmit_off_();
return;
}
uint8_t remote_state[FUJITSU_GENERAL_STATE_LENGTH] = {0};
remote_state[0] = FUJITSU_GENERAL_BASE_BYTE0;
remote_state[1] = FUJITSU_GENERAL_BASE_BYTE1;
remote_state[2] = FUJITSU_GENERAL_BASE_BYTE2;
remote_state[3] = FUJITSU_GENERAL_BASE_BYTE3;
remote_state[4] = FUJITSU_GENERAL_BASE_BYTE4;
remote_state[5] = FUJITSU_GENERAL_BASE_BYTE5;
remote_state[6] = FUJITSU_GENERAL_BASE_BYTE6;
remote_state[7] = FUJITSU_GENERAL_BASE_BYTE7;
remote_state[8] = FUJITSU_GENERAL_BASE_BYTE8;
remote_state[9] = FUJITSU_GENERAL_BASE_BYTE9;
remote_state[10] = FUJITSU_GENERAL_BASE_BYTE10;
remote_state[11] = FUJITSU_GENERAL_BASE_BYTE11;
remote_state[12] = FUJITSU_GENERAL_BASE_BYTE12;
remote_state[13] = FUJITSU_GENERAL_BASE_BYTE13;
remote_state[14] = FUJITSU_GENERAL_BASE_BYTE14;
remote_state[15] = FUJITSU_GENERAL_BASE_BYTE15;
// Set temperature
auto safecelsius =
(uint8_t) roundf(clamp(this->target_temperature, FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX));
remote_state[8] = (byte) safecelsius - 16;
remote_state[8] = remote_state[8] << 4;
// If not powered - set power on flag
if (!this->power_) {
remote_state[8] = (byte) remote_state[8] | FUJITSU_GENERAL_POWER_ON_MASK_BYTE8;
}
// Set mode
switch (this->mode) {
case climate::CLIMATE_MODE_COOL:
remote_state[9] = FUJITSU_GENERAL_MODE_COOL_BYTE9;
break;
case climate::CLIMATE_MODE_HEAT:
remote_state[9] = FUJITSU_GENERAL_MODE_HEAT_BYTE9;
break;
case climate::CLIMATE_MODE_DRY:
remote_state[9] = FUJITSU_GENERAL_MODE_DRY_BYTE9;
break;
case climate::CLIMATE_MODE_FAN_ONLY:
remote_state[9] = FUJITSU_GENERAL_MODE_FAN_BYTE9;
break;
case climate::CLIMATE_MODE_AUTO:
default:
remote_state[9] = FUJITSU_GENERAL_MODE_AUTO_BYTE9;
break;
// TODO: CLIMATE_MODE_10C are missing in esphome
}
// Set fan
switch (this->fan_mode) {
case climate::CLIMATE_FAN_HIGH:
remote_state[10] = FUJITSU_GENERAL_FAN_HIGH_BYTE10;
break;
case climate::CLIMATE_FAN_MEDIUM:
remote_state[10] = FUJITSU_GENERAL_FAN_MEDIUM_BYTE10;
break;
case climate::CLIMATE_FAN_LOW:
remote_state[10] = FUJITSU_GENERAL_FAN_LOW_BYTE10;
break;
case climate::CLIMATE_FAN_AUTO:
default:
remote_state[10] = FUJITSU_GENERAL_FAN_AUTO_BYTE10;
break;
}
// Set swing
switch (this->swing_mode) {
case climate::CLIMATE_SWING_VERTICAL:
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_VERTICAL_BYTE10 << 4);
break;
case climate::CLIMATE_SWING_HORIZONTAL:
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_HORIZONTAL_BYTE10 << 4);
break;
case climate::CLIMATE_SWING_BOTH:
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_BOTH_BYTE10 << 4);
break;
case climate::CLIMATE_SWING_OFF:
default:
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_NONE_BYTE10 << 4);
break;
}
// TODO: missing support for outdoor unit low noise
// remote_state[14] = (byte) remote_state[14] | FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14;
// CRC
remote_state[15] = 0;
for (int i = 7; i < 15; i++) {
remote_state[15] += (byte) remote_state[i]; // Addiction
}
remote_state[15] = 0x100 - remote_state[15]; // mod 256
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
data->set_carrier_frequency(FUJITSU_GENERAL_CARRIER_FREQUENCY);
// Header
data->mark(FUJITSU_GENERAL_HEADER_MARK);
data->space(FUJITSU_GENERAL_HEADER_SPACE);
// Data
for (uint8_t i : remote_state) {
// Send all Bits from Byte Data in Reverse Order
for (uint8_t mask = 00000001; mask > 0; mask <<= 1) { // iterate through bit mask
data->mark(FUJITSU_GENERAL_BIT_MARK);
bool bit = i & mask;
data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE);
// Next bits
}
}
// Footer
data->mark(FUJITSU_GENERAL_TRL_MARK);
data->space(FUJITSU_GENERAL_TRL_SPACE);
transmit.perform();
this->power_ = true;
}
void FujitsuGeneralClimate::transmit_off_() {
uint8_t remote_state[FUJITSU_GENERAL_OFF_LENGTH] = {0};
remote_state[0] = FUJITSU_GENERAL_OFF_BYTE0;
remote_state[1] = FUJITSU_GENERAL_OFF_BYTE1;
remote_state[2] = FUJITSU_GENERAL_OFF_BYTE2;
remote_state[3] = FUJITSU_GENERAL_OFF_BYTE3;
remote_state[4] = FUJITSU_GENERAL_OFF_BYTE4;
remote_state[5] = FUJITSU_GENERAL_OFF_BYTE5;
remote_state[6] = FUJITSU_GENERAL_OFF_BYTE6;
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
data->set_carrier_frequency(FUJITSU_GENERAL_CARRIER_FREQUENCY);
// Header
data->mark(FUJITSU_GENERAL_HEADER_MARK);
data->space(FUJITSU_GENERAL_HEADER_SPACE);
// Data
for (uint8_t i : remote_state) {
// Send all Bits from Byte Data in Reverse Order
for (uint8_t mask = 00000001; mask > 0; mask <<= 1) { // iterate through bit mask
data->mark(FUJITSU_GENERAL_BIT_MARK);
bool bit = i & mask;
data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE);
// Next bits
}
}
// Footer
data->mark(FUJITSU_GENERAL_TRL_MARK);
data->space(FUJITSU_GENERAL_TRL_SPACE);
transmit.perform();
this->power_ = false;
}
} // namespace fujitsu_general
} // namespace esphome