2021-03-08 15:54:18 +01:00

226 lines
6.3 KiB
C++
Executable File

#include "toshiba.h"
#include "esphome/core/log.h"
namespace esphome {
namespace toshiba {
static const char *TAG = "toshiba.climate";
const uint32_t TOSHIBA_HDR_MARK = 4400;
const uint32_t TOSHIBA_HDR_SPACE = 4400;
const uint32_t TOSHIBA_BIT_MARK = 550;
const uint32_t TOSHIBA_ONE_SPACE = 1600;
const uint32_t TOSHIBA_ZERO_SPACE = 550;
const uint8_t TOSHIBA_FAN_AUTO = 0x00;
const uint8_t TOSHIBA_MODE_OFF = 0xE0;
const uint8_t TOSHIBA_MODE_AUTO = 0x00;
const uint8_t TOSHIBA_MODE_HEAT = 0xC0;
const uint8_t TOSHIBA_MODE_COOL = 0x80;
const uint8_t TOSHIBA_MODE_DRY = 0x40;
const uint8_t TOSHIBA_FAN1 = 0x02;
const uint8_t TOSHIBA_FAN2 = 0x06;
const uint8_t TOSHIBA_FAN3 = 0x01;
const uint8_t TOSHIBA_FAN4 = 0x05;
const uint8_t TOSHIBA_FAN5 = 0x03;
const uint8_t TOSHIBA_PURIFIER = 0x08;
const uint8_t TOSHIBA_TEMP_MIN = 17;
const uint8_t TOSHIBA_TEMP_MAX = 30;
climate::ClimateTraits ToshibaClimate::traits() {
auto traits = climate::ClimateTraits();
traits.set_supports_current_temperature(this->sensor_ != nullptr);
traits.set_supports_auto_mode(true);
traits.set_supports_cool_mode(this->supports_cool_);
traits.set_supports_heat_mode(this->supports_heat_);
traits.set_supports_two_point_target_temperature(false);
traits.set_supports_away(false);
traits.set_visual_min_temperature(TOSHIBA_TEMP_MIN);
traits.set_visual_max_temperature(TOSHIBA_TEMP_MAX);
traits.set_visual_temperature_step(1);
return traits;
}
// TEMP MODE CHECK
// Off 0x4f 0xb0 0xc0 0x3f 0x80 0x06 0xe0 0x00 0x66
// 23 A H 0x4f 0xb0 0xc0 0x3f 0x80 0x06 0xc0 0x00 0x46
// AUTO 0x4f 0xb0 0xc0 0x3f 0x80 0x06 0x00 0x00 0x86
// Pure 0x4f 0xb0 0xc0 0x3f 0x80 0x06 0x00 0x08 0x8e
//
// High power 0x4f 0xb0 0x20 0xdf 0x90 0x06 0xc0 0x00 0x80 0xd6
// Sleep mode 0x4f 0xb0 0x20 0xdf 0x90 0x06 0xc0 0x00 0xc0 0x96
// Fix 0x4f 0xb0 0x80 0x7f 0x84 0x00 0x84
// Swing 0x4f 0xb0 0x80 0x7f 0x84 0x20 0xa4
void ToshibaClimate::fix_louvre() {
/*
* Fix
* 0x4f, 0xb0, 0x80, 0x7f, 0x84, 0x00, 0x84
*/
ESP_LOGI(TAG, "SEND FIX COMMAND");
uint8_t remote_state[] = { 0x4F, 0xB0, 0x80, 0x7F, 0x84, 0x00, 0x84 };
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
data->set_carrier_frequency(38000);
data->mark(TOSHIBA_HDR_MARK);
data->space(TOSHIBA_HDR_SPACE);
for (uint8_t i : remote_state)
for (uint8_t j = 0; j < 8; j++) {
data->mark(TOSHIBA_BIT_MARK);
bool bit = i & (1 << j);
data->space(bit ? TOSHIBA_ONE_SPACE : TOSHIBA_ZERO_SPACE);
}
data->mark(TOSHIBA_BIT_MARK);
data->space(0);
transmit.perform();
}
void ToshibaClimate::swing_louvre() {
/*
* Swing
* 0x4f, 0xb0, 0x80, 0x7f, 0x84, 0x20, 0xa4
*/
ESP_LOGI(TAG, "SEND SWING COMMAND");
uint8_t remote_state[] = { 0x4F, 0xB0, 0x80, 0x7F, 0x84, 0x20, 0xA4 };
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
data->set_carrier_frequency(38000);
data->mark(TOSHIBA_HDR_MARK);
data->space(TOSHIBA_HDR_SPACE);
for (uint8_t i : remote_state)
for (uint8_t j = 0; j < 8; j++) {
data->mark(TOSHIBA_BIT_MARK);
bool bit = i & (1 << j);
data->space(bit ? TOSHIBA_ONE_SPACE : TOSHIBA_ZERO_SPACE);
}
data->mark(TOSHIBA_BIT_MARK);
data->space(0);
transmit.perform();
}
void ToshibaClimate::setup() {
if (this->sensor_) {
this->sensor_->add_on_state_callback([this](float state) {
this->current_temperature = state;
// current temperature changed, publish state
this->publish_state();
});
this->current_temperature = this->sensor_->state;
} else
this->current_temperature = NAN;
// restore set points
auto restore = this->restore_state_();
if (restore.has_value()) {
restore->apply(this);
} else {
// restore from defaults
this->mode = climate::CLIMATE_MODE_AUTO;
// initialize target temperature to some value so that it's not NAN
this->target_temperature = 23;
}
}
void ToshibaClimate::control(const climate::ClimateCall &call) {
if (call.get_mode().has_value())
this->mode = *call.get_mode();
if (call.get_target_temperature().has_value())
this->target_temperature = *call.get_target_temperature();
this->transmit_state_();
this->publish_state();
}
uint8_t reverse_byte(uint8_t in) {
const uint8_t BITS = 8;
uint8_t out = 0;
for(int i = 0; i < BITS; i++) {
if(in & (1 << i)) {
out |= (1 << (BITS-1-i));
}
}
return out;
}
void ToshibaClimate::transmit_state_() {
uint8_t operating_mode;
uint8_t fan_speed = TOSHIBA_FAN_AUTO;
uint8_t temperature = 23;
uint8_t purifier = 0;
if (this->pure_) {
purifier = TOSHIBA_PURIFIER;
}
switch (this->mode) {
case climate::CLIMATE_MODE_HEAT:
operating_mode = TOSHIBA_MODE_HEAT;
break;
case climate::CLIMATE_MODE_COOL:
operating_mode = TOSHIBA_MODE_COOL;
break;
case climate::CLIMATE_MODE_AUTO:
operating_mode = TOSHIBA_MODE_AUTO;
break;
case climate::CLIMATE_MODE_OFF:
default:
operating_mode = TOSHIBA_MODE_OFF;
break;
}
temperature = (uint8_t) roundf(clamp(this->target_temperature, TOSHIBA_TEMP_MIN, TOSHIBA_TEMP_MAX));
uint8_t temperatures[] = {
0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06,
0x0e, 0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b
};
uint8_t remote_state[] = {
0x4F, 0xB0, 0xC0, 0x3F, 0x80,
(uint8_t)(temperatures[temperature - TOSHIBA_TEMP_MIN]),
(uint8_t)(operating_mode | fan_speed),
(uint8_t)(purifier),
0x00
};
uint8_t checksum{0};
for (uint8_t i = 0; i < 8; i++) {
checksum ^= reverse_byte(remote_state[i]);
}
remote_state[8] = reverse_byte(checksum);
ESP_LOGV(TAG, "Sending toshiba code: %u", remote_state);
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
data->set_carrier_frequency(38000);
// Header
data->mark(TOSHIBA_HDR_MARK);
data->space(TOSHIBA_HDR_SPACE);
// Data
for (uint8_t i : remote_state)
for (uint8_t j = 0; j < 8; j++) {
data->mark(TOSHIBA_BIT_MARK);
bool bit = i & (1 << j);
data->space(bit ? TOSHIBA_ONE_SPACE : TOSHIBA_ZERO_SPACE);
}
// End mark
data->mark(TOSHIBA_BIT_MARK);
data->space(0);
transmit.perform();
}
} // namespace toshiba
} // namespace esphome