10struct RacPt1411hwruFanSpeed {
15static const char *
const TAG =
"toshiba.climate";
96 0x04, 0x0C, 0x0D, 0x09, 0x08, 0x0A, 0x0B};
99 0x22, 0x06, 0x26, 0x07, 0x05, 0x25, 0x04, 0x24, 0x0C,
100 0x2C, 0x0D, 0x2D, 0x09, 0x08, 0x28, 0x0A, 0x2A, 0x0B};
123struct Ras2819tPacketSuffix {
168static const uint8_t RAS_2819T_TEMP_CODES[] = {
252struct Ras2819tSecondPacketCodes {
254 Ras2819tPacketSuffix suffix;
276static uint8_t get_ras_2819t_temp_code(
float temperature) {
277 int temp_index =
static_cast<int>(
temperature) - 18;
278 if (temp_index < 0 || temp_index >=
static_cast<int>(
sizeof(RAS_2819T_TEMP_CODES))) {
279 ESP_LOGW(TAG,
"Temperature %.1f°C out of range [18-30°C], defaulting to 24°C",
temperature);
283 return RAS_2819T_TEMP_CODES[temp_index];
289static float decode_ras_2819t_temperature(uint8_t temp_code) {
290 uint8_t base_temp_code = temp_code & 0xF0;
293 for (
size_t temp_index = 0; temp_index <
sizeof(RAS_2819T_TEMP_CODES); temp_index++) {
294 if (RAS_2819T_TEMP_CODES[temp_index] == base_temp_code) {
295 return static_cast<float>(temp_index + 18);
299 ESP_LOGW(TAG,
"Unknown temp code: 0x%02X, defaulting to 24°C", base_temp_code);
325static bool is_valid_ras_2819t_command(uint64_t rc_code_1, uint64_t rc_code_2 = 0) {
327 uint16_t header1 = (rc_code_1 >> 32) & 0xFFFF;
333 if (rc_code_2 == 0) {
335 if (rc_code_1 == valid_cmd) {
344 uint8_t header2 = (rc_code_2 >> 40) & 0xFF;
350 uint8_t temp_byte = (rc_code_1 >> 8) & 0xFF;
351 uint8_t temp_complement = rc_code_1 & 0xFF;
352 if (temp_byte !=
static_cast<uint8_t
>(~temp_complement)) {
357 uint16_t fan_code = (rc_code_1 >> 16) & 0xFFFF;
358 uint8_t fan2_byte = (rc_code_2 >> 32) & 0xFF;
361 bool valid_fan_combo =
false;
363 valid_fan_combo =
true;
365 valid_fan_combo =
true;
367 valid_fan_combo =
true;
369 valid_fan_combo =
true;
371 valid_fan_combo =
true;
373 valid_fan_combo =
true;
375 return valid_fan_combo;
392 if (restore.has_value()) {
393 restore->apply(
this);
418 ESP_LOGW(TAG,
"Invalid mode detected during setup, resetting to OFF");
423 if (!this->fan_mode.has_value()) {
424 ESP_LOGW(TAG,
"Fan mode not set during setup, defaulting to AUTO");
431#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
433 const char *fan_mode_str =
"NONE";
434 char fan_mode_buf[4];
435 if (this->fan_mode.has_value()) {
436 buf_append_printf(fan_mode_buf,
sizeof(fan_mode_buf), 0,
"%d",
static_cast<int>(this->fan_mode.value()));
437 fan_mode_str = fan_mode_buf;
439 ESP_LOGV(TAG,
"Setup complete - Mode: %d, Fan: %s, Swing: %d, Temp: %.1f",
static_cast<int>(this->
mode), fan_mode_str,
456 uint8_t message_length = 9;
463 message[2] = message_length - 6;
478 switch (this->mode) {
505 switch (this->fan_mode.value()) {
537 for (uint8_t i = 4; i < 8; i++) {
545 this->encode_(data,
message, message_length, 1);
570 switch (this->fan_mode.value()) {
601 index =
static_cast<uint8_t
>(roundf(temp_adjd));
621 switch (this->
mode) {
659 for (index = 6; index <= 10; index++) {
716 if (cs_send_update) {
718 }
else if (cs_state) {
724 switch (this->
mode) {
754 bool swing_changed = (this->
swing_mode != this->last_swing_mode_);
755 bool mode_changed = (this->
mode != this->last_mode_);
756 bool fan_changed = (this->fan_mode != this->last_fan_mode_);
757 bool temp_changed = (abs(this->
target_temperature - this->last_target_temperature_) > 0.1f);
759 bool only_swing_changed = swing_changed && !mode_changed && !fan_changed && !temp_changed;
761 if (only_swing_changed) {
764 auto *swing_data = swing_transmit.
get_data();
777 swing_transmit.perform();
781 this->last_mode_ = this->
mode;
782 this->last_fan_mode_ = this->
fan_mode;
811 uint8_t temp_code = get_ras_2819t_temp_code(
temperature);
820 ESP_LOGW(TAG,
"Dry mode only supports AUTO fan speed, forcing AUTO");
824 uint16_t fan_code = get_ras_2819t_fan_code(effective_fan_mode);
827 switch (this->
mode) {
830 message1[2] = (fan_code >> 8) & 0xFF;
831 message1[3] = fan_code & 0xFF;
832 message1[4] = temp_code;
833 message1[5] = ~temp_code;
838 message1[2] = (fan_code >> 8) & 0xFF;
839 message1[3] = fan_code & 0xFF;
858 message1[5] = ~message1[4];
863 message1[2] = (fan_code >> 8) & 0xFF;
864 message1[3] = fan_code & 0xFF;
871 message1[2] = (fan_code >> 8) & 0xFF;
872 message1[3] = fan_code & 0xFF;
873 message1[4] = temp_code;
874 message1[5] = ~temp_code;
882 Ras2819tSecondPacketCodes second_packet_codes = get_ras_2819t_second_packet_codes(effective_fan_mode);
885 switch (this->
mode) {
887 message2[1] = second_packet_codes.fan_byte;
889 message2[3] = second_packet_codes.suffix.byte3;
890 message2[4] = second_packet_codes.suffix.byte4;
891 message2[5] = second_packet_codes.suffix.byte5;
895 message2[1] = second_packet_codes.fan_byte;
897 message2[3] = second_packet_codes.suffix.byte3;
913 message2[1] = second_packet_codes.fan_byte;
915 message2[3] = second_packet_codes.suffix.byte3;
921 message2[1] = second_packet_codes.fan_byte;
923 message2[3] = second_packet_codes.suffix.byte3;
924 message2[4] = second_packet_codes.suffix.byte4;
925 message2[5] = second_packet_codes.suffix.byte5;
948 this->last_mode_ = this->
mode;
949 this->last_fan_mode_ = this->
fan_mode;
957 for (
auto i : header) {
958 if ((
message[0] == i) && (
message[1] ==
static_cast<uint8_t
>(~i)))
969 if (message1[i] != message2[i])
1008 ESP_LOGI(TAG,
"Mode: OFF");
1018 ESP_LOGI(TAG,
"Swing: OFF");
1021 ESP_LOGI(TAG,
"Swing: VERTICAL");
1030 uint8_t message1[6], message2[6];
1031 for (uint8_t i = 0; i < 6; i++) {
1032 message1[i] = (toshiba_data.
rc_code_1 >> (40 - i * 8)) & 0xFF;
1033 message2[i] = (toshiba_data.
rc_code_2 >> (40 - i * 8)) & 0xFF;
1037 uint8_t temp_code = message1[4];
1040 if ((message1[2] == 0x7B) && (message1[3] == 0x84)) {
1043 ESP_LOGI(TAG,
"Mode: OFF");
1044 }
else if ((message1[2] == 0x1F) && (message1[3] == 0xE0)) {
1046 if ((temp_code & 0x0F) == 0x08) {
1048 ESP_LOGI(TAG,
"Mode: AUTO");
1049 }
else if ((temp_code & 0x0F) == 0x04) {
1051 ESP_LOGI(TAG,
"Mode: DRY");
1054 ESP_LOGI(TAG,
"Mode: COOL (low temp)");
1058 if ((temp_code & 0x0F) == 0x0C) {
1060 ESP_LOGI(TAG,
"Mode: HEAT");
1061 }
else if (message1[5] == 0x1B) {
1063 ESP_LOGI(TAG,
"Mode: FAN_ONLY");
1066 ESP_LOGI(TAG,
"Mode: COOL");
1071 uint16_t fan_code = (message1[2] << 8) | message1[3];
1072 this->fan_mode = decode_ras_2819t_fan_mode(fan_code);
1082 ESP_LOGD(TAG,
"Unknown single-packet RAS-2819T command: 0x%" PRIX64, toshiba_data.
rc_code_1);
1090 auto decode_result = toshiba_protocol.
decode(data);
1092 if (decode_result.has_value()) {
1093 auto toshiba_data = decode_result.value();
1095 if (is_valid_ras_2819t_command(toshiba_data.rc_code_1, toshiba_data.rc_code_2)) {
1109 if (!this->decode_(&data,
message, message_length)) {
1121 message_length =
message[2] + 2;
1124 if (!this->decode_(&data, &
message[4], message_length)) {
1199 switch (message[2]) {
1308 switch (message[6] & 0xF0) {
1340 const uint8_t repeat) {
1343 for (uint8_t copy = 0; copy <= repeat; copy++) {
1346 for (uint8_t
byte = 0;
byte < nbytes;
byte++) {
1347 for (uint8_t bit = 0; bit < 8; bit++) {
1349 if (
message[
byte] & (1 << (7 - bit))) {
1360bool ToshibaClimate::decode_(remote_base::RemoteReceiveData *data, uint8_t *
message,
const uint8_t nbytes) {
1361 for (uint8_t
byte = 0;
byte < nbytes;
byte++) {
1362 for (uint8_t bit = 0; bit < 8; bit++) {
1364 message[byte] |= 1 << (7 - bit);
1366 message[byte] &=
static_cast<uint8_t
>(~(1 << (7 - bit)));
constexpr bool empty() const
Check if the set is empty.
ClimateMode mode
The active mode of the climate device.
optional< ClimateFanMode > fan_mode
The active fan mode of the climate device.
float target_temperature
The target temperature of the climate device.
ClimateSwingMode swing_mode
The active swing mode of the climate device.
float current_temperature
The current temperature of the climate device, as reported from the integration.
void publish_state()
Publish the state of the climate device, to be called from integrations.
optional< ClimateDeviceRestoreState > restore_state_()
Restore the state of the climate device, call this from your setup() method.
climate::ClimateSwingModeMask swing_modes_
float maximum_temperature_
float minimum_temperature_
bool expect_item(uint32_t mark, uint32_t space)
void space(uint32_t length)
void set_carrier_frequency(uint32_t carrier_frequency)
void mark(uint32_t length)
void item(uint32_t mark, uint32_t space)
RemoteTransmitterBase * transmitter_
RemoteTransmitData * get_data()
optional< ToshibaAcData > decode(RemoteReceiveData src) override
void add_on_state_callback(std::function< void(float)> &&callback)
Add a callback that will be called every time a filtered value arrives.
float state
This member variable stores the last state that has passed through all filters.
bool compare_rac_pt1411hwru_packets_(const uint8_t *message1, const uint8_t *message2)
bool is_valid_rac_pt1411hwru_message_(const uint8_t *message)
void transmit_ras_2819t_()
uint8_t is_valid_rac_pt1411hwru_header_(const uint8_t *message)
void transmit_rac_pt1411hwru_()
bool process_ras_2819t_command_(const remote_base::ToshibaAcData &toshiba_data)
void transmit_rac_pt1411hwru_temp_(bool cs_state=true, bool cs_send_update=true)
void transmit_state() override
bool on_receive(remote_base::RemoteReceiveData data) override
@ 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.
ClimateFanMode
NOTE: If adding values, update ClimateFanModeMask in climate_traits.h to use the new last value.
@ CLIMATE_FAN_MEDIUM
The fan mode is set to Medium.
@ CLIMATE_FAN_AUTO
The fan mode is set to Auto.
@ CLIMATE_FAN_LOW
The fan mode is set to Low.
@ CLIMATE_FAN_QUIET
The fan mode is set to Quiet.
@ CLIMATE_FAN_HIGH
The fan mode is set to High.
const uint8_t RAS_2819T_FAN2_MEDIUM
const uint8_t TOSHIBA_HEADER_LENGTH
const uint8_t RAC_PT1411HWRU_MODE_HEAT
const uint16_t RAS_2819T_VALID_HEADER1
const uint16_t TOSHIBA_HEADER_MARK
const uint8_t TOSHIBA_MODE_HEAT
const Ras2819tPacketSuffix RAS_2819T_SUFFIX_AUTO
const uint8_t RAC_PT1411HWRU_MODE_FAN
const uint8_t RAC_PT1411HWRU_CS_ENABLED
const uint16_t TOSHIBA_CARRIER_FREQUENCY
const uint8_t TOSHIBA_FAN_SPEED_2
const uint8_t RAC_PT1411HWRU_MODE_AUTO
const uint16_t RAS_2819T_FAN_AUTO
const RacPt1411hwruFanSpeed RAC_PT1411HWRU_NO_FAN
const uint8_t RAC_PT1411HWRU_MODE_COOL
const uint8_t RAS_2819T_DRY_BYTE3
const uint64_t RAS_2819T_POWER_OFF_COMMAND
const uint8_t RAC_PT1411HWRU_TEMPERATURE_FAN_ONLY
const float TOSHIBA_RAS_2819T_TEMP_C_MAX
const uint8_t TOSHIBA_COMMAND_MOTION
const Ras2819tPacketSuffix RAS_2819T_SUFFIX_LOW
const uint8_t RAS_2819T_FAN2_QUIET
const uint8_t RAS_2819T_FAN2_AUTO
const uint8_t RAC_PT1411HWRU_FLAG_FAH
const uint8_t RAS_2819T_HEAT_SUFFIX
const uint8_t RAS_2819T_AUTO_DRY_SUFFIX
const uint64_t RAS_2819T_SWING_TOGGLE
const uint8_t RAC_PT1411HWRU_CS_FOOTER_COOL
const uint8_t RAC_PT1411HWRU_CS_DATA
const Ras2819tPacketSuffix RAS_2819T_SUFFIX_MEDIUM
const uint8_t TOSHIBA_MODE_DRY
const uint8_t RAC_PT1411HWRU_FAN_OFF
const uint8_t TOSHIBA_FAN_SPEED_QUIET
const uint16_t TOSHIBA_BIT_MARK
const uint8_t RAC_PT1411HWRU_FLAG_NEG
const uint8_t RAS_2819T_VALID_HEADER2
const uint8_t RAS_2819T_HEADER2
const float TOSHIBA_RAC_PT1411HWRU_TEMP_F_MIN
const uint8_t TOSHIBA_POWER_HIGH
const uint8_t RAS_2819T_FAN2_LOW
const uint8_t TOSHIBA_MODE_FAN_ONLY
const uint8_t TOSHIBA_POWER_ECO
const float TOSHIBA_GENERIC_TEMP_C_MAX
const uint8_t TOSHIBA_MODE_COOL
const float TOSHIBA_GENERIC_TEMP_C_MIN
const uint16_t TOSHIBA_ZERO_SPACE
const uint8_t TOSHIBA_FAN_SPEED_3
constexpr RacPt1411hwruFanSpeed RAC_PT1411HWRU_FAN_LOW
const uint16_t TOSHIBA_GAP_SPACE
const uint8_t RAC_PT1411HWRU_MESSAGE_HEADER1
const uint8_t RAS_2819T_DRY_BYTE2
const float TOSHIBA_RAC_PT1411HWRU_TEMP_C_MAX
const uint8_t RAC_PT1411HWRU_CS_HEADER
const uint8_t TOSHIBA_COMMAND_POWER
const uint16_t RAS_2819T_FAN_MEDIUM
constexpr RacPt1411hwruFanSpeed RAC_PT1411HWRU_FAN_AUTO
const std::vector< uint8_t > RAC_PT1411HWRU_TEMPERATURE_F
const uint8_t TOSHIBA_COMMAND_TIMER
const uint8_t TOSHIBA_FAN_SPEED_4
const Ras2819tPacketSuffix RAS_2819T_SUFFIX_QUIET
const uint8_t TOSHIBA_COMMAND_DEFAULT
const uint8_t TOSHIBA_FAN_SPEED_1
const std::vector< uint8_t > RAC_PT1411HWRU_SWING_VERTICAL
const uint8_t RAC_PT1411HWRU_CS_FOOTER_AUTO
const uint16_t RAS_2819T_HEADER1
const uint8_t TOSHIBA_MOTION_SWING
const uint8_t TOSHIBA_MODE_AUTO
const uint8_t RAS_2819T_AUTO_TEMP_OFFSET
const uint8_t RAC_PT1411HWRU_MODE_DRY
const uint8_t RAS_2819T_FAN_ONLY_TEMP
const uint8_t RAS_2819T_AUTO_BYTE3
const uint16_t TOSHIBA_ONE_SPACE
constexpr RacPt1411hwruFanSpeed RAC_PT1411HWRU_FAN_HIGH
const uint8_t TOSHIBA_FAN_SPEED_5
const uint8_t RAC_PT1411HWRU_MESSAGE_HEADER0
const float TOSHIBA_RAS_2819T_TEMP_C_MIN
constexpr RacPt1411hwruFanSpeed RAC_PT1411HWRU_FAN_MED
const uint8_t RAC_PT1411HWRU_FLAG_FRAC
const uint8_t RAS_2819T_AUTO_BYTE2
const uint16_t TOSHIBA_HEADER_SPACE
const uint8_t RAS_2819T_HEAT_TEMP_OFFSET
const uint8_t TOSHIBA_MODE_OFF
const Ras2819tPacketSuffix RAS_2819T_SUFFIX_HIGH
const uint8_t RAC_PT1411HWRU_CS_FOOTER_HEAT
const uint8_t RAS_2819T_FAN2_HIGH
const uint16_t TOSHIBA_PACKET_SPACE
const uint8_t RAS_2819T_FAN_ONLY_TEMP_INV
const uint8_t RAC_PT1411HWRU_MODE_OFF
const float TOSHIBA_RAC_PT1411HWRU_TEMP_C_MIN
const uint8_t RAC_PT1411HWRU_MESSAGE_LENGTH
const uint8_t RAC_PT1411HWRU_FLAG_MASK
const uint8_t RAS_2819T_AUTO_DRY_FAN_BYTE
const uint16_t RAS_2819T_FAN_LOW
const std::array< uint64_t, 2 > RAS_2819T_VALID_SINGLE_COMMANDS
const uint8_t TOSHIBA_MOTION_FIX
const uint16_t RAS_2819T_FAN_QUIET
const std::vector< uint8_t > RAC_PT1411HWRU_TEMPERATURE_C
const uint16_t RAS_2819T_FAN_HIGH
const uint8_t RAC_PT1411HWRU_SWING_HEADER
const uint8_t RAS_2819T_MESSAGE_LENGTH
const uint8_t TOSHIBA_FAN_SPEED_AUTO
const uint8_t RAS_2819T_DRY_TEMP_OFFSET
const std::vector< uint8_t > RAC_PT1411HWRU_SWING_OFF
Providing packet encoding functions for exchanging data with a remote host.