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 || temp_index >=
static_cast<int>(
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");
431 ESP_LOGV(TAG,
"Setup complete - Mode: %d, Fan: %s, Swing: %d, Temp: %.1f",
static_cast<int>(this->
mode),
432 this->fan_mode.has_value() ? std::to_string(
static_cast<int>(this->fan_mode.value())).c_str() :
"NONE",
433 static_cast<int>(this->swing_mode), this->target_temperature);
448 uint8_t message_length = 9;
455 message[2] = message_length - 6;
470 switch (this->mode) {
497 switch (this->fan_mode.value()) {
529 for (uint8_t i = 4; i < 8; i++) {
537 this->encode_(data,
message, message_length, 1);
562 switch (this->fan_mode.value()) {
593 index =
static_cast<uint8_t
>(roundf(temp_adjd));
613 switch (this->
mode) {
651 for (index = 6; index <= 10; index++) {
708 if (cs_send_update) {
710 }
else if (cs_state) {
716 switch (this->
mode) {
746 bool swing_changed = (this->
swing_mode != this->last_swing_mode_);
747 bool mode_changed = (this->
mode != this->last_mode_);
748 bool fan_changed = (this->fan_mode != this->last_fan_mode_);
749 bool temp_changed = (abs(this->
target_temperature - this->last_target_temperature_) > 0.1f);
751 bool only_swing_changed = swing_changed && !mode_changed && !fan_changed && !temp_changed;
753 if (only_swing_changed) {
756 auto *swing_data = swing_transmit.
get_data();
769 swing_transmit.perform();
773 this->last_mode_ = this->
mode;
774 this->last_fan_mode_ = this->
fan_mode;
803 uint8_t temp_code = get_ras_2819t_temp_code(
temperature);
812 ESP_LOGW(TAG,
"Dry mode only supports AUTO fan speed, forcing AUTO");
816 uint16_t fan_code = get_ras_2819t_fan_code(effective_fan_mode);
819 switch (this->
mode) {
822 message1[2] = (fan_code >> 8) & 0xFF;
823 message1[3] = fan_code & 0xFF;
824 message1[4] = temp_code;
825 message1[5] = ~temp_code;
830 message1[2] = (fan_code >> 8) & 0xFF;
831 message1[3] = fan_code & 0xFF;
850 message1[5] = ~message1[4];
855 message1[2] = (fan_code >> 8) & 0xFF;
856 message1[3] = fan_code & 0xFF;
863 message1[2] = (fan_code >> 8) & 0xFF;
864 message1[3] = fan_code & 0xFF;
865 message1[4] = temp_code;
866 message1[5] = ~temp_code;
874 Ras2819tSecondPacketCodes second_packet_codes = get_ras_2819t_second_packet_codes(effective_fan_mode);
877 switch (this->
mode) {
879 message2[1] = second_packet_codes.fan_byte;
881 message2[3] = second_packet_codes.suffix.byte3;
882 message2[4] = second_packet_codes.suffix.byte4;
883 message2[5] = second_packet_codes.suffix.byte5;
887 message2[1] = second_packet_codes.fan_byte;
889 message2[3] = second_packet_codes.suffix.byte3;
905 message2[1] = second_packet_codes.fan_byte;
907 message2[3] = second_packet_codes.suffix.byte3;
913 message2[1] = second_packet_codes.fan_byte;
915 message2[3] = second_packet_codes.suffix.byte3;
916 message2[4] = second_packet_codes.suffix.byte4;
917 message2[5] = second_packet_codes.suffix.byte5;
940 this->last_mode_ = this->
mode;
941 this->last_fan_mode_ = this->
fan_mode;
949 for (
auto i : header) {
950 if ((
message[0] == i) && (
message[1] ==
static_cast<uint8_t
>(~i)))
961 if (message1[i] != message2[i])
1000 ESP_LOGI(TAG,
"Mode: OFF");
1010 ESP_LOGI(TAG,
"Swing: OFF");
1013 ESP_LOGI(TAG,
"Swing: VERTICAL");
1022 uint8_t message1[6], message2[6];
1023 for (uint8_t i = 0; i < 6; i++) {
1024 message1[i] = (toshiba_data.
rc_code_1 >> (40 - i * 8)) & 0xFF;
1025 message2[i] = (toshiba_data.
rc_code_2 >> (40 - i * 8)) & 0xFF;
1029 uint8_t temp_code = message1[4];
1032 if ((message1[2] == 0x7B) && (message1[3] == 0x84)) {
1035 ESP_LOGI(TAG,
"Mode: OFF");
1036 }
else if ((message1[2] == 0x1F) && (message1[3] == 0xE0)) {
1038 if ((temp_code & 0x0F) == 0x08) {
1040 ESP_LOGI(TAG,
"Mode: AUTO");
1041 }
else if ((temp_code & 0x0F) == 0x04) {
1043 ESP_LOGI(TAG,
"Mode: DRY");
1046 ESP_LOGI(TAG,
"Mode: COOL (low temp)");
1050 if ((temp_code & 0x0F) == 0x0C) {
1052 ESP_LOGI(TAG,
"Mode: HEAT");
1053 }
else if (message1[5] == 0x1B) {
1055 ESP_LOGI(TAG,
"Mode: FAN_ONLY");
1058 ESP_LOGI(TAG,
"Mode: COOL");
1063 uint16_t fan_code = (message1[2] << 8) | message1[3];
1064 this->fan_mode = decode_ras_2819t_fan_mode(fan_code);
1074 ESP_LOGD(TAG,
"Unknown single-packet RAS-2819T command: 0x%" PRIX64, toshiba_data.
rc_code_1);
1082 auto decode_result = toshiba_protocol.
decode(data);
1084 if (decode_result.has_value()) {
1085 auto toshiba_data = decode_result.value();
1087 if (is_valid_ras_2819t_command(toshiba_data.rc_code_1, toshiba_data.rc_code_2)) {
1101 if (!this->decode_(&data,
message, message_length)) {
1113 message_length =
message[2] + 2;
1116 if (!this->decode_(&data, &
message[4], message_length)) {
1191 switch (message[2]) {
1300 switch (message[6] & 0xF0) {
1332 const uint8_t repeat) {
1335 for (uint8_t copy = 0; copy <= repeat; copy++) {
1338 for (uint8_t
byte = 0;
byte < nbytes;
byte++) {
1339 for (uint8_t bit = 0; bit < 8; bit++) {
1341 if (
message[
byte] & (1 << (7 - bit))) {
1352bool ToshibaClimate::decode_(remote_base::RemoteReceiveData *data, uint8_t *
message,
const uint8_t nbytes) {
1353 for (uint8_t
byte = 0;
byte < nbytes;
byte++) {
1354 for (uint8_t bit = 0; bit < 8; bit++) {
1356 message[byte] |= 1 << (7 - bit);
1358 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.