9struct RacPt1411hwruFanSpeed {
14static const char *
const TAG =
"toshiba.climate";
95 0x04, 0x0C, 0x0D, 0x09, 0x08, 0x0A, 0x0B};
98 0x22, 0x06, 0x26, 0x07, 0x05, 0x25, 0x04, 0x24, 0x0C,
99 0x2C, 0x0D, 0x2D, 0x09, 0x08, 0x28, 0x0A, 0x2A, 0x0B};
122struct Ras2819tPacketSuffix {
167static const uint8_t RAS_2819T_TEMP_CODES[] = {
251struct Ras2819tSecondPacketCodes {
253 Ras2819tPacketSuffix suffix;
275static uint8_t get_ras_2819t_temp_code(
float temperature) {
276 int temp_index =
static_cast<int>(
temperature) - 18;
277 if (temp_index < 0 ||
static_cast<size_t>(temp_index) >=
sizeof(RAS_2819T_TEMP_CODES)) {
278 ESP_LOGW(TAG,
"Temperature %.1f°C out of range [18-30°C], defaulting to 24°C",
temperature);
282 return RAS_2819T_TEMP_CODES[temp_index];
288static float decode_ras_2819t_temperature(uint8_t temp_code) {
289 uint8_t base_temp_code = temp_code & 0xF0;
292 for (
size_t temp_index = 0; temp_index <
sizeof(RAS_2819T_TEMP_CODES); temp_index++) {
293 if (RAS_2819T_TEMP_CODES[temp_index] == base_temp_code) {
294 return static_cast<float>(temp_index + 18);
298 ESP_LOGW(TAG,
"Unknown temp code: 0x%02X, defaulting to 24°C", base_temp_code);
324static bool is_valid_ras_2819t_command(uint64_t rc_code_1, uint64_t rc_code_2 = 0) {
326 uint16_t header1 = (rc_code_1 >> 32) & 0xFFFF;
332 if (rc_code_2 == 0) {
334 if (rc_code_1 == valid_cmd) {
343 uint8_t header2 = (rc_code_2 >> 40) & 0xFF;
349 uint8_t temp_byte = (rc_code_1 >> 8) & 0xFF;
350 uint8_t temp_complement = rc_code_1 & 0xFF;
351 if (temp_byte !=
static_cast<uint8_t
>(~temp_complement)) {
356 uint16_t fan_code = (rc_code_1 >> 16) & 0xFFFF;
357 uint8_t fan2_byte = (rc_code_2 >> 32) & 0xFF;
360 bool valid_fan_combo =
false;
362 valid_fan_combo =
true;
364 valid_fan_combo =
true;
366 valid_fan_combo =
true;
368 valid_fan_combo =
true;
370 valid_fan_combo =
true;
372 valid_fan_combo =
true;
374 return valid_fan_combo;
391 if (restore.has_value()) {
392 restore->apply(
this);
417 ESP_LOGW(TAG,
"Invalid mode detected during setup, resetting to OFF");
422 if (!this->fan_mode.has_value()) {
423 ESP_LOGW(TAG,
"Fan mode not set during setup, defaulting to AUTO");
430#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
432 const char *fan_mode_str =
"NONE";
433 char fan_mode_buf[4];
434 if (this->fan_mode.has_value()) {
435 buf_append_printf(fan_mode_buf,
sizeof(fan_mode_buf), 0,
"%d",
static_cast<int>(this->fan_mode.value()));
436 fan_mode_str = fan_mode_buf;
438 ESP_LOGV(TAG,
"Setup complete - Mode: %d, Fan: %s, Swing: %d, Temp: %.1f",
static_cast<int>(this->
mode), fan_mode_str,
455 uint8_t message_length = 9;
462 message[2] = message_length - 6;
477 switch (this->mode) {
536 for (uint8_t i = 4; i < 8; i++) {
542 auto *data = transmit.get_data();
544 this->encode_(data,
message, message_length, 1);
555 auto *data = transmit.get_data();
600 index =
static_cast<uint8_t
>(roundf(temp_adjd));
620 switch (this->
mode) {
658 for (index = 6; index <= 10; index++) {
699 auto *data = transmit.get_data();
715 if (cs_send_update) {
717 }
else if (cs_state) {
723 switch (this->
mode) {
753 bool swing_changed = (this->
swing_mode != this->last_swing_mode_);
754 bool mode_changed = (this->
mode != this->last_mode_);
755 bool fan_changed = (this->fan_mode != this->last_fan_mode_);
756 bool temp_changed = (abs(this->
target_temperature - this->last_target_temperature_) > 0.1f);
758 bool only_swing_changed = swing_changed && !mode_changed && !fan_changed && !temp_changed;
760 if (only_swing_changed) {
763 auto *swing_data = swing_transmit.
get_data();
776 swing_transmit.perform();
780 this->last_mode_ = this->
mode;
781 this->last_fan_mode_ = this->
fan_mode;
810 uint8_t temp_code = get_ras_2819t_temp_code(
temperature);
819 ESP_LOGW(TAG,
"Dry mode only supports AUTO fan speed, forcing AUTO");
823 uint16_t fan_code = get_ras_2819t_fan_code(effective_fan_mode);
826 switch (this->
mode) {
829 message1[2] = (fan_code >> 8) & 0xFF;
830 message1[3] = fan_code & 0xFF;
831 message1[4] = temp_code;
832 message1[5] = ~temp_code;
837 message1[2] = (fan_code >> 8) & 0xFF;
838 message1[3] = fan_code & 0xFF;
857 message1[5] = ~message1[4];
862 message1[2] = (fan_code >> 8) & 0xFF;
863 message1[3] = fan_code & 0xFF;
870 message1[2] = (fan_code >> 8) & 0xFF;
871 message1[3] = fan_code & 0xFF;
872 message1[4] = temp_code;
873 message1[5] = ~temp_code;
881 Ras2819tSecondPacketCodes second_packet_codes = get_ras_2819t_second_packet_codes(effective_fan_mode);
884 switch (this->
mode) {
886 message2[1] = second_packet_codes.fan_byte;
888 message2[3] = second_packet_codes.suffix.byte3;
889 message2[4] = second_packet_codes.suffix.byte4;
890 message2[5] = second_packet_codes.suffix.byte5;
894 message2[1] = second_packet_codes.fan_byte;
896 message2[3] = second_packet_codes.suffix.byte3;
912 message2[1] = second_packet_codes.fan_byte;
914 message2[3] = second_packet_codes.suffix.byte3;
920 message2[1] = second_packet_codes.fan_byte;
922 message2[3] = second_packet_codes.suffix.byte3;
923 message2[4] = second_packet_codes.suffix.byte4;
924 message2[5] = second_packet_codes.suffix.byte5;
933 auto *data = transmit.get_data();
947 this->last_mode_ = this->
mode;
948 this->last_fan_mode_ = this->
fan_mode;
956 for (
auto i : HEADERS) {
957 if ((
message[0] == i) && (
message[1] ==
static_cast<uint8_t
>(~i)))
968 if (message1[i] != message2[i])
1007 ESP_LOGI(TAG,
"Mode: OFF");
1017 ESP_LOGI(TAG,
"Swing: OFF");
1020 ESP_LOGI(TAG,
"Swing: VERTICAL");
1029 uint8_t message1[6], message2[6];
1030 for (uint8_t i = 0; i < 6; i++) {
1031 message1[i] = (toshiba_data.
rc_code_1 >> (40 - i * 8)) & 0xFF;
1032 message2[i] = (toshiba_data.
rc_code_2 >> (40 - i * 8)) & 0xFF;
1036 uint8_t temp_code = message1[4];
1039 if ((message1[2] == 0x7B) && (message1[3] == 0x84)) {
1042 ESP_LOGI(TAG,
"Mode: OFF");
1043 }
else if ((message1[2] == 0x1F) && (message1[3] == 0xE0)) {
1045 if ((temp_code & 0x0F) == 0x08) {
1047 ESP_LOGI(TAG,
"Mode: AUTO");
1048 }
else if ((temp_code & 0x0F) == 0x04) {
1050 ESP_LOGI(TAG,
"Mode: DRY");
1053 ESP_LOGI(TAG,
"Mode: COOL (low temp)");
1057 if ((temp_code & 0x0F) == 0x0C) {
1059 ESP_LOGI(TAG,
"Mode: HEAT");
1060 }
else if (message1[5] == 0x1B) {
1062 ESP_LOGI(TAG,
"Mode: FAN_ONLY");
1065 ESP_LOGI(TAG,
"Mode: COOL");
1070 uint16_t fan_code = (message1[2] << 8) | message1[3];
1071 this->fan_mode = decode_ras_2819t_fan_mode(fan_code);
1081 ESP_LOGD(TAG,
"Unknown single-packet RAS-2819T command: 0x%" PRIX64, toshiba_data.
rc_code_1);
1089 auto decode_result = toshiba_protocol.
decode(data);
1091 if (decode_result.has_value()) {
1092 auto toshiba_data = decode_result.value();
1094 if (is_valid_ras_2819t_command(toshiba_data.rc_code_1, toshiba_data.rc_code_2)) {
1108 if (!this->decode_(&data,
message, message_length)) {
1120 message_length =
message[2] + 2;
1123 if (!this->decode_(&data, &
message[4], message_length)) {
1198 switch (message[2]) {
1307 switch (message[6] & 0xF0) {
1339 const uint8_t repeat) {
1342 for (uint8_t copy = 0; copy <= repeat; copy++) {
1345 for (uint8_t
byte = 0;
byte < nbytes;
byte++) {
1346 for (uint8_t bit = 0; bit < 8; bit++) {
1348 if (
message[
byte] & (1 << (7 - bit))) {
1359bool ToshibaClimate::decode_(remote_base::RemoteReceiveData *data, uint8_t *
message,
const uint8_t nbytes) {
1360 for (uint8_t
byte = 0;
byte < nbytes;
byte++) {
1361 for (uint8_t bit = 0; bit < 8; bit++) {
1363 message[byte] |= 1 << (7 - bit);
1365 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_
RemoteTransmitterBase * transmitter_
RemoteTransmitData * get_data()
optional< ToshibaAcData > decode(RemoteReceiveData src) override
void add_on_state_callback(F &&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_ON
The fan mode is set to On.
@ 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 std::vector< uint8_t > & data
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